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

#ifndef _KERNEL_H_
#define	_KERNEL_H_

/****************************************************************************/
/*				Definitions shared between C and assembly					*/
/****************************************************************************/

/*
*	The first field of the kernel state structure is an array of words.
*	These contain the data that is shared between the assembly and C code.
*	The definitions below are the array indices for the various objects.
*/

#define	BX_EXEC_OLD		0			// Pointer to the old context
#define	BX_EXEC_NEW		1			// Pointer to the new context
#define	BX_EXEC_LEN		2			// Length of the event queue
#define	BX_EXEC_ISR		3			// User's ISR address
#define	BX_EXEC_STK		4			// Kernel stack
#define	BX_EXEC_MOD		5			// Current operation mode

/*
*	Mode that can be examined by kernel functions
*/

#define	BX_MODE_USR		0			// Called by the user
#define	BX_MODE_SYS		1			// Called by the kernel event queue flush
#define	BX_MODE_IRQ		2			// Called by an interrupt handler

/*
*	Register index in the context array
*/

#define	BX_CNTX_R0		0			// General purpose register 0
#define	BX_CNTX_R1		1			// General purpose register 1
#define	BX_CNTX_R2		2			// General purpose register 2
#define	BX_CNTX_R3		3			// General purpose register 3
#define	BX_CNTX_R4		4			// General purpose register 4
#define	BX_CNTX_R5		5			// General purpose register 5
#define	BX_CNTX_R6		6			// General purpose register 6
#define	BX_CNTX_R7		7			// General purpose register 7
#define	BX_CNTX_R8		8			// General purpose register 8
#define	BX_CNTX_R9		9			// General purpose register 9
#define	BX_CNTX_SL		10			// Stack limit
#define	BX_CNTX_FP		11			// Frame pointer
#define	BX_CNTX_IP		12			// Intra-procedure scratch register
#define	BX_CNTX_SP		13			// Stack pointer
#define	BX_CNTX_LR		14			// Link register
#define	BX_CNTX_PC		15			// Program counter
#define	BX_CNTX_PS		16			// Processor status

#ifndef ASSEMBLY_SOURCE

#include <exec/BX.h>
#include <arm_defs.h>

//#include "../test/chips/LPC23xx/LPC2378.h"

/****************************************************************************/
/*				Kernel trace												*/
/****************************************************************************/

#define	TRACE_BUFF		0

#if		TRACE_BUFF
#define	TRACE_INIT()		_bx_TraceInit()
#define	TRACE( f, ... )	_bx_Trace( f, ## __VA_ARGS__ )
#else
#define	TRACE_INIT()
#define	TRACE( f, ... )
#endif

void	_bx_Trace( const char *fmt, ... );
void	_bx_TraceInit( void );

/****************************************************************************/
/*				Type definitions											*/
/****************************************************************************/

/*
*	Typedefs for structures not named in <BX.h>
*/

typedef struct _bx_wait BX_WAIT;
typedef struct _bx_prio BX_PRIO;
typedef struct _bx_pmut	BX_PMUT;

/*
*	Kernel event descriptor
*/

typedef struct {

	void	(*func)(void*,void*);	// Event function
	void	*par1;					// Event parameter 1
	void	*par2;					// Event parameter 2

} BX_EVNT;

/*
*	Kernel state descriptor
*/

typedef struct {

	volatile int		exec[ 6 ];	// Status info shared with the assembly bits
	BX_EVNT				*evts;		// Event queue
	unsigned int		eget;		// Event queue 'get' index
	unsigned int		eput;		// Event queue 'put' index
	unsigned int		elen;		// Size of the event queue
	unsigned int		ewtm;		// Event queue watermark
	unsigned int		cpsr;		// Current status mask
	BX_WAIT				rdyq;		// Ready queue
	BX_ISR_FUNC			idle;		// Idle "process"
	unsigned int		secs;		// System uptime, seconds
	unsigned int		usec;		// System uptime, microseconds
	int					tlk1;		// Timer queue lock 1
	int					tlk2;		// Timer queue lock 1
	BX_TIME				*tqhd;		// Timer queue head
	BX_TIME				*tqtl;		// Timer queue tail
	unsigned int		tacc;		// Accumulated time while locked

} BX_STAT;

