/****************************************************************************/
/*	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"

/****************************************************************************/
/*					Local prototypes										*/
/****************************************************************************/

static	void	_bx_ProcProtNorm( void *par1, void *par2 );

/****************************************************************************/
/*					Public API functions									*/
/****************************************************************************/

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

/**
*	Lowers the protection level of the caller.
*
*	The function lowers the protection level of the caller if the caller was
*	at a level higher than the parameter. If the caller's protection level is
*	already at or below the parameter, the function does not change the caller's
*	protection level. If the function is called from an ISR, then the protection
*	level can not be lowered below BX_PROT_IRQD, if called from a soft ISR
*	then the level can not be lowered below BX_PROT_COOP, or, if the kernel
*	disabled interrupts due to the event queue being over the watermak, below
*	BX_PROT_IRQD.
*	If the requested level is out of the allowed boundaries, then it will be
*	changed to be between the boundaries.
*
*	@param	prot	The required new protection level
*
*	@return			The protection level before the call
*
*	@attention
*		This function does not suspend the caller but can cause a context
*		switch as a result of the priority change.
*
*	@note
*		If called from a soft ISR with a parameter of BX_PROT_COOP, it may
*		not actually enable the IRQ level interrupt if the kernel's event
*		queue is over the watermark. Therefore, in a soft IRQ handler,
*		even if you know that an IRQ is pending, you should not expect
*		it to be accepted immediately after this call.
*/

int		BX_ProcProtLower( BX_PROT prot )
{
unsigned 	cpsr;
unsigned 	retv;
unsigned 	minp;
BX_PROC		*self;

	cpsr = _bx_CpsrGet();
	prot = prot > BX_PROT_FIQD ? BX_PROT_FIQD : prot;
	retv = BX_PROT_COOP;
	minp = BX_PROT_NORM;
	self = NULL;

	if ( cpsr & ARM_IRQDI_MASK ) retv = BX_PROT_IRQD;
	if ( cpsr & ARM_FIQDI_MASK ) retv = BX_PROT_FIQD;

	switch ( _bx_state.exec[ BX_EXEC_MOD ] ) {

		case BX_MODE_IRQ:

			// Called from an ISR, minimum priority is IRQ disabled

			minp = BX_PROT_IRQD;
			break;

		case BX_MODE_SYS:

			// Called from the kernel, Minimum is cooperative mode,
			// but if the kernel disabled interrupts because the
			// queue was too full, we must not override that.

			if ( _bx_state.cpsr & ARM_IRQDI_MASK )

				minp = BX_PROT_IRQD;
			else
				minp = BX_PROT_COOP;

			break;

		default:

			// User mode call, no minimum and the previous state
			// is not the value indicated by the current CPSR
			// but whatever is stored in the process descriptor.

			self = _bx_ProcSelf();
			retv = self->prot;
			break;
	}

	if ( prot < minp ) prot = minp;

	if ( prot < retv ) {

		// The protection level indeed must be lowered.

		if ( prot == BX_PROT_NORM ) {

			// We have to go through the event queue

			_bx_IrqDis();
			_bx_QueueEvent( cpsr & ~( ARM_IRQDI_MASK | ARM_FIQDI_MASK ),
								_bx_ProcProtNorm, self, NULL );
		}
		else {

			if ( self ) self->prot = prot;
			if ( prot < BX_PROT_FIQD ) cpsr &= ~ARM_FIQDI_MASK;
			if ( prot < BX_PROT_IRQD ) cpsr &= ~ARM_IRQDI_MASK;
			_bx_CpsrSet( cpsr );
		}
	}

	return( retv );
}

/** @} */

/****************************************************************************/
/*					Local functions											*/
/****************************************************************************/

/*
*	Set the protection level to BX_PROT_NORM for a process
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*	This function is called through the kernel event mechanism.
*	The process descriptor is passed in the first parameter.
*/

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

	(void) par2;

	proc->prot = BX_PROT_NORM;

	// If we're the first in the ready queue and we have a process behind us
	// that has higher priority than us, then we must reorder the queue

	if ( ! proc->prev && proc->next && proc->next->epri > proc->epri ) {

		_bx_ProcSuspend( proc, NULL );
		_bx_ProcRestart( proc, NULL );
	}
}

