/**
 * ============================================================================
 * = 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_vidd.c
 *
 * = DESCRIPTION
 *      Source file for the local VIDD component of the Stack Driver.
 *
 * 
 *
 * = CHANGE HISTORY
 *       7/3/2002 - creation time
 *
 * ============================================================================
 */

#define IX_ERROR_FILE_IDENT "$Id: ix_cc_stkdrv_vidd.c,v 1.81 2003/11/01 03:25:16 rranjeet Exp $"

#include <ix_ossl.h>
#include <ix_rm.h>

#include "ix_cc.h"
#include "ix_cc_error.h"

#include "cc/ix_cc_stkdrv_common.h"

#include "ix_netmacros.h"

#if (_IX_OS_TYPE_ == _IX_OS_VXWORKS_)

#include "cc/ix_cc_stkdrv_vidd.h"

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

#define INCLUDE_NPT 1
#ifdef INCLUDE_NPT
/* NPT requires that we make this defined. */
#define END_MACROS
#include "endLib.h"

#endif /* INCLUDE_NPT */

/* Offset to IP ID field from IP header start in bytes. */
#define IP_HEADER_IPID 4

/* Offset to checksum field from IP header start in bytes. */
#define IP_HEADER_CSUM 10

/*#define STKDRV_IF_ADDR_ADD 1*/

/**
 * MACRO NAME: _IX_CC_STKDRV_VIDD_GET_PHYSICAL_IF
 *
 * DESCRIPTION: 
 *
 * @Param:  - IN arg_portID - ID of the desired port.
 * @Param:  - INOUT arg_pPhysicalIf - pointer to the list of
 *          ports to traverse.  If we find the desired
 *          port structure, it will be returned in this pointer, otherwise
 *          it will be NULL.
 *
 * @Return:
 */
#define _IX_CC_STKDRV_VIDD_GET_PHYSICAL_IF( \
                                          arg_portId, \
                                          arg_pPhysicalIf \
                                          ) \
    do \
    { \
        /* Traverse the list of ports to find the port with the given port ID. */ \
        while((arg_pPhysicalIf) != NULL) \
        { \
            if((arg_pPhysicalIf)->pPhysicalIfInfo->portId == (arg_portId)) \
                break; \
            (arg_pPhysicalIf) = (arg_pPhysicalIf)->pNextPhysicalIf; \
        } \
    } \
    while(0)

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

/**
 * Variable declarations global to this file only.  Externs are followed by
 * static variables.
 */
/****************************************************************************
 ****************************************************************************
 * Driver functions:
 *    These routines are provided to the kernel during registration.
 *    These routines are the same across all instances of the driver.
 ****************************************************************************/
static NET_FUNCS ix_cc_stkdrv_vidd_func_table = {
    (FUNCPTR)ix_cc_stkdrv_vidd_npt_start,         /* Function to start the device. */
    (FUNCPTR)ix_cc_stkdrv_vidd_npt_stop,          /* Function to stop the device. */
    (FUNCPTR)ix_cc_stkdrv_vidd_npt_unload,        /* Unloading function for the driver. */
    (FUNCPTR)ix_cc_stkdrv_vidd_npt_ioctl,         /* Ioctl function for the driver. */
    (FUNCPTR)ix_cc_stkdrv_vidd_npt_send,          /* Send function for the driver. */
    (FUNCPTR)ix_cc_stkdrv_vidd_npt_mCastAddrAdd,  /* Multicast address add */
    (FUNCPTR)ix_cc_stkdrv_vidd_npt_mCastAddrDel,  /* Multicast address delete */
    (FUNCPTR)ix_cc_stkdrv_vidd_npt_mCastAddrGet,  /* Multicast table retrieve */
    (FUNCPTR)ix_cc_stkdrv_vidd_npt_pollSend,      /* Polling send function for the driver. */
    (FUNCPTR)ix_cc_stkdrv_vidd_npt_pollRcv   /* Polling receive function for the driver. */
};

/****************************************************************************/

/**
 * Extern function prototypes.
 */
extern int ipAttach(int unit, char* pDevice);
/**
 * Static function prototypes.
 */
PRIVATE ix_error _ix_cc_stkdrv_vidd_init_mem(ix_uint32 bufferSize, ix_cc_stkdrv_vidd_ctrl* arg_pViddCtrl);
PRIVATE ix_error _ix_cc_stkdrv_vidd_morph_ip_id(char* arg_pHeader);
PRIVATE ix_error _ix_cc_stkdrv_vidd_cleanup(ix_uint32 arg_checkErrors, void** arg_ppViddCtrl);
ix_error _ix_cc_stkdrv_vidd_set_property(
                                         ix_uint32 arg_propID,
                                         ix_cc_properties* arg_pProperty,
                                         ix_cc_stkdrv_vidd_ctrl* arg_pViddCtrl
                                         );

/**
 * NAME: ix_cc_stkdrv_vidd_init
 *
 * DESCRIPTION: This function is called by the CC Module to initialize the
 * local VIDD and register the VIDD's entry points with the CC Module.
 * 
 * @Param:  - IN ix_buffer_free_list_handle arg_hFreeList - handle to
 *          freelist used to allocated buffers for passing packets down
 *          from the stack.
 * @Param:  - IN ix_cc_stkdrv_fp_node* arg_pFP - pointer to the FP structure
 *          used to get data for all ports.
 * @Param:  - IN void* arg_pInitData - pointer to any additional initialization
 *          data that may be required by a specific handler module - unused
 *          in this case.
 * @Param:  - INOUT ix_cc_stkdrv_handler_module* arg_pHandlerModule -
 *          the function will fill in this structure with its entry points
 *          and contexts.
 *
 * @Return: IX_SUCCESS
 *          IX_CC_ERROR_OOM_SYSTEM
 *          IX_CC_STKDRV_VIDD_ERROR_MUX
 */
