/**
 * ============================================================================
 * = 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_internal.c
 *
 * = DESCRIPTION
 *      The file contains functions that represent internal functionality of QM core. 
 *      The functions in this file are not exposed to the clients of QM. 
 *
 *
 * = AUTHOR
 *      Herman Zaks
 *      Herman.Zaks@intel.com
 *
 **/
#define IX_ERROR_FILE_IDENT "$Id: ix_cc_qm_internal.c,v 1.53 2003/11/14 21:35:23 rranjeet 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_cc.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 !defined(IX_INCLUDE_REGISTRY) /* include configuration header for config values */
    #include "ix_config.h"
#endif

static ix_uint64   st_QMCorePktCount; /*internal packet counter */ 
#if IX_QM_TEST
    extern ix_buffer_free_list_handle g_hFreeList;
#endif

IX_CC_QM_INSTANCE_TABLE_DECLARE()
                                                   
/**
 * NAME: _ix_cc_qm_get_packet_count()
 *
 * DESCRIPTION: This is a internal function that returns a packet counter . 
 *          It is called by library API.
 * 
 * @Param:  - None
 *
 * @Return: the value of the st_QMCorePktCount;
 */
/* #define UNIT_TEST 1 */
ix_uint64 _ix_cc_qm_get_packet_count()
{
/*
#ifdef UNIT_TEST
    return 0xDEADBEAF;
#else
*/
    return st_QMCorePktCount;
/* #endif */
}

/**
 * NAME: _ix_cc_qm_increment_pkt_count()
 *
 * DESCRIPTION: Increments packet counter by one.
 * 
 * @Param:  - None
 *
 * @Return: the value of the st_QMCorePktCount;
 */

ix_uint64 _ix_cc_qm_increment_pkt_count()
{
    return ++st_QMCorePktCount;
}

/**
 * NAME: _ix_cc_qm_zero_pkt_count()
 *
 * DESCRIPTION: Resets packet counter to 0
 * 
 * @Param:  - None
 *
 * @Return: void
 */

void _ix_cc_qm_zero_pkt_count()
{
    st_QMCorePktCount = 0;
}

/**
 * NAME: _ix_cc_qm_reg_pkt_msg_handlers()
 *
 * DESCRIPTION: Registers message and packet handlers with the Framework (RM and CCI) and also 
 *              registers ublock packet communication ID with rm 
 * 
 * @Param: ix_cc_handle arg_hComponent  - handle of the QM core component
 *         ix_cc_qm_context* pContext - pointer to the QM handle context 
 *
 * @Return: IX_SUCCESS or valid ix_error;
 */


