/**
 * ============================================================================
 * = 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.1 for the IXP2000 Network Processor
 *
 * = FILENAME
 *      ix_cc_stkdrv.c
 *
 * = DESCRIPTION
 *      Source file for the local Core Component moodule of the Stack Driver.
 *
 * 
 *
 * = CHANGE HISTORY
 *       7/19/2002 - creation time
 *
 * ============================================================================
 */

#define IX_ERROR_FILE_IDENT "$Id: ix_cc_stkdrv.c,v 1.106 2003/12/12 00:53:17 ktseng Exp $"

#include "ix_ossl.h"
#include "ix_rm.h"
#if !defined(IX_EXCLUDE_CCI)
#include "ix_cci.h"
#endif /* end !defined(IX_EXCLUDE_CCI) */

#include "ix_cc_error.h"
#include "ix_cc.h"
#include "cc/ix_cc_stkdrv_common.h"

#include "cc/ix_cc_stkdrv_pktclass.h"
#include "cc/internal/internal_stkdrv_pktclass.h"
#include "cc/ix_cc_stkdrv_tm.h"
#include "cc/ix_cc_stkdrv.h"
#include "cc/internal/internal_stkdrv.h"

#include "ix_netmacros.h"

#include "cc/ix_cc_msup.h"

#if (defined(IX_CONFIGURATION_unit_test) || defined(MUX_STUB))
    /* global stack driver control structure used in tests. */
    /* Also needed if the MUX layer is stubbed on Vxsim. */
    ix_cc_stkdrv_ctrl* g_pStkdrvCtrl;
#endif /* (defined(IX_CONFIGURATION_unit_test) || defined(MUX_STUB)) */

#if defined(IX_CC_MSUP_STUB)
/* Stubbing for msg support library, used in unit tests. */
void* g_pUserContext;

#undef IX_MSUP_EXTRACT_MSG
#define IX_MSUP_EXTRACT_MSG( \
                             msg, \
                             mymsg, \
                             context \
                           ) \
    do { \
        ix_rm_buffer_get_data(msg, mymsg); \
        *context = g_pUserContext; \
    } \
    while(0)
#endif /* defined(IX_CC_MSUP_STUB) */


#include "bindings.h"


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

/* Length of ethernet header in bytes. */
#define IX_CC_ETHERNET_HDR_LEN 14

/* Constants from POS microblock.... should be in shared header? */
/* Used to construct PPP header for multicast packets. */
#define L2_HEADER_ALREADY_EXIST_ID	0xFFFF
#define PPP_L2_HEADER_SIZE		2
#define PPP_HEADER_IP	 			0x0021


/**
 * Types definitions whose scope is limited to this file.
 */

/**
 * TYPENAME: _ix_cc_stkdrv_get_num_ports_context
 * 
 * DESCRIPTION: Context for getting number of ports asynchronously.
 *
 */
typedef struct _ix_s_cc_stkdrv_get_num_ports_context
{
    ix_cc_stkdrv_cb_get_num_ports pUserCallback;
    void* pUserContext;
} _ix_cc_stkdrv_get_num_ports_context;


/**
 * TYPENAME: _ix_cc_stkdrv_get_num_ports_msg
 * 
 * DESCRIPTION: Message structure used for getting number
 *              of ports asynchronously.
 *
 */
typedef struct _ix_s_cc_stkdrv_get_num_ports_msg
{
    ix_uint32 numPorts;
} _ix_cc_stkdrv_get_num_ports_msg;


/**
 * TYPENAME: _ix_cc_stkdrv_get_property_context
 * 
 * DESCRIPTION: Context for getting property data asynchronously.
 *
 */
typedef struct _ix_s_cc_stkdrv_get_property_context
{
    ix_cc_stkdrv_cb_get_property pUserCallback;
    void* pUserContext;
} _ix_cc_stkdrv_get_property_context;

/**
 * TYPENAME: _ix_cc_stkdrv_get_properties_msg
 * 
 * DESCRIPTION: Structure to hold property query messages.
 *
 */
typedef struct _ix_s_cc_stkdrv_get_properties_msg
{
    ix_uint32 propID; /* ID of property update. */
    ix_uint32 portID; /* ID of port being queried. */
} _ix_cc_stkdrv_get_properties_msg;


/**
 * TYPENAME: _ix_cc_stkdrv_send_handler_msg_context
 * 
 * DESCRIPTION: Context for sending a message to a comm handler asynchronously.
 *
 */
typedef struct _ix_s_cc_stkdrv_send_handler_msg_context
{
    ix_cc_msghlp_callback pUserCallback;
    void* pUserContext;
} _ix_cc_stkdrv_send_handler_msg_context;

/**
 * TYPENAME: _ix_cc_stkdrv_send_handler_msg_message
 * 
 * DESCRIPTION: Structure to hold information used to send a message
 *              to a comm handler asynchronously.
 *
 */
typedef struct _ix_s_cc_stkdrv_send_handler_msg_message
{
    ix_cc_stkdrv_handler_id handlerID;
    ix_uint32 msgID;
    void* pMsg;
    ix_uint32 msgSize;
} _ix_cc_stkdrv_send_handler_msg_message;

/**
 * TYPENAME: _ix_cc_stkdrv_register_comm_handler_context
 * 
 * DESCRIPTION: Context for registering a comm handler asynchronously.
 *
 */
typedef struct _ix_s_cc_stkdrv_register_comm_handler_context
{
    ix_cc_stkdrv_cb_register_comm_handler pUserCallback;
    void* pUserContext;
} _ix_cc_stkdrv_register_comm_handler_context;

/**
 * TYPENAME: _ix_cc_stkdrv_register_comm_handler_msg
 * 
 * DESCRIPTION: Structure to hold information used to register
 *              a comm handler asynchronously.
 *
 */
typedef struct _ix_s_cc_stkdrv_register_comm_handler_msg
{
    ix_cc_stkdrv_handler_id handlerID;
    void* pInitContext;
    ix_cc_stkdrv_handler_module_init_cb initCB;
} _ix_cc_stkdrv_register_comm_handler_msg;

/**
 * TYPENAME: _ix_cc_stkdrv_unregister_comm_handler_msg
 * 
 * DESCRIPTION: Structure to hold information used to unregister
 *              a comm handler asynchronously.
 *
 */
typedef struct _ix_s_cc_stkdrv_unregister_comm_handler_msg
{
    ix_cc_stkdrv_handler_id handlerID;
} _ix_cc_stkdrv_unregister_comm_handler_msg;

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

/**
 * Extern function prototypes.
 */

/**
 * Static function prototypes - function and argument description for each function
 * is below along with each implementation.
 */
PRIVATE ix_error _ix_cc_stkdrv_process_pkt (ix_buffer_handle arg_hDataToken, ix_cc_stkdrv_ctrl* arg_pStkdrvCtrl);

PRIVATE ix_error _ix_cc_stkdrv_cb_get_num_ports(ix_error arg_Result, void *arg_pContext, void *arg_pMsg, ix_uint32 arg_MsgLen);
PRIVATE ix_error _ix_cc_stkdrv_cb_get_property(ix_error arg_Result, void *arg_pContext, void *arg_pMsg, ix_uint32 arg_MsgLen);

PRIVATE ix_error _ix_cc_stkdrv_set_property(ix_uint32 arg_propID, ix_cc_properties* arg_pProperty, ix_cc_stkdrv_physical_if_node* arg_pPhysicalIf, ix_cc_stkdrv_ctrl* arg_pStkdrvCtrl);

PRIVATE ix_error _ix_cc_stkdrv_get_property(ix_uint32 arg_propID, ix_cc_stkdrv_physical_if_node* arg_pPhysicalIf, ix_cc_properties* arg_pProperty);

PRIVATE ix_error _ix_cc_stkdrv_cleanup(ix_uint32 arg_checkErrors, void** arg_ppStkdrvCtrl);

PRIVATE ix_error _ix_cc_stkdrv_send_handler_msg(
                                                ix_cc_stkdrv_handler_id arg_handlerID,
                                                ix_uint32 arg_msgID,
                                                void* arg_pMsg,
                                                ix_uint32 arg_msgSize,
                                                ix_cc_stkdrv_ctrl* arg_pStkdrvCtrl,
                                                void** arg_ppReplyMsg,
                                                ix_uint32* arg_pReplyMsgSize
                                                );

PRIVATE ix_error _ix_cc_stkdrv_cb_send_handler_msg(
                                                   ix_error arg_Result,
                                                   void* arg_pContext,
                                                   void* arg_pMsg,
                                                   ix_uint32 arg_MsgLen
                                                   );

#if (!(_IX_OS_TYPE_ == _IX_OS_LINUX_KERNEL_))
PRIVATE ix_error _ix_cc_stkdrv_classify_output_id(
                                                  ix_buffer_handle arg_hBuffer,
                                                  ix_communication_id* arg_pOutputCommId
                                                  );
#endif

PRIVATE ix_error _ix_cc_stkdrv_register_comm_handler(
                                                     ix_cc_stkdrv_handler_id arg_handlerID,
                                                     ix_cc_stkdrv_ctrl* arg_pStkdrvCtrl,
                                                     void* arg_pContext,
                                                     ix_cc_stkdrv_handler_module_init_cb arg_initCB
                                                     );

PRIVATE ix_error _ix_cc_stkdrv_cb_register_comm_handler(
                                                        ix_error arg_Result,
                                                        void* arg_pContext,
                                                        void* arg_pMsg,
                                                        ix_uint32 arg_MsgLen
                                                        );
ix_error ix_cc_stkdrv_pkt_handler(
				  ix_buffer_handle arg_hDataToken,
				  ix_uint32 arg_UserData,
				  void* arg_pComponentContext
				  );
/**
 * Function definitions.
 */

/**
 * NAME: ix_cc_stkdrv_init
 *
 * DESCRIPTION: Initializes the core component - creates stack driver CC
 * control structure, and makes function calls to initialize and register
 * communication handlers.  If packet classification is enabled, initializes
 * packet filters.
 * 
 * @Param:  - ix_cc_handle arg_hCC - handle to Stack Driver core component;
 *          this shall be used later to get other services from the core
 *          component infrastructure.
 * @Param:  - INOUT void **arg_ppContext - location where the pointer to the
 *          control block (ix_cc_stkdrv_ctrl) allocated by Stack Driver core
 *          component will be stored.  The control block is internal to the
 *          Stack Driver core component and it contains information about
 *          Stack Driver internal data structures, allocated memory and other
 *          relevant information. This pointer will be used later, to be passed
 *          into the ix_cc_stkdrv_fini function for freeing memory when the
 *          Stack Driver component is being destroyed.  On input, this pointer
 *          contains the location of the input context passed in by the caller.
 *
 * @Return: IX_SUCCESS
 *          IX_CC_ERROR_OOM_SYSTEM
 *          IX_CC_STKDRV_ERROR_HANDLER_INIT
 *          IX_CC_STKDRV_ERROR_CCI_ADD_HANDLER
 *          IX_CC_STKDRV_ERROR_RM_ADD_HANDLER
 */
