/*
* co_timer.c - contains timer routines
*
* Copyright (c) 2012-2023 emotas embedded communication GmbH
*-------------------------------------------------------------------
* $Id: co_timer.c 51857 2024-01-19 07:23:08Z boe $
*
*
*-------------------------------------------------------------------
*
*
*/

/********************************************************************/
/**
* \brief timer routines
*
* \file co_timer.c
* contains timer routines
*
*/

/* header of standard C - libraries
---------------------------------------------------------------------------*/
#include <string.h>

/* header of project specific types
---------------------------------------------------------------------------*/
#include <gen_define.h>

#include <co_datatype.h>
#include <co_timer.h>
#include <co_commtask.h>
#include "ico_commtask.h"
#include "ico_timer.h"

/* constant definitions
---------------------------------------------------------------------------*/

/* local defined data types
---------------------------------------------------------------------------*/

/* list of external used functions, if not in headers
---------------------------------------------------------------------------*/

/* list of global defined functions
---------------------------------------------------------------------------*/

/* list of local defined functions
---------------------------------------------------------------------------*/
static RET_T addTimer(CO_LINE_DECL, CO_TIMER_T *pTimer);
static CO_TIMER_T *deleteTimer(CO_LINE_DECL, CO_CONST CO_TIMER_T *pTimer);

/* external variables
---------------------------------------------------------------------------*/

/* global variables
---------------------------------------------------------------------------*/

/* local defined variables
---------------------------------------------------------------------------*/
static UNSIGNED32	timerInterVal;
static CO_TIMER_T	*pCoTimer[CO_LINE_ARRAY_DECL];
static UNSIGNED32	actTick[CO_LINE_ARRAY_DECL];


/***************************************************************************/
/**
* \brief coTimerStart - start a timer
*
* This function starts a timer with the given timer interval (in usec).
* If the timer is elapsed,
* the indication function pointed by ptrToFct() with the parameter pData
* is called.
*
* Single-shot or cyclic timer can be defined using the CO_TIMER_ATTR_T attribute.
*
* \return
*	RET_T
*/
RET_T coTimerStart(
		CO_LINE_TYPE	CO_LINE,	/**< can line */
		CO_TIMER_T	*pTimer,			/**< pointer to timerstruct */
		UNSIGNED32		timerTime,			/**< timer time in usec */
		CO_TIMER_FCT_T pFct,				/**< function at timer elapsed */
		void			*pData,				/**< pointer for own data */
		CO_TIMER_ATTR_T	timerAttributes	/**< timer attributes */
	)
{
RET_T	retVal;

	/* check for 0 timer */
	if (timerTime == 0u)  {
		return(RET_INVALID_PARAMETER);
	}

	/* check for pFct is NULL */
	if (pFct == NULL)  {
		return(RET_INVALID_PARAMETER);
	}


	/* stop timer and check for validity of pTimer */
	(void)deleteTimer(CO_LINE, pTimer);

	/* calculate timer ticks */
	pTimer->ticks = timerTime / timerInterVal;
	if ((timerAttributes == CO_TIMER_ATTR_ROUNDUP)
	 || (timerAttributes == CO_TIMER_ATTR_ROUNDUP_CYCLIC))  {
		if ((timerTime % timerInterVal) != 0u)  {
			pTimer->ticks++;
		}
	}
	if (pTimer->ticks == 0u)  {
		pTimer->ticks = 1u;
	}

	pTimer->pFct = pFct;
	pTimer->attr = timerAttributes;
	pTimer->pData = pData;

	retVal = addTimer(CO_LINE, pTimer);

	return(retVal);
}


/***************************************************************************/
/**
* \brief coTimerStop - stop a timer
*
* This function stops the given timer.
*
* \return RET_T
* \retval RET_OK
*	timer successful removed
* \retval RET_INVALID_PARAMETER
*	timer not in timer list
*/
RET_T coTimerStop(
		CO_LINE_TYPE	CO_LINE,	/**< can line */
		CO_TIMER_T	*pTimer		/**< pointer to timerstruct */
	)
{
CO_TIMER_T	*pT;

	if (pTimer == NULL)  {
		return(RET_INVALID_PARAMETER);
	}

	pT = deleteTimer(CO_LINE, pTimer);
	if (pT == NULL)  {
		/* mark timer as stopped, for call in a cyclic timer callback */
		pTimer->attr = CO_TIMER_ATTR_STOPPED;

		return(RET_INVALID_PARAMETER);
	}

	return(RET_OK);
}


/***************************************************************************/
/**
* \brief coTimerIsActive - check if timer is active
*
* With this function can be ckecked,
* if a timer is currently in the timer list.
*
* \return BOOL_T
* \retval CO_TRUE
*	timer is active
* \retval CO_FALSE
*	timer is not active
*
*/
BOOL_T coTimerIsActive(
		CO_LINE_TYPE	CO_LINE,	/**< can line */
		CO_CONST CO_TIMER_T	*pTimer		/**< pointer to timer struct */
	)
{
CO_TIMER_T	*pT = pCoTimer[CO_LINE];

	while (pT != NULL)  {
		if (pT == pTimer)  {
			return(CO_TRUE);
		}
		pT = pT->pNext;
	}

	return(CO_FALSE);
}


/***************************************************************************/
/**
* \brief coTimerAttrChange - change timer attribute
*
* With this function timer attribute can be change.
*
*
*/
void coTimerAttrChange(
		CO_TIMER_T	*pTimer,			/**< pointer to timerstruct */
		CO_TIMER_ATTR_T	timerAttributes	/**< timer attributes */
	)
{
	pTimer->attr = timerAttributes;

	return;
}


