diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index bfe22076e8..9364ccce7d 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -376,6 +376,7 @@ PSEUDOMODULES += newlib_nano PSEUDOMODULES += nice ## @} PSEUDOMODULES += nrf24l01p_ng_diagnostics +PSEUDOMODULES += opendsme PSEUDOMODULES += openthread PSEUDOMODULES += picolibc PSEUDOMODULES += picolibc_stdout_buffered diff --git a/pkg/opendsme/Kconfig b/pkg/opendsme/Kconfig new file mode 100644 index 0000000000..afbc1f892a --- /dev/null +++ b/pkg/opendsme/Kconfig @@ -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 diff --git a/pkg/opendsme/Makefile b/pkg/opendsme/Makefile new file mode 100644 index 0000000000..a446542724 --- /dev/null +++ b/pkg/opendsme/Makefile @@ -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.$@ diff --git a/pkg/opendsme/Makefile.dep b/pkg/opendsme/Makefile.dep new file mode 100644 index 0000000000..a32697b3c4 --- /dev/null +++ b/pkg/opendsme/Makefile.dep @@ -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 ) + + +# Disable Auto-ACK (not supported by openDSME) +CFLAGS += -DCONFIG_IEEE802154_AUTO_ACK_DISABLE=1 + +USEMODULE += random diff --git a/pkg/opendsme/Makefile.include b/pkg/opendsme/Makefile.include new file mode 100644 index 0000000000..ee5671f31a --- /dev/null +++ b/pkg/opendsme/Makefile.include @@ -0,0 +1,4 @@ +INCLUDES += -I$(PKGDIRBASE)/opendsme +INCLUDES += -I$(RIOTBASE)/pkg/opendsme/include + +DIRS += $(RIOTBASE)/pkg/opendsme/contrib diff --git a/pkg/opendsme/Makefile.opendsme_acklayer b/pkg/opendsme/Makefile.opendsme_acklayer new file mode 100644 index 0000000000..9c32047c29 --- /dev/null +++ b/pkg/opendsme/Makefile.opendsme_acklayer @@ -0,0 +1,3 @@ +MODULE = opendsme_acklayer + +include $(RIOTBASE)/Makefile.base diff --git a/pkg/opendsme/Makefile.opendsme_adaption_layer b/pkg/opendsme/Makefile.opendsme_adaption_layer new file mode 100644 index 0000000000..d142303551 --- /dev/null +++ b/pkg/opendsme/Makefile.opendsme_adaption_layer @@ -0,0 +1,3 @@ +MODULE = opendsme_adaption_layer + +include $(RIOTBASE)/Makefile.base diff --git a/pkg/opendsme/Makefile.opendsme_adaption_layer_scheduling b/pkg/opendsme/Makefile.opendsme_adaption_layer_scheduling new file mode 100644 index 0000000000..e89e3dc5d4 --- /dev/null +++ b/pkg/opendsme/Makefile.opendsme_adaption_layer_scheduling @@ -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 diff --git a/pkg/opendsme/Makefile.opendsme_associationmanager b/pkg/opendsme/Makefile.opendsme_associationmanager new file mode 100644 index 0000000000..3e7381434f --- /dev/null +++ b/pkg/opendsme/Makefile.opendsme_associationmanager @@ -0,0 +1,3 @@ +MODULE = opendsme_associationmanager + +include $(RIOTBASE)/Makefile.base diff --git a/pkg/opendsme/Makefile.opendsme_beaconmanager b/pkg/opendsme/Makefile.opendsme_beaconmanager new file mode 100644 index 0000000000..ca9e5f8be4 --- /dev/null +++ b/pkg/opendsme/Makefile.opendsme_beaconmanager @@ -0,0 +1,3 @@ +MODULE = opendsme_beaconmanager + +include $(RIOTBASE)/Makefile.base diff --git a/pkg/opendsme/Makefile.opendsme_caplayer b/pkg/opendsme/Makefile.opendsme_caplayer new file mode 100644 index 0000000000..67b24ce39c --- /dev/null +++ b/pkg/opendsme/Makefile.opendsme_caplayer @@ -0,0 +1,3 @@ +MODULE = opendsme_caplayer + +include $(RIOTBASE)/Makefile.base diff --git a/pkg/opendsme/Makefile.opendsme_datastructures b/pkg/opendsme/Makefile.opendsme_datastructures new file mode 100644 index 0000000000..c4593fb32b --- /dev/null +++ b/pkg/opendsme/Makefile.opendsme_datastructures @@ -0,0 +1,3 @@ +MODULE = opendsme_datastructures + +include $(RIOTBASE)/Makefile.base diff --git a/pkg/opendsme/Makefile.opendsme_dsmelayer b/pkg/opendsme/Makefile.opendsme_dsmelayer new file mode 100644 index 0000000000..42f669d5f3 --- /dev/null +++ b/pkg/opendsme/Makefile.opendsme_dsmelayer @@ -0,0 +1,3 @@ +MODULE = opendsme_dsmelayer + +include $(RIOTBASE)/Makefile.base diff --git a/pkg/opendsme/Makefile.opendsme_gtsmanager b/pkg/opendsme/Makefile.opendsme_gtsmanager new file mode 100644 index 0000000000..c335ef4fde --- /dev/null +++ b/pkg/opendsme/Makefile.opendsme_gtsmanager @@ -0,0 +1,3 @@ +MODULE = opendsme_gtsmanager + +include $(RIOTBASE)/Makefile.base diff --git a/pkg/opendsme/Makefile.opendsme_mcps_sap b/pkg/opendsme/Makefile.opendsme_mcps_sap new file mode 100644 index 0000000000..f039366fcc --- /dev/null +++ b/pkg/opendsme/Makefile.opendsme_mcps_sap @@ -0,0 +1,3 @@ +MODULE = opendsme_mcps_sap + +include $(RIOTBASE)/Makefile.base diff --git a/pkg/opendsme/Makefile.opendsme_messagedispatcher b/pkg/opendsme/Makefile.opendsme_messagedispatcher new file mode 100644 index 0000000000..fba104bc2b --- /dev/null +++ b/pkg/opendsme/Makefile.opendsme_messagedispatcher @@ -0,0 +1,3 @@ +MODULE = opendsme_messagedispatcher + +include $(RIOTBASE)/Makefile.base diff --git a/pkg/opendsme/Makefile.opendsme_messages b/pkg/opendsme/Makefile.opendsme_messages new file mode 100644 index 0000000000..4c61271a06 --- /dev/null +++ b/pkg/opendsme/Makefile.opendsme_messages @@ -0,0 +1,3 @@ +MODULE = opendsme_messages + +include $(RIOTBASE)/Makefile.base diff --git a/pkg/opendsme/Makefile.opendsme_mlme_sap b/pkg/opendsme/Makefile.opendsme_mlme_sap new file mode 100644 index 0000000000..c6bd51a13a --- /dev/null +++ b/pkg/opendsme/Makefile.opendsme_mlme_sap @@ -0,0 +1,3 @@ +MODULE = opendsme_mlme_sap + +include $(RIOTBASE)/Makefile.base diff --git a/pkg/opendsme/Makefile.opendsme_pib b/pkg/opendsme/Makefile.opendsme_pib new file mode 100644 index 0000000000..0e2f00ca4a --- /dev/null +++ b/pkg/opendsme/Makefile.opendsme_pib @@ -0,0 +1,3 @@ +MODULE = opendsme_pib + +include $(RIOTBASE)/Makefile.base diff --git a/pkg/opendsme/contrib/DSMEMessage.cpp b/pkg/opendsme/contrib/DSMEMessage.cpp new file mode 100644 index 0000000000..3194e3dc4c --- /dev/null +++ b/pkg/opendsme/contrib/DSMEMessage.cpp @@ -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 + */ +#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(pkt->data)); + + pkt->type = CONFIG_OPENDSME_GNRC_PKTSNIP_TYPE; + gnrc_netif_hdr_t *hdr = static_cast(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(); +} +} + +/** @} */ diff --git a/pkg/opendsme/contrib/DSMEPlatform.cpp b/pkg/opendsme/contrib/DSMEPlatform.cpp new file mode 100644 index 0000000000..a70f613d7c --- /dev/null +++ b/pkg/opendsme/contrib/DSMEPlatform.cpp @@ -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 + */ + +#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(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(msg)); +} + +void DSMEPlatform::handleConfirmFromMCPSWrapper(IDSMEMessage *msg, + DataStatus::Data_Status dataStatus) +{ + this->handleConfirmFromMCPS(static_cast(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(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(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 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 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; +} + +} + +/** @} */ diff --git a/pkg/opendsme/contrib/Makefile b/pkg/opendsme/contrib/Makefile new file mode 100644 index 0000000000..d0b9f36296 --- /dev/null +++ b/pkg/opendsme/contrib/Makefile @@ -0,0 +1,3 @@ +MODULE = opendsme_riot_contrib + +include $(RIOTBASE)/Makefile.base diff --git a/pkg/opendsme/contrib/gnrc_netif_opendsme.cpp b/pkg/opendsme/contrib/gnrc_netif_opendsme.cpp new file mode 100644 index 0000000000..373ee933ca --- /dev/null +++ b/pkg/opendsme/contrib/gnrc_netif_opendsme.cpp @@ -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 + */ + +#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(&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(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); +} +} + +/** @} */ diff --git a/pkg/opendsme/include/opendsme/DSMEMessage.h b/pkg/opendsme/include/opendsme/DSMEMessage.h new file mode 100644 index 0000000000..7378a951ed --- /dev/null +++ b/pkg/opendsme/include/opendsme/DSMEMessage.h @@ -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 + */ + +#ifndef OPENDSME_DSMEMESSAGE_H +#define OPENDSME_DSMEMESSAGE_H + +#include +#include + +#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 */ +/** @} */ diff --git a/pkg/opendsme/include/opendsme/DSMEPlatform.h b/pkg/opendsme/include/opendsme/DSMEPlatform.h new file mode 100644 index 0000000000..9c163db699 --- /dev/null +++ b/pkg/opendsme/include/opendsme/DSMEPlatform.h @@ -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 + */ + +#ifndef OPENDSME_DSMEPLATFORM_H +#define OPENDSME_DSMEPLATFORM_H + +#include +#include + +#include +#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 txEndCallback) override; + + /** + * @brief prepare the next transmission + */ + bool prepareSendingCopy(DSMEMessage* msg, Delegate 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 txEndCallback) override; + /** + * @brief send an ACK message, delay until aTurnaRoundTime after reception_time has expired + */ + bool sendDelayedAck(DSMEMessage *ackMsg, DSMEMessage *receivedMsg, + Delegate 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 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 */ +/** @} */ diff --git a/pkg/opendsme/include/opendsme/dsme_atomic.h b/pkg/opendsme/include/opendsme/dsme_atomic.h new file mode 100644 index 0000000000..88578188d0 --- /dev/null +++ b/pkg/opendsme/include/opendsme/dsme_atomic.h @@ -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 + */ + +#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 +/** @} */ diff --git a/pkg/opendsme/include/opendsme/dsme_platform.h b/pkg/opendsme/include/opendsme/dsme_platform.h new file mode 100644 index 0000000000..32fde2b997 --- /dev/null +++ b/pkg/opendsme/include/opendsme/dsme_platform.h @@ -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 + */ + +#ifndef OPENDSME_DSME_PLATFORM_H +#define OPENDSME_DSME_PLATFORM_H + +#include +#include + +#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 */ +/** @} */ diff --git a/pkg/opendsme/include/opendsme/dsme_settings.h b/pkg/opendsme/include/opendsme/dsme_settings.h new file mode 100644 index 0000000000..335ffba74f --- /dev/null +++ b/pkg/opendsme/include/opendsme/dsme_settings.h @@ -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 + */ +#ifndef OPENDSME_DSME_SETTINGS_H +#define OPENDSME_DSME_SETTINGS_H + +#include + +#include +#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 */ +/** @} */ diff --git a/pkg/opendsme/include/opendsme/opendsme.h b/pkg/opendsme/include/opendsme/opendsme.h new file mode 100644 index 0000000000..cbcbbc6d6d --- /dev/null +++ b/pkg/opendsme/include/opendsme/opendsme.h @@ -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 + * + * @{ + * + * # 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 +#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 */ + +/** @} */ diff --git a/sys/include/net/netopt.h b/sys/include/net/netopt.h index 561fe19ee6..8f54be61cc 100644 --- a/sys/include/net/netopt.h +++ b/sys/include/net/netopt.h @@ -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. * diff --git a/sys/net/crosslayer/netopt/netopt.c b/sys/net/crosslayer/netopt/netopt.c index 22ca26515f..69bfddb8f2 100644 --- a/sys/net/crosslayer/netopt/netopt.c +++ b/sys/net/crosslayer/netopt/netopt.c @@ -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", };