/*********************************************************************** 
* NOTICE
* All files contained on this disk are subject to the licensing conditions
* issued by MOTOROLA Inc.
*
* All files are copyright 1993 by MOTOROLA Inc. 
************************************************************************/


/********************************************************
 * File:	timekeeper.c
 *
 * Description:
 *	This routine implements the timer services for
 *	the driver module. It is used by all modules
 *	on the board.
 *	Each module wishing to use the services of the
 *	timer must send the driver module a message,
 *	indicating to it that a table has been allocated
 *	for its timers and the frequency (in ticks) with
 *	which the table should be scanned.
 *
 * Routines:
 *	timekeeper
 *	check_timer
 *	keep_time
 *	timer_init
 *	clear_timer
 *
 * Author:
 *	Jonathan Masel
 ********************************************************/


#include "gct.h"
#include "timer.h"
#include "types.h"
#include "bss.h"
#include "msg.h"
#include "status.h"




/********************************************************
 * routine:	timer_tick
 *
 * description:
 *	This is the driver primitive called when
 *	a TIMER_TICK message is received.
 *	The message is in fact a pointer to a 32-bit value
 *	(not an allocated MSG), so relm should not be called.
 *	The timer tables are scanned as required - timeout
 *	notifications are sent where necessary.
 *
 * arguments:
 *
 * return code:
 *
 * side effects:
 *
 ********************************************************/
timer_tick(h)
HDR *h;
{
	GCT *gct;
	GWA *gwa;
	register struct timer *t;
	register int i;

	GETGCT(gct);
	gwa = (GWA *)gct->driver->work_area;
	t = gwa->timer;
	if( !t )
		return;		/* not ready yet */
	for(i = 0; i < gwa->num_timers; i++){
		if( t->timer_table ){
			if( --(t->tick_counter) == 0 ){
				t->tick_counter = t->resolution;
				check_timer(t->timer_table);
			}
		}
		t++;
	}
}



/********************************************************
 * routine:	check_timer
 *
 * description:
 *	Check the given timer table for timeouts.
 *	Send the appropriate message where necessary.
 *
 * arguments:
 *	t		points to the timer table being checked
 *
 * return code:
 *
 * side effects:
 *
 ********************************************************/
check_timer(tab)
TIMER_TABLE *tab;
{
	register unsigned short i, j;
	MSG *m;
	register struct t_child *t;
	GCT *gct;
	extern MSG *getm();

	GETGCT(gct);
	t = tab->t_child;
	tab->time++;

	for(i = 0; i < tab->max; i++){
		/*
		 * check timeouts for running timers only
		 */
		if( t->flags & TIMER_RUNNING )
			if( tab->time == t->timeout ){
				j = 1;
				/* timeout */
				m = getm(TM_EXP, i, 0, 4);
				if( m ){
					m->param[0] = t->t_type;
					if( (*gct->send)(tab->module, m) )
						relm((HDR *)m);
					else
						j = 0;
				}
				/*
				 * if we didn't manage to
				 * send the message, we should
				 * try again next time
				 */
				if( j )
					t->timeout++;
			}
		t++;
	}
}



/********************************************************
 * routine:	keep_time
 *
 * description:
 *	Start keeping time for a module. This routine is
 *	called after recdeiving a KEEP_TIME action request,
 *	which must be sent before any module can request
 *	subsequent timeouts.
 *
 * arguments:
 *	m		points to the message received
 *
 * retured status:
 *	0		on success
 *	error code	otherwise
 *
 * side effects:
 *
 ********************************************************/
keep_time(m)
MSG *m;
{
	GCT *gct;
	GWA *gwa;
	struct timer *t;
	TIMER_SERVICES *s;
	int i;

	GETGCT(gct);
	gwa = (GWA *)gct->driver->work_area;
	s = (TIMER_SERVICES *)m->param;
	t = gwa->timer;
	if( !t ){
		m->hdr.status = NOT_ALLOWED;
		return;
	}
	for(i = 0; i < gwa->num_timers; i++){
		if( !t->timer_table ){
			t->timer_table = s->timer_table;
			t->resolution = s->resolution;
			t->tick_counter = t->resolution;
			m->hdr.status = 0;
			conf_msg((HDR *)m, s->timer_table->module);
			return;
		}
		t++;
	}

	/*
	 * no more timers can be handled
	 */
	m->hdr.status = NO_MORE_TIMERS;
	conf_msg((HDR *)m, s->timer_table->module);
	return;
}



/********************************************************
 * routine:	timer_init
 *
 * description:
 *	Initialize timer variables.
 *
 * arguments:
 *
 * return code:
 *
 * side effects:
 *
 ********************************************************/
timer_init()
{
	GCT *gct;
	GWA *gwa;
	int i;
	struct timer *t;

	GETGCT(gct);
	gwa = (GWA *)gct->driver->work_area;

	/*
	 * allocate memory in driver_bss for timer table
	 */
	i = (int)&(gct->driver->child[gct->driver->max]);
	gwa->timer = (struct timer *)i;
	t = gwa->timer;
	for(i = 0; i < gwa->num_timers; i++){
		t->timer_table = 0;
		t++;
	}
}


/********************************************************
 * routine:	clear_timer
 *
 * description:
 *	Clear all timers in a given table.
 *
 * arguments:
 *	m	the message received.
 *
 * return code:
 *
 * side effects:
 *
 ********************************************************/
clear_timer(m)
MSG *m;
{
	int i, n;
	GCT *gct;
	GWA *gwa;
	struct timer *t;
	struct t_table *s;

	GETGCT(gct);
	gwa = (GWA *)gct->driver->work_area;
	t = gwa->timer;
	s = (struct t_table *)(m->param[0]);
	n = BAD_PARAM;
	for(i = 0; i < gwa->num_timers; i++){
		if( t->timer_table == s ){
			n = SUCCESS;
			t->timer_table = 0;
		}
		t++;
	}

	m->hdr.status = n;
	conf_msg((HDR *)m, s->module);
}
