zephyr/subsys/net/ip/trickle.c
Flavio Ceolin c4f7faea10 random: Include header where it is used
Unit tests were failing to build because random header was included by
kernel_includes.h. The problem is that rand32.h includes a generated
file that is either not generated or not included when building unit
tests. Also, it is better to limit the scope of this file to where it is
used.

Signed-off-by: Flavio Ceolin <flavio.ceolin@intel.com>
2020-07-08 21:05:36 -04:00

231 lines
4.7 KiB
C

/** @file
* @brief Trickle timer library
*
* This implements Trickle timer as specified in RFC 6206
*/
/*
* Copyright (c) 2016 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <logging/log.h>
LOG_MODULE_REGISTER(net_trickle, CONFIG_NET_TRICKLE_LOG_LEVEL);
#include <errno.h>
#include <sys/util.h>
#include <random/rand32.h>
#include <net/net_core.h>
#include <net/trickle.h>
#define TICK_MAX ~0
static void trickle_timeout(struct k_work *work);
static inline bool is_suppression_disabled(struct net_trickle *trickle)
{
return trickle->k == NET_TRICKLE_INFINITE_REDUNDANCY;
}
static inline bool is_tx_allowed(struct net_trickle *trickle)
{
return is_suppression_disabled(trickle) ||
(trickle->c < trickle->k);
}
static inline uint32_t get_end(struct net_trickle *trickle)
{
return trickle->Istart + trickle->I;
}
/* Returns a random time point t in [I/2 , I) */
static uint32_t get_t(uint32_t I)
{
I >>= 1;
NET_DBG("[%d, %d)", I, I << 1);
return I + (sys_rand32_get() % I);
}
static void double_interval_timeout(struct k_work *work)
{
struct net_trickle *trickle = CONTAINER_OF(work,
struct net_trickle,
timer);
uint32_t rand_time;
uint32_t last_end = get_end(trickle);
trickle->c = 0U;
NET_DBG("now %u (was at %u)", k_uptime_get_32(), last_end);
/* Check if we need to double the interval */
if (trickle->I <= (trickle->Imax_abs >> 1)) {
/* Double if I <= Imax/2 */
trickle->I <<= 1;
NET_DBG("double I %u", trickle->I);
} else {
trickle->I = trickle->Imax_abs;
NET_DBG("I %u", trickle->I);
}
/* Random t in [I/2, I) */
rand_time = get_t(trickle->I);
NET_DBG("doubling time %u", rand_time);
trickle->Istart = k_uptime_get_32() + rand_time;
k_delayed_work_init(&trickle->timer, trickle_timeout);
k_delayed_work_submit(&trickle->timer, K_MSEC(rand_time));
NET_DBG("last end %u new end %u for %u I %u",
last_end, get_end(trickle), trickle->Istart, trickle->I);
}
static inline void reschedule(struct net_trickle *trickle)
{
uint32_t now = k_uptime_get_32();
uint32_t diff = get_end(trickle) - now;
NET_DBG("now %d end in %d", now, diff);
/* Did the clock wrap */
if ((int32_t)diff < 0) {
diff = 0U;
NET_DBG("Clock wrap");
}
k_delayed_work_init(&trickle->timer, double_interval_timeout);
k_delayed_work_submit(&trickle->timer, K_MSEC(diff));
}
static void trickle_timeout(struct k_work *work)
{
struct net_trickle *trickle = CONTAINER_OF(work,
struct net_trickle,
timer);
NET_DBG("Trickle timeout at %d", k_uptime_get_32());
if (trickle->cb) {
NET_DBG("TX ok %d c(%u) < k(%u)",
is_tx_allowed(trickle), trickle->c, trickle->k);
trickle->cb(trickle, is_tx_allowed(trickle),
trickle->user_data);
}
if (net_trickle_is_running(trickle)) {
reschedule(trickle);
}
}
static void setup_new_interval(struct net_trickle *trickle)
{
uint32_t t;
trickle->c = 0U;
t = get_t(trickle->I);
trickle->Istart = k_uptime_get_32();
k_delayed_work_submit(&trickle->timer, K_MSEC(t));
NET_DBG("new interval at %d ends %d t %d I %d",
trickle->Istart,
get_end(trickle),
t,
trickle->I);
}
#define CHECK_IMIN(Imin) \
((Imin < 2) || (Imin > (TICK_MAX >> 1)))
int net_trickle_create(struct net_trickle *trickle,
uint32_t Imin,
uint8_t Imax,
uint8_t k)
{
NET_ASSERT(trickle && Imax > 0 && k > 0 && !CHECK_IMIN(Imin));
(void)memset(trickle, 0, sizeof(struct net_trickle));
trickle->Imin = Imin;
trickle->Imax = Imax;
trickle->Imax_abs = Imin << Imax;
trickle->k = k;
NET_ASSERT(trickle->Imax_abs);
NET_DBG("Imin %d Imax %u k %u Imax_abs %d",
trickle->Imin, trickle->Imax, trickle->k,
trickle->Imax_abs);
k_delayed_work_init(&trickle->timer, trickle_timeout);
return 0;
}
int net_trickle_start(struct net_trickle *trickle,
net_trickle_cb_t cb,
void *user_data)
{
NET_ASSERT(trickle && cb);
trickle->cb = cb;
trickle->user_data = user_data;
/* Random I in [Imin , Imax] */
trickle->I = trickle->Imin +
(sys_rand32_get() % (trickle->Imax_abs - trickle->Imin + 1));
setup_new_interval(trickle);
NET_DBG("start %d end %d in [%d , %d)",
trickle->Istart, get_end(trickle),
trickle->I >> 1, trickle->I);
return 0;
}
int net_trickle_stop(struct net_trickle *trickle)
{
NET_ASSERT(trickle);
k_delayed_work_cancel(&trickle->timer);
trickle->I = 0U;
return 0;
}
void net_trickle_consistency(struct net_trickle *trickle)
{
NET_ASSERT(trickle);
if (trickle->c < 0xFF) {
trickle->c++;
}
NET_DBG("consistency %u", trickle->c);
}
void net_trickle_inconsistency(struct net_trickle *trickle)
{
NET_ASSERT(trickle);
if (trickle->I != trickle->Imin) {
NET_DBG("inconsistency");
trickle->I = trickle->Imin;
}
setup_new_interval(trickle);
}