Added IEEE 802.15.4e TSCH (TimeSlotted Channel Hopping) MAC layer
This commit is contained in:
parent
1e0adbbf24
commit
b0f936263e
94
core/net/mac/nordc.c
Normal file
94
core/net/mac/nordc.c
Normal 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,
|
||||||
|
};
|
||||||
|
/*---------------------------------------------------------------------------*/
|
1
core/net/mac/tsch/Makefile.tsch
Normal file
1
core/net/mac/tsch/Makefile.tsch
Normal 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
201
core/net/mac/tsch/README.md
Normal 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
|
155
core/net/mac/tsch/tsch-log.c
Normal file
155
core/net/mac/tsch/tsch-log.c
Normal 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 */
|
138
core/net/mac/tsch/tsch-log.h
Normal file
138
core/net/mac/tsch/tsch-log.h
Normal 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__ */
|
413
core/net/mac/tsch/tsch-packet.c
Normal file
413
core/net/mac/tsch/tsch-packet.c
Normal 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;
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
104
core/net/mac/tsch/tsch-packet.h
Normal file
104
core/net/mac/tsch/tsch-packet.h
Normal 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__ */
|
475
core/net/mac/tsch/tsch-queue.c
Normal file
475
core/net/mac/tsch/tsch-queue.c
Normal 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);
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
177
core/net/mac/tsch/tsch-queue.h
Normal file
177
core/net/mac/tsch/tsch-queue.h
Normal 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__ */
|
107
core/net/mac/tsch/tsch-rpl.c
Normal file
107
core/net/mac/tsch/tsch-rpl.c
Normal 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 */
|
55
core/net/mac/tsch/tsch-rpl.h
Normal file
55
core/net/mac/tsch/tsch-rpl.h
Normal 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__ */
|
447
core/net/mac/tsch/tsch-schedule.c
Normal file
447
core/net/mac/tsch/tsch-schedule.c
Normal 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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
164
core/net/mac/tsch/tsch-schedule.h
Normal file
164
core/net/mac/tsch/tsch-schedule.h
Normal 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__ */
|
285
core/net/mac/tsch/tsch-security.c
Normal file
285
core/net/mac/tsch/tsch-security.c
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
127
core/net/mac/tsch/tsch-security.h
Normal file
127
core/net/mac/tsch/tsch-security.h
Normal 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__ */
|
968
core/net/mac/tsch/tsch-slot-operation.c
Normal file
968
core/net/mac/tsch/tsch-slot-operation.c
Normal 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, ¤t_asn);
|
||||||
|
if(with_encryption) {
|
||||||
|
packet = encrypted_packet;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* TSCH_SECURITY_ENABLED */
|
||||||
|
|
||||||
|
/* prepare packet to send: copy to radio buffer */
|
||||||
|
if(packet_ready && NETSTACK_RADIO.prepare(packet, packet_len) == 0) { /* 0 means success */
|
||||||
|
static rtimer_clock_t tx_duration;
|
||||||
|
|
||||||
|
#if CCA_ENABLED
|
||||||
|
cca_status = 1;
|
||||||
|
/* delay before CCA */
|
||||||
|
TSCH_SCHEDULE_AND_YIELD(pt, t, current_slot_start, TS_CCA_OFFSET, "cca");
|
||||||
|
TSCH_DEBUG_TX_EVENT();
|
||||||
|
NETSTACK_RADIO.on();
|
||||||
|
/* CCA */
|
||||||
|
BUSYWAIT_UNTIL_ABS(!(cca_status |= NETSTACK_RADIO.channel_clear()),
|
||||||
|
current_slot_start, TS_CCA_OFFSET + TS_CCA);
|
||||||
|
TSCH_DEBUG_TX_EVENT();
|
||||||
|
/* there is not enough time to turn radio off */
|
||||||
|
/* NETSTACK_RADIO.off(); */
|
||||||
|
if(cca_status == 0) {
|
||||||
|
mac_tx_status = MAC_TX_COLLISION;
|
||||||
|
} else
|
||||||
|
#endif /* CCA_ENABLED */
|
||||||
|
{
|
||||||
|
/* delay before TX */
|
||||||
|
TSCH_SCHEDULE_AND_YIELD(pt, t, current_slot_start, tsch_timing[tsch_ts_tx_offset] - RADIO_DELAY_BEFORE_TX, "TxBeforeTx");
|
||||||
|
TSCH_DEBUG_TX_EVENT();
|
||||||
|
/* send packet already in radio tx buffer */
|
||||||
|
mac_tx_status = NETSTACK_RADIO.transmit(packet_len);
|
||||||
|
/* Save tx timestamp */
|
||||||
|
tx_start_time = current_slot_start + tsch_timing[tsch_ts_tx_offset];
|
||||||
|
/* calculate TX duration based on sent packet len */
|
||||||
|
tx_duration = TSCH_PACKET_DURATION(packet_len);
|
||||||
|
/* limit tx_time to its max value */
|
||||||
|
tx_duration = MIN(tx_duration, tsch_timing[tsch_ts_max_tx]);
|
||||||
|
/* turn tadio off -- will turn on again to wait for ACK if needed */
|
||||||
|
NETSTACK_RADIO.off();
|
||||||
|
|
||||||
|
if(mac_tx_status == RADIO_TX_OK) {
|
||||||
|
if(!is_broadcast) {
|
||||||
|
uint8_t ackbuf[TSCH_PACKET_MAX_LEN];
|
||||||
|
int ack_len;
|
||||||
|
rtimer_clock_t ack_start_time;
|
||||||
|
int is_time_source;
|
||||||
|
radio_value_t radio_rx_mode;
|
||||||
|
struct ieee802154_ies ack_ies;
|
||||||
|
uint8_t ack_hdrlen;
|
||||||
|
frame802154_t frame;
|
||||||
|
|
||||||
|
/* Entering promiscuous mode so that the radio accepts the enhanced ACK */
|
||||||
|
NETSTACK_RADIO.get_value(RADIO_PARAM_RX_MODE, &radio_rx_mode);
|
||||||
|
NETSTACK_RADIO.set_value(RADIO_PARAM_RX_MODE, radio_rx_mode & (~RADIO_RX_MODE_ADDRESS_FILTER));
|
||||||
|
/* Unicast: wait for ack after tx: sleep until ack time */
|
||||||
|
TSCH_SCHEDULE_AND_YIELD(pt, t, current_slot_start,
|
||||||
|
tsch_timing[tsch_ts_tx_offset] + tx_duration + tsch_timing[tsch_ts_rx_ack_delay] - RADIO_DELAY_BEFORE_RX, "TxBeforeAck");
|
||||||
|
TSCH_DEBUG_TX_EVENT();
|
||||||
|
NETSTACK_RADIO.on();
|
||||||
|
/* Wait for ACK to come */
|
||||||
|
BUSYWAIT_UNTIL_ABS(NETSTACK_RADIO.receiving_packet(),
|
||||||
|
tx_start_time, tx_duration + tsch_timing[tsch_ts_rx_ack_delay] + tsch_timing[tsch_ts_ack_wait]);
|
||||||
|
TSCH_DEBUG_TX_EVENT();
|
||||||
|
|
||||||
|
ack_start_time = RTIMER_NOW();
|
||||||
|
|
||||||
|
/* Wait for ACK to finish */
|
||||||
|
BUSYWAIT_UNTIL_ABS(!NETSTACK_RADIO.receiving_packet(),
|
||||||
|
ack_start_time, tsch_timing[tsch_ts_max_ack]);
|
||||||
|
TSCH_DEBUG_TX_EVENT();
|
||||||
|
NETSTACK_RADIO.off();
|
||||||
|
|
||||||
|
/* Leaving promiscuous mode */
|
||||||
|
NETSTACK_RADIO.get_value(RADIO_PARAM_RX_MODE, &radio_rx_mode);
|
||||||
|
NETSTACK_RADIO.set_value(RADIO_PARAM_RX_MODE, radio_rx_mode | RADIO_RX_MODE_ADDRESS_FILTER);
|
||||||
|
|
||||||
|
/* Read ack frame */
|
||||||
|
ack_len = NETSTACK_RADIO.read((void *)ackbuf, sizeof(ackbuf));
|
||||||
|
|
||||||
|
is_time_source = 0;
|
||||||
|
/* The radio driver should return 0 if no valid packets are in the rx buffer */
|
||||||
|
if(ack_len > 0) {
|
||||||
|
is_time_source = current_neighbor != NULL && current_neighbor->is_time_source;
|
||||||
|
if(tsch_packet_parse_eack(ackbuf, ack_len, seqno,
|
||||||
|
&frame, &ack_ies, &ack_hdrlen) == 0) {
|
||||||
|
ack_len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if TSCH_SECURITY_ENABLED
|
||||||
|
if(ack_len != 0) {
|
||||||
|
if(!tsch_security_parse_frame(ackbuf, ack_hdrlen, ack_len - ack_hdrlen - tsch_security_mic_len(&frame),
|
||||||
|
&frame, ¤t_neighbor->addr, ¤t_asn)) {
|
||||||
|
TSCH_LOG_ADD(tsch_log_message,
|
||||||
|
snprintf(log->message, sizeof(log->message),
|
||||||
|
"!failed to authenticate ACK"));
|
||||||
|
ack_len = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TSCH_LOG_ADD(tsch_log_message,
|
||||||
|
snprintf(log->message, sizeof(log->message),
|
||||||
|
"!failed to parse ACK"));
|
||||||
|
}
|
||||||
|
#endif /* TSCH_SECURITY_ENABLED */
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ack_len != 0) {
|
||||||
|
if(is_time_source) {
|
||||||
|
int32_t eack_time_correction = US_TO_RTIMERTICKS(ack_ies.ie_time_correction);
|
||||||
|
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, ¤t_asn)) {
|
||||||
|
current_input->len -= tsch_security_mic_len(&frame);
|
||||||
|
} else {
|
||||||
|
TSCH_LOG_ADD(tsch_log_message,
|
||||||
|
snprintf(log->message, sizeof(log->message),
|
||||||
|
"!failed to authenticate frame %u", current_input->len));
|
||||||
|
frame_valid = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TSCH_LOG_ADD(tsch_log_message,
|
||||||
|
snprintf(log->message, sizeof(log->message),
|
||||||
|
"!failed to parse frame %u %u", header_len, current_input->len));
|
||||||
|
frame_valid = 0;
|
||||||
|
}
|
||||||
|
#endif /* TSCH_SECURITY_ENABLED */
|
||||||
|
|
||||||
|
if(frame_valid) {
|
||||||
|
if(linkaddr_cmp(&destination_address, &linkaddr_node_addr)
|
||||||
|
|| linkaddr_cmp(&destination_address, &linkaddr_null)) {
|
||||||
|
int do_nack = 0;
|
||||||
|
estimated_drift = ((int32_t)expected_rx_time - (int32_t)rx_start_time);
|
||||||
|
|
||||||
|
#ifdef TSCH_CALLBACK_DO_NACK
|
||||||
|
if(frame.fcf.ack_required) {
|
||||||
|
do_nack = TSCH_CALLBACK_DO_NACK(current_link,
|
||||||
|
&source_address, &destination_address);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if(frame.fcf.ack_required) {
|
||||||
|
static uint8_t ack_buf[TSCH_PACKET_MAX_LEN];
|
||||||
|
static int ack_len;
|
||||||
|
|
||||||
|
/* Build ACK frame */
|
||||||
|
ack_len = tsch_packet_create_eack(ack_buf, sizeof(ack_buf),
|
||||||
|
&source_address, frame.seq, (int16_t)RTIMERTICKS_TO_US(estimated_drift), do_nack);
|
||||||
|
|
||||||
|
#if TSCH_SECURITY_ENABLED
|
||||||
|
if(tsch_is_pan_secured) {
|
||||||
|
/* Secure ACK frame. There is only header and header IEs, therefore data len == 0. */
|
||||||
|
ack_len += tsch_security_secure_frame(ack_buf, ack_buf, ack_len, 0, ¤t_asn);
|
||||||
|
}
|
||||||
|
#endif /* TSCH_SECURITY_ENABLED */
|
||||||
|
|
||||||
|
/* Copy to radio buffer */
|
||||||
|
NETSTACK_RADIO.prepare((const void *)ack_buf, ack_len);
|
||||||
|
|
||||||
|
/* Wait for time to ACK and transmit ACK */
|
||||||
|
TSCH_SCHEDULE_AND_YIELD(pt, t, rx_start_time,
|
||||||
|
packet_duration + tsch_timing[tsch_ts_tx_ack_delay] - RADIO_DELAY_BEFORE_TX, "RxBeforeAck");
|
||||||
|
TSCH_DEBUG_RX_EVENT();
|
||||||
|
NETSTACK_RADIO.transmit(ack_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the sender is a time source, proceed to clock drift compensation */
|
||||||
|
n = tsch_queue_get_nbr(&source_address);
|
||||||
|
if(n != NULL && n->is_time_source) {
|
||||||
|
/* 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, ¤t_neighbor);
|
||||||
|
/* There is no packet to send, and this link does not have Rx flag. Instead of doing
|
||||||
|
* nothing, switch to the backup link (has Rx flag) if any. */
|
||||||
|
if(current_packet == NULL && !(current_link->link_options & LINK_OPTION_RX) && backup_link != NULL) {
|
||||||
|
current_link = backup_link;
|
||||||
|
current_packet = get_packet_and_neighbor_for_link(current_link, ¤t_neighbor);
|
||||||
|
}
|
||||||
|
/* Hop channel */
|
||||||
|
current_channel = tsch_calculate_channel(¤t_asn, current_link->channel_offset);
|
||||||
|
NETSTACK_RADIO.set_value(RADIO_PARAM_CHANNEL, current_channel);
|
||||||
|
/* Reset drift correction */
|
||||||
|
drift_correction = 0;
|
||||||
|
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(¤t_link->addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get next active link */
|
||||||
|
current_link = tsch_schedule_get_next_active_link(¤t_asn, ×lot_diff, &backup_link);
|
||||||
|
if(current_link == NULL) {
|
||||||
|
/* There is no next link. Fall back to default
|
||||||
|
* behavior: wake up at the next slot. */
|
||||||
|
timeslot_diff = 1;
|
||||||
|
}
|
||||||
|
/* Update ASN */
|
||||||
|
ASN_INC(current_asn, timeslot_diff);
|
||||||
|
/* Time to next wake up */
|
||||||
|
time_to_next_active_slot = timeslot_diff * tsch_timing[tsch_ts_timeslot_length] + drift_correction;
|
||||||
|
drift_correction = 0;
|
||||||
|
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(¤t_asn, ×lot_diff, &backup_link);
|
||||||
|
if(current_link == NULL) {
|
||||||
|
/* There is no next link. Fall back to default
|
||||||
|
* behavior: wake up at the next slot. */
|
||||||
|
timeslot_diff = 1;
|
||||||
|
}
|
||||||
|
/* Update ASN */
|
||||||
|
ASN_INC(current_asn, timeslot_diff);
|
||||||
|
/* Time to next wake up */
|
||||||
|
time_to_next_active_slot = timeslot_diff * tsch_timing[tsch_ts_timeslot_length];
|
||||||
|
/* Update current slot start */
|
||||||
|
prev_slot_start = current_slot_start;
|
||||||
|
current_slot_start += time_to_next_active_slot;
|
||||||
|
} while(!tsch_schedule_slot_operation(&slot_operation_timer, prev_slot_start, time_to_next_active_slot, 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;
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
118
core/net/mac/tsch/tsch-slot-operation.h
Normal file
118
core/net/mac/tsch/tsch-slot-operation.h
Normal 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
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
167
core/net/mac/tsch/tsch.h
Normal 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__ */
|
|
@ -55,6 +55,7 @@
|
||||||
#include "contiki-conf.h"
|
#include "contiki-conf.h"
|
||||||
#include "net/linkaddr.h"
|
#include "net/linkaddr.h"
|
||||||
#include "net/llsec/llsec802154.h"
|
#include "net/llsec/llsec802154.h"
|
||||||
|
#include "net/mac/tsch/tsch-conf.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief The size of the packetbuf, in bytes
|
* \brief The size of the packetbuf, in bytes
|
||||||
|
@ -313,6 +314,10 @@ enum {
|
||||||
PACKETBUF_ATTR_MAC_SEQNO,
|
PACKETBUF_ATTR_MAC_SEQNO,
|
||||||
PACKETBUF_ATTR_MAC_ACK,
|
PACKETBUF_ATTR_MAC_ACK,
|
||||||
PACKETBUF_ATTR_IS_CREATED_AND_SECURED,
|
PACKETBUF_ATTR_IS_CREATED_AND_SECURED,
|
||||||
|
#if TSCH_WITH_LINK_SELECTOR
|
||||||
|
PACKETBUF_ATTR_TSCH_SLOTFRAME,
|
||||||
|
PACKETBUF_ATTR_TSCH_TIMESLOT,
|
||||||
|
#endif /* TSCH_WITH_LINK_SELECTOR */
|
||||||
|
|
||||||
/* Scope 1 attributes: used between two neighbors only. */
|
/* Scope 1 attributes: used between two neighbors only. */
|
||||||
#if PACKETBUF_WITH_PACKET_TYPE
|
#if PACKETBUF_WITH_PACKET_TYPE
|
||||||
|
@ -329,14 +334,16 @@ enum {
|
||||||
PACKETBUF_ATTR_FRAME_TYPE,
|
PACKETBUF_ATTR_FRAME_TYPE,
|
||||||
#if LLSEC802154_SECURITY_LEVEL
|
#if LLSEC802154_SECURITY_LEVEL
|
||||||
PACKETBUF_ATTR_SECURITY_LEVEL,
|
PACKETBUF_ATTR_SECURITY_LEVEL,
|
||||||
|
#endif /* LLSEC802154_SECURITY_LEVEL */
|
||||||
|
#if LLSEC802154_USES_FRAME_COUNTER
|
||||||
PACKETBUF_ATTR_FRAME_COUNTER_BYTES_0_1,
|
PACKETBUF_ATTR_FRAME_COUNTER_BYTES_0_1,
|
||||||
PACKETBUF_ATTR_FRAME_COUNTER_BYTES_2_3,
|
PACKETBUF_ATTR_FRAME_COUNTER_BYTES_2_3,
|
||||||
|
#endif /* LLSEC802154_USES_FRAME_COUNTER */
|
||||||
#if LLSEC802154_USES_EXPLICIT_KEYS
|
#if LLSEC802154_USES_EXPLICIT_KEYS
|
||||||
PACKETBUF_ATTR_KEY_ID_MODE,
|
PACKETBUF_ATTR_KEY_ID_MODE,
|
||||||
PACKETBUF_ATTR_KEY_INDEX,
|
PACKETBUF_ATTR_KEY_INDEX,
|
||||||
PACKETBUF_ATTR_KEY_SOURCE_BYTES_0_1,
|
PACKETBUF_ATTR_KEY_SOURCE_BYTES_0_1,
|
||||||
#endif /* LLSEC802154_USES_EXPLICIT_KEYS */
|
#endif /* LLSEC802154_USES_EXPLICIT_KEYS */
|
||||||
#endif /* LLSEC802154_SECURITY_LEVEL */
|
|
||||||
|
|
||||||
/* Scope 2 attributes: used between end-to-end nodes. */
|
/* Scope 2 attributes: used between end-to-end nodes. */
|
||||||
#if NETSTACK_CONF_WITH_RIME
|
#if NETSTACK_CONF_WITH_RIME
|
||||||
|
@ -362,10 +369,15 @@ enum {
|
||||||
#if !LLSEC802154_SECURITY_LEVEL
|
#if !LLSEC802154_SECURITY_LEVEL
|
||||||
enum {
|
enum {
|
||||||
PACKETBUF_ATTR_SECURITY_LEVEL,
|
PACKETBUF_ATTR_SECURITY_LEVEL,
|
||||||
|
};
|
||||||
|
#endif /* LLSEC802154_SECURITY_LEVEL */
|
||||||
|
|
||||||
|
#if !LLSEC802154_USES_FRAME_COUNTER
|
||||||
|
enum {
|
||||||
PACKETBUF_ATTR_FRAME_COUNTER_BYTES_0_1,
|
PACKETBUF_ATTR_FRAME_COUNTER_BYTES_0_1,
|
||||||
PACKETBUF_ATTR_FRAME_COUNTER_BYTES_2_3
|
PACKETBUF_ATTR_FRAME_COUNTER_BYTES_2_3
|
||||||
};
|
};
|
||||||
#endif /* LLSEC802154_SECURITY_LEVEL */
|
#endif /* LLSEC802154_USES_FRAME_COUNTER */
|
||||||
|
|
||||||
/* Define surrogates when not using explicit keys */
|
/* Define surrogates when not using explicit keys */
|
||||||
#if !LLSEC802154_USES_EXPLICIT_KEYS
|
#if !LLSEC802154_USES_EXPLICIT_KEYS
|
||||||
|
|
Loading…
Reference in a new issue