/* HEX DUMP for BDs !!! Debug code only !!! */
mcpf_res_t report_PrintDump (McpU8 *pData, McpU32 datalen)
{
#ifdef _DEBUG
    McpS32  dbuflen=0;
    McpU32 j;
    char   dbuf[50];
    static const char hexdigits[16] = "0123456789ABCDEF";

    for(j=0; j < datalen;)
    {
        /* Add a byte to the line*/
        dbuf[dbuflen] =  hexdigits[(pData[j] >> 4)&0x0f];
        dbuf[dbuflen+1] = hexdigits[pData[j] & 0x0f];
        dbuf[dbuflen+2] = ' ';
        dbuf[dbuflen+3] = '\0';
        dbuflen += 3;
        j++;
        if((j % 16) == 0)
        {
            /* Dump a line every 16 hex digits*/
            MCPF_OS_REPORT(("%04.4x  %s\n", j-16, dbuf));
            dbuflen = 0;
        }
    }
    /* Flush if something has left in the line*/
    if(dbuflen)
        MCPF_OS_REPORT(("%04.4x  %s\n", j & 0xfff0, dbuf));
#else
    MCP_UNUSED_PARAMETER(pData);
    MCP_UNUSED_PARAMETER(datalen);
#endif
    return E_COMPLETE;
}
/** 
 * \fn     que_Create 
 * \brief  Create a queue. 
 * 
 * Allocate and init a queue object.
 * 
 * \note    
 * \param  hOs               - Handle to Os Abstraction Layer
 * \param  hReport           - Handle to report module
 * \param  uLimit            - Maximum items to store in queue
 * \param  uNodeHeaderOffset - Offset of NodeHeader field from the entry of the queued item.
 * \return Handle to the allocated queue 
 * \sa     que_Destroy
 */ 
handle_t que_Create (handle_t hMcpf, McpU32 uLimit, McpU32 uNodeHeaderOffset)
{
	TQueue *pQue;

	if(!hMcpf)
	{
		MCPF_OS_REPORT (("Mcpf handler is NULL\n"));
		return NULL;
	}

	/* allocate queue module */
	pQue = mcpf_mem_alloc (hMcpf, sizeof(TQueue));
	
	if (!pQue)
	{
		MCPF_OS_REPORT (("Error allocating the Queue Module\n"));
		return NULL;
	}
	
    mcpf_mem_zero (hMcpf, pQue, sizeof(TQueue));

	MCP_DL_LIST_InitializeHead (&pQue->tHead);

	/* Set the Queue parameters */
    pQue->hMcpf           = hMcpf;
	pQue->uLimit            = uLimit;
	pQue->uNodeHeaderOffset = uNodeHeaderOffset;

	return (handle_t)pQue;
}
void que_Print(handle_t hQue)
{
	TQueue *pQue = (TQueue *)hQue;

    MCPF_OS_REPORT(("que_Print: Count=%u MaxCount=%u Limit=%u Overflow=%u NodeHeaderOffset=%u Next=0x%x Prev=0x%x\n",
                    pQue->uCount, pQue->uMaxCount, pQue->uLimit, pQue->uOverflow, 
                    pQue->uNodeHeaderOffset, (McpU32)pQue->tHead.next, (McpU32)pQue->tHead.prev));
}
/** 
 * \fn     mcpf_getCurrentTime_inSec
 * \brief  Return time in seconds
 * 
 * This function will return the time in seconds from 1.1.1970.
 * 
 * \note
 * \param	hMcpf     - MCPF handler.
 * \return 	The time in seconds from 1.1.1970.
 * \sa     	mcpf_getCurrentTime_inSec
 */  
McpU32		mcpf_getCurrentTime_inSec(handle_t hMcpf)
{
	if (!hMcpf)
	{
		MCPF_OS_REPORT(("Mcpf handler is NULL\n"));
		return 0;
	}	
	return (os_get_current_time_inSec(((Tmcpf*)hMcpf)->hPla));  
}
static McpS32 mcpf_getExpiryInMilliSecs(handle_t hMcpf, McpU32 uEexpiryAbsoluteTime)
{	
	if (!hMcpf) 
	{
		MCPF_OS_REPORT(("Mcpf handler is NULL\n"));
		return 0;	
	}
	
	return (uEexpiryAbsoluteTime - mcpf_getCurrentTime_InMilliSec(hMcpf));
}
/** 
 * \fn     txnQ_Create 
 * \brief  Create the module
 * 
 * Allocate and clear the module's object.
 * 
 * \note   
 * \param  hMcpf - Handle to Os framework
 * \return Handle of the allocated object, NULL if allocation failed 
 * \sa     txnQ_Destroy
 */ 
