rockbox/firmware/kernel/timeout.c

98 lines
2.5 KiB
C
Raw Normal View History

/****************************************************************************
* Tick-based interval timers/one-shots - be mindful this is not really
* intended for continuous timers but for events that need to run for a short
* time and be cancelled without further software intervention.
****************************************************************************/
#include "config.h"
#include "system.h" /* TIME_AFTER */
#include "kernel.h"
#include "timeout.h"
#include "general.h"
/* list of active timeout events */
static struct timeout *tmo_list[MAX_NUM_TIMEOUTS+1];
/* timeout tick task - calls event handlers when they expire
* Event handlers may alter expiration, callback and data during operation.
*/
static void timeout_tick(void)
{
unsigned long tick = current_tick;
struct timeout **p = tmo_list;
struct timeout *curr;
for(curr = *p; curr != NULL; curr = *(++p))
{
int ticks;
if(TIME_BEFORE(tick, curr->expires))
continue;
/* this event has expired - call callback */
ticks = curr->callback(curr);
if(ticks > 0)
{
curr->expires = tick + ticks; /* reload */
}
else
{
timeout_cancel(curr); /* cancel */
}
}
}
/* Cancels a timeout callback - can be called from the ISR */
void timeout_cancel(struct timeout *tmo)
{
int oldlevel = disable_irq_save();
int rc = remove_array_ptr((void **)tmo_list, tmo);
if(rc >= 0 && *tmo_list == NULL)
{
tick_remove_task(timeout_tick); /* Last one - remove task */
}
restore_irq(oldlevel);
}
/* Adds a timeout callback - calling with an active timeout resets the
interval - can be called from the ISR */
void timeout_register(struct timeout *tmo, timeout_cb_type callback,
int ticks, intptr_t data)
{
int oldlevel;
void **arr, **p;
if(tmo == NULL)
return;
oldlevel = disable_irq_save();
/* See if this one is already registered */
arr = (void **)tmo_list;
p = find_array_ptr(arr, tmo);
if(p - arr < MAX_NUM_TIMEOUTS)
{
/* Vacancy */
if(*p == NULL)
{
/* Not present */
if(*tmo_list == NULL)
{
tick_add_task(timeout_tick); /* First one - add task */
}
*p = tmo;
}
tmo->callback = callback;
tmo->data = data;
tmo->expires = current_tick + ticks;
}
restore_irq(oldlevel);
}