139 lines
3.7 KiB
C
139 lines
3.7 KiB
C
/*
|
|
* Copyright (c) 2020 Nordic Semiconductor ASA
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <net/net_timeout.h>
|
|
#include <sys_clock.h>
|
|
|
|
void net_timeout_set(struct net_timeout *timeout,
|
|
uint32_t lifetime,
|
|
uint32_t now)
|
|
{
|
|
uint64_t expire_timeout;
|
|
|
|
timeout->timer_start = now;
|
|
|
|
/* Highly unlikely, but a zero timeout isn't correctly handled by the
|
|
* standard calculation.
|
|
*/
|
|
if (lifetime == 0U) {
|
|
timeout->wrap_counter = 0;
|
|
timeout->timer_timeout = 0;
|
|
return;
|
|
}
|
|
|
|
expire_timeout = (uint64_t)MSEC_PER_SEC * (uint64_t)lifetime;
|
|
timeout->wrap_counter = expire_timeout /
|
|
(uint64_t)NET_TIMEOUT_MAX_VALUE;
|
|
timeout->timer_timeout = expire_timeout -
|
|
(uint64_t)NET_TIMEOUT_MAX_VALUE *
|
|
(uint64_t)timeout->wrap_counter;
|
|
|
|
/* The implementation requires that the fractional timeout be zero
|
|
* only when the timeout has completed, so if the residual is zero
|
|
* copy over one timeout from the wrap.
|
|
*/
|
|
if (timeout->timer_timeout == 0U) {
|
|
timeout->timer_timeout = NET_TIMEOUT_MAX_VALUE;
|
|
timeout->wrap_counter -= 1;
|
|
}
|
|
}
|
|
|
|
int64_t net_timeout_deadline(const struct net_timeout *timeout,
|
|
int64_t now)
|
|
{
|
|
uint64_t start;
|
|
uint64_t deadline;
|
|
|
|
/* Reconstruct the full-precision start time assuming that the full
|
|
* precision start time is less than 2^32 ticks in the past.
|
|
*/
|
|
start = (uint64_t)now;
|
|
start -= (uint32_t)now - timeout->timer_start;
|
|
|
|
/* Offset the start time by the full precision remaining delay. */
|
|
deadline = start + timeout->timer_timeout;
|
|
deadline += (uint64_t)NET_TIMEOUT_MAX_VALUE
|
|
* (uint64_t)timeout->wrap_counter;
|
|
|
|
return (int64_t)deadline;
|
|
}
|
|
|
|
uint32_t net_timeout_remaining(const struct net_timeout *timeout,
|
|
uint32_t now)
|
|
{
|
|
int64_t ret = timeout->timer_timeout;
|
|
|
|
ret += timeout->wrap_counter * (uint64_t)NET_TIMEOUT_MAX_VALUE;
|
|
ret -= (int64_t)(int32_t)(now - timeout->timer_start);
|
|
if (ret <= 0) {
|
|
return 0;
|
|
}
|
|
|
|
return (uint32_t)((uint64_t)ret / MSEC_PER_SEC);
|
|
}
|
|
|
|
uint32_t net_timeout_evaluate(struct net_timeout *timeout,
|
|
uint32_t now)
|
|
{
|
|
uint32_t elapsed;
|
|
uint32_t last_delay;
|
|
int32_t remains;
|
|
bool wraps;
|
|
|
|
/* Time since last evaluation or set. */
|
|
elapsed = now - timeout->timer_start;
|
|
|
|
/* The delay used the last time this was evaluated. */
|
|
wraps = (timeout->wrap_counter > 0U);
|
|
last_delay = wraps
|
|
? NET_TIMEOUT_MAX_VALUE
|
|
: timeout->timer_timeout;
|
|
|
|
/* Time remaining until completion of the last delay. */
|
|
remains = (int32_t)(last_delay - elapsed);
|
|
|
|
/* If the deadline for the next event hasn't been reached yet just
|
|
* return the remaining time.
|
|
*/
|
|
if (remains > 0) {
|
|
return (uint32_t)remains;
|
|
}
|
|
|
|
/* Deadline has been reached. If we're not wrapping we've completed
|
|
* the last portion of the full timeout, so return zero to indicate
|
|
* the timeout has completed.
|
|
*/
|
|
if (!wraps) {
|
|
return 0U;
|
|
}
|
|
|
|
/* There's more to do. We need to update timer_start to correspond to
|
|
* now, then reduce the remaining time by the elapsed time. We know
|
|
* that's at least NET_TIMEOUT_MAX_VALUE, and can apply the
|
|
* reduction by decrementing the wrap count.
|
|
*/
|
|
timeout->timer_start = now;
|
|
elapsed -= NET_TIMEOUT_MAX_VALUE;
|
|
timeout->wrap_counter -= 1;
|
|
|
|
/* The residual elapsed must reduce timer_timeout, which is capped at
|
|
* NET_TIMEOUT_MAX_VALUE. But if subtracting would reduce the
|
|
* counter to zero or go negative we need to reduce the the wrap
|
|
* counter once more and add the residual to the counter, so the
|
|
* counter remains positive.
|
|
*/
|
|
if (timeout->timer_timeout > elapsed) {
|
|
timeout->timer_timeout -= elapsed;
|
|
} else {
|
|
timeout->timer_timeout += NET_TIMEOUT_MAX_VALUE - elapsed;
|
|
timeout->wrap_counter -= 1U;
|
|
}
|
|
|
|
return (timeout->wrap_counter == 0U)
|
|
? timeout->timer_timeout
|
|
: NET_TIMEOUT_MAX_VALUE;
|
|
}
|