ix_error ix_cc_stkdrv_init(
                           ix_cc_handle arg_hCC,
                           void **arg_ppContext
                           )
{
    ix_error err = IX_SUCCESS;

    ix_cc_stkdrv_ctrl* pStkdrvCtrl = NULL;

    ix_cc_stkdrv_fp_node* pFP = NULL;
    ix_cc_stkdrv_physical_if_node* pPhysicalIf = NULL;
    ix_cc_stkdrv_physical_if_info* pPhysicalIfInfo = NULL;
    ix_cc_stkdrv_virtual_if* pVirtualIf = NULL;
    ix_uint32 loop_var;

    ix_cc_init_context* pInitContext = (ix_cc_init_context*)*arg_ppContext;

    ix_cc_properties_node* pPropNode = NULL;

    ix_cc_stkdrv_handler_module* pHandlerModule = NULL;

    /* Check for NULL input context. */
    if(pInitContext == NULL)
        return IX_ERROR_LOCAL(IX_CC_ERROR_NULL, ("Stack Driver init called with NULL input context"));

    /* Allocate memory for stack driver control structure. */
    pStkdrvCtrl = ix_ossl_malloc(sizeof(ix_cc_stkdrv_ctrl));
    if(pStkdrvCtrl == NULL)
        return IX_ERROR_LOCAL(IX_CC_ERROR_OOM_SYSTEM, ("Could not allocate memory for stack driver control structure"));
    ix_ossl_memset(pStkdrvCtrl, 0, sizeof(ix_cc_stkdrv_ctrl));

    /* Save the control structure location into the context. */
    *arg_ppContext = (void*)pStkdrvCtrl;

#if (defined(IX_CONFIGURATION_unit_test) || defined(MUX_STUB))
    g_pStkdrvCtrl = pStkdrvCtrl;
#endif /* (defined(IX_CONFIGURATION_unit_test) || defined(MUX_STUB)) */

    /* Save the freelist handle into the control structure. */
    pStkdrvCtrl->hFreeList = pInitContext->hFreeList;

    /* Allocate memory for FP structure. */
    pStkdrvCtrl->pFPList = ix_ossl_malloc(sizeof(ix_cc_stkdrv_fp_node));
    pFP = pStkdrvCtrl->pFPList;
    if(pFP == NULL)
    {
        err = IX_ERROR_LOCAL(IX_CC_ERROR_OOM_SYSTEM, ("Could not allocate memory for FP structure"));
        goto label_1;
    }
    ix_ossl_memset(pFP, 0, sizeof(ix_cc_stkdrv_fp_node));

    /* Iterate through the ports defined in input context. */
    pPropNode = pInitContext->pPropertyList;
    while(pPropNode != NULL)
    {
        /* Allocate memory for the physical if structure. */
        pPhysicalIf = ix_ossl_malloc(sizeof(ix_cc_stkdrv_physical_if_node));
        if(pPhysicalIf == NULL)
        {
            err = IX_ERROR_LOCAL(IX_CC_ERROR_OOM_SYSTEM, ("Could not allocate memory for physical if structure"));
            goto label_3;
        }
        ix_ossl_memset(pPhysicalIf, 0, sizeof(ix_cc_stkdrv_physical_if_node));
        
        /* Allocate memory for physical if info structure. */
        pPhysicalIfInfo = ix_ossl_malloc(sizeof(ix_cc_stkdrv_physical_if_info));
        if(pPhysicalIfInfo == NULL)
        {
            err = IX_ERROR_LOCAL(IX_CC_ERROR_OOM_SYSTEM, ("Could not allocate memory for physical if info structure"));
            goto label_4;
        }
        ix_ossl_memset(pPhysicalIfInfo, 0,
           sizeof(ix_cc_stkdrv_physical_if_info));

        pPhysicalIfInfo->portId = pPropNode->pPropertyInfo->port_id;
        pPhysicalIfInfo->mediaType = pPropNode->pPropertyInfo->media_type;
        pPhysicalIfInfo->MTU = (ix_uint16)pPropNode->pPropertyInfo->mtu;
        pPhysicalIfInfo->physical_if_status =
           pPropNode->pPropertyInfo->physical_if_state;
        pPhysicalIfInfo->link_status = pPropNode->pPropertyInfo->link_state;
        pPhysicalIfInfo->linkSpeed = pPropNode->pPropertyInfo->link_speed;
        pFP->fpId = pPropNode->pPropertyInfo->blade_id;
        pPhysicalIfInfo->fpId = pFP->fpId;

        /* Loop to get all the virtual IFs for this physical interface */
        for (loop_var = 0;
             loop_var < pPropNode->pPropertyInfo->no_virtual_if;
             loop_var++)
        {
            /* There is no need to check if we are crossing the maximum
             * allowed IPv4 or IPv6 addresses as this test would have
             * been done already in properties.c file.
             */
            /* Allocate memory for virtual if structure. */
            pVirtualIf = ix_ossl_malloc(sizeof(ix_cc_stkdrv_virtual_if));
            if(pVirtualIf == NULL)
            {
                err = IX_ERROR_LOCAL(IX_CC_ERROR_OOM_SYSTEM,
                          ("Could not allocate memory for virtual if structure"));
                goto label_5;
            }
            /* Note that the pVirtualIf->pNextVirtualIf is set to NULL by the
             * following statement.
             */
            ix_ossl_memset(pVirtualIf, 0, sizeof(ix_cc_stkdrv_virtual_if));

            pVirtualIf->ipProp.ip_version =
                pPropNode->pPropertyInfo->ip_prop[loop_var].ip_version;
            if(pVirtualIf->ipProp.ip_version == IX_CC_IP_VERSION_IPV4)
            {
                pVirtualIf->ipProp.protocol.ipv4_prop.ipv4_address =
                    pPropNode->pPropertyInfo->ip_prop[loop_var].protocol.ipv4_prop.ipv4_address;
                pVirtualIf->ipProp.protocol.ipv4_prop.ipv4_subnet_mask =
                    pPropNode->pPropertyInfo->ip_prop[loop_var].protocol.ipv4_prop.ipv4_subnet_mask;
                pVirtualIf->ipProp.protocol.ipv4_prop.ipv4_broadcast =
                    pPropNode->pPropertyInfo->ip_prop[loop_var].protocol.ipv4_prop.ipv4_broadcast;
                pVirtualIf->ipProp.protocol.ipv4_prop.ipv4_gateway =
                    pPropNode->pPropertyInfo->ip_prop[loop_var].protocol.ipv4_prop.ipv4_gateway;
                /* Increment the virtual IF count */
                pPhysicalIfInfo->no_virtual_if_with_ipv4_addr++;
            }

#if defined(IX_IPV6_SUPPORT)
            else
            {
                IX_128_BIT_BINARY_OPERATION_ASSIGN (
                    pVirtualIf->ipProp.protocol.ipv6_prop.ipv6_address.addr,
                    pPropNode->pPropertyInfo->ip_prop[loop_var].protocol.ipv6_prop.ipv6_address.addr);
                pVirtualIf->ipProp.protocol.ipv6_prop.ipv6_address.prefixlen =
                    pPropNode->pPropertyInfo->ip_prop[loop_var].protocol.ipv6_prop.ipv6_address.prefixlen;
                IX_128_BIT_BINARY_OPERATION_ASSIGN (
                    pVirtualIf->ipProp.protocol.ipv6_prop.ipv6_gateway,
                    pPropNode->pPropertyInfo->ip_prop[loop_var].protocol.ipv6_prop.ipv6_gateway);
                /* Increment the virtual IF count */
                pPhysicalIfInfo->no_virtual_if_with_ipv6_addr++;
            }
#endif /* defined(IX_IPV6_SUPPORT) */

            ix_ossl_memcpy(&pVirtualIf->macAddr, &pPropNode->pPropertyInfo->mac_addr, IX_CC_MAC_ADDR_LEN);
            sprintf(pVirtualIf->name, "%s%lu", IX_CC_STKDRV_DEV_NAME, pPhysicalIfInfo->portId);

            /* Create the linked list of virtual IFs */
            if (pPhysicalIfInfo->pVirtualIfs != NULL)
            {
                /* Already some virtual IFs are present, add the present one
                 * at the front of the linked list.
                 */
                pVirtualIf->pNextVirtualIf = pPhysicalIfInfo->pVirtualIfs;
            }
            pPhysicalIfInfo->pVirtualIfs = pVirtualIf;
        } /* End of for */
        pPhysicalIf->pPhysicalIfInfo = pPhysicalIfInfo;
        
        /* Link the physical if into the FP's list of physical interfaces. */
        if(pFP->pPhysicalIfs == NULL)
        {
            pPhysicalIf->pNextPhysicalIf = NULL;
            pFP->pPhysicalIfs = pPhysicalIf;
        }
        else
        {
            pPhysicalIf->pNextPhysicalIf = pFP->pPhysicalIfs;
            pFP->pPhysicalIfs = pPhysicalIf;
        }

        /* Increment number of ports for this FP. */
        pFP->numPorts++;

        pPropNode = pPropNode->pNextPropNode;
    } /* end while(pPropNode != NULL) */

#if defined(IX_INCLUDE_REGISTRY)
    {
        /* Get the remote stack support flag from the registry. */
        ix_configuration_property_handle hProperty;
        ix_uint32 tempVal;

        if(ix_rm_cp_property_open(IX_CP_CORE_PROPERTY_ROOT,
                                      REPOSITORY_PATH_REMOTE_SUPPORT_FLAG,
                                      &hProperty) != IX_SUCCESS)
            goto local_support;
    
        if(ix_rm_cp_property_get_value_uint32(hProperty, &tempVal) != IX_SUCCESS)
        {
            ix_rm_cp_property_close( hProperty);
            goto local_support;
        }

        pStkdrvCtrl->remoteSupportFlag = (ix_uint8)tempVal;

        ix_rm_cp_property_close( hProperty);
    }
#else

    pStkdrvCtrl->remoteSupportFlag = IX_CC_STKDRV_REMOTE_SUPPORT_FLAG;

#endif /* defined(IX_INCLUDE_REGISTRY) */

    if(pStkdrvCtrl->remoteSupportFlag)
    {
        /* Register the TM with the stack driver core component. */
        IX_ERROR_CG(_ix_cc_stkdrv_register_comm_handler(
            IX_CC_STKDRV_HANDLER_ID_TRANSP_MODULE, pStkdrvCtrl, NULL,
            (ix_cc_stkdrv_handler_module_init_cb)ix_cc_stkdrv_tm_init),
            err, label_2);
    }
    /*    else */
local_support:
    {
        /* Register the VIDD with the stack driver core component. */
        IX_ERROR_CG(_ix_cc_stkdrv_register_comm_handler(
            IX_CC_STKDRV_HANDLER_ID_LOCAL_DRIVER, pStkdrvCtrl, NULL,
            (ix_cc_stkdrv_handler_module_init_cb)ix_cc_stkdrv_vidd_init),
            err, label_2);
    }
    
#if defined(IX_CC_STKDRV_PKT_CLASS)

    /* Initialize the filters. */
    IX_ERROR_CGT(ix_cc_stkdrv_init_filters(&pStkdrvCtrl->pFilterCtrl), err,
                 IX_CC_STKDRV_ERROR_FILTER_INIT,
                 IX_ERROR_LEVEL_LOCAL, label_1);

#else

    /*
     * If packet classification is not enabled, set the
     * filter control structure to NULL.
     */
    pStkdrvCtrl->pFilterCtrl = NULL;

#endif /* defined(IX_CC_STKDRV_PKT_CLASS) */

#if !defined(IX_EXCLUDE_CCI)
    /* Register the packet and message handlers with the CCI. */
    IX_ERROR_CGT(ix_cci_cc_add_packet_handler(arg_hCC,
                    (ix_uint32)IX_CC_STKDRV_LOW_PRIORITY_PKT_INPUT,
                    (ix_pkt_handler)ix_cc_stkdrv_low_priority_pkt_handler,
                    IX_INPUT_TYPE_SINGLE_SRC), err, 
                    IX_CC_STKDRV_ERROR_CCI_ADD_HANDLER, 
                    IX_ERROR_LEVEL_LOCAL, label_1);
    
    IX_ERROR_CGT(ix_cci_cc_add_message_handler(arg_hCC,
                    (ix_uint32)IX_CC_STKDRV_MSG_INPUT,
                    (ix_msg_handler)ix_cc_stkdrv_msg_handler,
                    IX_INPUT_TYPE_SINGLE_SRC), err,
                    IX_CC_STKDRV_ERROR_CCI_ADD_HANDLER, 
                    IX_ERROR_LEVEL_LOCAL, label_1);

    /**
     * Register the high priority packet handler as a message handler,
     * since messages have higher priority than packets.
     */
    IX_ERROR_CGT(ix_cci_cc_add_message_handler(arg_hCC,
                    (ix_uint32)IX_CC_STKDRV_HIGH_PRIORITY_PKT_INPUT,
                    (ix_msg_handler)ix_cc_stkdrv_high_priority_pkt_handler,
                    IX_INPUT_TYPE_SINGLE_SRC), err,
                    IX_CC_STKDRV_ERROR_CCI_ADD_HANDLER, 
                    IX_ERROR_LEVEL_LOCAL, label_1);

    /* register packet handler for outbound packets coming
       from control plane (and PDK)
    */
    IX_ERROR_CGT(ix_cci_cc_add_message_handler(arg_hCC,
                    (ix_uint32)IX_CC_STKDRV_FPM_INPUT,
                    (ix_msg_handler)ix_cc_stkdrv_tm_pkt_handler,
                    IX_INPUT_TYPE_SINGLE_SRC), err,
                    IX_CC_STKDRV_ERROR_CCI_ADD_HANDLER, 
                    IX_ERROR_LEVEL_LOCAL, label_1);
#else

    /* Register the packet and message handlers with the RM communication. */
    IX_ERROR_CGT(ix_rm_packet_handler_register(IX_CC_STKDRV_LOW_PRIORITY_PKT_INPUT,
                    (ix_comm_data_handler)ix_cc_stkdrv_low_priority_pkt_handler,
                    pStkdrvCtrl), err,
                    IX_CC_STKDRV_ERROR_RM_ADD_HANDLER, 
                    IX_ERROR_LEVEL_LOCAL, label_1);
    
    IX_ERROR_CGT(ix_rm_message_handler_register(IX_CC_STKDRV_MSG_INPUT,
                    (ix_comm_data_handler)ix_cc_stkdrv_msg_handler,
                    pStkdrvCtrl), err,
                    IX_CC_STKDRV_ERROR_RM_ADD_HANDLER, 
                    IX_ERROR_LEVEL_LOCAL, label_1);

    /**
     * Register the high priority packet handler as a message handler,
     * since messages have higher priority than packets.
     */
    IX_ERROR_CGT(ix_rm_message_handler_register(IX_CC_STKDRV_HIGH_PRIORITY_PKT_INPUT,
                    (ix_comm_data_handler)ix_cc_stkdrv_high_priority_pkt_handler,
                    pStkdrvCtrl), err,
                    IX_CC_STKDRV_ERROR_RM_ADD_HANDLER, 
                    IX_ERROR_LEVEL_LOCAL, label_1);

    /*  register packet handler for outbound packets coming
       from control plane (and PDK)
    */
    IX_ERROR_CGT(ix_rm_message_handler_register(IX_CC_STKDRV_FPM_INPUT,
                    (ix_comm_data_handler)ix_cc_stkdrv_tm_pkt_handler,
		    pStkdrvCtrl), err,
                    IX_CC_STKDRV_ERROR_RM_ADD_HANDLER, 
                    IX_ERROR_LEVEL_LOCAL, label_1);

#endif /* end !defined(IX_EXCLUDE_CCI) */

    return err;

label_5:
    if(pVirtualIf != NULL)
    {
        ix_ossl_free(pVirtualIf);
        pVirtualIf = NULL;
    }

label_4:
    if(pPhysicalIfInfo != NULL)
    {
        ix_ossl_free(pPhysicalIfInfo);
        pPhysicalIfInfo = NULL;
    }
    
label_3:
    if(pPhysicalIf != NULL)
    {
        ix_ossl_free(pPhysicalIf);
        pPhysicalIf = NULL;
    }

label_2:
    if(pHandlerModule != NULL)
    {
        if(pHandlerModule->pHandler_func != NULL)
        {
            ix_ossl_free(pHandlerModule->pHandler_func);
            pHandlerModule->pHandler_func = NULL;
        }
        ix_ossl_free(pHandlerModule);
        pHandlerModule = NULL;
    }

label_1:
    _ix_cc_stkdrv_cleanup(0, (void**)&pStkdrvCtrl);

    return err;
}

/**
 * NAME: ix_cc_stkdrv_fini
 *
 * DESCRIPTION: Termination function. Frees stack driver core component memory
 * and data structures.
 * 
 * @Param:  - IN ix_cc_handle arg_hCC - handle to the core component.
 * @Param:  - IN void* arg_pContext - pointer to the control block memory allocated
 *          earlier in ix_cc_stkdrv_init function.
 * @Return: IX_SUCCESS
 *          IX_CC_STKDRV_ERROR_CCI_REMOVE_HANDLER
 *          IX_CC_STKDRV_ERROR_RM_REMOVE_HANDLER
 *          IX_CC_STKDRV_ERROR_HANDLER_FINI
 *          IX_CC_ERROR_NULL
 */
ix_error ix_cc_stkdrv_fini(
                           ix_cc_handle arg_hCC,
                           void* arg_pContext
                           )
{
    return _ix_cc_stkdrv_cleanup(1, &arg_pContext);
}

/**
 * NAME: ix_cc_stkdrv_high_priority_pkt_handler
 *
 * DESCRIPTION: Packet processing function for high priority signaling packets.
 * Calls the internal function _ix_cc_stkdrv_process_pkt().
 * 
 * @Param:  - IN ix_buffer_handle arg_hDataToken - handle to a buffer which
 *          contains exception packets.
 * @Param:  - IN ix_uint32 arg_ExceptionCode - Exception code for the packet.
 *          Ignored in the Stack Driver.
 * @Param:  - IN void* arg_pComponentContext - pointer to Stack Driver core
 *          component context that will be passed to the core component when
 *          a packet arrives. This context was defined by the core component
 *          and passed to core components infrastructure through the
 *          ix_cc_stkdrv_init function.
 * @Return: IX_SUCCESS, or an appropriate error code on failure.
 */
ix_error ix_cc_stkdrv_high_priority_pkt_handler(
                                                ix_buffer_handle arg_hDataToken,
                                                ix_uint32 arg_ExceptionCode,
                                                void* arg_pComponentContext
                                                )
{
    ix_error err = IX_SUCCESS;

#if defined(IX_DEBUG)
    if(arg_pComponentContext == NULL)
        return IX_ERROR_WARNING(IX_CC_ERROR_NULL, ("Packet handler invoked with NULL context"));

    if(arg_hDataToken == IX_NULL_BUFFER_HANDLE)
        return IX_ERROR_WARNING(IX_CC_ERROR_NULL, ("Packet handler invoked with NULL buffer handle"));
#endif /* defined(IX_DEBUG) */

    IX_ERROR_C(_ix_cc_stkdrv_process_pkt(arg_hDataToken, (ix_cc_stkdrv_ctrl*)arg_pComponentContext), err);

    return err;
}

/**
 * NAME: ix_cc_stkdrv_low_priority_pkt_handler
 *
 * DESCRIPTION: Packet processing function for low priority data packets.
 * Calls the internal function _ix_cc_stkdrv_process_pkt().
 * 
 * @Param:  - IN ix_buffer_handle arg_hDataToken - handle to a buffer which
 *          contains exception packets.
 * @Param:  - IN ix_uint32 arg_ExceptionCode - Exception code for the packet.
 *          Ignored in the Stack Driver.
 * @Param:  - IN void* arg_pComponentContext - pointer to Stack Driver core
 *          component context that will be passed to the core component when
 *          a packet arrives. This context was defined by the core component
 *          and passed to core components infrastructure through the
 *          ix_cc_stkdrv_init function.
 * @Return: IX_SUCCESS, or an appropriate error code on failure.
 */
ix_error ix_cc_stkdrv_low_priority_pkt_handler(
                                               ix_buffer_handle arg_hDataToken,
                                               ix_uint32 arg_ExceptionCode,
                                               void* arg_pComponentContext
                                               )
{
    ix_error err = IX_SUCCESS;

#if defined(IX_DEBUG)
    if(arg_pComponentContext == NULL)
        return IX_ERROR_WARNING(IX_CC_ERROR_NULL, ("Packet handler invoked with NULL context"));

    if(arg_hDataToken == IX_NULL_BUFFER_HANDLE)
        return IX_ERROR_WARNING(IX_CC_ERROR_NULL, ("Packet handler invoked with NULL buffer handle"));
#endif /* defined(IX_DEBUG) */

    IX_ERROR_C(_ix_cc_stkdrv_process_pkt(arg_hDataToken, (ix_cc_stkdrv_ctrl*)arg_pComponentContext), err);

    return err;
}


#if (!(_IX_OS_TYPE_ == _IX_OS_LINUX_KERNEL_))
/**
 * NAME: _ix_cc_stkdrv_classify_output_id
 *
 * DESCRIPTION: This function performs a downstream classification
 * to determine to which core component to send the output
 * packet.  In the initial release this will always return the communication
 * ID of the IPv4 CC.
 * 
 * @Param:  - IN ix_buffer_handle arg_hBuffer - handle to the buffer to be classified.
 * @Param:  - OUT ix_communication_id* arg_pOutputCommId - pointer to where the output
 *          communication ID will be returned.
 * @Return: IX_SUCCESS
 */
ix_error _ix_cc_stkdrv_classify_output_id(
                                         ix_buffer_handle arg_hBuffer,
                                         ix_communication_id* arg_pOutputCommId
                                         )
{
    *arg_pOutputCommId = IX_CC_STKDRV_NEED_ROUTE_PKT_OUTPUT;
    return IX_SUCCESS;
}
#endif


