/****************************************************************************/
/*                                                                          */
/*  Name        rvf_task.c                                                  */
/*                                                                          */
/*  Function    this file contains \rvf task related functions               */
/*                                                                          */
/*  Version		0.1															*/
/*																			*/
/* 	Date       	Modification												*/
/*  ------------------------------------									*/
/*  3/12/99		Create														*/
/*	10/27/99	remove all non-nucleus sections (#ifdef)					*/
/*				change tasks priority and time_slicing						*/	
/*  11/17/1999	change RVF_create_task and RVF_get_taskid functions         */
/*	30/11/99	compliant to RV coding guidelines							*/
/*  28/08/2000	add mutex related functions.								*/
/*																			*/
/*	Author		David Lamy-Charrier (dlamy@tif.ti.com)						*/
/*																			*/
/* (C) Copyright 1999 by Texas Instruments Incorporated, All Rights Reserved*/
/****************************************************************************/

#ifndef _WINDOWS
	#include "config/rv.cfg"
#endif

#include "nucleus.h" 

#include "rv/rv_general.h"
#include "rvf/rvf_api.h"
#include "rvf/rvf_i.h"			
#include "rvm/rvm_i.h"	 /* ONLY for Task Codes */
#include "rvm/rvm_use_id_list.h"
#include <stdio.h>
#include <string.h>

/* include the rvtool_trace.h file only in the RivieraTool */
#ifdef	_WINDOWS
#ifndef	_CONSOLE
//		#include "rvtool_trace.h"
	#endif
	#include <windows.h>
#endif

#define RVF_STATIC_ALLOC_NB	1

/**********************************************************************
** Nucleus specific definitions
*/

typedef void (*NU_TASK_ENTRY)(UNSIGNED, VOID *);  //

/* array of tasks */
/**********************************************************************
* Note: if dynamic mem alloc of "pOSTCB" creates too much fragentation
* The origianl way me be utilised and pointed to by a addr_id table 
* structure, which is proposed for type 2 support.
***********************************************************************/

#define _RALLOC_TASK_CNTRL_BLK(tb)		rvf_get_buf(rvm_sys_mem_bank, sizeof(NU_TASK), tb)
#define	_RALLOC_TASK_EVT_GRP(tb)		rvf_get_buf(rvm_sys_mem_bank, sizeof(NU_EVENT_GROUP), tb)
#define _RALLOC_TASK_RT_ADDR_DATA(tb)	rvf_get_buf(rvm_sys_mem_bank, sizeof(T_RVF_RT_ADDR_ID_DATA), tb)
//#define _RALLOC_TASK_RT_TM(tb)			rvf_get_buf(rvm_sys_mem_bank, sizeof(T_RVF_TIMER_LIST_Q), tb)

/*static NU_TASK			bOSTCB[RVF_STATIC_ALLOC_NB];			 
static NU_EVENT_GROUP	bOSEvtGrp[RVF_STATIC_ALLOC_NB];			 
static UINT8			*bOSStack;
static UINT16			bOSStackSize;
static char				bOSTName[RVF_STATIC_ALLOC_NB][RVF_STATIC_ALLOC_NB];
*/

static UINT8	task_counter = 0;	
static INT16	OSDisableNesting = 0;
static BOOL		OSInterruptAlreadyMasked = FALSE;
static INT32    OSLastIntLevel;
static T_RVF_G_ADDR_ID _RDV=RVF_INVALID_ADDR_ID;

T_RVF_RET		_initSysRtData(void);


/*******************************************************************************
**
** Function         rvf_AllocTaskXyz()
**
** Description      
**                  
**
** Returns          T_RVF_MB_STATUS
**
*******************************************************************************/
T_RVF_RET _initSysRtData(void) {    /*A-M-E-N-D-E-D!*/
	int i=0;

	for (i=0; i < MAX_RVF_G_ADDR_ID; i++) pRtAddrIdTable[i]=NULL;

	//	pOSTCB[0]=&OSTCB[0];
	return RVF_OK;
}

/*******************************************************************************
**
** Function         rvf_init
**
** Description      This function is called once at startup to initialize
**                  all the rvf (timer, buffer...).
**
** Returns          void
**
*******************************************************************************/
void rvf_init(void) {
	/* UINT8   i;	*/

    /* Initialize RVF variables */
    /*for (i = 0; i < MAX_RVF_TASKS; i++) {
        OSStack[i] = 0;
        OSStackSize[i] = 0;
        OSTName[i][0] = 0;
		memset( &OSTCB[i],		0, sizeof(NU_TASK) );
		memset( &OSEvtGrp[i],	0, sizeof(NU_EVENT_GROUP) );
    }
	//	for (i = 0; i < 1; i++) memset( &OSTCB[i], 0, sizeof(NU_TASK) );
	*/
	_initSysRtData();
	_rvf_buffer_init();
	_rvf_timers_init();
}

/*******************************************************************************
**
** Function         _rvf_name_cpy
**
** Description      Internal function which copy a string in a buffer.
**                  The string may be null-terminated or length bytes long.
**
** Returns          void
**
*******************************************************************************/
void _rvf_name_cpy(char * dest, char * source, UINT8 length) {	
	UINT8 cpt;
	for( cpt = 0; cpt < length ; cpt++) {
		dest[cpt] = source[cpt];
		if (source[cpt] == 0) {	return;}
	}
}

T_RVF_G_ADDR_ID rvf_get_context() {
	T_RVF_G_ADDR_ID gid=rvf_get_taskid();
	if(pRtAddrIdTable[gid]->type_code==ET2_HOST_TASK) {
		return pRtAddrIdTable[gid]->virtualContext;
	} else {
		return pRtAddrIdTable[gid]->host_addr_id;
	}
}