ix_error _ix_cc_qm_reg_pkt_msg_handlers(const ix_cc_handle arg_hComponent, const ix_cc_qm_context* pContext)
{
    ix_error err = IX_SUCCESS;
    ix_uint32 instanceCC = IX_HANDLE_TO_INSTANCE(arg_hComponent);
#if !defined(IX_EXCLUDE_CCI)
    /* register message handler */
    IX_ERROR_CRT(ix_cci_cc_add_message_handler(arg_hComponent,
                                               IX_CC_QM_INSTANCE_TABLE_GET(instanceCC,msgIn),
                                               ix_cc_qm_msg_handler, IX_INPUT_TYPE_MULTI_SRC),
                 IX_CC_QM_ERROR_HANDLER_REGISTRATION, IX_ERROR_LEVEL_LOCAL);
    /* register packet handlers  - two registration for the same handle for ingress and egress QM*/
    /* first registration for the common handle on the ingress side */
    err = ix_cci_cc_add_packet_handler(arg_hComponent, 
                                       IX_CC_QM_INSTANCE_TABLE_GET(instanceCC,pktIn),
                                       ix_cc_qm_pkt_handler, IX_INPUT_TYPE_SINGLE_SRC);
    if (IX_SUCCESS != err)
    {
        ix_cci_cc_remove_message_handler(IX_CC_QM_INSTANCE_TABLE_GET(instanceCC,msgIn));
        return IX_ERROR_LOCAL(IX_CC_QM_ERROR_HANDLER_REGISTRATION, (" Ingress Packet handler registration has failed"));
    }

    /* second registration for the handle on the egress side - mainly this handle used for packets coming over 
    PCI from ingress QM. Single source type is selected for performance reason,
        so access to the handler invocation will not be locked by CCI*/
    /* TODO - explore idea of creating separate handler function for this communication id */
    err = ix_cci_cc_add_message_handler(arg_hComponent, 
                                        IX_CC_QM_INSTANCE_TABLE_GET(instanceCC,pktEgressIn),
                                       ix_cc_qm_pkt_handler, IX_INPUT_TYPE_SINGLE_SRC);
    if (IX_SUCCESS != err)
    {
        ix_cci_cc_remove_message_handler(IX_CC_QM_INSTANCE_TABLE_GET(instanceCC,msgIn));
        ix_cci_cc_remove_packet_handler(IX_CC_QM_INSTANCE_TABLE_GET(instanceCC,pktIn));
        return IX_ERROR_LOCAL(IX_CC_QM_ERROR_HANDLER_REGISTRATION, (" Egress Packet handler registration has failed"));
    }

#else
    /* Register the packet and message handlers with the RM communication. */
    IX_ERROR_CRT(ix_rm_message_handler_register(IX_CC_QM_INSTANCE_TABLE_GET(instanceCC,msgIn),
                    (ix_comm_data_handler)ix_cc_qm_msg_handler,
                    (void*)pContext),IX_CC_QM_ERROR_HANDLER_REGISTRATION, IX_ERROR_LEVEL_LOCAL);
    err = ix_rm_packet_handler_register(IX_CC_QM_INSTANCE_TABLE_GET(instanceCC,pktIn),
                    (ix_comm_data_handler)ix_cc_qm_pkt_handler, (void*)pContext);
    if (IX_SUCCESS != err)
    {
        ix_rm_message_handler_unregister(IX_CC_QM_INSTANCE_TABLE_GET(instanceCC,msgIn));
        return IX_ERROR_LOCAL(IX_CC_QM_ERROR_HANDLER_REGISTRATION, (" Ingress Packet handler registration has failed"));
    }

    err = ix_rm_message_handler_register(IX_CC_QM_INSTANCE_TABLE_GET(instanceCC,pktEgressIn),
                    (ix_comm_data_handler)ix_cc_qm_pkt_handler, (void*)pContext);
    if (IX_SUCCESS != err)
    {
        ix_rm_message_handler_unregister(IX_CC_QM_INSTANCE_TABLE_GET(instanceCC,msgIn));
        ix_rm_packet_handler_unregister(IX_CC_QM_INSTANCE_TABLE_GET(instanceCC,pktIn));
        return IX_ERROR_LOCAL(IX_CC_QM_ERROR_HANDLER_REGISTRATION, (" Egress Packet handler registration has failed"));
    }

#endif /* end !defined(IX_EXCLUDE_CCI) */
    /* associate hw ring with ublock comm id */
    return err;

label_1:
    if (IX_SUCCESS != err)
    {
#if !defined(IX_EXCLUDE_CCI)
        ix_cci_cc_remove_message_handler(IX_CC_QM_INSTANCE_TABLE_GET(instanceCC,msgIn));
        ix_cci_cc_remove_packet_handler(IX_CC_QM_INSTANCE_TABLE_GET(instanceCC,pktIn));
        ix_cci_cc_remove_message_handler(IX_CC_QM_INSTANCE_TABLE_GET(instanceCC,pktEgressIn));
#else
        ix_rm_message_handler_unregister(IX_CC_QM_INSTANCE_TABLE_GET(instanceCC,msgIn));
        ix_rm_packet_handler_unregister(IX_CC_QM_INSTANCE_TABLE_GET(instanceCC,pktIn));
        ix_rm_message_handler_unregister(IX_CC_QM_INSTANCE_TABLE_GET(instanceCC,pktEgressIn));
#endif
        
        err = IX_ERROR_LOCAL(IX_CC_QM_ERROR_UBLOCK_COMM_INIT, ("Failed to register internal packet handler with Ublock comm id")); 
    }
    return err;
}