/**
 * NAME: _ix_cc_stkdrv_send_multicast_packet
 *
 * DESCRIPTION: This function contructs a link-layer header for a multicast
 *              packet, and sends the packet directly to the Queue Manager
 *              core component, as no route lookup is necessary.
 * 
 * @Param:  - IN ix_buffer_handle arg_hBuffer - handle to the buffer being sent.
 * @Param:  - IN ix_cc_stkdrv_physical_if_node* arg_pPhysicalIf - pointer to the
 *          structure for the outgoing port.
 * @Param:  - IN ix_uint32 arg_dstIP - destination IP address for the packet
 *          in network byte order.
 * @Param:  - OUT ix_uint8* arg_pIPHdr - pointer to start of the IP header
 *          of the packet.
 * @Return: IX_SUCCESS
 *          IX_CC_ERROR_INTERNAL
 *          IX_CC_ERROR_SEND_FAIL
 */
ix_error _ix_cc_stkdrv_send_multicast_packet(
                                             ix_buffer_handle arg_hBuffer,
                                             ix_cc_stkdrv_physical_if_node* arg_pPhysicalIf,
                                             ix_uint32 arg_dstIP,
                                             ix_uint8* arg_pIPHdr
                                             )
{
    ix_error err = IX_SUCCESS;

    ix_hw_buffer_meta* pMeta;

    switch(arg_pPhysicalIf->pPhysicalIfInfo->mediaType)
    {
        case IX_MEDIA_TYPE_FAST_ETHERNET:
        case IX_MEDIA_TYPE_1GB_ETHERNET:
        {
            /**
             * Construct the L2 header based on the MAC address of the interface,
             * and add it to the front of the packet data.
             */
            ix_uint8 dstMulti[] = {0x01, 0x00, 0x5e, 0x00, 0x00, 0x00};
            ix_uint16 pktSize;
            ix_uint16 bufferSize;
            ix_uint16 metaOffset;
            ix_uint32 cellCount = 0;
            /**
             * Get the lower 23 bits of the destination IP address to obtain
             * the multicast group ID.
             */
            ix_uint32 groupID = arg_dstIP & 0x007FFFFF;

            /* Copy group ID into destination MAC address. */

            dstMulti[3] |= (groupID & 0x00ff0000);
            dstMulti[4] |= (groupID & 0x0000ff00);
            dstMulti[5] |= (groupID & 0x000000ff);

            /* Copy destination MAC address into L2 header. */
            ix_ossl_memcpy((char*)arg_pIPHdr - IX_CC_ETHERNET_HDR_LEN,
                dstMulti, IX_CC_MAC_ADDR_LEN);

            /* Copy source MAC address into L2 header. */
            ix_ossl_memcpy((char*)arg_pIPHdr - IX_CC_ETHERNET_HDR_LEN + IX_CC_MAC_ADDR_LEN,
                &arg_pPhysicalIf->pPhysicalIfInfo->pVirtualIfs->macAddr, IX_CC_MAC_ADDR_LEN);

            /* Set next hop ID to -1 so that the microblocks do not modify the L2 header. */
            IX_ERROR_CRT(ix_rm_buffer_get_meta(arg_hBuffer, (void**)&pMeta),
                IX_CC_ERROR_INTERNAL, IX_ERROR_LEVEL_WARNING);
            IX_RM_MEM_UINT16_WRITE(&pMeta->m_NextHopID, IX_HTON16(L2_HEADER_ALREADY_EXIST_ID));
           
           
            /* Set the type in the L2 header to IP. */
            *((ix_uint16*)arg_pIPHdr - 1) = IX_HTON16(0x0800);
            /* Extract the buffersize and packet size from the meta data of the buffer */
            bufferSize = IX_NTOH16(IX_RM_MEM_UINT16_READ(&pMeta->m_BufferSize));
            pktSize = IX_NTOH16(IX_RM_MEM_UINT16_READ(&pMeta->m_PacketSize));
            /* Update the buffer size and packet size to take care of L2 header prepended */
            IX_RM_MEM_UINT16_WRITE(&pMeta->m_PacketSize, IX_HTON16(pktSize + IX_CC_ETHERNET_HDR_LEN));            
            IX_RM_MEM_UINT16_WRITE(&pMeta->m_BufferSize,
            IX_HTON16(bufferSize + IX_CC_ETHERNET_HDR_LEN));
            metaOffset = IX_NTOH16(IX_RM_MEM_UINT16_READ(&pMeta->m_Offset));
            /* Set m_Offset to 128-Eth hdr len */
            IX_RM_MEM_UINT16_WRITE(&pMeta->m_Offset, IX_HTON16(metaOffset - IX_CC_ETHERNET_HDR_LEN));
             
                  

            /* Set cellcount in the buffer */
            cellCount = ix_cc_calculate_cell_count((ix_uint32)(bufferSize + IX_CC_ETHERNET_HDR_LEN), 1);
            /* Write the cell count into the buffer handle */
            ix_rm_buffer_cell_count_set(&arg_hBuffer, cellCount);


            break;
        }
        case IX_MEDIA_TYPE_POS:
        case IX_MEDIA_TYPE_ATM:
            /**
             * Let the interface microblocks construct the L2 header
             * for POS and ATM.
             */
            break;
        default:
            return IX_ERROR_WARNING(IX_CC_ERROR_RANGE, ("Unknown media type %d\n", arg_pPhysicalIf->pPhysicalIfInfo->mediaType));
    }

    /* Send the packet to the output core component (Queue Manager). */
    IX_ERROR_CRT(ix_rm_packet_send(IX_CC_STKDRV_MULTICAST_PKT_OUTPUT, arg_hBuffer, 0),
            IX_CC_ERROR_SEND_FAIL, IX_ERROR_LEVEL_WARNING);

    return err;

}

ix_error ix_cc_stkdrv_pkt_handler(
                                  ix_buffer_handle arg_hDataToken,
                                  ix_uint32 arg_UserData,
                                  void* arg_pComponentContext
                                  )
{
    ix_error err = IX_SUCCESS;
    
#if (_IX_OS_TYPE_ == _IX_OS_LINUX_KERNEL_)
    /* Modify the 3rd argument of this function to provide the packet type 
     * if it is available. Currently, the third param is set to IPv4 for
     * backward compatibility as far as behaviour is concerned */
    ix_cc_stkdrv_send_packet (arg_hDataToken, arg_pComponentContext, 
		                     IX_CC_STKDRV_OG_PKT_TYPE_IPV4);
#else
    ix_cc_stkdrv_send_packet (arg_hDataToken, arg_pComponentContext);
#endif

    return err;
}


/**
 * NAME: ix_cc_stkdrv_send_packet
 *
 * DESCRIPTION: This function is called by the handler module to the core
 * component module to queue the packet for transmission. The current implementation
 * for linux supports downstream classification, which means that IPv4 or IPv6 packets 
 * can be routed appropriately to respective core components. All IPv4 packets apart 
 * from multicast packets go to IPv4 core component. For these
 * multicast packets, this function constructs a multicast L2 header and
 * sends the packet to the Queue Manager. 
 * All IPV6 packets go to IPv6 core component.
 *
 * @Param:  - IN ix_buffer_handle arg_hBuffer - handle to the packet to be transmitted.
 * @Param:  - IN void* arg_pContext - pointer to the context.  In this case it
 *          will be a pointer to the CC port structure, ix_cc_stkdrv_physical_if_node.
 * @Return: IX_SUCCESS
 *          IX_CC_ERROR_SEND_FAIL
 */
#if (_IX_OS_TYPE_ == _IX_OS_LINUX_KERNEL_)
ix_error ix_cc_stkdrv_send_packet(
                                  ix_buffer_handle arg_hBuffer,
                                  void* arg_pContext,
                                  ix_cc_stkdrv_og_pkt_type arg_pktType
                                  )
{
    ix_error                      err = IX_SUCCESS;
    ix_uint32                     dstIP;
    void                          *pData = NULL;
    ix_communication_id           outputCommId;
    ix_cc_stkdrv_physical_if_node *pPhysicalIf;

    pPhysicalIf = (ix_cc_stkdrv_physical_if_node*)arg_pContext;

#if defined(IX_DEBUG)
    {
        ix_buffer_handle hBuffer = arg_hBuffer;
        ix_buffer_type   bufType;
        
        /* Check for NULL buffer. */
        if (hBuffer == IX_NULL_BUFFER_HANDLE)
            return IX_ERROR_WARNING(IX_CC_ERROR_NULL,
                ("NULL buffer handle sent from handler module"));

        /* Check that the buffer is a hardware buffer. */
        IX_ERROR_CG(ix_rm_buffer_get_type(hBuffer, &bufType),
            err, label_1);
        if(bufType != IX_BUFFER_TYPE_HARDWARE)
        {
            err = IX_ERROR_WARNING(IX_CC_STKDRV_ERROR_INVALID_BUF_TYPE,
                ("Packet handler invoked with software buffer"));
            goto label_1;
        }
#if defined(IX_CC_STKDRV_DUMP_PACKET)
        do
        {
            ix_hw_buffer_meta *pMeta;
            void              *pData;
            ix_uint32         bufno = 0;
            
            ix_rm_buffer_get_meta(arg_hBuffer, (void**)&pMeta);
            ix_rm_buffer_get_data(arg_hBuffer, &pData);
            _ix_cc_stkdrv_dump_packet(pData + IX_NTOH16(pMeta->m_Offset),
                                      IX_NTOH16(pMeta->m_BufferSize),
                                  (bufno++) ? "next buffer:" : "StkDrv send:");
            hBuffer = pMeta->m_HwNext;
        }
        while (hBuffer != IX_HTON32(IX_NULL_BUFFER_HANDLE) &&
               hBuffer != IX_HTON32(0xff));
#endif /* IX_CC_STKDRV_DUMP_PACKET */
    }
#endif /* defined(IX_DEBUG) */

    /* Perform a downstream classification based on the output port ID. */
#if defined(IX_CC_STKDRV_PKT_CLASS)
    IX_ERROR_CGT(_ix_cc_stkdrv_classify_output_id(arg_hBuffer, arg_pktType, &outputCommId, pPhysicalIf, &pData), err, IX_CC_STKDRV_ERROR_CANNOT_CLASSIFY_OUTPUT_ID, IX_ERROR_LEVEL_WARNING, label_1);
#else
    /* 
     * If PKT_CLASS flag is not defined, always assume that this is a IPV4 packet. 
     * For IPv6 support, one should always enable this flag. This is the same behaviour
     * as was supported when the outgoing packet classification feature was not added.
     * 
     */
    if(arg_pktType == IX_CC_STKDRV_OG_PKT_TYPE_IPV4)
    {
        void* pData;
        ix_uint32 dstIP;
        ix_uint32 classD;

        /* Check if the packet is multicast. */
        IX_ERROR_CG(ix_cc_hw_get_packet_data(arg_hBuffer, &pData), err, label_1);
    
        /* Get destination IP address in network-byte order. */
        dstIP = IX_NTOH32(*(((ix_uint32*)pData) + IX_CC_STKDRV_IPV4_DST_IP_OFFSET));

        classD = (dstIP >> 28);
        if(classD == 0xE)
           outputCommId = IX_CC_STKDRV_MULTICAST_PKT_OUTPUT;
	else
           outputCommId = IX_CC_STKDRV_NEED_ROUTE_PKT_OUTPUT;
    }
    else
    {
       err = IX_CC_STKDRV_ERROR_INVALID_BUF_TYPE;
       goto label_1;
    }
#endif

    /* Check to see if this is a multicast packet */
    if( (pData != NULL) && (outputCommId ==IX_CC_STKDRV_MULTICAST_PKT_OUTPUT) )
    {
       /* Get destination IP address in network-byte order. */
       dstIP = IX_NTOH32(*(((ix_uint32*)pData) + IX_CC_STKDRV_IPV4_DST_IP_OFFSET));

       IX_ERROR_CG(_ix_cc_stkdrv_send_multicast_packet(arg_hBuffer,
                pPhysicalIf, dstIP, (ix_uint8*)pData), err, label_1);
    }
    else
    {
       /* Send the packet to the output core component. */
       IX_ERROR_CGT(ix_rm_packet_send(outputCommId, arg_hBuffer, 0),
                err, IX_CC_ERROR_SEND_FAIL, IX_ERROR_LEVEL_WARNING, label_1);
    }

    return err;

label_1:
    /* Drop the buffer. */
    ix_rm_buffer_free_chain(arg_hBuffer);
    arg_hBuffer = IX_NULL_BUFFER_HANDLE;
    return err;
}

#else

ix_error ix_cc_stkdrv_send_packet(
                                  ix_buffer_handle arg_hBuffer,
                                  void* arg_pContext
                                  )
{
    ix_error err = IX_SUCCESS;
    ix_communication_id outputCommId = IX_CC_STKDRV_NEED_ROUTE_PKT_OUTPUT;
    ix_cc_stkdrv_physical_if_node* pPhysicalIf =
        (ix_cc_stkdrv_physical_if_node*)arg_pContext;

#if defined(IX_DEBUG)
    {
        ix_buffer_type bufType;

        /* Check for NULL buffer. */
        if(arg_hBuffer == IX_NULL_BUFFER_HANDLE)
            return IX_ERROR_WARNING(IX_CC_ERROR_NULL,
                ("NULL buffer handle sent from handler module"));

        /* Check that the buffer is a hardware buffer. */
        IX_ERROR_CG(ix_rm_buffer_get_type(arg_hBuffer, &bufType),
            err, label_1);
        if(bufType != IX_BUFFER_TYPE_HARDWARE)
        {
            err = IX_ERROR_WARNING(IX_CC_STKDRV_ERROR_INVALID_BUF_TYPE,
                ("Packet handler invoked with software buffer"));
            goto label_1;
        }

#if defined(IX_CC_STKDRV_DUMP_PACKET)
        {
            void* pData;
            ix_uint32 buflen;

            /* Dump packet to screen. */
            IX_ERROR_CG(ix_rm_buffer_get_meta(arg_hBuffer, &pData), err, label_1);
            buflen = (ix_uint32)IX_NTOH16(IX_RM_MEM_UINT16_READ(&((ix_hw_buffer_meta*)pData)->m_PacketSize));
            IX_ERROR_CG(ix_cc_hw_get_packet_data(arg_hBuffer, &pData), err, label_1);
            _ix_cc_stkdrv_dump_packet((ix_uint8*)pData, buflen, "Recd packet from VIDD:");
        }
#endif /* defined(IX_CC_STKDRV_DUMP_PACKET) */
    }
#endif /* defined(IX_DEBUG) */

    {
        void* pData;
        ix_uint32 dstIP;
        ix_uint32 classD;

        /* Check if the packet is multicast. */
        IX_ERROR_CG(ix_cc_hw_get_packet_data(arg_hBuffer, &pData), err, label_1);
    
        /* Get destination IP address in network-byte order. */
        dstIP = IX_NTOH32(*(((ix_uint32*)pData) + IX_CC_STKDRV_IPV4_DST_IP_OFFSET));

        classD = (dstIP >> 28);
        
        if(classD == 0xE)
        {
            IX_ERROR_CG(_ix_cc_stkdrv_send_multicast_packet(arg_hBuffer,
                pPhysicalIf, dstIP, (ix_uint8*)pData), err, label_1);

            return err;
        }
    }

    /* Perform a downstream classification based on the output port ID. */
    IX_ERROR_CGT(_ix_cc_stkdrv_classify_output_id(arg_hBuffer, &outputCommId),
                err, IX_CC_STKDRV_ERROR_CANNOT_CLASSIFY_OUTPUT_ID, IX_ERROR_LEVEL_WARNING, label_1);

    
    /* Send the packet to the output core component. */
    IX_ERROR_CGT(ix_rm_packet_send(outputCommId, arg_hBuffer, 0),
                err, IX_CC_ERROR_SEND_FAIL, IX_ERROR_LEVEL_WARNING, label_1);

    return err;

label_1:
    /* Drop the buffer. */
    ix_rm_buffer_free_chain(arg_hBuffer);
    arg_hBuffer = IX_NULL_BUFFER_HANDLE;
    return err;
}

#endif /* _IX_OS_LINUX_KERNEL_ */


/**
 * NAME: ix_cc_stkdrv_msg_handler
 *
 * DESCRIPTION: Message processing function for the stack driver.  Handles
 *              port property updates, and requests to get the number of ports
 *              or port properties.
 * 
 * @Param:  - IN ix_buffer_handle arg_hDataToken - buffer handle embedding
 *          information for the message.
 * @Param:  - IN ix_uint32 arg_UserData - message type, possible values
 *          defined in ix_cc_stkdrv_msg_id.
 * @Param:  - IN void* arg_pComponentContext - pointer to Stack Driver core
 *          component context that will be passed to the core component when
 *          a message arrives. This context was defined by the core component
 *          and passed to core components infrastructure through the
 *          ix_cc_stkdrv_init function.
 * @Return: IX_SUCCESS
 *          IX_CC_ERROR_NULL
 *          IX_CC_ERROR_ENTRY_NOT_FOUND
 *          IX_CC_ERROR_OOM_SYSTEM
 *          IX_CC_STKDRV_ERROR_INVALID_BLADE_ID
 */
