1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

opendsme: add initial support

This commit is contained in:
Jose Alamos 2022-06-01 14:55:31 +02:00
parent efc0d3d3d9
commit d7b51c687b
No known key found for this signature in database
GPG Key ID: F483EB800EF89DD9
31 changed files with 2765 additions and 0 deletions

View File

@ -376,6 +376,7 @@ PSEUDOMODULES += newlib_nano
PSEUDOMODULES += nice
## @}
PSEUDOMODULES += nrf24l01p_ng_diagnostics
PSEUDOMODULES += opendsme
PSEUDOMODULES += openthread
PSEUDOMODULES += picolibc
PSEUDOMODULES += picolibc_stdout_buffered

46
pkg/opendsme/Kconfig Normal file
View File

@ -0,0 +1,46 @@
# Copyright (c) 2022 HAW Hamburg
#
# This file is subject to the terms and conditions of the GNU Lesser
# General Public License v2.1. See the file LICENSE in the top level
# directory for more details.
#
menuconfig KCONFIG_USEPKG_OPENDSME
bool "Configure openDSME"
help
Configure openDSME using Kconfig.
if KCONFIG_USEPKG_OPENDSME
config OPENDSME_MAX_NEIGHBOURS
int "Maximum number of DSME neighbours"
default 20
config OPENDSME_MAX_LOST_BEACONS
int "Maximum number of lost beacons before assuming the device desynchronized"
default 8
help
Sets the maximum number of lost beacons before the MAC triggers a
de-association procedure. Higher values are beneficial in noisy
environments, because the MAC will keep synchronization despite losing some
beacons. However, lower values are better for mobile nodes, because devices
may sinchronize faster to a new coordinator.
config OPENDSME_CAP_QUEUE_SIZE
int "DSME CAP queue size (for CSMA/CA transmissions)"
default 8
help
The CAP queue stores frames to be sent during the Contention Access Period
using CSMA-CA. Because the transmission delay of CSMA-CA is lower compared to
GTS transmissions, small values are preferred to reduce memory requirements.
config OPENDSME_CFP_QUEUE_SIZE
int "DSME CFP queue size (for GTS transmissions)"
default 22
help
The CFP queue stores frames to be sent during the Contention Free Period
using a dedicated GTS. In contrast to CSMA-CA transmissions, GTS transmission
take longer as a result of slot schedules. Therefore, the GTS queue should
have more capacity than the CAP queue (OPENDSME_CAP_QUEUE_SIZE).
endif # KCONFIG_USEPKG_OPENDSME

58
pkg/opendsme/Makefile Normal file
View File

@ -0,0 +1,58 @@
PKG_NAME=opendsme
PKG_URL=https://github.com/inetrg/openDSME.git
PKG_VERSION=b1969296d0fc9a1556ecbef7c0b01538dff3e10c
PKG_LICENSE=GPL
include $(RIOTBASE)/pkg/pkg.mk
OPENDSME_MODULES = \
opendsme_dsmelayer \
opendsme_acklayer \
opendsme_associationmanager \
opendsme_beaconmanager \
opendsme_caplayer \
opendsme_gtsmanager \
opendsme_messagedispatcher \
opendsme_messages \
opendsme_datastructures \
opendsme_mcps_sap \
opendsme_mlme_sap \
opendsme_pib \
opendsme_adaption_layer \
opendsme_adaption_layer_scheduling \
#
CPPFLAGS += -Wno-deprecated-copy -Wno-unused-parameter -Wno-error
# dsmeLayer
DIR_DSME_LAYER = dsmeLayer
DIR_opendsme_dsmelayer = $(DIR_DSME_LAYER)
DIR_opendsme_acklayer = $(DIR_DSME_LAYER)/ackLayer
DIR_opendsme_associationmanager = $(DIR_DSME_LAYER)/associationManager
DIR_opendsme_beaconmanager = $(DIR_DSME_LAYER)/beaconManager
DIR_opendsme_caplayer = $(DIR_DSME_LAYER)/capLayer
DIR_opendsme_gtsmanager = $(DIR_DSME_LAYER)/gtsManager
DIR_opendsme_messagedispatcher = $(DIR_DSME_LAYER)/messageDispatcher
DIR_opendsme_messages = $(DIR_DSME_LAYER)/messages
# MAC Services
DIR_MAC_SERVICES = mac_services
DIR_opendsme_datastructures = $(DIR_MAC_SERVICES)/dataStructures
DIR_opendsme_mcps_sap = $(DIR_MAC_SERVICES)/mcps_sap
DIR_opendsme_mlme_sap = $(DIR_MAC_SERVICES)/mlme_sap
DIR_opendsme_pib = $(DIR_MAC_SERVICES)/pib
# DSME Adoption Layer
DIR_DSME_ADAPTION_LAYER = dsmeAdaptionLayer
DIR_opendsme_adaption_layer = $(DIR_DSME_ADAPTION_LAYER)
DIR_opendsme_adaption_layer_scheduling = $(DIR_DSME_ADAPTION_LAYER)/scheduling
.PHONY: opendsme_%
export SRCXXEXT=cc
all: $(OPENDSME_MODULES)
opendsme_%:
$(QQ)"$(MAKE)" -C $(PKG_SOURCE_DIR)/$(DIR_$@) -f $(CURDIR)/Makefile.$@

40
pkg/opendsme/Makefile.dep Normal file
View File

@ -0,0 +1,40 @@
FEATURES_REQUIRED += cpp
# Contrib code of openDSME
USEMODULE += opendsme_riot_contrib
# Internal openDSME modules
USEMODULE += opendsme_dsmelayer
USEMODULE += opendsme_acklayer
USEMODULE += opendsme_associationmanager
USEMODULE += opendsme_beaconmanager
USEMODULE += opendsme_caplayer
USEMODULE += opendsme_gtsmanager
USEMODULE += opendsme_messagedispatcher
USEMODULE += opendsme_messages
USEMODULE += opendsme_datastructures
USEMODULE += opendsme_mcps_sap
USEMODULE += opendsme_mlme_sap
USEMODULE += opendsme_pib
# openDSME adaption layer modules
USEMODULE += opendsme_adaption_layer
USEMODULE += opendsme_adaption_layer_scheduling
# required RIOT modules
USEMODULE += luid
USEMODULE += gnrc
USEMODULE += gnrc_netif
USEMODULE += ieee802154
USEMODULE += ztimer_usec
USEMODULE += cpp11-compat
CXXEXFLAGS += -Wno-unused-parameter -Wno-pedantic -Wno-missing-field-initializers -Wno-unused-but-set-variable -Wno-maybe-uninitialized -Wno-unused-variable -Wno-reorder -Wno-address -Wno-sign-compare -Wno-unused-function
FEATURES_REQUIRED += cpp # basic C++ support
FEATURES_REQUIRED += libstdcpp # libstdc++ support (for #include <cstdio>)
# Disable Auto-ACK (not supported by openDSME)
CFLAGS += -DCONFIG_IEEE802154_AUTO_ACK_DISABLE=1
USEMODULE += random

View File

@ -0,0 +1,4 @@
INCLUDES += -I$(PKGDIRBASE)/opendsme
INCLUDES += -I$(RIOTBASE)/pkg/opendsme/include
DIRS += $(RIOTBASE)/pkg/opendsme/contrib

View File

@ -0,0 +1,3 @@
MODULE = opendsme_acklayer
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,3 @@
MODULE = opendsme_adaption_layer
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,7 @@
MODULE = opendsme_adaption_layer_scheduling
# Include TPS and StaticScheduling schedulers
NO_AUTO_SRC := 1
SRCXX += TPS.cc StaticScheduling.cc
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,3 @@
MODULE = opendsme_associationmanager
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,3 @@
MODULE = opendsme_beaconmanager
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,3 @@
MODULE = opendsme_caplayer
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,3 @@
MODULE = opendsme_datastructures
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,3 @@
MODULE = opendsme_dsmelayer
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,3 @@
MODULE = opendsme_gtsmanager
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,3 @@
MODULE = opendsme_mcps_sap
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,3 @@
MODULE = opendsme_messagedispatcher
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,3 @@
MODULE = opendsme_messages
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,3 @@
MODULE = opendsme_mlme_sap
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,3 @@
MODULE = opendsme_pib
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,161 @@
/*
* Copyright (C) 2022 HAW Hamburg
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @{
*
* @file
* @author José I. Álamos <jose.alamos@haw-hamburg.de>
*/
#include "assert.h"
#include "opendsme/DSMEMessage.h"
#include "net/gnrc/pktdump.h"
#include "net/gnrc.h"
#include "opendsme/opendsme.h"
namespace dsme {
void DSMEMessage::prependFrom(DSMEMessageElement *msg)
{
gnrc_pktsnip_t *head = gnrc_pktbuf_add(pkt, NULL,
msg->getSerializationLength(), GNRC_NETTYPE_UNDEF);
/* Hardcorded to O-QPSK */
DSME_ASSERT(msg->getSerializationLength() <= IEEE802154_FRAME_LEN_MAX);
DSME_ASSERT(head);
int res = gnrc_pktbuf_merge(head);
DSME_ASSERT(res == 0);
DSME_ASSERT(gnrc_pkt_len(head) <= IEEE802154_FRAME_LEN_MAX);
pkt = head;
assert(head);
Serializer s((uint8_t *)head->data, SERIALIZATION);
msg->serialize(s);
}
void DSMEMessage::decapsulateTo(DSMEMessageElement *me)
{
this->copyTo(me);
this->dropHdr(me->getSerializationLength());
}
void DSMEMessage::copyTo(DSMEMessageElement *msg)
{
Serializer s(this->getPayload(), DESERIALIZATION);
msg->serialize(s);
DSME_ASSERT(this->getPayload() + msg->getSerializationLength() == s.getData());
}
uint8_t DSMEMessage::getMPDUSymbols()
{
/* Not used by OpenDSME */
assert(false);
return 0;
}
int DSMEMessage::loadBuffer(size_t len)
{
int res = -ENOBUFS;
gnrc_pktsnip_t *pkt = gnrc_pktbuf_add(NULL, NULL, len, GNRC_NETTYPE_UNDEF);
DSME_ASSERT(len <= IEEE802154_FRAME_LEN_MAX);
if (pkt == NULL) {
DSME_ASSERT(false);
goto end;
}
this->pkt = pkt;
res = 0;
end:
return res;
}
int DSMEMessage::loadBuffer(iolist_t *pkt)
{
int res = -ENOBUFS;
if (pkt == NULL) {
DSME_ASSERT(false);
goto end;
}
this->pkt = (gnrc_pktsnip_t *)pkt;
res = 0;
end:
return res;
}
int DSMEMessage::dropHdr(size_t len)
{
gnrc_pktsnip_t *hdr = gnrc_pktbuf_mark(this->pkt, len, GNRC_NETTYPE_UNDEF);
if (!hdr) {
DSME_ASSERT(false);
return -EINVAL;
}
gnrc_pktbuf_remove_snip(pkt, hdr);
return 0;
}
void DSMEMessage::releaseMessage()
{
DSME_ASSERT(!free);
if (pkt) {
gnrc_pktbuf_release(pkt);
}
free = true;
delete this;
}
iolist_t *DSMEMessage::getIolPayload()
{
return (iolist_t *)pkt;
}
void DSMEMessage::clearMessage()
{
pkt = NULL;
free = false;
prepare();
}
void DSMEMessage::dispatchMessage()
{
DSME_ASSERT(!free);
uint16_t addr = getHeader().getSrcAddr().getShortAddress();
uint8_t _addr[IEEE802154_SHORT_ADDRESS_LEN];
_addr[0] = addr >> 8;
_addr[1] = addr & 0xFF;
uint16_t dst_addr = getHeader().getDestAddr().getShortAddress();
uint8_t _dst_addr[IEEE802154_SHORT_ADDRESS_LEN];
_dst_addr[0] = dst_addr >> 8;
_dst_addr[1] = dst_addr & 0xFF;
gnrc_pktsnip_t *netif_hdr = gnrc_netif_hdr_build((uint8_t *)_addr, IEEE802154_SHORT_ADDRESS_LEN,
(uint8_t *)_dst_addr,
IEEE802154_SHORT_ADDRESS_LEN);
size_t mhr_len = ieee802154_get_frame_hdr_len(static_cast<uint8_t *>(pkt->data));
pkt->type = CONFIG_OPENDSME_GNRC_PKTSNIP_TYPE;
gnrc_netif_hdr_t *hdr = static_cast<gnrc_netif_hdr_t *>(netif_hdr->data);
gnrc_netif_hdr_set_netif(hdr, this->netif);
pkt = gnrc_pkt_append(pkt, netif_hdr);
if (gnrc_netapi_dispatch_receive(CONFIG_OPENDSME_GNRC_PKTSNIP_TYPE, GNRC_NETREG_DEMUX_CTX_ALL,
pkt)) {
/* Pass packet to GNRC */
pkt = NULL;
}
releaseMessage();
}
}
/** @} */

