/*******************************************************************************
 * (c) Copyright 2015 Microsemi Corporation.  All rights reserved.
 *
 *
 *
 * SVN $Revision: $
 * SVN $Date: $
 */

#include <api_oam_sdk/common/error_codes/inc/mr_error_codes.h>
#include <api_oam_sdk/common/registers/inc/mr_registers.h>
#include <api_oam_sdk/metroam_hal/inc/mr_hal.h>
#include <api_oam_sdk/table_access/inc/table_access.h>
#include <api_oam_sdk/table_access/inc/table_access_constants.h>
#include <stdio.h>
#include <stdlib.h>

#include "egl_assert.h"
#include "egl_log.h"

uint16_t	lastFreeRMepDBIdx;
extern	lcPmNumAlloc_t		lcPmNumAllocList[MAX_NUM_PM_SERVICES];
extern  maList_t            mrMAList[MAX_NUM_MEPS];

extern	volatile pmNum_t				lastFreePmNum;
extern	volatile lcNum_t				lastFreeLcNum;

extern 	char	msgStr[500];



static uint16_t		log_handle				= 0;						// handle used to log messages for this module

// -----------------------------------------------------------------------------------------------------
// This function must be called to initialise all global parameters associated with
// the HW memory scavenger code. It must be called before any MEPs or services are
// added to the HW.
// -----------------------------------------------------------------------------------------------------
void initMemAlloc(void)
{
	int		k;

	log_init();

	// Get a log handle if we don't already have one
	if (log_handle == 0)
		log_handle = log_register("TBL_ALLOC");
	
	DO_LOG_DEBUG(log_handle,"initMemAlloc:Initialise Scavenger parameters");

	// These pointers need to be initialised for use in the rMep table scavenger.
	lastFreeRMepDBIdx = 0;

	for (k=0;k<(MAX_NUM_MEPS);k++)
	{
		clrRMepDBInUse((uint32_t)k);						// mark all entries as free
	}
	
	for (k=0;k<(MAX_NUM_MEPS * 8);k++)
	{
		writeRawRxLMepNumRamLower(k,mp_tableEmpty,wanPort);	// Marks all entries in the RxLMepNum Tables as 'Not In Use'
		writeRawRxLMepNumRamUpper(k,RX_LMEP_NUM_RAM_IDM_PM_NUM_MASK | RX_LMEP_NUM_RAM_LMEP_NUM_MASK,wanPort);
		writeRawRxLMepNumRamLower(k,mp_tableEmpty,lanPort);	// Marks all entries in the RxLMepNum Tables as 'Not In Use'
		writeRawRxLMepNumRamUpper(k,RX_LMEP_NUM_RAM_IDM_PM_NUM_MASK | RX_LMEP_NUM_RAM_LMEP_NUM_MASK,lanPort);
	}

	// Mark all entries in the LMep Tables as 'Not In Use'
	for (k=0;k<(MAX_NUM_MEPS);k++)
	{
		clrLMepInUse((uint32_t)k);		// mark all entries as free
	}

	for (k=0;k<(MAX_NUM_MEPS*MAX_RMEPS_PER_LM);k++)
	{
		clrRMepTblInUse(k);
	}
	
	// Mark all txLMepNum and PmNum entries as invalid and set lMepNum to a null value
	for (k=0;k< 512;k++)//(MAX_NUM_PM_SERVICES > MAX_NUM_MEPS ? MAX_NUM_PM_SERVICES : MAX_NUM_MEPS);k++)
		mrHalRamWrite(k, TX_LMEP_MAP_SEL, 0);
		//writeRawPmTxLMepNumRam(k,0);
}

/*
 * This function is used to change the lowest level severity to log for
 * this module. Values of eLOG_LEVEL_DEBUG (lowest) to eLOG_LEVEL_ERROR
 * can be selected. Levels with greater severity than eLOG_LEVEL_ERROR
 * will default to eLOG_LEVEL_ERROR since severity of eLOG_LEVEL_ERROR
 * and higher are non maskable.
 */
void setTableMemAllocLogLevel(log_level_t logLevel)
{
	setModuleLogLevel(log_handle,logLevel);
}