handle_t txnQ_Create (const handle_t hMcpf)
{
    handle_t  hTxnQ;
    TTxnQObj  *pTxnQ;
    McpU32  i;

    hTxnQ = mcpf_mem_alloc(hMcpf, sizeof(TTxnQObj));
    if (hTxnQ == NULL)
        return NULL;
    
    pTxnQ = (TTxnQObj *)hTxnQ;

    mcpf_mem_zero(hMcpf, hTxnQ, sizeof(TTxnQObj));
    
    pTxnQ->hMcpf           = hMcpf;
    pTxnQ->pCurrTxn        = NULL;
    pTxnQ->uMinFuncId      = TXN_MAX_FUNCTIONS; /* Start at maximum and save minimal value in txnQ_Open */
    pTxnQ->uMaxFuncId      = 0;             /* Start at minimum and save maximal value in txnQ_Open */
    pTxnQ->bProtectTxnDone = MCP_TRUE;

    for (i = 0; i < TXN_MAX_FUNCTIONS; i++)
    {
        pTxnQ->aFuncInfo[i].eState          = FUNC_STATE_NONE;
        pTxnQ->aFuncInfo[i].uNumPrios       = 0;
        pTxnQ->aFuncInfo[i].pSingleStep     = NULL;
        pTxnQ->aFuncInfo[i].fTxnQueueDoneCb = NULL;
        pTxnQ->aFuncInfo[i].hCbHandle       = NULL;
    }
    
    /* Create the Bus-Driver module */
    pTxnQ->hBusDrv = busDrv_Create (hMcpf);
    if (pTxnQ->hBusDrv == NULL)
    {
        MCPF_OS_REPORT(("%s: Error - failed to create BusDrv\n", __FUNCTION__));
        txnQ_Destroy (hTxnQ);
        return NULL;
    }

    return pTxnQ;
}
/** 
 * \fn     mcpf_SLL_Create
 * \brief  Create a sorted linked list
 * 
 * This function creates a sorted linked list
 * 
 * \note
 * \param	hMcpf - MCPF handler.
 * \param	uLimit - max list length.
 * \param	uNodeHeaderOffset - Offset of node's header field in stored structure.
 * \param	uSortByFeildOffset - Offset of the field to sort by, in stored structure.
 * \param	tSortType - sort type, UP or DOWN.
 * \return 	List handler.
 * \sa     	mcpf_SLL_Create
 */ 
handle_t mcpf_SLL_Create (handle_t hMcpf, McpU32 uLimit, McpU32 uNodeHeaderOffset, 
							McpU32 uSortByFeildOffset, TSllSortType	tSortType)
{
    mcpf_SLL   *pSortedList;
    if (!hMcpf) 
	{
		MCPF_OS_REPORT(("Mcpf handler is NULL\n"));
		return NULL;	
	}
        
    /* Allocate memory for the sorted Linked List */
    pSortedList = mcpf_mem_alloc(hMcpf, sizeof(mcpf_SLL));

    if (NULL == pSortedList)
	{
		MCPF_REPORT_ERROR(pSortedList->hMcpf, MCPF_MODULE_LOG, ("mcpf_mem_alloc returned NULL\n"));
		return NULL;
	}
    
    /* Initialize the memory block to zero */
    mcpf_mem_zero(hMcpf, pSortedList, sizeof(mcpf_SLL));

    /* Initialize the list header */
	MCP_DL_LIST_InitializeHead (&pSortedList->tHead);

    /* Set the List paramaters */
	pSortedList->hMcpf = hMcpf;                  
	pSortedList->tSortType = tSortType;
	pSortedList->uSortByFieldOffset = (McpU16)uSortByFeildOffset;
	pSortedList->uNodeHeaderOffset  = (McpU16)uNodeHeaderOffset;
	pSortedList->uOverflow = 0;
	pSortedList->uLimit = (McpU16)uLimit;
	pSortedList->uCount = 0;
        
    return (handle_t)pSortedList;
}
/** 
 * \fn     mcpf_timer_start
 * \brief  Timer Start
 * 
 * This function will put the 'timer start' request in a sorted list, 
 * and will start an OS timer when necessary.
 * 
 * \note
 * \param	hMcpf     - MCPF handler.
 * \param	uExpiryTime - Requested expiry time in milliseconds (duration time).
 * \param	eTaskId - Source task id.
 * \param	*fCb - Timer expiration Cb.
 * \param	hCaller - Cb's handler.
 * \return 	Pointer to the timer handler.
 * \sa     	mcpf_timer_start
 */ 