View File

@ -0,0 +1,763 @@
/*
* Copyright (C) 2022 HAW Hamburg
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @{
*
* @file
* @author José I. Álamos <jose.alamos@haw-hamburg.de>
*/
#include "opendsme/opendsme.h"
#include "opendsme/DSMEPlatform.h"
#include "ztimer.h"
#include "iolist.h"
#include "event.h"
#include "event/thread.h"
#include "luid.h"
#include "dsmeAdaptionLayer/scheduling/TPS.h"
#include "dsmeAdaptionLayer/scheduling/StaticScheduling.h"
#include "board.h"
#define ENABLE_DEBUG 0
#include "debug.h"
/* Use default openDSME value for TPS scheduler alpha */
#define OPENDSME_TPS_ALPHA (0.1f)
/* Used for symbol counter calculation. Hardcoded to O-QPSK */
#define OPENDSME_TIMER_MASK (0xF)
#define OPENDSME_TIMER_OFFSET (4U)
namespace dsme {
/* The one and only openDSME instance */
DSMEPlatform *DSMEPlatform::instance = nullptr;
/********************* C functions *********************/
static void _cca_ev_handler(event_t *ev);
static void _acktimer_ev_handler(event_t *ev);
static void _acktimer_cb(void *arg);
static void _timer_ev_handler(event_t *ev);
static void _tx_done_handler(event_t *ev);
static void _rx_done_handler(event_t *ev);
static void _handle_rx_offload(event_t *ev);
static void _start_of_cfp_handler(event_t *ev);
/* Event used for ACK Timeout */
static event_t acktimer_ev = {0, _acktimer_ev_handler};
/* Event used for CCA Done */
static event_t cca_ev = {0, _cca_ev_handler};
/* Event used for timer events */
static event_t timer_event = {0, _timer_ev_handler};
/* Event used for TX Done */
static event_t tx_done_event = {0, _tx_done_handler};
/* Event used for RX Done */
static event_t rx_done_event = {0, _rx_done_handler};
/* Event used for offloading the receive procedure */
static event_t rx_offload_ev = {0, _handle_rx_offload};
/* Event used for offloading the start of a CFP */
static event_t start_of_cfp_ev = {0, _start_of_cfp_handler};
void _handle_rx_offload(event_t *ev)
{
dsme::DSMEPlatform::instance->processRxOffload();
}
void _start_of_cfp_handler(event_t *ev)
{
dsme::DSMEPlatform::instance->getDSME().handleStartOfCFP();
dsme::DSMEPlatform::instance->updateVisual();
}
static void _cca_ev_handler(event_t *ev)
{
dsme::DSMEPlatform::instance->processCCAEvent();
}
static void _acktimer_ev_handler(event_t *ev)
{
dsme::DSMEPlatform::instance->sendNow();
}
static void _acktimer_cb(void *arg)
{
dsme::DSMEPlatform::instance->offloadACKTimer();
}
static void _timer_ev_handler(event_t *ev)
{
dsme::DSMEPlatform::instance->getDSME().getEventDispatcher().timerInterrupt();
}
static void _tx_done_handler(event_t *ev)
{
dsme::DSMEPlatform::instance->processTXDoneEvent();
}
static void _rx_done_handler(event_t *ev)
{
dsme::DSMEPlatform::instance->processRxDone();
}
static void _hal_radio_cb(ieee802154_dev_t *dev, ieee802154_trx_ev_t status)
{
switch (status) {
case IEEE802154_RADIO_CONFIRM_TX_DONE:
dsme::DSMEPlatform::instance->offloadTXDoneEvent();
break;
case IEEE802154_RADIO_INDICATION_RX_START:
dsme::DSMEPlatform::instance->indicateRxStart();
break;
case IEEE802154_RADIO_INDICATION_CRC_ERROR:
break;
case IEEE802154_RADIO_INDICATION_TX_START:
break;
case IEEE802154_RADIO_INDICATION_RX_DONE:
dsme::DSMEPlatform::instance->offloadRXDoneEvent();
break;
case IEEE802154_RADIO_CONFIRM_CCA:
dsme::DSMEPlatform::instance->offloadCCAEvent();
break;
default:
DSME_ASSERT(false);
}
}
/********************* C++ functions *********************/
void DSMEPlatform::processRxOffload()
{
IDSMEMessage *message = this->message;
this->message = nullptr;
receiveFromAckLayerDelegate(message);
}
void DSMEPlatform::processCCAEvent()
{
bool clear = ieee802154_radio_confirm_cca(this->radio);
this->setPlatformState(DSMEPlatform::STATE_READY);
this->getDSME().dispatchCCAResult(clear);
}
void DSMEPlatform::processTXDoneEvent()
{
int res = ieee802154_radio_confirm_transmit(this->radio, NULL);
this->pending_tx = false;
DSME_ASSERT(res >= 0);
res = ieee802154_radio_set_rx(this->radio);
DSME_ASSERT(res == 0);
if (this->wait_for_ack) {
this->wait_for_ack = false;
ieee802154_radio_set_frame_filter_mode(this->radio, IEEE802154_FILTER_ACK_ONLY);
}
else {
ieee802154_radio_set_frame_filter_mode(this->radio, IEEE802154_FILTER_ACCEPT);
}
this->txEndCallback(true);
this->setPlatformState(DSMEPlatform::STATE_READY);
}
void DSMEPlatform::processRxDone()
{
if (this->state != STATE_READY) {
assert(false);
return;
}
DSMEMessage *message = getEmptyMessage();
message->netif = this->netif;
message->setStartOfFrameDelimiterSymbolCounter(rx_sfd);
int res;
res = ieee802154_radio_set_idle(this->radio, true);
DSME_ASSERT(res == 0);
int len = ieee802154_radio_len(this->radio);
if (len > 127 || len < 0) {
ieee802154_radio_read(this->radio, NULL, 127, NULL);
res = ieee802154_radio_set_rx(this->radio);
DSME_ASSERT(res == 0);
return;
}
res = message->loadBuffer(len);
DSME_ASSERT(res >= 0);
ieee802154_rx_info_t info;
res = ieee802154_radio_read(this->radio, message->getPayload(), 127, &info);
if (res < 0) {
message->releaseMessage();
res = ieee802154_radio_set_rx(this->radio);
DSME_ASSERT(res == 0);
return;
}
message->messageLQI = info.lqi;
message->messageRSSI = info.rssi;
const uint8_t *buf = message->getPayload();
bool success = message->getHeader().deserializeFrom(buf, len);
if (!success) {
message->releaseMessage();
res = ieee802154_radio_set_rx(this->radio);
DSME_ASSERT(res == 0);
return;
}
message->dropHdr(message->getHeader().getSerializationLength());
res = ieee802154_radio_set_rx(this->radio);
DSME_ASSERT(res == 0);
getDSME().getAckLayer().receive(message);
}
void DSMEPlatform::offloadCCAEvent()
{
event_post(this->getEventQueue(), &cca_ev);
}
void DSMEPlatform::offloadTXDoneEvent()
{
event_post(this->getEventQueue(), &tx_done_event);
}
void DSMEPlatform::indicateRxStart()
{
this->rx_sfd = this->getSymbolCounter();
}
void DSMEPlatform::offloadRXDoneEvent()
{
event_post(this->getEventQueue(), &rx_done_event);
}
void DSMEPlatform::offloadTimerEvent()
{
event_post(this->getEventQueue(), &timer_event);
}
void DSMEPlatform::offloadACKTimer()
{
event_post(this->getEventQueue(), &acktimer_ev);
}
static void _timer_cb(void *arg)
{
dsme::DSMEPlatform::instance->offloadTimerEvent();
}
void DSMEPlatform::sendFrame(uint16_t addr, iolist_t *pkt)
{
/* First 2 bytes are the ID */
if (!this->mac_pib.macAssociatedPANCoord) {
return;
}
DSMEMessage *message = getEmptyMessage();
if (message->loadBuffer(pkt) < 0) {
return;
}
IEEE802154MacAddress dst;
dst.setShortAddress(addr);
mcps_sap::DATA::request_parameters params;
message->getHeader().setSrcAddrMode(SHORT_ADDRESS);
message->getHeader().setDstAddrMode(SHORT_ADDRESS);
message->getHeader().setDstAddr(dst);
message->getHeader().setSrcPANId(this->mac_pib.macPANId);
message->getHeader().setDstPANId(this->mac_pib.macPANId);
this->dsmeAdaptionLayer.sendMessage(message);
}
DSMEPlatform::DSMEPlatform() :
phy_pib(),
mac_pib(phy_pib),
mcps_sap(dsme),
mlme_sap(dsme),
dsmeAdaptionLayer(dsme),
initialized(false),
state(STATE_READY)
{
instance = this;
this->timer.callback = _timer_cb;
this->timer.arg = this;
this->acktimer.callback = _acktimer_cb;
this->acktimer.arg = this;
}
DSMEPlatform::~DSMEPlatform()
{}
/**
* Creates an IEEE802154MacAddress out of an uint16_t short address
*/
void DSMEPlatform::translateMacAddress(uint16_t& from, IEEE802154MacAddress& to)
{
if (from == 0xFFFF) {
to = IEEE802154MacAddress(IEEE802154MacAddress::SHORT_BROADCAST_ADDRESS);
}
else {
to.setShortAddress(from);
}
}
void DSMEPlatform::initialize(bool pan_coord)
{
this->instance = this;
this->dsme.setPHY_PIB(&(this->phy_pib));
this->dsme.setMAC_PIB(&(this->mac_pib));
this->dsme.setMCPS(&(this->mcps_sap));
this->dsme.setMLME(&(this->mlme_sap));
/* Use all channels of the channel page 0 (O-QPSK) */
constexpr uint8_t MAX_CHANNELS = 16;
uint8_t channels[MAX_CHANNELS];
uint8_t num = MAX_CHANNELS;
channelList_t DSSS2450_channels(num);
for (uint8_t i = 0; i < num; i++) {
DSSS2450_channels[i] = 11 + i;
}
phy_pib.setDSSS2450ChannelPage(DSSS2450_channels);
/* Initialize Address */
IEEE802154MacAddress address;
uint8_t ext_addr[IEEE802154_LONG_ADDRESS_LEN];
network_uint16_t short_addr;
luid_base(ext_addr, sizeof(ext_addr));
address.setA1((ext_addr[0] << 8) | ext_addr[1]);
address.setA2((ext_addr[2] << 8) | ext_addr[3]);
address.setA3((ext_addr[4] << 8) | ext_addr[5]);
address.setA4((ext_addr[6] << 8) | ext_addr[7]);
this->mac_pib.macExtendedAddress = address;
/* TODO: UGLY HACK! To be removed when gnrc_netif<->netdev dependency is
* not granted */
this->radio = (ieee802154_dev_t *)this->netif->dev;
this->radio->cb = _hal_radio_cb;
ieee802154_radio_request_on(this->radio);
while (ieee802154_radio_confirm_on(this->radio) == -EAGAIN) {}
/* Disable Auto CSMA-CA */
ieee802154_radio_set_csma_params(this->radio, NULL, -1);
/* Accept all frames except ACK */
ieee802154_radio_set_frame_filter_mode(this->radio, IEEE802154_FILTER_ACCEPT);
/* Call more radio configurations here if needed... */
this->mac_pib.macShortAddress = this->mac_pib.macExtendedAddress.getShortAddress();
short_addr.u8[0] = this->mac_pib.macExtendedAddress.getShortAddress() >> 8;
short_addr.u8[1] = this->mac_pib.macExtendedAddress.getShortAddress() & 0xFF;
this->mac_pib.macIsPANCoord = pan_coord;
this->mac_pib.macIsCoord = pan_coord;
if (this->mac_pib.macIsPANCoord) {
DEBUG("This node is PAN coordinator\n");
this->mac_pib.macPANId = CONFIG_IEEE802154_DEFAULT_PANID;
}
ieee802154_radio_config_addr_filter(this->radio, IEEE802154_AF_PANID, &this->mac_pib.macPANId);
ieee802154_radio_config_addr_filter(this->radio, IEEE802154_AF_SHORT_ADDR, &short_addr);
ieee802154_radio_config_addr_filter(this->radio, IEEE802154_AF_EXT_ADDR, &ext_addr);
this->mac_pib.macCapReduction = CONFIG_IEEE802154_DSME_CAP_REDUCTION;
this->mac_pib.macAssociatedPANCoord = this->mac_pib.macIsPANCoord;
this->mac_pib.macSuperframeOrder = CONFIG_IEEE802154_DSME_SUPERFRAME_ORDER;
this->mac_pib.macMultiSuperframeOrder = CONFIG_IEEE802154_DSME_MULTISUPERFRAME_ORDER;
this->mac_pib.macBeaconOrder = CONFIG_IEEE802154_DSME_BEACON_ORDER;
this->mac_pib.macMinBE = CONFIG_IEEE802154_DEFAULT_CSMA_CA_MIN_BE;
this->mac_pib.macMaxBE = CONFIG_IEEE802154_DEFAULT_CSMA_CA_MAX_BE;
this->mac_pib.macMaxCSMABackoffs = CONFIG_IEEE802154_DEFAULT_CSMA_CA_RETRIES;
this->mac_pib.macMaxFrameRetries = CONFIG_IEEE802154_DEFAULT_MAX_FRAME_RETRANS;
this->mac_pib.macDSMEGTSExpirationTime = CONFIG_IEEE802154_DSME_GTS_EXPIRATION;
this->mac_pib.macResponseWaitTime = CONFIG_IEEE802154_DSME_MAC_RESPONSE_WAIT_TIME;
this->mac_pib.macChannelDiversityMode = Channel_Diversity_Mode::CHANNEL_HOPPING;
this->phy_pib.phyCurrentChannel = CONFIG_IEEE802154_DEFAULT_CHANNEL;
this->dsmeAdaptionLayer.setIndicationCallback(DELEGATE(&DSMEPlatform::
handleDataMessageFromMCPSWrapper,
*this));
this->dsmeAdaptionLayer.setConfirmCallback(DELEGATE(&DSMEPlatform::handleConfirmFromMCPSWrapper,
*this));
this->dsme.initialize(this);
channelList_t scanChannels;
scanChannels.add(CONFIG_IEEE802154_DEFAULT_CHANNEL);
if (IS_ACTIVE(CONFIG_IEEE802154_DSME_STATIC_GTS)) {
StaticScheduling *staticScheduling = new StaticScheduling(this->dsmeAdaptionLayer);
staticScheduling->setNegotiateChannels(false);
scheduling = staticScheduling;
}
else {
TPS *tps = new TPS(this->dsmeAdaptionLayer);
tps->setAlpha(OPENDSME_TPS_ALPHA);
tps->setMinFreshness(this->mac_pib.macDSMEGTSExpirationTime);
scheduling = tps;
}
this->dsmeAdaptionLayer.initialize(scanChannels, CONFIG_IEEE802154_DSME_SCAN_DURATION,
scheduling);
this->initialized = true;
}
#if IS_ACTIVE(CONFIG_IEEE802154_DSME_STATIC_GTS)
void DSMEPlatform::allocateGTS(uint8_t superframeID, uint8_t slotID, uint8_t channelID,
Direction direction, uint16_t address)
{
static_cast<StaticScheduling *>(scheduling)->allocateGTS(superframeID, slotID, channelID,
direction, address);
}
#endif
void DSMEPlatform::setGTSTransmission(bool gts)
{
this->dsmeAdaptionLayer.getMessageHelper().setGTSTransmission(gts);
}
void DSMEPlatform::setAckReq(bool ackReq)
{
this->dsmeAdaptionLayer.getMessageHelper().setAckReq(ackReq);
}
void DSMEPlatform::start()
{
DSME_ASSERT(this->initialized);
this->dsme.start();
this->dsmeAdaptionLayer.startAssociation();
}
void DSMEPlatform::getShortAddress(network_uint16_t *addr)
{
addr->u8[0] = this->mac_pib.macExtendedAddress.getShortAddress() >> 8;
addr->u8[1] = this->mac_pib.macExtendedAddress.getShortAddress() & 0xFF;
}
bool DSMEPlatform::isAssociated()
{
return this->mac_pib.macAssociatedPANCoord;
}
void DSMEPlatform::handleDataMessageFromMCPSWrapper(IDSMEMessage *msg)
{
this->handleDataMessageFromMCPS(static_cast<DSMEMessage *>(msg));
}
void DSMEPlatform::handleConfirmFromMCPSWrapper(IDSMEMessage *msg,
DataStatus::Data_Status dataStatus)
{
this->handleConfirmFromMCPS(static_cast<DSMEMessage *>(msg), dataStatus);
}
void DSMEPlatform::handleConfirmFromMCPS(DSMEMessage *msg, DataStatus::Data_Status dataStatus)
{
if (dataStatus == DataStatus::Data_Status::SUCCESS) {
/* TODO: Add to statistics */
}
IDSMEMessage *m = static_cast<IDSMEMessage *>(msg);
releaseMessage(m);
}
void DSMEPlatform::handleDataMessageFromMCPS(DSMEMessage *msg)
{
msg->dispatchMessage();
}
bool DSMEPlatform::isReceptionFromAckLayerPossible()
{
return this->state == STATE_READY;
}
void DSMEPlatform::handleReceivedMessageFromAckLayer(IDSMEMessage *message)
{
DSME_ASSERT(receiveFromAckLayerDelegate);
DSME_ASSERT(!this->message);
this->message = message;
event_post(this->getEventQueue(), &rx_offload_ev);
}
DSMEMessage *DSMEPlatform::getEmptyMessage()
{
DSMEMessage *msg = new DSMEMessage();
DSME_ASSERT(msg);
msg->clearMessage();
signalNewMsg(msg);
return msg;
}
void DSMEPlatform::signalNewMsg(DSMEMessage *msg)
{
/* Not used */
}
void DSMEPlatform::releaseMessage(IDSMEMessage *msg)
{
DSMEMessage *m = static_cast<DSMEMessage *>(msg);
m->releaseMessage();
}
void DSMEPlatform::startTimer(uint32_t symbolCounterValue)
{
uint32_t now = ztimer_now(ZTIMER_USEC);
uint32_t offset = now & OPENDSME_TIMER_MASK;
/* This works even if there's an overflow */
int32_t delta = ((symbolCounterValue - getSymbolCounter()) << OPENDSME_TIMER_OFFSET)
- offset;
ztimer_set(ZTIMER_USEC, &timer, (uint32_t)delta);
}
uint32_t DSMEPlatform::getSymbolCounter()
{
return ztimer_now(ZTIMER_USEC) >> OPENDSME_TIMER_OFFSET;
}
void DSMEPlatform::scheduleStartOfCFP()
{
event_post(this->getEventQueue(), &start_of_cfp_ev);
}
void DSMEPlatform::signalAckedTransmissionResult(bool success, uint8_t transmissionAttempts,
IEEE802154MacAddress receiver)
{
/* Not used */
}
/*
* Signal GTS allocation or deallocation
*/
void DSMEPlatform::signalGTSChange(bool deallocation, IEEE802154MacAddress counterpart,
uint16_t superframeID, uint8_t gtSlotID, uint8_t channel,
Direction direction)
{}
void DSMEPlatform::signalQueueLength(uint32_t length)
{}
/*
* Number of packets sent per CAP
*/
void DSMEPlatform::signalPacketsPerCAP(uint32_t packets)
{}
/*
* Number of failed packets per CAP
*/
void DSMEPlatform::signalFailedPacketsPerCAP(uint32_t packets)
{
/* Not used */
}
void DSMEPlatform::updateVisual()
{
/* Not used */
}
bool DSMEPlatform::setChannelNumber(uint8_t channel)
{
ieee802154_phy_conf_t conf = {
.phy_mode = IEEE802154_PHY_OQPSK,
.channel = channel,
.page = 0,
.pow = CONFIG_IEEE802154_DEFAULT_TXPOWER,
};
int res;
res = ieee802154_radio_set_idle(this->radio, true);
DSME_ASSERT(res == 0);
res = ieee802154_radio_config_phy(this->radio, &conf);
DSME_ASSERT(res == 0);
/* TODO: Find a better solution */
ieee802154_radio_config_addr_filter(this->radio, IEEE802154_AF_PANID, &this->mac_pib.macPANId);
res = ieee802154_radio_set_rx(this->radio);
DSME_ASSERT(res == 0);
return true;
}
uint8_t DSMEPlatform::getChannelNumber()
{
/* Apparently not used by OpenDSME */
DSME_ASSERT(false);
return 0;
}
bool DSMEPlatform::prepareSendingCopy(IDSMEMessage *msg, Delegate<void(bool)> txEndCallback)
{
DSMEMessage *m = (DSMEMessage *)msg;
this->state = DSMEPlatform::STATE_SEND;
this->txEndCallback = txEndCallback;
uint8_t mhr[IEEE802154_MAX_HDR_LEN];
uint8_t mhr_len = msg->getHeader().getSerializationLength();
uint8_t *p = mhr;
msg->getHeader().serializeTo(p);
iolist_t iol = {
.iol_next = (iolist_t *)m->getIolPayload(),
.iol_base = mhr,
.iol_len = mhr_len,
};
if (mhr[0] & IEEE802154_FCF_ACK_REQ) {
this->wait_for_ack = true;
}
else {
this->wait_for_ack = false;
}
int res = ieee802154_radio_set_idle(this->radio, true);
DSME_ASSERT(res == 0);
res = ieee802154_radio_write(this->radio, &iol);
DSME_ASSERT(res == 0);
return true;
}
bool DSMEPlatform::sendNow()
{
int res = ieee802154_radio_request_transmit(this->radio);
DSME_ASSERT(res == 0);
this->pending_tx = true;
return true;
}
void DSMEPlatform::abortPreparedTransmission()
{
/* Nothing to do here, since the Radio HAL will drop the frame if
* the write function is called again */
this->setPlatformState(DSMEPlatform::STATE_READY);
}
bool DSMEPlatform::sendDelayedAck(IDSMEMessage *ackMsg, IDSMEMessage *receivedMsg,
Delegate<void(bool)> txEndCallback)
{
DSMEMessage *m = (DSMEMessage *)ackMsg;
DSME_ASSERT(m != nullptr);
uint8_t ack[IEEE802154_ACK_FRAME_LEN - IEEE802154_FCS_LEN];
uint8_t mhr_len = ackMsg->getHeader().getSerializationLength();
DSME_ASSERT(mhr_len == sizeof(ack));
uint8_t *p = ack;
ackMsg->getHeader().serializeTo(p);
this->txEndCallback = txEndCallback;
iolist_t iol = {
.iol_next = NULL,
.iol_base = ack,
.iol_len = mhr_len,
};
int res = ieee802154_radio_set_idle(this->radio, true);
DSME_ASSERT(res == 0);
res = ieee802154_radio_write(this->radio, &iol);
DSME_ASSERT(res == 0);
/* Hardcoded to O-QPSK
* Preamble (4) | SFD (1) | PHY Hdr (1) | MAC Payload | FCS (2)
*/
uint32_t endOfReception = receivedMsg->getStartOfFrameDelimiterSymbolCounter()
+ receivedMsg->getTotalSymbols()
- 2 * 4 /* Preamble */
- 2 * 1; /* SFD */
uint32_t ackTime = endOfReception + aTurnaroundTime;
uint32_t now = getSymbolCounter();
uint32_t diff = ackTime - now;
ztimer_set(ZTIMER_USEC, &this->acktimer, diff * aSymbolDuration);
return true;
}
void DSMEPlatform::setReceiveDelegate(receive_delegate_t receiveDelegate)
{
this->receiveFromAckLayerDelegate = receiveDelegate;
}
bool DSMEPlatform::startCCA()
{
if (this->pending_tx) {
return false;
}
ieee802154_radio_request_cca(this->radio);
this->state = DSMEPlatform::STATE_CCA_WAIT;
return true;
}
void DSMEPlatform::turnTransceiverOn()
{
int res = ieee802154_radio_request_on(this->radio);
DSME_ASSERT(res == 0);
res = ieee802154_radio_confirm_on(this->radio);
DSME_ASSERT(res == 0);
ieee802154_radio_set_cca_threshold(this->radio, CONFIG_IEEE802154_CCA_THRESH_DEFAULT);
}
void DSMEPlatform::turnTransceiverOff()
{
int res = ieee802154_radio_off(this->radio);
DSME_ASSERT(res == 0);
}
bool DSMEPlatform::isRxEnabledOnCap()
{
/* TODO: This feature is experimental. Enable RX on CAP for now... */
return true;
}
}
/** @} */

