Added IEEE 802.15.4e TSCH (TimeSlotted Channel Hopping) MAC layer

This commit is contained in:
Simon Duquennoy 2015-09-21 22:01:01 +02:00
parent 1e0adbbf24
commit b0f936263e
20 changed files with 5239 additions and 2 deletions

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

@ -0,0 +1,94 @@
/*
* Copyright (c) 2014, SICS Swedish ICT.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the Contiki operating system.
*
*/
/**
* \file
* This RDC layer does nothing. It is meant for use with MAC
* layers that do not use a RDC at all, such as TSCH.
* \author
* Simon Duquennoy <simonduq@sics.se>
*
*/
#include "net/mac/rdc.h"
#include "net/netstack.h"
/*---------------------------------------------------------------------------*/
static void
send_packet(mac_callback_t sent, void *ptr)
{
}
/*---------------------------------------------------------------------------*/
static void
send_list(mac_callback_t sent, void *ptr, struct rdc_buf_list *buf_list)
{
}
/*---------------------------------------------------------------------------*/
static void
packet_input(void)
{
}
/*---------------------------------------------------------------------------*/
static int
on(void)
{
return 0;
}
/*---------------------------------------------------------------------------*/
static int
off(int keep_radio_on)
{
return 0;
}
/*---------------------------------------------------------------------------*/
static unsigned short
channel_check_interval(void)
{
return 0;
}
/*---------------------------------------------------------------------------*/
static void
init(void)
{
}
/*---------------------------------------------------------------------------*/
const struct rdc_driver nordc_driver = {
"nordc",
init,
send_packet,
send_list,
packet_input,
on,
off,
channel_check_interval,
};
/*---------------------------------------------------------------------------*/

View file

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

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

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

View file

@ -0,0 +1,155 @@
/*
* Copyright (c) 2014, SICS Swedish ICT.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the Contiki operating system.
*
*/
/**
* \file
* Log functions for TSCH, meant for logging from interrupt
* during a timeslot operation. Saves ASN, slot and link information
* and adds the log to a ringbuf for later printout.
* \author
* Simon Duquennoy <simonduq@sics.se>
*
*/
#include "contiki.h"
#include <stdio.h>
#include "net/mac/tsch/tsch.h"
#include "net/mac/tsch/tsch-queue.h"
#include "net/mac/tsch/tsch-private.h"
#include "net/mac/tsch/tsch-log.h"
#include "net/mac/tsch/tsch-packet.h"
#include "net/mac/tsch/tsch-schedule.h"
#include "net/mac/tsch/tsch-slot-operation.h"
#include "lib/ringbufindex.h"
#if TSCH_LOG_LEVEL >= 1
#define DEBUG DEBUG_PRINT
#else /* TSCH_LOG_LEVEL */
#define DEBUG DEBUG_NONE
#endif /* TSCH_LOG_LEVEL */
#include "net/ip/uip-debug.h"
#if TSCH_LOG_LEVEL >= 2 /* Skip this file for log levels 0 or 1 */
PROCESS_NAME(tsch_pending_events_process);
/* Check if TSCH_LOG_QUEUE_LEN is a power of two */
#if (TSCH_LOG_QUEUE_LEN & (TSCH_LOG_QUEUE_LEN - 1)) != 0
#error TSCH_LOG_QUEUE_LEN must be power of two
#endif
static struct ringbufindex log_ringbuf;
static struct tsch_log_t log_array[TSCH_LOG_QUEUE_LEN];
static int log_dropped = 0;
/*---------------------------------------------------------------------------*/
/* Process pending log messages */
void
tsch_log_process_pending(void)
{
static int last_log_dropped = 0;
int16_t log_index;
/* Loop on accessing (without removing) a pending input packet */
if(log_dropped != last_log_dropped) {
printf("TSCH:! logs dropped %u\n", log_dropped);
last_log_dropped = log_dropped;
}
while((log_index = ringbufindex_peek_get(&log_ringbuf)) != -1) {
struct tsch_log_t *log = &log_array[log_index];
struct tsch_slotframe *sf = tsch_schedule_get_slotframe_by_handle(log->link->slotframe_handle);
printf("TSCH: {asn-%x.%lx link-%u-%u-%u-%u ch-%u} ",
log->asn.ms1b, log->asn.ls4b,
log->link->slotframe_handle, sf ? sf->size.val : 0, log->link->timeslot, log->link->channel_offset,
tsch_calculate_channel(&log->asn, log->link->channel_offset));
switch(log->type) {
case tsch_log_tx:
printf("%s-%u-%u %u tx %d, st %d-%d",
log->tx.dest == 0 ? "bc" : "uc", log->tx.is_data, log->tx.sec_level,
log->tx.datalen,
log->tx.dest,
log->tx.mac_tx_status, log->tx.num_tx);
if(log->tx.drift_used) {
printf(", dr %d", log->tx.drift);
}
printf("\n");
break;
case tsch_log_rx:
printf("%s-%u-%u %u rx %d",
log->rx.is_unicast == 0 ? "bc" : "uc", log->rx.is_data, log->tx.sec_level,
log->rx.datalen,
log->rx.src);
if(log->rx.drift_used) {
printf(", dr %d", log->rx.drift);
}
printf(", edr %d\n", (int)log->rx.estimated_drift);
break;
case tsch_log_message:
printf("%s\n", log->message);
break;
}
/* Remove input from ringbuf */
ringbufindex_get(&log_ringbuf);
}
}
/*---------------------------------------------------------------------------*/
/* Prepare addition of a new log.
* Returns pointer to log structure if success, NULL otherwise */
struct tsch_log_t *
tsch_log_prepare_add(void)
{
int log_index = ringbufindex_peek_put(&log_ringbuf);
if(log_index != -1) {
struct tsch_log_t *log = &log_array[log_index];
log->asn = current_asn;
log->link = current_link;
return log;
} else {
log_dropped++;
return NULL;
}
}
/*---------------------------------------------------------------------------*/
/* Actually add the previously prepared log */
void
tsch_log_commit(void)
{
ringbufindex_put(&log_ringbuf);
process_poll(&tsch_pending_events_process);
}
/*---------------------------------------------------------------------------*/
/* Initialize log module */
void
tsch_log_init(void)
{
ringbufindex_init(&log_ringbuf, TSCH_LOG_QUEUE_LEN);
}
#endif /* TSCH_LOG_LEVEL */

View file

@ -0,0 +1,138 @@
/*
* Copyright (c) 2014, SICS Swedish ICT.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the Contiki operating system.
*
*/
#ifndef __TSCH_LOG_H__
#define __TSCH_LOG_H__
/********** Includes **********/
#include "contiki.h"
#include "sys/rtimer.h"
#include "net/mac/tsch/tsch-private.h"
/******** Configuration *******/
/* The length of the log queue, i.e. maximum number postponed log messages */
#ifdef TSCH_LOG_CONF_QUEUE_LEN
#define TSCH_LOG_QUEUE_LEN TSCH_LOG_CONF_QUEUE_LEN
#else /* TSCH_LOG_CONF_QUEUE_LEN */
#define TSCH_LOG_QUEUE_LEN 8
#endif /* TSCH_LOG_CONF_QUEUE_LEN */
/* Returns an integer ID from a link-layer address */
#ifdef TSCH_LOG_CONF_ID_FROM_LINKADDR
#define TSCH_LOG_ID_FROM_LINKADDR(addr) TSCH_LOG_CONF_ID_FROM_LINKADDR(addr)
#else /* TSCH_LOG_ID_FROM_LINKADDR */
#define TSCH_LOG_ID_FROM_LINKADDR(addr) ((addr) ? (addr)->u8[LINKADDR_SIZE - 1] : 0)
#endif /* TSCH_LOG_ID_FROM_LINKADDR */
/* TSCH log levels:
* 0: no log
* 1: basic PRINTF enabled
* 2: basic PRINTF enabled and tsch-log module enabled */
#ifdef TSCH_LOG_CONF_LEVEL
#define TSCH_LOG_LEVEL TSCH_LOG_CONF_LEVEL
#else /* TSCH_LOG_CONF_LEVEL */
#define TSCH_LOG_LEVEL 2
#endif /* TSCH_LOG_CONF_LEVEL */
#if TSCH_LOG_LEVEL < 2 /* For log level 0 or 1, the logging functions do nothing */
#define tsch_log_init()
#define tsch_log_process_pending()
#define TSCH_LOG_ADD(log_type, init_code)
#else /* TSCH_LOG_LEVEL */
/************ Types ***********/
/* Structure for a log. Union of different types of logs */
struct tsch_log_t {
enum { tsch_log_tx,
tsch_log_rx,
tsch_log_message
} type;
struct asn_t asn;
struct tsch_link *link;
union {
char message[48];
struct {
int mac_tx_status;
int dest;
int drift;
uint8_t num_tx;
uint8_t datalen;
uint8_t is_data;
uint8_t sec_level;
uint8_t drift_used;
} tx;
struct {
int src;
int drift;
int estimated_drift;
uint8_t datalen;
uint8_t is_unicast;
uint8_t is_data;
uint8_t sec_level;
uint8_t drift_used;
} rx;
};
};
/********** Functions *********/
/* Prepare addition of a new log.
* Returns pointer to log structure if success, NULL otherwise */
struct tsch_log_t *tsch_log_prepare_add(void);
/* Actually add the previously prepared log */
void tsch_log_commit(void);
/* Initialize log module */
void tsch_log_init(void);
/* Process pending log messages */
void tsch_log_process_pending(void);
/************ Macros **********/
/* Use this macro to add a log to the queue (will be printed out
* later, after leaving interrupt context) */
#define TSCH_LOG_ADD(log_type, init_code) do { \
struct tsch_log_t *log = tsch_log_prepare_add(); \
if(log != NULL) { \
log->type = (log_type); \
init_code; \
tsch_log_commit(); \
} \
} while(0);
#endif /* TSCH_LOG_LEVEL */
#endif /* __TSCH_LOG_H__ */

View file