handle_t  	mcpf_timer_start (handle_t hMcpf, McpU32 uExpiryTime, EmcpTaskId eTaskId, 
							  mcpf_timer_cb fCb, handle_t hCaller)
{
	Tmcpf				*pMcpf;
    TMcpfTimer			*pMcpfTimer, *pFirstTimer;
	McpU32				uCurrentExpiryTime = 0;
	McpU32				uListCount;
	
    if (!hMcpf) 
	{
		MCPF_OS_REPORT(("Mcpf handler is NULL\n"));
		return NULL;	
	}

	pMcpf = (Tmcpf*)hMcpf;

	/* Allocate Timer */
	pMcpfTimer = (TMcpfTimer*)mcpf_mem_alloc_from_pool(pMcpf, pMcpf->hTimerPool);

	if(NULL == pMcpfTimer)
	{
		MCPF_REPORT_ERROR(hMcpf, MCPF_MODULE_LOG, ("mcpf_timer_start, No memory available in TimerPool\n"));
        return NULL;
	}	 

	MCPF_ENTER_CRIT_SEC(hMcpf);
	/* Init Timer Fields */
	pMcpfTimer->uEexpiryTime = mcpf_getAbsoluteTime(hMcpf, uExpiryTime); 
	pMcpfTimer->eSource = eTaskId;
	pMcpfTimer->tCbData.fCb =  fCb ;                                                       
	pMcpfTimer->tCbData.hCaller = hCaller;    
	pMcpfTimer->eState = TMR_STATE_ACTIVE;		

	/* if list is not empty get current expiry time(Absolute) */
	uListCount = mcpf_SLL_Size(pMcpf->hTimerList);
	if(uListCount != 0)
	{
		/* Get the first timer on the list*/
		pFirstTimer = (TMcpfTimer*) mcpf_SLL_Get(pMcpf->hTimerList);	
		uCurrentExpiryTime = pFirstTimer->uEexpiryTime;
	}

	/* Insert new Timer to the list */
	if (RES_ERROR == mcpf_SLL_Insert(pMcpf->hTimerList, pMcpfTimer))
	{
		MCPF_EXIT_CRIT_SEC(hMcpf);
		mcpf_mem_free_from_pool(hMcpf, pMcpfTimer);
		MCPF_REPORT_ERROR(hMcpf, MCPF_MODULE_LOG, ("mcpf_SLL_Insert Failed\n"));
        return NULL;
	}	        

	/* If list was empty before insertion (first timer on the list) --> Start the OS Timer */
    if (uListCount == 0)
	{
		pMcpf->uTimerId  = os_timer_start(pMcpf->hPla, mcpf_timer_callback, 
											hMcpf, uExpiryTime);
	}	
	else if ( uCurrentExpiryTime > (pMcpfTimer->uEexpiryTime))
	{
		/* The previous first timer on the list is having greater exp time 
		   than the new one. Stop this timer and start the new one.*/
      
		/* Stop the current timer */
        if (MCP_FALSE == os_timer_stop(pMcpf->hPla, pMcpf->uTimerId))
		{
			MCPF_EXIT_CRIT_SEC(hMcpf);
			mcpf_mem_free_from_pool(hMcpf, pMcpfTimer);
			MCPF_REPORT_ERROR(hMcpf, MCPF_MODULE_LOG, ("os_timer_stop Failed\n"));
		    return NULL; 		
		}

		/* Start the new timer */
		pMcpf->uTimerId  = os_timer_start(pMcpf->hPla, mcpf_timer_callback, hMcpf, uExpiryTime);
		
	} 

	MCPF_EXIT_CRIT_SEC(hMcpf);
	return ((handle_t)pMcpfTimer);
}
/** 
 * \fn     mcpf_timer_callback
 * \brief  General Timer Callback
 * 
 * This function will be called upon expiry time interrupt.
 * 
 * \note
 * \param	hMcpf     - MCPF handler.
 * \param	uTimerId     - The timer Id of the expired timer
 * \return 	None
 * \sa     	mcpf_timer_callback
 */  