ix_error ix_cc_stkdrv_vidd_init(
                                ix_buffer_free_list_handle arg_hFreeList,
                                ix_cc_stkdrv_fp_node* arg_pFP,
                                void* arg_pInitData,
                                ix_cc_stkdrv_handler_module* arg_pHandlerModule
                                )
{
    ix_error err = IX_SUCCESS;

    ix_cc_stkdrv_vidd_ctrl* pViddCtrl = NULL;
    ix_cc_stkdrv_vidd_fp_node* pFP = NULL;
    ix_cc_stkdrv_vidd_physical_if_node* pPhysicalIf = NULL;
    ix_cc_stkdrv_physical_if_node* pCurrPhysicalIf = NULL;

    void* pDevice = NULL;

    /* Finish filling in NET_FUNCS table - Compiler does not allow us to
     * initialize these members statically as the others are above.
     */
    ix_cc_stkdrv_vidd_func_table.formAddress = ix_cc_stkdrv_vidd_npt_formAddress;
    ix_cc_stkdrv_vidd_func_table.packetDataGet = NULL;
    ix_cc_stkdrv_vidd_func_table.addrGet = NULL;
    ix_cc_stkdrv_vidd_func_table.endBind = NULL;

#if (_IX_BOARD_TYPE_ != _IX_IXDP2401_) && (_IX_BOARD_TYPE_ != _IX_IXDP2801_)
    /* Bug IGK0100020231
     * Don't modify VxWorks variable 'max_linkhdr', it causes exeptions in the
     * VxWorks kernel IP stack, and the new coping loop in the send function
     * does not require it.
     */
/**
 * Global VxWorks variable representing the largest possible link layer header
 * in a buffer chain - not initialized in SIMNT since network support in
 * not included on SIMNT.
 */
#if !defined(IX_TARGET_SIMNTgnu_Debug) && !defined(IX_TARGET_SIMNTgnu)
    /**
     * Inform the stack to reserve 128 bytes at the beginning of buffer
     * chains to have enough room for an L2 header.
     */
    if (max_linkhdr < IX_CC_SOP_HW_BUFFER_PREPEND)
        max_linkhdr = IX_CC_SOP_HW_BUFFER_PREPEND;
#endif !defined(IX_TARGET_SIMNTgnu_Debug) && !defined(IX_TARGET_SIMNTgnu)
#endif /* _IX_BOARD_TYPE_ */

    /* Allocate memory for VIDD control structure. */
    pViddCtrl = ix_ossl_malloc(sizeof(ix_cc_stkdrv_vidd_ctrl));
    if (pViddCtrl == NULL)
    {
        return IX_ERROR_LOCAL(IX_CC_ERROR_OOM_SYSTEM, ("Could not allocate memory for VIDD control structure"));
    }
    ix_ossl_memset(pViddCtrl, 0, sizeof(ix_cc_stkdrv_vidd_ctrl));

    /* Allocate memory for and create the shared buffer pool for the driver. */
    {
        ix_buffer_free_list_info freelistInfo;
        IX_ERROR_CG(ix_rm_buffer_free_list_get_info(arg_hFreeList, &freelistInfo), err, label_1);
        IX_ERROR_CG(_ix_cc_stkdrv_vidd_init_mem(freelistInfo.m_DataElementSize, pViddCtrl), err, label_1);
    }

    /* Allocate memory for FP structure. */
    pViddCtrl->pFps = ix_ossl_malloc(sizeof(ix_cc_stkdrv_vidd_fp_node));
    pFP = pViddCtrl->pFps;
    if (pFP == NULL)
    {
        err = IX_ERROR_LOCAL(IX_CC_ERROR_OOM_SYSTEM, ("Could not allocate memory for FP structure"));
        goto label_1;
    }

    /* Fill in FP structure. */
    pFP->fpId = arg_pFP->fpId;
    pFP->pNextFp = NULL;
    pFP->numPorts = 0;
    pFP->pPhysicalIfs = NULL;

    /* Create physical interfaces for the FP. */
    pCurrPhysicalIf = arg_pFP->pPhysicalIfs;
    while(pCurrPhysicalIf != NULL)
    {
        /* Allocate memory for physical if structure. */
        pPhysicalIf = ix_ossl_malloc(sizeof(ix_cc_stkdrv_vidd_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_1;
        }
        ix_ossl_memset(pPhysicalIf, 0, sizeof(ix_cc_stkdrv_vidd_physical_if_node));

        /* Store a pointer to the VIDD control structure in the physical if structure. */
        pPhysicalIf->pViddCtrl = pViddCtrl;
        /* Store a pointer to the physical if info into the physical if structure. */
        pPhysicalIf->pPhysicalIfInfo = pCurrPhysicalIf->pPhysicalIfInfo;
        
        /* Register the physical if with the MUX. */
        pDevice = muxDevLoad(pPhysicalIf->pPhysicalIfInfo->portId, ix_cc_stkdrv_vidd_npt_load, NULL, 1, (void*)pPhysicalIf);
        if (pDevice == NULL)
        {
            err = IX_ERROR_LOCAL(IX_CC_STKDRV_VIDD_ERROR_MUX, ("Could not register physical interface with MUX"));
            goto label_1;
        }

        pPhysicalIf->pMuxCookie = pDevice;

        /* Set the netpool for this interface to the VIDD netpool. */
        pPhysicalIf->end.pNetPool = pViddCtrl->pNetPool;

        /* Fill in port-specific context for passing packets to the CC module. */
        pPhysicalIf->pCCPktContext = pCurrPhysicalIf;

        pPhysicalIf->pNextPhysicalIf = NULL;

        /* Link this physical interface into the FP's list. */
        if(pFP->pPhysicalIfs == NULL)
            pFP->pPhysicalIfs = pPhysicalIf;
        else
        {
            pPhysicalIf->pNextPhysicalIf = pFP->pPhysicalIfs;
            pFP->pPhysicalIfs = pPhysicalIf;
        }

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

        /* Bind this MUX device to the IP protocol. */
        {
            STATUS ret_status = OK;
            char* devName = IX_CC_STKDRV_DEV_NAME;
            char devUnit[IX_CC_STKDRV_DEV_NAME_LEN];
            ix_uint32 ipAddr;
            char ipAddrString[IX_CC_STKDRV_IP_ADDR_STR_LEN];
            
            ipAddr = IX_HTON32(pPhysicalIf->pPhysicalIfInfo->pVirtualIfs->ipProp.protocol.ipv4_prop.ipv4_address);

            sprintf(devUnit, "%s%lu", devName, pPhysicalIf->pPhysicalIfInfo->portId);

            sprintf(ipAddrString, "%lu.%lu.%lu.%lu", ipAddr >> 24, (ipAddr >> 16) & 0xFF,
                (ipAddr >> 8) & 0xFF, ipAddr & 0xFF);
            

            /* Attach this MUX device to the IP stack. */
            ret_status = ipAttach(pPhysicalIf->pPhysicalIfInfo->portId, IX_CC_STKDRV_DEV_NAME);
            if(ret_status != OK)
            {
                err = IX_ERROR_LOCAL(IX_CC_STKDRV_VIDD_ERROR_MUX, ("Could not attach to IP stack"));
                goto label_1;
            }
#if defined(STKDRV_IF_ADDR_ADD)
            /* Set the subnet mask for the device. */
            ifMaskSet(devUnit,
                pPhysicalIf->pPhysicalIfInfo->pVirtualIfs->ipProp.protocol.ipv4_prop.ipv4_subnet_mask);

            /* Set the IP address for this device. */
            ifAddrSet(devUnit, ipAddrString);
#else
            /* Add the IP address for this interface. */
            ret_status = ifAddrAdd(devUnit, ipAddrString, NULL,
                pPhysicalIf->pPhysicalIfInfo->pVirtualIfs->ipProp.protocol.ipv4_prop.ipv4_subnet_mask);
            if(ret_status != OK)
            {
                err = IX_ERROR_LOCAL(IX_CC_STKDRV_VIDD_ERROR_MUX, ("Could not add IP address"));
                goto label_1;
            }
#endif /* if defined(STKDRV_IF_ADDR_ADD) */
        }        

        /* Get the next physical interface in the list. */
        pCurrPhysicalIf = pCurrPhysicalIf->pNextPhysicalIf;
    }

    /**
     * Perform handler registration - fill in the arg_pHandlerModule structure -
     * fill in pHandler_func with the function pointers and contexts for the local VIDD.
     */
    arg_pHandlerModule->pHandler_func->receive_pkt = (ix_cc_stkdrv_packet_cb)ix_cc_stkdrv_vidd_receive_pkt;
    arg_pHandlerModule->pHandler_func->receive_msg_str = (ix_cc_stkdrv_msg_str_cb)NULL;
    arg_pHandlerModule->pHandler_func->pPktContext = (void*)pViddCtrl;
    arg_pHandlerModule->pHandler_func->pMsgStrContext = NULL;
    arg_pHandlerModule->pHandler_func->receive_msg_int = (ix_cc_stkdrv_msg_int_cb)ix_cc_stkdrv_vidd_receive_msg_int;
    arg_pHandlerModule->pHandler_func->pMsgIntContext = (void*)pViddCtrl;
    arg_pHandlerModule->pHandler_func->fini = (ix_cc_stkdrv_handler_module_fini_cb)ix_cc_stkdrv_vidd_fini;
    arg_pHandlerModule->pHandler_func->pFiniContext = pViddCtrl;

    /* Take the freelist handle and put it in the VIDD control structure. */
    pViddCtrl->hFreeList = arg_hFreeList;

    return err;

label_1:
    if(pPhysicalIf != NULL)
    {
        ix_ossl_free(pPhysicalIf);
        pPhysicalIf = NULL;
    }

    _ix_cc_stkdrv_vidd_cleanup(0, (void**)&pViddCtrl);

    return err;
}

/**
 * NAME: _ix_cc_stkdrv_vidd_init_mem
 *
 * DESCRIPTION: This routine allocates the single shared memory structure common
 *              to all instances of the driver.
 * 
 * @Param:  - IN ix_uint32 arg_bufferSize - size in bytes of each buffer
 *          contained in the requested buffer pool.
 * @Param:  - IN ix_cc_stkdrv_vidd_ctrl* arg_pViddCtrl - pointer to the VIDD
 *          control structure.
 * @Return: IX_SUCCESS
 *          IX_CC_ERROR_OOM_SYSTEM
 *          IX_CC_STKDRV_VIDD_ERROR_MUX
 */
ix_error _ix_cc_stkdrv_vidd_init_mem(
                                     ix_uint32 arg_bufferSize,
                                     ix_cc_stkdrv_vidd_ctrl* arg_pViddCtrl
                                     )
{

    ix_error err = IX_SUCCESS;

    /****************************************************************************
    * VxWorks network memory pool
    *
    * All instances of the driver share the memory buffers, this is done to
    * help minimize the total size of the pool while still having reasonable
    * response to bursts of packets.  This pool itself is maintained because
    * incoming packets must be copied so that the IP header can be properly
    * double-word aligned.
    *
    * The NULL and 0 fields in the following variables are filled in
    * later in this function.
    */
    M_CL_CONFIG mclConfig = {0, 0, NULL, 0};
    CL_DESC     clDescTbl[] = {
        /* 
        clusterSize      num                             memArea   memSize
        -----------      ----                            -------   -------
        */
        {arg_bufferSize, IX_CC_STKDRV_VIDD_NUM_ELEMENTS, NULL,   0}
    };

    int clDescTblNumElt = (NELEMENTS(clDescTbl));

    int lastAllocCnt; /* Do not change after the for() loop below */
    int totClBlkCnt = 0;

    /* Allocate memory for each of the cluster block sizes.
     * Each cluster is allocated to be of size:
     * (clusterSize + sizeof(long)) * # clusters
     * These cluster (memory regions) are stored in the array
     * clDescTbl.  See netBufLib for more information.
     * Upon exit from this loop:
     *   - clDescTbl[i] will contain the allocated memory
     *   - totClBlkCnt will contain the total number of clusters blocks
     *                 across all allocations
     *   - lastAllocCnt will contain the last index of clDescTbl
     *                  that successfully allocated
     */
    for (lastAllocCnt = 0; lastAllocCnt < clDescTblNumElt; lastAllocCnt++)
    {
        /**
         * Count the total # of blks so that we know how many mBlks
         * to allocate later
         */
        totClBlkCnt += clDescTbl[lastAllocCnt].clNum;

        /* Allocate the cluster */
        clDescTbl[lastAllocCnt].memSize = 
            clDescTbl[lastAllocCnt].clNum * 
            (clDescTbl[lastAllocCnt].clSize + sizeof(long));
        clDescTbl[lastAllocCnt].memArea = ix_ossl_malloc(clDescTbl[lastAllocCnt].memSize);
        if ((char*)clDescTbl[lastAllocCnt].memArea == NULL) {
            err = IX_ERROR_LOCAL(IX_CC_ERROR_OOM_SYSTEM, ("Could not allocate memory for VxWorks network buffer pool"));
            goto cleanupClusters;
        }
    }
    /* Store pointer to cluster memory area, to be freed on shutdown. */
    /* We assume that there is only one buffer size in the buffer pool. */
    arg_pViddCtrl->pClustMem = clDescTbl[0].memArea;
        
    /* Determine the mBlk appropriate parameters to pass to netPoolInit.
     * You have to have at least the same number of mBlks as you
     * do clusters.  You have to have 1:1 correspondence between clusters
     * and cluster blocks.
     * Each mBlk also requires memory, the size of which is
     *   M_BLK_SZ + sizeof(long)
     * Each cluster block requires memory, the size of which is
     *   CL_BLK_SZ
     */
    mclConfig.mBlkNum  = totClBlkCnt;  
    mclConfig.clBlkNum = totClBlkCnt; 
    mclConfig.memSize  = (mclConfig.mBlkNum * 
                              (M_BLK_SZ + sizeof(long))) +
                             (mclConfig.clBlkNum * CL_BLK_SZ);

    /* Allocate memory on a 4-byte alignment. */
    mclConfig.memArea = memalign(4, mclConfig.memSize);
    if(mclConfig.memArea == NULL)
    {
        err = IX_ERROR_LOCAL(IX_CC_ERROR_OOM_SYSTEM, ("Could not allocate memory for VxWorks network buffer pool"));
        goto cleanupMblk;
    }
    /* Store pointer to Mblk memory area, to be freed at shutdown. */
    arg_pViddCtrl->pMclBlkCfg = mclConfig.memArea;

    /* Create the shared pool. */
    arg_pViddCtrl->pNetPool = ix_ossl_malloc(sizeof(NET_POOL));
    if(arg_pViddCtrl->pNetPool == NULL)
    {
        err = IX_ERROR_LOCAL(IX_CC_ERROR_OOM_SYSTEM, ("Could not allocate memory for VxWorks network buffer pool structure"));
        goto cleanupMblk;
    }
    ix_ossl_memset(arg_pViddCtrl->pNetPool, 0, sizeof(NET_POOL));
    if(netPoolInit(arg_pViddCtrl->pNetPool, &mclConfig, &clDescTbl[0], clDescTblNumElt, NULL) != OK)
    {
        err = IX_ERROR_LOCAL(IX_CC_STKDRV_VIDD_ERROR_MUX,
                             ("Could not create VxWorks network buffer pool"));
        goto cleanupMblk;
    }
        
    return err;
    
cleanupMblk:
    ix_ossl_free(mclConfig.memArea);
    mclConfig.memArea = NULL;
    arg_pViddCtrl->pMclBlkCfg = NULL;
cleanupClusters:
    for (lastAllocCnt--; lastAllocCnt >= 0; lastAllocCnt--)
    {
        ix_ossl_free(clDescTbl[lastAllocCnt].memArea);
        clDescTbl[lastAllocCnt].memArea = NULL;
        arg_pViddCtrl->pClustMem = NULL;
    }

    ix_ossl_free(arg_pViddCtrl->pNetPool);
    arg_pViddCtrl->pNetPool = NULL;
    return err;
}


/**
 * NAME: ix_cc_stkdrv_vidd_fini
 *
 * DESCRIPTION: This function is called by the CC Module to shutdown the local
 * VIDD and free any memory allocated to it.
 * 
 * @Param:  - IN void* arg_pContext - pointer to context, in this case the VIDD
 *          control structure.
 *
 * @Return: IX_SUCCESS, or an appropriate error token for failure.
 */
ix_error ix_cc_stkdrv_vidd_fini(
                                void* arg_pContext
                                )
{
    return _ix_cc_stkdrv_vidd_cleanup(1, &arg_pContext);
}


/**
 * NAME: _ix_cc_stkdrv_vidd_cleanup
 *
 * DESCRIPTION: Cleans up VIDD memory and data structures created during
 *              initialiaztion.
 * 
 * @Param:  - IN ix_uint32 arg_checkErrors - 0 indicates the function will
 *          not check for errors.  1 indicates the function will check for
 *          errors.
 * @Param:  - INOUT void** arg_ppViddCtrl - pointer to location of VIDD
 *          control structure.
 *
 * @Return: IX_SUCCESS
 *          IX_CC_ERROR_NULL
 *          IX_CC_STKDRV_VIDD_ERROR_MUX
 */
ix_error _ix_cc_stkdrv_vidd_cleanup(
                                    ix_uint32 arg_checkErrors,
                                    void** arg_ppViddCtrl
                                    )
{

    STATUS ret_status = OK;

    char* devName = IX_CC_STKDRV_DEV_NAME;

    ix_cc_stkdrv_vidd_ctrl* pViddCtrl = (ix_cc_stkdrv_vidd_ctrl*)*arg_ppViddCtrl;
    ix_cc_stkdrv_vidd_physical_if_node* pPhysicalIf = NULL;
    ix_cc_stkdrv_vidd_physical_if_node* pNextPhysicalIf = NULL;

    /* Check for NULL control structure. */
    if(pViddCtrl == NULL)
    {
        return IX_ERROR_LOCAL(IX_CC_ERROR_NULL, ("VIDD cleanup called with NULL control structure"));
    }

    /* Iterate through the ports for each FP. */
    if(pViddCtrl->pFps != NULL)
    {
        pPhysicalIf = pViddCtrl->pFps->pPhysicalIfs;
        while(pPhysicalIf != NULL)
        {
            pNextPhysicalIf = pPhysicalIf->pNextPhysicalIf;

            /* Stop the driver for each port. */
            ret_status = muxDevStop(pPhysicalIf->pMuxCookie);
            if((ret_status != OK) && (arg_checkErrors))
            {
                return IX_ERROR_LOCAL(IX_CC_STKDRV_VIDD_ERROR_MUX,
                    ("MUX could not stop the driver"));
            }
            
            /* Unregister the IP addresses for this driver. */
            ret_status = ifRouteDelete(devName, pPhysicalIf->pPhysicalIfInfo->portId);
            if(ret_status == ERROR)
                return IX_ERROR_LOCAL(IX_CC_STKDRV_VIDD_ERROR_MUX,
                        ("Could not delete IP address for port %lu",
                        pPhysicalIf->pPhysicalIfInfo->portId));

            /* Unregister this driver from the MUX. */
            ret_status = muxDevUnload((char*)IX_CC_STKDRV_DEV_NAME,
                pPhysicalIf->pPhysicalIfInfo->portId);
            if((ret_status != OK) && (arg_checkErrors))
            {
                return IX_ERROR_LOCAL(IX_CC_STKDRV_VIDD_ERROR_MUX,
                    ("MUX could not unload the driver"));
            }

            pPhysicalIf = NULL;
            pViddCtrl->pFps->numPorts--;
            pPhysicalIf = pNextPhysicalIf;
        }
        pViddCtrl->pFps->pPhysicalIfs = NULL;
        ix_ossl_free(pViddCtrl->pFps);
        pViddCtrl->pFps = NULL;
    }
    if(pViddCtrl->pNetPool != NULL)
    {
        /* Free the buffer pool. */
        ret_status = netPoolDelete(pViddCtrl->pNetPool);
        if((ret_status != OK) && (arg_checkErrors))
        {
            return IX_ERROR_LOCAL(IX_CC_STKDRV_VIDD_ERROR_MUX,
                ("MUX could not delete the VIDD buffer pool"));
        }

        ix_ossl_free(pViddCtrl->pMclBlkCfg);
        pViddCtrl->pMclBlkCfg = NULL;
        ix_ossl_free(pViddCtrl->pClustMem);
        pViddCtrl->pClustMem = NULL;
        ix_ossl_free(pViddCtrl->pNetPool);
        pViddCtrl->pNetPool = NULL;
    }
    pViddCtrl->hFreeList = 0;
    ix_ossl_free(pViddCtrl);
    pViddCtrl = NULL;

    return IX_SUCCESS;
}
    

/**
 * NAME: ix_cc_stkdrv_vidd_npt_load
 *
 * DESCRIPTION: This function registers a port with the VxWorks MUX layer.
 * The user does not call this function directly - the VxWorks MUX layer uses it
 * as an entry point when adding a new physical interface.  This function
 * is implemented as a two-pass algorithm - the MUX layer calls this
 * function twice - once with an empty initialization string, and then with
 * the real initialization string.
 * 
 * @Param:  - IN void* pBsp - optional BSP-specific information.  This will
 *          be used as a context, in this case a pointer to the VIDD control
 *          structure.
 * @Param:  - INOUT char* initString - on the first pass of this function,
 *          initString is passed in as an empty allocated string, and the
 *          base name of the interface (e.g. "eth") is copied into it.  On
 *          the second pass, initString is NULL.
 *
 * @Return: Pointer to the END_OBJ structure contained in the VIDD interface data structure.
 *          NULL
 */
END_OBJ* ix_cc_stkdrv_vidd_npt_load(
                                    char* initString,
                                    void *pBsp
                                    )
{
    ix_cc_stkdrv_vidd_physical_if_node* pPhysicalIf = NULL;
    STATUS err = OK;

    ix_uint32 ifType = 0;

    /* Case 1 - return the name of the driver */
    if (initString != NULL)
    {
        if('\0' == initString[0])
        {
            ix_ossl_memcpy(initString, (char*)IX_CC_STKDRV_DEV_NAME, IX_CC_STKDRV_DEV_NAME_LEN);
            return (0);
        }
    }
    
    /* Case 2 - create a new instance of the driver */
    if (pBsp == NULL)
    {
        return NULL;
    }
    pPhysicalIf = (ix_cc_stkdrv_vidd_physical_if_node*)pBsp;

    /* Initialize the END parts of the driver structure. */
    err = END_OBJ_INIT(&pPhysicalIf->end, (DEV_OBJ*)pPhysicalIf, 
                     IX_CC_STKDRV_DEV_NAME,
                     pPhysicalIf->pPhysicalIfInfo->portId,
                     &ix_cc_stkdrv_vidd_func_table,
                     "Stack Driver VIDD");
    if(err != OK)
    {
        return NULL;
    }

    /* Initialize the MIB2 parts of the driver structure */
    switch(pPhysicalIf->pPhysicalIfInfo->mediaType)
    {
        case IX_MEDIA_TYPE_FAST_ETHERNET:
            ifType = M2_ifType_fastEther;
            break;
        case IX_MEDIA_TYPE_1GB_ETHERNET:
            ifType = M2_ifType_gigabitEthernet;
            break;
        case IX_MEDIA_TYPE_ATM :
            ifType = M2_ifType_atm;
            break;
        case IX_MEDIA_TYPE_POS:
            ifType = M2_ifType_sonet;
            break;
        default:
            ifType = M2_ifType_other;
    }

    switch(pPhysicalIf->pPhysicalIfInfo->mediaType)
    {
        case IX_MEDIA_TYPE_FAST_ETHERNET:
        case IX_MEDIA_TYPE_1GB_ETHERNET:
            err = END_MIB_INIT(&pPhysicalIf->end, 
                        ifType,
                        pPhysicalIf->pPhysicalIfInfo->pVirtualIfs->macAddr,
                        IX_CC_MAC_ADDR_LEN,
                        (ix_uint16)pPhysicalIf->pPhysicalIfInfo->MTU,
                        pPhysicalIf->pPhysicalIfInfo->linkSpeed*1000000);
            break;
        case IX_MEDIA_TYPE_ATM:
        case IX_MEDIA_TYPE_POS:
        default:
            err = END_MIB_INIT(&pPhysicalIf->end, 
                    ifType,
                    NULL,
                    0,
                    (ix_uint16)pPhysicalIf->pPhysicalIfInfo->MTU,
                    pPhysicalIf->pPhysicalIfInfo->linkSpeed*1000000);   
    }
    
    if(err != OK)
    {
        return NULL;
    }

    
    /* Set the flags to indicate readiness. */
    if(pPhysicalIf->pPhysicalIfInfo->physical_if_status == IX_CC_PHYSICAL_IF_STATUS_UP)
    {
        END_OBJ_READY(&pPhysicalIf->end,
                       (IFF_UP | IFF_RUNNING | IFF_NOTRAILERS | IFF_BROADCAST
                       | IFF_MULTICAST | IFF_NOARP) & (~IFF_PROMISC) & (~IFF_RFC2233));
    }
    else
    {
        END_OBJ_READY(&pPhysicalIf->end,
                       (~IFF_UP) & (IFF_RUNNING | IFF_NOTRAILERS | IFF_BROADCAST
                       | IFF_MULTICAST | IFF_NOARP) & (~IFF_PROMISC) & (~IFF_RFC2233));
    }
    

    return (&pPhysicalIf->end);    
}



/**
 * NAME: _ix_cc_stkdrv_vidd_free_cluster
 *
 * DESCRIPTION: Callback to free the ix_buffer associated with a data cluster.
 * 
 * @Param:  - IN int arg_hBuffer - handle to the ix_buffer to be freed.
 * @Param:  - IN int arg2 - unused.
 * @Param:  - IN int arg3 - unused.
 *
 * @Return: None.
 */
void _ix_cc_stkdrv_vidd_free_cluster(int arg_hBuffer, int arg2, int arg3)
{
    ix_rm_buffer_free((ix_buffer_handle)arg_hBuffer);

    return;
}


/**
 * NAME: _ix_cc_stkdrv_vidd_translate_ix_buffer
 *
 * DESCRIPTION: Translates an ix_buffer into a VxWorks network buffer (mBlk).
 * 
 * @Param:  - IN 
 *
 * @Return: IX_SUCCESS
 *          IX_CC_ERROR_INTERNAL
 *          IX_ERROR_OOM_SYSTEM
 */
ix_error _ix_cc_stkdrv_vidd_translate_ix_buffer(
                                                ix_hw_buffer_meta* arg_pMeta,
                                                NET_POOL_ID arg_pNetPool,
                                                M_BLK_ID* arg_ppFirstMblk,
                                                M_BLK_ID* arg_ppPrevMblk,
                                                M_BLK_ID* arg_ppCurrMblk,
                                                ix_buffer_handle* arg_pBufferHandle,
                                                ix_buffer_handle* arg_pNextBufferHandle,
                                                ix_uint32* arg_pIsEOP
                                                )
{
    ix_error err = IX_SUCCESS;
    void* pData;
    ix_uint16 bufferLength = IX_NTOH16(IX_RM_MEM_UINT16_READ(&arg_pMeta->m_BufferSize));

    /**
     * Check the alignment of the IP header in the ix_buffer.  Get the appropriate type
     * of VxWorks buffer to contain the packet data. If the header is not double-word
     * aligned, we must copy IP data from ix_buffer into the mBlk. This is a
     * necessary performance hit as VxWorks requires its network buffers to be
     * double-word aligned.
     */
    _IX_CC_STKDRV_DEBUG_ERROR_CRT(ix_cc_hw_get_packet_data(*arg_pBufferHandle, &pData),
                IX_CC_ERROR_INTERNAL, IX_ERROR_LEVEL_WARNING);

    #if (defined(IX_TARGET_SIMNTgnu_Debug) || defined(IX_TARGET_SIMNTgnu))
        /**
         * Get an mBlk-clMblk-cluster construct (VxWorks buffer) from the memory pool.
         * If we cannot get a construct, return IX_CC_ERROR_OOM_SYSTEM.
         */
        *arg_ppCurrMblk = netTupleGet(arg_pNetPool, bufferLength, M_DONTWAIT, MT_DATA, FALSE);
        if(*arg_ppCurrMblk == NULL)
            return IX_ERROR_WARNING(IX_CC_ERROR_OOM_SYSTEM, ("Could not get Mblk from VxWorks buffer pool"));

        /* Always do memcpy on VxSim. */
        ix_ossl_memcpy((*arg_ppCurrMblk)->mBlkHdr.mData, pData, bufferLength);

        _IX_CC_STKDRV_DEBUG_ERROR_CRT(ix_rm_buffer_get_next(*arg_pBufferHandle, arg_pNextBufferHandle),
            IX_CC_ERROR_INTERNAL, IX_ERROR_LEVEL_WARNING);

        _IX_CC_STKDRV_DEBUG_ERROR_CRT(ix_rm_buffer_is_eop(*arg_pBufferHandle, arg_pIsEOP),
            IX_CC_ERROR_INTERNAL, IX_ERROR_LEVEL_WARNING);

        /* Free the ix_buffer. */
        err = ix_rm_buffer_free(*arg_pBufferHandle);
        #if defined(IX_DEBUG)
            if(err != IX_SUCCESS)
                err = IX_ERROR_CHAIN(err, IX_CC_ERROR_INTERNAL, IX_ERROR_LEVEL_WARNING,
                    ("RM could not free the buffer with handle 0x%x\n",
                        *arg_pBufferHandle));
        #endif /* defined(IX_DEBUG) */
        *arg_pBufferHandle = IX_NULL_BUFFER_HANDLE;

    #else

        if(((ix_uint32)pData & 0x00000003) != 0)
        {

            /**
             * Get an mBlk-clMblk-cluster construct (VxWorks buffer) from the memory pool.
             * If we cannot get a construct, return IX_CC_ERROR_OOM_SYSTEM.
             */
            *arg_ppCurrMblk = netTupleGet(arg_pNetPool, (int)bufferLength, M_DONTWAIT, MT_DATA, FALSE);
            if(*arg_ppCurrMblk == NULL)
                return IX_ERROR_WARNING(IX_CC_ERROR_OOM_SYSTEM, ("Could not get Mblk from VxWorks buffer pool"));

            /* IP header is not double-word aligned, must copy data */
            ix_ossl_memcpy((*arg_ppCurrMblk)->mBlkHdr.mData, pData, bufferLength);

            _IX_CC_STKDRV_DEBUG_ERROR_CRT(ix_rm_buffer_get_next(*arg_pBufferHandle, arg_pNextBufferHandle),
                IX_CC_ERROR_INTERNAL, IX_ERROR_LEVEL_WARNING);

            _IX_CC_STKDRV_DEBUG_ERROR_CRT(ix_rm_buffer_is_eop(*arg_pBufferHandle, arg_pIsEOP),
                IX_CC_ERROR_INTERNAL, IX_ERROR_LEVEL_WARNING);

            /* Free the ix_buffer. */
            err = ix_rm_buffer_free(*arg_pBufferHandle);
            #if defined(IX_DEBUG)
                if(err != IX_SUCCESS)
                    err = IX_ERROR_CHAIN(err, IX_CC_ERROR_INTERNAL, IX_ERROR_LEVEL_WARNING,
                        ("RM could not free the buffer with handle 0x%x\n",
                            *arg_pBufferHandle));
            #endif /* defined(IX_DEBUG) */
            *arg_pBufferHandle = IX_NULL_BUFFER_HANDLE;
        }

        else
        {
            /* IP header is double-word aligned */


            CL_BLK_ID pClBlk = NULL;

            /**
             * Get an mBlk (VxWorks buffer descriptor) from the memory pool.
             * If we cannot get an mBlk, return IX_CC_ERROR_OOM_SYSTEM.
             */
            *arg_ppCurrMblk = netMblkGet(arg_pNetPool, M_DONTWAIT, MT_DATA);
            if(*arg_ppCurrMblk == NULL)
            {
                err = IX_ERROR_WARNING(IX_CC_ERROR_OOM_SYSTEM, ("Could not get Mblk from VxWorks buffer pool"));
                goto label_1;
            }

            /**
             * Get a clBlk (VxWorks cluster descriptor) from the memory pool.
             * If we cannot get a clBlks, return IX_CC_ERROR_OOM_SYSTEM.
             */
            pClBlk = netClBlkGet(arg_pNetPool, M_DONTWAIT);
            if(pClBlk == NULL)
            {
                err = IX_ERROR_WARNING(IX_CC_ERROR_OOM_SYSTEM, ("Could not get clBlk from VxWorks buffer pool"));
                netMblkFree(arg_pNetPool, *arg_ppCurrMblk);
                *arg_ppCurrMblk = NULL;
                goto label_1;
            }

            /* Join the packet data with the cluster descriptor. */
            pClBlk = netClBlkJoin(pClBlk, (char*)pData, (int)bufferLength,
                (FUNCPTR)_ix_cc_stkdrv_vidd_free_cluster,
                (int)*arg_pBufferHandle, 0, 0);

            #if defined(IX_DEBUG)
                if(pClBlk == NULL)
                {
                    err = IX_ERROR_WARNING(IX_CC_ERROR_OOM_SYSTEM,
                        ("Could not get join clBlk to ix_buffer data"));
                    netMblkFree(arg_pNetPool, *arg_ppCurrMblk);
                    goto label_1;
                }
            #endif /* defined(IX_DEBUG) */

            /* Join the clBlk to the mBlk. */
            *arg_ppCurrMblk = netMblkClJoin(*arg_ppCurrMblk, pClBlk);

            #if defined(IX_DEBUG)
                if(*arg_ppCurrMblk == NULL)
                {
                    err = IX_ERROR_WARNING(IX_CC_ERROR_OOM_SYSTEM,
                        ("Could not get join mBlk to clBlk"));
                    netClBlkFree(arg_pNetPool, pClBlk);
                    pClBlk = NULL;
                    *arg_pBufferHandle = IX_NULL_BUFFER_HANDLE;
                    return err;
                }
            #endif /* defined(IX_DEBUG) */

            _IX_CC_STKDRV_DEBUG_ERROR_CRT(ix_rm_buffer_get_next(*arg_pBufferHandle, arg_pNextBufferHandle),
                IX_CC_ERROR_INTERNAL, IX_ERROR_LEVEL_WARNING);

            _IX_CC_STKDRV_DEBUG_ERROR_CRT(ix_rm_buffer_is_eop(*arg_pBufferHandle, arg_pIsEOP),
                IX_CC_ERROR_INTERNAL, IX_ERROR_LEVEL_WARNING);

        }
    #endif /* (defined(IX_TARGET_SIMNTgnu_Debug) || defined(IX_TARGET_SIMNTgnu)) */

    if(*arg_ppPrevMblk == NULL)
    {
        /* Store pointer to first mBlk in possible chain. */
        *arg_ppFirstMblk = *arg_ppCurrMblk;
        /* Indicates that this is the first mBlk in the chain. */
        (*arg_ppCurrMblk)->mBlkHdr.mFlags |= M_PKTHDR;
        /* Represents entire packet length. */
        (*arg_ppCurrMblk)->mBlkPktHdr.len = IX_NTOH16(IX_RM_MEM_UINT16_READ(&arg_pMeta->m_PacketSize));
        /* Represents how much data is in this mBlk of the chain. */
        (*arg_ppCurrMblk)->mBlkHdr.mLen = bufferLength;
    }
    else
    {
        /* Indicates this is not the first mBlk in the chain. */
        (*arg_ppCurrMblk)->mBlkHdr.mFlags &= ~M_PKTHDR;
        /* Link this mBlk to the previous mBlk. */
        (*arg_ppPrevMblk)->mBlkHdr.mNext = *arg_ppCurrMblk;
        /* Represents how much data is in this mBlk of the chain. */
        (*arg_ppCurrMblk)->mBlkHdr.mLen = bufferLength;
    }
    
#if defined(IX_CC_STKDRV_DUMP_PACKET)
    {
        _ix_cc_stkdrv_dump_packet((*arg_ppCurrMblk)->mBlkHdr.mData,
                             (*arg_ppCurrMblk)->mBlkHdr.mLen, "VIDD receive:");
    }
#endif

    return err;

#if (!defined(IX_TARGET_SIMNTgnu_Debug) && !defined(IX_TARGET_SIMNTgnu))
    label_1:
        /* Free the ix_buffer. */
        ix_rm_buffer_free_chain(*arg_pBufferHandle);
        *arg_pBufferHandle = IX_NULL_BUFFER_HANDLE;

        return err;
#endif /* (!defined(IX_TARGET_SIMNTgnu_Debug) && !defined(IX_TARGET_SIMNTgnu)) */
}


/**
 * NAME: ix_cc_stkdrv_vidd_receive_pkt
 *
 * DESCRIPTION: This is the VIDD entry point (packet processing
 * callback invoked by the CC Module) that receives packets from
 * the CC Module and passes them to the local network stack.
 * 
 * @Param:  - IN ix_buffer_handle arg_hBuffer - handle to the input packet.
 * @Param:  - IN void* arg_pCtx - pointer to the context, in this case
 *          the VIDD control structure.
 * @Param:  - IN ix_cc_stkdrv_packet_type packetType - packet type.
 *          Ignored in the VIDD case.
 *
 * @Return: IX_SUCCESS
 *          IX_CC_ERROR_OOM_SYSTEM
 *          IX_CC_ERROR_ENTRY_NOT_FOUND
 *          IX_CC_ERROR_NULL
 *          IX_CC_ERROR_INTERNAL
 */
ix_error ix_cc_stkdrv_vidd_receive_pkt(
                                       ix_buffer_handle arg_hBuffer,
                                       void* arg_pCtx, 
                                       ix_cc_stkdrv_packet_type packetType
                                       )
{
    ix_error err = IX_SUCCESS;

    ix_hw_buffer_meta* pMeta;

    ix_cc_stkdrv_vidd_ctrl* pViddCtrl;
    ix_cc_stkdrv_vidd_physical_if_node* pPhysicalIf = NULL;

    M_BLK_ID pMblk = NULL;
    M_BLK_ID pCurrMblk = NULL;
    M_BLK_ID pPrevMblk = NULL;

    ix_buffer_handle hCurrBuffer = arg_hBuffer;
    ix_buffer_handle hNextBuffer = IX_NULL_BUFFER_HANDLE;

    ix_uint32 portId;

    ix_uint32 isEOP = 0;

    /*ix_ossl_message_log("VIDD got pkt from CC\n");*/

    /**
     * Extract the input port ID from the meta-data of arg_hBuffer. Each interface in
     * the VIDD is represented by an END_OBJ data structure.
     */
    _IX_CC_STKDRV_DEBUG_ERROR_CGT(ix_rm_buffer_get_meta(arg_hBuffer, (void*)&pMeta), err,
                IX_CC_ERROR_INTERNAL, IX_ERROR_LEVEL_WARNING, label_1);
    portId = (ix_uint32)IX_NTOH16(IX_RM_MEM_UINT16_READ(&pMeta->m_InputPort));


    /**
     * Extract the VIDD control structure from arg_pCtx and return IX_CC_ERROR_NULL if it is
     * invalid.
     */
    #if defined(IX_DEBUG)
        if(arg_pCtx == NULL)
        {
            err = IX_ERROR_WARNING(IX_CC_ERROR_NULL, ("NULL context passed in"));
            goto label_1;
        }
    #endif /* defined(IX_DEBUG) */
    pViddCtrl = (ix_cc_stkdrv_vidd_ctrl*)arg_pCtx;

    /**
     * Given the input port ID, look-up the END_OBJ data structure from the list of
     * interfaces contained in the VIDD control structure.
     */
    pPhysicalIf = pViddCtrl->pFps->pPhysicalIfs;
    _IX_CC_STKDRV_VIDD_GET_PHYSICAL_IF(portId, pPhysicalIf);
    #if defined(IX_DEBUG)
        if(pPhysicalIf == NULL)
        {
            err = IX_ERROR_WARNING(IX_CC_ERROR_ENTRY_NOT_FOUND, ("Port ID %d not found", portId));
            goto label_1;
        }
    #endif /* defined(IX_DEBUG) */

    /* Translate the ix_buffer into a VxWorks buffer. */
    IX_ERROR_CG(_ix_cc_stkdrv_vidd_translate_ix_buffer(
            pMeta, pViddCtrl->pNetPool, &pMblk, &pPrevMblk, &pCurrMblk, &hCurrBuffer, &hNextBuffer, &isEOP),
        err, label_1);

    /* Handle buffer chains. */
    while(!isEOP)
    {
        hCurrBuffer = hNextBuffer;
        hNextBuffer = IX_NULL_BUFFER_HANDLE;
        _IX_CC_STKDRV_DEBUG_ERROR_CGT(ix_rm_buffer_get_meta(hCurrBuffer, (void**)&pMeta),
            err, IX_CC_ERROR_INTERNAL, IX_ERROR_LEVEL_WARNING, label_1);

        /* Save the Mblk pointer to be linked to the next Mblk in the chain. */
        pPrevMblk = pCurrMblk;

        /* Translate the ix_buffer into a VxWorks buffer. */
        IX_ERROR_CG(_ix_cc_stkdrv_vidd_translate_ix_buffer(
                pMeta, pViddCtrl->pNetPool, &pMblk, &pPrevMblk, &pCurrMblk, &hCurrBuffer, &hNextBuffer, &isEOP),
            err, label_1);
    }
    
    /* Call the receive routine for the stack with a pointer to the mBlk chain. */
    TK_RCV_RTN_CALL(&pPhysicalIf->end, pMblk, 0, 0x800, 0, 0); 
    
    arg_hBuffer = IX_NULL_BUFFER_HANDLE;

    return err;

label_1:

    END_ERR_ADD(&pPhysicalIf->end, MIB2_IN_ERRS, +1);

    /* Free mBlk chain */
    if(pMblk != NULL)
    {
        netMblkClChainFree(pMblk);
    }

    return err;
}


/**
 * NAME: ix_cc_stkdrv_vidd_npt_send
 *
 * DESCRIPTION: The MUX interface calls this function when the network
 * protocol is sending a network packet. Network buffers are represented 
 * by an mBlk chain in VxWorks. This function takes the packet stored in
 * the mBlk, performs all necessary checks on the packet, and stores the
 * packet data in an ix_buffer_handle before calling the stack driver
 * core component API ix_cc_stkdrv_send_packet() function.
 * 
 * @Param:  - IN END_OBJ* pEnd - pointer to the END_OBJ structure.
 *          It identifies the transmit interface.
 * @Param:  - IN M_BLK_ID pMblk - mBlk chain containing the network
 *          buffer. The data represents the full link layer frame. 
 * @Param:  - IN char* dstMacAddr - destination MAC address from the
 *          OS stack. Ignored by this function as the IPv4 CC will do a
 *          route lookup to obtain the destination MAC address.
 * @Param:  - IN long netType - network service type.  Ignored by VIDD.
 * @Param:  - IN void* pSpare - optional network service data.  Ignored by VIDD.
 *
 * @Return: OK
 *          ERROR
 */
STATUS ix_cc_stkdrv_vidd_npt_send(
    END_OBJ	*pEnd,
    M_BLK_ID	pHeadMblk,
    char	*dstMacAddr,
    long	netType,
    void	*pSpare)
{
    ix_error	err = IX_SUCCESS;
    ix_uint32	bufMaxLen = 0;
    ix_uint32   mblkLen = 0;
    ix_uint32   lengthToCopy = 0;
    ix_uint32   firstBuffer = 1;
    ix_uint32   cellCount = 0;
    ix_uint32   spaceLeftInIxBuffer = 0;
    ix_uint16   pktLen = 0;
    ix_uint16   copiedLen = 0;

    ix_cc_stkdrv_vidd_physical_if_node* pPhysicalIf = (ix_cc_stkdrv_vidd_physical_if_node*)pEnd;
    ix_uint32                  portId = pPhysicalIf->pPhysicalIfInfo->portId;
    ix_buffer_free_list_handle hFreeList = pPhysicalIf->pViddCtrl->hFreeList;
    ix_buffer_free_list_info   freeListInfo;
    ix_buffer_handle           hHeadBuffer = IX_NULL_BUFFER_HANDLE;
    ix_buffer_handle           hPrevBuffer = IX_NULL_BUFFER_HANDLE;
    ix_buffer_handle           hBuffer = IX_NULL_BUFFER_HANDLE;
    M_BLK_ID                   pMblkIterator;
    ix_hw_buffer_meta          *pMeta;
    ix_uint8                   *pBufData;
    ix_uint8                   *pMblkData;
    ix_uint16                  temp;
    ix_uint16                  bufferLinkedToHead = 0;
    

    if (NULL == pHeadMblk) return ERROR;

    /*
     * If the packet is an IP packet, turn off the most significant bit in
     * the IP ID.
     */
    _ix_cc_stkdrv_vidd_morph_ip_id(pHeadMblk->mBlkHdr.mData);

    IX_ERROR_CGT(ix_rm_buffer_free_list_get_info(hFreeList, &freeListInfo),
		 err, IX_CC_ERROR_OOM, IX_ERROR_LEVEL_WARNING,
		 label_no_first_buffer);
    bufMaxLen = freeListInfo.m_DataElementSize;

    /* Set currently available buffer length to 0 to indicate nothing allocated */
    spaceLeftInIxBuffer = 0;
    pktLen = pHeadMblk->mBlkPktHdr.len;

    pMblkIterator = pHeadMblk;
    /* As long as there are mblks remaining on the chain, pack them into ixbuffs */
    while (NULL != pMblkIterator)
    {
        mblkLen = pMblkIterator->mBlkHdr.mLen;
        pMblkData = pMblkIterator->mBlkHdr.mData;
        /* Fill ix buffers with this mblk until they are full */
        while (mblkLen > 0)
        {
            /*
             * If there is no more space left in any previously alloced ix buf,
             * alloc a new one.
             */
            if (0 == spaceLeftInIxBuffer)
            {
                /*
                 * If there is a head buffer, then we need to do special
                 * processing for the next buffers.
                 */
                if (IX_NULL_BUFFER_HANDLE != hHeadBuffer)
                {
                    if (bufferLinkedToHead)
                    {
                        /*
                         * Since this is a third or later buffer, link against 
                         * the previous buffer
                         */
                        IX_ERROR_CGT(ix_rm_buffer_link(&hPrevBuffer, &hBuffer), err,
                                     IX_CC_ERROR_INTERNAL, IX_ERROR_LEVEL_WARNING,
                                     label_buffer_linking_error);
                    }
                    /*
                     * If only one buffer has been allocated, no
                     * linking is required.
                     */
                    if ((0 == firstBuffer) && (0 == bufferLinkedToHead))
                    {
                        IX_ERROR_CGT(ix_rm_buffer_link(&hHeadBuffer, &hBuffer), err,
                                     IX_CC_ERROR_INTERNAL, IX_ERROR_LEVEL_WARNING,
                                     label_buffer_linking_error);
                        bufferLinkedToHead = 1;
                    }
                    firstBuffer = 0;
                }
                hPrevBuffer = hBuffer;

                /*
                 * Need to allocate a new buffer.  If this is the first ix buf
                 * to be allocated, do special handling
                 */
                if (1 == firstBuffer)
                {
                    IX_ERROR_CGT(ix_rm_buffer_alloc(hFreeList, &hHeadBuffer),
                                 err, IX_CC_ERROR_OOM, IX_ERROR_LEVEL_WARNING,
                                 label_no_first_buffer);
                    hPrevBuffer = hBuffer = hHeadBuffer;
                    IX_ERROR_CGT(ix_rm_buffer_get_meta(hHeadBuffer, (void**)&pMeta),
                                 err, IX_CC_ERROR_INTERNAL, IX_ERROR_LEVEL_WARNING,
                                 label_buffer_packing_error);
                    temp = IX_RM_MEM_UINT16_WRITE(&(pMeta->m_Offset),
                                                  IX_HTON16((ix_uint16)IX_CC_SOP_HW_BUFFER_PREPEND));
                    IX_ERROR_CGT(ix_rm_buffer_get_data(hHeadBuffer, (void**)&pBufData),
                                 err, IX_CC_ERROR_INTERNAL, IX_ERROR_LEVEL_WARNING,
                                 label_buffer_packing_error);
                    spaceLeftInIxBuffer = bufMaxLen - IX_CC_SOP_HW_BUFFER_PREPEND;
                    pBufData += IX_CC_SOP_HW_BUFFER_PREPEND;
                    temp = IX_RM_MEM_UINT16_WRITE(&(pMeta->m_PacketSize),
                                                  IX_HTON16((ix_uint16)pktLen));  
                }
                else
                {
                    IX_ERROR_CGT(ix_rm_buffer_alloc(hFreeList, &hBuffer),
                                 err, IX_CC_ERROR_OOM, IX_ERROR_LEVEL_WARNING,
                                 label_buffer_packing_error);
                    IX_ERROR_CGT(ix_rm_buffer_get_meta(hBuffer, (void**)&pMeta),
                                 err, IX_CC_ERROR_INTERNAL, IX_ERROR_LEVEL_WARNING,
                                 label_buffer_linking_error);
                    temp = IX_RM_MEM_UINT16_WRITE(&(pMeta->m_Offset), 0);
                    IX_ERROR_CGT(ix_rm_buffer_get_data(hBuffer, (void**)&pBufData),
                                 err, IX_CC_ERROR_INTERNAL, IX_ERROR_LEVEL_WARNING,
                                 label_buffer_linking_error);
                    spaceLeftInIxBuffer = bufMaxLen;
                }
                temp = IX_RM_MEM_UINT16_WRITE(&(pMeta->m_InputPort), 
                                              IX_HTON16((ix_uint16)portId));
                temp = IX_RM_MEM_UINT32_WRITE(&pMeta->m_HwNext, IX_HTON32(0xff));
                copiedLen = 0;
            }
            lengthToCopy = (spaceLeftInIxBuffer > mblkLen) ? mblkLen : spaceLeftInIxBuffer;
            ix_ossl_memcpy(pBufData, pMblkData, lengthToCopy);
            spaceLeftInIxBuffer -= lengthToCopy;
            mblkLen -= lengthToCopy;
            pMblkData += lengthToCopy;
            pBufData += lengthToCopy;
            copiedLen += lengthToCopy;
            cellCount = ix_cc_calculate_cell_count(copiedLen, firstBuffer);
            temp = IX_RM_MEM_UINT16_WRITE(&(pMeta->m_BufferSize),
                                          IX_HTON16(copiedLen));
            /*
             * If this is the first ix buffer, set the cell count in
             * the hHeadBuffer handle, otherwise set the cell count in
             * the hBuffer handle.
             */
            if (1 == firstBuffer)
            {
                ix_rm_buffer_cell_count_set(&hHeadBuffer, cellCount);
                hBuffer = hHeadBuffer;
            }
            else
            {
                ix_rm_buffer_cell_count_set(&hBuffer, cellCount);
            }
        }
        /* Get the next mblk */
        pMblkIterator = pMblkIterator->mBlkHdr.mNext;
    }

    /*
     * If more than one ix buffer was allocated, 
     * link the last ix buffer.
     * Only link it if there is data in it.  There is a case where the last
     * mblk in the chain is zero that can cause us to alloc an extra ix buffer
     * if the next to lask mblk filled our previous ix buffer exactly.
     */
    if ((copiedLen > 0) && (0 == firstBuffer))
    {
        if (bufferLinkedToHead)
        {
            /*
             * Since this is a third or later buffer, link against 
             * the previous buffer
             */
             IX_ERROR_CGT(ix_rm_buffer_link(&hPrevBuffer, &hBuffer), err,
                          IX_CC_ERROR_INTERNAL, IX_ERROR_LEVEL_WARNING,
                          label_buffer_linking_error);
        }
        else
        {
             IX_ERROR_CGT(ix_rm_buffer_link(&hHeadBuffer, &hBuffer), err,
		          IX_CC_ERROR_INTERNAL, IX_ERROR_LEVEL_WARNING,
		          label_buffer_linking_error);
        }
    }
 
    /* Successfully set up ix buffer, free mblk and transmit ixbuff */
    netMblkClChainFree(pHeadMblk);

    /**
     * Send the packet to the CC side -
     * in case of errors, the CC will free the buffer, so no need for
     * error checking here.
     */
    ix_cc_stkdrv_send_packet(hHeadBuffer, pPhysicalIf->pCCPktContext);

    /* Return OK to the MUX. */
    return (OK);
label_buffer_linking_error:
    ix_rm_buffer_free(hBuffer);
label_buffer_packing_error:
    /* Problem packing the ixbufs, free the alloced ix buf chain */
    ix_rm_buffer_free_chain(hHeadBuffer);
label_no_first_buffer:
    /* No first buffer available, free the mblk chain */
    netMblkClChainFree(pHeadMblk);
    return (ERROR);
} /* ix_cc_stkdrv_vidd_npt_send() */


/**
 * NAME: _ix_cc_stkdrv_vidd_morph_ip_id
 *
 * DESCRIPTION: Turn off the most significant bit in the ip id field of
 * the ip header.  IPv4 CC is capable of generating ICMP messages. In such
 * situations we have to make sure that IP packets generated by TCP/IP
 * stack and those (ICMP packets) generated by the IPv4 CC have unique IP ID
 * in the IP Header.  We ensure this by making IPv4 generate IP packets
 * with IP IDs with the most significant bit turned on and we turn off the
 * msbit for packets generated by the TCP/IP stack.  The checksum is
 * calculated incrementally. See RFC 1624 for details. Reads and writes
 * from the IP header are done by byte in case the IP header is not aligned.
 * This also ensures that the operations are endian-agnostic.
 * 
 * @Param:  - IN char* arg_pHeader - pointer to the start of the IP header.
 *
 * @Return: IX_SUCCESS
 */
ix_error _ix_cc_stkdrv_vidd_morph_ip_id(
                                        char* arg_pHeader
                                        )
{
  char* pIpId;
  ix_uint16 ipid;
  ix_uint16 csum;
  char* pCsum;
  ix_uint8  new_ipid;
  ix_uint8  new_csum;
  char  temp;

  /* read ipid from the header. ipid will be in big endian form in the header */
  pIpId = arg_pHeader + IP_HEADER_IPID;

  ipid = ((ix_uint8)(*pIpId)) << 8;
  ipid |= (ix_uint8)(*(pIpId+1));

  /* 
   * We will guarantee that the msb will always be set to 0. 
   * If it's already set to 0, we don't have anything to do
   */
  if (ipid & 0x8000) {

    /* read current value of csum */
    pCsum = arg_pHeader + IP_HEADER_CSUM;

    csum = ((ix_uint8)(*pCsum)) << 8;
    csum |= (ix_uint8)(*(pCsum+1));
    
    new_ipid = ipid & 0x7FFF;   /* turn off msbit */

    /* see RFC 1624 for the equation */
    new_csum = ~(~csum + ~ipid + new_ipid);

    /* write new_ipid value back to the header */
    temp = (char)(new_ipid >> 8);
    *pIpId = temp;
    temp = (char)(new_ipid & 0xff );
    *(pIpId+1) = temp;

    /* write new_csum value back to the header */
    temp = (char)(new_csum >> 8);
    *pCsum = temp;
    temp = (char)(new_csum & 0xff );
    *(pCsum+1) = temp;
  }

  return IX_SUCCESS;
}


/**
 * NAME: ix_cc_stkdrv_vidd_npt_unload
 *
 * DESCRIPTION: This function is called by the MUX to "release" the device.
 * The function is called for each port that has been activated by call to
 * the ix_cc_stkdrv_vidd_npt_load().
 * 
 * @Param:  - IN END_OBJ* pEnd - Pointer to END_OBJ data structure
 *          allocated by the Load() function.
 *
 * @Return: OK
 */
STATUS ix_cc_stkdrv_vidd_npt_unload(
                                    END_OBJ* pEnd
                                    )
{
    ix_cc_stkdrv_vidd_physical_if_node* pPhysicalIf =
        (ix_cc_stkdrv_vidd_physical_if_node*)pEnd;

    END_FLAGS_CLR(&pPhysicalIf->end,
                   IFF_UP | IFF_RUNNING | IFF_NOTRAILERS | IFF_BROADCAST
                   | IFF_MULTICAST | IFF_NOARP);
    
    /* pPhysicalIf is freed as part of the call to END_OBJECT_UNLOAD. */
    END_OBJECT_UNLOAD(&pPhysicalIf->end);

#if (!defined(IX_TARGET_SIMNTgnu_Debug) && !defined(IX_TARGET_SIMNTgnu))
    /**
     * Delete the semaphore created by END_OBJ_INIT.
     * This code is not relevant when running on SIMNT because the semaphore
     * would not have been created in SIMNT, where no MUX layer is implemented.
     * May need to change to support hot-swappability.
     */
    if(pPhysicalIf->end.txSem != 0)
    {
        semTake(pPhysicalIf->end.txSem, WAIT_FOREVER);
        semDelete(pPhysicalIf->end.txSem);
        pPhysicalIf->end.txSem = 0;
    }
#endif /* (!defined(IX_TARGET_SIMNTgnu_Debug) && !defined(IX_TARGET_SIMNTgnu)) */

    return OK;
}

/**
 * NAME: ix_cc_stkdrv_vidd_npt_ioctl
 *
 * DESCRIPTION: The VIDD needs to support ioctl commands to keep
 * the ioctl interface with existing network protocols. The table
 * below gives the list of commonly-used ioctl commands.
 * Command          Function                        Data Type       Supported
 * --------------------------------------------------------------------------
 * SIOCGIFMTU       Get MTU.                        char*           Yes
 * EIOCSFLAGS       Set device flags                int             Yes
 * EIOCGFLAGS       Get device flags                int             Yes
 * EIOCSADDR        Set device address              char*           No
 * EIOCGADDR        Get device address              char*           Yes
 * EIOCMULTIADD     Add multicast address           char*           No
 * EIOCMULTIDEL     Delete multicast address        char*           No
 * EIOCMULTIGET     Get multicast list              MULTI_TABLE*    No
 * EIOCPOLLSTART    Set device into polling mode    NULL            No
 * EIOCPOLLSTOP     Set device into interrupt mode  NULL            No
 * EIOCGFBUF        Get minimum first buffer
 *                  for chaining                    int             Yes
 * EIOCGHDRLEN      Get the size of the data
 *                  link header                     int             Yes
 * EIOCGNPT         Query a driver to determine
 *                  whether it is a NPT driver      int             Yes
 * EIOCGMIB2233     retrieves the RFC2233
 *                  MIB II table                    M2_ID*          Yes
 * EIOCGMIB2        Get the MIB-II counters
 *                  from the driver                 char*           Yes
 *
 * 
 * @Param:  - IN END_OBJ* pEnd - Pointer to END_OBJ data structure
 *          allocated by the Load() function.
 * @Param:  - IN int command - ioctl command.
 * @Param:  - INOUT caddr_t buffer - character buffer holding response from the command.
 *
 * @Return: OK
 *          EINVAL
 */
int ix_cc_stkdrv_vidd_npt_ioctl(
                                END_OBJ* pEnd,
                                int command,
                                caddr_t buffer
                                )
{
    ix_int32 value;
    int retval = OK;

    /* Cast pEnd to ix_cc_stkdrv_vidd_physical_if. */
    ix_cc_stkdrv_vidd_physical_if_node* pPhysicalIf = (ix_cc_stkdrv_vidd_physical_if_node*)pEnd;

    /* Switch on command - for all unsupported cases listed above, return EINVAL. */
    switch(command)
    {
        /* For SIOCGIFMTU, copy the MTU from pEnd into buffer. */
        case SIOCGIFMTU:
            *((ix_uint16*)buffer) = pPhysicalIf->pPhysicalIfInfo->MTU;
            break;

        
       /**
        * For EIOCSFLAGS, set the END flags - this does not affect the
        * underlying hardware in this release. Use the set_property API
        * instead to bring an interface up or down.
        */
        case EIOCSFLAGS:
            value = (long) buffer;
            if (value < 0)
            {
                value -= value;
                value--;
                END_FLAGS_CLR(pEnd, value);
            }
            else
            {
                END_FLAGS_SET(pEnd, value);
            }
            break;
    
        /* For EIOCGFLAGS, set *buffer to END_FLAGS_GET (pEnd). */
        case EIOCGFLAGS:
            *((ix_uint32*)buffer) = END_FLAGS_GET(pEnd);
            break;
    
        /* For EIOCGADDR, copy MAC address from pEnd into buffer. */
        case EIOCGADDR:
            ix_ossl_memcpy(buffer, (char*)pPhysicalIf->pPhysicalIfInfo->pVirtualIfs->macAddr, IX_CC_MAC_ADDR_LEN);
            break;
        
        /* For EIOCGFBUF, set *buffer to the minimum buffer size used in the VxWorks network buffer pool. */
        case EIOCGFBUF:
            *((ix_int16*)buffer) = (ix_int16)IX_CC_STKDRV_MIN_BUFFER_SIZE;
            break;

        /* For EIOCGHDRLEN, set *buffer to the L2 header length. */
        case EIOCGHDRLEN:
            *buffer = IX_CC_MAC_ADDR_LEN;
            break;

        /* For EIOCGNPT, return OK. */
        case EIOCGNPT:
            break;

        /* For EIOCGMIB2233, set *buffer to the pointer to the MIB2 table stored in pEnd. */
        case EIOCGMIB2233:
            if ((buffer == NULL) || (pEnd->pMib2Tbl == NULL))
            {
               retval = EINVAL;
            }
            else
            {
               *((M2_ID **)buffer) = pEnd->pMib2Tbl;
            }
            break;

        /* For EIOCGMIB2, copy the MIB2 table pointer stored in pEnd into buffer. */
        case EIOCGMIB2:
            if(buffer == NULL)
            {
                retval = EINVAL;
            }
            ix_ossl_memcpy((char*)buffer, (char*)&pEnd->mib2Tbl, sizeof(pEnd->mib2Tbl));
            break;

        /* For unsupported ioctl's and the default case, return EINVAL. */
        case EIOCSADDR:
        case EIOCMULTIADD:
        case EIOCMULTIDEL:
        case EIOCMULTIGET:
        case EIOCPOLLSTART:
        case EIOCPOLLSTOP:
        default:
            retval = EINVAL;
    }

    return retval;
}


/**
 * NAME: ix_cc_stkdrv_vidd_npt_start
 *
 * DESCRIPTION: This function notifies the MUX that an interface is
 *              being brought up.
 * 
 * @Param:  - IN END_OBJ* pEnd - Pointer to END_OBJ data structure
 *          allocated by the Load() function.
 *
 * @Return: OK
 */
STATUS ix_cc_stkdrv_vidd_npt_start(
                                     END_OBJ* pEnd
                                     )
{
    ix_cc_stkdrv_vidd_physical_if_node* pPhysicalIf = (ix_cc_stkdrv_vidd_physical_if_node*)pEnd;
    pPhysicalIf->end.flags |= IFF_UP;
    /**
     * If the driver changes the state of the flags element, the driver
     * should export this change by calling muxError().
     */
    {
        END_ERR flagErr = {END_ERR_UP, NULL, NULL};
        muxError(pEnd, &flagErr);
    }
    

    return OK;
}

/**
 * NAME: ix_cc_stkdrv_vidd_npt_stop
 *
 * DESCRIPTION: Stop() notifies the MUX layer that an interface is
 *              being brought down.
 * 
 * @Param:  - IN END_OBJ* pEnd - Pointer to END_OBJ data structure
 *          allocated by the Load() function.
 * @Param:  - INOUT
 * @Param:  - OUT  
 *
 * @Return: OK
 */
STATUS ix_cc_stkdrv_vidd_npt_stop(
                                    END_OBJ* pEnd
                                    )
{
    ix_cc_stkdrv_vidd_physical_if_node* pPhysicalIf = (ix_cc_stkdrv_vidd_physical_if_node*)pEnd;
    pPhysicalIf->end.flags &= ~IFF_UP;
    
    /**
     * If the driver changes the state of the flags element, the driver
     * should export this change by calling muxError().
     */
    {
        END_ERR flagErr = {END_ERR_DOWN, NULL, NULL};
        muxError(pEnd, &flagErr);
    }

    return OK;
}

/**
 * NAME: ix_cc_stkdrv_vidd_npt_mCastAddrAdd
 *
 * DESCRIPTION: This function adds a new link-layer multicast address
 * to the table of multicast addresses for the IXP interface - in the
 * initial release this call does not affect the underlying hardware.
 * 
 * @Param:  - IN END_OBJ* pEnd - pointer to the END_OBJ structure to
 *          help identify the IXP port.
 * @Param:  - IN char* pAddress - physical address to add to the
 *          Multicast table.
 *
 * @Return: OK
 *          ERROR
 */
STATUS ix_cc_stkdrv_vidd_npt_mCastAddrAdd(
                                          END_OBJ * pEnd,
                                          char* pAddress
                                          )
{
    ix_cc_stkdrv_vidd_physical_if_node* pPhysicalIf = (ix_cc_stkdrv_vidd_physical_if_node*)pEnd;
    return etherMultiAdd(&pPhysicalIf->end.multiList, pAddress);
}

/**
 * NAME: ix_cc_stkdrv_vidd_npt_mCastAddrDel
 *
 * DESCRIPTION: The function removes previously added link-layer
 * multicast address - in the initial release this does not affect
 * the underlying hardware.
 * 
 * @Param:  - IN END_OBJ* pEnd - pointer to the END_OBJ structure to
 *          help identify the IXP port.
 * @Param:  - IN char* pAddress - physical address to delete from the
 *          Multicast table.
 *
 * @Return: OK
 *          ERROR
 */
STATUS ix_cc_stkdrv_vidd_npt_mCastAddrDel(
                                          END_OBJ * pEnd,
                                          char* pAddress
                                          )
{
    ix_cc_stkdrv_vidd_physical_if_node* pPhysicalIf = (ix_cc_stkdrv_vidd_physical_if_node*)pEnd;
    return etherMultiDel(&pPhysicalIf->end.multiList, pAddress);
}

/**
 * NAME: ix_cc_stkdrv_vidd_npt_mCastAddrGet
 *
 * DESCRIPTION: This function gets the list of all multicast
 * addresses that are active on the interface. 
 * 
 * @Param:  - IN END_OBJ* pEnd - pointer to the END_OBJ structure to
 *          help identify the IXP port.
 * @Param:  - IN MULTI_TABLE* pTable - pointer to the structure where
 *          the list will be put.
 *
 * @Return: OK
 *          ERROR
 */
STATUS ix_cc_stkdrv_vidd_npt_mCastAddrGet(
                                          END_OBJ * pEnd,
                                          MULTI_TABLE* pTable
                                          )
{
    ix_cc_stkdrv_vidd_physical_if_node* pPhysicalIf = (ix_cc_stkdrv_vidd_physical_if_node*)pEnd;
    return etherMultiGet(&pPhysicalIf->end.multiList, pTable);
}

/**
 * NAME: ix_cc_stkdrv_vidd_npt_pollSend
 *
 * DESCRIPTION: Polling-mode equivalent to the send() routine.
 * This routine should transfer frames directly to the output core
 * component or exit immediately if the core component is busy -
 * this is not supported in the initial release and always returns ERROR.
 * 
 * @Param:  - IN END_OBJ* pEnd - pointer to the END_OBJ structure. It
 *          identifies the transmit interface.
 * @Param:  - IN M_BLK_ID pMblk - mBlk chain containing the network
 *          buffer. The data represents the full link layer frame.
 * @Param:  - IN char* dstMacAddr - destination MAC address from the
 *          OS stack. Ignored by this function as the VIDD will do its
 *          own route table lookup to obtain the destination MAC address.
 * @Param:  - IN long netType - network service type.
 * @Param:  - IN void* pSpare - optional network service data.
 *
 * @Return: ERROR
 */
STATUS ix_cc_stkdrv_vidd_npt_pollSend(END_OBJ* pEND,
                                      M_BLK_ID pPkt,
                                      char* dstAddr,
                                      long netType,
                                      void * pSpareData
                                      )
{
    return ERROR;
}

/**
 * NAME: ix_cc_stkdrv_vidd_npt_pollRcv
 *
 * DESCRIPTION: The current implementation of core component model
 * does not support PollReceive().  Therefore this function always
 * returns an error.
 * 
 * @Param:  - IN END_OBJ* pEnd - pointer to the END_OBJ structure. It
 *          identifies the transmit interface.
 * @Param:  - IN M_BLK_ID pMblk - mBlk chain containing the network
 *          buffer. The data represents the full link layer frame.
 * @Param:  - IN long* pNetSvc - network service type.
 * @Param:  - IN long* pNetOffset - offset to network frame.
 * @Param:  - IN void* pSpare - optional network service data.
 *
 * @Return: OK
 *          ERROR
 */
STATUS ix_cc_stkdrv_vidd_npt_pollRcv(END_OBJ* pEND,
                                     M_BLK_ID pMblk,
                                     long* pNetSvc,
                                     long* pNetOffset, 
                                     void* pSpareData
                                     )
{
    return ERROR;
}

/**
 * NAME: ix_cc_stkdrv_vidd_npt_pollRcv
 *
 * DESCRIPTION: For Ethernet interfaces, the stack will attempt to
 *              prepend a MAC address to outgoing packets.  Thus this
 *              function just returns OK, because the MAC address
 *              will be formed by the uBlocks.
 * 
 * @Param:  - IN M_BLK_ID arg_pData - mBlk chain containing outgoing data.
 * @Param:  - IN M_BLK_ID arg_pSrc - source address, in an mBlk.
 * @Param:  - IN M_BLK_ID arg_pDst - destination address, in an mBlk.
 * @Param:  - IN BOOL arg_bool.
 *
 * @Return: NULL.
 */
M_BLK_ID ix_cc_stkdrv_vidd_npt_formAddress(
                                         M_BLK_ID arg_pData,
                                         M_BLK_ID arg_pSrc,
                                         M_BLK_ID arg_pDst,
                                         BOOL arg_bool
                                         )
{
    return NULL;
}


/**
 * NAME: ix_cc_stkdrv_vidd_receive_msg_int
 *
 * DESCRIPTION: This is the VIDD entry point (integer message processing
 * callback invoked by the CC Module) that receives integer messages
 * from the CC Module and processes the interface property updates
 * contained within those messages.
 * 
 * @Param:  - IN ix_cc_stkdrv_vidd_msg_id arg_MsgId - identification of the message.
 * @Param:  - IN ix_uint32 arg_msg - integer message value.
 * @Param:  - IN void *arg_pContext - pointer to the context.  Here the context
 *          is the VIDD control structure.
 * @Param:  - OUT void** arg_ppReplyMsg - location of pointer to reply message,
 *          if any.
 * @Param:  - OUT ix_uint32* arg_pReplyMsgSize - pointer to size of reply message,
 *          if any.
 *
 * @Return: IX_SUCCESS
 *          IX_CC_ERROR_NULL
 *          IX_CC_STKDRV_VIDD_ERROR_MUX
 *          IX_CC_ERROR_UNDEFINED_MSG
 */
ix_error ix_cc_stkdrv_vidd_receive_msg_int(
                                           ix_cc_stkdrv_vidd_msg_id arg_msgId,
                                           const ix_uint32 arg_msg,
                                           void *arg_pContext,
                                           void** arg_ppReplyMsg,
                                           ix_uint32* arg_pReplyMsgSize
                                           )
{
    ix_error err = IX_SUCCESS;

    ix_cc_stkdrv_vidd_ctrl* pViddCtrl = (ix_cc_stkdrv_vidd_ctrl*)arg_pContext;

    if((arg_pContext == NULL) || (arg_ppReplyMsg == NULL) || (arg_pReplyMsgSize == NULL))
        return IX_ERROR_WARNING(IX_CC_ERROR_NULL, ("VIDD int msg handler invoked with NULL context or reply message pointer"));


    /* Initialize reply message to be empty. */
    *arg_ppReplyMsg = NULL;
    *arg_pReplyMsgSize = 0;

    switch(arg_msgId)
    {
        case IX_CC_STKDRV_VIDD_MSG_ID_PROPERTY_UPDATE:
        {
            /**
             * Handle message to update port properties, make
             * kernel-specific calls to notify the kernel of these
             * property updates.
             */
            ix_cc_stkdrv_vidd_property_update_msg* pMsg = 
                (ix_cc_stkdrv_vidd_property_update_msg*)arg_msg;

            IX_ERROR_CR(_ix_cc_stkdrv_vidd_set_property(pMsg->propID, pMsg->pProperty, pViddCtrl));

            break;
        }
        
        default:
            return IX_ERROR_WARNING(IX_CC_ERROR_UNDEFINED_MSG, ("Invalid message ID %d for int msg to VIDD", arg_msgId));
    }

    return err;
}


ix_error _ix_cc_stkdrv_vidd_set_property(
                                         ix_uint32 arg_propID,
                                         ix_cc_properties* arg_pProperty,
                                         ix_cc_stkdrv_vidd_ctrl* arg_pViddCtrl
                                         )
{
    ix_cc_stkdrv_vidd_physical_if_node* pPhysicalIf;

    /* Get the port for this port ID. */
    pPhysicalIf = arg_pViddCtrl->pFps->pPhysicalIfs;
    _IX_CC_STKDRV_VIDD_GET_PHYSICAL_IF(arg_pProperty->port_id, pPhysicalIf);
    if(pPhysicalIf == NULL)
        return IX_ERROR_WARNING(IX_CC_ERROR_NULL, ("Port %d not found", arg_pProperty->port_id));


    /* Handle message to set interface status. */
    if(arg_propID & IX_CC_SET_PROPERTY_PHYSICAL_IF_STATUS)
    {
        if((arg_pProperty->physical_if_state == IX_CC_PHYSICAL_IF_STATUS_UP) &&
            (arg_pProperty->link_state == IX_CC_LINK_STATUS_UP))
            muxDevStart((void*)pPhysicalIf->pMuxCookie);
        else if(arg_pProperty->physical_if_state == IX_CC_PHYSICAL_IF_STATUS_DOWN)
            muxDevStop((void*)pPhysicalIf->pMuxCookie);
    }

    /* Handle message to set link status. */
    if(arg_propID & IX_CC_SET_PROPERTY_LINK_STATUS)
    {
        if((arg_pProperty->physical_if_state == IX_CC_PHYSICAL_IF_STATUS_UP) &&
            (arg_pProperty->link_state == IX_CC_LINK_STATUS_UP))
            muxDevStart((void*)pPhysicalIf->pMuxCookie);
        else if(arg_pProperty->physical_if_state == IX_CC_LINK_STATUS_DOWN)
            muxDevStop((void*)pPhysicalIf->pMuxCookie);
    }

    /* Handle message to add IPv4 address. */
    if(arg_propID & IX_CC_SET_PROPERTY_ADD_INTERFACE_IPV4)
    {
        STATUS ret_status = OK;
        char* devName = IX_CC_STKDRV_DEV_NAME;

        char devUnit[IX_CC_STKDRV_DEV_NAME_LEN];
        char ipAddrString[IX_CC_STKDRV_IP_ADDR_STR_LEN];
        ix_uint32 ipAddr;
#if defined(STKDRV_IF_ADDR_ADD)
        ret_status = ifRouteDelete(devName, arg_pProperty->port_id);
        if(ret_status == ERROR)
            return IX_ERROR_WARNING(IX_CC_STKDRV_VIDD_ERROR_MUX,
                    ("Could not delete IP address for port %lu",
                    arg_pProperty->port_id));

    
        ipAddr = IX_HTON32(pPhysicalIf->pPhysicalIfInfo->pVirtualIfs->ipProp.protocol.ipv4_prop.ipv4_address);
    
        sprintf(devUnit, "%s%lu", devName, pPhysicalIf->pPhysicalIfInfo->portId);
        sprintf(ipAddrString, "%lu.%lu.%lu.%lu", ipAddr >> 24, (ipAddr >> 16) & 0xFF,
            (ipAddr >> 8) & 0xFF, ipAddr & 0xFF);

        /* Set the subnet mask for the device. */
        ifMaskSet(devUnit,
            pPhysicalIf->pPhysicalIfInfo->pVirtualIfs->ipProp.protocol.ipv4_prop.ipv4_subnet_mask);

        /* Set the IP address for this device. */
        ifAddrSet(devUnit, ipAddrString);
#else
        ipAddr = IX_HTON32(arg_pProperty->ip_prop[0].protocol.ipv4_prop.ipv4_address);
    
        sprintf(devUnit, "%s%lu", devName, pPhysicalIf->pPhysicalIfInfo->portId);
        sprintf(ipAddrString, "%lu.%lu.%lu.%lu", ipAddr >> 24, (ipAddr >> 16) & 0xFF,
            (ipAddr >> 8) & 0xFF, ipAddr & 0xFF);

        /* Add the virtual interface. */
        ifMaskSet (devUnit, arg_pProperty->ip_prop[0].protocol.ipv4_prop.ipv4_subnet_mask);
        ret_status = ifAddrSet(devUnit, ipAddrString);
        if(ret_status == ERROR)
            return IX_ERROR_WARNING(IX_CC_STKDRV_VIDD_ERROR_MUX,
                    ("Could not add IP address for port %lu",
                    arg_pProperty->port_id));
        
#endif /* defined(STKDRV_IF_ADDR_ADD) */
    }

    /**
     * Handle message to delete IPv4 address - in this release
     * we maintain only 1 IP address per port.
     */
    if(arg_propID & IX_CC_SET_PROPERTY_DEL_INTERFACE_IPV4)
    {
        STATUS ret_status = OK;
        char* devName = IX_CC_STKDRV_DEV_NAME;

        char devUnit[IX_CC_STKDRV_DEV_NAME_LEN];
        char ipAddrString[IX_CC_STKDRV_IP_ADDR_STR_LEN];
        
        ix_uint32 ipAddr;

        ipAddr = IX_HTON32(arg_pProperty->ip_prop[0].protocol.ipv4_prop.ipv4_address);
    
        sprintf(devUnit, "%s%lu", devName, pPhysicalIf->pPhysicalIfInfo->portId);
        sprintf(ipAddrString, "%lu.%lu.%lu.%lu", ipAddr >> 24, (ipAddr >> 16) & 0xFF,
            (ipAddr >> 8) & 0xFF, ipAddr & 0xFF);

#if 0
        ret_status = ifRouteDelete(devName, arg_pProperty->port_id);
#else
        ret_status = ifAddrDelete(devUnit, ipAddrString);
#endif /* if 0 */

        if(ret_status == ERROR)
            return IX_ERROR_WARNING(IX_CC_STKDRV_VIDD_ERROR_MUX,
                    ("Could not delete IP address for port %lu",
                    arg_pProperty->port_id));
    }

    /* Handle message to set IPv4 broadcast address. */
    if(arg_propID & IX_CC_SET_PROPERTY_ADD_BROADCAST_IPV4)
    {
        char* devName = IX_CC_STKDRV_DEV_NAME;

        char devUnit[IX_CC_STKDRV_DEV_NAME_LEN];
        char ipAddrString[IX_CC_STKDRV_IP_ADDR_STR_LEN];
        ix_uint32 ipAddr;

        ipAddr = IX_HTON32(pPhysicalIf->pPhysicalIfInfo->pVirtualIfs->ipProp.protocol.ipv4_prop.ipv4_broadcast);
    
        sprintf(devUnit, "%s%lu", devName, pPhysicalIf->pPhysicalIfInfo->portId);
        sprintf(ipAddrString, "%lu.%lu.%lu.%lu", ipAddr >> 24, (ipAddr >> 16) & 0xFF,
            (ipAddr >> 8) & 0xFF, ipAddr & 0xFF);

        /* Set the IPv4 broadcast address for this device. */
        ifBroadcastSet(devUnit, ipAddrString);
    }

    return IX_SUCCESS;
}



#if defined(IX_TARGET_XSCALEgnube_Debug)
    /**
     * Debug function provided by WRS - can be used to print out statistics for the VxWorks
     * buffer pool of this driver - not officially supported by WRS.
     *
     */
    void endPoolShow(char* devName, int unit)
    {
        END_OBJ* pEnd;

        if((pEnd = endFindByName(devName, unit)) != NULL)
            netPoolShow(pEnd->pNetPool);

        return;
    }
#endif /* defined(IX_TARGET_XSCALEgnube_Debug) */

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