/*
*	This is the function table for waitable objects. A little explanation
*	is in order. First of all, these functions are always called from the
*	kernel's event queue handler. That is, they run in IRQ context and usually
*	with interrupts enabled. They do not have to care about resource protection
*	and alike.
*
*	Each object on which a process can wait defines a function table.
*	The functions in the table determine the queueing policy of the object.
*	Each waitable object mantains a queue on which the processes that are
*	waiting for that particular object are held. The queue that the object
*	has is ordered according to its policy. Three functions are responsible
*	for the maintenance of that queue. The 'ins' function inserts a new
*	process into the queue, the 'del' function removes a process and the 'chg'
*	function is called if a process already on the queue has its effective
*	priority changed. These functions re-oder the queue, if necessary.
*	If the object can not be owned, like a semaphore, then that's all they
*	define in the function table and the 'chg' function always returns NULL.
*
*	Objects that can be owned by a process, such as a lock, can possibly alter
*	the effective priority of their owner, depending on their policy.
*	In order to maintain the priority of processes, these objects represent
*	a priority level themselves, which is the minimum effective priority of
*	their owner. If a process owns multiple objects, its effective priority
*	will be elevated to the maximum of the priorities associated with the
*	objects it owns.
*	Naturally, when a process' priority changes due to an object that it
*	owns, it can possibly change the priority of an other object that it
*	currently is waiting on. That, in turn, can change the priority of the
*	process that owns that object and so on. Therefore, there can be a
*	priority cange chain. Due to the fact that a process can only wait for
*	one object at any given time, the priority change is a chain, it can
*	not branch out. Therefore, its evaluation can be done in a loop, without
*	any additional state storage.
*	To facilitate the evaluation, objects that can change the priority of
*	their owner define additional functions and slightly extend the
*	functionality of the three basic functions. There is a function that
*	contains the priority change evaluation loop, _bx_PmutPrio(). Now the
*	priority changing objects define the the following functions:
*
*	'ins'	inserts a process to the object's waiting list and if the insertion
*			changed the priority of the object, calls _bx_PmutPrio() to change
*			the priority of its owner (it must have an owner, otherwise the
*			new process would not have been queued).
*
*	'del'	removes a process from the waiting list and calls the above function
*			if it has an owner (it may not have one, as a transient state) and
*			if the object priority changed due to the removal.
*
*	'chg'	is called when the priority of a process on the waiting queue is
*			changed. The function re-orders the queue, if needed and then checks
*			if it has changed the priority of the owner of the object. If no,
*			then it returns NULL. If yes, it returns the owner of the object
*			that can then be passed to _bx_PmutPrio(). The reason for this
*			function is not calling _bx_PmutPrio() itself is simply because
*			that function, in turn, might call the 'chg' function of an
*			other object that would lead to recursion of unknown depth.
*
*	'add'	adds an priority mutating object to the process' list of owned
*			objects. That operation might change the priority of the process,
*			thus this function might call _bx_PmutPrio().
*
*	'rem'	removes a priority mutating object from the process' list of
*			owned objects. Like 'add', it can also call _bx_PmutPrio().
*
*	'mut'	is called from _bx_PmutPrio(), to indicate that the priority
*			if the object changed. As mentioned earlier, the 'chg' function
*			reorders the waiting queue of the object and returns if that
*			affects the object's priority. The 'mut' function, in turn
*			performs the object priority change and reorders the list
*			of objects owned by the process that owns the object. If the
*			list reordering changes the effective priority of the process,
*			then 'mut' returns the process, if no change is necessary, it
*			returns NULL. The _bx_PmutPrio() function calls 'chg' and 'mut'
*			one after the other, walking through the ownership and waiting
*			chain until either one returns NULL, indicating that the last
*			change has no effect on any more object or process.
*
*	It should be noted that if a lock has a policy that is not priority
*	mutating, it will still be added to the owned object list. It will
*	not define the 'add', 'rem' and 'mut' functions and its 'chg' will
*	always return NULL, but since it is ownable, the owner process still
*	has to record it. Adding and removing such locks is done by special
*	functions, not invoking any of the above mentioned functionality.
*
*	It should be further noted that process proxies can wait on queues
*	and can be removed from waiting queues, but a proxy can never own an
*	object and the priority the proxy represents will never change. Therefore,
*	only the 'ins' and 'del' functions have to deal with proxies, all other
*	functions can, and do, assume that they only deal with real processes.
*/

struct _bx_poli {

	void	 (*ins)( BX_WAIT *, BX_PROC * );
	void	 (*del)( BX_WAIT *, BX_PROC * );
	BX_PROC	*(*chg)( BX_WAIT *, BX_PROC *, unsigned * );
	void	 (*add)( void *, void * );
	void	 (*rem)( void *, void * );
	BX_PROC	*(*mut)( BX_PMUT *, BX_PROC *, unsigned * );
};

/*
*	Timer states. TIMER_UNUSED MUST BE 0 !
*/

#define	TIMER_UNUSED	0			// The timer is not doing anything
#define	TIMER_ACTIVE	1			// The timer is currently active
#define	TIMER_CANCEL	2			// The timer is being cancelled
#define	TIMER_RELOAD	3			// The timer is being (re)loaded

/****************************************************************************/
/*				Kernel internal data declarations							*/
/****************************************************************************/

extern	BX_STAT	_bx_state;			// The kernel state

