/****************************************************************************/
/*                                                                          */
/*  Name        rvf_time.c                                                  */
/*                                                                          */
/*  Function    this file contains rvf time related functions               */
/*                                                                          */
/*  Version		0.1															*/
/*																			*/
/* 	Date       	Modification												*/
/*  ------------------------------------									*/
/*  3/12/99		Create														*/
/*  10/27/1999  remove all non-nucleus sections (#ifdef)					*/
/*	30/11/99	compliant to RV coding guidelines							*/
/*  31/01/2003  Timer Redesign/Impl.RV2    Gary TOTNEY						*/
/*																			*/
/*	Author		David Lamy-Charrier (dlamy@tif.ti.com)						*/
/*																			*/
/* (C) Copyright 1999 by Texas Instruments Incorporated, All Rights Reserved*/
/****************************************************************************/

#include "nucleus.h" 

#include "rvm/rvm_use_id_list.h"
#include "rvm/rvm_i.h"	// only for MBs [coupling issue]
#include "rvm/rvm_api.h"// MSG			[coupling issue]
#include "rvf/rvf_i.h"
#include "rvf/rvf_api.h"
#include <string.h>



/*
** Define the time control array
*/
static NU_TIMER		rvf_Timer[MAX_RVF_TASKS][RVF_NUM_TASK_TIMERS];

//extern T_RVF_MB_ID rvm_timer_mem_bank;

#define _RALLOC_TIMER_BLK(tb) rvf_get_buf(rvm_timer_mem_bank, sizeof(NU_TIMER), tb)
#define _RALLOC_TMS_MSG(id, tb) rvf_get_msg_buf(rvm_tm_notify_mem_bank, sizeof(T_RVF_TMS_MSG), id, tb)


/* PRIVATE FUNC FWD DECLs   */
T_RV_TM_ID _invoke_tm(T_RVF_G_ADDR_ID gid, UINT32 t, UINT8 cont, void* action, UINT8 lg);

/*******************************************************************************
**
** Function         _rvf_timer_expire
**
** Description      This internal function is called by Nucleus OS, when a task
**					timer expires. It sets the corresponding event.
**
** Returns          void
**
*******************************************************************************/
void _rvf_timer_expire(UNSIGNED timer_id)
{ 
	rvf_disable(14);
	/* send the corresponding timer event */
	rvf_send_event((UINT8)(timer_id / RVF_NUM_TASK_TIMERS),(UINT16)(EVENT_MASK(((timer_id % RVF_NUM_TASK_TIMERS)+4)) ) );
	rvf_enable();
}

void _rvf_tm_notify(UNSIGNED timer_id) {
	T_RVF_TM_UBLK			tmr;
	T_RVF_TM_ATTRIB_UBLK	attr;
	UINT32 tmid=0, cont=0, d;
	OPTION	opt;
	T_RVF_TMS_MSG*		 p_msg=NULL;
	
	if(timer_id) tmr.id=timer_id;

	if(NU_Timer_Information(tmr.ptr,attr.str, &opt, (UNSIGNED*)&d, (UNSIGNED*)&tmid, (UNSIGNED*)&d, (UNSIGNED*) &cont) == NU_INVALID_TIMER) { // use to get p_attrib
		rvf_send_trace ("FATAL: RVF TIMER ATTIRBUTE ERROR!",
						49,
						NULL_PARAM,
						RV_TRACE_LEVEL_WARNING,
						0);

	}
/*	printf("%ld %ld %d:",	attr.attrib.host_addr_id,
							attr.attrib.action,
							attr.attrib.legacyFlag);
*/
	if(!pRtAddrIdTable[attr.attrib.host_addr_id]) return; // Don't waste time in HISR			/* MUST BE REPLACED WITH CACHED MSG'ing  */

	// Will utilise active caching in near future, limiting HISR duration
	if((_RALLOC_TMS_MSG(RVM_TMS_MSG, (T_RVF_MSG**)&p_msg))==RVF_RED){
		rvf_send_trace ("FATAL: RVF TIMER HISR: Insufficient resources!",
						49,
						NULL_PARAM,
						RV_TRACE_LEVEL_WARNING,
						0);

		return; // (RV_MEMORY_ERR);
	} 
	p_msg->hdr.msg_id=RVM_TMS_MSG;
	p_msg->tm_id=tmr.id;
	p_msg->cont=cont;
	p_msg->action=attr.attrib.action;
	rvf_disable(14);
	rvf_send_priority_msg(attr.attrib.host_addr_id, p_msg); // NOTE: must have id and recognised in FSM core
	rvf_enable();
}

