diff --git a/Makefile.dep b/Makefile.dep index 76034c5d60e7180bd455e9f9aac656c57f065a2a..4247ce5d71ae992bbe28cb91fb57acac335b39f3 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -24,6 +24,14 @@ ifneq (,$(filter gnrc_mac,$(USEMODULE))) USEMODULE += csma_sender endif +ifneq (,$(filter gnrc_gomach,$(USEMODULE))) + USEMODULE += gnrc_netif + USEMODULE += random + USEMODULE += xtimer + USEMODULE += gnrc_mac + FEATURES_REQUIRED += periph_rtt +endif + ifneq (,$(filter nhdp,$(USEMODULE))) USEMODULE += sock_udp USEMODULE += xtimer diff --git a/examples/gnrc_networking_mac/Makefile b/examples/gnrc_networking_mac/Makefile index 2cd325938fe9e2378384c81c9aef051d8b79e315..c3825deff9c0874d7c8c75842c8b0bf787327d28 100644 --- a/examples/gnrc_networking_mac/Makefile +++ b/examples/gnrc_networking_mac/Makefile @@ -4,10 +4,12 @@ APPLICATION = gnrc_networking_mac # If no BOARD is found in the environment, use this default: BOARD ?= samr21-xpro -# Currently, LWMAC is only tested and evaluated through on samr21-xpro. -# Once LWMAC has also been tested through on other boards, the whitelist should be +# Currently, GoMacH has only been tested and evaluated through on samr21-xpro and iotlab-m3. +# Once GoMacH has also been tested through on other boards, the whitelist should be # then accordingly extended. -BOARD_WHITELIST := samr21-xpro +# Notably, for LWMAC, we have only evaluated it on samr21-xpro nodes. To this end, if +# you are going to include LWMAC for testing, you should only run the example on samr21-xpro. +BOARD_WHITELIST := samr21-xpro iotlab-m3 # This has to be the absolute path to the RIOT base directory: RIOTBASE ?= $(CURDIR)/../.. @@ -33,8 +35,10 @@ USEMODULE += ps USEMODULE += netstats_l2 USEMODULE += netstats_ipv6 USEMODULE += netstats_rpl -# Use LWMAC as the MAC layer protocol -USEMODULE += gnrc_lwmac +# Use GoMacH as the MAC layer protocol +USEMODULE += gnrc_gomach +# In case of using LWMAC MAC protocol instead of GoMacH, uncomment the following line and comment the above line +# USEMODULE += gnrc_lwmac # Comment this out to disable code in RIOT that does safety checking # which is not needed in a production environment but helps in the diff --git a/examples/gnrc_networking_mac/README.md b/examples/gnrc_networking_mac/README.md index 79dc962fd7d2117e4a1dc25dc2dd15145f7b4b2d..6a1a9e0aa6fd9fba0821d9a41d243260cf4622cd 100644 --- a/examples/gnrc_networking_mac/README.md +++ b/examples/gnrc_networking_mac/README.md @@ -1,8 +1,8 @@ # gnrc_networking_mac example -This example shows you how to try out communications between RIOT instances with LWMAC as the MAC layer ptotocol for IEEE 802.15.4 devices. -This example is generally based on `gnrc_networking` but embeds LWMAC to support low duty-cycle operation to conserve power. Also, it intends to show that the duty-cycled LWMAC can support popular upper layer protocols like UDP and RPL. -Currently, it seems that you can only use the samr21-xpro board to test this MAC, since some certain features of the protocol are only available on that platform. Also, the current implementation of LWMAC uses RTT as the underlying timer source. So, currently, LWMAC cannot run on nodes that don't have RTT. But, as a long-term plan, we will replace RTT by a general timer API as the underlying timer to make LWMAC available for more devices, when the related implementations are ready. +This example shows you how to try out communications between RIOT instances with duty-cycled MAC layer protocols (GoMacH and LWMAC) for IEEE 802.15.4 devices. +This example is generally based on `gnrc_networking` but embeds GoMacH (or LWMAC) to support low duty-cycle operation to conserve power. Also, it intends to show that the duty-cycled MAC protocol can support popular upper layer protocols like UDP and RPL. +Currently, it seems that you can only use samr21-xpro and iotlab-m3 boards (in case of using LWMAC, then only samr21-xpro board) to test with this example, since some certain features of the MAC protocol are only available on that platform. Also, the current implementations of GoMacH and LWMAC use RTT as the underlying timer source. So, currently, GoMacH and LWMAC cannot run on nodes that don't have RTT. But, as a long-term plan, we will replace RTT by a general timer API as the underlying timer to make GoMacH and LWMAC available for more devices, when the related implementations are ready. ## Usage @@ -15,13 +15,16 @@ make flash make term ``` +## Print out the achieved duty-cyle of GoMacH + +You can print out the radio duty-cyle (a roughly one) of GoMacH by setting the `GNRC_GOMACH_ENABLE_DUTYCYLE_RECORD` flag in `sys/include/net/gnrc/gomach/types.h` to "1". By doing so, each time when a device sends or receives a packet, it will print out its achieved radio duty-cycle value. + ## Print out the achieved duty-cyle of LWMAC -You can print out the radio duty-cyle (a roughly one) of LWMAC by setting the `LWMAC_ENABLE_DUTYCYLE_RECORD` flag in `sys/include/net/gnrc/lwmac/types.h` to "1". By doing so, each time when a device sends or receives a packet, it will print out its radio duty-cycle value. +You can print out the radio duty-cyle (a roughly one) of LWMAC by setting the `LWMAC_ENABLE_DUTYCYLE_RECORD` flag in `sys/include/net/gnrc/lwmac/types.h` to "1". By doing so, each time when a device sends or receives a packet, it will print out its achieved radio duty-cycle value. Also, by further enabling the debug flag in `sys/net/gnrc/link_layer/lwmac/tx_state_machine.c`, you will get the printout of how many preamble (WR) and time (sending delay) cost for sending this packet in the TX procedure of LWMAC. - -## Try UDP transmissions with LWMAC +## Try UDP transmissions with LWMAC (or GoMacH) In the RIOT shell, get to know the IP address of one node: diff --git a/examples/gnrc_networking_mac/main.c b/examples/gnrc_networking_mac/main.c index 6301f4291d8a3f83bfe07c93434d93c917724049..4b3449bf9138afb5cb96a724a1ebf54cb78135e8 100644 --- a/examples/gnrc_networking_mac/main.c +++ b/examples/gnrc_networking_mac/main.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2015 Freie Universität Berlin + * 2017 INRIA * * 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 @@ -14,6 +15,7 @@ * @brief Example application for demonstrating the RIOT network stack * * @author Hauke Petersen <hauke.petersen@fu-berlin.de> + * @author Shuguo Zhuo <shuguo.zhuo@inria.fr> * * @} */ diff --git a/sys/auto_init/netif/auto_init_at86rf2xx.c b/sys/auto_init/netif/auto_init_at86rf2xx.c index 59dbf2cdad9b6f55bea736e84ecb1fedd97ced5c..6f66879aa5314833255d8ab26236bfd7bf5a190b 100644 --- a/sys/auto_init/netif/auto_init_at86rf2xx.c +++ b/sys/auto_init/netif/auto_init_at86rf2xx.c @@ -25,6 +25,9 @@ #ifdef MODULE_GNRC_LWMAC #include "net/gnrc/lwmac/lwmac.h" #endif +#ifdef MODULE_GNRC_GOMACH +#include "net/gnrc/gomach/gomach.h" +#endif #include "net/gnrc.h" #include "at86rf2xx.h" @@ -50,7 +53,12 @@ void auto_init_at86rf2xx(void) LOG_DEBUG("[auto_init_netif] initializing at86rf2xx #%u\n", i); at86rf2xx_setup(&at86rf2xx_devs[i], &at86rf2xx_params[i]); -#ifdef MODULE_GNRC_LWMAC +#if defined(MODULE_GNRC_GOMACH) + gnrc_netif_gomach_create(_at86rf2xx_stacks[i], + AT86RF2XX_MAC_STACKSIZE, + AT86RF2XX_MAC_PRIO, "at86rf2xx-gomach", + (netdev_t *)&at86rf2xx_devs[i]); +#elif defined(MODULE_GNRC_LWMAC) gnrc_netif_lwmac_create(_at86rf2xx_stacks[i], AT86RF2XX_MAC_STACKSIZE, AT86RF2XX_MAC_PRIO, "at86rf2xx-lwmac", diff --git a/sys/include/net/gnrc/gomach/gomach.h b/sys/include/net/gnrc/gomach/gomach.h new file mode 100644 index 0000000000000000000000000000000000000000..a902f0f7dd386483a15ed033b1a5d4bb9f32c100 --- /dev/null +++ b/sys/include/net/gnrc/gomach/gomach.h @@ -0,0 +1,353 @@ +/* + * Copyright (C) 2017 INRIA + * + * 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 net_gnrc_gomach A traffic-adaptive multi-channel MAC + * @ingroup net_gnrc + * @brief A traffic adaptive MAC protocol that provides high traffic + * adaptability, high energy efficiency and high robustness. + * @{ + * + * @file + * @brief Implementation of GoMacH protocol + * + * @author Shuguo Zhuo <shuguo.zhuo@inria.fr> + */ + +#ifndef NET_GNRC_GOMACH_GOMACH_H +#define NET_GNRC_GOMACH_GOMACH_H + +#include "periph/rtt.h" +#include "kernel_types.h" +#include "net/gnrc/netif.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief The default duration of GoMacH's wake-up period (WP). + * + * GoMacH adopts the duty-cycle scheme that, by default, a node only wakes up + * for a short period of @ref GNRC_GOMACH_CP_DURATION_US in each cycle. In the + * rest of the cycle (except vTDMA), the node turns off the radio to conserve + * power. @ref GNRC_GOMACH_CP_DURATION_US should be at least longer than + * @ref GNRC_GOMACH_MAX_PREAM_INTERVAL_US, thus to guarantee that the receiver + * will not miss the preamble packet. + */ +#ifndef GNRC_GOMACH_CP_DURATION_US +#define GNRC_GOMACH_CP_DURATION_US (10U * US_PER_MS) +#endif + +/** + * @brief GoMacH's superframe duration, i.e., time between two consecutive wake-ups. + * + * This macro governs power consumption and GoMacH's reactiveness to traffic loads. + * In GoMacH, nodes adopt duty-cycle scheme to conserve power. That is, + * time is divided into repeated cycles (superframes), and in each + * cycle, a node only wakes up for a short period of time for receiving potential + * incoming packets for itself. This macro defines the wake-up interval, or, + * in other words, defines the cycle duration used in GoMacH. If the wake-up + * interval is short, nodes will wake up more frequently, which leads to quicker + * reactiveness of the MAC protocol for handling packet reception and transmission, + * but also results in higher power consumption due to more idle listening. + * In GoMacH, by default, we regard the wake-up period (WP) as the beginning of + * a cycle. + * + * Note that, GoMacH's superframe duration @ref GNRC_GOMACH_SUPERFRAME_DURATION_US + * should not be shorter than 10 times of @ref GNRC_GOMACH_CP_DURATION_US and not + * shorter than the RTT tickle interval. + */ +#ifndef GNRC_GOMACH_SUPERFRAME_DURATION_US +#define GNRC_GOMACH_SUPERFRAME_DURATION_US (300LU * US_PER_MS) +#endif + +#if ((GNRC_GOMACH_SUPERFRAME_DURATION_US < ((1000LU *US_PER_MS) / RTT_FREQUENCY)) || \ + (GNRC_GOMACH_SUPERFRAME_DURATION_US < (10 *GNRC_GOMACH_CP_DURATION_US))) +#undef GNRC_GOMACH_SUPERFRAME_DURATION_US +#if (((1000LU *US_PER_MS) / RTT_FREQUENCY) > (10 * GNRC_GOMACH_CP_DURATION_US)) +#define GNRC_GOMACH_SUPERFRAME_DURATION_US ((1000LU * US_PER_MS) / RTT_FREQUENCY) +#else +#define GNRC_GOMACH_SUPERFRAME_DURATION_US (10 * GNRC_GOMACH_CP_DURATION_US) +#endif +#endif + +/** + * @brief The maximum duration of the random period at the end of GoMacH's + * wake-up period (WP). + * + * Currently, GoMacH's WP is actually composed of @ref GNRC_GOMACH_CP_DURATION_US + * and (+) @ref GNRC_GOMACH_CP_RANDOM_END_US. We currently introduced this random + * period to avoid beacon collision among neighbor nodes. This macro may be removed + * in the future. + */ +#ifndef GNRC_GOMACH_CP_RANDOM_END_US +#define GNRC_GOMACH_CP_RANDOM_END_US (1U * US_PER_MS) +#endif + +/** + * @brief The maximum duration of GoMacH's wake-up period (WP). + * + * @ref GNRC_GOMACH_CP_DURATION_MAX_US defines the allowed maximum duration + * of GoMacH's WP period. A node will quit WP once it reaches this maximum + * duration. + * Note that, in GoMacH's WP, after each normal packet reception (except + * broadcast packet), a receiver will automatically extends the WP period + * (reset WP timeout), to receiver more potential incoming packets, before + * WP reaches this @ref GNRC_GOMACH_CP_DURATION_MAX_US duration. + */ +#ifndef GNRC_GOMACH_CP_DURATION_MAX_US +#define GNRC_GOMACH_CP_DURATION_MAX_US (5LU * GNRC_GOMACH_CP_DURATION_US) +#endif + +/** + * @brief The maximum time for waiting the receiver's beacon in GoMacH. + * + * After transmissions in the WP, if the sender still has pending packets + * for the receiver, it will wait for the receiver's incoming beacon that + * allocates dynamic transmission slots to it. @ref GNRC_GOMACH_WAIT_BEACON_TIME_US + * defines the maximum waiting time for the beacon. Once the beacon-waiting + * timeout expires, the sender will quit the vTMDA (slotted transmission) + * procedure, and restarts transmissions (started with normal CSMA attempts + * in the receiver's WP) in the next cycle for sending the pending packets + * to the same receiver. + */ +#ifndef GNRC_GOMACH_WAIT_BEACON_TIME_US +#define GNRC_GOMACH_WAIT_BEACON_TIME_US (GNRC_GOMACH_CP_DURATION_MAX_US) +#endif + +/** + * @brief The minimum gap between neighbor nodes' wake-up phases in GoMacH. + * + * To reduce beacon collisions and transmission collisions, GoMacH intends + * to avoid neighbor nodes' phases being too close to each other. This macro + * defines the minimum gap between two nodes's wake-up phases. If the sender + * finds its wake-up phase too closed to its receiver's, it will randomly + * select a new phase for itself. + */ +#ifndef GNRC_GOMACH_CP_MIN_GAP_US +#define GNRC_GOMACH_CP_MIN_GAP_US (25U * US_PER_MS) +#endif + +/** + * @brief Timeout duration for waiting @ref NETDEV_EVENT_RX_COMPLETE in GoMacH. + * + * Sometimes in GoMacH, if a node finds RX ongoing when it is just about to + * enter the next MAC state, it will set up a timeout for waiting this packet + * reception complete with a timeout of this @ref GNRC_GOMACH_WAIT_RX_END_US + * duration. + */ +#ifndef GNRC_GOMACH_WAIT_RX_END_US +#define GNRC_GOMACH_WAIT_RX_END_US (6U * US_PER_MS) +#endif + +/** + * @brief Timeout duration for confirming TX-No-ISR event in GoMacH. + * + * This macro is used to confirm/catch a case that a transmission doesn't have its + * @ref NETDEV_EVENT_TX_COMPLETE interrupt event, which is considered as a hardware + * abnormal event. Upon this timeout expiration, GoMach will accordingly take + * actions to maintain its state-machine. + */ +#ifndef GNRC_GOMACH_NO_TX_ISR_US +#define GNRC_GOMACH_NO_TX_ISR_US (50U * US_PER_MS) +#endif + +/** + * @brief Maximum time interval between two consecutive preamble packets in GoMacH. + * + * In GoMacH, a sender first uses preamble stream to track the receiver's wake-up + * phase (WP), if the receiver's WP is unknown. This macro defines the maximum + * time interval between twoconsecutive preamble packets. + */ +#ifndef GNRC_GOMACH_MAX_PREAM_INTERVAL_US +#define GNRC_GOMACH_MAX_PREAM_INTERVAL_US (6U * US_PER_MS) +#endif + +/** + * @brief Time interval between two consecutive preamble packets in GoMacH. + * + * In GoMacH, after a preamble is sent, the sender sets a timeout with + * @ref GNRC_GOMACH_PREAMBLE_INTERVAL_US duration for waiting to send the next + * preamble. Notably, this macro is with a very small value. In GoMacH, for + * receiving the preamble-ACK packet, the sender doesn't wait for the whole + * reception of the preamble-ACK. Instead, it only waits for the + * @ref NETDEV_EVENT_RX_STARTED event which leads to shorter time interval + * between two consecutive preamble transmissions. + */ +#ifndef GNRC_GOMACH_PREAMBLE_INTERVAL_US +#define GNRC_GOMACH_PREAMBLE_INTERVAL_US (2U * US_PER_MS) +#endif + +/** + * @brief Time interval between two consecutive broadcast packets in GoMacH. + * + * In GoMacH, when sending a broadcast packet, the sender broadcasts the same + * packet frame on its two public channels simultaneously, with a total duration + * of @ref GNRC_GOMACH_SUPERFRAME_DURATION_US to guarantee that all neighbors + * will get a copy. This macro defines the time interval between sending two + * consecutive broadcast copies. + */ +#ifndef GNRC_GOMACH_BCAST_INTERVAL_US +#define GNRC_GOMACH_BCAST_INTERVAL_US (1U * US_PER_MS) +#endif + +/** + * @brief The Maximum preamble duration time of GoMacH. + * + * Since GoMacH adopts duty-cycle scheme, to probe receiver's wake-up period + * a sender sends preamble streams to notice the receiver for communication. + * To ensure that the receiver will catch at least one preamble packet + * in a critical case that one public channel is jammed, the sender repeatedly + * broadcasts a stream of preamble packets with the broadcast duration + * (preamble duration) slightly longer than twice + * of @ref GNRC_GOMACH_SUPERFRAME_DURATION_US. + */ +#ifndef GNRC_GOMACH_PREAMBLE_DURATION_US +#define GNRC_GOMACH_PREAMBLE_DURATION_US (21LU * GNRC_GOMACH_SUPERFRAME_DURATION_US / 10) +#endif + +/** + * @brief The transmission slot size in GoMacH. + * + * GoMacH adopts dynamic slots allocation scheme to allocate transmission + * slots to senders that have pending packets. Each slot is for one data packet + * with ACK transmission. @ref GNRC_GOMACH_VTDMA_SLOT_SIZE_US is right sufficient + * for the transmission of the longest packet in IEEE 802.15.4 with ACK. Should + * not be changed. + */ +#ifndef GNRC_GOMACH_VTDMA_SLOT_SIZE_US +#define GNRC_GOMACH_VTDMA_SLOT_SIZE_US (5U * US_PER_MS) +#endif + +/** + * @brief Maximum times of CSMA TX attempts under busy-indication in the WP + * period of the receiver. + * + * Senders in GoMacH adopt CSMA scheme to send data packets in the WP period of + * the receiver. In case of having medium-busy feedback in WP and the TX failure + * count (due to busy) is below @ref GNRC_GOMACH_TX_BUSY_THRESHOLD, the sender + * continue to send the packet with CSMAin the receiver's WP, with the consideration + * that there may be multi-senderssimultaneously competing in WP and the WP will + * be continuously extended (thus the packet can be received). + */ +#ifndef GNRC_GOMACH_TX_BUSY_THRESHOLD +#define GNRC_GOMACH_TX_BUSY_THRESHOLD (5U) +#endif + +/** + * @brief Maximum WP period extension number in GoMacH. + * + * In GoMacH, the WP period of a receiver will be extended upon each successful + * packet reception (except receiving broadcast or preamble packet) to receive + * more potentialincoming packets. This macro defines the maximum WP period + * extension number allowed in GoMacH. + */ +#ifndef GNRC_GOMACH_CP_EXTEND_THRESHOLD +#define GNRC_GOMACH_CP_EXTEND_THRESHOLD (5U) +#endif + +/** + * @brief GoMacH's check-duplicate-packet unit life time in cycle count. + * + * In GoMacH, to avoid receiving duplicate-packet, we currently introduce a + * data type of @ref gnrc_gomach_dupchk_unit_t to record the recent senders' + * information (especially MAC TX sequence). This macro defines the + * check-duplicate-packet data unit's life time in cycle count. Once expired, + * the related data unit will be reset. This macro maybe removed in the future. + */ +#ifndef GNRC_GOMACH_RX_DUPCHK_UNIT_LIFE +#define GNRC_GOMACH_RX_DUPCHK_UNIT_LIFE (30U) +#endif + +/** + * @brief Maximum number of slots allowed to be allocated in one cycle. + * + * GoMacH dynamically allocates transmission slots to senders that have + * pending packets.This macro defines the maximum number of slots allowed + * to be allocated in one cycle. + */ +#ifndef GNRC_GOMACH_MAX_ALLOC_SLOTS_NUM +#define GNRC_GOMACH_MAX_ALLOC_SLOTS_NUM (25U) +#endif + +/** + * @brief Maximum t2k attempts before going to t2u in GoMacH. + * + * After phase-locked with the receiver, a sender runs a t2k (transmit-to-known) + * procedure to transmit packet to the phase-known device. However, due to + * factors like timer driftor busy-channel, a transmission attempt may fail + * in t2k. If the t2k failure count has reached this + * @ref GNRC_GOMACH_REPHASELOCK_THRESHOLD, the sender regards phase-locked failed + * due to timer drift. In this case, it will adopt t2u (transmit-to-unknown) + * procedure to get re-phase-locked with the receiver. + */ +#ifndef GNRC_GOMACH_REPHASELOCK_THRESHOLD +#define GNRC_GOMACH_REPHASELOCK_THRESHOLD (4U) +#endif + +/** + * @brief Maximum t2u attempts before dropping data packet in GoMacH. + * + * In case the receiver's phase is unknown to the sender, the sender adopts + * the t2u (transmit-to-unknown) procedure to get phase-locked with the + * receiver. This macrodefines the maximum t2u attempts before dropping the + * data packet in GoMacH. + */ +#ifndef GNRC_GOMACH_T2U_RETYR_THRESHOLD +#define GNRC_GOMACH_T2U_RETYR_THRESHOLD (2U) +#endif + +/** + * @brief Maximum t2u attempts before re-initiate radio in GoMacH. + * + * After a long period of run time, a radio may be in wrong condition which + * needs to be re-calibrated. This is indicated by having a series of + * continuous t2u failures (no preambleACK) in GoMacH. In cast we have + * @ref GNRC_GOMACH_MAX_T2U_RETYR_THRESHOLD number of t2u failures, then we + * re-initiate the radio, trying to re-calibrate the radio for bringing it + * back to normal condition. + */ +#ifndef GNRC_GOMACH_MAX_T2U_RETYR_THRESHOLD +#define GNRC_GOMACH_MAX_T2U_RETYR_THRESHOLD (10U) +#endif + +/** + * @brief Default message queue size to use for the GoMacH thread. + * + * The value of this macro should be enough for supporting the manipulation of + * GoMacH. + */ +#ifndef GNRC_GOMACH_IPC_MSG_QUEUE_SIZE +#define GNRC_GOMACH_IPC_MSG_QUEUE_SIZE (8U) +#endif + +/** + * @brief Creates an IEEE 802.15.4 GoMacH network interface + * + * @param[in] stack The stack for the GoMacH network interface's thread. + * @param[in] stacksize Size of @p stack. + * @param[in] priority Priority for the GoMacH network interface's thread. + * @param[in] name Name for the GoMacH network interface. May be NULL. + * @param[in] dev Device for the interface + * + * @see @ref gnrc_netif_create() + * + * @return The network interface on success. + * @return NULL, on error. + */ +gnrc_netif_t *gnrc_netif_gomach_create(char *stack, int stacksize, + char priority, char *name, + netdev_t *dev); + +#ifdef __cplusplus +} +#endif + +#endif /* NET_GNRC_GOMACH_GOMACH_H */ +/** @} */ diff --git a/sys/include/net/gnrc/gomach/hdr.h b/sys/include/net/gnrc/gomach/hdr.h new file mode 100644 index 0000000000000000000000000000000000000000..21d5532338134e00138561600f8e8a8f62d67aa4 --- /dev/null +++ b/sys/include/net/gnrc/gomach/hdr.h @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2017 INRIA + * + * 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 net_gnrc_gomach + * @{ + * + * @file + * @brief Header definition of GoMacH + * @internal + * @author Shuguo Zhuo <shuguo.zhuo@inria.fr> + */ + +#ifndef NET_GNRC_GOMACH_HDR_H +#define NET_GNRC_GOMACH_HDR_H + +#include <stdint.h> +#include <stdbool.h> + +#include "net/ieee802154.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief GoMacH internal L2 address structure. + */ +typedef struct { + uint8_t addr[IEEE802154_LONG_ADDRESS_LEN]; /**< Address of node. */ + uint8_t len; /**< Address length. */ +} gnrc_gomach_l2_addr_t; + +/** + * @brief Static initializer for gnrc_gomach_l2_addr_t. + */ +#define GNRC_GOMACH_L2_ADDR_INIT { { 0 }, 0 } + +/** + * @brief GoMacH beacon frame type. + */ +#define GNRC_GOMACH_FRAME_BEACON (0x01U) + +/** + * @brief GoMacH data frame type. + */ +#define GNRC_GOMACH_FRAME_DATA (0x02U) + +/** + * @brief GoMacH preamble frame type. + */ +#define GNRC_GOMACH_FRAME_PREAMBLE (0x03U) + +/** + * @brief GoMacH preamble-ACK frame type. + */ +#define GNRC_GOMACH_FRAME_PREAMBLE_ACK (0x04U) + +/** + * @brief GoMacH broadcast frame type. + */ +#define GNRC_GOMACH_FRAME_BROADCAST (0x05U) + +/** + * @brief GoMacH announce frame type. + * + * This frame type is specifically used to announce the chosen sub-channel + * sequence of the node to its one-hop neighbors. + */ +#define GNRC_GOMACH_FRAME_ANNOUNCE (0x06U) + +/** + * @brief GoMacH frame header. + */ +typedef struct { + uint8_t type; /**< Type of GoMacH frame. */ +} gnrc_gomach_hdr_t; + +/** + * @brief GoMacH Beacon frame + */ +typedef struct __attribute__((packed)) { + gnrc_gomach_hdr_t header; /**< Beacon frame header type. */ + uint8_t sub_channel_seq; /**< Sub-channel sequence of this node. */ + uint8_t schedulelist_size; /**< vTDMA schedule list size. */ +} gnrc_gomach_frame_beacon_t; + +/** + * @brief GoMacH data frame + */ +typedef struct __attribute__((packed)) { + gnrc_gomach_hdr_t header; /**< Data frame header type. */ + uint8_t queue_indicator; /**< Queue-length indicator of this node. */ +} gnrc_gomach_frame_data_t; + +/** + * @brief GoMacH sub-channel announce frame. + * + * This frame type is specifically used to announce the chosen sub-channel + * sequence of the node to its one-hop neighbors. + */ +typedef struct __attribute__((packed)) { + gnrc_gomach_hdr_t header; /**< Announce frame header type. */ + uint8_t subchannel_seq; /**< Sub-channel sequence of this node. */ +} gnrc_gomach_frame_announce_t; + +/** + * @brief GoMacH preamble frame. + */ +typedef struct __attribute__((packed)) { + gnrc_gomach_hdr_t header; /**< Preamble frame header type. */ + gnrc_gomach_l2_addr_t dst_addr; /**< Address of this node. */ +} gnrc_gomach_frame_preamble_t; + + +/** + * @brief GoMacH preamble_ack frame. + */ +typedef struct __attribute__((packed)) { + gnrc_gomach_hdr_t header; /**< Preamble-ACK frame header type. */ + gnrc_gomach_l2_addr_t dst_addr; /**< Address of this node. */ + uint32_t phase_in_us; /**< Current phase of this node. */ +} gnrc_gomach_frame_preamble_ack_t; + +/** + * @brief GoMacH broadcast frame. + */ +typedef struct __attribute__((packed)) { + gnrc_gomach_hdr_t header; /**< Broadcast frame header type. */ + uint8_t seq_nr; /**< Broadcast sequence of this node. */ +} gnrc_gomach_frame_broadcast_t; + +#ifdef __cplusplus +} +#endif + +#endif /* NET_GNRC_GOMACH_HDR_H */ +/** @} */ diff --git a/sys/include/net/gnrc/gomach/timeout.h b/sys/include/net/gnrc/gomach/timeout.h new file mode 100644 index 0000000000000000000000000000000000000000..5c2040bc8d2ec15629ca2b24ae84057c538abcc7 --- /dev/null +++ b/sys/include/net/gnrc/gomach/timeout.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2017 INRIA + * + * 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 net_gnrc_gomach + * @{ + * + * @file + * @brief Timeout handling of GoMacH. + * @internal + * @author Shuguo Zhuo <shuguo.zhuo@inria.fr> + */ + +#ifndef NET_GNRC_GOMACH_TIMEOUT_H +#define NET_GNRC_GOMACH_TIMEOUT_H + +#include <stdint.h> +#include <stdbool.h> + +#include "msg.h" +#include "xtimer.h" +#include "net/gnrc/netif.h" +#include "types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Set GoMacH timeout of type @p type of offset @p offset. + * + * @param[in,out] netif the network interface. + * @param[in] type GoMacH timeout type. + * @param[in] offset timeout offset. + */ +void gnrc_gomach_set_timeout(gnrc_netif_t *netif, + gnrc_gomach_timeout_type_t type, + uint32_t offset); + +/** + * @brief Clear GoMacH timeout of type @p type. + * + * @param[in,out] netif the network interface. + * @param[in] type GoMacH timeout type. + */ +void gnrc_gomach_clear_timeout(gnrc_netif_t *netif, gnrc_gomach_timeout_type_t type); + +/** + * @brief Check whether GoMacH timeout of type @p type is running. + * + * @param[in] netif the network interface. + * @param[in] type GoMacH timeout type. + * + * @return true, if timeout of type @p type is running. + * @return false, if timeout of type @p type is not running. + */ +bool gnrc_gomach_timeout_is_running(gnrc_netif_t *netif, gnrc_gomach_timeout_type_t type); + +/** + * @brief Check whether GoMacH timeout of type @p type is expired. It will clear + * the timeout once it is found expired. + * + * @param[in,out] netif the network interface. + * @param[in] type GoMacH timeout type. + * + * @return true, if timeout of type @p type is expired. + * @return false, if timeout of type @p type is not expired, or not exist. + */ +bool gnrc_gomach_timeout_is_expired(gnrc_netif_t *netif, gnrc_gomach_timeout_type_t type); + +/** + * @brief Reset all GoMacH timeouts. + * + * @param[in,out] netif the network interface. + */ +void gnrc_gomach_reset_timeouts(gnrc_netif_t *netif); + +/** + * @brief Make a specific GoMacH timeout expired. + * + * @param[in,out] timeout GoMacH timeout + */ +static inline void gnrc_gomach_timeout_make_expire(gnrc_gomach_timeout_t *timeout) +{ + assert(timeout); + + timeout->expired = true; +} + +#ifdef __cplusplus +} +#endif + +#endif /* NET_GNRC_GOMACH_TIMEOUT_H */ +/** @} */ diff --git a/sys/include/net/gnrc/gomach/types.h b/sys/include/net/gnrc/gomach/types.h new file mode 100644 index 0000000000000000000000000000000000000000..6386070c82b0cbe05a3e5fca189c1f94da6dfe6a --- /dev/null +++ b/sys/include/net/gnrc/gomach/types.h @@ -0,0 +1,326 @@ +/* + * Copyright (C) 2017 Shuguo Zhuo + * + * 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 net_gnrc_gomach + * @{ + * + * @file + * @brief Internal used types of GoMacH + * @internal + * @author Shuguo Zhuo <shuguo.zhuo@inria.fr> + */ + +#ifndef NET_GNRC_GOMACH_TYPES_H +#define NET_GNRC_GOMACH_TYPES_H + +#include <stdint.h> +#include <stdbool.h> + +#include "kernel_types.h" +#include "xtimer.h" +#include "net/gnrc.h" +#include "net/gnrc/gomach/hdr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief GoMacH RTT event type. + */ +#define GNRC_GOMACH_EVENT_RTT_TYPE (0x4300) + +/** + * @brief GoMacH RTT new cycle start event type. + */ +#define GNRC_GOMACH_EVENT_RTT_NEW_CYCLE (0x4301) + +/** + * @brief GoMacH timeout event type. + */ +#define GNRC_GOMACH_EVENT_TIMEOUT_TYPE (0x4400) + +/** + * @brief GoMacH uninitialized phase value. + */ +#define GNRC_GOMACH_PHASE_UNINITIALIZED (0) + +/** + * @brief GoMacH max phase value. + */ +#define GNRC_GOMACH_PHASE_MAX (-1) + +/** + * @brief GoMacH check duplicate packet buffer size. + */ +#define GNRC_GOMACH_DUPCHK_BUFFER_SIZE (8U) + +/** + * @brief The default largest number of parallel timeouts in GoMacH. + */ +#define GNRC_GOMACH_TIMEOUT_COUNT (6U) + +/** + * @brief The default number of slot-schedule-unit + * @ref gnrc_gomach_slosch_unit_t in GoMacH. + */ +#define GNRC_GOMACH_SLOSCH_UNIT_COUNT (11U) + +/** + * @brief MAC type of unknown in GoMacH for indicating that + * the node's phase is unknown. + */ +#define GNRC_GOMACH_TYPE_UNKNOWN (0U) + +/** + * @brief MAC type of known in GoMacH for indicating that the + * node's phase is known. + */ +#define GNRC_GOMACH_TYPE_KNOWN (1U) + +/** + * @brief Enable/disable duty-cycle record and print out. + * + * Set "1" to enable, set "0" to disable. + */ +#ifndef GNRC_GOMACH_ENABLE_DUTYCYLE_RECORD +#define GNRC_GOMACH_ENABLE_DUTYCYLE_RECORD (0U) +#endif + +/** + * @brief State-machine states of Broadcast procedure of GoMacH. + */ +typedef enum { + GNRC_GOMACH_BCAST_INIT, /**< Initiate broadcast. */ + GNRC_GOMACH_BCAST_SEND, /**< Send broadcast packet. */ + GNRC_GOMACH_BCAST_WAIT_TX_FINISH, /**< Wait for broadcast TX finish. */ + GNRC_GOMACH_BCAST_WAIT_NEXT_TX, /**< Wait for next broadcast TX timing. */ + GNRC_GOMACH_BCAST_END /**< End of broadcast procedure. */ +} gnrc_gomach_bcast_state_t; + +/** + * @brief State-machine states of Transmission-to-phase-known + * (t2k) procedure of GoMacH. + */ +typedef enum { + GNRC_GOMACH_T2K_INIT, /**< Initiate t2k procedure. */ + GNRC_GOMACH_T2K_WAIT_CP, /**< Wait for receiver's CP (wake-up) period. */ + GNRC_GOMACH_T2K_TRANS_IN_CP, /**< Transmit data in receiver's CP period. */ + GNRC_GOMACH_T2K_WAIT_CPTX_FEEDBACK, /**< Wait for CP transmission finish. */ + GNRC_GOMACH_T2K_WAIT_BEACON, /**< Wait receiver's beacon. */ + GNRC_GOMACH_T2K_WAIT_SLOTS, /**< Wait for the node's allocated slots period. */ + GNRC_GOMACH_T2K_VTDMA_TRANS, /**< Transmit data in allocate slots. */ + GNRC_GOMACH_T2K_WAIT_VTDMA_FEEDBACK, /**< Wait for TX finish in TX-slot. */ + GNRC_GOMACH_T2K_END /**< End of t2k procedure. */ +} gnrc_gomach_t2k_state_t; + +/** + * @brief State-machine states of Transmission-to-phase-unknown + * (t2u) procedure of GoMacH. + */ +typedef enum { + GNRC_GOMACH_T2U_INIT, /**< Initiate t2u procedure. */ + GNRC_GOMACH_T2U_PREAMBLE_PREPARE, /**< Prepare settings before sending preamble. */ + GNRC_GOMACH_T2U_SEND_PREAMBLE, /**< Send preamble in t2u. */ + GNRC_GOMACH_T2U_WAIT_PREAMBLE_TX, /**< Wait for Send preamble TX finish. */ + GNRC_GOMACH_T2U_WAIT_PREAMBLE_ACK, /**< Wait for preamble-ACK from receiver. */ + GNRC_GOMACH_T2U_SEND_DATA, /**< Send data packet to the receiver. */ + GNRC_GOMACH_T2U_WAIT_DATA_TX, /**< Wait for Send data TX finish. */ + GNRC_GOMACH_T2U_END /**< End of t2u procedure. */ +} gnrc_gomach_t2u_state_t; + +/** + * @brief State-machine states of basic management procedure of GoMacH. + */ +typedef enum { + GNRC_GOMACH_INIT, /**< Initiate GoMacH. */ + GNRC_GOMACH_LISTEN, /**< GoMacH's duty-cycled listen procedure. */ + GNRC_GOMACH_TRANSMIT /**< GoMacH's transmission procedure. */ +} gnrc_gomach_basic_state_t; + +/** + * @brief State-machine states of initialization procedure of GoMacH. + */ +typedef enum { + GNRC_GOMACH_INIT_PREPARE, /**< Prepare the initialization procedure of GoMacH. */ + GNRC_GOMACH_INIT_ANNC_SUBCHAN, /**< Announce the sub-channel sequence of the node. */ + GNRC_GOMACH_INIT_WAIT_FEEDBACK, /**< Wait for announce TX finish. */ + GNRC_GOMACH_INIT_END /**< End of the initialization procedure of GoMacH. */ +} gnrc_gomach_init_state_t; + +/** + * @brief State-machine states of duty-cycled listening procedure of GoMacH. + */ +typedef enum { + GNRC_GOMACH_LISTEN_CP_INIT, /**< Initiate the listen period. */ + GNRC_GOMACH_LISTEN_CP_LISTEN, /**< Listen for incoming packets. */ + GNRC_GOMACH_LISTEN_CP_END, /**< End of packet listen period. */ + GNRC_GOMACH_LISTEN_SEND_BEACON, /**< Send beacon packet when needed. */ + GNRC_GOMACH_LISTEN_WAIT_BEACON_TX, /**< Wait for send beacon TX finish. */ + GNRC_GOMACH_LISTEN_VTDMA_INIT, /**< Initiate the vTDMA period. */ + GNRC_GOMACH_LISTEN_VTDMA, /**< Listen for incoming packets in vTDMA. */ + GNRC_GOMACH_LISTEN_VTDMA_END, /**< End of the vTDMA period. */ + GNRC_GOMACH_LISTEN_SLEEP_INIT, /**< Initiate the sleep period. */ + GNRC_GOMACH_LISTEN_SLEEP, /**< Turn radio off to sleep. */ + GNRC_GOMACH_LISTEN_SLEEP_END /**< End of the sleep period. */ +} gnrc_gomach_listen_state_t; + +/** + * @brief State-machine states of basic transmission management procedure of GoMacH. + */ +typedef enum { + GNRC_GOMACH_TRANS_TO_UNKNOWN, /**< Transmit to phase-unknown node in GoMacH. */ + GNRC_GOMACH_TRANS_TO_KNOWN, /**< Transmit to phase-known node in GoMacH. */ + GNRC_GOMACH_BROADCAST /**< Broadcast packet in GoMacH. */ +} gnrc_gomach_transmit_state_t; + +/** + * @brief GoMacH timeout types. + */ +typedef enum { + GNRC_GOMACH_TIMEOUT_DISABLED, /**< Timeout is disabled. */ + GNRC_GOMACH_TIMEOUT_BCAST_FINISH, /**< Timeout of broadcast procedure end. */ + GNRC_GOMACH_TIMEOUT_BCAST_INTERVAL, /**< Timeout of next broadcast transmission. */ + GNRC_GOMACH_TIMEOUT_PREAMBLE, /**< Timeout of next preamble transmission. */ + GNRC_GOMACH_TIMEOUT_MAX_PREAM_INTERVAL, /**< Timeout of maximum preamble interval. */ + GNRC_GOMACH_TIMEOUT_PREAM_DURATION, /**< Timeout of maximum preamble duration. */ + GNRC_GOMACH_TIMEOUT_WAIT_CP, /**< Timeout of waiting receiver's CP period. */ + GNRC_GOMACH_TIMEOUT_WAIT_BEACON, /**< Timeout of waiting beacon. */ + GNRC_GOMACH_TIMEOUT_WAIT_SLOTS, /**< Timeout of waiting own slots. */ + GNRC_GOMACH_TIMEOUT_CP_END, /**< Timeout of CP (wake-up) period ending. */ + GNRC_GOMACH_TIMEOUT_CP_MAX, /**< Timeout of maximum CP duration. */ + GNRC_GOMACH_TIMEOUT_WAIT_RX_END, /**< Timeout of waiting reception complete. */ + GNRC_GOMACH_TIMEOUT_VTDMA, /**< Timeout of vTDMA period end. */ + GNRC_GOMACH_TIMEOUT_NO_TX_ISR, /**< Timeout of maximum transmission duration. */ +} gnrc_gomach_timeout_type_t; + +/** + * @brief GoMacH internal L2 address structure + */ +typedef struct { + uint8_t addr[IEEE802154_LONG_ADDRESS_LEN]; /**< Node's address. */ +} gnrc_gomach_l2_id_t; + +/** + * @brief GoMacH's slot-schedule-unit. + * + * This slot-schedule-unit is used to record related information of a node for + * running the dynamic slots allocation scheme in GoMacH. + */ +typedef struct { + gnrc_gomach_l2_addr_t node_addr; /**< Node's address. */ + uint8_t queue_indicator; /**< Node's queue-length indicator. */ +} gnrc_gomach_slosch_unit_t; + +/** + * @brief GoMacH's vTDMA (dynamic slots allocation) management unit. + */ +typedef struct { + uint8_t total_slots_num; /**< Number of total allocated transmission slots. */ + uint8_t sub_channel_seq; /**< Receiver's sub-channel sequence. */ +} gnrc_gomach_vtdma_manag_t; + +/** + * @brief GoMacH's data structure for recording sender's past TX information. + */ +typedef struct { + gnrc_gomach_l2_addr_t node_addr; /**< Node's address. */ + uint8_t seq; /**< Node's MAC packet sequence. */ + uint8_t life_cycle; /**< Lifetime of this record unit. */ +} gnrc_gomach_dupchk_unit_t; + +/** + * @brief GoMacH's data structure for recording TX information for avoiding receiving + * duplicate packets. + */ +typedef struct { + gnrc_gomach_dupchk_unit_t last_nodes[GNRC_GOMACH_DUPCHK_BUFFER_SIZE]; /**< Duplicate + check unit. */ + uint8_t queue_head; /**< Check queue's + head. */ +} gnrc_gomach_dupchk_t; + +/** + * @brief Type to pass information about packet parsing. + */ +typedef struct { + gnrc_gomach_hdr_t *header; /**< GoMacH header of packet. */ + gnrc_gomach_l2_addr_t src_addr; /**< Copied source address of packet. */ + gnrc_gomach_l2_addr_t dst_addr; /**< Copied destination address of packet. */ + uint8_t seq; /**< MAC sequence of the received packet. */ +} gnrc_gomach_packet_info_t; + +/** + * @brief Sender's vTDMA (dynamic slots allocation) management unit. + */ +typedef struct { + uint16_t sub_channel_seq; /**< Receiver's sub-channel sequence. */ + uint8_t slots_position; /**< Node's own slots position. */ + uint8_t slots_num; /**< Node's allocated slots number. */ +} gnrc_gomach_vtdma_t; + +/** + * @brief GoMacH's timeout structure + */ +typedef struct { + xtimer_t timer; /**< xtimer entity. */ + msg_t msg; /**< msg entity. */ + bool expired; /**< Timeout expiration indicator. */ + gnrc_gomach_timeout_type_t type; /**< GoMacH timeout type. */ +} gnrc_gomach_timeout_t; + +/** + * @brief Static initializer for @ref gnrc_gomach_timeout_t. + */ +#define GNRC_GOMACH_TIMEOUT_INIT { {}, {}, false, GNRC_GOMACH_TIMEOUT_DISABLED } + +/** + * @brief GoMacH's specific structure for storing internal states. + */ +typedef struct gomach { + gnrc_gomach_basic_state_t basic_state; /**< Basic state. */ + gnrc_gomach_init_state_t init_state; /**< Initialization state. */ + gnrc_gomach_timeout_t timeouts[GNRC_GOMACH_TIMEOUT_COUNT]; /**< GoMacH's timeouts. */ + uint16_t subchannel_occu_flags; /**< Sub-channel + usage indicator. */ + uint16_t sub_channel_seq; /**< Sub-channel sequence. */ + uint16_t pub_channel_1; /**< Public channel 1. */ + uint16_t pub_channel_2; /**< Public channel 2. */ + uint16_t cur_pub_channel; /**< Current public channel. */ + uint8_t cp_extend_count; /**< CP extend count. */ + uint32_t last_wakeup; /**< Last wake-up timing. */ + uint32_t backoff_phase_us; /**< Phase backoff time. */ + uint16_t gomach_info; /**< GoMacH's internal + information. */ + uint64_t last_wakeup_phase_us; /**< Last cycle wakeup phase. */ + int16_t rx_pkt_rssi; /**< RSSI of latest received + packet in dBm */ + uint8_t rx_pkt_lqi; /**< LQI of latest received + packet */ + + +#if (GNRC_GOMACH_ENABLE_DUTYCYLE_RECORD == 1) + /* Parameters for recording duty-cycle */ + uint64_t last_radio_on_time_ticks; /**< The last time in ticks + when radio is on */ + uint64_t radio_off_time_ticks; /**< The time in ticks when + radio is off */ + uint64_t system_start_time_ticks; /**< The time in ticks when + chip is started */ + uint64_t awake_duration_sum_ticks; /**< The sum of time in ticks + when radio is on */ +#endif +} gnrc_gomach_t; + +#ifdef __cplusplus +} +#endif + +#endif /* NET_GNRC_GOMACH_TYPES_H */ +/** @} */ diff --git a/sys/include/net/gnrc/mac/types.h b/sys/include/net/gnrc/mac/types.h index 9dcf895fa21bb84a1b535d51266929da105acba5..ed70a6170650846d18d5bc7cda9ee560a4f9774f 100644 --- a/sys/include/net/gnrc/mac/types.h +++ b/sys/include/net/gnrc/mac/types.h @@ -32,6 +32,9 @@ #ifdef MODULE_GNRC_LWMAC #include "net/gnrc/lwmac/types.h" #endif +#ifdef MODULE_GNRC_GOMACH +#include "net/gnrc/gomach/types.h" +#endif #ifdef __cplusplus extern "C" { @@ -75,6 +78,13 @@ typedef struct { gnrc_lwmac_rx_state_t state; /**< LWMAC specific internal reception state */ uint8_t rx_bad_exten_count; /**< Count how many unnecessary RX extensions have been executed */ #endif + +#ifdef MODULE_GNRC_GOMACH + gnrc_gomach_listen_state_t listen_state; /**< Listen state. */ + gnrc_gomach_slosch_unit_t slosch_list[GNRC_GOMACH_SLOSCH_UNIT_COUNT]; /**< Queue-indicator record units. */ + gnrc_gomach_vtdma_manag_t vtdma_manag; /**< vTDMA management unit. */ + gnrc_gomach_dupchk_t check_dup_pkt; /**< Check duplicate packet unit. */ +#endif } gnrc_mac_rx_t; /** @@ -110,6 +120,12 @@ typedef struct { #if (GNRC_MAC_TX_QUEUE_SIZE != 0) || defined(DOXYGEN) gnrc_priority_pktqueue_t queue; /**< TX queue for this particular Neighbor */ #endif /* (GNRC_MAC_TX_QUEUE_SIZE != 0) || defined(DOXYGEN) */ + +#ifdef MODULE_GNRC_GOMACH + uint16_t pub_chanseq; /**< Neighbor's current public channel sequence. */ + uint32_t cp_phase; /**< Neighbor's wake-up phase. */ + uint8_t mac_type; /**< Neighbor's phase-track indicator. */ +#endif } gnrc_mac_tx_neighbor_t; /** @@ -175,6 +191,22 @@ typedef struct { uint8_t tx_burst_count; /**< Count how many consecutive packets have been transmitted */ uint8_t tx_retry_count; /**< Count how many Tx-retrials have been executed before packet drop */ #endif + +#ifdef MODULE_GNRC_GOMACH + gnrc_gomach_transmit_state_t transmit_state; /**< Basic transmission state. */ + gnrc_gomach_bcast_state_t bcast_state; /**< Broadcast state. */ + gnrc_gomach_t2k_state_t t2k_state; /**< t2k (transmit-to-known) state. */ + gnrc_gomach_t2u_state_t t2u_state; /**< t2u (transmit-to-unknown) state. */ + uint8_t preamble_sent; /**< Preamble sent count. */ + uint8_t broadcast_seq; /**< Node's broadcast sequence. */ + uint8_t tx_seq; /**< Node's MAC transmission (TX) sequence. */ + gnrc_gomach_vtdma_t vtdma_para; /**< Node's vTMDA slots allocation management unit. */ + uint8_t no_ack_counter; /**< Counter for recording no-ACK times for data transmission. */ + uint8_t t2u_retry_counter; /**< Counter for recording t2u attempt failures. */ + uint8_t last_tx_neighbor_id; /**< Record last TX neighbor's sequence in the neighbor list. */ + uint8_t tx_busy_count; /**< Counter recording csma busy feedback times. */ + uint8_t t2u_fail_count; /**< Preamble trial failure count. */ +#endif } gnrc_mac_tx_t; /** diff --git a/sys/include/net/gnrc/netif/mac.h b/sys/include/net/gnrc/netif/mac.h index daf0de8c98c06c407cc0f7f7b285931e63250184..76dac3019b9a5736ad2cedb38a0511ca44d16ee8 100644 --- a/sys/include/net/gnrc/netif/mac.h +++ b/sys/include/net/gnrc/netif/mac.h @@ -45,6 +45,27 @@ extern "C" { */ #define GNRC_NETIF_MAC_INFO_CSMA_ENABLED (0x0100U) +#if defined(MODULE_GNRC_LWMAC) || defined(MODULE_GNRC_GOMACH) +/** + * @brief Data type to hold MAC protocols + */ +typedef union { +#ifdef MODULE_GNRC_LWMAC + /** + * @brief LWMAC specific structure object for storing LWMAC internal states. + */ + gnrc_lwmac_t lwmac; +#endif + +#ifdef MODULE_GNRC_GOMACH + /** + * @brief GoMacH specific structure object for storing GoMacH internal states. + */ + gnrc_gomach_t gomach; +#endif +} gnrc_mac_prot_t; +#endif + /** * @brief @ref net_gnrc_mac component of @ref gnrc_netif_mac_t */ @@ -80,11 +101,8 @@ typedef struct { gnrc_mac_tx_t tx; #endif /* ((GNRC_MAC_TX_QUEUE_SIZE != 0) || (GNRC_MAC_NEIGHBOR_COUNT == 0)) || DOXYGEN */ -#ifdef MODULE_GNRC_LWMAC - /** - * @brief LWMAC specific structure object for storing LWMAC internal states. - */ - gnrc_lwmac_t lwmac; +#if defined(MODULE_GNRC_LWMAC) || defined(MODULE_GNRC_GOMACH) + gnrc_mac_prot_t prot; #endif } gnrc_netif_mac_t; diff --git a/sys/include/net/gnrc/nettype.h b/sys/include/net/gnrc/nettype.h index 40d84fa9a536378ac92e74b5618862dceadfad89..a2446cc93df6b371fda592da1c7da16dee009414 100644 --- a/sys/include/net/gnrc/nettype.h +++ b/sys/include/net/gnrc/nettype.h @@ -56,6 +56,17 @@ typedef enum { GNRC_NETTYPE_SIXLOWPAN, /**< Protocol is 6LoWPAN */ #endif + /** + * @{ + * @name Link layer + */ +#ifdef MODULE_GNRC_GOMACH + GNRC_NETTYPE_GOMACH, /**< Protocol is GoMacH */ +#endif + /** + * @} + */ + /** * @{ * @name Link layer diff --git a/sys/net/gnrc/Makefile b/sys/net/gnrc/Makefile index 3e780ed72f39a0e579fc84b2ff5f6bb611df5d7a..372b1e3b737729dfabe02585949b2d15f86ed02d 100644 --- a/sys/net/gnrc/Makefile +++ b/sys/net/gnrc/Makefile @@ -55,6 +55,9 @@ endif ifneq (,$(filter gnrc_pktbuf_malloc,$(USEMODULE))) DIRS += pktbuf_malloc endif +ifneq (,$(filter gnrc_gomach,$(USEMODULE))) + DIRS += link_layer/gomach +endif ifneq (,$(filter gnrc_pktbuf_static,$(USEMODULE))) DIRS += pktbuf_static endif diff --git a/sys/net/gnrc/link_layer/gnrc_mac/internal.c b/sys/net/gnrc/link_layer/gnrc_mac/internal.c index 8e4fb09c293cfff10f12133ace25b7f81491e6ef..4506fefa927491570ebb5fccf2d9347c609f9914 100644 --- a/sys/net/gnrc/link_layer/gnrc_mac/internal.c +++ b/sys/net/gnrc/link_layer/gnrc_mac/internal.c @@ -252,6 +252,19 @@ void gnrc_mac_dispatch(gnrc_mac_rx_t *rx) /* make append netif header after payload again */ rx->dispatch_buffer[i]->next = netif; #endif + +#ifdef MODULE_GNRC_GOMACH + /* save pointer to netif header */ + gnrc_pktsnip_t *netif = rx->dispatch_buffer[i]->next->next; + + /* remove GoMacH header */ + rx->dispatch_buffer[i]->next->next = NULL; + gnrc_pktbuf_release(rx->dispatch_buffer[i]->next); + + /* make append netif header after payload again */ + rx->dispatch_buffer[i]->next = netif; +#endif + if (!gnrc_netapi_dispatch_receive(rx->dispatch_buffer[i]->type, GNRC_NETREG_DEMUX_CTX_ALL, rx->dispatch_buffer[i])) { diff --git a/sys/net/gnrc/link_layer/gomach/Makefile b/sys/net/gnrc/link_layer/gomach/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..a2674f734a6c4a1b612824e6d7da9b6cdf853ca8 --- /dev/null +++ b/sys/net/gnrc/link_layer/gomach/Makefile @@ -0,0 +1,3 @@ +MODULE = gnrc_gomach + +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/gnrc/link_layer/gomach/gomach.c b/sys/net/gnrc/link_layer/gomach/gomach.c new file mode 100644 index 0000000000000000000000000000000000000000..cd36a1ee930428646d15dd8c8628f1ca1d05e2e7 --- /dev/null +++ b/sys/net/gnrc/link_layer/gomach/gomach.c @@ -0,0 +1,2211 @@ +/* + * Copyright (C) 2017 INRIA + * + * 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 net_gnrc_gomach + * @{ + * + * @file + * @brief Implementation of GoMacH + * + * @author Shuguo Zhuo <shuguo.zhuo@inria.fr> + * @} + */ + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include <stdbool.h> + +#include "random.h" +#include "timex.h" +#include "periph/rtt.h" +#include "net/gnrc/netif.h" +#include "net/gnrc/netif/internal.h" +#include "net/gnrc/netif/ieee802154.h" +#include "net/netdev/ieee802154.h" +#include "net/gnrc.h" +#include "net/gnrc/nettype.h" +#include "net/netdev.h" +#include "net/gnrc/mac/internal.h" +#include "net/gnrc/gomach/gomach.h" +#include "net/gnrc/gomach/timeout.h" +#include "include/gomach_internal.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#ifndef LOG_LEVEL +/** + * @brief Default log level define + */ +#define LOG_LEVEL LOG_WARNING +#endif + +#include "log.h" + +/** + * @brief GoMacH thread's PID + */ +static kernel_pid_t gomach_pid; + +static void _gomach_init(gnrc_netif_t *netif); +static int _send(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt); +static gnrc_pktsnip_t *_recv(gnrc_netif_t *netif); +static void _gomach_msg_handler(gnrc_netif_t *netif, msg_t *msg); +static void _gomach_event_cb(netdev_t *dev, netdev_event_t event); + +static const gnrc_netif_ops_t gomach_ops = { + .init = _gomach_init, + .send = _send, + .recv = _recv, + .get = gnrc_netif_get_from_netdev, + .set = gnrc_netif_set_from_netdev, + .msg_handler = _gomach_msg_handler, +}; + +gnrc_netif_t *gnrc_netif_gomach_create(char *stack, int stacksize, + char priority, char *name, + netdev_t *dev) +{ + return gnrc_netif_create(stack, stacksize, priority, name, dev, + &gomach_ops); +} + +static gnrc_pktsnip_t *_recv(gnrc_netif_t *netif) +{ + netdev_t *dev = netif->dev; + netdev_ieee802154_rx_info_t rx_info; + netdev_ieee802154_t *state = (netdev_ieee802154_t *)netif->dev; + gnrc_pktsnip_t *pkt = NULL; + int bytes_expected = dev->driver->recv(dev, NULL, 0, NULL); + + if (bytes_expected > 0) { + int nread; + + pkt = gnrc_pktbuf_add(NULL, NULL, bytes_expected, GNRC_NETTYPE_UNDEF); + if (pkt == NULL) { + DEBUG("_recv_ieee802154: cannot allocate pktsnip.\n"); + return NULL; + } + nread = dev->driver->recv(dev, pkt->data, bytes_expected, &rx_info); + if (nread <= 0) { + gnrc_pktbuf_release(pkt); + return NULL; + } + if (!(state->flags & NETDEV_IEEE802154_RAW)) { + gnrc_pktsnip_t *ieee802154_hdr; + size_t mhr_len = ieee802154_get_frame_hdr_len(pkt->data); + + if (mhr_len == 0) { + DEBUG("_recv_ieee802154: illegally formatted frame received\n"); + gnrc_pktbuf_release(pkt); + return NULL; + } + nread -= mhr_len; + /* mark IEEE 802.15.4 header */ + ieee802154_hdr = gnrc_pktbuf_mark(pkt, mhr_len, GNRC_NETTYPE_UNDEF); + if (ieee802154_hdr == NULL) { + DEBUG("_recv_ieee802154: no space left in packet buffer\n"); + gnrc_pktbuf_release(pkt); + return NULL; + } + netif->mac.prot.gomach.rx_pkt_lqi = rx_info.lqi; + netif->mac.prot.gomach.rx_pkt_rssi = rx_info.rssi; + } + + DEBUG("_recv_ieee802154: reallocating.\n"); + gnrc_pktbuf_realloc_data(pkt, nread); + } + + return pkt; +} + +static void gomach_reinit_radio(gnrc_netif_t *netif) +{ + /* Initialize low-level driver. */ + netif->dev->driver->init(netif->dev); + + /* Set MAC address length. */ + uint16_t src_len = netif->l2addr_len; + netif->dev->driver->set(netif->dev, NETOPT_SRC_LEN, &src_len, sizeof(src_len)); + + /* Set the MAC address of the device. */ + if (netif->l2addr_len == IEEE802154_LONG_ADDRESS_LEN) { + netif->dev->driver->set(netif->dev, + NETOPT_ADDRESS_LONG, + netif->l2addr, + sizeof(netif->l2addr)); + } + else { + netif->dev->driver->set(netif->dev, + NETOPT_ADDR_LEN, + netif->l2addr, + sizeof(netif->l2addr)); + } + + /* Enable RX-start and TX-started and TX-END interrupts. */ + netopt_enable_t enable = NETOPT_ENABLE; + netif->dev->driver->set(netif->dev, NETOPT_RX_START_IRQ, &enable, sizeof(enable)); + netif->dev->driver->set(netif->dev, NETOPT_RX_END_IRQ, &enable, sizeof(enable)); + netif->dev->driver->set(netif->dev, NETOPT_TX_END_IRQ, &enable, sizeof(enable)); + +} + +static void _gomach_rtt_cb(void *arg) +{ + msg_t msg; + + msg.content.value = ((uint32_t) arg) & 0xffff; + msg.type = GNRC_GOMACH_EVENT_RTT_TYPE; + msg_send(&msg, gomach_pid); + + if (sched_context_switch_request) { + thread_yield(); + } +} + +static void _gomach_rtt_handler(uint32_t event, gnrc_netif_t *netif) +{ + switch (event & 0xffff) { + case GNRC_GOMACH_EVENT_RTT_NEW_CYCLE: { + /* Start duty-cycle scheme. */ + if (!gnrc_gomach_get_duty_cycle_start(netif)) { + gnrc_gomach_set_duty_cycle_start(netif, true); + rtt_clear_alarm(); + /* Record the new cycle's starting time. */ + netif->mac.prot.gomach.last_wakeup = rtt_get_counter(); + } + else { + /* The duty-cycle scheme has already started, + * record the new cycle's starting time. */ + netif->mac.prot.gomach.last_wakeup = rtt_get_alarm(); + gnrc_gomach_set_enter_new_cycle(netif, true); + } + + netif->mac.prot.gomach.last_wakeup_phase_us = xtimer_now_usec64(); + + /* Set next cycle's starting time. */ + uint32_t alarm = netif->mac.prot.gomach.last_wakeup + + RTT_US_TO_TICKS(GNRC_GOMACH_SUPERFRAME_DURATION_US); + rtt_set_alarm(alarm, _gomach_rtt_cb, (void *) GNRC_GOMACH_EVENT_RTT_NEW_CYCLE); + + /* Update neighbors' public channel phases. */ + gnrc_gomach_update_neighbor_pubchan(netif); + gnrc_gomach_set_update(netif, true); + } break; + default: { + LOG_ERROR("ERROR: [GOMACH] error RTT message type\n"); + break; + } + } +} + +static void gomach_bcast_init(gnrc_netif_t *netif) +{ + /* Disable auto-ACK when sending broadcast packets, thus not to receive packet. */ + gnrc_gomach_set_autoack(netif, NETOPT_DISABLE); + + /* Firstly turn the radio to public channel 1. */ + gnrc_gomach_turn_channel(netif, netif->mac.prot.gomach.pub_channel_1); + gnrc_gomach_set_on_pubchan_1(netif, true); + + netif->mac.tx.broadcast_seq++; + + /* Assemble the broadcast packet. */ + gnrc_pktsnip_t *pkt = netif->mac.tx.packet; + gnrc_pktsnip_t *payload = netif->mac.tx.packet->next; + + gnrc_gomach_frame_broadcast_t gomach_broadcast_hdr; + gomach_broadcast_hdr.header.type = GNRC_GOMACH_FRAME_BROADCAST; + gomach_broadcast_hdr.seq_nr = netif->mac.tx.broadcast_seq; + pkt->next = gnrc_pktbuf_add(pkt->next, &gomach_broadcast_hdr, + sizeof(gomach_broadcast_hdr), + GNRC_NETTYPE_GOMACH); + if (pkt->next == NULL) { + /* Make append payload after netif header again */ + netif->mac.tx.packet->next = payload; + gnrc_pktbuf_release(netif->mac.tx.packet); + netif->mac.tx.packet = NULL; + LOG_ERROR("ERROR: [GOMACH] bcast: no memory to assemble bcast packet, drop packet.\n"); + LOG_ERROR("ERROR: [GOMACH] bcast failed, go to listen mode.\n"); + netif->mac.tx.bcast_state = GNRC_GOMACH_BCAST_END; + gnrc_gomach_set_update(netif, true); + return; + } + + gnrc_gomach_set_timeout(netif, GNRC_GOMACH_TIMEOUT_BCAST_FINISH, + GNRC_GOMACH_SUPERFRAME_DURATION_US); + + gnrc_priority_pktqueue_flush(&netif->mac.rx.queue); + netif->mac.tx.bcast_state = GNRC_GOMACH_BCAST_SEND; + gnrc_gomach_set_update(netif, true); +} + +static bool _gomach_send_bcast_busy_handle(gnrc_netif_t *netif) +{ + /* Quit sending broadcast packet if we found ongoing transmissions, + * for collision avoidance. */ + if ((gnrc_gomach_get_netdev_state(netif) == NETOPT_STATE_RX) || + (gnrc_netif_get_rx_started(netif) == true)) { + LOG_DEBUG("[GOMACH] bcast: found ongoing transmission, quit broadcast.\n"); + /* Queue the broadcast packet back to the queue. */ + gnrc_pktsnip_t *payload = netif->mac.tx.packet->next->next; + + /* remove gomach header */ + netif->mac.tx.packet->next->next = NULL; + gnrc_pktbuf_release(netif->mac.tx.packet->next); + + /* make append payload after netif header again */ + netif->mac.tx.packet->next = payload; + + if (!gnrc_mac_queue_tx_packet(&netif->mac.tx, 0, netif->mac.tx.packet)) { + LOG_DEBUG("[GOMACH] bcast: TX queue full, release packet.\n"); + gnrc_pktbuf_release(netif->mac.tx.packet); + } + netif->mac.tx.packet = NULL; + + netif->mac.tx.bcast_state = GNRC_GOMACH_BCAST_END; + gnrc_gomach_set_update(netif, true); + return false; + } + return true; +} + +static void gomach_send_bcast_packet(gnrc_netif_t *netif) +{ + /* Quit sending broadcast packet if we found ongoing transmissions, + * for collision avoidance. */ + if (!_gomach_send_bcast_busy_handle(netif)) { + return; + } + + gnrc_pktbuf_hold(netif->mac.tx.packet, 1); + + /* Start sending the broadcast packet. */ + gnrc_gomach_send(netif, netif->mac.tx.packet, NETOPT_DISABLE); + + netif->mac.tx.bcast_state = GNRC_GOMACH_BCAST_WAIT_TX_FINISH; + gnrc_gomach_set_update(netif, false); +} + +static void gomach_wait_bcast_tx_finish(gnrc_netif_t *netif) +{ + if (gnrc_gomach_get_tx_finish(netif)) { + gnrc_gomach_set_timeout(netif, GNRC_GOMACH_TIMEOUT_BCAST_INTERVAL, + GNRC_GOMACH_BCAST_INTERVAL_US); + netif->mac.tx.bcast_state = GNRC_GOMACH_BCAST_WAIT_NEXT_TX; + gnrc_gomach_set_update(netif, false); + } + + /* This is to handle no-TX-complete issue. In case there is no no-TX-complete event, + * we will quit broadcasting, i.e., not getting stucked here. */ + if (gnrc_gomach_timeout_is_expired(netif, GNRC_GOMACH_TIMEOUT_BCAST_FINISH)) { + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_BCAST_INTERVAL); + netif->mac.tx.bcast_state = GNRC_GOMACH_BCAST_END; + gnrc_gomach_set_update(netif, true); + } +} + +static void gomach_wait_bcast_wait_next_tx(gnrc_netif_t *netif) +{ + /* Quit sending broadcast packet if we found ongoing transmissions, + * for collision avoidance. */ + if (!_gomach_send_bcast_busy_handle(netif)) { + return; + } + + /* If the whole broadcast duration timeouts, release the packet and go to t2u end. */ + if (gnrc_gomach_timeout_is_expired(netif, GNRC_GOMACH_TIMEOUT_BCAST_FINISH)) { + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_BCAST_INTERVAL); + gnrc_pktbuf_release(netif->mac.tx.packet); + netif->mac.tx.packet = NULL; + netif->mac.tx.bcast_state = GNRC_GOMACH_BCAST_END; + gnrc_gomach_set_update(netif, true); + return; + } + + /* Toggle the radio channel and go to send the next broadcast packet. */ + if (gnrc_gomach_timeout_is_expired(netif, GNRC_GOMACH_TIMEOUT_BCAST_INTERVAL)) { + if (gnrc_gomach_get_on_pubchan_1(netif)) { + gnrc_gomach_turn_channel(netif, netif->mac.prot.gomach.pub_channel_2); + gnrc_gomach_set_on_pubchan_1(netif, false); + } + else { + gnrc_gomach_turn_channel(netif, netif->mac.prot.gomach.pub_channel_1); + gnrc_gomach_set_on_pubchan_1(netif, true); + } + + netif->mac.tx.bcast_state = GNRC_GOMACH_BCAST_SEND; + gnrc_gomach_set_update(netif, true); + } +} + +static void gomach_bcast_end(gnrc_netif_t *netif) +{ + gnrc_gomach_set_netdev_state(netif, NETOPT_STATE_SLEEP); + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_BCAST_INTERVAL); + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_BCAST_FINISH); + + if (netif->mac.tx.packet) { + gnrc_pktbuf_release(netif->mac.tx.packet); + netif->mac.tx.packet = NULL; + } + netif->mac.tx.current_neighbor = NULL; + + /* Reset the t2u state. */ + netif->mac.tx.bcast_state = GNRC_GOMACH_BCAST_INIT; + + /* Switch to the listen mode. */ + netif->mac.prot.gomach.basic_state = GNRC_GOMACH_LISTEN; + netif->mac.rx.listen_state = GNRC_GOMACH_LISTEN_SLEEP; + gnrc_gomach_set_enter_new_cycle(netif, false); + gnrc_gomach_set_update(netif, true); +} + +static void gomach_bcast_update(gnrc_netif_t *netif) +{ + /* State machine of GoMacH's broadcast procedure. */ + switch (netif->mac.tx.bcast_state) { + case GNRC_GOMACH_BCAST_INIT: { + gomach_bcast_init(netif); + break; + } + case GNRC_GOMACH_BCAST_SEND: { + gomach_send_bcast_packet(netif); + break; + } + case GNRC_GOMACH_BCAST_WAIT_TX_FINISH: { + gomach_wait_bcast_tx_finish(netif); + break; + } + case GNRC_GOMACH_BCAST_WAIT_NEXT_TX: { + gomach_wait_bcast_wait_next_tx(netif); + break; + } + case GNRC_GOMACH_BCAST_END: { + gomach_bcast_end(netif); + break; + } + default: break; + } +} + +static void gomach_init_prepare(gnrc_netif_t *netif) +{ + rtt_clear_alarm(); + + /* Random delay for avoiding the same wake-up phase among devices. */ + uint32_t random_backoff = random_uint32_range(0, GNRC_GOMACH_SUPERFRAME_DURATION_US); + xtimer_usleep(random_backoff); + + gnrc_gomach_set_quit_cycle(netif, false); + netif->mac.prot.gomach.subchannel_occu_flags = 0; + + gnrc_priority_pktqueue_flush(&netif->mac.rx.queue); + + /* Since devices don't broadcast beacons on default, so no need to collect beacons. + * Go to announce its chosen sub-channel sequence. */ + netif->mac.prot.gomach.init_state = GNRC_GOMACH_INIT_ANNC_SUBCHAN; + gnrc_gomach_set_update(netif, true); +} + +static void gomach_init_announce_subchannel(gnrc_netif_t *netif) +{ + /* Choose a sub-channel for the device. */ + gnrc_gomach_init_choose_subchannel(netif); + + /* Announce the device's chosen sub-channel sequence to its neighbors. */ + gnrc_gomach_bcast_subchann_seq(netif, NETOPT_ENABLE); + + netif->mac.prot.gomach.init_state = GNRC_GOMACH_INIT_WAIT_FEEDBACK; + gnrc_gomach_set_update(netif, false); +} + +static void gomach_init_wait_announce_feedback(gnrc_netif_t *netif) +{ + if (gnrc_gomach_get_tx_finish(netif)) { + gnrc_priority_pktqueue_flush(&netif->mac.rx.queue); + netif->mac.prot.gomach.init_state = GNRC_GOMACH_INIT_END; + gnrc_gomach_set_update(netif, true); + } +} + +static void gomach_init_end(gnrc_netif_t *netif) +{ + /* Reset initialization state. */ + netif->mac.prot.gomach.init_state = GNRC_GOMACH_INIT_PREPARE; + /* Switch to duty-cycle listen mode. */ + netif->mac.prot.gomach.basic_state = GNRC_GOMACH_LISTEN; + netif->mac.rx.listen_state = GNRC_GOMACH_LISTEN_CP_INIT; + + /* Start duty-cycle scheme. */ + gnrc_gomach_set_duty_cycle_start(netif, false); + _gomach_rtt_handler(GNRC_GOMACH_EVENT_RTT_NEW_CYCLE, netif); + gnrc_gomach_set_update(netif, true); +} + +static void gomach_t2k_init(gnrc_netif_t *netif) +{ + /* Turn off radio to conserve power */ + gnrc_gomach_set_netdev_state(netif, NETOPT_STATE_SLEEP); + + gnrc_gomach_set_quit_cycle(netif, false); + + /* Set waiting timer for the targeted device! */ + long int wait_phase_duration = netif->mac.tx.current_neighbor->cp_phase - + gnrc_gomach_phase_now(netif); + + if (wait_phase_duration < 0) { + wait_phase_duration += GNRC_GOMACH_SUPERFRAME_DURATION_US; + } + + /* Upon several times of t2k failure, we now doubt that the phase-lock may fail due to drift. + * Here is the phase-lock auto-adjust scheme, trying to catch the neighbot's phase in case of + * phase-lock failure due to timer drift. + * Firstly, put the calculated phase ahead, check whether the neighbor's phase has gone ahead + * of the recorded one */ + if (netif->mac.tx.no_ack_counter == (GNRC_GOMACH_REPHASELOCK_THRESHOLD - 2)) { + if ((uint32_t)wait_phase_duration < GNRC_GOMACH_CP_DURATION_US) { + wait_phase_duration = (wait_phase_duration + GNRC_GOMACH_SUPERFRAME_DURATION_US) - + GNRC_GOMACH_CP_DURATION_US; + } + else { + wait_phase_duration = wait_phase_duration - GNRC_GOMACH_CP_DURATION_US; + } + } + /* If this is the last t2k trial, the phase-lock auto-adjust scheme delays the estimated phase + * a little bit, to see if the real phase is behind the original calculated one. */ + if (netif->mac.tx.no_ack_counter == (GNRC_GOMACH_REPHASELOCK_THRESHOLD - 1)) { + wait_phase_duration = wait_phase_duration + GNRC_GOMACH_CP_DURATION_US; + if ((uint32_t)wait_phase_duration > GNRC_GOMACH_SUPERFRAME_DURATION_US) { + wait_phase_duration = wait_phase_duration - GNRC_GOMACH_SUPERFRAME_DURATION_US; + } + } + + if ((uint32_t)wait_phase_duration > GNRC_GOMACH_SUPERFRAME_DURATION_US) { + wait_phase_duration = wait_phase_duration % GNRC_GOMACH_SUPERFRAME_DURATION_US; + } + gnrc_gomach_set_timeout(netif, GNRC_GOMACH_TIMEOUT_WAIT_CP, (uint32_t)wait_phase_duration); + + /* Flush the rx-queue. */ + gnrc_priority_pktqueue_flush(&netif->mac.rx.queue); + + netif->mac.tx.tx_busy_count = 0; + + netif->mac.tx.t2k_state = GNRC_GOMACH_T2K_WAIT_CP; + gnrc_gomach_set_update(netif, false); +} + +static void gomach_t2k_wait_cp(gnrc_netif_t *netif) +{ + if (gnrc_gomach_timeout_is_expired(netif, GNRC_GOMACH_TIMEOUT_WAIT_CP)) { + gnrc_gomach_set_netdev_state(netif, NETOPT_STATE_IDLE); + /* Turn radio onto the neighbor's public channel, which will not change in this cycle. */ + gnrc_gomach_turn_channel(netif, netif->mac.tx.current_neighbor->pub_chanseq); + + /* Disable auto-ack, don't try to receive packet! */ + gnrc_gomach_set_autoack(netif, NETOPT_DISABLE); + /* Require ACK for the packet waiting to be sent! */ + gnrc_gomach_set_ack_req(netif, NETOPT_ENABLE); + + /* Enable csma for sending the packet! */ + netopt_enable_t csma_enable = NETOPT_ENABLE; + netif->dev->driver->set(netif->dev, NETOPT_CSMA, &csma_enable, + sizeof(netopt_enable_t)); + + netif->mac.tx.t2k_state = GNRC_GOMACH_T2K_TRANS_IN_CP; + gnrc_gomach_set_update(netif, true); + } +} + +static void gomach_t2k_trans_in_cp(gnrc_netif_t *netif) +{ + /* To-do: should we add a rx-start security check and quit t2k when found + * ongoing transmissions? */ + + /* If we are retransmitting the packet, use the same sequence number for the + * packet to avoid duplicate packet reception at the receiver side. */ + if ((netif->mac.tx.no_ack_counter > 0) || (netif->mac.tx.tx_busy_count > 0)) { + netdev_ieee802154_t *device_state = (netdev_ieee802154_t *)netif->dev; + device_state->seq = netif->mac.tx.tx_seq; + } + + /* Send the data packet here. */ + int res = gnrc_gomach_send_data(netif, NETOPT_ENABLE); + if (res < 0) { + LOG_ERROR("ERROR: [GOMACH] t2k transmission fail: %d, drop packet.\n", res); + netif->mac.tx.no_ack_counter = 0; + + /* If res is < 0, the data packet will not be released in send(). + * so need to release the data here. */ + if (netif->mac.tx.packet != NULL) { + gnrc_pktbuf_release(netif->mac.tx.packet); + netif->mac.tx.packet = NULL; + } + + netif->mac.tx.current_neighbor = NULL; + netif->mac.tx.t2k_state = GNRC_GOMACH_T2K_END; + gnrc_gomach_set_update(netif, true); + return; + } + + gnrc_gomach_set_timeout(netif, GNRC_GOMACH_TIMEOUT_NO_TX_ISR, GNRC_GOMACH_NO_TX_ISR_US); + + netif->mac.tx.t2k_state = GNRC_GOMACH_T2K_WAIT_CPTX_FEEDBACK; + gnrc_gomach_set_update(netif, false); +} + +static void _cp_tx_success(gnrc_netif_t *netif) +{ + /* Since the packet will not be released by the sending function, + * so, here, if TX success, we first release the packet. */ + gnrc_pktbuf_release(netif->mac.tx.packet); + netif->mac.tx.packet = NULL; + + /* Here is the phase-lock auto-adjust scheme. Use the new adjusted + * phase upon success. Here the new phase will be put ahead to the + * original phase. */ + if (netif->mac.tx.no_ack_counter == (GNRC_GOMACH_REPHASELOCK_THRESHOLD - 2)) { + if (netif->mac.tx.current_neighbor->cp_phase >= + GNRC_GOMACH_CP_DURATION_US) { + netif->mac.tx.current_neighbor->cp_phase -= + GNRC_GOMACH_CP_DURATION_US; + } + else { + netif->mac.tx.current_neighbor->cp_phase += + GNRC_GOMACH_SUPERFRAME_DURATION_US; + netif->mac.tx.current_neighbor->cp_phase -= + GNRC_GOMACH_CP_DURATION_US; + } + } + /* Here is the phase-lock auto-adjust scheme. Use the new adjusted + * phase upon success. Here the new phase will be put behind the original + * phase. */ + if (netif->mac.tx.no_ack_counter == (GNRC_GOMACH_REPHASELOCK_THRESHOLD - 1)) { + netif->mac.tx.current_neighbor->cp_phase += + (GNRC_GOMACH_CP_DURATION_US + 20 * US_PER_MS); + + if (netif->mac.tx.current_neighbor->cp_phase >= + GNRC_GOMACH_SUPERFRAME_DURATION_US) { + netif->mac.tx.current_neighbor->cp_phase -= + GNRC_GOMACH_SUPERFRAME_DURATION_US; + } + } + + netif->mac.tx.no_ack_counter = 0; + netif->mac.tx.t2u_fail_count = 0; + + /* If has pending packets, join the vTDMA period, first wait for receiver's beacon. */ + if (gnrc_priority_pktqueue_length(&netif->mac.tx.current_neighbor->queue) > 0) { + netif->mac.tx.vtdma_para.slots_num = 0; + gnrc_gomach_set_timeout(netif, GNRC_GOMACH_TIMEOUT_WAIT_BEACON, + GNRC_GOMACH_WAIT_BEACON_TIME_US); + gnrc_priority_pktqueue_flush(&netif->mac.rx.queue); + netif->mac.tx.t2k_state = GNRC_GOMACH_T2K_WAIT_BEACON; + } + else { + netif->mac.tx.t2k_state = GNRC_GOMACH_T2K_END; + } + gnrc_gomach_set_update(netif, true); +} + +static bool _cp_tx_busy(gnrc_netif_t *netif) +{ + /* If the channel busy counter is below threshold, retry CSMA immediately, + * by knowing that the CP will be automatically extended. */ + if (netif->mac.tx.tx_busy_count < GNRC_GOMACH_TX_BUSY_THRESHOLD) { + netif->mac.tx.tx_busy_count++; + + /* Store the TX sequence number for this packet. Always use the same + * sequence number for sending the same packet, to avoid duplicated + * packet reception at the receiver. */ + netdev_ieee802154_t *device_state = (netdev_ieee802154_t *)netif->dev; + netif->mac.tx.tx_seq = device_state->seq - 1; + + netif->mac.tx.t2k_state = GNRC_GOMACH_T2K_TRANS_IN_CP; + gnrc_gomach_set_update(netif, true); + return false; + } + return true; +} + +static void _cp_tx_default(gnrc_netif_t *netif) +{ + netif->mac.tx.no_ack_counter++; + + LOG_DEBUG("[GOMACH] t2k %d times No-ACK.\n", netif->mac.tx.no_ack_counter); + + /* This packet will be retried. Store the TX sequence number for this packet. + * Always use the same sequence number for sending the same packet. */ + netdev_ieee802154_t *device_state = (netdev_ieee802154_t *)netif->dev; + netif->mac.tx.tx_seq = device_state->seq - 1; + + /* If no_ack_counter reaches the threshold, regarded as phase-lock failed. So + * retry to send the packet in t2u, i.e., try to phase-lock with the receiver + * again. */ + if (netif->mac.tx.no_ack_counter >= GNRC_GOMACH_REPHASELOCK_THRESHOLD) { + LOG_DEBUG("[GOMACH] t2k failed, go to t2u.\n"); + /* Here, we don't queue the packet again, but keep it in tx.packet. */ + netif->mac.tx.current_neighbor->mac_type = GNRC_GOMACH_TYPE_UNKNOWN; + netif->mac.tx.t2u_retry_counter = 0; + } + + netif->mac.tx.t2k_state = GNRC_GOMACH_T2K_END; + gnrc_gomach_set_update(netif, true); +} + +static void gomach_t2k_wait_cp_txfeedback(gnrc_netif_t *netif) +{ + if (gnrc_gomach_timeout_is_expired(netif, GNRC_GOMACH_TIMEOUT_NO_TX_ISR)) { + /* No TX-ISR, go to sleep. */ + netif->mac.tx.no_ack_counter++; + + netdev_ieee802154_t *device_state = (netdev_ieee802154_t *)netif->dev; + netif->mac.tx.tx_seq = device_state->seq - 1; + + /* Here, we don't queue the packet again, but keep it in tx.packet. */ + netif->mac.tx.t2k_state = GNRC_GOMACH_T2K_END; + gnrc_gomach_set_update(netif, true); + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_NO_TX_ISR); + return; + } + + if (gnrc_gomach_get_tx_finish(netif)) { + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_NO_TX_ISR); + + switch (gnrc_netif_get_tx_feedback(netif)) { + case TX_FEEDBACK_SUCCESS: { + _cp_tx_success(netif); + break; + } + case TX_FEEDBACK_BUSY: + if (!_cp_tx_busy(netif)) { + return; + } + case TX_FEEDBACK_NOACK: + default: { + _cp_tx_default(netif); + break; + } + } + } +} + +static void gomach_t2k_wait_beacon(gnrc_netif_t *netif) +{ + /* Process the beacon if we receive it. */ + if (gnrc_gomach_get_pkt_received(netif)) { + gnrc_gomach_set_pkt_received(netif, false); + gnrc_gomach_packet_process_in_wait_beacon(netif); + } + + /* If we need to quit t2k, don't release the current neighbor pointer. In the + * next cycle, we will try to send to the same receiver. */ + if (gnrc_gomach_get_quit_cycle(netif)) { + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_WAIT_BEACON); + netif->mac.tx.t2k_state = GNRC_GOMACH_T2K_END; + gnrc_gomach_set_update(netif, true); + return; + } + + if (netif->mac.tx.vtdma_para.slots_num > 0) { + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_WAIT_BEACON); + + /* If the sender gets allocated slots, go to attend the receiver's vTDMA for + * burst sending all the pending packets to the receiver. */ + if (netif->mac.tx.vtdma_para.slots_num > 0) { + /* Switch the radio to the sub-channel of the receiver. */ + gnrc_gomach_turn_channel(netif, netif->mac.tx.vtdma_para.sub_channel_seq); + + /* If the allocated slots period is not right behind the beacon, i.e., not the first + * one, turn off the radio and wait for its own slots period. */ + if (netif->mac.tx.vtdma_para.slots_position > 0) { + gnrc_gomach_set_netdev_state(netif, NETOPT_STATE_SLEEP); + + uint32_t wait_slots_duration = netif->mac.tx.vtdma_para.slots_position * + GNRC_GOMACH_VTDMA_SLOT_SIZE_US; + gnrc_gomach_set_timeout(netif, GNRC_GOMACH_TIMEOUT_WAIT_SLOTS, + wait_slots_duration); + + netif->mac.tx.t2k_state = GNRC_GOMACH_T2K_WAIT_SLOTS; + gnrc_gomach_set_update(netif, true); + } + else { + /* If the allocated slots period is the first one in vTDMA, + * start sending packets. */ + gnrc_pktsnip_t *pkt = + gnrc_priority_pktqueue_pop(&(netif->mac.tx.current_neighbor->queue)); + if (pkt != NULL) { + netif->mac.tx.packet = pkt; + netif->mac.tx.t2k_state = GNRC_GOMACH_T2K_VTDMA_TRANS; + } + else { + LOG_ERROR("ERROR: [GOMACH] t2k vTDMA: null packet.\n"); + netif->mac.tx.t2k_state = GNRC_GOMACH_T2K_END; + } + gnrc_gomach_set_update(netif, true); + } + } + else { + /* No slots get allocated, go to t2k end. */ + netif->mac.tx.t2k_state = GNRC_GOMACH_T2K_END; + gnrc_gomach_set_update(netif, true); + } + return; + } + + /* If no beacon during waiting period, go to t2k end. + * Or, if we have received beacon, but find no allocated slots, + * go to t2k as well. */ + if (gnrc_gomach_timeout_is_expired(netif, GNRC_GOMACH_TIMEOUT_WAIT_BEACON) || + !gnrc_gomach_timeout_is_running(netif, GNRC_GOMACH_TIMEOUT_WAIT_BEACON)) { + gnrc_priority_pktqueue_flush(&netif->mac.rx.queue); + LOG_DEBUG("[GOMACH] t2k: no beacon.\n"); + netif->mac.tx.t2k_state = GNRC_GOMACH_T2K_END; + gnrc_gomach_set_update(netif, true); + } +} + +static void gomach_t2k_wait_own_slots(gnrc_netif_t *netif) +{ + if (gnrc_gomach_timeout_is_expired(netif, GNRC_GOMACH_TIMEOUT_WAIT_SLOTS)) { + /* The node is now in its scheduled slots period, start burst sending packets. */ + gnrc_gomach_set_netdev_state(netif, NETOPT_STATE_IDLE); + + gnrc_pktsnip_t *pkt = gnrc_priority_pktqueue_pop(&(netif->mac.tx.current_neighbor->queue)); + if (pkt != NULL) { + netif->mac.tx.packet = pkt; + netif->mac.tx.t2k_state = GNRC_GOMACH_T2K_VTDMA_TRANS; + } + else { + LOG_ERROR("ERROR: [GOMACH] t2k vTDMA: null packet.\n"); + netif->mac.tx.t2k_state = GNRC_GOMACH_T2K_END; + } + gnrc_gomach_set_update(netif, true); + } +} + +static void gomach_t2k_trans_in_slots(gnrc_netif_t *netif) +{ + /* If this packet is being retransmitted, use the same recorded MAC sequence number. */ + if (netif->mac.tx.no_ack_counter > 0) { + netdev_ieee802154_t *device_state = (netdev_ieee802154_t *)netif->dev; + device_state->seq = netif->mac.tx.tx_seq; + } + + /* Send data packet in its allocated slots (scheduled slots period). */ + int res = gnrc_gomach_send_data(netif, NETOPT_DISABLE); + if (res < 0) { + LOG_ERROR("ERROR: [GOMACH] t2k vTDMA transmission fail: %d, drop packet.\n", res); + + /* If res is < 0, the data packet will not be released in send(). + * so need to release the data here. */ + if (netif->mac.tx.packet != NULL) { + gnrc_pktbuf_release(netif->mac.tx.packet); + netif->mac.tx.packet = NULL; + } + netif->mac.tx.current_neighbor = NULL; + netif->mac.tx.t2k_state = GNRC_GOMACH_T2K_END; + gnrc_gomach_set_update(netif, true); + return; + } + + gnrc_gomach_set_timeout(netif, GNRC_GOMACH_TIMEOUT_NO_TX_ISR, GNRC_GOMACH_NO_TX_ISR_US); + + netif->mac.tx.vtdma_para.slots_num--; + netif->mac.tx.t2k_state = GNRC_GOMACH_T2K_WAIT_VTDMA_FEEDBACK; + gnrc_gomach_set_update(netif, false); +} + +static void _t2k_wait_vtdma_tx_success(gnrc_netif_t *netif) +{ + /* First release the packet. */ + gnrc_pktbuf_release(netif->mac.tx.packet); + netif->mac.tx.packet = NULL; + netif->mac.tx.no_ack_counter = 0; + + /* If the sender has pending packets and scheduled slots, + * continue vTDMA transmission. */ + if ((netif->mac.tx.vtdma_para.slots_num > 0) && + (gnrc_priority_pktqueue_length(&netif->mac.tx.current_neighbor->queue) > 0)) { + gnrc_pktsnip_t *pkt = gnrc_priority_pktqueue_pop(&netif->mac.tx.current_neighbor->queue); + if (pkt != NULL) { + netif->mac.tx.packet = pkt; + netif->mac.tx.t2k_state = GNRC_GOMACH_T2K_VTDMA_TRANS; + } + else { + LOG_ERROR("ERROR: [GOMACH] t2k vTDMA: null packet.\n"); + netif->mac.tx.t2k_state = GNRC_GOMACH_T2K_END; + } + } + else { + /* If no scheduled slots or pending packets, go to t2k end. */ + netif->mac.tx.t2k_state = GNRC_GOMACH_T2K_END; + } + gnrc_gomach_set_update(netif, true); +} + +static void _t2k_wait_vtdma_tx_default(gnrc_netif_t *netif) +{ + /* In case of transmission failure in vTDMA, retransmit the packet in the next + * scheduled slot, or the next cycle's t2k procedure. */ + + /* Firstly, mark the current TX packet as not ACKed and record the MAC sequence + * number, such that the MAC will use the same MAC sequence to send it. + * Also, by marking no_ack_counter as non-zero, the neighbor and packet pointers + * will then not be released in t2k-end. Then, the packet can be retried right in + * the following cycle. */ + netif->mac.tx.no_ack_counter = 1; + + netdev_ieee802154_t *device_state = (netdev_ieee802154_t *)netif->dev; + netif->mac.tx.tx_seq = device_state->seq - 1; + + /* Do not release the packet here, continue sending the same packet. ***/ + if (netif->mac.tx.vtdma_para.slots_num > 0) { + LOG_DEBUG("[GOMACH] no ACK in vTDMA, retry in next slot.\n"); + netif->mac.tx.t2k_state = GNRC_GOMACH_T2K_VTDMA_TRANS; + } + else { + /* If no slots for sending, retry in next cycle's t2r, without releasing + * tx.packet pointer. */ + LOG_DEBUG("[GOMACH] no ACK in vTDMA, retry in next cycle.\n"); + + netif->mac.tx.t2k_state = GNRC_GOMACH_T2K_END; + } + gnrc_gomach_set_update(netif, true); +} + +static void gomach_t2k_wait_vtdma_transfeedback(gnrc_netif_t *netif) +{ + if (gnrc_gomach_timeout_is_expired(netif, GNRC_GOMACH_TIMEOUT_NO_TX_ISR)) { + /* No TX-ISR, go to sleep. */ + netif->mac.tx.no_ack_counter++; + + netdev_ieee802154_t *device_state = (netdev_ieee802154_t *)netif->dev; + netif->mac.tx.tx_seq = device_state->seq - 1; + + /* Here, we don't queue the packet again, but keep it in tx.packet. */ + netif->mac.tx.t2k_state = GNRC_GOMACH_T2K_END; + gnrc_gomach_set_update(netif, true); + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_NO_TX_ISR); + return; + } + + if (gnrc_gomach_get_tx_finish(netif)) { + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_NO_TX_ISR); + + switch (gnrc_netif_get_tx_feedback(netif)) { + case TX_FEEDBACK_SUCCESS: { + _t2k_wait_vtdma_tx_success(netif); + break; + } + case TX_FEEDBACK_BUSY: + case TX_FEEDBACK_NOACK: + default: { + _t2k_wait_vtdma_tx_default(netif); + break; + } + } + } +} + +static void gomach_t2k_end(gnrc_netif_t *netif) +{ + gnrc_gomach_set_netdev_state(netif, NETOPT_STATE_SLEEP); + + /* In GoMacH, normally, in case of transmission failure, no packet will be released + * in t2k. Failed packet will only be released in t2u. In case of continuous t2k + * failures, the MAC will goto t2u to retry the packet without releasing it here. */ + if ((netif->mac.tx.packet != NULL) && (netif->mac.tx.no_ack_counter == 0)) { + LOG_ERROR("ERROR: [GOMACH] t2k: releasing unexpected packet!\n"); + gnrc_pktbuf_release(netif->mac.tx.packet); + netif->mac.tx.packet = NULL; + } + + /* In case no_ack_counter is not 0 here, it means we will retry to send the packet + * in t2k or t2u, then, don't release the neighbor pointer. */ + if (netif->mac.tx.no_ack_counter == 0) { + netif->mac.tx.current_neighbor = NULL; + } + + /* Clear all timeouts. */ + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_WAIT_CP); + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_WAIT_BEACON); + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_WAIT_SLOTS); + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_NO_TX_ISR); + + /* Reset t2k_state to the initial state. */ + netif->mac.tx.t2k_state = GNRC_GOMACH_T2K_INIT; + + netif->mac.prot.gomach.basic_state = GNRC_GOMACH_LISTEN; + netif->mac.rx.listen_state = GNRC_GOMACH_LISTEN_SLEEP; + gnrc_gomach_set_enter_new_cycle(netif, false); + gnrc_gomach_set_update(netif, true); + +#if (GNRC_GOMACH_ENABLE_DUTYCYLE_RECORD == 1) + /* Output duty-cycle ratio */ + uint64_t duty; + duty = xtimer_now_usec64(); + duty = (netif->mac.prot.gomach.awake_duration_sum_ticks) * 100 / + (duty - netif->mac.prot.gomach.system_start_time_ticks); + printf("[GoMacH]: achieved radio duty-cycle: %lu %% \n", (uint32_t)duty); +#endif +} + +static void gomach_t2k_update(gnrc_netif_t *netif) +{ + /* State machine of GoMacH's t2k (transmit to phase-known device) procedure. */ + switch (netif->mac.tx.t2k_state) { + case GNRC_GOMACH_T2K_INIT: { + gomach_t2k_init(netif); + break; + } + case GNRC_GOMACH_T2K_WAIT_CP: { + gomach_t2k_wait_cp(netif); + break; + } + case GNRC_GOMACH_T2K_TRANS_IN_CP: { + gomach_t2k_trans_in_cp(netif); + break; + } + case GNRC_GOMACH_T2K_WAIT_CPTX_FEEDBACK: { + gomach_t2k_wait_cp_txfeedback(netif); + break; + } + case GNRC_GOMACH_T2K_WAIT_BEACON: { + gomach_t2k_wait_beacon(netif); + break; + } + case GNRC_GOMACH_T2K_WAIT_SLOTS: { + gomach_t2k_wait_own_slots(netif); + break; + } + case GNRC_GOMACH_T2K_VTDMA_TRANS: { + gomach_t2k_trans_in_slots(netif); + break; + } + case GNRC_GOMACH_T2K_WAIT_VTDMA_FEEDBACK: { + gomach_t2k_wait_vtdma_transfeedback(netif); + break; + } + case GNRC_GOMACH_T2K_END: { + gomach_t2k_end(netif); + break; + } + default: break; + } +} + +static void gomach_t2u_init(gnrc_netif_t *netif) +{ + /* since t2u is right following CP period (wake-up period), the radio is still on, + * so we don't need to turn on it again. */ + + LOG_DEBUG("[GOMACH] t2u initialization.\n"); + + gnrc_netif_set_rx_started(netif, false); + gnrc_gomach_set_quit_cycle(netif, false); + gnrc_gomach_set_pkt_received(netif, false); + netif->mac.tx.preamble_sent = 0; + gnrc_gomach_set_got_preamble_ack(netif, false); + gnrc_gomach_set_buffer_full(netif, false); + + /* Start sending the preamble firstly on public channel 1. */ + gnrc_gomach_turn_channel(netif, netif->mac.prot.gomach.pub_channel_1); + + /* Disable auto-ACK here! Don't try to reply ACK to any node. */ + gnrc_gomach_set_autoack(netif, NETOPT_DISABLE); + + gnrc_gomach_set_on_pubchan_1(netif, true); + + gnrc_priority_pktqueue_flush(&netif->mac.rx.queue); + + netif->mac.tx.t2u_state = GNRC_GOMACH_T2U_PREAMBLE_PREPARE; + gnrc_gomach_set_update(netif, true); +} + +static void gomach_t2u_send_preamble_prepare(gnrc_netif_t *netif) +{ + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_MAX_PREAM_INTERVAL); + + if (netif->mac.tx.preamble_sent != 0) { + /* Toggle the radio channel after each preamble transmission. */ + if (gnrc_gomach_get_on_pubchan_1(netif)) { + gnrc_gomach_turn_channel(netif, netif->mac.prot.gomach.pub_channel_2); + gnrc_gomach_set_on_pubchan_1(netif, false); + } + else { + gnrc_gomach_turn_channel(netif, netif->mac.prot.gomach.pub_channel_1); + gnrc_gomach_set_on_pubchan_1(netif, true); + } + gnrc_gomach_set_timeout(netif, GNRC_GOMACH_TIMEOUT_MAX_PREAM_INTERVAL, + GNRC_GOMACH_MAX_PREAM_INTERVAL_US); + } + else { + /* Here, for the first preamble, we set the pream_max_interval timeout to + * 5*MAX_PREAM_INTERVAL due to the fact that the first preamble is + * using csma for sending, and csma costs some time before actually sending + * the packet. */ + gnrc_gomach_set_timeout(netif, GNRC_GOMACH_TIMEOUT_MAX_PREAM_INTERVAL, + (5 * GNRC_GOMACH_MAX_PREAM_INTERVAL_US)); + } + + gnrc_gomach_set_max_pream_interv(netif, false); + netif->mac.tx.t2u_state = GNRC_GOMACH_T2U_SEND_PREAMBLE; + gnrc_gomach_set_update(netif, true); +} + +static void gomach_t2u_send_preamble(gnrc_netif_t *netif) +{ + /* Now, start sending preamble. */ + int res; + + /* The first preamble is sent with csma for collision avoidance. */ + if (netif->mac.tx.preamble_sent == 0) { + res = gnrc_gomach_send_preamble(netif, NETOPT_ENABLE); + gnrc_gomach_set_timeout(netif, GNRC_GOMACH_TIMEOUT_PREAM_DURATION, + GNRC_GOMACH_PREAMBLE_DURATION_US); + } + else { + res = gnrc_gomach_send_preamble(netif, NETOPT_DISABLE); + } + + if (res < 0) { + LOG_ERROR("ERROR: [GOMACH] t2u send preamble failed: %d\n", res); + } + + /* In case that packet-buffer is full, quit t2u and release packet. */ + if (res == -ENOBUFS) { + LOG_ERROR("ERROR: [GOMACH] t2u: no pkt-buffer for sending preamble.\n"); + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_PREAM_DURATION); + + netif->mac.tx.t2u_state = GNRC_GOMACH_T2U_END; + gnrc_gomach_set_update(netif, true); + return; + } + + gnrc_netif_set_rx_started(netif, false); + netif->mac.tx.preamble_sent++; + netif->mac.tx.t2u_state = GNRC_GOMACH_T2U_WAIT_PREAMBLE_TX; + gnrc_gomach_set_update(netif, false); +} + +static void gomach_t2u_wait_preamble_tx(gnrc_netif_t *netif) +{ + if (gnrc_gomach_get_tx_finish(netif)) { + /* Set preamble interval timeout. This is a very short timeout (1ms), + * just to catch the rx-start event of receiving possible preamble-ACK. */ + gnrc_gomach_set_timeout(netif, GNRC_GOMACH_TIMEOUT_PREAMBLE, + GNRC_GOMACH_PREAMBLE_INTERVAL_US); + + netif->mac.tx.t2u_state = GNRC_GOMACH_T2U_WAIT_PREAMBLE_ACK; + gnrc_gomach_set_update(netif, false); + return; + } + + /* This is mainly to handle no-TX-complete error. Once the max preamble interval + * timeout expired here (i.e., no-TX-complete error), we will quit waiting here + * and go to send the next preamble, thus the MAC will not get stucked here. */ + if (gnrc_gomach_timeout_is_expired(netif, GNRC_GOMACH_TIMEOUT_MAX_PREAM_INTERVAL)) { + gnrc_priority_pktqueue_flush(&netif->mac.rx.queue); + netif->mac.tx.t2u_state = GNRC_GOMACH_T2U_PREAMBLE_PREPARE; + gnrc_gomach_set_update(netif, true); + return; + } +} + +static bool _handle_in_t2u_send_preamble(gnrc_netif_t *netif) +{ + /* If packet buffer is full, release one packet to release memory, + * and reload the next packet. + * In t2u, we need at least some minimum memory to build the preamble packet. */ + if (gnrc_gomach_get_buffer_full(netif)) { + gnrc_gomach_set_buffer_full(netif, false); + + gnrc_gomach_set_update(netif, true); + + /* To-do: should we release all the buffered packets in the queue to + * release memory in such a critical situation? */ + LOG_DEBUG("[GOMACH] t2u: pkt-buffer full, release one pkt.\n"); + + if (netif->mac.tx.packet != NULL) { + gnrc_pktbuf_release(netif->mac.tx.packet); + netif->mac.tx.packet = NULL; + netif->mac.tx.no_ack_counter = 0; + } + /* Reload the next packet in the neighbor's queue. */ + gnrc_pktsnip_t *pkt = gnrc_priority_pktqueue_pop(&netif->mac.tx.current_neighbor->queue); + + if (pkt != NULL) { + netif->mac.tx.packet = pkt; + } + else { + LOG_DEBUG("[GOMACH] t2u: null packet, quit t2u.\n"); + netif->mac.tx.current_neighbor = NULL; + netif->mac.tx.t2u_state = GNRC_GOMACH_T2U_END; + return false; + } + } + + if (gnrc_gomach_timeout_is_expired(netif, GNRC_GOMACH_TIMEOUT_MAX_PREAM_INTERVAL)) { + gnrc_gomach_set_max_pream_interv(netif, true); + return true; + } + + /* if we are receiving packet, wait until RX is completed. */ + if ((!gnrc_gomach_timeout_is_running(netif, GNRC_GOMACH_TIMEOUT_WAIT_RX_END)) && + gnrc_netif_get_rx_started(netif) && + (!gnrc_gomach_get_max_pream_interv(netif))) { + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_PREAMBLE); + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_MAX_PREAM_INTERVAL); + + /* Set a timeout to wait for the complete of reception. */ + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_WAIT_RX_END); + + gnrc_gomach_set_timeout(netif, GNRC_GOMACH_TIMEOUT_WAIT_RX_END, + GNRC_GOMACH_WAIT_RX_END_US); + return false; + } + + if (gnrc_gomach_get_pkt_received(netif)) { + gnrc_gomach_set_pkt_received(netif, false); + gnrc_gomach_process_pkt_in_wait_preamble_ack(netif); + } + + /* Quit t2u if we have to, e.g., the device found ongoing bcast of other devices. */ + if (gnrc_gomach_get_quit_cycle(netif)) { + LOG_DEBUG("[GOMACH] quit t2u.\n"); + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_WAIT_RX_END); + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_PREAMBLE); + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_PREAM_DURATION); + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_MAX_PREAM_INTERVAL); + + netif->mac.tx.t2u_state = GNRC_GOMACH_T2U_END; + gnrc_gomach_set_update(netif, true); + return false; + } + + if (gnrc_gomach_timeout_is_expired(netif, GNRC_GOMACH_TIMEOUT_WAIT_RX_END)) { + gnrc_gomach_set_max_pream_interv(netif, true); + } + return true; +} + +static void gomach_t2u_wait_preamble_ack(gnrc_netif_t *netif) +{ + if (!_handle_in_t2u_send_preamble(netif)) { + return; + } + + if (gnrc_gomach_get_got_preamble_ack(netif)) { + /* Require ACK for the packet waiting to be sent! */ + gnrc_gomach_set_ack_req(netif, NETOPT_ENABLE); + + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_PREAMBLE); + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_PREAM_DURATION); + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_MAX_PREAM_INTERVAL); + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_WAIT_RX_END); + netif->mac.tx.t2u_state = GNRC_GOMACH_T2U_SEND_DATA; + netif->mac.tx.t2u_fail_count = 0; + gnrc_gomach_set_update(netif, true); + return; + } + + if (gnrc_gomach_timeout_is_expired(netif, GNRC_GOMACH_TIMEOUT_PREAM_DURATION)) { + netif->mac.tx.t2u_retry_counter++; + + /* If we reach the maximum t2u retry limit, release the data packet. */ + if (netif->mac.tx.t2u_retry_counter >= GNRC_GOMACH_T2U_RETYR_THRESHOLD) { + LOG_DEBUG("[GOMACH] t2u failed: no preamble-ACK.\n"); + netif->mac.tx.t2u_retry_counter = 0; + netif->mac.tx.t2u_state = GNRC_GOMACH_T2U_END; + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_PREAMBLE); + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_WAIT_RX_END); + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_MAX_PREAM_INTERVAL); + + netif->mac.tx.t2u_fail_count++; + } + else { + /* If we haven't reach the maximum t2u limit, try again. Set quit_current_cycle + * flag to true such that we will release the current neighbor pointer. */ + gnrc_gomach_set_quit_cycle(netif, true); + netif->mac.tx.t2u_state = GNRC_GOMACH_T2U_END; + } + + gnrc_gomach_set_update(netif, true); + return; + } + + /* If we didn't catch the RX-start event, go to send the next preamble. */ + if ((gnrc_gomach_timeout_is_expired(netif, GNRC_GOMACH_TIMEOUT_PREAMBLE)) || + gnrc_gomach_get_max_pream_interv(netif)) { + netif->mac.tx.t2u_state = GNRC_GOMACH_T2U_PREAMBLE_PREPARE; + gnrc_gomach_set_update(netif, true); + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_PREAMBLE); + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_MAX_PREAM_INTERVAL); + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_WAIT_RX_END); + } +} + +static void gomach_t2u_send_data(gnrc_netif_t *netif) +{ + /* If we are retrying to send the data, reload its original MAC sequence. */ + if (netif->mac.tx.no_ack_counter > 0) { + netdev_ieee802154_t *device_state = (netdev_ieee802154_t *)netif->dev; + device_state->seq = netif->mac.tx.tx_seq; + } + + /* Here, we send the data to the receiver. */ + int res = gnrc_gomach_send_data(netif, NETOPT_ENABLE); + if (res < 0) { + LOG_ERROR("ERROR: [GOMACH] t2u data sending error: %d.\n", res); + + /* If res is < 0, the data packet will not be released in send(). + * so need to release the data here. */ + if (netif->mac.tx.packet != NULL) { + gnrc_pktbuf_release(netif->mac.tx.packet); + netif->mac.tx.packet = NULL; + } + + netif->mac.tx.t2u_state = GNRC_GOMACH_T2U_END; + gnrc_gomach_set_update(netif, true); + return; + } + + gnrc_gomach_set_timeout(netif, GNRC_GOMACH_TIMEOUT_NO_TX_ISR, GNRC_GOMACH_NO_TX_ISR_US); + + netif->mac.tx.t2u_state = GNRC_GOMACH_T2U_WAIT_DATA_TX; + gnrc_gomach_set_update(netif, false); +} + +static void _t2u_data_tx_success(gnrc_netif_t *netif) +{ + /* If transmission succeeded, release the data. */ + gnrc_pktbuf_release(netif->mac.tx.packet); + netif->mac.tx.packet = NULL; + + netif->mac.tx.no_ack_counter = 0; + netif->mac.tx.t2u_retry_counter = 0; + + /* Attend the vTDMA procedure if the sender has pending packets for the receiver. */ + if (gnrc_priority_pktqueue_length(&netif->mac.tx.current_neighbor->queue) > 0) { + netif->mac.tx.t2u_state = GNRC_GOMACH_T2U_INIT; + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_PREAMBLE); + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_PREAM_DURATION); + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_WAIT_RX_END); + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_MAX_PREAM_INTERVAL); + + /* Switch to t2k procedure and wait for the beacon of the receiver. */ + netif->mac.tx.vtdma_para.slots_num = 0; + gnrc_gomach_set_timeout(netif, GNRC_GOMACH_TIMEOUT_WAIT_BEACON, + GNRC_GOMACH_WAIT_BEACON_TIME_US); + gnrc_priority_pktqueue_flush(&netif->mac.rx.queue); + + netif->mac.tx.t2k_state = GNRC_GOMACH_T2K_WAIT_BEACON; + netif->mac.tx.transmit_state = GNRC_GOMACH_TRANS_TO_KNOWN; + } + else { + netif->mac.tx.t2u_state = GNRC_GOMACH_T2U_END; + } +} + +static void _t2u_data_tx_fail(gnrc_netif_t *netif) +{ + netif->mac.tx.t2u_retry_counter++; + /* If we meet t2u retry limit, release the packet. */ + if (netif->mac.tx.t2u_retry_counter >= GNRC_GOMACH_T2U_RETYR_THRESHOLD) { + LOG_DEBUG("[GOMACH] t2u send data failed on channel %d," + " drop packet.\n", netif->mac.tx.current_neighbor->pub_chanseq); + gnrc_pktbuf_release(netif->mac.tx.packet); + netif->mac.tx.packet = NULL; + netif->mac.tx.current_neighbor = NULL; + netif->mac.tx.no_ack_counter = 0; + netif->mac.tx.t2u_retry_counter = 0; + netif->mac.tx.t2u_state = GNRC_GOMACH_T2U_END; + } + else { + /* Record the MAC sequence of the data, retry t2u in next cycle. */ + netif->mac.tx.no_ack_counter = GNRC_GOMACH_REPHASELOCK_THRESHOLD; + netdev_ieee802154_t *device_state = (netdev_ieee802154_t *)netif->dev; + netif->mac.tx.tx_seq = device_state->seq - 1; + + LOG_DEBUG("[GOMACH] t2u send data failed on channel %d.\n", + netif->mac.tx.current_neighbor->pub_chanseq); + /* Set quit_current_cycle to true, thus not to release current_neighbour pointer + * in t2u-end */ + gnrc_gomach_set_quit_cycle(netif, true); + netif->mac.tx.t2u_state = GNRC_GOMACH_T2U_END; + } +} + +static void gomach_t2u_wait_tx_feedback(gnrc_netif_t *netif) +{ + if (gnrc_gomach_timeout_is_expired(netif, GNRC_GOMACH_TIMEOUT_NO_TX_ISR)) { + /* No TX-ISR, go to sleep. */ + netif->mac.tx.t2u_retry_counter++; + + netif->mac.tx.no_ack_counter = GNRC_GOMACH_REPHASELOCK_THRESHOLD; + netdev_ieee802154_t *device_state = (netdev_ieee802154_t *)netif->dev; + netif->mac.tx.tx_seq = device_state->seq - 1; + + gnrc_gomach_set_quit_cycle(netif, true); + netif->mac.tx.t2u_state = GNRC_GOMACH_T2U_END; + + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_NO_TX_ISR); + gnrc_gomach_set_update(netif, true); + return; + } + + if (gnrc_gomach_get_tx_finish(netif)) { + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_NO_TX_ISR); + + if (gnrc_netif_get_tx_feedback(netif) == TX_FEEDBACK_SUCCESS) { + _t2u_data_tx_success(netif); + } + else { + _t2u_data_tx_fail(netif); + } + gnrc_gomach_set_update(netif, true); + } +} + +static void gomach_t2u_end(gnrc_netif_t *netif) +{ + gnrc_gomach_set_netdev_state(netif, NETOPT_STATE_SLEEP); + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_PREAMBLE); + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_PREAM_DURATION); + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_WAIT_RX_END); + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_MAX_PREAM_INTERVAL); + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_NO_TX_ISR); + + /* In case quit_current_cycle is true, don't release neighbor pointer, + * will retry t2u immediately in next cycle.*/ + if (!gnrc_gomach_get_quit_cycle(netif)) { + if (netif->mac.tx.packet != NULL) { + gnrc_pktbuf_release(netif->mac.tx.packet); + netif->mac.tx.packet = NULL; + netif->mac.tx.no_ack_counter = 0; + LOG_WARNING("WARNING: [GOMACH] t2u: drop packet.\n"); + } + netif->mac.tx.current_neighbor = NULL; + } + + /* Reset t2u state. */ + netif->mac.tx.t2u_state = GNRC_GOMACH_T2U_INIT; + + /* Resume to listen state and go to sleep. */ + netif->mac.prot.gomach.basic_state = GNRC_GOMACH_LISTEN; + netif->mac.rx.listen_state = GNRC_GOMACH_LISTEN_SLEEP; + gnrc_gomach_set_enter_new_cycle(netif, false); + gnrc_gomach_set_update(netif, true); + +#if (GNRC_GOMACH_ENABLE_DUTYCYLE_RECORD == 1) + /* Output duty-cycle ratio */ + uint64_t duty; + duty = xtimer_now_usec64(); + duty = (netif->mac.prot.gomach.awake_duration_sum_ticks) * 100 / + (duty - netif->mac.prot.gomach.system_start_time_ticks); + printf("[GoMacH]: achieved radio duty-cycle: %lu %% \n", (uint32_t)duty); +#endif +} + +static void gomach_t2u_update(gnrc_netif_t *netif) +{ + /* State machine of GoMacH's t2u (transmit to phase-unknown device) procedure. */ + switch (netif->mac.tx.t2u_state) { + case GNRC_GOMACH_T2U_INIT: { + gomach_t2u_init(netif); + break; + } + case GNRC_GOMACH_T2U_PREAMBLE_PREPARE: { + gomach_t2u_send_preamble_prepare(netif); + break; + } + case GNRC_GOMACH_T2U_SEND_PREAMBLE: { + gomach_t2u_send_preamble(netif); + break; + } + case GNRC_GOMACH_T2U_WAIT_PREAMBLE_TX: { + gomach_t2u_wait_preamble_tx(netif); + break; + } + case GNRC_GOMACH_T2U_WAIT_PREAMBLE_ACK: { + gomach_t2u_wait_preamble_ack(netif); + break; + } + case GNRC_GOMACH_T2U_SEND_DATA: { + gomach_t2u_send_data(netif); + break; + } + case GNRC_GOMACH_T2U_WAIT_DATA_TX: { + gomach_t2u_wait_tx_feedback(netif); + break; + } + case GNRC_GOMACH_T2U_END: { + gomach_t2u_end(netif); + break; + } + default: break; + } +} + +static void _gomach_phase_backoff(gnrc_netif_t *netif) +{ + /* Execute phase backoff for avoiding CP (wake-up period) overlap. */ + rtt_clear_alarm(); + xtimer_usleep(netif->mac.prot.gomach.backoff_phase_us); + + rtt_set_counter(0); + netif->mac.prot.gomach.last_wakeup = rtt_get_counter(); + + uint32_t alarm = netif->mac.prot.gomach.last_wakeup + + RTT_US_TO_TICKS(GNRC_GOMACH_SUPERFRAME_DURATION_US); + + rtt_set_alarm(alarm, _gomach_rtt_cb, (void *) GNRC_GOMACH_EVENT_RTT_NEW_CYCLE); + + gnrc_gomach_update_neighbor_phase(netif); + + LOG_INFO("INFO: [GOMACH] phase backoffed: %lu us.\n", + netif->mac.prot.gomach.backoff_phase_us); +} + +static void gomach_listen_init(gnrc_netif_t *netif) +{ + /* Reset last_seq_info, for avoiding receiving duplicate packets. + * To-do: remove this in the future? */ + for (uint8_t i = 0; i < GNRC_GOMACH_DUPCHK_BUFFER_SIZE; i++) { + if (netif->mac.rx.check_dup_pkt.last_nodes[i].node_addr.len != 0) { + netif->mac.rx.check_dup_pkt.last_nodes[i].life_cycle++; + if (netif->mac.rx.check_dup_pkt.last_nodes[i].life_cycle >= + GNRC_GOMACH_RX_DUPCHK_UNIT_LIFE) { + netif->mac.rx.check_dup_pkt.last_nodes[i].node_addr.len = 0; + netif->mac.rx.check_dup_pkt.last_nodes[i].node_addr.addr[0] = 0; + netif->mac.rx.check_dup_pkt.last_nodes[i].node_addr.addr[1] = 0; + netif->mac.rx.check_dup_pkt.last_nodes[i].seq = 0; + netif->mac.rx.check_dup_pkt.last_nodes[i].life_cycle = 0; + } + } + } + + if (netif->mac.tx.t2u_fail_count >= GNRC_GOMACH_MAX_T2U_RETYR_THRESHOLD) { + netif->mac.tx.t2u_fail_count = 0; + LOG_DEBUG("[GOMACH]: Re-initialize radio."); + gomach_reinit_radio(netif); + } + gnrc_gomach_set_enter_new_cycle(netif, false); + + /* Set listen period timeout. */ + uint32_t listen_period = random_uint32_range(0, GNRC_GOMACH_CP_RANDOM_END_US) + + GNRC_GOMACH_CP_DURATION_US; + gnrc_gomach_set_timeout(netif, GNRC_GOMACH_TIMEOUT_CP_END, listen_period); + gnrc_gomach_set_timeout(netif, GNRC_GOMACH_TIMEOUT_CP_MAX, GNRC_GOMACH_CP_DURATION_MAX_US); + + gnrc_netif_set_rx_started(netif, false); + gnrc_gomach_set_pkt_received(netif, false); + netif->mac.prot.gomach.cp_extend_count = 0; + gnrc_gomach_set_quit_cycle(netif, false); + gnrc_gomach_set_unintd_preamble(netif, false); + gnrc_gomach_set_beacon_fail(netif, false); + gnrc_gomach_set_cp_end(netif, false); + gnrc_gomach_set_got_preamble(netif, false); + + /* Flush RX queue and turn on radio. */ + gnrc_priority_pktqueue_flush(&netif->mac.rx.queue); + gnrc_gomach_set_netdev_state(netif, NETOPT_STATE_IDLE); + + /* Turn to current public channel. */ + gnrc_gomach_turn_channel(netif, netif->mac.prot.gomach.cur_pub_channel); + + /* Enable Auto-ACK for data packet reception. */ + gnrc_gomach_set_autoack(netif, NETOPT_ENABLE); + + netif->mac.rx.listen_state = GNRC_GOMACH_LISTEN_CP_LISTEN; + gnrc_gomach_set_update(netif, false); +} + +static void _cp_listen_get_pkt(gnrc_netif_t *netif) +{ + gnrc_gomach_cp_packet_process(netif); + + /* If the device has replied a preamble-ACK, it must waits for the data. + * Here, we extend the CP. */ + if (gnrc_gomach_get_got_preamble(netif)) { + gnrc_gomach_set_got_preamble(netif, false); + gnrc_gomach_set_cp_end(netif, false); + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_CP_END); + gnrc_gomach_set_timeout(netif, GNRC_GOMACH_TIMEOUT_CP_END, GNRC_GOMACH_CP_DURATION_US); + } + else if ((!gnrc_gomach_get_unintd_preamble(netif)) && + (!gnrc_gomach_get_quit_cycle(netif))) { + gnrc_gomach_set_got_preamble(netif, false); + gnrc_gomach_set_cp_end(netif, false); + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_CP_END); + gnrc_gomach_set_timeout(netif, GNRC_GOMACH_TIMEOUT_CP_END, GNRC_GOMACH_CP_DURATION_US); + } +} + +static void _cp_listen_end(gnrc_netif_t *netif) +{ + /* If we found ongoing reception, wait for reception complete. */ + if ((gnrc_gomach_get_netdev_state(netif) == NETOPT_STATE_RX) && + (netif->mac.prot.gomach.cp_extend_count < GNRC_GOMACH_CP_EXTEND_THRESHOLD)) { + netif->mac.prot.gomach.cp_extend_count++; + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_WAIT_RX_END); + gnrc_gomach_set_timeout(netif, GNRC_GOMACH_TIMEOUT_WAIT_RX_END, + GNRC_GOMACH_WAIT_RX_END_US); + } + else { + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_WAIT_RX_END); + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_CP_END); + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_CP_MAX); + netif->mac.rx.listen_state = GNRC_GOMACH_LISTEN_CP_END; + gnrc_gomach_set_update(netif, true); + } +} + +static void gomach_listen_cp_listen(gnrc_netif_t *netif) +{ + if (gnrc_gomach_get_pkt_received(netif)) { + gnrc_gomach_set_pkt_received(netif, false); + _cp_listen_get_pkt(netif); + } + + /* If we have reached the maximum CP duration, quit CP. */ + if (gnrc_gomach_timeout_is_expired(netif, GNRC_GOMACH_TIMEOUT_CP_MAX)) { + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_WAIT_RX_END); + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_CP_END); + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_CP_MAX); + netif->mac.rx.listen_state = GNRC_GOMACH_LISTEN_CP_END; + gnrc_gomach_set_update(netif, true); + return; + } + + if ((gnrc_gomach_timeout_is_expired(netif, GNRC_GOMACH_TIMEOUT_CP_END))) { + gnrc_gomach_set_cp_end(netif, true); + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_CP_END); + } + + /* If CP duration timeouted or we must quit CP, go to CP end. */ + if (gnrc_gomach_get_cp_end(netif) || gnrc_gomach_get_quit_cycle(netif)) { + _cp_listen_end(netif); + } +} + +static void gomach_listen_cp_end(gnrc_netif_t *netif) +{ + gnrc_priority_pktqueue_flush(&netif->mac.rx.queue); + gnrc_mac_dispatch(&netif->mac.rx); + + /* If we need to quit communications in this cycle, go to sleep. */ + if (gnrc_gomach_get_quit_cycle(netif)) { + netif->mac.rx.listen_state = GNRC_GOMACH_LISTEN_SLEEP_INIT; + } + else { + netif->mac.rx.listen_state = GNRC_GOMACH_LISTEN_SEND_BEACON; + } + gnrc_gomach_set_update(netif, true); +} + +static void gomach_listen_send_beacon(gnrc_netif_t *netif) +{ + /* First check if there are slots needed to be allocated. */ + uint8_t slot_num = 0; + + for (uint8_t i = 0; i < GNRC_GOMACH_SLOSCH_UNIT_COUNT; i++) { + if (netif->mac.rx.slosch_list[i].queue_indicator > 0) { + slot_num += netif->mac.rx.slosch_list[i].queue_indicator; + break; + } + } + + if (slot_num > 0) { + /* Disable auto-ACK. Thus not to receive packet (attempt to reply ACK) anymore. */ + gnrc_gomach_set_autoack(netif, NETOPT_DISABLE); + + /* Assemble and send the beacon. */ + int res = gnrc_gomach_send_beacon(netif); + if (res < 0) { + LOG_ERROR("ERROR: [GOMACH] send beacon error: %d.\n", res); + gnrc_gomach_set_beacon_fail(netif, true); + gnrc_gomach_set_update(netif, true); + } + else { + gnrc_gomach_set_update(netif, false); + } + } + else { + /* No need to send beacon, go to next state. */ + gnrc_gomach_set_beacon_fail(netif, true); + gnrc_gomach_set_update(netif, true); + } + + netif->mac.rx.listen_state = GNRC_GOMACH_LISTEN_WAIT_BEACON_TX; +} + +static void _no_vtdma_after_cp(gnrc_netif_t *netif) +{ + /* If the device hasn't allocated transmission slots, check whether it has packets + * to transmit to neighbor. */ + if (gnrc_gomach_find_next_tx_neighbor(netif)) { + /* Now, we have packet to send. */ + + if (netif->mac.tx.current_neighbor == &netif->mac.tx.neighbors[0]) { + /* The packet is for broadcasting. */ + + /* If we didn't find ongoing preamble stream, go to send broadcast packet. */ + if (!gnrc_gomach_get_unintd_preamble(netif)) { + netif->mac.prot.gomach.basic_state = GNRC_GOMACH_TRANSMIT; + netif->mac.tx.transmit_state = GNRC_GOMACH_BROADCAST; + } + else { + /* If we find ongoing preamble stream, go to sleep. */ + netif->mac.rx.listen_state = GNRC_GOMACH_LISTEN_SLEEP_INIT; + } + } + else { + /* The packet waiting to be sent is for unicast. */ + switch (netif->mac.tx.current_neighbor->mac_type) { + case GNRC_GOMACH_TYPE_UNKNOWN: { + /* The neighbor's phase is unknown yet, try to run t2u (transmission + * to unknown device) procedure to phase-lock the neighbor. */ + + /* If we didn't find ongoing preamble stream, go to t2u procedure. */ + if (!gnrc_gomach_get_unintd_preamble(netif)) { + netif->mac.prot.gomach.basic_state = GNRC_GOMACH_TRANSMIT; + netif->mac.tx.transmit_state = GNRC_GOMACH_TRANS_TO_UNKNOWN; + } + else { + netif->mac.rx.listen_state = GNRC_GOMACH_LISTEN_SLEEP_INIT; + } + break; + } + case GNRC_GOMACH_TYPE_KNOWN: { + /* If the neighbor's phase is known, go to t2k (transmission + * to known device) procedure. Here, we don't worry that the t2k + * unicast transmission will interrupt with possible ongoing + * preamble transmissions of other devices. */ + netif->mac.prot.gomach.basic_state = GNRC_GOMACH_TRANSMIT; + netif->mac.tx.transmit_state = GNRC_GOMACH_TRANS_TO_KNOWN; + break; + } + default: { + LOG_ERROR("ERROR: [GOMACH] vTDMA: unknown MAC type of " + "the neighbor.\n"); + break; + } + } + } + } + else { + /* No packet to send, go to sleep. */ + netif->mac.rx.listen_state = GNRC_GOMACH_LISTEN_SLEEP_INIT; + } + + gnrc_gomach_set_update(netif, true); +} + +static void gomach_listen_wait_beacon_tx(gnrc_netif_t *netif) +{ + if ((gnrc_gomach_timeout_is_expired(netif, GNRC_GOMACH_TIMEOUT_NO_TX_ISR))) { + /* No TX-ISR, go to sleep. */ + LOG_DEBUG("[GOMACH]: no TX-finish ISR."); + netif->mac.rx.listen_state = GNRC_GOMACH_LISTEN_SLEEP_INIT; + gnrc_gomach_set_update(netif, true); + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_NO_TX_ISR); + return; + } + + if (gnrc_gomach_get_tx_finish(netif) || + gnrc_gomach_get_beacon_fail(netif)) { + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_NO_TX_ISR); + + if ((netif->mac.rx.vtdma_manag.total_slots_num > 0) && + (!gnrc_gomach_get_beacon_fail(netif))) { + /* If the device has allocated transmission slots to other nodes, + * switch to vTDMA period to receive packets. */ + netif->mac.rx.listen_state = GNRC_GOMACH_LISTEN_VTDMA_INIT; + gnrc_gomach_set_update(netif, true); + } + else { + _no_vtdma_after_cp(netif); + } + } +} + +static void gomach_vtdma_init(gnrc_netif_t *netif) +{ + /* Switch the radio to the device's sub-channel. */ + gnrc_gomach_turn_channel(netif, netif->mac.prot.gomach.sub_channel_seq); + + /* Enable Auto ACK again for data reception */ + gnrc_gomach_set_autoack(netif, NETOPT_ENABLE); + + /* Set the vTDMA period timeout. */ + uint32_t vtdma_duration = netif->mac.rx.vtdma_manag.total_slots_num * + GNRC_GOMACH_VTDMA_SLOT_SIZE_US; + gnrc_gomach_set_timeout(netif, GNRC_GOMACH_TIMEOUT_VTDMA, vtdma_duration); + + gnrc_gomach_set_vTDMA_end(netif, false); + + netif->mac.rx.listen_state = GNRC_GOMACH_LISTEN_VTDMA; + gnrc_gomach_set_update(netif, false); +} + +static void gomach_vtdma(gnrc_netif_t *netif) +{ + /* Process received packet here. */ + if (gnrc_gomach_get_pkt_received(netif)) { + gnrc_gomach_set_pkt_received(netif, false); + gnrc_gomach_packet_process_in_vtdma(netif); + } + + if (gnrc_gomach_timeout_is_expired(netif, GNRC_GOMACH_TIMEOUT_VTDMA)) { + gnrc_gomach_set_vTDMA_end(netif, true); + } + + /* Go to vTDMA end after vTDMA timeout expires. */ + if (gnrc_gomach_get_vTDMA_end(netif)) { + /* Wait for reception complete if found ongoing transmission. */ + if (gnrc_gomach_get_netdev_state(netif) == NETOPT_STATE_RX) { + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_WAIT_RX_END); + gnrc_gomach_set_timeout(netif, GNRC_GOMACH_TIMEOUT_WAIT_RX_END, + GNRC_GOMACH_WAIT_RX_END_US); + return; + } + + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_WAIT_RX_END); + netif->mac.rx.listen_state = GNRC_GOMACH_LISTEN_VTDMA_END; + gnrc_gomach_set_update(netif, true); + } +} + +static void gomach_vtdma_end(gnrc_netif_t *netif) +{ + gnrc_priority_pktqueue_flush(&netif->mac.rx.queue); + gnrc_mac_dispatch(&netif->mac.rx); + + /* Switch the radio to the public-channel. */ + gnrc_gomach_turn_channel(netif, netif->mac.prot.gomach.cur_pub_channel); + + /* Check if there is packet to send. */ + if (gnrc_gomach_find_next_tx_neighbor(netif)) { + if (netif->mac.tx.current_neighbor == &netif->mac.tx.neighbors[0]) { + /* The packet is for broadcasting. */ + if (!gnrc_gomach_get_unintd_preamble(netif)) { + netif->mac.prot.gomach.basic_state = GNRC_GOMACH_TRANSMIT; + netif->mac.tx.transmit_state = GNRC_GOMACH_BROADCAST; + } + else { + netif->mac.rx.listen_state = GNRC_GOMACH_LISTEN_SLEEP_INIT; + } + } + else { + switch (netif->mac.tx.current_neighbor->mac_type) { + /* The packet waiting to be sent is for unicast. */ + case GNRC_GOMACH_TYPE_UNKNOWN: { + /* The neighbor's phase is unknown yet, try to run t2u (transmission + * to unknown device) procedure to phase-lock the neighbor. */ + if (!gnrc_gomach_get_unintd_preamble(netif)) { + netif->mac.prot.gomach.basic_state = GNRC_GOMACH_TRANSMIT; + netif->mac.tx.transmit_state = GNRC_GOMACH_TRANS_TO_UNKNOWN; + } + else { + netif->mac.rx.listen_state = GNRC_GOMACH_LISTEN_SLEEP_INIT; + } + } break; + case GNRC_GOMACH_TYPE_KNOWN: { + /* If the neighbor's phase is known, go to t2k (transmission + * to known device) procedure. Here, we don't worry that the t2k + * unicast transmission will interrupt with possible ongoing + * preamble transmissions of other devices. */ + netif->mac.prot.gomach.basic_state = GNRC_GOMACH_TRANSMIT; + netif->mac.tx.transmit_state = GNRC_GOMACH_TRANS_TO_KNOWN; + } break; + default: { + LOG_ERROR("ERROR: [GOMACH] vTDMA: unknown MAC type of the neighbor.\n"); + break; + } + } + } + } + else { + /* No packet to send, go to sleep. */ + netif->mac.rx.listen_state = GNRC_GOMACH_LISTEN_SLEEP_INIT; + } + + gnrc_gomach_set_update(netif, true); +} + +static void gomach_sleep_init(gnrc_netif_t *netif) +{ + /* Turn off the radio during sleep period to conserve power. */ + gnrc_gomach_set_netdev_state(netif, NETOPT_STATE_SLEEP); + netif->mac.rx.listen_state = GNRC_GOMACH_LISTEN_SLEEP; + gnrc_gomach_set_update(netif, true); +} + +static void gomach_sleep(gnrc_netif_t *netif) +{ + /* If we are entering a new cycle, quit sleeping. */ + if (gnrc_gomach_get_enter_new_cycle(netif)) { + netif->mac.rx.listen_state = GNRC_GOMACH_LISTEN_SLEEP_END; + gnrc_gomach_set_update(netif, true); + } +} + +static void gomach_sleep_end(gnrc_netif_t *netif) +{ + /* Run phase-backoff if needed, select a new wake-up phase. */ + if (gnrc_gomach_get_phase_backoff(netif)) { + gnrc_gomach_set_phase_backoff(netif, false); + _gomach_phase_backoff(netif); + } + + /* Go to CP (start of the new cycle), start listening on the public-channel. */ + netif->mac.rx.listen_state = GNRC_GOMACH_LISTEN_CP_INIT; + gnrc_gomach_set_update(netif, true); +} + +static void gomach_update(gnrc_netif_t *netif) +{ + switch (netif->mac.prot.gomach.basic_state) { + case GNRC_GOMACH_INIT: { + /* State machine of GoMacH's initialization procedure. */ + switch (netif->mac.prot.gomach.init_state) { + case GNRC_GOMACH_INIT_PREPARE: { + gomach_init_prepare(netif); + break; + } + case GNRC_GOMACH_INIT_ANNC_SUBCHAN: { + gomach_init_announce_subchannel(netif); + break; + } + case GNRC_GOMACH_INIT_WAIT_FEEDBACK: { + gomach_init_wait_announce_feedback(netif); + break; + } + case GNRC_GOMACH_INIT_END: { + gomach_init_end(netif); + break; + } + default: break; + } + break; + } + case GNRC_GOMACH_LISTEN: { + /* State machine of GoMacH's duty-cycled listen procedure. */ + switch (netif->mac.rx.listen_state) { + case GNRC_GOMACH_LISTEN_CP_INIT: { + gomach_listen_init(netif); + break; + } + case GNRC_GOMACH_LISTEN_CP_LISTEN: { + gomach_listen_cp_listen(netif); + break; + } + case GNRC_GOMACH_LISTEN_CP_END: { + gomach_listen_cp_end(netif); + break; + } + case GNRC_GOMACH_LISTEN_SEND_BEACON: { + gomach_listen_send_beacon(netif); + break; + } + case GNRC_GOMACH_LISTEN_WAIT_BEACON_TX: { + gomach_listen_wait_beacon_tx(netif); + break; + } + case GNRC_GOMACH_LISTEN_VTDMA_INIT: { + gomach_vtdma_init(netif); + break; + } + case GNRC_GOMACH_LISTEN_VTDMA: { + gomach_vtdma(netif); + break; + } + case GNRC_GOMACH_LISTEN_VTDMA_END: { + gomach_vtdma_end(netif); + break; + } + case GNRC_GOMACH_LISTEN_SLEEP_INIT: { + gomach_sleep_init(netif); + break; + } + case GNRC_GOMACH_LISTEN_SLEEP: { + gomach_sleep(netif); + break; + } + case GNRC_GOMACH_LISTEN_SLEEP_END: { + gomach_sleep_end(netif); + break; + } + default: break; + } + break; + } + case GNRC_GOMACH_TRANSMIT: { + /* State machine of GoMacH's basic transmission scheme. */ + switch (netif->mac.tx.transmit_state) { + case GNRC_GOMACH_TRANS_TO_UNKNOWN: { + gomach_t2u_update(netif); + break; + } + case GNRC_GOMACH_TRANS_TO_KNOWN: { + gomach_t2k_update(netif); + break; + } + case GNRC_GOMACH_BROADCAST: { + gomach_bcast_update(netif); + break; + } + default: break; + } + break; + } + default: break; + } +} + +static int _send(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt) +{ + if (!gnrc_mac_queue_tx_packet(&netif->mac.tx, 0, pkt)) { + /* TX packet queue full, release the packet. */ + DEBUG("[GOMACH] TX queue full, drop packet.\n"); + gnrc_pktbuf_release(pkt); + } + gnrc_gomach_set_update(netif, true); + + while (gnrc_gomach_get_update(netif)) { + gnrc_gomach_set_update(netif, false); + gomach_update(netif); + } + + return 0; +} + +static void _gomach_msg_handler(gnrc_netif_t *netif, msg_t *msg) +{ + switch (msg->type) { + case GNRC_GOMACH_EVENT_RTT_TYPE: { + _gomach_rtt_handler(msg->content.value, netif); + break; + } + case GNRC_GOMACH_EVENT_TIMEOUT_TYPE: { + /* GoMacH timeout expires. */ + gnrc_gomach_timeout_make_expire((gnrc_gomach_timeout_t *) msg->content.ptr); + gnrc_gomach_set_update(netif, true); + break; + } + default: { + DEBUG("[GoMacH]: Unknown command %" PRIu16 "\n", msg->type); + break; + } + } + + while (gnrc_gomach_get_update(netif)) { + gnrc_gomach_set_update(netif, false); + gomach_update(netif); + } +} + +/** + * @brief Function called by the device driver on device events + * + * @param[in] event type of event + */ +static void _gomach_event_cb(netdev_t *dev, netdev_event_t event) +{ + gnrc_netif_t *netif = (gnrc_netif_t *) dev->context; + + if (event == NETDEV_EVENT_ISR) { + msg_t msg; + + msg.type = NETDEV_MSG_TYPE_EVENT; + msg.content.ptr = (void *) netif; + + if (msg_send(&msg, netif->pid) <= 0) { + DEBUG("[GOMACH] gnrc_netdev: possibly lost interrupt.\n"); + } + } + else { + DEBUG("gnrc_netdev: event triggered -> %i\n", event); + switch (event) { + case NETDEV_EVENT_RX_STARTED: { + gnrc_netif_set_rx_started(netif, true); + gnrc_gomach_set_update(netif, true); + break; + } + case NETDEV_EVENT_RX_COMPLETE: { + gnrc_gomach_set_update(netif, true); + + gnrc_pktsnip_t *pkt = netif->ops->recv(netif); + if (pkt == NULL) { + gnrc_gomach_set_buffer_full(netif, true); + + LOG_DEBUG("[GOMACH] gnrc_netdev: packet is NULL, memory full?\n"); + gnrc_gomach_set_pkt_received(netif, false); + gnrc_netif_set_rx_started(netif, false); + break; + } + + if (!gnrc_netif_get_rx_started(netif)) { + LOG_DEBUG("[GOMACH] gnrc_netdev: maybe sending kicked in " + "and frame buffer is now corrupted?\n"); + gnrc_pktbuf_release(pkt); + gnrc_netif_set_rx_started(netif, false); + break; + } + + gnrc_netif_set_rx_started(netif, false); + + if (!gnrc_mac_queue_rx_packet(&netif->mac.rx, 0, pkt)) { + LOG_ERROR("ERROR: [GOMACH] gnrc_netdev: can't push RX packet, queue full?\n"); + gnrc_pktbuf_release(pkt); + gnrc_gomach_set_pkt_received(netif, false); + break; + } + else { + gnrc_gomach_set_pkt_received(netif, true); + } + break; + } + case NETDEV_EVENT_TX_COMPLETE: { + gnrc_netif_set_tx_feedback(netif, TX_FEEDBACK_SUCCESS); + gnrc_gomach_set_tx_finish(netif, true); + gnrc_gomach_set_netdev_state(netif, NETOPT_STATE_IDLE); + gnrc_gomach_set_update(netif, true); + break; + } + case NETDEV_EVENT_TX_NOACK: { + gnrc_netif_set_tx_feedback(netif, TX_FEEDBACK_NOACK); + gnrc_gomach_set_tx_finish(netif, true); + gnrc_gomach_set_netdev_state(netif, NETOPT_STATE_IDLE); + gnrc_gomach_set_update(netif, true); + break; + } + case NETDEV_EVENT_TX_MEDIUM_BUSY: { + gnrc_netif_set_tx_feedback(netif, TX_FEEDBACK_BUSY); + gnrc_gomach_set_tx_finish(netif, true); + gnrc_gomach_set_netdev_state(netif, NETOPT_STATE_IDLE); + gnrc_gomach_set_update(netif, true); + break; + } + default: { + DEBUG("WARNING [GoMacH]: unhandled event %u.\n", event); + } + } + } + + while (gnrc_gomach_get_update(netif)) { + gnrc_gomach_set_update(netif, false); + gomach_update(netif); + } +} + +static void _gomach_init(gnrc_netif_t *netif) +{ + netdev_t *dev; + + dev = netif->dev; + dev->event_callback = _gomach_event_cb; + + /* Initialize RTT. */ + rtt_init(); + + /* Store pid globally, so that IRQ can use it to send message. */ + gomach_pid = netif->pid; + + /* Set MAC address length. */ + uint16_t src_len = IEEE802154_LONG_ADDRESS_LEN; + dev->driver->set(dev, NETOPT_SRC_LEN, &src_len, sizeof(src_len)); + + /* Get the MAC address of the device. */ + netif->l2addr_len = dev->driver->get(dev, + NETOPT_ADDRESS_LONG, + netif->l2addr, + sizeof(netif->l2addr)); + + /* Initialize GoMacH's state machines. */ + netif->mac.prot.gomach.basic_state = GNRC_GOMACH_INIT; + netif->mac.prot.gomach.init_state = GNRC_GOMACH_INIT_PREPARE; + netif->mac.rx.listen_state = GNRC_GOMACH_LISTEN_CP_INIT; + netif->mac.tx.transmit_state = GNRC_GOMACH_TRANS_TO_UNKNOWN; + netif->mac.tx.bcast_state = GNRC_GOMACH_BCAST_INIT; + netif->mac.tx.t2k_state = GNRC_GOMACH_T2K_INIT; + netif->mac.tx.t2u_state = GNRC_GOMACH_T2U_INIT; + + /* Initialize GoMacH's channels. */ + netif->mac.prot.gomach.sub_channel_seq = 13; + netif->mac.prot.gomach.pub_channel_1 = 26; + netif->mac.prot.gomach.pub_channel_2 = 11; + netif->mac.prot.gomach.cur_pub_channel = netif->mac.prot.gomach.pub_channel_1; + gnrc_gomach_turn_channel(netif, netif->mac.prot.gomach.cur_pub_channel); + + /* Enable RX-start and TX-started and TX-END interrupts. */ + netopt_enable_t enable = NETOPT_ENABLE; + dev->driver->set(dev, NETOPT_RX_START_IRQ, &enable, sizeof(enable)); + dev->driver->set(dev, NETOPT_TX_END_IRQ, &enable, sizeof(enable)); + + /* Initialize broadcast sequence number. This at least differs from board + * to board. */ + netif->mac.tx.broadcast_seq = netif->l2addr[netif->l2addr_len - 1]; + + /* Reset all timeouts just to be sure. */ + gnrc_gomach_reset_timeouts(netif); + + /* Initialize GoMacH's other key parameters. */ + netif->mac.tx.no_ack_counter = 0; + gnrc_gomach_set_enter_new_cycle(netif, false); + netif->mac.rx.vtdma_manag.sub_channel_seq = 26; + netif->mac.prot.gomach.subchannel_occu_flags = 0; + gnrc_gomach_set_pkt_received(netif, false); + gnrc_gomach_set_update(netif, false); + gnrc_gomach_set_duty_cycle_start(netif, false); + gnrc_gomach_set_quit_cycle(netif, false); + + gnrc_gomach_set_beacon_fail(netif, false); + gnrc_gomach_set_buffer_full(netif, false); + gnrc_gomach_set_phase_backoff(netif, false); + netif->mac.rx.check_dup_pkt.queue_head = 0; + netif->mac.tx.last_tx_neighbor_id = 0; + + netdev_ieee802154_t *device_state = (netdev_ieee802154_t *)netif->dev; + device_state->seq = netif->l2addr[netif->l2addr_len - 1]; + + /* Initialize GoMacH's duplicate-check scheme. */ + for (uint8_t i = 0; i < GNRC_GOMACH_DUPCHK_BUFFER_SIZE; i++) { + netif->mac.rx.check_dup_pkt.last_nodes[i].node_addr.len = 0; + } + + /* Set the random seed. */ + uint32_t seed = 0; + seed = netif->l2addr[netif->l2addr_len - 2]; + seed = seed << 8; + seed |= netif->l2addr[netif->l2addr_len - 1]; + random_init(seed); + + netif->mac.tx.t2u_fail_count = 0; + +#if (GNRC_GOMACH_ENABLE_DUTYCYLE_RECORD == 1) + /* Start duty cycle recording */ + netif->mac.prot.gomach.system_start_time_ticks = xtimer_now_usec64(); + netif->mac.prot.gomach.last_radio_on_time_ticks = + netif->mac.prot.gomach.system_start_time_ticks; + netif->mac.prot.gomach.awake_duration_sum_ticks = 0; + netif->mac.prot.gomach.gomach_info |= GNRC_GOMACH_INTERNAL_INFO_RADIO_IS_ON; +#endif + + gnrc_gomach_set_update(netif, true); + + while (gnrc_gomach_get_update(netif)) { + gnrc_gomach_set_update(netif, false); + gomach_update(netif); + } +} diff --git a/sys/net/gnrc/link_layer/gomach/gomach_internal.c b/sys/net/gnrc/link_layer/gomach/gomach_internal.c new file mode 100644 index 0000000000000000000000000000000000000000..afe2404b1707a195e4c8c76be1d838245d45026f --- /dev/null +++ b/sys/net/gnrc/link_layer/gomach/gomach_internal.c @@ -0,0 +1,1430 @@ +/* + * Copyright (C) 2017 INRIA + * + * 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 net_gnrc_gomach + * @{ + * + * @file + * @brief Implementation of GoMacH's internal functions. + * + * @author Shuguo Zhuo <shuguo.zhuo@inria.fr> + * @} + */ + +#include <stdbool.h> + +#include "periph/rtt.h" +#include "random.h" +#include "net/gnrc.h" +#include "net/gnrc/mac/types.h" +#include "net/gnrc/mac/mac.h" +#include "net/gnrc/mac/internal.h" +#include "net/gnrc/gomach/hdr.h" +#include "net/gnrc/gomach/gomach.h" +#include "net/gnrc/gomach/timeout.h" +#include "net/gnrc/gomach/types.h" +#include "include/gomach_internal.h" +#include "net/gnrc/netif/ieee802154.h" +#include "net/netdev/ieee802154.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#ifndef LOG_LEVEL +/** + * @brief Default log level define + */ +#define LOG_LEVEL LOG_WARNING +#endif + +#include "log.h" + +int _gnrc_gomach_transmit(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt) +{ + netdev_t *dev = netif->dev; + netdev_ieee802154_t *state = (netdev_ieee802154_t *)netif->dev; + gnrc_netif_hdr_t *netif_hdr; + gnrc_pktsnip_t *vec_snip; + const uint8_t *src, *dst = NULL; + int res = 0; + size_t n, src_len, dst_len; + uint8_t mhr[IEEE802154_MAX_HDR_LEN]; + uint8_t flags = (uint8_t)(state->flags & NETDEV_IEEE802154_SEND_MASK); + le_uint16_t dev_pan = byteorder_btols(byteorder_htons(state->pan)); + + flags |= IEEE802154_FCF_TYPE_DATA; + if (pkt == NULL) { + DEBUG("_send_ieee802154: pkt was NULL\n"); + return -EINVAL; + } + if (pkt->type != GNRC_NETTYPE_NETIF) { + DEBUG("_send_ieee802154: first header is not generic netif header\n"); + return -EBADMSG; + } + netif_hdr = pkt->data; + /* prepare destination address */ + if (netif_hdr->flags & /* If any of these flags is set assume broadcast */ + (GNRC_NETIF_HDR_FLAGS_BROADCAST | GNRC_NETIF_HDR_FLAGS_MULTICAST)) { + dst = ieee802154_addr_bcast; + dst_len = IEEE802154_ADDR_BCAST_LEN; + } + else { + dst = gnrc_netif_hdr_get_dst_addr(netif_hdr); + dst_len = netif_hdr->dst_l2addr_len; + } + src_len = netif_hdr->src_l2addr_len; + if (src_len > 0) { + src = gnrc_netif_hdr_get_src_addr(netif_hdr); + } + else { + src_len = netif->l2addr_len; + src = netif->l2addr; + } + /* fill MAC header, seq should be set by device */ + if ((res = ieee802154_set_frame_hdr(mhr, src, src_len, + dst, dst_len, dev_pan, + dev_pan, flags, state->seq++)) == 0) { + DEBUG("_send_ieee802154: Error preperaring frame\n"); + return -EINVAL; + } + /* prepare packet for sending */ + vec_snip = gnrc_pktbuf_get_iovec(pkt, &n); + if (vec_snip != NULL) { + struct iovec *vector; + + pkt = vec_snip; /* reassign for later release; vec_snip is prepended to pkt */ + vector = (struct iovec *)pkt->data; + vector[0].iov_base = mhr; + vector[0].iov_len = (size_t)res; +#ifdef MODULE_NETSTATS_L2 + if (netif_hdr->flags & + (GNRC_NETIF_HDR_FLAGS_BROADCAST | GNRC_NETIF_HDR_FLAGS_MULTICAST)) { + netif->dev->stats.tx_mcast_count++; + } + else { + netif->dev->stats.tx_unicast_count++; + } +#endif +#ifdef MODULE_GNRC_MAC + if (netif->mac.mac_info & GNRC_NETIF_MAC_INFO_CSMA_ENABLED) { + res = csma_sender_csma_ca_send(dev, vector, n, &netif->mac.csma_conf); + } + else { + res = dev->driver->send(dev, vector, n); + } +#else + res = dev->driver->send(dev, vector, n); +#endif + } + else { + return -ENOBUFS; + } + /* release old data */ + gnrc_pktbuf_release(pkt); + return res; +} + +static gnrc_pktsnip_t *_make_netif_hdr(uint8_t *mhr) +{ + gnrc_pktsnip_t *snip; + uint8_t src[IEEE802154_LONG_ADDRESS_LEN], dst[IEEE802154_LONG_ADDRESS_LEN]; + int src_len, dst_len; + le_uint16_t _pan_tmp; /* TODO: hand-up PAN IDs to GNRC? */ + + dst_len = ieee802154_get_dst(mhr, dst, &_pan_tmp); + src_len = ieee802154_get_src(mhr, src, &_pan_tmp); + if ((dst_len < 0) || (src_len < 0)) { + DEBUG("_make_netif_hdr: unable to get addresses\n"); + return NULL; + } + /* allocate space for header */ + snip = gnrc_netif_hdr_build(src, (size_t)src_len, dst, (size_t)dst_len); + if (snip == NULL) { + DEBUG("_make_netif_hdr: no space left in packet buffer\n"); + return NULL; + } + /* set broadcast flag for broadcast destination */ + if ((dst_len == 2) && (dst[0] == 0xff) && (dst[1] == 0xff)) { + gnrc_netif_hdr_t *hdr = snip->data; + hdr->flags |= GNRC_NETIF_HDR_FLAGS_BROADCAST; + } + return snip; +} + +static int _parse_packet(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt, + gnrc_gomach_packet_info_t *info) +{ + assert(info != NULL); + assert(pkt != NULL); + + netdev_ieee802154_t *state = (netdev_ieee802154_t *)netif->dev; + /* Get the packet sequence number */ + info->seq = ieee802154_get_seq(pkt->next->data); + + gnrc_pktsnip_t *netif_snip = _make_netif_hdr(pkt->next->data); + if (netif_snip == NULL) { + DEBUG("gomach: no space left in packet buffer\n"); + gnrc_pktbuf_release(pkt); + return -ENODATA; + } + + gnrc_netif_hdr_t *netif_hdr = netif_snip->data; + netif_hdr->lqi = netif->mac.prot.gomach.rx_pkt_lqi; + netif_hdr->rssi = netif->mac.prot.gomach.rx_pkt_rssi; + netif_hdr->if_pid = netif->pid; + pkt->type = state->proto; + gnrc_pktbuf_remove_snip(pkt, pkt->next); + LL_APPEND(pkt, netif_snip); + + gnrc_pktsnip_t *gomach_snip = NULL; + gnrc_gomach_hdr_t *gomach_hdr = NULL; + + netif_snip = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_NETIF); + if (netif_snip == NULL) { + return -ENODATA; + } + else { + netif_hdr = netif_snip->data; + } + + if (netif_hdr->dst_l2addr_len > sizeof(info->dst_addr)) { + return -ENODATA; + } + + if (netif_hdr->src_l2addr_len > sizeof(info->src_addr)) { + return -ENODATA; + } + + /* Dissect GoMacH header, Every frame has header as first member */ + gomach_hdr = (gnrc_gomach_hdr_t *) pkt->data; + + switch (gomach_hdr->type) { + case GNRC_GOMACH_FRAME_BEACON: { + gomach_snip = gnrc_pktbuf_mark(pkt, sizeof(gnrc_gomach_frame_beacon_t), + GNRC_NETTYPE_GOMACH); + break; + } + case GNRC_GOMACH_FRAME_PREAMBLE: { + gomach_snip = gnrc_pktbuf_mark(pkt, sizeof(gnrc_gomach_frame_preamble_t), + GNRC_NETTYPE_GOMACH); + break; + } + case GNRC_GOMACH_FRAME_PREAMBLE_ACK: { + gomach_snip = gnrc_pktbuf_mark(pkt, sizeof(gnrc_gomach_frame_preamble_ack_t), + GNRC_NETTYPE_GOMACH); + break; + } + case GNRC_GOMACH_FRAME_DATA: { + gomach_snip = gnrc_pktbuf_mark(pkt, sizeof(gnrc_gomach_frame_data_t), + GNRC_NETTYPE_GOMACH); + break; + } + case GNRC_GOMACH_FRAME_ANNOUNCE: { + gomach_snip = gnrc_pktbuf_mark(pkt, sizeof(gnrc_gomach_frame_announce_t), + GNRC_NETTYPE_GOMACH); + break; + } + case GNRC_GOMACH_FRAME_BROADCAST: { + gomach_snip = gnrc_pktbuf_mark(pkt, sizeof(gnrc_gomach_frame_broadcast_t), + GNRC_NETTYPE_GOMACH); + break; + } + + default: { + return -ENODATA; + } + } + + /* Memory location may have changed while marking. */ + gomach_hdr = gomach_snip->data; + + /* Get the destination address. */ + switch (gomach_hdr->type) { + case GNRC_GOMACH_FRAME_PREAMBLE: { + info->dst_addr = ((gnrc_gomach_frame_preamble_t *)gomach_hdr)->dst_addr; + break; + } + case GNRC_GOMACH_FRAME_PREAMBLE_ACK: { + info->dst_addr = ((gnrc_gomach_frame_preamble_ack_t *)gomach_hdr)->dst_addr; + break; + } + case GNRC_GOMACH_FRAME_DATA: { + if (netif_hdr->dst_l2addr_len) { + info->dst_addr.len = netif_hdr->dst_l2addr_len; + memcpy(info->dst_addr.addr, + gnrc_netif_hdr_get_dst_addr(netif_hdr), + netif_hdr->dst_l2addr_len); + } + break; + } + default: { + break; + } + } + + /* Get the source address. */ + if (netif_hdr->src_l2addr_len) { + info->src_addr.len = netif_hdr->src_l2addr_len; + memcpy(info->src_addr.addr, + gnrc_netif_hdr_get_src_addr(netif_hdr), + netif_hdr->src_l2addr_len); + } + + info->header = gomach_hdr; + return 0; +} + +uint64_t gnrc_gomach_phase_now(gnrc_netif_t *netif) +{ + assert(netif != NULL); + + uint64_t phase_now = xtimer_now_usec64(); + + /* in case timer overflows */ + if (phase_now < netif->mac.prot.gomach.last_wakeup_phase_us) { + uint64_t gap_to_full = GNRC_GOMACH_PHASE_MAX - + netif->mac.prot.gomach.last_wakeup_phase_us; + phase_now += gap_to_full; + } + else { + phase_now = phase_now - netif->mac.prot.gomach.last_wakeup_phase_us; + } + + return phase_now; +} + +void gnrc_gomach_set_netdev_state(gnrc_netif_t *netif, netopt_state_t devstate) +{ + assert(netif != NULL); + + netif->dev->driver->set(netif->dev, + NETOPT_STATE, + &devstate, + sizeof(devstate)); + +#if (GNRC_GOMACH_ENABLE_DUTYCYLE_RECORD == 1) + if (devstate == NETOPT_STATE_IDLE) { + if (!(netif->mac.prot.gomach.gomach_info & GNRC_GOMACH_INTERNAL_INFO_RADIO_IS_ON)) { + netif->mac.prot.gomach.last_radio_on_time_ticks = xtimer_now_usec64(); + netif->mac.prot.gomach.gomach_info |= GNRC_GOMACH_INTERNAL_INFO_RADIO_IS_ON; + } + return; + } + else if ((devstate == NETOPT_STATE_SLEEP) && + (netif->mac.prot.gomach.gomach_info & GNRC_GOMACH_INTERNAL_INFO_RADIO_IS_ON)) { + netif->mac.prot.gomach.radio_off_time_ticks = xtimer_now_usec64(); + + netif->mac.prot.gomach.awake_duration_sum_ticks += + (netif->mac.prot.gomach.radio_off_time_ticks - + netif->mac.prot.gomach.last_radio_on_time_ticks); + + netif->mac.prot.gomach.gomach_info &= ~GNRC_GOMACH_INTERNAL_INFO_RADIO_IS_ON; + } +#endif +} + +int gnrc_gomach_send(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt, netopt_enable_t csma_enable) +{ + assert(netif != NULL); + assert(pkt != NULL); + + /* Enable/disable CSMA according to the input. */ + netif->dev->driver->set(netif->dev, NETOPT_CSMA, &csma_enable, + sizeof(netopt_enable_t)); + + gnrc_gomach_set_tx_finish(netif, false); + gnrc_netif_set_tx_feedback(netif, TX_FEEDBACK_UNDEF); + return _gnrc_gomach_transmit(netif, pkt); +} + +int gnrc_gomach_send_preamble_ack(gnrc_netif_t *netif, gnrc_gomach_packet_info_t *info) +{ + assert(netif != NULL); + assert(info != NULL); + + gnrc_pktsnip_t *gomach_pkt = NULL; + gnrc_pktsnip_t *pkt = NULL; + gnrc_netif_hdr_t *nethdr_preamble_ack = NULL; + + /* Start assemble the preamble-ACK packet according to preamble packet info. */ + gnrc_gomach_frame_preamble_ack_t gomach_preamble_ack_hdr; + + gomach_preamble_ack_hdr.header.type = GNRC_GOMACH_FRAME_PREAMBLE_ACK; + gomach_preamble_ack_hdr.dst_addr = info->src_addr; + /* Tell the preamble sender the device's (preamble-ACK sender) current phase. + * This is to allow the preamble sender to deduce the exact phase of the receiver. */ + gomach_preamble_ack_hdr.phase_in_us = gnrc_gomach_phase_now(netif); + + pkt = gnrc_pktbuf_add(NULL, &gomach_preamble_ack_hdr, sizeof(gomach_preamble_ack_hdr), + GNRC_NETTYPE_GOMACH); + if (pkt == NULL) { + LOG_ERROR("ERROR: [GOMACH]: pktbuf add failed in gnrc_gomach_send_preamble_ack().\n"); + return -ENOBUFS; + } + gomach_pkt = pkt; + + pkt = gnrc_pktbuf_add(pkt, NULL, sizeof(gnrc_netif_hdr_t), GNRC_NETTYPE_NETIF); + if (pkt == NULL) { + LOG_ERROR("ERROR: [GOMACH]: netif_hdr add failed in gnrc_gomach_send_preamble_ack().\n"); + gnrc_pktbuf_release(gomach_pkt); + return -ENOBUFS; + } + gomach_pkt = pkt; + + gnrc_pktsnip_t *netif_snip = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_NETIF); + if (netif_snip == NULL) { + LOG_ERROR("[GOMACH]: NO netif_hdr found in gnrc_gomach_send_preamble_ack().\n"); + gnrc_pktbuf_release(gomach_pkt); + return -ENOBUFS; + } + else { + nethdr_preamble_ack = netif_snip->data; + } + + /* Construct NETIF header and insert address for preamble-ACK packet. */ + gnrc_netif_hdr_init(nethdr_preamble_ack, 0, 0); + + /* Send preamble-ACK as broadcast. */ + nethdr_preamble_ack->flags |= GNRC_NETIF_HDR_FLAGS_BROADCAST; + + int res = gnrc_gomach_send(netif, pkt, NETOPT_DISABLE); + if (res < 0) { + LOG_ERROR("ERROR: [GOMACH]: send preamble-ack failed in" + " gnrc_gomach_send_preamble_ack().\n"); + gnrc_pktbuf_release(gomach_pkt); + } + return res; +} + +static bool _assemble_beacon(gnrc_netif_t *netif, uint8_t total_tdma_slot_num, + uint8_t total_tdma_node_num, uint8_t *slots_list, + gnrc_gomach_l2_id_t *id_list, gnrc_pktsnip_t **pkt, + gnrc_pktsnip_t **gomach_pkt, + gnrc_gomach_frame_beacon_t *gomach_beaocn_hdr) +{ + /* If there are slots to allocate, add the slots list and the ID list to + * the beacon! */ + netif->mac.rx.vtdma_manag.total_slots_num = total_tdma_slot_num; + + /* Add the slots list to the beacon. */ + *pkt = gnrc_pktbuf_add(NULL, slots_list, total_tdma_node_num * sizeof(uint8_t), + GNRC_NETTYPE_GOMACH); + if (*pkt == NULL) { + LOG_ERROR("ERROR: [GOMACH]: pktbuf add failed in gnrc_gomach_send_beacon().\n"); + return false; + } + *gomach_pkt = *pkt; + + /* Add the ID list to the beacon. */ + *pkt = gnrc_pktbuf_add(*pkt, id_list, total_tdma_node_num * sizeof(gnrc_gomach_l2_id_t), + GNRC_NETTYPE_GOMACH); + if (*pkt == NULL) { + LOG_ERROR("ERROR: [GOMACH]: pktbuf add failed in gnrc_gomach_send_beacon().\n"); + gnrc_pktbuf_release(*gomach_pkt); + return false; + } + *gomach_pkt = *pkt; + + /* Add the GoMacH header to the beacon. */ + *pkt = gnrc_pktbuf_add(*pkt, gomach_beaocn_hdr, sizeof(gnrc_gomach_frame_beacon_t), + GNRC_NETTYPE_GOMACH); + if (*pkt == NULL) { + LOG_ERROR("ERROR: [GOMACH]: pktbuf add failed in gnrc_gomach_send_beacon().\n"); + gnrc_pktbuf_release(*gomach_pkt); + return false; + } + + return true; +} + +int gnrc_gomach_send_beacon(gnrc_netif_t *netif) +{ + assert(netif != NULL); + + uint8_t i; + uint8_t j = 0; + uint8_t total_tdma_node_num = 0; + uint8_t total_tdma_slot_num = 0; + gnrc_pktsnip_t *pkt = NULL; + gnrc_pktsnip_t *gomach_pkt = NULL; + gnrc_netif_hdr_t *nethdr_beacon = NULL; + + /* Start assemble the beacon packet */ + gnrc_gomach_frame_beacon_t gomach_beaocn_hdr; + gomach_beaocn_hdr.header.type = GNRC_GOMACH_FRAME_BEACON; + gomach_beaocn_hdr.sub_channel_seq = netif->mac.prot.gomach.sub_channel_seq; + + /* Start generating the slots list and the related ID list for guiding + * the following vTMDA procedure (slotted transmission). */ + netif->mac.rx.vtdma_manag.total_slots_num = 0; + + gnrc_gomach_l2_id_t id_list[GNRC_GOMACH_SLOSCH_UNIT_COUNT]; + uint8_t slots_list[GNRC_GOMACH_SLOSCH_UNIT_COUNT]; + + for (i = 0; i < GNRC_GOMACH_SLOSCH_UNIT_COUNT; i++) { + if (netif->mac.rx.slosch_list[i].queue_indicator > 0) { + /* Record the device's (that will be allocated slots) address to the ID list. */ + memcpy(id_list[j].addr, + netif->mac.rx.slosch_list[i].node_addr.addr, + netif->mac.rx.slosch_list[i].node_addr.len); + + /* Record the number of allocated slots to the slots list. */ + slots_list[j] = netif->mac.rx.slosch_list[i].queue_indicator; + + total_tdma_node_num++; + total_tdma_slot_num += slots_list[j]; + + /* If there is no room for allocating more slots, stop. */ + if (total_tdma_slot_num >= GNRC_GOMACH_MAX_ALLOC_SLOTS_NUM) { + uint8_t redueced_slots_num; + redueced_slots_num = total_tdma_slot_num - GNRC_GOMACH_MAX_ALLOC_SLOTS_NUM; + slots_list[j] -= redueced_slots_num; + total_tdma_slot_num -= redueced_slots_num; + break; + } + j++; + } + } + + gomach_beaocn_hdr.schedulelist_size = total_tdma_node_num; + + if (total_tdma_node_num > 0) { + if (!_assemble_beacon(netif, total_tdma_slot_num, total_tdma_node_num, + slots_list, id_list, &pkt, &gomach_pkt, &gomach_beaocn_hdr)) { + return -ENOBUFS; + } + gomach_pkt = pkt; + } + else { + /* If there is no slots to allocate, quit sending beacon! */ + return -ENOBUFS; + } + + /* Add the Netif header. */ + pkt = gnrc_pktbuf_add(pkt, NULL, sizeof(gnrc_netif_hdr_t), GNRC_NETTYPE_NETIF); + if (pkt == NULL) { + LOG_ERROR("ERROR: [GOMACH]: pktbuf add failed in gnrc_gomach_send_beacon().\n"); + gnrc_pktbuf_release(gomach_pkt); + return -ENOBUFS; + } + gomach_pkt = pkt; + + gnrc_pktsnip_t *beacon_netif_snip = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_NETIF); + if (beacon_netif_snip == NULL) { + LOG_ERROR("[GOMACH]: NO netif_hdr found in send_beacon().\n"); + gnrc_pktbuf_release(pkt); + return -ENOBUFS; + } + else { + nethdr_beacon = beacon_netif_snip->data; + } + + /* Construct NETIF header. */ + gnrc_netif_hdr_init(nethdr_beacon, 0, 0); + + /* Send beacon as broadcast*/ + nethdr_beacon->flags |= GNRC_NETIF_HDR_FLAGS_BROADCAST; + + int res; + if (gnrc_gomach_get_unintd_preamble(netif)) { + /* Use csma for collision avoidance if we found ongoing preamble transmission. */ + res = gnrc_gomach_send(netif, pkt, NETOPT_ENABLE); + } + else { + /* Send the beacon without CSMA if there is no ongoing preamble transmission. */ + res = gnrc_gomach_send(netif, pkt, NETOPT_DISABLE); + } + + if (res < 0) { + LOG_ERROR("ERROR: [GOMACH]: send beacon failed, release it.\n"); + gnrc_pktbuf_release(pkt); + } + else { + gnrc_gomach_set_timeout(netif, GNRC_GOMACH_TIMEOUT_NO_TX_ISR, + GNRC_GOMACH_NO_TX_ISR_US); + } + return res; +} + +int gnrc_gomach_dispatch_defer(gnrc_pktsnip_t *buffer[], gnrc_pktsnip_t *pkt) +{ + assert(buffer != NULL); + assert(pkt != NULL); + + for (unsigned i = 0; i < GNRC_MAC_DISPATCH_BUFFER_SIZE; i++) { + /* Buffer will be filled bottom-up and emptied completely so no holes */ + if (buffer[i] == NULL) { + buffer[i] = pkt; + return 0; + } + } + + gnrc_pktbuf_release(pkt); + LOG_ERROR("ERROR: [GOMACH]: dispatch buffer full, drop pkt.\n"); + + return -ENOBUFS; +} + +void gnrc_gomach_indicator_update(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt, + gnrc_gomach_packet_info_t *pa_info) +{ + assert(netif != NULL); + assert(pkt != NULL); + assert(pa_info != NULL); + + gnrc_gomach_frame_data_t *gomach_data_hdr = NULL; + + gnrc_pktsnip_t *gomach_snip = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_GOMACH); + if (gomach_snip == NULL) { + LOG_ERROR("[GOMACH]: No gomach header found in gnrc_gomach_indicator_update().\n"); + return; + } + else { + gomach_data_hdr = gomach_snip->data; + } + + if (gomach_data_hdr == NULL) { + LOG_ERROR("[GOMACH]: GoMacH's data header is null.\n"); + return; + } + + uint8_t i; + /* Check whether the device has been registered or not. */ + for (i = 0; i < GNRC_GOMACH_SLOSCH_UNIT_COUNT; i++) { + if (memcmp(&netif->mac.rx.slosch_list[i].node_addr.addr, + &pa_info->src_addr.addr, + pa_info->src_addr.len) == 0) { + /* Update the sender's queue-length indicator. */ + netif->mac.rx.slosch_list[i].queue_indicator = gomach_data_hdr->queue_indicator; + return; + } + } + + /* The sender has not registered yet. */ + for (i = 0; i < GNRC_GOMACH_SLOSCH_UNIT_COUNT; i++) { + if ((netif->mac.rx.slosch_list[i].node_addr.len == 0) || + (netif->mac.rx.slosch_list[i].queue_indicator == 0)) { + netif->mac.rx.slosch_list[i].node_addr.len = pa_info->src_addr.len; + memcpy(netif->mac.rx.slosch_list[i].node_addr.addr, + pa_info->src_addr.addr, + pa_info->src_addr.len); + + /* Update the sender's queue-length indicator. */ + netif->mac.rx.slosch_list[i].queue_indicator = gomach_data_hdr->queue_indicator; + return; + } + } +} + +bool gnrc_gomach_check_duplicate(gnrc_netif_t *netif, gnrc_gomach_packet_info_t *pa_info) +{ + assert(netif != NULL); + assert(pa_info != NULL); + + uint8_t i; + /* First check if we can found the same source sender ID in the recorded info units. */ + for (i = 0; i < GNRC_GOMACH_DUPCHK_BUFFER_SIZE; i++) { + if (memcmp(&netif->mac.rx.check_dup_pkt.last_nodes[i].node_addr.addr, + &pa_info->src_addr.addr, + pa_info->src_addr.len) == 0) { + netif->mac.rx.check_dup_pkt.last_nodes[i].life_cycle = 0; + if (netif->mac.rx.check_dup_pkt.last_nodes[i].seq == pa_info->seq) { + /* Found same MAC sequence, this is duplicate packet . */ + return true; + } + else { + netif->mac.rx.check_dup_pkt.last_nodes[i].seq = pa_info->seq; + return false; + } + } + } + + /* Look for a free info unit */ + for (i = 0; i < GNRC_GOMACH_DUPCHK_BUFFER_SIZE; i++) { + if (netif->mac.rx.check_dup_pkt.last_nodes[i].node_addr.len == 0) { + netif->mac.rx.check_dup_pkt.last_nodes[i].node_addr.len = pa_info->src_addr.len; + memcpy(netif->mac.rx.check_dup_pkt.last_nodes[i].node_addr.addr, + pa_info->src_addr.addr, + pa_info->src_addr.len); + netif->mac.rx.check_dup_pkt.last_nodes[i].seq = pa_info->seq; + netif->mac.rx.check_dup_pkt.last_nodes[i].life_cycle = 0; + return false; + } + } + + return false; +} + +static void _cp_packet_process_preamble(gnrc_netif_t *netif, + gnrc_gomach_packet_info_t *info, + gnrc_pktsnip_t *pkt) +{ + if (memcmp(&netif->l2addr, &info->dst_addr.addr, + netif->l2addr_len) == 0) { + /* Get a preamble packet that is for the device itself. */ + gnrc_gomach_set_got_preamble(netif, true); + + /* If reception is not going on, reply preamble-ack. */ + if (gnrc_gomach_get_netdev_state(netif) == NETOPT_STATE_IDLE) { + /* Disable auto-ack. */ + gnrc_gomach_set_autoack(netif, NETOPT_DISABLE); + + int res = gnrc_gomach_send_preamble_ack(netif, info); + if (res < 0) { + LOG_ERROR("ERROR: [GOMACH]: send preamble-ACK failed: %d.\n", res); + } + + /* Enable Auto ACK again for data reception. */ + gnrc_gomach_set_autoack(netif, NETOPT_ENABLE); + } + } + else { + /* Receives unintended preamble that is not for the device. */ + gnrc_gomach_set_unintd_preamble(netif, true); + } + gnrc_pktbuf_release(pkt); +} + +static void _cp_packet_process_data(gnrc_netif_t *netif, + gnrc_gomach_packet_info_t *info, + gnrc_pktsnip_t *pkt) +{ + if (memcmp(&netif->l2addr, &info->dst_addr.addr, + netif->l2addr_len) == 0) { + /* The data is for itself, now update the sender's queue-length indicator. */ + gnrc_gomach_indicator_update(netif, pkt, info); + + /* Check that whether this is a duplicate packet. */ + if ((gnrc_gomach_check_duplicate(netif, info))) { + gnrc_pktbuf_release(pkt); + LOG_DEBUG("[GOMACH]: received a duplicate packet.\n"); + return; + } + gnrc_gomach_dispatch_defer(netif->mac.rx.dispatch_buffer, pkt); + gnrc_mac_dispatch(&netif->mac.rx); + +#if (GNRC_GOMACH_ENABLE_DUTYCYLE_RECORD == 1) + /* Output radio duty-cycle ratio */ + uint64_t duty; + duty = xtimer_now_usec64(); + duty = (netif->mac.prot.gomach.awake_duration_sum_ticks) * 100 / + (duty - netif->mac.prot.gomach.system_start_time_ticks); + printf("[GoMacH]: achieved radio duty-cycle: %lu %% \n", (uint32_t)duty); +#endif + } + else { + /* If the data is not for the device, release it. */ + gnrc_pktbuf_release(pkt); + } +} + +static inline void _cp_packet_process_bcast(gnrc_netif_t *netif, + gnrc_pktsnip_t *pkt) +{ + /* Receive a broadcast packet, quit the listening period to avoid receive duplicate + * broadcast packet. */ + gnrc_gomach_set_quit_cycle(netif, true); + gnrc_gomach_dispatch_defer(netif->mac.rx.dispatch_buffer, pkt); + gnrc_mac_dispatch(&netif->mac.rx); +} + +void gnrc_gomach_cp_packet_process(gnrc_netif_t *netif) +{ + assert(netif != NULL); + + gnrc_pktsnip_t *pkt; + gnrc_gomach_packet_info_t receive_packet_info; + + while ((pkt = gnrc_priority_pktqueue_pop(&netif->mac.rx.queue)) != NULL) { + /* Parse the received packet, fetch key MAC informations. */ + int res = _parse_packet(netif, pkt, &receive_packet_info); + if (res != 0) { + LOG_DEBUG("[GOMACH] CP: Packet could not be parsed: %i\n", res); + gnrc_pktbuf_release(pkt); + continue; + } + + switch (receive_packet_info.header->type) { + case GNRC_GOMACH_FRAME_PREAMBLE: { + _cp_packet_process_preamble(netif, &receive_packet_info, pkt); + break; + } + + case GNRC_GOMACH_FRAME_DATA: { + _cp_packet_process_data(netif, &receive_packet_info, pkt); + break; + } + case GNRC_GOMACH_FRAME_BROADCAST: { + _cp_packet_process_bcast(netif, pkt); + break; + } + default: { + gnrc_pktbuf_release(pkt); + break; + } + } + } +} + +void gnrc_gomach_init_choose_subchannel(gnrc_netif_t *netif) +{ + assert(netif != NULL); + + uint16_t subchannel_seq, own_id; + + own_id = 0; + own_id = netif->l2addr[netif->l2addr_len - 2]; + own_id = own_id << 8; + own_id |= netif->l2addr[netif->l2addr_len - 1]; + + /* First randomly set a sub-channel sequence, which ranges from 12 to 25. */ + subchannel_seq = 12 + (own_id % 14); + + /* Find a free sub-channel sequence. */ + int i = 0; + for (i = 0; i < 14; i++) { + uint16_t check_seq = subchannel_seq - 11; + check_seq = (1 << check_seq); + + if (check_seq & netif->mac.prot.gomach.subchannel_occu_flags) { + LOG_INFO("INFO: [GOMACH]: sub-channel already occupied, find a new one.\n"); + own_id += 1; + subchannel_seq = 12 + (own_id % 14); + } + else { + break; + } + } + + netif->mac.prot.gomach.sub_channel_seq = subchannel_seq; +} + +int gnrc_gomach_send_preamble(gnrc_netif_t *netif, netopt_enable_t csma_enable) +{ + assert(netif != NULL); + + gnrc_pktsnip_t *pkt; + gnrc_netif_hdr_t *nethdr_preamble; + gnrc_pktsnip_t *gomach_pkt; + + /* Assemble the preamble packet. */ + gnrc_gomach_frame_preamble_t gomach_preamble_hdr; + + gomach_preamble_hdr.header.type = GNRC_GOMACH_FRAME_PREAMBLE; + memcpy(gomach_preamble_hdr.dst_addr.addr, + netif->mac.tx.current_neighbor->l2_addr, + netif->mac.tx.current_neighbor->l2_addr_len); + gomach_preamble_hdr.dst_addr.len = netif->mac.tx.current_neighbor->l2_addr_len; + + pkt = gnrc_pktbuf_add(NULL, &gomach_preamble_hdr, sizeof(gomach_preamble_hdr), + GNRC_NETTYPE_GOMACH); + if (pkt == NULL) { + LOG_ERROR("ERROR: [GOMACH]: pktbuf add failed in gnrc_gomach_send_preamble().\n"); + return -ENOBUFS; + } + gomach_pkt = pkt; + + pkt = gnrc_pktbuf_add(pkt, NULL, sizeof(gnrc_netif_hdr_t), GNRC_NETTYPE_NETIF); + if (pkt == NULL) { + LOG_ERROR("ERROR: [GOMACH]: netif add failed in gnrc_gomach_send_preamble().\n"); + gnrc_pktbuf_release(gomach_pkt); + return -ENOBUFS; + } + gomach_pkt = pkt; + + gnrc_pktsnip_t *netif_snip = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_NETIF); + if (netif_snip == NULL) { + LOG_ERROR("[GOMACH]: No netif_hdr found in gnrc_gomach_send_preamble().\n"); + gnrc_pktbuf_release(gomach_pkt); + return -ENOBUFS; + } + else { + nethdr_preamble = netif_snip->data; + } + + /* Construct NETIF header and initiate address fields. */ + gnrc_netif_hdr_init(nethdr_preamble, 0, 0); + + /* Send preamble packet as broadcast. */ + nethdr_preamble->flags |= GNRC_NETIF_HDR_FLAGS_BROADCAST; + + return gnrc_gomach_send(netif, pkt, csma_enable); +} + +int gnrc_gomach_bcast_subchann_seq(gnrc_netif_t *netif, netopt_enable_t use_csma) +{ + assert(netif != NULL); + + gnrc_pktsnip_t *pkt; + gnrc_pktsnip_t *gomach_pkt; + gnrc_netif_hdr_t *nethdr_announce; + + /* Assemble the sub-channel sequence announce packet. */ + gnrc_gomach_frame_announce_t gomach_announce_hdr; + + gomach_announce_hdr.header.type = GNRC_GOMACH_FRAME_ANNOUNCE; + gomach_announce_hdr.subchannel_seq = netif->mac.prot.gomach.sub_channel_seq; + + pkt = gnrc_pktbuf_add(NULL, &gomach_announce_hdr, sizeof(gomach_announce_hdr), + GNRC_NETTYPE_GOMACH); + if (pkt == NULL) { + LOG_ERROR("ERROR: [GOMACH]: pktbuf add failed in gnrc_gomach_bcast_subchann_seq().\n"); + return -ENOBUFS; + } + gomach_pkt = pkt; + + pkt = gnrc_pktbuf_add(pkt, NULL, sizeof(gnrc_netif_hdr_t), GNRC_NETTYPE_NETIF); + if (pkt == NULL) { + gnrc_pktbuf_release(gomach_pkt); + LOG_ERROR("ERROR: [GOMACH]: netif add failed in gnrc_gomach_bcast_subchann_seq().\n"); + return -ENOBUFS; + } + + gnrc_pktsnip_t *netif_snip = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_NETIF); + if (netif_snip == NULL) { + LOG_ERROR("[GOMACH]: No netif_hdr found in gnrc_gomach_bcast_subchann_seq().\n"); + gnrc_pktbuf_release(pkt); + return -ENOBUFS; + } + else { + nethdr_announce = netif_snip->data; + } + + /* Construct NETIF header and initiate address fields. */ + gnrc_netif_hdr_init(nethdr_announce, 0, 0); + + /* Send the packet as broadcast. */ + nethdr_announce->flags |= GNRC_NETIF_HDR_FLAGS_BROADCAST; + + return gnrc_gomach_send(netif, pkt, use_csma); +} + +void gnrc_gomach_process_preamble_ack(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt) +{ + assert(netif != NULL); + assert(pkt != NULL); + + gnrc_gomach_frame_preamble_ack_t *gomach_preamble_ack_hdr = NULL; + + gnrc_pktsnip_t *gomach_snip = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_GOMACH); + if (gomach_snip == NULL) { + LOG_ERROR("[GOMACH]: No gomach_snip found in gnrc_gomach_process_preamble_ack().\n"); + return; + } + else { + gomach_preamble_ack_hdr = gomach_snip->data; + } + + if (gomach_preamble_ack_hdr == NULL) { + LOG_ERROR("[GOMACH]: preamble_ack_hdr is null.\n"); + return; + } + + /* Mark the neighbor as phase-known */ + netif->mac.tx.current_neighbor->mac_type = GNRC_GOMACH_TYPE_KNOWN; + + /* Fetch and deduce the exact wake-up phase of the neighbor. */ + long int phase_us = gnrc_gomach_phase_now(netif) - + gomach_preamble_ack_hdr->phase_in_us; + + if (phase_us < 0) { + phase_us += GNRC_GOMACH_SUPERFRAME_DURATION_US; + } + + if (((uint32_t)phase_us > (GNRC_GOMACH_SUPERFRAME_DURATION_US - GNRC_GOMACH_CP_MIN_GAP_US)) || + ((uint32_t)phase_us < GNRC_GOMACH_CP_MIN_GAP_US)) { + LOG_DEBUG("[GOMACH] t2u: own phase is close to the neighbor's.\n"); + gnrc_gomach_set_phase_backoff(netif, true); + /* Set a random phase-backoff value. */ + netif->mac.prot.gomach.backoff_phase_us = + random_uint32_range(GNRC_GOMACH_CP_MIN_GAP_US, + (GNRC_GOMACH_SUPERFRAME_DURATION_US - GNRC_GOMACH_CP_MIN_GAP_US)); + } + + netif->mac.tx.current_neighbor->cp_phase = phase_us; + + /* Record the public-channel phase of the neighbor. */ + if (gnrc_gomach_get_enter_new_cycle(netif) && + ((uint32_t)phase_us > gnrc_gomach_phase_now(netif))) { + if (gnrc_gomach_get_on_pubchan_1(netif)) { + netif->mac.tx.current_neighbor->pub_chanseq = netif->mac.prot.gomach.pub_channel_2; + } + else { + netif->mac.tx.current_neighbor->pub_chanseq = netif->mac.prot.gomach.pub_channel_1; + } + } + else { + if (gnrc_gomach_get_on_pubchan_1(netif)) { + netif->mac.tx.current_neighbor->pub_chanseq = netif->mac.prot.gomach.pub_channel_1; + } + else { + netif->mac.tx.current_neighbor->pub_chanseq = netif->mac.prot.gomach.pub_channel_2; + } + } +} + +static inline void _wait_preamble_ack_preamble(gnrc_netif_t *netif, + gnrc_pktsnip_t *pkt) +{ + /* Found other ongoing preamble transmission, quit its own t2u for + * collision avoidance. */ + gnrc_pktbuf_release(pkt); + LOG_DEBUG("[GOMACH] t2u: found other preamble, quit t2u.\n"); + gnrc_gomach_set_quit_cycle(netif, true); +} + +static bool _wait_preamble_ack_preambleack(gnrc_netif_t *netif, + gnrc_gomach_packet_info_t *info, + gnrc_pktsnip_t *pkt) +{ + if ((memcmp(&netif->l2addr, &info->dst_addr.addr, + netif->l2addr_len) == 0) && + (memcmp(&netif->mac.tx.current_neighbor->l2_addr, + &info->src_addr.addr, + netif->mac.tx.current_neighbor->l2_addr_len) == 0)) { + /* Got preamble-ACK from targeted device. */ + gnrc_gomach_set_got_preamble_ack(netif, true); + + /* Analyze the preamble-ACK to get phase-locked with the neighbor device. */ + gnrc_gomach_process_preamble_ack(netif, pkt); + + gnrc_pktbuf_release(pkt); + gnrc_priority_pktqueue_flush(&netif->mac.rx.queue); + return false; + } + + /* Preamble-ACK is not from targeted device. release it. */ + gnrc_pktbuf_release(pkt); + return true; +} + +static bool _wait_preamble_ack_data(gnrc_netif_t *netif, + gnrc_gomach_packet_info_t *info, + gnrc_pktsnip_t *pkt) +{ + if (memcmp(&netif->l2addr, &info->dst_addr.addr, + netif->l2addr_len) == 0) { + /* The data is for itself, now update the sender's queue-length indicator. */ + gnrc_gomach_indicator_update(netif, pkt, info); + + /* Check that whether this is a duplicate packet. */ + if ((gnrc_gomach_check_duplicate(netif, info))) { + gnrc_pktbuf_release(pkt); + LOG_DEBUG("[GOMACH] t2u: received a duplicate packet.\n"); + return false; + } + + gnrc_gomach_dispatch_defer(netif->mac.rx.dispatch_buffer, pkt); + gnrc_mac_dispatch(&netif->mac.rx); + } + else { + /* If the data is not for the device, release it. */ + gnrc_pktbuf_release(pkt); + } + return true; +} + +static inline void _wait_preamble_ack_bcast(gnrc_netif_t *netif, + gnrc_pktsnip_t *pkt) +{ + /* Release the received broadcast pkt. Only receive broadcast packets in CP, + * thus to reduce complexity. */ + gnrc_gomach_set_quit_cycle(netif, true); + gnrc_pktbuf_release(pkt); + LOG_DEBUG("WARNING: [GOMACH] t2u: receive a broadcast packet, quit t2u.\n"); +} + +void gnrc_gomach_process_pkt_in_wait_preamble_ack(gnrc_netif_t *netif) +{ + assert(netif != NULL); + + gnrc_pktsnip_t *pkt; + gnrc_gomach_packet_info_t receive_packet_info; + + while ((pkt = gnrc_priority_pktqueue_pop(&netif->mac.rx.queue)) != NULL) { + /* Parse the received packet. */ + int res = _parse_packet(netif, pkt, &receive_packet_info); + if (res != 0) { + LOG_DEBUG("[GOMACH] t2u: Packet could not be parsed: %i\n", res); + gnrc_pktbuf_release(pkt); + continue; + } + + switch (receive_packet_info.header->type) { + case GNRC_GOMACH_FRAME_PREAMBLE: { + _wait_preamble_ack_preamble(netif, pkt); + break; + } + case GNRC_GOMACH_FRAME_PREAMBLE_ACK: { + if (!_wait_preamble_ack_preambleack(netif, &receive_packet_info, pkt)) { + return; + } + break; + } + case GNRC_GOMACH_FRAME_DATA: { + if (!_wait_preamble_ack_data(netif, &receive_packet_info, pkt)) { + return; + } + break; + } + case GNRC_GOMACH_FRAME_BROADCAST: { + _wait_preamble_ack_bcast(netif, pkt); + break; + } + default: { + gnrc_pktbuf_release(pkt); + break; + } + } + } +} + +int gnrc_gomach_send_data(gnrc_netif_t *netif, netopt_enable_t csma_enable) +{ + assert(netif != NULL); + + gnrc_pktsnip_t *pkt = netif->mac.tx.packet; + + assert(pkt != NULL); + + /* Insert GoMacH header above NETIF header. */ + gnrc_gomach_frame_data_t *gomach_data_hdr_pointer; + + gnrc_pktsnip_t *gomach_snip = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_GOMACH); + if (gomach_snip != NULL) { + gomach_data_hdr_pointer = gomach_snip->data; + } + else { + gomach_data_hdr_pointer = NULL; + } + + if (gomach_data_hdr_pointer == NULL) { + /* No GoMacH header yet, build one. */ + gnrc_gomach_frame_data_t gomach_data_hdr; + gomach_data_hdr.header.type = GNRC_GOMACH_FRAME_DATA; + + /* Set the queue-length indicator according to its current queue situation. */ + gomach_data_hdr.queue_indicator = + gnrc_priority_pktqueue_length(&netif->mac.tx.current_neighbor->queue); + + /* Save the payload pointer. */ + gnrc_pktsnip_t *payload = netif->mac.tx.packet->next; + + pkt->next = gnrc_pktbuf_add(pkt->next, &gomach_data_hdr, sizeof(gomach_data_hdr), + GNRC_NETTYPE_GOMACH); + if (pkt->next == NULL) { + LOG_ERROR("ERROR: [GOMACH]: pktbuf add failed in gnrc_gomach_send_data().\n"); + + /* Make append payload after netif header again. */ + netif->mac.tx.packet->next = payload; + return -ENOBUFS; + } + } + else { + /* GoMacH header exists, update the queue-indicator. */ + gomach_data_hdr_pointer->queue_indicator = + gnrc_priority_pktqueue_length(&netif->mac.tx.current_neighbor->queue); + } + + gnrc_pktbuf_hold(netif->mac.tx.packet, 1); + + /* Send the data packet here. */ + return gnrc_gomach_send(netif, netif->mac.tx.packet, csma_enable); +} + +bool gnrc_gomach_find_next_tx_neighbor(gnrc_netif_t *netif) +{ + assert(netif != NULL); + + int next = -1; + + /* If current neighbor pointer is not NULL, it means we have pending packet from last + * t2u or t2k or bcast to send. In this case, return immediately. */ + if (netif->mac.tx.current_neighbor != NULL) { + return true; + } + + /* First check whether we have broadcast packet to send. */ + if (gnrc_priority_pktqueue_length(&netif->mac.tx.neighbors[0].queue) > 0) { + next = 0; + } + else { + /* Find the next neighbor to send data packet to. */ + + /* Don't always start checking with ID 0, take turns to check every neighbor's queue, + * thus to be more fair. */ + uint8_t j = netif->mac.tx.last_tx_neighbor_id + 1; + + if (j >= GNRC_MAC_NEIGHBOR_COUNT) { + j = 1; + } + + for (uint8_t i = 1; i < GNRC_MAC_NEIGHBOR_COUNT; i++) { + if (gnrc_priority_pktqueue_length(&netif->mac.tx.neighbors[j].queue) > 0) { + netif->mac.tx.last_tx_neighbor_id = j; + next = (int) j; + break; + } + else { + j++; + if (j >= GNRC_MAC_NEIGHBOR_COUNT) { + j = 1; + } + } + } + } + + if (next >= 0) { + gnrc_pktsnip_t *pkt = gnrc_priority_pktqueue_pop(&netif->mac.tx.neighbors[next].queue); + if (pkt != NULL) { + netif->mac.tx.packet = pkt; + netif->mac.tx.current_neighbor = &netif->mac.tx.neighbors[next]; + netif->mac.tx.tx_seq = 0; + netif->mac.tx.t2u_retry_counter = 0; + return true; + } + else { + return false; + } + } + + return false; +} + +void gnrc_gomach_beacon_process(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt) +{ + assert(netif != NULL); + assert(pkt != NULL); + + gnrc_gomach_frame_beacon_t *gomach_beacon_hdr = NULL; + gnrc_pktsnip_t *gomach_snip = NULL; + + gnrc_gomach_l2_id_t *id_list; + uint8_t *slots_list; + uint8_t schedulelist_size = 0; + bool got_allocated_slots; + uint8_t id_position; + + gnrc_pktsnip_t *beacon_snip = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_GOMACH); + if (beacon_snip == NULL) { + LOG_ERROR("[GOMACH]: No beacon-snip found in gnrc_gomach_beacon_process().\n"); + return; + } + else { + gomach_beacon_hdr = beacon_snip->data; + } + + if (gomach_beacon_hdr == NULL) { + LOG_ERROR("ERROR: [GOMACH]: GoMacH's beacon header is null.\n"); + return; + } + + schedulelist_size = gomach_beacon_hdr->schedulelist_size; + netif->mac.tx.vtdma_para.sub_channel_seq = gomach_beacon_hdr->sub_channel_seq; + + if (schedulelist_size == 0) { + /* No allocated slots. */ + netif->mac.tx.vtdma_para.slots_num = 0; + netif->mac.tx.vtdma_para.slots_position = 0; + return; + } + + /* Take the ID-list out. */ + gomach_snip = gnrc_pktbuf_mark(pkt, schedulelist_size * sizeof(gnrc_gomach_l2_id_t), + GNRC_NETTYPE_GOMACH); + id_list = gomach_snip->data; + + /* Take the slots-list out. */ + slots_list = pkt->data; + + /* Check whether this device has been allocated slots. */ + int i = 0; + got_allocated_slots = false; + id_position = 0; + + for (i = 0; i < schedulelist_size; i++) { + if (memcmp(netif->l2addr, id_list[i].addr, netif->l2addr_len) == 0) { + got_allocated_slots = true; + id_position = i; + break; + } + } + + if (got_allocated_slots == true) { + /* Find the slots number and the related slots position. */ + netif->mac.tx.vtdma_para.slots_num = slots_list[id_position]; + + uint8_t slots_position = 0; + for (i = 0; i < id_position; i++) { + slots_position += slots_list[i]; + } + netif->mac.tx.vtdma_para.slots_position = slots_position; + } + else { + /* No allocated slots. */ + netif->mac.tx.vtdma_para.slots_num = 0; + netif->mac.tx.vtdma_para.slots_position = 0; + } +} + +void gnrc_gomach_packet_process_in_wait_beacon(gnrc_netif_t *netif) +{ + assert(netif != NULL); + + gnrc_pktsnip_t *pkt; + gnrc_gomach_packet_info_t receive_packet_info; + + while ((pkt = gnrc_priority_pktqueue_pop(&netif->mac.rx.queue)) != NULL) { + /* Parse the received packet. */ + int res = _parse_packet(netif, pkt, &receive_packet_info); + if (res != 0) { + LOG_DEBUG("[GOMACH] t2k: Packet could not be parsed: %i\n", res); + gnrc_pktbuf_release(pkt); + continue; + } + + switch (receive_packet_info.header->type) { + case GNRC_GOMACH_FRAME_BEACON: { + if (memcmp(&netif->mac.tx.current_neighbor->l2_addr, + &receive_packet_info.src_addr.addr, + netif->mac.tx.current_neighbor->l2_addr_len) == 0) { + gnrc_gomach_clear_timeout(netif, GNRC_GOMACH_TIMEOUT_WAIT_BEACON); + gnrc_gomach_beacon_process(netif, pkt); + } + gnrc_pktbuf_release(pkt); + break; + } + case GNRC_GOMACH_FRAME_PREAMBLE: { + /* Release preamble packet no matter the preamble is for it or not, + * and quit the t2k procedure. */ + gnrc_gomach_set_quit_cycle(netif, true); + gnrc_pktbuf_release(pkt); + break; + } + case GNRC_GOMACH_FRAME_DATA: { + /* It is unlikely that we will received a data for us here. + * This means the device' CP is close with its destination's. */ + if (memcmp(&netif->l2addr, &receive_packet_info.dst_addr.addr, + netif->l2addr_len) == 0) { + gnrc_gomach_indicator_update(netif, pkt, &receive_packet_info); + + if ((gnrc_gomach_check_duplicate(netif, &receive_packet_info))) { + gnrc_pktbuf_release(pkt); + LOG_DEBUG("[GOMACH]: received a duplicate packet.\n"); + return; + } + + gnrc_gomach_dispatch_defer(netif->mac.rx.dispatch_buffer, pkt); + gnrc_mac_dispatch(&netif->mac.rx); + } + else { + gnrc_pktbuf_release(pkt); + } + break; + } + case GNRC_GOMACH_FRAME_BROADCAST: { + gnrc_gomach_set_quit_cycle(netif, true); + gnrc_pktbuf_release(pkt); + break; + } + default: { + gnrc_pktbuf_release(pkt); + break; + } + } + } +} + +void gnrc_gomach_packet_process_in_vtdma(gnrc_netif_t *netif) +{ + assert(netif != NULL); + + gnrc_pktsnip_t *pkt; + gnrc_gomach_packet_info_t receive_packet_info; + + while ((pkt = gnrc_priority_pktqueue_pop(&netif->mac.rx.queue)) != NULL) { + /* Parse the received packet. */ + int res = _parse_packet(netif, pkt, &receive_packet_info); + if (res != 0) { + LOG_DEBUG("[GOMACH] vtdma: Packet could not be parsed: %i\n", res); + gnrc_pktbuf_release(pkt); + continue; + } + + switch (receive_packet_info.header->type) { + case GNRC_GOMACH_FRAME_DATA: { + gnrc_gomach_indicator_update(netif, pkt, &receive_packet_info); + + if ((gnrc_gomach_check_duplicate(netif, &receive_packet_info))) { + gnrc_pktbuf_release(pkt); + LOG_DEBUG("[GOMACH] vtdma: received a duplicate packet.\n"); + return; + } + + gnrc_gomach_dispatch_defer(netif->mac.rx.dispatch_buffer, pkt); + gnrc_mac_dispatch(&netif->mac.rx); + break; + } + default: { + gnrc_pktbuf_release(pkt); + break; + } + } + } +} + +void gnrc_gomach_update_neighbor_phase(gnrc_netif_t *netif) +{ + assert(netif != NULL); + + for (uint8_t i = 1; i < GNRC_MAC_NEIGHBOR_COUNT; i++) { + if (netif->mac.tx.neighbors[i].mac_type == GNRC_GOMACH_TYPE_KNOWN) { + long int tmp = netif->mac.tx.neighbors[i].cp_phase - + netif->mac.prot.gomach.backoff_phase_us; + if (tmp < 0) { + tmp += GNRC_GOMACH_SUPERFRAME_DURATION_US; + + /* Toggle the neighbor's public channel phase if tmp < 0. */ + if (netif->mac.tx.neighbors[i].pub_chanseq == + netif->mac.prot.gomach.pub_channel_1) { + netif->mac.tx.neighbors[i].pub_chanseq = netif->mac.prot.gomach.pub_channel_2; + } + else { + netif->mac.tx.neighbors[i].pub_chanseq = netif->mac.prot.gomach.pub_channel_1; + } + } + netif->mac.tx.neighbors[i].cp_phase = (uint32_t)tmp; + } + } +} + +void gnrc_gomach_update_neighbor_pubchan(gnrc_netif_t *netif) +{ + assert(netif != NULL); + + /* Toggle this device's current channel. */ + if (netif->mac.prot.gomach.cur_pub_channel == netif->mac.prot.gomach.pub_channel_1) { + netif->mac.prot.gomach.cur_pub_channel = netif->mac.prot.gomach.pub_channel_2; + } + else { + netif->mac.prot.gomach.cur_pub_channel = netif->mac.prot.gomach.pub_channel_1; + } + + /* Toggle TX neighbors' current channel. */ + for (uint8_t i = 1; i < GNRC_MAC_NEIGHBOR_COUNT; i++) { + if (netif->mac.tx.neighbors[i].mac_type == GNRC_GOMACH_TYPE_KNOWN) { + if (netif->mac.tx.neighbors[i].pub_chanseq == netif->mac.prot.gomach.pub_channel_1) { + netif->mac.tx.neighbors[i].pub_chanseq = netif->mac.prot.gomach.pub_channel_2; + } + else { + netif->mac.tx.neighbors[i].pub_chanseq = netif->mac.prot.gomach.pub_channel_1; + } + } + } +} diff --git a/sys/net/gnrc/link_layer/gomach/include/gomach_internal.h b/sys/net/gnrc/link_layer/gomach/include/gomach_internal.h new file mode 100644 index 0000000000000000000000000000000000000000..c819a820695fe787b4e9e40a3b6f519160afd8ae --- /dev/null +++ b/sys/net/gnrc/link_layer/gomach/include/gomach_internal.h @@ -0,0 +1,917 @@ +/* + * Copyright (C) 2017 INRIA + * + * 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 net_gnrc_gomach + * @{ + * + * @file + * @brief GoMacH's internal functions. + * @internal + * @author Shuguo Zhuo <shuguo.zhuo@inria.fr> + */ + +#ifndef GOMACH_INTERNAL_H +#define GOMACH_INTERNAL_H + +#include <stdint.h> + +#include "periph/rtt.h" +#include "net/gnrc/netif.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Flag to track if the transmission has finished. + */ +#define GNRC_GOMACH_INFO_TX_FINISHED (0x0008U) + +/** + * @brief Flag to track if a packet has been successfully received. + */ +#define GNRC_GOMACH_INFO_PKT_RECEIVED (0x0010U) + +/** + * @brief Flag to track if need to update GoMacH. + */ +#define GNRC_GOMACH_INTERNAL_INFO_ND_UPDATE (0x0001U) + +/** + * @brief Flag to track if need to quit the current cycle in GoMacH. + */ +#define GNRC_GOMACH_INTERNAL_INFO_QUIT_CYCLE (0x0002U) + +/** + * @brief Flag to track if CP period has ended in GoMacH. + */ +#define GNRC_GOMACH_INTERNAL_INFO_CP_END (0x0004U) + +/** + * @brief Flag to track if vTDMA has ended in GoMacH. + */ +#define GNRC_GOMACH_INTERNAL_INFO_VTDMA_END (0x0008U) + +/** + * @brief Flag to track if the node has received unintended preamble. + */ +#define GNRC_GOMACH_INTERNAL_INFO_UNINTD_PREAMBLE (0x0010U) + +/** + * @brief Flag to track if need to quit the current cycle in GoMacH. + */ +#define GNRC_GOMACH_INTERNAL_INFO_GOT_PREAMBLE (0x0020U) + +/** + * @brief Flag to track if node's duty-cycle has started in GoMacH. + */ +#define GNRC_GOMACH_INTERNAL_INFO_DUTY_CYCLE_START (0x0040U) + +/** + * @brief Flag to track if node need to backoff its phase in GoMacH. + */ +#define GNRC_GOMACH_INTERNAL_INFO_PHASE_BACKOFF (0x0080U) + +/** + * @brief Flag to track if beacon transmission fail in GoMacH. + */ +#define GNRC_GOMACH_INTERNAL_INFO_BEACON_FAIL (0x0200U) + +/** + * @brief Flag to track if node's packet buffer is full in GoMacH. + */ +#define GNRC_GOMACH_INTERNAL_INFO_BUFFER_FULL (0x0400U) + +/** + * @brief Flag to track if node has entered a new cycle in GoMacH. + */ +#define GNRC_GOMACH_INTERNAL_INFO_ENTER_NEW_CYCLE (0x0800U) + +/** + * @brief Flag to track if node has got preamble-ACK in GoMacH. + */ +#define GNRC_GOMACH_INTERNAL_INFO_GOT_PREAMBLEACK (0x1000U) + +/** + * @brief Flag to track if node's radio is on public-channel-1. + */ +#define GNRC_GOMACH_INTERNAL_INFO_ON_PUBCHAN_1 (0x2000U) + +/** + * @brief Flag to track if node has reached maximum preamble interval. + */ +#define GNRC_GOMACH_INTERNAL_INFO_MAX_PREAM_INTERV (0x4000U) + +/** + * @brief Flag to track if node has turned on its radio. + */ +#define GNRC_GOMACH_INTERNAL_INFO_RADIO_IS_ON (0x8000U) + +/** + * @brief Set the @ref GNRC_GOMACH_INFO_TX_FINISHED flag of the device. + * + * @param[in,out] netif the network interface. + * @param[in] tx_finish value for GoMacH's + * @ref GNRC_GOMACH_INFO_TX_FINISHED flag. + * + */ +static inline void gnrc_gomach_set_tx_finish(gnrc_netif_t *netif, bool tx_finish) +{ + if (tx_finish) { + netif->mac.mac_info |= GNRC_GOMACH_INFO_TX_FINISHED; + } + else { + netif->mac.mac_info &= ~GNRC_GOMACH_INFO_TX_FINISHED; + } +} + +/** + * @brief Get the @ref GNRC_GOMACH_INFO_TX_FINISHED flag of the device. + * + * @param[in] netif the network interface. + * + * @return true if TX has finished. + * @return false if TX hasn't finished yet. + */ +static inline bool gnrc_gomach_get_tx_finish(gnrc_netif_t *netif) +{ + return (netif->mac.mac_info & GNRC_GOMACH_INFO_TX_FINISHED); +} + +/** + * @brief Set the @ref GNRC_GOMACH_INFO_PKT_RECEIVED flag of the device. + * + * @param[in,out] netif the network interface. + * @param[in] received value for GoMacH's + * @ref GNRC_GOMACH_INFO_PKT_RECEIVED flag. + * + */ +static inline void gnrc_gomach_set_pkt_received(gnrc_netif_t *netif, bool received) +{ + if (received) { + netif->mac.mac_info |= GNRC_GOMACH_INFO_PKT_RECEIVED; + } + else { + netif->mac.mac_info &= ~GNRC_GOMACH_INFO_PKT_RECEIVED; + } +} + +/** + * @brief Get the @ref GNRC_GOMACH_INFO_PKT_RECEIVED flag of the device. + * + * @param[in] netif the network interface. + * + * @return true if radio has successfully received a packet. + * @return false if radio hasn't received a packet yet. + */ +static inline bool gnrc_gomach_get_pkt_received(gnrc_netif_t *netif) +{ + return (netif->mac.mac_info & GNRC_GOMACH_INFO_PKT_RECEIVED); +} + +/** + * @brief Set the @ref GNRC_GOMACH_INTERNAL_INFO_QUIT_CYCLE flag of the device. + * + * @param[in,out] netif the network interface. + * @param[in] quit value for GoMacH's + * @ref GNRC_GOMACH_INTERNAL_INFO_QUIT_CYCLE flag. + * + */ +static inline void gnrc_gomach_set_quit_cycle(gnrc_netif_t *netif, bool quit) +{ + if (quit) { + netif->mac.prot.gomach.gomach_info |= GNRC_GOMACH_INTERNAL_INFO_QUIT_CYCLE; + } + else { + netif->mac.prot.gomach.gomach_info &= ~GNRC_GOMACH_INTERNAL_INFO_QUIT_CYCLE; + } +} + +/** + * @brief Get the @ref GNRC_GOMACH_INTERNAL_INFO_QUIT_CYCLE flag of the device. + * + * @param[in] netif the network interface. + * + * @return true if need to quit cycle. + * @return false if no need to quit cycle. + */ +static inline bool gnrc_gomach_get_quit_cycle(gnrc_netif_t *netif) +{ + return (netif->mac.prot.gomach.gomach_info & GNRC_GOMACH_INTERNAL_INFO_QUIT_CYCLE); +} + +/** + * @brief Set the @ref GNRC_GOMACH_INTERNAL_INFO_GOT_PREAMBLE flag of the device. + * + * @param[in,out] netif the network interface. + * @param[in] got_preamble value for GoMacH's + * @ref GNRC_GOMACH_INTERNAL_INFO_GOT_PREAMBLE flag. + * + */ +static inline void gnrc_gomach_set_got_preamble(gnrc_netif_t *netif, bool got_preamble) +{ + if (got_preamble) { + netif->mac.prot.gomach.gomach_info |= GNRC_GOMACH_INTERNAL_INFO_GOT_PREAMBLE; + } + else { + netif->mac.prot.gomach.gomach_info &= ~GNRC_GOMACH_INTERNAL_INFO_GOT_PREAMBLE; + } +} + +/** + * @brief Get the @ref GNRC_GOMACH_INTERNAL_INFO_GOT_PREAMBLE flag of the device. + * + * @param[in] netif the network interface. + * + * @return true if get preamble packet. + * @return false if not get preamble packet yet. + */ +static inline bool gnrc_gomach_get_got_preamble(gnrc_netif_t *netif) +{ + return (netif->mac.prot.gomach.gomach_info & GNRC_GOMACH_INTERNAL_INFO_GOT_PREAMBLE); +} + +/** + * @brief Set the @ref GNRC_GOMACH_INTERNAL_INFO_CP_END flag of the device. + * + * @param[in,out] netif the network interface. + * @param[in] cp_end value for GoMacH's + * @ref GNRC_GOMACH_INTERNAL_INFO_CP_END flag. + * + */ +static inline void gnrc_gomach_set_cp_end(gnrc_netif_t *netif, bool cp_end) +{ + if (cp_end) { + netif->mac.prot.gomach.gomach_info |= GNRC_GOMACH_INTERNAL_INFO_CP_END; + } + else { + netif->mac.prot.gomach.gomach_info &= ~GNRC_GOMACH_INTERNAL_INFO_CP_END; + } +} + +/** + * @brief Get the @ref GNRC_GOMACH_INTERNAL_INFO_CP_END flag of the device. + * + * @param[in] netif the network interface. + * + * @return true if cp has ended. + * @return false if cp hasn't ended yet. + */ +static inline bool gnrc_gomach_get_cp_end(gnrc_netif_t *netif) +{ + return (netif->mac.prot.gomach.gomach_info & GNRC_GOMACH_INTERNAL_INFO_CP_END); +} + +/** + * @brief Set the @ref GNRC_GOMACH_INTERNAL_INFO_VTDMA_END flag of the device. + * + * @param[in,out] netif the network interface. + * @param[in] vtdma_end value for GoMacH's + * @ref GNRC_GOMACH_INTERNAL_INFO_VTDMA_END flag. + * + */ +static inline void gnrc_gomach_set_vTDMA_end(gnrc_netif_t *netif, bool vtdma_end) +{ + if (vtdma_end) { + netif->mac.prot.gomach.gomach_info |= GNRC_GOMACH_INTERNAL_INFO_VTDMA_END; + } + else { + netif->mac.prot.gomach.gomach_info &= ~GNRC_GOMACH_INTERNAL_INFO_VTDMA_END; + } +} + +/** + * @brief Get the @ref GNRC_GOMACH_INTERNAL_INFO_VTDMA_END flag of the device. + * + * @param[in] netif the network interface. + * + * @return true if vTDMA has ended. + * @return false if vTDMA hasn't ended yet. + */ +static inline bool gnrc_gomach_get_vTDMA_end(gnrc_netif_t *netif) +{ + return (netif->mac.prot.gomach.gomach_info & GNRC_GOMACH_INTERNAL_INFO_VTDMA_END); +} + +/** + * @brief Set the @ref GNRC_GOMACH_INTERNAL_INFO_UNINTD_PREAMBLE flag of the device. + * + * @param[in,out] netif the network interface. + * @param[in] uintd_preamble value for GoMacH's + * @ref GNRC_GOMACH_INTERNAL_INFO_UNINTD_PREAMBLE flag. + * + */ +static inline void gnrc_gomach_set_unintd_preamble(gnrc_netif_t *netif, bool uintd_preamble) +{ + if (uintd_preamble) { + netif->mac.prot.gomach.gomach_info |= GNRC_GOMACH_INTERNAL_INFO_UNINTD_PREAMBLE; + } + else { + netif->mac.prot.gomach.gomach_info &= ~GNRC_GOMACH_INTERNAL_INFO_UNINTD_PREAMBLE; + } +} + +/** + * @brief Get the @ref GNRC_GOMACH_INTERNAL_INFO_UNINTD_PREAMBLE flag of the device. + * + * @param[in] netif the network interface. + * + * @return true if has received unintended-preamble. + * @return false if hasn't received unintended-preamble yet. + */ +static inline bool gnrc_gomach_get_unintd_preamble(gnrc_netif_t *netif) +{ + return (netif->mac.prot.gomach.gomach_info & GNRC_GOMACH_INTERNAL_INFO_UNINTD_PREAMBLE); +} + +/** + * @brief Set the @ref GNRC_GOMACH_INTERNAL_INFO_ND_UPDATE flag of the device. + * + * @param[in,out] netif the network interface. + * @param[in] update value for GoMacH's + * @ref GNRC_GOMACH_INTERNAL_INFO_ND_UPDATE flag. + * + */ +static inline void gnrc_gomach_set_update(gnrc_netif_t *netif, bool update) +{ + if (update) { + netif->mac.prot.gomach.gomach_info |= GNRC_GOMACH_INTERNAL_INFO_ND_UPDATE; + } + else { + netif->mac.prot.gomach.gomach_info &= ~GNRC_GOMACH_INTERNAL_INFO_ND_UPDATE; + } +} + +/** + * @brief Get the @ref GNRC_GOMACH_INTERNAL_INFO_ND_UPDATE flag of the device. + * + * @param[in] netif the network interface. + * + * @return true if need update GoMacH. + * @return false if no need to update GoMacH. + */ +static inline bool gnrc_gomach_get_update(gnrc_netif_t *netif) +{ + return (netif->mac.prot.gomach.gomach_info & GNRC_GOMACH_INTERNAL_INFO_ND_UPDATE); +} + +/** + * @brief Set the @ref GNRC_GOMACH_INTERNAL_INFO_DUTY_CYCLE_START flag of the device. + * + * @param[in,out] netif the network interface. + * @param[in] start value for GoMacH's + * @ref GNRC_GOMACH_INTERNAL_INFO_DUTY_CYCLE_START flag. + * + */ +static inline void gnrc_gomach_set_duty_cycle_start(gnrc_netif_t *netif, bool start) +{ + if (start) { + netif->mac.prot.gomach.gomach_info |= GNRC_GOMACH_INTERNAL_INFO_DUTY_CYCLE_START; + } + else { + netif->mac.prot.gomach.gomach_info &= ~GNRC_GOMACH_INTERNAL_INFO_DUTY_CYCLE_START; + } +} + +/** + * @brief Get the @ref GNRC_GOMACH_INTERNAL_INFO_DUTY_CYCLE_START flag of the device. + * + * @param[in] netif the network interface. + * + * @return true if duty-cycle has started. + * @return false if duty-cycle hasn't started yet. + */ +static inline bool gnrc_gomach_get_duty_cycle_start(gnrc_netif_t *netif) +{ + return (netif->mac.prot.gomach.gomach_info & GNRC_GOMACH_INTERNAL_INFO_DUTY_CYCLE_START); +} + +/** + * @brief Set the @ref GNRC_GOMACH_INTERNAL_INFO_PHASE_BACKOFF flag of the device. + * + * @param[in,out] netif the network interface. + * @param[in] backoff value for GoMacH's + * @ref GNRC_GOMACH_INTERNAL_INFO_PHASE_BACKOFF flag. + * + */ +static inline void gnrc_gomach_set_phase_backoff(gnrc_netif_t *netif, bool backoff) +{ + if (backoff) { + netif->mac.prot.gomach.gomach_info |= GNRC_GOMACH_INTERNAL_INFO_PHASE_BACKOFF; + } + else { + netif->mac.prot.gomach.gomach_info &= ~GNRC_GOMACH_INTERNAL_INFO_PHASE_BACKOFF; + } +} + +/** + * @brief Get the @ref GNRC_GOMACH_INTERNAL_INFO_PHASE_BACKOFF flag of the device. + * + * @param[in] netif the network interface. + * + * @return true if need to run phase backoff. + * @return false if no need to run phase backoff. + */ +static inline bool gnrc_gomach_get_phase_backoff(gnrc_netif_t *netif) +{ + return (netif->mac.prot.gomach.gomach_info & GNRC_GOMACH_INTERNAL_INFO_PHASE_BACKOFF); +} + +/** + * @brief Set the @ref GNRC_GOMACH_INTERNAL_INFO_BEACON_FAIL flag of the device. + * + * @param[in,out] netif the network interface. + * @param[in] fail value for GoMacH's + * @ref GNRC_GOMACH_INTERNAL_INFO_BEACON_FAIL flag. + * + */ +static inline void gnrc_gomach_set_beacon_fail(gnrc_netif_t *netif, bool fail) +{ + if (fail) { + netif->mac.prot.gomach.gomach_info |= GNRC_GOMACH_INTERNAL_INFO_BEACON_FAIL; + } + else { + netif->mac.prot.gomach.gomach_info &= ~GNRC_GOMACH_INTERNAL_INFO_BEACON_FAIL; + } +} + +/** + * @brief Get the @ref GNRC_GOMACH_INTERNAL_INFO_BEACON_FAIL flag of the device. + * + * @param[in] netif the network interface. + * + * @return true if send beacon fail. + * @return false upon beacon transmission success. + */ +static inline bool gnrc_gomach_get_beacon_fail(gnrc_netif_t *netif) +{ + return (netif->mac.prot.gomach.gomach_info & GNRC_GOMACH_INTERNAL_INFO_BEACON_FAIL); +} + +/** + * @brief Set the @ref GNRC_GOMACH_INTERNAL_INFO_BUFFER_FULL flag of the device. + * + * @param[in,out] netif the network interface. + * @param[in] full value for GoMacH's + * @ref GNRC_GOMACH_INTERNAL_INFO_BUFFER_FULL flag. + * + */ +static inline void gnrc_gomach_set_buffer_full(gnrc_netif_t *netif, bool full) +{ + if (full) { + netif->mac.prot.gomach.gomach_info |= GNRC_GOMACH_INTERNAL_INFO_BUFFER_FULL; + } + else { + netif->mac.prot.gomach.gomach_info &= ~GNRC_GOMACH_INTERNAL_INFO_BUFFER_FULL; + } +} + +/** + * @brief Get the @ref GNRC_GOMACH_INTERNAL_INFO_BUFFER_FULL flag of the device. + * + * @param[in] netif the network interface. + * + * @return true if node's packet buffer is full. + * @return false if node's packet buffer is not full. + */ +static inline bool gnrc_gomach_get_buffer_full(gnrc_netif_t *netif) +{ + return (netif->mac.prot.gomach.gomach_info & GNRC_GOMACH_INTERNAL_INFO_BUFFER_FULL); +} + +/** + * @brief Set the @ref GNRC_GOMACH_INTERNAL_INFO_ENTER_NEW_CYCLE flag of the device. + * + * @param[in,out] netif the network interface. + * @param[in] enter value for GoMacH's + * @ref GNRC_GOMACH_INTERNAL_INFO_ENTER_NEW_CYCLE flag. + * + */ +static inline void gnrc_gomach_set_enter_new_cycle(gnrc_netif_t *netif, bool enter) +{ + if (enter) { + netif->mac.prot.gomach.gomach_info |= GNRC_GOMACH_INTERNAL_INFO_ENTER_NEW_CYCLE; + } + else { + netif->mac.prot.gomach.gomach_info &= ~GNRC_GOMACH_INTERNAL_INFO_ENTER_NEW_CYCLE; + } +} + +/** + * @brief Get the @ref GNRC_GOMACH_INTERNAL_INFO_ENTER_NEW_CYCLE flag of the device. + * + * @param[in] netif the network interface. + * + * @return true if node has entered a new cycle. + * @return false if node hasn't entered a new cycle yet. + */ +static inline bool gnrc_gomach_get_enter_new_cycle(gnrc_netif_t *netif) +{ + return (netif->mac.prot.gomach.gomach_info & GNRC_GOMACH_INTERNAL_INFO_ENTER_NEW_CYCLE); +} + +/** + * @brief Set the @ref GNRC_GOMACH_INTERNAL_INFO_GOT_PREAMBLEACK flag of the device. + * + * @param[in,out] netif the network interface. + * @param[in] got value for GoMacH's + * @ref GNRC_GOMACH_INTERNAL_INFO_GOT_PREAMBLEACK flag. + * + */ +static inline void gnrc_gomach_set_got_preamble_ack(gnrc_netif_t *netif, bool got) +{ + if (got) { + netif->mac.prot.gomach.gomach_info |= GNRC_GOMACH_INTERNAL_INFO_GOT_PREAMBLEACK; + } + else { + netif->mac.prot.gomach.gomach_info &= ~GNRC_GOMACH_INTERNAL_INFO_GOT_PREAMBLEACK; + } +} + +/** + * @brief Get the @ref GNRC_GOMACH_INTERNAL_INFO_GOT_PREAMBLEACK flag of the device. + * + * @param[in] netif the network interface. + * + * @return true if node has got preamble-ACK. + * @return false if node hasn't got preamble-ACK yet. + */ +static inline bool gnrc_gomach_get_got_preamble_ack(gnrc_netif_t *netif) +{ + return (netif->mac.prot.gomach.gomach_info & GNRC_GOMACH_INTERNAL_INFO_GOT_PREAMBLEACK); +} + +/** + * @brief Set the @ref GNRC_GOMACH_INTERNAL_INFO_ON_PUBCHAN_1 flag of the device. + * + * @param[in,out] netif the network interface. + * @param[in] on_pubchan_1 value for GoMacH's + * @ref GNRC_GOMACH_INTERNAL_INFO_ON_PUBCHAN_1 flag. + * + */ +static inline void gnrc_gomach_set_on_pubchan_1(gnrc_netif_t *netif, bool on_pubchan_1) +{ + if (on_pubchan_1) { + netif->mac.prot.gomach.gomach_info |= GNRC_GOMACH_INTERNAL_INFO_ON_PUBCHAN_1; + } + else { + netif->mac.prot.gomach.gomach_info &= ~GNRC_GOMACH_INTERNAL_INFO_ON_PUBCHAN_1; + } +} + +/** + * @brief Get the @ref GNRC_GOMACH_INTERNAL_INFO_ON_PUBCHAN_1 flag of the device. + * + * @param[in] netif the network interface. + * + * @return true if node is on public channel 1. + * @return false if node is not on public channel 1. + */ +static inline bool gnrc_gomach_get_on_pubchan_1(gnrc_netif_t *netif) +{ + return (netif->mac.prot.gomach.gomach_info & GNRC_GOMACH_INTERNAL_INFO_ON_PUBCHAN_1); +} + +/** + * @brief Set the @ref GNRC_GOMACH_INTERNAL_INFO_MAX_PREAM_INTERV flag of the device. + * + * @param[in,out] netif the network interface. + * @param[in] max value for GoMacH's @ref + * GNRC_GOMACH_INTERNAL_INFO_MAX_PREAM_INTERV flag. + * + */ +static inline void gnrc_gomach_set_max_pream_interv(gnrc_netif_t *netif, bool max) +{ + if (max) { + netif->mac.prot.gomach.gomach_info |= GNRC_GOMACH_INTERNAL_INFO_MAX_PREAM_INTERV; + } + else { + netif->mac.prot.gomach.gomach_info &= ~GNRC_GOMACH_INTERNAL_INFO_MAX_PREAM_INTERV; + } +} + +/** + * @brief Get the @ref GNRC_GOMACH_INTERNAL_INFO_MAX_PREAM_INTERV flag of the device. + * + * @param[in] netif the network interface. + * + * @return true if node has reached maximum preamble interval. + * @return false if node hasn't reached maximum preamble interval yet. + */ +static inline bool gnrc_gomach_get_max_pream_interv(gnrc_netif_t *netif) +{ + return (netif->mac.prot.gomach.gomach_info & GNRC_GOMACH_INTERNAL_INFO_MAX_PREAM_INTERV); +} + +/** + * @brief Get device's current phase. + * + * @param[in] netif the network interface. + * + * @return device's current phase. + */ +uint64_t gnrc_gomach_phase_now(gnrc_netif_t *netif); + +/** + * @brief Shortcut to set the state of netdev + * + * @param[in] netif ptr to the network interface + * @param[in] devstate new state for netdev + */ +void gnrc_gomach_set_netdev_state(gnrc_netif_t *netif, netopt_state_t devstate); + +/** + * @brief Set the auto-ACK parameter of the device. + * + * @param[in,out] netif the network interface. + * @param[in] autoack value for the auto-ACK parameter. + * + */ +static inline void gnrc_gomach_set_autoack(gnrc_netif_t *netif, netopt_enable_t autoack) +{ + assert(netif != NULL); + + netif->dev->driver->set(netif->dev, + NETOPT_AUTOACK, + &autoack, + sizeof(autoack)); +} + +/** + * @brief Set the ACK-require parameter of the device. + * + * @param[in,out] netif the network interface. + * @param[in] ack_req value for the ACK-require parameter. + * + */ +static inline void gnrc_gomach_set_ack_req(gnrc_netif_t *netif, netopt_enable_t ack_req) +{ + assert(netif != NULL); + + netif->dev->driver->set(netif->dev, + NETOPT_ACK_REQ, + &ack_req, + sizeof(ack_req)); +} + +/** + * @brief Shortcut to get the state of netdev. + * + * @param[in] netif the network interface. + * + * @return state of netdev upon success. + * @return -ENOSYS, upon failure. + */ +static inline netopt_state_t gnrc_gomach_get_netdev_state(gnrc_netif_t *netif) +{ + assert(netif != NULL); + + netopt_state_t state; + + if (0 < netif->dev->driver->get(netif->dev, + NETOPT_STATE, + &state, + sizeof(state))) { + return state; + } + return -ENOSYS; +} + +/** + * @brief Turn the radio to a specific channel. + * + * @param[in,out] netif the network interface. + * @param[in] channel_num targeted channel number to turn to. + * + */ +static inline void gnrc_gomach_turn_channel(gnrc_netif_t *netif, uint16_t channel_num) +{ + assert(netif != NULL); + + netif->dev->driver->set(netif->dev, + NETOPT_CHANNEL, + &channel_num, + sizeof(channel_num)); +} + +/** + * @brief send a @ref net_gnrc_pkt "packet" over the network interface in GoMacH + * + * @internal + * + * @pre `netif != NULL && pkt != NULL` + * + * @note The function re-formats the content of @p pkt to a format expected + * by the netdev_driver_t::send() method of gnrc_netif_t::dev and + * releases the packet before returning (so no additional release + * should be required after calling this method). + * + * @param[in] netif The network interface. + * @param[in] pkt A packet to send. + * + * @return The number of bytes actually sent on success + * @return -EBADMSG, if the @ref net_gnrc_netif_hdr in @p pkt is missing + * or is in an unexpected format. + * @return -ENOTSUP, if sending @p pkt in the given format isn't supported + * (e.g. empty payload with Ethernet). + * @return Any negative error code reported by gnrc_netif_t::dev. + */ +int _gnrc_gomach_transmit(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt); + +/** + * @brief Check if the received packet is a duplicate packet. + * + * @param[in] netif the network interface. + * @param[in] pa_info ptr to received packet's parsed information. + * + * @return true if the received packet is a duplicate packet. + * @return false if the received packet is not a duplicate packet. + */ +bool gnrc_gomach_check_duplicate(gnrc_netif_t *netif, gnrc_gomach_packet_info_t *pa_info); + +/** + * @brief Send a pktsnip in GoMacH. + * + * @param[in] netif the network interface. + * @param[in] pkt ptr to the packet for sending. + * @param[in] csma_enable value of csma-enable parameter. + * + * @return >0 upon sending success. + * @return 0< upon sending failure. + */ +int gnrc_gomach_send(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt, netopt_enable_t csma_enable); + +/** + * @brief Reply a preamble-ACK packet in GoMacH. + * + * @param[in] netif the network interface. + * @param[in] info ptr to the info of the preamble packet. + * + * @return >0 upon sending success. + * @return 0< upon sending failure. + */ +int gnrc_gomach_send_preamble_ack(gnrc_netif_t *netif, gnrc_gomach_packet_info_t *info); + +/** + * @brief Broadcast a beacon packet in GoMacH. + * + * @param[in] netif the network interface. + * + * @return >0 upon sending success. + * @return 0< upon sending failure. + */ +int gnrc_gomach_send_beacon(gnrc_netif_t *netif); + +/** + * @brief Store the received packet to the dispatch buffer. + * + * @param[in,out] buffer RX dispatch packet buffer + * @param[in] pkt received packet + * + * @return 0 if correctly stored + * @return <0 on error + */ +int gnrc_gomach_dispatch_defer(gnrc_pktsnip_t * buffer[], gnrc_pktsnip_t * pkt); + +/** + * @brief Update the queue-length indicator of the packet sender. + * + * @param[in,out] netif the network interface. + * @param[in] pkt received packet + * @param[in] pa_info ptr to the info of the received packet. + * + */ +void gnrc_gomach_indicator_update(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt, + gnrc_gomach_packet_info_t *pa_info); + +/** + * @brief Process packets received during the CP (wake-up) period of GoMacH. + * + * @param[in,out] netif the network interface. + * + */ +void gnrc_gomach_cp_packet_process(gnrc_netif_t *netif); + +/** + * @brief Choose a sub-channel for a device running GoMacH. + * + * @param[in,out] netif the network interface. + * + */ +void gnrc_gomach_init_choose_subchannel(gnrc_netif_t *netif); + +/** + * @brief Broadcast the chosen sub-channel sequence to the device's neighbors. + * + * @param[in] netif the network interface. + * @param[in] use_csma value of csma-enable parameter. + * + * @return >0 upon sending success. + * @return 0< upon sending failure. + */ +int gnrc_gomach_bcast_subchann_seq(gnrc_netif_t *netif, netopt_enable_t use_csma); + +/** + * @brief Send a preamble packet to the targeted neighbor. + * + * @param[in] netif the network interface. + * @param[in] csma_enable value of csma-enable parameter. + * + * @return >0 upon sending success. + * @return 0< upon sending failure. + */ +int gnrc_gomach_send_preamble(gnrc_netif_t *netif, netopt_enable_t csma_enable); + +/** + * @brief Process the received preamble-ACK packet to get phase-locked with the sender. + * + * @param[in,out] netif the network interface. + * @param[in] pkt ptr to the received preamble-ACK. + * + */ +void gnrc_gomach_process_preamble_ack(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt); + +/** + * @brief Process the received packets to when waiting for the preamble-ACK packet. + * + * @param[in,out] netif the network interface. + * + */ +void gnrc_gomach_process_pkt_in_wait_preamble_ack(gnrc_netif_t *netif); + +/** + * @brief Send a data packet to the targeted neighbor. + * + * @param[in,out] netif the network interface. + * @param[in] csma_enable value of csma-enable parameter. + * + * @return >0 upon sending success. + * @return 0< upon sending failure. + */ +int gnrc_gomach_send_data(gnrc_netif_t *netif, netopt_enable_t csma_enable); + +/** + * @brief Find a neighbor that is next to send packet to. + * + * @param[in,out] netif the network interface. + * + * @return true, if found next TX neighbor. + * @return false, if not found next TX neighbor. + * + */ +bool gnrc_gomach_find_next_tx_neighbor(gnrc_netif_t *netif); + +/** + * @brief Process the received beacon packet. + * + * @param[in,out] netif the network interface. + * @param[in] pkt ptr to the received beacon. + * + */ +void gnrc_gomach_beacon_process(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt); + +/** + * @brief Process the received packets when waiting for the beacon during + * t2k procedure in GoMacH. + * + * @param[in,out] netif the network interface. + * + */ +void gnrc_gomach_packet_process_in_wait_beacon(gnrc_netif_t *netif); + +/** + * @brief Process the received packets in the vTDMA period in GoMacH. + * + * @param[in,out] netif the network interface. + * + */ +void gnrc_gomach_packet_process_in_vtdma(gnrc_netif_t *netif); + +/** + * @brief Update the TX neighbors' phases in GoMacH. + * + * @param[in,out] netif the network interface. + * + */ +void gnrc_gomach_update_neighbor_phase(gnrc_netif_t *netif); + +/** + * @brief Update the TX neighbors' public channel phase in GoMacH. + * + * @param[in,out] netif the network interface. + * + */ +void gnrc_gomach_update_neighbor_pubchan(gnrc_netif_t *netif); + +#ifdef __cplusplus +} +#endif + +#endif /* GOMACH_INTERNAL_H */ +/** @} */ diff --git a/sys/net/gnrc/link_layer/gomach/timeout.c b/sys/net/gnrc/link_layer/gomach/timeout.c new file mode 100644 index 0000000000000000000000000000000000000000..686d9b82a956eb501d9e36e20fb1652866ab90f1 --- /dev/null +++ b/sys/net/gnrc/link_layer/gomach/timeout.c @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2017 INRIA + * + * 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 net_gnrc_gomach + * @{ + * + * @file + * @brief Implementation of GoMacH's timeout module. + * + * @author Shuguo Zhuo <shuguo.zhuo@inria.fr> + * @} + */ + +#include "xtimer.h" +#include "net/gnrc/gomach/gomach.h" +#include "net/gnrc/gomach/timeout.h" +#include "net/gnrc/gomach/types.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +/* Return index >= 0 if found, -ENONENT if not found. */ +static int _gomach_find_timeout(gnrc_gomach_t *gomach, gnrc_gomach_timeout_type_t type) +{ + assert(gomach); + + for (unsigned i = 0; i < GNRC_GOMACH_TIMEOUT_COUNT; i++) { + if (gomach->timeouts[i].type == type) { + return i; + } + } + return -ENOENT; +} + +inline bool gnrc_gomach_timeout_is_running(gnrc_netif_t *netif, + gnrc_gomach_timeout_type_t type) +{ + assert(netif); + return (_gomach_find_timeout(&netif->mac.prot.gomach, type) >= 0); +} + +bool gnrc_gomach_timeout_is_expired(gnrc_netif_t *netif, gnrc_gomach_timeout_type_t type) +{ + assert(netif); + + int index = _gomach_find_timeout(&netif->mac.prot.gomach, type); + if (index >= 0) { + if (netif->mac.prot.gomach.timeouts[index].expired) { + xtimer_remove(&(netif->mac.prot.gomach.timeouts[index].timer)); + netif->mac.prot.gomach.timeouts[index].type = GNRC_GOMACH_TIMEOUT_DISABLED; + } + return netif->mac.prot.gomach.timeouts[index].expired; + } + return false; +} + +gnrc_gomach_timeout_t *_gomach_acquire_timeout(gnrc_netif_t *netif, + gnrc_gomach_timeout_type_t type) +{ + assert(netif); + + if (gnrc_gomach_timeout_is_running(netif, type)) { + return NULL; + } + + for (unsigned i = 0; i < GNRC_GOMACH_TIMEOUT_COUNT; i++) { + if (netif->mac.prot.gomach.timeouts[i].type == GNRC_GOMACH_TIMEOUT_DISABLED) { + netif->mac.prot.gomach.timeouts[i].type = type; + return &netif->mac.prot.gomach.timeouts[i]; + } + } + return NULL; +} + +void gnrc_gomach_clear_timeout(gnrc_netif_t *netif, gnrc_gomach_timeout_type_t type) +{ + assert(netif); + + int index = _gomach_find_timeout(&netif->mac.prot.gomach, type); + if (index >= 0) { + xtimer_remove(&(netif->mac.prot.gomach.timeouts[index].timer)); + netif->mac.prot.gomach.timeouts[index].type = GNRC_GOMACH_TIMEOUT_DISABLED; + netif->mac.prot.gomach.timeouts[index].expired = false; + } +} + +void gnrc_gomach_set_timeout(gnrc_netif_t *netif, + gnrc_gomach_timeout_type_t type, + uint32_t offset) +{ + assert(netif); + + gnrc_gomach_timeout_t *timeout; + if ((timeout = _gomach_acquire_timeout(netif, type))) { + timeout->expired = false; + timeout->msg.type = GNRC_GOMACH_EVENT_TIMEOUT_TYPE; + timeout->msg.content.ptr = (void *) timeout; + xtimer_set_msg(&(timeout->timer), offset, + &(timeout->msg), netif->pid); + } + else { + DEBUG("[GoMacH]: Cannot set timeout, too many concurrent timeouts\n"); + } +} + +void gnrc_gomach_reset_timeouts(gnrc_netif_t *netif) +{ + assert(netif); + + for (unsigned i = 0; i < GNRC_GOMACH_TIMEOUT_COUNT; i++) { + if (netif->mac.prot.gomach.timeouts[i].type != GNRC_GOMACH_TIMEOUT_DISABLED) { + xtimer_remove(&(netif->mac.prot.gomach.timeouts[i].timer)); + netif->mac.prot.gomach.timeouts[i].type = GNRC_GOMACH_TIMEOUT_DISABLED; + } + } +} diff --git a/sys/net/gnrc/link_layer/lwmac/include/lwmac_internal.h b/sys/net/gnrc/link_layer/lwmac/include/lwmac_internal.h index 9d244f7ca0813b6e0d18a93c780310cdecf0025e..0949e53ae026fd84a52ead29ce8eb9fe1887412d 100644 --- a/sys/net/gnrc/link_layer/lwmac/include/lwmac_internal.h +++ b/sys/net/gnrc/link_layer/lwmac/include/lwmac_internal.h @@ -227,10 +227,10 @@ static inline bool gnrc_lwmac_get_quit_rx(gnrc_netif_t *netif) static inline void gnrc_lwmac_set_dutycycle_active(gnrc_netif_t *netif, bool active) { if (active) { - netif->mac.lwmac.lwmac_info |= GNRC_LWMAC_DUTYCYCLE_ACTIVE; + netif->mac.prot.lwmac.lwmac_info |= GNRC_LWMAC_DUTYCYCLE_ACTIVE; } else { - netif->mac.lwmac.lwmac_info &= ~GNRC_LWMAC_DUTYCYCLE_ACTIVE; + netif->mac.prot.lwmac.lwmac_info &= ~GNRC_LWMAC_DUTYCYCLE_ACTIVE; } } @@ -244,7 +244,7 @@ static inline void gnrc_lwmac_set_dutycycle_active(gnrc_netif_t *netif, bool act */ static inline bool gnrc_lwmac_get_dutycycle_active(gnrc_netif_t *netif) { - return (netif->mac.lwmac.lwmac_info & GNRC_LWMAC_DUTYCYCLE_ACTIVE); + return (netif->mac.prot.lwmac.lwmac_info & GNRC_LWMAC_DUTYCYCLE_ACTIVE); } /** @@ -257,10 +257,10 @@ static inline bool gnrc_lwmac_get_dutycycle_active(gnrc_netif_t *netif) static inline void gnrc_lwmac_set_reschedule(gnrc_netif_t *netif, bool reschedule) { if (reschedule) { - netif->mac.lwmac.lwmac_info |= GNRC_LWMAC_NEEDS_RESCHEDULE; + netif->mac.prot.lwmac.lwmac_info |= GNRC_LWMAC_NEEDS_RESCHEDULE; } else { - netif->mac.lwmac.lwmac_info &= ~GNRC_LWMAC_NEEDS_RESCHEDULE; + netif->mac.prot.lwmac.lwmac_info &= ~GNRC_LWMAC_NEEDS_RESCHEDULE; } } @@ -274,7 +274,7 @@ static inline void gnrc_lwmac_set_reschedule(gnrc_netif_t *netif, bool reschedul */ static inline bool gnrc_lwmac_get_reschedule(gnrc_netif_t *netif) { - return (netif->mac.lwmac.lwmac_info & GNRC_LWMAC_NEEDS_RESCHEDULE); + return (netif->mac.prot.lwmac.lwmac_info & GNRC_LWMAC_NEEDS_RESCHEDULE); } /** diff --git a/sys/net/gnrc/link_layer/lwmac/lwmac.c b/sys/net/gnrc/link_layer/lwmac/lwmac.c index 0e990015be9bcdd9b69174653fcf93243c238c84..e59f8bac37c2185e7258f613cb09a90c55902a4b 100644 --- a/sys/net/gnrc/link_layer/lwmac/lwmac.c +++ b/sys/net/gnrc/link_layer/lwmac/lwmac.c @@ -245,7 +245,7 @@ inline void lwmac_schedule_update(gnrc_netif_t *netif) void lwmac_set_state(gnrc_netif_t *netif, gnrc_lwmac_state_t newstate) { - gnrc_lwmac_state_t oldstate = netif->mac.lwmac.state; + gnrc_lwmac_state_t oldstate = netif->mac.prot.lwmac.state; if (newstate == oldstate) { return; @@ -257,7 +257,7 @@ void lwmac_set_state(gnrc_netif_t *netif, gnrc_lwmac_state_t newstate) } /* Already change state, but might be reverted to oldstate when needed */ - netif->mac.lwmac.state = newstate; + netif->mac.prot.lwmac.state = newstate; /* Actions when leaving old state */ switch (oldstate) { @@ -269,8 +269,8 @@ void lwmac_set_state(gnrc_netif_t *netif, gnrc_lwmac_state_t newstate) /* Output duty-cycle ratio */ uint64_t duty; duty = (uint64_t) rtt_get_counter(); - duty = ((uint64_t) netif->mac.lwmac.awake_duration_sum_ticks) * 100 / - (duty - (uint64_t)netif->mac.lwmac.system_start_time_ticks); + duty = ((uint64_t) netif->mac.prot.lwmac.awake_duration_sum_ticks) * 100 / + (duty - (uint64_t)netif->mac.prot.lwmac.system_start_time_ticks); printf("[LWMAC]: achieved duty-cycle: %lu %% \n", (uint32_t)duty); #endif break; @@ -305,8 +305,8 @@ void lwmac_set_state(gnrc_netif_t *netif, gnrc_lwmac_state_t newstate) RTT_US_TO_TICKS(GNRC_LWMAC_WAKEUP_INTERVAL_US - (3 * GNRC_LWMAC_WAKEUP_DURATION_US / 2))); LOG_WARNING("WARNING: [LWMAC] phase backoffed: %lu us\n", RTT_TICKS_TO_US(alarm)); - netif->mac.lwmac.last_wakeup = netif->mac.lwmac.last_wakeup + alarm; - alarm = _next_inphase_event(netif->mac.lwmac.last_wakeup, + netif->mac.prot.lwmac.last_wakeup = netif->mac.prot.lwmac.last_wakeup + alarm; + alarm = _next_inphase_event(netif->mac.prot.lwmac.last_wakeup, RTT_US_TO_TICKS(GNRC_LWMAC_WAKEUP_INTERVAL_US)); rtt_set_alarm(alarm, rtt_cb, (void *) GNRC_LWMAC_EVENT_RTT_WAKEUP_PENDING); } @@ -441,12 +441,12 @@ static void _rx_management_failed(gnrc_netif_t *netif) /* Here we check if we are close to the end of the cycle. If yes, * go to sleep. Firstly, get the relative phase. */ uint32_t phase = rtt_get_counter(); - if (phase < netif->mac.lwmac.last_wakeup) { - phase = (RTT_US_TO_TICKS(GNRC_LWMAC_PHASE_MAX) - netif->mac.lwmac.last_wakeup) + + if (phase < netif->mac.prot.lwmac.last_wakeup) { + phase = (RTT_US_TO_TICKS(GNRC_LWMAC_PHASE_MAX) - netif->mac.prot.lwmac.last_wakeup) + phase; } else { - phase = phase - netif->mac.lwmac.last_wakeup; + phase = phase - netif->mac.prot.lwmac.last_wakeup; } /* If the relative phase is beyond 4/5 cycle time, go to sleep. */ if (phase > (4 * RTT_US_TO_TICKS(GNRC_LWMAC_WAKEUP_INTERVAL_US) / 5)) { @@ -472,12 +472,12 @@ static void _rx_management_success(gnrc_netif_t *netif) /* Here we check if we are close to the end of the cycle. If yes, * go to sleep. Firstly, get the relative phase. */ uint32_t phase = rtt_get_counter(); - if (phase < netif->mac.lwmac.last_wakeup) { - phase = (RTT_US_TO_TICKS(GNRC_LWMAC_PHASE_MAX) - netif->mac.lwmac.last_wakeup) + + if (phase < netif->mac.prot.lwmac.last_wakeup) { + phase = (RTT_US_TO_TICKS(GNRC_LWMAC_PHASE_MAX) - netif->mac.prot.lwmac.last_wakeup) + phase; } else { - phase = phase - netif->mac.lwmac.last_wakeup; + phase = phase - netif->mac.prot.lwmac.last_wakeup; } /* If the relative phase is beyond 4/5 cycle time, go to sleep. */ if (phase > (4 * RTT_US_TO_TICKS(GNRC_LWMAC_WAKEUP_INTERVAL_US) / 5)) { @@ -619,7 +619,7 @@ static void _lwmac_update_listening(gnrc_netif_t *netif) /* Dispatch first as there still may be broadcast packets. */ gnrc_mac_dispatch(&netif->mac.rx); - netif->mac.lwmac.state = GNRC_LWMAC_SLEEPING; + netif->mac.prot.lwmac.state = GNRC_LWMAC_SLEEPING; /* Enable duty cycling again */ rtt_handler(GNRC_LWMAC_EVENT_RTT_RESUME, netif); @@ -648,7 +648,7 @@ static bool lwmac_update(gnrc_netif_t *netif) { gnrc_lwmac_set_reschedule(netif, false); - switch (netif->mac.lwmac.state) { + switch (netif->mac.prot.lwmac.state) { case GNRC_LWMAC_SLEEPING: { /* Quit scheduling transmission if 'quit-tx' flag is found set, thus * to avoid potential collisions with ongoing transmissions of other @@ -673,7 +673,7 @@ static bool lwmac_update(gnrc_netif_t *netif) break; } default: - LOG_DEBUG("[LWMAC] No actions in state %u\n", netif->mac.lwmac.state); + LOG_DEBUG("[LWMAC] No actions in state %u\n", netif->mac.prot.lwmac.state); } return gnrc_lwmac_get_reschedule(netif); @@ -699,8 +699,8 @@ void rtt_handler(uint32_t event, gnrc_netif_t *netif) switch (event & 0xffff) { case GNRC_LWMAC_EVENT_RTT_WAKEUP_PENDING: { /* A new cycle starts, set sleep timing and initialize related MAC-info flags. */ - netif->mac.lwmac.last_wakeup = rtt_get_alarm(); - alarm = _next_inphase_event(netif->mac.lwmac.last_wakeup, + netif->mac.prot.lwmac.last_wakeup = rtt_get_alarm(); + alarm = _next_inphase_event(netif->mac.prot.lwmac.last_wakeup, RTT_US_TO_TICKS(GNRC_LWMAC_WAKEUP_DURATION_US)); rtt_set_alarm(alarm, rtt_cb, (void *) GNRC_LWMAC_EVENT_RTT_SLEEP_PENDING); gnrc_lwmac_set_quit_tx(netif, false); @@ -712,7 +712,7 @@ void rtt_handler(uint32_t event, gnrc_netif_t *netif) } case GNRC_LWMAC_EVENT_RTT_SLEEP_PENDING: { /* Set next wake-up timing. */ - alarm = _next_inphase_event(netif->mac.lwmac.last_wakeup, + alarm = _next_inphase_event(netif->mac.prot.lwmac.last_wakeup, RTT_US_TO_TICKS(GNRC_LWMAC_WAKEUP_INTERVAL_US)); rtt_set_alarm(alarm, rtt_cb, (void *) GNRC_LWMAC_EVENT_RTT_WAKEUP_PENDING); lwmac_set_state(netif, GNRC_LWMAC_SLEEPING); @@ -730,14 +730,14 @@ void rtt_handler(uint32_t event, gnrc_netif_t *netif) case GNRC_LWMAC_EVENT_RTT_PAUSE: { rtt_clear_alarm(); LOG_DEBUG("[LWMAC] RTT: Stop duty cycling, now in state %u\n", - netif->mac.lwmac.state); + netif->mac.prot.lwmac.state); gnrc_lwmac_set_dutycycle_active(netif, false); break; } case GNRC_LWMAC_EVENT_RTT_RESUME: { LOG_DEBUG("[LWMAC] RTT: Resume duty cycling\n"); rtt_clear_alarm(); - alarm = _next_inphase_event(netif->mac.lwmac.last_wakeup, + alarm = _next_inphase_event(netif->mac.prot.lwmac.last_wakeup, RTT_US_TO_TICKS(GNRC_LWMAC_WAKEUP_INTERVAL_US)); rtt_set_alarm(alarm, rtt_cb, (void *) GNRC_LWMAC_EVENT_RTT_WAKEUP_PENDING); gnrc_lwmac_set_dutycycle_active(netif, true); @@ -934,9 +934,9 @@ static void _lwmac_init(gnrc_netif_t *netif) #if (GNRC_LWMAC_ENABLE_DUTYCYLE_RECORD == 1) /* Start duty cycle recording */ - netif->mac.lwmac.system_start_time_ticks = rtt_get_counter(); - netif->mac.lwmac.last_radio_on_time_ticks = netif->mac.lwmac.system_start_time_ticks; - netif->mac.lwmac.awake_duration_sum_ticks = 0; - netif->mac.lwmac.lwmac_info |= GNRC_LWMAC_RADIO_IS_ON; + netif->mac.prot.lwmac.system_start_time_ticks = rtt_get_counter(); + netif->mac.prot.lwmac.last_radio_on_time_ticks = netif->mac.prot.lwmac.system_start_time_ticks; + netif->mac.prot.lwmac.awake_duration_sum_ticks = 0; + netif->mac.prot.lwmac.lwmac_info |= GNRC_LWMAC_RADIO_IS_ON; #endif } diff --git a/sys/net/gnrc/link_layer/lwmac/lwmac_internal.c b/sys/net/gnrc/link_layer/lwmac/lwmac_internal.c index 34a5e16b74007e0923eb13aefa4df1ad8b696463..a4668e746ca136d292eaac339b69c139ced8a9d1 100644 --- a/sys/net/gnrc/link_layer/lwmac/lwmac_internal.c +++ b/sys/net/gnrc/link_layer/lwmac/lwmac_internal.c @@ -198,21 +198,21 @@ void _gnrc_lwmac_set_netdev_state(gnrc_netif_t *netif, netopt_state_t devstate) #if (GNRC_LWMAC_ENABLE_DUTYCYLE_RECORD == 1) if (devstate == NETOPT_STATE_IDLE) { - if (!(netif->mac.lwmac.lwmac_info & GNRC_LWMAC_RADIO_IS_ON)) { - netif->mac.lwmac.last_radio_on_time_ticks = rtt_get_counter(); - netif->mac.lwmac.lwmac_info |= GNRC_LWMAC_RADIO_IS_ON; + if (!(netif->mac.prot.lwmac.lwmac_info & GNRC_LWMAC_RADIO_IS_ON)) { + netif->mac.prot.lwmac.last_radio_on_time_ticks = rtt_get_counter(); + netif->mac.prot.lwmac.lwmac_info |= GNRC_LWMAC_RADIO_IS_ON; } return; } else if ((devstate == NETOPT_STATE_SLEEP) && - (netif->mac.lwmac.lwmac_info & GNRC_LWMAC_RADIO_IS_ON)) { - netif->mac.lwmac.radio_off_time_ticks = rtt_get_counter(); + (netif->mac.prot.lwmac.lwmac_info & GNRC_LWMAC_RADIO_IS_ON)) { + netif->mac.prot.lwmac.radio_off_time_ticks = rtt_get_counter(); - netif->mac.lwmac.awake_duration_sum_ticks += - (netif->mac.lwmac.radio_off_time_ticks - - netif->mac.lwmac.last_radio_on_time_ticks); + netif->mac.prot.lwmac.awake_duration_sum_ticks += + (netif->mac.prot.lwmac.radio_off_time_ticks - + netif->mac.prot.lwmac.last_radio_on_time_ticks); - netif->mac.lwmac.lwmac_info &= ~GNRC_LWMAC_RADIO_IS_ON; + netif->mac.prot.lwmac.lwmac_info &= ~GNRC_LWMAC_RADIO_IS_ON; } #endif } diff --git a/sys/net/gnrc/link_layer/lwmac/rx_state_machine.c b/sys/net/gnrc/link_layer/lwmac/rx_state_machine.c index fa7854175e8ee2d6b6f6b08612c48f967cba6855..6d77f39d88aa556a641d23744bbc4b93b5b8a840 100644 --- a/sys/net/gnrc/link_layer/lwmac/rx_state_machine.c +++ b/sys/net/gnrc/link_layer/lwmac/rx_state_machine.c @@ -138,13 +138,13 @@ static bool _send_wa(gnrc_netif_t *netif) /* Embed the current 'relative phase timing' (counted from the start of this cycle) * of the receiver into its WA packet, thus to allow the sender to infer the * receiver's exact wake-up timing */ - if (phase_now > _gnrc_lwmac_ticks_to_phase(netif->mac.lwmac.last_wakeup)) { + if (phase_now > _gnrc_lwmac_ticks_to_phase(netif->mac.prot.lwmac.last_wakeup)) { lwmac_hdr.current_phase = (phase_now - - _gnrc_lwmac_ticks_to_phase(netif->mac.lwmac.last_wakeup)); + _gnrc_lwmac_ticks_to_phase(netif->mac.prot.lwmac.last_wakeup)); } else { lwmac_hdr.current_phase = (phase_now + RTT_US_TO_TICKS(GNRC_LWMAC_WAKEUP_INTERVAL_US)) - - _gnrc_lwmac_ticks_to_phase(netif->mac.lwmac.last_wakeup); + _gnrc_lwmac_ticks_to_phase(netif->mac.prot.lwmac.last_wakeup); } pkt = gnrc_pktbuf_add(NULL, &lwmac_hdr, sizeof(lwmac_hdr), GNRC_NETTYPE_LWMAC); diff --git a/sys/net/gnrc/link_layer/lwmac/timeout.c b/sys/net/gnrc/link_layer/lwmac/timeout.c index 6b3f8d42d38762aace9b6aa66278b87fd7f82369..ed0fd70bdc29ba07634279ba5d75d41860afd951 100644 --- a/sys/net/gnrc/link_layer/lwmac/timeout.c +++ b/sys/net/gnrc/link_layer/lwmac/timeout.c @@ -62,19 +62,19 @@ inline bool gnrc_lwmac_timeout_is_running(gnrc_netif_t *netif, gnrc_lwmac_timeout_type_t type) { assert(netif); - return (_lwmac_find_timeout(&netif->mac.lwmac, type) >= 0); + return (_lwmac_find_timeout(&netif->mac.prot.lwmac, type) >= 0); } bool gnrc_lwmac_timeout_is_expired(gnrc_netif_t *netif, gnrc_lwmac_timeout_type_t type) { assert(netif); - int index = _lwmac_find_timeout(&netif->mac.lwmac, type); + int index = _lwmac_find_timeout(&netif->mac.prot.lwmac, type); if (index >= 0) { - if (netif->mac.lwmac.timeouts[index].expired) { - _lwmac_clear_timeout(&netif->mac.lwmac.timeouts[index]); + if (netif->mac.prot.lwmac.timeouts[index].expired) { + _lwmac_clear_timeout(&netif->mac.prot.lwmac.timeouts[index]); } - return netif->mac.lwmac.timeouts[index].expired; + return netif->mac.prot.lwmac.timeouts[index].expired; } return false; } @@ -89,9 +89,9 @@ gnrc_lwmac_timeout_t *_lwmac_acquire_timeout(gnrc_netif_t *netif, } for (unsigned i = 0; i < GNRC_LWMAC_TIMEOUT_COUNT; i++) { - if (netif->mac.lwmac.timeouts[i].type == GNRC_LWMAC_TIMEOUT_DISABLED) { - netif->mac.lwmac.timeouts[i].type = type; - return &netif->mac.lwmac.timeouts[i]; + if (netif->mac.prot.lwmac.timeouts[i].type == GNRC_LWMAC_TIMEOUT_DISABLED) { + netif->mac.prot.lwmac.timeouts[i].type = type; + return &netif->mac.prot.lwmac.timeouts[i]; } } return NULL; @@ -108,9 +108,9 @@ void gnrc_lwmac_clear_timeout(gnrc_netif_t *netif, gnrc_lwmac_timeout_type_t typ { assert(netif); - int index = _lwmac_find_timeout(&netif->mac.lwmac, type); + int index = _lwmac_find_timeout(&netif->mac.prot.lwmac, type); if (index >= 0) { - _lwmac_clear_timeout(&netif->mac.lwmac.timeouts[index]); + _lwmac_clear_timeout(&netif->mac.prot.lwmac.timeouts[index]); } } @@ -141,8 +141,8 @@ void gnrc_lwmac_reset_timeouts(gnrc_netif_t *netif) assert(netif); for (unsigned i = 0; i < GNRC_LWMAC_TIMEOUT_COUNT; i++) { - if (netif->mac.lwmac.timeouts[i].type != GNRC_LWMAC_TIMEOUT_DISABLED) { - _lwmac_clear_timeout(&netif->mac.lwmac.timeouts[i]); + if (netif->mac.prot.lwmac.timeouts[i].type != GNRC_LWMAC_TIMEOUT_DISABLED) { + _lwmac_clear_timeout(&netif->mac.prot.lwmac.timeouts[i]); } } } diff --git a/sys/net/gnrc/link_layer/lwmac/tx_state_machine.c b/sys/net/gnrc/link_layer/lwmac/tx_state_machine.c index 72e2133217c43163620dd0314bf01dfb8aac0d8d..c3e501dff4c9706a5fb356bc45baa1f186b5148a 100644 --- a/sys/net/gnrc/link_layer/lwmac/tx_state_machine.c +++ b/sys/net/gnrc/link_layer/lwmac/tx_state_machine.c @@ -316,7 +316,7 @@ static uint8_t _packet_process_in_wait_for_wa(gnrc_netif_t *netif) } uint32_t own_phase; - own_phase = _gnrc_lwmac_ticks_to_phase(netif->mac.lwmac.last_wakeup); + own_phase = _gnrc_lwmac_ticks_to_phase(netif->mac.prot.lwmac.last_wakeup); if (own_phase >= netif->mac.tx.timestamp) { own_phase = own_phase - netif->mac.tx.timestamp; @@ -464,10 +464,10 @@ static bool _send_data(gnrc_netif_t *netif) DEBUG("[LWMAC-tx]: spent %lu WR in TX\n", netif->mac.tx.wr_sent); #if (LWMAC_ENABLE_DUTYCYLE_RECORD == 1) - netif->mac.lwmac.pkt_start_sending_time_ticks = - rtt_get_counter() - netif->mac.lwmac.pkt_start_sending_time_ticks; + netif->mac.prot.lwmac.pkt_start_sending_time_ticks = + rtt_get_counter() - netif->mac.prot.lwmac.pkt_start_sending_time_ticks; DEBUG("[LWMAC-tx]: pkt sending delay in TX: %lu us\n", - RTT_TICKS_TO_US(netif->mac.lwmac.pkt_start_sending_time_ticks)); + RTT_TICKS_TO_US(netif->mac.prot.lwmac.pkt_start_sending_time_ticks)); #endif return true; @@ -492,7 +492,7 @@ void gnrc_lwmac_tx_start(gnrc_netif_t *netif, netif->mac.tx.wr_sent = 0; #if (LWMAC_ENABLE_DUTYCYLE_RECORD == 1) - netif->mac.lwmac.pkt_start_sending_time_ticks = rtt_get_counter(); + netif->mac.prot.lwmac.pkt_start_sending_time_ticks = rtt_get_counter(); #endif } diff --git a/tests/gomach/Makefile b/tests/gomach/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..0d07a58b2675366920491ddc18c34d6d741c0714 --- /dev/null +++ b/tests/gomach/Makefile @@ -0,0 +1,49 @@ +# name of your application +APPLICATION = gomach + +# This has to be the absolute path to the RIOT base directory: +RIOTBASE ?= $(CURDIR)/../.. + +# If no BOARD is found in the environment, use this default: +BOARD ?= samr21-xpro + +# Currently, GoMacH has only been tested and evaluated through on samr21-xpro and iotlab-m3 +# nodes. Once GoMacH has also been tested through on other devices, the whitelist should +# be then accordingly extended. +BOARD_WHITELIST := samr21-xpro iotlab-m3 + +# Comment this out to disable code in RIOT that does safety checking +# which is not needed in a production environment but helps in the +# development process: +CFLAGS += -DDEVELHELP + +# Modules to include: +USEMODULE += shell +USEMODULE += shell_commands +USEMODULE += ps +# include and auto-initialize all available sensors +USEMODULE += saul_default + +# Use modules for networking +# gnrc is a meta module including all required, basic gnrc networking modules +USEMODULE += gnrc +# use the default network interface for the board +USEMODULE += gnrc_netdev_default +# automatically initialize the network interface +USEMODULE += auto_init_gnrc_netif +# shell command to send L2 packets with a simple string +USEMODULE += gnrc_txtsnd +# the application dumps received packets to stdout +USEMODULE += gnrc_pktdump +# Use GoMacH as the MAC protocol +USEMODULE += gnrc_gomach + +# We use only the lower layers of the GNRC network stack, hence, we can +# reduce the size of the packet buffer a bit +CFLAGS += -DGNRC_PKTBUF_SIZE=1024 + +# Set a custom channel if needed +DEFAULT_CHANNEL ?= 26 +CFLAGS += -DIEEE802154_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL) + +include $(RIOTBASE)/Makefile.include diff --git a/tests/gomach/README.md b/tests/gomach/README.md new file mode 100644 index 0000000000000000000000000000000000000000..1fc7093fa5c1661336f948281f21eab08dbec70a --- /dev/null +++ b/tests/gomach/README.md @@ -0,0 +1,53 @@ +GoMacH test application +======================= +This application is a showcase for testing GoMacH communications. Using it +for your board, you should be able to interactively use any hardware +that is supported for communications among devices based on GoMacH. + +Usage +===== + +Build, flash and start the application: +``` +export BOARD=your_board +make +make flash +make term +``` + +The `term` make target starts a terminal emulator for your board. It +connects to a default port so you can interact with the shell, usually +that is `/dev/ttyUSB0`. If your port is named differently, the +`PORT=/dev/yourport` variable can be used to override this. + + +Example output +============== + +The `ifconfig` command will help you to configure all available network +interfaces. On an samr21 board it will print something like: +``` +2015-09-16 16:58:37,762 - INFO # ifconfig +2015-09-16 16:58:37,766 - INFO # Iface 4 HWaddr: 9e:72 Channel: 26 NID: 0x23 TX-Power: 0dBm State: IDLE CSMA Retries: 4 +2015-09-16 16:58:37,768 - INFO # Long HWaddr: 36:32:48:33:46:da:9e:72 +2015-09-16 16:58:37,769 - INFO # AUTOACK CSMA +2015-09-16 16:58:37,770 - INFO # Source address length: 2 +``` + +The `txtsnd` command allows you to send a simple string directly over the link +layer (here, it is GoMacH) using unicast or broadcast. The application will also automatically print +information about any received packet over the serial. This will look like: +``` +2015-09-16 16:59:29,187 - INFO # PKTDUMP: data received: +2015-09-16 16:59:29,189 - INFO # ~~ SNIP 0 - size: 28 byte, type: +NETTYPE_UNDEF (0) +2015-09-16 16:59:29,190 - INFO # 000000 7b 3b 3a 02 85 00 e7 fb 00 00 00 00 01 +02 5a 55 +2015-09-16 16:59:29,192 - INFO # 000010 40 42 3e 62 f2 1a 00 00 00 00 00 00 +2015-09-16 16:59:29,194 - INFO # ~~ SNIP 1 - size: 18 byte, type: +NETTYPE_NETIF (-1) +2015-09-16 16:59:29,195 - INFO # if_pid: 4 rssi: 49 lqi: 78 +2015-09-16 16:59:29,196 - INFO # src_l2addr: 5a:55:40:42:3e:62:f2:1a +2015-09-16 16:59:29,197 - INFO # dst_l2addr: ff:ff +2015-09-16 16:59:29,198 - INFO # ~~ PKT - 2 snips, total size: 46 byte +``` diff --git a/tests/gomach/main.c b/tests/gomach/main.c new file mode 100644 index 0000000000000000000000000000000000000000..21ce1c535d6eaadc7f18fd56710a0b305ee619c6 --- /dev/null +++ b/tests/gomach/main.c @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2017 INRIA + * + * 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 tests + * @{ + * + * @file + * @brief Test application for testing the GoMacH implementation + * + * @author Shuguo Zhuo <shuguo.zhuo@inria.fr> + * + * @} + */ + +#include <stdio.h> +#include <string.h> + +#include "thread.h" +#include "shell.h" +#include "shell_commands.h" +#include "net/gnrc/pktdump.h" +#include "net/gnrc.h" + +int main(void) +{ + gnrc_netreg_entry_t dump = GNRC_NETREG_ENTRY_INIT_PID(GNRC_NETREG_DEMUX_CTX_ALL, + gnrc_pktdump_pid); + gnrc_netreg_register(GNRC_NETTYPE_UNDEF, &dump); + + char line_buf[SHELL_DEFAULT_BUFSIZE]; + shell_run(NULL, line_buf, SHELL_DEFAULT_BUFSIZE); + + return 0; +}