Contiki-Inga 3.x
trickle-timer.c
Go to the documentation of this file.
1 /**
2  * \addtogroup trickle-timer
3  * @{
4  */
5 
6 /**
7  * \file
8  * Trickle timer library implementation.
9  * \author
10  * George Oikonomou - <oikonomou@users.sourceforge.net>
11  */
12 
13 /*
14  * Copyright (c) 2012, George Oikonomou - <oikonomou@users.sourceforge.net>
15  * All rights reserved.
16  *
17  * Redistribution and use in source and binary forms, with or without
18  * modification, are permitted provided that the following conditions
19  * are met:
20  *
21  * 1. Redistributions of source code must retain the above copyright
22  * notice, this list of conditions and the following disclaimer.
23  * 2. Redistributions in binary form must reproduce the above copyright
24  * notice, this list of conditions and the following disclaimer in the
25  * documentation and/or other materials provided with the distribution.
26  * 3. Neither the name of the copyright holder nor the names of its
27  * contributors may be used to endorse or promote products derived
28  * from this software without specific prior written permission.
29  *
30  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
31  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
32  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
33  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
34  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
35  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
36  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
37  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
38  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
41  * OF THE POSSIBILITY OF SUCH DAMAGE.
42  */
43 #include "contiki-conf.h"
44 #include "lib/trickle-timer.h"
45 #include "sys/ctimer.h"
46 #include "sys/cc.h"
47 #include "lib/random.h"
48 /*---------------------------------------------------------------------------*/
49 #define DEBUG 0
50 
51 #if DEBUG
52 #include <stdio.h>
53 #define PRINTF(...) printf(__VA_ARGS__)
54 #else
55 #define PRINTF(...)
56 #endif
57 /*---------------------------------------------------------------------------*/
58 /**
59  * \brief Wide randoms for platforms using a 4-byte wide clock
60  * (see ::TRICKLE_TIMER_WIDE_RAND)
61  */
62 #if TRICKLE_TIMER_WIDE_RAND
63 #define tt_rand() wide_rand()
64 #else
65 #define tt_rand() random_rand()
66 #endif
67 /*---------------------------------------------------------------------------*/
68 /* Declarations of variables of local interest */
69 /*---------------------------------------------------------------------------*/
70 static struct trickle_timer *loctt; /* Pointer to a struct for local use */
71 static clock_time_t loc_clock; /* A local, general-purpose placeholder */
72 
73 static void fire(void *ptr);
74 static void double_interval(void *ptr);
75 /*---------------------------------------------------------------------------*/
76 /* Local utilities and functions to be used as ctimer callbacks */
77 /*---------------------------------------------------------------------------*/
78 #if TRICKLE_TIMER_WIDE_RAND
79 /* Returns a 4-byte wide, unsigned random number */
80 static uint32_t
81 wide_rand()
82 {
83  return ((uint32_t)random_rand() << 16 | random_rand());
84 }
85 #endif
86 /*---------------------------------------------------------------------------*/
87 /*
88  * Returns the maximum sane Imax value for a given Imin
89  *
90  * This function is a variant of a fairly standard 'Count Leading Zeros'. It
91  * has three flavours. The most suitable one for a specific platform can be
92  * configured by changing the value of TRICKLE_TIMER_CONF_MAX_IMAX_WIDTH
93  * in the platform's contiki-conf.h
94  */
95 #if TRICKLE_TIMER_ERROR_CHECKING
96 static uint8_t
97 max_imax(clock_time_t value)
98 {
99  uint8_t zeros = 0;
100 #if (TRICKLE_TIMER_MAX_IMAX_WIDTH==TRICKLE_TIMER_MAX_IMAX_GENERIC)
101  uint8_t i;
102  clock_time_t mask = 0xFFFF;
103 
104  value--;
105 
106  for(i = sizeof(clock_time_t) << 2; i > 0; i >>= 1) {
107  if((value & (mask <<= i)) == 0) {
108  zeros += i;
109  value <<= i;
110  }
111  }
112 
113 #elif (TRICKLE_TIMER_MAX_IMAX_WIDTH==TRICKLE_TIMER_MAX_IMAX_16_BIT)
114  if((value & 0xFF00) == 0) {
115  zeros += 8;
116  value <<= 8;
117  }
118  if((value & 0xF000) == 0) {
119  zeros += 4;
120  value <<= 4;
121  }
122  if((value & 0xC000) == 0) {
123  zeros += 2;
124  value <<= 2;
125  }
126  if((value & 0x8000) == 0) {
127  zeros++;
128  }
129 #elif (TRICKLE_TIMER_MAX_IMAX_WIDTH==TRICKLE_TIMER_MAX_IMAX_32_BIT)
130  if((value & 0xFFFF0000) == 0) {
131  zeros += 16;
132  value <<= 16;
133  }
134  if((value & 0xFF000000) == 0) {
135  zeros += 8;
136  value <<= 8;
137  }
138  if((value & 0xF0000000) == 0) {
139  zeros += 4;
140  value <<= 4;
141  }
142  if((value & 0xC0000000) == 0) {
143  zeros += 2;
144  value <<= 2;
145  }
146  if((value & 0x80000000) == 0) {
147  zeros += 1;
148  }
149 #endif
150 
151  return zeros - 1; /* Always non-negative due to the range of 'value' */
152 }
153 #endif /* TRICKLE_TIMER_ERROR_CHECKING */
154 /*---------------------------------------------------------------------------*/
155 /* Returns a random time point t in [I/2 , I) */
156 static clock_time_t
157 get_t(clock_time_t i_cur)
158 {
159  i_cur >>= 1;
160 
161  PRINTF("trickle_timer get t: [%lu, %lu)\n", (unsigned long)i_cur,
162  (unsigned long)(i_cur << 1));
163 
164  return i_cur + (tt_rand() % i_cur);
165 }
166 /*---------------------------------------------------------------------------*/
167 static void
168 schedule_for_end(struct trickle_timer *tt)
169 {
170  /* Reset our ctimer, schedule interval_end to run at time I */
171  clock_time_t now = clock_time();
172 
173  loc_clock = TRICKLE_TIMER_INTERVAL_END(tt) - now;
174 
175  PRINTF("trickle_timer sched for end: at %lu, end in %ld\n",
176  (unsigned long)clock_time(), (signed long)loc_clock);
177 
178  /* Interval's end will happen in loc_clock ticks. Make sure this isn't in
179  * the past... */
180  if(loc_clock > (TRICKLE_TIMER_CLOCK_MAX >> 1)) {
181  loc_clock = 0; /* Interval ended in the past, schedule for in 0 */
182  PRINTF("trickle_timer doubling: Was in the past. Compensating\n");
183  }
184 
185  ctimer_set(&tt->ct, loc_clock, double_interval, tt);
186 }
187 /*---------------------------------------------------------------------------*/
188 /* This is used as a ctimer callback, thus its argument must be void *. ptr is
189  * a pointer to the struct trickle_timer that fired */
190 static void
191 double_interval(void *ptr)
192 {
193  clock_time_t last_end;
194 
195  /* 'cast' ptr to a struct trickle_timer */
196  loctt = (struct trickle_timer *)ptr;
197 
198  loctt->c = 0;
199 
200  PRINTF("trickle_timer doubling: at %lu, (was for %lu), ",
201  (unsigned long)clock_time(),
202  (unsigned long)TRICKLE_TIMER_INTERVAL_END(loctt));
203 
204  /* Remember the previous interval's end (absolute time), before we double */
205  last_end = TRICKLE_TIMER_INTERVAL_END(loctt);
206 
207  /* Double the interval if we have to */
208  if(loctt->i_cur <= TRICKLE_TIMER_INTERVAL_MAX(loctt) >> 1) {
209  /* If I <= Imax/2, we double */
210  loctt->i_cur <<= 1;
211  PRINTF("I << 1 = %lu\n", (unsigned long)loctt->i_cur);
212  } else {
213  /* We may have I > Imax/2 but I <> Imax, in which case we set to Imax
214  * This will happen when I didn't start as Imin (before the first reset) */
215  loctt->i_cur = TRICKLE_TIMER_INTERVAL_MAX(loctt);
216  PRINTF("I = Imax = %lu\n", (unsigned long)loctt->i_cur);
217  }
218 
219  /* Random t in [I/2, I) */
220  loc_clock = get_t(loctt->i_cur);
221 
222  PRINTF("trickle_timer doubling: t=%lu\n", (unsigned long)loc_clock);
223 
224 #if TRICKLE_TIMER_COMPENSATE_DRIFT
225  /* Schedule for t ticks after the previous interval's end, not after now. If
226  * that is in the past, schedule in 0 */
227  loc_clock = (last_end + loc_clock) - clock_time();
228  PRINTF("trickle_timer doubling: at %lu, in %ld ticks\n",
229  (unsigned long)clock_time(), (signed long)loc_clock);
230  if(loc_clock > (TRICKLE_TIMER_CLOCK_MAX >> 1)) {
231  /* Oops, that's in the past */
232  loc_clock = 0;
233  PRINTF("trickle_timer doubling: Was in the past. Compensating\n");
234  }
235  ctimer_set(&loctt->ct, loc_clock, fire, loctt);
236 
237  /* Store the actual interval start (absolute time), we need it later.
238  * We pretend that it started at the same time when the last one ended */
239  loctt->i_start = last_end;
240 #else
241  /* Assumed that the previous interval's end is 'now' and schedule in t ticks
242  * after 'now', ignoring potential offsets */
243  ctimer_set(&loctt->ct, loc_clock, fire, loctt);
244  /* Store the actual interval start (absolute time), we need it later */
245  loctt->i_start = loctt->ct.etimer.timer.start;
246 #endif
247 
248  PRINTF("trickle_timer doubling: Last end %lu, new end %lu, for %lu, I=%lu\n",
249  (unsigned long)last_end,
250  (unsigned long)TRICKLE_TIMER_INTERVAL_END(loctt),
251  (unsigned long)(loctt->ct.etimer.timer.start +
252  loctt->ct.etimer.timer.interval),
253  (unsigned long)(loctt->i_cur));
254 }
255 /*---------------------------------------------------------------------------*/
256 /* Called by the ctimer module at time t within the current interval. ptr is
257  * a pointer to the struct trickle_timer of interest */
258 static void
259 fire(void *ptr)
260 {
261  /* 'cast' c to a struct trickle_timer */
262  loctt = (struct trickle_timer *)ptr;
263 
264  PRINTF("trickle_timer fire: at %lu (was for %lu)\n",
265  (unsigned long)clock_time(),
266  (unsigned long)(loctt->ct.etimer.timer.start +
267  loctt->ct.etimer.timer.interval));
268 
269  if(loctt->cb) {
270  /*
271  * Call the protocol's TX callback, with the suppression status as an
272  * argument.
273  */
274  PRINTF("trickle_timer fire: Suppression Status %u (%u < %u)\n",
275  TRICKLE_TIMER_PROTO_TX_ALLOW(loctt), loctt->c, loctt->k);
276  loctt->cb(loctt->cb_arg, TRICKLE_TIMER_PROTO_TX_ALLOW(loctt));
277  }
278 
279  if(trickle_timer_is_running(loctt)) {
280  schedule_for_end(loctt);
281  }
282 }
283 /*---------------------------------------------------------------------------*/
284 /* New trickle interval, either due to a newly set trickle timer or due to an
285  * inconsistency. Schedule 'fire' to be called in t ticks. */
286 static void
287 new_interval(struct trickle_timer *tt)
288 {
289  tt->c = 0;
290 
291  /* Random t in [I/2, I) */
292  loc_clock = get_t(tt->i_cur);
293 
294  ctimer_set(&tt->ct, loc_clock, fire, tt);
295 
296  /* Store the actual interval start (absolute time), we need it later */
297  tt->i_start = tt->ct.etimer.timer.start;
298  PRINTF("trickle_timer new interval: at %lu, ends %lu, ",
299  (unsigned long)clock_time(),
300  (unsigned long)TRICKLE_TIMER_INTERVAL_END(tt));
301  PRINTF("t=%lu, I=%lu\n", (unsigned long)loc_clock, (unsigned long)tt->i_cur);
302 }
303 /*---------------------------------------------------------------------------*/
304 /* Functions to be called by the protocol implementation */
305 /*---------------------------------------------------------------------------*/
306 void
308 {
309  if(tt->c < 0xFF) {
310  tt->c++;
311  }
312  PRINTF("trickle_timer consistency: c=%u\n", tt->c);
313 }
314 /*---------------------------------------------------------------------------*/
315 void
317 {
318  /* "If I is equal to Imin when Trickle hears an "inconsistent" transmission,
319  * Trickle does nothing." */
320  if(tt->i_cur != tt->i_min) {
321  PRINTF("trickle_timer inconsistency\n");
322  tt->i_cur = tt->i_min;
323 
324  new_interval(tt);
325  }
326 }
327 /*---------------------------------------------------------------------------*/
328 uint8_t
329 trickle_timer_config(struct trickle_timer *tt, clock_time_t i_min,
330  uint8_t i_max, uint8_t k)
331 {
332 #if TRICKLE_TIMER_ERROR_CHECKING
333  /*
334  * Although in theory Imin=1 is a valid value, it would break get_t() and
335  * doesn't make sense anyway. Thus Valid Imin values are in the range:
336  * 1 < Imin <= (TRICKLE_TIMER_CLOCK_MAX >> 1) + 1
337  */
338  if(TRICKLE_TIMER_IMIN_IS_BAD(i_min)) {
339  PRINTF("trickle_timer config: Bad Imin value\n");
340  return TRICKLE_TIMER_ERROR;
341  }
342 
343  if(tt == NULL || i_max == 0 || k == 0) {
344  PRINTF("trickle_timer config: Bad arguments\n");
345  return TRICKLE_TIMER_ERROR;
346  }
347 
348  /*
349  * If clock_time_t is not wide enough to store Imin << Imax, we adjust Imax
350  *
351  * This means that 'we' are likely to have a different Imax than 'them'
352  * See RFC 6206, sec 6.3 for the consequences of this situation
353  */
354  if(TRICKLE_TIMER_IPAIR_IS_BAD(i_min, i_max)) {
355  PRINTF("trickle_timer config: %lu << %u would exceed clock boundaries. ",
356  (unsigned long)i_min, i_max);
357 
358  /* For this Imin, get the maximum sane Imax */
359  i_max = max_imax(i_min);
360  PRINTF("trickle_timer config: Using Imax=%u\n", i_max);
361  }
362 #endif
363 
364  tt->i_min = i_min;
365  tt->i_max = i_max;
366  tt->i_max_abs = i_min << i_max;
367  tt->k = k;
368 
369  PRINTF("trickle_timer config: Imin=%lu, Imax=%u, k=%u\n",
370  (unsigned long)tt->i_min, tt->i_max, tt->k);
371 
372  return TRICKLE_TIMER_SUCCESS;
373 }
374 /*---------------------------------------------------------------------------*/
375 uint8_t
377  void *ptr)
378 {
379 #if TRICKLE_TIMER_ERROR_CHECKING
380  /* Sanity checks */
381  if(tt == NULL || proto_cb == NULL) {
382  PRINTF("trickle_timer set: Bad arguments\n");
383  return TRICKLE_TIMER_ERROR;
384  }
385 #endif
386 
387  tt->cb = proto_cb;
388  tt->cb_arg = ptr;
389 
390  /* Random I in [Imin , Imax] */
391  tt->i_cur = tt->i_min +
392  (tt_rand() % (TRICKLE_TIMER_INTERVAL_MAX(tt) - tt->i_min + 1));
393 
394  PRINTF("trickle_timer set: I=%lu in [%lu , %lu]\n", (unsigned long)tt->i_cur,
395  (unsigned long)tt->i_min,
396  (unsigned long)TRICKLE_TIMER_INTERVAL_MAX(tt));
397 
398  new_interval(tt);
399 
400  PRINTF("trickle_timer set: at %lu, ends %lu, t=%lu in [%lu , %lu)\n",
401  (unsigned long)tt->i_start,
402  (unsigned long)TRICKLE_TIMER_INTERVAL_END(tt),
403  (unsigned long)tt->ct.etimer.timer.interval,
404  (unsigned long)tt->i_cur >> 1, (unsigned long)tt->i_cur);
405 
406  return TRICKLE_TIMER_SUCCESS;
407 }
408 /*---------------------------------------------------------------------------*/
409 /** @} */