/**
 * NAME: _ix_cc_qm_dereg_pkt_msg_handlers()
 *
 * DESCRIPTION: Unregisters message and packet handlers with the Framework (RM and CCI)
 * 
 * @Param:  - None
 *
 * @Return: IX_SUCCESS or valid ix_error;
 */


ix_error _ix_cc_qm_unreg_pkt_msg_handlers(const ix_cc_handle arg_hComponent)
{
    ix_uint32 instanceCC = IX_HANDLE_TO_INSTANCE(arg_hComponent);

#if !defined(IX_EXCLUDE_CCI)    
    /* unregister message handler */
    IX_ERROR_CR(ix_cci_cc_remove_message_handler(IX_CC_QM_INSTANCE_TABLE_GET(instanceCC,msgIn)));
    /* unregister packet handlers */
    /* first for the common handle */
    IX_ERROR_CR(ix_cci_cc_remove_packet_handler(IX_CC_QM_INSTANCE_TABLE_GET(instanceCC,pktIn)));
    /* second for the PCI specific handle on the egress side */
    IX_ERROR_CR(ix_cci_cc_remove_message_handler(IX_CC_QM_INSTANCE_TABLE_GET(instanceCC,pktEgressIn)));
#else
    IX_ERROR_CR(ix_rm_message_handler_unregister(IX_CC_QM_INSTANCE_TABLE_GET(instanceCC,msgIn)));
    IX_ERROR_CR(ix_rm_packet_handler_unregister(IX_CC_QM_INSTANCE_TABLE_GET(instanceCC,pktIn)));
    IX_ERROR_CR(ix_rm_message_handler_unregister(IX_CC_QM_INSTANCE_TABLE_GET(instanceCC,pktEgressIn)));
#endif /* end !defined(IX_EXCLUDE_CCI) */

    return IX_SUCCESS;
}




/**
 * NAME: _ix_cc_qm_update_configuration()
 *
 * DESCRIPTION: Populates configuration data structure with configuration data for QM microblock
 *              and core
 * 
 * @Param:  - ix_cc_qm_context* pContext - pointer to the QM configuration data structure 
 *
 * @Return: IX_SUCCESS or valid ix_error;
 */