/********************************************************************************/
/*                                                                              */
/*    Function Name:   rvf_create_timer                                            */
/*                                                                              */
/*    Purpose:         This function is reponsible for stopping timers.         */
/*                                                                              */                      
/*    Revision History:                                                         */
/*                                                                              */
/********************************************************************************/
T_RVF_TIMER_ID rvf_create_timer(T_RVF_G_ADDR_ID		g_addrId,
							UINT32					tmDuration,
							BOOLEAN					isContinuous,
							void*					p_action) {
	
	return _invoke_tm(g_addrId, tmDuration, isContinuous, p_action, 0);
}

// this exists as part of an on-going initiative to deprecate rvf: start/stop timers
T_RV_TM_ID _invoke_tm(T_RVF_G_ADDR_ID gid, UINT32 t, UINT8 cont, void* action, UINT8 lg) {
	T_RVF_TM_UBLK			tmr;
	T_RVF_TM_ATTRIB_UBLK	tm_attrib;
	UINT32					re_sched=0;
	OPTION					opt=NU_DISABLE_TIMER;
	STATUS					status;

	if(t) opt=NU_ENABLE_TIMER;
	if(cont) re_sched=t;

	// TO DO... ret. 0 if alloc fails
	if(_RALLOC_TIMER_BLK((T_RVF_BUFFER**)&tmr.ptr)) return 0;
	
	tm_attrib.attrib.host_addr_id	=gid;
	tm_attrib.attrib.action			=action;
	tm_attrib.attrib.legacyFlag		=lg;

   	status=NU_Create_Timer( tmr.ptr,			// TIMER CONTROL BLOCK
							tm_attrib.str,		// timer name (overloaded)
							_rvf_tm_notify,		// expiration routine to call					
							tmr.id, 			// unique id which enables to find a specific task and a specific timer
							t,					// duration				
							re_sched,			// continuous = 1		
							opt);

	if(status!=NU_SUCCESS) tmr.id=0;
	
	return (UINT32)tmr.id;
}

/*T_RVF_RET rvf_get_remaining_time(T_RV_TM_ID tid, UINT32* t) {
	T_RVF_TM_UBLK			tmr;

	tmr.id=tid;
	if(NU_Get_Remaining_Time(tmr.ptr, (UNSIGNED*)&t)==NU_INVALID_TIMER) return RV_INVALID_PARAMETER;

	return RVF_OK;
}*/

/*******************************************************************************
**
** Function         rvf_del_timer
**
** Description      This function is called by an application to delete a timer
**                  entry from a timer list.
**
** Returns          void
**
*******************************************************************************/
void rvf_del_timer(T_RV_TM_ID tm_id) {
	T_RVF_TM_UBLK			tmr;

	if(tm_id) tmr.id=tm_id;

	NU_Control_Timer(tmr.ptr, NU_DISABLE_TIMER);
	NU_Delete_Timer(tmr.ptr);
	rvf_free_buf(tmr.ptr);
}

void rvf_reset_timer(T_RV_TM_ID tm_id, UINT32 new_duration,
										BOOLEAN isContinuous) {
	T_RVF_TM_UBLK			tmr;
	UINT32					re_sched=0;

	if(isContinuous)re_sched=new_duration;
	if(tm_id)		tmr.id=tm_id;

	NU_Control_Timer(tmr.ptr, NU_DISABLE_TIMER);

	if(new_duration) {
		NU_Reset_Timer(	tmr.ptr,
						_rvf_tm_notify,
						new_duration,
						re_sched, 
						NU_ENABLE_TIMER);
	}

}

