/**
 * ============================================================================
 * = 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 for the IXP2X00 Network Processor
 *
 * = FILENAME
 *      ix_cc_qm.c
 *
 * = DESCRIPTION
 *         The file contains API functions exposed by QM Core Component
 *
 * = AUTHOR
 *      Herman Zaks
 *      Herman.Zaks@intel.com
 *
 **/

#define IX_ERROR_FILE_IDENT "$Id: ix_cc_qm.c,v 1.41 2003/12/12 21:33:08 ktseng Exp $"

#include "ix_cc_error.h"
#include "ix_types.h"
#include "ix_error.h"
#include "ix_rm.h"

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

#include "ix_netmacros.h"
#include "ix_cc.h"
#include "cc/ix_cc_msup.h"
#include "cc/ix_cc_qm.h"
#include "cc/internal/ix_cc_qm_internal.h"
#include "cc/ix_cc_qm_frmwrk.h"

#include "bindings.h"

#if IX_QM_TEST /* export free list handle to be used by test app*/ 
    ix_buffer_free_list_handle g_hFreeList;
#endif

#ifndef CCI_MULTIINSTANCE

IX_CC_QM_INSTANCE_TABLE_DEFINE()    
IX_CC_QM_INSTANCE_ENTRY_DEFINE(IX_CC_QM_MSG_INPUT,IX_CC_QM_COMMON_PKT_INPUT,IX_CC_QM_EGRESS_PKT_INPUT,IX_CC_QM_EGRESS_PKT_OUTPUT,IX_CC_QM_UBLOCK_PKT_OUTPUT)
IX_CC_QM_INSTANCE_TABLE_END

#else

IX_CC_QM_INSTANCE_TABLE_DECLARE()

#endif

 /**
 * NAME: ix_cc_qm_init()
 *
 * DESCRIPTION: Initialization function invoked by the Execution Engine of the CCI. 
 *              This function should be called only once to initialize QM component.  This function 
 *              performs allocation of memory for symbols to be patched to QM microblock,
 *              registration of packet and message handlers, allocation and initialization 
 *              of internal data structures
 * 
 * @Param:  -   IN  ix_cc_handle arg_hCcHandle - handle of the QM given by CCI 
 *              INOUT void** arg_ppContext - IN address ix_cc_init_context
 *                                  OUT address to fill up with the pointer to the QM Core Context,
 *                                              
 *
 * @Return: IX_SUCCESS or valid ix_error if code fails ;
 */

ix_error ix_cc_qm_init(ix_cc_handle arg_hCcHandle, void** arg_ppContext)
{
    ix_cc_init_context* pInitContext = NULL;
    ix_cc_qm_context* pContext = NULL;
    
    ix_error err = IX_SUCCESS;
    ix_uint32 _instance = 0;

    /* Check the input arguments */
    if (IX_NULL_HANDLE == arg_hCcHandle) 
    {
        return IX_ERROR_WARNING(IX_CC_QM_INVALID_CC_HANDLE,("Invalid CC handle in ix_cc_qm_init"));
        
    }
    if (NULL == arg_ppContext)
    {
        return IX_ERROR_WARNING(IX_CC_ERROR_NULL,("Null Context argument in ix_cc_qm_init"));
    }
    /* get memory for context structure */ 
    pContext = (ix_cc_qm_context*)ix_ossl_malloc(sizeof(ix_cc_qm_context));
    if (NULL == pContext)
    {
        return IX_ERROR_WARNING(IX_CC_ERROR_OOM_SYSTEM,("Out of system memory in ix_cc_qm_init"));
    }
    /* get memory for config structure */
    pContext->pUblockConfig = (ix_cc_qm_ublock_config*)ix_ossl_malloc(sizeof(ix_cc_qm_ublock_config));
    if (NULL == pContext->pUblockConfig)
    {
        ix_ossl_free(pContext);
        return IX_ERROR_WARNING(IX_CC_ERROR_OOM_SYSTEM, ("Out of system memory in ix_cc_qm_init "));
    }
    pContext->hQMHandle = arg_hCcHandle;
    /* this function cleans own memory in case of error */
    _instance = IX_HANDLE_TO_INSTANCE(arg_hCcHandle);
    IX_ERROR_C(_ix_cc_qm_update_configuration(_instance, pContext),err);
    if (err != IX_SUCCESS)
    {
        /*free previously allocated memory */
        ix_ossl_free(pContext->pUblockConfig);
        ix_ossl_free(pContext);
        return err;
    }
    IX_ERROR_C(_ix_cc_qm_patch_microblock(pContext),err); /* cleans own memory for errors */
    if (err != IX_SUCCESS)
    {
        _ix_cc_qm_free_init_resources(pContext);
        return err;
    }
    /* register packet and message handler */
    IX_ERROR_C(_ix_cc_qm_reg_pkt_msg_handlers(arg_hCcHandle,pContext),err);
    if (err != IX_SUCCESS)
    {
        _ix_cc_qm_free_init_resources(pContext);
        return err;
    }
    _ix_cc_qm_zero_pkt_count(); /* reset packet counter */
    /* get blade id setting from  arg_ppContext */
    pInitContext = (ix_cc_init_context*)*arg_ppContext;
    if ((NULL != pInitContext->pPropertyList) && (NULL != pInitContext->pPropertyList->pPropertyInfo))
    {
        pContext->localBladeId = pInitContext->pPropertyList->pPropertyInfo->blade_id;
    } 

#if IX_QM_DEBUG    
    ix_ossl_message_log("Free list handle = %ld \n",pInitContext->hFreeList);
    ix_ossl_message_log("Blade ID = %ld \n", pContext->localBladeId);
#endif
    /* set CC context */
    *arg_ppContext = pContext;
    return err;
}

