#include <rt/timer.h>
#include <rt/container.h>
#include <rt/queue.h>
#include <rt/task.h>
#include <rt/tick.h>
static void timer_command_push(struct rt_timer *timer, enum rt_timer_op op,
unsigned long period)
{
const struct rt_timer_command timer_cmd = {
.timer = timer,
.period = period,
.op = op,
};
rt_queue_push(timer->queue, &timer_cmd);
}
static bool timer_command_trypush(struct rt_timer *timer, enum rt_timer_op op,
unsigned long period)
{
const struct rt_timer_command timer_cmd = {
.timer = timer,
.period = period,
.op = op,
};
return rt_queue_trypush(timer->queue, &timer_cmd);
}
static bool timer_command_timedpush(struct rt_timer *timer, enum rt_timer_op op,
unsigned long period,
unsigned long block_ticks)
{
const struct rt_timer_command timer_cmd = {
.timer = timer,
.period = period,
.op = op,
};
return rt_queue_timedpush(timer->queue, &timer_cmd, block_ticks);
}
void rt_timer_start(struct rt_timer *timer)
{
timer_command_push(timer, RT_TIMER_OP_START, 0);
}
bool rt_timer_trystart(struct rt_timer *timer)
{
return timer_command_trypush(timer, RT_TIMER_OP_START, 0);
}
bool rt_timer_timedstart(struct rt_timer *timer, unsigned long block_ticks)
{
return timer_command_timedpush(timer, RT_TIMER_OP_START, 0, block_ticks);
}
void rt_timer_stop(struct rt_timer *timer)
{
timer_command_push(timer, RT_TIMER_OP_STOP, 0);
}
bool rt_timer_trystop(struct rt_timer *timer)
{
return timer_command_trypush(timer, RT_TIMER_OP_STOP, 0);
}
bool rt_timer_timedstop(struct rt_timer *timer, unsigned long block_ticks)
{
return timer_command_timedpush(timer, RT_TIMER_OP_STOP, 0, block_ticks);
}
void rt_timer_change_period(struct rt_timer *timer, unsigned long period)
{
timer_command_push(timer, RT_TIMER_OP_CHANGE_PERIOD, period);
}
bool rt_timer_trychange_period(struct rt_timer *timer, unsigned long period)
{
return timer_command_trypush(timer, RT_TIMER_OP_CHANGE_PERIOD, period);
}
bool rt_timer_timedchange_period(struct rt_timer *timer, unsigned long period,
unsigned long block_ticks)
{
return timer_command_timedpush(timer, RT_TIMER_OP_CHANGE_PERIOD, period,
block_ticks);
}
static inline struct rt_timer *timer_from_list(const struct rt_list *l)
{
return rt_container_of(l, struct rt_timer, list);
}
static inline void timer_call(const struct rt_timer *timer)
{
if (timer->has_arg)
{
timer->fn.with_arg(timer->arg);
}
else
{
timer->fn.no_arg();
}
}
static void timer_start(struct rt_list *list, struct rt_timer *timer,
unsigned long base_tick)
{
struct rt_list *next;
for (next = rt_list_front(list); next != list; next = next->next)
{
if ((timer->tick - base_tick) <
(timer_from_list(next)->tick - base_tick))
{
break;
}
}
rt_list_push_back(next, &timer->list);
}
static inline void timer_stop(struct rt_timer *timer)
{
rt_list_remove(&timer->list);
}
void rt_timer_daemon(uintptr_t arg)
{
rt_task_drop_privilege();
struct rt_queue *const queue = (struct rt_queue *)arg;
RT_LIST(timer_list);
unsigned long expired_tick = 0;
for (;;)
{
const unsigned long ticks_to_advance = rt_tick_count() - expired_tick;
unsigned long command_timeout = 1000000000UL;
while (!rt_list_is_empty(&timer_list))
{
struct rt_timer *const timer =
timer_from_list(rt_list_front(&timer_list));
unsigned long ticks_to_next_expiry = timer->tick - expired_tick;
if (ticks_to_next_expiry > ticks_to_advance)
{
ticks_to_next_expiry -= ticks_to_advance;
if (command_timeout > ticks_to_next_expiry)
{
command_timeout = ticks_to_next_expiry;
}
break;
}
timer_call(timer);
timer_stop(timer);
if (timer->periodic)
{
timer->tick += timer->period;
timer_start(&timer_list, timer, expired_tick);
}
}
expired_tick += ticks_to_advance;
struct rt_timer_command cmd;
if (rt_queue_timedpop(queue, &cmd, command_timeout))
{
switch (cmd.op)
{
case RT_TIMER_OP_START:
cmd.timer->tick = rt_tick_count() + cmd.timer->period;
timer_stop(cmd.timer);
timer_start(&timer_list, cmd.timer, expired_tick);
break;
case RT_TIMER_OP_STOP:
timer_stop(cmd.timer);
break;
case RT_TIMER_OP_CHANGE_PERIOD:
cmd.timer->period = cmd.period;
break;
}
}
}
}