@ -0,0 +1,413 @@
/*
* Copyright (c) 2014, SICS Swedish ICT.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the Contiki operating system.
*
*/
/**
* \file
* TSCH packet format management
* \author
* Simon Duquennoy <simonduq@sics.se>
* Beshr Al Nahas <beshr@sics.se>
*/
#include "contiki.h"
#include "net/packetbuf.h"
#include "net/mac/tsch/tsch.h"
#include "net/mac/tsch/tsch-packet.h"
#include "net/mac/tsch/tsch-private.h"
#include "net/mac/tsch/tsch-schedule.h"
#include "net/mac/tsch/tsch-security.h"
#include "net/mac/tsch/tsch-log.h"
#include "net/mac/frame802154.h"
#include "net/mac/framer-802154.h"
#include "net/netstack.h"
#include "net/llsec/anti-replay.h"
#include "lib/ccm-star.h"
#include "lib/aes-128.h"
#include <stdio.h>
#include <string.h>
#if TSCH_LOG_LEVEL >= 1
#define DEBUG DEBUG_PRINT
#else /* TSCH_LOG_LEVEL */
#define DEBUG DEBUG_NONE
#endif /* TSCH_LOG_LEVEL */
#include "net/ip/uip-debug.h"
/*---------------------------------------------------------------------------*/
/* Construct enhanced ACK packet and return ACK length */
int
tsch_packet_create_eack(uint8_t *buf, int buf_size,
linkaddr_t *dest_addr, uint8_t seqno, int16_t drift, int nack)
{
int ret;
uint8_t curr_len = 0;
frame802154_t p;
struct ieee802154_ies ies;
memset(&p, 0, sizeof(p));
p.fcf.frame_type = FRAME802154_ACKFRAME;
p.fcf.frame_version = FRAME802154_IEEE802154E_2012;
p.fcf.ie_list_present = 1;
/* Compression unset. According to IEEE802.15.4e-2012:
* - if no address is present: elide PAN ID
* - if at least one address is present: include exactly one PAN ID (dest by default) */
p.fcf.panid_compression = 0;
p.dest_pid = IEEE802154_PANID;
p.seq = seqno;
#if TSCH_PACKET_EACK_WITH_DEST_ADDR
if(dest_addr != NULL) {
p.fcf.dest_addr_mode = FRAME802154_LONGADDRMODE;
linkaddr_copy((linkaddr_t *)&p.dest_addr, dest_addr);
}
#endif
#if TSCH_PACKET_EACK_WITH_SRC_ADDR
p.fcf.src_addr_mode = FRAME802154_LONGADDRMODE;
p.src_pid = IEEE802154_PANID;
linkaddr_copy((linkaddr_t *)&p.src_addr, &linkaddr_node_addr);
#endif
#if TSCH_SECURITY_ENABLED
if(tsch_is_pan_secured) {
p.fcf.security_enabled = 1;
p.aux_hdr.security_control.security_level = TSCH_SECURITY_KEY_SEC_LEVEL_ACK;
p.aux_hdr.security_control.key_id_mode = FRAME802154_1_BYTE_KEY_ID_MODE;
p.aux_hdr.security_control.frame_counter_suppression = 1;
p.aux_hdr.security_control.frame_counter_size = 1;
p.aux_hdr.key_index = TSCH_SECURITY_KEY_INDEX_ACK;
}
#endif /* TSCH_SECURITY_ENABLED */
if((curr_len = frame802154_create(&p, buf)) == 0) {
return 0;
}
/* Append IE timesync */
memset(&ies, 0, sizeof(ies));
ies.ie_time_correction = drift;
ies.ie_is_nack = nack;
if((ret = frame80215e_create_ie_header_ack_nack_time_correction(buf+curr_len, buf_size-curr_len, &ies)) == -1) {
return -1;
}
curr_len += ret;
return curr_len;
}
/*---------------------------------------------------------------------------*/
/* Parse enhanced ACK packet, extract drift and nack */
int
tsch_packet_parse_eack(const uint8_t *buf, int buf_size,
uint8_t seqno, frame802154_t *frame, struct ieee802154_ies *ies, uint8_t *hdr_len)
{
uint8_t curr_len = 0;
int ret;
linkaddr_t dest;
if(frame == NULL || buf_size < 0) {
return 0;
}
/* Parse 802.15.4-2006 frame, i.e. all fields before Information Elements */
if((ret = frame802154_parse((uint8_t *)buf, buf_size, frame)) < 3) {
return 0;
}
if(hdr_len != NULL) {
*hdr_len = ret;
}
curr_len += ret;
/* Check seqno */
if(seqno != frame->seq) {
return 0;
}
/* Check destination PAN ID */
if(frame802154_check_dest_panid(frame) == 0) {
return 0;
}
/* Check destination address (if any) */
if(frame802154_extract_linkaddr(frame, NULL, &dest) == 0 ||
(!linkaddr_cmp(&dest, &linkaddr_node_addr)
&& !linkaddr_cmp(&dest, &linkaddr_null))) {
return 0;
}
if(ies != NULL) {
memset(ies, 0, sizeof(struct ieee802154_ies));
}
if(frame->fcf.ie_list_present) {
int mic_len = 0;
#if TSCH_SECURITY_ENABLED
/* Check if there is space for the security MIC (if any) */
mic_len = tsch_security_mic_len(frame);
if(buf_size < curr_len + mic_len) {
return 0;
}
#endif /* TSCH_SECURITY_ENABLED */
/* Parse information elements. We need to substract the MIC length, as the exact payload len is needed while parsing */
if((ret = frame802154e_parse_information_elements(buf + curr_len, buf_size - curr_len - mic_len, ies)) == -1) {
return 0;
}
curr_len += ret;
}
if(hdr_len != NULL) {
*hdr_len += ies->ie_payload_ie_offset;
}
return curr_len;
}
/*---------------------------------------------------------------------------*/
/* Create an EB packet */
int
tsch_packet_create_eb(uint8_t *buf, int buf_size, uint8_t seqno,
uint8_t *hdr_len, uint8_t *tsch_sync_ie_offset)
{
int ret = 0;
uint8_t curr_len = 0;
uint8_t mlme_ie_offset;
frame802154_t p;
struct ieee802154_ies ies;
if(buf_size < TSCH_PACKET_MAX_LEN) {
return 0;
}
/* Create 802.15.4 header */
memset(&p, 0, sizeof(p));
p.fcf.frame_type = FRAME802154_BEACONFRAME;
p.fcf.ie_list_present = 1;
p.fcf.frame_version = FRAME802154_IEEE802154E_2012;
p.fcf.src_addr_mode = FRAME802154_LONGADDRMODE;
p.fcf.dest_addr_mode = FRAME802154_SHORTADDRMODE;
p.seq = seqno;
p.fcf.sequence_number_suppression = FRAME802154_SUPPR_SEQNO;
/* It is important not to compress PAN ID, as this would result in not including either
* source nor destination PAN ID, leaving potential joining devices unaware of the PAN ID. */
p.fcf.panid_compression = 0;
p.src_pid = frame802154_get_pan_id();
p.dest_pid = frame802154_get_pan_id();
linkaddr_copy((linkaddr_t *)&p.src_addr, &linkaddr_node_addr);
p.dest_addr[0] = 0xff;
p.dest_addr[1] = 0xff;
#if TSCH_SECURITY_ENABLED
if(tsch_is_pan_secured) {
p.fcf.security_enabled = packetbuf_attr(PACKETBUF_ATTR_SECURITY_LEVEL) > 0;
p.aux_hdr.security_control.security_level = packetbuf_attr(PACKETBUF_ATTR_SECURITY_LEVEL);
p.aux_hdr.security_control.key_id_mode = packetbuf_attr(PACKETBUF_ATTR_KEY_ID_MODE);
p.aux_hdr.security_control.frame_counter_suppression = 1;
p.aux_hdr.security_control.frame_counter_size = 1;
p.aux_hdr.key_index = packetbuf_attr(PACKETBUF_ATTR_KEY_INDEX);
}
#endif /* TSCH_SECURITY_ENABLED */
if((curr_len = frame802154_create(&p, buf)) == 0) {
return 0;
}
/* Prepare Information Elements for inclusion in the EB */
memset(&ies, 0, sizeof(ies));
/* Add TSCH timeslot timing IE. */
#if TSCH_PACKET_EB_WITH_TIMESLOT_TIMING
{
int i;
ies.ie_tsch_timeslot_id = 1;
for(i = 0; i < tsch_ts_elements_count; i++) {
ies.ie_tsch_timeslot[i] = RTIMERTICKS_TO_US(tsch_timing[i]);
}
}
#endif /* TSCH_PACKET_EB_WITH_TIMESLOT_TIMING */
/* Add TSCH hopping sequence IE */
#if TSCH_PACKET_EB_WITH_HOPPING_SEQUENCE
if(tsch_hopping_sequence_length.val <= sizeof(ies.ie_hopping_sequence_list)) {
ies.ie_channel_hopping_sequence_id = 1;
ies.ie_hopping_sequence_len = tsch_hopping_sequence_length.val;
memcpy(ies.ie_hopping_sequence_list, tsch_hopping_sequence, ies.ie_hopping_sequence_len);
}
#endif /* TSCH_PACKET_EB_WITH_HOPPING_SEQUENCE */
/* Add Slotframe and Link IE */
#if TSCH_PACKET_EB_WITH_SLOTFRAME_AND_LINK
{
/* Send slotframe 0 with link at timeslot 0 */
struct tsch_slotframe *sf0 = tsch_schedule_get_slotframe_by_handle(0);
struct tsch_link *link0 = tsch_schedule_get_link_by_timeslot(sf0, 0);
if(sf0 && link0) {
ies.ie_tsch_slotframe_and_link.num_slotframes = 1;
ies.ie_tsch_slotframe_and_link.slotframe_handle = sf0->handle;
ies.ie_tsch_slotframe_and_link.slotframe_size = sf0->size.val;
ies.ie_tsch_slotframe_and_link.num_links = 1;
ies.ie_tsch_slotframe_and_link.links[0].timeslot = link0->timeslot;
ies.ie_tsch_slotframe_and_link.links[0].channel_offset = link0->channel_offset;
ies.ie_tsch_slotframe_and_link.links[0].link_options = link0->link_options;
}
}
#endif /* TSCH_PACKET_EB_WITH_SLOTFRAME_AND_LINK */
/* First add header-IE termination IE to stipulate that next come payload IEs */
if((ret = frame80215e_create_ie_header_list_termination_1(buf + curr_len, buf_size - curr_len, &ies)) == -1) {
return -1;
}
curr_len += ret;
/* We start payload IEs, save offset */
if(hdr_len != NULL) {
*hdr_len = curr_len;
}
/* Save offset of the MLME IE descriptor, we need to know the total length
* before writing it */
mlme_ie_offset = curr_len;
curr_len += 2; /* Space needed for MLME descriptor */
/* Save the offset of the TSCH Synchronization IE, needed to update ASN and join priority before sending */
if(tsch_sync_ie_offset != NULL) {
*tsch_sync_ie_offset = curr_len;
}
if((ret = frame80215e_create_ie_tsch_synchronization(buf + curr_len, buf_size - curr_len, &ies)) == -1) {
return -1;
}
curr_len += ret;
if((ret = frame80215e_create_ie_tsch_timeslot(buf + curr_len, buf_size - curr_len, &ies)) == -1) {
return -1;
}
curr_len += ret;
if((ret = frame80215e_create_ie_tsch_channel_hopping_sequence(buf + curr_len, buf_size - curr_len, &ies)) == -1) {
return -1;
}
curr_len += ret;
if((ret = frame80215e_create_ie_tsch_slotframe_and_link(buf + curr_len, buf_size - curr_len, &ies)) == -1) {
return -1;
}
curr_len += ret;
ies.ie_mlme_len = curr_len - mlme_ie_offset - 2;
if((ret = frame80215e_create_ie_mlme(buf + mlme_ie_offset, buf_size - mlme_ie_offset, &ies)) == -1) {
return -1;
}
/* Payload IE list termination: optional */
/*
if((ret = frame80215e_create_ie_payload_list_termination(buf + curr_len, buf_size - curr_len, &ies)) == -1) {
return -1;
}
curr_len += ret;
*/
return curr_len;
}
/*---------------------------------------------------------------------------*/
/* Update ASN in EB packet */
int
tsch_packet_update_eb(uint8_t *buf, int buf_size, uint8_t tsch_sync_ie_offset)
{
struct ieee802154_ies ies;
ies.ie_asn = current_asn;
ies.ie_join_priority = tsch_join_priority;
frame80215e_create_ie_tsch_synchronization(buf+tsch_sync_ie_offset, buf_size-tsch_sync_ie_offset, &ies);
return 1;
}
/*---------------------------------------------------------------------------*/
/* Parse a IEEE 802.15.4e TSCH Enhanced Beacon (EB) */
int
tsch_packet_parse_eb(const uint8_t *buf, int buf_size,
frame802154_t *frame, struct ieee802154_ies *ies, uint8_t *hdr_len, int frame_without_mic)
{
uint8_t curr_len = 0;
int ret;
if(frame == NULL || buf_size < 0) {
return 0;
}
/* Parse 802.15.4-2006 frame, i.e. all fields before Information Elements */
if((ret = frame802154_parse((uint8_t *)buf, buf_size, frame)) == 0) {
PRINTF("TSCH:! parse_eb: failed to parse frame\n");
return 0;
}
if(frame->fcf.frame_version < FRAME802154_IEEE802154E_2012
|| frame->fcf.frame_type != FRAME802154_BEACONFRAME) {
PRINTF("TSCH:! parse_eb: frame is not a valid TSCH beacon. Frame version %u, type %u, FCF %02x %02x\n",
frame->fcf.frame_version, frame->fcf.frame_type, buf[0], buf[1]);
PRINTF("TSCH:! parse_eb: frame was from 0x%x/", frame->src_pid);
PRINTLLADDR((const uip_lladdr_t *)&frame->src_addr);
PRINTF(" to 0x%x/", frame->dest_pid);
PRINTLLADDR((const uip_lladdr_t *)&frame->dest_addr);
PRINTF("\n");
return 0;
}
if(hdr_len != NULL) {
*hdr_len = ret;
}
curr_len += ret;
if(ies != NULL) {
memset(ies, 0, sizeof(struct ieee802154_ies));
ies->ie_join_priority = 0xff; /* Use max value in case the Beacon does not include a join priority */
}
if(frame->fcf.ie_list_present) {
/* Calculate space needed for the security MIC, if any, before attempting to parse IEs */
int mic_len = 0;
#if TSCH_SECURITY_ENABLED
if(!frame_without_mic) {
mic_len = tsch_security_mic_len(frame);
if(buf_size < curr_len + mic_len) {
return 0;
}
}
#endif /* TSCH_SECURITY_ENABLED */
/* Parse information elements. We need to substract the MIC length, as the exact payload len is needed while parsing */
if((ret = frame802154e_parse_information_elements(buf + curr_len, buf_size - curr_len - mic_len, ies)) == -1) {
PRINTF("TSCH:! parse_eb: failed to parse IEs\n");
return 0;
}
curr_len += ret;
}
if(hdr_len != NULL) {
*hdr_len += ies->ie_payload_ie_offset;
}
return curr_len;
}
/*---------------------------------------------------------------------------*/