/**
 * NAME: ix_cc_qm_fini()
 *
 * DESCRIPTION: Shut down function invoked by the Execution Engine of the CCI
 * 
 * @Param:  -   IN  ix_cc_handle arg_hCcHandle - handle of the QM given by CCI 
 *              IN void *arg_ppContext - pointer to the QM Core Context
 *
 * @Return: IX_SUCCESS or valid ix_error if code fails ;
 */

ix_error ix_cc_qm_fini(ix_cc_handle arg_hCcHandle, void *arg_pContext)
{
    /* check the arguments */
    ix_error err = IX_SUCCESS;
    ix_cc_qm_context* pContext = arg_pContext;
    if (NULL == arg_pContext) 
    {
        err = IX_ERROR_WARNING(IX_CC_ERROR_NULL, ("Null component context in ix_cc_qm_fini"));
    }
    else if (arg_hCcHandle != (pContext->hQMHandle)) 
    {
        err = IX_ERROR_WARNING (IX_CC_QM_INVALID_CC_HANDLE, 
                                ("Invalid component handle in ix_cc_qm_fini"));
    }
    if (IX_SUCCESS == err)
    {
        ix_hw_queue_handle hDropQueue;
        _ix_cc_qm_zero_pkt_count(); /* reset packet counter */
        /* remove packet handlers for ingress and egress */
        IX_ERROR_C(_ix_cc_qm_unreg_pkt_msg_handlers(arg_hCcHandle),err);
        /* delete hw_queue */
        hDropQueue = IX_RM_HW_QUEUE_CREATE_HANDLE(0, pContext->pUblockConfig->drop_queue_entry);
        IX_ERROR_CT(ix_rm_hw_queue_delete(hDropQueue),err, IX_CC_QM_ERROR_FREE_HW_QUEUE, 
                                IX_ERROR_LEVEL_WARNING);
        
        /* rm memory de-allocation */
        IX_ERROR_CT(ix_rm_mem_free((void*)pContext->pUblockConfig->qd_sram_virtual_base),err, IX_CC_QM_ERROR_FREE_MEM, 
                                IX_ERROR_LEVEL_WARNING);
        /* ossl memory de-allocation */
        ix_ossl_free(pContext->pUblockConfig);
        ix_ossl_free(pContext);
    }
    return err;
}


/**
 * NAME: ix_cc_qm_msg_handler()
 *
 * DESCRIPTION: Message handling function invoked by the Execution Engine of the CCI
 * 
 * @Param:  -   IN ix_buffer_handle arg_Msg - handle of the IX buffer - actual message buffer 
 *              IN ix_uint32 arg_MsgId  - identifies message id.
 *              IN void* arg_pContext - pointer to the context of QM Core Component created in init function
 *
 * @Note:       For message ids IX_CC_QM_MSG_GETPKTCOUNT errors are returned 
 *              to the caller in the message support reply call  
 * @Return: IX_SUCCESS or valid ix_error if code fails ;
 */