static void 	mcpf_timer_callback(handle_t hMcpf, McpUint uTimerId)
{
	Tmcpf		*pMcpf;
    TMcpfTimer  	*pMcpfTimer;    
	McpU32		uExpiryTime;
	TMcpfTimer  	*pExpiredTimersQ[10]; 
	McpU16		i, counter = 0;

    if (!hMcpf) 
	{
		MCPF_OS_REPORT(("Mcpf handler is NULL\n"));
		return;	
	}
	pMcpf = (Tmcpf*)hMcpf;

	MCPF_ENTER_CRIT_SEC(hMcpf);

	/* Check if expired timer is already handled, and now another OS timer was alreay started */
	if(uTimerId != pMcpf->uTimerId)
	{
		MCPF_EXIT_CRIT_SEC(hMcpf);
		return;
	}
	
	do
    	{
		pMcpfTimer = (TMcpfTimer*)mcpf_SLL_Retrieve(pMcpf->hTimerList);

		/* This is added to handle race condition between 
		   this function and the 'mcpf_timer_stop()' function. 
		   This condition can only be valid in the first iteration of the loop.
		   The timer was stopped before the expiration Cb is handled, 
		   and the timer is already been removed from the timer's list. */
		if(pMcpfTimer == NULL)
		{
			MCPF_EXIT_CRIT_SEC(hMcpf);
			return;
		}
		
		/* Store a copy of the exp time of retrieved timer */
		uExpiryTime = pMcpfTimer->uEexpiryTime;

		pMcpfTimer->eState	= TMR_STATE_EXPIRED;	

		/* Save all expired timers, in order to send an 
		    experation message outside the critical section. */
		pExpiredTimersQ[counter] = pMcpfTimer;
		counter++;

		/* Get the Next Timer from the List */
		pMcpfTimer = mcpf_SLL_Get(pMcpf->hTimerList);		

    	} while( (pMcpfTimer) && (uExpiryTime == pMcpfTimer->uEexpiryTime) );
    
	if(pMcpfTimer)
	{
		/* Start the next timer request */
		uExpiryTime = mcpf_getExpiryInMilliSecs(hMcpf, pMcpfTimer->uEexpiryTime);    
		pMcpf->uTimerId = os_timer_start(pMcpf->hPla, mcpf_timer_callback, hMcpf, uExpiryTime);  
	}
	MCPF_EXIT_CRIT_SEC(hMcpf);

	for(i = 0; i < counter; i++)
	{
		pMcpfTimer = pExpiredTimersQ[i];
		mcpf_SendMsg(hMcpf, 
					pMcpfTimer->eSource,	/* Destination task is the task that started the timer */
					0,						/* Timer's Queue Id is alway 0 */
					pMcpfTimer->eSource,	/* Source task is the task that started the timer */
					0,						/* Since in timer queue there will be only
												message about the timer expiration */
					0,						/* opcode */
					sizeof(TMcpfTimer),		/* Length of the data */
					0,						/* User Defined is NOT USED*/
					pMcpfTimer);			/* The data is the timer structure */
	}
}
/** 
 * \fn     mcpf_timer_stop
 * \brief  Timer Stop
 * 
 * This function will remove the timer from the list
 * and will stop the OS timer when necessary.
 * 
 * \note
 * \param	hMcpf     - MCPF handler.
 * \param	hTimer - Timer's handler.
 * \return 	Result of operation: OK or ERROR
 * \sa     	mcpf_timer_stop
 */  