/*******************************************************************************
**
** Function         _rvf_timers_init
**
** Description      This internal function is called once at startup to initialize
**                  all the timer structures.
**
** Returns          void
**
*******************************************************************************/
void _rvf_timers_init(void)
{
    UINT8   task_num, timer_num;

    for (task_num = 0; task_num< MAX_RVF_TASKS; task_num++)
    {
		for (timer_num = 0; timer_num < RVF_NUM_TASK_TIMERS; timer_num++)
		{
			NU_Create_Timer(	&(rvf_Timer[task_num][timer_num]),/* TIMER CONTROL BLOCK*/
								"", /* timer name */
								_rvf_timer_expire, /* expiration routine to call*/					
								(task_num * RVF_NUM_TASK_TIMERS) +timer_num,
								/* unique id which enables to find a specific task and a specific timer*/
								0,
								0,
								NU_DISABLE_TIMER);
		}		
    }
}

/*******************************************************************************
**
** Function         rvf_get_time_stamp
**
** Description      This function formats the time into a user area
**
** Returns          the address of the user area containing the formatted time
**
*******************************************************************************/
char *rvf_get_time_stamp(char *tbuf)
{
	UINT32 ms_time;
	UINT32 s_time;
	UINT32 m_time;
	UINT32 h_time;
    char   *p_out = tbuf;

	ms_time = rvf_get_tick_count();
	s_time  = ms_time/100;   /* 100 Ticks per second */
	m_time  = s_time/60;
	h_time  = m_time/60;

	ms_time -= s_time*100;
	s_time  -= m_time*60;
	m_time  -= h_time*60;

    *p_out++ = (char)((h_time / 10) + '0');
    *p_out++ = (char)((h_time % 10) + '0');
    *p_out++ = ':';
    *p_out++ = (char)((m_time / 10) + '0');
    *p_out++ = (char)((m_time % 10) + '0');
    *p_out++ = ':';
    *p_out++ = (char)((s_time / 10) + '0');
    *p_out++ = (char)((s_time % 10) + '0');
    *p_out++ = ':';
    *p_out++ = (char)((ms_time / 10) + '0');
    *p_out++ = (char)((ms_time % 10) + '0');
    *p_out++ = ':';
    *p_out   = 0;

	return tbuf;
}


/*******************************************************************************
**
** Function         rvf_get_tick_count
**
** Description      This function returns the current Nucleus system ticks
**
** Returns          ticks
**
*******************************************************************************/
UINT32	rvf_get_tick_count(void)
{
	return NU_Retrieve_Clock();
}


/*******************************************************************************
**
** Function         rvf_start_timer
**
** Description      An application can call this function to start one of
**                  it's four general purpose timers. Any of the four timers
**                  can be 1-shot or continuous. If a timer is already running,
**                  it will be reset to the new paramaters.
**
** Returns          void
**
*******************************************************************************/
void rvf_start_timer (UINT8 tnum, UINT32 ticks, BOOLEAN is_continuous)
{	T_RVF_G_ADDR_ID     task_id = rvf_get_taskid();
	
	if(tnum==RVF_TIMER_3 || tnum==RVF_TIMER_2) { // reserved for rvf timer service 
		rvf_send_trace ("WARNING: Timers 2 & 3 are deprecated!",
						37,
						NULL_PARAM,
						RV_TRACE_LEVEL_DEBUG_LOW,
						0); 
//		return;
	}
	if (!ticks) // check if ticks == 0, set it to 1 
		{	ticks = 1;
		}

	// disable Nucleus timer 
	NU_Control_Timer(	&(rvf_Timer[task_id][tnum]),
						NU_DISABLE_TIMER );

	// reset the timer with the new settings 
	NU_Reset_Timer(	&(rvf_Timer[task_id][tnum]),
					_rvf_timer_expire,
					ticks,
					is_continuous? ticks:0, // if timer is continuous, reload it 
					NU_ENABLE_TIMER);
}

/*******************************************************************************
**
** Function         rvf_stop_timer
**
** Description      An application can call this function to stop one of
**                  it's four general purpose timers. There is no harm in
**                  stopping a timer that is already stopped.
**
** Returns          void
**
*******************************************************************************/
void rvf_stop_timer (UINT8 tnum)
{	T_RVF_G_ADDR_ID  task_id = rvf_get_taskid();

	/* disable Nucleus timer */
	NU_Control_Timer(	&(rvf_Timer[task_id][tnum]),
						NU_DISABLE_TIMER );
}