View File

@ -0,0 +1,3 @@
MODULE = opendsme_riot_contrib
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,188 @@
/*
* Copyright (C) 2022 HAW Hamburg
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @{
*
* @file
* @author José I. Álamos <jose.alamos@haw-hamburg.de>
*/
#include "opendsme/DSMEPlatform.h"
#include "opendsme/opendsme.h"
#include "mac_services/DSME_Common.h"
#include "net/gnrc/netif/hdr.h"
dsme::DSMEPlatform m_dsme;
extern "C" {
static bool _pan_coord;
extern void heap_stats(void);
static int _send(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt)
{
/* Note that there the short address is assigned by the coordinator and the
* user should not have control over it.
* Given that, and the fact the current MAC does not communicate with
* different PANs, we can always use the short address */
uint8_t bcast[2] = { 0xFF, 0xFF };
uint8_t *addr;
pkt = gnrc_pktbuf_start_write(pkt);
gnrc_netif_hdr_t *hdr = (gnrc_netif_hdr_t *)pkt->data;
if (hdr->flags &= GNRC_NETIF_HDR_FLAGS_MULTICAST) {
addr = static_cast<uint8_t *>(&bcast[0]);
}
else if (hdr->dst_l2addr_len == IEEE802154_LONG_ADDRESS_LEN) {
addr = gnrc_netif_hdr_get_dst_addr(hdr)
+ IEEE802154_LONG_ADDRESS_LEN
- IEEE802154_SHORT_ADDRESS_LEN;
}
else if (hdr->dst_l2addr_len == IEEE802154_SHORT_ADDRESS_LEN) {
addr = gnrc_netif_hdr_get_dst_addr(hdr);
}
else {
gnrc_pktbuf_release(pkt);
return -EBADMSG;
}
uint16_t _addr = byteorder_ntohs(*((network_uint16_t *)addr));
pkt = gnrc_pktbuf_remove_snip(pkt, pkt);
m_dsme.sendFrame(_addr, (iolist_t *)pkt);
return 0;
}
static gnrc_pktsnip_t *_recv(gnrc_netif_t *netif)
{
/* Not used */
return NULL;
}
static int _get(gnrc_netif_t *netif, gnrc_netapi_opt_t *opt)
{
gnrc_netif_acquire(netif);
int res;
network_uint16_t addr;
le_uint64_t ext_addr;
uint8_t *addr_ptr = static_cast<uint8_t *>(ext_addr.u8);
switch (opt->opt) {
case NETOPT_MAX_PDU_SIZE:
*((uint16_t *)opt->data) = IEEE802154_FRAME_LEN_MAX;
res = sizeof(uint16_t);
break;
case NETOPT_ADDR_LEN:
*((uint16_t *)opt->data) = IEEE802154_SHORT_ADDRESS_LEN;
res = sizeof(uint16_t);
break;
case NETOPT_ADDRESS:
assert(opt->data_len >= IEEE802154_SHORT_ADDRESS_LEN);
m_dsme.getShortAddress(&addr);
memcpy(opt->data, &addr, IEEE802154_SHORT_ADDRESS_LEN);
res = IEEE802154_SHORT_ADDRESS_LEN;
break;
case NETOPT_ADDRESS_LONG: {
assert(opt->data_len >= IEEE802154_LONG_ADDRESS_LEN);
addr_ptr << m_dsme.getAddress();
*((be_uint64_t *)opt->data) = byteorder_ltobll(ext_addr);
res = IEEE802154_LONG_ADDRESS_LEN;
break;
}
case NETOPT_LINK:
assert(opt->data_len >= sizeof(netopt_enable_t));
*((netopt_enable_t *)opt->data) = m_dsme.isAssociated()
? NETOPT_ENABLE
: NETOPT_DISABLE;
return sizeof(netopt_enable_t);
default:
res = -ENOTSUP;
break;
}
return res;
}
static int _set(gnrc_netif_t *netif, const gnrc_netapi_opt_t *opt)
{
int res;
switch (opt->opt) {
case NETOPT_LINK:
m_dsme.initialize(_pan_coord);
m_dsme.start();
break;
case NETOPT_PAN_COORD:
if (*((bool *)opt->data) == true) {
_pan_coord = true;
}
else {
_pan_coord = false;
}
res = sizeof(netopt_enable_t);
break;
#if IS_ACTIVE(CONFIG_IEEE802154_DSME_STATIC_GTS)
case NETOPT_GTS_ALLOC: {
ieee802154_dsme_alloc_t *alloc = (ieee802154_dsme_alloc_t *)opt->data;
uint16_t _addr = byteorder_ntohs(alloc->addr);
m_dsme.allocateGTS(alloc->superframe_id, alloc->slot_id, alloc->channel_id,
alloc->tx ? dsme::Direction::TX : dsme::Direction::RX, _addr);
res = sizeof(ieee802154_dsme_alloc_t);
break;
}
#endif
case NETOPT_PROTO:
assert(opt->data_len == sizeof(gnrc_nettype_t));
res = sizeof(gnrc_nettype_t);
break;
case NETOPT_GTS_TX:
assert(opt->data_len == sizeof(netopt_enable_t));
m_dsme.setGTSTransmission(*((netopt_enable_t *)opt->data) ? true : false);
res = sizeof(netopt_enable_t);
break;
case NETOPT_ACK_REQ:
assert(opt->data_len == sizeof(netopt_enable_t));
m_dsme.setAckReq(*((netopt_enable_t *)opt->data) ? true : false);
res = sizeof(netopt_enable_t);
break;
case NETOPT_SRC_LEN:
res = sizeof(uint16_t);
break;
default:
res = -ENOTSUP;
break;
}
return res;
}
static int _init(gnrc_netif_t *netif)
{
netif->flags = 0;
netif_register(&netif->netif);
return 0;
}
static const gnrc_netif_ops_t dsme_ops = {
.init = _init,
.send = _send,
.recv = _recv,
.get = _get,
.set = _set,
};
int gnrc_netif_opendsme_create(gnrc_netif_t *netif, char *stack, int stacksize,
char priority, const char *name, netdev_t *dev)
{
m_dsme.setGNRCNetif(netif);
return gnrc_netif_create(netif, stack, stacksize, priority, name, dev,
&dsme_ops);
}
}
/** @} */