// -----------------------------------------------------------------------------------------------------
// This function is used to retrieve the index of the next free location in the RMepDB RAM
// It searches the table from the last known free location until the last index in the RAM
// If a free location is found the index is returned and the return value is NO_ERR.
// If a free location is not found then the ERR_HW_RAM_FULL code is returned.
// -----------------------------------------------------------------------------------------------------
int requestNextFreeRMepDBIdx(uint16_t *index)
{
	uint16_t	k;

	for (k=lastFreeRMepDBIdx;k<(MAX_NUM_MEPS);k++)
	{
		if (isFreeRMepDB(k))
		{
			*index = k;
			lastFreeRMepDBIdx = k;
			DO_LOG_DEBUG(log_handle,"requestNextFreeRMepDBIdx:Allocated location %d in rMepDB RAM",k);
			return(NO_ERR);
		}
	}
	DO_LOG_INFO(log_handle,"requestNextFreeRMepDBIdx:rMepDB RAM full");
	return(ERR_HW_RAM_FULL);
}

// -----------------------------------------------------------------------------------------------------
// This function is called to indicate to the Scavenger that the previously request
// 'free' index has been accepted and put to use, so it must be marked as no longer
// free.
// -----------------------------------------------------------------------------------------------------
void acceptNextFreeRMepDBIdx(uint16_t index)
{
	DO_LOG_DEBUG(log_handle,"acceptNextFreeRMepDBIdx:Accepted index %d",index);
	setRMepDBInUse(index);
}

// -----------------------------------------------------------------------------------------------------
// This function is called to indicate to the Scavenger that the previously used
// index can now be added to the free pool.
// -----------------------------------------------------------------------------------------------------
void releaseRMepDBIdx(uint16_t index)
{
	clrRMepDBInUse(index);

	// update the last free index if the released index was a lower index
	if (index < lastFreeRMepDBIdx)
		lastFreeRMepDBIdx = index;

	DO_LOG_DEBUG(log_handle,"releaseRMepDBIdx:Released index %d",index);
}


// -----------------------------------------------------------------------------------------------------
// This function is used to retrieve the index of the next blockLength consecutive free
// locations in the rxLMepNum RAM.
// It searches the table from the last known free location until the last index in the RAM
// If blockLength consecutive free locations are found the index to the start of this list
// is returned and the return value is NO_ERR.
// If blockLength consecutive free locations are not found but there are blockLength free
// location in the list then the function attempts to move other list entries to
// concatenate the free entries in to consecutive locations.
// If there are not blockLength consecutive free locations then the ERR_HW_RAM_FULL
// code is returned.
// -----------------------------------------------------------------------------------------------------
int requestNextFreeRxLMepRAMIdx(rxLMepTblIdx_t *startIndex, iFace_t iFace)
{
	rxLMepTblIdx_t		k;
	//log_handle = 2;
#ifdef DEBUG_PRINT_L2
	ts_printf("table:requestNextFreeRxLMepRAMIdx\n\r");
#endif
	for (k=0;k<MAX_NUM_MEPS;k++)
	{
	    //ts_printf("requestNextFreeRMepDBIdx:Allocated location %d in rxLMep RAM1,iFace %d\n\r ",k,iFace);
		// Test is the next entry free?
		if (isFreeRxLMepNumRam(k,iFace))
		{
			// Entry is free...
			*startIndex = k;
			//DO_LOG_DEBUG(log_handle,"requestNextFreeRMepDBIdx:Allocated location %d in rxLMep RAM",k);
			//ts_printf("requestNextFreeRMepDBIdx:Allocated location %d in rxLMep RAM",k);
			return(NO_ERR);
		}
	}
#ifdef DEBUG_PRINT_L2
	ts_printf("requestNextFreeRMepDBIdx:rxLMep RAM full\n\r");
#endif
	//DO_LOG_INFO(log_handle,"requestNextFreeRMepDBIdx:rxLMep RAM full");
	return(ERR_HW_RAM_FULL);	// The RAM is full so exit with RAM FULL Error
}

// -----------------------------------------------------------------------------------------------------
// This function is called to indicate to the Scavenger that the previously request
// 'free' index has been accepted and put to use, so it must be marked as no longer
// free.
// -----------------------------------------------------------------------------------------------------
void acceptNextFreeRxLMepRAMIdx(rxLMepTblIdx_t index, iFace_t iFace)
{
	DO_LOG_DEBUG(log_handle,"acceptNextFreeRxLMepRAMIdx:Accepted index %d",index);
	setRxLMepNumRamInUse(index,iFace);
}