void rvf_setRDV(T_RVF_G_ADDR_ID tid,T_RVF_G_ADDR_ID vid) { 	
	if(pRtAddrIdTable[tid])	pRtAddrIdTable[tid]->virtualContext=vid;
}


/**************************************************************
*	Function         _rvf_name_cpy
*
***************************************************************/
T_RVF_G_ADDR_ID rvf_allocate_task_id(UINT8 isRealTask) {
	T_RVF_G_ADDR_ID i=0;
	UINT8 isTask=1;

	/* Note: differentiation is made between REAL and VIRTUAL IDs
	 * to provide for backwards compatabible LEGACY timer implementations
	 * The Real task ids should be in sync with:
	 * "static rvf_Timer[MAX_RVF_TASKS][RVF_NUM_TASK_TIMERS]",
	 * defined in "rvf_time.c". In the future, one NU_Timer blk will
	 * be pointed to in the rt global addr table. Hence, eliminating 
	 * the large resource of "rvf_Timer[][]"
	 */
	if(!pRtAddrIdTable[i]) return RVF_INVALID_ADDR_ID;

	if(isRealTask) {
		/* REAL TASK									*/
		for(i=0; pRtAddrIdTable[i]!=NULL && i<MAX_RVF_TASKS; i++);

		if (i < MAX_RVF_TASKS)	{ /* alloc. and init. */ 
			if(_RALLOC_TASK_RT_ADDR_DATA((T_RVF_BUFFER**)&pRtAddrIdTable[i])) return RVF_INVALID_TASK;
			memset( pRtAddrIdTable[i], 0, sizeof(T_RVF_RT_ADDR_ID_DATA) );
			rvf_mbox_buffer_init(pRtAddrIdTable[i]);

		} else	return RVF_INVALID_ADDR_ID;
	} else {
		/* VIRTUAL TASK				*/
		for(i=MAX_RVF_TASKS; pRtAddrIdTable[i]!=NULL && i<MAX_RVF_G_ADDR_ID; i++);

		if (i < MAX_RVF_G_ADDR_ID)	{ /* alloc. and init. */ 
			if(_RALLOC_TASK_RT_ADDR_DATA((T_RVF_BUFFER**)&pRtAddrIdTable[i])) return RVF_INVALID_TASK;
			memset( pRtAddrIdTable[i], 0, sizeof(T_RVF_RT_ADDR_ID_DATA) );
			rvf_mbox_buffer_init(pRtAddrIdTable[i]);

		} else	return RVF_INVALID_ADDR_ID;
	}
	
	return  i;
}

/* MUST RESOLVE ERROR CODES '1' is just for P of C */
T_RVF_RET rvf_setRtAddrSweIndex(T_RVF_G_ADDR_ID id, UINT8 sweIndex) {
	if(id>=MAX_RVF_G_ADDR_ID) return 1;
	pRtAddrIdTable[id]->swe_db_index=sweIndex;
//	pRtAddrIdTable[id]->type_code=tcode; /* allows type to be set if not in swe */
	return RVF_OK;
}

T_RVF_RET rvf_setHostTaskStackPtr(T_RVF_G_ADDR_ID id, UINT8* pStack) { /* deprecated ! */
	if(id>=MAX_RVF_TASKS) return 1;
	pRtAddrIdTable[id]->p_os_stack=pStack;

	return RVF_OK;
}

T_RVF_RET rvf_isHostingTaskIdle(T_RVF_G_ADDR_ID id, UINT8* status) { /* deprecated ! */
	if(id>=MAX_RVF_TASKS)	return RVF_INVALID_PARAMETER;
	if(!pRtAddrIdTable[id])	return RVF_INVALID_PARAMETER;
	if(pRtAddrIdTable[id]->type_code!=ET2_HOST_TASK) return RVF_INVALID_PARAMETER;

	if(pRtAddrIdTable[id]->hosting_count==0) *status=1;
	return RVF_OK;
}

/* convenience/helper fnc. */
T_RVF_G_ADDR_ID resolveHostAddrId(T_RVF_G_ADDR_ID id) { 
	if(!pRtAddrIdTable[id]) return RVF_INVALID_ADDR_ID;
	return pRtAddrIdTable[id]->host_addr_id;
}

/* HostingCounter enables one to deduce if task can be terminated	*/
/* ERROR return val must be revised									*/
T_RVF_RET rvf_registerToHost(T_RVF_G_ADDR_ID host_id, T_RVF_G_ADDR_ID eid) {
	UINT8 i;

	if (host_id >= MAX_RVF_TASKS || eid >= MAX_RVF_G_ADDR_ID )  return RV_INVALID_PARAMETER;; 

	for(i=0;pRtAddrIdTable[host_id]->parasites[i]!=0 && i < MAX_PARASITES; i++);

		pRtAddrIdTable[host_id]->parasites[i]=eid;
		pRtAddrIdTable[host_id]->hosting_count++;

	return RVF_OK;
}

T_RVF_RET rvf_unregisterFromHost(T_RVF_G_ADDR_ID host_id, T_RVF_G_ADDR_ID pid) {
	UINT8 i=0;
	if (pRtAddrIdTable[host_id]->hosting_count !=0) {
		for(i=0; i<MAX_PARASITES || pRtAddrIdTable[host_id]->parasites[i]==pid; i++);
			pRtAddrIdTable[host_id]->parasites[i]=0;

		pRtAddrIdTable[host_id]->hosting_count--;
	} else return RV_INVALID_PARAMETER;

	return RVF_OK;
}

T_RVF_RET rvf_associateGrpToHost(T_RVF_G_ADDR_ID host_id, T_RVF_GD_ID gd_id) {

	if (host_id >= MAX_RVF_TASKS )  return RV_INVALID_PARAMETER; 

	pRtAddrIdTable[host_id]->gdHost=gd_id;
		
	return RVF_OK;
}