View File

@ -0,0 +1,340 @@
/*
* Copyright (C) 2022 HAW Hamburg
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @defgroup pkg_opendsme_dsmemessage DSME Message interface implementation.
* @ingroup pkg_opendsme
* @brief Implementation of the DSME Message interface for GNRC.
*
* @{
*
* @file
* @brief DSME Message interface implementation for GNRC.
*
* @author José I. Álamos <jose.alamos@haw-hamburg.de>
*/
#ifndef OPENDSME_DSMEMESSAGE_H
#define OPENDSME_DSMEMESSAGE_H
#include <stdint.h>
#include <string.h>
#include "dsmeLayer/messages/IEEE802154eMACHeader.h"
#include "interfaces/IDSMEMessage.h"
#include "iolist.h"
#include "mac_services/dataStructures/DSMEMessageElement.h"
#include "net/gnrc/pktbuf.h"
#include "opendsme/dsme_settings.h"
#include "opendsme/dsme_platform.h"
#ifdef __cplusplus
extern "C" {
#endif
namespace dsme {
/**
* @brief Forward declaration of DSMEPlatform class
*/
class DSMEPlatform;
/**
* @brief DSME Message interface implementation for GNRC
*/
class DSMEMessage : public IDSMEMessage {
public:
/************* IDSMEMessage implementation *************/
/**
* @brief prepend a header to current message
*/
void prependFrom(DSMEMessageElement *msg) override;
/**
* @brief decapsulate header to a message
*/
void decapsulateTo(DSMEMessageElement *msg) override;
/**
* @brief copy payload to DSME Message Element
*/
void copyTo(DSMEMessageElement *msg);
/**
* @brief check whether the message has payload
*/
bool hasPayload()
{
return this->pkt != NULL && this->pkt->size > 0;
}
/**
* @brief get the symbol counter at the end of the SFD
*/
uint32_t getStartOfFrameDelimiterSymbolCounter() override
{
return startOfFrameDelimiterSymbolCounter;
}
/**
* @brief set the symbol counter at the end of the SFD
*/
void setStartOfFrameDelimiterSymbolCounter(uint32_t symbolCounter) override
{
startOfFrameDelimiterSymbolCounter = symbolCounter;
}
/**
* @brief get the total number of symbols in current frame
*/
uint16_t getTotalSymbols()
{
DSME_ASSERT(pkt);
/* Hardcoded to O-QPSK */
uint16_t bytes = macHdr.getSerializationLength()
+ pkt->size
+ 2 /* FCS */
+ 4 /* Preamble */
+ 1 /* SFD */
+ 1; /* PHY Header */
return bytes * 2; /* 4 bit per symbol */
}
/**
* @brief get number of MPDU Symbols
* @note not used by openDSME
*/
uint8_t getMPDUSymbols() override;
/**
* @brief get IEEE 802.15.4 header
*/
IEEE802154eMACHeader& getHeader()
{
return macHdr;
}
/**
* @brief get LQI of the message
*/
uint8_t getLQI() override
{
return messageLQI;
}
/**
* @brief check whether the message was received via MCPS
*/
bool getReceivedViaMCPS() override
{
return receivedViaMCPS;
}
/**
* @brief indicated that message was received via MCPS
*/
void setReceivedViaMCPS(bool receivedViaMCPS) override
{
this->receivedViaMCPS = receivedViaMCPS;
}
/**
* @brief whether the message is being sent
*/
bool getCurrentlySending() override
{
return currentlySending;
}
/**
* @brief indicate that the message is being sent
*/
void setCurrentlySending(bool currentlySending) override
{
this->currentlySending = currentlySending;
}
/**
* @brief increase retry counter for current message
*/
void increaseRetryCounter() override
{
this->retryCounter++;
}
/**
* @brief get retry counter
*/
uint8_t getRetryCounter() override
{
return this->retryCounter;
}
/**
* @brief get payload length
*/
uint8_t getPayloadLength()
{
DSME_ASSERT(pkt);
return pkt->size;
}
/**
* @brief get RSSI of frame
*/
int8_t getRSSI() override
{
return this->messageRSSI;
}
/**
* @brief preallocate buffer with a given length
*/
int loadBuffer(size_t len);
/**
* @brief load a GNRC packet into the internal openDSME message representation
*/
int loadBuffer(iolist_t *pkt);
/**
* @brief get buffer associated to the current payload
*/
uint8_t *getPayload()
{
DSME_ASSERT(pkt);
return (uint8_t *)pkt->data;
}
/**
* @brief drop a number of bytes from the header
*/
int dropHdr(size_t len);
/**
* @brief release the message
*/
void releaseMessage();
/**
* @brief dispatch the message to upper layers
*/
void dispatchMessage();
/**
* @brief get the IOLIST representation of the message
*/
iolist_t *getIolPayload();
/**
* @brief clear the message
*/
void clearMessage();
/**
* @brief whether the message is being sent on the first try
*/
bool firstTry;
/**
* @brief whether the message is free
*/
bool free;
private:
/**
* @brief DSMEMessage constructor
*/
DSMEMessage() :
messageRSSI(0),
messageLQI(0),
receivedViaMCPS(false),
currentlySending(false),
retryCounter(0)
{}
/**
* @brief DSMEMessage destructor
*/
~DSMEMessage()
{}
/**
* @brief prepare message
*/
void prepare()
{
currentlySending = false;
firstTry = true;
receivedViaMCPS = false;
retryCounter = 0;
macHdr.reset();
}
/**
* @brief descriptor of the MAC header
*/
IEEE802154eMACHeader macHdr;
/**
* @brief stores the RSSI of the received frame
*/
uint8_t messageRSSI;
/**
* @brief stores the LQI of the received frame
*/
uint8_t messageLQI;
/**
* @brief stores a pointer to the GNRC Pktbuf representation
*/
gnrc_pktsnip_t *pkt;
/**
* @brief stores the timestamp of the preamble
*/
uint32_t startOfFrameDelimiterSymbolCounter;
/**
* @brief stores whether the message was received via MCPS
*/
bool receivedViaMCPS;
/**
* @brief stores whether the frame is currently being sent
*/
bool currentlySending;
/**
* @brief number of retransmission attempts
*/
uint8_t retryCounter;
/**
* @brief pointer to the GNRC network interface
*/
gnrc_netif_t *netif;
/* declare related classes as friends */
friend class DSMEPlatform;
friend class DSMEMessageElement;
friend class DSMEPlatformBase;
friend class DSMEMessageBuffer;
};
}
#ifdef __cplusplus
}
#endif
#endif /* OPENDSME_DSMEMESSAGE_H */
/** @} */

