/****************************************************************************/
/*	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 "coop.h"

/** @addtogroup coop_time */
/** @{ */

/**
*	Start a timer.
*
*	The timer is represented by a BZ_TIME structure, which you have to allocate.
*	You can allocate it on the stack, as long as you guarantee that it does not
*	go out of scope before it expires.
*
*	The timer will be started with an initial period defined by the first
*	argument. If the second argument is not 0, then when the timer expires,
*	it will be restarted by the period defined by the second argument. That
*	way you can create a periodic source of events.
*
*	When a timer expires, it delivers an event specified by the fourth argument
*	to the process defined by the third argument. It can be BZ_??? if you want
*	the event delivered to all processes. Only one event is delivered, if you
*	specify more than one bit in the event map, only the lowest numbered bit
*	of that mask will be delivered. The event mask can not be empty.
*
*	Starting a timer that is already running might lead to system crash. If
*	you want to re-use a timer structure, you must be sure that it is not in
*	use.
*
*	@param	timer	Pointer to a BZ_TIME structure, representing the timer
*
*	@param	initial	The initial period of the timer, in microseconds.
*
*	@param	repeat	The subsequent period of the timer, in microseconds.
*					If this parameter is 0, then when the timer expires after
*					the initial period, it stops. If this argument is not 0,
*					then the timer will be restarted, using the time specified
*					by this argument. Thus you get a periodic timer, which will
*					stop only when you call BZ_TimerCancel() on it.
*
*	@param	pid		The timer when expires delivers an event. This argument
*					specifies the process to which the event will be delivered.
*
*	@param	event	The event mask to be delivered. It must not be 0. It should
*					not contain bits reserved for the system; if it does, then
*					those bits will be deleted from the mask. Furthermore, it
*					should have only one bit set. If there are more than one
*					bits set in the mask, then only the lowest numbered bit
*					will be delivered when the timer expires.
*
*	@retval
*	@retval	0		Success
*/

int		BZ_TimerStart( BZ_TIME *timer, unsigned initial, unsigned repeat,
				   	   int pid, int event )
{
BZ_STAT	*base = &_bz_stat;
int		stat, bcnt;

	if ( ! ( event &= ( 1 << BZ_EVENT_MAX ) - 1 ) )

		return( BZ_SIGNAL );

	if ( pid != BZ_PID_BCAST && (unsigned) pid >= base->maxp )

		return( BZ_ILLEGAL_PID );

	for ( bcnt = 0 ; ! ( event & 1 ) ; event >>= 1, bcnt++ );

	timer->time = initial;
	timer->rept = repeat;
	timer->proc = pid;
	timer->sign = bcnt;

	stat = BZ_IrqDisable( 1 );
	_bz_TimerAdd( base, timer );
	BZ_IrqDisable( stat );
	return( 0 );
}

/** @} */

/****************************************************************************/
/*				Library-visible public functions							*/
/****************************************************************************/

/*
*	Adds a timer to the timer queue
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*	The function adds a timer to the timer queue. This function must be
*	called with interrupts disabled.
*/

void	_bz_TimerAdd( BZ_STAT *base, BZ_TIME *this )
{
BZ_TIME	*that;

	this->time = this->rept;
	this->next = NULL;

	if ( ! ( that = base->tptr ) ) {

		base->tptr = this;
		return;
	}

	if ( that->time <= this->time ) {

		this->time -= that->time;
		this->next  = that;
		base->tptr  = this;
		return;
	}

	for ( ;; ) {

		this->time -= that->time;

		if ( ! that->next ) {

			that->next = this;
			break;
		}

		if ( that->next->time >= this->time ) {

			that->next->time -= this->time;
			this->next = that->next;
			that->next = this;
			break;
		}

		that = that->next;
	}
}