// -----------------------------------------------------------------------------------------------------
// This function is called to indicate to the Scavenger that the previously used
// list of length blockLength starting at index can now be added to the free pool.
// -----------------------------------------------------------------------------------------------------
void releaseRxLMepRAMIdx(rxLMepTblIdx_t index, iFace_t iFace)
{
	DO_LOG_DEBUG(log_handle,"releaseRxLMepRAMIdx:Released index %d",index);
	clrRxLMepNumRamInUse(index,iFace);
}

// -----------------------------------------------------------------------------------------------------
// This function is used to retrieve the index of the next free location in the lMep RAM
// It searches the table from the last known free location until the last index in the RAM
// If a free location is found the index is returned and the return value is NO_ERR.
// If a free location is not found then the ERR_HW_RAM_FULL code is returned.
// -----------------------------------------------------------------------------------------------------
int requestNextFreeLMepTblIdx(maNum_t maNum, lMepNum_t *index)
{
#if 0
	int				retVal;
	lMepNum_t		k;
	lMepTblEntry_t	lMepTblEntry;
	uint32_t		rdData;

	// first see if the LMep can be placed in the same location as the associated MA
	// This is not always possible if there is more than 1 lMep on that MA
	if ((retVal = readRawLMepTbl(mrMAList[maNum].maidIdx*LMEP_ENTRY_SIZE,&rdData)))
		return(retVal);

	if ((((rdData & LMEP_RAM_MA_NUM_MASK) >> LMEP_RAM_MA_NUM_OFFSET) == maNum) && ((rdData & LMEP_RAM_MEPID_MASK) == 0))
	{
		*index = mrMAList[maNum].maidIdx;
		DO_LOG_DEBUG(log_handle,"requestNextFreeLMepTblIdx:Allocated location %d in LMepTbl",*index);
		return(NO_ERR);
	}

	// If the mrMAList[maNum].maidIdx is already occupied then we need to find a empty entry
	for (k=0;k<MAX_NUM_MEPS;k++)
	{
		if (isFreeLMep(k))
		{
			// Read the MAID from the maNum index
			if ((retVal = readLMepTbl(mrMAList[maNum].maidIdx,&lMepTblEntry)))
				return(retVal);

			// Copy it to the newly allocated location
			lMepTblEntry.lMepID = 0;
			//lMepTblEntry.interval = 0;					// Clear any existing
			//lMepTblEntry.maNum = MR_NULL_MA_NUM;			// set to Null in case the calling function doesn't end up using the entry
			//lMepTblEntry.mdNum = MR_NULL_MD_NUM;			// set to Null in case the calling function doesn't end up using the entry
			if ((retVal = writeLMepTbl(k,&lMepTblEntry)))
				return(retVal);

			*index = k;
			DO_LOG_DEBUG(log_handle,"requestNextFreeLMepTblIdx:Allocated location %d in LMepTbl",k);
			return(NO_ERR);
		}
	}
#endif
	DO_LOG_INFO(log_handle,"requestNextFreeLMepTblIdx:LMepTbl Full");
	return(ERR_HW_RAM_FULL);
}

// -----------------------------------------------------------------------------------------------------
// This function is called to indicate to the Scavenger that the previously request
// 'free' index has been accepted and put to use, so it must be marked as no longer
// free.
// -----------------------------------------------------------------------------------------------------
void acceptNextFreeLMepTblIdx(lMepNum_t index)
{
	DO_LOG_DEBUG(log_handle,"acceptNextFreeLMepTblIdx:Accepted index %d",index);
	setLMepInUse(index);
}

// -----------------------------------------------------------------------------------------------------
// This function is called to indicate to the Scavenger that the previously used
// index can now be added to the free pool.
// -----------------------------------------------------------------------------------------------------
void releaseLMepTblIdx(lMepNum_t index)
{
	DO_LOG_DEBUG(log_handle,"releaseLMepTblIdx:Released index %d",index);
	clrLMepInUse(index);
}