View file

@ -0,0 +1,104 @@
/*
* Copyright (c) 2014, SICS Swedish ICT.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the Contiki operating system.
*
*/
#ifndef __TSCH_PACKET_H__
#define __TSCH_PACKET_H__
/********** Includes **********/
#include "contiki.h"
#include "net/mac/tsch/tsch-private.h"
#include "net/mac/frame802154.h"
#include "net/mac/frame802154e-ie.h"
/******** Configuration *******/
/* TSCH EB: include timeslot timing Information Element? */
#ifdef TSCH_PACKET_CONF_EB_WITH_TIMESLOT_TIMING
#define TSCH_PACKET_EB_WITH_TIMESLOT_TIMING TSCH_PACKET_CONF_EB_WITH_TIMESLOT_TIMING
#else
#define TSCH_PACKET_EB_WITH_TIMESLOT_TIMING 0
#endif
/* TSCH EB: include hopping sequence Information Element? */
#ifdef TSCH_PACKET_CONF_EB_WITH_HOPPING_SEQUENCE
#define TSCH_PACKET_EB_WITH_HOPPING_SEQUENCE TSCH_PACKET_CONF_EB_WITH_HOPPING_SEQUENCE
#else
#define TSCH_PACKET_EB_WITH_HOPPING_SEQUENCE 0
#endif
/* TSCH EB: include slotframe and link Information Element? */
#ifdef TSCH_PACKET_CONF_EB_WITH_SLOTFRAME_AND_LINK
#define TSCH_PACKET_EB_WITH_SLOTFRAME_AND_LINK TSCH_PACKET_CONF_EB_WITH_SLOTFRAME_AND_LINK
#else
#define TSCH_PACKET_EB_WITH_SLOTFRAME_AND_LINK 0
#endif
/* Include source address in ACK? */
#ifdef TSCH_PACKET_CONF_EACK_WITH_SRC_ADDR
#define TSCH_PACKET_EACK_WITH_SRC_ADDR TSCH_PACKET_CONF_EACK_WITH_SRC_ADDR
#else
#define TSCH_PACKET_EACK_WITH_SRC_ADDR 0
#endif
/* Include destination address in ACK? */
#ifdef TSCH_PACKET_CONF_EACK_WITH_DEST_ADDR
#define TSCH_PACKET_EACK_WITH_DEST_ADDR TSCH_PACKET_CONF_EACK_WITH_DEST_ADDR
#else
#define TSCH_PACKET_EACK_WITH_DEST_ADDR 1 /* Include destination address
by default, useful in case of duplicate seqno */
#endif
/********** Constants *********/
/* Max TSCH packet lenght */
#define TSCH_PACKET_MAX_LEN 127
/********** Functions *********/
/* Construct enhanced ACK packet and return ACK length */
int tsch_packet_create_eack(uint8_t *buf, int buf_size,
linkaddr_t *dest_addr, uint8_t seqno, int16_t drift, int nack);
/* Parse enhanced ACK packet, extract drift and nack */
int tsch_packet_parse_eack(const uint8_t *buf, int buf_size,
uint8_t seqno, frame802154_t *frame, struct ieee802154_ies *ies, uint8_t *hdr_len);
/* Create an EB packet */
int tsch_packet_create_eb(uint8_t *buf, int buf_size,
uint8_t seqno, uint8_t *hdr_len, uint8_t *tsch_sync_ie_ptr);
/* Update ASN in EB packet */
int tsch_packet_update_eb(uint8_t *buf, int buf_size, uint8_t tsch_sync_ie_offset);
/* Parse EB and extract ASN and join priority */
int tsch_packet_parse_eb(const uint8_t *buf, int buf_size,
frame802154_t *frame, struct ieee802154_ies *ies,
uint8_t *hdrlen, int frame_without_mic);
#endif /* __TSCH_PACKET_H__ */

View file

@ -0,0 +1,475 @@
/*
* Copyright (c) 2014, SICS Swedish ICT.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the Contiki operating system.
*
*/
/**
* \file
* Per-neighbor packet queues for TSCH MAC.
* The list of neighbors uses the TSCH lock, but per-neighbor packet array are lock-free.
* Read-only operation on neighbor and packets are allowed from interrupts and outside of them.
* *Other operations are allowed outside of interrupt only.*
* \author
* Simon Duquennoy <simonduq@sics.se>
* Beshr Al Nahas <beshr@sics.se>
* Domenico De Guglielmo <d.deguglielmo@iet.unipi.it >
*/
#include "contiki.h"
#include "lib/list.h"
#include "lib/memb.h"
#include "net/queuebuf.h"
#include "net/mac/rdc.h"
#include "net/mac/tsch/tsch.h"
#include "net/mac/tsch/tsch-private.h"
#include "net/mac/tsch/tsch-queue.h"
#include "net/mac/tsch/tsch-schedule.h"
#include "net/mac/tsch/tsch-slot-operation.h"
#include "net/mac/tsch/tsch-log.h"
#include <string.h>
#if TSCH_LOG_LEVEL >= 1
#define DEBUG DEBUG_PRINT
#else /* TSCH_LOG_LEVEL */
#define DEBUG DEBUG_NONE
#endif /* TSCH_LOG_LEVEL */
#include "net/ip/uip-debug.h"
/* Check if TSCH_QUEUE_NUM_PER_NEIGHBOR is power of two */
#if (TSCH_QUEUE_NUM_PER_NEIGHBOR & (TSCH_QUEUE_NUM_PER_NEIGHBOR - 1)) != 0
#error TSCH_QUEUE_NUM_PER_NEIGHBOR must be power of two
#endif
/* We have as many packets are there are queuebuf in the system */
MEMB(packet_memb, struct tsch_packet, QUEUEBUF_NUM);
MEMB(neighbor_memb, struct tsch_neighbor, TSCH_QUEUE_MAX_NEIGHBOR_QUEUES);
LIST(neighbor_list);
/* Broadcast and EB virtual neighbors */
struct tsch_neighbor *n_broadcast;
struct tsch_neighbor *n_eb;
/*---------------------------------------------------------------------------*/
/**
* A pseudo-random generator with better properties than msp430-libc's default
**/
static uint32_t tsch_random_seed;
static void
tsch_random_init(uint32_t x)
{
tsch_random_seed = x;
}
static uint8_t
tsch_random_byte(uint8_t window)
{
tsch_random_seed = tsch_random_seed * 1103515245 + 12345;
return (tsch_random_seed / 65536) & window;
}
/*---------------------------------------------------------------------------*/
/* 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 = new_addr ? tsch_queue_add_nbr(new_addr) : NULL;
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) */
n->backoff_window = tsch_random_byte((1 << n->backoff_exponent) - 1);
/* 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);
tsch_random_init(*((uint32_t *)&linkaddr_node_addr) +
*((uint32_t *)&linkaddr_node_addr + 1));
memb_init(&neighbor_memb);
memb_init(&packet_memb);
/* Add virtual EB and the broadcast neighbors */
n_eb = tsch_queue_add_nbr(&tsch_eb_address);
n_broadcast = tsch_queue_add_nbr(&tsch_broadcast_address);
}
/*---------------------------------------------------------------------------*/

View file

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

View file

