1 /** @file
2 * @brief Trickle timer library
3 *
4 * This implements Trickle timer as specified in RFC 6206
5 */
6
7 /*
8 * Copyright (c) 2016 Intel Corporation
9 *
10 * SPDX-License-Identifier: Apache-2.0
11 */
12
13 #include <zephyr/logging/log.h>
14 LOG_MODULE_REGISTER(net_trickle, CONFIG_NET_TRICKLE_LOG_LEVEL);
15
16 #include <errno.h>
17 #include <zephyr/sys/util.h>
18 #include <zephyr/random/random.h>
19
20 #include <zephyr/net/net_core.h>
21 #include <zephyr/net/trickle.h>
22
23 #define TICK_MAX ~0
24
25 static void trickle_timeout(struct k_work *work);
26
is_suppression_disabled(struct net_trickle * trickle)27 static inline bool is_suppression_disabled(struct net_trickle *trickle)
28 {
29 return trickle->k == NET_TRICKLE_INFINITE_REDUNDANCY;
30 }
31
is_tx_allowed(struct net_trickle * trickle)32 static inline bool is_tx_allowed(struct net_trickle *trickle)
33 {
34 return is_suppression_disabled(trickle) ||
35 (trickle->c < trickle->k);
36 }
37
get_end(struct net_trickle * trickle)38 static inline uint32_t get_end(struct net_trickle *trickle)
39 {
40 return trickle->Istart + trickle->I;
41 }
42
43 /* Returns a random time point t in [I/2 , I) */
get_t(uint32_t I)44 static uint32_t get_t(uint32_t I)
45 {
46 I >>= 1;
47
48 NET_DBG("[%d, %d)", I, I << 1);
49
50 return I + (sys_rand32_get() % I);
51 }
52
double_interval_timeout(struct net_trickle * trickle)53 static void double_interval_timeout(struct net_trickle *trickle)
54 {
55 uint32_t rand_time;
56 uint32_t last_end = get_end(trickle);
57
58 trickle->c = 0U;
59
60 NET_DBG("now %u (was at %u)", k_uptime_get_32(), last_end);
61
62 /* Check if we need to double the interval */
63 if (trickle->I <= (trickle->Imax_abs >> 1)) {
64 /* Double if I <= Imax/2 */
65 trickle->I <<= 1;
66
67 NET_DBG("double I %u", trickle->I);
68 } else {
69 trickle->I = trickle->Imax_abs;
70
71 NET_DBG("I %u", trickle->I);
72 }
73
74 /* Random t in [I/2, I) */
75 rand_time = get_t(trickle->I);
76
77 NET_DBG("doubling time %u", rand_time);
78
79 trickle->Istart = k_uptime_get_32() + rand_time;
80 trickle->double_to = false;
81
82 k_work_reschedule(&trickle->timer, K_MSEC(rand_time));
83
84 NET_DBG("last end %u new end %u for %u I %u",
85 last_end, get_end(trickle), trickle->Istart, trickle->I);
86 }
87
reschedule(struct net_trickle * trickle)88 static inline void reschedule(struct net_trickle *trickle)
89 {
90 uint32_t now = k_uptime_get_32();
91 uint32_t diff = get_end(trickle) - now;
92
93 NET_DBG("now %d end in %d", now, diff);
94
95 /* Did the clock wrap */
96 if ((int32_t)diff < 0) {
97 diff = 0U;
98 NET_DBG("Clock wrap");
99 }
100
101 trickle->double_to = true;
102
103 k_work_reschedule(&trickle->timer, K_MSEC(diff));
104 }
105
interval_timeout(struct net_trickle * trickle)106 static void interval_timeout(struct net_trickle *trickle)
107 {
108 NET_DBG("Trickle timeout at %d", k_uptime_get_32());
109
110 if (trickle->cb) {
111 NET_DBG("TX ok %d c(%u) < k(%u)",
112 is_tx_allowed(trickle), trickle->c, trickle->k);
113
114 trickle->cb(trickle, is_tx_allowed(trickle),
115 trickle->user_data);
116 }
117
118 if (net_trickle_is_running(trickle)) {
119 reschedule(trickle);
120 }
121 }
122
trickle_timeout(struct k_work * work)123 static void trickle_timeout(struct k_work *work)
124 {
125 struct k_work_delayable *dwork = k_work_delayable_from_work(work);
126 struct net_trickle *trickle = CONTAINER_OF(dwork,
127 struct net_trickle,
128 timer);
129
130 if (trickle->double_to) {
131 double_interval_timeout(trickle);
132 } else {
133 interval_timeout(trickle);
134 }
135 }
136
setup_new_interval(struct net_trickle * trickle)137 static void setup_new_interval(struct net_trickle *trickle)
138 {
139 uint32_t t;
140
141 trickle->c = 0U;
142
143 t = get_t(trickle->I);
144
145 trickle->Istart = k_uptime_get_32();
146
147 k_work_reschedule(&trickle->timer, K_MSEC(t));
148
149 NET_DBG("new interval at %d ends %d t %d I %d",
150 trickle->Istart,
151 get_end(trickle),
152 t,
153 trickle->I);
154 }
155
156 #define CHECK_IMIN(Imin) \
157 ((Imin < 2) || (Imin > (TICK_MAX >> 1)))
158
net_trickle_create(struct net_trickle * trickle,uint32_t Imin,uint8_t Imax,uint8_t k)159 int net_trickle_create(struct net_trickle *trickle,
160 uint32_t Imin,
161 uint8_t Imax,
162 uint8_t k)
163 {
164 NET_ASSERT(trickle && Imax > 0 && k > 0 && !CHECK_IMIN(Imin));
165
166 (void)memset(trickle, 0, sizeof(struct net_trickle));
167
168 trickle->Imin = Imin;
169 trickle->Imax = Imax;
170 trickle->Imax_abs = Imin << Imax;
171 trickle->k = k;
172
173 NET_ASSERT(trickle->Imax_abs);
174
175 NET_DBG("Imin %d Imax %u k %u Imax_abs %u",
176 trickle->Imin, trickle->Imax, trickle->k,
177 trickle->Imax_abs);
178
179 k_work_init_delayable(&trickle->timer, trickle_timeout);
180
181 return 0;
182 }
183
net_trickle_start(struct net_trickle * trickle,net_trickle_cb_t cb,void * user_data)184 int net_trickle_start(struct net_trickle *trickle,
185 net_trickle_cb_t cb,
186 void *user_data)
187 {
188 NET_ASSERT(trickle && cb);
189
190 trickle->cb = cb;
191 trickle->user_data = user_data;
192 trickle->double_to = false;
193
194 /* Random I in [Imin , Imax] */
195 trickle->I = trickle->Imin +
196 (sys_rand32_get() % (trickle->Imax_abs - trickle->Imin + 1));
197
198 setup_new_interval(trickle);
199
200 NET_DBG("start %d end %d in [%d , %d)",
201 trickle->Istart, get_end(trickle),
202 trickle->I >> 1, trickle->I);
203
204 return 0;
205 }
206
net_trickle_stop(struct net_trickle * trickle)207 int net_trickle_stop(struct net_trickle *trickle)
208 {
209 NET_ASSERT(trickle);
210
211 k_work_cancel_delayable(&trickle->timer);
212
213 trickle->I = 0U;
214
215 return 0;
216 }
217
net_trickle_consistency(struct net_trickle * trickle)218 void net_trickle_consistency(struct net_trickle *trickle)
219 {
220 NET_ASSERT(trickle);
221
222 if (trickle->c < 0xFF) {
223 trickle->c++;
224 }
225
226 NET_DBG("consistency %u", trickle->c);
227 }
228
net_trickle_inconsistency(struct net_trickle * trickle)229 void net_trickle_inconsistency(struct net_trickle *trickle)
230 {
231 NET_ASSERT(trickle);
232
233 if (trickle->I != trickle->Imin) {
234 NET_DBG("inconsistency");
235
236 trickle->I = trickle->Imin;
237 }
238
239 setup_new_interval(trickle);
240 }
241