411 lines
14 KiB
C
411 lines
14 KiB
C
/*
|
|
* Copyright (c) 2012, George Oikonomou - <oikonomou@users.sourceforge.net>
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the copyright holder nor the names of its
|
|
* contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
|
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/**
|
|
* \file
|
|
* Trickle timer library implementation.
|
|
* \author
|
|
* George Oikonomou - <oikonomou@users.sourceforge.net>
|
|
*/
|
|
|
|
/**
|
|
* \addtogroup trickle-timer
|
|
* @{
|
|
*/
|
|
|
|
#include "contiki.h"
|
|
#include "lib/trickle-timer.h"
|
|
#include "sys/ctimer.h"
|
|
#include "sys/cc.h"
|
|
#include "lib/random.h"
|
|
/*---------------------------------------------------------------------------*/
|
|
#define DEBUG 0
|
|
|
|
#if DEBUG
|
|
#include <stdio.h>
|
|
#define PRINTF(...) printf(__VA_ARGS__)
|
|
#else
|
|
#define PRINTF(...)
|
|
#endif
|
|
/*---------------------------------------------------------------------------*/
|
|
/**
|
|
* \brief Wide randoms for platforms using a 4-byte wide clock
|
|
* (see ::TRICKLE_TIMER_WIDE_RAND)
|
|
*/
|
|
#if TRICKLE_TIMER_WIDE_RAND
|
|
#define tt_rand() wide_rand()
|
|
#else
|
|
#define tt_rand() random_rand()
|
|
#endif
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Declarations of variables of local interest */
|
|
/*---------------------------------------------------------------------------*/
|
|
static struct trickle_timer *loctt; /* Pointer to a struct for local use */
|
|
static clock_time_t loc_clock; /* A local, general-purpose placeholder */
|
|
|
|
static void fire(void *ptr);
|
|
static void double_interval(void *ptr);
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Local utilities and functions to be used as ctimer callbacks */
|
|
/*---------------------------------------------------------------------------*/
|
|
#if TRICKLE_TIMER_WIDE_RAND
|
|
/* Returns a 4-byte wide, unsigned random number */
|
|
static uint32_t
|
|
wide_rand(void)
|
|
{
|
|
return ((uint32_t)random_rand() << 16 | random_rand());
|
|
}
|
|
#endif
|
|
/*---------------------------------------------------------------------------*/
|
|
/*
|
|
* Returns the maximum sane Imax value for a given Imin
|
|
*
|
|
* This function is a variant of a fairly standard 'Count Leading Zeros'. It
|
|
* has three flavours. The most suitable one for a specific platform can be
|
|
* configured by changing the value of TRICKLE_TIMER_CONF_MAX_IMAX_WIDTH
|
|
* in the platform's contiki-conf.h
|
|
*/
|
|
#if TRICKLE_TIMER_ERROR_CHECKING
|
|
static uint8_t
|
|
max_imax(clock_time_t value)
|
|
{
|
|
uint8_t zeros = 0;
|
|
#if (TRICKLE_TIMER_MAX_IMAX_WIDTH==TRICKLE_TIMER_MAX_IMAX_GENERIC)
|
|
uint8_t i;
|
|
clock_time_t mask = 0xFFFF;
|
|
|
|
value--;
|
|
|
|
for(i = sizeof(clock_time_t) << 2; i > 0; i >>= 1) {
|
|
if((value & (mask <<= i)) == 0) {
|
|
zeros += i;
|
|
value <<= i;
|
|
}
|
|
}
|
|
|
|
#elif (TRICKLE_TIMER_MAX_IMAX_WIDTH==TRICKLE_TIMER_MAX_IMAX_16_BIT)
|
|
if((value & 0xFF00) == 0) {
|
|
zeros += 8;
|
|
value <<= 8;
|
|
}
|
|
if((value & 0xF000) == 0) {
|
|
zeros += 4;
|
|
value <<= 4;
|
|
}
|
|
if((value & 0xC000) == 0) {
|
|
zeros += 2;
|
|
value <<= 2;
|
|
}
|
|
if((value & 0x8000) == 0) {
|
|
zeros++;
|
|
}
|
|
#elif (TRICKLE_TIMER_MAX_IMAX_WIDTH==TRICKLE_TIMER_MAX_IMAX_32_BIT)
|
|
if((value & 0xFFFF0000) == 0) {
|
|
zeros += 16;
|
|
value <<= 16;
|
|
}
|
|
if((value & 0xFF000000) == 0) {
|
|
zeros += 8;
|
|
value <<= 8;
|
|
}
|
|
if((value & 0xF0000000) == 0) {
|
|
zeros += 4;
|
|
value <<= 4;
|
|
}
|
|
if((value & 0xC0000000) == 0) {
|
|
zeros += 2;
|
|
value <<= 2;
|
|
}
|
|
if((value & 0x80000000) == 0) {
|
|
zeros += 1;
|
|
}
|
|
#endif
|
|
|
|
return zeros - 1; /* Always non-negative due to the range of 'value' */
|
|
}
|
|
#endif /* TRICKLE_TIMER_ERROR_CHECKING */
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Returns a random time point t in [I/2 , I) */
|
|
static clock_time_t
|
|
get_t(clock_time_t i_cur)
|
|
{
|
|
i_cur >>= 1;
|
|
|
|
PRINTF("trickle_timer get t: [%lu, %lu)\n", (unsigned long)i_cur,
|
|
(unsigned long)(i_cur << 1));
|
|
|
|
return i_cur + (tt_rand() % i_cur);
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
static void
|
|
schedule_for_end(struct trickle_timer *tt)
|
|
{
|
|
/* Reset our ctimer, schedule interval_end to run at time I */
|
|
clock_time_t now = clock_time();
|
|
|
|
loc_clock = TRICKLE_TIMER_INTERVAL_END(tt) - now;
|
|
|
|
PRINTF("trickle_timer sched for end: at %lu, end in %ld\n",
|
|
(unsigned long)clock_time(), (signed long)loc_clock);
|
|
|
|
/* Interval's end will happen in loc_clock ticks. Make sure this isn't in
|
|
* the past... */
|
|
if(loc_clock > (TRICKLE_TIMER_CLOCK_MAX >> 1)) {
|
|
loc_clock = 0; /* Interval ended in the past, schedule for in 0 */
|
|
PRINTF("trickle_timer doubling: Was in the past. Compensating\n");
|
|
}
|
|
|
|
ctimer_set(&tt->ct, loc_clock, double_interval, tt);
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
/* This is used as a ctimer callback, thus its argument must be void *. ptr is
|
|
* a pointer to the struct trickle_timer that fired */
|
|
static void
|
|
double_interval(void *ptr)
|
|
{
|
|
clock_time_t last_end;
|
|
|
|
/* 'cast' ptr to a struct trickle_timer */
|
|
loctt = (struct trickle_timer *)ptr;
|
|
|
|
loctt->c = 0;
|
|
|
|
PRINTF("trickle_timer doubling: at %lu, (was for %lu), ",
|
|
(unsigned long)clock_time(),
|
|
(unsigned long)TRICKLE_TIMER_INTERVAL_END(loctt));
|
|
|
|
/* Remember the previous interval's end (absolute time), before we double */
|
|
last_end = TRICKLE_TIMER_INTERVAL_END(loctt);
|
|
|
|
/* Double the interval if we have to */
|
|
if(loctt->i_cur <= TRICKLE_TIMER_INTERVAL_MAX(loctt) >> 1) {
|
|
/* If I <= Imax/2, we double */
|
|
loctt->i_cur <<= 1;
|
|
PRINTF("I << 1 = %lu\n", (unsigned long)loctt->i_cur);
|
|
} else {
|
|
/* We may have I > Imax/2 but I <> Imax, in which case we set to Imax
|
|
* This will happen when I didn't start as Imin (before the first reset) */
|
|
loctt->i_cur = TRICKLE_TIMER_INTERVAL_MAX(loctt);
|
|
PRINTF("I = Imax = %lu\n", (unsigned long)loctt->i_cur);
|
|
}
|
|
|
|
/* Random t in [I/2, I) */
|
|
loc_clock = get_t(loctt->i_cur);
|
|
|
|
PRINTF("trickle_timer doubling: t=%lu\n", (unsigned long)loc_clock);
|
|
|
|
#if TRICKLE_TIMER_COMPENSATE_DRIFT
|
|
/* Schedule for t ticks after the previous interval's end, not after now. If
|
|
* that is in the past, schedule in 0 */
|
|
loc_clock = (last_end + loc_clock) - clock_time();
|
|
PRINTF("trickle_timer doubling: at %lu, in %ld ticks\n",
|
|
(unsigned long)clock_time(), (signed long)loc_clock);
|
|
if(loc_clock > (TRICKLE_TIMER_CLOCK_MAX >> 1)) {
|
|
/* Oops, that's in the past */
|
|
loc_clock = 0;
|
|
PRINTF("trickle_timer doubling: Was in the past. Compensating\n");
|
|
}
|
|
ctimer_set(&loctt->ct, loc_clock, fire, loctt);
|
|
|
|
/* Store the actual interval start (absolute time), we need it later.
|
|
* We pretend that it started at the same time when the last one ended */
|
|
loctt->i_start = last_end;
|
|
#else
|
|
/* Assumed that the previous interval's end is 'now' and schedule in t ticks
|
|
* after 'now', ignoring potential offsets */
|
|
ctimer_set(&loctt->ct, loc_clock, fire, loctt);
|
|
/* Store the actual interval start (absolute time), we need it later */
|
|
loctt->i_start = loctt->ct.etimer.timer.start;
|
|
#endif
|
|
|
|
PRINTF("trickle_timer doubling: Last end %lu, new end %lu, for %lu, I=%lu\n",
|
|
(unsigned long)last_end,
|
|
(unsigned long)TRICKLE_TIMER_INTERVAL_END(loctt),
|
|
(unsigned long)(loctt->ct.etimer.timer.start +
|
|
loctt->ct.etimer.timer.interval),
|
|
(unsigned long)(loctt->i_cur));
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Called by the ctimer module at time t within the current interval. ptr is
|
|
* a pointer to the struct trickle_timer of interest */
|
|
static void
|
|
fire(void *ptr)
|
|
{
|
|
/* 'cast' c to a struct trickle_timer */
|
|
loctt = (struct trickle_timer *)ptr;
|
|
|
|
PRINTF("trickle_timer fire: at %lu (was for %lu)\n",
|
|
(unsigned long)clock_time(),
|
|
(unsigned long)(loctt->ct.etimer.timer.start +
|
|
loctt->ct.etimer.timer.interval));
|
|
|
|
if(loctt->cb) {
|
|
/*
|
|
* Call the protocol's TX callback, with the suppression status as an
|
|
* argument.
|
|
*/
|
|
PRINTF("trickle_timer fire: Suppression Status %u (%u < %u)\n",
|
|
TRICKLE_TIMER_PROTO_TX_ALLOW(loctt), loctt->c, loctt->k);
|
|
loctt->cb(loctt->cb_arg, TRICKLE_TIMER_PROTO_TX_ALLOW(loctt));
|
|
}
|
|
|
|
if(trickle_timer_is_running(loctt)) {
|
|
schedule_for_end(loctt);
|
|
}
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
/* New trickle interval, either due to a newly set trickle timer or due to an
|
|
* inconsistency. Schedule 'fire' to be called in t ticks. */
|
|
static void
|
|
new_interval(struct trickle_timer *tt)
|
|
{
|
|
tt->c = 0;
|
|
|
|
/* Random t in [I/2, I) */
|
|
loc_clock = get_t(tt->i_cur);
|
|
|
|
ctimer_set(&tt->ct, loc_clock, fire, tt);
|
|
|
|
/* Store the actual interval start (absolute time), we need it later */
|
|
tt->i_start = tt->ct.etimer.timer.start;
|
|
PRINTF("trickle_timer new interval: at %lu, ends %lu, ",
|
|
(unsigned long)clock_time(),
|
|
(unsigned long)TRICKLE_TIMER_INTERVAL_END(tt));
|
|
PRINTF("t=%lu, I=%lu\n", (unsigned long)loc_clock, (unsigned long)tt->i_cur);
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Functions to be called by the protocol implementation */
|
|
/*---------------------------------------------------------------------------*/
|
|
void
|
|
trickle_timer_consistency(struct trickle_timer *tt)
|
|
{
|
|
if(tt->c < 0xFF) {
|
|
tt->c++;
|
|
}
|
|
PRINTF("trickle_timer consistency: c=%u\n", tt->c);
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
void
|
|
trickle_timer_inconsistency(struct trickle_timer *tt)
|
|
{
|
|
/* "If I is equal to Imin when Trickle hears an "inconsistent" transmission,
|
|
* Trickle does nothing." */
|
|
if(tt->i_cur != tt->i_min) {
|
|
PRINTF("trickle_timer inconsistency\n");
|
|
tt->i_cur = tt->i_min;
|
|
|
|
new_interval(tt);
|
|
}
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
uint8_t
|
|
trickle_timer_config(struct trickle_timer *tt, clock_time_t i_min,
|
|
uint8_t i_max, uint8_t k)
|
|
{
|
|
#if TRICKLE_TIMER_ERROR_CHECKING
|
|
/*
|
|
* Although in theory Imin=1 is a valid value, it would break get_t() and
|
|
* doesn't make sense anyway. Thus Valid Imin values are in the range:
|
|
* 1 < Imin <= (TRICKLE_TIMER_CLOCK_MAX >> 1) + 1
|
|
*/
|
|
if(TRICKLE_TIMER_IMIN_IS_BAD(i_min)) {
|
|
PRINTF("trickle_timer config: Bad Imin value\n");
|
|
return TRICKLE_TIMER_ERROR;
|
|
}
|
|
|
|
if(tt == NULL || i_max == 0 || k == 0) {
|
|
PRINTF("trickle_timer config: Bad arguments\n");
|
|
return TRICKLE_TIMER_ERROR;
|
|
}
|
|
|
|
/*
|
|
* If clock_time_t is not wide enough to store Imin << Imax, we adjust Imax
|
|
*
|
|
* This means that 'we' are likely to have a different Imax than 'them'
|
|
* See RFC 6206, sec 6.3 for the consequences of this situation
|
|
*/
|
|
if(TRICKLE_TIMER_IPAIR_IS_BAD(i_min, i_max)) {
|
|
PRINTF("trickle_timer config: %lu << %u would exceed clock boundaries. ",
|
|
(unsigned long)i_min, i_max);
|
|
|
|
/* For this Imin, get the maximum sane Imax */
|
|
i_max = max_imax(i_min);
|
|
PRINTF("trickle_timer config: Using Imax=%u\n", i_max);
|
|
}
|
|
#endif
|
|
|
|
tt->i_min = i_min;
|
|
tt->i_max = i_max;
|
|
tt->i_max_abs = i_min << i_max;
|
|
tt->k = k;
|
|
|
|
PRINTF("trickle_timer config: Imin=%lu, Imax=%u, k=%u\n",
|
|
(unsigned long)tt->i_min, tt->i_max, tt->k);
|
|
|
|
return TRICKLE_TIMER_SUCCESS;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
uint8_t
|
|
trickle_timer_set(struct trickle_timer *tt, trickle_timer_cb_t proto_cb,
|
|
void *ptr)
|
|
{
|
|
#if TRICKLE_TIMER_ERROR_CHECKING
|
|
/* Sanity checks */
|
|
if(tt == NULL || proto_cb == NULL) {
|
|
PRINTF("trickle_timer set: Bad arguments\n");
|
|
return TRICKLE_TIMER_ERROR;
|
|
}
|
|
#endif
|
|
|
|
tt->cb = proto_cb;
|
|
tt->cb_arg = ptr;
|
|
|
|
/* Random I in [Imin , Imax] */
|
|
tt->i_cur = tt->i_min +
|
|
(tt_rand() % (TRICKLE_TIMER_INTERVAL_MAX(tt) - tt->i_min + 1));
|
|
|
|
PRINTF("trickle_timer set: I=%lu in [%lu , %lu]\n", (unsigned long)tt->i_cur,
|
|
(unsigned long)tt->i_min,
|
|
(unsigned long)TRICKLE_TIMER_INTERVAL_MAX(tt));
|
|
|
|
new_interval(tt);
|
|
|
|
PRINTF("trickle_timer set: at %lu, ends %lu, t=%lu in [%lu , %lu)\n",
|
|
(unsigned long)tt->i_start,
|
|
(unsigned long)TRICKLE_TIMER_INTERVAL_END(tt),
|
|
(unsigned long)tt->ct.etimer.timer.interval,
|
|
(unsigned long)tt->i_cur >> 1, (unsigned long)tt->i_cur);
|
|
|
|
return TRICKLE_TIMER_SUCCESS;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
/** @} */
|