@ -0,0 +1,107 @@
/*
* Copyright (c) 2014, SICS Swedish ICT.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
/**
* \file
* Interaction between TSCH and RPL
*
* \author Simon Duquennoy <simonduq@sics.se>
*/
#if UIP_CONF_IPV6_RPL
#include "contiki.h"
#include "net/rpl/rpl.h"
#include "net/rpl/rpl-private.h"
#include "net/mac/tsch/tsch.h"
#include "net/mac/tsch/tsch-private.h"
#include "net/mac/tsch/tsch-schedule.h"
#include "net/mac/tsch/tsch-log.h"
#include "tsch-rpl.h"
#if TSCH_LOG_LEVEL >= 1
#define DEBUG DEBUG_PRINT
#else /* TSCH_LOG_LEVEL */
#define DEBUG DEBUG_NONE
#endif /* TSCH_LOG_LEVEL */
#include "net/ip/uip-debug.h"
/*---------------------------------------------------------------------------*/
/* To use, set #define TSCH_CALLBACK_JOINING_NETWORK tsch_rpl_callback_joining_network */
void
tsch_rpl_callback_joining_network(void)
{
}
/*---------------------------------------------------------------------------*/
/* Upon leaving a TSCH network, perform a local repair
* (cleanup neighbor state, reset Trickle timer etc)
* To use, set #define TSCH_CALLBACK_LEAVING_NETWORK tsch_rpl_callback_leaving_network */
void
tsch_rpl_callback_leaving_network(void)
{
rpl_dag_t *dag = rpl_get_any_dag();
if(dag != NULL) {
rpl_local_repair(dag->instance);
}
}
/*---------------------------------------------------------------------------*/
/* Set TSCH EB period based on current RPL DIO period.
* To use, set #define RPL_CALLBACK_PARENT_SWITCH tsch_rpl_callback_new_dio_interval */
void
tsch_rpl_callback_new_dio_interval(uint8_t dio_interval)
{
/* Transmit EBs only if we have a valid rank as per 6TiSCH minimal */
rpl_dag_t *dag = rpl_get_any_dag();
if(dag != NULL && dag->rank != INFINITE_RANK) {
/* If we are root set TSCH as coordinator */
if(dag->rank == ROOT_RANK(dag->instance)) {
tsch_set_coordinator(1);
}
/* Set EB period */
tsch_set_eb_period(TSCH_EB_PERIOD);
/* Set join priority based on RPL rank */
tsch_set_join_priority(DAG_RANK(dag->rank, dag->instance) - 1);
} else {
tsch_set_eb_period(0);
}
}
/*---------------------------------------------------------------------------*/
/* Set TSCH time source based on current RPL preferred parent.
* To use, set #define RPL_CALLBACK_PARENT_SWITCH tsch_rpl_callback_parent_switch */
void
tsch_rpl_callback_parent_switch(rpl_parent_t *old, rpl_parent_t *new)
{
if(tsch_is_associated == 1) {
tsch_queue_update_time_source(
(const linkaddr_t *)uip_ds6_nbr_lladdr_from_ipaddr(
rpl_get_parent_ipaddr(new)));
}
}
#endif /* UIP_CONF_IPV6_RPL */

View file

@ -0,0 +1,55 @@
/*
* Copyright (c) 2014, SICS Swedish ICT.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#ifndef __TSCH_RPL_H__
#define __TSCH_RPL_H__
/********** Includes **********/
#include "net/rpl/rpl.h"
#include "net/mac/tsch/tsch-queue.h"
/********** Functions *********/
/* To use, set #define TSCH_CALLBACK_JOINING_NETWORK tsch_rpl_callback_joining_network */
void tsch_rpl_callback_joining_network(void);
/* Upon leaving a TSCH network, perform a local repair
* (cleanup neighbor state, reset Trickle timer etc)
* To use, set #define TSCH_CALLBACK_LEAVING_NETWORK tsch_rpl_callback_leaving_network */
void tsch_rpl_callback_leaving_network(void);
/* Set TSCH EB period based on current RPL DIO period.
* To use, set #define RPL_CALLBACK_PARENT_SWITCH tsch_rpl_callback_new_dio_interval */
void tsch_rpl_callback_new_dio_interval(uint8_t dio_interval);
/* Set TSCH time source based on current RPL preferred parent.
* To use, set #define RPL_CALLBACK_PARENT_SWITCH tsch_rpl_callback_parent_switch */
void tsch_rpl_callback_parent_switch(rpl_parent_t *old, rpl_parent_t *new);
#endif /* __TSCH_RPL_H__ */

View file

@ -0,0 +1,447 @@
/*
* Copyright (c) 2014, SICS Swedish ICT.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the Contiki operating system.
*
*/
/**
* \file
* IEEE 802.15.4 TSCH MAC schedule manager.
* \author
* Simon Duquennoy <simonduq@sics.se>
* Beshr Al Nahas <beshr@sics.se>
*/
#include "contiki.h"
#include "dev/leds.h"
#include "lib/memb.h"
#include "net/nbr-table.h"
#include "net/packetbuf.h"
#include "net/queuebuf.h"
#include "net/mac/tsch/tsch.h"
#include "net/mac/tsch/tsch-queue.h"
#include "net/mac/tsch/tsch-private.h"
#include "net/mac/tsch/tsch-packet.h"
#include "net/mac/tsch/tsch-schedule.h"
#include "net/mac/tsch/tsch-log.h"
#include "net/mac/frame802154.h"
#include "sys/process.h"
#include "sys/rtimer.h"
#include <string.h>
#if TSCH_LOG_LEVEL >= 1
#define DEBUG DEBUG_PRINT
#else /* TSCH_LOG_LEVEL */
#define DEBUG DEBUG_NONE
#endif /* TSCH_LOG_LEVEL */
#include "net/ip/uip-debug.h"
/* Pre-allocated space for links */
MEMB(link_memb, struct tsch_link, TSCH_SCHEDULE_MAX_LINKS);
/* Pre-allocated space for slotframes */
MEMB(slotframe_memb, struct tsch_slotframe, TSCH_SCHEDULE_MAX_SLOTFRAMES);
/* List of slotframes (each slotframe holds its own list of links) */
LIST(slotframe_list);
/* Adds and returns a slotframe (NULL if failure) */
struct tsch_slotframe *
tsch_schedule_add_slotframe(uint16_t handle, uint16_t size)
{
if(size == 0) {
return NULL;
}
if(tsch_schedule_get_slotframe_by_handle(handle)) {
/* A slotframe with this handle already exists */
return NULL;
}
if(tsch_get_lock()) {
struct tsch_slotframe *sf = memb_alloc(&slotframe_memb);
if(sf != NULL) {
/* Initialize the slotframe */
sf->handle = handle;
ASN_DIVISOR_INIT(sf->size, size);
LIST_STRUCT_INIT(sf, links_list);
/* Add the slotframe to the global list */
list_add(slotframe_list, sf);
}
PRINTF("TSCH-schedule: add_slotframe %u %u\n",
handle, size);
tsch_release_lock();
return sf;
}
return NULL;
}
/*---------------------------------------------------------------------------*/
/* Removes all slotframes, resulting in an empty schedule */
int
tsch_schedule_remove_all_slotframes(void)
{
struct tsch_slotframe *sf;
while((sf = list_head(slotframe_list))) {
if(tsch_schedule_remove_slotframe(sf) == 0) {
return 0;
}
}
return 1;
}
/*---------------------------------------------------------------------------*/
/* Removes a slotframe Return 1 if success, 0 if failure */
int
tsch_schedule_remove_slotframe(struct tsch_slotframe *slotframe)
{
if(slotframe != NULL) {
/* Remove all links belonging to this slotframe */
struct tsch_link *l;
while((l = list_head(slotframe->links_list))) {
tsch_schedule_remove_link(slotframe, l);
}
/* Now that the slotframe has no links, remove it. */
if(tsch_get_lock()) {
PRINTF("TSCH-schedule: remove slotframe %u %u\n", slotframe->handle, slotframe->size.val);
memb_free(&slotframe_memb, slotframe);
list_remove(slotframe_list, slotframe);
tsch_release_lock();
return 1;
}
}
return 0;
}
/*---------------------------------------------------------------------------*/
/* Looks for a slotframe from a handle */
struct tsch_slotframe *
tsch_schedule_get_slotframe_by_handle(uint16_t handle)
{
if(!tsch_is_locked()) {
struct tsch_slotframe *sf = list_head(slotframe_list);
while(sf != NULL) {
if(sf->handle == handle) {
return sf;
}
sf = list_item_next(sf);
}
}
return NULL;
}
/*---------------------------------------------------------------------------*/
/* Looks for a link from a handle */
struct tsch_link *
tsch_schedule_get_link_by_handle(uint16_t handle)
{
if(!tsch_is_locked()) {
struct tsch_slotframe *sf = list_head(slotframe_list);
while(sf != NULL) {
struct tsch_link *l = list_head(sf->links_list);
/* Loop over all items. Assume there is max one link per timeslot */
while(l != NULL) {
if(l->handle == handle) {
return l;
}
l = list_item_next(l);
}
sf = list_item_next(sf);
}
}
return NULL;
}
/*---------------------------------------------------------------------------*/
/* Adds a link to a slotframe, return a pointer to it (NULL if failure) */
struct tsch_link *
tsch_schedule_add_link(struct tsch_slotframe *slotframe,
uint8_t link_options, enum link_type link_type, const linkaddr_t *address,
uint16_t timeslot, uint16_t channel_offset)
{
struct tsch_link *l = NULL;
if(slotframe != NULL) {
/* We currently support only one link per timeslot in a given slotframe. */
/* Start with removing the link currently installed at this timeslot (needed
* to keep neighbor state in sync with link options etc.) */
tsch_schedule_remove_link_by_timeslot(slotframe, timeslot);
if(!tsch_get_lock()) {
PRINTF("TSCH-schedule:! add_link memb_alloc couldn't take lock\n");
} else {
l = memb_alloc(&link_memb);
if(l == NULL) {
PRINTF("TSCH-schedule:! add_link memb_alloc failed\n");
} else {
static int current_link_handle = 0;
struct tsch_neighbor *n;
/* Add the link to the slotframe */
list_add(slotframe->links_list, l);
/* Initialize link */
l->handle = current_link_handle++;
l->link_options = link_options;
l->link_type = link_type;
l->slotframe_handle = slotframe->handle;
l->timeslot = timeslot;
l->channel_offset = channel_offset;
l->data = NULL;
if(address == NULL) {
address = &linkaddr_null;
}
linkaddr_copy(&l->addr, address);
PRINTF("TSCH-schedule: add_link %u %u %u %u %u %u\n",
slotframe->handle, link_options, link_type, timeslot, channel_offset, TSCH_LOG_ID_FROM_LINKADDR(address));
/* Release the lock before we update the neighbor (will take the lock) */
tsch_release_lock();
if(l->link_options & LINK_OPTION_TX) {
n = tsch_queue_add_nbr(&l->addr);
/* We have a tx link to this neighbor, update counters */
if(n != NULL) {
n->tx_links_count++;
if(!(l->link_options & LINK_OPTION_SHARED)) {
n->dedicated_tx_links_count++;
}
}
}
}
}
}
return l;
}
/*---------------------------------------------------------------------------*/
/* Removes a link from slotframe. Return 1 if success, 0 if failure */
int
tsch_schedule_remove_link(struct tsch_slotframe *slotframe, struct tsch_link *l)
{
if(slotframe != NULL && l != NULL && l->slotframe_handle == slotframe->handle) {
if(tsch_get_lock()) {
uint8_t link_options;
linkaddr_t addr;
/* Save link option and addr in local variables as we need them
* after freeing the link */
link_options = l->link_options;
linkaddr_copy(&addr, &l->addr);
/* The link to be removed is scheduled as next, set it to NULL
* to abort the next link operation */
if(l == current_link) {
current_link = NULL;
}
PRINTF("TSCH-schedule: remove_link %u %u %u %u %u\n",
slotframe->handle, l->link_options, l->timeslot, l->channel_offset,
TSCH_LOG_ID_FROM_LINKADDR(&l->addr));
list_remove(slotframe->links_list, l);
memb_free(&link_memb, l);
/* Release the lock before we update the neighbor (will take the lock) */
tsch_release_lock();
/* This was a tx link to this neighbor, update counters */
if(link_options & LINK_OPTION_TX) {
struct tsch_neighbor *n = tsch_queue_add_nbr(&addr);
if(n != NULL) {
n->tx_links_count--;
if(!(link_options & LINK_OPTION_SHARED)) {
n->dedicated_tx_links_count--;
}
}
}
return 1;
} else {
PRINTF("TSCH-schedule:! remove_link memb_alloc couldn't take lock\n");
}
}
return 0;
}
/*---------------------------------------------------------------------------*/
/* Removes a link from slotframe and timeslot. Return a 1 if success, 0 if failure */
int
tsch_schedule_remove_link_by_timeslot(struct tsch_slotframe *slotframe, uint16_t timeslot)
{
return slotframe != NULL &&
tsch_schedule_remove_link(slotframe, tsch_schedule_get_link_by_timeslot(slotframe, timeslot));
}
/*---------------------------------------------------------------------------*/
/* Looks within a slotframe for a link with a given timeslot */
struct tsch_link *
tsch_schedule_get_link_by_timeslot(struct tsch_slotframe *slotframe, uint16_t timeslot)
{
if(!tsch_is_locked()) {
if(slotframe != NULL) {
struct tsch_link *l = list_head(slotframe->links_list);
/* Loop over all items. Assume there is max one link per timeslot */
while(l != NULL) {
if(l->timeslot == timeslot) {
return l;
}
l = list_item_next(l);
}
return l;
}
}
return NULL;
}
/*---------------------------------------------------------------------------*/
/* Returns the next active link after a given ASN, and a backup link (for the same ASN, with Rx flag) */
struct tsch_link *
tsch_schedule_get_next_active_link(struct asn_t *asn, uint16_t *time_offset,
struct tsch_link **backup_link)
{
uint16_t time_to_curr_best = 0;
struct tsch_link *curr_best = NULL;
struct tsch_link *curr_backup = NULL; /* Keep a back link in case the current link
turns out useless when the time comes. For instance, for a Tx-only link, if there is
no outgoing packet in queue. In that case, run the backup link instead. The backup link
must have Rx flag set. */
if(!tsch_is_locked()) {
struct tsch_slotframe *sf = list_head(slotframe_list);
/* For each slotframe, look for the earliest occurring link */
while(sf != NULL) {
/* Get timeslot from ASN, given the slotframe length */
uint16_t timeslot = ASN_MOD(*asn, sf->size);
struct tsch_link *l = list_head(sf->links_list);
while(l != NULL) {
uint16_t time_to_timeslot =
l->timeslot > timeslot ?
l->timeslot - timeslot :
sf->size.val + l->timeslot - timeslot;
if(curr_best == NULL || time_to_timeslot < time_to_curr_best) {
time_to_curr_best = time_to_timeslot;
curr_best = l;
curr_backup = NULL;
} else if(time_to_timeslot == time_to_curr_best) {
struct tsch_link *new_best = NULL;
/* Two links are overlapping, we need to select one of them.
* By standard: prioritize Tx links first, second by lowest handle */
if((curr_best->link_options & LINK_OPTION_TX) == (l->link_options & LINK_OPTION_TX)) {
/* Both or neither links have Tx, select the one with lowest handle */
if(l->slotframe_handle < curr_best->slotframe_handle) {
new_best = l;
}
} else {
/* Select the link that has the Tx option */
if(l->link_options & LINK_OPTION_TX) {
new_best = l;
}
}
/* Maintain backup_link */
if(curr_backup == NULL) {
/* Check if 'l' best can be used as backup */
if(new_best != l && (l->link_options & LINK_OPTION_RX)) { /* Does 'l' have Rx flag? */
curr_backup = l;
}
/* Check if curr_best can be used as backup */
if(new_best != curr_best && (curr_best->link_options & LINK_OPTION_RX)) { /* Does curr_best have Rx flag? */
curr_backup = curr_best;
}
}
/* Maintain curr_best */
if(new_best != NULL) {
curr_best = new_best;
}
}
l = list_item_next(l);
}
sf = list_item_next(sf);
}
if(time_offset != NULL) {
*time_offset = time_to_curr_best;
}
}
if(backup_link != NULL) {
*backup_link = curr_backup;
}
return curr_best;
}
/*---------------------------------------------------------------------------*/
/* Module initialization, call only once at startup. Returns 1 is success, 0 if failure. */
int
tsch_schedule_init(void)
{
if(tsch_get_lock()) {
memb_init(&link_memb);
memb_init(&slotframe_memb);
list_init(slotframe_list);
tsch_release_lock();
return 1;
} else {
return 0;
}
}
/*---------------------------------------------------------------------------*/
/* Create a 6TiSCH minimal schedule */
void
tsch_schedule_create_minimal(void)
{
struct tsch_slotframe *sf_min;
/* First, empty current schedule */
tsch_schedule_remove_all_slotframes();
/* Build 6TiSCH minimal schedule.
* We pick a slotframe length of TSCH_SCHEDULE_DEFAULT_LENGTH */
sf_min = tsch_schedule_add_slotframe(0, TSCH_SCHEDULE_DEFAULT_LENGTH);
/* Add a single Tx|Rx|Shared slot using broadcast address (i.e. usable for unicast and broadcast).
* We set the link type to advertising, which is not compliant with 6TiSCH minimal schedule
* but is required according to 802.15.4e if also used for EB transmission.
* Timeslot: 0, channel offset: 0. */
tsch_schedule_add_link(sf_min,
LINK_OPTION_RX | LINK_OPTION_TX | LINK_OPTION_SHARED | LINK_OPTION_TIME_KEEPING,
LINK_TYPE_ADVERTISING, &tsch_broadcast_address,
0, 0);
}
/*---------------------------------------------------------------------------*/
/* Prints out the current schedule (all slotframes and links) */
void
tsch_schedule_print(void)
{
if(!tsch_is_locked()) {
struct tsch_slotframe *sf = list_head(slotframe_list);
printf("Schedule: slotframe list\n");
while(sf != NULL) {
struct tsch_link *l = list_head(sf->links_list);
printf("[Slotframe] Handle %u, size %u\n", sf->handle, sf->size.val);
printf("List of links:\n");
while(l != NULL) {
printf("[Link] Options %02x, type %u, timeslot %u, channel offset %u, address %u\n",
l->link_options, l->link_type, l->timeslot, l->channel_offset, l->addr.u8[7]);
l = list_item_next(l);
}
sf = list_item_next(sf);
}
printf("Schedule: end of slotframe list\n");
}
}
/*---------------------------------------------------------------------------*/