/****************************************************************************/
/*				Kernel internal prototypes									*/
/****************************************************************************/

/*
*	Event queue management
*/

void	_bx_QueueClear( void );
void	_bx_QueueFlush( void );
void	_bx_QueueEvent( int cpsr, void (*func)(void*,void*),
					    void *par1, void *par2 );

/*
*	Ready queue management
*/

void	_bx_ProcRestart( void *par1, void *par2 );
void	_bx_ProcSuspend( void *par1, void *par2 );
BX_PROC	*_bx_ProcRequeue( BX_WAIT *unused, BX_PROC *this, unsigned *pptr );

/*
*	Process functions
*/

void	_bx_ProcKill( void *par1, void *par2 );
void	_bx_ProcExit( void ) __attribute__ ((noreturn));
void	_bx_ProcLoad( void *cntx ) __attribute__ ((noreturn));

/*
*	Policy functions
*/

void	_bx_PoliDelete( BX_WAIT *wait, BX_PROC *proc );
void	_bx_PoliInsertFifo( BX_WAIT *wait, BX_PROC *proc );
void	_bx_PoliInsertPrio( BX_WAIT *wait, BX_PROC *base );
BX_PROC	*_bx_PoliChangeFifo( BX_WAIT *wait, BX_PROC *proc, unsigned *prio );
BX_PROC	*_bx_PoliChangePrio( BX_WAIT *wait, BX_PROC *base, unsigned *prio );

/*
*	Priority mutating functions
*/

void	_bx_PmutPrio( BX_PROC *proc, unsigned *prio );
void	_bx_PmutAdd( void *par1, void *par2 );
void	_bx_PmutRem( void *par1, void *par2 );
void	_bx_PmutIns( BX_PMUT *pmut, unsigned prio );
BX_PROC	*_bx_PmutMut( BX_PMUT *pmut, BX_PROC *proc, unsigned *prio );

/*
*	Other stuff
*/

int		_bx_LockTest( BX_LOCK *lock, BX_PROC *self );
void	_bx_TimerProcess( void *par1, void *par2 );
void	_bx_TimerExpired( void *par1, void *par2 );
void	_bx_TimerStart( BX_TIME *timer, unsigned expire,
					    unsigned period, int cpsr );

/****************************************************************************/
/*			Inline functions to manipulate the status register				*/
/****************************************************************************/

/*
*	Return the current process
*/

static inline BX_PROC *_bx_ProcSelf( void )
{
	return( (BX_PROC *) _bx_state.rdyq.head );
}

/****************************************************************************/
/*			Inline functions to manipulate the status register				*/
/****************************************************************************/

/*
*	Read the status register
*	~~~~~~~~~~~~~~~~~~~~~~~~
*/

static inline int	_bx_CpsrGet( void )
{
#ifndef __thumb__
	int		cpsr;
	asm volatile ( " mrs %[reg],cpsr\n" : [reg] "=r" (cpsr)	);
	return( cpsr );
#else
	register int cpsr asm ("r0");
	asm volatile ( " bl _bx_CpsrGet\n" : "=r" (cpsr) :: "lr" );
	return( cpsr );
#endif
}

/*
*	Set the status register
*	~~~~~~~~~~~~~~~~~~~~~~~
*/

static inline void	_bx_CpsrSet( int cpsr )
{
#ifndef __thumb__
	asm volatile ( " msr cpsr,%[reg]\n"	:: [reg] "r" (cpsr & ~ARM_THUMB_MASK) );
#else
	register int temp asm ("r0") = cpsr;
	asm volatile ( " bl _bx_CpsrSet\n" :: "r" (temp) : "lr"	);
#endif
}

/*
*	Read the saved status register
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/

#if 0
static inline int	_bx_SpsrGet( void )
{
#ifndef __thumb__
	int		spsr;
	asm volatile ( " mrs %[reg],spsr\n" : [reg] "=r" (spsr)	);
	return( spsr );
#else
	register int spsr asm ("r0");
	asm volatile ( " bl _bx_SpsrGet\n" : "=r" (spsr) :: "lr" );
	return( spsr );
#endif
}

/*
*	Set the saved status register
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/

static inline void	_bx_SpsrSet( int spsr )
{
#ifndef __thumb__
	asm volatile ( " msr spsr,%[reg]\n" :: [reg] "r" (spsr) );
#else
	register int temp asm ("r0") = spsr;
	asm volatile ( " bl _bx_SpsrSet\n" :: "r" (temp) : "lr"	);
#endif
}

#endif

void	_bx_SpsrDis( void );

/*
*	Disable the interrupt and return the previous status reg
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/

static inline int	_bx_IrqDis( void )
{
int		cpsr = _bx_CpsrGet();
#ifdef __thumb__
	if ( ! ( cpsr & ARM_IRQDI_MASK ) )
#endif
		_bx_CpsrSet( cpsr | ARM_IRQDI_MASK );

	return( cpsr );
}

/*
*	Re-enable the interrupt as per the saved status reg
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*	THIS FUNCTION MUST NOT BE USED FROM EVENT QUEUE FUNCTIONS!!!
*/