ix_error ix_cc_qm_msg_handler(ix_buffer_handle arg_Msg, 
                              ix_uint32 arg_MsgId,
                                void* arg_pContext)
{
    ix_error err = IX_SUCCESS;
    ix_error msup_err = IX_SUCCESS;

    ix_uint64* pMsgData = NULL; 
    void* pContextId = NULL;

    if (arg_MsgId == IX_CC_QM_MSG_GET_QUEUE_DESCRIPTOR) 
    {
        ix_cc_qm_context *pContext    = arg_pContext;
        ix_uint32        *pMsgData    = NULL; 
        ix_uint32         MsgReply;

        #ifdef IX_DEBUG_WRED
        ix_ossl_message_log("ix_cc_qm_msg_handler(): got add message \n");
        #endif
        
        IX_MSUP_EXTRACT_MSG(arg_Msg, (void**)&pMsgData, &pContextId);
        if ( pMsgData == NULL )
        {
            return IX_ERROR_WARNING(IX_CC_ERROR_NULL,
                       ( "ix_cc_qm_msg_handler(): invalid buffer" ));
        }    

        #ifdef IX_DEBUG_WRED
        ix_ossl_message_log("ix_cc_qm_msg_handler(): queue number addr    : 0x%x \n",(((ix_uint32*)pMsgData)));
        ix_ossl_message_log("ix_cc_qm_msg_handler(): queue number         : 0x%x \n",(*((ix_uint32*)pMsgData)));
        #endif

        ix_rm_get_virtual_address(
                   IX_MEMORY_TYPE_SRAM,
                   pContext->pUblockConfig->qd_channel_num,
                   pContext->pUblockConfig->qd_sram_base + IX_QD_SIZE * (*((ix_uint32*)pMsgData)),
                   (ix_uint8** )&MsgReply);

        #ifdef IX_DEBUG_WRED
        ix_ossl_message_log("ix_cc_qm_msg_handler(): queue descriptor base: 0x%x \n",pContext->pUblockConfig->qd_sram_virtual_base);
        ix_ossl_message_log("ix_cc_qm_msg_handler(): queue size           : 0x%x \n",IX_QD_SIZE);
        ix_ossl_message_log("ix_cc_qm_msg_handler(): queue descriptor     : 0x%x \n",MsgReply);
        ix_ossl_message_log("ix_cc_qm_msg_handler(): sending reply. \n");
        #endif
        
        /* send return message, length and retrieved context to message support library */
        err = ix_cc_msup_send_reply_msg(pContextId, &MsgReply, sizeof(MsgReply), err);
        if(err != IX_SUCCESS) 
        {
            err = IX_ERROR_WARNING(IX_CC_ERROR_SEND_FAIL,
                    ("ix_cc_qm_msg_handler(): Send reply message failed"));
        }    
    
        if (IX_SUCCESS != ix_rm_buffer_free_chain(arg_Msg)) 
            err = IX_ERROR_WARNING(IX_CC_QM_ERROR_FREE_HW_QUEUE,
                    ("ix_cc_qm_msg_handler(): Error during releasing buffers"));

        return err;
    } 


#ifdef IX_QM_DEBUG
    if (IX_NULL_BUFFER_HANDLE == arg_Msg)
    {
        return IX_ERROR_WARNING(IX_CC_QM_INVALID_HANDLE,
                                ("Null buffer in ix_cc_qm_msg_handler"));
    }
#endif

    if (arg_MsgId != IX_CC_QM_MSG_GETPKTCOUNT) 
    {
        err = IX_ERROR_WARNING(IX_CC_ERROR_UNDEFINED_MSG,("Undefined message in ix_cc_qm_msg_handler"));
    }
    /* get the context ID anyway even in case of error */
    /* get the context id for the message */ 
    msup_err = IX_MSUP_EXTRACT_MSG(arg_Msg, (void**)&pMsgData, &pContextId);
    if (IX_SUCCESS != msup_err)
    {
        /* free the message buffer and return */
        if (IX_SUCCESS != ix_rm_buffer_free(arg_Msg))
        {
            ix_ossl_message_log("Error freeing message buffer in ix_cc_qm_msg_handler\n");
        }
        return IX_ERROR_CHAIN(msup_err,IX_CC_QM_ERROR_MSUP,IX_ERROR_LEVEL_REMOTE,
                                ("Error in IX_MSUP_EXTRACT_MSG invoked from ix_cc_qm_msg_handler"));
    }
    if (NULL == pMsgData)
    {
        err = IX_ERROR_WARNING(IX_CC_ERROR_NULL,("NULL Message Data"));   
    }
    if (IX_SUCCESS == err) /* everything is fine lets reuse this buffer data */
    {
      /* get pkt count */
      err = ix_cc_qm_get_packet_count(pMsgData);
    }
    /* send message back using support message library call*/
    err = ix_cc_msup_send_reply_msg(pContextId,pMsgData, sizeof(ix_uint64),err);    
    /* free the buffer */
    if (IX_SUCCESS != ix_rm_buffer_free(arg_Msg))
    {
        ix_ossl_message_log("Error freeing message buffer in QM message handler\n");
    }
    return err;
}

