diff --git a/apps/orchestra/Makefile.orchestra b/apps/orchestra/Makefile.orchestra new file mode 100644 index 000000000..314fdac23 --- /dev/null +++ b/apps/orchestra/Makefile.orchestra @@ -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 diff --git a/apps/orchestra/README.md b/apps/orchestra/README.md new file mode 100644 index 000000000..3a15b2e71 --- /dev/null +++ b/apps/orchestra/README.md @@ -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. diff --git a/apps/orchestra/orchestra-conf.h b/apps/orchestra/orchestra-conf.h new file mode 100644 index 000000000..910917620 --- /dev/null +++ b/apps/orchestra/orchestra-conf.h @@ -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 + */ + +#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__ */ diff --git a/apps/orchestra/orchestra-rule-default-common.c b/apps/orchestra/orchestra-rule-default-common.c new file mode 100644 index 000000000..49ac3b496 --- /dev/null +++ b/apps/orchestra/orchestra-rule-default-common.c @@ -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 + */ + +#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, +}; diff --git a/apps/orchestra/orchestra-rule-eb-per-time-source.c b/apps/orchestra/orchestra-rule-eb-per-time-source.c new file mode 100644 index 000000000..0f774f531 --- /dev/null +++ b/apps/orchestra/orchestra-rule-eb-per-time-source.c @@ -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 + */ + +#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, +}; diff --git a/apps/orchestra/orchestra-rule-unicast-per-neighbor.c b/apps/orchestra/orchestra-rule-unicast-per-neighbor.c new file mode 100644 index 000000000..b70a907d0 --- /dev/null +++ b/apps/orchestra/orchestra-rule-unicast-per-neighbor.c @@ -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 + */ + +#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, +}; diff --git a/apps/orchestra/orchestra.c b/apps/orchestra/orchestra.c new file mode 100644 index 000000000..52e47a7ff --- /dev/null +++ b/apps/orchestra/orchestra.c @@ -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 + */ + +#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, ×lot)) { + 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"); +} diff --git a/apps/orchestra/orchestra.h b/apps/orchestra/orchestra.h new file mode 100644 index 000000000..a895335b5 --- /dev/null +++ b/apps/orchestra/orchestra.h @@ -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 + */ + +#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__ */ diff --git a/core/dev/radio.h b/core/dev/radio.h index 5cd8fb264..982acc51d 100644 --- a/core/dev/radio.h +++ b/core/dev/radio.h @@ -150,6 +150,12 @@ enum { /* Received signal strength indicator in dBm. */ 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. * The address is specified in network byte order. @@ -159,6 +165,11 @@ enum { */ 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) */ /* The lowest radio channel. */ @@ -192,6 +203,7 @@ enum { */ #define RADIO_RX_MODE_ADDRESS_FILTER (1 << 0) #define RADIO_RX_MODE_AUTOACK (1 << 1) +#define RADIO_RX_MODE_POLL_MODE (1 << 2) /** * The radio transmission mode controls whether transmissions should diff --git a/platform/jn516x/lib/ringbufindex.c b/core/lib/ringbufindex.c similarity index 100% rename from platform/jn516x/lib/ringbufindex.c rename to core/lib/ringbufindex.c diff --git a/platform/jn516x/lib/ringbufindex.h b/core/lib/ringbufindex.h similarity index 100% rename from platform/jn516x/lib/ringbufindex.h rename to core/lib/ringbufindex.h diff --git a/core/net/ipv6/uip-ds6-route.c b/core/net/ipv6/uip-ds6-route.c index 49674d8ba..3f4ac81e7 100644 --- a/core/net/ipv6/uip-ds6-route.c +++ b/core/net/ipv6/uip-ds6-route.c @@ -47,12 +47,22 @@ #include +/* 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 information about what routes go through what neighbor. This neighbor table is registered with the central nbr-table repository so that it will be maintained along with the rest of the neighbor 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); /* 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; } 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. */ @@ -435,6 +448,10 @@ uip_ds6_route_rm(uip_ds6_route_t *route) #endif /* (DEBUG) & DEBUG_ANNOTATE */ PRINTF("uip_ds6_route_rm: removing neighbor too\n"); 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(&neighborroutememb, neighbor_route); diff --git a/core/net/ipv6/uip-ds6-route.h b/core/net/ipv6/uip-ds6-route.h index 27cbee79b..342bfeffa 100644 --- a/core/net/ipv6/uip-ds6-route.h +++ b/core/net/ipv6/uip-ds6-route.h @@ -40,9 +40,13 @@ #ifndef UIP_DS6_ROUTE_H #define UIP_DS6_ROUTE_H +#include "net/ip/uip.h" +#include "net/nbr-table.h" #include "sys/stimer.h" #include "lib/list.h" +NBR_TABLE_DECLARE(nbr_routes); + void uip_ds6_route_init(void); #ifndef UIP_CONF_UIP_DS6_NOTIFICATIONS diff --git a/core/net/llsec/llsec802154.h b/core/net/llsec/llsec802154.h index 6ab6a9793..2ccd30515 100644 --- a/core/net/llsec/llsec802154.h +++ b/core/net/llsec/llsec802154.h @@ -85,6 +85,12 @@ #define LLSEC802154_USES_EXPLICIT_KEYS 0 #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 #define LLSEC802154_HTONS(n) (n) #define LLSEC802154_HTONL(n) (n) diff --git a/core/net/mac/frame802154.c b/core/net/mac/frame802154.c index 3cd85d20b..c2982a014 100644 --- a/core/net/mac/frame802154.c +++ b/core/net/mac/frame802154.c @@ -44,11 +44,11 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * -*/ + */ /* * \brief This file is where the main functions that relate to frame * manipulation will reside. -*/ + */ /** * \file @@ -61,7 +61,7 @@ /** * \addtogroup frame802154 * @{ -*/ + */ #include "sys/cc.h" #include "net/mac/frame802154.h" @@ -69,11 +69,18 @@ #include "net/linkaddr.h" #include +/** \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 * in the 802.15.4 header. This structure is used in \ref frame802154_create() */ 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_addr_len; /**< Length (in bytes) of destination address 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 */ +/*---------------------------------------------------------------------------*/ +/* 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 field_len(frame802154_t *p, field_length_t *flen) { + int has_src_panid; + int has_dest_panid; + /* init flen to zeros */ memset(flen, 0, sizeof(field_length_t)); /* Determine lengths of each field based on fcf and other args */ - if(p->fcf.dest_addr_mode & 3) { - flen->dest_pid_len = 2; + if((p->fcf.sequence_number_suppression & 1) == 0) { + 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; } - /* 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; - - /* compressed header, only do dest pid */ - flen->src_pid_len = 0; - } else { - p->fcf.panid_compression = 0; + if(has_dest_panid) { + flen->dest_pid_len = 2; } /* determine address lengths */ @@ -144,9 +320,16 @@ field_len(frame802154_t *p, field_length_t *flen) #if LLSEC802154_SECURITY_LEVEL /* Aux security header */ 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 - + 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 */ ; } @@ -161,14 +344,14 @@ field_len(frame802154_t *p, field_length_t *flen) * frame to send. * * \return The length of the frame header. -*/ + */ int frame802154_hdrlen(frame802154_t *p) { field_length_t flen; field_len(p, &flen); - return 3 + flen.dest_pid_len + flen.dest_addr_len + - flen.src_pid_len + flen.src_addr_len + flen.aux_sec_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; } /*----------------------------------------------------------------------------*/ /** @@ -181,7 +364,7 @@ frame802154_hdrlen(frame802154_t *p) * \param buf Pointer to the buffer to use for the frame. * * \return The length of the frame header -*/ + */ int 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 */ field_len(p, &flen); - + /* OK, now we have field lengths. Time to actually construct */ /* the outgoing frame, and store it in buf */ 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.ack_required & 1) << 5) | ((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.src_addr_mode & 3) << 6); - /* sequence number */ - buf[2] = p->seq; - pos = 3; + pos = 2; + + /* Sequence number */ + if(flen.seqno_len == 1) { + buf[pos++] = p->seq; + } /* Destination PAN ID */ 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--) { buf[pos++] = p->src_addr[c - 1]; } - #if LLSEC802154_SECURITY_LEVEL /* Aux header */ if(flen.aux_sec_len) { buf[pos++] = p->aux_hdr.security_control.security_level #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 */ + | (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); - pos += 4; + if(p->aux_hdr.security_control.frame_counter_suppression == 0) { + /* 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 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; frame802154_fcf_t fcf; int c; + int has_src_panid; + int has_dest_panid; #if LLSEC802154_USES_EXPLICIT_KEYS uint8_t key_id_mode; #endif /* LLSEC802154_USES_EXPLICIT_KEYS */ - if(len < 3) { + if(len < 2) { return 0; } @@ -289,20 +486,32 @@ frame802154_parse(uint8_t *data, int len, frame802154_t *pf) fcf.ack_required = (p[0] >> 5) & 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.frame_version = (p[1] >> 4) & 3; fcf.src_addr_mode = (p[1] >> 6) & 3; /* copy fcf and seqNum */ memcpy(&pf->fcf, &fcf, sizeof(frame802154_fcf_t)); - pf->seq = p[2]; - p += 3; /* Skip first three bytes */ + p += 2; /* Skip first two 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 */ if(fcf.dest_addr_mode) { - /* Destination PAN */ - pf->dest_pid = p[0] + (p[1] << 8); - p += 2; + if(has_dest_panid) { + /* Destination PAN */ + pf->dest_pid = p[0] + (p[1] << 8); + p += 2; + } else { + pf->dest_pid = 0; + } /* Destination address */ /* 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 */ if(fcf.src_addr_mode) { /* Source PAN */ - if(!fcf.panid_compression) { + if(has_src_panid) { pf->src_pid = p[0] + (p[1] << 8); p += 2; + if(!has_dest_panid) { + pf->dest_pid = pf->src_pid; + } } else { 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); pf->src_pid = 0; } - + #if LLSEC802154_SECURITY_LEVEL if(fcf.security_enabled) { pf->aux_hdr.security_control.security_level = p[0] & 7; #if LLSEC802154_USES_EXPLICIT_KEYS pf->aux_hdr.security_control.key_id_mode = (p[0] >> 3) & 3; #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; - - memcpy(pf->aux_hdr.frame_counter.u8, p, 4); - p += 4; - + + if(pf->aux_hdr.security_control.frame_counter_suppression == 0) { + 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 key_id_mode = pf->aux_hdr.security_control.key_id_mode; if(key_id_mode) { diff --git a/core/net/mac/frame802154.h b/core/net/mac/frame802154.h index 64a367120..defb64b53 100644 --- a/core/net/mac/frame802154.h +++ b/core/net/mac/frame802154.h @@ -41,7 +41,7 @@ * 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. -*/ + */ /** * \addtogroup net @@ -59,14 +59,14 @@ * This file converts to and from a structure to a packed 802.15.4 * frame. * -*/ - + */ /* Includes */ #ifndef FRAME_802154_H #define FRAME_802154_H #include "contiki-conf.h" +#include "net/linkaddr.h" #ifdef IEEE802154_CONF_PANID #define IEEE802154_PANID IEEE802154_CONF_PANID @@ -74,6 +74,18 @@ #define IEEE802154_PANID 0xABCD #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 */ /** \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_BROADCASTPANDID (0xFFFF) -#define FRAME802154_IEEE802154_2003 (0x00) -#define FRAME802154_IEEE802154_2006 (0x01) +#define FRAME802154_IEEE802154_2003 (0x00) +#define FRAME802154_IEEE802154_2006 (0x01) +#define FRAME802154_IEEE802154E_2012 (0x02) #define FRAME802154_SECURITY_LEVEL_NONE (0) #define FRAME802154_SECURITY_LEVEL_MIC_32 (1) @@ -125,7 +138,7 @@ * 3. Addressing fields - 4 - 20 bytes - Variable * 4. Aux security header - 0 - 14 bytes - Variable * 5. CRC - 2 bytes - Fixed -*/ + */ /** * \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 ack_required; /**< 1 bit. Is an ack frame required? */ 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 frame_version; /**< 2 bit. 802.15.4 frame version */ uint8_t src_addr_mode; /**< 2 bit. Source address mode, see 802.15.4 */ @@ -146,6 +161,8 @@ typedef struct { typedef struct { uint8_t security_level; /**< 3 bit. security level */ 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 */ } frame802154_scf_t; @@ -193,6 +210,20 @@ int frame802154_hdrlen(frame802154_t *p); int frame802154_create(frame802154_t *p, uint8_t *buf); 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 */ /** @} */ diff --git a/core/net/mac/frame802154e-ie.c b/core/net/mac/frame802154e-ie.c new file mode 100644 index 000000000..55c397631 --- /dev/null +++ b/core/net/mac/frame802154e-ie.c @@ -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 + */ + +#include +#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; +} diff --git a/core/net/mac/frame802154e-ie.h b/core/net/mac/frame802154e-ie.h new file mode 100644 index 000000000..d6747557c --- /dev/null +++ b/core/net/mac/frame802154e-ie.h @@ -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 + */ + +#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 */ diff --git a/core/net/mac/framer-802154.c b/core/net/mac/framer-802154.c index ce96f6239..14ccc5aa3 100644 --- a/core/net/mac/framer-802154.c +++ b/core/net/mac/framer-802154.c @@ -62,30 +62,6 @@ static uint8_t mac_dsn; 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 create_frame(int type, int do_create) @@ -93,6 +69,10 @@ create_frame(int type, int do_create) frame802154_t params; int hdr_len; + if(frame802154_get_pan_id() == 0xffff) { + return -1; + } + /* init to zeros */ memset(¶ms, 0, sizeof(params)); @@ -109,10 +89,14 @@ create_frame(int type, int do_create) } else { 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.sequence_number_suppression = FRAME802154_SUPPR_SEQNO; - /* Insert IEEE 802.15.4 (2006) version bits. */ - params.fcf.frame_version = FRAME802154_IEEE802154_2006; + /* Insert IEEE 802.15.4 version bits. */ + params.fcf.frame_version = FRAME802154_VERSION; #if LLSEC802154_SECURITY_LEVEL if(packetbuf_attr(PACKETBUF_ATTR_SECURITY_LEVEL)) { @@ -120,8 +104,13 @@ create_frame(int type, int do_create) } /* Setting security-related attributes */ 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[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 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); @@ -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 in the rime attributes to tell the mac to use long or short for phase 2. - */ + */ if(LINKADDR_SIZE == 2) { /* Use short address mode if linkaddr size is short. */ params.fcf.src_addr_mode = FRAME802154_SHORTADDRMODE; } else { 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()) { /* Broadcast requires short address mode. */ params.fcf.dest_addr_mode = FRAME802154_SHORTADDRMODE; params.dest_addr[0] = 0xFF; params.dest_addr[1] = 0xFF; - } else { linkaddr_copy((linkaddr_t *)¶ms.dest_addr, 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. */ - 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 @@ -191,7 +179,6 @@ create_frame(int type, int do_create) if(!do_create) { /* Only calculate header length */ return hdr_len; - } else if(packetbuf_hdralloc(hdr_len)) { frame802154_create(¶ms, packetbuf_hdrptr()); @@ -223,35 +210,41 @@ parse(void) { frame802154_t frame; int hdr_len; - + hdr_len = frame802154_parse(packetbuf_dataptr(), packetbuf_datalen(), &frame); - + if(hdr_len && packetbuf_hdrreduce(hdr_len)) { packetbuf_set_attr(PACKETBUF_ATTR_FRAME_TYPE, frame.fcf.frame_type); - + if(frame.fcf.dest_addr_mode) { - if(frame.dest_pid != mac_src_pan_id && - frame.dest_pid != FRAME802154_BROADCASTPANDID) { + if(frame.dest_pid != frame802154_get_pan_id() && + frame.dest_pid != FRAME802154_BROADCASTPANDID) { /* Packet to another PAN */ PRINTF("15.4: for another pan %u\n", frame.dest_pid); 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_SENDER, (linkaddr_t *)&frame.src_addr); 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 packetbuf_set_attr(PACKETBUF_ATTR_PACKET_ID, frame.seq); #endif - + #if LLSEC802154_SECURITY_LEVEL if(frame.fcf.security_enabled) { 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_2_3, frame.aux_hdr.frame_counter.u16[1]); +#endif /* LLSEC802154_USES_FRAME_COUNTER */ #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_INDEX, frame.aux_hdr.key_index); @@ -264,7 +257,7 @@ parse(void) PRINTADDR(packetbuf_addr(PACKETBUF_ADDR_SENDER)); PRINTADDR(packetbuf_addr(PACKETBUF_ADDR_RECEIVER)); PRINTF("%d %u (%u)\n", hdr_len, packetbuf_datalen(), packetbuf_totlen()); - + return hdr_len; } return FRAMER_FAILED; diff --git a/core/net/mac/nordc.c b/core/net/mac/nordc.c new file mode 100644 index 000000000..bb41335ff --- /dev/null +++ b/core/net/mac/nordc.c @@ -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 + * + */ + +#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, +}; +/*---------------------------------------------------------------------------*/ diff --git a/core/net/mac/tsch/Makefile.tsch b/core/net/mac/tsch/Makefile.tsch new file mode 100644 index 000000000..e19ff8b13 --- /dev/null +++ b/core/net/mac/tsch/Makefile.tsch @@ -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 diff --git a/core/net/mac/tsch/README.md b/core/net/mac/tsch/README.md new file mode 100644 index 000000000..ffd6dc789 --- /dev/null +++ b/core/net/mac/tsch/README.md @@ -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 diff --git a/core/net/mac/tsch/tsch-adaptive-timesync.c b/core/net/mac/tsch/tsch-adaptive-timesync.c new file mode 100644 index 000000000..0b4601621 --- /dev/null +++ b/core/net/mac/tsch/tsch-adaptive-timesync.c @@ -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 + * + */ + +#include "tsch-adaptive-timesync.h" +#include "tsch-log.h" +#include + +#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 */ diff --git a/core/net/mac/tsch/tsch-adaptive-timesync.h b/core/net/mac/tsch/tsch-adaptive-timesync.h new file mode 100644 index 000000000..d043ca021 --- /dev/null +++ b/core/net/mac/tsch/tsch-adaptive-timesync.h @@ -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__ */ diff --git a/core/net/mac/tsch/tsch-asn.h b/core/net/mac/tsch/tsch-asn.h new file mode 100644 index 000000000..53f7582d4 --- /dev/null +++ b/core/net/mac/tsch/tsch-asn.h @@ -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 + * + */ + +#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__ */ diff --git a/core/net/mac/tsch/tsch-conf.h b/core/net/mac/tsch/tsch-conf.h new file mode 100644 index 000000000..bd9726042 --- /dev/null +++ b/core/net/mac/tsch/tsch-conf.h @@ -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 + */ + +#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__ */ diff --git a/core/net/mac/tsch/tsch-log.c b/core/net/mac/tsch/tsch-log.c new file mode 100644 index 000000000..bfb033e9d --- /dev/null +++ b/core/net/mac/tsch/tsch-log.c @@ -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 + * + */ + +#include "contiki.h" +#include +#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 */ diff --git a/core/net/mac/tsch/tsch-log.h b/core/net/mac/tsch/tsch-log.h new file mode 100644 index 000000000..9b8032577 --- /dev/null +++ b/core/net/mac/tsch/tsch-log.h @@ -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__ */ diff --git a/core/net/mac/tsch/tsch-packet.c b/core/net/mac/tsch/tsch-packet.c new file mode 100644 index 000000000..dbfad6082 --- /dev/null +++ b/core/net/mac/tsch/tsch-packet.c @@ -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 + * Beshr Al Nahas + */ + +#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 +#include + +#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; +} +/*---------------------------------------------------------------------------*/ diff --git a/core/net/mac/tsch/tsch-packet.h b/core/net/mac/tsch/tsch-packet.h new file mode 100644 index 000000000..24f533a88 --- /dev/null +++ b/core/net/mac/tsch/tsch-packet.h @@ -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__ */ diff --git a/core/net/mac/tsch/tsch-private.h b/core/net/mac/tsch/tsch-private.h new file mode 100644 index 000000000..4a26abff3 --- /dev/null +++ b/core/net/mac/tsch/tsch-private.h @@ -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 + * Beshr Al Nahas + */ + +#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__ */ diff --git a/core/net/mac/tsch/tsch-queue.c b/core/net/mac/tsch/tsch-queue.c new file mode 100644 index 000000000..afe868030 --- /dev/null +++ b/core/net/mac/tsch/tsch-queue.c @@ -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 + * Beshr Al Nahas + * Domenico De Guglielmo + */ + +#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 + +#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); +} +/*---------------------------------------------------------------------------*/ diff --git a/core/net/mac/tsch/tsch-queue.h b/core/net/mac/tsch/tsch-queue.h new file mode 100644 index 000000000..b4061be23 --- /dev/null +++ b/core/net/mac/tsch/tsch-queue.h @@ -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__ */ diff --git a/core/net/mac/tsch/tsch-rpl.c b/core/net/mac/tsch/tsch-rpl.c new file mode 100644 index 000000000..772c886b3 --- /dev/null +++ b/core/net/mac/tsch/tsch-rpl.c @@ -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 + */ + +#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 */ diff --git a/core/net/mac/tsch/tsch-rpl.h b/core/net/mac/tsch/tsch-rpl.h new file mode 100644 index 000000000..43d7b0d2a --- /dev/null +++ b/core/net/mac/tsch/tsch-rpl.h @@ -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__ */ diff --git a/core/net/mac/tsch/tsch-schedule.c b/core/net/mac/tsch/tsch-schedule.c new file mode 100644 index 000000000..8b15e0faf --- /dev/null +++ b/core/net/mac/tsch/tsch-schedule.c @@ -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 + * Beshr Al Nahas + */ + +#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 + +#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"); + } +} +/*---------------------------------------------------------------------------*/ diff --git a/core/net/mac/tsch/tsch-schedule.h b/core/net/mac/tsch/tsch-schedule.h new file mode 100644 index 000000000..7b8af2801 --- /dev/null +++ b/core/net/mac/tsch/tsch-schedule.h @@ -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__ */ diff --git a/core/net/mac/tsch/tsch-security.c b/core/net/mac/tsch/tsch-security.c new file mode 100644 index 000000000..e7439c519 --- /dev/null +++ b/core/net/mac/tsch/tsch-security.c @@ -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 + */ + +#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 +#include + +#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; + } +} diff --git a/core/net/mac/tsch/tsch-security.h b/core/net/mac/tsch/tsch-security.h new file mode 100644 index 000000000..694afa78e --- /dev/null +++ b/core/net/mac/tsch/tsch-security.h @@ -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__ */ diff --git a/core/net/mac/tsch/tsch-slot-operation.c b/core/net/mac/tsch/tsch-slot-operation.c new file mode 100644 index 000000000..3174787c2 --- /dev/null +++ b/core/net/mac/tsch/tsch-slot-operation.c @@ -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 + * Beshr Al Nahas + * + */ + +#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, ¤t_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, ¤t_neighbor->addr, ¤t_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, ¤t_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, ¤t_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, ¤t_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, ¤t_neighbor); + } + /* Hop channel */ + current_channel = tsch_calculate_channel(¤t_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(¤t_link->addr); + } + + /* Get next active link */ + current_link = tsch_schedule_get_next_active_link(¤t_asn, ×lot_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(¤t_asn, ×lot_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; +} +/*---------------------------------------------------------------------------*/ diff --git a/core/net/mac/tsch/tsch-slot-operation.h b/core/net/mac/tsch/tsch-slot-operation.h new file mode 100644 index 000000000..f74778c00 --- /dev/null +++ b/core/net/mac/tsch/tsch-slot-operation.h @@ -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__ */ diff --git a/core/net/mac/tsch/tsch.c b/core/net/mac/tsch/tsch.c new file mode 100644 index 000000000..1d9701e42 --- /dev/null +++ b/core/net/mac/tsch/tsch.c @@ -0,0 +1,1032 @@ +/* + * 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.4 TSCH MAC implementation. + * Does not use any RDC layer. Should be used with nordc. + * \author + * Simon Duquennoy + * Beshr Al Nahas + * + */ + +#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 "lib/random.h" + +#if FRAME802154_VERSION < FRAME802154_IEEE802154E_2012 +#error TSCH: FRAME802154_VERSION must be at least FRAME802154_IEEE802154E_2012 +#endif + +#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" + +/* Use to collect link statistics even on Keep-Alive, even though they were + * not sent from an upper layer and don't have a valid packet_sent callback */ +#ifndef TSCH_LINK_NEIGHBOR_CALLBACK +void uip_ds6_link_neighbor_callback(int status, int numtx); +#define TSCH_LINK_NEIGHBOR_CALLBACK(dest, status, num) uip_ds6_link_neighbor_callback(status, num) +#endif /* TSCH_LINK_NEIGHBOR_CALLBACK */ + +/* 802.15.4 duplicate frame detection */ +struct seqno { + linkaddr_t sender; + uint8_t seqno; +}; + +/* Size of the sequence number history */ +#ifdef NETSTACK_CONF_MAC_SEQNO_HISTORY +#define MAX_SEQNOS NETSTACK_CONF_MAC_SEQNO_HISTORY +#else /* NETSTACK_CONF_MAC_SEQNO_HISTORY */ +#define MAX_SEQNOS 8 +#endif /* NETSTACK_CONF_MAC_SEQNO_HISTORY */ + +/* Seqno history */ +static struct seqno received_seqnos[MAX_SEQNOS]; + +/* Let TSCH select a time source with no help of an upper layer. + * We do so using statistics from incoming EBs */ +#if TSCH_AUTOSELECT_TIME_SOURCE +int best_neighbor_eb_count; +struct eb_stat { + int rx_count; + int jp; +}; +NBR_TABLE(struct eb_stat, eb_stats); +#endif /* TSCH_AUTOSELECT_TIME_SOURCE */ + +/* TSCH channel hopping sequence */ +uint8_t tsch_hopping_sequence[TSCH_HOPPING_SEQUENCE_MAX_LEN]; +struct asn_divisor_t tsch_hopping_sequence_length; + +/* Default TSCH timeslot timing (in micro-second) */ +static const uint16_t tsch_default_timing_us[tsch_ts_elements_count] = { + TSCH_DEFAULT_TS_CCA_OFFSET, + TSCH_DEFAULT_TS_CCA, + TSCH_DEFAULT_TS_TX_OFFSET, + TSCH_DEFAULT_TS_RX_OFFSET, + TSCH_DEFAULT_TS_RX_ACK_DELAY, + TSCH_DEFAULT_TS_TX_ACK_DELAY, + TSCH_DEFAULT_TS_RX_WAIT, + TSCH_DEFAULT_TS_ACK_WAIT, + TSCH_DEFAULT_TS_RX_TX, + TSCH_DEFAULT_TS_MAX_ACK, + TSCH_DEFAULT_TS_MAX_TX, + TSCH_DEFAULT_TS_TIMESLOT_LENGTH, +}; +/* TSCH timeslot timing (in rtimer ticks) */ +rtimer_clock_t tsch_timing[tsch_ts_elements_count]; + +#if LINKADDR_SIZE == 8 +/* 802.15.4 broadcast MAC address */ +const linkaddr_t tsch_broadcast_address = { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } }; +/* Address used for the EB virtual neighbor queue */ +const linkaddr_t tsch_eb_address = { { 0, 0, 0, 0, 0, 0, 0, 0 } }; +#else /* LINKADDR_SIZE == 8 */ +const linkaddr_t tsch_broadcast_address = { { 0xff, 0xff } }; +const linkaddr_t tsch_eb_address = { { 0, 0 } }; +#endif /* LINKADDR_SIZE == 8 */ + +/* Is TSCH started? */ +int tsch_is_started = 0; +/* Has TSCH initialization failed? */ +int tsch_is_initialized = 0; +/* Are we coordinator of the TSCH network? */ +int tsch_is_coordinator = 0; +/* Are we associated to a TSCH network? */ +int tsch_is_associated = 0; +/* Is the PAN running link-layer security? */ +int tsch_is_pan_secured = TSCH_SECURITY_ENABLED; +/* The current Absolute Slot Number (ASN) */ +struct asn_t current_asn; +/* Device rank or join priority: + * For PAN coordinator: 0 -- lower is better */ +uint8_t tsch_join_priority; +/* The current TSCH sequence number, used for both data and EBs */ +static uint8_t tsch_packet_seqno = 0; +/* Current period for EB output */ +static clock_time_t tsch_current_eb_period; + +/* timer for sending keepalive messages */ +static struct ctimer keepalive_timer; + +/* TSCH processes and protothreads */ +PT_THREAD(tsch_scan(struct pt *pt)); +PROCESS(tsch_process, "TSCH: main process"); +PROCESS(tsch_send_eb_process, "TSCH: send EB process"); +PROCESS(tsch_pending_events_process, "TSCH: pending events process"); + +/* Other function prototypes */ +static void packet_input(void); + +/* Getters and setters */ + +/*---------------------------------------------------------------------------*/ +void +tsch_set_coordinator(int enable) +{ + tsch_is_coordinator = enable; + tsch_set_eb_period(TSCH_EB_PERIOD); +} +/*---------------------------------------------------------------------------*/ +void +tsch_set_pan_secured(int enable) +{ + tsch_is_pan_secured = TSCH_SECURITY_ENABLED && enable; +} +/*---------------------------------------------------------------------------*/ +void +tsch_set_join_priority(uint8_t jp) +{ + tsch_join_priority = jp; +} +/*---------------------------------------------------------------------------*/ +void +tsch_set_eb_period(uint32_t period) +{ + tsch_current_eb_period = period; +} +/*---------------------------------------------------------------------------*/ +static void +tsch_reset(void) +{ + int i; + frame802154_set_pan_id(0xffff); + /* First make sure pending packet callbacks are sent etc */ + process_post_synch(&tsch_pending_events_process, PROCESS_EVENT_POLL, NULL); + /* Empty all neighbor queues */ + /* tsch_queue_flush_all(); */ + /* Remove unused neighbors */ + tsch_queue_free_unused_neighbors(); + tsch_queue_update_time_source(NULL); + /* Initialize global variables */ + tsch_join_priority = 0xff; + ASN_INIT(current_asn, 0, 0); + current_link = NULL; + /* Reset timeslot timing to defaults */ + for(i = 0; i < tsch_ts_elements_count; i++) { + tsch_timing[i] = US_TO_RTIMERTICKS(tsch_default_timing_us[i]); + } +#ifdef TSCH_CALLBACK_LEAVING_NETWORK + TSCH_CALLBACK_LEAVING_NETWORK(); +#endif +#if TSCH_AUTOSELECT_TIME_SOURCE + best_neighbor_eb_count = 0; + nbr_table_register(eb_stats, NULL); + tsch_set_eb_period(TSCH_EB_PERIOD); +#endif +} + +/* TSCH keep-alive functions */ + +/*---------------------------------------------------------------------------*/ +/* Tx callback for keepalive messages */ +static void +keepalive_packet_sent(void *ptr, int status, int transmissions) +{ +#ifdef TSCH_LINK_NEIGHBOR_CALLBACK + TSCH_LINK_NEIGHBOR_CALLBACK(packetbuf_addr(PACKETBUF_ADDR_RECEIVER), status, transmissions); +#endif + PRINTF("TSCH: KA sent to %u, st %d-%d\n", + TSCH_LOG_ID_FROM_LINKADDR(packetbuf_addr(PACKETBUF_ADDR_RECEIVER)), status, transmissions); + tsch_schedule_keepalive(); +} +/*---------------------------------------------------------------------------*/ +/* Prepare and send a keepalive message */ +static void +keepalive_send() +{ + if(tsch_is_associated) { + struct tsch_neighbor *n = tsch_queue_get_time_source(); + /* Simply send an empty packet */ + packetbuf_clear(); + packetbuf_set_addr(PACKETBUF_ADDR_RECEIVER, &n->addr); + NETSTACK_LLSEC.send(keepalive_packet_sent, NULL); + PRINTF("TSCH: sending KA to %u\n", + TSCH_LOG_ID_FROM_LINKADDR(&n->addr)); + } +} +/*---------------------------------------------------------------------------*/ +/* Set ctimer to send a keepalive message after expiration of TSCH_KEEPALIVE_TIMEOUT */ +void +tsch_schedule_keepalive() +{ + /* Pick a delay in the range [TSCH_KEEPALIVE_TIMEOUT*0.9, TSCH_KEEPALIVE_TIMEOUT[ */ + if(!tsch_is_coordinator && tsch_is_associated) { + unsigned long delay = (TSCH_KEEPALIVE_TIMEOUT - TSCH_KEEPALIVE_TIMEOUT / 10) + + random_rand() % (TSCH_KEEPALIVE_TIMEOUT / 10); + ctimer_set(&keepalive_timer, delay, keepalive_send, NULL); + } +} +/*---------------------------------------------------------------------------*/ +static void +eb_input(struct input_packet *current_input) +{ + /* PRINTF("TSCH: EB received\n"); */ + frame802154_t frame; + /* Verify incoming EB (does its ASN match our Rx time?), + * and update our join priority. */ + struct ieee802154_ies eb_ies; + + if(tsch_packet_parse_eb(current_input->payload, current_input->len, + &frame, &eb_ies, NULL, 1)) { + /* PAN ID check and authentication done at rx time */ + +#if TSCH_AUTOSELECT_TIME_SOURCE + if(!tsch_is_coordinator) { + /* Maintain EB received counter for every neighbor */ + struct eb_stat *stat = (struct eb_stat *)nbr_table_get_from_lladdr(eb_stats, &frame.src_addr); + if(stat == NULL) { + stat = (struct eb_stat *)nbr_table_add_lladdr(eb_stats, &frame.src_addr); + } + if(stat != NULL) { + stat->rx_count++; + stat->jp = eb_ies.join_priority; + best_neighbor_eb_count = MAX(best_neighbor_eb_count, stat->rx_count); + } + /* Select best time source */ + struct eb_stat *best_stat = NULL; + stat = nbr_table_head(eb_stats); + while(stat != NULL) { + /* Is neighbor eligible as a time source? */ + if(stat->rx_count > best_neighbor_eb_count / 2) { + if(best_stat == NULL || + stat->jp < best_stat->jp) { + best_stat = stat; + } + } + stat = nbr_table_next(eb_stats, stat); + } + /* Update time source */ + if(best_stat != NULL) { + tsch_queue_update_time_source(nbr_table_get_lladdr(eb_stats, best_stat)); + tsch_join_priority = best_stat->jp + 1; + } + } +#endif + + struct tsch_neighbor *n = tsch_queue_get_time_source(); + /* Did the EB come from our time source? */ + if(n != NULL && linkaddr_cmp((linkaddr_t *)&frame.src_addr, &n->addr)) { + /* Check for ASN drift */ + int32_t asn_diff = ASN_DIFF(current_input->rx_asn, eb_ies.ie_asn); + if(asn_diff != 0) { + /* We disagree with our time source's ASN -- leave the network */ + PRINTF("TSCH:! ASN drifted by %ld, leaving the network\n", asn_diff); + tsch_disassociate(); + } + + if(eb_ies.ie_join_priority >= TSCH_MAX_JOIN_PRIORITY) { + /* Join priority unacceptable. Leave network. */ + PRINTF("TSCH:! EB JP too high %u, leaving the network\n", + eb_ies.ie_join_priority); + tsch_disassociate(); + } else { +#if TSCH_AUTOSELECT_TIME_SOURCE + /* Update join priority */ + if(tsch_join_priority != eb_ies.ie_join_priority + 1) { + PRINTF("TSCH: update JP from EB %u -> %u\n", + tsch_join_priority, eb_ies.ie_join_priority + 1); + tsch_join_priority = eb_ies.ie_join_priority + 1; + } +#endif /* TSCH_AUTOSELECT_TIME_SOURCE */ + } + } + } +} + +/*---------------------------------------------------------------------------*/ +/* Process pending input packet(s) */ +static void +tsch_rx_process_pending() +{ + int16_t input_index; + /* Loop on accessing (without removing) a pending input packet */ + while((input_index = ringbufindex_peek_get(&input_ringbuf)) != -1) { + struct input_packet *current_input = &input_array[input_index]; + frame802154_t frame; + uint8_t ret = frame802154_parse(current_input->payload, current_input->len, &frame); + int is_data = ret && frame.fcf.frame_type == FRAME802154_DATAFRAME; + int is_eb = ret + && frame.fcf.frame_version == FRAME802154_IEEE802154E_2012 + && frame.fcf.frame_type == FRAME802154_BEACONFRAME; + + if(is_data) { + /* Skip EBs and other control messages */ + /* Copy to packetbuf for processing */ + packetbuf_copyfrom(current_input->payload, current_input->len); + packetbuf_set_attr(PACKETBUF_ATTR_RSSI, current_input->rssi); + } + + /* Remove input from ringbuf */ + ringbufindex_get(&input_ringbuf); + + if(is_data) { + /* Pass to upper layers */ + packet_input(); + } else if(is_eb) { + eb_input(current_input); + } + } +} + +/*---------------------------------------------------------------------------*/ +/* Pass sent packets to upper layer */ +static void +tsch_tx_process_pending() +{ + int16_t dequeued_index; + /* Loop on accessing (without removing) a pending input packet */ + while((dequeued_index = ringbufindex_peek_get(&dequeued_ringbuf)) != -1) { + struct tsch_packet *p = dequeued_array[dequeued_index]; + /* Put packet into packetbuf for packet_sent callback */ + queuebuf_to_packetbuf(p->qb); + /* Call packet_sent callback */ + mac_call_sent_callback(p->sent, p->ptr, p->ret, p->transmissions); + /* Free packet queuebuf */ + tsch_queue_free_packet(p); + /* Free all unused neighbors */ + tsch_queue_free_unused_neighbors(); + /* Remove dequeued packet from ringbuf */ + ringbufindex_get(&dequeued_ringbuf); + } +} +/*---------------------------------------------------------------------------*/ +/* Setup TSCH as a coordinator */ +static void +tsch_start_coordinator(void) +{ + frame802154_set_pan_id(IEEE802154_PANID); + /* Initialize hopping sequence as default */ + memcpy(tsch_hopping_sequence, TSCH_DEFAULT_HOPPING_SEQUENCE, sizeof(TSCH_DEFAULT_HOPPING_SEQUENCE)); + ASN_DIVISOR_INIT(tsch_hopping_sequence_length, sizeof(TSCH_DEFAULT_HOPPING_SEQUENCE)); +#if TSCH_SCHEDULE_WITH_6TISCH_MINIMAL + tsch_schedule_create_minimal(); +#endif + + tsch_is_associated = 1; + tsch_join_priority = 0; + + PRINTF("TSCH: starting as coordinator, PAN ID %x, asn-%x.%lx\n", + frame802154_get_pan_id(), current_asn.ms1b, current_asn.ls4b); + + /* Start slot operation */ + tsch_slot_operation_sync(RTIMER_NOW(), ¤t_asn); +} +/*---------------------------------------------------------------------------*/ +/* Leave the TSCH network */ +void +tsch_disassociate(void) +{ + if(tsch_is_associated == 1) { + tsch_is_associated = 0; + process_post(&tsch_process, PROCESS_EVENT_POLL, NULL); + PRINTF("TSCH: leaving the network\n"); + } +} +/*---------------------------------------------------------------------------*/ +/* Attempt to associate to a network form an incoming EB */ +static int +tsch_associate(const struct input_packet *input_eb, rtimer_clock_t timestamp) +{ + frame802154_t frame; + struct ieee802154_ies ies; + uint8_t hdrlen; + int i; + + if(input_eb == NULL || tsch_packet_parse_eb(input_eb->payload, input_eb->len, + &frame, &ies, &hdrlen, 0) == 0) { + PRINTF("TSCH:! failed to parse EB (len %u)\n", input_eb->len); + return 0; + } + + current_asn = ies.ie_asn; + tsch_join_priority = ies.ie_join_priority + 1; + +#if TSCH_JOIN_SECURED_ONLY + if(frame.fcf.security_enabled == 0) { + PRINTF("TSCH:! parse_eb: EB is not secured\n"); + return 0; + } +#endif /* TSCH_JOIN_SECURED_ONLY */ + +#if TSCH_SECURITY_ENABLED + if(!tsch_security_parse_frame(input_eb->payload, hdrlen, + input_eb->len - hdrlen - tsch_security_mic_len(&frame), + &frame, (linkaddr_t*)&frame.src_addr, ¤t_asn)) { + PRINTF("TSCH:! parse_eb: failed to authenticate\n"); + return 0; + } +#endif /* TSCH_SECURITY_ENABLED */ + +#if !TSCH_SECURITY_ENABLED + if(frame.fcf.security_enabled == 1) { + PRINTF("TSCH:! parse_eb: we do not support security, but EB is secured\n"); + return 0; + } +#endif /* !TSCH_SECURITY_ENABLED */ + +#if TSCH_JOIN_MY_PANID_ONLY + /* Check if the EB comes from the PAN ID we expect */ + if(frame.src_pid != IEEE802154_PANID) { + PRINTF("TSCH:! parse_eb: PAN ID %x != %x\n", frame.src_pid, IEEE802154_PANID); + return 0; + } +#endif /* TSCH_JOIN_MY_PANID_ONLY */ + + /* There was no join priority (or 0xff) in the EB, do not join */ + if(ies.ie_join_priority == 0xff) { + PRINTF("TSCH:! parse_eb: no join priority\n"); + return 0; + } + + /* TSCH timeslot timing */ + for(i = 0; i < tsch_ts_elements_count; i++) { + if(ies.ie_tsch_timeslot_id == 0) { + tsch_timing[i] = US_TO_RTIMERTICKS(tsch_default_timing_us[i]); + } else { + tsch_timing[i] = US_TO_RTIMERTICKS(ies.ie_tsch_timeslot[i]); + } + } + + /* TSCH hopping sequence */ + if(ies.ie_channel_hopping_sequence_id == 0) { + memcpy(tsch_hopping_sequence, TSCH_DEFAULT_HOPPING_SEQUENCE, sizeof(TSCH_DEFAULT_HOPPING_SEQUENCE)); + ASN_DIVISOR_INIT(tsch_hopping_sequence_length, sizeof(TSCH_DEFAULT_HOPPING_SEQUENCE)); + } else { + if(ies.ie_hopping_sequence_len <= sizeof(tsch_hopping_sequence)) { + memcpy(tsch_hopping_sequence, ies.ie_hopping_sequence_list, ies.ie_hopping_sequence_len); + ASN_DIVISOR_INIT(tsch_hopping_sequence_length, ies.ie_hopping_sequence_len); + } else { + PRINTF("TSCH:! parse_eb: hopping sequence too long (%u)\n", ies.ie_hopping_sequence_len); + return 0; + } + } + +#if TSCH_CHECK_TIME_AT_ASSOCIATION > 0 + /* Divide by 4k and multiply again to avoid integer overflow */ + uint32_t expected_asn = 4096 * TSCH_CLOCK_TO_SLOTS(clock_time() / 4096, tsch_timing_timeslot_length); /* Expected ASN based on our current time*/ + int32_t asn_threshold = TSCH_CHECK_TIME_AT_ASSOCIATION * 60ul * TSCH_CLOCK_TO_SLOTS(CLOCK_SECOND, tsch_timing_timeslot_length); + int32_t asn_diff = (int32_t)current_asn.ls4b - expected_asn; + if(asn_diff > asn_threshold) { + PRINTF("TSCH:! EB ASN rejected %lx %lx %ld\n", + current_asn.ls4b, expected_asn, asn_diff); + return 0; + } +#endif + +#if TSCH_INIT_SCHEDULE_FROM_EB + /* Create schedule */ + if(ies.ie_tsch_slotframe_and_link.num_slotframes == 0) { +#if TSCH_SCHEDULE_WITH_6TISCH_MINIMAL + PRINTF("TSCH: parse_eb: no schedule, setting up minimal schedule\n"); + tsch_schedule_create_minimal(); +#else + PRINTF("TSCH: parse_eb: no schedule\n"); +#endif + } else { + /* First, empty current schedule */ + tsch_schedule_remove_all_slotframes(); + /* We support only 0 or 1 slotframe in this IE */ + int num_links = ies.ie_tsch_slotframe_and_link.num_links; + if(num_links <= FRAME802154E_IE_MAX_LINKS) { + int i; + struct tsch_slotframe *sf = tsch_schedule_add_slotframe( + ies.ie_tsch_slotframe_and_link.slotframe_handle, + ies.ie_tsch_slotframe_and_link.slotframe_size); + for(i = 0; i < num_links; i++) { + tsch_schedule_add_link(sf, + ies.ie_tsch_slotframe_and_link.links[i].link_options, + LINK_TYPE_ADVERTISING, &tsch_broadcast_address, + ies.ie_tsch_slotframe_and_link.links[i].timeslot, ies.ie_tsch_slotframe_and_link.links[i].channel_offset); + } + } else { + PRINTF("TSCH:! parse_eb: too many links in schedule (%u)\n", num_links); + return 0; + } + } +#endif /* TSCH_INIT_SCHEDULE_FROM_EB */ + + if(tsch_join_priority < TSCH_MAX_JOIN_PRIORITY) { + struct tsch_neighbor *n; + + /* Add coordinator to list of neighbors, lock the entry */ + n = tsch_queue_add_nbr((linkaddr_t *)&frame.src_addr); + + if(n != NULL) { + tsch_queue_update_time_source((linkaddr_t *)&frame.src_addr); + +#ifdef TSCH_CALLBACK_JOINING_NETWORK + TSCH_CALLBACK_JOINING_NETWORK(); +#endif + + /* Set PANID */ + frame802154_set_pan_id(frame.src_pid); + + /* Synchronize on EB */ + tsch_slot_operation_sync(timestamp - tsch_timing[tsch_ts_tx_offset], ¤t_asn); + + /* Update global flags */ + tsch_is_associated = 1; + tsch_is_pan_secured = frame.fcf.security_enabled; + + /* Association done, schedule keepalive messages */ + tsch_schedule_keepalive(); + + PRINTF("TSCH: association done, sec %u, PAN ID %x, asn-%x.%lx, jp %u, timeslot id %u, hopping id %u, slotframe len %u with %u links, from ", + tsch_is_pan_secured, + frame.src_pid, + current_asn.ms1b, current_asn.ls4b, tsch_join_priority, + ies.ie_tsch_timeslot_id, + ies.ie_channel_hopping_sequence_id, + ies.ie_tsch_slotframe_and_link.slotframe_size, + ies.ie_tsch_slotframe_and_link.num_links); + PRINTLLADDR((const uip_lladdr_t *)&frame.src_addr); + PRINTF("\n"); + + return 1; + } + } + PRINTF("TSCH:! did not associate.\n"); + return 0; +} + +/* Processes and protothreads used by TSCH */ + +/*---------------------------------------------------------------------------*/ +/* Scanning protothread, called by tsch_process: + * Listen to different channels, and when receiving an EB, + * attempt to associate. + */ +PT_THREAD(tsch_scan(struct pt *pt)) +{ + PT_BEGIN(pt); + + static struct input_packet input_eb; + static struct etimer scan_timer; + + ASN_INIT(current_asn, 0, 0); + + etimer_set(&scan_timer, CLOCK_SECOND / TSCH_ASSOCIATION_POLL_FREQUENCY); + + while(!tsch_is_associated && !tsch_is_coordinator) { + /* Hop to any channel offset */ + static int current_channel = 0; + /* Time when we started scanning on current_channel */ + static clock_time_t current_channel_since = 0; + + /* We are not coordinator, try to associate */ + rtimer_clock_t t0; + int is_packet_pending = 0; + clock_time_t now_seconds = clock_seconds(); + + /* Switch to a (new) channel for scanning */ + if(current_channel == 0 || now_seconds != current_channel_since) { + /* Pick a channel at random in TSCH_JOIN_HOPPING_SEQUENCE */ + uint8_t scan_channel = TSCH_JOIN_HOPPING_SEQUENCE[ + random_rand() % sizeof(TSCH_JOIN_HOPPING_SEQUENCE)]; + if(current_channel != scan_channel) { + NETSTACK_RADIO.set_value(RADIO_PARAM_CHANNEL, scan_channel); + current_channel = scan_channel; + PRINTF("TSCH: scanning on channel %u\n", scan_channel); + } + current_channel_since = now_seconds; + } + + /* Turn radio on and wait for EB */ + NETSTACK_RADIO.on(); + + is_packet_pending = NETSTACK_RADIO.pending_packet(); + if(!is_packet_pending && NETSTACK_RADIO.receiving_packet()) { + /* If we are currently receiving a packet, wait until end of reception */ + t0 = RTIMER_NOW(); + BUSYWAIT_UNTIL_ABS((is_packet_pending = NETSTACK_RADIO.pending_packet()), t0, RTIMER_SECOND / 100); + } + + if(is_packet_pending) { + /* Save packet timestamp */ + NETSTACK_RADIO.get_object(RADIO_PARAM_LAST_PACKET_TIMESTAMP, &t0, sizeof(rtimer_clock_t)); + + /* Read packet */ + input_eb.len = NETSTACK_RADIO.read(input_eb.payload, TSCH_PACKET_MAX_LEN); + + /* Parse EB and attempt to associate */ + PRINTF("TSCH: association: received packet (%u bytes) on channel %u\n", input_eb.len, current_channel); + + tsch_associate(&input_eb, t0); + } + + if(tsch_is_associated) { + /* End of association, turn the radio off */ + NETSTACK_RADIO.off(); + } else if(!tsch_is_coordinator) { + /* Go back to scanning */ + etimer_reset(&scan_timer); + PT_WAIT_UNTIL(pt, etimer_expired(&scan_timer)); + } + } + + PT_END(pt); +} + +/*---------------------------------------------------------------------------*/ +/* The main TSCH process */ +PROCESS_THREAD(tsch_process, ev, data) +{ + static struct pt scan_pt; + + PROCESS_BEGIN(); + + while(1) { + + while(!tsch_is_associated) { + if(tsch_is_coordinator) { + /* We are coordinator, start operating now */ + tsch_start_coordinator(); + } else { + /* Start scanning, will attempt to join when receiving an EB */ + PROCESS_PT_SPAWN(&scan_pt, tsch_scan(&scan_pt)); + } + } + + /* We are part of a TSCH network, start slot operation */ + tsch_slot_operation_start(); + + /* Yield our main process. Slot operation will re-schedule itself + * as long as we are associated */ + PROCESS_YIELD_UNTIL(!tsch_is_associated); + + /* Will need to re-synchronize */ + tsch_reset(); + } + + PROCESS_END(); +} + +/*---------------------------------------------------------------------------*/ +/* A periodic process to send TSCH Enhanced Beacons (EB) */ +PROCESS_THREAD(tsch_send_eb_process, ev, data) +{ + static struct etimer eb_timer; + + PROCESS_BEGIN(); + + /* Wait until association */ + etimer_set(&eb_timer, CLOCK_SECOND / 10); + while(!tsch_is_associated) { + PROCESS_WAIT_UNTIL(etimer_expired(&eb_timer)); + etimer_reset(&eb_timer); + } + + /* Set an initial delay except for coordinator, which should send an EB asap */ + if(!tsch_is_coordinator) { + etimer_set(&eb_timer, random_rand() % TSCH_EB_PERIOD); + PROCESS_WAIT_UNTIL(etimer_expired(&eb_timer)); + } + + while(1) { + unsigned long delay; + + if(tsch_is_associated && tsch_current_eb_period > 0) { + /* Enqueue EB only if there isn't already one in queue */ + if(tsch_queue_packet_count(&tsch_eb_address) == 0) { + int eb_len; + uint8_t hdr_len = 0; + uint8_t tsch_sync_ie_offset; + /* Prepare the EB packet and schedule it to be sent */ + packetbuf_clear(); + /* We don't use seqno 0 */ + if(++tsch_packet_seqno == 0) { + tsch_packet_seqno++; + } + packetbuf_set_attr(PACKETBUF_ATTR_FRAME_TYPE, FRAME802154_BEACONFRAME); + packetbuf_set_attr(PACKETBUF_ATTR_MAC_SEQNO, tsch_packet_seqno); +#if TSCH_SECURITY_ENABLED + if(tsch_is_pan_secured) { + /* Set security level, key id and index */ + packetbuf_set_attr(PACKETBUF_ATTR_SECURITY_LEVEL, TSCH_SECURITY_KEY_SEC_LEVEL_EB); + packetbuf_set_attr(PACKETBUF_ATTR_KEY_ID_MODE, FRAME802154_1_BYTE_KEY_ID_MODE); /* Use 1-byte key index */ + packetbuf_set_attr(PACKETBUF_ATTR_KEY_INDEX, TSCH_SECURITY_KEY_INDEX_EB); + } +#endif /* TSCH_SECURITY_ENABLED */ + eb_len = tsch_packet_create_eb(packetbuf_dataptr(), PACKETBUF_SIZE, + tsch_packet_seqno, &hdr_len, &tsch_sync_ie_offset); + if(eb_len != 0) { + struct tsch_packet *p; + packetbuf_set_datalen(eb_len); + /* Enqueue EB packet */ + if(!(p = tsch_queue_add_packet(&tsch_eb_address, NULL, NULL))) { + PRINTF("TSCH:! could not enqueue EB packet\n"); + } else { + PRINTF("TSCH: enqueue EB packet %u %u\n", eb_len, hdr_len); + p->tsch_sync_ie_offset = tsch_sync_ie_offset; + p->header_len = hdr_len; + } + } + } + } + if(tsch_current_eb_period > 0) { + /* Next EB transmission with a random delay + * within [tsch_current_eb_period*0.75, tsch_current_eb_period[ */ + delay = (tsch_current_eb_period - tsch_current_eb_period / 4) + + random_rand() % (tsch_current_eb_period / 4); + } else { + delay = TSCH_EB_PERIOD; + } + etimer_set(&eb_timer, delay); + PROCESS_WAIT_UNTIL(etimer_expired(&eb_timer)); + } + PROCESS_END(); +} + +/*---------------------------------------------------------------------------*/ +/* A process that is polled from interrupt and calls tx/rx input + * callbacks, outputs pending logs. */ +PROCESS_THREAD(tsch_pending_events_process, ev, data) +{ + PROCESS_BEGIN(); + while(1) { + PROCESS_YIELD_UNTIL(ev == PROCESS_EVENT_POLL); + tsch_rx_process_pending(); + tsch_tx_process_pending(); + tsch_log_process_pending(); + } + PROCESS_END(); +} + +/* Functions from the Contiki MAC layer driver interface */ + +/*---------------------------------------------------------------------------*/ +static void +tsch_init(void) +{ + radio_value_t radio_rx_mode; + radio_value_t radio_tx_mode; + rtimer_clock_t t; + + /* Radio Rx mode */ + if(NETSTACK_RADIO.get_value(RADIO_PARAM_RX_MODE, &radio_rx_mode) != RADIO_RESULT_OK) { + printf("TSCH:! radio does not support getting RADIO_PARAM_RX_MODE. Abort init.\n"); + return; + } + /* Disable radio in frame filtering */ + radio_rx_mode &= ~RADIO_RX_MODE_ADDRESS_FILTER; + /* Unset autoack */ + radio_rx_mode &= ~RADIO_RX_MODE_AUTOACK; + /* Set radio in poll mode */ + radio_rx_mode |= RADIO_RX_MODE_POLL_MODE; + if(NETSTACK_RADIO.set_value(RADIO_PARAM_RX_MODE, radio_rx_mode) != RADIO_RESULT_OK) { + printf("TSCH:! radio does not support setting required RADIO_PARAM_RX_MODE. Abort init.\n"); + return; + } + + /* Radio Tx mode */ + if(NETSTACK_RADIO.get_value(RADIO_PARAM_TX_MODE, &radio_tx_mode) != RADIO_RESULT_OK) { + printf("TSCH:! radio does not support getting RADIO_PARAM_TX_MODE. Abort init.\n"); + return; + } + /* Unset CCA */ + radio_tx_mode &= ~RADIO_TX_MODE_SEND_ON_CCA; + if(NETSTACK_RADIO.set_value(RADIO_PARAM_TX_MODE, radio_tx_mode) != RADIO_RESULT_OK) { + printf("TSCH:! radio does not support setting required RADIO_PARAM_TX_MODE. Abort init.\n"); + return; + } + /* Test setting channel */ + if(NETSTACK_RADIO.set_value(RADIO_PARAM_CHANNEL, TSCH_DEFAULT_HOPPING_SEQUENCE[0]) != RADIO_RESULT_OK) { + printf("TSCH:! radio does not support setting channel. Abort init.\n"); + return; + } + /* Test getting timestamp */ + if(NETSTACK_RADIO.get_object(RADIO_PARAM_LAST_PACKET_TIMESTAMP, &t, sizeof(rtimer_clock_t)) != RADIO_RESULT_OK) { + printf("TSCH:! radio does not support getting last packet timestamp. Abort init.\n"); + return; + } + /* Check max hopping sequence length vs default sequence length */ + if(TSCH_HOPPING_SEQUENCE_MAX_LEN < sizeof(TSCH_DEFAULT_HOPPING_SEQUENCE)) { + printf("TSCH:! TSCH_HOPPING_SEQUENCE_MAX_LEN < sizeof(TSCH_DEFAULT_HOPPING_SEQUENCE). Abort init.\n"); + } + + /* Init TSCH sub-modules */ + tsch_reset(); + tsch_queue_init(); + tsch_schedule_init(); + tsch_log_init(); + ringbufindex_init(&input_ringbuf, TSCH_MAX_INCOMING_PACKETS); + ringbufindex_init(&dequeued_ringbuf, TSCH_DEQUEUED_ARRAY_SIZE); + + tsch_is_initialized = 1; + +#if TSCH_AUTOSTART + /* Start TSCH operation. + * If TSCH_AUTOSTART is not set, one needs to call NETSTACK_MAC.on() to start TSCH. */ + NETSTACK_MAC.on(); +#endif /* TSCH_AUTOSTART */ +} +/*---------------------------------------------------------------------------*/ +/* Function send for TSCH-MAC, puts the packet in packetbuf in the MAC queue */ +static void +send_packet(mac_callback_t sent, void *ptr) +{ + int ret = MAC_TX_DEFERRED; + int packet_count_before; + int hdr_len = 0; + const linkaddr_t *addr = packetbuf_addr(PACKETBUF_ADDR_RECEIVER); + + if(!tsch_is_associated) { + if(!tsch_is_initialized) { + PRINTF("TSCH:! not initialized (see earlier logs), drop outgoing packet\n"); + } else { + PRINTF("TSCH:! not associated, drop outgoing packet\n"); + } + ret = MAC_TX_ERR; + mac_call_sent_callback(sent, ptr, ret, 1); + return; + } + + /* PACKETBUF_ATTR_MAC_SEQNO cannot be zero, due to a pecuilarity + in framer-802154.c. */ + if(++tsch_packet_seqno == 0) { + tsch_packet_seqno++; + } + + /* Ask for ACK if we are sending anything other than broadcast */ + if(!linkaddr_cmp(addr, &linkaddr_null)) { + packetbuf_set_attr(PACKETBUF_ATTR_MAC_ACK, 1); + } else { + /* Broadcast packets shall be added to broadcast queue + * The broadcast address in Contiki is linkaddr_null which is equal + * to tsch_eb_address */ + addr = &tsch_broadcast_address; + } + + packetbuf_set_attr(PACKETBUF_ATTR_FRAME_TYPE, FRAME802154_DATAFRAME); + packetbuf_set_attr(PACKETBUF_ATTR_MAC_SEQNO, tsch_packet_seqno); + +#if TSCH_SECURITY_ENABLED + if(tsch_is_pan_secured) { + /* Set security level, key id and index */ + packetbuf_set_attr(PACKETBUF_ATTR_SECURITY_LEVEL, TSCH_SECURITY_KEY_SEC_LEVEL_OTHER); + packetbuf_set_attr(PACKETBUF_ATTR_KEY_ID_MODE, FRAME802154_1_BYTE_KEY_ID_MODE); /* Use 1-byte key index */ + packetbuf_set_attr(PACKETBUF_ATTR_KEY_INDEX, TSCH_SECURITY_KEY_INDEX_OTHER); + } +#endif /* TSCH_SECURITY_ENABLED */ + + packet_count_before = tsch_queue_packet_count(addr); + + if((hdr_len = NETSTACK_FRAMER.create()) < 0) { + PRINTF("TSCH:! can't send packet due to framer error\n"); + ret = MAC_TX_ERR; + } else { + struct tsch_packet *p; + /* Enqueue packet */ + p = tsch_queue_add_packet(addr, sent, ptr); + if(p == NULL) { + PRINTF("TSCH:! can't send packet !tsch_queue_add_packet\n"); + ret = MAC_TX_ERR; + } else { + p->header_len = hdr_len; + PRINTF("TSCH: send packet to %u with seqno %u, queue %u %u, len %u %u\n", + TSCH_LOG_ID_FROM_LINKADDR(addr), tsch_packet_seqno, + packet_count_before, + tsch_queue_packet_count(addr), + p->header_len, + queuebuf_datalen(p->qb)); + } + } + if(ret != MAC_TX_DEFERRED) { + mac_call_sent_callback(sent, ptr, ret, 1); + } +} +/*---------------------------------------------------------------------------*/ +static void +packet_input(void) +{ + int frame_parsed = 1; + + frame_parsed = NETSTACK_FRAMER.parse(); + + if(frame_parsed < 0) { + PRINTF("TSCH:! failed to parse %u\n", packetbuf_datalen()); + } else { + int duplicate = 0; + + /* Seqno of 0xffff means no seqno */ + if(packetbuf_attr(PACKETBUF_ATTR_MAC_SEQNO) != 0xffff) { + /* Check for duplicate packet by comparing the sequence number + of the incoming packet with the last few ones we saw. */ + int i; + for(i = 0; i < MAX_SEQNOS; ++i) { + if(packetbuf_attr(PACKETBUF_ATTR_MAC_SEQNO) == received_seqnos[i].seqno && + linkaddr_cmp(packetbuf_addr(PACKETBUF_ADDR_SENDER), + &received_seqnos[i].sender)) { + /* Drop the packet. */ + PRINTF("TSCH:! drop dup ll from %u seqno %u\n", + TSCH_LOG_ID_FROM_LINKADDR(packetbuf_addr(PACKETBUF_ADDR_SENDER)), + packetbuf_attr(PACKETBUF_ATTR_MAC_SEQNO)); + duplicate = 1; + } + } + if(!duplicate) { + for(i = MAX_SEQNOS - 1; i > 0; --i) { + memcpy(&received_seqnos[i], &received_seqnos[i - 1], + sizeof(struct seqno)); + } + received_seqnos[0].seqno = packetbuf_attr(PACKETBUF_ATTR_MAC_SEQNO); + linkaddr_copy(&received_seqnos[0].sender, + packetbuf_addr(PACKETBUF_ADDR_SENDER)); + } + } + + if(!duplicate) { + PRINTF("TSCH: received from %u with seqno %u\n", + TSCH_LOG_ID_FROM_LINKADDR(packetbuf_addr(PACKETBUF_ADDR_SENDER)), + packetbuf_attr(PACKETBUF_ATTR_MAC_SEQNO)); + NETSTACK_LLSEC.input(); + } + } +} +/*---------------------------------------------------------------------------*/ +static int +turn_on(void) +{ + if(tsch_is_initialized == 1 && tsch_is_started == 0) { + tsch_is_started = 1; + /* Process tx/rx callback and log messages whenever polled */ + process_start(&tsch_pending_events_process, NULL); + /* periodically send TSCH EBs */ + process_start(&tsch_send_eb_process, NULL); + /* try to associate to a network or start one if setup as coordinator */ + process_start(&tsch_process, NULL); + PRINTF("TSCH: starting as %s\n", tsch_is_coordinator ? "coordinator" : "node"); + return 1; + } + return 0; +} +/*---------------------------------------------------------------------------*/ +static int +turn_off(int keep_radio_on) +{ + return 1; +} +/*---------------------------------------------------------------------------*/ +static unsigned short +channel_check_interval(void) +{ + return 0; +} +/*---------------------------------------------------------------------------*/ +const struct mac_driver tschmac_driver = { + "TSCH", + tsch_init, + send_packet, + packet_input, + turn_on, + turn_off, + channel_check_interval, +}; +/*---------------------------------------------------------------------------*/ diff --git a/core/net/mac/tsch/tsch.h b/core/net/mac/tsch/tsch.h new file mode 100644 index 000000000..31b5d41f9 --- /dev/null +++ b/core/net/mac/tsch/tsch.h @@ -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__ */ diff --git a/core/net/packetbuf.h b/core/net/packetbuf.h index 3c1839bf1..12b0d9dce 100644 --- a/core/net/packetbuf.h +++ b/core/net/packetbuf.h @@ -55,6 +55,7 @@ #include "contiki-conf.h" #include "net/linkaddr.h" #include "net/llsec/llsec802154.h" +#include "net/mac/tsch/tsch-conf.h" /** * \brief The size of the packetbuf, in bytes @@ -313,6 +314,10 @@ enum { PACKETBUF_ATTR_MAC_SEQNO, PACKETBUF_ATTR_MAC_ACK, 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. */ #if PACKETBUF_WITH_PACKET_TYPE @@ -329,14 +334,16 @@ enum { PACKETBUF_ATTR_FRAME_TYPE, #if LLSEC802154_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_2_3, +#endif /* LLSEC802154_USES_FRAME_COUNTER */ #if LLSEC802154_USES_EXPLICIT_KEYS PACKETBUF_ATTR_KEY_ID_MODE, PACKETBUF_ATTR_KEY_INDEX, PACKETBUF_ATTR_KEY_SOURCE_BYTES_0_1, #endif /* LLSEC802154_USES_EXPLICIT_KEYS */ -#endif /* LLSEC802154_SECURITY_LEVEL */ /* Scope 2 attributes: used between end-to-end nodes. */ #if NETSTACK_CONF_WITH_RIME @@ -362,10 +369,15 @@ enum { #if !LLSEC802154_SECURITY_LEVEL enum { 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_2_3 }; -#endif /* LLSEC802154_SECURITY_LEVEL */ +#endif /* LLSEC802154_USES_FRAME_COUNTER */ /* Define surrogates when not using explicit keys */ #if !LLSEC802154_USES_EXPLICIT_KEYS diff --git a/core/net/rpl/rpl-dag.c b/core/net/rpl/rpl-dag.c index 7403f9931..20507449d 100644 --- a/core/net/rpl/rpl-dag.c +++ b/core/net/rpl/rpl-dag.c @@ -60,6 +60,11 @@ #define DEBUG DEBUG_NONE #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; 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"); +#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 * neighbor table. */ nbr_table_unlock(rpl_parents, dag->preferred_parent); diff --git a/core/net/rpl/rpl-timers.c b/core/net/rpl/rpl-timers.c index 54cb3ddef..f8c913b61 100644 --- a/core/net/rpl/rpl-timers.c +++ b/core/net/rpl/rpl-timers.c @@ -50,6 +50,11 @@ #define DEBUG DEBUG_NONE #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; @@ -124,6 +129,10 @@ new_dio_interval(rpl_instance_t *instance) /* schedule the timer */ PRINTF("RPL: Scheduling DIO timer %lu ticks in future (Interval)\n", ticks); 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 diff --git a/cpu/msp430/rtimer-arch.h b/cpu/msp430/rtimer-arch.h index 446ef99e1..6063292b1 100644 --- a/cpu/msp430/rtimer-arch.h +++ b/cpu/msp430/rtimer-arch.h @@ -48,6 +48,20 @@ #define RTIMER_ARCH_SECOND (4096U*8) #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); #endif /* RTIMER_ARCH_H_ */ diff --git a/dev/cc2420/cc2420.c b/dev/cc2420/cc2420.c index 66a551250..1782aae06 100644 --- a/dev/cc2420/cc2420.c +++ b/dev/cc2420/cc2420.c @@ -140,6 +140,13 @@ static volatile uint16_t last_packet_timestamp; 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_off(void); @@ -154,6 +161,12 @@ static int cc2420_receiving_packet(void); static int pending_packet(void); static int get_cca_threshold(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; uint8_t cc2420_last_correlation; @@ -161,6 +174,11 @@ uint8_t cc2420_last_correlation; static uint8_t receive_on; 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 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: *value = cc2420_get_channel(); 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: v = cc2420_get_txpower(); *value = OUTPUT_POWER_MIN; @@ -194,6 +230,14 @@ get_value(radio_param_t param, radio_value_t *value) /* Return the RSSI value in dBm */ *value = cc2420_rssi(); 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: *value = 11; return RADIO_RESULT_OK; @@ -233,6 +277,21 @@ set_value(radio_param_t param, radio_value_t value) } cc2420_set_channel(value); 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: if(value < OUTPUT_POWER_MIN || value > OUTPUT_POWER_MAX) { return RADIO_RESULT_INVALID_VALUE; @@ -256,6 +315,17 @@ set_value(radio_param_t param, radio_value_t value) static radio_result_t 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; } @@ -447,7 +517,10 @@ wait_for_transmission(void) static void on(void) { - CC2420_ENABLE_FIFOP_INT(); + if(!poll_mode) { + CC2420_ENABLE_FIFOP_INT(); + } + strobe(CC2420_SRXON); ENERGEST_ON(ENERGEST_TYPE_LISTEN); @@ -465,7 +538,9 @@ off(void) ENERGEST_OFF(ENERGEST_TYPE_LISTEN); strobe(CC2420_SRFOFF); - CC2420_DISABLE_FIFOP_INT(); + if(!poll_mode) { + CC2420_DISABLE_FIFOP_INT(); + } if(!CC2420_FIFOP_IS_1) { flushrx(); @@ -539,14 +614,6 @@ set_txpower(uint8_t power) 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 cc2420_init(void) { @@ -573,20 +640,15 @@ cc2420_init(void) /* And wait until it stabilizes */ wait_for_status(BV(CC2420_XOSC16M_STABLE)); - /* Turn on/off automatic packet acknowledgment and address decoding. */ - reg = getreg(CC2420_MDMCTRL0); + /* Set auto-ack and frame filtering */ + 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 and provides us with RSSI and link quality indication (LQI) information. */ + reg = getreg(CC2420_MDMCTRL0); reg |= AUTOCRC; - setreg(CC2420_MDMCTRL0, reg); /* Set transmission turnaround time to the lower setting (8 symbols @@ -614,6 +676,8 @@ cc2420_init(void) flushrx(); + set_poll_mode(0); + process_start(&cc2420_process, NULL); return 1; } @@ -646,13 +710,13 @@ cc2420_transmit(unsigned short payload_len) #define LOOP_20_SYMBOLS CC2420_CONF_SYMBOL_LOOP_COUNT #endif -#if WITH_SEND_CCA - strobe(CC2420_SRXON); - wait_for_status(BV(CC2420_RSSI_VALID)); - strobe(CC2420_STXONCCA); -#else /* WITH_SEND_CCA */ - strobe(CC2420_STXON); -#endif /* WITH_SEND_CCA */ + if(send_on_cca) { + strobe(CC2420_SRXON); + wait_for_status(BV(CC2420_RSSI_VALID)); + strobe(CC2420_STXONCCA); + } else { + strobe(CC2420_STXON); + } for(i = LOOP_20_SYMBOLS; i > 0; i--) { if(CC2420_SFD_IS_1) { #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. */ RIMESTATS_ADD(contentiondrop); PRINTF("cc2420: do_send() transmission never started\n"); @@ -869,7 +933,7 @@ PROCESS_THREAD(cc2420_process, ev, data) PRINTF("cc2420_process: started\n"); 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"); @@ -913,25 +977,31 @@ cc2420_read(void *buf, unsigned short bufsize) if(footer[1] & FOOTER1_CRC_OK) { cc2420_last_rssi = footer[0] + RSSI_OFFSET; cc2420_last_correlation = footer[1] & FOOTER1_CORRELATION; - - packetbuf_set_attr(PACKETBUF_ATTR_RSSI, cc2420_last_rssi); - packetbuf_set_attr(PACKETBUF_ATTR_LINK_QUALITY, cc2420_last_correlation); - + if(!poll_mode) { + /* Not in poll mode: packetbuf should not be accessed in interrupt context. + * 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); } else { RIMESTATS_ADD(badcrc); len = FOOTER_LEN; } - - if(CC2420_FIFOP_IS_1) { - if(!CC2420_FIFO_IS_1) { - /* Clean up in case of FIFO overflow! This happens for every - * full length frame and is signaled by FIFOP = 1 and FIFO = - * 0. */ - flushrx(); - } else { - /* Another packet has been received and needs attention. */ - process_poll(&cc2420_process); + + if(!poll_mode) { + if(CC2420_FIFOP_IS_1) { + if(!CC2420_FIFO_IS_1) { + /* Clean up in case of FIFO overflow! This happens for every + * full length frame and is signaled by FIFOP = 1 and FIFO = + * 0. */ + flushrx(); + } else { + /* Another packet has been received and needs attention. */ + process_poll(&cc2420_process); + } } } @@ -1062,3 +1132,64 @@ cc2420_set_cca_threshold(int value) 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; +} +/*---------------------------------------------------------------------------*/ diff --git a/examples/ipv6/rpl-tsch/Makefile b/examples/ipv6/rpl-tsch/Makefile new file mode 100644 index 000000000..734c1499e --- /dev/null +++ b/examples/ipv6/rpl-tsch/Makefile @@ -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 diff --git a/examples/ipv6/rpl-tsch/README.md b/examples/ipv6/rpl-tsch/README.md new file mode 100644 index 000000000..e82db3d63 --- /dev/null +++ b/examples/ipv6/rpl-tsch/README.md @@ -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. diff --git a/examples/ipv6/rpl-tsch/node.c b/examples/ipv6/rpl-tsch/node.c new file mode 100644 index 000000000..2aee41d81 --- /dev/null +++ b/examples/ipv6/rpl-tsch/node.c @@ -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 + */ + +#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(); +} +/*---------------------------------------------------------------------------*/ diff --git a/examples/ipv6/rpl-tsch/project-conf.h b/examples/ipv6/rpl-tsch/project-conf.h new file mode 100644 index 000000000..7ecfb74c1 --- /dev/null +++ b/examples/ipv6/rpl-tsch/project-conf.h @@ -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 + */ + +#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__ */ diff --git a/examples/ipv6/rpl-tsch/rpl-tsch-z1.csc b/examples/ipv6/rpl-tsch/rpl-tsch-z1.csc new file mode 100644 index 000000000..90bee7525 --- /dev/null +++ b/examples/ipv6/rpl-tsch/rpl-tsch-z1.csc @@ -0,0 +1,267 @@ + + + [APPS_DIR]/mrm + [APPS_DIR]/mspsim + [APPS_DIR]/avrora + [APPS_DIR]/serial_socket + [APPS_DIR]/collect-view + [APPS_DIR]/powertracker + + RPL+TSCH + 123456 + 1000000 + + org.contikios.cooja.radiomediums.UDGM + 50.0 + 100.0 + 1.0 + 1.0 + + + 40000 + + + org.contikios.cooja.mspmote.Z1MoteType + z11 + Z1 Mote Type #z11 + [CONFIG_DIR]/node.c + make node.z1 TARGET=z1 + [CONFIG_DIR]/node.z1 + org.contikios.cooja.interfaces.Position + org.contikios.cooja.interfaces.RimeAddress + org.contikios.cooja.interfaces.IPAddress + org.contikios.cooja.interfaces.Mote2MoteRelations + org.contikios.cooja.interfaces.MoteAttributes + org.contikios.cooja.mspmote.interfaces.MspClock + org.contikios.cooja.mspmote.interfaces.MspMoteID + org.contikios.cooja.mspmote.interfaces.MspButton + org.contikios.cooja.mspmote.interfaces.Msp802154Radio + org.contikios.cooja.mspmote.interfaces.MspDefaultSerial + org.contikios.cooja.mspmote.interfaces.MspLED + org.contikios.cooja.mspmote.interfaces.MspDebugOutput + + + + + org.contikios.cooja.interfaces.Position + -1.285769821276336 + 38.58045647334346 + 0.0 + + + org.contikios.cooja.mspmote.interfaces.MspClock + 1.0 + + + org.contikios.cooja.mspmote.interfaces.MspMoteID + 1 + + z11 + + + + + org.contikios.cooja.interfaces.Position + -19.324109516886306 + 76.23135780254927 + 0.0 + + + org.contikios.cooja.mspmote.interfaces.MspClock + 1.0 + + + org.contikios.cooja.mspmote.interfaces.MspMoteID + 2 + + z11 + + + + + org.contikios.cooja.interfaces.Position + 5.815501305791592 + 76.77463755494317 + 0.0 + + + org.contikios.cooja.mspmote.interfaces.MspClock + 1.0 + + + org.contikios.cooja.mspmote.interfaces.MspMoteID + 3 + + z11 + + + + + org.contikios.cooja.interfaces.Position + 31.920697784030082 + 50.5212265977149 + 0.0 + + + org.contikios.cooja.mspmote.interfaces.MspClock + 1.0 + + + org.contikios.cooja.mspmote.interfaces.MspMoteID + 4 + + z11 + + + + + org.contikios.cooja.interfaces.Position + 47.21747673247198 + 30.217765340599726 + 0.0 + + + org.contikios.cooja.mspmote.interfaces.MspClock + 1.0 + + + org.contikios.cooja.mspmote.interfaces.MspMoteID + 5 + + z11 + + + + + org.contikios.cooja.interfaces.Position + 10.622284947035123 + 109.81862399725188 + 0.0 + + + org.contikios.cooja.mspmote.interfaces.MspClock + 1.0 + + + org.contikios.cooja.mspmote.interfaces.MspMoteID + 6 + + z11 + + + + + org.contikios.cooja.interfaces.Position + 52.41150716335335 + 109.93228340481916 + 0.0 + + + org.contikios.cooja.mspmote.interfaces.MspClock + 1.0 + + + org.contikios.cooja.mspmote.interfaces.MspMoteID + 7 + + z11 + + + + + org.contikios.cooja.interfaces.Position + 70.18727461718498 + 70.06861701541145 + 0.0 + + + org.contikios.cooja.mspmote.interfaces.MspClock + 1.0 + + + org.contikios.cooja.mspmote.interfaces.MspMoteID + 8 + + z11 + + + + + org.contikios.cooja.interfaces.Position + 80.29870484201041 + 99.37351603835938 + 0.0 + + + org.contikios.cooja.mspmote.interfaces.MspClock + 1.0 + + + org.contikios.cooja.mspmote.interfaces.MspMoteID + 9 + + z11 + + + + org.contikios.cooja.plugins.SimControl + 242 + 3 + 160 + 11 + 241 + + + org.contikios.cooja.plugins.Visualizer + + true + org.contikios.cooja.plugins.skins.IDVisualizerSkin + org.contikios.cooja.plugins.skins.GridVisualizerSkin + org.contikios.cooja.plugins.skins.TrafficVisualizerSkin + org.contikios.cooja.plugins.skins.UDGMVisualizerSkin + 1.7405603810040515 0.0 0.0 1.7405603810040515 47.95980153208088 -42.576134155447555 + + 236 + 2 + 230 + 1 + 1 + + + org.contikios.cooja.plugins.LogListener + + ID:1 + + + + 1031 + 0 + 394 + 273 + 6 + + + org.contikios.cooja.plugins.TimeLine + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + + + + 16529.88882215865 + + 1304 + 1 + 311 + 0 + 412 + + + diff --git a/platform/jn516x/Makefile.jn516x b/platform/jn516x/Makefile.jn516x index c7d0353d5..412a1cbd4 100644 --- a/platform/jn516x/Makefile.jn516x +++ b/platform/jn516x/Makefile.jn516x @@ -82,7 +82,7 @@ OBJDUMP:=$(CROSS_COMPILE)-objdump ARCH = ccm-star.c exceptions.c rtimer-arch.c rtimer-arch-slow.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 TARGET_WITH_UART0 ?= 1 TARGET_WITH_UART1 ?= 0 @@ -145,7 +145,7 @@ CLEAN += *.jn516x.bin MODULES += core/net \ core/net/mac \ - core/net/mac/contikimac \ + core/net/mac/contikimac core/net/mac/tsch \ core/net/llsec core/net/llsec/noncoresec CONTIKI_TARGET_SOURCEFILES += $(ARCH) diff --git a/platform/jn516x/contiki-conf.h b/platform/jn516x/contiki-conf.h index 41135c187..a1e74b251 100644 --- a/platform/jn516x/contiki-conf.h +++ b/platform/jn516x/contiki-conf.h @@ -59,6 +59,10 @@ #define NETSTACK_CONF_FRAMER framer_802154 #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 #ifndef IEEE802154_CONF_PANID diff --git a/platform/jn516x/dev/micromac-radio.c b/platform/jn516x/dev/micromac-radio.c index 556019e5c..e2d087aa2 100644 --- a/platform/jn516x/dev/micromac-radio.c +++ b/platform/jn516x/dev/micromac-radio.c @@ -206,72 +206,6 @@ PROCESS(micromac_radio_process, "micromac_radio_driver"); #define RADIO_RX_MODE_POLL_MODE (1 << 2) #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 get_packet_timestamp(void) diff --git a/platform/sky/contiki-conf.h b/platform/sky/contiki-conf.h index 53f22f87f..ff4ad8578 100644 --- a/platform/sky/contiki-conf.h +++ b/platform/sky/contiki-conf.h @@ -37,6 +37,10 @@ #define CC2420_CONF_AUTOACK 1 #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 per-packet power profiling. */ #define CONTIKIMAC_CONF_COMPOWER 1 diff --git a/platform/sky/platform-conf.h b/platform/sky/platform-conf.h index f5530f3aa..cb6ea0cba 100644 --- a/platform/sky/platform-conf.h +++ b/platform/sky/platform-conf.h @@ -46,6 +46,15 @@ /* Platform TMOTE_SKY */ #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_BUTTON 1 #define PLATFORM_HAS_LIGHT 1 diff --git a/platform/z1/contiki-conf.h b/platform/z1/contiki-conf.h index 783b3ec8c..44d74efdb 100644 --- a/platform/z1/contiki-conf.h +++ b/platform/z1/contiki-conf.h @@ -103,6 +103,10 @@ #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_END 0x2000 diff --git a/platform/z1/platform-conf.h b/platform/z1/platform-conf.h index 2d05c96b0..15cab1127 100644 --- a/platform/z1/platform-conf.h +++ b/platform/z1/platform-conf.h @@ -43,6 +43,15 @@ */ #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_BUTTON 1 #define PLATFORM_HAS_RADIO 1 diff --git a/regression-tests/01-compile-base/Makefile b/regression-tests/01-compile-base/Makefile index ae70727ba..484880773 100644 --- a/regression-tests/01-compile-base/Makefile +++ b/regression-tests/01-compile-base/Makefile @@ -34,6 +34,9 @@ wget/minimal-net \ zolertia/z1/z1 \ settings-example/avr-raven \ ipv6/multicast/sky \ +ipv6/rpl-tsch/z1 \ +ipv6/rpl-tsch/z1:MAKE_WITH_ORCHESTRA=1 \ +ipv6/rpl-tsch/z1:MAKE_WITH_SECURITY=1 TOOLS= diff --git a/regression-tests/11-ipv6/19-z1-rpl-tsch.csc b/regression-tests/11-ipv6/19-z1-rpl-tsch.csc new file mode 100644 index 000000000..7488e8c3b --- /dev/null +++ b/regression-tests/11-ipv6/19-z1-rpl-tsch.csc @@ -0,0 +1,288 @@ + + + [APPS_DIR]/mrm + [APPS_DIR]/mspsim + [APPS_DIR]/avrora + [APPS_DIR]/serial_socket + [APPS_DIR]/collect-view + [APPS_DIR]/powertracker + + RPL+TSCH + 123456 + 1000000 + + org.contikios.cooja.radiomediums.UDGM + 50.0 + 100.0 + 1.0 + 1.0 + + + 40000 + + + org.contikios.cooja.mspmote.Z1MoteType + z11 + Z1 Mote Type #z11 + [CONTIKI_DIR]/examples/ipv6/rpl-tsch/node.c + make TARGET=z1 clean +make node.z1 TARGET=z1 MAKE_WITH_ORCHESTRA=0 MAKE_WITH_SECURITY=0 + [CONTIKI_DIR]/examples/ipv6/rpl-tsch/node.z1 + org.contikios.cooja.interfaces.Position + org.contikios.cooja.interfaces.RimeAddress + org.contikios.cooja.interfaces.IPAddress + org.contikios.cooja.interfaces.Mote2MoteRelations + org.contikios.cooja.interfaces.MoteAttributes + org.contikios.cooja.mspmote.interfaces.MspClock + org.contikios.cooja.mspmote.interfaces.MspMoteID + org.contikios.cooja.mspmote.interfaces.MspButton + org.contikios.cooja.mspmote.interfaces.Msp802154Radio + org.contikios.cooja.mspmote.interfaces.MspDefaultSerial + org.contikios.cooja.mspmote.interfaces.MspLED + org.contikios.cooja.mspmote.interfaces.MspDebugOutput + + + + + org.contikios.cooja.interfaces.Position + -1.285769821276336 + 38.58045647334346 + 0.0 + + + org.contikios.cooja.mspmote.interfaces.MspClock + 1.0 + + + org.contikios.cooja.mspmote.interfaces.MspMoteID + 1 + + z11 + + + + + org.contikios.cooja.interfaces.Position + -19.324109516886306 + 76.23135780254927 + 0.0 + + + org.contikios.cooja.mspmote.interfaces.MspClock + 1.0 + + + org.contikios.cooja.mspmote.interfaces.MspMoteID + 2 + + z11 + + + + + org.contikios.cooja.interfaces.Position + 5.815501305791592 + 76.77463755494317 + 0.0 + + + org.contikios.cooja.mspmote.interfaces.MspClock + 1.0 + + + org.contikios.cooja.mspmote.interfaces.MspMoteID + 3 + + z11 + + + + + org.contikios.cooja.interfaces.Position + 31.920697784030082 + 50.5212265977149 + 0.0 + + + org.contikios.cooja.mspmote.interfaces.MspClock + 1.0 + + + org.contikios.cooja.mspmote.interfaces.MspMoteID + 4 + + z11 + + + + + org.contikios.cooja.interfaces.Position + 47.21747673247198 + 30.217765340599726 + 0.0 + + + org.contikios.cooja.mspmote.interfaces.MspClock + 1.0 + + + org.contikios.cooja.mspmote.interfaces.MspMoteID + 5 + + z11 + + + + + org.contikios.cooja.interfaces.Position + 10.622284947035123 + 109.81862399725188 + 0.0 + + + org.contikios.cooja.mspmote.interfaces.MspClock + 1.0 + + + org.contikios.cooja.mspmote.interfaces.MspMoteID + 6 + + z11 + + + + + org.contikios.cooja.interfaces.Position + 52.41150716335335 + 109.93228340481916 + 0.0 + + + org.contikios.cooja.mspmote.interfaces.MspClock + 1.0 + + + org.contikios.cooja.mspmote.interfaces.MspMoteID + 7 + + z11 + + + + + org.contikios.cooja.interfaces.Position + 70.18727461718498 + 70.06861701541145 + 0.0 + + + org.contikios.cooja.mspmote.interfaces.MspClock + 1.0 + + + org.contikios.cooja.mspmote.interfaces.MspMoteID + 8 + + z11 + + + + + org.contikios.cooja.interfaces.Position + 80.29870484201041 + 99.37351603835938 + 0.0 + + + org.contikios.cooja.mspmote.interfaces.MspClock + 1.0 + + + org.contikios.cooja.mspmote.interfaces.MspMoteID + 9 + + z11 + + + + org.contikios.cooja.plugins.SimControl + 242 + 4 + 160 + 11 + 241 + + + org.contikios.cooja.plugins.Visualizer + + true + org.contikios.cooja.plugins.skins.IDVisualizerSkin + org.contikios.cooja.plugins.skins.GridVisualizerSkin + org.contikios.cooja.plugins.skins.TrafficVisualizerSkin + org.contikios.cooja.plugins.skins.UDGMVisualizerSkin + 1.7405603810040515 0.0 0.0 1.7405603810040515 47.95980153208088 -42.576134155447555 + + 236 + 3 + 230 + 1 + 1 + + + org.contikios.cooja.plugins.LogListener + + ID:1 + + + + 1031 + 0 + 394 + 273 + 6 + + + org.contikios.cooja.plugins.TimeLine + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + + + + 16529.88882215865 + + 1304 + 2 + 311 + 0 + 412 + + + org.contikios.cooja.plugins.ScriptRunner + + + true + + 764 + 1 + 995 + 963 + 111 + + + diff --git a/regression-tests/11-ipv6/20-z1-rpl-tsch-orchestra.csc b/regression-tests/11-ipv6/20-z1-rpl-tsch-orchestra.csc new file mode 100644 index 000000000..3fecd2040 --- /dev/null +++ b/regression-tests/11-ipv6/20-z1-rpl-tsch-orchestra.csc @@ -0,0 +1,293 @@ + + + [APPS_DIR]/mrm + [APPS_DIR]/mspsim + [APPS_DIR]/avrora + [APPS_DIR]/serial_socket + [APPS_DIR]/collect-view + [APPS_DIR]/powertracker + + RPL+TSCH+Orchestrsa + 123456 + 1000000 + + org.contikios.cooja.radiomediums.UDGM + 50.0 + 100.0 + 1.0 + 1.0 + + + 40000 + + + org.contikios.cooja.mspmote.Z1MoteType + z11 + Z1 Mote Type #z11 + [CONTIKI_DIR]/examples/ipv6/rpl-tsch/node.c + make TARGET=z1 clean +make node.z1 TARGET=z1 MAKE_WITH_ORCHESTRA=1 MAKE_WITH_SECURITY=0 + [CONTIKI_DIR]/examples/ipv6/rpl-tsch/node.z1 + org.contikios.cooja.interfaces.Position + org.contikios.cooja.interfaces.RimeAddress + org.contikios.cooja.interfaces.IPAddress + org.contikios.cooja.interfaces.Mote2MoteRelations + org.contikios.cooja.interfaces.MoteAttributes + org.contikios.cooja.mspmote.interfaces.MspClock + org.contikios.cooja.mspmote.interfaces.MspMoteID + org.contikios.cooja.mspmote.interfaces.MspButton + org.contikios.cooja.mspmote.interfaces.Msp802154Radio + org.contikios.cooja.mspmote.interfaces.MspDefaultSerial + org.contikios.cooja.mspmote.interfaces.MspLED + org.contikios.cooja.mspmote.interfaces.MspDebugOutput + + + + + org.contikios.cooja.interfaces.Position + -1.285769821276336 + 38.58045647334346 + 0.0 + + + org.contikios.cooja.mspmote.interfaces.MspClock + 1.0 + + + org.contikios.cooja.mspmote.interfaces.MspMoteID + 1 + + z11 + + + + + org.contikios.cooja.interfaces.Position + -19.324109516886306 + 76.23135780254927 + 0.0 + + + org.contikios.cooja.mspmote.interfaces.MspClock + 1.0 + + + org.contikios.cooja.mspmote.interfaces.MspMoteID + 2 + + z11 + + + + + org.contikios.cooja.interfaces.Position + 5.815501305791592 + 76.77463755494317 + 0.0 + + + org.contikios.cooja.mspmote.interfaces.MspClock + 1.0 + + + org.contikios.cooja.mspmote.interfaces.MspMoteID + 3 + + z11 + + + + + org.contikios.cooja.interfaces.Position + 31.920697784030082 + 50.5212265977149 + 0.0 + + + org.contikios.cooja.mspmote.interfaces.MspClock + 1.0 + + + org.contikios.cooja.mspmote.interfaces.MspMoteID + 4 + + z11 + + + + + org.contikios.cooja.interfaces.Position + 47.21747673247198 + 30.217765340599726 + 0.0 + + + org.contikios.cooja.mspmote.interfaces.MspClock + 1.0 + + + org.contikios.cooja.mspmote.interfaces.MspMoteID + 5 + + z11 + + + + + org.contikios.cooja.interfaces.Position + 10.622284947035123 + 109.81862399725188 + 0.0 + + + org.contikios.cooja.mspmote.interfaces.MspClock + 1.0 + + + org.contikios.cooja.mspmote.interfaces.MspMoteID + 6 + + z11 + + + + + org.contikios.cooja.interfaces.Position + 52.41150716335335 + 109.93228340481916 + 0.0 + + + org.contikios.cooja.mspmote.interfaces.MspClock + 1.0 + + + org.contikios.cooja.mspmote.interfaces.MspMoteID + 7 + + z11 + + + + + org.contikios.cooja.interfaces.Position + 70.18727461718498 + 70.06861701541145 + 0.0 + + + org.contikios.cooja.mspmote.interfaces.MspClock + 1.0 + + + org.contikios.cooja.mspmote.interfaces.MspMoteID + 8 + + z11 + + + + + org.contikios.cooja.interfaces.Position + 80.29870484201041 + 99.37351603835938 + 0.0 + + + org.contikios.cooja.mspmote.interfaces.MspClock + 1.0 + + + org.contikios.cooja.mspmote.interfaces.MspMoteID + 9 + + z11 + + + + org.contikios.cooja.plugins.SimControl + 242 + 4 + 160 + 11 + 241 + + + org.contikios.cooja.plugins.Visualizer + + true + org.contikios.cooja.plugins.skins.IDVisualizerSkin + org.contikios.cooja.plugins.skins.GridVisualizerSkin + org.contikios.cooja.plugins.skins.TrafficVisualizerSkin + org.contikios.cooja.plugins.skins.UDGMVisualizerSkin + 1.7405603810040515 0.0 0.0 1.7405603810040515 47.95980153208088 -42.576134155447555 + + 236 + 3 + 230 + 1 + 1 + + + org.contikios.cooja.plugins.LogListener + + ID:1 + + + + 1031 + 0 + 394 + 273 + 6 + + + org.contikios.cooja.plugins.TimeLine + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + + + + 16529.88882215865 + + 1304 + 2 + 311 + 0 + 412 + + + org.contikios.cooja.plugins.ScriptRunner + + + true + + 764 + 1 + 995 + 963 + 111 + + + diff --git a/regression-tests/11-ipv6/21-z1-rpl-tsch-security.csc b/regression-tests/11-ipv6/21-z1-rpl-tsch-security.csc new file mode 100644 index 000000000..a24f7fe5b --- /dev/null +++ b/regression-tests/11-ipv6/21-z1-rpl-tsch-security.csc @@ -0,0 +1,293 @@ + + + [APPS_DIR]/mrm + [APPS_DIR]/mspsim + [APPS_DIR]/avrora + [APPS_DIR]/serial_socket + [APPS_DIR]/collect-view + [APPS_DIR]/powertracker + + RPL+TSCH+Security + 123456 + 1000000 + + org.contikios.cooja.radiomediums.UDGM + 50.0 + 100.0 + 1.0 + 1.0 + + + 40000 + + + org.contikios.cooja.mspmote.Z1MoteType + z11 + Z1 Mote Type #z11 + [CONTIKI_DIR]/examples/ipv6/rpl-tsch/node.c + make TARGET=z1 clean +make node.z1 TARGET=z1 MAKE_WITH_ORCHESTRA=0 MAKE_WITH_SECURITY=1 + [CONTIKI_DIR]/examples/ipv6/rpl-tsch/node.z1 + org.contikios.cooja.interfaces.Position + org.contikios.cooja.interfaces.RimeAddress + org.contikios.cooja.interfaces.IPAddress + org.contikios.cooja.interfaces.Mote2MoteRelations + org.contikios.cooja.interfaces.MoteAttributes + org.contikios.cooja.mspmote.interfaces.MspClock + org.contikios.cooja.mspmote.interfaces.MspMoteID + org.contikios.cooja.mspmote.interfaces.MspButton + org.contikios.cooja.mspmote.interfaces.Msp802154Radio + org.contikios.cooja.mspmote.interfaces.MspDefaultSerial + org.contikios.cooja.mspmote.interfaces.MspLED + org.contikios.cooja.mspmote.interfaces.MspDebugOutput + + + + + org.contikios.cooja.interfaces.Position + -1.285769821276336 + 38.58045647334346 + 0.0 + + + org.contikios.cooja.mspmote.interfaces.MspClock + 1.0 + + + org.contikios.cooja.mspmote.interfaces.MspMoteID + 1 + + z11 + + + + + org.contikios.cooja.interfaces.Position + -19.324109516886306 + 76.23135780254927 + 0.0 + + + org.contikios.cooja.mspmote.interfaces.MspClock + 1.0 + + + org.contikios.cooja.mspmote.interfaces.MspMoteID + 2 + + z11 + + + + + org.contikios.cooja.interfaces.Position + 5.815501305791592 + 76.77463755494317 + 0.0 + + + org.contikios.cooja.mspmote.interfaces.MspClock + 1.0 + + + org.contikios.cooja.mspmote.interfaces.MspMoteID + 3 + + z11 + + + + + org.contikios.cooja.interfaces.Position + 31.920697784030082 + 50.5212265977149 + 0.0 + + + org.contikios.cooja.mspmote.interfaces.MspClock + 1.0 + + + org.contikios.cooja.mspmote.interfaces.MspMoteID + 4 + + z11 + + + + + org.contikios.cooja.interfaces.Position + 47.21747673247198 + 30.217765340599726 + 0.0 + + + org.contikios.cooja.mspmote.interfaces.MspClock + 1.0 + + + org.contikios.cooja.mspmote.interfaces.MspMoteID + 5 + + z11 + + + + + org.contikios.cooja.interfaces.Position + 10.622284947035123 + 109.81862399725188 + 0.0 + + + org.contikios.cooja.mspmote.interfaces.MspClock + 1.0 + + + org.contikios.cooja.mspmote.interfaces.MspMoteID + 6 + + z11 + + + + + org.contikios.cooja.interfaces.Position + 52.41150716335335 + 109.93228340481916 + 0.0 + + + org.contikios.cooja.mspmote.interfaces.MspClock + 1.0 + + + org.contikios.cooja.mspmote.interfaces.MspMoteID + 7 + + z11 + + + + + org.contikios.cooja.interfaces.Position + 70.18727461718498 + 70.06861701541145 + 0.0 + + + org.contikios.cooja.mspmote.interfaces.MspClock + 1.0 + + + org.contikios.cooja.mspmote.interfaces.MspMoteID + 8 + + z11 + + + + + org.contikios.cooja.interfaces.Position + 80.29870484201041 + 99.37351603835938 + 0.0 + + + org.contikios.cooja.mspmote.interfaces.MspClock + 1.0 + + + org.contikios.cooja.mspmote.interfaces.MspMoteID + 9 + + z11 + + + + org.contikios.cooja.plugins.SimControl + 242 + 4 + 160 + 11 + 241 + + + org.contikios.cooja.plugins.Visualizer + + true + org.contikios.cooja.plugins.skins.IDVisualizerSkin + org.contikios.cooja.plugins.skins.GridVisualizerSkin + org.contikios.cooja.plugins.skins.TrafficVisualizerSkin + org.contikios.cooja.plugins.skins.UDGMVisualizerSkin + 1.7405603810040515 0.0 0.0 1.7405603810040515 47.95980153208088 -42.576134155447555 + + 236 + 3 + 230 + 1 + 1 + + + org.contikios.cooja.plugins.LogListener + + ID:1 + + + + 1031 + 0 + 394 + 273 + 6 + + + org.contikios.cooja.plugins.TimeLine + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + + + + 16529.88882215865 + + 1304 + 2 + 311 + 0 + 412 + + + org.contikios.cooja.plugins.ScriptRunner + + + true + + 764 + 1 + 995 + 963 + 111 + + + diff --git a/regression-tests/22-compile-nxp-ports/Makefile b/regression-tests/22-compile-nxp-ports/Makefile index 7ff68eaef..b2c529d28 100644 --- a/regression-tests/22-compile-nxp-ports/Makefile +++ b/regression-tests/22-compile-nxp-ports/Makefile @@ -3,13 +3,17 @@ TOOLSDIR=../../tools # build jn516x examples, covering IPv6, RPL, CoAP, Rime, Nullrdc, Contikimac EXAMPLES = \ +hello-world/jn516x \ jn516x/dr1175-sensors/jn516x \ jn516x/rime/jn516x \ jn516x/rpl/border-router/jn516x \ jn516x/rpl/node/jn516x \ jn516x/rpl/coap-dongle-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=