/*T_RVF_RET rvf_unregisterGrpFromHost(T_RVF_G_ADDR_ID host_id, T_RVF_G_ADDR_ID pid) {
	if (pRtAddrIdTable[host_id]->hosting_count !=0) {
		pRtAddrIdTable[host_id]->parasites[pid]=0;
		pRtAddrIdTable[host_id]->hosting_count--;
	} else return RV_INVALID_PARAMETER;

	return RVF_OK;
}*/

T_RVF_G_ADDR_ID rvf_resolveHostingAddrId(T_RVM_GROUP_DIRECTIVE gd) {
	int i=0;

	for(i=1; i<MAX_RVF_TASKS; i++) {		// i=1 by-pass RVM task 
		if(pRtAddrIdTable[i]!=NULL) {
			if( pRtAddrIdTable[i]->type_code==ET2_HOST_TASK &&
//				pRtAddrIdTable[i]->priority==priority &&	// to do ...
//				pRtAddrIdTable[i]->os_stack_size>=stack_size
				pRtAddrIdTable[i]->gdHost==gd.group_directive &&
				pRtAddrIdTable[i]->hosting_count<MAX_PARASITES ) return i; // def. 10 A+ .
		}
	}

	return RVF_INVALID_ADDR_ID; /* nothing found ret. param must be invalid*/
}

/*********************************************************************
 * start() and stop() should be added to params, due to defered calling 
 * Allow for parasites to be added at RT and their start to be called.
 *********************************************************************/
T_RVF_RET rvf_create_virtual_task (T_RV_RET (* handle_message)(T_RV_HDR * msg),
								   T_RV_RET (* handle_timer)(T_RV_HDR * msg),
								   T_RVF_G_ADDR_ID task_id, T_RVF_G_ADDR_ID host_task_id, char *taskname,
								   UINT8 priority, UINT8 tcode) {

	if(!pRtAddrIdTable[task_id]) {  /* allow for static init. or previous dyn init, eg. idle*/	
		if(_RALLOC_TASK_RT_ADDR_DATA((T_RVF_BUFFER**)&pRtAddrIdTable[task_id])) return RVF_INTERNAL_ERR;
		memset( pRtAddrIdTable[task_id], 0, sizeof(T_RVF_RT_ADDR_ID_DATA) );

		rvf_mbox_buffer_init(pRtAddrIdTable[task_id]);
	} /*else printf("RVF: task rt addr %d already alloc'ed\n", task_id); */

	pRtAddrIdTable[task_id]->host_addr_id=host_task_id;
	pRtAddrIdTable[task_id]->symbolic_name=taskname;
	pRtAddrIdTable[task_id]->handle_message=handle_message;
	pRtAddrIdTable[task_id]->handle_timer=handle_timer;
	pRtAddrIdTable[task_id]->type_code=tcode;

	pRtAddrIdTable[task_id]->pOSTCB=NULL;		/* init to NULL for res.'free' */ 
	pRtAddrIdTable[task_id]->pOSEvtGrp=NULL;
	pRtAddrIdTable[task_id]->p_os_stack=NULL;
//	pRtAddrIdTable[task_id]->p_tm_q=NULL;

//	rvf_registerToHost(host_task_id, task_id);
	
	return RVF_OK;
}

T_RVF_RET rvf_register_t3_handlers (T_RVF_G_ADDR_ID task_id,
								   T_RV_RET (* handle_message)(T_RV_HDR * msg),
								   T_RV_RET (* handle_timer)(T_RV_HDR * msg) ) {

	pRtAddrIdTable[task_id]->handle_message=handle_message;
	pRtAddrIdTable[task_id]->handle_timer=handle_timer;

	return RVF_OK;
}

/* RVM must create a stack with its MB and pass as params to task create */
/*T_RVF_RET rvf_create_host_task (T_RV_RET (* proxy)(void), T_RVF_G_ADDR_ID task_id,
								   char *taskname, UINT8 *stack, UINT16 stacksize, 
								   UINT8 priority, UINT8 tcode, UINT8 time_slicing, T_RVF_TASK_STATE suspend) {

	return rvf_create_task(	(TASKPTR)proxy, task_id, taskname, stack, stacksize,\
							priority, tcode, time_slicing,	suspend ) ;
									
}*/

/*******************************************************************************
**
** Function         rvf_create_task
**
** Description      This function is called to create a new rvf task.
**                  time_slice represents the number of Nucleus ticks before a task is interrupted.
**					0 for no time-slicing.
**
** Returns          RVF_OK if successful, else an error code
**
*******************************************************************************/
T_RVF_RET rvf_create_legacy_task (TASKPTR task_entry, UINT8 task_id, char *taskname, UINT8 *stack, UINT16 stacksize, UINT8 priority, UINT8 time_slicing, T_RVF_TASK_STATE is_suspend) {

	return rvf_create_task(task_entry,task_id,taskname,stack,stacksize,priority,ET4_TASK,time_slicing,is_suspend);
}