ix_error _ix_cc_qm_update_configuration(ix_uint32 arg_instance,
                                        ix_cc_qm_context* pContext)
{
    ix_error err = IX_SUCCESS;
    ix_uint32 Value = 0;     
#ifdef IX_INCLUDE_REGISTRY
    char pPropName[256];    
    {
        ix_configuration_property_handle hProperty;
/* get total number of queue descriptors */ 
    
#ifdef CCI_MULTIINSTANCE
        sprintf(pPropName, "%s%ld%s%s", "QM_", arg_instance, "/", 
                REPOSITORY_PATH_QM_QUEUE_DESC_TOTAL);
#else
        sprintf(pPropName, "%s%s%s", "QM", "/", 
                REPOSITORY_PATH_QM_QUEUE_DESC_TOTAL);        
#endif
        IX_ERROR_CRT(ix_rm_cp_property_open( IX_CP_CORE_PROPERTY_ROOT,
                                  pPropName,
                                  &hProperty), IX_CC_QM_ERROR_REPOSITORY_OPEN, IX_ERROR_LEVEL_WARNING);

        IX_ERROR_CRT(ix_rm_cp_property_get_value_uint32(hProperty, &Value ),
                        IX_CC_QM_ERROR_REPOSITORY_READ, IX_ERROR_LEVEL_WARNING);
        ix_rm_cp_property_close( hProperty);
		pContext->pUblockConfig->total_queue_desc = Value;

/* get the SRAM channel number */
        
#ifdef CCI_MULTIINSTANCE
        /* Do not take it from the Registry. In the multiinstance CC environment,
           the "arg_instance" parameter determines the channel number; could be
           (0 == upstream == channel 0), (1 == downstream == channel 1) */
		pContext->pUblockConfig->qd_channel_num = arg_instance;
#else
        IX_ERROR_CRT(ix_rm_cp_property_open( IX_CP_CORE_PROPERTY_ROOT,
                                  REPOSITORY_PATH_SRAM_CHANNEL_NUM,
                                  &hProperty), IX_CC_QM_ERROR_REPOSITORY_OPEN, IX_ERROR_LEVEL_WARNING);
        IX_ERROR_CRT(ix_rm_cp_property_get_value_uint32(hProperty, &Value ),
                        IX_CC_QM_ERROR_REPOSITORY_READ, IX_ERROR_LEVEL_WARNING);
        ix_rm_cp_property_close( hProperty);
		pContext->pUblockConfig->qd_channel_num = Value;
#endif
        
/* get microengine number */
#ifdef CCI_MULTIINSTANCE
        sprintf(pPropName, "%s%ld%s%s", "QM_", arg_instance, "/", 
                REPOSITORY_PATH_QM_UENG_NUM);
#else
        sprintf(pPropName, "%s%s%s", "QM", "/", 
                REPOSITORY_PATH_QM_UENG_NUM);
#endif
        IX_ERROR_CRT(ix_rm_cp_property_open( IX_CP_CORE_PROPERTY_ROOT,
                                  pPropName,
                                  &hProperty), IX_CC_QM_ERROR_REPOSITORY_OPEN, IX_ERROR_LEVEL_WARNING);
        IX_ERROR_CRT(ix_rm_cp_property_get_value_uint32(hProperty, &Value ),
                        IX_CC_QM_ERROR_REPOSITORY_READ, IX_ERROR_LEVEL_WARNING);
        ix_rm_cp_property_close( hProperty);
		pContext->pUblockConfig->ueng_number = Value;
/* get local blade id */
        IX_ERROR_CRT(ix_rm_cp_property_open( IX_CP_CORE_PROPERTY_ROOT,
                                  REPOSITORY_PATH_LOCAL_BLADE_ID,
                                  &hProperty), IX_CC_QM_ERROR_REPOSITORY_OPEN, IX_ERROR_LEVEL_WARNING);
        IX_ERROR_CRT(ix_rm_cp_property_get_value_uint32(hProperty, &Value ),
                        IX_CC_QM_ERROR_REPOSITORY_READ, IX_ERROR_LEVEL_WARNING);
        ix_rm_cp_property_close( hProperty);
        pContext->localBladeId = Value;
/* get Ingress /Egress side */
        IX_ERROR_CRT(ix_rm_cp_property_open( IX_CP_CORE_PROPERTY_ROOT,
                                  REPOSITORY_PATH_INGRESS_EGRESS,
                                  &hProperty), IX_CC_QM_ERROR_REPOSITORY_OPEN, IX_ERROR_LEVEL_WARNING);
        IX_ERROR_CRT(ix_rm_cp_property_get_value_uint32(hProperty, &Value ),
                        IX_CC_QM_ERROR_REPOSITORY_READ, IX_ERROR_LEVEL_WARNING);
        ix_rm_cp_property_close( hProperty);
		pContext->ingressEgress = Value;
/* get the switch fabric loopback flag */

	
        IX_ERROR_CRT(ix_rm_cp_property_open( IX_CP_CORE_PROPERTY_ROOT,
                                  REPOSITORY_PATH_SWITCH_FABRIC_LOOPBACK,
                                  &hProperty), IX_CC_QM_ERROR_REPOSITORY_OPEN, IX_ERROR_LEVEL_WARNING);
        IX_ERROR_CRT(ix_rm_cp_property_get_value_uint32(hProperty, &Value),
                        IX_CC_QM_ERROR_REPOSITORY_READ, IX_ERROR_LEVEL_WARNING);
		pContext->switchFabricLoopback = Value;
		ix_ossl_message_log("pContext->switchFabricLoopback = %d\n",pContext->switchFabricLoopback);
        ix_rm_cp_property_close( hProperty);

#if defined( IX_INCLUDE_WRED )

#ifdef CCI_MULTIINSTANCE
        sprintf(pPropName, "%s%ld%s%s", "WRED_", arg_instance, "/", 
                REPOSITORY_PATH_WRED_UENG_MASK);
#else
        sprintf(pPropName, "%s%s%s", "WRED", "/", 
                REPOSITORY_PATH_WRED_UENG_MASK);
#endif

         IX_ERROR_CRT( ix_rm_cp_property_open( IX_CP_CORE_PROPERTY_ROOT,
           pPropName, &hProperty ),
           IX_CC_QM_ERROR_REPOSITORY_OPEN, IX_ERROR_LEVEL_WARNING );
 
         IX_ERROR_CRT( ix_rm_cp_property_get_value_uint32( hProperty, &Value ),
           IX_CC_QM_ERROR_REPOSITORY_READ, IX_ERROR_LEVEL_WARNING );

         ix_rm_cp_property_close( hProperty );
         pContext->pUblockConfig->wred_ueng_mask = Value;
#endif /* IX_INCLUDE_WRED */


#if defined( IX_INCLUDE_QM_MASK ) && !defined( IX_PLATFORM_2801 )

#ifdef CCI_MULTIINSTANCE         
         sprintf(pPropName, "%s%ld%s%s", "QM_", arg_instance, "/", 
                REPOSITORY_PATH_QM_UENG_MASK);
#else
         sprintf(pPropName, "%s%s%s", "QM", "/", 
                REPOSITORY_PATH_QM_UENG_MASK);         
#endif

         IX_ERROR_CRT( ix_rm_cp_property_open( IX_CP_CORE_PROPERTY_ROOT,
                                               pPropName, &hProperty ),
                       IX_CC_QM_ERROR_REPOSITORY_OPEN, IX_ERROR_LEVEL_WARNING );
 
         IX_ERROR_CRT( ix_rm_cp_property_get_value_uint32( hProperty, &Value ),
                       IX_CC_QM_ERROR_REPOSITORY_READ, IX_ERROR_LEVEL_WARNING );

         ix_rm_cp_property_close( hProperty );
         pContext->pUblockConfig->qm_ueng_mask = Value;
#endif /* IX_INCLUDE_QM_MASK */
         
    }
#else

                
    /* read config data from config header file */
    pContext->pUblockConfig->total_queue_desc= QUEUE_DESC_TOTAL;
    pContext->localBladeId                   = LOCAL_BLADE_ID;
    pContext->ingressEgress                  = INGRESS_EGRESS;
    pContext->switchFabricLoopback           = IX_SWITCH_FABRIC_LOOPBACK_FLAG;
    pContext->pUblockConfig->qd_channel_num  = SRAM_CHANNEL_NUM;

  #if defined( IX_INCLUDE_WRED )
     pContext->pUblockConfig->wred_ueng_mask = WRED_UENG_MASK;
  #endif /* IX_INCLUDE_WRED */

  #if defined( IX_INCLUDE_QM_MASK ) && !defined( IX_PLATFORM_2801 )

#if (_IX_HARDWARE_TYPE_ == _IX_HW_2800_)
    if (IX_INGRESS == pContext->ingressEgress)
    {
        pContext->pUblockConfig->qm_ueng_mask   = QM_UENG_MASK_INGRESS;
    }
    else
    {
        pContext->pUblockConfig->qm_ueng_mask   = QM_UENG_MASK_EGRESS;
    }
#else /* (_IX_HARDWARE_TYPE_ == _IX_HW_2800_) */
     pContext->pUblockConfig->qm_ueng_mask   = QM_UENG_MASK;
#endif /* (_IX_HARDWARE_TYPE_ == _IX_HW_2800_) */
  #else
    if (IX_INGRESS == pContext->ingressEgress)
    {
        pContext->pUblockConfig->ueng_number = QM_INGRESS_UENG_NUM;
    }
    else
    {
        pContext->pUblockConfig->ueng_number = QM_EGRESS_UENG_NUM;
    }
  #endif /* IX_INCLUDE_QM_MASK */
   
#endif
    /* get ID for the drop queue entry through resource manager */
    /* rm reserves first 8 or 16 qarrays for itself */
    /* should give handle to the next free one */
    {
        ix_hw_queue_handle hDropQueue;
        void* pPhysicalAddress = NULL; /* holder of physical memory */
        ix_uint32 offset;
        ix_uint32 channel;
        ix_memory_type memType; 
 
        IX_ERROR_CRT(ix_rm_hw_queue_create(pContext->pUblockConfig->qd_channel_num,
                                           IX_CC_QM_DROP_QUEUE_NUMBER,
                                           0,
                                           &hDropQueue),
                     IX_CC_QM_ERROR_BUF_QUEUES_ALLOC_FAILED,
                     IX_ERROR_LEVEL_WARNING);
        
        pContext->pUblockConfig->drop_queue_entry = IX_RM_HW_QUEUE_GET_INDEX(hDropQueue);
       /* allocate SRAM memory for Queue Descriptors microblock */
        err = ix_rm_mem_alloc(IX_MEMORY_TYPE_SRAM,pContext->pUblockConfig->qd_channel_num, 
                              pContext->pUblockConfig->total_queue_desc * IX_QD_SIZE + 16, 
                              (void*)&pContext->pUblockConfig->qd_sram_virtual_base);

        if (IX_SUCCESS != err)
        {
            /* delete hw_queue */
            ix_rm_hw_queue_delete(hDropQueue);
            return IX_ERROR_WARNING(IX_CC_ERROR_OOM_SRAM,("Failed to allocate SRAM memeory for QM ublock"));
        }
        err = ix_rm_get_phys_offset((void*)pContext->pUblockConfig->qd_sram_virtual_base, 
                        &memType, &channel, &offset, &pPhysicalAddress); 
        if (IX_SUCCESS != err)
        {
            /* delete hw_queue */
            ix_rm_hw_queue_delete(hDropQueue);
            /* free memory */
            ix_rm_mem_free((void*)pContext->pUblockConfig->qd_sram_virtual_base);
            return IX_ERROR_WARNING(IX_CC_QM_ERROR_VIRTUAL_TO_PHYSICAL,("Failed to convert virtual SRAM address to physical"));
        }
        /* set memeory to zero */
        ix_ossl_memset((void*)pContext->pUblockConfig->qd_sram_virtual_base,0,(pContext->pUblockConfig->total_queue_desc * IX_QD_SIZE)+ 16);
	offset += 16;
        offset &= 0xFFFFFFF0; 
        pContext->pUblockConfig->qd_sram_base = offset;

    }
    return err;
}