/***************************************************************************/
/**
* \internal
*
* \brief addTimer - add timer to timer list
*
* \return RET_T
*
*/
static RET_T addTimer(
		CO_LINE_TYPE	CO_LINE,	/* can line */
		CO_TIMER_T		*pTimer		/* pointer to timer structure */
	)
{
CO_TIMER_T	*pT;
CO_TIMER_T	*pLast;

	pTimer->actTicks = pTimer->ticks + actTick[CO_LINE];

	/* first timer ? */
	if (pCoTimer[CO_LINE] == NULL)  {
		pCoTimer[CO_LINE] = pTimer;
		pTimer->pNext = NULL;
		return(RET_OK);
	}

	/* look for correct index */
	pT = pCoTimer[CO_LINE];
	pLast = NULL;
	while ((pTimer->actTicks >= pT->actTicks) && (pT->pNext != NULL))  {
		pLast = pT;
		pT = pT->pNext;
	}

	/* save it before */
	if (pTimer->actTicks < pT->actTicks)  {
		if (pLast == NULL)  {
			pCoTimer[CO_LINE] = pTimer;
		} else {
			pLast->pNext = pTimer;
		}
		pTimer->pNext = pT;
	} else {
		if (pT->pNext == NULL)  {
			pT->pNext = pTimer;
			pTimer->pNext = NULL;
		} else {
			pTimer->pNext = pT->pNext;
			pT->pNext = pTimer;
		}
	}

	return(RET_OK);
}


/***************************************************************************/
/**
* \internal
*
* \brief deleteTimer - delete timer from the timer list
*
* \return NULL - timer was not in the list
* \return pTimer - timer succesfully deleted
*
*/
static CO_TIMER_T* deleteTimer(
		CO_LINE_TYPE	CO_LINE,	/* can line */
		CO_CONST CO_TIMER_T	*pTimer		/* pointer to timer structure */
	)
{
CO_TIMER_T	*pT = pCoTimer[CO_LINE];
CO_TIMER_T	*pLast = NULL;

	while (pT != NULL)  {
		if (pT == pTimer)  {
			if (pLast == NULL)  {
				pCoTimer[CO_LINE] = pT->pNext;
			} else {
				pLast->pNext = pT->pNext;
			}
			pT->pNext = NULL;
			return(pT);
		}
		pLast = pT;
		pT = pT->pNext;
	}

	return(NULL);
}


/***************************************************************************/
/**
* \brief coTimerTick - timer tick elapsed
*
* This function should be called,
* if the CANopen timer has been elapsed
* to signal a new timer interval to the stack.
*
* It can be called at interrupt level.
*
*
*/
void coTimerTick(
		void
	)
{
CO_LINE_TYPE	CO_LINE;

	for (canLine = 0u; canLine < CO_MULTI_LINE_CNT; canLine ++)
	{
		actTick[CO_LINE]++;
		coCommTaskSet(CO_LINE, CO_COMMTASK_EVENT_TIMER);
	}
}


/***************************************************************************/
/**
* \internal
*
* \brief icoTimerCheck - check next timer
*
*
*/
void icoTimerCheck(
		CO_LINE_TYPE	CO_LINE		/* can line */
	)
{
CO_TIMER_T	*pT;
CO_TIMER_T	*pAct = NULL;
UNSIGNED32	ticks;

	/* if no timer enabled ? */
	if (pCoTimer[CO_LINE] == NULL)  {
		actTick[CO_LINE] = 0u;
		return;
	}

	/* save act ticks */
	pT = pCoTimer[CO_LINE];
	ticks = actTick[CO_LINE];

	/* first timer reached ? */
	if (ticks < pT->actTicks)  {
		return;
	}

	actTick[CO_LINE] -= ticks;

	if (ticks > pT->actTicks)  {
		ticks = pT->actTicks;
	}

	/* sub ticks from all timers */
	while (pT != NULL)  {
		pT->actTicks -= ticks;
		pT = pT->pNext;
	}

	/* call timer functions */
	pT = pCoTimer[CO_LINE];
	while (pT != NULL)  {
		if (pT->actTicks == 0u)  {
			pAct = pT;
			pT = pAct->pNext;

			/* remove timer from list */
			(void)deleteTimer(CO_LINE, pAct);

			/* call function */
			if (pAct->pFct != NULL)  {
				pAct->pFct(CO_LINE, pAct->pData);
			}

			/* cyclic timer ? */
			if ((pAct->attr == CO_TIMER_ATTR_ROUNDUP_CYCLIC)
			 || (pAct->attr == CO_TIMER_ATTR_ROUNDDOWN_CYCLIC))  {
				/* add timer again */
				(void)addTimer(CO_LINE, pAct);
			}
		} else {
			break;
		}
	}
}


/***************************************************************************/
/**
* \brief coTimerInit - init timer interval
*
* This function initializes the internal timer handling.
* It does nothing with the hardware timer
* and initializes only internal variables.
* <br>The given timer interval is used to calculate
* the timer period for timer depending functions
* started by coTimerStart().
*
*/
void coTimerInit(
		UNSIGNED32	timerVal		/**< timer interval in usec */
	)
{
CO_LINE_TYPE	CO_LINE;

	timerInterVal = timerVal;
	for (CO_LINE = 0u; CO_LINE < CO_MULTI_LINE_CNT; CO_LINE++)
	{
		pCoTimer[CO_LINE] = NULL;
		actTick[CO_LINE] = 0u;
	}
}