T_RVF_RET rvf_create_task (TASKPTR task_entry, T_RVF_G_ADDR_ID task_id, char *taskname, UINT8 *stack, UINT16 stacksize,
						   UINT8 priority, UINT8 tcode, UINT8 time_slicing, T_RVF_TASK_STATE suspend) {

	/*if (task_id >= MAX_RVF_TASKS) { return 1; } */
	if (task_counter >= MAX_RVF_TASKS) { return 1; }

	/* fill the task stack with a 0xFE pattern to allow use with stack monitoring tool */
	memset( stack, 0xFE, stacksize );

	/* allow for immediate task creation, eg. no alloc taskId(), but #defined task No. Dangerous!*/	
	if(!pRtAddrIdTable[task_id]) {  
		if(_RALLOC_TASK_RT_ADDR_DATA((T_RVF_BUFFER**)&pRtAddrIdTable[task_id])) return RVF_INTERNAL_ERR;
		memset( pRtAddrIdTable[task_id], 0, sizeof(T_RVF_RT_ADDR_ID_DATA) );

		rvf_mbox_buffer_init(pRtAddrIdTable[task_id]);
	} /*else printf("RVF: task rt addr %d already alloc'ed\n", task_id); */

	/*if(_RALLOC_TASK_RT_TM((T_RVF_BUFFER**)&pRtAddrIdTable[task_id]->p_tm_q)) {
		rvf_free_buf(pRtAddrIdTable[task_id]);
		pRtAddrIdTable[task_id]=NULL;
		return RVF_INTERNAL_ERR;
	} else pRtAddrIdTable[task_id]->p_tm_q->timerCnt=0;*/
	
	pRtAddrIdTable[task_id]->hosting_count=0;
	pRtAddrIdTable[task_id]->host_addr_id=task_id;
	pRtAddrIdTable[task_id]->symbolic_name=taskname;
	pRtAddrIdTable[task_id]->priority=priority;
	pRtAddrIdTable[task_id]->type_code=tcode;

	memset(&pRtAddrIdTable[task_id]->parasites, 0, (sizeof(T_RVF_G_ADDR_ID)*MAX_PARASITES));

	if (stack) {
	    pRtAddrIdTable[task_id]->p_os_stack     = (UINT8 *)stack;// - stacksize;
	    pRtAddrIdTable[task_id]->os_stack_size	= stacksize;
    } else {
//		rvf_free_buf(pRtAddrIdTable[task_id]->p_tm_q);
		rvf_free_buf(pRtAddrIdTable[task_id]);
		pRtAddrIdTable[task_id]=NULL;
		return RVF_INTERNAL_ERR;
	}

	if(_RALLOC_TASK_CNTRL_BLK((T_RVF_BUFFER**)&pRtAddrIdTable[task_id]->pOSTCB)) {
		rvf_free_buf(stack);
//		rvf_free_buf(pRtAddrIdTable[task_id]->p_tm_q);
		rvf_free_buf(pRtAddrIdTable[task_id]);
		pRtAddrIdTable[task_id]=NULL;
		return RVF_INTERNAL_ERR;
	}
	memset( pRtAddrIdTable[task_id]->pOSTCB, 0, sizeof(NU_TASK) );

	if(_RALLOC_TASK_EVT_GRP((T_RVF_BUFFER**)&pRtAddrIdTable[task_id]->pOSEvtGrp)) {
		rvf_free_buf(pRtAddrIdTable[task_id]->pOSTCB);
		rvf_free_buf(stack);
//		rvf_free_buf(pRtAddrIdTable[task_id]->p_tm_q);
		rvf_free_buf(pRtAddrIdTable[task_id]);
		pRtAddrIdTable[task_id]=NULL;
		return RVF_INTERNAL_ERR;
	}
	memset( pRtAddrIdTable[task_id]->pOSEvtGrp, 0, sizeof(NU_EVENT_GROUP) );

	/* Create one Event Group for this task */
	if( NU_SUCCESS != \
		NU_Create_Event_Group (pRtAddrIdTable[task_id]->pOSEvtGrp,\
		taskname) ) {
		return RVF_INTERNAL_ERR;
	}

	//}
    /* Create Task */
	if ( NU_SUCCESS != 
	NU_Create_Task (pRtAddrIdTable[task_id]->pOSTCB,            /* Task Control Block */
		            taskname,     /*taskname,*/					/* Task Name */
		            (NU_TASK_ENTRY )task_entry,					/* Task Entry Function */
		            0,            /* why prev. task_id ???  */	/* ARGC  A-M-E-N-D-E-D! */
		            NULL,										/* ARGV */
		            pRtAddrIdTable[task_id]->p_os_stack,        /* Begining of Stack */
		            pRtAddrIdTable[task_id]->os_stack_size,		/* Stack size */
		            priority,									/* Priority */
		            time_slicing,								/* Time Slicing Period*/
		            NU_PREEMPT,									/* Preemption allowed */
					(OPTION)(suspend == RUNNING ? NU_START : NU_NO_START) )/* Start the task or suspend it */
		)	{	
		return RVF_INTERNAL_ERR;
	}
	task_counter++;   /* MUST 'DEC' ON TASK TERMINATION	*/
	
	return RVF_OK;
}

/* to be called from func. 'create_tasks()' of module "create_RVtasks.c" 
 * utilises static alloated system variables								*/
/*T_RVF_RET rvf_create_boot_task (TASKPTR task_entry, UINT8 task_id, char *taskname, UINT8 *stack, UINT16 stacksize, UINT8 priority, UINT8 time_slicing, T_RVF_TASK_STATE suspend) {
	if (task_id >= MAX_RVF_TASKS)	{		return 1;}

    if (stack)    {
	    bOSStack     = (UINT8 *)stack;// - stacksize;
	    bOSStackSize = stacksize;
    }
	// copy the task name into an internal buffer 
	_rvf_name_cpy( &(bOSTName[0][0]), taskname, RVF_MAX_TASK_LEN);

	// fill the task stack with a 0xFE pattern to allow use with stack monitoring tool 
	memset( stack, 0xFE, stacksize );

	// Create one Event Group for this task 
	if( NU_SUCCESS != NU_Create_Event_Group (&bOSEvtGrp[0], taskname) ){
		return RVF_INTERNAL_ERR;
	}

    // Create Task 
	if ( NU_SUCCESS != 
	NU_Create_Task (&bOSTCB[0],                 // (reserved)Task Control Block 
		            taskname,	                // Task Name 
		            (NU_TASK_ENTRY )task_entry,	// Task Entry Function 
		            task_id,		            // ARGC 
		            NULL,		                // ARGV 
		            bOSStack,					// Begining of Stack 
		            stacksize,	                // Stack size 
		            priority,					// Priority 
		            time_slicing,			    // Time Slicing Period
		            NU_PREEMPT,                 // Preemption allowed 
					(OPTION)(suspend == RUNNING ? NU_START : NU_NO_START) )// Start the task or suspend it 
		)
	{	return RVF_INTERNAL_ERR;
	}
	task_counter++;
	return RVF_OK;
}*/