View file

@ -0,0 +1,164 @@
/*
* Copyright (c) 2014, SICS Swedish ICT.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the Contiki operating system.
*
*/
#ifndef __TSCH_SCHEDULE_H__
#define __TSCH_SCHEDULE_H__
/********** Includes **********/
#include "contiki.h"
#include "lib/list.h"
#include "net/mac/tsch/tsch-private.h"
#include "net/mac/tsch/tsch-queue.h"
#include "net/mac/tsch/tsch-slot-operation.h"
#include "net/linkaddr.h"
/******** Configuration *******/
/* Initializes TSCH with a 6TiSCH minimal schedule */
#ifdef TSCH_SCHEDULE_CONF_WITH_6TISCH_MINIMAL
#define TSCH_SCHEDULE_WITH_6TISCH_MINIMAL TSCH_SCHEDULE_CONF_WITH_6TISCH_MINIMAL
#else
#define TSCH_SCHEDULE_WITH_6TISCH_MINIMAL 1
#endif
/* 6TiSCH Minimal schedule slotframe length */
#ifdef TSCH_SCHEDULE_CONF_DEFAULT_LENGTH
#define TSCH_SCHEDULE_DEFAULT_LENGTH TSCH_SCHEDULE_CONF_DEFAULT_LENGTH
#else
#define TSCH_SCHEDULE_DEFAULT_LENGTH 7
#endif
/* Max number of TSCH slotframes */
#ifdef TSCH_SCHEDULE_CONF_MAX_SLOTFRAMES
#define TSCH_SCHEDULE_MAX_SLOTFRAMES TSCH_SCHEDULE_CONF_MAX_SLOTFRAMES
#else
#define TSCH_SCHEDULE_MAX_SLOTFRAMES 4
#endif
/* Max number of links */
#ifdef TSCH_SCHEDULE_CONF_MAX_LINKS
#define TSCH_SCHEDULE_MAX_LINKS TSCH_SCHEDULE_CONF_MAX_LINKS
#else
#define TSCH_SCHEDULE_MAX_LINKS 32
#endif
/********** Constants *********/
/* Link options */
#define LINK_OPTION_TX 1
#define LINK_OPTION_RX 2
#define LINK_OPTION_SHARED 4
#define LINK_OPTION_TIME_KEEPING 8
/************ Types ***********/
/* 802.15.4e link types.
* LINK_TYPE_ADVERTISING_ONLY is an extra one: for EB-only links. */
enum link_type { LINK_TYPE_NORMAL, LINK_TYPE_ADVERTISING, LINK_TYPE_ADVERTISING_ONLY };
struct tsch_link {
/* Links are stored as a list: "next" must be the first field */
struct tsch_link *next;
/* Unique identifier */
uint16_t handle;
/* MAC address of neighbor */
linkaddr_t addr;
/* Slotframe identifier */
uint16_t slotframe_handle;
/* Identifier of Slotframe to which this link belongs
* Unused. */
/* uint8_t handle; */
/* Timeslot for this link */
uint16_t timeslot;
/* Channel offset for this link */
uint16_t channel_offset;
/* A bit string that defines
* b0 = Transmit, b1 = Receive, b2 = Shared, b3 = Timekeeping, b4 = reserved */
uint8_t link_options;
/* Type of link. NORMAL = 0. ADVERTISING = 1, and indicates
the link may be used to send an Enhanced beacon. */
enum link_type link_type;
/* Any other data for upper layers */
void *data;
};
struct tsch_slotframe {
/* Slotframes are stored as a list: "next" must be the first field */
struct tsch_slotframe *next;
/* Unique identifier */
uint16_t handle;
/* Number of timeslots in the slotframe.
* Stored as struct asn_divisor_t because we often need ASN%size */
struct asn_divisor_t size;
/* List of links belonging to this slotframe */
LIST_STRUCT(links_list);
};
/********** Functions *********/
/* Module initialization, call only once at startup. Returns 1 is success, 0 if failure. */
int tsch_schedule_init(void);
/* Create a 6TiSCH minimal schedule */
void tsch_schedule_create_minimal(void);
/* Prints out the current schedule (all slotframes and links) */
void tsch_schedule_print(void);
/* Adds and returns a slotframe (NULL if failure) */
struct tsch_slotframe *tsch_schedule_add_slotframe(uint16_t handle, uint16_t size);
/* Looks for a slotframe from a handle */
struct tsch_slotframe *tsch_schedule_get_slotframe_by_handle(uint16_t handle);
/* Removes a slotframe Return 1 if success, 0 if failure */
int tsch_schedule_remove_slotframe(struct tsch_slotframe *slotframe);
/* Removes all slotframes, resulting in an empty schedule */
int tsch_schedule_remove_all_slotframes(void);
/* Returns next slotframe */
struct tsch_slotframe *tsch_schedule_slotframes_next(struct tsch_slotframe *sf);
/* Adds a link to a slotframe, return a pointer to it (NULL if failure) */
struct tsch_link *tsch_schedule_add_link(struct tsch_slotframe *slotframe,
uint8_t link_options, enum link_type link_type, const linkaddr_t *address,
uint16_t timeslot, uint16_t channel_offset);
/* Looks for a link from a handle */
struct tsch_link *tsch_schedule_get_link_by_handle(uint16_t handle);
/* Looks within a slotframe for a link with a given timeslot */
struct tsch_link *tsch_schedule_get_link_by_timeslot(struct tsch_slotframe *slotframe, uint16_t timeslot);
/* Removes a link. Return 1 if success, 0 if failure */
int tsch_schedule_remove_link(struct tsch_slotframe *slotframe, struct tsch_link *l);
/* Removes a link from slotframe and timeslot. Return a 1 if success, 0 if failure */
int tsch_schedule_remove_link_by_timeslot(struct tsch_slotframe *slotframe, uint16_t timeslot);
/* Returns the next active link after a given ASN, and a backup link (for the same ASN, with Rx flag) */
struct tsch_link * tsch_schedule_get_next_active_link(struct asn_t *asn, uint16_t *time_offset,
struct tsch_link **backup_link);
#endif /* __TSCH_SCHEDULE_H__ */