View File

@ -0,0 +1,546 @@
/*
* Copyright (C) 2022 HAW Hamburg
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @defgroup pkg_opendsme_dsmeplatform DSME Platform interface implementation.
* @ingroup pkg_opendsme
* @brief Implementation of the DSME Platform interface.
*
* @{
*
* @file
* @brief DSME Platform interface implementation
*
* @author José I. Álamos <jose.alamos@haw-hamburg.de>
*/
#ifndef OPENDSME_DSMEPLATFORM_H
#define OPENDSME_DSMEPLATFORM_H
#include <stdint.h>
#include <stdlib.h>
#include <string>
#include "random.h"
#include "byteorder.h"
#include "dsmeAdaptionLayer/DSMEAdaptionLayer.h"
#include "dsmeLayer/DSMELayer.h"
#include "helper/DSMEDelegate.h"
#include "interfaces/IDSMEPlatform.h"
#include "mac_services/dataStructures/IEEE802154MacAddress.h"
#include "mac_services/mcps_sap/MCPS_SAP.h"
#include "mac_services/mlme_sap/MLME_SAP.h"
#include "mac_services/pib/MAC_PIB.h"
#include "mac_services/pib/PHY_PIB.h"
#include "mac_services/pib/dsme_phy_constants.h"
#include "mac_services/DSME_Common.h"
#include "net/gnrc/netif.h"
#include "net/ieee802154/radio.h"
#include "opendsme/dsme_settings.h"
#include "opendsme/DSMEMessage.h"
#include "ztimer.h"
#ifdef __cplusplus
extern "C" {
#endif
namespace dsme {
struct DSMESettings;
class DSMELayer;
class DSMEAdaptionLayer;
/**
* @brief DSMEPlatform interface implementation for GNRC
*/
class DSMEPlatform : public IDSMEPlatform {
public:
/**
* @brief DSMEPlatform constructor
*/
DSMEPlatform();
/**
* @brief DSMEPlatform destructor
*/
~DSMEPlatform();
/**
* @brief pointer to the DSME instance
*/
static DSMEPlatform* instance;
/**
* @brief initialize MAC with a role (PAN coordinator, child)
*/
void initialize(bool pan_coord);
/**
* @brief to be called by the upper layer in order to send a frame
*/
void sendFrame(uint16_t addr, iolist_t *pkt);
/**
* @brief start DSME
*/
void start();
/**
* @brief get the DSME layer
*/
DSMELayer& getDSME() {
return dsme;
}
/**
* @brief check whether the node associated
*/
bool isAssociated();
#if IS_ACTIVE(CONFIG_IEEE802154_DSME_STATIC_GTS) || DOXYGEN
/**
* @brief allocate a GTS slot
*/
void allocateGTS(uint8_t superframeID, uint8_t slotID, uint8_t channelID,
Direction direction, uint16_t address);
#endif
/**
* @brief get short address
*/
void getShortAddress(network_uint16_t *addr);
/**
* @brief set GTS or CAP transmission
*/
void setGTSTransmission(bool gts);
/**
* @brief set ACK_REQ bit
*/
void setAckReq(bool ackReq);
/**
* @brief request to offload the CCA Done event
*/
void offloadCCAEvent();
/**
* @brief request to offload the TX Done event
*/
void offloadTXDoneEvent();
/**
* @brief indicate the MAC layer that the reception started
*/
void indicateRxStart();
/**
* @brief request to offload RX Done event
*/
void offloadRXDoneEvent();
/**
* @brief request to offload ACK Timer event
*/
void offloadACKTimer();
/**
* @brief request to offload Timer event
*/
void offloadTimerEvent();
/**
* @brief set the platform state
*/
void setPlatformState(uint8_t state) {
this->state = state;
}
/**
* @brief process the CCA Done event
*/
void processCCAEvent();
/**
* @brief process the TX Done event
*/
void processTXDoneEvent();
/**
* @brief process the RX Done event from radio
*/
void processRxDone();
/**
* @brief process the offload event of received frame
*/
void processRxOffload();
/**
* @brief set the GNRC netif
*/
void setGNRCNetif(gnrc_netif_t *netif) {
this->netif = netif;
}
/**
* @brief state of the Platform layer
*/
enum {
STATE_READY = 0, STATE_CCA_WAIT = 1, STATE_SEND = 2,
};
/*********** IDSMEPlatform implementation ***********/
/**
* @brief get channel number
*/
uint8_t getChannelNumber() override;
/**
* @brief set channel number
*/
bool setChannelNumber(uint8_t k) override;
/**
* @brief Directly send packet without delay and without CSMA
* but keep the message (the caller has to ensure that the message is eventually released)
* This might lead to an additional memory copy in the platform
*/
bool sendNow() override;
/**
* @brief prepare the next transmission
*/
bool prepareSendingCopy(IDSMEMessage* msg, Delegate<void(bool)> txEndCallback) override;
/**
* @brief prepare the next transmission
*/
bool prepareSendingCopy(DSMEMessage* msg, Delegate<void(bool)> txEndCallback);
/**
* @brief abort an already prepared transmission
*/
void abortPreparedTransmission() override;
/**
* @brief send an ACK message, delay until aTurnaRoundTime after reception_time has expired
*/
bool sendDelayedAck(IDSMEMessage *ackMsg, IDSMEMessage *receivedMsg,
Delegate<void(bool)> txEndCallback) override;
/**
* @brief send an ACK message, delay until aTurnaRoundTime after reception_time has expired
*/
bool sendDelayedAck(DSMEMessage *ackMsg, DSMEMessage *receivedMsg,
Delegate<void(bool)> txEndCallback);
/**
* @brief set the receive callback
*/
void setReceiveDelegate(receive_delegate_t receiveDelegate) override;
/**
* @brief check whether the ACK layer is busy
*/
bool isReceptionFromAckLayerPossible() override;
/**
* @brief handle reception of frames from ACK Layer
*/
void handleReceivedMessageFromAckLayer(IDSMEMessage* message) override;
/**
* @brief handle reception of frames from ACK Layer
*/
void handleReceivedMessageFromAckLayer(DSMEMessage* message);
/**
* @brief get an empty message
*/
DSMEMessage* getEmptyMessage() override;
/**
* @brief release a message
*/
void releaseMessage(IDSMEMessage* msg) override;
/**
* @brief release a message
*/
void releaseMessage(DSMEMessage* msg);
/**
* @brief start CCA procedure
*/
bool startCCA() override;
/**
* @brief start timer
*/
void startTimer(uint32_t symbolCounterValue) override;
/**
* @brief get elapsed number of symbols since initialization
*/
uint32_t getSymbolCounter() override;
/**
* @brief get a uint16_t random number
*/
uint16_t getRandom() override {
return (random_uint32() % UINT16_MAX);
}
/**
* @brief update visual components of openDSME
* @note to be used in a simulation environment and therefore not used
* in RIOT
*/
void updateVisual() override;
/**
* @brief callback to offload the start of CFP
*/
void scheduleStartOfCFP();
/**
* @brief Get the minimum LQI. Beacons with LQI lower than this will not be
* considered when deciding for a coordinator to associate to.
*/
uint8_t getMinCoordinatorLQI() override{
return CONFIG_IEEE802154_DSME_MIN_COORD_LQI;
};
/**
* @brief turn on transceiver
*/
void turnTransceiverOn();
/**
* @brief turn off transceiver
*/
void turnTransceiverOff();
/**
* @brief get extended address
*/
IEEE802154MacAddress& getAddress() {
return this->mac_pib.macExtendedAddress;
}
/**
* @brief signal finish of ACK'd transmission
*/
void signalAckedTransmissionResult(bool success, uint8_t transmissionAttempts,
IEEE802154MacAddress receiver) override;
/**
* @brief signal a change in GTS status
*/
void signalGTSChange(bool deallocation, IEEE802154MacAddress counterpart,
uint16_t superframeID, uint8_t gtSlotID, uint8_t channel,
Direction direction) override;
/**
* @brief signal a change in queue length
*/
void signalQueueLength(uint32_t length) override;
/**
* @brief signal the number of packets transmitted during the last CAP
*/
void signalPacketsPerCAP(uint32_t packets) override;
/**
* @brief signal the number of failed packets transmitted during the last CAP
*/
void signalFailedPacketsPerCAP(uint32_t packets) override;
/**
* @brief callback to check where RX is on during CAP
*/
bool isRxEnabledOnCap() override;
protected:
/**
* @brief Copy constructor is not allowed.
*/
DSMEPlatform(const DSMEPlatform&);
/**
* @brief Assignment operator is not allowed.
*/
DSMEPlatform& operator=(const DSMEPlatform&);
/**
* @brief delegate callback for TX end
*/
Delegate<void(bool)> txEndCallback;
/**
* @brief signal creation of new message
*/
void signalNewMsg(DSMEMessage* msg);
/**
* @brief signal release of message
*/
virtual void signalReleasedMsg(DSMEMessage* msg) {}
/**
* @brief dSMEAdaptionLayer wrapper for MCPS Indication callbacks
*/
void handleDataMessageFromMCPSWrapper(IDSMEMessage* msg);
/**
* @brief dSMEAdaptionLayer wrapper for MCPS Indication callbacks
*/
void handleDataMessageFromMCPS(DSMEMessage* msg);
/**
* @brief dSMEAdaptionLayer wrapper for MCPS Confirm callbacks
*/
void handleConfirmFromMCPSWrapper(IDSMEMessage* msg, DataStatus::Data_Status dataStatus);
/**
* @brief dSMEAdaptionLayer wrapper for MCPS Confirm callbacks
*/
void handleConfirmFromMCPS(DSMEMessage* msg, DataStatus::Data_Status dataStatus);
/**
* @brief translate MAC Address representation
*/
void translateMacAddress(uint16_t& from, IEEE802154MacAddress& to);
/**
* @brief get the event queue of the MAC
*/
event_queue_t *getEventQueue() {
return &this->netif->evq[GNRC_NETIF_EVQ_INDEX_PRIO_LOW];
}
/**
* @brief holds the PHY Information Base
*/
PHY_PIB phy_pib;
/**
* @brief holds the MAC Information Base
*/
MAC_PIB mac_pib;
/**
* @brief descriptor of the DSME MAC
*/
DSMELayer dsme;
/**
* @brief descriptor of the MCPS Service Access Point
*/
mcps_sap::MCPS_SAP mcps_sap;
/**
* @brief descriptor of the MLME Service Access Point
*/
mlme_sap::MLME_SAP mlme_sap;
/**
* @brief descriptor of the DSME Adaption Layer
*/
DSMEAdaptionLayer dsmeAdaptionLayer;
/**
* @brief whether the MAC is initialized
*/
bool initialized;
/**
* @brief whether there is a scan or sync in progress
*/
bool scanOrSyncInProgress{false};
/**
* @brief whether the association is in progress
*/
bool associationInProgress{false};
/**
* @brief whether the MAC is synchronized
*/
bool syncActive{false};
/**
* @brief whether the MAC keeps the receiver on during CAP
*/
bool rx_on_cap{true};
/**
* @brief delegate callback for passing frames to the ACK layer
*/
receive_delegate_t receiveFromAckLayerDelegate;
/**
* @brief pointer to the GNRC interface
*/
gnrc_netif_t *netif;
/**
* @brief timer used for the MAC
*/
ztimer_t timer;
/**
* @brief used to hold an incoming message before passing it to the MAC
*/
IDSMEMessage *message;
/**
* @brief pointer to the scheduler
*/
GTSScheduling* scheduling = nullptr;
/**
* @brief timestamp (in number of symbols) of the last received preamble
*/
uint32_t rx_sfd;
/**
* @brief timer used for ACK timeout events
*/
ztimer_t acktimer;
/**
* @brief state of the platform layer
*/
uint8_t state;
/**
* @brief whether the MAC expects an ACK frame
*/
bool wait_for_ack;
/**
* @brief whether there is a pending TX frame
*/
bool pending_tx;
/**
* @brief pointer to the IEEE 802.15.4 HAL descriptor
*/
ieee802154_dev_t *radio;
};
}
#ifdef __cplusplus
}
#endif
#endif /* OPENDSME_DSMEPLATFORM_H */
/** @} */