/* Later timer blk must be added, once made dynamic   */ 
T_RVF_RET rvf_free_sys_resources(T_RVF_G_ADDR_ID gid, UINT8 rm) {

	if(!pRtAddrIdTable[gid]) return RVF_INTERNAL_ERR;

	if(rm==1 || rm==2) {
		if(pRtAddrIdTable[gid]->pOSTCB)rvf_free_buf(pRtAddrIdTable[gid]->pOSTCB);
		if(pRtAddrIdTable[gid]->pOSEvtGrp)rvf_free_buf(pRtAddrIdTable[gid]->pOSEvtGrp);
		if(pRtAddrIdTable[gid]->p_os_stack)rvf_free_buf(pRtAddrIdTable[gid]->p_os_stack);

		task_counter--;
	}
	if(rm==0 || rm==2) {
		if(pRtAddrIdTable[gid])rvf_free_buf(pRtAddrIdTable[gid]);
		pRtAddrIdTable[gid]=NULL;
	}

	return RVF_OK;
}

/*******************************************************************************
**
** Function         rvf_exit_task
**
** Description      This function is called to stop a rvf task.
**					A task can kill another task or itself.
**
** Returns          void
**
*******************************************************************************/
void rvf_exit_task (T_RVF_G_ADDR_ID task_id)
{
	if(!pRtAddrIdTable[task_id]) return;
	/*
	** Empty task's mail box
	*/
	_rvf_empty_mailboxes(task_id);

	/*
	** Terminate task
	*/
	NU_Terminate_Task(pRtAddrIdTable[task_id]->pOSTCB); /*&OSTCB[task_id]);*/
	NU_Delete_Task(pRtAddrIdTable[task_id]->pOSTCB);

	/*
	** Delete related event group
	*/
	NU_Delete_Event_Group (pRtAddrIdTable[task_id]->pOSEvtGrp);

	pRtAddrIdTable[task_id]->p_os_stack=0;
    //OSStack[task_id]     = 0;
}


/*******************************************************************************
**
** Function         rvf_suspend_task
**
** Description      This function is called to suspend a rvf task.
**					A task can suspend another task or itself.
**
** Returns          void
**
*******************************************************************************/
T_RVF_RET rvf_suspend_task (T_RVF_G_ADDR_ID task_id)
{
	if(!pRtAddrIdTable[task_id]) return RVF_INVALID_PARAMETER;
	NU_Suspend_Task(pRtAddrIdTable[task_id]->pOSTCB); // A-M-E-N-D-E-D! 

	return RVF_OK;
}


/*******************************************************************************
**
** Function         rvf_wait
**
** Description      This function is called by tasks to wait for a specific
**                  event or set of events. The task may specify the duration
**                  that it wants to wait for, or 0 if infinite.
**
** Returns          the event mask of received events or zero if timeout
**
*******************************************************************************/
UINT16 rvf_wait (UINT16 flag, UINT32 timeout) {
	T_RVF_G_ADDR_ID     rtask = rvf_get_taskid();

	if (!timeout) timeout = 0xFFFFFFFFL;
	
	return rvf_evt_wait(rtask, flag, timeout);
}

UINT16 rvf_evt_wait(T_RVF_G_ADDR_ID rtask, UINT16 flag, UINT32 timeout) {

#define RVF_RET_TIME_OUT	0

	UINT16		mbxEvt = 0;
	UNSIGNED    evt   = 0;
	UNSIGNED	clear = 0;
	STATUS		status_ret; 

    /* Check if anything in any of the mailboxes. Possible race condition. */
   
	if (rtask>=MAX_RVF_TASKS || !pRtAddrIdTable[rtask]) {
		RVM_TRACE_WARNING_PARAM("RVF: Illegal MBOX or MBOX not ready!", rtask);
		return (UINT16) RVF_RET_TIME_OUT; 
	}

	if (pRtAddrIdTable[rtask] && pRtAddrIdTable[rtask]->OSTaskQFirst[0])
        mbxEvt |= RVF_TASK_MBOX_0_EVT_MASK;
    if (pRtAddrIdTable[rtask] && pRtAddrIdTable[rtask]->OSTaskQFirst[1])
        mbxEvt |= RVF_TASK_MBOX_1_EVT_MASK;
    if (pRtAddrIdTable[rtask] && pRtAddrIdTable[rtask]->OSTaskQFirst[2])
        mbxEvt |= RVF_TASK_MBOX_2_EVT_MASK;
    if (pRtAddrIdTable[rtask] && pRtAddrIdTable[rtask]->OSTaskQFirst[3])
        mbxEvt |= RVF_TASK_MBOX_3_EVT_MASK;

    /* If any valid event if pending, return immediately */
    if (mbxEvt & flag)
    {
       /* Return only those bits which user wants... */
       evt = (UINT16) (mbxEvt & flag);

	   /* clear the nucleus event(s) for mailboxes */
	   if ( mbxEvt & 0x000F ) /* a mailbox event is signaled*/
	   {   NU_Retrieve_Events (pRtAddrIdTable[rtask]->pOSEvtGrp, (UNSIGNED) mbxEvt & 0x000F, NU_AND_CONSUME,
                        (UNSIGNED *)&clear, NU_NO_SUSPEND);
	   }

       	   
        return ((UINT16) evt);
    }


    if(pRtAddrIdTable[rtask]) {
		status_ret = NU_Retrieve_Events (pRtAddrIdTable[rtask]->pOSEvtGrp,
										 (UNSIGNED) flag, NU_OR_CONSUME,
										 (UNSIGNED *)&evt, timeout );
		if ( status_ret == NU_SUCCESS) {
			return (UINT16) evt;
		} else { /* timeout or error case */
			return (UINT16) RVF_RET_TIME_OUT;
		}
	} return (UINT16) RVF_RET_TIME_OUT;
}

