Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 | /** @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
*/
#if defined(CONFIG_NET_DEBUG_TRICKLE)
#define SYS_LOG_DOMAIN "net/trickle"
#define NET_LOG_ENABLED 1
#endif
#include <errno.h>
#include <misc/util.h>
#include <net/net_core.h>
#include <net/trickle.h>
#define TICK_MAX ~0
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 u32_t get_end(struct net_trickle *trickle)
{
return trickle->Istart + trickle->I;
}
/* Returns a random time point t in [I/2 , I) */
static u32_t get_t(u32_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);
u32_t rand_time;
#if defined(CONFIG_NET_DEBUG_TRICKLE) && (CONFIG_SYS_LOG_NET_LEVEL > 2)
u32_t last_end = get_end(trickle);
#endif
trickle->c = 0;
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_submit(&trickle->timer, 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)
{
u32_t now = k_uptime_get_32();
u32_t diff = get_end(trickle) - now;
NET_DBG("now %d end in %d", now, diff);
/* Did the clock wrap */
if ((s32_t)diff < 0) {
diff = 0;
NET_DBG("Clock wrap");
}
k_delayed_work_init(&trickle->timer, double_interval_timeout);
k_delayed_work_submit(&trickle->timer, 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)
{
u32_t t;
trickle->c = 0;
t = get_t(trickle->I);
trickle->Istart = k_uptime_get_32();
k_delayed_work_submit(&trickle->timer, 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,
u32_t Imin,
u8_t Imax,
u8_t k)
{
NET_ASSERT(trickle && Imax > 0 && k > 0 && !CHECK_IMIN(Imin));
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 = 0;
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);
}
|