/**
 * ============================================================================
 * = COPYRIGHT
 *              INTEL CORPORATION PROPRIETARY INFORMATION
 *   This software is supplied under the terms of a license agreement or
 *   nondisclosure agreement with Intel Corporation and may not be copied 
 *   or disclosed except in accordance with the terms in that agreement.
 *      Copyright (C) 2000-2002 Intel Corporation.All rights reserved.
 *
 * = PRODUCT
 *      Intel(r) IXA SDK 3.0 for the IXP2000 Network Processor, Release 6
 *
 * = FILENAME
 *      ix_cc_stkdrv_tm.c
 *
 * = DESCRIPTION
 *      Source file for the Transport Module component of the Stack Driver.
 *
 * = AUTHOR
 *       Aaron Luk
 *       aaron.luk@intel.com
 *
 * = CHANGE HISTORY
 *      11/24/2002 11:03:47 PM - creation time
 *
 * ============================================================================
 */

#define IX_ERROR_FILE_IDENT "$Id: ix_cc_stkdrv_tm.c,v 1.22 2003/11/18 18:42:43 ktseng Exp $"

#if defined(IX_PLATFORM_2401) || defined(IX_PLATFORM_2801)
/* Improvement
 * Cleanup in headers - don't include user space stdio.h, use <> when including
 * headers from global include paths. The string.h is excessive here, OSSL
 * covers this.
 */
#include <ix_ossl.h>
#include <ix_rm.h>

#include <ix_cc_error.h>

#else /* IX_PLATFORM_2x01 */

#include "ix_cc_error.h"
#include <stdio.h>


#if (_IX_OS_TYPE_ != _IX_OS_LINUX_KERNEL_)
#include <string.h>
#endif

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

#endif /* IX_PLATFORM_2x01 */

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

#include "ix_cc.h"
#include "cc/ix_cc_stkdrv_common.h"
#include "cc/ix_cc_stkdrv.h"
#include "cc/ix_cc_stkdrv_pktclass.h"
#include "cc/ix_cc_msup.h"
#include "bindings.h"
#include "ix_netmacros.h"

#include "cc/internal/internal_stkdrv.h"

#if defined(IX_CC_STKDRV_TRANSPORT_MODULE)

#include "cc/ix_cc_stkdrv_tm.h"

/**
 * NAME: ix_cc_stkdrv_tm_init
 *
 * DESCRIPTION: This function is called by the CC Module to initialize the
 * Transport Module and register the Transport Module'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 FP Module.
 * @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 - 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_error ix_cc_stkdrv_tm_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_cc_stkdrv_tm_ctrl* pTMCtrl = NULL;

    ix_error err = IX_SUCCESS;
    
    /* Allocate memory for TM control structure. */
    pTMCtrl = ix_ossl_malloc(sizeof(ix_cc_stkdrv_tm_ctrl));
    if (pTMCtrl == NULL)
    {
        return IX_ERROR_LOCAL(IX_CC_ERROR_OOM_SYSTEM, ("Could not allocate memory for TM control structure"));
    }
    ix_ossl_memset(pTMCtrl, 0, sizeof(ix_cc_stkdrv_tm_ctrl));

    /* Fill in free list handle. */
    pTMCtrl->hFreeList = arg_hFreeList;

    /* Fill in FP pointer. */
    pTMCtrl->pFP = arg_pFP;

    /**
     * Perform handler registration - fill in the arg_pHandlerModule structure -
     * fill in pHandler_func with the function pointers and contexts for the TM.
     */
    arg_pHandlerModule->pHandler_func->receive_pkt =
        (ix_cc_stkdrv_packet_cb)ix_cc_stkdrv_tm_receive_pkt;
    arg_pHandlerModule->pHandler_func->receive_msg_str = (ix_cc_stkdrv_msg_str_cb)NULL;
    arg_pHandlerModule->pHandler_func->pPktContext = (void*)pTMCtrl;
    arg_pHandlerModule->pHandler_func->pMsgStrContext = NULL;
    arg_pHandlerModule->pHandler_func->receive_msg_int =
        (ix_cc_stkdrv_msg_int_cb)ix_cc_stkdrv_tm_receive_msg_int;
    arg_pHandlerModule->pHandler_func->pMsgIntContext = (void*)pTMCtrl;
    arg_pHandlerModule->pHandler_func->fini =
        (ix_cc_stkdrv_handler_module_fini_cb)ix_cc_stkdrv_tm_fini;
    arg_pHandlerModule->pHandler_func->pFiniContext = pTMCtrl;

    #if !defined(IX_EXCLUDE_CCI)
    {
        ix_exe_handle hExecEngine;
        ix_uint32 eeNum;
        void* pEEContext;

        ix_cc_handle hTMCC;

        /* Get information about the current execution engine. */
        IX_ERROR_CG(ix_cci_exe_get_info(&hExecEngine, &eeNum, &pEEContext), err, label_1);

        /* Initialize core component-portion of the transport module. */
        IX_ERROR_CG(ix_cci_cc_create(hExecEngine, ix_cc_stkdrv_tm_cc_init,
            ix_cc_stkdrv_tm_cc_fini, (void*)pTMCtrl, &hTMCC), err, label_1);
    
        /* Set handle to TM core component in the TM control structure. */
        pTMCtrl->hTMCC = hTMCC;
    }
    #else
        /* Initialize core component-portion of the transport module. */
        IX_ERROR_CG(ix_cc_stkdrv_tm_cc_init(0, &pTMCtrl), err, label_1);
    
        /* Set handle to TM core component in the TM control structure. */
        pTMCtrl->hTMCC = 0;
    #endif /* !defined(IX_EXCLUDE_CCI) */

    return IX_SUCCESS;

