/*
* timer/timer.c
*
* Copyright (C) SGS-THOMSON Microelectronics Ltd. 1997
*
* Generic time handling functions for external timers
*/
#include <stddef.h>
#include <os20.h>
#include <os20i.h>
#include <task.h>
#include <c1timeri.h>
#include <c1timer.h>
#include <element.h>
#include <assert.h>
#include <debug.h>
#define FIXOFFSET(s) ((char *) s-offsetof(tdesc_t,tdesc_tlink))
list_t timer_queue;
static void timer_enable_int(void)
{
(*timer_enable_int_ptr)();
}
static void timer_raise_int(void)
{
(*timer_raise_int_ptr)();
}
static void timer_disable_int(void)
{
(*timer_disable_int_ptr)();
}
static void timer_set(int p )
{
(*timer_set_ptr)(p);
}
static int timer_read(void)
{
return ((*timer_read_ptr)());
}
static __inline void timer_queue_add(int Time)
{
tdesc_t* t;
tdesc_t* tdesc;
element_t* elem;
__optasm {
ldtdesc;
stl tdesc;
}
tdesc->tdesc_tdelay = Time;
elem = HEAD(&timer_queue);
if (is_empty(&timer_queue)) {
add_head(&timer_queue,(element_t*) &(tdesc->tdesc_tlink));
timer_set(Time);
} else {
while((elem != (element_t *) &timer_queue)) {
t = (tdesc_t *) FIXOFFSET(elem);
if (time_after( t->tdesc_tdelay, Time )) {
if (HEAD(&timer_queue) == elem) {
add_head(&timer_queue,(element_t *) &tdesc->tdesc_tlink);
timer_set(Time);
} else {
element_t *e = (element_t *) &tdesc->tdesc_tlink;
NEXT(PREV(elem)) = e;
PREV(e) = PREV(elem);
PREV(elem) = e;
NEXT(e) = elem;
}
return;
} else {
elem = NEXT(elem);
}
}
/* end of list */
add_tail(&timer_queue,(element_t *) &tdesc->tdesc_tlink);
}
return;
}
static __inline void timer_enable(void)
{
tdesc_t* tdesc;
__optasm {
ldtdesc;
stl tdesc;
}
timer_enable_int(); /* enable interrupt or timer */
if (tdesc->tdesc_tstate == ENABLING_P)
if (time_after(timer_read(),tdesc->tdesc_tdelay))
timer_raise_int();
}
void timer_wait(const clock_t *timeout)
{
tdesc_t *tdesc;
INTERRUPT_DECLARE;
/* if the timeout is not infinate we must setup an expiry time */
if (TIMEOUT_INFINITY != timeout) {
/* enqueue the tdesc onto the timer queue */
INTERRUPT_LOCK();
timer_queue_add(*timeout);
INTERRUPT_UNLOCK();
/* enable the timer plugins timer interrupt (and check that the timer
* is not already due)
*/
timer_enable();
}
__optasm {
ldtdesc;
stl tdesc;
}
/* block the task unless the timer has already elapsed or the semaphore
* has already been signaled.
*
* this mimics the ST20-C2 behaviour since waiting for a time in the
* past or very near future does not guarantee a deschedule.
*/
INTERRUPT_LOCK();
if (tdesc->tdesc_tstate == ENABLING_P) {
tdesc->tdesc_tstate = WAITING_P;
TIMESLICE_UNLOCK();
__optasm {
stop;
}
}
INTERRUPT_UNLOCK();
}
static void _task_delay_until(unsigned int Time)
{
tdesc_t* tdesc;
__optasm {
ldtdesc;
stl tdesc;
}
tdesc->tdesc_tstate = ENABLING_P;
timer_wait(&Time);
}
static void _task_delay(unsigned int Delay)
{
_task_delay_until(timer_read()+Delay);
}
void timer_interrupt(void)
{
tdesc_t* tdesc;
element_t* elem, *next;
FATAL_ERROR_IF(task_context_interrupt != task_context(NULL, NULL),
"illegal operation *not* from interrupt");
elem = HEAD(&timer_queue);
while(elem!=(element_t *) &timer_queue) {
tdesc = (tdesc_t *) FIXOFFSET(elem);
next = (element_t*)tdesc->tdesc_tlink;
if ( time_after( timer_read(),tdesc->tdesc_tdelay)) {
rem_element(elem);
if (tdesc->tdesc_tstate == WAITING_P) {
__optasm {
ldl tdesc;
run;
}
} else {
tdesc->tdesc_tstate = READY_P;
}
elem = next;
} else {
timer_set(tdesc->tdesc_tdelay);
if (!time_after(timer_read(),tdesc->tdesc_tdelay)) {
return;
}
}
}
if (is_empty(&timer_queue))
timer_disable_int();
return;
}
void timer_initialize(timer_api_t *api)
{
FATAL_ERROR_IF_NOT_TASK();
FATAL_ERROR_IF_LOCKED();
init_list((list_t *) &timer_queue);
time_now_ptr = api->timer_read;
task_delay_ptr = &_task_delay;
task_delay_until_ptr = &_task_delay_until;
timer_set_ptr = api->timer_set;
timer_read_ptr = api->timer_read;
timer_enable_int_ptr = api->timer_enable_int;
timer_disable_int_ptr = api->timer_disable_int;
timer_raise_int_ptr = api->timer_raise_int;
}