/****************************************************************************/
/*	Copyright (c) 2009, Bendor Research Pty. Ltd. All rights reserved.		*/
/*	This software is released under the GNU General Public License, version	*/
/*	3, with the exemptions that 1) the header files installed by this 	 	*/
/*	package can be included in closed-source code and 2) the libraries		*/
/*	built by this package can be statically linked into a closed-source 	*/
/*	binary. See the file LICENSE for the details.							*/
/*	This software is provided "AS IS", without any warranty or guarantee.	*/
/*	By using this software you acknowledge that neither Bendor Research nor	*/
/*	any of its personnel or affiliates can be held liable for any damages,	*/
/*	including, but not limited to, loss of business, loss of money, loss of */
/*	life, loss of reputation, arising out of this software being faulty or	*/
/*	not performing as advertised, even if Bendor Research was aware of such	*/
/*	faults in the software. You use this software at your own risk, any and */
/*	all liabilities that arise out of your using this software are strictly	*/
/*	yours.																	*/
/****************************************************************************/

#include "kernel.h"

/** @addtogroup bx_proc */
/** @{ */

/**
*	Creates a new process.
*
*	@param	proc	Pointer to a process descriptor that will hold the internal
*					state of the newly created process.
*	@param	func	Pointer to a function returning void and receiving a void
*					pointer. This function will be the entry point of the new
*					process. This function must <b>not</b> return, if it does,
*					the system will crash.
*	@param	parm	The parameter that will be passed to the function.
*	@param	stack	The initial stack pointer for the new process. It must be
*					aligned on a 32-bit word boundary. If you use and unsigned
*					int array, declared like stk[S_SIZE] then you should pass
*					a pointer to just behind the last element of the array.
*					That is, you should pass stk+S_SIZE. If you pass an
*					incorrect stack pointer, the system will crash. <i>It goes
*					without saying that the stack areas for processes must be
*					distinct, even if you start the same function for different
*					processes. Do not laugh. I've seen it done.</i>
*	@param	prio	The initial priority of the process, between BX_PRIO_MIN
*					(least important) and BX_PRIO_MAX (most important). If the
*					parameter is out of these bounds, it will be cropped.
*	@param	prot	The initial protection level of the process. If the level
*					passed is higher than BX_PROT_FIQD, then it will be set to
*					BX_PROT_FIQD.
*
*	@retval	>0				Success
*	@retval	BX_ERR_CONTEXT	The function was called from an ISR context.
*
*	@attention
*		This function should not be called from an ISR or delayed ISR.
*		This function does not suspend the caller but can cause a context
*		switch if a higher priority process is created by the call.
*/

int		BX_ProcSpawn( BX_PROC *proc, BX_PROC_FUNC func, void *parm,
					  int *stack, unsigned int prio, BX_PROT prot )
{
int		cpsr;

	if ( _bx_state.exec[ BX_EXEC_MOD ] != BX_MODE_USR ) {

		// Processes can not be started from interrupt level,
		// report error.

		proc->type = 0;
		return( BX_ERR_CONTEXT );
	}

	// Limit the priority and protection levels

	if ( prio > BX_PRIO_MAX  ) prio = BX_PRIO_MAX;
	if ( prot > BX_PROT_FIQD ) prot = BX_PROT_FIQD;

	// Work out the status register for the new process

	cpsr = ARM_STATE_SYS;

	if ( (int) func & 1 )		cpsr |= ARM_THUMB_MASK;
	if ( prot > BX_PROT_COOP )	cpsr |= ARM_IRQDI_MASK;
	if ( prot > BX_PROT_IRQD )	cpsr |= ARM_FIQDI_MASK;

	proc->type = BX_TYPE_PROC;
	proc->epri = prio;
	proc->wait = NULL;
	proc->prev = NULL;
	proc->next = NULL;
	proc->rpri = prio;
	proc->prot = prot;
	proc->head = NULL;
	proc->tail = NULL;
	proc->sigs = 0;
	proc->mask = 0;
	proc->cntx[ BX_CNTX_R0 ] = (int) parm;
	proc->cntx[ BX_CNTX_SP ] = (int) stack;
	proc->cntx[ BX_CNTX_LR ] = (int) _bx_ProcExit;
	proc->cntx[ BX_CNTX_PC ] = (int) func;
	proc->cntx[ BX_CNTX_PS ] = cpsr;

	BX_TimerInit( &proc->timo );

	// Wake up the new process (i.e. put it onto the ready queue) and
	// report success.

	_bx_QueueEvent( _bx_IrqDis(), _bx_ProcRestart, proc, NULL );
	return( +1 );
}

/** @} */

/*
*	This function is called when a process exits
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/

void	_bx_ProcExit( void )
{
int		cpsr;
BX_PROC	*proc;

	proc = _bx_ProcSelf();
	cpsr = _bx_IrqDis();
	proc->type = 0;
	_bx_QueueEvent( 0, _bx_ProcSuspend, proc, NULL ); // FIXME!!!
	_bx_QueueEvent( cpsr, _bx_ProcKill, proc, NULL );
	for(;;);
}

/*
*	This is the kill function
*	~~~~~~~~~~~~~~~~~~~~~~~~~
*/

void	_bx_ProcKill( void *par1, void *par2 )
{
BX_PROC	*proc = par1;

	// FIXME

	(void) proc;
	(void) par2;
}