/*******************************************************************************
**
** Function         rvf_wait_for_specific_msg
**
** Description      This function is called by tasks to wait for a specific
**                  message in the specified mailbox. The task may specify the duration
**                  that it wants to wait for, or 0 if infinite.
**
** Returns          A pointer to the message, NULL in case of time-out.
**
*******************************************************************************/
T_RVF_BUFFER * rvf_wait_for_specific_msg(UINT16 msg_code, UINT8 mbox, UINT32 timeout)
{
	T_RVF_G_ADDR_ID task_id = rvf_get_taskid();
	T_RVF_BUFFER * p_buf = NULL;
	T_RVF_INTERNAL_BUF * p_hdr;
	UNSIGNED clear = 0;
	STATUS status_ret;
	UINT32 wait_time;
	UINT32 init_time = rvf_get_tick_count();

	
	/* check input parameter */
	if ( mbox >= RVF_NUM_TASK_MBOX)  /* NOTE: must be def to 2 max */
	{	rvf_send_trace( "RVF: rvf_wait_for_specific_msg(): invalid mailbox id", 52, NULL_PARAM, RV_TRACE_LEVEL_ERROR, RVM_USE_ID);
		return p_buf;
	}

	if(!timeout)
		timeout = 0xFFFFFFFFL;
	wait_time = timeout;

	while( (rvf_get_tick_count() - init_time) < timeout  )
	{
		/* test all messages in the mailbox */
		if( pRtAddrIdTable[task_id]->OSTaskQFirst[mbox] )
		{
			rvf_disable(9);
			p_hdr = pRtAddrIdTable[task_id]->OSTaskQFirst[mbox];
			p_buf = MEM2USER(p_hdr);
			/* test the first one */
			if ( ((T_RV_HDR *)p_buf)->msg_id == msg_code )
			{	/* message found, return it */
				pRtAddrIdTable[task_id]->OSTaskQFirst[mbox] = p_hdr->p_next;
				p_hdr->p_next				= NULL;

#if RVF_ENABLE_BUF_LINKAGE_CHECK
				RVF_SET_BUF_UNLINKED(p_hdr);
#endif
				/* clear the Nucleus Event for this mailbox */
				NU_Retrieve_Events( pRtAddrIdTable[task_id]->pOSEvtGrp, EVENT_MASK(mbox), NU_AND_CONSUME, 
								(UNSIGNED *)&clear, NU_NO_SUSPEND);
				rvf_enable();
				return p_buf;
			}

			while(p_hdr->p_next != NULL)
			{
				p_buf = MEM2USER(p_hdr->p_next);
				if ( ((T_RV_HDR *)p_buf)->msg_id == msg_code )
				{	/* remove it from the list */
					p_hdr->p_next = ( (T_RVF_INTERNAL_BUF *) USER2MEM(p_buf))->p_next;
					/* check if it the last one */
					if ( pRtAddrIdTable[task_id]->OSTaskQLast[mbox]  == USER2MEM(p_buf) )
					{	pRtAddrIdTable[task_id]->OSTaskQLast[mbox]  = p_hdr;
					}
					((T_RVF_INTERNAL_BUF *) USER2MEM(p_buf))->p_next = NULL;

	#if RVF_ENABLE_BUF_LINKAGE_CHECK
					RVF_SET_BUF_UNLINKED(USER2MEM(p_buf));
	#endif
					rvf_enable();
					return p_buf;
				}
				p_hdr = p_hdr->p_next;
			}

			rvf_enable();
		}
		
		/* here, the message has not been found, so wait for a new message */
		
		if ((wait_time != 0xFFFFFFFFL) &&
			(timeout > rvf_get_tick_count() - init_time))
		{
			/* NU_Retrieve_Events bug: cannot call function with a parameter with F in MSB */
			wait_time = (timeout - (rvf_get_tick_count() - init_time)) & 0x0FFFFFFFL;
		}

		status_ret = NU_Retrieve_Events( pRtAddrIdTable[task_id]->pOSEvtGrp, EVENT_MASK(mbox), NU_OR_CONSUME,
										(UNSIGNED *)&clear, wait_time);

		if( status_ret != NU_SUCCESS) /* time out */
		{	return NULL;
		}
		
	}
	return NULL;
}


/*******************************************************************************
**
** Function         rvf_delay
**
** Description      This function is called by tasks to sleep unconditionally
**                  for a specified amount of time.
**
** Returns          void
**
*******************************************************************************/
void rvf_delay (UINT32 timeout)
{
	if (timeout == 0)
	{	timeout = 1;
	}

	NU_Sleep(timeout);

}


/*******************************************************************************
**
** Function         rvf_send_event
**
** Description      This function is called by tasks to send events to other
**                  tasks. Tasks can also send events to themselves.
**
** Returns          0 if all OK, else 1
**
*******************************************************************************/
UINT8 rvf_send_event (T_RVF_G_ADDR_ID task_id, UINT16 event) {	

	if (task_id >= MAX_RVF_TASKS || !pRtAddrIdTable[task_id] ) return 1;

	NU_Set_Events (pRtAddrIdTable[task_id]->pOSEvtGrp, (UNSIGNED)event, NU_OR);

	return 0;
}