ix_error ix_cc_stkdrv_msg_handler(
                                  ix_buffer_handle arg_hDataToken,
                                  ix_uint32 arg_UserData,
                                  void* arg_pComponentContext
                                  )
{
    ix_error err = IX_SUCCESS;
    
    ix_cc_stkdrv_msg_id msgId = (ix_cc_stkdrv_msg_id)arg_UserData;
    void* pMsg;
    void* pContext = NULL;

    ix_uint32 propId;
    ix_uint32 portId;
    ix_uint32 bladeId;

    ix_cc_properties_msg* pPropMsg;

    ix_cc_stkdrv_ctrl* pStkdrvCtrl = (ix_cc_stkdrv_ctrl*)arg_pComponentContext;

    ix_cc_stkdrv_physical_if_node* pPhysicalIf = NULL;
    ix_cc_properties* pProperty;

#if defined(IX_DEBUG)
    /* Check for NULL context. */
    if(pStkdrvCtrl == NULL)
        return IX_ERROR_WARNING(IX_CC_ERROR_NULL, ("Msg handler invoked with NULL context"));
#endif /* defined(IX_DEBUG) */

    /* Convert message using IX_MSUP_EXTRACT_MSG(msg, mymsg, context) macro. */
    IX_MSUP_EXTRACT_MSG(arg_hDataToken, &pMsg, &pContext);

    switch(msgId)
    {
        /* Handle property update message. */
        case IX_CC_COMMON_MSG_ID_PROP_UPDATE:
            pPropMsg = (ix_cc_properties_msg*)pMsg;
            pProperty = &pPropMsg->propData;

            if(pProperty == NULL)
            {
                err = IX_ERROR_WARNING(IX_CC_ERROR_NULL,
                    ("NULL property structure passed in"));
                break;
            }

            /* Get blade ID from message. */
            bladeId = pProperty->blade_id;

/* For now, accept all property updates. */
#if 0
            /* Ignore property updates not related to the local blade. */
            if(bladeId != pStkdrvCtrl->pFPList->fpId)
            {
                err = IX_ERROR_WARNING(IX_CC_STKDRV_ERROR_INVALID_BLADE_ID,
                    ("Blade ID %d does not match configured blade ID %d\n",
                    bladeId, pStkdrvCtrl->pFPList->fpId));
                break;
            }
#endif /* if 0 */

            /* Get property ID from message. */
            propId = pPropMsg->propID;

            /* Get port ID from message. */
            portId = pProperty->port_id;

            /* Find the port with the given port ID. */
            pPhysicalIf = pStkdrvCtrl->pFPList->pPhysicalIfs;
            _IX_CC_STKDRV_GET_PHYSICAL_IF(portId, pPhysicalIf);
            if(pPhysicalIf == NULL)
            {
                err = IX_ERROR_WARNING(IX_CC_ERROR_ENTRY_NOT_FOUND, ("Port %d not found", portId));
                break;
            }

            /* Set all properties in pPhysicalIf from pProperty indicated by propId. */
            IX_ERROR_C(_ix_cc_stkdrv_set_property(propId, pProperty, pPhysicalIf, pStkdrvCtrl), err);

            break;
        case IX_CC_STKDRV_MSG_ID_GET_NUM_PORTS:

            #if defined(IX_DEBUG)
                /* Check for NULL context. */
                if(pContext == NULL)
                    return IX_ERROR_WARNING(IX_CC_ERROR_NULL, ("Msg handler invoked with NULL context"));
            #endif /* defined(IX_DEBUG) */

            /* Send reply message. */
            ix_cc_msup_send_reply_msg(pContext, &pStkdrvCtrl->pFPList->numPorts,
                sizeof(ix_uint32), err);

            break;

        /* Handle get property message. */
        case IX_CC_STKDRV_MSG_ID_GET_PROPERTY:
        {
            _ix_cc_stkdrv_get_properties_msg* pGetPropMsg;
            ix_cc_properties* pReplyMsg = NULL;
            ix_uint32 replyMsgSize = 0;

            #if defined(IX_DEBUG)
                /* Check for NULL context. */
                if(pContext == NULL)
                    return IX_ERROR_WARNING(IX_CC_ERROR_NULL, ("Msg handler invoked with NULL context"));
            #endif /* defined(IX_DEBUG) */

            pGetPropMsg = (_ix_cc_stkdrv_get_properties_msg*)pMsg;
            
            /* Get property ID from message. */
            propId = pGetPropMsg->propID;

            /* Get port ID from message. */
            portId = pGetPropMsg->portID;

            pPhysicalIf = pStkdrvCtrl->pFPList->pPhysicalIfs;

            if(portId != IX_CC_GET_ALL_PORTS)
            {
                /* Find the port with the given port ID. */
                _IX_CC_STKDRV_GET_PHYSICAL_IF(portId, pPhysicalIf);
                if(pPhysicalIf == NULL)
                {
                    err = IX_ERROR_WARNING(IX_CC_ERROR_ENTRY_NOT_FOUND, ("Port %d not found", portId));
                    goto label_1;
                }

                replyMsgSize = sizeof(ix_cc_properties);
                pReplyMsg = ix_ossl_malloc(replyMsgSize);

                if(pReplyMsg == NULL)
                {
                    replyMsgSize = 0;
                    err = IX_ERROR_WARNING(IX_CC_ERROR_OOM_SYSTEM, ("Could not allocate memory for reply message"));
                    goto label_1;
                }

                /* Put all properties from pPhysicalIf into pReplyMsg indicated by propId. */
                _ix_cc_stkdrv_get_property(propId, pPhysicalIf, pReplyMsg);
            }
            else
            {
                /* Get properties for all ports. */

                ix_uint32 i;

                replyMsgSize = sizeof(ix_cc_properties) * pStkdrvCtrl->pFPList->numPorts;
                pReplyMsg = ix_ossl_malloc(replyMsgSize);
                
                if(pReplyMsg == NULL)
                {
                    replyMsgSize = 0;
                    err = IX_ERROR_WARNING(IX_CC_ERROR_OOM_SYSTEM, ("Could not allocate memory for reply message"));
                    goto label_1;
                }

                for(i=0; pPhysicalIf != NULL; i++, pPhysicalIf = pPhysicalIf->pNextPhysicalIf)
                {
                    /* Put all properties from pPhysicalIf into pReplyMsg indicated by propId. */
                    _ix_cc_stkdrv_get_property(propId, pPhysicalIf, &pReplyMsg[i]);
                }
            }
            
label_1:
            /* Send reply message. */
            ix_cc_msup_send_reply_msg(pContext, pReplyMsg, replyMsgSize, err);
            break;
        }

        /* Handle case to send a message to a comm handler. */
        case IX_CC_STKDRV_MSG_ID_SEND_HANDLER_MSG:
            #if defined(IX_DEBUG)
                /* Check for NULL context. */
                if(pContext == NULL)
                    return IX_ERROR_WARNING(IX_CC_ERROR_NULL, ("Msg handler invoked with NULL context"));
            #endif /* defined(IX_DEBUG) */

            {
                _ix_cc_stkdrv_send_handler_msg_message* pHandlerMsg =
                    (_ix_cc_stkdrv_send_handler_msg_message*)pMsg;

                void* pReplyMsg = NULL;
                ix_uint32 replyMsgSize = 0;

                /**
                 * Call internal function to send the message to the appropriate
                 * comm handler.
                 */
                IX_ERROR_C(_ix_cc_stkdrv_send_handler_msg(
                    pHandlerMsg->handlerID, pHandlerMsg->msgID, pHandlerMsg->pMsg,
                    pHandlerMsg->msgSize, pStkdrvCtrl, &pReplyMsg, &replyMsgSize), err);

                /* Send reply message. */
                ix_cc_msup_send_reply_msg(pContext, pReplyMsg, replyMsgSize, err);
            }

            break;

        /* Handle case to register a comm handler. */
        case IX_CC_STKDRV_MSG_ID_REGISTER_HANDLER:
            #if defined(IX_DEBUG)
                /* Check for NULL context. */
                if(pContext == NULL)
                    return IX_ERROR_WARNING(IX_CC_ERROR_NULL, ("Msg handler invoked with NULL context"));
            #endif /* defined(IX_DEBUG) */

            {
                _ix_cc_stkdrv_register_comm_handler_msg* pHandlerMsg =
                    (_ix_cc_stkdrv_register_comm_handler_msg*)pMsg;

                /**
                 * Call internal function to register the appropriate
                 * comm handler.
                 */
                IX_ERROR_C(_ix_cc_stkdrv_register_comm_handler(
                    pHandlerMsg->handlerID, pStkdrvCtrl,
                    pHandlerMsg->pInitContext, pHandlerMsg->initCB), err);

                /* Send reply message. */
                ix_cc_msup_send_reply_msg(pContext, NULL, 0, err);
            }

            break;

        /* Messages specific to packet classification and filtering. */
        #if defined(IX_CC_STKDRV_PKT_CLASS)
            /* Handle case to add a filter. */
            case IX_CC_STKDRV_MSG_ID_ADD_FILTER:
                #if defined(IX_DEBUG)
                    /* Check for NULL context. */
                    if(pContext == NULL)
                        return IX_ERROR_WARNING(IX_CC_ERROR_NULL, ("Msg handler invoked with NULL context"));
                #endif /* defined(IX_DEBUG) */

                {
                    _ix_cc_stkdrv_add_filter_msg* pAddFilterMsg =
                        (_ix_cc_stkdrv_add_filter_msg*)pMsg;

                    ix_cc_stkdrv_filter_handle filterHandle;
                
                    /**
                     * Call internal function to add the filter with
                     * the data from the message.
                     */
                    IX_ERROR_C(_ix_cc_stkdrv_add_filter(&pAddFilterMsg->filter,
                        pAddFilterMsg->priority,
                        pStkdrvCtrl->pFilterCtrl,
                        &filterHandle), err);

                    /* Send reply message. */
                    ix_cc_msup_send_reply_msg(pContext, &filterHandle,
                        sizeof(ix_cc_stkdrv_filter_handle),
                        err);
                }

                break;

            /* Handle case to remove a filter. */
            case IX_CC_STKDRV_MSG_ID_REMOVE_FILTER:
                #if defined(IX_DEBUG)
                    /* Check for NULL context. */
                    if(pContext == NULL)
                        return IX_ERROR_WARNING(IX_CC_ERROR_NULL, ("Msg handler invoked with NULL context"));
                #endif /* defined(IX_DEBUG) */

                {
                    _ix_cc_stkdrv_remove_filter_msg* pRemoveFilterMsg =
                        (_ix_cc_stkdrv_remove_filter_msg*)pMsg;

                    /**
                     * Call internal function to remove the filter with
                     * the data from the message.
                     */
                    IX_ERROR_C(_ix_cc_stkdrv_remove_filter(
                        pRemoveFilterMsg->filterHandle,
                        pStkdrvCtrl->pFilterCtrl), err);

                    /* Send reply message. */
                    ix_cc_msup_send_reply_msg(pContext, NULL, 0, err);
                }

                break;

            /* Handle case to remove all filters. */
            case IX_CC_STKDRV_MSG_ID_REMOVE_ALL_FILTERS:
                #if defined(IX_DEBUG)
                    /* Check for NULL context. */
                    if(pContext == NULL)
                        return IX_ERROR_WARNING(IX_CC_ERROR_NULL, ("Msg handler invoked with NULL context"));
                #endif /* defined(IX_DEBUG) */

                /* Call internal function to remove all filters. */
                IX_ERROR_C(_ix_cc_stkdrv_remove_all_filters(pStkdrvCtrl->pFilterCtrl), err);

                /* Send reply message. */
                ix_cc_msup_send_reply_msg(pContext, NULL, 0, err);

                break;

            /* Handle case to modify a filter. */
            case IX_CC_STKDRV_MSG_ID_MODIFY_FILTER:
                #if defined(IX_DEBUG)
                    /* Check for NULL context. */
                    if(pContext == NULL)
                        return IX_ERROR_WARNING(IX_CC_ERROR_NULL, ("Msg handler invoked with NULL context"));
                #endif /* defined(IX_DEBUG) */

                {
                    _ix_cc_stkdrv_modify_filter_msg* pModifyFilterMsg =
                        (_ix_cc_stkdrv_modify_filter_msg*)pMsg;

                    ix_cc_stkdrv_filter_handle filterHandle =
                        pModifyFilterMsg->filterHandle;
                
                    /**
                     * Call internal function to modify the filter with
                     * the data from the message.
                     */
                    IX_ERROR_C(_ix_cc_stkdrv_modify_filter(&pModifyFilterMsg->filter,
                        pModifyFilterMsg->priority,
                        pStkdrvCtrl->pFilterCtrl,
                        &filterHandle), err);

                    /* Send reply message. */
                    ix_cc_msup_send_reply_msg(pContext, &filterHandle,
                        sizeof(ix_cc_stkdrv_filter_handle),
                        err);
                }

                break;

            /* Handle case to register a filter type. */
            case IX_CC_STKDRV_MSG_ID_REGISTER_FILTER_TYPE:
                #if defined(IX_DEBUG)
                    /* Check for NULL context. */
                    if(pContext == NULL)
                        return IX_ERROR_WARNING(IX_CC_ERROR_NULL, ("Msg handler invoked with NULL context"));
                #endif /* defined(IX_DEBUG) */

                {
                    _ix_cc_stkdrv_register_filter_type_msg* pRegisterMsg =
                        (_ix_cc_stkdrv_register_filter_type_msg*)pMsg;

                    /**
                     * Call internal function to register the callback
                     * for the specific filter typpe.
                     */
                    IX_ERROR_C(_ix_cc_stkdrv_register_filter_type(
                        pRegisterMsg->filterType,
                        pRegisterMsg->checkFilterCB,
                        pStkdrvCtrl->pFilterCtrl), err);

                    /* Send reply message. */
                    ix_cc_msup_send_reply_msg(pContext, NULL, 0, err);
                }

                break;

            /* Handle case to unregister a filter type. */
            case IX_CC_STKDRV_MSG_ID_UNREGISTER_FILTER_TYPE:
                #if defined(IX_DEBUG)
                    /* Check for NULL context. */
                    if(pContext == NULL)
                        return IX_ERROR_WARNING(IX_CC_ERROR_NULL, ("Msg handler invoked with NULL context"));
                #endif /* defined(IX_DEBUG) */

                {
                    _ix_cc_stkdrv_unregister_filter_type_msg* pRegisterMsg =
                        (_ix_cc_stkdrv_unregister_filter_type_msg*)pMsg;

                    /**
                     * Call internal function to unregister the callback
                     * for the specific filter typpe.
                     */
                    IX_ERROR_C(_ix_cc_stkdrv_unregister_filter_type(
                        pRegisterMsg->filterType,
                        pStkdrvCtrl->pFilterCtrl), err);

                    /* Send reply message. */
                    ix_cc_msup_send_reply_msg(pContext, NULL, 0, err);
                }

                break;


        #endif /* defined(IX_CC_STKDRV_PKT_CLASS) */

        default:
            err = IX_ERROR_WARNING(IX_CC_ERROR_UNDEFINED_MSG,
                                ("Msg handler invoked with invalid msg ID &d", msgId));
            break;
    }

    #if !defined(IX_CC_MSUP_STUB)
        ix_ossl_free(pMsg);
        pMsg = NULL;
    #endif /* !defined(IX_CC_MSUP_STUB) */

    IX_ERROR_C(ix_rm_buffer_free_chain(arg_hDataToken), err);

    return err;
}


/**
 * NAME: ix_cc_stkdrv_async_get_num_ports
 *
 * DESCRIPTION: This function is called to get the number of
 * ports configured on the Stack Driver.
 * 
 * @Param:  - IN ix_cc_stkdrv_cb_get_num_ports arg_pCallback - pointer to the
 *          client's callback function to be invoked once the message returns.
 * @Param:  - IN void* arg_pContext - pointer to client context.
 * @Return: IX_SUCCESS
 *          IX_CC_ERROR_NULL
 *          IX_CC_ERROR_OOM_SYSTEM
 */
