libnx/nx/source/kernel/utimer.c

116 lines
2.8 KiB
C

// Copyright 2018 plutoo
#include "result.h"
#include "arm/counter.h"
#include "kernel/svc.h"
#include "kernel/utimer.h"
#include "wait.h"
#define STOPPED 0
static bool _utimerBeginWait(Waitable* ww, WaiterNode* w, u64 cur_tick, u64* next_tick);
static Result _utimerOnTimeout(Waitable* ww, u64 old_tick);
static Result _utimerOnSignal(Waitable* ww);
static const WaitableMethods g_utimerVt = {
.beginWait = _utimerBeginWait,
.onTimeout = _utimerOnTimeout,
.onSignal = _utimerOnSignal,
};
void utimerCreate(UTimer* t, u64 interval, TimerType type)
{
_waitableInitialize(&t->waitable, &g_utimerVt);
t->next_tick = STOPPED;
t->interval = armNsToTicks(interval);
t->type = type;
}
void utimerStart(UTimer* t)
{
mutexLock(&t->waitable.mutex);
if (t->next_tick == STOPPED) {
u64 new_tick = armGetSystemTick() + t->interval;
t->next_tick = new_tick;
_waitableSignalAllListeners(&t->waitable);
}
mutexUnlock(&t->waitable.mutex);
}
void utimerStop(UTimer* t)
{
mutexLock(&t->waitable.mutex);
if (t->next_tick != STOPPED) {
t->next_tick = STOPPED;
_waitableSignalAllListeners(&t->waitable);
}
mutexUnlock(&t->waitable.mutex);
}
static void _utimerRecalculate(UTimer* t, u64 old_tick)
{
if (t->next_tick == old_tick) {
u64 interval = t->interval;
u64 new_tick = 0;
switch (t->type) {
case TimerType_OneShot:
new_tick = STOPPED;
break;
case TimerType_Repeating:
new_tick = old_tick + ((armGetSystemTick() - old_tick + interval - 1)/interval)*interval;
break;
}
t->next_tick = new_tick;
}
}
static bool _utimerBeginWait(Waitable* ww, WaiterNode* w, u64 cur_tick, u64* next_tick)
{
UTimer* t = (UTimer*)ww;
mutexLock(&t->waitable.mutex);
// By default we do want to perform a wait, because if the
// timer's start/stop state changes we want to detect that.
u64 timer_tick = t->next_tick;
bool do_wait = true;
// Skip timer if stopped.
if (timer_tick != STOPPED) {
s64 diff = timer_tick - cur_tick;
// If the timer is already signalled, we're done.
if (diff < 0) {
_utimerRecalculate(t, timer_tick);
do_wait = false;
} else
*next_tick = diff;
}
if (do_wait)
_waiterNodeAdd(w);
mutexUnlock(&t->waitable.mutex);
return do_wait;
}
static Result _utimerOnTimeout(Waitable* ww, u64 old_tick)
{
UTimer* t = (UTimer*)ww;
mutexLock(&t->waitable.mutex);
_utimerRecalculate(t, old_tick);
mutexUnlock(&t->waitable.mutex);
return 0;
}
static Result _utimerOnSignal(Waitable* ww)
{
// Timer state changed, so we need to retry the wait.
return KERNELRESULT(Cancelled);
}