/*******************************************************************************
**
** Function         rvf_get_taskid
**
** Description      This function gets the currently running task ID.
**
** Returns          task ID
**
*******************************************************************************/
T_RVF_G_ADDR_ID rvf_get_taskid(void) {	/* Retrieve the taskid using index of the task pointer in the OSTCB array */
	NU_TASK * currTask=0;
	T_RVF_G_ADDR_ID taskId=RVF_INVALID_ADDR_ID;

	currTask = NU_Current_Task_Pointer();

	if( currTask != NU_NULL) {
		/* find the task pointer in the OSTCB array */
		for ( taskId = 0; taskId < MAX_RVF_TASKS; taskId++) {
			if( (pRtAddrIdTable[taskId]!= NULL) && 
				((pRtAddrIdTable[taskId]->pOSTCB)) == currTask) return taskId;
		}
		return RVF_INVALID_ADDR_ID;
	} else {
		return RVF_INVALID_ADDR_ID; /* error case, must return an error code */
	}
}


/*******************************************************************************
**
** Function         rvf_get_taskname
**
** Description      This function gets the currently running task name.
**
** Returns          pointer to task name or NULL if error
**
*******************************************************************************/
char* rvf_get_taskname(void)
{	T_RVF_G_ADDR_ID id = rvf_get_taskid();
	
	if (id == 0xFF) return NULL; /* return NULL if rvf_get_taskid returns 0xFF */

    return pRtAddrIdTable[id]->symbolic_name; /*(OSTName[ id ]);*/
}

/*******************************************************************************
**
** Function         rvf_enable
**
** Description      This function enables interrupts.
**
** Returns          void
**
*******************************************************************************/
void rvf_enable(void)
{

	if( --OSDisableNesting == 0)	/* Control nesting interrupt */
	{	
		if( OSInterruptAlreadyMasked == TRUE) /*	check if interrupts have been disabled outside RVF, 
													in that case, do not enable interrupts */
		{	OSInterruptAlreadyMasked = FALSE;
		}
		else
		{	NU_Control_Interrupts(OSLastIntLevel);
		}
	}

}

#ifdef _WINDOWS

/*******************************************************************************
**
** Function         INT_Check_IRQ_Mask()
**
** Description      This function checks if the IRQ are disabled (outside RVF).
**
** Returns          IRQ mask
**
*******************************************************************************/
UINT32 INT_Check_IRQ_Mask(void)
{
	return 0;
}
#else

/*-------------------------------------------------------*/ 
/* INT_Check_IRQ_Mask()                                  */
/*-------------------------------------------------------*/
/*                                                       */
/* Description: check in the CPSR register if the IRQ	 */
/*              are masked out or not.                   */
/* ------------                                          */
/*												         */
/*-------------------------------------------------------*/
/* Declaration of ASM INT_Check_IRQ_Mask function */
UINT32 INT_Check_IRQ_Mask(void);

asm("           .def	$INT_Check_IRQ_Mask");
asm("$INT_Check_IRQ_Mask  ");			    
asm("           .ref _INT_32_Check_IRQ_Mask");
asm(".state16");
asm("	ADR	r0,_INT_32_Check_IRQ_Mask  ");
asm("	BX r0  ");

asm("	.align");
asm("	.state32");
asm("           .def _INT_32_Check_IRQ_Mask");
asm("_INT_32_Check_IRQ_Mask  ");

asm("	MRS	r0,CPSR  ");   // pick up CPSR 
asm("	BX	lr  ");        // return to caller

#endif


/*******************************************************************************
**
** Function         rvf_disable
**
** Description      This function disables interrupts.
**
** Returns          void
**
*******************************************************************************/

#define RVF_IRQ_DISABLED_MASK 0x00000080

void rvf_disable(UINT8 who)
{

	/* Control interrupt nesting ourselves */
	if (OSDisableNesting == 0)
	{
		if ( INT_Check_IRQ_Mask() & RVF_IRQ_DISABLED_MASK)	/* if IRQ are disabled (outside RVF) */
		{	OSInterruptAlreadyMasked = TRUE;
		}
		else
		{	OSLastIntLevel = NU_Control_Interrupts(NU_DISABLE_INTERRUPTS);
		}
	}
	OSDisableNesting++;

}


/*******************************************************************************
**
** Function         rvf_used_stack
**
** Description      This function tries to calculate the amount of
**                  stack used by looking for a zero.
**
** Returns          the number of non-zero bytes on the stack
**
*******************************************************************************/
UINT16 rvf_used_stack(T_RVF_G_ADDR_ID task)
{
	UINT16 j, stacksize;
	UINT8 *p;

	if(!pRtAddrIdTable[task]) return 0;

	stacksize = pRtAddrIdTable[task]->os_stack_size; /*OSStackSize[task];*/
	p = pRtAddrIdTable[task]->p_os_stack; /*OSStack[task];*/
	for(j = 0; (j < stacksize) && (*p++ == 0xFE); j++);

	return ((UINT16)(stacksize - j));
}


/*******************************************************************************
**
** Function         rvf_dump_tasks
**
** Description      This function dump all the rvf tasks.
**
** Returns          void
**
*******************************************************************************/
void rvf_dump_tasks()
{	UINT8 num_task;
	char task_info[100];

	rvf_send_trace("*** START DUMPING TASKS ***", 27, NULL_PARAM, RV_TRACE_LEVEL_DEBUG_HIGH, RVM_USE_ID);
	
	/* for each task, display its name, its id, its stack size*/
	rvf_send_trace("*TASK_NAME Id Stack_size Used_stack", 35, NULL_PARAM, RV_TRACE_LEVEL_DEBUG_HIGH, RVM_USE_ID);
		
	for ( num_task = 0; num_task < MAX_RVF_G_ADDR_ID; num_task++ )
	{	
		/* trace the task if it has been created*/
		if (pRtAddrIdTable[num_task] != 0 ) {	
			sprintf( task_info, "%10.10s %2d      %5d      %5d",
				pRtAddrIdTable[num_task]->symbolic_name /*OSTName[num_task]*/, 
				num_task, 
				pRtAddrIdTable[num_task]->os_stack_size, 
				rvf_used_stack( num_task) );
			rvf_send_trace( task_info, 35, NULL_PARAM, RV_TRACE_LEVEL_DEBUG_HIGH, RVM_USE_ID);		
		}
	}
	/* find a way to track task stack usage and display it */
	/* using NU_Check_Stack, NU_Task_Information or finding the first non-zero value in the stack */
}