View File

@ -0,0 +1,45 @@
/*
* Copyright (C) 2022 HAW Hamburg
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup pkg_opendsme
*
* @{
*
* @file
*
* @author José I. Álamos <jose.alamos@haw-hamburg.de>
*/
#ifndef OPENDSME_DSME_ATOMIC_H
#define OPENDSME_DSME_ATOMIC_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Begin atomic operations on openDSME.
*
* @note Since openDSME runs on one thread, this is not required
*/
#define dsme_atomicBegin()
/**
* @brief End atomic operations on openDSME.
*
* @note Since openDSME runs on one thread, this is not required
*/
#define dsme_atomicEnd()
#endif /* OPENDSME_DSME_ATOMIC_H */
#ifdef __cplusplus
}
#endif
/** @} */

View File

@ -0,0 +1,52 @@
/*
* Copyright (C) 2022 HAW Hamburg
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup pkg_opendsme
*
* @{
*
* @file
*
* @author José I. Álamos <jose.alamos@haw-hamburg.de>
*/
#ifndef OPENDSME_DSME_PLATFORM_H
#define OPENDSME_DSME_PLATFORM_H
#include <assert.h>
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
#define ASSERT(x) assert(x) /**< openDSME assert macro implementation */
#define DSME_ASSERT(x) assert(x) /**< openDSME DSME assert macro implementation */
#define DSME_SIM_ASSERT(x) /**< not used */
#define LOG_INFO(x) /**< not used */
#define LOG_INFO_PURE(x) /**< not used */
#define LOG_INFO_PREFIX /**< not used */
#define LOG_ERROR(x) /**< not used */
#define HEXOUT std::hex /**< openDSME HEXOUT macro implementation */
#define DECOUT std::dec /**< openDSME DECout macro implementation */
#define LOG_ENDL std::endl /**< openDSME log endline implementation */
#define LOG_DEBUG(x) /**< not used */
#define LOG_DEBUG_PURE(x) /**< not used */
#define LOG_DEBUG_PREFIX /**< not used */
#ifdef __cplusplus
}
#endif
#endif /* OPENDSME_DSME_PLATFORM_H */
/** @} */