/**
 * NAME: _ix_cc_qm_patch_microblock()
 *
 * DESCRIPTION: Patches QM microblock
 * 
 * @Param:  - ix_cc_qm_context* pContext - pointer to the QM context data structure 
 *
 * @Return: IX_SUCCESS or valid ix_error;
 */

ix_error _ix_cc_qm_patch_microblock(const ix_cc_qm_context* pContext)
{
    int i = 0;
    ix_cc_qm_ublock_config* pConfig = pContext->pUblockConfig;
    ix_uint32 chanNum = pConfig->qd_channel_num;
    ix_uint32 offset = pConfig->qd_sram_base;
    /* patch config data to the QM microblocks  */
    /* allocate data structures for import variables */
    ix_imported_symbol aImportSymbols[IX_CC_QM_PATCH_SYMBOLS];
    /* set channel number into offset */
    IX_CC_SET_SRAM_CHANNEL_INTO_OFFSET(chanNum, offset);

#if (_IX_HARDWARE_TYPE_ == _IX_HW_2800_)

    ix_uint32 num_symbols = 0;

#if defined( IX_PLATFORM_2801 )
    /*
     * Platform specific for IXDP2801.
     * Temporary solution for release 3.5 - remove it when the 2800 scheduler
     * will be integrated in the 2801 uCode.
     */
    /* populate array of import variables */
    SET_UBLOCK_SYMBOLS(aImportSymbols[i],offset,IX_QM_SYMBOL_QD_SRAM_BASE );
    i++;
    SET_UBLOCK_SYMBOLS(aImportSymbols[i],pConfig->total_queue_desc,IX_QM_SYMBOL_QD_TOTAL);         
    i++; 
    /* This patch is only for 2801 platform */
    SET_UBLOCK_SYMBOLS(aImportSymbols[i],pConfig->drop_queue_entry, IX_QM_SYMBOL_QM_DROP_QUEUE_ENTRY);
    num_symbols = IX_CC_QM_PATCH_SYMBOLS;

#else /* NOT IX_PLATFORM_2801 */
    
    if (IX_INGRESS == pContext->ingressEgress)
    {
        SET_UBLOCK_SYMBOLS(aImportSymbols[i], 
                           offset, 
                           IX_QM_SYMBOL_QD_SRAM_BASE);
        num_symbols = IX_CC_QM_PATCH_SYMBOLS_INGRESS;
    }
    else
    {
        SET_UBLOCK_SYMBOLS(aImportSymbols[i], 
                           offset, 
                           IX_QM_SYMBOL_QD_SRAM_BASE );
        num_symbols = IX_CC_QM_PATCH_SYMBOLS_EGRESS;
    }
    
#endif /* IX_PLATFORM_2801 */
    
#else /* (_IX_HARDWARE_TYPE_ != _IX_HW_2800_) */

    /* populate array of import variables */
    SET_UBLOCK_SYMBOLS(aImportSymbols[i],offset,IX_QM_SYMBOL_QD_SRAM_BASE );
    i++;
    SET_UBLOCK_SYMBOLS(aImportSymbols[i],pConfig->total_queue_desc,IX_QM_SYMBOL_QD_TOTAL);         
    i++; 
    /* Only for 240X platform */
    SET_UBLOCK_SYMBOLS(aImportSymbols[i],pConfig->drop_queue_entry, IX_QM_SYMBOL_QM_DROP_QUEUE_ENTRY);         

#endif /* (_IX_HARDWARE_TYPE_ == _IX_HW_2800_) */


#ifdef IX_QM_DEBUG
    ix_ossl_message_log("Patch QM symbols to microengine #%ld\n", pContext->pUblockConfig->ueng_number);
    for (i=0; i<IX_CC_QM_PATCH_SYMBOLS;i++)
    {
        ix_ossl_message_log("aImportSymbols[%d].m_Name = %s, aImportSymbols[%d].m_Value = %ld \n",
                            i, aImportSymbols[i].m_Name, i, aImportSymbols[i].m_Value);
    }
   
#endif

 #if defined( IX_INCLUDE_WRED )
 {
    /* ix_uint32 _t = pContext->pUblockConfig->wred_ueng_mask; */

    #ifdef IX_QM_DEBUG 
    /* ix_ossl_message_log( "Patching QD_SRAM_BASE symbol to the WRED microblocks...\n" ); */
    #endif /* IX_QM_DEBUG */             

 }
 #endif /* IX_INCLUDE_WRED */


 #if !defined( IX_INCLUDE_QM_MASK ) || defined( IX_PLATFORM_2801 )
    IX_ERROR_CRT(ix_rm_ueng_patch_symbols(pContext->pUblockConfig->ueng_number,IX_CC_QM_PATCH_SYMBOLS,aImportSymbols),
                                            IX_CC_QM_ERROR_PATCH_SYMBOLS, IX_ERROR_LEVEL_WARNING);
 #else
 {    
    ix_uint32 _t = pContext->pUblockConfig->qm_ueng_mask;

    #ifdef IX_QM_DEBUG 
      ix_ossl_message_log( "Patching QD_SRAM_BASE symbol to other microblocks...\n" );
    #endif /* IX_QM_DEBUG */

    i = 0; while ( _t != 0 )
    {
           if ( _t & 0x01 )
             {
#if (_IX_HARDWARE_TYPE_ == _IX_HW_2800_)
               IX_ERROR_CR( ix_rm_ueng_patch_symbols( 
                 g_aMicroengineNumberMapping[i], num_symbols, aImportSymbols));
#else /* (_IX_HARDWARE_TYPE_ == _IX_HW_2800_) */
               IX_ERROR_CR( ix_rm_ueng_patch_symbols( 
                 g_aMicroengineNumberMapping[i], IX_CC_QM_PATCH_SYMBOLS, aImportSymbols ) );
#endif /* (_IX_HARDWARE_TYPE_ == _IX_HW_2800_) */
             }
 
           i++; _t >>= 1;
    }

 }
 #endif /* IX_INCLUDE_QM_MASK */    


    /* it assumed that ix_rm_ueng_load() will be called by the system app after all core components are initialized */

    return (IX_SUCCESS);
}