/****************************************************************************/
/* For Big Endian Processors swap the bytes                                 */
#if defined(__BIG_ENDIAN)
UINT16 ntohs(UINT16 n)
{
	register UINT8  tmp;
	register UINT8  *p=(UINT8 *)&n;

	tmp  = p[0];
	p[0] = p[1];
	p[1] = tmp;

	return n;
}

UINT32 ntohl(UINT32 n)
{
	register UINT8 tmp;
	register UINT8 *p=(UINT8 *)&n;

	tmp  = p[0];
	p[0] = p[3];
	p[3] = tmp;

	tmp  = p[1];
	p[1] = p[2];
	p[2] = tmp;

	return n;
}

#endif /* __BIG_ENDIAN*/




/******************************************************************************
**
** Function			rvf_send_trace
**
** Description		This function displays a message essentially for debug purposes.
**					It displays the msg_length characters of the string pointed by msg
**					and the value of val.		
**
** Returns			void
**
******************************************************************************/
#ifdef  _WINDOWS 
#ifndef _CONSOLE
void rvf_send_trace1( INT8 * msg, UINT8 msg_length, UINT32 val, UINT8 TRACE_LEVEL, UINT32 swe_use_id)
{
/* Function to display trace message for Tool */

	UINT32 trace_type = swe_use_id;
	
	Trace( msg, msg_length, val, TRACE_LEVEL, trace_type);
}
#endif	
#endif	

#ifdef _CONSOLE  /* CONSOLE */
void rvf_send_trace1( INT8 * msg, UINT8 msg_length, UINT32 val, UINT8 TRACE_LEVEL, UINT32 swe_use_id) {
	const	int MAX		= 1000; 
	static	int l		= 0;
	char	buf[100];
	HANDLE	out			= 0;
    int nb;

	if(!out)out=GetStdHandle(STD_OUTPUT_HANDLE); 

	rvf_disable(25);
	
	sprintf(buf,"%s %d\n", msg, val);
	WriteConsole(out, buf, strlen(buf), &nb, NULL);
	
	if(l>=MAX) {
		system("cls");
		l=0;
	} else l++;

	rvf_enable();
}
#endif			/* CONSOLE */

/*******************************************************************************
**
** Function         rvf_resume_task
**
** Description      This function is called to resume a rvf task which is in a suspend state.
**
** Returns          RVF_OK if successful, else an error code
**
*******************************************************************************/
T_RVF_RET rvf_resume_task( T_RVF_G_ADDR_ID taskid)
{
	if(!pRtAddrIdTable[taskid]) return RVF_INVALID_PARAMETER;
	if(!pRtAddrIdTable[taskid]->pOSTCB) return RVF_INVALID_PARAMETER;
	/* resume the task */
	if ( NU_INVALID_TASK == NU_Resume_Task( pRtAddrIdTable[taskid]->pOSTCB) ) {// A-M-E-N-D-E-D!
		return RVF_INTERNAL_ERR; //RVF_INVALID_PARAMETER;
	}
	return RVF_OK;
}

/*******************************************************************************
**
** Function         rvf_initialize_mutex
**
** Description      This function initialize a mutex structure, which will be used 
**					to protect shared variables against simultaneous access.
**
** Returns          RVF_OK if successful, else an error code
**
*******************************************************************************/
T_RVF_RET		rvf_initialize_mutex( T_RVF_MUTEX * mutex)
{

	/* initializes the mutex structure */
	
	if( NU_Create_Semaphore( (NU_SEMAPHORE *)mutex, "RVF", 1, NU_PRIORITY ) != NU_SUCCESS)
	{	return RVF_INTERNAL_ERR;
	}
	
	return RVF_OK;
}

/*******************************************************************************
**
** Function         rvf_lock_mutex
**
** Description      This function locks a mutex to avoid simultaneous access.
**					If the mutex is already locked, the task is suspended 
**					until the mutex is unlocked.
**
** Returns          RVF_OK if successful, else an error code
**
*******************************************************************************/
T_RVF_RET		rvf_lock_mutex( T_RVF_MUTEX * mutex)
{
	if( NU_Obtain_Semaphore( (NU_SEMAPHORE *)mutex, NU_SUSPEND ) != NU_SUCCESS)
	{	return RVF_INTERNAL_ERR;
	}

	return RVF_OK;
}

/*******************************************************************************
**
** Function         rvf_unlock_mutex
**
** Description      This function unlocks a mutex to avoid simultaneous access.
**
** Returns          RVF_OK if successful, else an error code
**
*******************************************************************************/
T_RVF_RET		rvf_unlock_mutex( T_RVF_MUTEX * mutex)
{
	if( NU_Release_Semaphore( (NU_SEMAPHORE *)mutex ) != NU_SUCCESS)
	{	return RVF_INTERNAL_ERR;
	}

	return RVF_OK;
}

/*******************************************************************************
**
** Function         rvf_delete_mutex
**
** Description      This function deletes a previously created mutex.
**
** Returns          RVF_OK if successful, else an error code
**
*******************************************************************************/
T_RVF_RET		rvf_delete_mutex( T_RVF_MUTEX * mutex)
{
	if( NU_Delete_Semaphore( (NU_SEMAPHORE *)mutex ) != NU_SUCCESS)
	{	return RVF_INTERNAL_ERR;
	}

	return RVF_OK;
}

void rvf_yield() { NU_Relinquish(); }

/* convenience function    */ 
UINT8 rvf_isType2() {
	if(pRtAddrIdTable[rvf_get_taskid()]->type_code==ET2_HOST_TASK) return 1;
	else return 0;
}