View File

@ -0,0 +1,166 @@
/*
* Copyright (C) 2022 HAW Hamburg
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup pkg_opendsme
*
* @{
*
* @file
*
* @author José I. Álamos <jose.alamos@haw-hamburg.de>
*/
#ifndef OPENDSME_DSME_SETTINGS_H
#define OPENDSME_DSME_SETTINGS_H
#include <stdint.h>
#include <stdio.h>
#include "kernel_defines.h"
#include "net/ieee802154.h"
#include "opendsme.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief maximum number of lost beacons before leaving the network
*/
#define DSME_MAX_LOST_BEACONS CONFIG_OPENDSME_MAX_LOST_BEACONS
namespace dsme {
namespace const_redefines {
/**
* @brief fixed value, see Table 8-80 of IEEE 802.15.4-2015, this includes beacon, CAP and GTS
*/
constexpr uint8_t aNumSuperframeSlots = 16;
/**
* @brief fixed value, see 8.1.3 of IEEE 802.15.4-2011 (assuming no UWB PHY)
*/
constexpr uint8_t macSIFSPeriod = 12;
/**
* @brief fixed value, see 8.1.3 of IEEE 802.15.4-2011 (assuming no UWB PHY)
*/
constexpr uint8_t macLIFSPeriod = 40;
}
/**
* @brief guard time before a DSME event
*/
constexpr uint8_t PRE_EVENT_SHIFT = const_redefines::macLIFSPeriod;
/**
* @brief Minimum number of CSMA slots
*/
constexpr uint8_t MIN_CSMA_SLOTS = 0;
/**
* @brief maximum number of GTS slots
*/
constexpr uint8_t MAX_GTSLOTS = const_redefines::aNumSuperframeSlots - MIN_CSMA_SLOTS - 1;
/**
* @brief maximum number of IEEE 802.15.4 channels
*/
constexpr uint8_t MAX_CHANNELS = 16; /* For O-QPSK */
/**
* @brief minimum number of IEEE 802.15.4 channels
*/
constexpr uint8_t MIN_CHANNEL = 11; /* For O-QPSK */
/**
* @brief default MAC channel
*/
constexpr uint8_t MAC_DEFAULT_CHANNEL = CONFIG_IEEE802154_DEFAULT_CHANNEL; /* For O-QPSK */
/**
* @brief maximum number of neighbors
*/
constexpr uint8_t MAX_NEIGHBORS = CONFIG_OPENDSME_MAX_NEIGHBOURS;
/**
* @brief default PAN ID
*/
constexpr uint16_t MAC_DEFAULT_NWK_ID = CONFIG_IEEE802154_DEFAULT_PANID;
/**
* @brief Minimum superframe order
*/
constexpr uint8_t MIN_SO = CONFIG_IEEE802154_DSME_SUPERFRAME_ORDER;
/**
* @brief Maximum beacon order
*/
constexpr uint8_t MAX_BO = CONFIG_IEEE802154_DSME_BEACON_ORDER;
/**
* @brief Maximum multi-superframe order
*/
constexpr uint8_t MAX_MO = CONFIG_IEEE802154_DSME_MULTISUPERFRAME_ORDER;
/**
* @brief Maximum number of slots per superframe
*/
constexpr uint16_t MAX_SLOTS_PER_SUPERFRAMES = 1 << (uint16_t)(MAX_BO - MIN_SO);
/**
* @brief Maximum number of superframes per beacon interval
*/
constexpr uint16_t MAX_TOTAL_SUPERFRAMES = 1 << (uint16_t)(MAX_BO - MIN_SO);
/**
* @brief Maximum number of superframes per multi-superframe
*/
constexpr uint16_t MAX_SUPERFRAMES_PER_MULTI_SUPERFRAME = 1 << (uint16_t)(MAX_MO - MIN_SO);
/**
* @brief Maximum number of GTS slots per superframe
*/
constexpr uint16_t MAX_OCCUPIED_SLOTS = MAX_SUPERFRAMES_PER_MULTI_SUPERFRAME*MAX_GTSLOTS*MAX_CHANNELS;
/**
* @brief Maximum number of SAB units
*/
constexpr uint8_t MAX_SAB_UNITS = 1;
/**
* @brief Size of the CAP queue
*/
constexpr uint16_t CAP_QUEUE_SIZE = CONFIG_OPENDSME_CAP_QUEUE_SIZE;
/**
* @brief Size of the CFP queue
*/
constexpr uint16_t TOTAL_GTS_QUEUE_SIZE = CONFIG_OPENDSME_CFP_QUEUE_SIZE;
/**
* @brief Size of the CAP queue
*/
constexpr uint16_t UPPER_LAYER_QUEUE_SIZE = 4;
/**
* @brief Broadcast PAN ID
*/
constexpr uint16_t DSME_BROADCAST_PAN_ID = 0xffff;
/**
* @brief Additional time to wait for an ACK
*/
constexpr uint8_t ADDITIONAL_ACK_WAIT_DURATION = 63;
}
#ifdef __cplusplus
}
#endif
#endif /* OPENDSME_DSME_SETTINGS_H */
/** @} */

View File