ix_error ix_cc_stkdrv_async_get_num_ports(
                                         ix_cc_stkdrv_cb_get_num_ports arg_pCallback,
                                         void* arg_pContext
                                         )
{
    ix_error err = IX_SUCCESS;

    _ix_cc_stkdrv_get_num_ports_context* pContext = NULL;

    /* Check for invalid callback. */
    if(arg_pCallback == NULL)
        return IX_ERROR_WARNING(IX_CC_ERROR_NULL, ("NULL callback passed in"));

    /* Allocate memory for message support context. */
    pContext = ix_ossl_malloc(sizeof(_ix_cc_stkdrv_get_num_ports_context));
    if(pContext == NULL)
        return IX_ERROR_WARNING(IX_CC_ERROR_OOM_SYSTEM, ("Could not allocate memory for context to get number of ports"));

    /* Fill in message support context. */
    pContext->pUserCallback = arg_pCallback;
    pContext->pUserContext = arg_pContext;

    /* Send an asynchronous message to the Stack Driver requesting the number of ports. */
    IX_ERROR_CG(ix_cc_msup_send_async_msg(IX_CC_STKDRV_MSG_INPUT,
                                        (ix_cc_msghlp_callback)_ix_cc_stkdrv_cb_get_num_ports,
                                        pContext,
                                        IX_CC_STKDRV_MSG_ID_GET_NUM_PORTS,
                                        NULL,
                                        0), err, label_1);

    return err;

label_1:
    /* Free memory for message support context, only if an error has occurred. */
    ix_ossl_free(pContext);
    pContext = NULL;

    return err;
}



/**
 * NAME: _ix_cc_stkdrv_cb_get_num_ports
 *
 * DESCRIPTION: This is the internal callback function for getting
 *              number of ports asynchronously.
 * 
 * @Param:  - IN ix_error arg_Result - error code returned from
 *          the asynchronous operation.
 * @Param:  - IN void* arg_pContext - pointer to reply message
 *          context, of the type _ix_cc_stkdrv_get_num_ports_context.
 * @Param:  - IN void* arg_pMsg - pointer to reply message.
 * @Param:  - IN ix_uint32 arg_MsgLen - length of reply message.
 * @Return: IX_SUCCESS
 *          IX_CC_ERROR_NULL
 */
ix_error _ix_cc_stkdrv_cb_get_num_ports(
                                        ix_error arg_Result,
                                        void* arg_pContext,
                                        void* arg_pMsg,
                                        ix_uint32 arg_MsgLen
                                        )
{
    ix_error err = IX_SUCCESS;

    _ix_cc_stkdrv_get_num_ports_context* pContext = (_ix_cc_stkdrv_get_num_ports_context*)arg_pContext;

    if(pContext == NULL)
        return IX_ERROR_WARNING(IX_CC_ERROR_NULL, ("Internal callback invoked with NULL context"));

    /* Call user's callback function. */
    IX_ERROR_C((pContext->pUserCallback)(arg_Result, pContext->pUserContext, (ix_uint32*)arg_pMsg), err);

    return err;
}


/**
 * NAME: ix_cc_stkdrv_async_get_property
 *
 * DESCRIPTION: This function is called to get the interface
 *              properties of a specific port.
 * 
 * @Param:  - IN ix_cc_stkdrv_cb_get_property arg_pCallback - pointer to the
 *          client's callback function to be invoked once the message returns.
 * @Param:  - IN void* arg_pContext - pointer to client context.
 * @Param:  - IN ix_uint32 arg_propId - property IDs for the properties
 *          being requested.
 * @Param:  - IN ix_uint32 arg_portId - ID of port being queried. Use 
 *          IX_CC_GET_ALL_PORTS to get all port properties.
 * @Return: IX_SUCCESS
 *          IX_CC_ERROR_NULL
 *          IX_CC_ERROR_OOM_SYSTEM
 */
ix_error ix_cc_stkdrv_async_get_property(
                                        ix_cc_stkdrv_cb_get_property arg_pCallback,
                                        void* arg_pContext,
                                        ix_uint32 arg_propId,
                                        ix_uint32 arg_portId
                                        )
{
    ix_error err = IX_SUCCESS;

    _ix_cc_stkdrv_get_property_context* pContext = NULL;
    _ix_cc_stkdrv_get_properties_msg* pMsg = NULL;

    if(arg_pCallback == NULL)
        return IX_ERROR_WARNING(IX_CC_ERROR_NULL, ("NULL callback passed in"));

    /* Allocate memory for message structure. */
    pMsg = ix_ossl_malloc(sizeof(_ix_cc_stkdrv_get_properties_msg));
    if(pMsg == NULL)
        return IX_ERROR_WARNING(IX_CC_ERROR_OOM_SYSTEM, ("Could not allocate memory for message to get number of ports"));

    /* Fill in message structure. */
    pMsg->propID = arg_propId;
    pMsg->portID = arg_portId;

    /* Allocate memory for message support context. */
    pContext = ix_ossl_malloc(sizeof(_ix_cc_stkdrv_get_property_context));
    if(pContext == NULL)
    {
        err = IX_ERROR_WARNING(IX_CC_ERROR_OOM_SYSTEM, ("Could not allocate memory for context to get number of ports"));
        goto label_1;
    }

    /* Fill in message support context. */
    pContext->pUserCallback = arg_pCallback;
    pContext->pUserContext = arg_pContext;

    /* Send an asynchronous message to the Stack Driver requesting interface properties. */
    IX_ERROR_CG(ix_cc_msup_send_async_msg(IX_CC_STKDRV_MSG_INPUT,
                                        (ix_cc_msghlp_callback)_ix_cc_stkdrv_cb_get_property,
                                        pContext,
                                        IX_CC_STKDRV_MSG_ID_GET_PROPERTY,
                                        pMsg,
                                        sizeof(_ix_cc_stkdrv_get_properties_msg)), err, label_2);

    /* Free memory allocated to message structure. */
    ix_ossl_free(pMsg);
    pMsg = NULL;

    return err;

label_2:
    /* Free memory for message support context, only if an error has occurred. */
    ix_ossl_free(pContext);
    pContext = NULL;

label_1:
    /* Free memory allocated to message structure. */
    ix_ossl_free(pMsg);
    pMsg = NULL;

    return err;
}



/**
 * NAME: _ix_cc_stkdrv_cb_get_property
 *
 * DESCRIPTION: This is the internal callback function for getting
 *              interface properties asynchronously.
 * 
 * @Param:  - IN ix_error arg_Result - error code returned from
 *          the asynchronous get property operation.
 * @Param:  - IN void* arg_pContext - pointer to reply message
 *          context, of the type _ix_cc_stkdrv_get_property_context.
 * @Param:  - IN void* arg_pMsg - pointer to reply message.  This memory
 *          is only valid for the duration of this routine - the user
 *          callback will have to copy its data into its own memory.
 * @Param:  - IN ix_uint32 arg_MsgLen - length of reply message.
 * @Return: IX_SUCCESS
 *          IX_CC_ERROR_NULL
 */
ix_error _ix_cc_stkdrv_cb_get_property(
                                       ix_error arg_Result,
                                       void* arg_pContext,
                                       void* arg_pMsg,
                                       ix_uint32 arg_MsgLen
                                       )
{
    ix_error err = IX_SUCCESS;

    _ix_cc_stkdrv_get_property_context* pContext = (_ix_cc_stkdrv_get_property_context*)arg_pContext;

    /* Check for valid context. */
    if(pContext == NULL)
        return IX_ERROR_WARNING(IX_CC_ERROR_NULL, ("Internal callback invoked with NULL context"));

    /* Call user's callback function. */
    IX_ERROR_C((pContext->pUserCallback)(arg_Result, pContext->pUserContext, (ix_cc_properties*)arg_pMsg), err);

    /* Free the msg memory, only if the msg support library is stubbed. */
#if defined(IX_CC_MSUP_STUB)
    if(arg_pMsg != NULL)
    {
        ix_ossl_free(arg_pMsg);
        arg_pMsg = NULL;
    }
#endif /* defined(IX_CC_MSUP_STUB) */

    return err;
}



/**
 * NAME: _ix_cc_stkdrv_process_pkt
 *
 * DESCRIPTION: Processes a locally destined packet.
 * 
 * @Param:  - IN ix_buffer_handle arg_hDataToken - handle to the packet.
 * @Param:  - IN ix_cc_stkdrv_ctrl* arg_pStkdrvCtrl - pointer to the
 *          stack driver control structure.
 *
 * @Return: IX_SUCCESS
 *          IX_CC_ERROR_ENTRY_NOT_FOUND
 *          IX_CC_STKDRV_ERROR_CANNOT_CLASSIFY_PKT
 *          IX_CC_STKDRV_ERROR_HANDLER_RECV
 */
ix_error _ix_cc_stkdrv_process_pkt(
                                   ix_buffer_handle arg_hDataToken,
                                   ix_cc_stkdrv_ctrl* arg_pStkdrvCtrl
                                   )
{
    ix_error err = IX_SUCCESS;
    ix_cc_stkdrv_handler_module* pHandlerModule;

    ix_cc_stkdrv_handler_id handlerId;
    ix_cc_stkdrv_packet_type packetType;

    /* Check for remote support. */
    if(arg_pStkdrvCtrl->remoteSupportFlag)
    {
        /* By default (no classification) send to TM. */
        handlerId = IX_CC_STKDRV_HANDLER_ID_TRANSP_MODULE;
        packetType = IX_CC_STKDRV_PACKET_TYPE_REMOTE_LEGACY;
    }
    else
    {
        /* By default (no classification) send to local VIDD. */
        handlerId = IX_CC_STKDRV_HANDLER_ID_LOCAL_DRIVER;
        packetType = IX_CC_STKDRV_PACKET_TYPE_LOCAL_LEGACY;
    }

#if defined(IX_DEBUG)
    {
        ix_buffer_type bufType;

        /* Check that the buffer is a hardware buffer. */
        IX_ERROR_CG(ix_rm_buffer_get_type(arg_hDataToken, &bufType), err, label_1);
        if(bufType != IX_BUFFER_TYPE_HARDWARE)
        {
            err = IX_ERROR_WARNING(IX_CC_STKDRV_ERROR_INVALID_BUF_TYPE,
                ("Packet handler invoked with software buffer"));
            goto label_1;
        }
    }
#endif /* defined(IX_DEBUG) */

#if defined(IX_CC_STKDRV_PKT_CLASS)
    /* Classify the buffer to get the appropriate handlerId and packet type. */
    IX_ERROR_CGT(ix_cc_stkdrv_classify_pkt (arg_hDataToken, arg_pStkdrvCtrl->pFilterCtrl,
                                    &handlerId, &packetType), err, IX_CC_STKDRV_ERROR_CANNOT_CLASSIFY_PKT, IX_ERROR_LEVEL_WARNING, label_1);
#endif

    /* Find the appropriate handler (e.g. local VIDD, transport module) from the list of handlers. */
    pHandlerModule = arg_pStkdrvCtrl->pHandlerList;
    _IX_CC_STKDRV_GET_HANDLER_MODULE(handlerId, pHandlerModule);
    if(pHandlerModule == NULL)
    {
        err = IX_ERROR_WARNING(IX_CC_ERROR_ENTRY_NOT_FOUND, ("Could not find handler with ID %d", (ix_uint32)handlerId));
        goto label_1;
    }
    
    /* Invoke the receive packet callback for the handler. */
    #if defined(IX_DEBUG)
        if(pHandlerModule->pHandler_func->receive_pkt == NULL)
        {
            err = IX_ERROR_WARNING(IX_CC_STKDRV_ERROR_HANDLER_RECV, ("Handler %d does not have a packet callback registered", (ix_uint32)handlerId));
            goto label_1;
        }
    #endif /* defined(IX_DEBUG) */

    IX_ERROR_CGT(pHandlerModule->pHandler_func->receive_pkt(
            arg_hDataToken, pHandlerModule->pHandler_func->pPktContext, packetType),
        err, IX_CC_STKDRV_ERROR_HANDLER_RECV, IX_ERROR_LEVEL_WARNING, label_1);

    return err;

label_1:
    if(arg_hDataToken != IX_NULL_BUFFER_HANDLE)
    {
        ix_rm_buffer_free_chain(arg_hDataToken);
        arg_hDataToken = IX_NULL_BUFFER_HANDLE;
    }

    return err;
}


/**
 * NAME: _ix_cc_stkdrv_set_property
 *
 * DESCRIPTION: Updates the interface properties, specified by a property ID
 *              and a property data structure, for a given port.
 * 
 * @Param:  - IN ix_uint32 arg_propID - ID of the property/properties to be set.
 *          Possible values defined in ix_cc_properties.h
 * @Param:  - IN ix_cc_properties* arg_pProperty - pointer to the structure that
 *          will provide property data.
 * @Param:  - IN ix_cc_stkdrv_physical_if_node* arg_pPhysicalIf - pointer to the
 *          port structure whose properties will be updated.
 * @Param:  - IN ix_cc_stkdrv_ctrl* arg_pStkdrvCtrl - pointer to the stack driver
 *          control structure.
 *
 * @Return: IX_SUCCESS if successful or a valid ix_error token on failure.
 */