EMcpfRes	 	mcpf_timer_stop (handle_t hMcpf, handle_t hTimer)
{
	Tmcpf		*pMcpf;
    TMcpfTimer	*pMcpfTimer;
	McpS32		uExpiryTime;
	TMcpfTimer  	*pExpiredTimersQ[10]; 
	McpU16		i, counter = 0;
	McpBool		bIsOnlyTimer = MCP_FALSE;
	McpBool		bIsFirstTimer = MCP_FALSE;
	EMcpfRes		eRetVal = RES_OK;

    if (!hMcpf) 
	{
		MCPF_OS_REPORT(("Mcpf handler is NULL\n"));
		return RES_ERROR;
	}
        	
	pMcpf = (Tmcpf*)hMcpf;

    pMcpfTimer = (TMcpfTimer *)hTimer;

	MCPF_ENTER_CRIT_SEC(hMcpf);
	/* hTimer has already expired */
	if (pMcpfTimer->eState == TMR_STATE_EXPIRED ) 
    {
	       pMcpfTimer->eState = TMR_STATE_DELETED;
		MCPF_EXIT_CRIT_SEC(hMcpf);
		MCPF_REPORT_INFORMATION(hMcpf, MCPF_MODULE_LOG, ("mcpf_timer_stop: hTimer has already expired"));
		return RES_OK;
    }

	
	/* If it is the first/only timer on the list --> need to stop the OS timer */
	if(mcpf_SLL_Size(pMcpf->hTimerList) == 1)
	{
		bIsOnlyTimer = MCP_TRUE;
	}
	else if(mcpf_SLL_Get(pMcpf->hTimerList) == pMcpfTimer)
		bIsFirstTimer = MCP_TRUE;
	
	/* Remove Timer From the List */
	if ( (eRetVal = mcpf_SLL_Remove(pMcpf->hTimerList,hTimer)) == RES_OK)
	{
		/* If it is the only timer on the list --> need to stop the OS timer */
		if(bIsOnlyTimer)    
		{ 
			if (MCP_TRUE == os_timer_stop(pMcpf->hPla, pMcpf->uTimerId))
				eRetVal = RES_COMPLETE;
			else
				eRetVal = RES_ERROR;
			
			pMcpf->uTimerId = 0;
		}
		else if(bIsFirstTimer) /* It is the First Timer on the list */
		{
			if (MCP_TRUE == os_timer_stop(pMcpf->hPla, pMcpf->uTimerId))
			{
				pMcpf->uTimerId = 0;
				pMcpfTimer = mcpf_SLL_Get(pMcpf->hTimerList);
				
				do
				{		
					/* Get actual exp time in msecs from absolute exp time in the list */
					uExpiryTime = mcpf_getExpiryInMilliSecs(hMcpf,pMcpfTimer->uEexpiryTime);
					if(uExpiryTime <= 0)
					{
						pExpiredTimersQ[counter] = mcpf_SLL_Retrieve(pMcpf->hTimerList);
						counter++;
					}
					else
					{
						pMcpf->uTimerId = os_timer_start(pMcpf->hPla, mcpf_timer_callback, hMcpf, (McpU32)uExpiryTime);
						break;
					}
					pMcpfTimer = mcpf_SLL_Get(pMcpf->hTimerList);	
					
				} while (pMcpfTimer);
			}
			else 
			{
				pMcpf->uTimerId = 0;
				eRetVal = RES_ERROR; 
			}
		} 
	}
	MCPF_EXIT_CRIT_SEC(hMcpf);

	for(i = 0; i < counter; i++)
	{
		pMcpfTimer = pExpiredTimersQ[i];
		mcpf_SendMsg(hMcpf, 
					pMcpfTimer->eSource,	/* Destination task is the task that started the timer */
					0,						/* Timer's Queue Id is alway 0 */
					pMcpfTimer->eSource,	/* Source task is the task that started the timer */
					0,						/* Since in timer queue there will be only
												message about the timer expiration */
					0,						/* opcode */
					sizeof(TMcpfTimer),		/* Length of the data */
					0,						/* User Defined is NOT USED*/
					pMcpfTimer);			/* The data is the timer structure */
	}
	
	/* Free Timer */
	if (RES_ERROR == mcpf_mem_free_from_pool(pMcpf, (McpU8*) hTimer))
		return RES_ERROR;

	return eRetVal;
}