/**
 * NAME: _ix_cc_qm_free_init_resources()
 *
 * DESCRIPTION: Free resources that were allocated during initialization stage. This function is called from
 *              init call in case of error and deletes hw queue, shared SRAM memory, ublock configuration structure 
 *              and QM context
 * 
 * @Param:  - IN ix_cc_qm_context* pContext - pointer to the QM context data structure 
 *
 * @Return: void
 */
void _ix_cc_qm_free_init_resources(ix_cc_qm_context* pContext)
{
    ix_hw_queue_handle hDropQueue = IX_RM_HW_QUEUE_CREATE_HANDLE(0, pContext->pUblockConfig->drop_queue_entry);
    ix_rm_hw_queue_delete(hDropQueue);
    /* rm memory de-allocation */
    ix_rm_mem_free((void*)pContext->pUblockConfig->qd_sram_virtual_base);

    ix_ossl_free(pContext->pUblockConfig);
    ix_ossl_free(pContext);
}


/**
 * NAME: _ix_cc_qm_ublock_pkt_handler()
 *
 * DESCRIPTION: Core to microblock packet handling function internal to QM 
 * 
 * @Param:  -   IN ix_buffer_handle arg_hBuffer - 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 ; Note this function does not translate rm errors, relying
 *          that whoever calls this function will do the translation.
 */