ix_error _ix_cc_stkdrv_set_property(
                                    ix_uint32 arg_propID,
                                    ix_cc_properties* arg_pProperty,
                                    ix_cc_stkdrv_physical_if_node* arg_pPhysicalIf,
                                    ix_cc_stkdrv_ctrl* arg_pStkdrvCtrl
                                    )
{
    ix_error err = IX_SUCCESS;

    ix_cc_stkdrv_virtual_if* pVirtualIf = arg_pPhysicalIf->pPhysicalIfInfo->pVirtualIfs;
    ix_cc_stkdrv_virtual_if* pNewVirtualIf;
    ix_cc_stkdrv_virtual_if* pPrevVirtualIf = NULL;
    ix_cc_stkdrv_handler_module* pHandlerModule = NULL;
    ix_uint32 loop;
    ix_uint32 total_no_virtual_if;
    ix_uint8 flag;

    /* These following property updates do not need looping */ 
    /* Set MTU. */
    if((arg_propID & IX_CC_SET_PROPERTY_MTU) != 0)
       arg_pPhysicalIf->pPhysicalIfInfo->MTU = arg_pProperty->mtu;
   
    /* Set port status. */
    if((arg_propID & IX_CC_SET_PROPERTY_PHYSICAL_IF_STATUS) != 0)
    {
        /* Set interface state according to property update. */
        arg_pPhysicalIf->pPhysicalIfInfo->physical_if_status =
            arg_pProperty->physical_if_state;
    }

    /* Set MAC Address. */
    if((arg_propID & IX_CC_SET_PROPERTY_MAC_ADDR) != 0)
        ix_ossl_memcpy(&pVirtualIf->macAddr,
                       &arg_pProperty->mac_addr,
                       IX_CC_MAC_ADDR_LEN);

    /* Set link speed. */
    if((arg_propID & IX_CC_SET_PROPERTY_LINK_SPEED) != 0)
        arg_pPhysicalIf->pPhysicalIfInfo->linkSpeed = arg_pProperty->link_speed;

    /* Set link status. */
    if((arg_propID & IX_CC_SET_PROPERTY_LINK_STATUS) != 0)
        arg_pPhysicalIf->pPhysicalIfInfo->link_status = arg_pProperty->link_state;
    else
    {
        for (loop = 0;
             loop < arg_pProperty->no_virtual_if;
             loop++)
        {
            /* Add IP interface. */
            if(((arg_propID & IX_CC_SET_PROPERTY_ADD_INTERFACE_IPV4) != 0)
#if defined(IX_IPV6_SUPPORT)
               || ((arg_propID & IX_CC_SET_PROPERTY_ADD_INTERFACE_IPV6) != 0)
#endif /* defined(IX_IPV6_SUPPORT) */
              )
            {
                /* Check if we have crossed the maximum number of
                 * virtual interfaces at any time. Actually this check
                 * needs to be done initially, but that would make the code
                 * look ugly, hence the following calculation is needed.
                 * Note that this check is redundant, but avoids scanning
                 * the whole list of IPv4/IPv6 addresses.
                 */ 
                total_no_virtual_if =
                    arg_pPhysicalIf->pPhysicalIfInfo->no_virtual_if_with_ipv4_addr;
#if defined(IX_IPV6_SUPPORT)
                total_no_virtual_if +=
                    arg_pPhysicalIf->pPhysicalIfInfo->no_virtual_if_with_ipv6_addr;
#endif /* defined(IX_IPV6_SUPPORT) */
                if ((total_no_virtual_if + arg_pProperty->no_virtual_if
                     - loop)
                    >= PROP_MAX_VIRTUAL_IF)
                {
                    err = IX_ERROR_WARNING(IX_CC_ERROR_FULL,
                        ("Exceeding allowable number of virtual interfaces"));
                    return err;
                }
                /* Allocate memory for new virtual interface structure. */
                pNewVirtualIf = ix_ossl_malloc(sizeof(ix_cc_stkdrv_virtual_if));
                if(pNewVirtualIf == NULL)
                {
                    err = IX_ERROR_WARNING(IX_CC_ERROR_OOM_SYSTEM,
                        ("Could not allocate memory for virtual interface"));
                    return err; 
                }
                /* Initialize virtual interface structure to zeroes. */
                ix_ossl_memset(pNewVirtualIf, 0, 
                    sizeof(ix_cc_stkdrv_virtual_if));
                if((arg_propID & IX_CC_SET_PROPERTY_ADD_INTERFACE_IPV4) != 0)
                {
                    /* Check if the number of configured IPv4 addrs exceeds
                     * the allowed number of addresses.
                     */
                    if (arg_pPhysicalIf->pPhysicalIfInfo->no_virtual_if_with_ipv4_addr
                        >= PROP_MAX_IPV4_ADDRESSES)
                    {
                        err = IX_ERROR_WARNING(IX_CC_ERROR_FULL,
                                  ("Exceeding allowable number of virtual interfaces"));
                        ix_ossl_free (pNewVirtualIf);
                        continue;
                    }
                    /* Check if this address is already present */
                    _IX_CC_STKDRV_VIDD_CHECK_DUPLICATE_IPV4_ADDR (arg_pPhysicalIf,
                        arg_pProperty->ip_prop[loop].protocol.ipv4_prop.ipv4_address,
                        flag);
                    if (flag == 1)
                    {
                        /* Entry already present */
                        IX_ERROR_WARNING(IX_CC_ERROR_DUPLICATE_ENTRY,
                            ("IPv4 Entry already present"));
                        ix_ossl_free (pNewVirtualIf);
                        continue;
                    }
                    /* Initialize IPv4 portion of new virtual interface. */
                    pNewVirtualIf->ipProp.ip_version = IX_CC_IP_VERSION_IPV4;
                    pNewVirtualIf->ipProp.protocol.ipv4_prop.ipv4_address =
                        arg_pProperty->ip_prop[loop].protocol.ipv4_prop.ipv4_address;
                    pNewVirtualIf->ipProp.protocol.ipv4_prop.ipv4_subnet_mask
                        = arg_pProperty->ip_prop[loop].protocol.ipv4_prop.ipv4_subnet_mask;
                    ix_ossl_memcpy(&pNewVirtualIf->macAddr,
                        &arg_pPhysicalIf->pPhysicalIfInfo->pVirtualIfs->macAddr,
                        IX_CC_MAC_ADDR_LEN);
                    /* Increment the number of virtual IF */
                    arg_pPhysicalIf->pPhysicalIfInfo->no_virtual_if_with_ipv4_addr++;
                }
#if defined(IX_IPV6_SUPPORT)
                else if((arg_propID & IX_CC_SET_PROPERTY_ADD_INTERFACE_IPV6)
                         != 0)
                {
                    /* Check if the number of configured IPv4 addrs exceeds
                     * the allowed number of addresses.
                     */
                    if (arg_pPhysicalIf->pPhysicalIfInfo->no_virtual_if_with_ipv6_addr
                        >= PROP_MAX_IPV6_ADDRESSES)
                    {
                        err = IX_ERROR_WARNING(IX_CC_ERROR_FULL,
                                  ("Exceeding allowable number of virtual interfaces"));
                        ix_ossl_free (pNewVirtualIf);
                        continue;
                    }
                    /* Check if this address is already present */
                    _IX_CC_STKDRV_VIDD_CHECK_DUPLICATE_IPV6_ADDR (arg_pPhysicalIf,
                        arg_pProperty->ip_prop[loop].protocol.ipv6_prop.ipv6_address.addr,
                        flag);
                    if (flag == 1)
                    {
                        /* Entry already present */
                        IX_ERROR_WARNING(IX_CC_ERROR_DUPLICATE_ENTRY,
                            ("IPv6 Entry already present"));
                        ix_ossl_free (pNewVirtualIf);
                        continue;
                    }
                    /* Initialize IPv6 portion of new virtual interface. */
                    pNewVirtualIf->ipProp.ip_version = IX_CC_IP_VERSION_IPV6;
                    IX_128_BIT_BINARY_OPERATION_ASSIGN (
                        pNewVirtualIf->ipProp.protocol.ipv6_prop.ipv6_address.addr,
                        arg_pProperty->ip_prop[loop].protocol.ipv6_prop.ipv6_address.addr);
                    pNewVirtualIf->ipProp.protocol.ipv6_prop.ipv6_address.prefixlen =
                        arg_pProperty->ip_prop[loop].protocol.ipv6_prop.ipv6_address.prefixlen;
                    IX_128_BIT_BINARY_OPERATION_ASSIGN (
                        pNewVirtualIf->ipProp.protocol.ipv6_prop.ipv6_gateway,
                        arg_pProperty->ip_prop[loop].protocol.ipv6_prop.ipv6_gateway);
                    /* Increment the number of virtual IF */
                    arg_pPhysicalIf->pPhysicalIfInfo->no_virtual_if_with_ipv6_addr++;
                }
#endif /* defined(IX_IPV6_SUPPORT) */

                /* Link the new virtual interface in the list. */
                pNewVirtualIf->pNextVirtualIf = pVirtualIf;
                arg_pPhysicalIf->pPhysicalIfInfo->pVirtualIfs = pNewVirtualIf;
                pVirtualIf = pNewVirtualIf;
                /* This is needed to avoid pVirtualIf getting affected
                 * further down
                 */
                continue;
            }
            /* For all the following options, we need to search for the
             * virtual interface on which we need to act.
             */
            while(pVirtualIf != NULL)
            {
                /* Check for a matching IP interface. */
                if(
                   ((pVirtualIf->ipProp.ip_version == IX_CC_IP_VERSION_IPV4) &&
                    (pVirtualIf->ipProp.protocol.ipv4_prop.ipv4_address ==
                     arg_pProperty->ip_prop[loop].protocol.ipv4_prop.ipv4_address)
                   )
#if defined(IX_IPV6_SUPPORT)
                   || ((pVirtualIf->ipProp.ip_version == IX_CC_IP_VERSION_IPV6) &&
                       (IX_128_BIT_BINARY_OPERATION_EQ (
                         pVirtualIf->ipProp.protocol.ipv6_prop.ipv6_address.addr,
                         arg_pProperty->ip_prop[loop].protocol.ipv6_prop.ipv6_address.addr))
                      )
#endif /* defined(IX_IPV6_SUPPORT) */
                  )
                {
                    break;
                }
                pPrevVirtualIf = pVirtualIf;
                pVirtualIf = pVirtualIf->pNextVirtualIf;;
            }
            /*
             *  Check if the entry is found.
             */
            if (pVirtualIf == NULL)
            {
                return IX_ERROR_WARNING(IX_CC_ERROR_NULL,
                    ("No matching entry found"));
            }
            /* Set IPv4 gateway. */
            if((arg_propID & IX_CC_SET_PROPERTY_ADD_GATEWAY_IPV4) != 0)
                pVirtualIf->ipProp.protocol.ipv4_prop.ipv4_gateway =
                    arg_pProperty->ip_prop[loop].protocol.ipv4_prop.ipv4_gateway;
#if defined(IX_IPV6_SUPPORT)
            /* Set IPv6 gateway. */
            else if ((arg_propID & IX_CC_SET_PROPERTY_ADD_GATEWAY_IPV6) != 0)
            {
                IX_128_BIT_BINARY_OPERATION_ASSIGN (
                    pVirtualIf->ipProp.protocol.ipv6_prop.ipv6_gateway,
                    arg_pProperty->ip_prop[loop].protocol.ipv6_prop.ipv6_gateway);
            }
#endif /* defined(IX_IPV6_SUPPORT) */

            /* Set IPv4 broadcast. */
            if((arg_propID & IX_CC_SET_PROPERTY_ADD_BROADCAST_IPV4) != 0)
                pVirtualIf->ipProp.protocol.ipv4_prop.ipv4_broadcast =
                arg_pProperty->ip_prop[loop].protocol.ipv4_prop.ipv4_broadcast;

            /* Delete IPv4 broadcast - in this release set it to 0. */ 
            if((arg_propID & IX_CC_SET_PROPERTY_DEL_BROADCAST_IPV4) != 0)
                pVirtualIf->ipProp.protocol.ipv4_prop.ipv4_broadcast = 0;

            /* Delete virtual interface. */
            if(((arg_propID & IX_CC_SET_PROPERTY_DEL_INTERFACE_IPV4) != 0)
#if defined(IX_IPV6_SUPPORT)
               || ((arg_propID & IX_CC_SET_PROPERTY_DEL_INTERFACE_IPV6) != 0)
#endif /* defined(IX_IPV6_SUPPORT) */
              )
            {
                /* Unlink the virtual interface from the list. */
                if(pPrevVirtualIf != NULL)
                    pPrevVirtualIf->pNextVirtualIf = pVirtualIf->pNextVirtualIf;
                else
                    arg_pPhysicalIf->pPhysicalIfInfo->pVirtualIfs =
                    pVirtualIf->pNextVirtualIf;
                /* Free memory allocated to virtual interface. */
                ix_ossl_free(pVirtualIf);
                if((arg_propID & IX_CC_SET_PROPERTY_DEL_INTERFACE_IPV4) != 0)
                {
                    /* Decrement the number of virtual IF */
                    arg_pPhysicalIf->pPhysicalIfInfo->no_virtual_if_with_ipv4_addr--;
                }
#if defined(IX_IPV6_SUPPORT)
                else
                {
                    /* Decrement the number of virtual IF */
                    arg_pPhysicalIf->pPhysicalIfInfo->no_virtual_if_with_ipv6_addr--;
                }
#endif /* defined(IX_IPV6_SUPPORT) */
            }

            /* Delete IPv4 gateway - in this release set it to 0. */
            if((arg_propID & IX_CC_SET_PROPERTY_DEL_GATEWAY_IPV4) != 0)
                pVirtualIf->ipProp.protocol.ipv4_prop.ipv4_gateway = 0;
#if defined(IX_IPV6_SUPPORT)
            /* Delete IPv6 gateway - in this release set it to 0. */
            else if((arg_propID & IX_CC_SET_PROPERTY_DEL_GATEWAY_IPV6) != 0)
            {
                pVirtualIf->ipProp.protocol.ipv6_prop.ipv6_gateway.aUint32[0] = 0;
                pVirtualIf->ipProp.protocol.ipv6_prop.ipv6_gateway.aUint32[1] = 0;
                pVirtualIf->ipProp.protocol.ipv6_prop.ipv6_gateway.aUint32[2] = 0;
                pVirtualIf->ipProp.protocol.ipv6_prop.ipv6_gateway.aUint32[3] = 0;
            }
#endif /* defined(IX_IPV6_SUPPORT) */
            /* Reset the pointers */
            pPrevVirtualIf = NULL;
            pVirtualIf = arg_pPhysicalIf->pPhysicalIfInfo->pVirtualIfs;
        } /* End of for */
    } /* End of else */

    /* Find local VIDD handler module. */
    pHandlerModule = arg_pStkdrvCtrl->pHandlerList;
    _IX_CC_STKDRV_GET_HANDLER_MODULE(IX_CC_STKDRV_HANDLER_ID_LOCAL_DRIVER, pHandlerModule);
    if(pHandlerModule == NULL)
    {
       err = IX_ERROR_WARNING(IX_CC_ERROR_ENTRY_NOT_FOUND,
                              ("Could not find handler with ID %d",
                                (ix_uint32)IX_CC_STKDRV_HANDLER_ID_LOCAL_DRIVER));
    }
    /* Send message to VIDD notifying it of property updates. */
    else if(pHandlerModule->pHandler_func->receive_msg_int != NULL)
    {
        void* pReplyMsg;
        ix_uint32 replyMsgSize;

        ix_cc_stkdrv_vidd_property_update_msg updateMsg;
        updateMsg.propID = arg_propID;
        updateMsg.pProperty = arg_pProperty;
    
        IX_ERROR_C(pHandlerModule->pHandler_func->receive_msg_int(
                    IX_CC_STKDRV_VIDD_MSG_ID_PROPERTY_UPDATE,
                    (ix_uint32)&updateMsg,
                    pHandlerModule->pHandler_func->pMsgIntContext, &pReplyMsg, &replyMsgSize),
            err);
    }


    return err;
}

/**
 * NAME: _ix_cc_stkdrv_get_property
 *
 * DESCRIPTION: Gets the property info, specified by property ID for a given port.
 * 
 * @Param:  - IN ix_uint32 arg_propID - ID of the property/properties.
 *          Possible values defined in ix_cc_properties.h
 * @Param:  - IN ix_cc_stkdrv_physical_if_node* arg_pPhysicalIf - pointer to the
 *          port being queried.
 * @Param:  - OUT ix_cc_properties* arg_pProperty - pointer to the structure that
 *          will contain the retrieved property data.
 *
 * @Return: IX_SUCCESS.
 */
ix_error _ix_cc_stkdrv_get_property(
                                    ix_uint32 arg_propID,
                                    ix_cc_stkdrv_physical_if_node* arg_pPhysicalIf,
                                    ix_cc_properties* arg_pProperty
                                    )
{
    ix_cc_stkdrv_virtual_if* pVirtualIf = arg_pPhysicalIf->pPhysicalIfInfo->pVirtualIfs;
    ix_uint32 loop1_var;
    ix_uint32 loop2_var;
    ix_uint32 total_no_virtual_if;

    /* Return local blade ID and port ID with all properties. */
    arg_pProperty->blade_id = arg_pPhysicalIf->pPhysicalIfInfo->fpId;
    arg_pProperty->port_id = arg_pPhysicalIf->pPhysicalIfInfo->portId;

    arg_pProperty->no_virtual_if = 0;
    /* Calculate the maximum number of virtual ifs */
    total_no_virtual_if =
        arg_pPhysicalIf->pPhysicalIfInfo->no_virtual_if_with_ipv4_addr;
#if defined(IX_IPV6_SUPPORT)
    total_no_virtual_if +=
        arg_pPhysicalIf->pPhysicalIfInfo->no_virtual_if_with_ipv6_addr;
#endif /* defined(IX_IPV6_SUPPORT) */
    /* Get IPv4 properties. */
    if((arg_propID & IX_CC_GET_PROPERTY_INTERFACE_IPV4) != 0)
    {
        for (loop1_var = 0, loop2_var = 0;
             loop1_var < total_no_virtual_if;
             loop1_var++)
        {
            /* Check if the present interface is IPv4 */
            if (pVirtualIf->ipProp.ip_version == IX_CC_IP_VERSION_IPV4)
            {
                arg_pProperty->no_virtual_if++;
                arg_pProperty->ip_prop[loop2_var].ip_version = IX_CC_IP_VERSION_IPV4;
                arg_pProperty->ip_prop[loop2_var].protocol.ipv4_prop.ipv4_address =
                    pVirtualIf->ipProp.protocol.ipv4_prop.ipv4_address;
                arg_pProperty->ip_prop[loop2_var].protocol.ipv4_prop.ipv4_subnet_mask =
                    pVirtualIf->ipProp.protocol.ipv4_prop.ipv4_subnet_mask;
                arg_pProperty->ip_prop[loop2_var].protocol.ipv4_prop.ipv4_gateway =
                    pVirtualIf->ipProp.protocol.ipv4_prop.ipv4_gateway;
                arg_pProperty->ip_prop[loop2_var].protocol.ipv4_prop.ipv4_broadcast =
                    pVirtualIf->ipProp.protocol.ipv4_prop.ipv4_broadcast;
                loop2_var++;
            }
            /* Move to the next virtual IF */
            pVirtualIf = pVirtualIf->pNextVirtualIf;
        }
    }
#if defined(IX_IPV6_SUPPORT)
    /* Get IPv6 properties. */
    if((arg_propID & IX_CC_GET_PROPERTY_INTERFACE_IPV6) != 0)
    {
        for (loop1_var = 0, loop2_var = 0;
             loop1_var < total_no_virtual_if;
             loop1_var++)
        {
            /* Check if the present interface is IPv6 */
           if (pVirtualIf->ipProp.ip_version == IX_CC_IP_VERSION_IPV6)
           {
               arg_pProperty->no_virtual_if++;
               arg_pProperty->ip_prop[loop2_var].ip_version = IX_CC_IP_VERSION_IPV6;
               IX_128_BIT_BINARY_OPERATION_ASSIGN (
                   arg_pProperty->ip_prop[loop2_var].protocol.ipv6_prop.ipv6_address.addr,
                   pVirtualIf->ipProp.protocol.ipv6_prop.ipv6_address.addr);
               arg_pProperty->ip_prop[loop2_var].protocol.ipv6_prop.ipv6_address.prefixlen =
                   pVirtualIf->ipProp.protocol.ipv6_prop.ipv6_address.prefixlen;
               IX_128_BIT_BINARY_OPERATION_ASSIGN (
                   arg_pProperty->ip_prop[loop2_var].protocol.ipv6_prop.ipv6_gateway,
                   pVirtualIf->ipProp.protocol.ipv6_prop.ipv6_gateway);
               loop2_var++;
           }
           /* Move to the next virtual IF */
           pVirtualIf = pVirtualIf->pNextVirtualIf;
        }
    }
#endif /* defined(IX_IPV6_SUPPORT) */

    /* Get MTU. */
    if((arg_propID & IX_CC_GET_PROPERTY_MTU) != 0)
        arg_pProperty->mtu = arg_pPhysicalIf->pPhysicalIfInfo->MTU;
    
    /* Get port status. */
    if((arg_propID & IX_CC_GET_PROPERTY_PHYSICAL_IF_STATUS) != 0)
        arg_pProperty->physical_if_state =
            arg_pPhysicalIf->pPhysicalIfInfo->physical_if_status;

    /* Get MAC Address. */
    if((arg_propID & IX_CC_GET_PROPERTY_MAC_ADDR) != 0)
        ix_ossl_memcpy(&arg_pProperty->mac_addr,
                        &arg_pPhysicalIf->pPhysicalIfInfo->pVirtualIfs->macAddr,
                        IX_CC_MAC_ADDR_LEN);

    /* Get link speed. */
    if((arg_propID & IX_CC_GET_PROPERTY_LINK_SPEED) != 0)
        arg_pProperty->link_speed = arg_pPhysicalIf->pPhysicalIfInfo->linkSpeed;

    /* Get media type. */
    if((arg_propID & IX_CC_GET_PROPERTY_MEDIA_TYPE) != 0)
        arg_pProperty->media_type = arg_pPhysicalIf->pPhysicalIfInfo->mediaType;

    if((arg_propID & IX_CC_GET_PROPERTY_LINK_STATUS) != 0)
        arg_pProperty->link_state = arg_pPhysicalIf->pPhysicalIfInfo->link_status;

    return IX_SUCCESS;
}


