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

/*
*	Flushes the kernel's evet queue
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*	This function runs on the kernel stack. When it is called,
*	the interrupts are disabled and when it leaves they will be
*	disabled again. During the execution, however, they can be
*	enabled. This function is called from the IRQ handler and it
*	must not be called from anywhere else.
*/

void	_bx_QueueFlush( void )
{
BX_STAT			*stat;
BX_EVNT			*evnt;
unsigned int	qlen, mark, getx, emax, cpsr;

	stat = &_bx_state;
	mark = stat->ewtm;
	getx = stat->eget;
	emax = stat->elen;
	evnt = stat->evts + getx;
	cpsr = _bx_CpsrGet();

	for ( ;; ) {

		while ( ( qlen = stat->exec[ BX_EXEC_LEN ] ) ) {

			// There event queue is not empty. Decrement the queue
			// length. Note that the length of the queue can not be
			// cached since interrupts can add new events.

			stat->exec[ BX_EXEC_LEN ] = --qlen;

			if ( qlen < mark ) {

				// The queue length is below the watermark,
				// we can enable the interrupts

				stat->cpsr = cpsr & ~ARM_IRQDI_MASK;
				_bx_CpsrSet( stat->cpsr );
			}

			// Call the event handler, possibly with interrupts enabled.
			// Note that while we're in this loop and interrupts are
			// enabled, they can be disabled by an interrupt that makes
			// the event queue longer than the watermark.
			// Therefore the event handling functions must NEVER enable
			// interrupts without consulting the queue length first - that
			// is, they must use the _bx_IrqEnaSys() function, which does
			// exactly that.

			(*evnt->func)( evnt->par1, evnt->par2 );

			// Get the pointer to the next event and update the
			// 'get' index to the queue as well.

			evnt++;
			getx++;

			if ( getx == emax ) {

				getx = 0;
				evnt = stat->evts;
			}

			// Make sure that the interrupt is disabled for the
			// event queue length test.

			_bx_CpsrSet( cpsr );
		}

		// Now the queue is empty. If we have a process to run,
		// then we have to return to it, that is, leave the loop.

		if ( stat->exec[ BX_EXEC_NEW ] ) break;

		// There is no process to run, call the idle handler.
		// The idle handler should not enable interrupts,
		// but even if it does, it's not dangerous.

		if ( stat->idle ) (*stat->idle)();

		// Enable interrupts so that the system can be
		// ticked over. Note that we do not have to update
		// stat->eget, because _bx_QueueEvent doesn't use it.

		_bx_CpsrSet( cpsr & ~ARM_IRQDI_MASK );

		// Now disable interrupts again and check if
		// anything has been put into the queue

		_bx_CpsrSet( cpsr );
	}

	// Event queue processing is finished, update the get pointer
	// in the kernel status structure and return to the interrupt
	// handler.

	stat->eget = getx;
}

/*
*	Puts an event onto the kernel's event queue
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*	This function can be called from all sorts of contexts, but one
*	thing is sure, the interrupt is disabled at entry.
*
*	If the function is called from an interrupt context, then
*	it queues the request and returns.
*
*	If it is called from the kernel's event queue handler, then
*	it sets the interrupt flag as per the current status, executes
*	the event and returns.
*
*	If its is called from user context, then it queues the event.
*	Then, if the passed status register is 0, then simply returns.
*	If it is not 0, then it switches to kernel mode, to empty the
*	queue. This might cause all sorts of context switches, but when
*	control gets back to this point, then the passed status register
*	is loaded into CPSR and the function returns.
*/

void	_bx_QueueEvent( int cpsr, void (*func)(void*,void*),
					    void *par1, void *par2 )
{
BX_STAT		*stat;
BX_EVNT		*evnt;
unsigned	qlen;
int			doit;

	// THIS SHALL BE REMOVED LATER!!! (when the bug is found)

	if ( ( _bx_CpsrGet() | ARM_IRQDI_MASK ) == 0 ) {

		asm volatile ( " swi " );
	}

//	_bx_CpsrSet( _bx_CpsrGet() | ARM_IRQDI_MASK );

	stat = &_bx_state;
	doit = 0;

	switch ( stat->exec[ BX_EXEC_MOD ] ) {

		case BX_MODE_IRQ:

			// The caller is an interrupt handler. Queue the event
			// and do not initiate event processing.

			cpsr = 0;
			break;

		case BX_MODE_USR:

			// We were called from user land. We always queue the event
			// and by default start the event processing

			break;

		default:

			// System mode call. We execute the event.

			doit = 1;
			break;
	}

	if ( doit ) {

		// We have to execute the event directly. If we
		// are called from the kernel itself, then we
		// also enable interrupts.

		_bx_CpsrSet( stat->cpsr );
		(*func)( par1, par2 );
	}
	else {

		// We have to queue the event

		if ( ( qlen = stat->exec[ BX_EXEC_LEN ] ) < stat->elen ) {

			// The event queue is not full. Get the next event slot.

			evnt = stat->evts + stat->eput++;
			if ( stat->eput == stat->elen ) stat->eput = 0;

			// Store the event

			evnt->func = func;
			evnt->par1 = par1;
			evnt->par2 = par2;

			// Increment the length of the event queue

			stat->exec[ BX_EXEC_LEN ] = ++qlen;

			if ( qlen > stat->ewtm ) {

				// The number of events is above the watermark.
				// We must disable further interrupts.

				stat->cpsr |= ARM_IRQDI_MASK;
				_bx_SpsrDis();
			}

			if ( cpsr ) {

				// We were called from user land with a valid
				// status register. We have to run the queue
				// and then restore the status reg.

				_bx_QueueClear();
				_bx_CpsrSet( cpsr );
			}
		}
	}
}

