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

void dump( const char *name );
void dump( const char *name )
{
BX_STAT	*stat = &_bx_state;
BX_PROC	*ptr;

	UartPuts( name );
	UartPuts( ":[" );

	ptr = stat->rdyq.head;
	UartHexl( (int) ptr );
	UartPutc( ',' );
	UartHexl( (int) stat->rdyq.tail );
	UartPuts( "]" );

	while ( ptr ) {

		UartPuts( " (" );
		UartHexl( (int) ptr->prev );
		UartPutc( ',' );
		UartHexl( (int) ptr->next );
		UartPuts( ") " );
		ptr = ptr->next;
	}

	UartPuts( "\r\n" );
}

/*
*	This function removes the par1 process from the ready queue
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*	It uses single linked list, which is not really efficient,
*	but since the removal most of the time happens with the
*	head of the ready queue, it is fast in practice.
*/

void	_bx_ProcSuspend( void *par1, void *par2 )
{
BX_STAT	*stat = &_bx_state;
BX_PROC	*proc = par1;

	(void) par2;

	// Remove the process from the queue

	_bx_ProcDel( &stat->rdyq, proc );

	// Set the current context to the head of the ready queue.
	// If the queue is empty, we have to set it to NULL.

	if ( stat->rdyq.head )

		stat->exec[ BX_EXEC_NEW ] = (int) stat->rdyq.head->cntx;
	else
		stat->exec[ BX_EXEC_NEW ] = (int) NULL;
// dump( "suspend" );
}

/*
*	Wakes up the par1 process
*	~~~~~~~~~~~~~~~~~~~~~~~~~
*	The process is placed onto the ready queue, by its effective
*	priority. If the current process is protected, then the new
*	process will get behind it even if it has higher priority.
*/

void	_bx_ProcRestart( void *par1, void *par2 )
{
BX_STAT		*stat = &_bx_state;
BX_PROC		*this = par1;
BX_PROC		*that;

	(void) par2;

	// Set the process' priority function to the relevant one

	if ( ( that = stat->rdyq.head ) && that->prot > BX_PROT_NORM ) {

		// The first process must be skipped

		that = that->next;
	}

	// Now find the insertion place. If identical priority
	// processes are present, the new one will be the last.

	while ( that && that->epri >= this->epri ) that = that->next;

	// Insert the process just before the one we found

	if ( that )

{
// dump( "Restart ins" );
		_bx_ProcInsBefore( &stat->rdyq, this, that );
}
	else

{
// dump( "Restart add" );
		_bx_ProcAddTail( &stat->rdyq, this );
}

	// Set the current context to the head of the ready queue.
	// We do know that the ready queue is definitely not empty.

	stat->exec[ BX_EXEC_NEW ] = (int) stat->rdyq.head->cntx;
// dump( "restart" );
}

/*
*	Sets the priority of the process to the given value
*/

BX_PROC	*_bx_ProcRequeue( BX_WAIT *unused, BX_PROC *this, unsigned *pptr )
{
BX_STAT		*stat = &_bx_state;
BX_PROC		*that;
unsigned	oldp, newp;

	(void) unused;

	newp = *pptr;
	oldp = this->epri;

	if ( newp == oldp ) {

		// Nothing has been changed

// dump( "requeue" );
		return( NULL );
	}

	this->epri = newp;

	if ( newp > oldp ) {

// UartPuts( "Requeue go up\r\n" );
		// Process priority is increased, we have to move it towards the
		// head of the ready queue.

		for ( that=this->prev; that && that->epri < newp ; that=that->prev );

		if ( ! that && stat->rdyq.head != this &&
			 stat->rdyq.head->prot < BX_PROT_COOP ) {

			// The process should go to the top of the ready queue,
			// but the currently running process is protected, thus
			// it should go beyond that.

			that = stat->rdyq.head;
		}

		if ( this->prev != that ) {

			_bx_ProcDel( &stat->rdyq, this );

			if ( that ) {

				_bx_ProcInsAfter( &stat->rdyq, this, that );
			}
			else {

				_bx_ProcAddHead( &stat->rdyq, this );
			}
		}
	}
	else {

// UartPuts( "Requeue go down" );
		// The priority went down, we have to move the process towards the tail.

		for ( that=this->next ; that && that->epri >= newp ; that=that->next );
//UartPuts( " this=" );
//UartHexl( (int) this );
//UartPuts( " that=" );
//UartHexl( (int) that );
//UartPuts( "\r\n" );

		if ( this->next != that ) {

			_bx_ProcDel( &stat->rdyq, this );
//dump( "after delete" );

			if ( that ) {

				_bx_ProcInsBefore( &stat->rdyq, this, that );
//dump( "after ins before" );
			}
			else {

				_bx_ProcAddTail( &stat->rdyq, this );
//dump( "after add tail" );
			}
		}
	}

	// Set the new head of the ready queue, in case it changed

	stat->exec[ BX_EXEC_NEW ] = (int) stat->rdyq.head->cntx;
//dump( "requeue" );
	return( NULL );
}