View file

@ -0,0 +1,285 @@
/*
* Copyright (c) 2014, SICS Swedish ICT.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the Contiki operating system.
*
*/
/**
* \file
* TSCH security
* \author
* Simon Duquennoy <simonduq@sics.se>
*/
#include "contiki.h"
#include "net/mac/tsch/tsch.h"
#include "net/mac/tsch/tsch-packet.h"
#include "net/mac/tsch/tsch-private.h"
#include "net/mac/tsch/tsch-schedule.h"
#include "net/mac/tsch/tsch-security.h"
#include "net/mac/tsch/tsch-log.h"
#include "net/mac/frame802154.h"
#include "net/mac/framer-802154.h"
#include "net/netstack.h"
#include "net/packetbuf.h"
#include "lib/ccm-star.h"
#include "lib/aes-128.h"
#include <stdio.h>
#include <string.h>
#if TSCH_LOG_LEVEL >= 1
#define DEBUG DEBUG_PRINT
#else /* TSCH_LOG_LEVEL */
#define DEBUG DEBUG_NONE
#endif /* TSCH_LOG_LEVEL */
#include "net/ip/uip-debug.h"
/* The two keys K1 and K2 from 6TiSCH minimal configuration
* K1: well-known, used for EBs
* K2: secret, used for data and ACK
* */
static aes_key keys[] = {
TSCH_SECURITY_K1,
TSCH_SECURITY_K2
};
#define N_KEYS (sizeof(keys) / sizeof(aes_key))
/*---------------------------------------------------------------------------*/
static void
aead(const uint8_t *nonce,
uint8_t *m, uint8_t m_len,
const uint8_t *a, uint8_t a_len,
uint8_t *result, uint8_t mic_len,
int forward)
{
if(!forward) {
/* decrypt */
CCM_STAR.ctr(m, m_len, nonce);
}
CCM_STAR.mic(
(const uint8_t *)m, m_len,
nonce,
a, a_len,
result,
mic_len);
if(forward) {
/* encrypt */
CCM_STAR.ctr(m, m_len, nonce);
}
}
/*---------------------------------------------------------------------------*/
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]);
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]);
aead(nonce,
(uint8_t *)hdr + a_len, m_len,
(uint8_t *)hdr, a_len,
generated_mic, mic_len, 0
);
if(mic_len > 0 && memcmp(generated_mic, hdr + hdrlen + datalen, mic_len) != 0) {
return 0;
} else {
return 1;
}
}

View file

@ -0,0 +1,127 @@
/*
* Copyright (c) 2014, SICS Swedish ICT.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the Contiki operating system.
*
*/
#ifndef __TSCH_SECURITY_H__
#define __TSCH_SECURITY_H__
/********** Includes **********/
#include "contiki.h"
#include "net/mac/tsch/tsch-asn.h"
#include "net/mac/tsch/tsch-private.h"
#include "net/mac/frame802154.h"
#include "net/mac/frame802154e-ie.h"
/******** Configuration *******/
/* To enable TSCH security:
* - set LLSEC802154_CONF_SECURITY_LEVEL
* - set LLSEC802154_CONF_USES_EXPLICIT_KEYS
* - unset LLSEC802154_CONF_USES_FRAME_COUNTER
* */
#define TSCH_SECURITY_ENABLED (LLSEC802154_CONF_SECURITY_LEVEL != 0)
#if TSCH_SECURITY_ENABLED && !LLSEC802154_CONF_USES_EXPLICIT_KEYS
#error TSCH_SECURITY_ENABLED set but LLSEC802154_CONF_USES_EXPLICIT_KEYS unset
#endif /* TSCH_SECURITY_ENABLED */
#if TSCH_SECURITY_ENABLED && LLSEC802154_CONF_USES_FRAME_COUNTER
#error TSCH_SECURITY_ENABLED set but LLSEC802154_CONF_USES_FRAME_COUNTER set
#endif /* TSCH_SECURITY_ENABLED */
/* K1, defined in 6TiSCH minimal, is well-known (offers no security) and used for EBs only */
#ifdef TSCH_SECURITY_CONF_K1
#define TSCH_SECURITY_K1 TSCH_SECURITY_CONF_K1
#else /* TSCH_SECURITY_CONF_K1 */
#define TSCH_SECURITY_K1 { 0x36, 0x54, 0x69, 0x53, 0x43, 0x48, 0x20, 0x6D, 0x69, 0x6E, 0x69, 0x6D, 0x61, 0x6C, 0x31, 0x35 }
#endif /* TSCH_SECURITY_CONF_K1 */
/* K2, defined in 6TiSCH minimal, is used for all but EB traffic */
#ifdef TSCH_SECURITY_CONF_K2
#define TSCH_SECURITY_K2 TSCH_SECURITY_CONF_K2
#else /* TSCH_SECURITY_CONF_K2 */
#define TSCH_SECURITY_K2 { 0xde, 0xad, 0xbe, 0xef, 0xfa, 0xce, 0xca, 0xfe, 0xde, 0xad, 0xbe, 0xef, 0xfa, 0xce, 0xca, 0xfe }
#endif /* TSCH_SECURITY_CONF_K2 */
/* Key used for EBs */
#ifdef TSCH_SECURITY_CONF_KEY_INDEX_EB
#define TSCH_SECURITY_KEY_INDEX_EB TSCH_SECURITY_CONF_KEY_INDEX_EB
#else
#define TSCH_SECURITY_KEY_INDEX_EB 1 /* Use K1 as per 6TiSCH minimal */
#endif
/* Security level for EBs */
#ifdef TSCH_SECURITY_CONF_SEC_LEVEL_EB
#define TSCH_SECURITY_KEY_SEC_LEVEL_EB TSCH_SECURITY_CONF_SEC_LEVEL_EB
#else
#define TSCH_SECURITY_KEY_SEC_LEVEL_EB 1 /* MIC-32, as per 6TiSCH minimal */
#endif
/* Key used for ACK */
#ifdef TSCH_SECURITY_CONF_KEY_INDEX_ACK
#define TSCH_SECURITY_KEY_INDEX_ACK TSCH_SECURITY_CONF_KEY_INDEX_ACK
#else
#define TSCH_SECURITY_KEY_INDEX_ACK 2 /* Use K2 as per 6TiSCH minimal */
#endif
/* Security level for ACKs */
#ifdef TSCH_SECURITY_CONF_SEC_LEVEL_ACK
#define TSCH_SECURITY_KEY_SEC_LEVEL_ACK TSCH_SECURITY_CONF_SEC_LEVEL_ACK
#else
#define TSCH_SECURITY_KEY_SEC_LEVEL_ACK 5 /* Encryption + MIC-32, as per 6TiSCH minimal */
#endif
/* Key used for Other (Data, Cmd) */
#ifdef TSCH_SECURITY_CONF_KEY_INDEX_OTHER
#define TSCH_SECURITY_KEY_INDEX_OTHER TSCH_SECURITY_CONF_KEY_INDEX_OTHER
#else
#define TSCH_SECURITY_KEY_INDEX_OTHER 2 /* Use K2 as per 6TiSCH minimal */
#endif
/* Security level for Other (Data, Cmd) */
#ifdef TSCH_SECURITY_CONF_SEC_LEVEL_OTHER
#define TSCH_SECURITY_KEY_SEC_LEVEL_OTHER TSCH_SECURITY_CONF_SEC_LEVEL_OTHER
#else
#define TSCH_SECURITY_KEY_SEC_LEVEL_OTHER 5 /* Encryption + MIC-32, as per 6TiSCH minimal */
#endif
/************ Types ***********/
typedef uint8_t aes_key[16];
/********** Functions *********/
int tsch_security_mic_len(const frame802154_t *frame);
int tsch_security_secure_frame(uint8_t *hdr, uint8_t *outbuf,
int hdrlen, int datalen, struct asn_t *asn);
int tsch_security_parse_frame(const uint8_t *hdr, int hdrlen, int datalen,
const frame802154_t *frame, const linkaddr_t *sender, struct asn_t *asn);
#endif /* __TSCH_SECURITY_H__ */

View file

