/****************************************************************************/
/*	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 */
/** @{ */

/**
*	Raises the caller's protection level.
*
*	The parameter is one of the defined protection levels.
*	If the current protection level is is lower than the one specified by
*	the parameter, then the caller's protection level will be elevated to
*	the desired level. If the requested level is less than or equal to the
*	current protection level, then the function does nothing. In any case,
*	the function returns the protection level before the call. This function
*	can be called from interrupt service routines as well as soft interrupts.
*	A real ISR is always at least at level BX_PROT_IRQD, a soft interrupt is
*	at least at level BX_PROT_COOP (even though soft interrupts can not call
*	kernel functions that would suspend them). The function's intended usage,
*	when called from ISR-s or soft interrupts is to protect a short bit of
*	code from FIQs or IRQs. It should be noted that in an ISR, especially
*	if you are in ARM mode, it is quite faster to disable the FIQ by a
*	call to BX_FiqDisable() call than calling this function. This, in an
*	ISR the two fragments below have the same semantics, although the
*	second is more efficient:
*
*	@code
*	void my_isr( void )
*	{
*	int		old_status;
*
*		... do something ...
*		// now we must protect ourselves from FIQs
*		old_status = BX_ProcProtRaise( BX_PROT_FIQD );
*		// Do the protected bits
*		... we're protected ...
*		BX_ProcProtLower( old_status );
*	}
*
*	void my_isr( void )
*	{
*	int		old_status;
*
*		... do something ...
*		// now we must protect ourselves from FIQs
*		old_status = BX_FiqDisable();
*		// Do the protected bits
*		... we're protected ...
*		BX_FiqRestore( old_status );
*	}
*	@endcode
*
*	However, the same is not true for functions that can be called from
*	ISRs as well as from soft interrupts or user code. In that case the
*	efficient approach of directly manipulating the processor's status
*	register is not sufficient because the kernel is not notified about
*	the status change. This then can result in lost kernel events, context
*	switching a process while the interrupt is disabled and other very
*	hard to debug problems. Therefore, it is advised that if you are
*	not absolutely certain that the code fragment that needs FIQ protection
*	will never, ever be called from a context other than a real IRQ ISR,
*	use the BX_ProcProtRaise() and BX_ProcProtLower() mechanism. If you
*	are sure that the fragment is only executed within an IRQ ISR, then
*	it is more efficient to use the BX_FiqDisable() and BX_FiqRestore()
*	functions. Note that the latter function pair does not check if the
*	caller is an IRQ ISR or not (the check is omitted for efficincy) and
*	thus you need to be careful with them.
*
*	@param	prot	The desired protection level
*	@return			The protection level before the call
*/

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

	prot = prot > BX_PROT_FIQD ? BX_PROT_FIQD : prot;
	cpsr = _bx_CpsrGet();
	self = NULL;

	switch ( _bx_state.exec[ BX_EXEC_MOD ] ) {

		case BX_MODE_IRQ:

			// Called from an ISR

			if ( cpsr & ARM_FIQDI_MASK )

				retv = BX_PROT_FIQD;
			else
				retv = BX_PROT_IRQD;

			break;

		case BX_MODE_SYS:

			// Called from the kernel context

			if ( cpsr & ARM_FIQDI_MASK )

				retv = BX_PROT_FIQD;

			else if ( cpsr & ARM_IRQDI_MASK )

				retv = BX_PROT_IRQD;
			else
				retv = BX_PROT_COOP;

			break;

		default:

			// Called from user land

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

	if ( prot > retv ) {

		// The level is indeed elevated

		if ( self ) self->prot = prot;
		if ( prot > BX_PROT_COOP ) cpsr |= ARM_IRQDI_MASK;
		if ( prot > BX_PROT_IRQD ) cpsr |= ARM_FIQDI_MASK;
		_bx_CpsrSet( cpsr );
	}

	return( retv );
}

/** @} */