@ -0,0 +1,284 @@
/*
* Copyright (C) 2022 HAW Hamburg
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @defgroup pkg_opendsme openDSME - IEEE 802.15.4 DSME
* @ingroup pkg
* @ingroup net
* @brief Support for IEEE 802.15.4 Deterministic and Synchronous Multi-channel Extension.
*
* @author José I. Álamos <jose.alamos@haw-hamburg.de>
*
* @{
*
* # About
*
* The IEEE 802.15.4 standard with its widespread usage in wireless sensor and
* actuator networks was extended by several techniques that allow reliable data
* transmission for critical applications, such as industrial plants. This
* includes the Deterministic and Synchronous Multi-channel Extension (DSME) that
* allows for distributed assignment of time slots on multiple channels.
*
* RIOT provides DSME support via the open source implementation
* [openDSME](https://opendsme.org/).
*
* # Features
* - Topology agnostic (peer-to-peer, star, mesh, etc).
* - By design, a DSME network can be extended just by adding more coordinators near the edge.
* - Automatic resolution of beacon collision
* - Frame transmission using either CSMA-CA or multi-channel GTS (guaranteed time slot)
* - Built-in slot negotiation (dynamic allocation) and static allocation.
*
* ## Network formation
*
* A PAN coordinator device initializes a DSME superframe structure which consists
* of a series of superframes that repeat indefinitely. A superframe consists
* of a Beacon Slot, a Contention Access Period and Contention Free Period.
*
* BS CAP CFP BS
* +------------------------------------------------------+----
* | | |--|--|--|--|--|--| | ...
* | | |--|--|--|--|--|--| |
* | | |--|--|--|--|--|--| | ...
* | | |--|--|--|--|--|--| |
* +---+--------------------------------+--+--+--+--+--+--+---+
* <------------------- Superframe ----------------------->
*
* Each period of the superframe serves a dedicated purpose:
* - Beacon Slot: Used for beacon transmission. PAN Coordinators and Coordinators
* transmit beacons periodically in these beacon slots. All devices (except the
* PAN coordinator) listen to their parent coordinator beacons and synchronize
* their superframe structure accordingly. Each coordinator (including the PAN
* Coordinator) transmits beacon periocally. To avoid beacon collisions, DSME
* allows to set the beacon interval of all devices in multiples of the
* superframe structure. This allows other coordinator devices to transmit
* beacons in a different superframe offset and therefore avoid beacon
* collisions. The beacon slot has a duration of one slot.
* - Contention Access Period: During CAP, devices transmit using slotted
* CSMA-CA. This means, the CAP is divided in a series of CCA slots. These
* slots are aligned with the beginning of the CAP. A device can transmit
* only if CCA assess clear channel on three consecutive slots. If there's
* not enough CAP time left, the frame will be postponed until the next CAP
* period. On CSMA-CA transmissions, the MAC put the message in a CAP queue
* and transmits as soon as possible. By default, the CAP queue size is 8
* frames. The CAP has a duration of 8 slots
* - Contention Free Period: During CFP devices transmit using a dedicated
* Guaranteed Time Slot. GTS are multi-channel, which allows concurrent
* communication in the same GTS. On transmission schedule, the MAC puts
* the message in a CFP queue and transmit in the next available GTS. By
* default, the CFP queue size is 22. To (de)allocate a GTS, the upper layer
* triggers a GTS Request to negotiate slots with a neighbour device. The GTS
* Request is sent during CAP, as all MAC commands. The CFP has 7
* multichannel GTS (each one with a duration of one slot) and the number of
* channels depends on the underlying PHY (16 for O-QPSK). Note that slots
* are unidirectional (TX or RX only) and cannot transmit neither broadcast
* frames nor MAC commands.
*
* DSME allows to extend the number of GTS resources by combining superframes
* into a multisuperframe structure. The number of superframes per multisuperframes
* is configurable. This extend the number of GTS to seven times the number of
* superframes per multisuperframe.
*
* Joining devices scan for beacons and perform the association procedure with
* the the (PAN) coordinator. On success, the device is ready to communicate with
* other devices in the DSME network.
* To extend the network, coordinator devices can associate to any other
* coordinator (including the PAN coordinator) and start emitting beacons in a
* different Beacon Slot offset. Devices can then join the network via the new
* coordinator. DSME manages beacon scheduling automatically and handles beacon
* collisions accordingly.
*
* ## DSME superframe structure
*
* The DSME superframe structure relies on three configuration parameters:
* Superframe Order (SO), Multisuperframe Order (MO) and Beacon Order (BO).
* These parameters affect the slot duration (and therefore the superframe duration),
* the number of superframes per multisuperframe and the beacon interval.
*
* The DSME standard defines the slot duration as
* `slot_duration = aBaseSlotDuration * aSymbolDuration * 2^SO`. and the superframe
* duration as "sf_duration = 16 * slot_duration".
* `aBaseSlotDuration=60` as per standard and `aSymbolDuration` (us) depends on the
* underlying PHY (16 for O-QPSK).
*
* The number of superframes per multisuperframe is given by
* `sf_per_msf=2^(MO-SO)`.
*
* The beacon interval (in number of multisuperframes) is given by
* `msf_per_bi=2^(BO-SO)`
*
* For example, an O-QPSK PHY with SO=3, MO=4 and BO=5 renders a slot duration
* of 7.68 ms, a superframe duration of 122.8 ms, 2 superframes per multisuperframe
* (msf_duration=245.7 ms) and a beacon interval of 2 multisuperframes
* (beacon_interval=492 ms).
*
* The slot duration trades off maximum frame length and transmission delay during
* GTS transmissions. A value equal or higher than 3 allows transmission of 127 bytes
* frames.
* Increasing the number of superframes per multisuperframe increases
* on one hand transmission delay, because the period of a single GTS is the duration of the
* multisuperframe. On the other hand it decreases energy consumption, because a
* device transmit less often.
* Increasing the beacon interval extends the number of coordinators in wireless
* reach and reduces energy consumption. However, it slows the scanning and joining
* procedure.
*
* The configuration of SO, MO and BO must be compliant with:
* ```
* 0 <= SO <= MO <= BO < 15
* ```
*
* ## CAP Reduction
*
* With the purpose of reducing energy consumption and extending the GTS
* resources even more, the MAC defines a CAP Reduction mode that turns
* all CAP (except the first one in a multisuperframe) into CFP. This adds
* 8 GTS per superframe that reduces CAP.
*
* With CAP Reduction on, increasing the number of superframes per multisuperframe
* improves energy efficiency, because nodes spend less time on CAP. However,
* note that this renders less CAP time which is required for slot negotiation.
*
* ## GTS coordinates
*
* A slot is defined by a superframe ID, a superframe slot and
* a channel offset. The superframe ID is contained in `{0..n-1}`, where
* `n` is the number of superframes per multisuperframe. The ID of the first superframe
* (the one aligned with the start of a multisuperframe) is 0.
*
* The superframe slot values depend on the superframe ID.
* ```
* sf_slot = {0..6} if superframe_ID==0
* sf_slot = {8..14} if superframe_ID!=0 && cap_reduction=0
* sf_slot = {0..14} if superframe_ID!=0 && cap_reduction=1
* ```
*
* The channel offset is `{0..m-1}`, with `m` the number of channels.
*
* # Usage in RIOT
*
* The RIOT implementation uses the `DSMEAdaptionLayer` wrapper internally (provided by
* openDSME) to manage scanning/joining and automatic slot allocation. The MAC
* implementation defines several scheduling strategies, but the default is
* used (Traffic-aware and Predictive Scheduling).
* The port introduces an openDSME GNRC Network Interface, that can be used via
* @ref net_gnrc_netapi and @ref net_gnrc_netreg.
*
* The following network options (@ref net_netopt) are used for DSME setup.
*
* Configuration:
* - @ref NETOPT_PAN_COORD. Set PAN coordinator or Child role
* - @ref NETOPT_GTS_ALLOC. Allocate a GTS slot manually (only available via
* @ref CONFIG_IEEE802154_DSME_STATIC_GTS).
* - @ref NETOPT_GTS_TX. Set the next transmission to either GTS or CSMA-CA.
* - @ref NETOPT_ACK_REQ. Set the ACK Request bit to enable confirmed
* transmissions.
* - @ref NETOPT_LINK. Start the DSME network (PAN Coordinator) or join an
* existing network (Child).
*
* ## MAC configuration
*
*/
#ifndef OPENDSME_OPENDSME_H
#define OPENDSME_OPENDSME_H
#include <stdbool.h>
#include "byteorder.h"
#include "net/gnrc/netif.h"
#include "net/gnrc/pktbuf.h"
/**
* @brief default GNRC Pktsnip type for openDSME packets
*/
#ifndef CONFIG_OPENDSME_GNRC_PKTSNIP_TYPE
#define CONFIG_OPENDSME_GNRC_PKTSNIP_TYPE GNRC_NETTYPE_UNDEF
#endif
/**
* @brief maximum number of DSME neighbours
*/
#ifndef CONFIG_OPENDSME_MAX_NEIGHBOURS
#define CONFIG_OPENDSME_MAX_NEIGHBOURS (20U)
#endif
/**
* @brief maximum number of lost beacons before assuming the device desynchronized.
*
* Sets the maximum number of lost beacons before the MAC triggers a
* de-association procedure. Higher values are beneficial in noisy
* environments, because the MAC will keep synchronization despite losing some
* beacons. However, lower values are better for mobile nodes, because devices
* may sinchronize faster to a new coordinator.
*/
#ifndef CONFIG_OPENDSME_MAX_LOST_BEACONS
#define CONFIG_OPENDSME_MAX_LOST_BEACONS (8U)
#endif
/**
* @brief DSME CAP queue size
*
* The CAP queue stores frames to be sent during the Contention Access Period
* using CSMA-CA. Because the transmission delay of CSMA-CA is lower compared to
* GTS transmissions, small values are preferred to reduce memory requirements.
*/
#ifndef CONFIG_OPENDSME_CAP_QUEUE_SIZE
#define CONFIG_OPENDSME_CAP_QUEUE_SIZE (8U)
#endif
/**
* @brief DSME CFP queue size (for GTS transmissions)
*
* The CFP queue stores frames to be sent during the Contention Free Period
* using a dedicated GTS. In contrast to CSMA-CA transmissions, GTS transmission
* take longer as a result of slot schedules. Therefore, the GTS queue should have
* more capacity than the CAP queue (@ref CONFIG_OPENDSME_CAP_QUEUE_SIZE)
*/
#ifndef CONFIG_OPENDSME_CFP_QUEUE_SIZE
#define CONFIG_OPENDSME_CFP_QUEUE_SIZE (22U)
#endif
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief DSME Allocation descriptor
*/
typedef struct {
network_uint16_t addr; /**< neighbour address */
uint8_t superframe_id; /**< superframe ID */
uint8_t slot_id; /**< slot ID */
uint8_t channel_id; /**< channel ID */
bool tx; /**< whether the GTS is TX */
} ieee802154_dsme_alloc_t;
/**
* @brief Creates an openDSME network interface
*
* @param[out] netif The interface. May not be `NULL`.
* @param[in] stack The stack for the network interface's thread.
* @param[in] stacksize Size of @p stack.
* @param[in] priority Priority for the network interface's thread.
* @param[in] name Name for the network interface. May be NULL.
* @param[in] dev Device for the interface
*
* @see @ref gnrc_netif_create()
*
* @return 0 on success
* @return negative number on error
*/
int gnrc_netif_opendsme_create(gnrc_netif_t *netif, char *stack, int stacksize,
char priority, const char *name, netdev_t *dev);
#ifdef __cplusplus
}
#endif
#endif /* OPENDSME_OPENDSME_H */
/** @} */

View File

@ -828,6 +828,25 @@ typedef enum {
* @brief (array of byte arrays) Leave an link layer multicast group
*/
NETOPT_L2_GROUP_LEAVE,
/**
* @brief (netopt_enable_t) Set/Get PAN coordinator role
*/
NETOPT_PAN_COORD,
/**
* @brief (ieee802154_dsme_alloc_t) Allocate DSME GTS slot
*/
NETOPT_GTS_ALLOC,
/**
* @brief (netopt_enable_t) Transmit frames using GTS transmission
*
* When set, frames are sent using a Guaranteed Time Slot (GTS). Otherwise
* with CSMA/CA.
*/
NETOPT_GTS_TX,
/**
* @brief maximum number of options defined here.
*

View File

@ -133,6 +133,9 @@ static const char *_netopt_strmap[] = {
[NETOPT_BATMON] = "NETOPT_BATMON",
[NETOPT_L2_GROUP] = "NETOPT_L2_GROUP",
[NETOPT_L2_GROUP_LEAVE] = "NETOPT_L2_GROUP_LEAVE",
[NETOPT_PAN_COORD] = "NETOPT_PAN_COORD",
[NETOPT_GTS_ALLOC] = "NETOPT_GTS_ALLOC",
[NETOPT_GTS_TX] = "NETOPT_GTS_TX",
[NETOPT_NUMOF] = "NETOPT_NUMOF",
};