static inline void _bx_IrqEnaUsr( int cpsr )
{
#ifdef __thumb__
	if ( ! ( cpsr & ARM_IRQDI_MASK ) )
#endif
		_bx_CpsrSet( cpsr );
}

/*
*	Re-enable the interrupt from an event handler
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/

static inline void _bx_IrqEnaSys( int cpsr )
{
	if ( ! ( cpsr & ARM_IRQDI_MASK ) ) _bx_CpsrSet( _bx_state.cpsr );
}

/*
*	Conditionally re-enable the interrupt
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/

static inline void _bx_IrqRes( int cpsr )
{
	if ( _bx_state.exec[ BX_EXEC_MOD ] == BX_MODE_SYS ) {

		cpsr = _bx_state.cpsr;
	}

#ifdef __thumb__
	if ( ! ( cpsr & ARM_IRQDI_MASK ) )
#endif
		_bx_CpsrSet( cpsr );
}

/****************************************************************************/
/*		Inlines that handle the mutating object list of a process			*/
/****************************************************************************/

/*
*	Inserts a process 'this' onto the list 'list' before the object 'that'
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*	If 'that' is NULL, then 'this' will be placed to the tail of the queue.
*/

static inline void	_bx_ProcInsBefore( BX_WAIT *list, BX_PROC *this, BX_PROC *that )
{
	this->next = that;

	if ( ! ( this->prev = that->prev ) )

		list->head = this;
	else
		that->prev->next = this;

	that->prev = this;
	this->wait = list;
}

/*
*	Inserts a process 'this' onto the list 'list' after the object 'that'
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*	If 'that' is NULL, then 'this' will be placed to the head of the queue.
*/

static inline void	_bx_ProcInsAfter( BX_WAIT *list, BX_PROC *this, BX_PROC *that )
{
	this->prev = that;

	if ( ! ( this->next = that->next ) )

		list->tail = this;
	else
		that->next->prev = this;

	that->next = this;
	this->wait = list;
}

static inline void _bx_ProcDel( BX_WAIT *list, BX_PROC *this )
{
	if ( this->prev )

		this->prev->next = this->next;
	else
		list->head = this->next;

	if ( this->next )

		this->next->prev = this->prev;
	else
		list->tail = this->prev;

	this->wait = NULL;
}

static inline void _bx_ProcAddTail( BX_WAIT *list, BX_PROC *this )
{
	if ( ! ( this->prev = list->tail ) )

		list->head = this;
	else
		list->tail->next = this;

	this->next = NULL;
	this->wait = list;
	list->tail = this;
}

static inline void _bx_ProcAddHead( BX_WAIT *list, BX_PROC *this )
{
	if ( ! ( this->next = list->head ) )

		list->tail = this;
	else
		list->head->prev = this;

	this->prev = NULL;
	this->wait = list;
	list->head = this;
}

/****************************************************************************/
/*		Inlines that handle the mutating object list of a process			*/
/****************************************************************************/

static inline void _bx_PmutAddTail( BX_PROC *proc, BX_PMUT *pmut )
{
	if ( ! ( pmut->prev = proc->tail ) )

		proc->head = pmut;
	else
		proc->tail->next = pmut;

	pmut->next = NULL;
	pmut->proc = proc;
	proc->tail = pmut;
}

static inline void	_bx_PmutInsAfter( BX_PROC *proc, BX_PMUT *pmut, BX_PMUT *that )
{
	pmut->prev = that;

	if ( ! ( pmut->next = that->next ) )

		proc->tail = pmut;
	else
		that->next->prev = pmut;

	that->next = pmut;
	pmut->proc = proc;
}

static inline void	_bx_PmutInsBefore( BX_PROC *proc, BX_PMUT *pmut, BX_PMUT *that )
{
	pmut->next = that;

	if ( ! ( pmut->prev = that->prev ) )

		proc->head = pmut;
	else
		that->prev->next = pmut;

	that->prev = pmut;
	pmut->proc = proc;
}

static inline void	_bx_PmutDel( BX_PROC *proc, BX_PMUT *pmut )
{
	if ( pmut->prev )

		pmut->prev->next = pmut->next;
	else
		proc->head = pmut->next;

	if ( pmut->next )

		pmut->next->prev = pmut->prev;
	else
		proc->tail = pmut->prev;

	pmut->proc = NULL;
}

extern void UartPuts( const char * );
extern void UartPutc( char );
extern void UartHexl( int );
extern void UartHexb( int );

#endif
#endif

