/****************************************************************************/
/*	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_ProcNice( void *par1, void *par2 );

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

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

/**
*	Change the priority of the caller.
*
*	The real priority of the caller will be set to the given value.
*	If the priority is higher than the current effective priority of
*	the caller, then the effective priority will be elevated to the new
*	level. If the new priority is lower than the effective priority of
*	the caller, then the effective priority will be adjusted, subject to
*	priority ceiling and priority inheritance rules. If the requested
*	priority is higher than BX_MAX_PRIO, then it will be limited to that
*	value.
*
*	@param	prio			The requested new priority. If this value is
*							negative, then no priority change is performed
*							but the current real priority is returned.
*
*	@retval	BX_ERR_CONTEXT	The function was not called from an user context
*
*	@retval >=0				The previous real priority of the caller
*
*	@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 as a result of the priority change.
*/

int		BX_ProcNice( int prio )
{
int			retv;
unsigned 	cpsr;
BX_PROC		*self;

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

		// Called from an ISR or a queue run.

		retv = BX_ERR_CONTEXT;
	}
	else {

		prio = prio >= BX_PRIO_MAX ? BX_PRIO_MAX : prio;
		self = _bx_ProcSelf();
		retv = self->rpri;

		if ( prio >= 0 ) {

			cpsr = _bx_IrqDis();
			self->rpri = prio;

			if ( (unsigned) prio >= self->epri ) {

				// The priority change makes the effectice priority go up.
				// We know that this process is running, therefore it is not
				// suspended on any mutable object. Therefore, raising its
				// priority could not possibly cause any priority cascade.
				// Furthermore, it is now on the head of the ready queue and
				// increasing its priority will not change the queue either.
				// Thus, we don't have to go through the event queue.

				self->rpri = prio;
				self->epri = prio;
				_bx_IrqEnaUsr( cpsr );
			}
			else {

				// We're dropping the priority of the process. This might
				// cause a reordering of the ready queue so we must go
				// through the event queue.

				_bx_QueueEvent( cpsr, _bx_ProcNice, self, (void *) prio );
			}
		}
	}

	return( retv );
}

/** @} */

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

static	void	_bx_ProcNice( void *par1, void *par2 )
{
BX_PROC		*proc = par1;
unsigned	prio = (unsigned) par2;

	// We are called if we have to drop the effective priority. We know
	// that the caller is running and is at the head of the ready queue.
	// All we have to do is to determine the lowest level of the effective
	// priority, sink it and then possibly re-schedule the process.

	if ( proc->head && prio < proc->head->prio ) prio = proc->head->prio;
	_bx_ProcRequeue( NULL, proc, &prio );
}

