blob: 4ec39d2d222dfc78f73d2a8925048242904b0c16 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <string.h>
#include "os/mynewt.h"
#include "timesched/timesched.h"
static TAILQ_HEAD(timesched_q, timesched_timer) g_timesched_q;
static struct os_callout g_timesched_co;
void
timesched_resched(void)
{
struct timesched_timer *timer;
os_time_t ticks;
struct os_timeval time;
uint64_t msec;
os_callout_stop(&g_timesched_co);
timer = TAILQ_FIRST(&g_timesched_q);
if (!timer) {
/* No timer was started, no need to start callout */
return;
}
os_gettimeofday(&time, NULL);
os_timersub(&timer->expire, &time, &time);
if (time.tv_sec < 0) {
/* We're already past expiry time - fire callout "immediately" */
ticks = 0;
} else {
msec = time.tv_sec * 1000 + time.tv_usec / 1000;
/*
* We could just schedule next callout to expire at calculated value,
* however we need to assume time may be adjusted on device in the
* meantime. Since there is no notification from OS that this happened,
* we'll just schedule callout at shorter interval to adjust callout if
* necessary. For now let's use 1 minute max.
*/
msec = min(msec, 60 * 1000);
ticks = os_time_ms_to_ticks32(msec);
}
os_callout_reset(&g_timesched_co, ticks);
}
static void
timesched_timer_co_cb(struct os_event *ev)
{
struct timesched_timer *timer;
struct os_timeval time;
os_sr_t sr;
os_gettimeofday(&time, NULL);
OS_ENTER_CRITICAL(sr);
timer = TAILQ_FIRST(&g_timesched_q);
while (timer && OS_TIMEVAL_LEQ(timer->expire, time)) {
os_eventq_put(timer->evq, &timer->ev);
TAILQ_REMOVE(&g_timesched_q, timer, link);
timer = TAILQ_FIRST(&g_timesched_q);
}
OS_EXIT_CRITICAL(sr);
timesched_resched();
}
void
timesched_timer_init(struct timesched_timer *timer, struct os_eventq *evq,
os_event_fn *ev_cb, void *ev_arg)
{
memset(timer, 0, sizeof(*timer));
timer->evq = evq;
timer->ev.ev_cb = ev_cb;
timer->ev.ev_arg = ev_arg;
}
int
timesched_timer_start(struct timesched_timer *timer, struct os_timeval *utctime)
{
struct timesched_timer *entry;
os_sr_t sr;
timer->expire = *utctime;
OS_ENTER_CRITICAL(sr);
if (TAILQ_EMPTY(&g_timesched_q)) {
TAILQ_INSERT_HEAD(&g_timesched_q, timer, link);
} else {
TAILQ_FOREACH(entry, &g_timesched_q, link) {
if (OS_TIMEVAL_LT(timer->expire, entry->expire)) {
TAILQ_INSERT_BEFORE(entry, timer, link);
break;
}
}
if (!entry) {
TAILQ_INSERT_TAIL(&g_timesched_q, timer, link);
}
}
OS_EXIT_CRITICAL(sr);
timesched_resched();
return OS_OK;
}
int
timesched_timer_stop(struct timesched_timer *timer)
{
os_sr_t sr;
OS_ENTER_CRITICAL(sr);
TAILQ_REMOVE(&g_timesched_q, timer, link);
OS_EXIT_CRITICAL(sr);
return OS_OK;
}
void
timesched_init(void)
{
os_callout_init(&g_timesched_co, os_eventq_dflt_get(),
timesched_timer_co_cb, NULL);
}