@ -0,0 +1,968 @@
/*
* Copyright (c) 2015, SICS Swedish ICT.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the Contiki operating system.
*
*/
/**
* \file
* TSCH slot operation implementation, running from interrupt.
* \author
* Simon Duquennoy <simonduq@sics.se>
* Beshr Al Nahas <beshr@sics.se>
*
*/
#include "contiki.h"
#include "dev/radio.h"
#include "net/netstack.h"
#include "net/packetbuf.h"
#include "net/queuebuf.h"
#include "net/mac/framer-802154.h"
#include "net/mac/tsch/tsch.h"
#include "net/mac/tsch/tsch-slot-operation.h"
#include "net/mac/tsch/tsch-queue.h"
#include "net/mac/tsch/tsch-private.h"
#include "net/mac/tsch/tsch-log.h"
#include "net/mac/tsch/tsch-packet.h"
#include "net/mac/tsch/tsch-security.h"
#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_QUEUE_NUM_PER_NEIGHBOR 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.52 uSec) */
static int32_t drift_correction = 0;
static struct tsch_neighbor *drift_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", 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 non-zero return value signals to tsch_slot_operation a missed deadline.
* If conditional: schedule only if the deadline is not missed, else busy wait.
* If not conditional: schedule regardless of deadline miss. */
static uint8_t
tsch_schedule_slot_operation(struct rtimer *tm, rtimer_clock_t ref_time, rtimer_clock_t offset, int conditional, const char *str)
{
rtimer_clock_t now = RTIMER_NOW();
int r;
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-%d %s %d %d",
conditional, str,
(int)(now-ref_time), (int)offset);
);
if(conditional) {
BUSYWAIT_UNTIL_ABS(0, ref_time, 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 */
#define TSCH_SCHEDULE_AND_YIELD(pt, tm, ref_time, offset, str) \
do { \
if(tsch_schedule_slot_operation(tm, ref_time, offset, 1, str)) { \
PT_YIELD(pt); \
} \
} 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) {
/* is this a data packet? */
static uint8_t is_data;
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;
is_data = ((((uint8_t *)(packet))[0]) & 7) == FRAME802154_DATAFRAME;
/* read seqno from payload */
seqno = ((uint8_t *)(packet))[2];
/* if this is an EB, then update its Sync-IE */
if(current_neighbor == n_eb) {
packet_ready = tsch_packet_update_eb(packet, packet_len, current_packet->tsch_sync_ie_offset);
} else {
packet_ready = 1;
}
#if TSCH_SECURITY_ENABLED
if(tsch_is_pan_secured) {
/* If we are going to encrypt, we need to generate the output in a separate buffer and keep
* the original untouched. This is to allow for future retransmissions. */
int with_encryption = queuebuf_attr(current_packet->qb, PACKETBUF_ATTR_SECURITY_LEVEL) & 0x4;
packet_len += tsch_security_secure_frame(packet, with_encryption ? encrypted_packet : packet, current_packet->header_len,
packet_len - current_packet->header_len, &current_asn);
if(with_encryption) {
packet = encrypted_packet;
}
}
#endif /* TSCH_SECURITY_ENABLED */
/* prepare packet to send: copy to radio buffer */
if(packet_ready && NETSTACK_RADIO.prepare(packet, packet_len) == 0) { /* 0 means success */
static rtimer_clock_t tx_duration;
#if CCA_ENABLED
cca_status = 1;
/* delay before CCA */
TSCH_SCHEDULE_AND_YIELD(pt, t, current_slot_start, TS_CCA_OFFSET, "cca");
TSCH_DEBUG_TX_EVENT();
NETSTACK_RADIO.on();
/* CCA */
BUSYWAIT_UNTIL_ABS(!(cca_status |= NETSTACK_RADIO.channel_clear()),
current_slot_start, TS_CCA_OFFSET + TS_CCA);
TSCH_DEBUG_TX_EVENT();
/* there is not enough time to turn radio off */
/* NETSTACK_RADIO.off(); */
if(cca_status == 0) {
mac_tx_status = MAC_TX_COLLISION;
} else
#endif /* CCA_ENABLED */
{
/* delay before TX */
TSCH_SCHEDULE_AND_YIELD(pt, t, current_slot_start, tsch_timing[tsch_ts_tx_offset] - RADIO_DELAY_BEFORE_TX, "TxBeforeTx");
TSCH_DEBUG_TX_EVENT();
/* send packet already in radio tx buffer */
mac_tx_status = NETSTACK_RADIO.transmit(packet_len);
/* Save tx timestamp */
tx_start_time = current_slot_start + tsch_timing[tsch_ts_tx_offset];
/* calculate TX duration based on sent packet len */
tx_duration = TSCH_PACKET_DURATION(packet_len);
/* limit tx_time to its max value */
tx_duration = MIN(tx_duration, tsch_timing[tsch_ts_max_tx]);
/* turn tadio off -- will turn on again to wait for ACK if needed */
NETSTACK_RADIO.off();
if(mac_tx_status == RADIO_TX_OK) {
if(!is_broadcast) {
uint8_t ackbuf[TSCH_PACKET_MAX_LEN];
int ack_len;
rtimer_clock_t ack_start_time;
int is_time_source;
radio_value_t radio_rx_mode;
struct ieee802154_ies ack_ies;
uint8_t ack_hdrlen;
frame802154_t frame;
/* Entering promiscuous mode so that the radio accepts the enhanced ACK */
NETSTACK_RADIO.get_value(RADIO_PARAM_RX_MODE, &radio_rx_mode);
NETSTACK_RADIO.set_value(RADIO_PARAM_RX_MODE, radio_rx_mode & (~RADIO_RX_MODE_ADDRESS_FILTER));
/* Unicast: wait for ack after tx: sleep until ack time */
TSCH_SCHEDULE_AND_YIELD(pt, t, current_slot_start,
tsch_timing[tsch_ts_tx_offset] + tx_duration + tsch_timing[tsch_ts_rx_ack_delay] - RADIO_DELAY_BEFORE_RX, "TxBeforeAck");
TSCH_DEBUG_TX_EVENT();
NETSTACK_RADIO.on();
/* Wait for ACK to come */
BUSYWAIT_UNTIL_ABS(NETSTACK_RADIO.receiving_packet(),
tx_start_time, tx_duration + tsch_timing[tsch_ts_rx_ack_delay] + tsch_timing[tsch_ts_ack_wait]);
TSCH_DEBUG_TX_EVENT();
ack_start_time = RTIMER_NOW();
/* Wait for ACK to finish */
BUSYWAIT_UNTIL_ABS(!NETSTACK_RADIO.receiving_packet(),
ack_start_time, tsch_timing[tsch_ts_max_ack]);
TSCH_DEBUG_TX_EVENT();
NETSTACK_RADIO.off();
/* Leaving promiscuous mode */
NETSTACK_RADIO.get_value(RADIO_PARAM_RX_MODE, &radio_rx_mode);
NETSTACK_RADIO.set_value(RADIO_PARAM_RX_MODE, radio_rx_mode | RADIO_RX_MODE_ADDRESS_FILTER);
/* Read ack frame */
ack_len = NETSTACK_RADIO.read((void *)ackbuf, sizeof(ackbuf));
is_time_source = 0;
/* The radio driver should return 0 if no valid packets are in the rx buffer */
if(ack_len > 0) {
is_time_source = current_neighbor != NULL && current_neighbor->is_time_source;
if(tsch_packet_parse_eack(ackbuf, ack_len, seqno,
&frame, &ack_ies, &ack_hdrlen) == 0) {
ack_len = 0;
}
#if TSCH_SECURITY_ENABLED
if(ack_len != 0) {
if(!tsch_security_parse_frame(ackbuf, ack_hdrlen, ack_len - ack_hdrlen - tsch_security_mic_len(&frame),
&frame, &current_neighbor->addr, &current_asn)) {
TSCH_LOG_ADD(tsch_log_message,
snprintf(log->message, sizeof(log->message),
"!failed to authenticate ACK"));
ack_len = 0;
}
} else {
TSCH_LOG_ADD(tsch_log_message,
snprintf(log->message, sizeof(log->message),
"!failed to parse ACK"));
}
#endif /* TSCH_SECURITY_ENABLED */
}
if(ack_len != 0) {
if(is_time_source) {
int32_t eack_time_correction = US_TO_RTIMERTICKS(ack_ies.ie_time_correction);
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);
);
}
drift_neighbor = current_neighbor;
/* 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 = drift_neighbor != NULL;
log->tx.is_data = is_data;
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;
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();
if(!NETSTACK_RADIO.receiving_packet()) {
/* Check if receiving within guard time */
BUSYWAIT_UNTIL_ABS(NETSTACK_RADIO.receiving_packet(),
current_slot_start, tsch_timing[tsch_ts_rx_offset] + tsch_timing[tsch_ts_rx_wait]);
TSCH_DEBUG_RX_EVENT();
/* Save packet timestamp */
rx_start_time = RTIMER_NOW();
}
if(!NETSTACK_RADIO.receiving_packet() && !NETSTACK_RADIO.pending_packet()) {
NETSTACK_RADIO.off();
/* no packets on air */
} else {
/* Wait until packet is received, turn radio off */
BUSYWAIT_UNTIL_ABS(!NETSTACK_RADIO.receiving_packet(),
current_slot_start, tsch_timing[tsch_ts_rx_offset] + tsch_timing[tsch_ts_rx_wait] + tsch_timing[tsch_ts_max_tx]);
TSCH_DEBUG_RX_EVENT();
NETSTACK_RADIO.off();
#if TSCH_RESYNC_WITH_SFD_TIMESTAMPS
/* At the end of the reception, get an more accurate estimate of SFD arrival time */
NETSTACK_RADIO.get_object(RADIO_PARAM_LAST_PACKET_TIMESTAMP, &rx_start_time, sizeof(rtimer_clock_t));
#endif
if(NETSTACK_RADIO.pending_packet()) {
static int frame_valid;
static int header_len;
static frame802154_t frame;
radio_value_t radio_last_rssi;
NETSTACK_RADIO.get_value(RADIO_PARAM_LAST_RSSI, &radio_last_rssi);
/* Read packet */
current_input->len = NETSTACK_RADIO.read((void *)current_input->payload, TSCH_PACKET_MAX_LEN);
current_input->rx_asn = current_asn;
current_input->rssi = (signed)radio_last_rssi;
header_len = frame802154_parse((uint8_t *)current_input->payload, current_input->len, &frame);
frame_valid = header_len > 0 &&
frame802154_check_dest_panid(&frame) &&
frame802154_extract_linkaddr(&frame, &source_address, &destination_address);
packet_duration = TSCH_PACKET_DURATION(current_input->len);
#if TSCH_SECURITY_ENABLED
/* Decrypt and verify incoming frame */
if(frame_valid) {
if(tsch_security_parse_frame(
current_input->payload, header_len, current_input->len - header_len - tsch_security_mic_len(&frame),
&frame, &source_address, &current_asn)) {
current_input->len -= tsch_security_mic_len(&frame);
} else {
TSCH_LOG_ADD(tsch_log_message,
snprintf(log->message, sizeof(log->message),
"!failed to authenticate frame %u", current_input->len));
frame_valid = 0;
}
} else {
TSCH_LOG_ADD(tsch_log_message,
snprintf(log->message, sizeof(log->message),
"!failed to parse frame %u %u", header_len, current_input->len));
frame_valid = 0;
}
#endif /* TSCH_SECURITY_ENABLED */
if(frame_valid) {
if(linkaddr_cmp(&destination_address, &linkaddr_node_addr)
|| linkaddr_cmp(&destination_address, &linkaddr_null)) {
int do_nack = 0;
estimated_drift = ((int32_t)expected_rx_time - (int32_t)rx_start_time);
#ifdef TSCH_CALLBACK_DO_NACK
if(frame.fcf.ack_required) {
do_nack = TSCH_CALLBACK_DO_NACK(current_link,
&source_address, &destination_address);
}
#endif
if(frame.fcf.ack_required) {
static uint8_t ack_buf[TSCH_PACKET_MAX_LEN];
static int ack_len;
/* Build ACK frame */
ack_len = tsch_packet_create_eack(ack_buf, sizeof(ack_buf),
&source_address, frame.seq, (int16_t)RTIMERTICKS_TO_US(estimated_drift), do_nack);
#if TSCH_SECURITY_ENABLED
if(tsch_is_pan_secured) {
/* Secure ACK frame. There is only header and header IEs, therefore data len == 0. */
ack_len += tsch_security_secure_frame(ack_buf, ack_buf, ack_len, 0, &current_asn);
}
#endif /* TSCH_SECURITY_ENABLED */
/* Copy to radio buffer */
NETSTACK_RADIO.prepare((const void *)ack_buf, ack_len);
/* Wait for time to ACK and transmit ACK */
TSCH_SCHEDULE_AND_YIELD(pt, t, rx_start_time,
packet_duration + tsch_timing[tsch_ts_tx_ack_delay] - RADIO_DELAY_BEFORE_TX, "RxBeforeAck");
TSCH_DEBUG_RX_EVENT();
NETSTACK_RADIO.transmit(ack_len);
}
/* If the sender is a time source, proceed to clock drift compensation */
n = tsch_queue_get_nbr(&source_address);
if(n != NULL && n->is_time_source) {
/* Keep track of last sync time */
last_sync_asn = current_asn;
/* Save estimated drift */
drift_correction = -estimated_drift;
drift_neighbor = n;
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 = drift_neighbor != NULL;
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:%x:%x",
destination_address.u8[4], destination_address.u8[5],
destination_address.u8[6], destination_address.u8[7]);
);
}
/* Poll process for processing of pending input and logs */
process_poll(&tsch_pending_events_process);
}
}
}
if(input_queue_drop != 0) {
TSCH_LOG_ADD(tsch_log_message,
snprintf(log->message, sizeof(log->message),
"!queue full skipped %u", input_queue_drop);
);
input_queue_drop = 0;
}
}
TSCH_DEBUG_RX_EVENT();
PT_END(pt);
}
/*---------------------------------------------------------------------------*/
/* Protothread for slot operation, called from rtimer interrupt
* and scheduled from tsch_schedule_slot_operation */
static
PT_THREAD(tsch_slot_operation(struct rtimer *t, void *ptr))
{
TSCH_DEBUG_INTERRUPT();
PT_BEGIN(&slot_operation_pt);
/* Loop over all active slots */
while(tsch_is_associated) {
if(current_link == NULL || tsch_lock_requested) { /* Skip slot operation if there is no link
or if there is a pending request for getting the lock */
/* Issue a log whenever skipping a slot */
TSCH_LOG_ADD(tsch_log_message,
snprintf(log->message, sizeof(log->message),
"!skipped slot %u %u %u",
tsch_locked,
tsch_lock_requested,
current_link == NULL);
);
} else {
uint8_t current_channel;
TSCH_DEBUG_SLOT_START();
tsch_in_slot_operation = 1;
/* Get a packet ready to be sent */
current_packet = get_packet_and_neighbor_for_link(current_link, &current_neighbor);
/* There is no packet to send, and this link does not have Rx flag. Instead of doing
* nothing, switch to the backup link (has Rx flag) if any. */
if(current_packet == NULL && !(current_link->link_options & LINK_OPTION_RX) && backup_link != NULL) {
current_link = backup_link;
current_packet = get_packet_and_neighbor_for_link(current_link, &current_neighbor);
}
/* Hop channel */
current_channel = tsch_calculate_channel(&current_asn, current_link->channel_offset);
NETSTACK_RADIO.set_value(RADIO_PARAM_CHANNEL, current_channel);
/* Reset drift correction */
drift_correction = 0;
drift_neighbor = NULL;
/* 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));
);
tsch_disassociate();
} else {
/* backup of drift correction for printing debug messages */
/* int32_t drift_correction_backup = drift_correction; */
uint16_t timeslot_diff = 0;
rtimer_clock_t prev_slot_start;
/* Time to next wake up */
rtimer_clock_t time_to_next_active_slot;
/* Schedule next wakeup skipping slots if missed deadline */
do {
if(current_link != NULL
&& current_link->link_options & LINK_OPTION_TX
&& current_link->link_options & LINK_OPTION_SHARED) {
/* Decrement the backoff window for all neighbors able to transmit over
* this Tx, Shared link. */
tsch_queue_update_all_backoff_windows(&current_link->addr);
}
/* Get next active link */
current_link = tsch_schedule_get_next_active_link(&current_asn, &timeslot_diff, &backup_link);
if(current_link == NULL) {
/* There is no next link. Fall back to default
* behavior: wake up at the next slot. */
timeslot_diff = 1;
}
/* Update ASN */
ASN_INC(current_asn, timeslot_diff);
/* Time to next wake up */
time_to_next_active_slot = timeslot_diff * tsch_timing[tsch_ts_timeslot_length] + drift_correction;
drift_correction = 0;
drift_neighbor = NULL;
/* Update current slot start */
prev_slot_start = current_slot_start;
current_slot_start += time_to_next_active_slot;
} while(!tsch_schedule_slot_operation(t, prev_slot_start, time_to_next_active_slot, 1, "main"));
}
tsch_in_slot_operation = 0;
PT_YIELD(&slot_operation_pt);
}
PT_END(&slot_operation_pt);
}
/*---------------------------------------------------------------------------*/
/* Set global time before starting slot operation,
* with a rtimer time and an ASN */
void
tsch_slot_operation_start(void)
{
static struct rtimer slot_operation_timer;
rtimer_clock_t time_to_next_active_slot;
rtimer_clock_t prev_slot_start;
TSCH_DEBUG_INIT();
do {
uint16_t timeslot_diff;
/* Get next active link */
current_link = tsch_schedule_get_next_active_link(&current_asn, &timeslot_diff, &backup_link);
if(current_link == NULL) {
/* There is no next link. Fall back to default
* behavior: wake up at the next slot. */
timeslot_diff = 1;
}
/* Update ASN */
ASN_INC(current_asn, timeslot_diff);
/* Time to next wake up */
time_to_next_active_slot = timeslot_diff * tsch_timing[tsch_ts_timeslot_length];
/* Update current slot start */
prev_slot_start = current_slot_start;
current_slot_start += time_to_next_active_slot;
} while(!tsch_schedule_slot_operation(&slot_operation_timer, prev_slot_start, time_to_next_active_slot, 1, "association"));
}
/*---------------------------------------------------------------------------*/
/* Start actual slot operation */
void
tsch_slot_operation_sync(rtimer_clock_t next_slot_start,
struct asn_t *next_slot_asn)
{
current_slot_start = next_slot_start;
current_asn = *next_slot_asn;
last_sync_asn = current_asn;
current_link = NULL;
}
/*---------------------------------------------------------------------------*/

View file

@ -0,0 +1,118 @@
/*
* 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
#define TSCH_DEQUEUED_ARRAY_SIZE 16
#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
/* 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
/*********** 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__ */

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

File diff suppressed because it is too large Load diff

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

@ -0,0 +1,167 @@
/*
* Copyright (c) 2015, SICS Swedish ICT.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the Contiki operating system.
*
*/
#ifndef __TSCH_H__
#define __TSCH_H__
/********** Includes **********/
#include "contiki.h"
#include "net/mac/mac.h"
#include "net/mac/tsch/tsch-security.h"
/******** Configuration *******/
/* Max time before sending a unicast keep-alive message to the time source */
#ifdef TSCH_CONF_KEEPALIVE_TIMEOUT
#define TSCH_KEEPALIVE_TIMEOUT TSCH_CONF_KEEPALIVE_TIMEOUT
#else
/* Time to desynch assuming a drift of 40 PPM (80 PPM between two nodes) and guard time of +/-1ms: 12.5s. */
#define TSCH_KEEPALIVE_TIMEOUT (12 * CLOCK_SECOND)
#endif
/* Max time without synchronization before leaving the PAN */
#ifdef TSCH_CONF_DESYNC_THRESHOLD
#define TSCH_DESYNC_THRESHOLD TSCH_CONF_DESYNC_THRESHOLD
#else
#define TSCH_DESYNC_THRESHOLD (4 * TSCH_KEEPALIVE_TIMEOUT)
#endif
/* Period between two consecutive EBs */
#ifdef TSCH_CONF_EB_PERIOD
#define TSCH_EB_PERIOD TSCH_CONF_EB_PERIOD
#else
#define TSCH_EB_PERIOD (4 * CLOCK_SECOND)
#endif
/* Max acceptable join priority */
#ifdef TSCH_CONF_MAX_JOIN_PRIORITY
#define TSCH_MAX_JOIN_PRIORITY TSCH_CONF_MAX_JOIN_PRIORITY
#else
#define TSCH_MAX_JOIN_PRIORITY 32
#endif
/* Start TSCH automatically after init? If not, the upper layers
* must call NETSTACK_MAC.on() to start it. Useful when the
* application needs to control when the nodes are to start
* scanning or advertising.*/
#ifdef TSCH_CONF_AUTOSTART
#define TSCH_AUTOSTART TSCH_CONF_AUTOSTART
#else
#define TSCH_AUTOSTART 1
#endif
/* Join only secured networks? (discard EBs with security disabled) */
#ifdef TSCH_CONF_JOIN_SECURED_ONLY
#define TSCH_JOIN_SECURED_ONLY TSCH_CONF_JOIN_SECURED_ONLY
#else
/* By default, set if TSCH_SECURITY_ENABLED is also non-zero */
#define TSCH_JOIN_SECURED_ONLY TSCH_SECURITY_ENABLED
#endif
/* By default, join any PAN ID. Otherwise, wait for an EB from IEEE802154_PANID */
#ifdef TSCH_CONF_JOIN_MY_PANID_ONLY
#define TSCH_JOIN_MY_PANID_ONLY TSCH_CONF_JOIN_MY_PANID_ONLY
#else
#define TSCH_JOIN_MY_PANID_ONLY 0
#endif
/* The radio polling frequency (in Hz) during association process */
#ifdef TSCH_CONF_ASSOCIATION_POLL_FREQUENCY
#define TSCH_ASSOCIATION_POLL_FREQUENCY TSCH_CONF_ASSOCIATION_POLL_FREQUENCY
#else
#define TSCH_ASSOCIATION_POLL_FREQUENCY 100
#endif
/* When associating, check ASN against our own uptime (time in minutes)..
* Useful to force joining only with nodes started roughly at the same time.
* Set to the max number of minutes acceptable. */
#ifdef TSCH_CONF_CHECK_TIME_AT_ASSOCIATION
#define TSCH_CHECK_TIME_AT_ASSOCIATION TSCH_CONF_CHECK_TIME_AT_ASSOCIATION
#else
#define TSCH_CHECK_TIME_AT_ASSOCIATION 0
#endif
/* By default: initialize schedule from EB when associating, using the
* slotframe and links Information Element */
#ifdef TSCH_CONF_INIT_SCHEDULE_FROM_EB
#define TSCH_INIT_SCHEDULE_FROM_EB TSCH_CONF_INIT_SCHEDULE_FROM_EB
#else
#define TSCH_INIT_SCHEDULE_FROM_EB 1
#endif
/* An ad-hoc mechanism to have TSCH select its time source without the
* help of an upper-layer, simply by collecting statistics on received
* EBs and their join priority. Disabled by default as we recomment
* mapping the time source on the RPL preferred parent
* (via tsch_rpl_callback_parent_switch) */
#ifdef TSCH_CONF_AUTOSELECT_TIME_SOURCE
#define TSCH_AUTOSELECT_TIME_SOURCE TSCH_CONF_AUTOSELECT_TIME_SOURCE
#else
#define TSCH_AUTOSELECT_TIME_SOURCE 0
#endif /* TSCH_CONF_EB_AUTOSELECT */
/*********** Callbacks *********/
/* Called by TSCH when joining a network */
#ifdef TSCH_CALLBACK_JOINING_NETWORK
void TSCH_CALLBACK_JOINING_NETWORK();
#endif
/* Called by TSCH when leaving a network */
#ifdef TSCH_CALLBACK_LEAVING_NETWORK
void TSCH_CALLBACK_LEAVING_NETWORK();
#endif
/***** External Variables *****/
/* Are we coordinator of the TSCH network? */
extern int tsch_is_coordinator;
/* Are we associated to a TSCH network? */
extern int tsch_is_associated;
/* Is the PAN running link-layer security? */
extern int tsch_is_pan_secured;
/* The TSCH MAC driver */
extern const struct mac_driver tschmac_driver;
/********** Functions *********/
/* The the TSCH join priority */
void tsch_set_join_priority(uint8_t jp);
/* The the period at which EBs are sent */
void tsch_set_eb_period(uint32_t period);
/* Set the node as PAN coordinator */
void tsch_set_coordinator(int enable);
/* Set the pan as secured or not */
void tsch_set_pan_secured(int enable);
#endif /* __TSCH_H__ */

View file

@ -55,6 +55,7 @@
#include "contiki-conf.h"
#include "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