#if defined(IX_QM_PKT_DUMP)
PRIVATE ix_error _ix_cc_qm_pkt_dump(
        void *arg_pContext,
        ix_buffer_handle arg_hDataToken)
{
    ix_cc_qm_context* pContext = (ix_cc_qm_context*)arg_pContext;
    void* pData; 
    ix_uint8* pTemp;
    ix_uint32 sizeOfData;
    void* pMeta;
    ix_uint32 i;
	ix_error err = IX_SUCCESS;

    /* dump content of the buffer */
    ix_ossl_message_log(" Packet data in ix_cc_qm_pkt_handler() \n");
    err = ix_cc_hw_get_packet_data(arg_hDataToken, &pData);
    if (IX_SUCCESS != err)
    {
        ix_rm_buffer_free_chain(arg_hDataToken);
        return IX_ERROR_WARNING(err, ("Failed to get buffer data"));
    }
    err = ix_rm_buffer_get_meta(arg_hDataToken, &pMeta);
    if (IX_SUCCESS != err)
    {
        ix_rm_buffer_free_chain(arg_hDataToken);
        return IX_ERROR_WARNING(err, ("Failed to get meta data"));
    }
    sizeOfData = ((ix_hw_buffer_meta*)pMeta)->m_BufferSize; 
    pTemp = ix_ossl_malloc(sizeOfData);
    ix_ossl_memcpy(pTemp, pData, sizeOfData);
    ix_ossl_message_log("Printing the buffer ...\n");
    for ( i=0;i < sizeOfData; i++)
    {
        printf("%02x ", pTemp[i]);
        if ( (i+1) % 4 == 0)
        {
            printf("\n");
        }
    }
    ix_ossl_message_log("\n");
    ix_ossl_free(pTemp);
    /* end dumping of the contents */
    ix_ossl_message_log("pContext->ingressEgress = %d, pContext->switchFabricLoopback = %d, pContext->localBladeId = %d, pMeta->m_FabricPort = %d \n", pContext->ingressEgress, pContext->switchFabricLoopback, pContext->localBladeId,	((ix_hw_buffer_meta*)pMeta)->m_FabricPort);			

    return err;
}
#endif /* IX_QM_PKT_DUMP */

/**
 * NAME: ix_cc_qm_pkt_handler()
 *
 * DESCRIPTION: Packet handling function invoked by the Execution Engine of the CCI
 * 
 * @Param:  -   IN ix_buffer_handle arg_hDataToken - handle of the IX buffer - represents packet in transmission 
 *              IN ix_uint32 arg_ExceptionCode  - exception code  - not used in this function
 *              IN void* arg_pContext - pointer to the context of QM Core Component created in init function
 *
 * @Return: IX_SUCCESS or valid ix_error if code fails ;
 */