/*******************************************************************************
**
** Function         rvf_init_timer_list
**
** Description      This function is called by applications when they
**                  want to initialize a timer list.
**
** Returns          void
**
*******************************************************************************/
void rvf_init_timer_list (T_RVF_TIMER_LIST_Q *p_timer_listq)
{
    p_timer_listq->p_first    = NULL;
    p_timer_listq->p_last     = NULL;
    p_timer_listq->last_ticks = 0;
}

/*******************************************************************************
**
** Function         rvf_init_timer_list_entry
**
** Description      This function is called by the applications when they
**                  want to initialize a timer list entry. This must be
**                  done prior to first use of the entry.
**
** Returns          void
**
*******************************************************************************/
void rvf_init_timer_list_entry (T_RVF_TIMER_LIST_ENT  *p_tle)
{
    p_tle->p_next  = NULL;
    p_tle->p_prev  = NULL;
    p_tle->ticks   = 0xFFFFFFFFL;
}


/*******************************************************************************
**
** Function         rvf_update_timer_list
**
** Description      This function is called by the applications when they
**                  want to update a timer list. This should be at every
**                  timer list unit tick, e.g. once per sec, once per minute etc.
**
** Returns          the number of timers that have expired
**
*******************************************************************************/
UINT16 rvf_revise_timer_list (T_RVF_G_ADDR_ID gid){
	if(!pRtAddrIdTable[gid] );
//	   !pRtAddrIdTable[gid]->p_tm_q ||
//		pRtAddrIdTable[gid]->p_tm_q->timerCnt==0 ||
//		pRtAddrIdTable[gid]->polling_tm!=0 ) return 0;			/* rvf abort     */
//	return rvf_update_timer_list(pRtAddrIdTable[gid]->p_tm_q);	/* tms notify    */
	return 0;
}
UINT16 rvf_update_timer_list (T_RVF_TIMER_LIST_Q *p_timer_listq)
{
    T_RVF_TIMER_LIST_ENT  *p_tle;
    UINT16          num_time_out = 0;

    p_tle = p_timer_listq->p_first;

    /* First, get the guys who have previously timed out */
    while ((p_tle) && (p_tle->ticks == 0))
    {
        num_time_out++;
        p_tle = p_tle->p_next;
    }

    /* Now, get the guys who have just timed out */
    if ((p_tle) && (p_tle->ticks))
    {
        if (--p_tle->ticks == 0)
        {
            while (p_tle != NULL && p_tle->ticks == 0)
            {
                num_time_out++;
                p_tle = p_tle->p_next;
            }
        }
    }

    if (p_timer_listq->last_ticks)
        p_timer_listq->last_ticks--;

    return (num_time_out);
}



/*******************************************************************************
**
** Function         rvf_add_to_timer_list
**
** Description      This function is called by an application to add a timer
**                  entry to a timer list.
**
** Returns          void
**
*******************************************************************************/
void rvf_add_to_timer_list (T_RVF_TIMER_LIST_Q *p_timer_listq, T_RVF_TIMER_LIST_ENT  *p_tle)
{
    UINT32           nr_ticks_total;
    T_RVF_TIMER_LIST_ENT  *p_temp;

    if (p_tle->ticks >= p_timer_listq->last_ticks)
    {
        if (p_timer_listq->p_first == NULL)
            p_timer_listq->p_first = p_tle;
        else
        {
            if (p_timer_listq->p_last != NULL)
                p_timer_listq->p_last->p_next = p_tle;

            p_tle->p_prev = p_timer_listq->p_last;
        }

        p_tle->p_next = NULL;
        p_timer_listq->p_last = p_tle;
        nr_ticks_total = p_tle->ticks;
        p_tle->ticks = p_tle->ticks - p_timer_listq->last_ticks;

        p_timer_listq->last_ticks = nr_ticks_total;
    }
    else
    {
        p_temp = p_timer_listq->p_first;
        while (p_tle->ticks > p_temp->ticks)
        {
            p_tle->ticks = p_tle->ticks - p_temp->ticks;
            p_temp = p_temp->p_next;
        }
        if (p_temp == p_timer_listq->p_first)
        {
            p_tle->p_next = p_timer_listq->p_first;
            p_timer_listq->p_first->p_prev = p_tle;
            p_timer_listq->p_first = p_tle;
        }
        else
        {
            p_temp->p_prev->p_next = p_tle;
            p_tle->p_prev = p_temp->p_prev;
            p_temp->p_prev = p_tle;
            p_tle->p_next = p_temp;
        }
        p_temp->ticks = p_temp->ticks - p_tle->ticks;
    }
}