label_1:
    ix_ossl_free(pTMCtrl);
    pTMCtrl = NULL;
    return err;
}

/**
 * NAME: ix_cc_stkdrv_tm_fini
 *
 * DESCRIPTION: This function is called by the CC Module to shutdown the
 * Transport Module and free any memory allocated to it.
 * 
 * @Param:  - IN ix_cc_stkdrv_tm_ctrl* arg_pTMCtrl - pointer to TM
 *          control structure.
 *
 * @Return: IX_SUCCESS

 */
ix_error ix_cc_stkdrv_tm_fini(
                              ix_cc_stkdrv_tm_ctrl* arg_pTMCtrl
                              )
{
    /* Ensure TM control structure is not NULL. */
    if(arg_pTMCtrl == NULL)
        return IX_ERROR_LOCAL(IX_CC_ERROR_NULL, ("TM fini invoked with NULL context"));

    #if !defined(IX_EXCLUDE_CCI)
        /* Finalize core component portion of the transport module. */
        ix_cci_cc_destroy(arg_pTMCtrl->hTMCC);
    #else
        /* Finalize core component portion of the transport module. */
        ix_cc_stkdrv_tm_cc_fini(0, arg_pTMCtrl);
    #endif /* !defined(IX_EXCLUDE_CCI) */

    /* Free memory allocated to the TM control structure. */
    ix_ossl_free(arg_pTMCtrl);
    arg_pTMCtrl = NULL;

    return IX_SUCCESS;
}

/**
 * NAME: ix_cc_stkdrv_tm_receive_msg_int
 *
 * DESCRIPTION: This is the TM entry point (integer message processing
 * callback invoked by the CC Module) that receives integer messages
 * from the CC Module.
 * 
 * @Param:  - IN ix_cc_stkdrv_tm_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 TM 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_UNDEFINED_MSG
 */
ix_error ix_cc_stkdrv_tm_receive_msg_int(
                                         ix_cc_stkdrv_tm_msg_id arg_msgId,
                                         const ix_uint32 arg_msg,
                                         void* arg_pContext,
                                         void** arg_ppReplyMsg,
                                         ix_uint32* arg_pReplyMsgSize
                                         )
{
    ix_error err = IX_SUCCESS;

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

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

    return err;
}

/**
 * NAME: ix_cc_stkdrv_tm_receive_pkt
 *
 * DESCRIPTION: This is the TM entry point (packet processing
 * callback invoked by the CC Module) that receives packets from
 * the CC Module and passes them to the FP Module.
 * 
 * @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
 *          pointer to the TM control structure.
 * @Param:  - IN ix_cc_stkdrv_packet_type arg_packetType - packet type.
 *          Will be passed on to the FP Module's receive routine.
 *
 * @Return: IX_SUCCESS
 *          IX_CC_ERROR_NULL
 *          IX_CC_ERROR_SEND_FAIL
 */
ix_error ix_cc_stkdrv_tm_receive_pkt(
                                     ix_buffer_handle arg_hBuffer,
                                     void* arg_pCtx,
                                     ix_cc_stkdrv_packet_type arg_packetType
                                     )
{

    /* Send the packet to the FP Module core component. */
    IX_ERROR_CRT(ix_rm_packet_send(IX_CC_FP_MODULE_PKT_INPUT,
        arg_hBuffer, (ix_uint32)arg_packetType),
        IX_CC_ERROR_SEND_FAIL, IX_ERROR_LEVEL_WARNING);
    
    return IX_SUCCESS;
}

/**
 * NAME: ix_cc_stkdrv_tm_cc_init
 *
 * DESCRIPTION: Initializes the TM core component.
 * 
 * @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 will be stored.
 *
 * @Return: IX_SUCCESS
 *          IX_CC_STKDRV_ERROR_CCI_ADD_HANDLER
 *          IX_CC_STKDRV_ERROR_RM_ADD_HANDLER
 */
