Merge pull request #1285 from simonduq/pr/tsch

TSCH + Basic 6TiSCH
This commit is contained in:
Simon Duquennoy 2015-12-07 14:29:10 +01:00
commit 4302e23ddc
65 changed files with 9574 additions and 203 deletions

View file

@ -0,0 +1 @@
orchestra_src = orchestra.c orchestra-rule-default-common.c orchestra-rule-eb-per-time-source.c orchestra-rule-unicast-per-neighbor.c

51
apps/orchestra/README.md Normal file
View file

@ -0,0 +1,51 @@
# Orchestra
## Overview
Orchestra is an autonomous scheduling solution for TSCH, where nodes maintain
their own schedule solely based on their local RPL state. There is no centralized
scheduler nor negociatoin with neighbors, i.e. no traffic overhead. The default
Orchestra rules can be used out-of-box in any RPL network, reducing contention
to a low level. Orchestra is described and evaluated in
[*Orchestra: Robust Mesh Networks Through Autonomously Scheduled TSCH*](http://www.simonduquennoy.net/papers/duquennoy15orchestra.pdf), ACM SenSys'15.
## Requirements
Orchestra requires a system running TSCH and RPL.
For sender-based unicast slots (`ORCHESTRA_UNICAST_SENDER_BASED`), it requires
RPL with downwards routing enabled (relies on DAO).
## Getting Started
To use Orchestra, add a couple global definitions, e.g in your `project-conf.h` file.
Disable 6TiSCH minimal schedule:
`#define TSCH_SCHEDULE_CONF_WITH_6TISCH_MINIMAL 0`
Enable TSCH link selector (allows Orchestra to assign TSCH links to outgoing packets):
`#define TSCH_CONF_WITH_LINK_SELECTOR 1`
Set up the following callbacks:
```
#define TSCH_CALLBACK_NEW_TIME_SOURCE orchestra_callback_new_time_source
#define TSCH_CALLBACK_PACKET_READY orchestra_callback_packet_ready
#define NETSTACK_CONF_ROUTING_NEIGHBOR_ADDED_CALLBACK orchestra_callback_child_added
#define NETSTACK_CONF_ROUTING_NEIGHBOR_REMOVED_CALLBACK orchestra_callback_child_removed
```
To use Orchestra, fist add it to your makefile `APPS` with `APPS += orchestra`.
Finally:
* add Orchestra to your makefile `APPS` with `APPS += orchestra`;
* start Orchestra by calling `orchestra_init()` from your application, after
including `#include "orchestra.h"`.
## Configuration
Orchestra comes with a number of pre-installed rules, `orchestra-rule-*.c`.
You can define your own by using any of these as a template.
A default Orchestra configuration is described in `orchestra-conf.h`, define your own
`ORCHESTRA_CONF_*` macros to override modify the rule set and change rules configuration.

View file

@ -0,0 +1,105 @@
/*
* Copyright (c) 2015, Swedish Institute of Computer Science.
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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
* Orchestra configuration
*
* \author Simon Duquennoy <simonduq@sics.se>
*/
#ifndef __ORCHESTRA_CONF_H__
#define __ORCHESTRA_CONF_H__
#ifdef ORCHESTRA_CONF_RULES
#define ORCHESTRA_RULES ORCHESTRA_CONF_RULES
#else /* ORCHESTRA_CONF_RULES */
/* A default configuration with:
* - a sender-based slotframe for EB transmission
* - a sender-based or receiver-based slotframe for unicast to RPL parents and children
* - a common shared slotframe for any other traffic (mostly broadcast)
* */
#define ORCHESTRA_RULES { &eb_per_time_source, \
&unicast_per_neighbor, \
&default_common, \
}
#endif /* ORCHESTRA_CONF_RULES */
/* Length of the various slotframes. Tune to balance network capacity,
* contention, energy, latency. */
#ifdef ORCHESTRA_CONF_EBSF_PERIOD
#define ORCHESTRA_EBSF_PERIOD ORCHESTRA_CONF_EBSF_PERIOD
#else /* ORCHESTRA_CONF_EBSF_PERIOD */
#define ORCHESTRA_EBSF_PERIOD 397
#endif /* ORCHESTRA_CONF_EBSF_PERIOD */
#ifdef ORCHESTRA_CONF_COMMON_SHARED_PERIOD
#define ORCHESTRA_COMMON_SHARED_PERIOD ORCHESTRA_CONF_COMMON_SHARED_PERIOD
#else /* ORCHESTRA_CONF_COMMON_SHARED_PERIOD */
#define ORCHESTRA_COMMON_SHARED_PERIOD 31
#endif /* ORCHESTRA_CONF_COMMON_SHARED_PERIOD */
#ifdef ORCHESTRA_CONF_UNICAST_PERIOD
#define ORCHESTRA_UNICAST_PERIOD ORCHESTRA_CONF_UNICAST_PERIOD
#else /* ORCHESTRA_CONF_UNICAST_PERIOD */
#define ORCHESTRA_UNICAST_PERIOD 17
#endif /* ORCHESTRA_CONF_UNICAST_PERIOD */
/* Is the per-neighbor unicast slotframe sender-based (if not, it is receiver-based).
* Note: sender-based works only with RPL storing mode as it relies on DAO and
* routing entries to keep track of children and parents. */
#ifdef ORCHESTRA_CONF_UNICAST_SENDER_BASED
#define ORCHESTRA_UNICAST_SENDER_BASED ORCHESTRA_CONF_UNICAST_SENDER_BASED
#else /* ORCHESTRA_CONF_UNICAST_SENDER_BASED */
#define ORCHESTRA_UNICAST_SENDER_BASED 0
#endif /* ORCHESTRA_CONF_UNICAST_SENDER_BASED */
/* The hash function used to assign timeslot to a given node (based on its link-layer address) */
#ifdef ORCHESTRA_CONF_LINKADDR_HASH
#define ORCHESTRA_LINKADDR_HASH ORCHESTRA_CONF_LINKADDR_HASH
#else /* ORCHESTRA_CONF_LINKADDR_HASH */
#define ORCHESTRA_LINKADDR_HASH(addr) ((addr != NULL) ? (addr)->u8[LINKADDR_SIZE - 1] : -1)
#endif /* ORCHESTRA_CONF_LINKADDR_HASH */
/* The maximum hash */
#ifdef ORCHESTRA_CONF_MAX_HASH
#define ORCHESTRA_MAX_HASH ORCHESTRA_CONF_MAX_HASH
#else /* ORCHESTRA_CONF_MAX_HASH */
#define ORCHESTRA_MAX_HASH 0x7fff
#endif /* ORCHESTRA_CONF_MAX_HASH */
/* Is the "hash" function collision-free? (e.g. it maps to unique node-ids) */
#ifdef ORCHESTRA_CONF_COLLISION_FREE_HASH
#define ORCHESTRA_COLLISION_FREE_HASH ORCHESTRA_CONF_COLLISION_FREE_HASH
#else /* ORCHESTRA_CONF_COLLISION_FREE_HASH */
#define ORCHESTRA_COLLISION_FREE_HASH 0 /* Set to 1 if ORCHESTRA_LINKADDR_HASH returns unique hashes */
#endif /* ORCHESTRA_CONF_COLLISION_FREE_HASH */
#endif /* __ORCHESTRA_CONF_H__ */

View file

@ -0,0 +1,86 @@
/*
* Copyright (c) 2015, Swedish Institute of Computer Science.
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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
* Orchestra: a slotframe with a single shared link, common to all nodes
* in the network, used for unicast and broadcast.
*
* \author Simon Duquennoy <simonduq@sics.se>
*/
#include "contiki.h"
#include "orchestra.h"
static uint16_t slotframe_handle = 0;
static uint16_t channel_offset = 0;
#if ORCHESTRA_EBSF_PERIOD > 0
/* There is a slotframe for EBs, use this slotframe for non-EB traffic only */
#define ORCHESTRA_COMMON_SHARED_TYPE LINK_TYPE_NORMAL
#else
/* There is no slotframe for EBs, use this slotframe both EB and non-EB traffic */
#define ORCHESTRA_COMMON_SHARED_TYPE LINK_TYPE_ADVERTISING
#endif
/*---------------------------------------------------------------------------*/
static int
select_packet(uint16_t *slotframe, uint16_t *timeslot)
{
/* We are the default slotframe, select anything */
if(slotframe != NULL) {
*slotframe = slotframe_handle;
}
if(timeslot != NULL) {
*timeslot = 0;
}
return 1;
}
/*---------------------------------------------------------------------------*/
static void
init(uint16_t sf_handle)
{
slotframe_handle = sf_handle;
channel_offset = slotframe_handle;
/* Default slotframe: for broadcast or unicast to neighbors we
* do not have a link to */
struct tsch_slotframe *sf_common = tsch_schedule_add_slotframe(slotframe_handle, ORCHESTRA_COMMON_SHARED_PERIOD);
tsch_schedule_add_link(sf_common,
LINK_OPTION_RX | LINK_OPTION_TX | LINK_OPTION_SHARED,
ORCHESTRA_COMMON_SHARED_TYPE, &tsch_broadcast_address,
0, channel_offset);
}
/*---------------------------------------------------------------------------*/
struct orchestra_rule default_common = {
init,
NULL,
select_packet,
NULL,
NULL,
};

View file

@ -0,0 +1,116 @@
/*
* Copyright (c) 2015, Swedish Institute of Computer Science.
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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
* Orchestra: a slotframe dedicated to transmission of EBs.
* Nodes transmit at a timeslot defined as hash(MAC) % ORCHESTRA_EBSF_PERIOD
* Nodes listen at a timeslot defined as hash(time_source.MAC) % ORCHESTRA_EBSF_PERIOD
* \author Simon Duquennoy <simonduq@sics.se>
*/
#include "contiki.h"
#include "orchestra.h"
#include "net/packetbuf.h"
static uint16_t slotframe_handle = 0;
static uint16_t channel_offset = 0;
static struct tsch_slotframe *sf_eb;
/*---------------------------------------------------------------------------*/
static uint16_t
get_node_timeslot(const linkaddr_t *addr)
{
#if ORCHESTRA_EBSF_PERIOD > 0
return ORCHESTRA_LINKADDR_HASH(addr) % ORCHESTRA_EBSF_PERIOD;
#else
return 0xffff;
#endif
}
/*---------------------------------------------------------------------------*/
static int
select_packet(uint16_t *slotframe, uint16_t *timeslot)
{
/* Select EBs only */
if(packetbuf_attr(PACKETBUF_ATTR_FRAME_TYPE) == FRAME802154_BEACONFRAME) {
if(slotframe != NULL) {
*slotframe = slotframe_handle;
}
if(timeslot != NULL) {
*timeslot = get_node_timeslot(&linkaddr_node_addr);
}
return 1;
}
return 0;
}
/*---------------------------------------------------------------------------*/
static void
new_time_source(const struct tsch_neighbor *old, const struct tsch_neighbor *new)
{
uint16_t old_ts = get_node_timeslot(&old->addr);
uint16_t new_ts = get_node_timeslot(&new->addr);
if(new_ts == old_ts) {
return;
}
if(old_ts != 0xffff) {
/* Stop listening to the old time source's EBs */
tsch_schedule_remove_link_by_timeslot(sf_eb, old_ts);
}
if(new_ts != 0xffff) {
/* Listen to the time source's EBs */
tsch_schedule_add_link(sf_eb,
LINK_OPTION_RX,
LINK_TYPE_ADVERTISING_ONLY, NULL,
new_ts, 0);
}
}
/*---------------------------------------------------------------------------*/
static void
init(uint16_t sf_handle)
{
slotframe_handle = sf_handle;
channel_offset = sf_handle;
sf_eb = tsch_schedule_add_slotframe(slotframe_handle, ORCHESTRA_EBSF_PERIOD);
/* EB link: every neighbor uses its own to avoid contention */
tsch_schedule_add_link(sf_eb,
LINK_OPTION_TX,
LINK_TYPE_ADVERTISING_ONLY, &tsch_broadcast_address,
get_node_timeslot(&linkaddr_node_addr), 0);
}
/*---------------------------------------------------------------------------*/
struct orchestra_rule eb_per_time_source = {
init,
new_time_source,
select_packet,
NULL,
NULL,
};

View file

@ -0,0 +1,194 @@
/*
* Copyright (c) 2015, Swedish Institute of Computer Science.
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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
* Orchestra: a slotframe dedicated to unicast data transmission.
* If sender-based:
* Nodes listen at a timeslot defined as hash(MAC) % ORCHESTRA_SB_UNICAST_PERIOD
* Nodes transmit at: for each nbr in RPL children and RPL preferred parent,
* hash(nbr.MAC) % ORCHESTRA_SB_UNICAST_PERIOD
* If receiver-based: the opposite
*
* \author Simon Duquennoy <simonduq@sics.se>
*/
#include "contiki.h"
#include "orchestra.h"
#include "net/ipv6/uip-ds6-route.h"
#include "net/packetbuf.h"
#if ORCHESTRA_UNICAST_SENDER_BASED && ORCHESTRA_COLLISION_FREE_HASH
#define UNICAST_SLOT_SHARED_FLAG ((ORCHESTRA_UNICAST_PERIOD < (ORCHESTRA_MAX_HASH + 1)) ? LINK_OPTION_SHARED : 0)
#else
#define UNICAST_SLOT_SHARED_FLAG LINK_OPTION_SHARED
#endif
static uint16_t slotframe_handle = 0;
static uint16_t channel_offset = 0;
static struct tsch_slotframe *sf_unicast;
/*---------------------------------------------------------------------------*/
static uint16_t
get_node_timeslot(const linkaddr_t *addr)
{
if(addr != NULL && ORCHESTRA_UNICAST_PERIOD > 0) {
return ORCHESTRA_LINKADDR_HASH(addr) % ORCHESTRA_UNICAST_PERIOD;
} else {
return 0xffff;
}
}
/*---------------------------------------------------------------------------*/
static int
neighbor_has_uc_link(const linkaddr_t *linkaddr)
{
if(linkaddr != NULL && !linkaddr_cmp(linkaddr, &linkaddr_null)) {
if((orchestra_parent_knows_us || !ORCHESTRA_UNICAST_SENDER_BASED)
&& linkaddr_cmp(&orchestra_parent_linkaddr, linkaddr)) {
return 1;
}
if(nbr_table_get_from_lladdr(nbr_routes, (linkaddr_t *)linkaddr) != NULL) {
return 1;
}
}
return 0;
}
/*---------------------------------------------------------------------------*/
static void
add_uc_link(const linkaddr_t *linkaddr)
{
if(linkaddr != NULL) {
uint16_t timeslot = get_node_timeslot(linkaddr);
tsch_schedule_add_link(sf_unicast,
ORCHESTRA_UNICAST_SENDER_BASED ? LINK_OPTION_RX : LINK_OPTION_TX | UNICAST_SLOT_SHARED_FLAG,
LINK_TYPE_NORMAL, &tsch_broadcast_address,
timeslot, channel_offset);
}
}
/*---------------------------------------------------------------------------*/
static void
remove_uc_link(const linkaddr_t *linkaddr)
{
uint16_t timeslot;
struct tsch_link *l;
if(linkaddr == NULL) {
return;
}
timeslot = get_node_timeslot(linkaddr);
l = tsch_schedule_get_link_by_timeslot(sf_unicast, timeslot);
if(l == NULL) {
return;
}
/* Does our current parent need this timeslot? */
if(timeslot == get_node_timeslot(&orchestra_parent_linkaddr)) {
/* Yes, this timeslot is being used, return */
return;
}
/* Does any other child need this timeslot?
* (lookup all route next hops) */
nbr_table_item_t *item = nbr_table_head(nbr_routes);
while(item != NULL) {
linkaddr_t *addr = nbr_table_get_lladdr(nbr_routes, item);
if(timeslot == get_node_timeslot(addr)) {
/* Yes, this timeslot is being used, return */
return;
}
item = nbr_table_next(nbr_routes, item);
}
tsch_schedule_remove_link(sf_unicast, l);
}
/*---------------------------------------------------------------------------*/
static void
child_added(const linkaddr_t *linkaddr)
{
add_uc_link(linkaddr);
}
/*---------------------------------------------------------------------------*/
static void
child_removed(const linkaddr_t *linkaddr)
{
remove_uc_link(linkaddr);
}
/*---------------------------------------------------------------------------*/
static int
select_packet(uint16_t *slotframe, uint16_t *timeslot)
{
/* Select data packets we have a unicast link to */
const linkaddr_t *dest = packetbuf_addr(PACKETBUF_ADDR_RECEIVER);
if(packetbuf_attr(PACKETBUF_ATTR_FRAME_TYPE) == FRAME802154_DATAFRAME
&& neighbor_has_uc_link(dest)) {
if(slotframe != NULL) {
*slotframe = slotframe_handle;
}
if(timeslot != NULL) {
*timeslot = ORCHESTRA_UNICAST_SENDER_BASED ? get_node_timeslot(&linkaddr_node_addr) : get_node_timeslot(dest);
}
return 1;
}
return 0;
}
/*---------------------------------------------------------------------------*/
static void
new_time_source(const struct tsch_neighbor *old, const struct tsch_neighbor *new)
{
if(new != old) {
const linkaddr_t *new_addr = new != NULL ? &new->addr : NULL;
if(new_addr != NULL) {
linkaddr_copy(&orchestra_parent_linkaddr, new_addr);
} else {
linkaddr_copy(&orchestra_parent_linkaddr, &linkaddr_null);
}
remove_uc_link(new_addr);
add_uc_link(new_addr);
}
}
/*---------------------------------------------------------------------------*/
static void
init(uint16_t sf_handle)
{
slotframe_handle = sf_handle;
channel_offset = sf_handle;
/* Slotframe for unicast transmissions */
sf_unicast = tsch_schedule_add_slotframe(slotframe_handle, ORCHESTRA_UNICAST_PERIOD);
uint16_t timeslot = get_node_timeslot(&linkaddr_node_addr);
tsch_schedule_add_link(sf_unicast,
ORCHESTRA_UNICAST_SENDER_BASED ? LINK_OPTION_TX | UNICAST_SLOT_SHARED_FLAG: LINK_OPTION_RX,
LINK_TYPE_NORMAL, &tsch_broadcast_address,
timeslot, channel_offset);
}
/*---------------------------------------------------------------------------*/
struct orchestra_rule unicast_per_neighbor = {
init,
new_time_source,
select_packet,
child_added,
child_removed,
};

166
apps/orchestra/orchestra.c Normal file
View file

@ -0,0 +1,166 @@
/*
* Copyright (c) 2015, Swedish Institute of Computer Science.
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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
* Orchestra: an autonomous scheduler for TSCH exploiting RPL state.
* See "Orchestra: Robust Mesh Networks Through Autonomously Scheduled TSCH", ACM SenSys'15
*
* \author Simon Duquennoy <simonduq@sics.se>
*/
#include "contiki.h"
#include "orchestra.h"
#include "net/packetbuf.h"
#include "net/ipv6/uip-icmp6.h"
#include "net/rpl/rpl-private.h"
#include "net/rime/rime.h" /* Needed for so-called rime-sniffer */
#define DEBUG DEBUG_PRINT
#include "net/ip/uip-debug.h"
/* A net-layer sniffer for packets sent and received */
static void orchestra_packet_received(void);
static void orchestra_packet_sent(int mac_status);
RIME_SNIFFER(orchestra_sniffer, orchestra_packet_received, orchestra_packet_sent);
/* The current RPL preferred parent's link-layer address */
linkaddr_t orchestra_parent_linkaddr;
/* Set to one only after getting an ACK for a DAO sent to our preferred parent */
int orchestra_parent_knows_us = 0;
/* The set of Orchestra rules in use */
const struct orchestra_rule *all_rules[] = ORCHESTRA_RULES;
#define NUM_RULES (sizeof(all_rules) / sizeof(struct orchestra_rule *))
/*---------------------------------------------------------------------------*/
static void
orchestra_packet_received(void)
{
}
/*---------------------------------------------------------------------------*/
static void
orchestra_packet_sent(int mac_status)
{
/* Check if our parent just ACKed a DAO */
if(orchestra_parent_knows_us == 0
&& mac_status == MAC_TX_OK
&& packetbuf_attr(PACKETBUF_ATTR_NETWORK_ID) == UIP_PROTO_ICMP6
&& packetbuf_attr(PACKETBUF_ATTR_CHANNEL) == (ICMP6_RPL << 8 | RPL_CODE_DAO)) {
if(!linkaddr_cmp(&orchestra_parent_linkaddr, &linkaddr_null)
&& linkaddr_cmp(&orchestra_parent_linkaddr, packetbuf_addr(PACKETBUF_ADDR_RECEIVER))) {
orchestra_parent_knows_us = 1;
}
}
}
/*---------------------------------------------------------------------------*/
void
orchestra_callback_child_added(const linkaddr_t *addr)
{
/* Notify all Orchestra rules that a child was added */
int i;
for(i = 0; i < NUM_RULES; i++) {
if(all_rules[i]->child_added != NULL) {
all_rules[i]->child_added(addr);
}
}
}
/*---------------------------------------------------------------------------*/
void
orchestra_callback_child_removed(const linkaddr_t *addr)
{
/* Notify all Orchestra rules that a child was removed */
int i;
for(i = 0; i < NUM_RULES; i++) {
if(all_rules[i]->child_removed != NULL) {
all_rules[i]->child_removed(addr);
}
}
}
/*---------------------------------------------------------------------------*/
void
orchestra_callback_packet_ready(void)
{
int i;
/* By default, use any slotframe, any timeslot */
uint16_t slotframe = 9;
uint16_t timeslot = 0xffff;
/* Loop over all rules until finding one able to handle the packet */
for(i = 0; i < NUM_RULES; i++) {
if(all_rules[i]->select_packet != NULL) {
if(all_rules[i]->select_packet(&slotframe, &timeslot)) {
break;
}
}
}
#if TSCH_WITH_LINK_SELECTOR
packetbuf_set_attr(PACKETBUF_ATTR_TSCH_SLOTFRAME, slotframe);
packetbuf_set_attr(PACKETBUF_ATTR_TSCH_TIMESLOT, timeslot);
#endif
}
/*---------------------------------------------------------------------------*/
void
orchestra_callback_new_time_source(const struct tsch_neighbor *old, const struct tsch_neighbor *new)
{
/* Orchestra assumes that the time source is also the RPL parent.
* This is the case if the following is set:
* #define RPL_CALLBACK_PARENT_SWITCH tsch_rpl_callback_parent_switch
* */
int i;
if(new != old) {
orchestra_parent_knows_us = 0;
}
for(i = 0; i < NUM_RULES; i++) {
if(all_rules[i]->new_time_source != NULL) {
all_rules[i]->new_time_source(old, new);
}
}
}
/*---------------------------------------------------------------------------*/
void
orchestra_init(void)
{
int i;
/* Snoop on packet transmission to know if our parent knows about us
* (i.e. has ACKed at one of our DAOs since we decided to use it as a parent) */
rime_sniffer_add(&orchestra_sniffer);
linkaddr_copy(&orchestra_parent_linkaddr, &linkaddr_null);
/* Initialize all Orchestra rules */
for(i = 0; i < NUM_RULES; i++) {
if(all_rules[i]->init != NULL) {
PRINTF("Orchestra: initializing rule %u\n", i);
all_rules[i]->init(i);
}
}
PRINTF("Orchestra: initialization done\n");
}

View file

@ -0,0 +1,74 @@
/*
* Copyright (c) 2015, Swedish Institute of Computer Science.
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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
* Orchestra header file
*
* \author Simon Duquennoy <simonduq@sics.se>
*/
#ifndef __ORCHESTRA_H__
#define __ORCHESTRA_H__
#include "net/mac/tsch/tsch.h"
#include "net/mac/tsch/tsch-conf.h"
#include "net/mac/tsch/tsch-schedule.h"
#include "orchestra-conf.h"
/* The structure of an Orchestra rule */
struct orchestra_rule {
void (* init)(uint16_t slotframe_handle);
void (* new_time_source)(const struct tsch_neighbor *old, const struct tsch_neighbor *new);
int (* select_packet)(uint16_t *slotframe, uint16_t *timeslot);
void (* child_added)(const linkaddr_t *addr);
void (* child_removed)(const linkaddr_t *addr);
};
struct orchestra_rule eb_per_time_source;
struct orchestra_rule unicast_per_neighbor;
struct orchestra_rule default_common;
extern linkaddr_t orchestra_parent_linkaddr;
extern int orchestra_parent_knows_us;
/* Call from application to start Orchestra */
void orchestra_init(void);
/* Callbacks requied for Orchestra to operate */
/* Set with #define TSCH_CALLBACK_PACKET_READY orchestra_callback_packet_ready */
void orchestra_callback_packet_ready(void);
/* Set with #define TSCH_CALLBACK_NEW_TIME_SOURCE orchestra_callback_new_time_source */
void orchestra_callback_new_time_source(const struct tsch_neighbor *old, const struct tsch_neighbor *new);
/* Set with #define NETSTACK_CONF_ROUTING_NEIGHBOR_ADDED_CALLBACK orchestra_callback_child_added */
void orchestra_callback_child_added(const linkaddr_t *addr);
/* Set with #define NETSTACK_CONF_ROUTING_NEIGHBOR_REMOVED_CALLBACK orchestra_callback_child_removed */
void orchestra_callback_child_removed(const linkaddr_t *addr);
#endif /* __ORCHESTRA_H__ */

View file

@ -150,6 +150,12 @@ enum {
/* Received signal strength indicator in dBm. */ /* Received signal strength indicator in dBm. */
RADIO_PARAM_RSSI, RADIO_PARAM_RSSI,
/* RSSI of the last received packet */
RADIO_PARAM_LAST_RSSI,
/* Link quality of the last received packet */
RADIO_PARAM_LAST_LINK_QUALITY,
/* /*
* Long (64 bits) address for the radio, which is used by the address filter. * Long (64 bits) address for the radio, which is used by the address filter.
* The address is specified in network byte order. * The address is specified in network byte order.
@ -159,6 +165,11 @@ enum {
*/ */
RADIO_PARAM_64BIT_ADDR, RADIO_PARAM_64BIT_ADDR,
/* Last packet timestamp, of type rtimer_clock_t.
* Because this parameter value mat be larger than what fits in radio_value_t,
* it needs to be used with radio.get_object()/set_object(). */
RADIO_PARAM_LAST_PACKET_TIMESTAMP,
/* Constants (read only) */ /* Constants (read only) */
/* The lowest radio channel. */ /* The lowest radio channel. */
@ -192,6 +203,7 @@ enum {
*/ */
#define RADIO_RX_MODE_ADDRESS_FILTER (1 << 0) #define RADIO_RX_MODE_ADDRESS_FILTER (1 << 0)
#define RADIO_RX_MODE_AUTOACK (1 << 1) #define RADIO_RX_MODE_AUTOACK (1 << 1)
#define RADIO_RX_MODE_POLL_MODE (1 << 2)
/** /**
* The radio transmission mode controls whether transmissions should * The radio transmission mode controls whether transmissions should

View file

@ -47,12 +47,22 @@
#include <string.h> #include <string.h>
/* A configurable function called after adding a new neighbor as next hop */
#ifdef NETSTACK_CONF_ROUTING_NEIGHBOR_ADDED_CALLBACK
void NETSTACK_CONF_ROUTING_NEIGHBOR_ADDED_CALLBACK(const linkaddr_t *addr);
#endif /* NETSTACK_CONF_ROUTING_NEIGHBOR_ADDED_CALLBACK */
/* A configurable function called after removing a next hop neighbor */
#ifdef NETSTACK_CONF_ROUTING_NEIGHBOR_REMOVED_CALLBACK
void NETSTACK_CONF_ROUTING_NEIGHBOR_REMOVED_CALLBACK(const linkaddr_t *addr);
#endif /* NETSTACK_CONF_ROUTING_NEIGHBOR_REMOVED_CALLBACK */
/* The nbr_routes holds a neighbor table to be able to maintain /* The nbr_routes holds a neighbor table to be able to maintain
information about what routes go through what neighbor. This information about what routes go through what neighbor. This
neighbor table is registered with the central nbr-table repository neighbor table is registered with the central nbr-table repository
so that it will be maintained along with the rest of the neighbor so that it will be maintained along with the rest of the neighbor
tables in the system. */ tables in the system. */
NBR_TABLE(struct uip_ds6_route_neighbor_routes, nbr_routes); NBR_TABLE_GLOBAL(struct uip_ds6_route_neighbor_routes, nbr_routes);
MEMB(neighborroutememb, struct uip_ds6_route_neighbor_route, UIP_DS6_ROUTE_NB); MEMB(neighborroutememb, struct uip_ds6_route_neighbor_route, UIP_DS6_ROUTE_NB);
/* Each route is repressented by a uip_ds6_route_t structure and /* Each route is repressented by a uip_ds6_route_t structure and
@ -335,6 +345,9 @@ uip_ds6_route_add(uip_ipaddr_t *ipaddr, uint8_t length,
return NULL; return NULL;
} }
LIST_STRUCT_INIT(routes, route_list); LIST_STRUCT_INIT(routes, route_list);
#ifdef NETSTACK_CONF_ROUTING_NEIGHBOR_ADDED_CALLBACK
NETSTACK_CONF_ROUTING_NEIGHBOR_ADDED_CALLBACK((const linkaddr_t *)nexthop_lladdr);
#endif
} }
/* Allocate a routing entry and populate it. */ /* Allocate a routing entry and populate it. */
@ -435,6 +448,10 @@ uip_ds6_route_rm(uip_ds6_route_t *route)
#endif /* (DEBUG) & DEBUG_ANNOTATE */ #endif /* (DEBUG) & DEBUG_ANNOTATE */
PRINTF("uip_ds6_route_rm: removing neighbor too\n"); PRINTF("uip_ds6_route_rm: removing neighbor too\n");
nbr_table_remove(nbr_routes, route->neighbor_routes->route_list); nbr_table_remove(nbr_routes, route->neighbor_routes->route_list);
#ifdef NETSTACK_CONF_ROUTING_NEIGHBOR_REMOVED_CALLBACK
NETSTACK_CONF_ROUTING_NEIGHBOR_REMOVED_CALLBACK(
(const linkaddr_t *)nbr_table_get_lladdr(nbr_routes, route->neighbor_routes->route_list));
#endif
} }
memb_free(&routememb, route); memb_free(&routememb, route);
memb_free(&neighborroutememb, neighbor_route); memb_free(&neighborroutememb, neighbor_route);

View file

@ -40,9 +40,13 @@
#ifndef UIP_DS6_ROUTE_H #ifndef UIP_DS6_ROUTE_H
#define UIP_DS6_ROUTE_H #define UIP_DS6_ROUTE_H
#include "net/ip/uip.h"
#include "net/nbr-table.h"
#include "sys/stimer.h" #include "sys/stimer.h"
#include "lib/list.h" #include "lib/list.h"
NBR_TABLE_DECLARE(nbr_routes);
void uip_ds6_route_init(void); void uip_ds6_route_init(void);
#ifndef UIP_CONF_UIP_DS6_NOTIFICATIONS #ifndef UIP_CONF_UIP_DS6_NOTIFICATIONS

View file

@ -85,6 +85,12 @@
#define LLSEC802154_USES_EXPLICIT_KEYS 0 #define LLSEC802154_USES_EXPLICIT_KEYS 0
#endif /* LLSEC802154_CONF_USES_EXPLICIT_KEYS */ #endif /* LLSEC802154_CONF_USES_EXPLICIT_KEYS */
#ifdef LLSEC802154_CONF_USES_FRAME_COUNTER
#define LLSEC802154_USES_FRAME_COUNTER LLSEC802154_CONF_USES_FRAME_COUNTER
#else /* LLSEC802154_CONF_USES_FRAME_COUNTER */
#define LLSEC802154_USES_FRAME_COUNTER (LLSEC802154_SECURITY_LEVEL != FRAME802154_SECURITY_LEVEL_NONE)
#endif /* LLSEC802154_CONF_USES_FRAME_COUNTER */
#if UIP_BYTE_ORDER == UIP_LITTLE_ENDIAN #if UIP_BYTE_ORDER == UIP_LITTLE_ENDIAN
#define LLSEC802154_HTONS(n) (n) #define LLSEC802154_HTONS(n) (n)
#define LLSEC802154_HTONL(n) (n) #define LLSEC802154_HTONL(n) (n)

View file

@ -44,11 +44,11 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
* *
*/ */
/* /*
* \brief This file is where the main functions that relate to frame * \brief This file is where the main functions that relate to frame
* manipulation will reside. * manipulation will reside.
*/ */
/** /**
* \file * \file
@ -61,7 +61,7 @@
/** /**
* \addtogroup frame802154 * \addtogroup frame802154
* @{ * @{
*/ */
#include "sys/cc.h" #include "sys/cc.h"
#include "net/mac/frame802154.h" #include "net/mac/frame802154.h"
@ -69,11 +69,18 @@
#include "net/linkaddr.h" #include "net/linkaddr.h"
#include <string.h> #include <string.h>
/** \brief The 16-bit identifier of the PAN on which the device is
* operating. If this value is 0xffff, the device is not
* associated.
*/
static uint16_t mac_pan_id = IEEE802154_PANID;
/** /**
* \brief Structure that contains the lengths of the various addressing and security fields * \brief Structure that contains the lengths of the various addressing and security fields
* in the 802.15.4 header. This structure is used in \ref frame802154_create() * in the 802.15.4 header. This structure is used in \ref frame802154_create()
*/ */
typedef struct { typedef struct {
uint8_t seqno_len; /**< Length (in bytes) of sequence number field */
uint8_t dest_pid_len; /**< Length (in bytes) of destination PAN ID field */ uint8_t dest_pid_len; /**< Length (in bytes) of destination PAN ID field */
uint8_t dest_addr_len; /**< Length (in bytes) of destination address field */ uint8_t dest_addr_len; /**< Length (in bytes) of destination address field */
uint8_t src_pid_len; /**< Length (in bytes) of source PAN ID field */ uint8_t src_pid_len; /**< Length (in bytes) of source PAN ID field */
@ -111,30 +118,199 @@ get_key_id_len(uint8_t key_id_mode)
} }
} }
#endif /* LLSEC802154_USES_EXPLICIT_KEYS */ #endif /* LLSEC802154_USES_EXPLICIT_KEYS */
/*---------------------------------------------------------------------------*/
/* Get current PAN ID */
uint16_t
frame802154_get_pan_id(void)
{
return mac_pan_id;
}
/*---------------------------------------------------------------------------*/
/* Set current PAN ID */
void
frame802154_set_pan_id(uint16_t pan_id)
{
mac_pan_id = pan_id;
}
/*----------------------------------------------------------------------------*/
/* Tells whether a given Frame Control Field indicates a frame with
* source PANID and/or destination PANID */
void
frame802154_has_panid(frame802154_fcf_t *fcf, int *has_src_pan_id, int *has_dest_pan_id)
{
int src_pan_id = 0;
int dest_pan_id = 0;
if(fcf == NULL) {
return;
}
if(fcf->frame_version == FRAME802154_IEEE802154E_2012) {
if(!fcf->panid_compression) {
/* Compressed PAN ID == no PAN ID at all */
if(fcf->dest_addr_mode == fcf->dest_addr_mode) {
/* No address or both addresses: include destination PAN ID */
dest_pan_id = 1;
} else if(fcf->dest_addr_mode) {
/* Only dest address, include dest PAN ID */
dest_pan_id = 1;
} else if(fcf->src_addr_mode) {
/* Only src address, include src PAN ID */
src_pan_id = 1;
}
}
if(fcf->dest_addr_mode == 0 && fcf->dest_addr_mode == 1) {
/* No address included, include dest PAN ID conditionally */
if(!fcf->panid_compression) {
dest_pan_id = 1;
}
}
/* Remove the following rule the day rows 2 and 3 from table 2a are fixed: */
if(fcf->dest_addr_mode == 0 && fcf->dest_addr_mode == 0) {
/* Not meaningful, we include a PAN ID iff the compress flag is set, but
* this is what the standard currently stipulates */
dest_pan_id = fcf->panid_compression;
}
} else {
/* No PAN ID in ACK */
if(fcf->frame_type != FRAME802154_ACKFRAME) {
if(!fcf->panid_compression && fcf->src_addr_mode & 3) {
/* If compressed, don't inclue source PAN ID */
src_pan_id = 1;
}
if(fcf->dest_addr_mode & 3) {
dest_pan_id = 1;
}
}
}
if(has_src_pan_id != NULL) {
*has_src_pan_id = src_pan_id;
}
if(has_dest_pan_id != NULL) {
*has_dest_pan_id = dest_pan_id;
}
}
/*---------------------------------------------------------------------------*/
/* Check if the destination PAN ID, if any, matches ours */
int
frame802154_check_dest_panid(frame802154_t *frame)
{
int has_dest_panid;
if(frame == NULL) {
return 0;
}
frame802154_has_panid(&frame->fcf, NULL, &has_dest_panid);
if(has_dest_panid
&& frame->dest_pid != frame802154_get_pan_id()
&& frame->dest_pid != FRAME802154_BROADCASTPANDID) {
/* Packet to another PAN */
return 0;
}
return 1;
}
/*---------------------------------------------------------------------------*/
/* Check is the address is a broadcast address, whatever its size */
int
frame802154_is_broadcast_addr(uint8_t mode, uint8_t *addr)
{
int i = mode == FRAME802154_SHORTADDRMODE ? 2 : 8;
while(i-- > 0) {
if(addr[i] != 0xff) {
return 0;
}
}
return 1;
}
/*---------------------------------------------------------------------------*/
/* Check and extract source and destination linkaddr from frame */
int
frame802154_extract_linkaddr(frame802154_t *frame,
linkaddr_t *source_address, linkaddr_t *dest_address)
{
int src_addr_len;
int dest_addr_len;
if(frame == NULL) {
return 0;
}
/* Check and extract source address */
src_addr_len = frame->fcf.src_addr_mode ?
((frame->fcf.src_addr_mode == FRAME802154_SHORTADDRMODE) ? 2 : 8) : 0;
if(src_addr_len == 0 || frame802154_is_broadcast_addr(frame->fcf.src_addr_mode, frame->src_addr)) {
/* Broadcast address */
if(source_address != NULL) {
linkaddr_copy(source_address, &linkaddr_null);
}
} else {
/* Unicast address */
if(src_addr_len != LINKADDR_SIZE) {
/* Destination address has a size we can not handle */
return 0;
}
if(source_address != NULL) {
linkaddr_copy(source_address, (linkaddr_t *)frame->src_addr);
}
}
/* Check and extract destination address */
dest_addr_len = frame->fcf.dest_addr_mode ?
((frame->fcf.dest_addr_mode == FRAME802154_SHORTADDRMODE) ? 2 : 8) : 0;
if(dest_addr_len == 0 || frame802154_is_broadcast_addr(frame->fcf.dest_addr_mode, frame->dest_addr)) {
/* Broadcast address */
if(dest_address != NULL) {
linkaddr_copy(dest_address, &linkaddr_null);
}
} else {
/* Unicast address */
if(dest_addr_len != LINKADDR_SIZE) {
/* Destination address has a size we can not handle */
return 0;
}
if(dest_address != NULL) {
linkaddr_copy(dest_address, (linkaddr_t *)frame->dest_addr);
}
}
return 1;
}
/*----------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------*/
static void static void
field_len(frame802154_t *p, field_length_t *flen) field_len(frame802154_t *p, field_length_t *flen)
{ {
int has_src_panid;
int has_dest_panid;
/* init flen to zeros */ /* init flen to zeros */
memset(flen, 0, sizeof(field_length_t)); memset(flen, 0, sizeof(field_length_t));
/* Determine lengths of each field based on fcf and other args */ /* Determine lengths of each field based on fcf and other args */
if(p->fcf.dest_addr_mode & 3) { if((p->fcf.sequence_number_suppression & 1) == 0) {
flen->dest_pid_len = 2; flen->seqno_len = 1;
} }
if(p->fcf.src_addr_mode & 3) {
/* IEEE802.15.4e changes the meaning of PAN ID Compression (see Table 2a).
* In this case, we leave the decision whether to compress PAN ID or not
* up to the caller. */
if(p->fcf.frame_version < FRAME802154_IEEE802154E_2012) {
/* Set PAN ID compression bit if src pan id matches dest pan id. */
if(p->fcf.dest_addr_mode & 3 && p->fcf.src_addr_mode & 3 &&
p->src_pid == p->dest_pid) {
p->fcf.panid_compression = 1;
} else {
p->fcf.panid_compression = 0;
}
}
frame802154_has_panid(&p->fcf, &has_src_panid, &has_dest_panid);
if(has_src_panid) {
flen->src_pid_len = 2; flen->src_pid_len = 2;
} }
/* Set PAN ID compression bit if src pan id matches dest pan id. */ if(has_dest_panid) {
if(p->fcf.dest_addr_mode & 3 && p->fcf.src_addr_mode & 3 && flen->dest_pid_len = 2;
p->src_pid == p->dest_pid) {
p->fcf.panid_compression = 1;
/* compressed header, only do dest pid */
flen->src_pid_len = 0;
} else {
p->fcf.panid_compression = 0;
} }
/* determine address lengths */ /* determine address lengths */
@ -144,9 +320,16 @@ field_len(frame802154_t *p, field_length_t *flen)
#if LLSEC802154_SECURITY_LEVEL #if LLSEC802154_SECURITY_LEVEL
/* Aux security header */ /* Aux security header */
if(p->fcf.security_enabled & 1) { if(p->fcf.security_enabled & 1) {
flen->aux_sec_len = 5 flen->aux_sec_len = 1; /* FCF + possibly frame counter and key ID */
if(p->aux_hdr.security_control.frame_counter_suppression == 0) {
if(p->aux_hdr.security_control.frame_counter_size == 1) {
flen->aux_sec_len += 5;
} else {
flen->aux_sec_len += 4;
}
}
#if LLSEC802154_USES_EXPLICIT_KEYS #if LLSEC802154_USES_EXPLICIT_KEYS
+ get_key_id_len(p->aux_hdr.security_control.key_id_mode); flen->aux_sec_len += get_key_id_len(p->aux_hdr.security_control.key_id_mode);
#endif /* LLSEC802154_USES_EXPLICIT_KEYS */ #endif /* LLSEC802154_USES_EXPLICIT_KEYS */
; ;
} }
@ -161,14 +344,14 @@ field_len(frame802154_t *p, field_length_t *flen)
* frame to send. * frame to send.
* *
* \return The length of the frame header. * \return The length of the frame header.
*/ */
int int
frame802154_hdrlen(frame802154_t *p) frame802154_hdrlen(frame802154_t *p)
{ {
field_length_t flen; field_length_t flen;
field_len(p, &flen); field_len(p, &flen);
return 3 + flen.dest_pid_len + flen.dest_addr_len + return 2 + flen.seqno_len + flen.dest_pid_len + flen.dest_addr_len +
flen.src_pid_len + flen.src_addr_len + flen.aux_sec_len; flen.src_pid_len + flen.src_addr_len + flen.aux_sec_len;
} }
/*----------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------*/
/** /**
@ -181,7 +364,7 @@ frame802154_hdrlen(frame802154_t *p)
* \param buf Pointer to the buffer to use for the frame. * \param buf Pointer to the buffer to use for the frame.
* *
* \return The length of the frame header * \return The length of the frame header
*/ */
int int
frame802154_create(frame802154_t *p, uint8_t *buf) frame802154_create(frame802154_t *p, uint8_t *buf)
{ {
@ -193,7 +376,7 @@ frame802154_create(frame802154_t *p, uint8_t *buf)
#endif /* LLSEC802154_USES_EXPLICIT_KEYS */ #endif /* LLSEC802154_USES_EXPLICIT_KEYS */
field_len(p, &flen); field_len(p, &flen);
/* OK, now we have field lengths. Time to actually construct */ /* OK, now we have field lengths. Time to actually construct */
/* the outgoing frame, and store it in buf */ /* the outgoing frame, and store it in buf */
buf[0] = (p->fcf.frame_type & 7) | buf[0] = (p->fcf.frame_type & 7) |
@ -201,13 +384,18 @@ frame802154_create(frame802154_t *p, uint8_t *buf)
((p->fcf.frame_pending & 1) << 4) | ((p->fcf.frame_pending & 1) << 4) |
((p->fcf.ack_required & 1) << 5) | ((p->fcf.ack_required & 1) << 5) |
((p->fcf.panid_compression & 1) << 6); ((p->fcf.panid_compression & 1) << 6);
buf[1] = ((p->fcf.dest_addr_mode & 3) << 2) | buf[1] = ((p->fcf.sequence_number_suppression & 1)) |
((p->fcf.ie_list_present & 1)) << 1 |
((p->fcf.dest_addr_mode & 3) << 2) |
((p->fcf.frame_version & 3) << 4) | ((p->fcf.frame_version & 3) << 4) |
((p->fcf.src_addr_mode & 3) << 6); ((p->fcf.src_addr_mode & 3) << 6);
/* sequence number */ pos = 2;
buf[2] = p->seq;
pos = 3; /* Sequence number */
if(flen.seqno_len == 1) {
buf[pos++] = p->seq;
}
/* Destination PAN ID */ /* Destination PAN ID */
if(flen.dest_pid_len == 2) { if(flen.dest_pid_len == 2) {
@ -230,17 +418,24 @@ frame802154_create(frame802154_t *p, uint8_t *buf)
for(c = flen.src_addr_len; c > 0; c--) { for(c = flen.src_addr_len; c > 0; c--) {
buf[pos++] = p->src_addr[c - 1]; buf[pos++] = p->src_addr[c - 1];
} }
#if LLSEC802154_SECURITY_LEVEL #if LLSEC802154_SECURITY_LEVEL
/* Aux header */ /* Aux header */
if(flen.aux_sec_len) { if(flen.aux_sec_len) {
buf[pos++] = p->aux_hdr.security_control.security_level buf[pos++] = p->aux_hdr.security_control.security_level
#if LLSEC802154_USES_EXPLICIT_KEYS #if LLSEC802154_USES_EXPLICIT_KEYS
| (p->aux_hdr.security_control.key_id_mode << 3) | (p->aux_hdr.security_control.key_id_mode << 3)
#endif /* LLSEC802154_USES_EXPLICIT_KEYS */ #endif /* LLSEC802154_USES_EXPLICIT_KEYS */
| (p->aux_hdr.security_control.frame_counter_suppression << 5)
| (p->aux_hdr.security_control.frame_counter_size << 6)
; ;
memcpy(buf + pos, p->aux_hdr.frame_counter.u8, 4); if(p->aux_hdr.security_control.frame_counter_suppression == 0) {
pos += 4; /* We support only 4-byte counters */
memcpy(buf + pos, p->aux_hdr.frame_counter.u8, 4);
pos += 4;
if(p->aux_hdr.security_control.frame_counter_size == 1) {
pos++;
}
}
#if LLSEC802154_USES_EXPLICIT_KEYS #if LLSEC802154_USES_EXPLICIT_KEYS
key_id_mode = p->aux_hdr.security_control.key_id_mode; key_id_mode = p->aux_hdr.security_control.key_id_mode;
@ -272,11 +467,13 @@ frame802154_parse(uint8_t *data, int len, frame802154_t *pf)
uint8_t *p; uint8_t *p;
frame802154_fcf_t fcf; frame802154_fcf_t fcf;
int c; int c;
int has_src_panid;
int has_dest_panid;
#if LLSEC802154_USES_EXPLICIT_KEYS #if LLSEC802154_USES_EXPLICIT_KEYS
uint8_t key_id_mode; uint8_t key_id_mode;
#endif /* LLSEC802154_USES_EXPLICIT_KEYS */ #endif /* LLSEC802154_USES_EXPLICIT_KEYS */
if(len < 3) { if(len < 2) {
return 0; return 0;
} }
@ -289,20 +486,32 @@ frame802154_parse(uint8_t *data, int len, frame802154_t *pf)
fcf.ack_required = (p[0] >> 5) & 1; fcf.ack_required = (p[0] >> 5) & 1;
fcf.panid_compression = (p[0] >> 6) & 1; fcf.panid_compression = (p[0] >> 6) & 1;
fcf.sequence_number_suppression = p[1] & 1;
fcf.ie_list_present = (p[1] >> 1) & 1;
fcf.dest_addr_mode = (p[1] >> 2) & 3; fcf.dest_addr_mode = (p[1] >> 2) & 3;
fcf.frame_version = (p[1] >> 4) & 3; fcf.frame_version = (p[1] >> 4) & 3;
fcf.src_addr_mode = (p[1] >> 6) & 3; fcf.src_addr_mode = (p[1] >> 6) & 3;
/* copy fcf and seqNum */ /* copy fcf and seqNum */
memcpy(&pf->fcf, &fcf, sizeof(frame802154_fcf_t)); memcpy(&pf->fcf, &fcf, sizeof(frame802154_fcf_t));
pf->seq = p[2]; p += 2; /* Skip first two bytes */
p += 3; /* Skip first three bytes */
if(fcf.sequence_number_suppression == 0) {
pf->seq = p[0];
p++;
}
frame802154_has_panid(&fcf, &has_src_panid, &has_dest_panid);
/* Destination address, if any */ /* Destination address, if any */
if(fcf.dest_addr_mode) { if(fcf.dest_addr_mode) {
/* Destination PAN */ if(has_dest_panid) {
pf->dest_pid = p[0] + (p[1] << 8); /* Destination PAN */
p += 2; pf->dest_pid = p[0] + (p[1] << 8);
p += 2;
} else {
pf->dest_pid = 0;
}
/* Destination address */ /* Destination address */
/* l = addr_len(fcf.dest_addr_mode); */ /* l = addr_len(fcf.dest_addr_mode); */
@ -329,9 +538,12 @@ frame802154_parse(uint8_t *data, int len, frame802154_t *pf)
/* Source address, if any */ /* Source address, if any */
if(fcf.src_addr_mode) { if(fcf.src_addr_mode) {
/* Source PAN */ /* Source PAN */
if(!fcf.panid_compression) { if(has_src_panid) {
pf->src_pid = p[0] + (p[1] << 8); pf->src_pid = p[0] + (p[1] << 8);
p += 2; p += 2;
if(!has_dest_panid) {
pf->dest_pid = pf->src_pid;
}
} else { } else {
pf->src_pid = pf->dest_pid; pf->src_pid = pf->dest_pid;
} }
@ -357,18 +569,25 @@ frame802154_parse(uint8_t *data, int len, frame802154_t *pf)
linkaddr_copy((linkaddr_t *)&(pf->src_addr), &linkaddr_null); linkaddr_copy((linkaddr_t *)&(pf->src_addr), &linkaddr_null);
pf->src_pid = 0; pf->src_pid = 0;
} }
#if LLSEC802154_SECURITY_LEVEL #if LLSEC802154_SECURITY_LEVEL
if(fcf.security_enabled) { if(fcf.security_enabled) {
pf->aux_hdr.security_control.security_level = p[0] & 7; pf->aux_hdr.security_control.security_level = p[0] & 7;
#if LLSEC802154_USES_EXPLICIT_KEYS #if LLSEC802154_USES_EXPLICIT_KEYS
pf->aux_hdr.security_control.key_id_mode = (p[0] >> 3) & 3; pf->aux_hdr.security_control.key_id_mode = (p[0] >> 3) & 3;
#endif /* LLSEC802154_USES_EXPLICIT_KEYS */ #endif /* LLSEC802154_USES_EXPLICIT_KEYS */
pf->aux_hdr.security_control.frame_counter_suppression = p[0] >> 5;
pf->aux_hdr.security_control.frame_counter_size = p[0] >> 6;
p += 1; p += 1;
memcpy(pf->aux_hdr.frame_counter.u8, p, 4); if(pf->aux_hdr.security_control.frame_counter_suppression == 0) {
p += 4; memcpy(pf->aux_hdr.frame_counter.u8, p, 4);
p += 4;
if(pf->aux_hdr.security_control.frame_counter_size == 1) {
p ++;
}
}
#if LLSEC802154_USES_EXPLICIT_KEYS #if LLSEC802154_USES_EXPLICIT_KEYS
key_id_mode = pf->aux_hdr.security_control.key_id_mode; key_id_mode = pf->aux_hdr.security_control.key_id_mode;
if(key_id_mode) { if(key_id_mode) {

View file

@ -41,7 +41,7 @@
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * 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 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
*/ */
/** /**
* \addtogroup net * \addtogroup net
@ -59,14 +59,14 @@
* This file converts to and from a structure to a packed 802.15.4 * This file converts to and from a structure to a packed 802.15.4
* frame. * frame.
* *
*/ */
/* Includes */ /* Includes */
#ifndef FRAME_802154_H #ifndef FRAME_802154_H
#define FRAME_802154_H #define FRAME_802154_H
#include "contiki-conf.h" #include "contiki-conf.h"
#include "net/linkaddr.h"
#ifdef IEEE802154_CONF_PANID #ifdef IEEE802154_CONF_PANID
#define IEEE802154_PANID IEEE802154_CONF_PANID #define IEEE802154_PANID IEEE802154_CONF_PANID
@ -74,6 +74,18 @@
#define IEEE802154_PANID 0xABCD #define IEEE802154_PANID 0xABCD
#endif /* IEEE802154_CONF_PANID */ #endif /* IEEE802154_CONF_PANID */
#ifdef FRAME802154_CONF_VERSION
#define FRAME802154_VERSION FRAME802154_CONF_VERSION
#else /* FRAME802154_CONF_VERSION */
#define FRAME802154_VERSION FRAME802154_IEEE802154_2006
#endif /* FRAME802154_CONF_VERSION */
#ifdef FRAME802154_CONF_SUPPR_SEQNO
#define FRAME802154_SUPPR_SEQNO FRAME802154_CONF_SUPPR_SEQNO
#else /* FRAME802154_CONF_SUPPR_SEQNO */
#define FRAME802154_SUPPR_SEQNO 0
#endif /* FRAME802154_CONF_SUPPR_SEQNO */
/* Macros & Defines */ /* Macros & Defines */
/** \brief These are some definitions of values used in the FCF. See the 802.15.4 spec for details. /** \brief These are some definitions of values used in the FCF. See the 802.15.4 spec for details.
@ -97,8 +109,9 @@
#define FRAME802154_BROADCASTADDR (0xFFFF) #define FRAME802154_BROADCASTADDR (0xFFFF)
#define FRAME802154_BROADCASTPANDID (0xFFFF) #define FRAME802154_BROADCASTPANDID (0xFFFF)
#define FRAME802154_IEEE802154_2003 (0x00) #define FRAME802154_IEEE802154_2003 (0x00)
#define FRAME802154_IEEE802154_2006 (0x01) #define FRAME802154_IEEE802154_2006 (0x01)
#define FRAME802154_IEEE802154E_2012 (0x02)
#define FRAME802154_SECURITY_LEVEL_NONE (0) #define FRAME802154_SECURITY_LEVEL_NONE (0)
#define FRAME802154_SECURITY_LEVEL_MIC_32 (1) #define FRAME802154_SECURITY_LEVEL_MIC_32 (1)
@ -125,7 +138,7 @@
* 3. Addressing fields - 4 - 20 bytes - Variable * 3. Addressing fields - 4 - 20 bytes - Variable
* 4. Aux security header - 0 - 14 bytes - Variable * 4. Aux security header - 0 - 14 bytes - Variable
* 5. CRC - 2 bytes - Fixed * 5. CRC - 2 bytes - Fixed
*/ */
/** /**
* \brief Defines the bitfields of the frame control field (FCF). * \brief Defines the bitfields of the frame control field (FCF).
@ -136,7 +149,9 @@ typedef struct {
uint8_t frame_pending; /**< 1 bit. True if sender has more data to send */ uint8_t frame_pending; /**< 1 bit. True if sender has more data to send */
uint8_t ack_required; /**< 1 bit. Is an ack frame required? */ uint8_t ack_required; /**< 1 bit. Is an ack frame required? */
uint8_t panid_compression; /**< 1 bit. Is this a compressed header? */ uint8_t panid_compression; /**< 1 bit. Is this a compressed header? */
/* uint8_t reserved; */ /**< 3 bit. Unused bits */ /* uint8_t reserved; */ /**< 1 bit. Unused bit */
uint8_t sequence_number_suppression; /**< 1 bit. Does the header omit sequence number?, see 802.15.4e */
uint8_t ie_list_present; /**< 1 bit. Does the header contain Information Elements?, see 802.15.4e */
uint8_t dest_addr_mode; /**< 2 bit. Destination address mode, see 802.15.4 */ uint8_t dest_addr_mode; /**< 2 bit. Destination address mode, see 802.15.4 */
uint8_t frame_version; /**< 2 bit. 802.15.4 frame version */ uint8_t frame_version; /**< 2 bit. 802.15.4 frame version */
uint8_t src_addr_mode; /**< 2 bit. Source address mode, see 802.15.4 */ uint8_t src_addr_mode; /**< 2 bit. Source address mode, see 802.15.4 */
@ -146,6 +161,8 @@ typedef struct {
typedef struct { typedef struct {
uint8_t security_level; /**< 3 bit. security level */ uint8_t security_level; /**< 3 bit. security level */
uint8_t key_id_mode; /**< 2 bit. Key identifier mode */ uint8_t key_id_mode; /**< 2 bit. Key identifier mode */
uint8_t frame_counter_suppression; /**< 1 bit. Frame counter suppression */
uint8_t frame_counter_size; /**< 1 bit. Frame counter size (0: 4 bytes, 1: 5 bytes) */
uint8_t reserved; /**< 3 bit. Reserved bits */ uint8_t reserved; /**< 3 bit. Reserved bits */
} frame802154_scf_t; } frame802154_scf_t;
@ -193,6 +210,20 @@ int frame802154_hdrlen(frame802154_t *p);
int frame802154_create(frame802154_t *p, uint8_t *buf); int frame802154_create(frame802154_t *p, uint8_t *buf);
int frame802154_parse(uint8_t *data, int length, frame802154_t *pf); int frame802154_parse(uint8_t *data, int length, frame802154_t *pf);
/* Get current PAN ID */
uint16_t frame802154_get_pan_id(void);
/* Set current PAN ID */
void frame802154_set_pan_id(uint16_t pan_id);
/* Tells whether a given Frame Control Field indicates a frame with
* source PANID and/or destination PANID */
void frame802154_has_panid(frame802154_fcf_t *fcf, int *has_src_pan_id, int *has_dest_pan_id);
/* Check if the destination PAN ID, if any, matches ours */
int frame802154_check_dest_panid(frame802154_t *frame);
/* Check is the address is a broadcast address, whatever its size */
int frame802154_is_broadcast_addr(uint8_t mode, uint8_t *addr);
/* Check and extract source and destination linkaddr from frame */
int frame802154_extract_linkaddr(frame802154_t *frame, linkaddr_t *source_address, linkaddr_t *dest_address);
/** @} */ /** @} */
#endif /* FRAME_802154_H */ #endif /* FRAME_802154_H */
/** @} */ /** @} */

View file

@ -0,0 +1,585 @@
/*
* Copyright (c) 2015, SICS Swedish ICT.
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*
*/
/**
* \file
* IEEE 802.15.4e Information Element (IE) creation and parsing.
* \author
* Simon Duquennoy <simonduq@sics.se>
*/
#include <string.h>
#include "net/mac/frame802154e-ie.h"
#define DEBUG DEBUG_NONE
#include "net/ip/uip-debug.h"
/* c.f. IEEE 802.15.4e Table 4b */
enum ieee802154e_header_ie_id {
HEADER_IE_LE_CSL = 0x1a,
HEADER_IE_LE_RIT,
HEADER_IE_DSME_PAN_DESCRIPTOR,
HEADER_IE_RZ_TIME,
HEADER_IE_ACK_NACK_TIME_CORRECTION,
HEADER_IE_GACK,
HEADER_IE_LOW_LATENCY_NETWORK_INFO,
HEADER_IE_LIST_TERMINATION_1 = 0x7e,
HEADER_IE_LIST_TERMINATION_2 = 0x7f,
};
/* c.f. IEEE 802.15.4e Table 4c */
enum ieee802154e_payload_ie_id {
PAYLOAD_IE_ESDU = 0,
PAYLOAD_IE_MLME,
PAYLOAD_IE_LIST_TERMINATION = 0xf,
};
/* c.f. IEEE 802.15.4e Table 4d */
enum ieee802154e_mlme_short_subie_id {
MLME_SHORT_IE_TSCH_SYNCHRONIZATION = 0x1a,
MLME_SHORT_IE_TSCH_SLOFTRAME_AND_LINK,
MLME_SHORT_IE_TSCH_TIMESLOT,
MLME_SHORT_IE_TSCH_HOPPING_TIMING,
MLME_SHORT_IE_TSCH_EB_FILTER,
MLME_SHORT_IE_TSCH_MAC_METRICS_1,
MLME_SHORT_IE_TSCH_MAC_METRICS_2,
};
/* c.f. IEEE 802.15.4e Table 4e */
enum ieee802154e_mlme_long_subie_id {
MLME_LONG_IE_TSCH_CHANNEL_HOPPING_SEQUENCE = 0x9,
};
#define WRITE16(buf, val) \
do { ((uint8_t *)(buf))[0] = (val) & 0xff; \
((uint8_t *)(buf))[1] = ((val) >> 8) & 0xff; } while(0);
#define READ16(buf, var) \
(var) = ((uint8_t *)(buf))[0] | ((uint8_t *)(buf))[1] << 8
/* Create a header IE 2-byte descriptor */
static void
create_header_ie_descriptor(uint8_t *buf, uint8_t element_id, int ie_len)
{
uint16_t ie_desc;
/* Header IE descriptor: b0-6: len, b7-14: element id:, b15: type: 0 */
ie_desc = (ie_len & 0x7f) + ((element_id & 0xff) << 7);
WRITE16(buf, ie_desc);
}
/* Create a payload IE 2-byte descriptor */
static void
create_payload_ie_descriptor(uint8_t *buf, uint8_t group_id, int ie_len)
{
uint16_t ie_desc;
/* MLME Long IE descriptor: b0-10: len, b11-14: group id:, b15: type: 1 */
ie_desc = (ie_len & 0x07ff) + ((group_id & 0x0f) << 11) + (1 << 15);
WRITE16(buf, ie_desc);
}
/* Create a MLME short IE 2-byte descriptor */
static void
create_mlme_short_ie_descriptor(uint8_t *buf, uint8_t sub_id, int ie_len)
{
uint16_t ie_desc;
/* MLME Short IE descriptor: b0-7: len, b8-14: sub id:, b15: type: 0 */
ie_desc = (ie_len & 0xff) + ((sub_id & 0x7f) << 8);
WRITE16(buf, ie_desc);
}
/* Create a MLME long IE 2-byte descriptor */
static void
create_mlme_long_ie_descriptor(uint8_t *buf, uint8_t sub_id, int ie_len)
{
uint16_t ie_desc;
/* MLME Long IE descriptor: b0-10: len, b11-14: sub id:, b15: type: 1 */
ie_desc = (ie_len & 0x07ff) + ((sub_id & 0x0f) << 11) + (1 << 15);
WRITE16(buf, ie_desc);
}
/* Header IE. ACK/NACK time correction. Used in enhanced ACKs */
int
frame80215e_create_ie_header_ack_nack_time_correction(uint8_t *buf, int len,
struct ieee802154_ies *ies)
{
int ie_len = 2;
if(len >= 2 + ie_len && ies != NULL) {
int16_t drift_us;
uint16_t time_sync_field;
drift_us = ies->ie_time_correction;
time_sync_field = drift_us & 0x0fff;
if(ies->ie_is_nack) {
time_sync_field |= 0x8000;
}
WRITE16(buf+2, time_sync_field);
create_header_ie_descriptor(buf, HEADER_IE_ACK_NACK_TIME_CORRECTION, ie_len);
return 2 + ie_len;
} else {
return -1;
}
}
/* Header IE. List termination 1 (Signals the end of the Header IEs when
* followed by payload IEs) */
int
frame80215e_create_ie_header_list_termination_1(uint8_t *buf, int len,
struct ieee802154_ies *ies)
{
int ie_len = 0;
if(len >= 2 + ie_len && ies != NULL) {
create_header_ie_descriptor(buf, HEADER_IE_LIST_TERMINATION_1, 0);
return 2 + ie_len;
} else {
return -1;
}
}
/* Header IE. List termination 2 (Signals the end of the Header IEs when
* followed by an unformatted payload) */
int
frame80215e_create_ie_header_list_termination_2(uint8_t *buf, int len,
struct ieee802154_ies *ies)
{
int ie_len = 0;
if(len >= 2 + ie_len && ies != NULL) {
create_header_ie_descriptor(buf, HEADER_IE_LIST_TERMINATION_2, 0);
return 2 + ie_len;
} else {
return -1;
}
}
/* Payload IE. List termination */
int
frame80215e_create_ie_payload_list_termination(uint8_t *buf, int len,
struct ieee802154_ies *ies)
{
int ie_len = 0;
if(len >= 2 + ie_len && ies != NULL) {
create_payload_ie_descriptor(buf, PAYLOAD_IE_LIST_TERMINATION, 0);
return 2 + ie_len;
} else {
return -1;
}
}
/* Payload IE. MLME. Used to nest sub-IEs */
int
frame80215e_create_ie_mlme(uint8_t *buf, int len,
struct ieee802154_ies *ies)
{
int ie_len = 0;
if(len >= 2 + ie_len && ies != NULL) {
/* The length of the outer MLME IE is the total length of sub-IEs */
create_payload_ie_descriptor(buf, PAYLOAD_IE_MLME, ies->ie_mlme_len);
return 2 + ie_len;
} else {
return -1;
}
}
/* MLME sub-IE. TSCH synchronization. Used in EBs: ASN and join priority */
int
frame80215e_create_ie_tsch_synchronization(uint8_t *buf, int len,
struct ieee802154_ies *ies)
{
int ie_len = 6;
if(len >= 2 + ie_len && ies != NULL) {
buf[2] = ies->ie_asn.ls4b;
buf[3] = ies->ie_asn.ls4b >> 8;
buf[4] = ies->ie_asn.ls4b >> 16;
buf[5] = ies->ie_asn.ls4b >> 24;
buf[6] = ies->ie_asn.ms1b;
buf[7] = ies->ie_join_priority;
create_mlme_short_ie_descriptor(buf, MLME_SHORT_IE_TSCH_SYNCHRONIZATION, ie_len);
return 2 + ie_len;
} else {
return -1;
}
}
/* MLME sub-IE. TSCH slotframe and link. Used in EBs: initial schedule */
int
frame80215e_create_ie_tsch_slotframe_and_link(uint8_t *buf, int len,
struct ieee802154_ies *ies)
{
if(ies != NULL) {
int i;
int num_slotframes = ies->ie_tsch_slotframe_and_link.num_slotframes;
int num_links = ies->ie_tsch_slotframe_and_link.num_links;
int ie_len = 1 + num_slotframes * (4 + 5 * num_links);
if(num_slotframes > 1 || num_links > FRAME802154E_IE_MAX_LINKS
|| len < 2 + ie_len) {
/* We support only 0 or 1 slotframe in this IE and a predefined maximum number of links */
return -1;
}
/* Insert IE */
buf[2] = num_slotframes;
/* Insert slotframe */
if(num_slotframes == 1) {
buf[2 + 1] = ies->ie_tsch_slotframe_and_link.slotframe_handle;
WRITE16(buf + 2 + 2, ies->ie_tsch_slotframe_and_link.slotframe_size);
buf[2 + 4] = num_links;
/* Loop over links */
for(i = 0; i < num_links; i++) {
/* Insert links */
WRITE16(buf + 2 + 5 + i * 5, ies->ie_tsch_slotframe_and_link.links[i].timeslot);
WRITE16(buf + 2 + 5 + i * 5 + 2, ies->ie_tsch_slotframe_and_link.links[i].channel_offset);
buf[2 + 5 + i * 5 + 4] = ies->ie_tsch_slotframe_and_link.links[i].link_options;
}
}
create_mlme_short_ie_descriptor(buf, MLME_SHORT_IE_TSCH_SLOFTRAME_AND_LINK, ie_len);
return 2 + ie_len;
} else {
return -1;
}
}
/* MLME sub-IE. TSCH timeslot. Used in EBs: timeslot template (timing) */
int
frame80215e_create_ie_tsch_timeslot(uint8_t *buf, int len,
struct ieee802154_ies *ies)
{
int ie_len;
if(ies == NULL) {
return -1;
}
/* Only ID if ID == 0, else full timing description */
ie_len = ies->ie_tsch_timeslot_id == 0 ? 1 : 25;
if(len >= 2 + ie_len) {
buf[2] = ies->ie_tsch_timeslot_id;
if(ies->ie_tsch_timeslot_id != 0) {
int i;
for(i = 0; i < tsch_ts_elements_count; i++) {
WRITE16(buf + 3 + 2 * i, ies->ie_tsch_timeslot[i]);
}
}
create_mlme_short_ie_descriptor(buf, MLME_SHORT_IE_TSCH_TIMESLOT, ie_len);
return 2 + ie_len;
} else {
return -1;
}
}
/* MLME sub-IE. TSCH channel hopping sequence. Used in EBs: hopping sequence */
int
frame80215e_create_ie_tsch_channel_hopping_sequence(uint8_t *buf, int len,
struct ieee802154_ies *ies)
{
int ie_len;
if(ies == NULL || ies->ie_hopping_sequence_len > sizeof(ies->ie_hopping_sequence_list)) {
return -1;
}
ie_len = ies->ie_channel_hopping_sequence_id == 0 ? 1 : 12 + ies->ie_hopping_sequence_len;
if(len >= 2 + ie_len && ies != NULL) {
buf[2] = ies->ie_channel_hopping_sequence_id;
buf[3] = 0; /* channel page */
WRITE16(buf + 4, 0); /* number of channels */
WRITE16(buf + 6, 0); /* phy configuration */
WRITE16(buf + 8, 0);
/* Extended bitmap. Size: 0 */
WRITE16(buf + 10, ies->ie_hopping_sequence_len); /* sequence len */
memcpy(buf + 12, ies->ie_hopping_sequence_list, ies->ie_hopping_sequence_len); /* sequence list */
WRITE16(buf + 12 + ies->ie_hopping_sequence_len, 0); /* current hop */
create_mlme_long_ie_descriptor(buf, MLME_LONG_IE_TSCH_CHANNEL_HOPPING_SEQUENCE, ie_len);
return 2 + ie_len;
} else {
return -1;
}
}
/* Parse a header IE */
static int
frame802154e_parse_header_ie(const uint8_t *buf, int len,
uint8_t element_id, struct ieee802154_ies *ies)
{
switch(element_id) {
case HEADER_IE_ACK_NACK_TIME_CORRECTION:
if(len == 2) {
if(ies != NULL) {
/* If the originator was a time source neighbor, the receiver adjust
* its own clock by incorporating the received drift correction */
uint16_t time_sync_field = 0;
int16_t drift_us = 0;
/* Extract drift correction from Sync-IE, cast from 12 to 16-bit,
* and convert it to RTIMER ticks.
* See page 88 in IEEE Std 802.15.4e-2012. */
READ16(buf, time_sync_field);
/* First extract NACK */
ies->ie_is_nack = (time_sync_field & (uint16_t)0x8000) ? 1 : 0;
/* Then cast from 12 to 16 bit signed */
if(time_sync_field & 0x0800) { /* Negative integer */
drift_us = time_sync_field | 0xf000;
} else { /* Positive integer */
drift_us = time_sync_field & 0x0fff;
}
/* Convert to RTIMER ticks */
ies->ie_time_correction = drift_us;
}
return len;
}
break;
}
return -1;
}
/* Parse a MLME short IE */
static int
frame802154e_parse_mlme_short_ie(const uint8_t *buf, int len,
uint8_t sub_id, struct ieee802154_ies *ies)
{
switch(sub_id) {
case MLME_SHORT_IE_TSCH_SLOFTRAME_AND_LINK:
if(len >= 1) {
int i;
int num_slotframes = buf[0];
int num_links = buf[4];
if(num_slotframes == 0) {
return len;
}
if(num_slotframes <= 1 && num_links <= FRAME802154E_IE_MAX_LINKS
&& len == 1 + num_slotframes * (4 + 5 * num_links)) {
if(ies != NULL) {
/* We support only 0 or 1 slotframe in this IE and a predefined maximum number of links */
ies->ie_tsch_slotframe_and_link.num_slotframes = buf[0];
ies->ie_tsch_slotframe_and_link.slotframe_handle = buf[1];
READ16(buf + 2, ies->ie_tsch_slotframe_and_link.slotframe_size);
ies->ie_tsch_slotframe_and_link.num_links = buf[4];
for(i = 0; i < num_links; i++) {
READ16(buf + 5 + i * 5, ies->ie_tsch_slotframe_and_link.links[i].timeslot);
READ16(buf + 5 + i * 5 + 2, ies->ie_tsch_slotframe_and_link.links[i].channel_offset);
ies->ie_tsch_slotframe_and_link.links[i].link_options = buf[5 + i * 5 + 4];
}
}
return len;
}
}
break;
case MLME_SHORT_IE_TSCH_SYNCHRONIZATION:
if(len == 6) {
if(ies != NULL) {
ies->ie_asn.ls4b = (uint32_t)buf[0];
ies->ie_asn.ls4b |= (uint32_t)buf[1] << 8;
ies->ie_asn.ls4b |= (uint32_t)buf[2] << 16;
ies->ie_asn.ls4b |= (uint32_t)buf[3] << 24;
ies->ie_asn.ms1b = (uint8_t)buf[4];
ies->ie_join_priority = (uint8_t)buf[5];
}
return len;
}
break;
case MLME_SHORT_IE_TSCH_TIMESLOT:
if(len == 1 || len == 25) {
if(ies != NULL) {
ies->ie_tsch_timeslot_id = buf[0];
if(len == 25) {
int i;
for(i = 0; i < tsch_ts_elements_count; i++) {
READ16(buf+1+2*i, ies->ie_tsch_timeslot[i]);
}
}
}
return len;
}
break;
}
return -1;
}
/* Parse a MLME long IE */
static int
frame802154e_parse_mlme_long_ie(const uint8_t *buf, int len,
uint8_t sub_id, struct ieee802154_ies *ies)
{
switch(sub_id) {
case MLME_LONG_IE_TSCH_CHANNEL_HOPPING_SEQUENCE:
if(len > 0) {
if(ies != NULL) {
ies->ie_channel_hopping_sequence_id = buf[0];
if(len > 1) {
READ16(buf+8, ies->ie_hopping_sequence_len); /* sequence len */
if(ies->ie_hopping_sequence_len <= sizeof(ies->ie_hopping_sequence_list)
&& len == 12 + ies->ie_hopping_sequence_len) {
memcpy(ies->ie_hopping_sequence_list, buf+10, ies->ie_hopping_sequence_len); /* sequence list */
}
}
}
return len;
}
break;
}
return -1;
}
/* Parse all IEEE 802.15.4e Information Elements (IE) from a frame */
int
frame802154e_parse_information_elements(const uint8_t *buf, uint8_t buf_size,
struct ieee802154_ies *ies)
{
const uint8_t *start = buf;
uint16_t ie_desc;
uint8_t type;
uint8_t id;
uint16_t len = 0;
int nested_mlme_len = 0;
enum {PARSING_HEADER_IE, PARSING_PAYLOAD_IE, PARSING_MLME_SUBIE} parsing_state;
if(ies == NULL) {
return -1;
}
/* Always look for a header IE first (at least "list termination 1") */
parsing_state = PARSING_HEADER_IE;
ies->ie_payload_ie_offset = 0;
/* Loop over all IEs */
while(buf_size > 0) {
if(buf_size < 2) { /* Not enough space for IE descriptor */
return -1;
}
READ16(buf, ie_desc);
buf_size -= 2;
buf += 2;
type = ie_desc & 0x8000 ? 1 : 0; /* b15 */
PRINTF("frame802154e: ie type %u, current state %u\n", type, parsing_state);
switch(parsing_state) {
case PARSING_HEADER_IE:
if(type != 0) {
PRINTF("frame802154e: wrong type %04x\n", ie_desc);
return -1;
}
/* Header IE: 2 bytes descriptor, c.f. fig 48n in IEEE 802.15.4e */
len = ie_desc & 0x007f; /* b0-b6 */
id = (ie_desc & 0x7f80) >> 7; /* b7-b14 */
PRINTF("frame802154e: header ie len %u id %x\n", len, id);
switch(id) {
case HEADER_IE_LIST_TERMINATION_1:
if(len == 0) {
/* End of payload IE list, now expect payload IEs */
parsing_state = PARSING_PAYLOAD_IE;
ies->ie_payload_ie_offset = buf - start; /* Save IE header len */
PRINTF("frame802154e: list termination 1, look for payload IEs\n");
} else {
PRINTF("frame802154e: list termination 1, wrong len %u\n", len);
return -1;
}
break;
case HEADER_IE_LIST_TERMINATION_2:
/* End of IE parsing */
if(len == 0) {
ies->ie_payload_ie_offset = buf - start; /* Save IE header len */
PRINTF("frame802154e: list termination 2\n");
return buf + len - start;
} else {
PRINTF("frame802154e: list termination 2, wrong len %u\n", len);
return -1;
}
default:
if(len > buf_size || frame802154e_parse_header_ie(buf, len, id, ies) == -1) {
PRINTF("frame802154e: failed to parse\n");
return -1;
}
break;
}
break;
case PARSING_PAYLOAD_IE:
if(type != 1) {
PRINTF("frame802154e: wrong type %04x\n", ie_desc);
return -1;
}
/* Payload IE: 2 bytes descriptor, c.f. fig 48o in IEEE 802.15.4e */
len = ie_desc & 0x7ff; /* b0-b10 */
id = (ie_desc & 0x7800) >> 11; /* b11-b14 */
PRINTF("frame802154e: payload ie len %u id %x\n", len, id);
switch(id) {
case PAYLOAD_IE_MLME:
/* Now expect 'len' bytes of MLME sub-IEs */
parsing_state = PARSING_MLME_SUBIE;
nested_mlme_len = len;
len = 0; /* Reset len as we want to read subIEs and not jump over them */
PRINTF("frame802154e: entering MLME ie with len %u\n", nested_mlme_len);
break;
case PAYLOAD_IE_LIST_TERMINATION:
PRINTF("frame802154e: payload ie list termination %u\n", len);
return (len == 0) ? buf + len - start : -1;
default:
PRINTF("frame802154e: non-supported payload ie\n");
return -1;
}
break;
case PARSING_MLME_SUBIE:
/* MLME sub-IE: 2 bytes descriptor, c.f. fig 48q in IEEE 802.15.4e */
/* type == 0 means short sub-IE, type == 1 means long sub-IE */
if(type == 0) {
/* Short sub-IE, c.f. fig 48r in IEEE 802.15.4e */
len = ie_desc & 0x00ff; /* b0-b7 */
id = (ie_desc & 0x7f00) >> 8; /* b8-b14 */
PRINTF("frame802154e: short mlme ie len %u id %x\n", len, id);
if(len > buf_size || frame802154e_parse_mlme_short_ie(buf, len, id, ies) == -1) {
PRINTF("frame802154e: failed to parse ie\n");
return -1;
}
} else {
/* Long sub-IE, c.f. fig 48s in IEEE 802.15.4e */
len = ie_desc & 0x7ff; /* b0-b10 */
id = (ie_desc & 0x7800) >> 11; /* b11-b14 */
PRINTF("frame802154e: long mlme ie len %u id %x\n", len, id);
if(len > buf_size || frame802154e_parse_mlme_long_ie(buf, len, id, ies) == -1) {
PRINTF("frame802154e: failed to parse ie\n");
return -1;
}
}
/* Update remaining nested MLME len */
nested_mlme_len -= 2 + len;
if(nested_mlme_len < 0) {
PRINTF("frame802154e: found more sub-IEs than initially advertised\n");
/* We found more sub-IEs than initially advertised */
return -1;
}
if(nested_mlme_len == 0) {
PRINTF("frame802154e: end of MLME IE parsing\n");
/* End of IE parsing, look for another payload IE */
parsing_state = PARSING_PAYLOAD_IE;
}
break;
}
buf += len;
buf_size -= len;
}
if(parsing_state == PARSING_HEADER_IE) {
ies->ie_payload_ie_offset = buf - start; /* Save IE header len */
}
return buf - start;
}

View file

@ -0,0 +1,120 @@
/*
* Copyright (c) 2015, SICS Swedish ICT.
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*
*/
/**
* \file
* IEEE 802.15.4e Information Element (IE) creation and parsing.
* \author
* Simon Duquennoy <simonduq@sics.se>
*/
#ifndef FRAME_802154E_H
#define FRAME_802154E_H
#include "contiki.h"
/* We need definitions from tsch-private.h for TSCH-specific information elements */
#include "net/mac/tsch/tsch-private.h"
#define FRAME802154E_IE_MAX_LINKS 4
/* Structures used for the Slotframe and Links information element */
struct tsch_slotframe_and_links_link {
uint16_t timeslot;
uint16_t channel_offset;
uint8_t link_options;
};
struct tsch_slotframe_and_links {
uint8_t num_slotframes; /* We support only 0 or 1 slotframe in this IE */
uint8_t slotframe_handle;
uint16_t slotframe_size;
uint8_t num_links;
struct tsch_slotframe_and_links_link links[FRAME802154E_IE_MAX_LINKS];
};
/* The information elements that we currently support */
struct ieee802154_ies {
/* Header IEs */
int16_t ie_time_correction;
uint8_t ie_is_nack;
/* Payload MLME */
uint8_t ie_payload_ie_offset;
uint16_t ie_mlme_len;
/* Payload Short MLME IEs */
uint8_t ie_tsch_synchronization_offset;
struct asn_t ie_asn;
uint8_t ie_join_priority;
uint8_t ie_tsch_timeslot_id;
uint16_t ie_tsch_timeslot[tsch_ts_elements_count];
struct tsch_slotframe_and_links ie_tsch_slotframe_and_link;
/* Payload Long MLME IEs */
uint8_t ie_channel_hopping_sequence_id;
/* We include and parse only the sequence len and list and omit unused fields */
uint16_t ie_hopping_sequence_len;
uint8_t ie_hopping_sequence_list[TSCH_HOPPING_SEQUENCE_MAX_LEN];
};
/** Insert various Information Elements **/
/* Header IE. ACK/NACK time correction. Used in enhanced ACKs */
int frame80215e_create_ie_header_ack_nack_time_correction(uint8_t *buf, int len,
struct ieee802154_ies *ies);
/* Header IE. List termination 1 (Signals the end of the Header IEs when
* followed by payload IEs) */
int frame80215e_create_ie_header_list_termination_1(uint8_t *buf, int len,
struct ieee802154_ies *ies);
/* Header IE. List termination 2 (Signals the end of the Header IEs when
* followed by an unformatted payload) */
int frame80215e_create_ie_header_list_termination_2(uint8_t *buf, int len,
struct ieee802154_ies *ies);
/* Payload IE. List termination */
int frame80215e_create_ie_payload_list_termination(uint8_t *buf, int len,
struct ieee802154_ies *ies);
/* Payload IE. MLME. Used to nest sub-IEs */
int frame80215e_create_ie_mlme(uint8_t *buf, int len,
struct ieee802154_ies *ies);
/* MLME sub-IE. TSCH synchronization. Used in EBs: ASN and join priority */
int frame80215e_create_ie_tsch_synchronization(uint8_t *buf, int len,
struct ieee802154_ies *ies);
/* MLME sub-IE. TSCH slotframe and link. Used in EBs: initial schedule */
int frame80215e_create_ie_tsch_slotframe_and_link(uint8_t *buf, int len,
struct ieee802154_ies *ies);
/* MLME sub-IE. TSCH timeslot. Used in EBs: timeslot template (timing) */
int frame80215e_create_ie_tsch_timeslot(uint8_t *buf, int len,
struct ieee802154_ies *ies);
/* MLME sub-IE. TSCH channel hopping sequence. Used in EBs: hopping sequence */
int frame80215e_create_ie_tsch_channel_hopping_sequence(uint8_t *buf, int len,
struct ieee802154_ies *ies);
/* Parse all Information Elements of a frame */
int frame802154e_parse_information_elements(const uint8_t *buf, uint8_t buf_size,
struct ieee802154_ies *ies);
#endif /* FRAME_802154E_H */

View file

@ -62,30 +62,6 @@ static uint8_t mac_dsn;
static uint8_t initialized = 0; static uint8_t initialized = 0;
/** \brief The 16-bit identifier of the PAN on which the device is
* sending to. If this value is 0xffff, the device is not
* associated.
*/
static const uint16_t mac_dst_pan_id = IEEE802154_PANID;
/** \brief The 16-bit identifier of the PAN on which the device is
* operating. If this value is 0xffff, the device is not
* associated.
*/
static const uint16_t mac_src_pan_id = IEEE802154_PANID;
/*---------------------------------------------------------------------------*/
static int
is_broadcast_addr(uint8_t mode, uint8_t *addr)
{
int i = mode == FRAME802154_SHORTADDRMODE ? 2 : 8;
while(i-- > 0) {
if(addr[i] != 0xff) {
return 0;
}
}
return 1;
}
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
static int static int
create_frame(int type, int do_create) create_frame(int type, int do_create)
@ -93,6 +69,10 @@ create_frame(int type, int do_create)
frame802154_t params; frame802154_t params;
int hdr_len; int hdr_len;
if(frame802154_get_pan_id() == 0xffff) {
return -1;
}
/* init to zeros */ /* init to zeros */
memset(&params, 0, sizeof(params)); memset(&params, 0, sizeof(params));
@ -109,10 +89,14 @@ create_frame(int type, int do_create)
} else { } else {
params.fcf.ack_required = packetbuf_attr(PACKETBUF_ATTR_MAC_ACK); params.fcf.ack_required = packetbuf_attr(PACKETBUF_ATTR_MAC_ACK);
} }
/* We do not compress PAN ID in outgoing frames, i.e. include one PAN ID (dest by default)
* There is one exception, seemingly a typo in Table 2a: rows 2 and 3: when there is no
* source nor destination address, we have dest PAN ID iff compression is *set*. */
params.fcf.panid_compression = 0; params.fcf.panid_compression = 0;
params.fcf.sequence_number_suppression = FRAME802154_SUPPR_SEQNO;
/* Insert IEEE 802.15.4 (2006) version bits. */ /* Insert IEEE 802.15.4 version bits. */
params.fcf.frame_version = FRAME802154_IEEE802154_2006; params.fcf.frame_version = FRAME802154_VERSION;
#if LLSEC802154_SECURITY_LEVEL #if LLSEC802154_SECURITY_LEVEL
if(packetbuf_attr(PACKETBUF_ATTR_SECURITY_LEVEL)) { if(packetbuf_attr(PACKETBUF_ATTR_SECURITY_LEVEL)) {
@ -120,8 +104,13 @@ create_frame(int type, int do_create)
} }
/* Setting security-related attributes */ /* Setting security-related attributes */
params.aux_hdr.security_control.security_level = packetbuf_attr(PACKETBUF_ATTR_SECURITY_LEVEL); params.aux_hdr.security_control.security_level = packetbuf_attr(PACKETBUF_ATTR_SECURITY_LEVEL);
#if LLSEC802154_USES_FRAME_COUNTER
params.aux_hdr.frame_counter.u16[0] = packetbuf_attr(PACKETBUF_ATTR_FRAME_COUNTER_BYTES_0_1); params.aux_hdr.frame_counter.u16[0] = packetbuf_attr(PACKETBUF_ATTR_FRAME_COUNTER_BYTES_0_1);
params.aux_hdr.frame_counter.u16[1] = packetbuf_attr(PACKETBUF_ATTR_FRAME_COUNTER_BYTES_2_3); params.aux_hdr.frame_counter.u16[1] = packetbuf_attr(PACKETBUF_ATTR_FRAME_COUNTER_BYTES_2_3);
#else /* LLSEC802154_USES_FRAME_COUNTER */
params.aux_hdr.security_control.frame_counter_suppression = 1;
params.aux_hdr.security_control.frame_counter_size = 1;
#endif /* LLSEC802154_USES_FRAME_COUNTER */
#if LLSEC802154_USES_EXPLICIT_KEYS #if LLSEC802154_USES_EXPLICIT_KEYS
params.aux_hdr.security_control.key_id_mode = packetbuf_attr(PACKETBUF_ATTR_KEY_ID_MODE); params.aux_hdr.security_control.key_id_mode = packetbuf_attr(PACKETBUF_ATTR_KEY_ID_MODE);
params.aux_hdr.key_index = packetbuf_attr(PACKETBUF_ATTR_KEY_INDEX); params.aux_hdr.key_index = packetbuf_attr(PACKETBUF_ATTR_KEY_INDEX);
@ -150,21 +139,20 @@ create_frame(int type, int do_create)
/** /**
\todo For phase 1 the addresses are all long. We'll need a mechanism \todo For phase 1 the addresses are all long. We'll need a mechanism
in the rime attributes to tell the mac to use long or short for phase 2. in the rime attributes to tell the mac to use long or short for phase 2.
*/ */
if(LINKADDR_SIZE == 2) { if(LINKADDR_SIZE == 2) {
/* Use short address mode if linkaddr size is short. */ /* Use short address mode if linkaddr size is short. */
params.fcf.src_addr_mode = FRAME802154_SHORTADDRMODE; params.fcf.src_addr_mode = FRAME802154_SHORTADDRMODE;
} else { } else {
params.fcf.src_addr_mode = FRAME802154_LONGADDRMODE; params.fcf.src_addr_mode = FRAME802154_LONGADDRMODE;
} }
params.dest_pid = mac_dst_pan_id; params.dest_pid = frame802154_get_pan_id();
if(packetbuf_holds_broadcast()) { if(packetbuf_holds_broadcast()) {
/* Broadcast requires short address mode. */ /* Broadcast requires short address mode. */
params.fcf.dest_addr_mode = FRAME802154_SHORTADDRMODE; params.fcf.dest_addr_mode = FRAME802154_SHORTADDRMODE;
params.dest_addr[0] = 0xFF; params.dest_addr[0] = 0xFF;
params.dest_addr[1] = 0xFF; params.dest_addr[1] = 0xFF;
} else { } else {
linkaddr_copy((linkaddr_t *)&params.dest_addr, linkaddr_copy((linkaddr_t *)&params.dest_addr,
packetbuf_addr(PACKETBUF_ADDR_RECEIVER)); packetbuf_addr(PACKETBUF_ADDR_RECEIVER));
@ -177,7 +165,7 @@ create_frame(int type, int do_create)
} }
/* Set the source PAN ID to the global variable. */ /* Set the source PAN ID to the global variable. */
params.src_pid = mac_src_pan_id; params.src_pid = frame802154_get_pan_id();
/* /*
* Set up the source address using only the long address mode for * Set up the source address using only the long address mode for
@ -191,7 +179,6 @@ create_frame(int type, int do_create)
if(!do_create) { if(!do_create) {
/* Only calculate header length */ /* Only calculate header length */
return hdr_len; return hdr_len;
} else if(packetbuf_hdralloc(hdr_len)) { } else if(packetbuf_hdralloc(hdr_len)) {
frame802154_create(&params, packetbuf_hdrptr()); frame802154_create(&params, packetbuf_hdrptr());
@ -223,35 +210,41 @@ parse(void)
{ {
frame802154_t frame; frame802154_t frame;
int hdr_len; int hdr_len;
hdr_len = frame802154_parse(packetbuf_dataptr(), packetbuf_datalen(), &frame); hdr_len = frame802154_parse(packetbuf_dataptr(), packetbuf_datalen(), &frame);
if(hdr_len && packetbuf_hdrreduce(hdr_len)) { if(hdr_len && packetbuf_hdrreduce(hdr_len)) {
packetbuf_set_attr(PACKETBUF_ATTR_FRAME_TYPE, frame.fcf.frame_type); packetbuf_set_attr(PACKETBUF_ATTR_FRAME_TYPE, frame.fcf.frame_type);
if(frame.fcf.dest_addr_mode) { if(frame.fcf.dest_addr_mode) {
if(frame.dest_pid != mac_src_pan_id && if(frame.dest_pid != frame802154_get_pan_id() &&
frame.dest_pid != FRAME802154_BROADCASTPANDID) { frame.dest_pid != FRAME802154_BROADCASTPANDID) {
/* Packet to another PAN */ /* Packet to another PAN */
PRINTF("15.4: for another pan %u\n", frame.dest_pid); PRINTF("15.4: for another pan %u\n", frame.dest_pid);
return FRAMER_FAILED; return FRAMER_FAILED;
} }
if(!is_broadcast_addr(frame.fcf.dest_addr_mode, frame.dest_addr)) { if(!frame802154_is_broadcast_addr(frame.fcf.dest_addr_mode, frame.dest_addr)) {
packetbuf_set_addr(PACKETBUF_ADDR_RECEIVER, (linkaddr_t *)&frame.dest_addr); packetbuf_set_addr(PACKETBUF_ADDR_RECEIVER, (linkaddr_t *)&frame.dest_addr);
} }
} }
packetbuf_set_addr(PACKETBUF_ADDR_SENDER, (linkaddr_t *)&frame.src_addr); packetbuf_set_addr(PACKETBUF_ADDR_SENDER, (linkaddr_t *)&frame.src_addr);
packetbuf_set_attr(PACKETBUF_ATTR_PENDING, frame.fcf.frame_pending); packetbuf_set_attr(PACKETBUF_ATTR_PENDING, frame.fcf.frame_pending);
packetbuf_set_attr(PACKETBUF_ATTR_MAC_SEQNO, frame.seq); if(frame.fcf.sequence_number_suppression == 0) {
packetbuf_set_attr(PACKETBUF_ATTR_MAC_SEQNO, frame.seq);
} else {
packetbuf_set_attr(PACKETBUF_ATTR_MAC_SEQNO, 0xffff);
}
#if NETSTACK_CONF_WITH_RIME #if NETSTACK_CONF_WITH_RIME
packetbuf_set_attr(PACKETBUF_ATTR_PACKET_ID, frame.seq); packetbuf_set_attr(PACKETBUF_ATTR_PACKET_ID, frame.seq);
#endif #endif
#if LLSEC802154_SECURITY_LEVEL #if LLSEC802154_SECURITY_LEVEL
if(frame.fcf.security_enabled) { if(frame.fcf.security_enabled) {
packetbuf_set_attr(PACKETBUF_ATTR_SECURITY_LEVEL, frame.aux_hdr.security_control.security_level); packetbuf_set_attr(PACKETBUF_ATTR_SECURITY_LEVEL, frame.aux_hdr.security_control.security_level);
#if LLSEC802154_USES_FRAME_COUNTER
packetbuf_set_attr(PACKETBUF_ATTR_FRAME_COUNTER_BYTES_0_1, frame.aux_hdr.frame_counter.u16[0]); packetbuf_set_attr(PACKETBUF_ATTR_FRAME_COUNTER_BYTES_0_1, frame.aux_hdr.frame_counter.u16[0]);
packetbuf_set_attr(PACKETBUF_ATTR_FRAME_COUNTER_BYTES_2_3, frame.aux_hdr.frame_counter.u16[1]); packetbuf_set_attr(PACKETBUF_ATTR_FRAME_COUNTER_BYTES_2_3, frame.aux_hdr.frame_counter.u16[1]);
#endif /* LLSEC802154_USES_FRAME_COUNTER */
#if LLSEC802154_USES_EXPLICIT_KEYS #if LLSEC802154_USES_EXPLICIT_KEYS
packetbuf_set_attr(PACKETBUF_ATTR_KEY_ID_MODE, frame.aux_hdr.security_control.key_id_mode); packetbuf_set_attr(PACKETBUF_ATTR_KEY_ID_MODE, frame.aux_hdr.security_control.key_id_mode);
packetbuf_set_attr(PACKETBUF_ATTR_KEY_INDEX, frame.aux_hdr.key_index); packetbuf_set_attr(PACKETBUF_ATTR_KEY_INDEX, frame.aux_hdr.key_index);
@ -264,7 +257,7 @@ parse(void)
PRINTADDR(packetbuf_addr(PACKETBUF_ADDR_SENDER)); PRINTADDR(packetbuf_addr(PACKETBUF_ADDR_SENDER));
PRINTADDR(packetbuf_addr(PACKETBUF_ADDR_RECEIVER)); PRINTADDR(packetbuf_addr(PACKETBUF_ADDR_RECEIVER));
PRINTF("%d %u (%u)\n", hdr_len, packetbuf_datalen(), packetbuf_totlen()); PRINTF("%d %u (%u)\n", hdr_len, packetbuf_datalen(), packetbuf_totlen());
return hdr_len; return hdr_len;
} }
return FRAMER_FAILED; return FRAMER_FAILED;

94
core/net/mac/nordc.c Normal file
View file

@ -0,0 +1,94 @@
/*
* Copyright (c) 2014, SICS Swedish ICT.
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*
*/
/**
* \file
* This RDC layer does nothing. It is meant for use with MAC
* layers that do not use a RDC at all, such as TSCH.
* \author
* Simon Duquennoy <simonduq@sics.se>
*
*/
#include "net/mac/rdc.h"
#include "net/netstack.h"
/*---------------------------------------------------------------------------*/
static void
send_packet(mac_callback_t sent, void *ptr)
{
}
/*---------------------------------------------------------------------------*/
static void
send_list(mac_callback_t sent, void *ptr, struct rdc_buf_list *buf_list)
{
}
/*---------------------------------------------------------------------------*/
static void
packet_input(void)
{
}
/*---------------------------------------------------------------------------*/
static int
on(void)
{
return 0;
}
/*---------------------------------------------------------------------------*/
static int
off(int keep_radio_on)
{
return 0;
}
/*---------------------------------------------------------------------------*/
static unsigned short
channel_check_interval(void)
{
return 0;
}
/*---------------------------------------------------------------------------*/
static void
init(void)
{
}
/*---------------------------------------------------------------------------*/
const struct rdc_driver nordc_driver = {
"nordc",
init,
send_packet,
send_list,
packet_input,
on,
off,
channel_check_interval,
};
/*---------------------------------------------------------------------------*/

View file

@ -0,0 +1 @@
CONTIKI_SOURCEFILES += tsch.c tsch-slot-operation.c tsch-queue.c tsch-packet.c tsch-schedule.c tsch-log.c tsch-rpl.c tsch-adaptive-timesync.c

201
core/net/mac/tsch/README.md Normal file
View file

@ -0,0 +1,201 @@
# IEEE 802.15.4e TSCH (TimeSlotted Channel Hopping)
## Overview
TSCH is a MAC layer of the [IEEE 802.15.4e-2012 amendment][ieee802.15.4e-2012],
currently being integrated as part of the new IEEE 802.15.4-2015.
[6TiSCH][ietf-6tisch-wg] is an IETF Working Group focused on IPv6 over TSCH.
This is a Contiki implementation of TSCH and the 6TiSCH so-called "minimal configuration",
which defines how to run a basic RPL+TSCH network.
It was developped by:
* Simon Duquennoy, SICS, simonduq@sics.se, github user: [simonduq](https://github.com/simonduq)
* Beshr Al Nahas, SICS (now Chalmers University), beshr@chalmers.se, github user: [beshrns](https://github.com/beshrns)
You can find an extensive evaluation of this implementation in our paper [*Orchestra: Robust Mesh Networks Through Autonomously Scheduled TSCH*](http://www.simonduquennoy.net/papers/duquennoy15orchestra.pdf), ACM SenSys'15.
## Features
This implementation includes:
* Standard IEEE 802.15.4e-2012 frame version 2
* Standard TSCH joining procedure with Enhanced Beacons with the following Information Elements:
* TSCH synchronization (join priority and ASN)
* TSCH slotframe and link (basic schedule)
* TSCH timeslot (timeslot timing template)
* TSCH channel hopping sequence (hopping sequence template)
* Standard TSCH link selection and slot operation (10ms slots by default)
* Standard TSCH synchronization, including with ACK/NACK time correction Information Element
* Standard TSCH queues and CSMA-CA mechanism
* Standard TSCH security
* Standard 6TiSCH TSCH-RPL interaction (6TiSCH Minimal Configuration and Minimal Schedule)
* A scheduling API to add/remove slotframes and links
* A system for logging from TSCH timeslot operation interrupt, with postponed printout
* Orchestra: an autonomous scheduler for TSCH+RPL networks
It has been tested on the following platforms:
* NXP JN516x (`jn516x`, tested on hardware)
* Tmote Sky (`sky`, tested on hardware and in cooja)
* Zolertia Z1 (`z1`, tested in cooja only)
This implementation was present at the ETSI Plugtest
event in Prague in July 2015, and did successfully inter-operate with all
four implementations it was tested against.
We have designed this implementation with IPv6 and RPL in mind, but the code is fully independent
from upper layers (with the exception of the optional `tsch-rpl.[ch]`), and has been
also tested with Rime (currently only with 64-bit link-layer addresses).
## Code structure
The IEEE 802.15.4e-2012 frame format is implemented in:
* `core/net/mac/frame802154.[ch]`: handling of frame version 2
* `core/net/mac/frame802154-ie.[ch]`: handling of Information Elements
TSCH is implemented in:
* `core/net/mac/tsch/tsch.[ch]`: TSCH management (association, keep-alive), processes handling pending
outgoing and incoming packets, and interface with Contiki's upper layers as a MAC driver. TSCH does not
require a RDC (nordc is recommended).
* `tsch-slot-operation.[ch]`: TSCH low-level slot operation, fully interrupt-driven. Node wake up at every active
slot (according to the slotframes and links installed), transmit or receive frames and ACKs. Received packets are
stored in a ringbuf for latter upper-layer processing. Outgoing packets that are dequeued (because acknowledged
or dropped) are stored in another ringbuf for upper-layer processing.
* `tsch-asn.h`: TSCH macros for Absolute Slot Number (ASN) handling.
* `tsch-packet.[ch]`: TSCH Enhanced ACK (EACK) and enhanced Beacon (EB) creation and parsing.
* `tsch-queue.[ch]`: TSCH per-neighbor queue, neighbor state, and CSMA-CA.
* `tsch-schedule.[ch]`: TSCH slotframe and link handling, and API for slotframe and link installation/removal.
* `tsch-security.[ch]`: TSCH security, i.e. securing frames and ACKs from interrupt with ASN as part of the Nonce.
Implements the 6TiSCH minimal configuration K1-K2 keys pair.
* `tsch-rpl.[ch]`: used for TSCH+RPL networks, to align TSCH and RPL states (preferred parent -> time source,
rank -> join priority) as defined in the 6TiSCH minimal configuration.
* `tsch-log.[ch]`: logging system for TSCH, including delayed messages for logging from slot operation interrupt.
Orchestra is implemented in:
* `apps/orchestra`: see `apps/orchestra/README.md` for more information.
## Using TSCH
A simple TSCH+RPL example is included under `examples/ipv6/rpl-tsch`.
To use TSCH, first make sure your platform supports it.
Currently, `jn516x`, `sky` and `z1` are the supported platforms.
To add your own, we refer the reader to the next section.
To add TSCH to your application, first include the TSCH module from your makefile with:
`MODULES += core/net/mac/tsch`
Then, enable TSCH from your project conf with the following:
```
/* Netstack layers */
#undef NETSTACK_CONF_MAC
#define NETSTACK_CONF_MAC tschmac_driver
#undef NETSTACK_CONF_RDC
#define NETSTACK_CONF_RDC nordc_driver
#undef NETSTACK_CONF_FRAMER
#define NETSTACK_CONF_FRAMER framer_802154
/* IEEE802.15.4 frame version */
#undef FRAME802154_CONF_VERSION
#define FRAME802154_CONF_VERSION FRAME802154_IEEE802154E_2012
```
If you are running with RPL, it is recommended to enable the `tsch-rpl` module with:
```
/* TSCH and RPL callbacks */
#define RPL_CALLBACK_PARENT_SWITCH tsch_rpl_callback_parent_switch
#define RPL_CALLBACK_NEW_DIO_INTERVAL tsch_rpl_callback_new_dio_interval
#define TSCH_CALLBACK_JOINING_NETWORK tsch_rpl_callback_joining_network
#define TSCH_CALLBACK_LEAVING_NETWORK tsch_rpl_callback_leaving_network
```
On CC2420-based platforms, enable SFD timestamps with:
```
/* Disable DCO calibration (uses timerB) */
#undef DCOSYNCH_CONF_ENABLED
#define DCOSYNCH_CONF_ENABLED 0
/* Enable SFD timestamps (uses timerB) */
#undef CC2420_CONF_SFD_TIMESTAMPS
#define CC2420_CONF_SFD_TIMESTAMPS 1
```
To configure TSCH, see the macros in `.h` files under `core/net/mac/tsch/` and redefine your own in your `project-conf.h`.
## Using TSCH with Security
To include TSCH standard-compliant security, set the following:
```
/* Enable security */
#undef LLSEC802154_CONF_SECURITY_LEVEL
#define LLSEC802154_CONF_SECURITY_LEVEL 1
/* TSCH uses explicit keys to identify k1 and k2 */
#undef LLSEC802154_CONF_USES_EXPLICIT_KEYS
#define LLSEC802154_CONF_USES_EXPLICIT_KEYS 1
/* TSCH uses the ASN rather than frame counter to construct the Nonce */
#undef LLSEC802154_CONF_USES_FRAME_COUNTER
#define LLSEC802154_CONF_USES_FRAME_COUNTER 0
```
The keys can be configured in `net/mac/tsch/tsch-security.h`.
Nodes handle security level and keys dynamically, i.e. as specified by the incoming frame header rather that compile-time defined.
By default, when including security, the PAN coordinator will transmit secured EBs.
Use `tsch_set_pan_secured` to explicitly ask the coordinator to secure EBs or not.
When associating, nodes with security included can join both secured or non-secured networks.
Set `TSCH_CONF_JOIN_SECURED_ONLY` to force joining secured networks only.
Likewise, set `TSCH_JOIN_MY_PANID_ONLY` to force joining networks with a specific PANID only.
## TSCH Scheduling
By default (see `TSCH_SCHEDULE_WITH_6TISCH_MINIMAL`), our implementation runs a 6TiSCH minimal schedule, which emulates an always-on link on top of TSCH.
The schedule consists in a single shared slot for all transmissions and receptions, in a slotframe of length `TSCH_SCHEDULE_DEFAULT_LENGTH`.
As an alternative, we provide Orchestra (under `apps/orchestra`), an autonomous scheduling solution for TSCH where nodes maintain their own schedule locally, solely based on their local RPL state.
Orchestra can be simply enabled and should work out-of-the-box with its default settings as long as RPL is also enabled.
See `apps/orchestra/README.md` for more information.
Finally, one can also implement his own scheduler, centralized or distributed, based on the scheduling API provides in `core/net/mac/tsch/tsch-schedule.h`.
## Porting TSCH to a new platform
Porting TSCH to a new platform requires a few new features in the radio driver, a number of timing-related configuration paramters.
The easiest is probably to start from one of the existing port: `jn516x`, `sky`, `z1`.
### Radio features required for TSCH
The main new feature required for TSCH is the so-called *poll mode*, a new Rx mode for Contiki radio drivers.
In poll mode, radio interrupts are disabled, and the radio driver never calls upper layers.
Instead, TSCH will poll the driver for incoming packets, from interrupt, exactly when it expects one.
TSCH will check when initializing (in `tsch_init`) that the radio driver supports all required features, namely:
* get and set Rx mode (`RADIO_PARAM_RX_MODE`) as follows:
* enable address filtering with `RADIO_RX_MODE_ADDRESS_FILTER`
* disable auto-ack with `RADIO_RX_MODE_AUTOACK`
* enable poll mode with `RADIO_RX_MODE_POLL_MODE`
* get and set Tx mode (`RADIO_PARAM_TX_MODE`) as follows:
* disable CCA-before-sending with `RADIO_TX_MODE_SEND_ON_CCA`
* set radio channel with `RADIO_PARAM_CHANNEL`
* get last packet timestamp with `RADIO_PARAM_LAST_PACKET_TIMESTAMP`
* optionally: get last packet RSSI with `RADIO_PARAM_LAST_RSSI`
* optionally: get last packet LQI with `RADIO_PARAM_LAST_LQI`
### Timing macros required for TSCH
The following macros must be provided:
* `US_TO_RTIMERTICKS(US)`: converts micro-seconds to rtimer ticks
* `RTIMERTICKS_TO_US(T)`: converts rtimer ticks to micro-seconds
* `RADIO_DELAY_BEFORE_TX`: the delay between radio Tx request and SFD sent, in rtimer ticks
* `RADIO_DELAY_BEFORE_RX`: the delay between radio Rx request and start listening, in rtimer ticks
* optionally, `TSCH_CONF_DEFAULT_TIMESLOT_LENGTH`: the default TSCH timeslot length, useful i.e. for platforms
too slow for the default 10ms timeslots.
## Additional documentation
1. [IEEE 802.15.4e-2012 ammendment][ieee802.15.4e-2012]
2. [IETF 6TiSCH Working Group][ietf-6tisch-wg]
[ieee802.15.4e-2012]: http://standards.ieee.org/getieee802/download/802.15.4e-2012.pdf
[ietf-6tisch-wg]: https://datatracker.ietf.org/wg/6tisch

View file

@ -0,0 +1,186 @@
/*
* Copyright (c) 2015, SICS Swedish ICT.
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*
*/
/**
* \file
* TSCH adaptive time synchronization
* \author
* Atis Elsts <atis.elsts@sics.se>
*
*/
#include "tsch-adaptive-timesync.h"
#include "tsch-log.h"
#include <stdio.h>
#if TSCH_ADAPTIVE_TIMESYNC
/* Estimated drift of the time-source neighbor. Can be negative.
* Units used: ppm multiplied by 256. */
static int32_t drift_ppm;
/* Ticks compensated locally since the last timesync time */
static int32_t compensated_ticks;
/* Number of already recorded timesync history entries */
static uint8_t timesync_entry_count;
/* Since last learning of the drift; may be more than time since last timesync */
static uint32_t asn_since_last_learning;
/* Units in which drift is stored: ppm * 256 */
#define TSCH_DRIFT_UNIT (1000L * 1000 * 256)
/*---------------------------------------------------------------------------*/
/* Add a value to a moving average estimator */
static int32_t
timesync_entry_add(int32_t val, uint32_t time_delta)
{
#define NUM_TIMESYNC_ENTRIES 8
static int32_t buffer[NUM_TIMESYNC_ENTRIES];
static uint8_t pos;
int i;
if(timesync_entry_count == 0) {
pos = 0;
}
buffer[pos] = val;
if(timesync_entry_count < NUM_TIMESYNC_ENTRIES) {
timesync_entry_count++;
}
pos = (pos + 1) % NUM_TIMESYNC_ENTRIES;
val = 0;
for(i = 0; i < timesync_entry_count; ++i) {
val += buffer[i];
}
return val / timesync_entry_count;
}
/*---------------------------------------------------------------------------*/
/* Learn the neighbor drift rate at ppm */
static void
timesync_learn_drift_ticks(uint32_t time_delta_asn, int32_t drift_ticks)
{
/* should fit in 32-bit unsigned integer */
uint32_t time_delta_ticks = time_delta_asn * tsch_timing[tsch_ts_timeslot_length];
int32_t real_drift_ticks = drift_ticks + compensated_ticks;
int32_t last_drift_ppm = (int32_t)((int64_t)real_drift_ticks * TSCH_DRIFT_UNIT / time_delta_ticks);
drift_ppm = timesync_entry_add(last_drift_ppm, time_delta_ticks);
}
/*---------------------------------------------------------------------------*/
/* Either reset or update the neighbor's drift */
void
tsch_timesync_update(struct tsch_neighbor *n, uint16_t time_delta_asn, int32_t drift_correction)
{
/* Account the drift if either this is a new timesource,
* or the timedelta is not too small, as smaller timedelta
* means proportionally larger measurement error. */
if(last_timesource_neighbor != n) {
last_timesource_neighbor = n;
drift_ppm = 0;
timesync_entry_count = 0;
compensated_ticks = 0;
asn_since_last_learning = 0;
} else {
asn_since_last_learning += time_delta_asn;
if(asn_since_last_learning >= 4 * TSCH_SLOTS_PER_SECOND) {
timesync_learn_drift_ticks(asn_since_last_learning, drift_correction);
compensated_ticks = 0;
asn_since_last_learning = 0;
} else {
/* Too small timedelta, do not recalculate the drift to avoid introducing error. instead account for the corrected ticks */
compensated_ticks += drift_correction;
}
}
}
/*---------------------------------------------------------------------------*/
/* Error-accumulation free compensation algorithm */
static int32_t
compensate_internal(uint32_t time_delta_usec, int32_t drift_ppm, int32_t *remainder, int16_t *tick_conversion_error)
{
int64_t d = (int64_t)time_delta_usec * drift_ppm + *remainder;
int32_t amount = d / TSCH_DRIFT_UNIT;
int32_t amount_ticks;
*remainder = (int32_t)(d - amount * TSCH_DRIFT_UNIT);
amount += *tick_conversion_error;
amount_ticks = US_TO_RTIMERTICKS(amount);
*tick_conversion_error = amount - RTIMERTICKS_TO_US(amount_ticks);
if(ABS(amount_ticks) > RTIMER_ARCH_SECOND / 128) {
TSCH_LOG_ADD(tsch_log_message,
snprintf(log->message, sizeof(log->message),
"!too big compensation %ld delta %ld", amount_ticks, time_delta_usec));
amount_ticks = (amount_ticks > 0 ? RTIMER_ARCH_SECOND : -RTIMER_ARCH_SECOND) / 128;
}
return amount_ticks;
}
/*---------------------------------------------------------------------------*/
/* Do the compensation step before scheduling a new timeslot */
int32_t
tsch_timesync_adaptive_compensate(rtimer_clock_t time_delta_ticks)
{
int32_t result = 0;
uint32_t time_delta_usec = RTIMERTICKS_TO_US_64(time_delta_ticks);
/* compensate, but not if the neighbor is not known */
if(drift_ppm && last_timesource_neighbor != NULL) {
static int32_t remainder;
static int16_t tick_conversion_error;
result = compensate_internal(time_delta_usec, drift_ppm,
&remainder, &tick_conversion_error);
compensated_ticks += result;
}
if(TSCH_BASE_DRIFT_PPM) {
static int32_t base_drift_remainder;
static int16_t base_drift_tick_conversion_error;
result += compensate_internal(time_delta_usec, 256L * TSCH_BASE_DRIFT_PPM,
&base_drift_remainder, &base_drift_tick_conversion_error);
}
return result;
}
/*---------------------------------------------------------------------------*/
#else /* TSCH_ADAPTIVE_TIMESYNC */
/*---------------------------------------------------------------------------*/
void
tsch_timesync_update(struct tsch_neighbor *n, uint16_t time_delta_asn, int32_t drift_correction)
{
}
/*---------------------------------------------------------------------------*/
int32_t
tsch_timesync_adaptive_compensate(rtimer_clock_t delta_ticks)
{
return 0;
}
/*---------------------------------------------------------------------------*/
#endif /* TSCH_ADAPTIVE_TIMESYNC */

View file

@ -0,0 +1,89 @@
/*
* Copyright (c) 2015, SICS Swedish ICT.
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*
*/
#ifndef __TSCH_ADAPTIVE_TIMESYNC_H__
#define __TSCH_ADAPTIVE_TIMESYNC_H__
/********** Includes **********/
#include "contiki.h"
#include "net/mac/tsch/tsch-private.h"
/******** Configuration *******/
/* Use SFD timestamp for synchronization? By default we merely rely on rtimer and busy wait
* until SFD is high, which we found to provide greater accuracy on JN516x and CC2420.
* Note: for association, however, we always use SFD timestamp to know the time of arrival
* of the EB (because we do not busy-wait for the whole scanning process)
* */
#ifdef TSCH_CONF_RESYNC_WITH_SFD_TIMESTAMPS
#define TSCH_RESYNC_WITH_SFD_TIMESTAMPS TSCH_CONF_RESYNC_WITH_SFD_TIMESTAMPS
#else
#define TSCH_RESYNC_WITH_SFD_TIMESTAMPS 0
#endif
/* If enabled, remove jitter due to measurement errors */
#ifdef TSCH_CONF_TIMESYNC_REMOVE_JITTER
#define TSCH_TIMESYNC_REMOVE_JITTER TSCH_CONF_TIMESYNC_REMOVE_JITTER
#else
#define TSCH_TIMESYNC_REMOVE_JITTER TSCH_RESYNC_WITH_SFD_TIMESTAMPS
#endif
/* The jitter to remove in ticks.
* This should be the sum of measurement errors on Tx and Rx nodes.
* */
#define TSCH_TIMESYNC_MEASUREMENT_ERROR US_TO_RTIMERTICKS(32)
/* Base drift value.
* Used to compensate locally know inaccuracies, such as
* the effect of having a binary 32.768 kHz timer as the TSCH time base. */
#ifdef TSCH_CONF_BASE_DRIFT_PPM
#define TSCH_BASE_DRIFT_PPM TSCH_CONF_BASE_DRIFT_PPM
#else
#define TSCH_BASE_DRIFT_PPM 0
#endif
/* The approximate number of slots per second */
#define TSCH_SLOTS_PER_SECOND (1000000 / TSCH_DEFAULT_TS_TIMESLOT_LENGTH)
/***** External Variables *****/
/* The neighbor last used as our time source */
extern struct tsch_neighbor *last_timesource_neighbor;
/********** Functions *********/
void tsch_timesync_update(struct tsch_neighbor *n, uint16_t time_delta_asn, int32_t drift_correction);
int32_t tsch_timesync_adaptive_compensate(rtimer_clock_t delta_ticks);
#endif /* __TSCH_ADAPTIVE_TIMESYNC_H__ */

View file

@ -0,0 +1,97 @@
/*
* Copyright (c) 2015, SICS Swedish ICT.
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*
*/
/**
* \file
* TSCH 5-Byte Absolute Slot Number (ASN) management
* \author
* Simon Duquennoy <simonduq@sics.se>
*
*/
#ifndef __TSCH_ASN_H__
#define __TSCH_ASN_H__
/************ Types ***********/
/* The ASN is an absolute slot number over 5 bytes. */
struct asn_t {
uint32_t ls4b; /* least significant 4 bytes */
uint8_t ms1b; /* most significant 1 byte */
};
/* For quick modulo operation on ASN */
struct asn_divisor_t {
uint16_t val; /* Divisor value */
uint16_t asn_ms1b_remainder; /* Remainder of the operation 0x100000000 / val */
};
/************ Macros **********/
/* Initialize ASN */
#define ASN_INIT(asn, ms1b_, ls4b_) do { \
(asn).ms1b = (ms1b_); \
(asn).ls4b = (ls4b_); \
} while(0);
/* Increment an ASN by inc (32 bits) */
#define ASN_INC(asn, inc) do { \
uint32_t new_ls4b = (asn).ls4b + (inc); \
if(new_ls4b < (asn).ls4b) { (asn).ms1b++; } \
(asn).ls4b = new_ls4b; \
} while(0);
/* Decrement an ASN by inc (32 bits) */
#define ASN_DEC(asn, dec) do { \
uint32_t new_ls4b = (asn).ls4b - (dec); \
if(new_ls4b > (asn).ls4b) { (asn).ms1b--; } \
(asn).ls4b = new_ls4b; \
} while(0);
/* Returns the 32-bit diff between asn1 and asn2 */
#define ASN_DIFF(asn1, asn2) \
((asn1).ls4b - (asn2).ls4b)
/* Initialize a struct asn_divisor_t */
#define ASN_DIVISOR_INIT(div, val_) do { \
(div).val = (val_); \
(div).asn_ms1b_remainder = ((0xffffffff % (val_)) + 1) % (val_); \
} while(0);
/* Returns the result (16 bits) of a modulo operation on ASN,
* with divisor being a struct asn_divisor_t */
#define ASN_MOD(asn, div) \
((uint16_t)((asn).ls4b % (div).val) \
+ (uint16_t)((asn).ms1b * (div).asn_ms1b_remainder % (div).val)) \
% (div).val
#endif /* __TSCH_ASN_H__ */

View file

@ -0,0 +1,178 @@
/*
* Copyright (c) 2015, SICS Swedish ICT.
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*
*/
/**
* \file
* TSCH configuration
* \author
* Simon Duquennoy <simonduq@sics.se>
*/
#ifndef __TSCH_CONF_H__
#define __TSCH_CONF_H__
/********** Includes **********/
#include "contiki.h"
/******** Configuration *******/
/* Default IEEE 802.15.4e hopping sequences, obtained from https://gist.github.com/twatteyne/2e22ee3c1a802b685695 */
/* 16 channels, sequence length 16 */
#define TSCH_HOPPING_SEQUENCE_16_16 (uint8_t[]){ 16, 17, 23, 18, 26, 15, 25, 22, 19, 11, 12, 13, 24, 14, 20, 21 }
/* 4 channels, sequence length 16 */
#define TSCH_HOPPING_SEQUENCE_4_16 (uint8_t[]){ 20, 26, 25, 26, 15, 15, 25, 20, 26, 15, 26, 25, 20, 15, 20, 25 }
/* 4 channels, sequence length 4 */
#define TSCH_HOPPING_SEQUENCE_4_4 (uint8_t[]){ 15, 25, 26, 20 }
/* 1 channel, sequence length 1 */
#define TSCH_HOPPING_SEQUENCE_1_1 (uint8_t[]){ 20 }
/* Default hopping sequence, used in case hopping sequence ID == 0 */
#ifdef TSCH_CONF_DEFAULT_HOPPING_SEQUENCE
#define TSCH_DEFAULT_HOPPING_SEQUENCE TSCH_CONF_DEFAULT_HOPPING_SEQUENCE
#else
#define TSCH_DEFAULT_HOPPING_SEQUENCE TSCH_HOPPING_SEQUENCE_4_4
#endif
/* Hopping sequence used for joining (scan channels) */
#ifdef TSCH_CONF_JOIN_HOPPING_SEQUENCE
#define TSCH_JOIN_HOPPING_SEQUENCE TSCH_CONF_JOIN_HOPPING_SEQUENCE
#else
#define TSCH_JOIN_HOPPING_SEQUENCE TSCH_DEFAULT_HOPPING_SEQUENCE
#endif
/* Maximum length of the TSCH channel hopping sequence. Must be greater or
* equal to the length of TSCH_DEFAULT_HOPPING_SEQUENCE. */
#ifdef TSCH_CONF_HOPPING_SEQUENCE_MAX_LEN
#define TSCH_HOPPING_SEQUENCE_MAX_LEN TSCH_CONF_HOPPING_SEQUENCE_MAX_LEN
#else
#define TSCH_HOPPING_SEQUENCE_MAX_LEN 16
#endif
/* Timeslot timing */
#ifndef TSCH_CONF_DEFAULT_TIMESLOT_LENGTH
#define TSCH_CONF_DEFAULT_TIMESLOT_LENGTH 10000
#endif /* TSCH_CONF_DEFAULT_TIMESLOT_LENGTH */
/* Configurable Rx guard time is micro-seconds */
#ifndef TSCH_CONF_RX_WAIT
#define TSCH_CONF_RX_WAIT 2200
#endif /* TSCH_CONF_RX_WAIT */
/* The default timeslot timing in the standard is a guard time of
* 2200 us, a Tx offset of 2120 us and a Rx offset of 1120 us.
* As a result, the listening device has a guard time not centered
* on the expected Tx time. This is to be fixed in the next iteration
* of the standard. This can be enabled with:
* #define TSCH_DEFAULT_TS_TX_OFFSET 2120
* #define TSCH_DEFAULT_TS_RX_OFFSET 1120
* #define TSCH_DEFAULT_TS_RX_WAIT 2200
*
* Instead, we align the Rx guard time on expected Tx time. The Rx
* guard time is user-configurable with TSCH_CONF_RX_WAIT.
* (TS_TX_OFFSET - (TS_RX_WAIT / 2)) instead */
#if TSCH_CONF_DEFAULT_TIMESLOT_LENGTH == 10000
/* Default timeslot timing as per IEEE 802.15.4e */
#define TSCH_DEFAULT_TS_CCA_OFFSET 1800
#define TSCH_DEFAULT_TS_CCA 128
#define TSCH_DEFAULT_TS_TX_OFFSET 2120
#define TSCH_DEFAULT_TS_RX_OFFSET (TSCH_DEFAULT_TS_TX_OFFSET - (TSCH_CONF_RX_WAIT / 2))
#define TSCH_DEFAULT_TS_RX_ACK_DELAY 800
#define TSCH_DEFAULT_TS_TX_ACK_DELAY 1000
#define TSCH_DEFAULT_TS_RX_WAIT TSCH_CONF_RX_WAIT
#define TSCH_DEFAULT_TS_ACK_WAIT 400
#define TSCH_DEFAULT_TS_RX_TX 192
#define TSCH_DEFAULT_TS_MAX_ACK 2400
#define TSCH_DEFAULT_TS_MAX_TX 4256
#define TSCH_DEFAULT_TS_TIMESLOT_LENGTH 10000
#elif TSCH_CONF_DEFAULT_TIMESLOT_LENGTH == 15000
/* Default timeslot timing for platfroms requiring 15ms slots */
#define TSCH_DEFAULT_TS_CCA_OFFSET 1800
#define TSCH_DEFAULT_TS_CCA 128
#define TSCH_DEFAULT_TS_TX_OFFSET 4000
#define TSCH_DEFAULT_TS_RX_OFFSET (TSCH_DEFAULT_TS_TX_OFFSET - (TSCH_CONF_RX_WAIT / 2))
#define TSCH_DEFAULT_TS_RX_ACK_DELAY 3600
#define TSCH_DEFAULT_TS_TX_ACK_DELAY 4000
#define TSCH_DEFAULT_TS_RX_WAIT TSCH_CONF_RX_WAIT
#define TSCH_DEFAULT_TS_ACK_WAIT 800
#define TSCH_DEFAULT_TS_RX_TX 2072
#define TSCH_DEFAULT_TS_MAX_ACK 2400
#define TSCH_DEFAULT_TS_MAX_TX 4256
#define TSCH_DEFAULT_TS_TIMESLOT_LENGTH 15000
#elif TSCH_CONF_DEFAULT_TIMESLOT_LENGTH == 65000
/* 65ms timeslot, i.e. nearly the max length allowed by standard (16-bit unsigned in micro-seconds).
* Useful for running link-layer security on sky or z1 in Cooja, where only S/W security is supported.
* Note: this slot timing would require a total of 120ms. If a slot overlaps with the next active slot,
* the latter will be skipped.
* This configuration is mostly a work-around to test link-layer security in Cooja, it is recommended
* to use it with a 6TiSCH minimal schedule of length >= 2. */
#define TSCH_DEFAULT_TS_CCA_OFFSET 1800
#define TSCH_DEFAULT_TS_CCA 128
#define TSCH_DEFAULT_TS_TX_OFFSET 52000
#define TSCH_DEFAULT_TS_RX_OFFSET (TSCH_DEFAULT_TS_TX_OFFSET - (TSCH_CONF_RX_WAIT / 2))
#define TSCH_DEFAULT_TS_RX_ACK_DELAY 58600
#define TSCH_DEFAULT_TS_TX_ACK_DELAY 59000
#define TSCH_DEFAULT_TS_RX_WAIT TSCH_CONF_RX_WAIT
#define TSCH_DEFAULT_TS_ACK_WAIT 800
#define TSCH_DEFAULT_TS_RX_TX 2072
#define TSCH_DEFAULT_TS_MAX_ACK 2400
#define TSCH_DEFAULT_TS_MAX_TX 4256
#define TSCH_DEFAULT_TS_TIMESLOT_LENGTH 65000
#else
#error "TSCH: Unsupported default timeslot length"
#endif
/* A custom feature allowing upper layers to assign packets to
* a specific slotframe and link */
#ifdef TSCH_CONF_WITH_LINK_SELECTOR
#define TSCH_WITH_LINK_SELECTOR TSCH_CONF_WITH_LINK_SELECTOR
#else /* TSCH_CONF_WITH_LINK_SELECTOR */
#define TSCH_WITH_LINK_SELECTOR 0
#endif /* TSCH_CONF_WITH_LINK_SELECTOR */
/* Estimate the drift of the time-source neighbor and compensate for it? */
#ifdef TSCH_CONF_ADAPTIVE_TIMESYNC
#define TSCH_ADAPTIVE_TIMESYNC TSCH_CONF_ADAPTIVE_TIMESYNC
#else
#define TSCH_ADAPTIVE_TIMESYNC 0
#endif
#endif /* __TSCH_CONF_H__ */

View file

@ -0,0 +1,155 @@
/*
* Copyright (c) 2014, SICS Swedish ICT.
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*
*/
/**
* \file
* Log functions for TSCH, meant for logging from interrupt
* during a timeslot operation. Saves ASN, slot and link information
* and adds the log to a ringbuf for later printout.
* \author
* Simon Duquennoy <simonduq@sics.se>
*
*/
#include "contiki.h"
#include <stdio.h>
#include "net/mac/tsch/tsch.h"
#include "net/mac/tsch/tsch-queue.h"
#include "net/mac/tsch/tsch-private.h"
#include "net/mac/tsch/tsch-log.h"
#include "net/mac/tsch/tsch-packet.h"
#include "net/mac/tsch/tsch-schedule.h"
#include "net/mac/tsch/tsch-slot-operation.h"
#include "lib/ringbufindex.h"
#if TSCH_LOG_LEVEL >= 1
#define DEBUG DEBUG_PRINT
#else /* TSCH_LOG_LEVEL */
#define DEBUG DEBUG_NONE
#endif /* TSCH_LOG_LEVEL */
#include "net/ip/uip-debug.h"
#if TSCH_LOG_LEVEL >= 2 /* Skip this file for log levels 0 or 1 */
PROCESS_NAME(tsch_pending_events_process);
/* Check if TSCH_LOG_QUEUE_LEN is a power of two */
#if (TSCH_LOG_QUEUE_LEN & (TSCH_LOG_QUEUE_LEN - 1)) != 0
#error TSCH_LOG_QUEUE_LEN must be power of two
#endif
static struct ringbufindex log_ringbuf;
static struct tsch_log_t log_array[TSCH_LOG_QUEUE_LEN];
static int log_dropped = 0;
/*---------------------------------------------------------------------------*/
/* Process pending log messages */
void
tsch_log_process_pending(void)
{
static int last_log_dropped = 0;
int16_t log_index;
/* Loop on accessing (without removing) a pending input packet */
if(log_dropped != last_log_dropped) {
printf("TSCH:! logs dropped %u\n", log_dropped);
last_log_dropped = log_dropped;
}
while((log_index = ringbufindex_peek_get(&log_ringbuf)) != -1) {
struct tsch_log_t *log = &log_array[log_index];
struct tsch_slotframe *sf = tsch_schedule_get_slotframe_by_handle(log->link->slotframe_handle);
printf("TSCH: {asn-%x.%lx link-%u-%u-%u-%u ch-%u} ",
log->asn.ms1b, log->asn.ls4b,
log->link->slotframe_handle, sf ? sf->size.val : 0, log->link->timeslot, log->link->channel_offset,
tsch_calculate_channel(&log->asn, log->link->channel_offset));
switch(log->type) {
case tsch_log_tx:
printf("%s-%u-%u %u tx %d, st %d-%d",
log->tx.dest == 0 ? "bc" : "uc", log->tx.is_data, log->tx.sec_level,
log->tx.datalen,
log->tx.dest,
log->tx.mac_tx_status, log->tx.num_tx);
if(log->tx.drift_used) {
printf(", dr %d", log->tx.drift);
}
printf("\n");
break;
case tsch_log_rx:
printf("%s-%u-%u %u rx %d",
log->rx.is_unicast == 0 ? "bc" : "uc", log->rx.is_data, log->tx.sec_level,
log->rx.datalen,
log->rx.src);
if(log->rx.drift_used) {
printf(", dr %d", log->rx.drift);
}
printf(", edr %d\n", (int)log->rx.estimated_drift);
break;
case tsch_log_message:
printf("%s\n", log->message);
break;
}
/* Remove input from ringbuf */
ringbufindex_get(&log_ringbuf);
}
}
/*---------------------------------------------------------------------------*/
/* Prepare addition of a new log.
* Returns pointer to log structure if success, NULL otherwise */
struct tsch_log_t *
tsch_log_prepare_add(void)
{
int log_index = ringbufindex_peek_put(&log_ringbuf);
if(log_index != -1) {
struct tsch_log_t *log = &log_array[log_index];
log->asn = current_asn;
log->link = current_link;
return log;
} else {
log_dropped++;
return NULL;
}
}
/*---------------------------------------------------------------------------*/
/* Actually add the previously prepared log */
void
tsch_log_commit(void)
{
ringbufindex_put(&log_ringbuf);
process_poll(&tsch_pending_events_process);
}
/*---------------------------------------------------------------------------*/
/* Initialize log module */
void
tsch_log_init(void)
{
ringbufindex_init(&log_ringbuf, TSCH_LOG_QUEUE_LEN);
}
#endif /* TSCH_LOG_LEVEL */

View file

@ -0,0 +1,138 @@
/*
* Copyright (c) 2014, SICS Swedish ICT.
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*
*/
#ifndef __TSCH_LOG_H__
#define __TSCH_LOG_H__
/********** Includes **********/
#include "contiki.h"
#include "sys/rtimer.h"
#include "net/mac/tsch/tsch-private.h"
/******** Configuration *******/
/* The length of the log queue, i.e. maximum number postponed log messages */
#ifdef TSCH_LOG_CONF_QUEUE_LEN
#define TSCH_LOG_QUEUE_LEN TSCH_LOG_CONF_QUEUE_LEN
#else /* TSCH_LOG_CONF_QUEUE_LEN */
#define TSCH_LOG_QUEUE_LEN 8
#endif /* TSCH_LOG_CONF_QUEUE_LEN */
/* Returns an integer ID from a link-layer address */
#ifdef TSCH_LOG_CONF_ID_FROM_LINKADDR
#define TSCH_LOG_ID_FROM_LINKADDR(addr) TSCH_LOG_CONF_ID_FROM_LINKADDR(addr)
#else /* TSCH_LOG_ID_FROM_LINKADDR */
#define TSCH_LOG_ID_FROM_LINKADDR(addr) ((addr) ? (addr)->u8[LINKADDR_SIZE - 1] : 0)
#endif /* TSCH_LOG_ID_FROM_LINKADDR */
/* TSCH log levels:
* 0: no log
* 1: basic PRINTF enabled
* 2: basic PRINTF enabled and tsch-log module enabled */
#ifdef TSCH_LOG_CONF_LEVEL
#define TSCH_LOG_LEVEL TSCH_LOG_CONF_LEVEL
#else /* TSCH_LOG_CONF_LEVEL */
#define TSCH_LOG_LEVEL 2
#endif /* TSCH_LOG_CONF_LEVEL */
#if TSCH_LOG_LEVEL < 2 /* For log level 0 or 1, the logging functions do nothing */
#define tsch_log_init()
#define tsch_log_process_pending()
#define TSCH_LOG_ADD(log_type, init_code)
#else /* TSCH_LOG_LEVEL */
/************ Types ***********/
/* Structure for a log. Union of different types of logs */
struct tsch_log_t {
enum { tsch_log_tx,
tsch_log_rx,
tsch_log_message
} type;
struct asn_t asn;
struct tsch_link *link;
union {
char message[48];
struct {
int mac_tx_status;
int dest;
int drift;
uint8_t num_tx;
uint8_t datalen;
uint8_t is_data;
uint8_t sec_level;
uint8_t drift_used;
} tx;
struct {
int src;
int drift;
int estimated_drift;
uint8_t datalen;
uint8_t is_unicast;
uint8_t is_data;
uint8_t sec_level;
uint8_t drift_used;
} rx;
};
};
/********** Functions *********/
/* Prepare addition of a new log.
* Returns pointer to log structure if success, NULL otherwise */
struct tsch_log_t *tsch_log_prepare_add(void);
/* Actually add the previously prepared log */
void tsch_log_commit(void);
/* Initialize log module */
void tsch_log_init(void);
/* Process pending log messages */
void tsch_log_process_pending(void);
/************ Macros **********/
/* Use this macro to add a log to the queue (will be printed out
* later, after leaving interrupt context) */
#define TSCH_LOG_ADD(log_type, init_code) do { \
struct tsch_log_t *log = tsch_log_prepare_add(); \
if(log != NULL) { \
log->type = (log_type); \
init_code; \
tsch_log_commit(); \
} \
} while(0);
#endif /* TSCH_LOG_LEVEL */
#endif /* __TSCH_LOG_H__ */

View file

@ -0,0 +1,413 @@
/*
* Copyright (c) 2014, SICS Swedish ICT.
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*
*/
/**
* \file
* TSCH packet format management
* \author
* Simon Duquennoy <simonduq@sics.se>
* Beshr Al Nahas <beshr@sics.se>
*/
#include "contiki.h"
#include "net/packetbuf.h"
#include "net/mac/tsch/tsch.h"
#include "net/mac/tsch/tsch-packet.h"
#include "net/mac/tsch/tsch-private.h"
#include "net/mac/tsch/tsch-schedule.h"
#include "net/mac/tsch/tsch-security.h"
#include "net/mac/tsch/tsch-log.h"
#include "net/mac/frame802154.h"
#include "net/mac/framer-802154.h"
#include "net/netstack.h"
#include "net/llsec/anti-replay.h"
#include "lib/ccm-star.h"
#include "lib/aes-128.h"
#include <stdio.h>
#include <string.h>
#if TSCH_LOG_LEVEL >= 1
#define DEBUG DEBUG_PRINT
#else /* TSCH_LOG_LEVEL */
#define DEBUG DEBUG_NONE
#endif /* TSCH_LOG_LEVEL */
#include "net/ip/uip-debug.h"
/*---------------------------------------------------------------------------*/
/* Construct enhanced ACK packet and return ACK length */
int
tsch_packet_create_eack(uint8_t *buf, int buf_size,
linkaddr_t *dest_addr, uint8_t seqno, int16_t drift, int nack)
{
int ret;
uint8_t curr_len = 0;
frame802154_t p;
struct ieee802154_ies ies;
memset(&p, 0, sizeof(p));
p.fcf.frame_type = FRAME802154_ACKFRAME;
p.fcf.frame_version = FRAME802154_IEEE802154E_2012;
p.fcf.ie_list_present = 1;
/* Compression unset. According to IEEE802.15.4e-2012:
* - if no address is present: elide PAN ID
* - if at least one address is present: include exactly one PAN ID (dest by default) */
p.fcf.panid_compression = 0;
p.dest_pid = IEEE802154_PANID;
p.seq = seqno;
#if TSCH_PACKET_EACK_WITH_DEST_ADDR
if(dest_addr != NULL) {
p.fcf.dest_addr_mode = FRAME802154_LONGADDRMODE;
linkaddr_copy((linkaddr_t *)&p.dest_addr, dest_addr);
}
#endif
#if TSCH_PACKET_EACK_WITH_SRC_ADDR
p.fcf.src_addr_mode = FRAME802154_LONGADDRMODE;
p.src_pid = IEEE802154_PANID;
linkaddr_copy((linkaddr_t *)&p.src_addr, &linkaddr_node_addr);
#endif
#if TSCH_SECURITY_ENABLED
if(tsch_is_pan_secured) {
p.fcf.security_enabled = 1;
p.aux_hdr.security_control.security_level = TSCH_SECURITY_KEY_SEC_LEVEL_ACK;
p.aux_hdr.security_control.key_id_mode = FRAME802154_1_BYTE_KEY_ID_MODE;
p.aux_hdr.security_control.frame_counter_suppression = 1;
p.aux_hdr.security_control.frame_counter_size = 1;
p.aux_hdr.key_index = TSCH_SECURITY_KEY_INDEX_ACK;
}
#endif /* TSCH_SECURITY_ENABLED */
if((curr_len = frame802154_create(&p, buf)) == 0) {
return 0;
}
/* Append IE timesync */
memset(&ies, 0, sizeof(ies));
ies.ie_time_correction = drift;
ies.ie_is_nack = nack;
if((ret = frame80215e_create_ie_header_ack_nack_time_correction(buf+curr_len, buf_size-curr_len, &ies)) == -1) {
return -1;
}
curr_len += ret;
return curr_len;
}
/*---------------------------------------------------------------------------*/
/* Parse enhanced ACK packet, extract drift and nack */
int
tsch_packet_parse_eack(const uint8_t *buf, int buf_size,
uint8_t seqno, frame802154_t *frame, struct ieee802154_ies *ies, uint8_t *hdr_len)
{
uint8_t curr_len = 0;
int ret;
linkaddr_t dest;
if(frame == NULL || buf_size < 0) {
return 0;
}
/* Parse 802.15.4-2006 frame, i.e. all fields before Information Elements */
if((ret = frame802154_parse((uint8_t *)buf, buf_size, frame)) < 3) {
return 0;
}
if(hdr_len != NULL) {
*hdr_len = ret;
}
curr_len += ret;
/* Check seqno */
if(seqno != frame->seq) {
return 0;
}
/* Check destination PAN ID */
if(frame802154_check_dest_panid(frame) == 0) {
return 0;
}
/* Check destination address (if any) */
if(frame802154_extract_linkaddr(frame, NULL, &dest) == 0 ||
(!linkaddr_cmp(&dest, &linkaddr_node_addr)
&& !linkaddr_cmp(&dest, &linkaddr_null))) {
return 0;
}
if(ies != NULL) {
memset(ies, 0, sizeof(struct ieee802154_ies));
}
if(frame->fcf.ie_list_present) {
int mic_len = 0;
#if TSCH_SECURITY_ENABLED
/* Check if there is space for the security MIC (if any) */
mic_len = tsch_security_mic_len(frame);
if(buf_size < curr_len + mic_len) {
return 0;
}
#endif /* TSCH_SECURITY_ENABLED */
/* Parse information elements. We need to substract the MIC length, as the exact payload len is needed while parsing */
if((ret = frame802154e_parse_information_elements(buf + curr_len, buf_size - curr_len - mic_len, ies)) == -1) {
return 0;
}
curr_len += ret;
}
if(hdr_len != NULL) {
*hdr_len += ies->ie_payload_ie_offset;
}
return curr_len;
}
/*---------------------------------------------------------------------------*/
/* Create an EB packet */
int
tsch_packet_create_eb(uint8_t *buf, int buf_size, uint8_t seqno,
uint8_t *hdr_len, uint8_t *tsch_sync_ie_offset)
{
int ret = 0;
uint8_t curr_len = 0;
uint8_t mlme_ie_offset;
frame802154_t p;
struct ieee802154_ies ies;
if(buf_size < TSCH_PACKET_MAX_LEN) {
return 0;
}
/* Create 802.15.4 header */
memset(&p, 0, sizeof(p));
p.fcf.frame_type = FRAME802154_BEACONFRAME;
p.fcf.ie_list_present = 1;
p.fcf.frame_version = FRAME802154_IEEE802154E_2012;
p.fcf.src_addr_mode = FRAME802154_LONGADDRMODE;
p.fcf.dest_addr_mode = FRAME802154_SHORTADDRMODE;
p.seq = seqno;
p.fcf.sequence_number_suppression = FRAME802154_SUPPR_SEQNO;
/* It is important not to compress PAN ID, as this would result in not including either
* source nor destination PAN ID, leaving potential joining devices unaware of the PAN ID. */
p.fcf.panid_compression = 0;
p.src_pid = frame802154_get_pan_id();
p.dest_pid = frame802154_get_pan_id();
linkaddr_copy((linkaddr_t *)&p.src_addr, &linkaddr_node_addr);
p.dest_addr[0] = 0xff;
p.dest_addr[1] = 0xff;
#if TSCH_SECURITY_ENABLED
if(tsch_is_pan_secured) {
p.fcf.security_enabled = packetbuf_attr(PACKETBUF_ATTR_SECURITY_LEVEL) > 0;
p.aux_hdr.security_control.security_level = packetbuf_attr(PACKETBUF_ATTR_SECURITY_LEVEL);
p.aux_hdr.security_control.key_id_mode = packetbuf_attr(PACKETBUF_ATTR_KEY_ID_MODE);
p.aux_hdr.security_control.frame_counter_suppression = 1;
p.aux_hdr.security_control.frame_counter_size = 1;
p.aux_hdr.key_index = packetbuf_attr(PACKETBUF_ATTR_KEY_INDEX);
}
#endif /* TSCH_SECURITY_ENABLED */
if((curr_len = frame802154_create(&p, buf)) == 0) {
return 0;
}
/* Prepare Information Elements for inclusion in the EB */
memset(&ies, 0, sizeof(ies));
/* Add TSCH timeslot timing IE. */
#if TSCH_PACKET_EB_WITH_TIMESLOT_TIMING
{
int i;
ies.ie_tsch_timeslot_id = 1;
for(i = 0; i < tsch_ts_elements_count; i++) {
ies.ie_tsch_timeslot[i] = RTIMERTICKS_TO_US(tsch_timing[i]);
}
}
#endif /* TSCH_PACKET_EB_WITH_TIMESLOT_TIMING */
/* Add TSCH hopping sequence IE */
#if TSCH_PACKET_EB_WITH_HOPPING_SEQUENCE
if(tsch_hopping_sequence_length.val <= sizeof(ies.ie_hopping_sequence_list)) {
ies.ie_channel_hopping_sequence_id = 1;
ies.ie_hopping_sequence_len = tsch_hopping_sequence_length.val;
memcpy(ies.ie_hopping_sequence_list, tsch_hopping_sequence, ies.ie_hopping_sequence_len);
}
#endif /* TSCH_PACKET_EB_WITH_HOPPING_SEQUENCE */
/* Add Slotframe and Link IE */
#if TSCH_PACKET_EB_WITH_SLOTFRAME_AND_LINK
{
/* Send slotframe 0 with link at timeslot 0 */
struct tsch_slotframe *sf0 = tsch_schedule_get_slotframe_by_handle(0);
struct tsch_link *link0 = tsch_schedule_get_link_by_timeslot(sf0, 0);
if(sf0 && link0) {
ies.ie_tsch_slotframe_and_link.num_slotframes = 1;
ies.ie_tsch_slotframe_and_link.slotframe_handle = sf0->handle;
ies.ie_tsch_slotframe_and_link.slotframe_size = sf0->size.val;
ies.ie_tsch_slotframe_and_link.num_links = 1;
ies.ie_tsch_slotframe_and_link.links[0].timeslot = link0->timeslot;
ies.ie_tsch_slotframe_and_link.links[0].channel_offset = link0->channel_offset;
ies.ie_tsch_slotframe_and_link.links[0].link_options = link0->link_options;
}
}
#endif /* TSCH_PACKET_EB_WITH_SLOTFRAME_AND_LINK */
/* First add header-IE termination IE to stipulate that next come payload IEs */
if((ret = frame80215e_create_ie_header_list_termination_1(buf + curr_len, buf_size - curr_len, &ies)) == -1) {
return -1;
}
curr_len += ret;
/* We start payload IEs, save offset */
if(hdr_len != NULL) {
*hdr_len = curr_len;
}
/* Save offset of the MLME IE descriptor, we need to know the total length
* before writing it */
mlme_ie_offset = curr_len;
curr_len += 2; /* Space needed for MLME descriptor */
/* Save the offset of the TSCH Synchronization IE, needed to update ASN and join priority before sending */
if(tsch_sync_ie_offset != NULL) {
*tsch_sync_ie_offset = curr_len;
}
if((ret = frame80215e_create_ie_tsch_synchronization(buf + curr_len, buf_size - curr_len, &ies)) == -1) {
return -1;
}
curr_len += ret;
if((ret = frame80215e_create_ie_tsch_timeslot(buf + curr_len, buf_size - curr_len, &ies)) == -1) {
return -1;
}
curr_len += ret;
if((ret = frame80215e_create_ie_tsch_channel_hopping_sequence(buf + curr_len, buf_size - curr_len, &ies)) == -1) {
return -1;
}
curr_len += ret;
if((ret = frame80215e_create_ie_tsch_slotframe_and_link(buf + curr_len, buf_size - curr_len, &ies)) == -1) {
return -1;
}
curr_len += ret;
ies.ie_mlme_len = curr_len - mlme_ie_offset - 2;
if((ret = frame80215e_create_ie_mlme(buf + mlme_ie_offset, buf_size - mlme_ie_offset, &ies)) == -1) {
return -1;
}
/* Payload IE list termination: optional */
/*
if((ret = frame80215e_create_ie_payload_list_termination(buf + curr_len, buf_size - curr_len, &ies)) == -1) {
return -1;
}
curr_len += ret;
*/
return curr_len;
}
/*---------------------------------------------------------------------------*/
/* Update ASN in EB packet */
int
tsch_packet_update_eb(uint8_t *buf, int buf_size, uint8_t tsch_sync_ie_offset)
{
struct ieee802154_ies ies;
ies.ie_asn = current_asn;
ies.ie_join_priority = tsch_join_priority;
frame80215e_create_ie_tsch_synchronization(buf+tsch_sync_ie_offset, buf_size-tsch_sync_ie_offset, &ies);
return 1;
}
/*---------------------------------------------------------------------------*/
/* Parse a IEEE 802.15.4e TSCH Enhanced Beacon (EB) */
int
tsch_packet_parse_eb(const uint8_t *buf, int buf_size,
frame802154_t *frame, struct ieee802154_ies *ies, uint8_t *hdr_len, int frame_without_mic)
{
uint8_t curr_len = 0;
int ret;
if(frame == NULL || buf_size < 0) {
return 0;
}
/* Parse 802.15.4-2006 frame, i.e. all fields before Information Elements */
if((ret = frame802154_parse((uint8_t *)buf, buf_size, frame)) == 0) {
PRINTF("TSCH:! parse_eb: failed to parse frame\n");
return 0;
}
if(frame->fcf.frame_version < FRAME802154_IEEE802154E_2012
|| frame->fcf.frame_type != FRAME802154_BEACONFRAME) {
PRINTF("TSCH:! parse_eb: frame is not a valid TSCH beacon. Frame version %u, type %u, FCF %02x %02x\n",
frame->fcf.frame_version, frame->fcf.frame_type, buf[0], buf[1]);
PRINTF("TSCH:! parse_eb: frame was from 0x%x/", frame->src_pid);
PRINTLLADDR((const uip_lladdr_t *)&frame->src_addr);
PRINTF(" to 0x%x/", frame->dest_pid);
PRINTLLADDR((const uip_lladdr_t *)&frame->dest_addr);
PRINTF("\n");
return 0;
}
if(hdr_len != NULL) {
*hdr_len = ret;
}
curr_len += ret;
if(ies != NULL) {
memset(ies, 0, sizeof(struct ieee802154_ies));
ies->ie_join_priority = 0xff; /* Use max value in case the Beacon does not include a join priority */
}
if(frame->fcf.ie_list_present) {
/* Calculate space needed for the security MIC, if any, before attempting to parse IEs */
int mic_len = 0;
#if TSCH_SECURITY_ENABLED
if(!frame_without_mic) {
mic_len = tsch_security_mic_len(frame);
if(buf_size < curr_len + mic_len) {
return 0;
}
}
#endif /* TSCH_SECURITY_ENABLED */
/* Parse information elements. We need to substract the MIC length, as the exact payload len is needed while parsing */
if((ret = frame802154e_parse_information_elements(buf + curr_len, buf_size - curr_len - mic_len, ies)) == -1) {
PRINTF("TSCH:! parse_eb: failed to parse IEs\n");
return 0;
}
curr_len += ret;
}
if(hdr_len != NULL) {
*hdr_len += ies->ie_payload_ie_offset;
}
return curr_len;
}
/*---------------------------------------------------------------------------*/

View file

@ -0,0 +1,104 @@
/*
* Copyright (c) 2014, SICS Swedish ICT.
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*
*/
#ifndef __TSCH_PACKET_H__
#define __TSCH_PACKET_H__
/********** Includes **********/
#include "contiki.h"
#include "net/mac/tsch/tsch-private.h"
#include "net/mac/frame802154.h"
#include "net/mac/frame802154e-ie.h"
/******** Configuration *******/
/* TSCH EB: include timeslot timing Information Element? */
#ifdef TSCH_PACKET_CONF_EB_WITH_TIMESLOT_TIMING
#define TSCH_PACKET_EB_WITH_TIMESLOT_TIMING TSCH_PACKET_CONF_EB_WITH_TIMESLOT_TIMING
#else
#define TSCH_PACKET_EB_WITH_TIMESLOT_TIMING 0
#endif
/* TSCH EB: include hopping sequence Information Element? */
#ifdef TSCH_PACKET_CONF_EB_WITH_HOPPING_SEQUENCE
#define TSCH_PACKET_EB_WITH_HOPPING_SEQUENCE TSCH_PACKET_CONF_EB_WITH_HOPPING_SEQUENCE
#else
#define TSCH_PACKET_EB_WITH_HOPPING_SEQUENCE 0
#endif
/* TSCH EB: include slotframe and link Information Element? */
#ifdef TSCH_PACKET_CONF_EB_WITH_SLOTFRAME_AND_LINK
#define TSCH_PACKET_EB_WITH_SLOTFRAME_AND_LINK TSCH_PACKET_CONF_EB_WITH_SLOTFRAME_AND_LINK
#else
#define TSCH_PACKET_EB_WITH_SLOTFRAME_AND_LINK 0
#endif
/* Include source address in ACK? */
#ifdef TSCH_PACKET_CONF_EACK_WITH_SRC_ADDR
#define TSCH_PACKET_EACK_WITH_SRC_ADDR TSCH_PACKET_CONF_EACK_WITH_SRC_ADDR
#else
#define TSCH_PACKET_EACK_WITH_SRC_ADDR 0
#endif
/* Include destination address in ACK? */
#ifdef TSCH_PACKET_CONF_EACK_WITH_DEST_ADDR
#define TSCH_PACKET_EACK_WITH_DEST_ADDR TSCH_PACKET_CONF_EACK_WITH_DEST_ADDR
#else
#define TSCH_PACKET_EACK_WITH_DEST_ADDR 1 /* Include destination address
by default, useful in case of duplicate seqno */
#endif
/********** Constants *********/
/* Max TSCH packet lenght */
#define TSCH_PACKET_MAX_LEN 127
/********** Functions *********/
/* Construct enhanced ACK packet and return ACK length */
int tsch_packet_create_eack(uint8_t *buf, int buf_size,
linkaddr_t *dest_addr, uint8_t seqno, int16_t drift, int nack);
/* Parse enhanced ACK packet, extract drift and nack */
int tsch_packet_parse_eack(const uint8_t *buf, int buf_size,
uint8_t seqno, frame802154_t *frame, struct ieee802154_ies *ies, uint8_t *hdr_len);
/* Create an EB packet */
int tsch_packet_create_eb(uint8_t *buf, int buf_size,
uint8_t seqno, uint8_t *hdr_len, uint8_t *tsch_sync_ie_ptr);
/* Update ASN in EB packet */
int tsch_packet_update_eb(uint8_t *buf, int buf_size, uint8_t tsch_sync_ie_offset);
/* Parse EB and extract ASN and join priority */
int tsch_packet_parse_eb(const uint8_t *buf, int buf_size,
frame802154_t *frame, struct ieee802154_ies *ies,
uint8_t *hdrlen, int frame_without_mic);
#endif /* __TSCH_PACKET_H__ */

View file

@ -0,0 +1,115 @@
/*
* Copyright (c) 2014, SICS Swedish ICT.
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*
*/
/**
* \file
* Private TSCH definitions
* (meant for use by TSCH implementation files only)
* \author
* Simon Duquennoy <simonduq@sics.se>
* Beshr Al Nahas <beshr@sics.se>
*/
#ifndef __TSCH_PRIVATE_H__
#define __TSCH_PRIVATE_H__
/********** Includes **********/
#include "contiki.h"
#include "net/linkaddr.h"
#include "net/mac/tsch/tsch-asn.h"
#include "net/mac/tsch/tsch-conf.h"
/************ Types ***********/
/* TSCH timeslot timing elements. Used to index timeslot timing
* of different units, such as rtimer tick or micro-second */
enum tsch_timeslot_timing_elements {
tsch_ts_cca_offset,
tsch_ts_cca,
tsch_ts_tx_offset,
tsch_ts_rx_offset,
tsch_ts_rx_ack_delay,
tsch_ts_tx_ack_delay,
tsch_ts_rx_wait,
tsch_ts_ack_wait,
tsch_ts_rx_tx,
tsch_ts_max_ack,
tsch_ts_max_tx,
tsch_ts_timeslot_length,
tsch_ts_elements_count, /* Not a timing element */
};
/***** External Variables *****/
/* 802.15.4 broadcast MAC address */
extern const linkaddr_t tsch_broadcast_address;
/* The address we use to identify EB queue */
extern const linkaddr_t tsch_eb_address;
/* The current Absolute Slot Number (ASN) */
extern struct asn_t current_asn;
extern uint8_t tsch_join_priority;
extern struct tsch_link *current_link;
/* TSCH channel hopping sequence */
extern uint8_t tsch_hopping_sequence[TSCH_HOPPING_SEQUENCE_MAX_LEN];
extern struct asn_divisor_t tsch_hopping_sequence_length;
/* TSCH timeslot timing (in rtimer ticks) */
extern rtimer_clock_t tsch_timing[tsch_ts_elements_count];
/* TSCH processes */
PROCESS_NAME(tsch_process);
PROCESS_NAME(tsch_send_eb_process);
PROCESS_NAME(tsch_pending_events_process);
/********** Functions *********/
/* Set TSCH to send a keepalive message after TSCH_KEEPALIVE_TIMEOUT */
void tsch_schedule_keepalive(void);
/* Leave the TSCH network */
void tsch_disassociate(void);
/************ Macros **********/
/* Calculate packet tx/rx duration in rtimer ticks based on sent
* packet len in bytes with 802.15.4 250kbps data rate.
* One byte = 32us. Add two bytes for CRC and one for len field */
#define TSCH_PACKET_DURATION(len) US_TO_RTIMERTICKS(32 * ((len) + 3))
/* Convert rtimer ticks to clock and vice versa */
#define TSCH_CLOCK_TO_TICKS(c) (((c) * RTIMER_SECOND) / CLOCK_SECOND)
#define TSCH_CLOCK_TO_SLOTS(c, timeslot_length) (TSCH_CLOCK_TO_TICKS(c) / timeslot_length)
/* Wait for a condition with timeout t0+offset. */
#define BUSYWAIT_UNTIL_ABS(cond, t0, offset) \
while(!(cond) && RTIMER_CLOCK_LT(RTIMER_NOW(), (t0) + (offset))) ;
#endif /* __TSCH_PRIVATE_H__ */

View file

@ -0,0 +1,466 @@
/*
* Copyright (c) 2014, SICS Swedish ICT.
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*
*/
/**
* \file
* Per-neighbor packet queues for TSCH MAC.
* The list of neighbors uses the TSCH lock, but per-neighbor packet array are lock-free.
* Read-only operation on neighbor and packets are allowed from interrupts and outside of them.
* *Other operations are allowed outside of interrupt only.*
* \author
* Simon Duquennoy <simonduq@sics.se>
* Beshr Al Nahas <beshr@sics.se>
* Domenico De Guglielmo <d.deguglielmo@iet.unipi.it >
*/
#include "contiki.h"
#include "lib/list.h"
#include "lib/memb.h"
#include "lib/random.h"
#include "net/queuebuf.h"
#include "net/mac/rdc.h"
#include "net/mac/tsch/tsch.h"
#include "net/mac/tsch/tsch-private.h"
#include "net/mac/tsch/tsch-queue.h"
#include "net/mac/tsch/tsch-schedule.h"
#include "net/mac/tsch/tsch-slot-operation.h"
#include "net/mac/tsch/tsch-log.h"
#include <string.h>
#if TSCH_LOG_LEVEL >= 1
#define DEBUG DEBUG_PRINT
#else /* TSCH_LOG_LEVEL */
#define DEBUG DEBUG_NONE
#endif /* TSCH_LOG_LEVEL */
#include "net/ip/uip-debug.h"
/* Check if TSCH_QUEUE_NUM_PER_NEIGHBOR is power of two */
#if (TSCH_QUEUE_NUM_PER_NEIGHBOR & (TSCH_QUEUE_NUM_PER_NEIGHBOR - 1)) != 0
#error TSCH_QUEUE_NUM_PER_NEIGHBOR must be power of two
#endif
/* We have as many packets are there are queuebuf in the system */
MEMB(packet_memb, struct tsch_packet, QUEUEBUF_NUM);
MEMB(neighbor_memb, struct tsch_neighbor, TSCH_QUEUE_MAX_NEIGHBOR_QUEUES);
LIST(neighbor_list);
/* Broadcast and EB virtual neighbors */
struct tsch_neighbor *n_broadcast;
struct tsch_neighbor *n_eb;
/*---------------------------------------------------------------------------*/
/* Add a TSCH neighbor */
struct tsch_neighbor *
tsch_queue_add_nbr(const linkaddr_t *addr)
{
struct tsch_neighbor *n = NULL;
/* If we have an entry for this neighbor already, we simply update it */
n = tsch_queue_get_nbr(addr);
if(n == NULL) {
if(tsch_get_lock()) {
/* Allocate a neighbor */
n = memb_alloc(&neighbor_memb);
if(n != NULL) {
/* Initialize neighbor entry */
memset(n, 0, sizeof(struct tsch_neighbor));
ringbufindex_init(&n->tx_ringbuf, TSCH_QUEUE_NUM_PER_NEIGHBOR);
linkaddr_copy(&n->addr, addr);
n->is_broadcast = linkaddr_cmp(addr, &tsch_eb_address)
|| linkaddr_cmp(addr, &tsch_broadcast_address);
tsch_queue_backoff_reset(n);
/* Add neighbor to the list */
list_add(neighbor_list, n);
}
tsch_release_lock();
}
}
return n;
}
/*---------------------------------------------------------------------------*/
/* Get a TSCH neighbor */
struct tsch_neighbor *
tsch_queue_get_nbr(const linkaddr_t *addr)
{
if(!tsch_is_locked()) {
struct tsch_neighbor *n = list_head(neighbor_list);
while(n != NULL) {
if(linkaddr_cmp(&n->addr, addr)) {
return n;
}
n = list_item_next(n);
}
}
return NULL;
}
/*---------------------------------------------------------------------------*/
/* Get a TSCH time source (we currently assume there is only one) */
struct tsch_neighbor *
tsch_queue_get_time_source(void)
{
if(!tsch_is_locked()) {
struct tsch_neighbor *curr_nbr = list_head(neighbor_list);
while(curr_nbr != NULL) {
if(curr_nbr->is_time_source) {
return curr_nbr;
}
curr_nbr = list_item_next(curr_nbr);
}
}
return NULL;
}
/*---------------------------------------------------------------------------*/
/* Update TSCH time source */
int
tsch_queue_update_time_source(const linkaddr_t *new_addr)
{
if(!tsch_is_locked()) {
if(!tsch_is_coordinator) {
struct tsch_neighbor *old_time_src = tsch_queue_get_time_source();
struct tsch_neighbor *new_time_src = NULL;
if(new_addr != NULL) {
/* Get/add neighbor, return 0 in case of failure */
new_time_src = tsch_queue_add_nbr(new_addr);
if(new_time_src == NULL) {
return 0;
}
}
if(new_time_src != old_time_src) {
PRINTF("TSCH: update time source: %u -> %u\n",
TSCH_LOG_ID_FROM_LINKADDR(old_time_src ? &old_time_src->addr : NULL),
TSCH_LOG_ID_FROM_LINKADDR(new_time_src ? &new_time_src->addr : NULL));
/* Update time source */
if(new_time_src != NULL) {
new_time_src->is_time_source = 1;
}
if(old_time_src != NULL) {
old_time_src->is_time_source = 0;
}
#ifdef TSCH_CALLBACK_NEW_TIME_SOURCE
TSCH_CALLBACK_NEW_TIME_SOURCE(old_time_src, new_time_src);
#endif
}
return 1;
}
}
return 0;
}
/*---------------------------------------------------------------------------*/
/* Flush a neighbor queue */
static void
tsch_queue_flush_nbr_queue(struct tsch_neighbor *n)
{
while(!tsch_queue_is_empty(n)) {
struct tsch_packet *p = tsch_queue_remove_packet_from_queue(n);
if(p != NULL) {
/* Set return status for packet_sent callback */
p->ret = MAC_TX_ERR;
PRINTF("TSCH-queue:! flushing packet\n");
/* Call packet_sent callback */
mac_call_sent_callback(p->sent, p->ptr, p->ret, p->transmissions);
/* Free packet queuebuf */
tsch_queue_free_packet(p);
}
}
}
/*---------------------------------------------------------------------------*/
/* Remove TSCH neighbor queue */
static void
tsch_queue_remove_nbr(struct tsch_neighbor *n)
{
if(n != NULL) {
if(tsch_get_lock()) {
/* Remove neighbor from list */
list_remove(neighbor_list, n);
tsch_release_lock();
/* Flush queue */
tsch_queue_flush_nbr_queue(n);
/* Free neighbor */
memb_free(&neighbor_memb, n);
}
}
}
/*---------------------------------------------------------------------------*/
/* Add packet to neighbor queue. Use same lockfree implementation as ringbuf.c (put is atomic) */
struct tsch_packet *
tsch_queue_add_packet(const linkaddr_t *addr, mac_callback_t sent, void *ptr)
{
struct tsch_neighbor *n = NULL;
int16_t put_index = -1;
struct tsch_packet *p = NULL;
if(!tsch_is_locked()) {
n = tsch_queue_add_nbr(addr);
if(n != NULL) {
put_index = ringbufindex_peek_put(&n->tx_ringbuf);
if(put_index != -1) {
p = memb_alloc(&packet_memb);
if(p != NULL) {
/* Enqueue packet */
#ifdef TSCH_CALLBACK_PACKET_READY
TSCH_CALLBACK_PACKET_READY();
#endif
p->qb = queuebuf_new_from_packetbuf();
if(p->qb != NULL) {
p->sent = sent;
p->ptr = ptr;
p->ret = MAC_TX_DEFERRED;
p->transmissions = 0;
/* Add to ringbuf (actual add committed through atomic operation) */
n->tx_array[put_index] = p;
ringbufindex_put(&n->tx_ringbuf);
return p;
} else {
memb_free(&packet_memb, p);
}
}
}
}
}
PRINTF("TSCH-queue:! add packet failed: %u %p %d %p %p\n", tsch_is_locked(), n, put_index, p, p ? p->qb : NULL);
return 0;
}
/*---------------------------------------------------------------------------*/
/* Returns the number of packets currently in the queue */
int
tsch_queue_packet_count(const linkaddr_t *addr)
{
struct tsch_neighbor *n = NULL;
if(!tsch_is_locked()) {
n = tsch_queue_add_nbr(addr);
if(n != NULL) {
return ringbufindex_elements(&n->tx_ringbuf);
}
}
return -1;
}
/*---------------------------------------------------------------------------*/
/* Remove first packet from a neighbor queue */
struct tsch_packet *
tsch_queue_remove_packet_from_queue(struct tsch_neighbor *n)
{
if(!tsch_is_locked()) {
if(n != NULL) {
/* Get and remove packet from ringbuf (remove committed through an atomic operation */
int16_t get_index = ringbufindex_get(&n->tx_ringbuf);
if(get_index != -1) {
return n->tx_array[get_index];
} else {
return NULL;
}
}
}
return NULL;
}
/*---------------------------------------------------------------------------*/
/* Free a packet */
void
tsch_queue_free_packet(struct tsch_packet *p)
{
if(p != NULL) {
queuebuf_free(p->qb);
memb_free(&packet_memb, p);
}
}
/*---------------------------------------------------------------------------*/
/* Flush all neighbor queues */
void
tsch_queue_flush_all(void)
{
/* Deallocate unneeded neighbors */
if(!tsch_is_locked()) {
struct tsch_neighbor *n = list_head(neighbor_list);
while(n != NULL) {
struct tsch_neighbor *next_n = list_item_next(n);
tsch_queue_flush_nbr_queue(n);
n = next_n;
}
}
}
/*---------------------------------------------------------------------------*/
/* Deallocate neighbors with empty queue */
void
tsch_queue_free_unused_neighbors(void)
{
/* Deallocate unneeded neighbors */
if(!tsch_is_locked()) {
struct tsch_neighbor *n = list_head(neighbor_list);
while(n != NULL) {
struct tsch_neighbor *next_n = list_item_next(n);
/* Queue is empty, no tx link to this neighbor: deallocate.
* Always keep time source and virtual broadcast neighbors. */
if(!n->is_broadcast && !n->is_time_source && !n->tx_links_count
&& tsch_queue_is_empty(n)) {
tsch_queue_remove_nbr(n);
}
n = next_n;
}
}
}
/*---------------------------------------------------------------------------*/
/* Is the neighbor queue empty? */
int
tsch_queue_is_empty(const struct tsch_neighbor *n)
{
return !tsch_is_locked() && n != NULL && ringbufindex_empty(&n->tx_ringbuf);
}
/*---------------------------------------------------------------------------*/
/* Returns the first packet from a neighbor queue */
struct tsch_packet *
tsch_queue_get_packet_for_nbr(const struct tsch_neighbor *n, struct tsch_link *link)
{
if(!tsch_is_locked()) {
int is_shared_link = link != NULL && link->link_options & LINK_OPTION_SHARED;
if(n != NULL) {
int16_t get_index = ringbufindex_peek_get(&n->tx_ringbuf);
if(get_index != -1 &&
!(is_shared_link && !tsch_queue_backoff_expired(n))) { /* If this is a shared link,
make sure the backoff has expired */
#if TSCH_WITH_LINK_SELECTOR
int packet_attr_slotframe = queuebuf_attr(n->tx_array[get_index]->qb, PACKETBUF_ATTR_TSCH_SLOTFRAME);
int packet_attr_timeslot = queuebuf_attr(n->tx_array[get_index]->qb, PACKETBUF_ATTR_TSCH_TIMESLOT);
if(packet_attr_slotframe != 0xffff && packet_attr_slotframe != link->slotframe_handle) {
return NULL;
}
if(packet_attr_timeslot != 0xffff && packet_attr_timeslot != link->timeslot) {
return NULL;
}
#endif
return n->tx_array[get_index];
}
}
}
return NULL;
}
/*---------------------------------------------------------------------------*/
/* Returns the head packet from a neighbor queue (from neighbor address) */
struct tsch_packet *
tsch_queue_get_packet_for_dest_addr(const linkaddr_t *addr, struct tsch_link *link)
{
if(!tsch_is_locked()) {
return tsch_queue_get_packet_for_nbr(tsch_queue_get_nbr(addr), link);
}
return NULL;
}
/* Returns the head packet of any neighbor queue with zero backoff counter.
* Writes pointer to the neighbor in *n */
struct tsch_packet *
tsch_queue_get_unicast_packet_for_any(struct tsch_neighbor **n, struct tsch_link *link)
{
if(!tsch_is_locked()) {
struct tsch_neighbor *curr_nbr = list_head(neighbor_list);
struct tsch_packet *p = NULL;
while(curr_nbr != NULL) {
if(!curr_nbr->is_broadcast && curr_nbr->tx_links_count == 0) {
/* Only look up for non-broadcast neighbors we do not have a tx link to */
p = tsch_queue_get_packet_for_nbr(curr_nbr, link);
if(p != NULL) {
if(n != NULL) {
*n = curr_nbr;
}
return p;
}
}
curr_nbr = list_item_next(curr_nbr);
}
}
return NULL;
}
/*---------------------------------------------------------------------------*/
/* May the neighbor transmit over a shared link? */
int
tsch_queue_backoff_expired(const struct tsch_neighbor *n)
{
return n->backoff_window == 0;
}
/*---------------------------------------------------------------------------*/
/* Reset neighbor backoff */
void
tsch_queue_backoff_reset(struct tsch_neighbor *n)
{
n->backoff_window = 0;
n->backoff_exponent = TSCH_MAC_MIN_BE;
}
/*---------------------------------------------------------------------------*/
/* Increment backoff exponent, pick a new window */
void
tsch_queue_backoff_inc(struct tsch_neighbor *n)
{
/* Increment exponent */
n->backoff_exponent = MIN(n->backoff_exponent + 1, TSCH_MAC_MAX_BE);
/* Pick a window (number of shared slots to skip). Ignore least significant
* few bits, which, on some embedded implementations of rand (e.g. msp430-libc),
* are known to have poor pseudo-random properties. */
n->backoff_window = (random_rand() >> 6) % (1 << n->backoff_exponent);
/* Add one to the window as we will decrement it at the end of the current slot
* through tsch_queue_update_all_backoff_windows */
n->backoff_window++;
}
/*---------------------------------------------------------------------------*/
/* Decrement backoff window for all queues directed at dest_addr */
void
tsch_queue_update_all_backoff_windows(const linkaddr_t *dest_addr)
{
if(!tsch_is_locked()) {
int is_broadcast = linkaddr_cmp(dest_addr, &tsch_broadcast_address);
struct tsch_neighbor *n = list_head(neighbor_list);
while(n != NULL) {
if(n->backoff_window != 0 /* Is the queue in backoff state? */
&& ((n->tx_links_count == 0 && is_broadcast)
|| (n->tx_links_count > 0 && linkaddr_cmp(dest_addr, &n->addr)))) {
n->backoff_window--;
}
n = list_item_next(n);
}
}
}
/*---------------------------------------------------------------------------*/
/* Initialize TSCH queue module */
void
tsch_queue_init(void)
{
list_init(neighbor_list);
memb_init(&neighbor_memb);
memb_init(&packet_memb);
/* Add virtual EB and the broadcast neighbors */
n_eb = tsch_queue_add_nbr(&tsch_eb_address);
n_broadcast = tsch_queue_add_nbr(&tsch_broadcast_address);
}
/*---------------------------------------------------------------------------*/

View file

@ -0,0 +1,193 @@
/*
* Copyright (c) 2014, SICS Swedish ICT.
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*
*/
#ifndef __TSCH_QUEUE_H__
#define __TSCH_QUEUE_H__
/********** Includes **********/
#include "contiki.h"
#include "lib/ringbufindex.h"
#include "net/linkaddr.h"
#include "net/mac/tsch/tsch-schedule.h"
/******** Configuration *******/
/* The maximum number of outgoing packets towards each neighbor
* Must be power of two to enable atomic ringbuf operations.
* Note: the total number of outgoing packets in the system (for
* all neighbors) is defined via QUEUEBUF_CONF_NUM */
#ifdef TSCH_QUEUE_CONF_NUM_PER_NEIGHBOR
#define TSCH_QUEUE_NUM_PER_NEIGHBOR TSCH_QUEUE_CONF_NUM_PER_NEIGHBOR
#else
/* By default, round QUEUEBUF_CONF_NUM to next power of two
* (in the range [4;256]) */
#if QUEUEBUF_CONF_NUM <= 4
#define TSCH_QUEUE_NUM_PER_NEIGHBOR 4
#elif QUEUEBUF_CONF_NUM <= 8
#define TSCH_QUEUE_NUM_PER_NEIGHBOR 8
#elif QUEUEBUF_CONF_NUM <= 16
#define TSCH_QUEUE_NUM_PER_NEIGHBOR 16
#elif QUEUEBUF_CONF_NUM <= 32
#define TSCH_QUEUE_NUM_PER_NEIGHBOR 32
#elif QUEUEBUF_CONF_NUM <= 64
#define TSCH_QUEUE_NUM_PER_NEIGHBOR 64
#elif QUEUEBUF_CONF_NUM <= 128
#define TSCH_QUEUE_NUM_PER_NEIGHBOR 128
#else
#define TSCH_QUEUE_NUM_PER_NEIGHBOR 256
#endif
#endif
/* The number of neighbor queues. There are two queues allocated at all times:
* one for EBs, one for broadcasts. Other queues are for unicast to neighbors */
#ifdef TSCH_QUEUE_CONF_MAX_NEIGHBOR_QUEUES
#define TSCH_QUEUE_MAX_NEIGHBOR_QUEUES TSCH_QUEUE_CONF_MAX_NEIGHBOR_QUEUES
#else
#define TSCH_QUEUE_MAX_NEIGHBOR_QUEUES ((NBR_TABLE_CONF_MAX_NEIGHBORS) + 2)
#endif
/* TSCH CSMA-CA parameters, see IEEE 802.15.4e-2012 */
/* Min backoff exponent */
#ifdef TSCH_CONF_MAC_MIN_BE
#define TSCH_MAC_MIN_BE TSCH_CONF_MAC_MIN_BE
#else
#define TSCH_MAC_MIN_BE 1
#endif
/* Max backoff exponent */
#ifdef TSCH_CONF_MAC_MAX_BE
#define TSCH_MAC_MAX_BE TSCH_CONF_MAC_MAX_BE
#else
#define TSCH_MAC_MAX_BE 7
#endif
/* Max number of re-transmissions */
#ifdef TSCH_CONF_MAC_MAX_FRAME_RETRIES
#define TSCH_MAC_MAX_FRAME_RETRIES TSCH_CONF_MAC_MAX_FRAME_RETRIES
#else
#define TSCH_MAC_MAX_FRAME_RETRIES 8
#endif
/*********** Callbacks *********/
/* Called by TSCH when switching time source */
#ifdef TSCH_CALLBACK_NEW_TIME_SOURCE
struct tsch_neighbor;
void TSCH_CALLBACK_NEW_TIME_SOURCE(const struct tsch_neighbor *old, const struct tsch_neighbor *new);
#endif
/* Called by TSCH every time a packet is ready to be added to the send queue */
#ifdef TSCH_CALLBACK_PACKET_READY
void TSCH_CALLBACK_PACKET_READY(void);
#endif
/************ Types ***********/
/* TSCH packet information */
struct tsch_packet {
struct queuebuf *qb; /* pointer to the queuebuf to be sent */
mac_callback_t sent; /* callback for this packet */
void *ptr; /* MAC callback parameter */
uint8_t transmissions; /* #transmissions performed for this packet */
uint8_t ret; /* status -- MAC return code */
uint8_t header_len; /* length of header and header IEs (needed for link-layer security) */
uint8_t tsch_sync_ie_offset; /* Offset within the frame used for quick update of EB ASN and join priority */
};
/* TSCH neighbor information */
struct tsch_neighbor {
/* Neighbors are stored as a list: "next" must be the first field */
struct tsch_neighbor *next;
linkaddr_t addr; /* MAC address of the neighbor */
uint8_t is_broadcast; /* is this neighbor a virtual neighbor used for broadcast (of data packets or EBs) */
uint8_t is_time_source; /* is this neighbor a time source? */
uint8_t backoff_exponent; /* CSMA backoff exponent */
uint8_t backoff_window; /* CSMA backoff window (number of slots to skip) */
uint8_t last_backoff_window; /* Last CSMA backoff window */
uint8_t tx_links_count; /* How many links do we have to this neighbor? */
uint8_t dedicated_tx_links_count; /* How many dedicated links do we have to this neighbor? */
/* Array for the ringbuf. Contains pointers to packets.
* Its size must be a power of two to allow for atomic put */
struct tsch_packet *tx_array[TSCH_QUEUE_NUM_PER_NEIGHBOR];
/* Circular buffer of pointers to packet. */
struct ringbufindex tx_ringbuf;
};
/***** External Variables *****/
/* Broadcast and EB virtual neighbors */
extern struct tsch_neighbor *n_broadcast;
extern struct tsch_neighbor *n_eb;
/********** Functions *********/
/* Add a TSCH neighbor */
struct tsch_neighbor *tsch_queue_add_nbr(const linkaddr_t *addr);
/* Get a TSCH neighbor */
struct tsch_neighbor *tsch_queue_get_nbr(const linkaddr_t *addr);
/* Get a TSCH time source (we currently assume there is only one) */
struct tsch_neighbor *tsch_queue_get_time_source(void);
/* Update TSCH time source */
int tsch_queue_update_time_source(const linkaddr_t *new_addr);
/* Add packet to neighbor queue. Use same lockfree implementation as ringbuf.c (put is atomic) */
struct tsch_packet *tsch_queue_add_packet(const linkaddr_t *addr, mac_callback_t sent, void *ptr);
/* Returns the number of packets currently a given neighbor queue */
int tsch_queue_packet_count(const linkaddr_t *addr);
/* Remove first packet from a neighbor queue. The packet is stored in a separate
* dequeued packet list, for later processing. Return the packet. */
struct tsch_packet *tsch_queue_remove_packet_from_queue(struct tsch_neighbor *n);
/* Free a packet */
void tsch_queue_free_packet(struct tsch_packet *p);
/* Flush all neighbor queues */
void tsch_queue_flush_all(void);
/* Deallocate neighbors with empty queue */
void tsch_queue_free_unused_neighbors(void);
/* Is the neighbor queue empty? */
int tsch_queue_is_empty(const struct tsch_neighbor *n);
/* Returns the first packet from a neighbor queue */
struct tsch_packet *tsch_queue_get_packet_for_nbr(const struct tsch_neighbor *n, struct tsch_link *link);
/* Returns the head packet from a neighbor queue (from neighbor address) */
struct tsch_packet *tsch_queue_get_packet_for_dest_addr(const linkaddr_t *addr, struct tsch_link *link);
/* Returns the head packet of any neighbor queue with zero backoff counter.
* Writes pointer to the neighbor in *n */
struct tsch_packet *tsch_queue_get_unicast_packet_for_any(struct tsch_neighbor **n, struct tsch_link *link);
/* May the neighbor transmit over a share link? */
int tsch_queue_backoff_expired(const struct tsch_neighbor *n);
/* Reset neighbor backoff */
void tsch_queue_backoff_reset(struct tsch_neighbor *n);
/* Increment backoff exponent, pick a new window */
void tsch_queue_backoff_inc(struct tsch_neighbor *n);
/* Decrement backoff window for all queues directed at dest_addr */
void tsch_queue_update_all_backoff_windows(const linkaddr_t *dest_addr);
/* Initialize TSCH queue module */
void tsch_queue_init(void);
#endif /* __TSCH_QUEUE_H__ */

View file

@ -0,0 +1,107 @@
/*
* Copyright (c) 2014, SICS Swedish ICT.
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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
* Interaction between TSCH and RPL
*
* \author Simon Duquennoy <simonduq@sics.se>
*/
#if UIP_CONF_IPV6_RPL
#include "contiki.h"
#include "net/rpl/rpl.h"
#include "net/rpl/rpl-private.h"
#include "net/mac/tsch/tsch.h"
#include "net/mac/tsch/tsch-private.h"
#include "net/mac/tsch/tsch-schedule.h"
#include "net/mac/tsch/tsch-log.h"
#include "tsch-rpl.h"
#if TSCH_LOG_LEVEL >= 1
#define DEBUG DEBUG_PRINT
#else /* TSCH_LOG_LEVEL */
#define DEBUG DEBUG_NONE
#endif /* TSCH_LOG_LEVEL */
#include "net/ip/uip-debug.h"
/*---------------------------------------------------------------------------*/
/* To use, set #define TSCH_CALLBACK_JOINING_NETWORK tsch_rpl_callback_joining_network */
void
tsch_rpl_callback_joining_network(void)
{
}
/*---------------------------------------------------------------------------*/
/* Upon leaving a TSCH network, perform a local repair
* (cleanup neighbor state, reset Trickle timer etc)
* To use, set #define TSCH_CALLBACK_LEAVING_NETWORK tsch_rpl_callback_leaving_network */
void
tsch_rpl_callback_leaving_network(void)
{
rpl_dag_t *dag = rpl_get_any_dag();
if(dag != NULL) {
rpl_local_repair(dag->instance);
}
}
/*---------------------------------------------------------------------------*/
/* Set TSCH EB period based on current RPL DIO period.
* To use, set #define RPL_CALLBACK_PARENT_SWITCH tsch_rpl_callback_new_dio_interval */
void
tsch_rpl_callback_new_dio_interval(uint8_t dio_interval)
{
/* Transmit EBs only if we have a valid rank as per 6TiSCH minimal */
rpl_dag_t *dag = rpl_get_any_dag();
if(dag != NULL && dag->rank != INFINITE_RANK) {
/* If we are root set TSCH as coordinator */
if(dag->rank == ROOT_RANK(dag->instance)) {
tsch_set_coordinator(1);
}
/* Set EB period */
tsch_set_eb_period(TSCH_EB_PERIOD);
/* Set join priority based on RPL rank */
tsch_set_join_priority(DAG_RANK(dag->rank, dag->instance) - 1);
} else {
tsch_set_eb_period(0);
}
}
/*---------------------------------------------------------------------------*/
/* Set TSCH time source based on current RPL preferred parent.
* To use, set #define RPL_CALLBACK_PARENT_SWITCH tsch_rpl_callback_parent_switch */
void
tsch_rpl_callback_parent_switch(rpl_parent_t *old, rpl_parent_t *new)
{
if(tsch_is_associated == 1) {
tsch_queue_update_time_source(
(const linkaddr_t *)uip_ds6_nbr_lladdr_from_ipaddr(
rpl_get_parent_ipaddr(new)));
}
}
#endif /* UIP_CONF_IPV6_RPL */

View file

@ -0,0 +1,55 @@
/*
* Copyright (c) 2014, SICS Swedish ICT.
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
*/
#ifndef __TSCH_RPL_H__
#define __TSCH_RPL_H__
/********** Includes **********/
#include "net/rpl/rpl.h"
#include "net/mac/tsch/tsch-queue.h"
/********** Functions *********/
/* To use, set #define TSCH_CALLBACK_JOINING_NETWORK tsch_rpl_callback_joining_network */
void tsch_rpl_callback_joining_network(void);
/* Upon leaving a TSCH network, perform a local repair
* (cleanup neighbor state, reset Trickle timer etc)
* To use, set #define TSCH_CALLBACK_LEAVING_NETWORK tsch_rpl_callback_leaving_network */
void tsch_rpl_callback_leaving_network(void);
/* Set TSCH EB period based on current RPL DIO period.
* To use, set #define RPL_CALLBACK_PARENT_SWITCH tsch_rpl_callback_new_dio_interval */
void tsch_rpl_callback_new_dio_interval(uint8_t dio_interval);
/* Set TSCH time source based on current RPL preferred parent.
* To use, set #define RPL_CALLBACK_PARENT_SWITCH tsch_rpl_callback_parent_switch */
void tsch_rpl_callback_parent_switch(rpl_parent_t *old, rpl_parent_t *new);
#endif /* __TSCH_RPL_H__ */

View file

@ -0,0 +1,447 @@
/*
* Copyright (c) 2014, SICS Swedish ICT.
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*
*/
/**
* \file
* IEEE 802.15.4 TSCH MAC schedule manager.
* \author
* Simon Duquennoy <simonduq@sics.se>
* Beshr Al Nahas <beshr@sics.se>
*/
#include "contiki.h"
#include "dev/leds.h"
#include "lib/memb.h"
#include "net/nbr-table.h"
#include "net/packetbuf.h"
#include "net/queuebuf.h"
#include "net/mac/tsch/tsch.h"
#include "net/mac/tsch/tsch-queue.h"
#include "net/mac/tsch/tsch-private.h"
#include "net/mac/tsch/tsch-packet.h"
#include "net/mac/tsch/tsch-schedule.h"
#include "net/mac/tsch/tsch-log.h"
#include "net/mac/frame802154.h"
#include "sys/process.h"
#include "sys/rtimer.h"
#include <string.h>
#if TSCH_LOG_LEVEL >= 1
#define DEBUG DEBUG_PRINT
#else /* TSCH_LOG_LEVEL */
#define DEBUG DEBUG_NONE
#endif /* TSCH_LOG_LEVEL */
#include "net/ip/uip-debug.h"
/* Pre-allocated space for links */
MEMB(link_memb, struct tsch_link, TSCH_SCHEDULE_MAX_LINKS);
/* Pre-allocated space for slotframes */
MEMB(slotframe_memb, struct tsch_slotframe, TSCH_SCHEDULE_MAX_SLOTFRAMES);
/* List of slotframes (each slotframe holds its own list of links) */
LIST(slotframe_list);
/* Adds and returns a slotframe (NULL if failure) */
struct tsch_slotframe *
tsch_schedule_add_slotframe(uint16_t handle, uint16_t size)
{
if(size == 0) {
return NULL;
}
if(tsch_schedule_get_slotframe_by_handle(handle)) {
/* A slotframe with this handle already exists */
return NULL;
}
if(tsch_get_lock()) {
struct tsch_slotframe *sf = memb_alloc(&slotframe_memb);
if(sf != NULL) {
/* Initialize the slotframe */
sf->handle = handle;
ASN_DIVISOR_INIT(sf->size, size);
LIST_STRUCT_INIT(sf, links_list);
/* Add the slotframe to the global list */
list_add(slotframe_list, sf);
}
PRINTF("TSCH-schedule: add_slotframe %u %u\n",
handle, size);
tsch_release_lock();
return sf;
}
return NULL;
}
/*---------------------------------------------------------------------------*/
/* Removes all slotframes, resulting in an empty schedule */
int
tsch_schedule_remove_all_slotframes(void)
{
struct tsch_slotframe *sf;
while((sf = list_head(slotframe_list))) {
if(tsch_schedule_remove_slotframe(sf) == 0) {
return 0;
}
}
return 1;
}
/*---------------------------------------------------------------------------*/
/* Removes a slotframe Return 1 if success, 0 if failure */
int
tsch_schedule_remove_slotframe(struct tsch_slotframe *slotframe)
{
if(slotframe != NULL) {
/* Remove all links belonging to this slotframe */
struct tsch_link *l;
while((l = list_head(slotframe->links_list))) {
tsch_schedule_remove_link(slotframe, l);
}
/* Now that the slotframe has no links, remove it. */
if(tsch_get_lock()) {
PRINTF("TSCH-schedule: remove slotframe %u %u\n", slotframe->handle, slotframe->size.val);
memb_free(&slotframe_memb, slotframe);
list_remove(slotframe_list, slotframe);
tsch_release_lock();
return 1;
}
}
return 0;
}
/*---------------------------------------------------------------------------*/
/* Looks for a slotframe from a handle */
struct tsch_slotframe *
tsch_schedule_get_slotframe_by_handle(uint16_t handle)
{
if(!tsch_is_locked()) {
struct tsch_slotframe *sf = list_head(slotframe_list);
while(sf != NULL) {
if(sf->handle == handle) {
return sf;
}
sf = list_item_next(sf);
}
}
return NULL;
}
/*---------------------------------------------------------------------------*/
/* Looks for a link from a handle */
struct tsch_link *
tsch_schedule_get_link_by_handle(uint16_t handle)
{
if(!tsch_is_locked()) {
struct tsch_slotframe *sf = list_head(slotframe_list);
while(sf != NULL) {
struct tsch_link *l = list_head(sf->links_list);
/* Loop over all items. Assume there is max one link per timeslot */
while(l != NULL) {
if(l->handle == handle) {
return l;
}
l = list_item_next(l);
}
sf = list_item_next(sf);
}
}
return NULL;
}
/*---------------------------------------------------------------------------*/
/* Adds a link to a slotframe, return a pointer to it (NULL if failure) */
struct tsch_link *
tsch_schedule_add_link(struct tsch_slotframe *slotframe,
uint8_t link_options, enum link_type link_type, const linkaddr_t *address,
uint16_t timeslot, uint16_t channel_offset)
{
struct tsch_link *l = NULL;
if(slotframe != NULL) {
/* We currently support only one link per timeslot in a given slotframe. */
/* Start with removing the link currently installed at this timeslot (needed
* to keep neighbor state in sync with link options etc.) */
tsch_schedule_remove_link_by_timeslot(slotframe, timeslot);
if(!tsch_get_lock()) {
PRINTF("TSCH-schedule:! add_link memb_alloc couldn't take lock\n");
} else {
l = memb_alloc(&link_memb);
if(l == NULL) {
PRINTF("TSCH-schedule:! add_link memb_alloc failed\n");
} else {
static int current_link_handle = 0;
struct tsch_neighbor *n;
/* Add the link to the slotframe */
list_add(slotframe->links_list, l);
/* Initialize link */
l->handle = current_link_handle++;
l->link_options = link_options;
l->link_type = link_type;
l->slotframe_handle = slotframe->handle;
l->timeslot = timeslot;
l->channel_offset = channel_offset;
l->data = NULL;
if(address == NULL) {
address = &linkaddr_null;
}
linkaddr_copy(&l->addr, address);
PRINTF("TSCH-schedule: add_link %u %u %u %u %u %u\n",
slotframe->handle, link_options, link_type, timeslot, channel_offset, TSCH_LOG_ID_FROM_LINKADDR(address));
/* Release the lock before we update the neighbor (will take the lock) */
tsch_release_lock();
if(l->link_options & LINK_OPTION_TX) {
n = tsch_queue_add_nbr(&l->addr);
/* We have a tx link to this neighbor, update counters */
if(n != NULL) {
n->tx_links_count++;
if(!(l->link_options & LINK_OPTION_SHARED)) {
n->dedicated_tx_links_count++;
}
}
}
}
}
}
return l;
}
/*---------------------------------------------------------------------------*/
/* Removes a link from slotframe. Return 1 if success, 0 if failure */
int
tsch_schedule_remove_link(struct tsch_slotframe *slotframe, struct tsch_link *l)
{
if(slotframe != NULL && l != NULL && l->slotframe_handle == slotframe->handle) {
if(tsch_get_lock()) {
uint8_t link_options;
linkaddr_t addr;
/* Save link option and addr in local variables as we need them
* after freeing the link */
link_options = l->link_options;
linkaddr_copy(&addr, &l->addr);
/* The link to be removed is scheduled as next, set it to NULL
* to abort the next link operation */
if(l == current_link) {
current_link = NULL;
}
PRINTF("TSCH-schedule: remove_link %u %u %u %u %u\n",
slotframe->handle, l->link_options, l->timeslot, l->channel_offset,
TSCH_LOG_ID_FROM_LINKADDR(&l->addr));
list_remove(slotframe->links_list, l);
memb_free(&link_memb, l);
/* Release the lock before we update the neighbor (will take the lock) */
tsch_release_lock();
/* This was a tx link to this neighbor, update counters */
if(link_options & LINK_OPTION_TX) {
struct tsch_neighbor *n = tsch_queue_add_nbr(&addr);
if(n != NULL) {
n->tx_links_count--;
if(!(link_options & LINK_OPTION_SHARED)) {
n->dedicated_tx_links_count--;
}
}
}
return 1;
} else {
PRINTF("TSCH-schedule:! remove_link memb_alloc couldn't take lock\n");
}
}
return 0;
}
/*---------------------------------------------------------------------------*/
/* Removes a link from slotframe and timeslot. Return a 1 if success, 0 if failure */
int
tsch_schedule_remove_link_by_timeslot(struct tsch_slotframe *slotframe, uint16_t timeslot)
{
return slotframe != NULL &&
tsch_schedule_remove_link(slotframe, tsch_schedule_get_link_by_timeslot(slotframe, timeslot));
}
/*---------------------------------------------------------------------------*/
/* Looks within a slotframe for a link with a given timeslot */
struct tsch_link *
tsch_schedule_get_link_by_timeslot(struct tsch_slotframe *slotframe, uint16_t timeslot)
{
if(!tsch_is_locked()) {
if(slotframe != NULL) {
struct tsch_link *l = list_head(slotframe->links_list);
/* Loop over all items. Assume there is max one link per timeslot */
while(l != NULL) {
if(l->timeslot == timeslot) {
return l;
}
l = list_item_next(l);
}
return l;
}
}
return NULL;
}
/*---------------------------------------------------------------------------*/
/* Returns the next active link after a given ASN, and a backup link (for the same ASN, with Rx flag) */
struct tsch_link *
tsch_schedule_get_next_active_link(struct asn_t *asn, uint16_t *time_offset,
struct tsch_link **backup_link)
{
uint16_t time_to_curr_best = 0;
struct tsch_link *curr_best = NULL;
struct tsch_link *curr_backup = NULL; /* Keep a back link in case the current link
turns out useless when the time comes. For instance, for a Tx-only link, if there is
no outgoing packet in queue. In that case, run the backup link instead. The backup link
must have Rx flag set. */
if(!tsch_is_locked()) {
struct tsch_slotframe *sf = list_head(slotframe_list);
/* For each slotframe, look for the earliest occurring link */
while(sf != NULL) {
/* Get timeslot from ASN, given the slotframe length */
uint16_t timeslot = ASN_MOD(*asn, sf->size);
struct tsch_link *l = list_head(sf->links_list);
while(l != NULL) {
uint16_t time_to_timeslot =
l->timeslot > timeslot ?
l->timeslot - timeslot :
sf->size.val + l->timeslot - timeslot;
if(curr_best == NULL || time_to_timeslot < time_to_curr_best) {
time_to_curr_best = time_to_timeslot;
curr_best = l;
curr_backup = NULL;
} else if(time_to_timeslot == time_to_curr_best) {
struct tsch_link *new_best = NULL;
/* Two links are overlapping, we need to select one of them.
* By standard: prioritize Tx links first, second by lowest handle */
if((curr_best->link_options & LINK_OPTION_TX) == (l->link_options & LINK_OPTION_TX)) {
/* Both or neither links have Tx, select the one with lowest handle */
if(l->slotframe_handle < curr_best->slotframe_handle) {
new_best = l;
}
} else {
/* Select the link that has the Tx option */
if(l->link_options & LINK_OPTION_TX) {
new_best = l;
}
}
/* Maintain backup_link */
if(curr_backup == NULL) {
/* Check if 'l' best can be used as backup */
if(new_best != l && (l->link_options & LINK_OPTION_RX)) { /* Does 'l' have Rx flag? */
curr_backup = l;
}
/* Check if curr_best can be used as backup */
if(new_best != curr_best && (curr_best->link_options & LINK_OPTION_RX)) { /* Does curr_best have Rx flag? */
curr_backup = curr_best;
}
}
/* Maintain curr_best */
if(new_best != NULL) {
curr_best = new_best;
}
}
l = list_item_next(l);
}
sf = list_item_next(sf);
}
if(time_offset != NULL) {
*time_offset = time_to_curr_best;
}
}
if(backup_link != NULL) {
*backup_link = curr_backup;
}
return curr_best;
}
/*---------------------------------------------------------------------------*/
/* Module initialization, call only once at startup. Returns 1 is success, 0 if failure. */
int
tsch_schedule_init(void)
{
if(tsch_get_lock()) {
memb_init(&link_memb);
memb_init(&slotframe_memb);
list_init(slotframe_list);
tsch_release_lock();
return 1;
} else {
return 0;
}
}
/*---------------------------------------------------------------------------*/
/* Create a 6TiSCH minimal schedule */
void
tsch_schedule_create_minimal(void)
{
struct tsch_slotframe *sf_min;
/* First, empty current schedule */
tsch_schedule_remove_all_slotframes();
/* Build 6TiSCH minimal schedule.
* We pick a slotframe length of TSCH_SCHEDULE_DEFAULT_LENGTH */
sf_min = tsch_schedule_add_slotframe(0, TSCH_SCHEDULE_DEFAULT_LENGTH);
/* Add a single Tx|Rx|Shared slot using broadcast address (i.e. usable for unicast and broadcast).
* We set the link type to advertising, which is not compliant with 6TiSCH minimal schedule
* but is required according to 802.15.4e if also used for EB transmission.
* Timeslot: 0, channel offset: 0. */
tsch_schedule_add_link(sf_min,
LINK_OPTION_RX | LINK_OPTION_TX | LINK_OPTION_SHARED | LINK_OPTION_TIME_KEEPING,
LINK_TYPE_ADVERTISING, &tsch_broadcast_address,
0, 0);
}
/*---------------------------------------------------------------------------*/
/* Prints out the current schedule (all slotframes and links) */
void
tsch_schedule_print(void)
{
if(!tsch_is_locked()) {
struct tsch_slotframe *sf = list_head(slotframe_list);
printf("Schedule: slotframe list\n");
while(sf != NULL) {
struct tsch_link *l = list_head(sf->links_list);
printf("[Slotframe] Handle %u, size %u\n", sf->handle, sf->size.val);
printf("List of links:\n");
while(l != NULL) {
printf("[Link] Options %02x, type %u, timeslot %u, channel offset %u, address %u\n",
l->link_options, l->link_type, l->timeslot, l->channel_offset, l->addr.u8[7]);
l = list_item_next(l);
}
sf = list_item_next(sf);
}
printf("Schedule: end of slotframe list\n");
}
}
/*---------------------------------------------------------------------------*/

View file

@ -0,0 +1,164 @@
/*
* Copyright (c) 2014, SICS Swedish ICT.
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*
*/
#ifndef __TSCH_SCHEDULE_H__
#define __TSCH_SCHEDULE_H__
/********** Includes **********/
#include "contiki.h"
#include "lib/list.h"
#include "net/mac/tsch/tsch-private.h"
#include "net/mac/tsch/tsch-queue.h"
#include "net/mac/tsch/tsch-slot-operation.h"
#include "net/linkaddr.h"
/******** Configuration *******/
/* Initializes TSCH with a 6TiSCH minimal schedule */
#ifdef TSCH_SCHEDULE_CONF_WITH_6TISCH_MINIMAL
#define TSCH_SCHEDULE_WITH_6TISCH_MINIMAL TSCH_SCHEDULE_CONF_WITH_6TISCH_MINIMAL
#else
#define TSCH_SCHEDULE_WITH_6TISCH_MINIMAL 1
#endif
/* 6TiSCH Minimal schedule slotframe length */
#ifdef TSCH_SCHEDULE_CONF_DEFAULT_LENGTH
#define TSCH_SCHEDULE_DEFAULT_LENGTH TSCH_SCHEDULE_CONF_DEFAULT_LENGTH
#else
#define TSCH_SCHEDULE_DEFAULT_LENGTH 7
#endif
/* Max number of TSCH slotframes */
#ifdef TSCH_SCHEDULE_CONF_MAX_SLOTFRAMES
#define TSCH_SCHEDULE_MAX_SLOTFRAMES TSCH_SCHEDULE_CONF_MAX_SLOTFRAMES
#else
#define TSCH_SCHEDULE_MAX_SLOTFRAMES 4
#endif
/* Max number of links */
#ifdef TSCH_SCHEDULE_CONF_MAX_LINKS
#define TSCH_SCHEDULE_MAX_LINKS TSCH_SCHEDULE_CONF_MAX_LINKS
#else
#define TSCH_SCHEDULE_MAX_LINKS 32
#endif
/********** Constants *********/
/* Link options */
#define LINK_OPTION_TX 1
#define LINK_OPTION_RX 2
#define LINK_OPTION_SHARED 4
#define LINK_OPTION_TIME_KEEPING 8
/************ Types ***********/
/* 802.15.4e link types.
* LINK_TYPE_ADVERTISING_ONLY is an extra one: for EB-only links. */
enum link_type { LINK_TYPE_NORMAL, LINK_TYPE_ADVERTISING, LINK_TYPE_ADVERTISING_ONLY };
struct tsch_link {
/* Links are stored as a list: "next" must be the first field */
struct tsch_link *next;
/* Unique identifier */
uint16_t handle;
/* MAC address of neighbor */
linkaddr_t addr;
/* Slotframe identifier */
uint16_t slotframe_handle;
/* Identifier of Slotframe to which this link belongs
* Unused. */
/* uint8_t handle; */
/* Timeslot for this link */
uint16_t timeslot;
/* Channel offset for this link */
uint16_t channel_offset;
/* A bit string that defines
* b0 = Transmit, b1 = Receive, b2 = Shared, b3 = Timekeeping, b4 = reserved */
uint8_t link_options;
/* Type of link. NORMAL = 0. ADVERTISING = 1, and indicates
the link may be used to send an Enhanced beacon. */
enum link_type link_type;
/* Any other data for upper layers */
void *data;
};
struct tsch_slotframe {
/* Slotframes are stored as a list: "next" must be the first field */
struct tsch_slotframe *next;
/* Unique identifier */
uint16_t handle;
/* Number of timeslots in the slotframe.
* Stored as struct asn_divisor_t because we often need ASN%size */
struct asn_divisor_t size;
/* List of links belonging to this slotframe */
LIST_STRUCT(links_list);
};
/********** Functions *********/
/* Module initialization, call only once at startup. Returns 1 is success, 0 if failure. */
int tsch_schedule_init(void);
/* Create a 6TiSCH minimal schedule */
void tsch_schedule_create_minimal(void);
/* Prints out the current schedule (all slotframes and links) */
void tsch_schedule_print(void);
/* Adds and returns a slotframe (NULL if failure) */
struct tsch_slotframe *tsch_schedule_add_slotframe(uint16_t handle, uint16_t size);
/* Looks for a slotframe from a handle */
struct tsch_slotframe *tsch_schedule_get_slotframe_by_handle(uint16_t handle);
/* Removes a slotframe Return 1 if success, 0 if failure */
int tsch_schedule_remove_slotframe(struct tsch_slotframe *slotframe);
/* Removes all slotframes, resulting in an empty schedule */
int tsch_schedule_remove_all_slotframes(void);
/* Returns next slotframe */
struct tsch_slotframe *tsch_schedule_slotframes_next(struct tsch_slotframe *sf);
/* Adds a link to a slotframe, return a pointer to it (NULL if failure) */
struct tsch_link *tsch_schedule_add_link(struct tsch_slotframe *slotframe,
uint8_t link_options, enum link_type link_type, const linkaddr_t *address,
uint16_t timeslot, uint16_t channel_offset);
/* Looks for a link from a handle */
struct tsch_link *tsch_schedule_get_link_by_handle(uint16_t handle);
/* Looks within a slotframe for a link with a given timeslot */
struct tsch_link *tsch_schedule_get_link_by_timeslot(struct tsch_slotframe *slotframe, uint16_t timeslot);
/* Removes a link. Return 1 if success, 0 if failure */
int tsch_schedule_remove_link(struct tsch_slotframe *slotframe, struct tsch_link *l);
/* Removes a link from slotframe and timeslot. Return a 1 if success, 0 if failure */
int tsch_schedule_remove_link_by_timeslot(struct tsch_slotframe *slotframe, uint16_t timeslot);
/* Returns the next active link after a given ASN, and a backup link (for the same ASN, with Rx flag) */
struct tsch_link * tsch_schedule_get_next_active_link(struct asn_t *asn, uint16_t *time_offset,
struct tsch_link **backup_link);
#endif /* __TSCH_SCHEDULE_H__ */

View file

@ -0,0 +1,261 @@
/*
* Copyright (c) 2014, SICS Swedish ICT.
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*
*/
/**
* \file
* TSCH security
* \author
* Simon Duquennoy <simonduq@sics.se>
*/
#include "contiki.h"
#include "net/mac/tsch/tsch.h"
#include "net/mac/tsch/tsch-packet.h"
#include "net/mac/tsch/tsch-private.h"
#include "net/mac/tsch/tsch-schedule.h"
#include "net/mac/tsch/tsch-security.h"
#include "net/mac/tsch/tsch-log.h"
#include "net/mac/frame802154.h"
#include "net/mac/framer-802154.h"
#include "net/netstack.h"
#include "net/packetbuf.h"
#include "lib/ccm-star.h"
#include "lib/aes-128.h"
#include <stdio.h>
#include <string.h>
#if TSCH_LOG_LEVEL >= 1
#define DEBUG DEBUG_PRINT
#else /* TSCH_LOG_LEVEL */
#define DEBUG DEBUG_NONE
#endif /* TSCH_LOG_LEVEL */
#include "net/ip/uip-debug.h"
/* The two keys K1 and K2 from 6TiSCH minimal configuration
* K1: well-known, used for EBs
* K2: secret, used for data and ACK
* */
static aes_key keys[] = {
TSCH_SECURITY_K1,
TSCH_SECURITY_K2
};
#define N_KEYS (sizeof(keys) / sizeof(aes_key))
/*---------------------------------------------------------------------------*/
static void
tsch_security_init_nonce(uint8_t *nonce,
const linkaddr_t *sender, struct asn_t *asn)
{
memcpy(nonce, sender, 8);
nonce[8] = asn->ms1b;
nonce[9] = (asn->ls4b >> 24) & 0xff;
nonce[10] = (asn->ls4b >> 16) & 0xff;
nonce[11] = (asn->ls4b >> 8) & 0xff;
nonce[12] = (asn->ls4b) & 0xff;
}
/*---------------------------------------------------------------------------*/
static int
tsch_security_check_level(const frame802154_t *frame)
{
uint8_t required_security_level;
uint8_t required_key_index;
/* Sanity check */
if(frame == NULL) {
return 0;
}
/* Non-secured frame, ok iff we are not in a secured PAN
* (i.e. scanning or associated to a non-secured PAN) */
if(frame->fcf.security_enabled == 0) {
return !(tsch_is_associated == 1 && tsch_is_pan_secured == 1);
}
/* The frame is secured, that we are not in an unsecured PAN */
if(tsch_is_associated == 1 && tsch_is_pan_secured == 0) {
return 0;
}
/* The frame is secured, check its security level */
switch(frame->fcf.frame_type) {
case FRAME802154_BEACONFRAME:
required_security_level = TSCH_SECURITY_KEY_SEC_LEVEL_EB;
required_key_index = TSCH_SECURITY_KEY_INDEX_EB;
break;
case FRAME802154_ACKFRAME:
required_security_level = TSCH_SECURITY_KEY_SEC_LEVEL_ACK;
required_key_index = TSCH_SECURITY_KEY_INDEX_ACK;
break;
default:
required_security_level = TSCH_SECURITY_KEY_SEC_LEVEL_OTHER;
required_key_index = TSCH_SECURITY_KEY_INDEX_OTHER;
break;
}
return frame->aux_hdr.security_control.security_level == required_security_level
&& frame->aux_hdr.key_index == required_key_index;
}
/*---------------------------------------------------------------------------*/
int
tsch_security_mic_len(const frame802154_t *frame)
{
if(frame != NULL && frame->fcf.security_enabled) {
return 2 << (frame->aux_hdr.security_control.security_level & 0x03);
} else {
return 0;
}
}
/*---------------------------------------------------------------------------*/
int
tsch_security_secure_frame(uint8_t *hdr, uint8_t *outbuf,
int hdrlen, int datalen, struct asn_t *asn)
{
frame802154_t frame;
uint8_t key_index = 0;
uint8_t security_level = 0;
uint8_t with_encryption;
uint8_t mic_len;
uint8_t nonce[16];
uint8_t a_len;
uint8_t m_len;
if(hdr == NULL || outbuf == NULL || hdrlen < 0 || datalen < 0) {
return 0;
}
/* Parse the frame header to extract security settings */
if(frame802154_parse(hdr, hdrlen + datalen, &frame) < 3) {
return 0;
}
if(!frame.fcf.security_enabled) {
/* Security is not enabled for this frame, we're done */
return 1;
}
/* Read security key index */
key_index = frame.aux_hdr.key_index;
security_level = frame.aux_hdr.security_control.security_level;
with_encryption = (security_level & 0x4) ? 1 : 0;
mic_len = tsch_security_mic_len(&frame);
if(key_index == 0 || key_index > N_KEYS) {
return 0;
}
tsch_security_init_nonce(nonce, &linkaddr_node_addr, asn);
if(with_encryption) {
a_len = hdrlen;
m_len = datalen;
} else {
a_len = hdrlen + datalen;
m_len = 0;
}
/* Copy source data to output */
if(hdr != outbuf) {
memcpy(outbuf, hdr, a_len + m_len);
}
CCM_STAR.set_key(keys[key_index - 1]);
CCM_STAR.aead(nonce,
outbuf + a_len, m_len,
outbuf, a_len,
outbuf + hdrlen + datalen, mic_len, 1
);
return mic_len;
}
/*---------------------------------------------------------------------------*/
int
tsch_security_parse_frame(const uint8_t *hdr, int hdrlen, int datalen,
const frame802154_t *frame, const linkaddr_t *sender, struct asn_t *asn)
{
uint8_t generated_mic[16];
uint8_t key_index = 0;
uint8_t security_level = 0;
uint8_t with_encryption;
uint8_t mic_len;
uint8_t nonce[16];
uint8_t a_len;
uint8_t m_len;
if(frame == NULL || hdr == NULL || hdrlen < 0 || datalen < 0) {
return 0;
}
if(!tsch_security_check_level(frame)) {
/* Wrong security level */
return 0;
}
/* No security: nothing more to check */
if(!frame->fcf.security_enabled) {
return 1;
}
key_index = frame->aux_hdr.key_index;
security_level = frame->aux_hdr.security_control.security_level;
with_encryption = (security_level & 0x4) ? 1 : 0;
mic_len = tsch_security_mic_len(frame);
/* Check if key_index is in supported range */
if(key_index == 0 || key_index > N_KEYS) {
return 0;
}
tsch_security_init_nonce(nonce, sender, asn);
if(with_encryption) {
a_len = hdrlen;
m_len = datalen;
} else {
a_len = hdrlen + datalen;
m_len = 0;
}
CCM_STAR.set_key(keys[key_index - 1]);
CCM_STAR.aead(nonce,
(uint8_t *)hdr + a_len, m_len,
(uint8_t *)hdr, a_len,
generated_mic, mic_len, 0
);
if(mic_len > 0 && memcmp(generated_mic, hdr + hdrlen + datalen, mic_len) != 0) {
return 0;
} else {
return 1;
}
}

View file

@ -0,0 +1,127 @@
/*
* Copyright (c) 2014, SICS Swedish ICT.
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*
*/
#ifndef __TSCH_SECURITY_H__
#define __TSCH_SECURITY_H__
/********** Includes **********/
#include "contiki.h"
#include "net/mac/tsch/tsch-asn.h"
#include "net/mac/tsch/tsch-private.h"
#include "net/mac/frame802154.h"
#include "net/mac/frame802154e-ie.h"
/******** Configuration *******/
/* To enable TSCH security:
* - set LLSEC802154_CONF_SECURITY_LEVEL
* - set LLSEC802154_CONF_USES_EXPLICIT_KEYS
* - unset LLSEC802154_CONF_USES_FRAME_COUNTER
* */
#define TSCH_SECURITY_ENABLED (LLSEC802154_CONF_SECURITY_LEVEL != 0)
#if TSCH_SECURITY_ENABLED && !LLSEC802154_CONF_USES_EXPLICIT_KEYS
#error TSCH_SECURITY_ENABLED set but LLSEC802154_CONF_USES_EXPLICIT_KEYS unset
#endif /* TSCH_SECURITY_ENABLED */
#if TSCH_SECURITY_ENABLED && LLSEC802154_CONF_USES_FRAME_COUNTER
#error TSCH_SECURITY_ENABLED set but LLSEC802154_CONF_USES_FRAME_COUNTER set
#endif /* TSCH_SECURITY_ENABLED */
/* K1, defined in 6TiSCH minimal, is well-known (offers no security) and used for EBs only */
#ifdef TSCH_SECURITY_CONF_K1
#define TSCH_SECURITY_K1 TSCH_SECURITY_CONF_K1
#else /* TSCH_SECURITY_CONF_K1 */
#define TSCH_SECURITY_K1 { 0x36, 0x54, 0x69, 0x53, 0x43, 0x48, 0x20, 0x6D, 0x69, 0x6E, 0x69, 0x6D, 0x61, 0x6C, 0x31, 0x35 }
#endif /* TSCH_SECURITY_CONF_K1 */
/* K2, defined in 6TiSCH minimal, is used for all but EB traffic */
#ifdef TSCH_SECURITY_CONF_K2
#define TSCH_SECURITY_K2 TSCH_SECURITY_CONF_K2
#else /* TSCH_SECURITY_CONF_K2 */
#define TSCH_SECURITY_K2 { 0xde, 0xad, 0xbe, 0xef, 0xfa, 0xce, 0xca, 0xfe, 0xde, 0xad, 0xbe, 0xef, 0xfa, 0xce, 0xca, 0xfe }
#endif /* TSCH_SECURITY_CONF_K2 */
/* Key used for EBs */
#ifdef TSCH_SECURITY_CONF_KEY_INDEX_EB
#define TSCH_SECURITY_KEY_INDEX_EB TSCH_SECURITY_CONF_KEY_INDEX_EB
#else
#define TSCH_SECURITY_KEY_INDEX_EB 1 /* Use K1 as per 6TiSCH minimal */
#endif
/* Security level for EBs */
#ifdef TSCH_SECURITY_CONF_SEC_LEVEL_EB
#define TSCH_SECURITY_KEY_SEC_LEVEL_EB TSCH_SECURITY_CONF_SEC_LEVEL_EB
#else
#define TSCH_SECURITY_KEY_SEC_LEVEL_EB 1 /* MIC-32, as per 6TiSCH minimal */
#endif
/* Key used for ACK */
#ifdef TSCH_SECURITY_CONF_KEY_INDEX_ACK
#define TSCH_SECURITY_KEY_INDEX_ACK TSCH_SECURITY_CONF_KEY_INDEX_ACK
#else
#define TSCH_SECURITY_KEY_INDEX_ACK 2 /* Use K2 as per 6TiSCH minimal */
#endif
/* Security level for ACKs */
#ifdef TSCH_SECURITY_CONF_SEC_LEVEL_ACK
#define TSCH_SECURITY_KEY_SEC_LEVEL_ACK TSCH_SECURITY_CONF_SEC_LEVEL_ACK
#else
#define TSCH_SECURITY_KEY_SEC_LEVEL_ACK 5 /* Encryption + MIC-32, as per 6TiSCH minimal */
#endif
/* Key used for Other (Data, Cmd) */
#ifdef TSCH_SECURITY_CONF_KEY_INDEX_OTHER
#define TSCH_SECURITY_KEY_INDEX_OTHER TSCH_SECURITY_CONF_KEY_INDEX_OTHER
#else
#define TSCH_SECURITY_KEY_INDEX_OTHER 2 /* Use K2 as per 6TiSCH minimal */
#endif
/* Security level for Other (Data, Cmd) */
#ifdef TSCH_SECURITY_CONF_SEC_LEVEL_OTHER
#define TSCH_SECURITY_KEY_SEC_LEVEL_OTHER TSCH_SECURITY_CONF_SEC_LEVEL_OTHER
#else
#define TSCH_SECURITY_KEY_SEC_LEVEL_OTHER 5 /* Encryption + MIC-32, as per 6TiSCH minimal */
#endif
/************ Types ***********/
typedef uint8_t aes_key[16];
/********** Functions *********/
int tsch_security_mic_len(const frame802154_t *frame);
int tsch_security_secure_frame(uint8_t *hdr, uint8_t *outbuf,
int hdrlen, int datalen, struct asn_t *asn);
int tsch_security_parse_frame(const uint8_t *hdr, int hdrlen, int datalen,
const frame802154_t *frame, const linkaddr_t *sender, struct asn_t *asn);
#endif /* __TSCH_SECURITY_H__ */

View file

@ -0,0 +1,988 @@
/*
* Copyright (c) 2015, SICS Swedish ICT.
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*
*/
/**
* \file
* TSCH slot operation implementation, running from interrupt.
* \author
* Simon Duquennoy <simonduq@sics.se>
* Beshr Al Nahas <beshr@sics.se>
*
*/
#include "contiki.h"
#include "dev/radio.h"
#include "net/netstack.h"
#include "net/packetbuf.h"
#include "net/queuebuf.h"
#include "net/mac/framer-802154.h"
#include "net/mac/tsch/tsch.h"
#include "net/mac/tsch/tsch-slot-operation.h"
#include "net/mac/tsch/tsch-queue.h"
#include "net/mac/tsch/tsch-private.h"
#include "net/mac/tsch/tsch-log.h"
#include "net/mac/tsch/tsch-packet.h"
#include "net/mac/tsch/tsch-security.h"
#include "net/mac/tsch/tsch-adaptive-timesync.h"
#if TSCH_LOG_LEVEL >= 1
#define DEBUG DEBUG_PRINT
#else /* TSCH_LOG_LEVEL */
#define DEBUG DEBUG_NONE
#endif /* TSCH_LOG_LEVEL */
#include "net/ip/uip-debug.h"
/* TSCH debug macros, i.e. to set LEDs or GPIOs on various TSCH
* timeslot events */
#ifndef TSCH_DEBUG_INIT
#define TSCH_DEBUG_INIT()
#endif
#ifndef TSCH_DEBUG_INTERRUPT
#define TSCH_DEBUG_INTERRUPT()
#endif
#ifndef TSCH_DEBUG_RX_EVENT
#define TSCH_DEBUG_RX_EVENT()
#endif
#ifndef TSCH_DEBUG_TX_EVENT
#define TSCH_DEBUG_TX_EVENT()
#endif
#ifndef TSCH_DEBUG_SLOT_START
#define TSCH_DEBUG_SLOT_START()
#endif
#ifndef TSCH_DEBUG_SLOT_END
#define TSCH_DEBUG_SLOT_END()
#endif
/* Check if TSCH_MAX_INCOMING_PACKETS is power of two */
#if (TSCH_MAX_INCOMING_PACKETS & (TSCH_MAX_INCOMING_PACKETS - 1)) != 0
#error TSCH_MAX_INCOMING_PACKETS must be power of two
#endif
/* Check if TSCH_DEQUEUED_ARRAY_SIZE is power of two and greater or equal to QUEUEBUF_NUM */
#if TSCH_DEQUEUED_ARRAY_SIZE < QUEUEBUF_NUM
#error TSCH_DEQUEUED_ARRAY_SIZE must be greater or equal to QUEUEBUF_NUM
#endif
#if (TSCH_DEQUEUED_ARRAY_SIZE & (TSCH_DEQUEUED_ARRAY_SIZE - 1)) != 0
#error TSCH_DEQUEUED_ARRAY_SIZE must be power of two
#endif
/* Truncate received drift correction information to maximum half
* of the guard time (one fourth of TSCH_DEFAULT_TS_RX_WAIT) */
#define SYNC_IE_BOUND ((int32_t)US_TO_RTIMERTICKS(TSCH_DEFAULT_TS_RX_WAIT / 4))
/* By default: check that rtimer runs at >=32kHz and use a guard time of 10us */
#if RTIMER_SECOND < (32 * 1024)
#error "TSCH: RTIMER_SECOND < (32 * 1024)"
#endif
#if RTIMER_SECOND >= 200000
#define RTIMER_GUARD (RTIMER_SECOND / 100000)
#else
#define RTIMER_GUARD 2u
#endif
/* A ringbuf storing outgoing packets after they were dequeued.
* Will be processed layer by tsch_tx_process_pending */
struct ringbufindex dequeued_ringbuf;
struct tsch_packet *dequeued_array[TSCH_DEQUEUED_ARRAY_SIZE];
/* A ringbuf storing incoming packets.
* Will be processed layer by tsch_rx_process_pending */
struct ringbufindex input_ringbuf;
struct input_packet input_array[TSCH_MAX_INCOMING_PACKETS];
/* Last time we received Sync-IE (ACK or data packet from a time source) */
static struct asn_t last_sync_asn;
/* A global lock for manipulating data structures safely from outside of interrupt */
static volatile int tsch_locked = 0;
/* As long as this is set, skip all slot operation */
static volatile int tsch_lock_requested = 0;
/* Last estimated drift in RTIMER ticks
* (Sky: 1 tick = 30.517578125 usec exactly) */
static int32_t drift_correction = 0;
/* Is drift correction used? (Can be true even if drift_correction == 0) */
static uint8_t is_drift_correction_used;
/* The neighbor last used as our time source */
struct tsch_neighbor *last_timesource_neighbor = NULL;
/* Used from tsch_slot_operation and sub-protothreads */
static rtimer_clock_t volatile current_slot_start;
/* Are we currently inside a slot? */
static volatile int tsch_in_slot_operation = 0;
/* Info about the link, packet and neighbor of
* the current (or next) slot */
struct tsch_link *current_link = NULL;
/* A backup link with Rx flag, overlapping with current_link.
* If the current link is Tx-only and the Tx queue
* is empty while executing the link, fallback to the backup link. */
static struct tsch_link *backup_link = NULL;
static struct tsch_packet *current_packet = NULL;
static struct tsch_neighbor *current_neighbor = NULL;
/* Protothread for association */
PT_THREAD(tsch_scan(struct pt *pt));
/* Protothread for slot operation, called from rtimer interrupt
* and scheduled from tsch_schedule_slot_operation */
static PT_THREAD(tsch_slot_operation(struct rtimer *t, void *ptr));
static struct pt slot_operation_pt;
/* Sub-protothreads of tsch_slot_operation */
static PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t));
static PT_THREAD(tsch_rx_slot(struct pt *pt, struct rtimer *t));
/*---------------------------------------------------------------------------*/
/* TSCH locking system. TSCH is locked during slot operations */
/* Is TSCH locked? */
int
tsch_is_locked(void)
{
return tsch_locked;
}
/* Lock TSCH (no slot operation) */
int
tsch_get_lock(void)
{
if(!tsch_locked) {
rtimer_clock_t busy_wait_time;
int busy_wait = 0; /* Flag used for logging purposes */
/* Make sure no new slot operation will start */
tsch_lock_requested = 1;
/* Wait for the end of current slot operation. */
if(tsch_in_slot_operation) {
busy_wait = 1;
busy_wait_time = RTIMER_NOW();
while(tsch_in_slot_operation);
busy_wait_time = RTIMER_NOW() - busy_wait_time;
}
if(!tsch_locked) {
/* Take the lock if it is free */
tsch_locked = 1;
tsch_lock_requested = 0;
if(busy_wait) {
/* Issue a log whenever we had to busy wait until getting the lock */
TSCH_LOG_ADD(tsch_log_message,
snprintf(log->message, sizeof(log->message),
"!get lock delay %u", (unsigned)busy_wait_time);
);
}
return 1;
}
}
TSCH_LOG_ADD(tsch_log_message,
snprintf(log->message, sizeof(log->message),
"!failed to lock");
);
return 0;
}
/* Release TSCH lock */
void
tsch_release_lock(void)
{
tsch_locked = 0;
}
/*---------------------------------------------------------------------------*/
/* Channel hopping utility functions */
/* Return channel from ASN and channel offset */
uint8_t
tsch_calculate_channel(struct asn_t *asn, uint8_t channel_offset)
{
uint16_t index_of_0 = ASN_MOD(*asn, tsch_hopping_sequence_length);
uint16_t index_of_offset = (index_of_0 + channel_offset) % tsch_hopping_sequence_length.val;
return tsch_hopping_sequence[index_of_offset];
}
/*---------------------------------------------------------------------------*/
/* Timing utility functions */
/* Checks if the current time has passed a ref time + offset. Assumes
* a single overflow and ref time prior to now. */
static uint8_t
check_timer_miss(rtimer_clock_t ref_time, rtimer_clock_t offset, rtimer_clock_t now)
{
rtimer_clock_t target = ref_time + offset;
int now_has_overflowed = now < ref_time;
int target_has_overflowed = target < ref_time;
if(now_has_overflowed == target_has_overflowed) {
/* Both or none have overflowed, just compare now to the target */
return target <= now;
} else {
/* Either now or target of overflowed.
* If it is now, then it has passed the target.
* If it is target, then we haven't reached it yet.
* */
return now_has_overflowed;
}
}
/*---------------------------------------------------------------------------*/
/* Schedule a wakeup at a specified offset from a reference time.
* Provides basic protection against missed deadlines and timer overflows
* A return value of zero signals a missed deadline: no rtimer was scheduled. */
static uint8_t
tsch_schedule_slot_operation(struct rtimer *tm, rtimer_clock_t ref_time, rtimer_clock_t offset, const char *str)
{
rtimer_clock_t now = RTIMER_NOW();
int r;
/* Subtract RTIMER_GUARD before checking for deadline miss
* because we can not schedule rtimer less than RTIMER_GUARD in the future */
int missed = check_timer_miss(ref_time, offset - RTIMER_GUARD, now);
if(missed) {
TSCH_LOG_ADD(tsch_log_message,
snprintf(log->message, sizeof(log->message),
"!dl-miss %s %d %d",
str, (int)(now-ref_time), (int)offset);
);
return 0;
}
ref_time += offset;
r = rtimer_set(tm, ref_time, 1, (void (*)(struct rtimer *, void *))tsch_slot_operation, NULL);
if(r != RTIMER_OK) {
return 0;
}
return 1;
}
/*---------------------------------------------------------------------------*/
/* Schedule slot operation conditionally, and YIELD if success only.
* Always attempt to schedule RTIMER_GUARD before the target to make sure to wake up
* ahead of time and then busy wait to exactly hit the target. */
#define TSCH_SCHEDULE_AND_YIELD(pt, tm, ref_time, offset, str) \
do { \
if(tsch_schedule_slot_operation(tm, ref_time, offset - RTIMER_GUARD, str)) { \
PT_YIELD(pt); \
} \
BUSYWAIT_UNTIL_ABS(0, ref_time, offset); \
} while(0);
/*---------------------------------------------------------------------------*/
/* Get EB, broadcast or unicast packet to be sent, and target neighbor. */
static struct tsch_packet *
get_packet_and_neighbor_for_link(struct tsch_link *link, struct tsch_neighbor **target_neighbor)
{
struct tsch_packet *p = NULL;
struct tsch_neighbor *n = NULL;
/* Is this a Tx link? */
if(link->link_options & LINK_OPTION_TX) {
/* is it for advertisement of EB? */
if(link->link_type == LINK_TYPE_ADVERTISING || link->link_type == LINK_TYPE_ADVERTISING_ONLY) {
/* fetch EB packets */
n = n_eb;
p = tsch_queue_get_packet_for_nbr(n, link);
}
if(link->link_type != LINK_TYPE_ADVERTISING_ONLY) {
/* NORMAL link or no EB to send, pick a data packet */
if(p == NULL) {
/* Get neighbor queue associated to the link and get packet from it */
n = tsch_queue_get_nbr(&link->addr);
p = tsch_queue_get_packet_for_nbr(n, link);
/* if it is a broadcast slot and there were no broadcast packets, pick any unicast packet */
if(p == NULL && n == n_broadcast) {
p = tsch_queue_get_unicast_packet_for_any(&n, link);
}
}
}
}
/* return nbr (by reference) */
if(target_neighbor != NULL) {
*target_neighbor = n;
}
return p;
}
/*---------------------------------------------------------------------------*/
/* Post TX: Update neighbor state after a transmission */
static int
update_neighbor_state(struct tsch_neighbor *n, struct tsch_packet *p,
struct tsch_link *link, uint8_t mac_tx_status)
{
int in_queue = 1;
int is_shared_link = link->link_options & LINK_OPTION_SHARED;
int is_unicast = !n->is_broadcast;
if(mac_tx_status == MAC_TX_OK) {
/* Successful transmission */
tsch_queue_remove_packet_from_queue(n);
in_queue = 0;
/* Update CSMA state in the unicast case */
if(is_unicast) {
if(is_shared_link || tsch_queue_is_empty(n)) {
/* If this is a shared link, reset backoff on success.
* Otherwise, do so only is the queue is empty */
tsch_queue_backoff_reset(n);
}
}
} else {
/* Failed transmission */
if(p->transmissions >= TSCH_MAC_MAX_FRAME_RETRIES + 1) {
/* Drop packet */
tsch_queue_remove_packet_from_queue(n);
in_queue = 0;
}
/* Update CSMA state in the unicast case */
if(is_unicast) {
/* Failures on dedicated (== non-shared) leave the backoff
* window nor exponent unchanged */
if(is_shared_link) {
/* Shared link: increment backoff exponent, pick a new window */
tsch_queue_backoff_inc(n);
}
}
}
return in_queue;
}
/*---------------------------------------------------------------------------*/
static
PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t))
{
/**
* TX slot:
* 1. Copy packet to radio buffer
* 2. Perform CCA if enabled
* 3. Sleep until it is time to transmit
* 4. Wait for ACK if it is a unicast packet
* 5. Extract drift if we received an E-ACK from a time source neighbor
* 6. Update CSMA parameters according to TX status
* 7. Schedule mac_call_sent_callback
**/
/* tx status */
static uint8_t mac_tx_status;
/* is the packet in its neighbor's queue? */
uint8_t in_queue;
static int dequeued_index;
static int packet_ready = 1;
PT_BEGIN(pt);
TSCH_DEBUG_TX_EVENT();
/* First check if we have space to store a newly dequeued packet (in case of
* successful Tx or Drop) */
dequeued_index = ringbufindex_peek_put(&dequeued_ringbuf);
if(dequeued_index != -1) {
if(current_packet == NULL || current_packet->qb == NULL) {
mac_tx_status = MAC_TX_ERR_FATAL;
} else {
/* packet payload */
static void *packet;
#if TSCH_SECURITY_ENABLED
/* encrypted payload */
static uint8_t encrypted_packet[TSCH_PACKET_MAX_LEN];
#endif /* TSCH_SECURITY_ENABLED */
/* packet payload length */
static uint8_t packet_len;
/* packet seqno */
static uint8_t seqno;
/* is this a broadcast packet? (wait for ack?) */
static uint8_t is_broadcast;
static rtimer_clock_t tx_start_time;
#if CCA_ENABLED
static uint8_t cca_status;
#endif
/* get payload */
packet = queuebuf_dataptr(current_packet->qb);
packet_len = queuebuf_datalen(current_packet->qb);
/* is this a broadcast packet? (wait for ack?) */
is_broadcast = current_neighbor->is_broadcast;
/* read seqno from payload */
seqno = ((uint8_t *)(packet))[2];
/* if this is an EB, then update its Sync-IE */
if(current_neighbor == n_eb) {
packet_ready = tsch_packet_update_eb(packet, packet_len, current_packet->tsch_sync_ie_offset);
} else {
packet_ready = 1;
}
#if TSCH_SECURITY_ENABLED
if(tsch_is_pan_secured) {
/* If we are going to encrypt, we need to generate the output in a separate buffer and keep
* the original untouched. This is to allow for future retransmissions. */
int with_encryption = queuebuf_attr(current_packet->qb, PACKETBUF_ATTR_SECURITY_LEVEL) & 0x4;
packet_len += tsch_security_secure_frame(packet, with_encryption ? encrypted_packet : packet, current_packet->header_len,
packet_len - current_packet->header_len, &current_asn);
if(with_encryption) {
packet = encrypted_packet;
}
}
#endif /* TSCH_SECURITY_ENABLED */
/* prepare packet to send: copy to radio buffer */
if(packet_ready && NETSTACK_RADIO.prepare(packet, packet_len) == 0) { /* 0 means success */
static rtimer_clock_t tx_duration;
#if CCA_ENABLED
cca_status = 1;
/* delay before CCA */
TSCH_SCHEDULE_AND_YIELD(pt, t, current_slot_start, TS_CCA_OFFSET, "cca");
TSCH_DEBUG_TX_EVENT();
NETSTACK_RADIO.on();
/* CCA */
BUSYWAIT_UNTIL_ABS(!(cca_status |= NETSTACK_RADIO.channel_clear()),
current_slot_start, TS_CCA_OFFSET + TS_CCA);
TSCH_DEBUG_TX_EVENT();
/* there is not enough time to turn radio off */
/* NETSTACK_RADIO.off(); */
if(cca_status == 0) {
mac_tx_status = MAC_TX_COLLISION;
} else
#endif /* CCA_ENABLED */
{
/* delay before TX */
TSCH_SCHEDULE_AND_YIELD(pt, t, current_slot_start, tsch_timing[tsch_ts_tx_offset] - RADIO_DELAY_BEFORE_TX, "TxBeforeTx");
TSCH_DEBUG_TX_EVENT();
/* send packet already in radio tx buffer */
mac_tx_status = NETSTACK_RADIO.transmit(packet_len);
/* Save tx timestamp */
tx_start_time = current_slot_start + tsch_timing[tsch_ts_tx_offset];
/* calculate TX duration based on sent packet len */
tx_duration = TSCH_PACKET_DURATION(packet_len);
/* limit tx_time to its max value */
tx_duration = MIN(tx_duration, tsch_timing[tsch_ts_max_tx]);
/* turn tadio off -- will turn on again to wait for ACK if needed */
NETSTACK_RADIO.off();
if(mac_tx_status == RADIO_TX_OK) {
if(!is_broadcast) {
uint8_t ackbuf[TSCH_PACKET_MAX_LEN];
int ack_len;
rtimer_clock_t ack_start_time;
int is_time_source;
radio_value_t radio_rx_mode;
struct ieee802154_ies ack_ies;
uint8_t ack_hdrlen;
frame802154_t frame;
/* Entering promiscuous mode so that the radio accepts the enhanced ACK */
NETSTACK_RADIO.get_value(RADIO_PARAM_RX_MODE, &radio_rx_mode);
NETSTACK_RADIO.set_value(RADIO_PARAM_RX_MODE, radio_rx_mode & (~RADIO_RX_MODE_ADDRESS_FILTER));
/* Unicast: wait for ack after tx: sleep until ack time */
TSCH_SCHEDULE_AND_YIELD(pt, t, current_slot_start,
tsch_timing[tsch_ts_tx_offset] + tx_duration + tsch_timing[tsch_ts_rx_ack_delay] - RADIO_DELAY_BEFORE_RX, "TxBeforeAck");
TSCH_DEBUG_TX_EVENT();
NETSTACK_RADIO.on();
/* Wait for ACK to come */
BUSYWAIT_UNTIL_ABS(NETSTACK_RADIO.receiving_packet(),
tx_start_time, tx_duration + tsch_timing[tsch_ts_rx_ack_delay] + tsch_timing[tsch_ts_ack_wait]);
TSCH_DEBUG_TX_EVENT();
ack_start_time = RTIMER_NOW();
/* Wait for ACK to finish */
BUSYWAIT_UNTIL_ABS(!NETSTACK_RADIO.receiving_packet(),
ack_start_time, tsch_timing[tsch_ts_max_ack]);
TSCH_DEBUG_TX_EVENT();
NETSTACK_RADIO.off();
/* Leaving promiscuous mode */
NETSTACK_RADIO.get_value(RADIO_PARAM_RX_MODE, &radio_rx_mode);
NETSTACK_RADIO.set_value(RADIO_PARAM_RX_MODE, radio_rx_mode | RADIO_RX_MODE_ADDRESS_FILTER);
/* Read ack frame */
ack_len = NETSTACK_RADIO.read((void *)ackbuf, sizeof(ackbuf));
is_time_source = 0;
/* The radio driver should return 0 if no valid packets are in the rx buffer */
if(ack_len > 0) {
is_time_source = current_neighbor != NULL && current_neighbor->is_time_source;
if(tsch_packet_parse_eack(ackbuf, ack_len, seqno,
&frame, &ack_ies, &ack_hdrlen) == 0) {
ack_len = 0;
}
#if TSCH_SECURITY_ENABLED
if(ack_len != 0) {
if(!tsch_security_parse_frame(ackbuf, ack_hdrlen, ack_len - ack_hdrlen - tsch_security_mic_len(&frame),
&frame, &current_neighbor->addr, &current_asn)) {
TSCH_LOG_ADD(tsch_log_message,
snprintf(log->message, sizeof(log->message),
"!failed to authenticate ACK"));
ack_len = 0;
}
} else {
TSCH_LOG_ADD(tsch_log_message,
snprintf(log->message, sizeof(log->message),
"!failed to parse ACK"));
}
#endif /* TSCH_SECURITY_ENABLED */
}
if(ack_len != 0) {
if(is_time_source) {
int32_t eack_time_correction = US_TO_RTIMERTICKS(ack_ies.ie_time_correction);
int32_t since_last_timesync = ASN_DIFF(current_asn, last_sync_asn);
if(eack_time_correction > SYNC_IE_BOUND) {
drift_correction = SYNC_IE_BOUND;
} else if(eack_time_correction < -SYNC_IE_BOUND) {
drift_correction = -SYNC_IE_BOUND;
} else {
drift_correction = eack_time_correction;
}
if(drift_correction != eack_time_correction) {
TSCH_LOG_ADD(tsch_log_message,
snprintf(log->message, sizeof(log->message),
"!truncated dr %d %d", (int)eack_time_correction, (int)drift_correction);
);
}
is_drift_correction_used = 1;
tsch_timesync_update(current_neighbor, since_last_timesync, drift_correction);
/* Keep track of sync time */
last_sync_asn = current_asn;
tsch_schedule_keepalive();
}
mac_tx_status = MAC_TX_OK;
} else {
mac_tx_status = MAC_TX_NOACK;
}
} else {
mac_tx_status = MAC_TX_OK;
}
} else {
mac_tx_status = MAC_TX_ERR;
}
}
}
}
current_packet->transmissions++;
current_packet->ret = mac_tx_status;
/* Post TX: Update neighbor state */
in_queue = update_neighbor_state(current_neighbor, current_packet, current_link, mac_tx_status);
/* The packet was dequeued, add it to dequeued_ringbuf for later processing */
if(in_queue == 0) {
dequeued_array[dequeued_index] = current_packet;
ringbufindex_put(&dequeued_ringbuf);
}
/* Log every tx attempt */
TSCH_LOG_ADD(tsch_log_tx,
log->tx.mac_tx_status = mac_tx_status;
log->tx.num_tx = current_packet->transmissions;
log->tx.datalen = queuebuf_datalen(current_packet->qb);
log->tx.drift = drift_correction;
log->tx.drift_used = is_drift_correction_used;
log->tx.is_data = ((((uint8_t *)(queuebuf_dataptr(current_packet->qb)))[0]) & 7) == FRAME802154_DATAFRAME;
log->tx.sec_level = queuebuf_attr(current_packet->qb, PACKETBUF_ATTR_SECURITY_LEVEL);
log->tx.dest = TSCH_LOG_ID_FROM_LINKADDR(queuebuf_addr(current_packet->qb, PACKETBUF_ADDR_RECEIVER));
);
/* Poll process for later processing of packet sent events and logs */
process_poll(&tsch_pending_events_process);
}
TSCH_DEBUG_TX_EVENT();
PT_END(pt);
}
/*---------------------------------------------------------------------------*/
static
PT_THREAD(tsch_rx_slot(struct pt *pt, struct rtimer *t))
{
/**
* RX slot:
* 1. Check if it is used for TIME_KEEPING
* 2. Sleep and wake up just before expected RX time (with a guard time: TS_LONG_GT)
* 3. Check for radio activity for the guard time: TS_LONG_GT
* 4. Prepare and send ACK if needed
* 5. Drift calculated in the ACK callback registered with the radio driver. Use it if receiving from a time source neighbor.
**/
struct tsch_neighbor *n;
static linkaddr_t source_address;
static linkaddr_t destination_address;
static int16_t input_index;
static int input_queue_drop = 0;
PT_BEGIN(pt);
TSCH_DEBUG_RX_EVENT();
input_index = ringbufindex_peek_put(&input_ringbuf);
if(input_index == -1) {
input_queue_drop++;
} else {
static struct input_packet *current_input;
/* Estimated drift based on RX time */
static int32_t estimated_drift;
/* Rx timestamps */
static rtimer_clock_t rx_start_time;
static rtimer_clock_t expected_rx_time;
static rtimer_clock_t packet_duration;
uint8_t packet_seen;
expected_rx_time = current_slot_start + tsch_timing[tsch_ts_tx_offset];
/* Default start time: expected Rx time */
rx_start_time = expected_rx_time;
current_input = &input_array[input_index];
/* Wait before starting to listen */
TSCH_SCHEDULE_AND_YIELD(pt, t, current_slot_start, tsch_timing[tsch_ts_rx_offset] - RADIO_DELAY_BEFORE_RX, "RxBeforeListen");
TSCH_DEBUG_RX_EVENT();
/* Start radio for at least guard time */
NETSTACK_RADIO.on();
packet_seen = NETSTACK_RADIO.receiving_packet();
if(!packet_seen) {
/* Check if receiving within guard time */
BUSYWAIT_UNTIL_ABS((packet_seen = NETSTACK_RADIO.receiving_packet()),
current_slot_start, tsch_timing[tsch_ts_rx_offset] + tsch_timing[tsch_ts_rx_wait]);
}
if(packet_seen) {
TSCH_DEBUG_RX_EVENT();
/* Save packet timestamp */
rx_start_time = RTIMER_NOW() - RADIO_DELAY_BEFORE_DETECT;
}
if(!NETSTACK_RADIO.receiving_packet() && !NETSTACK_RADIO.pending_packet()) {
NETSTACK_RADIO.off();
/* no packets on air */
} else {
/* Wait until packet is received, turn radio off */
BUSYWAIT_UNTIL_ABS(!NETSTACK_RADIO.receiving_packet(),
current_slot_start, tsch_timing[tsch_ts_rx_offset] + tsch_timing[tsch_ts_rx_wait] + tsch_timing[tsch_ts_max_tx]);
TSCH_DEBUG_RX_EVENT();
NETSTACK_RADIO.off();
#if TSCH_RESYNC_WITH_SFD_TIMESTAMPS
/* At the end of the reception, get an more accurate estimate of SFD arrival time */
NETSTACK_RADIO.get_object(RADIO_PARAM_LAST_PACKET_TIMESTAMP, &rx_start_time, sizeof(rtimer_clock_t));
#endif
if(NETSTACK_RADIO.pending_packet()) {
static int frame_valid;
static int header_len;
static frame802154_t frame;
radio_value_t radio_last_rssi;
NETSTACK_RADIO.get_value(RADIO_PARAM_LAST_RSSI, &radio_last_rssi);
/* Read packet */
current_input->len = NETSTACK_RADIO.read((void *)current_input->payload, TSCH_PACKET_MAX_LEN);
current_input->rx_asn = current_asn;
current_input->rssi = (signed)radio_last_rssi;
header_len = frame802154_parse((uint8_t *)current_input->payload, current_input->len, &frame);
frame_valid = header_len > 0 &&
frame802154_check_dest_panid(&frame) &&
frame802154_extract_linkaddr(&frame, &source_address, &destination_address);
packet_duration = TSCH_PACKET_DURATION(current_input->len);
#if TSCH_SECURITY_ENABLED
/* Decrypt and verify incoming frame */
if(frame_valid) {
if(tsch_security_parse_frame(
current_input->payload, header_len, current_input->len - header_len - tsch_security_mic_len(&frame),
&frame, &source_address, &current_asn)) {
current_input->len -= tsch_security_mic_len(&frame);
} else {
TSCH_LOG_ADD(tsch_log_message,
snprintf(log->message, sizeof(log->message),
"!failed to authenticate frame %u", current_input->len));
frame_valid = 0;
}
} else {
TSCH_LOG_ADD(tsch_log_message,
snprintf(log->message, sizeof(log->message),
"!failed to parse frame %u %u", header_len, current_input->len));
frame_valid = 0;
}
#endif /* TSCH_SECURITY_ENABLED */
if(frame_valid) {
if(linkaddr_cmp(&destination_address, &linkaddr_node_addr)
|| linkaddr_cmp(&destination_address, &linkaddr_null)) {
int do_nack = 0;
estimated_drift = ((int32_t)expected_rx_time - (int32_t)rx_start_time);
#if TSCH_TIMESYNC_REMOVE_JITTER
/* remove jitter due to measurement errors */
if(abs(estimated_drift) <= TSCH_TIMESYNC_MEASUREMENT_ERROR) {
estimated_drift = 0;
} else if(estimated_drift > 0) {
estimated_drift -= TSCH_TIMESYNC_MEASUREMENT_ERROR;
} else {
estimated_drift += TSCH_TIMESYNC_MEASUREMENT_ERROR;
}
#endif
#ifdef TSCH_CALLBACK_DO_NACK
if(frame.fcf.ack_required) {
do_nack = TSCH_CALLBACK_DO_NACK(current_link,
&source_address, &destination_address);
}
#endif
if(frame.fcf.ack_required) {
static uint8_t ack_buf[TSCH_PACKET_MAX_LEN];
static int ack_len;
/* Build ACK frame */
ack_len = tsch_packet_create_eack(ack_buf, sizeof(ack_buf),
&source_address, frame.seq, (int16_t)RTIMERTICKS_TO_US(estimated_drift), do_nack);
#if TSCH_SECURITY_ENABLED
if(tsch_is_pan_secured) {
/* Secure ACK frame. There is only header and header IEs, therefore data len == 0. */
ack_len += tsch_security_secure_frame(ack_buf, ack_buf, ack_len, 0, &current_asn);
}
#endif /* TSCH_SECURITY_ENABLED */
/* Copy to radio buffer */
NETSTACK_RADIO.prepare((const void *)ack_buf, ack_len);
/* Wait for time to ACK and transmit ACK */
TSCH_SCHEDULE_AND_YIELD(pt, t, rx_start_time,
packet_duration + tsch_timing[tsch_ts_tx_ack_delay] - RADIO_DELAY_BEFORE_TX, "RxBeforeAck");
TSCH_DEBUG_RX_EVENT();
NETSTACK_RADIO.transmit(ack_len);
}
/* If the sender is a time source, proceed to clock drift compensation */
n = tsch_queue_get_nbr(&source_address);
if(n != NULL && n->is_time_source) {
int32_t since_last_timesync = ASN_DIFF(current_asn, last_sync_asn);
/* Keep track of last sync time */
last_sync_asn = current_asn;
/* Save estimated drift */
drift_correction = -estimated_drift;
is_drift_correction_used = 1;
tsch_timesync_update(n, since_last_timesync, -estimated_drift);
tsch_schedule_keepalive();
}
/* Add current input to ringbuf */
ringbufindex_put(&input_ringbuf);
/* Log every reception */
TSCH_LOG_ADD(tsch_log_rx,
log->rx.src = TSCH_LOG_ID_FROM_LINKADDR((linkaddr_t*)&frame.src_addr);
log->rx.is_unicast = frame.fcf.ack_required;
log->rx.datalen = current_input->len;
log->rx.drift = drift_correction;
log->rx.drift_used = is_drift_correction_used;
log->rx.is_data = frame.fcf.frame_type == FRAME802154_DATAFRAME;
log->rx.sec_level = frame.aux_hdr.security_control.security_level;
log->rx.estimated_drift = estimated_drift;
);
} else {
TSCH_LOG_ADD(tsch_log_message,
snprintf(log->message, sizeof(log->message),
"!not for us %x:%x",
destination_address.u8[LINKADDR_SIZE - 2], destination_address.u8[LINKADDR_SIZE - 1]);
);
}
/* Poll process for processing of pending input and logs */
process_poll(&tsch_pending_events_process);
}
}
}
if(input_queue_drop != 0) {
TSCH_LOG_ADD(tsch_log_message,
snprintf(log->message, sizeof(log->message),
"!queue full skipped %u", input_queue_drop);
);
input_queue_drop = 0;
}
}
TSCH_DEBUG_RX_EVENT();
PT_END(pt);
}
/*---------------------------------------------------------------------------*/
/* Protothread for slot operation, called from rtimer interrupt
* and scheduled from tsch_schedule_slot_operation */
static
PT_THREAD(tsch_slot_operation(struct rtimer *t, void *ptr))
{
TSCH_DEBUG_INTERRUPT();
PT_BEGIN(&slot_operation_pt);
/* Loop over all active slots */
while(tsch_is_associated) {
if(current_link == NULL || tsch_lock_requested) { /* Skip slot operation if there is no link
or if there is a pending request for getting the lock */
/* Issue a log whenever skipping a slot */
TSCH_LOG_ADD(tsch_log_message,
snprintf(log->message, sizeof(log->message),
"!skipped slot %u %u %u",
tsch_locked,
tsch_lock_requested,
current_link == NULL);
);
} else {
uint8_t current_channel;
TSCH_DEBUG_SLOT_START();
tsch_in_slot_operation = 1;
/* Get a packet ready to be sent */
current_packet = get_packet_and_neighbor_for_link(current_link, &current_neighbor);
/* There is no packet to send, and this link does not have Rx flag. Instead of doing
* nothing, switch to the backup link (has Rx flag) if any. */
if(current_packet == NULL && !(current_link->link_options & LINK_OPTION_RX) && backup_link != NULL) {
current_link = backup_link;
current_packet = get_packet_and_neighbor_for_link(current_link, &current_neighbor);
}
/* Hop channel */
current_channel = tsch_calculate_channel(&current_asn, current_link->channel_offset);
NETSTACK_RADIO.set_value(RADIO_PARAM_CHANNEL, current_channel);
/* Reset drift correction */
drift_correction = 0;
is_drift_correction_used = 0;
/* Decide whether it is a TX/RX/IDLE or OFF slot */
/* Actual slot operation */
if(current_packet != NULL) {
/* We have something to transmit, do the following:
* 1. send
* 2. update_backoff_state(current_neighbor)
* 3. post tx callback
**/
static struct pt slot_tx_pt;
PT_SPAWN(&slot_operation_pt, &slot_tx_pt, tsch_tx_slot(&slot_tx_pt, t));
} else if((current_link->link_options & LINK_OPTION_RX)) {
/* Listen */
static struct pt slot_rx_pt;
PT_SPAWN(&slot_operation_pt, &slot_rx_pt, tsch_rx_slot(&slot_rx_pt, t));
}
TSCH_DEBUG_SLOT_END();
}
/* End of slot operation, schedule next slot or resynchronize */
/* Do we need to resynchronize? i.e., wait for EB again */
if(!tsch_is_coordinator && (ASN_DIFF(current_asn, last_sync_asn) >
(100 * TSCH_CLOCK_TO_SLOTS(TSCH_DESYNC_THRESHOLD / 100, tsch_timing[tsch_ts_timeslot_length])))) {
TSCH_LOG_ADD(tsch_log_message,
snprintf(log->message, sizeof(log->message),
"! leaving the network, last sync %u",
(unsigned)ASN_DIFF(current_asn, last_sync_asn));
);
last_timesource_neighbor = NULL;
tsch_disassociate();
} else {
/* backup of drift correction for printing debug messages */
/* int32_t drift_correction_backup = drift_correction; */
uint16_t timeslot_diff = 0;
rtimer_clock_t prev_slot_start;
/* Time to next wake up */
rtimer_clock_t time_to_next_active_slot;
/* Schedule next wakeup skipping slots if missed deadline */
do {
if(current_link != NULL
&& current_link->link_options & LINK_OPTION_TX
&& current_link->link_options & LINK_OPTION_SHARED) {
/* Decrement the backoff window for all neighbors able to transmit over
* this Tx, Shared link. */
tsch_queue_update_all_backoff_windows(&current_link->addr);
}
/* Get next active link */
current_link = tsch_schedule_get_next_active_link(&current_asn, &timeslot_diff, &backup_link);
if(current_link == NULL) {
/* There is no next link. Fall back to default
* behavior: wake up at the next slot. */
timeslot_diff = 1;
}
/* Update ASN */
ASN_INC(current_asn, timeslot_diff);
/* Time to next wake up */
time_to_next_active_slot = timeslot_diff * tsch_timing[tsch_ts_timeslot_length] + drift_correction;
drift_correction = 0;
is_drift_correction_used = 0;
/* Update current slot start */
prev_slot_start = current_slot_start;
current_slot_start += time_to_next_active_slot;
current_slot_start += tsch_timesync_adaptive_compensate(time_to_next_active_slot);
} while(!tsch_schedule_slot_operation(t, prev_slot_start, time_to_next_active_slot, "main"));
}
tsch_in_slot_operation = 0;
PT_YIELD(&slot_operation_pt);
}
PT_END(&slot_operation_pt);
}
/*---------------------------------------------------------------------------*/
/* Set global time before starting slot operation,
* with a rtimer time and an ASN */
void
tsch_slot_operation_start(void)
{
static struct rtimer slot_operation_timer;
rtimer_clock_t time_to_next_active_slot;
rtimer_clock_t prev_slot_start;
TSCH_DEBUG_INIT();
do {
uint16_t timeslot_diff;
/* Get next active link */
current_link = tsch_schedule_get_next_active_link(&current_asn, &timeslot_diff, &backup_link);
if(current_link == NULL) {
/* There is no next link. Fall back to default
* behavior: wake up at the next slot. */
timeslot_diff = 1;
}
/* Update ASN */
ASN_INC(current_asn, timeslot_diff);
/* Time to next wake up */
time_to_next_active_slot = timeslot_diff * tsch_timing[tsch_ts_timeslot_length];
/* Update current slot start */
prev_slot_start = current_slot_start;
current_slot_start += time_to_next_active_slot;
} while(!tsch_schedule_slot_operation(&slot_operation_timer, prev_slot_start, time_to_next_active_slot, "association"));
}
/*---------------------------------------------------------------------------*/
/* Start actual slot operation */
void
tsch_slot_operation_sync(rtimer_clock_t next_slot_start,
struct asn_t *next_slot_asn)
{
current_slot_start = next_slot_start;
current_asn = *next_slot_asn;
last_sync_asn = current_asn;
current_link = NULL;
}
/*---------------------------------------------------------------------------*/

View file

@ -0,0 +1,123 @@
/*
* Copyright (c) 2015, SICS Swedish ICT.
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*
*/
#ifndef __TSCH_SLOT_OPERATION_H__
#define __TSCH_SLOT_OPERATION_H__
/********** Includes **********/
#include "contiki.h"
#include "lib/ringbufindex.h"
#include "net/mac/tsch/tsch-packet.h"
#include "net/mac/tsch/tsch-private.h"
/******** Configuration *******/
/* Size of the ring buffer storing dequeued outgoing packets (only an array of pointers).
* Must be power of two, and greater or equal to QUEUEBUF_NUM */
#ifdef TSCH_CONF_DEQUEUED_ARRAY_SIZE
#define TSCH_DEQUEUED_ARRAY_SIZE TSCH_CONF_DEQUEUED_ARRAY_SIZE
#else
/* By default, round QUEUEBUF_CONF_NUM to next power of two
* (in the range [4;256]) */
#if QUEUEBUF_CONF_NUM <= 4
#define TSCH_DEQUEUED_ARRAY_SIZE 4
#elif QUEUEBUF_CONF_NUM <= 8
#define TSCH_DEQUEUED_ARRAY_SIZE 8
#elif QUEUEBUF_CONF_NUM <= 16
#define TSCH_DEQUEUED_ARRAY_SIZE 16
#elif QUEUEBUF_CONF_NUM <= 32
#define TSCH_DEQUEUED_ARRAY_SIZE 32
#elif QUEUEBUF_CONF_NUM <= 64
#define TSCH_DEQUEUED_ARRAY_SIZE 64
#elif QUEUEBUF_CONF_NUM <= 128
#define TSCH_DEQUEUED_ARRAY_SIZE 128
#else
#define TSCH_DEQUEUED_ARRAY_SIZE 256
#endif
#endif
/* Size of the ring buffer storing incoming packets.
* Must be power of two */
#ifdef TSCH_CONF_MAX_INCOMING_PACKETS
#define TSCH_MAX_INCOMING_PACKETS TSCH_CONF_MAX_INCOMING_PACKETS
#else
#define TSCH_MAX_INCOMING_PACKETS 4
#endif
/*********** Callbacks *********/
/* Called by TSCH form interrupt after receiving a frame, enabled upper-layer to decide
* whether to ACK or NACK */
#ifdef TSCH_CALLBACK_DO_NACK
int TSCH_CALLBACK_DO_NACK(struct tsch_link *link, linkaddr_t *src, linkaddr_t *dst);
#endif
/************ Types ***********/
/* Stores data about an incoming packet */
struct input_packet {
uint8_t payload[TSCH_PACKET_MAX_LEN]; /* Packet payload */
struct asn_t rx_asn; /* ASN when the packet was received */
int len; /* Packet len */
uint16_t rssi; /* RSSI for this packet */
};
/***** External Variables *****/
/* A ringbuf storing outgoing packets after they were dequeued.
* Will be processed layer by tsch_tx_process_pending */
extern struct ringbufindex dequeued_ringbuf;
extern struct tsch_packet *dequeued_array[TSCH_DEQUEUED_ARRAY_SIZE];
/* A ringbuf storing incoming packets.
* Will be processed layer by tsch_rx_process_pending */
extern struct ringbufindex input_ringbuf;
extern struct input_packet input_array[TSCH_MAX_INCOMING_PACKETS];
/********** Functions *********/
/* Returns a 802.15.4 channel from an ASN and channel offset */
uint8_t tsch_calculate_channel(struct asn_t *asn, uint8_t channel_offset);
/* Is TSCH locked? */
int tsch_is_locked(void);
/* Lock TSCH (no link operation) */
int tsch_get_lock(void);
/* Release TSCH lock */
void tsch_release_lock(void);
/* Set global time before starting slot operation,
* with a rtimer time and an ASN */
void tsch_slot_operation_sync(rtimer_clock_t next_slot_start,
struct asn_t *next_slot_asn);
/* Start actual slot operation */
void tsch_slot_operation_start(void);
#endif /* __TSCH_SLOT_OPERATION_H__ */

1032
core/net/mac/tsch/tsch.c Normal file

File diff suppressed because it is too large Load diff

167
core/net/mac/tsch/tsch.h Normal file
View file

@ -0,0 +1,167 @@
/*
* Copyright (c) 2015, SICS Swedish ICT.
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*
*/
#ifndef __TSCH_H__
#define __TSCH_H__
/********** Includes **********/
#include "contiki.h"
#include "net/mac/mac.h"
#include "net/mac/tsch/tsch-security.h"
/******** Configuration *******/
/* Max time before sending a unicast keep-alive message to the time source */
#ifdef TSCH_CONF_KEEPALIVE_TIMEOUT
#define TSCH_KEEPALIVE_TIMEOUT TSCH_CONF_KEEPALIVE_TIMEOUT
#else
/* Time to desynch assuming a drift of 40 PPM (80 PPM between two nodes) and guard time of +/-1ms: 12.5s. */
#define TSCH_KEEPALIVE_TIMEOUT (12 * CLOCK_SECOND)
#endif
/* Max time without synchronization before leaving the PAN */
#ifdef TSCH_CONF_DESYNC_THRESHOLD
#define TSCH_DESYNC_THRESHOLD TSCH_CONF_DESYNC_THRESHOLD
#else
#define TSCH_DESYNC_THRESHOLD (4 * TSCH_KEEPALIVE_TIMEOUT)
#endif
/* Period between two consecutive EBs */
#ifdef TSCH_CONF_EB_PERIOD
#define TSCH_EB_PERIOD TSCH_CONF_EB_PERIOD
#else
#define TSCH_EB_PERIOD (4 * CLOCK_SECOND)
#endif
/* Max acceptable join priority */
#ifdef TSCH_CONF_MAX_JOIN_PRIORITY
#define TSCH_MAX_JOIN_PRIORITY TSCH_CONF_MAX_JOIN_PRIORITY
#else
#define TSCH_MAX_JOIN_PRIORITY 32
#endif
/* Start TSCH automatically after init? If not, the upper layers
* must call NETSTACK_MAC.on() to start it. Useful when the
* application needs to control when the nodes are to start
* scanning or advertising.*/
#ifdef TSCH_CONF_AUTOSTART
#define TSCH_AUTOSTART TSCH_CONF_AUTOSTART
#else
#define TSCH_AUTOSTART 1
#endif
/* Join only secured networks? (discard EBs with security disabled) */
#ifdef TSCH_CONF_JOIN_SECURED_ONLY
#define TSCH_JOIN_SECURED_ONLY TSCH_CONF_JOIN_SECURED_ONLY
#else
/* By default, set if TSCH_SECURITY_ENABLED is also non-zero */
#define TSCH_JOIN_SECURED_ONLY TSCH_SECURITY_ENABLED
#endif
/* By default, join any PAN ID. Otherwise, wait for an EB from IEEE802154_PANID */
#ifdef TSCH_CONF_JOIN_MY_PANID_ONLY
#define TSCH_JOIN_MY_PANID_ONLY TSCH_CONF_JOIN_MY_PANID_ONLY
#else
#define TSCH_JOIN_MY_PANID_ONLY 0
#endif
/* The radio polling frequency (in Hz) during association process */
#ifdef TSCH_CONF_ASSOCIATION_POLL_FREQUENCY
#define TSCH_ASSOCIATION_POLL_FREQUENCY TSCH_CONF_ASSOCIATION_POLL_FREQUENCY
#else
#define TSCH_ASSOCIATION_POLL_FREQUENCY 100
#endif
/* When associating, check ASN against our own uptime (time in minutes)..
* Useful to force joining only with nodes started roughly at the same time.
* Set to the max number of minutes acceptable. */
#ifdef TSCH_CONF_CHECK_TIME_AT_ASSOCIATION
#define TSCH_CHECK_TIME_AT_ASSOCIATION TSCH_CONF_CHECK_TIME_AT_ASSOCIATION
#else
#define TSCH_CHECK_TIME_AT_ASSOCIATION 0
#endif
/* By default: initialize schedule from EB when associating, using the
* slotframe and links Information Element */
#ifdef TSCH_CONF_INIT_SCHEDULE_FROM_EB
#define TSCH_INIT_SCHEDULE_FROM_EB TSCH_CONF_INIT_SCHEDULE_FROM_EB
#else
#define TSCH_INIT_SCHEDULE_FROM_EB 1
#endif
/* An ad-hoc mechanism to have TSCH select its time source without the
* help of an upper-layer, simply by collecting statistics on received
* EBs and their join priority. Disabled by default as we recomment
* mapping the time source on the RPL preferred parent
* (via tsch_rpl_callback_parent_switch) */
#ifdef TSCH_CONF_AUTOSELECT_TIME_SOURCE
#define TSCH_AUTOSELECT_TIME_SOURCE TSCH_CONF_AUTOSELECT_TIME_SOURCE
#else
#define TSCH_AUTOSELECT_TIME_SOURCE 0
#endif /* TSCH_CONF_EB_AUTOSELECT */
/*********** Callbacks *********/
/* Called by TSCH when joining a network */
#ifdef TSCH_CALLBACK_JOINING_NETWORK
void TSCH_CALLBACK_JOINING_NETWORK();
#endif
/* Called by TSCH when leaving a network */
#ifdef TSCH_CALLBACK_LEAVING_NETWORK
void TSCH_CALLBACK_LEAVING_NETWORK();
#endif
/***** External Variables *****/
/* Are we coordinator of the TSCH network? */
extern int tsch_is_coordinator;
/* Are we associated to a TSCH network? */
extern int tsch_is_associated;
/* Is the PAN running link-layer security? */
extern int tsch_is_pan_secured;
/* The TSCH MAC driver */
extern const struct mac_driver tschmac_driver;
/********** Functions *********/
/* The the TSCH join priority */
void tsch_set_join_priority(uint8_t jp);
/* The the period at which EBs are sent */
void tsch_set_eb_period(uint32_t period);
/* Set the node as PAN coordinator */
void tsch_set_coordinator(int enable);
/* Set the pan as secured or not */
void tsch_set_pan_secured(int enable);
#endif /* __TSCH_H__ */

View file

@ -55,6 +55,7 @@
#include "contiki-conf.h" #include "contiki-conf.h"
#include "net/linkaddr.h" #include "net/linkaddr.h"
#include "net/llsec/llsec802154.h" #include "net/llsec/llsec802154.h"
#include "net/mac/tsch/tsch-conf.h"
/** /**
* \brief The size of the packetbuf, in bytes * \brief The size of the packetbuf, in bytes
@ -313,6 +314,10 @@ enum {
PACKETBUF_ATTR_MAC_SEQNO, PACKETBUF_ATTR_MAC_SEQNO,
PACKETBUF_ATTR_MAC_ACK, PACKETBUF_ATTR_MAC_ACK,
PACKETBUF_ATTR_IS_CREATED_AND_SECURED, PACKETBUF_ATTR_IS_CREATED_AND_SECURED,
#if TSCH_WITH_LINK_SELECTOR
PACKETBUF_ATTR_TSCH_SLOTFRAME,
PACKETBUF_ATTR_TSCH_TIMESLOT,
#endif /* TSCH_WITH_LINK_SELECTOR */
/* Scope 1 attributes: used between two neighbors only. */ /* Scope 1 attributes: used between two neighbors only. */
#if PACKETBUF_WITH_PACKET_TYPE #if PACKETBUF_WITH_PACKET_TYPE
@ -329,14 +334,16 @@ enum {
PACKETBUF_ATTR_FRAME_TYPE, PACKETBUF_ATTR_FRAME_TYPE,
#if LLSEC802154_SECURITY_LEVEL #if LLSEC802154_SECURITY_LEVEL
PACKETBUF_ATTR_SECURITY_LEVEL, PACKETBUF_ATTR_SECURITY_LEVEL,
#endif /* LLSEC802154_SECURITY_LEVEL */
#if LLSEC802154_USES_FRAME_COUNTER
PACKETBUF_ATTR_FRAME_COUNTER_BYTES_0_1, PACKETBUF_ATTR_FRAME_COUNTER_BYTES_0_1,
PACKETBUF_ATTR_FRAME_COUNTER_BYTES_2_3, PACKETBUF_ATTR_FRAME_COUNTER_BYTES_2_3,
#endif /* LLSEC802154_USES_FRAME_COUNTER */
#if LLSEC802154_USES_EXPLICIT_KEYS #if LLSEC802154_USES_EXPLICIT_KEYS
PACKETBUF_ATTR_KEY_ID_MODE, PACKETBUF_ATTR_KEY_ID_MODE,
PACKETBUF_ATTR_KEY_INDEX, PACKETBUF_ATTR_KEY_INDEX,
PACKETBUF_ATTR_KEY_SOURCE_BYTES_0_1, PACKETBUF_ATTR_KEY_SOURCE_BYTES_0_1,
#endif /* LLSEC802154_USES_EXPLICIT_KEYS */ #endif /* LLSEC802154_USES_EXPLICIT_KEYS */
#endif /* LLSEC802154_SECURITY_LEVEL */
/* Scope 2 attributes: used between end-to-end nodes. */ /* Scope 2 attributes: used between end-to-end nodes. */
#if NETSTACK_CONF_WITH_RIME #if NETSTACK_CONF_WITH_RIME
@ -362,10 +369,15 @@ enum {
#if !LLSEC802154_SECURITY_LEVEL #if !LLSEC802154_SECURITY_LEVEL
enum { enum {
PACKETBUF_ATTR_SECURITY_LEVEL, PACKETBUF_ATTR_SECURITY_LEVEL,
};
#endif /* LLSEC802154_SECURITY_LEVEL */
#if !LLSEC802154_USES_FRAME_COUNTER
enum {
PACKETBUF_ATTR_FRAME_COUNTER_BYTES_0_1, PACKETBUF_ATTR_FRAME_COUNTER_BYTES_0_1,
PACKETBUF_ATTR_FRAME_COUNTER_BYTES_2_3 PACKETBUF_ATTR_FRAME_COUNTER_BYTES_2_3
}; };
#endif /* LLSEC802154_SECURITY_LEVEL */ #endif /* LLSEC802154_USES_FRAME_COUNTER */
/* Define surrogates when not using explicit keys */ /* Define surrogates when not using explicit keys */
#if !LLSEC802154_USES_EXPLICIT_KEYS #if !LLSEC802154_USES_EXPLICIT_KEYS

View file

@ -60,6 +60,11 @@
#define DEBUG DEBUG_NONE #define DEBUG DEBUG_NONE
#include "net/ip/uip-debug.h" #include "net/ip/uip-debug.h"
/* A configurable function called after every RPL parent switch */
#ifdef RPL_CALLBACK_PARENT_SWITCH
void RPL_CALLBACK_PARENT_SWITCH(rpl_parent_t *old, rpl_parent_t *new);
#endif /* RPL_CALLBACK_PARENT_SWITCH */
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
extern rpl_of_t RPL_OF; extern rpl_of_t RPL_OF;
static rpl_of_t * const objective_functions[] = {&RPL_OF}; static rpl_of_t * const objective_functions[] = {&RPL_OF};
@ -188,6 +193,10 @@ rpl_set_preferred_parent(rpl_dag_t *dag, rpl_parent_t *p)
} }
PRINTF("\n"); PRINTF("\n");
#ifdef RPL_CALLBACK_PARENT_SWITCH
RPL_CALLBACK_PARENT_SWITCH(dag->preferred_parent, p);
#endif /* RPL_CALLBACK_PARENT_SWITCH */
/* Always keep the preferred parent locked, so it remains in the /* Always keep the preferred parent locked, so it remains in the
* neighbor table. */ * neighbor table. */
nbr_table_unlock(rpl_parents, dag->preferred_parent); nbr_table_unlock(rpl_parents, dag->preferred_parent);

View file

@ -50,6 +50,11 @@
#define DEBUG DEBUG_NONE #define DEBUG DEBUG_NONE
#include "net/ip/uip-debug.h" #include "net/ip/uip-debug.h"
/* A configurable function called after update of the RPL DIO interval */
#ifdef RPL_CALLBACK_NEW_DIO_INTERVAL
void RPL_CALLBACK_NEW_DIO_INTERVAL(uint8_t dio_interval);
#endif /* RPL_CALLBACK_NEW_DIO_INTERVAL */
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
static struct ctimer periodic_timer; static struct ctimer periodic_timer;
@ -124,6 +129,10 @@ new_dio_interval(rpl_instance_t *instance)
/* schedule the timer */ /* schedule the timer */
PRINTF("RPL: Scheduling DIO timer %lu ticks in future (Interval)\n", ticks); PRINTF("RPL: Scheduling DIO timer %lu ticks in future (Interval)\n", ticks);
ctimer_set(&instance->dio_timer, ticks, &handle_dio_timer, instance); ctimer_set(&instance->dio_timer, ticks, &handle_dio_timer, instance);
#ifdef RPL_CALLBACK_NEW_DIO_INTERVAL
RPL_CALLBACK_NEW_DIO_INTERVAL(instance->dio_intcurrent);
#endif /* RPL_CALLBACK_NEW_DIO_INTERVAL */
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
static void static void

View file

@ -48,6 +48,20 @@
#define RTIMER_ARCH_SECOND (4096U*8) #define RTIMER_ARCH_SECOND (4096U*8)
#endif #endif
/* Do the math in 32bits to save precision.
* Round to nearest integer rather than truncate. */
#define US_TO_RTIMERTICKS(US) ((US) >= 0 ? \
(((int32_t)(US) * (RTIMER_ARCH_SECOND) + 500000) / 1000000L) : \
((int32_t)(US) * (RTIMER_ARCH_SECOND) - 500000) / 1000000L)
#define RTIMERTICKS_TO_US(T) ((T) >= 0 ? \
(((int32_t)(T) * 1000000L + ((RTIMER_ARCH_SECOND) / 2)) / (RTIMER_ARCH_SECOND)) : \
((int32_t)(T) * 1000000L - ((RTIMER_ARCH_SECOND) / 2)) / (RTIMER_ARCH_SECOND))
/* A 64-bit version because the 32-bit one cannot handle T >= 4295 ticks.
Intended only for positive values of T. */
#define RTIMERTICKS_TO_US_64(T) ((uint32_t)(((uint64_t)(T) * 1000000 + ((RTIMER_ARCH_SECOND) / 2)) / (RTIMER_ARCH_SECOND)))
rtimer_clock_t rtimer_arch_now(void); rtimer_clock_t rtimer_arch_now(void);
#endif /* RTIMER_ARCH_H_ */ #endif /* RTIMER_ARCH_H_ */

View file

@ -140,6 +140,13 @@ static volatile uint16_t last_packet_timestamp;
PROCESS(cc2420_process, "CC2420 driver"); PROCESS(cc2420_process, "CC2420 driver");
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
#define AUTOACK (1 << 4)
#define AUTOCRC (1 << 5)
#define ADR_DECODE (1 << 11)
#define RXFIFO_PROTECTION (1 << 9)
#define CORR_THR(n) (((n) & 0x1f) << 6)
#define FIFOP_THR(n) ((n) & 0x7f)
#define RXBPF_LOCUR (1 << 13);
int cc2420_on(void); int cc2420_on(void);
int cc2420_off(void); int cc2420_off(void);
@ -154,6 +161,12 @@ static int cc2420_receiving_packet(void);
static int pending_packet(void); static int pending_packet(void);
static int get_cca_threshold(void); static int get_cca_threshold(void);
static int cc2420_cca(void); static int cc2420_cca(void);
static uint16_t getreg(enum cc2420_register regname);
static void set_frame_filtering(uint8_t enable);
static void set_poll_mode(uint8_t enable);
static void set_send_on_cca(uint8_t enable);
static void set_auto_ack(uint8_t enable);
signed char cc2420_last_rssi; signed char cc2420_last_rssi;
uint8_t cc2420_last_correlation; uint8_t cc2420_last_correlation;
@ -161,6 +174,11 @@ uint8_t cc2420_last_correlation;
static uint8_t receive_on; static uint8_t receive_on;
static int channel; static int channel;
/* Are we currently in poll mode? */
static uint8_t volatile poll_mode = 0;
/* Do we perform a CCA before sending? */
static uint8_t send_on_cca = WITH_SEND_CCA;
static radio_result_t static radio_result_t
get_value(radio_param_t param, radio_value_t *value) get_value(radio_param_t param, radio_value_t *value)
{ {
@ -176,6 +194,24 @@ get_value(radio_param_t param, radio_value_t *value)
case RADIO_PARAM_CHANNEL: case RADIO_PARAM_CHANNEL:
*value = cc2420_get_channel(); *value = cc2420_get_channel();
return RADIO_RESULT_OK; return RADIO_RESULT_OK;
case RADIO_PARAM_RX_MODE:
*value = 0;
if(getreg(CC2420_MDMCTRL0) & ADR_DECODE) {
*value |= RADIO_RX_MODE_ADDRESS_FILTER;
}
if(getreg(CC2420_MDMCTRL0) & AUTOACK) {
*value |= RADIO_RX_MODE_AUTOACK;
}
if(poll_mode) {
*value |= RADIO_RX_MODE_POLL_MODE;
}
return RADIO_RESULT_OK;
case RADIO_PARAM_TX_MODE:
*value = 0;
if(send_on_cca) {
*value |= RADIO_TX_MODE_SEND_ON_CCA;
}
return RADIO_RESULT_OK;
case RADIO_PARAM_TXPOWER: case RADIO_PARAM_TXPOWER:
v = cc2420_get_txpower(); v = cc2420_get_txpower();
*value = OUTPUT_POWER_MIN; *value = OUTPUT_POWER_MIN;
@ -194,6 +230,14 @@ get_value(radio_param_t param, radio_value_t *value)
/* Return the RSSI value in dBm */ /* Return the RSSI value in dBm */
*value = cc2420_rssi(); *value = cc2420_rssi();
return RADIO_RESULT_OK; return RADIO_RESULT_OK;
case RADIO_PARAM_LAST_RSSI:
/* RSSI of the last packet received */
*value = cc2420_last_rssi;
return RADIO_RESULT_OK;
case RADIO_PARAM_LAST_LINK_QUALITY:
/* LQI of the last packet received */
*value = cc2420_last_correlation;
return RADIO_RESULT_OK;
case RADIO_CONST_CHANNEL_MIN: case RADIO_CONST_CHANNEL_MIN:
*value = 11; *value = 11;
return RADIO_RESULT_OK; return RADIO_RESULT_OK;
@ -233,6 +277,21 @@ set_value(radio_param_t param, radio_value_t value)
} }
cc2420_set_channel(value); cc2420_set_channel(value);
return RADIO_RESULT_OK; return RADIO_RESULT_OK;
case RADIO_PARAM_RX_MODE:
if(value & ~(RADIO_RX_MODE_ADDRESS_FILTER |
RADIO_RX_MODE_AUTOACK | RADIO_RX_MODE_POLL_MODE)) {
return RADIO_RESULT_INVALID_VALUE;
}
set_frame_filtering((value & RADIO_RX_MODE_ADDRESS_FILTER) != 0);
set_auto_ack((value & RADIO_RX_MODE_AUTOACK) != 0);
set_poll_mode((value & RADIO_RX_MODE_POLL_MODE) != 0);
return RADIO_RESULT_OK;
case RADIO_PARAM_TX_MODE:
if(value & ~(RADIO_TX_MODE_SEND_ON_CCA)) {
return RADIO_RESULT_INVALID_VALUE;
}
set_send_on_cca((value & RADIO_TX_MODE_SEND_ON_CCA) != 0);
return RADIO_RESULT_OK;
case RADIO_PARAM_TXPOWER: case RADIO_PARAM_TXPOWER:
if(value < OUTPUT_POWER_MIN || value > OUTPUT_POWER_MAX) { if(value < OUTPUT_POWER_MIN || value > OUTPUT_POWER_MAX) {
return RADIO_RESULT_INVALID_VALUE; return RADIO_RESULT_INVALID_VALUE;
@ -256,6 +315,17 @@ set_value(radio_param_t param, radio_value_t value)
static radio_result_t static radio_result_t
get_object(radio_param_t param, void *dest, size_t size) get_object(radio_param_t param, void *dest, size_t size)
{ {
if(param == RADIO_PARAM_LAST_PACKET_TIMESTAMP) {
#if CC2420_CONF_SFD_TIMESTAMPS
if(size != sizeof(rtimer_clock_t) || !dest) {
return RADIO_RESULT_INVALID_VALUE;
}
*(rtimer_clock_t*)dest = cc2420_sfd_start_time;
return RADIO_RESULT_OK;
#else
return RADIO_RESULT_NOT_SUPPORTED;
#endif
}
return RADIO_RESULT_NOT_SUPPORTED; return RADIO_RESULT_NOT_SUPPORTED;
} }
@ -447,7 +517,10 @@ wait_for_transmission(void)
static void static void
on(void) on(void)
{ {
CC2420_ENABLE_FIFOP_INT(); if(!poll_mode) {
CC2420_ENABLE_FIFOP_INT();
}
strobe(CC2420_SRXON); strobe(CC2420_SRXON);
ENERGEST_ON(ENERGEST_TYPE_LISTEN); ENERGEST_ON(ENERGEST_TYPE_LISTEN);
@ -465,7 +538,9 @@ off(void)
ENERGEST_OFF(ENERGEST_TYPE_LISTEN); ENERGEST_OFF(ENERGEST_TYPE_LISTEN);
strobe(CC2420_SRFOFF); strobe(CC2420_SRFOFF);
CC2420_DISABLE_FIFOP_INT(); if(!poll_mode) {
CC2420_DISABLE_FIFOP_INT();
}
if(!CC2420_FIFOP_IS_1) { if(!CC2420_FIFOP_IS_1) {
flushrx(); flushrx();
@ -539,14 +614,6 @@ set_txpower(uint8_t power)
setreg(CC2420_TXCTRL, reg); setreg(CC2420_TXCTRL, reg);
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
#define AUTOACK (1 << 4)
#define AUTOCRC (1 << 5)
#define ADR_DECODE (1 << 11)
#define RXFIFO_PROTECTION (1 << 9)
#define CORR_THR(n) (((n) & 0x1f) << 6)
#define FIFOP_THR(n) ((n) & 0x7f)
#define RXBPF_LOCUR (1 << 13);
/*---------------------------------------------------------------------------*/
int int
cc2420_init(void) cc2420_init(void)
{ {
@ -573,20 +640,15 @@ cc2420_init(void)
/* And wait until it stabilizes */ /* And wait until it stabilizes */
wait_for_status(BV(CC2420_XOSC16M_STABLE)); wait_for_status(BV(CC2420_XOSC16M_STABLE));
/* Turn on/off automatic packet acknowledgment and address decoding. */ /* Set auto-ack and frame filtering */
reg = getreg(CC2420_MDMCTRL0); set_auto_ack(CC2420_CONF_AUTOACK);
set_frame_filtering(CC2420_CONF_AUTOACK);
#if CC2420_CONF_AUTOACK
reg |= AUTOACK | ADR_DECODE;
#else
reg &= ~(AUTOACK | ADR_DECODE);
#endif /* CC2420_CONF_AUTOACK */
/* Enabling CRC in hardware; this is required by AUTOACK anyway /* Enabling CRC in hardware; this is required by AUTOACK anyway
and provides us with RSSI and link quality indication (LQI) and provides us with RSSI and link quality indication (LQI)
information. */ information. */
reg = getreg(CC2420_MDMCTRL0);
reg |= AUTOCRC; reg |= AUTOCRC;
setreg(CC2420_MDMCTRL0, reg); setreg(CC2420_MDMCTRL0, reg);
/* Set transmission turnaround time to the lower setting (8 symbols /* Set transmission turnaround time to the lower setting (8 symbols
@ -614,6 +676,8 @@ cc2420_init(void)
flushrx(); flushrx();
set_poll_mode(0);
process_start(&cc2420_process, NULL); process_start(&cc2420_process, NULL);
return 1; return 1;
} }
@ -646,13 +710,13 @@ cc2420_transmit(unsigned short payload_len)
#define LOOP_20_SYMBOLS CC2420_CONF_SYMBOL_LOOP_COUNT #define LOOP_20_SYMBOLS CC2420_CONF_SYMBOL_LOOP_COUNT
#endif #endif
#if WITH_SEND_CCA if(send_on_cca) {
strobe(CC2420_SRXON); strobe(CC2420_SRXON);
wait_for_status(BV(CC2420_RSSI_VALID)); wait_for_status(BV(CC2420_RSSI_VALID));
strobe(CC2420_STXONCCA); strobe(CC2420_STXONCCA);
#else /* WITH_SEND_CCA */ } else {
strobe(CC2420_STXON); strobe(CC2420_STXON);
#endif /* WITH_SEND_CCA */ }
for(i = LOOP_20_SYMBOLS; i > 0; i--) { for(i = LOOP_20_SYMBOLS; i > 0; i--) {
if(CC2420_SFD_IS_1) { if(CC2420_SFD_IS_1) {
#if PACKETBUF_WITH_PACKET_TYPE #if PACKETBUF_WITH_PACKET_TYPE
@ -704,7 +768,7 @@ cc2420_transmit(unsigned short payload_len)
} }
} }
/* If we are using WITH_SEND_CCA, we get here if the packet wasn't /* If we send with cca (cca_on_send), we get here if the packet wasn't
transmitted because of other channel activity. */ transmitted because of other channel activity. */
RIMESTATS_ADD(contentiondrop); RIMESTATS_ADD(contentiondrop);
PRINTF("cc2420: do_send() transmission never started\n"); PRINTF("cc2420: do_send() transmission never started\n");
@ -869,7 +933,7 @@ PROCESS_THREAD(cc2420_process, ev, data)
PRINTF("cc2420_process: started\n"); PRINTF("cc2420_process: started\n");
while(1) { while(1) {
PROCESS_YIELD_UNTIL(ev == PROCESS_EVENT_POLL); PROCESS_YIELD_UNTIL(!poll_mode && ev == PROCESS_EVENT_POLL);
PRINTF("cc2420_process: calling receiver callback\n"); PRINTF("cc2420_process: calling receiver callback\n");
@ -913,25 +977,31 @@ cc2420_read(void *buf, unsigned short bufsize)
if(footer[1] & FOOTER1_CRC_OK) { if(footer[1] & FOOTER1_CRC_OK) {
cc2420_last_rssi = footer[0] + RSSI_OFFSET; cc2420_last_rssi = footer[0] + RSSI_OFFSET;
cc2420_last_correlation = footer[1] & FOOTER1_CORRELATION; cc2420_last_correlation = footer[1] & FOOTER1_CORRELATION;
if(!poll_mode) {
packetbuf_set_attr(PACKETBUF_ATTR_RSSI, cc2420_last_rssi); /* Not in poll mode: packetbuf should not be accessed in interrupt context.
packetbuf_set_attr(PACKETBUF_ATTR_LINK_QUALITY, cc2420_last_correlation); * In poll mode, the last packet RSSI and link quality can be obtained through
* RADIO_PARAM_LAST_RSSI and RADIO_PARAM_LAST_LINK_QUALITY */
packetbuf_set_attr(PACKETBUF_ATTR_RSSI, cc2420_last_rssi);
packetbuf_set_attr(PACKETBUF_ATTR_LINK_QUALITY, cc2420_last_correlation);
}
RIMESTATS_ADD(llrx); RIMESTATS_ADD(llrx);
} else { } else {
RIMESTATS_ADD(badcrc); RIMESTATS_ADD(badcrc);
len = FOOTER_LEN; len = FOOTER_LEN;
} }
if(CC2420_FIFOP_IS_1) { if(!poll_mode) {
if(!CC2420_FIFO_IS_1) { if(CC2420_FIFOP_IS_1) {
/* Clean up in case of FIFO overflow! This happens for every if(!CC2420_FIFO_IS_1) {
* full length frame and is signaled by FIFOP = 1 and FIFO = /* Clean up in case of FIFO overflow! This happens for every
* 0. */ * full length frame and is signaled by FIFOP = 1 and FIFO =
flushrx(); * 0. */
} else { flushrx();
/* Another packet has been received and needs attention. */ } else {
process_poll(&cc2420_process); /* Another packet has been received and needs attention. */
process_poll(&cc2420_process);
}
} }
} }
@ -1062,3 +1132,64 @@ cc2420_set_cca_threshold(int value)
RELEASE_LOCK(); RELEASE_LOCK();
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/* Set or unset frame autoack */
static void
set_auto_ack(uint8_t enable)
{
GET_LOCK();
uint16_t reg = getreg(CC2420_MDMCTRL0);
if(enable) {
reg |= AUTOACK;
} else {
reg &= ~(AUTOACK);
}
setreg(CC2420_MDMCTRL0, reg);
RELEASE_LOCK();
}
/*---------------------------------------------------------------------------*/
/* Set or unset frame filtering */
static void
set_frame_filtering(uint8_t enable)
{
GET_LOCK();
/* Turn on/off address decoding. */
uint16_t reg = getreg(CC2420_MDMCTRL0);
if(enable) {
reg |= ADR_DECODE;
} else {
reg &= ~(ADR_DECODE);
}
setreg(CC2420_MDMCTRL0, reg);
RELEASE_LOCK();
}
/*---------------------------------------------------------------------------*/
/* Enable or disable radio interrupts (both FIFOP and SFD timer capture) */
static void
set_poll_mode(uint8_t enable)
{
GET_LOCK();
poll_mode = enable;
if(enable) {
/* Disable FIFOP interrupt */
CC2420_CLEAR_FIFOP_INT();
CC2420_DISABLE_FIFOP_INT();
} else {
/* Initialize and enable FIFOP interrupt */
CC2420_FIFOP_INT_INIT();
CC2420_ENABLE_FIFOP_INT();
CC2420_CLEAR_FIFOP_INT();
}
RELEASE_LOCK();
}
/*---------------------------------------------------------------------------*/
/* Enable or disable CCA before sending */
static void
set_send_on_cca(uint8_t enable)
{
send_on_cca = enable;
}
/*---------------------------------------------------------------------------*/

View file

@ -0,0 +1,22 @@
CONTIKI_PROJECT = node
all: $(CONTIKI_PROJECT)
CONTIKI=../../..
CFLAGS += -DPROJECT_CONF_H=\"project-conf.h\"
CONTIKI_WITH_IPV6 = 1
MAKE_WITH_ORCHESTRA ?= 0 # force Orchestra from command line
MAKE_WITH_SECURITY ?= 0 # force Security from command line
APPS += orchestra
MODULES += core/net/mac/tsch
ifeq ($(MAKE_WITH_ORCHESTRA),1)
CFLAGS += -DWITH_ORCHESTRA=1
endif
ifeq ($(MAKE_WITH_SECURITY),1)
CFLAGS += -DWITH_SECURITY=1
endif
include $(CONTIKI)/Makefile.include

View file

@ -0,0 +1,10 @@
A RPL+TSCH node. Will act as basic node by default, but can be configured at startup
using the user button and following instructions from the log output. Every press
of a button toggles the mode as 6ln, 6dr or 6dr-sec (detailled next). After 10s with
no button press, the node starts in the last setting. The modes are:
* 6ln (default): 6lowpan node, will join a RPL+TSCH network and act as router.
* 6dr: 6lowpan DAG Root, will start its own RPL+TSCH network. Note this is not a
border router, i.e. it does not have a serial interface with connection to
the Internet. For a border router, see ../border-router.
* 6dr-sec: 6lowpan DAG Root, starting a RPL+TSCH network with link-layer security
enabled. 6ln nodes are able to join both non-secured or secured networks.

View file

@ -0,0 +1,210 @@
/*
* Copyright (c) 2015, SICS Swedish ICT.
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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
* A RPL+TSCH node able to act as either a simple node (6ln),
* DAG Root (6dr) or DAG Root with security (6dr-sec)
* Press use button at startup to configure.
*
* \author Simon Duquennoy <simonduq@sics.se>
*/
#include "contiki.h"
#include "node-id.h"
#include "net/rpl/rpl.h"
#include "net/ipv6/uip-ds6-route.h"
#include "net/mac/tsch/tsch.h"
#if WITH_ORCHESTRA
#include "orchestra.h"
#endif /* WITH_ORCHESTRA */
#define DEBUG DEBUG_PRINT
#include "net/ip/uip-debug.h"
#define CONFIG_VIA_BUTTON PLATFORM_HAS_BUTTON
#if CONFIG_VIA_BUTTON
#include "button-sensor.h"
#endif /* CONFIG_VIA_BUTTON */
/*---------------------------------------------------------------------------*/
PROCESS(node_process, "RPL Node");
#if CONFIG_VIA_BUTTON
AUTOSTART_PROCESSES(&node_process, &sensors_process);
#else /* CONFIG_VIA_BUTTON */
AUTOSTART_PROCESSES(&node_process);
#endif /* CONFIG_VIA_BUTTON */
/*---------------------------------------------------------------------------*/
static void
print_network_status(void)
{
int i;
uint8_t state;
uip_ds6_defrt_t *default_route;
uip_ds6_route_t *route;
PRINTA("--- Network status ---\n");
/* Our IPv6 addresses */
PRINTA("- Server IPv6 addresses:\n");
for(i = 0; i < UIP_DS6_ADDR_NB; i++) {
state = uip_ds6_if.addr_list[i].state;
if(uip_ds6_if.addr_list[i].isused &&
(state == ADDR_TENTATIVE || state == ADDR_PREFERRED)) {
PRINTA("-- ");
uip_debug_ipaddr_print(&uip_ds6_if.addr_list[i].ipaddr);
PRINTA("\n");
}
}
/* Our default route */
PRINTA("- Default route:\n");
default_route = uip_ds6_defrt_lookup(uip_ds6_defrt_choose());
if(default_route != NULL) {
PRINTA("-- ");
uip_debug_ipaddr_print(&default_route->ipaddr);;
PRINTA(" (lifetime: %lu seconds)\n", (unsigned long)default_route->lifetime.interval);
} else {
PRINTA("-- None\n");
}
/* Our routing entries */
PRINTA("- Routing entries (%u in total):\n", uip_ds6_route_num_routes());
route = uip_ds6_route_head();
while(route != NULL) {
PRINTA("-- ");
uip_debug_ipaddr_print(&route->ipaddr);
PRINTA(" via ");
uip_debug_ipaddr_print(uip_ds6_route_nexthop(route));
PRINTA(" (lifetime: %lu seconds)\n", (unsigned long)route->state.lifetime);
route = uip_ds6_route_next(route);
}
PRINTA("----------------------\n");
}
/*---------------------------------------------------------------------------*/
static void
net_init(uip_ipaddr_t *br_prefix)
{
uip_ipaddr_t global_ipaddr;
if(br_prefix) { /* We are RPL root. Will be set automatically
as TSCH pan coordinator via the tsch-rpl module */
memcpy(&global_ipaddr, br_prefix, 16);
uip_ds6_set_addr_iid(&global_ipaddr, &uip_lladdr);
uip_ds6_addr_add(&global_ipaddr, 0, ADDR_AUTOCONF);
rpl_set_root(RPL_DEFAULT_INSTANCE, &global_ipaddr);
rpl_set_prefix(rpl_get_any_dag(), br_prefix, 64);
rpl_repair_root(RPL_DEFAULT_INSTANCE);
}
NETSTACK_MAC.on();
}
/*---------------------------------------------------------------------------*/
PROCESS_THREAD(node_process, ev, data)
{
static struct etimer et;
PROCESS_BEGIN();
/* 3 possible roles:
* - role_6ln: simple node, will join any network, secured or not
* - role_6dr: DAG root, will advertise (unsecured) beacons
* - role_6dr_sec: DAG root, will advertise secured beacons
* */
static int is_coordinator = 0;
static enum { role_6ln, role_6dr, role_6dr_sec } node_role;
node_role = role_6ln;
/* Set node with ID == 1 as coordinator, convenient in Cooja. */
if(node_id == 1) {
if(LLSEC802154_CONF_SECURITY_LEVEL) {
node_role = role_6dr_sec;
} else {
node_role = role_6dr;
}
} else {
node_role = role_6ln;
}
#if CONFIG_VIA_BUTTON
{
#define CONFIG_WAIT_TIME 5
SENSORS_ACTIVATE(button_sensor);
etimer_set(&et, CLOCK_SECOND * CONFIG_WAIT_TIME);
while(!etimer_expired(&et)) {
printf("Init: current role: %s. Will start in %u seconds. Press user button to toggle mode.\n",
node_role == role_6ln ? "6ln" : (node_role == role_6dr) ? "6dr" : "6dr-sec",
CONFIG_WAIT_TIME);
PROCESS_WAIT_EVENT_UNTIL(((ev == sensors_event) &&
(data == &button_sensor) && button_sensor.value(0) > 0)
|| etimer_expired(&et));
if(ev == sensors_event && data == &button_sensor && button_sensor.value(0) > 0) {
node_role = (node_role + 1) % 3;
if(LLSEC802154_CONF_SECURITY_LEVEL == 0 && node_role == role_6dr_sec) {
node_role = (node_role + 1) % 3;
}
etimer_restart(&et);
}
}
}
#endif /* CONFIG_VIA_BUTTON */
printf("Init: node starting with role %s\n",
node_role == role_6ln ? "6ln" : (node_role == role_6dr) ? "6dr" : "6dr-sec");
tsch_set_pan_secured(LLSEC802154_CONF_SECURITY_LEVEL && (node_role == role_6dr_sec));
is_coordinator = node_role > role_6ln;
if(is_coordinator) {
uip_ipaddr_t prefix;
uip_ip6addr(&prefix, 0xaaaa, 0, 0, 0, 0, 0, 0, 0);
net_init(&prefix);
} else {
net_init(NULL);
}
#if WITH_ORCHESTRA
orchestra_init();
#endif /* WITH_ORCHESTRA */
/* Print out routing tables every minute */
etimer_set(&et, CLOCK_SECOND * 60);
while(1) {
print_network_status();
PROCESS_YIELD_UNTIL(etimer_expired(&et));
etimer_reset(&et);
}
PROCESS_END();
}
/*---------------------------------------------------------------------------*/

View file

@ -0,0 +1,161 @@
/*
* Copyright (c) 2015, SICS Swedish ICT.
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
*/
/**
* \author Simon Duquennoy <simonduq@sics.se>
*/
#ifndef __PROJECT_CONF_H__
#define __PROJECT_CONF_H__
/* Set to run orchestra */
#ifndef WITH_ORCHESTRA
#define WITH_ORCHESTRA 0
#endif /* WITH_ORCHESTRA */
/* Set to enable TSCH security */
#ifndef WITH_SECURITY
#define WITH_SECURITY 0
#endif /* WITH_SECURITY */
/*******************************************************/
/********************* Enable TSCH *********************/
/*******************************************************/
/* Netstack layers */
#undef NETSTACK_CONF_MAC
#define NETSTACK_CONF_MAC tschmac_driver
#undef NETSTACK_CONF_RDC
#define NETSTACK_CONF_RDC nordc_driver
#undef NETSTACK_CONF_FRAMER
#define NETSTACK_CONF_FRAMER framer_802154
/* IEEE802.15.4 frame version */
#undef FRAME802154_CONF_VERSION
#define FRAME802154_CONF_VERSION FRAME802154_IEEE802154E_2012
/* TSCH and RPL callbacks */
#define RPL_CALLBACK_PARENT_SWITCH tsch_rpl_callback_parent_switch
#define RPL_CALLBACK_NEW_DIO_INTERVAL tsch_rpl_callback_new_dio_interval
#define TSCH_CALLBACK_JOINING_NETWORK tsch_rpl_callback_joining_network
#define TSCH_CALLBACK_LEAVING_NETWORK tsch_rpl_callback_leaving_network
/* Needed for cc2420 platforms only */
/* Disable DCO calibration (uses timerB) */
#undef DCOSYNCH_CONF_ENABLED
#define DCOSYNCH_CONF_ENABLED 0
/* Enable SFD timestamps (uses timerB) */
#undef CC2420_CONF_SFD_TIMESTAMPS
#define CC2420_CONF_SFD_TIMESTAMPS 1
/*******************************************************/
/******************* Configure TSCH ********************/
/*******************************************************/
/* TSCH logging. 0: disabled. 1: basic log. 2: with delayed
* log messages from interrupt */
#undef TSCH_LOG_CONF_LEVEL
#define TSCH_LOG_CONF_LEVEL 2
/* IEEE802.15.4 PANID */
#undef IEEE802154_CONF_PANID
#define IEEE802154_CONF_PANID 0xabcd
/* Do not start TSCH at init, wait for NETSTACK_MAC.on() */
#undef TSCH_CONF_AUTOSTART
#define TSCH_CONF_AUTOSTART 0
/* 6TiSCH minimal schedule length.
* Larger values result in less frequent active slots: reduces capacity and saves energy. */
#undef TSCH_SCHEDULE_CONF_DEFAULT_LENGTH
#define TSCH_SCHEDULE_CONF_DEFAULT_LENGTH 3
#if WITH_SECURITY
/* Enable security */
#undef LLSEC802154_CONF_SECURITY_LEVEL
#define LLSEC802154_CONF_SECURITY_LEVEL 1
/* TSCH uses explicit keys to identify k1 and k2 */
#undef LLSEC802154_CONF_USES_EXPLICIT_KEYS
#define LLSEC802154_CONF_USES_EXPLICIT_KEYS 1
/* TSCH uses the ASN rather than frame counter to construct the Nonce */
#undef LLSEC802154_CONF_USES_FRAME_COUNTER
#define LLSEC802154_CONF_USES_FRAME_COUNTER 0
#endif /* WITH_SECURITY */
#if WITH_ORCHESTRA
/* See apps/orchestra/README.md for more Orchestra configuration options */
#define TSCH_SCHEDULE_CONF_WITH_6TISCH_MINIMAL 0 /* No 6TiSCH minimal schedule */
#define TSCH_CONF_WITH_LINK_SELECTOR 1 /* Orchestra requires per-packet link selection */
/* Orchestra callbacks */
#define TSCH_CALLBACK_NEW_TIME_SOURCE orchestra_callback_new_time_source
#define TSCH_CALLBACK_PACKET_READY orchestra_callback_packet_ready
#define NETSTACK_CONF_ROUTING_NEIGHBOR_ADDED_CALLBACK orchestra_callback_child_added
#define NETSTACK_CONF_ROUTING_NEIGHBOR_REMOVED_CALLBACK orchestra_callback_child_removed
#endif /* WITH_ORCHESTRA */
/*******************************************************/
/************* Other system configuration **************/
/*******************************************************/
#if CONTIKI_TARGET_Z1
/* Save some space to fit the limited RAM of the z1 */
#undef UIP_CONF_TCP
#define UIP_CONF_TCP 0
#undef QUEUEBUF_CONF_NUM
#define QUEUEBUF_CONF_NUM 4
#undef UIP_CONF_MAX_ROUTES
#define UIP_CONF_MAX_ROUTES 8
#undef NBR_TABLE_CONF_MAX_NEIGHBORS
#define NBR_TABLE_CONF_MAX_NEIGHBORS 8
#undef UIP_CONF_ND6_SEND_NA
#define UIP_CONF_ND6_SEND_NA 0
#undef SICSLOWPAN_CONF_FRAG
#define SICSLOWPAN_CONF_FRAG 0
#if WITH_SECURITY
/* Note: on sky or z1 in cooja, crypto operations are done in S/W and
* cannot be accommodated in normal slots. Use 65ms slots instead, and
* a very short 6TiSCH minimal schedule length */
#undef TSCH_CONF_DEFAULT_TIMESLOT_LENGTH
#define TSCH_CONF_DEFAULT_TIMESLOT_LENGTH 65000
#undef TSCH_SCHEDULE_CONF_DEFAULT_LENGTH
#define TSCH_SCHEDULE_CONF_DEFAULT_LENGTH 2
/* Reduce log level to make space for security on z1 */
#undef TSCH_LOG_CONF_LEVEL
#define TSCH_LOG_CONF_LEVEL 1
#endif /* WITH_SECURITY */
#endif /* CONTIKI_TARGET_Z1 */
#endif /* __PROJECT_CONF_H__ */

View file

@ -0,0 +1,267 @@
<?xml version="1.0" encoding="UTF-8"?>
<simconf>
<project EXPORT="discard">[APPS_DIR]/mrm</project>
<project EXPORT="discard">[APPS_DIR]/mspsim</project>
<project EXPORT="discard">[APPS_DIR]/avrora</project>
<project EXPORT="discard">[APPS_DIR]/serial_socket</project>
<project EXPORT="discard">[APPS_DIR]/collect-view</project>
<project EXPORT="discard">[APPS_DIR]/powertracker</project>
<simulation>
<title>RPL+TSCH</title>
<randomseed>123456</randomseed>
<motedelay_us>1000000</motedelay_us>
<radiomedium>
org.contikios.cooja.radiomediums.UDGM
<transmitting_range>50.0</transmitting_range>
<interference_range>100.0</interference_range>
<success_ratio_tx>1.0</success_ratio_tx>
<success_ratio_rx>1.0</success_ratio_rx>
</radiomedium>
<events>
<logoutput>40000</logoutput>
</events>
<motetype>
org.contikios.cooja.mspmote.Z1MoteType
<identifier>z11</identifier>
<description>Z1 Mote Type #z11</description>
<source EXPORT="discard">[CONFIG_DIR]/node.c</source>
<commands EXPORT="discard">make node.z1 TARGET=z1</commands>
<firmware EXPORT="copy">[CONFIG_DIR]/node.z1</firmware>
<moteinterface>org.contikios.cooja.interfaces.Position</moteinterface>
<moteinterface>org.contikios.cooja.interfaces.RimeAddress</moteinterface>
<moteinterface>org.contikios.cooja.interfaces.IPAddress</moteinterface>
<moteinterface>org.contikios.cooja.interfaces.Mote2MoteRelations</moteinterface>
<moteinterface>org.contikios.cooja.interfaces.MoteAttributes</moteinterface>
<moteinterface>org.contikios.cooja.mspmote.interfaces.MspClock</moteinterface>
<moteinterface>org.contikios.cooja.mspmote.interfaces.MspMoteID</moteinterface>
<moteinterface>org.contikios.cooja.mspmote.interfaces.MspButton</moteinterface>
<moteinterface>org.contikios.cooja.mspmote.interfaces.Msp802154Radio</moteinterface>
<moteinterface>org.contikios.cooja.mspmote.interfaces.MspDefaultSerial</moteinterface>
<moteinterface>org.contikios.cooja.mspmote.interfaces.MspLED</moteinterface>
<moteinterface>org.contikios.cooja.mspmote.interfaces.MspDebugOutput</moteinterface>
</motetype>
<mote>
<breakpoints />
<interface_config>
org.contikios.cooja.interfaces.Position
<x>-1.285769821276336</x>
<y>38.58045647334346</y>
<z>0.0</z>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspClock
<deviation>1.0</deviation>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspMoteID
<id>1</id>
</interface_config>
<motetype_identifier>z11</motetype_identifier>
</mote>
<mote>
<breakpoints />
<interface_config>
org.contikios.cooja.interfaces.Position
<x>-19.324109516886306</x>
<y>76.23135780254927</y>
<z>0.0</z>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspClock
<deviation>1.0</deviation>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspMoteID
<id>2</id>
</interface_config>
<motetype_identifier>z11</motetype_identifier>
</mote>
<mote>
<breakpoints />
<interface_config>
org.contikios.cooja.interfaces.Position
<x>5.815501305791592</x>
<y>76.77463755494317</y>
<z>0.0</z>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspClock
<deviation>1.0</deviation>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspMoteID
<id>3</id>
</interface_config>
<motetype_identifier>z11</motetype_identifier>
</mote>
<mote>
<breakpoints />
<interface_config>
org.contikios.cooja.interfaces.Position
<x>31.920697784030082</x>
<y>50.5212265977149</y>
<z>0.0</z>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspClock
<deviation>1.0</deviation>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspMoteID
<id>4</id>
</interface_config>
<motetype_identifier>z11</motetype_identifier>
</mote>
<mote>
<breakpoints />
<interface_config>
org.contikios.cooja.interfaces.Position
<x>47.21747673247198</x>
<y>30.217765340599726</y>
<z>0.0</z>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspClock
<deviation>1.0</deviation>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspMoteID
<id>5</id>
</interface_config>
<motetype_identifier>z11</motetype_identifier>
</mote>
<mote>
<breakpoints />
<interface_config>
org.contikios.cooja.interfaces.Position
<x>10.622284947035123</x>
<y>109.81862399725188</y>
<z>0.0</z>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspClock
<deviation>1.0</deviation>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspMoteID
<id>6</id>
</interface_config>
<motetype_identifier>z11</motetype_identifier>
</mote>
<mote>
<breakpoints />
<interface_config>
org.contikios.cooja.interfaces.Position
<x>52.41150716335335</x>
<y>109.93228340481916</y>
<z>0.0</z>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspClock
<deviation>1.0</deviation>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspMoteID
<id>7</id>
</interface_config>
<motetype_identifier>z11</motetype_identifier>
</mote>
<mote>
<breakpoints />
<interface_config>
org.contikios.cooja.interfaces.Position
<x>70.18727461718498</x>
<y>70.06861701541145</y>
<z>0.0</z>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspClock
<deviation>1.0</deviation>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspMoteID
<id>8</id>
</interface_config>
<motetype_identifier>z11</motetype_identifier>
</mote>
<mote>
<breakpoints />
<interface_config>
org.contikios.cooja.interfaces.Position
<x>80.29870484201041</x>
<y>99.37351603835938</y>
<z>0.0</z>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspClock
<deviation>1.0</deviation>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspMoteID
<id>9</id>
</interface_config>
<motetype_identifier>z11</motetype_identifier>
</mote>
</simulation>
<plugin>
org.contikios.cooja.plugins.SimControl
<width>242</width>
<z>3</z>
<height>160</height>
<location_x>11</location_x>
<location_y>241</location_y>
</plugin>
<plugin>
org.contikios.cooja.plugins.Visualizer
<plugin_config>
<moterelations>true</moterelations>
<skin>org.contikios.cooja.plugins.skins.IDVisualizerSkin</skin>
<skin>org.contikios.cooja.plugins.skins.GridVisualizerSkin</skin>
<skin>org.contikios.cooja.plugins.skins.TrafficVisualizerSkin</skin>
<skin>org.contikios.cooja.plugins.skins.UDGMVisualizerSkin</skin>
<viewport>1.7405603810040515 0.0 0.0 1.7405603810040515 47.95980153208088 -42.576134155447555</viewport>
</plugin_config>
<width>236</width>
<z>2</z>
<height>230</height>
<location_x>1</location_x>
<location_y>1</location_y>
</plugin>
<plugin>
org.contikios.cooja.plugins.LogListener
<plugin_config>
<filter>ID:1</filter>
<formatted_time />
<coloring />
</plugin_config>
<width>1031</width>
<z>0</z>
<height>394</height>
<location_x>273</location_x>
<location_y>6</location_y>
</plugin>
<plugin>
org.contikios.cooja.plugins.TimeLine
<plugin_config>
<mote>0</mote>
<mote>1</mote>
<mote>2</mote>
<mote>3</mote>
<mote>4</mote>
<mote>5</mote>
<mote>6</mote>
<mote>7</mote>
<mote>8</mote>
<showRadioRXTX />
<showRadioHW />
<showLEDs />
<zoomfactor>16529.88882215865</zoomfactor>
</plugin_config>
<width>1304</width>
<z>1</z>
<height>311</height>
<location_x>0</location_x>
<location_y>412</location_y>
</plugin>
</simconf>

View file

@ -82,7 +82,7 @@ OBJDUMP:=$(CROSS_COMPILE)-objdump
ARCH = ccm-star.c exceptions.c rtimer-arch.c rtimer-arch-slow.c \ ARCH = ccm-star.c exceptions.c rtimer-arch.c rtimer-arch-slow.c \
slip_uart0.c clock.c micromac-radio.c \ slip_uart0.c clock.c micromac-radio.c \
mtarch.c node-id.c watchdog.c log.c ringbufindex.c slip.c sprintf.c mtarch.c node-id.c watchdog.c log.c slip.c sprintf.c
# Default uart0 for printf and slip # Default uart0 for printf and slip
TARGET_WITH_UART0 ?= 1 TARGET_WITH_UART0 ?= 1
TARGET_WITH_UART1 ?= 0 TARGET_WITH_UART1 ?= 0
@ -145,7 +145,7 @@ CLEAN += *.jn516x.bin
MODULES += core/net \ MODULES += core/net \
core/net/mac \ core/net/mac \
core/net/mac/contikimac \ core/net/mac/contikimac core/net/mac/tsch \
core/net/llsec core/net/llsec/noncoresec core/net/llsec core/net/llsec/noncoresec
CONTIKI_TARGET_SOURCEFILES += $(ARCH) CONTIKI_TARGET_SOURCEFILES += $(ARCH)

View file

@ -59,6 +59,10 @@
#define NETSTACK_CONF_FRAMER framer_802154 #define NETSTACK_CONF_FRAMER framer_802154
#endif /* NETSTACK_CONF_FRAMER */ #endif /* NETSTACK_CONF_FRAMER */
/* IEEE802.15.4 frame version */
#undef FRAME802154_CONF_VERSION
#define FRAME802154_CONF_VERSION FRAME802154_IEEE802154E_2012
#define PACKETBUF_CONF_ATTRS_INLINE 1 #define PACKETBUF_CONF_ATTRS_INLINE 1
#ifndef IEEE802154_CONF_PANID #ifndef IEEE802154_CONF_PANID

View file

@ -206,72 +206,6 @@ PROCESS(micromac_radio_process, "micromac_radio_driver");
#define RADIO_RX_MODE_POLL_MODE (1 << 2) #define RADIO_RX_MODE_POLL_MODE (1 << 2)
#endif /* RADIO_RX_MODE_POLL_MODE */ #endif /* RADIO_RX_MODE_POLL_MODE */
#ifndef FRAME802154_IEEE802154E_2012
/* We define here the missing few features this driver needs from IEEE802.15.4e */
#define FRAME802154_IEEE802154E_2012 (0x02)
/*----------------------------------------------------------------------------*/
uint16_t
frame802154_get_pan_id()
{
return IEEE802154_PANID;
}
/*----------------------------------------------------------------------------*/
static void
frame802154_has_panid(frame802154_fcf_t *fcf, int *has_src_pan_id, int *has_dest_pan_id)
{
int src_pan_id = 0;
int dest_pan_id = 0;
if(fcf == NULL) {
return;
}
if(fcf->frame_version == FRAME802154_IEEE802154E_2012) {
if(!fcf->panid_compression) {
/* Compressed PAN ID == no PAN ID at all */
if(fcf->dest_addr_mode == fcf->dest_addr_mode) {
/* No address or both addresses: include destination PAN ID */
dest_pan_id = 1;
} else if(fcf->dest_addr_mode) {
/* Only dest address, include dest PAN ID */
dest_pan_id = 1;
} else if(fcf->src_addr_mode) {
/* Only src address, include src PAN ID */
src_pan_id = 1;
}
}
if(fcf->dest_addr_mode == 0 && fcf->dest_addr_mode == 1) {
/* No address included, include dest PAN ID conditionally */
if(!fcf->panid_compression) {
dest_pan_id = 1;
/* Remove the following rule the day rows 2 and 3 from table 2a are fixed: */
}
}
if(fcf->dest_addr_mode == 0 && fcf->dest_addr_mode == 0) {
/* Not meaningful, we include a PAN ID iff the compress flag is set, but
* this is what the standard currently stipulates */
dest_pan_id = fcf->panid_compression;
}
} else
/* No PAN ID in ACK */
if(fcf->frame_type != FRAME802154_ACKFRAME) {
if(!fcf->panid_compression && fcf->src_addr_mode & 3) {
/* If compressed, don't inclue source PAN ID */
src_pan_id = 1;
}
if(fcf->dest_addr_mode & 3) {
dest_pan_id = 1;
}
}
if(has_src_pan_id != NULL) {
*has_src_pan_id = src_pan_id;
}
if(has_dest_pan_id != NULL) {
*has_dest_pan_id = dest_pan_id;
}
}
#endif /* FRAME802154_IEEE802154E_2012 */
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
static rtimer_clock_t static rtimer_clock_t
get_packet_timestamp(void) get_packet_timestamp(void)

View file

@ -37,6 +37,10 @@
#define CC2420_CONF_AUTOACK 1 #define CC2420_CONF_AUTOACK 1
#endif /* CC2420_CONF_AUTOACK */ #endif /* CC2420_CONF_AUTOACK */
/* The TSCH default slot length of 10ms is a bit too short for this platform,
* use 15ms instead. */
#define TSCH_CONF_DEFAULT_TIMESLOT_LENGTH 15000
/* Specify whether the RDC layer should enable /* Specify whether the RDC layer should enable
per-packet power profiling. */ per-packet power profiling. */
#define CONTIKIMAC_CONF_COMPOWER 1 #define CONTIKIMAC_CONF_COMPOWER 1

View file

@ -46,6 +46,15 @@
/* Platform TMOTE_SKY */ /* Platform TMOTE_SKY */
#define TMOTE_SKY 1 #define TMOTE_SKY 1
/* Delay between GO signal and SFD: radio fixed delay + 4Bytes preample + 1B SFD -- 1Byte time is 32us
* ~327us + 129preample = 456 us */
#define RADIO_DELAY_BEFORE_TX ((unsigned)US_TO_RTIMERTICKS(456))
/* Delay between GO signal and start listening
* ~50us delay + 129preample + ?? = 183 us */
#define RADIO_DELAY_BEFORE_RX ((unsigned)US_TO_RTIMERTICKS(183))
/* Delay between the SFD finishes arriving and it is detected in software */
#define RADIO_DELAY_BEFORE_DETECT 0
#define PLATFORM_HAS_LEDS 1 #define PLATFORM_HAS_LEDS 1
#define PLATFORM_HAS_BUTTON 1 #define PLATFORM_HAS_BUTTON 1
#define PLATFORM_HAS_LIGHT 1 #define PLATFORM_HAS_LIGHT 1

View file

@ -103,6 +103,10 @@
#define IEEE802154_CONF_PANID 0xABCD #define IEEE802154_CONF_PANID 0xABCD
/* The TSCH default slot length of 10ms is a bit too short for this platform,
* use 15ms instead. */
#define TSCH_CONF_DEFAULT_TIMESLOT_LENGTH 15000
#define SHELL_VARS_CONF_RAM_BEGIN 0x1100 #define SHELL_VARS_CONF_RAM_BEGIN 0x1100
#define SHELL_VARS_CONF_RAM_END 0x2000 #define SHELL_VARS_CONF_RAM_END 0x2000

View file

@ -43,6 +43,15 @@
*/ */
#define ZOLERTIA_Z1 1 /* Enric */ #define ZOLERTIA_Z1 1 /* Enric */
/* Delay between GO signal and SFD: radio fixed delay + 4Bytes preample + 1B SFD -- 1Byte time is 32us
* ~327us + 129preample = 456 us */
#define RADIO_DELAY_BEFORE_TX ((unsigned)US_TO_RTIMERTICKS(456))
/* Delay between GO signal and start listening
* ~50us delay + 129preample + ?? = 183 us */
#define RADIO_DELAY_BEFORE_RX ((unsigned)US_TO_RTIMERTICKS(183))
/* Delay between the SFD finishes arriving and it is detected in software */
#define RADIO_DELAY_BEFORE_DETECT 0
#define PLATFORM_HAS_LEDS 1 #define PLATFORM_HAS_LEDS 1
#define PLATFORM_HAS_BUTTON 1 #define PLATFORM_HAS_BUTTON 1
#define PLATFORM_HAS_RADIO 1 #define PLATFORM_HAS_RADIO 1

View file

@ -34,6 +34,9 @@ wget/minimal-net \
zolertia/z1/z1 \ zolertia/z1/z1 \
settings-example/avr-raven \ settings-example/avr-raven \
ipv6/multicast/sky \ ipv6/multicast/sky \
ipv6/rpl-tsch/z1 \
ipv6/rpl-tsch/z1:MAKE_WITH_ORCHESTRA=1 \
ipv6/rpl-tsch/z1:MAKE_WITH_SECURITY=1
TOOLS= TOOLS=

View file

@ -0,0 +1,288 @@
<?xml version="1.0" encoding="UTF-8"?>
<simconf>
<project EXPORT="discard">[APPS_DIR]/mrm</project>
<project EXPORT="discard">[APPS_DIR]/mspsim</project>
<project EXPORT="discard">[APPS_DIR]/avrora</project>
<project EXPORT="discard">[APPS_DIR]/serial_socket</project>
<project EXPORT="discard">[APPS_DIR]/collect-view</project>
<project EXPORT="discard">[APPS_DIR]/powertracker</project>
<simulation>
<title>RPL+TSCH</title>
<randomseed>123456</randomseed>
<motedelay_us>1000000</motedelay_us>
<radiomedium>
org.contikios.cooja.radiomediums.UDGM
<transmitting_range>50.0</transmitting_range>
<interference_range>100.0</interference_range>
<success_ratio_tx>1.0</success_ratio_tx>
<success_ratio_rx>1.0</success_ratio_rx>
</radiomedium>
<events>
<logoutput>40000</logoutput>
</events>
<motetype>
org.contikios.cooja.mspmote.Z1MoteType
<identifier>z11</identifier>
<description>Z1 Mote Type #z11</description>
<source EXPORT="discard">[CONTIKI_DIR]/examples/ipv6/rpl-tsch/node.c</source>
<commands EXPORT="discard">make TARGET=z1 clean
make node.z1 TARGET=z1 MAKE_WITH_ORCHESTRA=0 MAKE_WITH_SECURITY=0</commands>
<firmware EXPORT="copy">[CONTIKI_DIR]/examples/ipv6/rpl-tsch/node.z1</firmware>
<moteinterface>org.contikios.cooja.interfaces.Position</moteinterface>
<moteinterface>org.contikios.cooja.interfaces.RimeAddress</moteinterface>
<moteinterface>org.contikios.cooja.interfaces.IPAddress</moteinterface>
<moteinterface>org.contikios.cooja.interfaces.Mote2MoteRelations</moteinterface>
<moteinterface>org.contikios.cooja.interfaces.MoteAttributes</moteinterface>
<moteinterface>org.contikios.cooja.mspmote.interfaces.MspClock</moteinterface>
<moteinterface>org.contikios.cooja.mspmote.interfaces.MspMoteID</moteinterface>
<moteinterface>org.contikios.cooja.mspmote.interfaces.MspButton</moteinterface>
<moteinterface>org.contikios.cooja.mspmote.interfaces.Msp802154Radio</moteinterface>
<moteinterface>org.contikios.cooja.mspmote.interfaces.MspDefaultSerial</moteinterface>
<moteinterface>org.contikios.cooja.mspmote.interfaces.MspLED</moteinterface>
<moteinterface>org.contikios.cooja.mspmote.interfaces.MspDebugOutput</moteinterface>
</motetype>
<mote>
<breakpoints />
<interface_config>
org.contikios.cooja.interfaces.Position
<x>-1.285769821276336</x>
<y>38.58045647334346</y>
<z>0.0</z>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspClock
<deviation>1.0</deviation>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspMoteID
<id>1</id>
</interface_config>
<motetype_identifier>z11</motetype_identifier>
</mote>
<mote>
<breakpoints />
<interface_config>
org.contikios.cooja.interfaces.Position
<x>-19.324109516886306</x>
<y>76.23135780254927</y>
<z>0.0</z>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspClock
<deviation>1.0</deviation>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspMoteID
<id>2</id>
</interface_config>
<motetype_identifier>z11</motetype_identifier>
</mote>
<mote>
<breakpoints />
<interface_config>
org.contikios.cooja.interfaces.Position
<x>5.815501305791592</x>
<y>76.77463755494317</y>
<z>0.0</z>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspClock
<deviation>1.0</deviation>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspMoteID
<id>3</id>
</interface_config>
<motetype_identifier>z11</motetype_identifier>
</mote>
<mote>
<breakpoints />
<interface_config>
org.contikios.cooja.interfaces.Position
<x>31.920697784030082</x>
<y>50.5212265977149</y>
<z>0.0</z>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspClock
<deviation>1.0</deviation>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspMoteID
<id>4</id>
</interface_config>
<motetype_identifier>z11</motetype_identifier>
</mote>
<mote>
<breakpoints />
<interface_config>
org.contikios.cooja.interfaces.Position
<x>47.21747673247198</x>
<y>30.217765340599726</y>
<z>0.0</z>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspClock
<deviation>1.0</deviation>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspMoteID
<id>5</id>
</interface_config>
<motetype_identifier>z11</motetype_identifier>
</mote>
<mote>
<breakpoints />
<interface_config>
org.contikios.cooja.interfaces.Position
<x>10.622284947035123</x>
<y>109.81862399725188</y>
<z>0.0</z>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspClock
<deviation>1.0</deviation>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspMoteID
<id>6</id>
</interface_config>
<motetype_identifier>z11</motetype_identifier>
</mote>
<mote>
<breakpoints />
<interface_config>
org.contikios.cooja.interfaces.Position
<x>52.41150716335335</x>
<y>109.93228340481916</y>
<z>0.0</z>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspClock
<deviation>1.0</deviation>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspMoteID
<id>7</id>
</interface_config>
<motetype_identifier>z11</motetype_identifier>
</mote>
<mote>
<breakpoints />
<interface_config>
org.contikios.cooja.interfaces.Position
<x>70.18727461718498</x>
<y>70.06861701541145</y>
<z>0.0</z>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspClock
<deviation>1.0</deviation>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspMoteID
<id>8</id>
</interface_config>
<motetype_identifier>z11</motetype_identifier>
</mote>
<mote>
<breakpoints />
<interface_config>
org.contikios.cooja.interfaces.Position
<x>80.29870484201041</x>
<y>99.37351603835938</y>
<z>0.0</z>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspClock
<deviation>1.0</deviation>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspMoteID
<id>9</id>
</interface_config>
<motetype_identifier>z11</motetype_identifier>
</mote>
</simulation>
<plugin>
org.contikios.cooja.plugins.SimControl
<width>242</width>
<z>4</z>
<height>160</height>
<location_x>11</location_x>
<location_y>241</location_y>
</plugin>
<plugin>
org.contikios.cooja.plugins.Visualizer
<plugin_config>
<moterelations>true</moterelations>
<skin>org.contikios.cooja.plugins.skins.IDVisualizerSkin</skin>
<skin>org.contikios.cooja.plugins.skins.GridVisualizerSkin</skin>
<skin>org.contikios.cooja.plugins.skins.TrafficVisualizerSkin</skin>
<skin>org.contikios.cooja.plugins.skins.UDGMVisualizerSkin</skin>
<viewport>1.7405603810040515 0.0 0.0 1.7405603810040515 47.95980153208088 -42.576134155447555</viewport>
</plugin_config>
<width>236</width>
<z>3</z>
<height>230</height>
<location_x>1</location_x>
<location_y>1</location_y>
</plugin>
<plugin>
org.contikios.cooja.plugins.LogListener
<plugin_config>
<filter>ID:1</filter>
<formatted_time />
<coloring />
</plugin_config>
<width>1031</width>
<z>0</z>
<height>394</height>
<location_x>273</location_x>
<location_y>6</location_y>
</plugin>
<plugin>
org.contikios.cooja.plugins.TimeLine
<plugin_config>
<mote>0</mote>
<mote>1</mote>
<mote>2</mote>
<mote>3</mote>
<mote>4</mote>
<mote>5</mote>
<mote>6</mote>
<mote>7</mote>
<mote>8</mote>
<showRadioRXTX />
<showRadioHW />
<showLEDs />
<zoomfactor>16529.88882215865</zoomfactor>
</plugin_config>
<width>1304</width>
<z>2</z>
<height>311</height>
<location_x>0</location_x>
<location_y>412</location_y>
</plugin>
<plugin>
org.contikios.cooja.plugins.ScriptRunner
<plugin_config>
<script>TIMEOUT(300000); /* Time out after 5 minutes */&#xD;
&#xD;
/* Wait until a node (can only be the DAGRoot) has&#xD;
* 8 routing entries (i.e. can reach every node) */&#xD;
log.log("Waiting for routing tables to fill\n");&#xD;
WAIT_UNTIL(msg.endsWith("Routing entries (8 in total):"));&#xD;
log.log("Root routing table ready\n");&#xD;
&#xD;
log.testOK(); /* Report test success and quit */</script>
<active>true</active>
</plugin_config>
<width>764</width>
<z>1</z>
<height>995</height>
<location_x>963</location_x>
<location_y>111</location_y>
</plugin>
</simconf>

View file

@ -0,0 +1,293 @@
<?xml version="1.0" encoding="UTF-8"?>
<simconf>
<project EXPORT="discard">[APPS_DIR]/mrm</project>
<project EXPORT="discard">[APPS_DIR]/mspsim</project>
<project EXPORT="discard">[APPS_DIR]/avrora</project>
<project EXPORT="discard">[APPS_DIR]/serial_socket</project>
<project EXPORT="discard">[APPS_DIR]/collect-view</project>
<project EXPORT="discard">[APPS_DIR]/powertracker</project>
<simulation>
<title>RPL+TSCH+Orchestrsa</title>
<randomseed>123456</randomseed>
<motedelay_us>1000000</motedelay_us>
<radiomedium>
org.contikios.cooja.radiomediums.UDGM
<transmitting_range>50.0</transmitting_range>
<interference_range>100.0</interference_range>
<success_ratio_tx>1.0</success_ratio_tx>
<success_ratio_rx>1.0</success_ratio_rx>
</radiomedium>
<events>
<logoutput>40000</logoutput>
</events>
<motetype>
org.contikios.cooja.mspmote.Z1MoteType
<identifier>z11</identifier>
<description>Z1 Mote Type #z11</description>
<source EXPORT="discard">[CONTIKI_DIR]/examples/ipv6/rpl-tsch/node.c</source>
<commands EXPORT="discard">make TARGET=z1 clean
make node.z1 TARGET=z1 MAKE_WITH_ORCHESTRA=1 MAKE_WITH_SECURITY=0</commands>
<firmware EXPORT="copy">[CONTIKI_DIR]/examples/ipv6/rpl-tsch/node.z1</firmware>
<moteinterface>org.contikios.cooja.interfaces.Position</moteinterface>
<moteinterface>org.contikios.cooja.interfaces.RimeAddress</moteinterface>
<moteinterface>org.contikios.cooja.interfaces.IPAddress</moteinterface>
<moteinterface>org.contikios.cooja.interfaces.Mote2MoteRelations</moteinterface>
<moteinterface>org.contikios.cooja.interfaces.MoteAttributes</moteinterface>
<moteinterface>org.contikios.cooja.mspmote.interfaces.MspClock</moteinterface>
<moteinterface>org.contikios.cooja.mspmote.interfaces.MspMoteID</moteinterface>
<moteinterface>org.contikios.cooja.mspmote.interfaces.MspButton</moteinterface>
<moteinterface>org.contikios.cooja.mspmote.interfaces.Msp802154Radio</moteinterface>
<moteinterface>org.contikios.cooja.mspmote.interfaces.MspDefaultSerial</moteinterface>
<moteinterface>org.contikios.cooja.mspmote.interfaces.MspLED</moteinterface>
<moteinterface>org.contikios.cooja.mspmote.interfaces.MspDebugOutput</moteinterface>
</motetype>
<mote>
<breakpoints />
<interface_config>
org.contikios.cooja.interfaces.Position
<x>-1.285769821276336</x>
<y>38.58045647334346</y>
<z>0.0</z>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspClock
<deviation>1.0</deviation>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspMoteID
<id>1</id>
</interface_config>
<motetype_identifier>z11</motetype_identifier>
</mote>
<mote>
<breakpoints />
<interface_config>
org.contikios.cooja.interfaces.Position
<x>-19.324109516886306</x>
<y>76.23135780254927</y>
<z>0.0</z>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspClock
<deviation>1.0</deviation>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspMoteID
<id>2</id>
</interface_config>
<motetype_identifier>z11</motetype_identifier>
</mote>
<mote>
<breakpoints />
<interface_config>
org.contikios.cooja.interfaces.Position
<x>5.815501305791592</x>
<y>76.77463755494317</y>
<z>0.0</z>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspClock
<deviation>1.0</deviation>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspMoteID
<id>3</id>
</interface_config>
<motetype_identifier>z11</motetype_identifier>
</mote>
<mote>
<breakpoints />
<interface_config>
org.contikios.cooja.interfaces.Position
<x>31.920697784030082</x>
<y>50.5212265977149</y>
<z>0.0</z>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspClock
<deviation>1.0</deviation>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspMoteID
<id>4</id>
</interface_config>
<motetype_identifier>z11</motetype_identifier>
</mote>
<mote>
<breakpoints />
<interface_config>
org.contikios.cooja.interfaces.Position
<x>47.21747673247198</x>
<y>30.217765340599726</y>
<z>0.0</z>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspClock
<deviation>1.0</deviation>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspMoteID
<id>5</id>
</interface_config>
<motetype_identifier>z11</motetype_identifier>
</mote>
<mote>
<breakpoints />
<interface_config>
org.contikios.cooja.interfaces.Position
<x>10.622284947035123</x>
<y>109.81862399725188</y>
<z>0.0</z>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspClock
<deviation>1.0</deviation>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspMoteID
<id>6</id>
</interface_config>
<motetype_identifier>z11</motetype_identifier>
</mote>
<mote>
<breakpoints />
<interface_config>
org.contikios.cooja.interfaces.Position
<x>52.41150716335335</x>
<y>109.93228340481916</y>
<z>0.0</z>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspClock
<deviation>1.0</deviation>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspMoteID
<id>7</id>
</interface_config>
<motetype_identifier>z11</motetype_identifier>
</mote>
<mote>
<breakpoints />
<interface_config>
org.contikios.cooja.interfaces.Position
<x>70.18727461718498</x>
<y>70.06861701541145</y>
<z>0.0</z>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspClock
<deviation>1.0</deviation>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspMoteID
<id>8</id>
</interface_config>
<motetype_identifier>z11</motetype_identifier>
</mote>
<mote>
<breakpoints />
<interface_config>
org.contikios.cooja.interfaces.Position
<x>80.29870484201041</x>
<y>99.37351603835938</y>
<z>0.0</z>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspClock
<deviation>1.0</deviation>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspMoteID
<id>9</id>
</interface_config>
<motetype_identifier>z11</motetype_identifier>
</mote>
</simulation>
<plugin>
org.contikios.cooja.plugins.SimControl
<width>242</width>
<z>4</z>
<height>160</height>
<location_x>11</location_x>
<location_y>241</location_y>
</plugin>
<plugin>
org.contikios.cooja.plugins.Visualizer
<plugin_config>
<moterelations>true</moterelations>
<skin>org.contikios.cooja.plugins.skins.IDVisualizerSkin</skin>
<skin>org.contikios.cooja.plugins.skins.GridVisualizerSkin</skin>
<skin>org.contikios.cooja.plugins.skins.TrafficVisualizerSkin</skin>
<skin>org.contikios.cooja.plugins.skins.UDGMVisualizerSkin</skin>
<viewport>1.7405603810040515 0.0 0.0 1.7405603810040515 47.95980153208088 -42.576134155447555</viewport>
</plugin_config>
<width>236</width>
<z>3</z>
<height>230</height>
<location_x>1</location_x>
<location_y>1</location_y>
</plugin>
<plugin>
org.contikios.cooja.plugins.LogListener
<plugin_config>
<filter>ID:1</filter>
<formatted_time />
<coloring />
</plugin_config>
<width>1031</width>
<z>0</z>
<height>394</height>
<location_x>273</location_x>
<location_y>6</location_y>
</plugin>
<plugin>
org.contikios.cooja.plugins.TimeLine
<plugin_config>
<mote>0</mote>
<mote>1</mote>
<mote>2</mote>
<mote>3</mote>
<mote>4</mote>
<mote>5</mote>
<mote>6</mote>
<mote>7</mote>
<mote>8</mote>
<showRadioRXTX />
<showRadioHW />
<showLEDs />
<zoomfactor>16529.88882215865</zoomfactor>
</plugin_config>
<width>1304</width>
<z>2</z>
<height>311</height>
<location_x>0</location_x>
<location_y>412</location_y>
</plugin>
<plugin>
org.contikios.cooja.plugins.ScriptRunner
<plugin_config>
<script>TIMEOUT(300000); /* Time out after 5 minutes */&#xD;
&#xD;
log.log("Waiting for Orchestra to start\n");&#xD;
/* Check that Orchestra is running */&#xD;
WAIT_UNTIL(msg.startsWith("Orchestra:"));&#xD;
log.log("Orchestra started\n");&#xD;
&#xD;
/* Wait until a node (can only be the DAGRoot) has&#xD;
* 8 routing entries (i.e. can reach every node) */&#xD;
log.log("Waiting for routing tables to fill\n");&#xD;
WAIT_UNTIL(msg.endsWith("Routing entries (8 in total):"));&#xD;
log.log("Root routing table ready\n");&#xD;
&#xD;
log.testOK(); /* Report test success and quit */</script>
<active>true</active>
</plugin_config>
<width>764</width>
<z>1</z>
<height>995</height>
<location_x>963</location_x>
<location_y>111</location_y>
</plugin>
</simconf>

View file

@ -0,0 +1,293 @@
<?xml version="1.0" encoding="UTF-8"?>
<simconf>
<project EXPORT="discard">[APPS_DIR]/mrm</project>
<project EXPORT="discard">[APPS_DIR]/mspsim</project>
<project EXPORT="discard">[APPS_DIR]/avrora</project>
<project EXPORT="discard">[APPS_DIR]/serial_socket</project>
<project EXPORT="discard">[APPS_DIR]/collect-view</project>
<project EXPORT="discard">[APPS_DIR]/powertracker</project>
<simulation>
<title>RPL+TSCH+Security</title>
<randomseed>123456</randomseed>
<motedelay_us>1000000</motedelay_us>
<radiomedium>
org.contikios.cooja.radiomediums.UDGM
<transmitting_range>50.0</transmitting_range>
<interference_range>100.0</interference_range>
<success_ratio_tx>1.0</success_ratio_tx>
<success_ratio_rx>1.0</success_ratio_rx>
</radiomedium>
<events>
<logoutput>40000</logoutput>
</events>
<motetype>
org.contikios.cooja.mspmote.Z1MoteType
<identifier>z11</identifier>
<description>Z1 Mote Type #z11</description>
<source EXPORT="discard">[CONTIKI_DIR]/examples/ipv6/rpl-tsch/node.c</source>
<commands EXPORT="discard">make TARGET=z1 clean
make node.z1 TARGET=z1 MAKE_WITH_ORCHESTRA=0 MAKE_WITH_SECURITY=1</commands>
<firmware EXPORT="copy">[CONTIKI_DIR]/examples/ipv6/rpl-tsch/node.z1</firmware>
<moteinterface>org.contikios.cooja.interfaces.Position</moteinterface>
<moteinterface>org.contikios.cooja.interfaces.RimeAddress</moteinterface>
<moteinterface>org.contikios.cooja.interfaces.IPAddress</moteinterface>
<moteinterface>org.contikios.cooja.interfaces.Mote2MoteRelations</moteinterface>
<moteinterface>org.contikios.cooja.interfaces.MoteAttributes</moteinterface>
<moteinterface>org.contikios.cooja.mspmote.interfaces.MspClock</moteinterface>
<moteinterface>org.contikios.cooja.mspmote.interfaces.MspMoteID</moteinterface>
<moteinterface>org.contikios.cooja.mspmote.interfaces.MspButton</moteinterface>
<moteinterface>org.contikios.cooja.mspmote.interfaces.Msp802154Radio</moteinterface>
<moteinterface>org.contikios.cooja.mspmote.interfaces.MspDefaultSerial</moteinterface>
<moteinterface>org.contikios.cooja.mspmote.interfaces.MspLED</moteinterface>
<moteinterface>org.contikios.cooja.mspmote.interfaces.MspDebugOutput</moteinterface>
</motetype>
<mote>
<breakpoints />
<interface_config>
org.contikios.cooja.interfaces.Position
<x>-1.285769821276336</x>
<y>38.58045647334346</y>
<z>0.0</z>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspClock
<deviation>1.0</deviation>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspMoteID
<id>1</id>
</interface_config>
<motetype_identifier>z11</motetype_identifier>
</mote>
<mote>
<breakpoints />
<interface_config>
org.contikios.cooja.interfaces.Position
<x>-19.324109516886306</x>
<y>76.23135780254927</y>
<z>0.0</z>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspClock
<deviation>1.0</deviation>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspMoteID
<id>2</id>
</interface_config>
<motetype_identifier>z11</motetype_identifier>
</mote>
<mote>
<breakpoints />
<interface_config>
org.contikios.cooja.interfaces.Position
<x>5.815501305791592</x>
<y>76.77463755494317</y>
<z>0.0</z>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspClock
<deviation>1.0</deviation>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspMoteID
<id>3</id>
</interface_config>
<motetype_identifier>z11</motetype_identifier>
</mote>
<mote>
<breakpoints />
<interface_config>
org.contikios.cooja.interfaces.Position
<x>31.920697784030082</x>
<y>50.5212265977149</y>
<z>0.0</z>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspClock
<deviation>1.0</deviation>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspMoteID
<id>4</id>
</interface_config>
<motetype_identifier>z11</motetype_identifier>
</mote>
<mote>
<breakpoints />
<interface_config>
org.contikios.cooja.interfaces.Position
<x>47.21747673247198</x>
<y>30.217765340599726</y>
<z>0.0</z>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspClock
<deviation>1.0</deviation>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspMoteID
<id>5</id>
</interface_config>
<motetype_identifier>z11</motetype_identifier>
</mote>
<mote>
<breakpoints />
<interface_config>
org.contikios.cooja.interfaces.Position
<x>10.622284947035123</x>
<y>109.81862399725188</y>
<z>0.0</z>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspClock
<deviation>1.0</deviation>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspMoteID
<id>6</id>
</interface_config>
<motetype_identifier>z11</motetype_identifier>
</mote>
<mote>
<breakpoints />
<interface_config>
org.contikios.cooja.interfaces.Position
<x>52.41150716335335</x>
<y>109.93228340481916</y>
<z>0.0</z>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspClock
<deviation>1.0</deviation>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspMoteID
<id>7</id>
</interface_config>
<motetype_identifier>z11</motetype_identifier>
</mote>
<mote>
<breakpoints />
<interface_config>
org.contikios.cooja.interfaces.Position
<x>70.18727461718498</x>
<y>70.06861701541145</y>
<z>0.0</z>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspClock
<deviation>1.0</deviation>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspMoteID
<id>8</id>
</interface_config>
<motetype_identifier>z11</motetype_identifier>
</mote>
<mote>
<breakpoints />
<interface_config>
org.contikios.cooja.interfaces.Position
<x>80.29870484201041</x>
<y>99.37351603835938</y>
<z>0.0</z>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspClock
<deviation>1.0</deviation>
</interface_config>
<interface_config>
org.contikios.cooja.mspmote.interfaces.MspMoteID
<id>9</id>
</interface_config>
<motetype_identifier>z11</motetype_identifier>
</mote>
</simulation>
<plugin>
org.contikios.cooja.plugins.SimControl
<width>242</width>
<z>4</z>
<height>160</height>
<location_x>11</location_x>
<location_y>241</location_y>
</plugin>
<plugin>
org.contikios.cooja.plugins.Visualizer
<plugin_config>
<moterelations>true</moterelations>
<skin>org.contikios.cooja.plugins.skins.IDVisualizerSkin</skin>
<skin>org.contikios.cooja.plugins.skins.GridVisualizerSkin</skin>
<skin>org.contikios.cooja.plugins.skins.TrafficVisualizerSkin</skin>
<skin>org.contikios.cooja.plugins.skins.UDGMVisualizerSkin</skin>
<viewport>1.7405603810040515 0.0 0.0 1.7405603810040515 47.95980153208088 -42.576134155447555</viewport>
</plugin_config>
<width>236</width>
<z>3</z>
<height>230</height>
<location_x>1</location_x>
<location_y>1</location_y>
</plugin>
<plugin>
org.contikios.cooja.plugins.LogListener
<plugin_config>
<filter>ID:1</filter>
<formatted_time />
<coloring />
</plugin_config>
<width>1031</width>
<z>0</z>
<height>394</height>
<location_x>273</location_x>
<location_y>6</location_y>
</plugin>
<plugin>
org.contikios.cooja.plugins.TimeLine
<plugin_config>
<mote>0</mote>
<mote>1</mote>
<mote>2</mote>
<mote>3</mote>
<mote>4</mote>
<mote>5</mote>
<mote>6</mote>
<mote>7</mote>
<mote>8</mote>
<showRadioRXTX />
<showRadioHW />
<showLEDs />
<zoomfactor>16529.88882215865</zoomfactor>
</plugin_config>
<width>1304</width>
<z>2</z>
<height>311</height>
<location_x>0</location_x>
<location_y>412</location_y>
</plugin>
<plugin>
org.contikios.cooja.plugins.ScriptRunner
<plugin_config>
<script>TIMEOUT(300000); /* Time out after 5 minutes */&#xD;
&#xD;
log.log("Waiting for association with security\n");&#xD;
/* Check that nodes associate with security */&#xD;
WAIT_UNTIL(msg.startsWith("TSCH: association done, sec 1,"));&#xD;
log.log("Association with security done\n");&#xD;
&#xD;
/* Wait until a node (can only be the DAGRoot) has&#xD;
* 8 routing entries (i.e. can reach every node) */&#xD;
log.log("Waiting for routing tables to fill\n");&#xD;
WAIT_UNTIL(msg.endsWith("Routing entries (8 in total):"));&#xD;
log.log("Root routing table ready\n");&#xD;
&#xD;
log.testOK(); /* Report test success and quit */</script>
<active>true</active>
</plugin_config>
<width>764</width>
<z>1</z>
<height>995</height>
<location_x>963</location_x>
<location_y>111</location_y>
</plugin>
</simconf>

View file

@ -3,13 +3,17 @@ TOOLSDIR=../../tools
# build jn516x examples, covering IPv6, RPL, CoAP, Rime, Nullrdc, Contikimac # build jn516x examples, covering IPv6, RPL, CoAP, Rime, Nullrdc, Contikimac
EXAMPLES = \ EXAMPLES = \
hello-world/jn516x \
jn516x/dr1175-sensors/jn516x \ jn516x/dr1175-sensors/jn516x \
jn516x/rime/jn516x \ jn516x/rime/jn516x \
jn516x/rpl/border-router/jn516x \ jn516x/rpl/border-router/jn516x \
jn516x/rpl/node/jn516x \ jn516x/rpl/node/jn516x \
jn516x/rpl/coap-dongle-node/jn516x \ jn516x/rpl/coap-dongle-node/jn516x \
jn516x/rpl/coap-dr1175-node/jn516x \ jn516x/rpl/coap-dr1175-node/jn516x \
jn516x/rpl/coap-dr1199-node/jn516x jn516x/rpl/coap-dr1199-node/jn516x \
ipv6/rpl-tsch/jn516x \
ipv6/rpl-tsch/jn516x:MAKE_WITH_ORCHESTRA=1 \
ipv6/rpl-tsch/jn516x:MAKE_WITH_SECURITY=1
TOOLS= TOOLS=