/*******************************************************************************
**
** Function         rvf_remove_from_timer_list
**
** Description      This function is called by an application to remove a timer
**                  entry from a timer list.
**
** Returns          void
**
*******************************************************************************/
void rvf_remove_from_timer_list (T_RVF_TIMER_LIST_Q *p_timer_listq, T_RVF_TIMER_LIST_ENT  *p_tle)
{
    if (p_tle == NULL || p_tle->ticks == 0xFFFFFFFFL || p_timer_listq->p_first == NULL)
    {
        return;
    }

    /* Add the ticks remaining in this timer to the next guy in the list.
    */
    if (p_tle->p_next != NULL)
    {
        p_tle->p_next->ticks += p_tle->ticks;
    }
    else
    {
        p_timer_listq->last_ticks -= p_tle->ticks;
    }

    /* Unlink timer from the list.
    */
    if (p_timer_listq->p_first == p_tle)
    {
        p_timer_listq->p_first = p_tle->p_next;

        if (p_timer_listq->p_first != NULL)
            p_timer_listq->p_first->p_prev = NULL;

        if (p_timer_listq->p_last == p_tle)
            p_timer_listq->p_last = NULL;
    }
    else
    {
        if (p_timer_listq->p_last == p_tle)
        {
            p_timer_listq->p_last = p_tle->p_prev;

            if (p_timer_listq->p_last != NULL)
                p_timer_listq->p_last->p_next = NULL;
        }
        else
        {
            if (p_tle->p_next != NULL && p_tle->p_next->p_prev == p_tle)
                p_tle->p_next->p_prev = p_tle->p_prev;
            else
            {
                /* Error case - chain messed up ?? */
                return;
            }

            if (p_tle->p_prev != NULL && p_tle->p_prev->p_next == p_tle)
                p_tle->p_prev->p_next = p_tle->p_next;
            else
            {
                /* Error case - chain messed up ?? */
                return;
            }
        }
    }

    p_tle->p_next = p_tle->p_prev = NULL;
    p_tle->ticks = 0xFFFFFFFFL;
}


/*******************************************************************************
**
** Function         rvf_get_expired_entry
**
** Description      This function returns a pointer to the first expired entry in
**					the timer list queue.
**					If no entry in the queue has expired, it returns NULL.
**
** Returns          T_RVF_TIMER_LIST_ENT * : pointer to the expired entry if any.
**
*******************************************************************************/
T_RVF_TIMER_LIST_ENT * rvf_get_expired_entry (T_RVF_TIMER_LIST_Q *p_timer_listq)
{	
	T_RVF_TIMER_LIST_ENT * p_tle;
	/* test if the first entry in the queue has expired */
	if ( (p_timer_listq->p_first != NULL) && (p_timer_listq->p_first->ticks == 0) )
	{	

		p_tle = p_timer_listq->p_first;
		if(p_tle->t_init) {
			p_tle->ticks=p_tle->t_init;
		} else {
			/* unlink entry from the list */
			p_timer_listq->p_first = p_tle->p_next;

			if (p_timer_listq->p_first != NULL)
				p_timer_listq->p_first->p_prev = NULL;

			if (p_timer_listq->p_last == p_tle)
				p_timer_listq->p_last = NULL;
			
			p_tle->p_next = NULL;
			p_tle->p_prev = NULL;
			p_tle->ticks = 0xFFFFFFFFL;
			//return p_tle;
		}
		return p_tle;
	}
	else
	{	return NULL;
	}
}









