You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
138 lines
3.7 KiB
138 lines
3.7 KiB
/* |
|
* Copyright (c) 2020 Nordic Semiconductor ASA |
|
* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
*/ |
|
|
|
#include <zephyr/net/net_timeout.h> |
|
#include <zephyr/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 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; |
|
}
|
|
|