ix_error ix_cc_stkdrv_tm_cc_init(
                                 ix_cc_handle arg_hCC,
                                 void** arg_ppContext
                                 )
{
    ix_error err = IX_SUCCESS;

#if !defined(IX_EXCLUDE_CCI)
    /* Register the packet and message handlers with the CCI. */
    IX_ERROR_CRT(ix_cci_cc_add_packet_handler(arg_hCC,
                    (ix_uint32)IX_CC_STKDRV_TM_PKT_INPUT,
                    (ix_pkt_handler)ix_cc_stkdrv_tm_pkt_handler,
                    IX_INPUT_TYPE_SINGLE_SRC), 
                    IX_CC_STKDRV_ERROR_CCI_ADD_HANDLER, 
                    IX_ERROR_LEVEL_LOCAL);

#else

    /* Register the packet and message handlers with the RM communication. */
    IX_ERROR_CRT(ix_rm_packet_handler_register(IX_CC_STKDRV_TM_PKT_INPUT,
                    (ix_comm_data_handler)ix_cc_stkdrv_tm_pkt_handler,
                    *arg_ppContext),
                    IX_CC_STKDRV_ERROR_RM_ADD_HANDLER, 
                    IX_ERROR_LEVEL_LOCAL);
    
#endif /* end !defined(IX_EXCLUDE_CCI) */

    return err;
}

/**
 * NAME: ix_cc_stkdrv_tm_cc_fini
 *
 * DESCRIPTION: Termination function. Frees transport module 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.
 * @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_tm_cc_fini(
                                 ix_cc_handle arg_hCC,
                                 void* arg_pContext
                                 )
{
    /* Remove packet handler. */
#if !defined(IX_EXCLUDE_CCI)
    IX_ERROR_CRT(ix_cci_cc_remove_packet_handler(IX_CC_STKDRV_TM_PKT_INPUT),
        IX_CC_STKDRV_ERROR_CCI_REMOVE_HANDLER, IX_ERROR_LEVEL_LOCAL);
#else
    IX_ERROR_CRT(ix_rm_packet_handler_unregister(IX_CC_STKDRV_TM_PKT_INPUT),
        IX_CC_STKDRV_ERROR_RM_REMOVE_HANDLER, IX_ERROR_LEVEL_LOCAL);
#endif /* end !defined(IX_EXCLUDE_CCI) */
    
    return IX_SUCCESS;
}

/**
 * NAME: ix_cc_stkdrv_tm_pkt_handler
 *
 * DESCRIPTION: Packet processing function for transport module.
 * 
 * @Param:  - IN ix_buffer_handle arg_hDataToken - handle to a buffer which
 *          contains exception packets.
 * @Param:  - IN ix_uint32 arg_userData - User data for the packet.
 *          Represents output port ID in this case.
 * @Param:  - IN void* arg_pComponentContext - pointer to TM core
 *          component context.
 * @Return: IX_SUCCESS, or an appropriate error code on failure.
 */
ix_error ix_cc_stkdrv_tm_pkt_handler(
                                     ix_buffer_handle arg_hDataToken,
                                     ix_uint32 arg_userData,
                                     void* arg_pComponentContext
                                     )
{
    /*ix_cc_stkdrv_tm_ctrl* pTMCtrl = (ix_cc_stkdrv_tm_ctrl*)arg_pComponentContext;*/
    ix_cc_stkdrv_ctrl* pTMCtrl = (ix_cc_stkdrv_ctrl*)arg_pComponentContext;
    ix_cc_stkdrv_physical_if_node* pPhysicalIf = NULL;

    /* Find port with the given port ID. */
    pPhysicalIf = pTMCtrl->pFPList->pPhysicalIfs;
    _IX_CC_STKDRV_GET_PHYSICAL_IF(arg_userData, pPhysicalIf);

    if(pPhysicalIf == NULL)
    {
        ix_rm_buffer_free_chain(arg_hDataToken);
        return IX_ERROR_WARNING(IX_CC_ERROR_ENTRY_NOT_FOUND,
            ("Port with ID %d not found\n", arg_userData));
    }

    /**
     * Send the packet to the CC side - 
     * in case of errors, the CC will free the buffer, so no need for
     * error cleanup here.
     */
#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_ERROR_CR(ix_cc_stkdrv_send_packet(arg_hDataToken, (void*)pPhysicalIf,
		                     IX_CC_STKDRV_OG_PKT_TYPE_IPV4));
#else
    IX_ERROR_CR(ix_cc_stkdrv_send_packet(arg_hDataToken, (void*)pPhysicalIf));
#endif

    return IX_SUCCESS;
}

#endif /* defined(IX_CC_STKDRV_TRANSPORT_MODULE) */