ix_error _ix_cc_qm_ublock_pkt_handler(
                                ix_buffer_handle arg_hBuffer, 
                                ix_uint32 arg_ExceptionCode,
                                void* arg_pContext 
                              )
{
    ix_uint32 instanceCC;
    
#ifdef IX_QM_PKT_DUMP
    static const char s_aFunctionIdentifier[] = "_ix_cc_qm_ublock_pkt_handler";
#endif /* IX_QM_PKT_DUMP */

    ix_error err = IX_SUCCESS;

    instanceCC = IX_HANDLE_TO_INSTANCE(((ix_cc_qm_context *)arg_pContext)->hQMHandle); 
    
    err = ix_rm_packet_send(IX_CC_QM_INSTANCE_TABLE_GET(instanceCC,pktUblockOut),
                            arg_hBuffer,
                            arg_ExceptionCode);

    return err; 

}




#if IX_QM_TEST
/**
 * NAME: qm_get_free_list()
 *
 * DESCRIPTION: gets the global handle of the free list - used for testing only
 * 
 * @Param:  -   OUT ix_buffer_free_list_handle* arg_phFreeList - were handle of the buffer free list will be 
 *              stored. 
 *
 * @Return: IX_SUCCESS or valid ix_error if code fails ; 
 */

ix_error qm_get_free_list(ix_buffer_free_list_handle* arg_phFreeList)
{
    ix_error err = IX_SUCCESS;
    if (NULL == arg_phFreeList)
    {
        return IX_ERROR_WARNING(IX_CC_ERROR_NULL,("Null input argument"));
    }
    *arg_phFreeList = g_hFreeList;
    return err;
}


#endif