/**
 * NAME: _ix_cc_stkdrv_cleanup
 *
 * DESCRIPTION: Gets the property info, specified by property ID for a given port.
 * 
 * @Param:  - IN ix_uint32 arg_checkErrors - indicates whether to check for errors
 *          or not in this function.
 * @Param:  - INOUT void** arg_ppStkdrvCtrl - location of pointer to stack driver
 *          control strucutre.
 *
 * @Return: IX_SUCCESS
 *          IX_CC_STKDRV_ERROR_CCI_REMOVE_HANDLER
 *          IX_CC_STKDRV_ERROR_RM_REMOVE_HANDLER
 *          IX_CC_STKDRV_ERROR_HANDLER_FINI
 *          IX_CC_ERROR_NULL
 */
ix_error _ix_cc_stkdrv_cleanup(
                               ix_uint32 arg_checkErrors,
                               void** arg_ppStkdrvCtrl
                               )
{
    ix_cc_stkdrv_ctrl* pStkdrvCtrl = (ix_cc_stkdrv_ctrl*)*arg_ppStkdrvCtrl;
    ix_cc_stkdrv_fp_node* pFP = NULL;
    ix_cc_stkdrv_physical_if_node* pPhysicalIf = NULL;
    ix_cc_stkdrv_physical_if_node* pNextPhysicalIf = NULL;
    ix_cc_stkdrv_physical_if_info* pPhysicalIfInfo = NULL;

    ix_cc_stkdrv_handler_module* pHandlerModule = NULL;
    ix_cc_stkdrv_handler_module* pNextHandlerModule = NULL;
    ix_cc_stkdrv_virtual_if* pCurrVirtualIf;
    ix_cc_stkdrv_virtual_if* pNextLocalVirtualIf;
    ix_uint32 loop_var;
    ix_uint32 total_no_virtual_if;

    if(pStkdrvCtrl != NULL)
    {

        if(arg_checkErrors == 1)
        {
            /* Remove packet and message handlers. */
            #if !defined(IX_EXCLUDE_CCI)
                IX_ERROR_CRT(ix_cci_cc_remove_message_handler(IX_CC_STKDRV_HIGH_PRIORITY_PKT_INPUT),
                    IX_CC_STKDRV_ERROR_CCI_REMOVE_HANDLER, IX_ERROR_LEVEL_LOCAL);
                IX_ERROR_CRT(ix_cci_cc_remove_message_handler(IX_CC_STKDRV_MSG_INPUT),
                    IX_CC_STKDRV_ERROR_CCI_REMOVE_HANDLER, IX_ERROR_LEVEL_LOCAL);
                IX_ERROR_CRT(ix_cci_cc_remove_packet_handler(IX_CC_STKDRV_LOW_PRIORITY_PKT_INPUT),
                    IX_CC_STKDRV_ERROR_CCI_REMOVE_HANDLER, IX_ERROR_LEVEL_LOCAL);
            #else
                IX_ERROR_CRT(ix_rm_message_handler_unregister(IX_CC_STKDRV_HIGH_PRIORITY_PKT_INPUT),
                    IX_CC_STKDRV_ERROR_RM_REMOVE_HANDLER, IX_ERROR_LEVEL_LOCAL);
                IX_ERROR_CRT(ix_rm_message_handler_unregister(IX_CC_STKDRV_MSG_INPUT),
                    IX_CC_STKDRV_ERROR_RM_REMOVE_HANDLER, IX_ERROR_LEVEL_LOCAL);
                IX_ERROR_CRT(ix_rm_packet_handler_unregister(IX_CC_STKDRV_LOW_PRIORITY_PKT_INPUT),
                    IX_CC_STKDRV_ERROR_RM_REMOVE_HANDLER, IX_ERROR_LEVEL_LOCAL);
            #endif /* end !defined(IX_EXCLUDE_CCI) */
        }
        else
        {
            /* Remove packet and message handlers. */
            #if !defined(IX_EXCLUDE_CCI)
                ix_cci_cc_remove_message_handler(IX_CC_STKDRV_HIGH_PRIORITY_PKT_INPUT);
                ix_cci_cc_remove_message_handler(IX_CC_STKDRV_MSG_INPUT);
                ix_cci_cc_remove_packet_handler(IX_CC_STKDRV_LOW_PRIORITY_PKT_INPUT);
            #else
                ix_rm_message_handler_unregister(IX_CC_STKDRV_HIGH_PRIORITY_PKT_INPUT);
                ix_rm_message_handler_unregister(IX_CC_STKDRV_MSG_INPUT);
                ix_rm_packet_handler_unregister(IX_CC_STKDRV_LOW_PRIORITY_PKT_INPUT);
            #endif /* end !defined(IX_EXCLUDE_CCI) */
        }

#if defined(IX_CC_STKDRV_PKT_CLASS)
        if(arg_checkErrors == 1)
        {
           /* Finalize the filters. */
           IX_ERROR_CRT(ix_cc_stkdrv_fini_filters(&pStkdrvCtrl->pFilterCtrl),
                        IX_CC_STKDRV_ERROR_FILTER_FINI,
                        IX_ERROR_LEVEL_LOCAL);
        }
        else
        {
           ix_cc_stkdrv_fini_filters(&pStkdrvCtrl->pFilterCtrl);
        }
#endif /* defined(IX_CC_STKDRV_PKT_CLASS) */

        /* Free memory for handler module structures. */
        pHandlerModule = pStkdrvCtrl->pHandlerList;
        while(pHandlerModule != NULL)
        {
            pNextHandlerModule = pHandlerModule->pNextHandler;
            if(pHandlerModule->pHandler_func != NULL)
            {
                /* Call fini function for this handler module. */
                if(pHandlerModule->pHandler_func->fini != NULL)
                {
                    if(arg_checkErrors == 1)
                    {
                        IX_ERROR_CRT(pHandlerModule->pHandler_func->fini(pHandlerModule->pHandler_func->pFiniContext),
                            IX_CC_STKDRV_ERROR_HANDLER_FINI, IX_ERROR_LEVEL_LOCAL);
                    }
                    else
                        pHandlerModule->pHandler_func->fini(pHandlerModule->pHandler_func->pFiniContext);
                }

                /* Free memory allocated to callback structure. */
                ix_ossl_free(pHandlerModule->pHandler_func);
                pHandlerModule->pHandler_func = NULL;
            }
            ix_ossl_free(pHandlerModule);
            pHandlerModule = pNextHandlerModule;
        }
        pStkdrvCtrl->pHandlerList = NULL;

        if(pStkdrvCtrl->pFPList != NULL)
        {
            pFP = pStkdrvCtrl->pFPList;
            pPhysicalIf = pFP->pPhysicalIfs;
            
            /* Free memory for all physical ifs. */
            while(pPhysicalIf != NULL)
            {
                pNextPhysicalIf = pPhysicalIf->pNextPhysicalIf;
                pPhysicalIfInfo = pPhysicalIf->pPhysicalIfInfo;
                if(pPhysicalIfInfo != NULL)
                {
                    if(pPhysicalIfInfo->pVirtualIfs != NULL)
                    {
                        /* Calculate the maximum number of virtual ifs */
                        total_no_virtual_if =
                            pPhysicalIfInfo->no_virtual_if_with_ipv4_addr;
#if defined(IX_IPV6_SUPPORT)
                        total_no_virtual_if +=
                            pPhysicalIfInfo->no_virtual_if_with_ipv6_addr;
#endif /* defined(IX_IPV6_SUPPORT) */
                        /* Scan through the list of Virtual IFs and
                         * free the memory.
                         */
                        pCurrVirtualIf = pPhysicalIfInfo->pVirtualIfs;
                        for (loop_var = 0;
                             loop_var < total_no_virtual_if;
                             loop_var++
                            )
                        {
                            pNextLocalVirtualIf = pCurrVirtualIf->pNextVirtualIf;
                            ix_ossl_free(pCurrVirtualIf);
                            pCurrVirtualIf = pNextLocalVirtualIf;
                        }
                        pPhysicalIfInfo->no_virtual_if_with_ipv4_addr = 0;
                        pPhysicalIfInfo->no_virtual_if_with_ipv6_addr = 0;
                        pPhysicalIfInfo->pVirtualIfs = NULL;
                    }
                    ix_ossl_free(pPhysicalIfInfo);
                    pPhysicalIfInfo = NULL;
                }
                ix_ossl_free(pPhysicalIf);
                pFP->numPorts--;
                pPhysicalIf = pNextPhysicalIf;
            }
            pFP->pPhysicalIfs = NULL;
            ix_ossl_free(pFP);
            pFP = NULL;
            pStkdrvCtrl->pFPList = NULL;
        }
        ix_ossl_free(pStkdrvCtrl);
        pStkdrvCtrl = NULL;

#if (defined(IX_CONFIGURATION_unit_test) || defined(MUX_STUB))
        g_pStkdrvCtrl = NULL;
#endif /* (defined(IX_CONFIGURATION_unit_test) || defined(MUX_STUB)) */
    }
    else
        return IX_ERROR_LOCAL(IX_CC_ERROR_NULL, ("Stack driver fini called with NULL context"));

    return IX_SUCCESS;
}

/**
 * NAME: ix_cc_stkdrv_send_msg_str
 *
 * DESCRIPTION: These message functions are used for sending messages
 * from a handler module to the core component module.  In this
 * release there is no need to send any messages from a handler
 * module to the CC Module, so these functions are stubs.
 * 
 * @Param:  - IN ix_cc_stkdrv_cc_module_msg_id arg_msgID - ID of the msg.
 * @Param:  - IN const char* arg_pMsg - pointer to the msg data.
 * @Param:  - IN ix_uint32 arg_MsgSize - size of the msg in bytes.
 * @Param:  - IN void* arg_pContext - pointer to the context.
 *
 * @Return: IX_SUCCESS
 */
ix_error ix_cc_stkdrv_send_msg_str(
                                   ix_cc_stkdrv_cc_module_msg_id arg_msgID,
                                   const char* arg_pMsg,
                                   ix_uint32 arg_MsgSize,
                                   void* arg_pContext
                                   )
{
    return IX_SUCCESS;
}

/**
 * NAME: ix_cc_stkdrv_send_msg_int
 *
 * DESCRIPTION: These message functions are used for sending messages
 * from a handler module to the core component module.  In this
 * release there is no need to send any messages from a handler
 * module to the CC Module, so these functions are stubs.
 * 
 * @Param:  - IN ix_cc_stkdrv_cc_module_msg_id arg_msgID - ID of the msg.
 * @Param:  - IN ix_uint32 arg_MsgValue - the integer msg.
 * @Param:  - IN void* arg_pContext - pointer to the context.
 *
 * @Return: IX_SUCCESS
 */
ix_error ix_cc_stkdrv_send_msg_int(
                                   ix_cc_stkdrv_cc_module_msg_id arg_msgID,
                                   ix_uint32 arg_MsgValue,
                                   void* arg_pContext
                                   )
{
    return IX_SUCCESS;
}

/**
 * NAME: ix_cc_strkdrv_dump_packet
 *
 * DESCRIPTION: Dumps data to the screen.
 * 
 * @Param:  - IN ix_uint8* arg_pData - pointer to start of data to be dumped.
 * @Param:  - IN ix_uint32 arg_len - length of data to be dumped in bytes.
 * @Param:  - IN ix_uint8* arg_pOutputString - string to be output before dump.
 *
 * @Return: None.
 */
void _ix_cc_stkdrv_dump_packet(
                              ix_uint8* arg_pData,
                              ix_uint32 arg_len,
                              ix_uint8* arg_pOutputString
                              )
{
    ix_uint32 ctr;

    ix_ossl_message_log("%s\n", arg_pOutputString);
    ix_ossl_message_log("starting at %p going for %lu bytes.\n", arg_pData, arg_len );
    for(ctr = 0; ctr < arg_len; ctr++)
    {
        if (ctr && ((ctr % 16 ) == 0) )
            ix_ossl_message_log("\n");
        ix_ossl_message_log("%02x", *arg_pData++ );
        if (ctr && (((ctr + 1) % 4) == 0))
            ix_ossl_message_log(" ");
    }
    ix_ossl_message_log("\n");
}



/**
 * NAME: ix_cc_stkdrv_async_send_handler_msg
 *
 * DESCRIPTION: Function used to send a message to a communication handler
 *              asynchronously.  This caller must be in the same address
 *              space as the communication handler to which it is trying
 *              to send a message.
 *
 * @Param:  - IN ix_cc_msghlp_callback arg_pCallback -
 *          pointer to the client's callback function to be invoked once
 *          the message returns.
 * @Param:  - IN void* arg_pContext - pointer to client context.
 * @Param:  - IN ix_cc_stkdrv_handler_id arg_handlerID - ID of the communication
 *          handler to which the message is to be delivered.
 * @Param:  - IN ix_uint32 arg_msgID - handler-specific message ID.
 * @Param:  - IN void* arg_pMsg - pointer to the message data.
 * @Param:  - IN ix_uint32 arg_msgSize - size of the message in bytes.
 *
 * @Return: IX_SUCCESS
 *          IX_CC_ERROR_NULL
 *          IX_CC_ERROR_OOM_SYSTEM
 */
ix_error ix_cc_stkdrv_async_send_handler_msg(
                                             ix_cc_msghlp_callback arg_pCallback,
                                             void* arg_pContext,
                                             ix_cc_stkdrv_handler_id arg_handlerID,
                                             ix_uint32 arg_msgID,
                                             void* arg_pMsg,
                                             ix_uint32 arg_msgSize
                                             )
{
    ix_error err = IX_SUCCESS;

    _ix_cc_stkdrv_send_handler_msg_context* pContext = NULL;
    _ix_cc_stkdrv_send_handler_msg_message* pMsg = NULL;

    /* Check for invalid callback. */
    if(arg_pCallback == NULL)
        return IX_ERROR_WARNING(IX_CC_ERROR_NULL, ("NULL callback passed in"));

    /* Allocate memory for message structure. */
    pMsg = ix_ossl_malloc(sizeof(_ix_cc_stkdrv_send_handler_msg_message));
    if(pMsg == NULL)
        return IX_ERROR_WARNING(IX_CC_ERROR_OOM_SYSTEM,
            ("Could not allocate memory for message to send message to comm handler"));

    /* Allocate memory for message support context. */
    pContext = ix_ossl_malloc(sizeof(_ix_cc_stkdrv_send_handler_msg_context));
    if(pContext == NULL)
    {
        err = IX_ERROR_WARNING(IX_CC_ERROR_OOM_SYSTEM,
            ("Could not allocate memory for context to send message to comm handler"));
        goto label_1;
    }

    /* Fill in message support context. */
    pContext->pUserCallback = arg_pCallback;
    pContext->pUserContext = arg_pContext;

    /* Fill in message. */
    pMsg->handlerID = arg_handlerID;
    pMsg->msgID = arg_msgID;
    pMsg->pMsg = arg_pMsg;
    pMsg->msgSize = arg_msgSize;

    /**
     * Send an asynchronous message to the Stack Driver to send
     * a message to a comm handler.
     */

    IX_ERROR_CG(ix_cc_msup_send_async_msg(IX_CC_STKDRV_MSG_INPUT,
                                        (ix_cc_msghlp_callback)_ix_cc_stkdrv_cb_send_handler_msg,
                                        pContext,
                                        IX_CC_STKDRV_MSG_ID_SEND_HANDLER_MSG,
                                        pMsg,
                                        sizeof(_ix_cc_stkdrv_send_handler_msg_message)), err, label_2);

    /* Free memory allocated to message structure. */
    ix_ossl_free(pMsg);
    pMsg = NULL;

    return err;

label_2:
    /* Free memory for message support context, only if an error has occurred. */
    ix_ossl_free(pContext);
    pContext = NULL;

label_1:
    /* Free memory allocated to message structure. */
    ix_ossl_free(pMsg);
    pMsg = NULL;

    return err;
}