ix_error ix_cc_qm_pkt_handler(ix_buffer_handle arg_hDataToken, 
                                ix_uint32 arg_ExceptionCode, 
                                void *arg_pContext)
{
    ix_error err = IX_SUCCESS;
    ix_uint32 instanceCC;

#if defined(IX_QM_DEBUG) || defined(IX_PCI_COMM)
    ix_cc_qm_context* pContext = (ix_cc_qm_context*)arg_pContext;
#endif /* IX_QM_PKT_DEBUG || IX_QM_DEBUG || IX_PCI_COMM */

#if defined(IX_QM_DEBUG)
    if (NULL == pContext) 
    {
        ix_rm_buffer_free_chain(arg_hDataToken);
        return IX_ERROR_WARNING(IX_CC_ERROR_NULL,("null pContext "));
    }
    instanceCC = IX_HANDLE_TO_INSTANCE(((ix_cc_qm_context *)arg_pContext)->hQMHandle);

    if (IX_NULL_BUFFER_HANDLE == arg_hDataToken)
    {
        return IX_ERROR_WARNING(IX_CC_QM_INVALID_HANDLE,("NULL  pkt buffer handle" ));
    }
#endif /* IX_QM_DEBUG */

#ifdef IX_PCI_COMM
    if (pContext->ingressEgress == IX_INGRESS &&
        pContext->switchFabricLoopback == IX_SWITCH_FABRIC_NO_LOOPBACK )
    {
        /* get blade ID from the meta data */
        void* pMetaData;
        err = ix_rm_buffer_get_meta(arg_hDataToken, &pMetaData);
        if (IX_SUCCESS != err) /* handle the error  - free the buffer */
        {
            if (IX_SUCCESS != ix_rm_buffer_free_chain(arg_hDataToken))
            {
                 ix_ossl_message_log("Error freeing packet buffer in QM packet handler\n");
            }
            return IX_ERROR_WARNING(IX_CC_QM_ERROR_GET_META_DATA, ("Failed get_meta call"));
        }
        /* check if output blade is the local blade id */
        if (((ix_hw_buffer_meta*)pMetaData)->m_FabricPort == IX_HTON16(pContext->localBladeId))
        {
            /* send packets to the other side over PCI */
            err = ix_rm_packet_send(IX_CC_QM_INSTANCE_TABLE_GET(instanceCC,pktEgressOut),arg_hDataToken,arg_ExceptionCode); 
        } /* */
        else
        {
            err = ix_rm_packet_send(IX_CC_QM_INSTANCE_TABLE_GET(instanceCC,pktUblockOut),arg_hDataToken,arg_ExceptionCode);
            /* err = _ix_cc_qm_ublock_pkt_handler(arg_hDataToken,arg_ExceptionCode,arg_pContext); */
        }
    }
    else
#endif
    {
        /* for performance reasons we might want to change to directly invoking the function*/
        /* err = ix_rm_packet_send(IX_CC_QM_INSTANCE_TABLE_GET(instanceCC,pktUblockOut),arg_hDataToken,arg_ExceptionCode);*/ 
        err = _ix_cc_qm_ublock_pkt_handler(arg_hDataToken,arg_ExceptionCode,arg_pContext); 
    }
    /* increment the pkt counter */
    if (IX_SUCCESS == err)
    {
        _ix_cc_qm_increment_pkt_count();
    }
    else /* error from buffer send */
    { 
        err = IX_ERROR_CHAIN(err, IX_CC_ERROR_SEND_FAIL, IX_ERROR_LEVEL_WARNING,
				("Send packet fail in pkt handler"));
        if (IX_SUCCESS != ix_rm_buffer_free_chain(arg_hDataToken))
        {
             ix_ossl_message_log("Error freeing packet buffer in QM packet handler\n");
        }
    }
    return err;
}


 /**
 * NAME: ix_cc_qm_get_packet_count()
 *
 * DESCRIPTION: This is a Library API (Level 0 support ) function that returns a packet count. It is called by Messaging API 
 * and returns value of the global count.
 * 
 * @Param:  - OUT ix_uint64* arg_pPktCount - returns count of packets processed by QM core;
 *
 * @Return: IX_SUCCESS in case of success or valid ix_error if call fails
 */

ix_error ix_cc_qm_get_packet_count(ix_uint64* arg_pPktCount)
{
    ix_error err = IX_SUCCESS;
    if (NULL == arg_pPktCount)
    {
        err = IX_ERROR_WARNING(IX_CC_ERROR_NULL,("Null arg_pPktCount "));
    }
    else
    {
        *arg_pPktCount = _ix_cc_qm_get_packet_count();
    }
    return err;
}



/*******************************  FILE HISTORY  *******************************
 * \main\sdk_3_0\lp_merge\1 Thu Aug 28 18:55:56 2003 adziecia
 * IGK0100025366: LP MERGE: Core Corponents merge checkin 
 *  
 *****************************************************************************/