/**
 * NAME: _ix_cc_stkdrv_send_handler_msg
 *
 * DESCRIPTION: Sends a message to the specified comm handler.
 *
 * @Param:  - IN ix_cc_stkdrv_handler_id arg_handlerID - ID of the communication
 *          handler to which the message is to be delivered.
 * @Param:  - IN ix_uint32 arg_msgID - handler-specific message ID.
 * @Param:  - IN void* arg_pMsg - pointer to the message data.
 * @Param:  - IN ix_uint32 arg_msgSize - size of the message in bytes.
 * @Param:  - IN ix_cc_stkdrv_ctrl* arg_pStkdrvCtrl - pointer to stack driver
 *          control structure.
 * @Param:  - OUT void** arg_ppReplyMsg - location of pointer to reply message.
 * @Param:  - OUT ix_uint32* arg_pReplyMsgSize - pointer to size of reply message.
 *
 * @Return: IX_SUCCESS
 */
ix_error _ix_cc_stkdrv_send_handler_msg(
                                        ix_cc_stkdrv_handler_id arg_handlerID,
                                        ix_uint32 arg_msgID,
                                        void* arg_pMsg,
                                        ix_uint32 arg_msgSize,
                                        ix_cc_stkdrv_ctrl* arg_pStkdrvCtrl,
                                        void** arg_ppReplyMsg,
                                        ix_uint32* arg_pReplyMsgSize
                                        )
{
    ix_error err = IX_SUCCESS;

    ix_cc_stkdrv_handler_module* pHandlerModule = NULL;

    /* Find the appropriate comm handler. */
    pHandlerModule = arg_pStkdrvCtrl->pHandlerList;
    _IX_CC_STKDRV_GET_HANDLER_MODULE(arg_handlerID, pHandlerModule);
    if(pHandlerModule == NULL)
        err = IX_ERROR_WARNING(IX_CC_ERROR_ENTRY_NOT_FOUND,
            ("Could not find handler with ID %d", (ix_uint32)arg_handlerID));


    /* Send message to the appropriate comm handler. */
    if(arg_msgSize == sizeof(ix_uint32))
        if(pHandlerModule->pHandler_func->receive_msg_int != NULL)
        {
            IX_ERROR_C(pHandlerModule->pHandler_func->receive_msg_int(
                        arg_msgID,
                        (ix_uint32)arg_pMsg,
                        pHandlerModule->pHandler_func->pMsgIntContext,
                        arg_ppReplyMsg,
                        arg_pReplyMsgSize), err);
        }
        else
            return IX_ERROR_WARNING(IX_CC_ERROR_NULL,
                ("No callback registered to send integer messages to handler with ID %d", (ix_uint32)arg_handlerID));
    else
        if(pHandlerModule->pHandler_func->receive_msg_str != NULL)
        {
            IX_ERROR_C(pHandlerModule->pHandler_func->receive_msg_str(
                        arg_msgID,
                        arg_pMsg,
                        arg_msgSize,
                        pHandlerModule->pHandler_func->pMsgStrContext,
                        arg_ppReplyMsg,
                        arg_pReplyMsgSize), err);
        }
        else
            return IX_ERROR_WARNING(IX_CC_ERROR_NULL, ("No callback registered to send string messages to handler with ID %d", (ix_uint32)arg_handlerID));

    return err;
}

/**
 * NAME: _ix_cc_stkdrv_cb_send_handler_msg
 *
 * DESCRIPTION: This is the internal callback function for sending a 
 *              message to a comm handler asynchronously.
 * 
 * @Param:  - IN ix_error arg_Result - error code returned from
 *          the asynchronous message send operation.
 * @Param:  - IN void* arg_pContext - pointer to reply message
 *          context.
 * @Param:  - IN void* arg_pMsg - pointer to reply message.  This memory
 *          is only valid for the duration of this routine - the user
 *          callback will have to copy its data into its own memory.
 * @Param:  - IN ix_uint32 arg_MsgLen - length of reply message.
 * @Return: IX_SUCCESS
 *          IX_CC_ERROR_NULL
 */
ix_error _ix_cc_stkdrv_cb_send_handler_msg(
                                           ix_error arg_Result,
                                           void* arg_pContext,
                                           void* arg_pMsg,
                                           ix_uint32 arg_MsgLen
                                           )
{
    ix_error err = IX_SUCCESS;

    _ix_cc_stkdrv_send_handler_msg_context* pContext = (_ix_cc_stkdrv_send_handler_msg_context*)arg_pContext;

    /* Check for valid context. */
    if(pContext == NULL)
        return IX_ERROR_WARNING(IX_CC_ERROR_NULL, ("Internal callback invoked with NULL context"));

    /* Call user's callback function. */
    IX_ERROR_C((pContext->pUserCallback)(arg_Result, pContext->pUserContext, arg_pMsg, arg_MsgLen), err);

    /* Free the msg memory, only if the msg support library is stubbed. */
#if defined(IX_CC_MSUP_STUB)
    if(arg_pMsg != NULL)
    {
        ix_ossl_free(arg_pMsg);
        arg_pMsg = NULL;
    }
#endif /* defined(IX_CC_MSUP_STUB) */

    return err;
}


/**
 * NAME: ix_cc_stkdrv_async_register_comm_handler
 *
 * DESCRIPTION: Function used to register a message to a communication handler
 *              asynchronously.  The comm handler must be in the same
 *              address space as the stack driver core component.
 *
 * @Param:  - IN ix_cc_stkdrv_cb_register_comm_handler arg_pCallback -
 *          pointer to the client's callback function to be invoked once
 *          the message returns.
 * @Param:  - IN void* arg_pContext - pointer to client context.
 * @Param:  - IN ix_cc_stkdrv_handler_id arg_handlerID - ID of the comm handler
 *          being registered.
 * @Param:  - IN void* arg_pInitContext - pointer to any user-defined context
 *          to be passed into the initialization routine.
 * @Param:  - IN ix_cc_stkdrv_handler_module_init_cb arg_initCB - callback function
 *          to initialize the comm handler.
 *
 * @Return: IX_SUCCESS
 *          IX_CC_ERROR_NULL
 *          IX_CC_ERROR_OOM_SYSTEM
 */
ix_error ix_cc_stkdrv_async_register_comm_handler(
                                                  ix_cc_stkdrv_cb_register_comm_handler arg_pCallback,
                                                  void* arg_pContext,
                                                  ix_cc_stkdrv_handler_id arg_handlerID,
                                                  void* arg_pInitContext,
                                                  ix_cc_stkdrv_handler_module_init_cb arg_initCB
                                                  )
{
    ix_error err = IX_SUCCESS;

    _ix_cc_stkdrv_register_comm_handler_context* pContext = NULL;
    _ix_cc_stkdrv_register_comm_handler_msg* pMsg = NULL;

    /* Check for invalid callback. */
    if(arg_pCallback == NULL)
        return IX_ERROR_WARNING(IX_CC_ERROR_NULL, ("NULL callback passed in"));

    /* Allocate memory for message structure. */
    pMsg = ix_ossl_malloc(sizeof(_ix_cc_stkdrv_register_comm_handler_msg));
    if(pMsg == NULL)
        return IX_ERROR_WARNING(IX_CC_ERROR_OOM_SYSTEM,
            ("Could not allocate memory for message to register comm handler"));

    /* Allocate memory for message support context. */
    pContext = ix_ossl_malloc(sizeof(_ix_cc_stkdrv_register_comm_handler_context));
    if(pContext == NULL)
    {
        err = IX_ERROR_WARNING(IX_CC_ERROR_OOM_SYSTEM,
            ("Could not allocate memory for context to register comm handler"));
        goto label_1;
    }

    /* Fill in message support context. */
    pContext->pUserCallback = arg_pCallback;
    pContext->pUserContext = arg_pContext;

    /* Fill in message. */
    pMsg->handlerID = arg_handlerID;
    pMsg->pInitContext = arg_pInitContext;
    pMsg->initCB = arg_initCB;

    /**
     * Send an asynchronous message to the Stack Driver to register
     * a comm handler.
     */
    IX_ERROR_CG(ix_cc_msup_send_async_msg(IX_CC_STKDRV_MSG_INPUT,
                                        (ix_cc_msghlp_callback)_ix_cc_stkdrv_cb_register_comm_handler,
                                        pContext,
                                        IX_CC_STKDRV_MSG_ID_REGISTER_HANDLER,
                                        pMsg,
                                        sizeof(_ix_cc_stkdrv_register_comm_handler_msg)), err, label_2);

    /* Free memory allocated to message structure. */
    ix_ossl_free(pMsg);
    pMsg = NULL;

    return err;

label_2:
    /* Free memory for message support context, only if an error has occurred. */
    ix_ossl_free(pContext);
    pContext = NULL;

label_1:
    /* Free memory allocated to message structure. */
    ix_ossl_free(pMsg);
    pMsg = NULL;

    return err;
}


/**
 * NAME: _ix_cc_stkdrv_register_comm_handler
 *
 * DESCRIPTION: Registers a comm handler with the stack driver core component.
 * 
 * @Param:  - IN ix_cc_stkdrv_handler_id arg_handlerID - ID of the comm handler
 *          being registered.
 * @Param:  - IN ix_cc_stkdrv_ctrl* arg_pStkdrvCtrl - pointer to stack driver
 *          control structure.
 * @Param:  - IN void* arg_pContext - pointer to any user-defined context
 *          to be passed into the initialization routine.
 * @Param:  - IN ix_cc_stkdrv_handler_module_init_cb arg_initCB - callback function
 *          to initialize the comm handler.
 * @Return: IX_SUCCESS
 *          IX_CC_ERROR_DUPLICATE_ENTRY
 *          IX_CC_ERROR_OOM_SYSTEM
 *          IX_CC_ERROR_NULL
 *          IX_CC_STKDRV_ERROR_HANDLER_INIT
 */
ix_error _ix_cc_stkdrv_register_comm_handler(
                                             ix_cc_stkdrv_handler_id arg_handlerID,
                                             ix_cc_stkdrv_ctrl* arg_pStkdrvCtrl,
                                             void* arg_pContext,
                                             ix_cc_stkdrv_handler_module_init_cb arg_initCB
                                             )
{
    ix_error err = IX_SUCCESS;

    ix_cc_stkdrv_handler_module* pHandlerModule;

    /* Ensure that we are not registering a handler that already exists. */
    /* Find the appropriate comm handler. */
    pHandlerModule = arg_pStkdrvCtrl->pHandlerList;
    _IX_CC_STKDRV_GET_HANDLER_MODULE(arg_handlerID, pHandlerModule);
    if(pHandlerModule != NULL)
        return IX_ERROR_WARNING(IX_CC_ERROR_DUPLICATE_ENTRY, ("Handler with ID %d already exists\n", arg_handlerID));

    /* Allocate memory for the handler module structure. */
    pHandlerModule = ix_ossl_malloc(sizeof(ix_cc_stkdrv_handler_module));
    if(pHandlerModule == NULL)
        return IX_ERROR_LOCAL(IX_CC_ERROR_OOM_SYSTEM, ("Could not allocate memory for handler module structure"));

    ix_ossl_memset(pHandlerModule, 0, sizeof(ix_cc_stkdrv_handler_module));

    /* Set the handler ID. */
    pHandlerModule->id = arg_handlerID;

    /* Allocate memory for handler function structure. */
    pHandlerModule->pHandler_func = ix_ossl_malloc(sizeof(ix_cc_stkdrv_handler_func));
    if(pHandlerModule->pHandler_func == NULL)
    {
        err = IX_ERROR_LOCAL(IX_CC_ERROR_OOM_SYSTEM, ("Could not allocate memory for handler module functions structure"));
        goto label_1;
    }
    ix_ossl_memset(pHandlerModule->pHandler_func, 0, sizeof(ix_cc_stkdrv_handler_func));

    /* Initialize the handler module, and register its callbacks. */
    IX_ERROR_CGT(arg_initCB(arg_pStkdrvCtrl->hFreeList, arg_pStkdrvCtrl->pFPList,
                 arg_pContext, pHandlerModule), err, IX_CC_STKDRV_ERROR_HANDLER_INIT,
                 IX_ERROR_LEVEL_LOCAL, label_2);

    /* Store pointer to this handler module in the list of handler modules. */
    pHandlerModule->pNextHandler = arg_pStkdrvCtrl->pHandlerList;
    arg_pStkdrvCtrl->pHandlerList = pHandlerModule;

    return err;

label_2:
    ix_ossl_free(pHandlerModule->pHandler_func);
label_1:
    ix_ossl_free(pHandlerModule);

    return err;
}


/**
 * NAME: _ix_cc_stkdrv_cb_register_comm_handler
 *
 * DESCRIPTION: This is the internal callback function for registering
 *              a comm handler asynchronously.
 * 
 * @Param:  - IN ix_error arg_Result - error code returned from
 *          the asynchronous registration operation.
 * @Param:  - IN void* arg_pContext - pointer to reply message
 *          context.
 * @Param:  - IN void* arg_pMsg - pointer to reply message.  This memory
 *          is only valid for the duration of this routine - the user
 *          callback will have to copy its data into its own memory.
 * @Param:  - IN ix_uint32 arg_MsgLen - length of reply message.
 * @Return: IX_SUCCESS
 *          IX_CC_ERROR_NULL
 */
ix_error _ix_cc_stkdrv_cb_register_comm_handler(
                                                ix_error arg_Result,
                                                void* arg_pContext,
                                                void* arg_pMsg,
                                                ix_uint32 arg_MsgLen
                                                )
{
    ix_error err = IX_SUCCESS;

    _ix_cc_stkdrv_register_comm_handler_context* pContext = (_ix_cc_stkdrv_register_comm_handler_context*)arg_pContext;

    /* Check for valid context. */
    if(pContext == NULL)
        return IX_ERROR_WARNING(IX_CC_ERROR_NULL, ("Internal callback invoked with NULL context"));

    /* Call user's callback function. */
    IX_ERROR_C((pContext->pUserCallback)(arg_Result, pContext->pUserContext), err);

    /* Free the msg memory, only if the msg support library is stubbed. */
#if defined(IX_CC_MSUP_STUB)
    if(arg_pMsg != NULL)
    {
        ix_ossl_free(arg_pMsg);
        arg_pMsg = NULL;
    }
#endif /* defined(IX_CC_MSUP_STUB) */

    return err;
}


#if (_IX_OS_TYPE_ == _IX_OS_WIN32)

/* Stub to allow Stack Driver to compile and link in Win32. */
ix_error ix_cc_stkdrv_vidd_init(
                                ix_buffer_free_list_handle arg_hFreeList,
                                ix_cc_stkdrv_fp_node* arg_pFP,
                                ix_cc_stkdrv_handler_module* arg_pHandlerModule
                                )
{
    arg_pHandlerModule->pNextHandler = NULL;
    arg_pHandlerModule->pHandler_func->fini = NULL;
    arg_pHandlerModule->pHandler_func->pFiniContext = NULL;
    arg_pHandlerModule->pHandler_func->pMsgIntContext = NULL;
    arg_pHandlerModule->pHandler_func->pMsgStrContext = NULL;
    arg_pHandlerModule->pHandler_func->pPktContext = NULL;
    arg_pHandlerModule->pHandler_func->receive_msg_int = NULL;
    arg_pHandlerModule->pHandler_func->receive_msg_str = NULL;
    arg_pHandlerModule->pHandler_func->receive_pkt = NULL;
    return IX_SUCCESS;
}

#endif /* (_IX_OS_TYPE_ != _IX_OS_VXWORKS_) */
