diff --git a/Makefile.dep b/Makefile.dep
index 949924ec7281fd9acc0baf8e628b458eb2588d96..2a4fb44e1dc55c1b4cda2fa5e17d227af6277a93 100644
--- a/Makefile.dep
+++ b/Makefile.dep
@@ -524,6 +524,12 @@ ifneq (,$(filter netstats_%, $(USEMODULE)))
   USEMODULE += netstats
 endif
 
+ifneq (,$(filter gnrc_lwmac,$(USEMODULE)))
+  USEMODULE += gnrc_mac
+  USEMODULE += gnrc_netdev
+  FEATURES_REQUIRED += periph_rtt
+endif
+
 ifneq (,$(filter pthread,$(USEMODULE)))
   USEMODULE += xtimer
   USEMODULE += timex
diff --git a/sys/auto_init/netif/auto_init_at86rf2xx.c b/sys/auto_init/netif/auto_init_at86rf2xx.c
index 88ba50284ece6f2b634a9ee82251d46f667e99d2..1766b4f7c250084167ae62f22b7eecbcea06759d 100644
--- a/sys/auto_init/netif/auto_init_at86rf2xx.c
+++ b/sys/auto_init/netif/auto_init_at86rf2xx.c
@@ -23,6 +23,7 @@
 #include "board.h"
 #include "net/gnrc/netdev.h"
 #include "net/gnrc/netdev/ieee802154.h"
+#include "net/gnrc/lwmac/lwmac.h"
 #include "net/gnrc.h"
 
 #include "at86rf2xx.h"
@@ -58,11 +59,19 @@ void auto_init_at86rf2xx(void)
             LOG_ERROR("[auto_init_netif] error initializing at86rf2xx radio #%u\n", i);
         }
         else {
+#ifdef MODULE_GNRC_LWMAC
+            gnrc_lwmac_init(_at86rf2xx_stacks[i],
+                            AT86RF2XX_MAC_STACKSIZE,
+                            AT86RF2XX_MAC_PRIO,
+                            "at86rf2xx-lwmac",
+                            &gnrc_adpt[i]);
+#else
             gnrc_netdev_init(_at86rf2xx_stacks[i],
                              AT86RF2XX_MAC_STACKSIZE,
                              AT86RF2XX_MAC_PRIO,
                              "at86rf2xx",
                              &gnrc_adpt[i]);
+#endif
         }
     }
 }
diff --git a/sys/include/net/gnrc/lwmac/hdr.h b/sys/include/net/gnrc/lwmac/hdr.h
new file mode 100644
index 0000000000000000000000000000000000000000..08fd018a3f23f5009e0f894545d488ecdcdef138
--- /dev/null
+++ b/sys/include/net/gnrc/lwmac/hdr.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2015 Daniel Krebs
+ *               2016 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_lwmac
+ * @{
+ *
+ * @file
+ * @brief       Header definition LWMAC
+ * @internal
+ * @author      Daniel Krebs <github@daniel-krebs.net>
+ * @author      Shuguo Zhuo  <shuguo.zhuo@inria.fr>
+ */
+
+#ifndef NET_GNRC_LWMAC_HDR_H
+#define NET_GNRC_LWMAC_HDR_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "net/ieee802154.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief   LWMAC WR (wake-up request packet, i.e., preamble packet) frame type
+ */
+#define GNRC_LWMAC_FRAMETYPE_WR             (0x01U)
+
+/**
+ * @brief   LWMAC WA (wake-up answer packet, i.e., preamble-ACK packet) frame type
+ */
+#define GNRC_LWMAC_FRAMETYPE_WA             (0x02U)
+
+/**
+ * @brief   LWMAC data frame type
+ */
+#define GNRC_LWMAC_FRAMETYPE_DATA           (0x03U)
+
+/**
+ * @brief   LWMAC data frame type with pending data transmission request
+ */
+#define GNRC_LWMAC_FRAMETYPE_DATA_PENDING   (0x04U)
+
+/**
+ * @brief   LWMAC broadcast frame type
+ */
+#define GNRC_LWMAC_FRAMETYPE_BROADCAST      (0x05U)
+
+/**
+ * @brief   LWMAC internal L2 address structure
+ */
+typedef struct {
+    uint8_t addr[IEEE802154_LONG_ADDRESS_LEN]; /**< address of node */
+    uint8_t len;                               /**< address */
+} gnrc_lwmac_l2_addr_t;
+
+/**
+ * @brief Static initializer for l2_addr_t.
+ */
+#define GNRC_LWMAC_L2_ADDR_INITIAL      { { 0 }, 0 }
+
+/**
+ * @brief   LWMAC header
+ */
+typedef struct {
+    uint8_t type; /**< type of frame */
+} gnrc_lwmac_hdr_t;
+
+/**
+ * @brief   LWMAC WR (wake-up request packet, i.e., preamble packet) frame
+ */
+typedef struct __attribute__((packed)) {
+    gnrc_lwmac_hdr_t header;       /**< WR packet header type */
+    gnrc_lwmac_l2_addr_t dst_addr; /**< WR is broadcast, so destination address needed */
+} gnrc_lwmac_frame_wr_t;
+
+/**
+ * @brief   LWMAC WA (wake-up answer packet, i.e., preamble-ACK packet) frame
+ */
+typedef struct __attribute__((packed)) {
+    gnrc_lwmac_hdr_t header;       /**< WA packet header type */
+    gnrc_lwmac_l2_addr_t dst_addr; /**< WA is broadcast, so destination address needed */
+    uint32_t current_phase;        /**< Node's current phase value */
+} gnrc_lwmac_frame_wa_t;
+
+/**
+ * @brief   LWMAC broadcast data frame
+ */
+typedef struct __attribute__((packed)) {
+    gnrc_lwmac_hdr_t header; /**< Broadcast packet header type */
+    uint8_t seq_nr;          /**< Broadcast sequence */
+} gnrc_lwmac_frame_broadcast_t;
+
+/**
+ * @brief   LWMAC unicast data frame
+ */
+typedef struct __attribute__((packed)) {
+    gnrc_lwmac_hdr_t header; /**< Data packet header type */
+} gnrc_lwmac_frame_data_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NET_GNRC_LWMAC_HDR_H */
+/** @} */
diff --git a/sys/include/net/gnrc/lwmac/lwmac.h b/sys/include/net/gnrc/lwmac/lwmac.h
new file mode 100644
index 0000000000000000000000000000000000000000..b682da4c6e9092eb3fc7deca0bf49784e674f5f3
--- /dev/null
+++ b/sys/include/net/gnrc/lwmac/lwmac.h
@@ -0,0 +1,324 @@
+/*
+ * Copyright (C) 2015 Daniel Krebs
+ *               2016 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_lwmac Simplest possible MAC layer
+ * @ingroup     net_gnrc
+ * @brief       Lightweight MAC protocol that allows for duty cycling to save
+ *              energy.
+ *
+ * ## LWMAC implementation
+ *
+ * ## Radio duty cycling
+ * LWMAC adopts the radio duty-cycle scheme to conserve power. Namely, in each
+ * cycle period (MAC superframe), a node device wakes up for a short period of
+ * time (called listen period or wake-up period) for receiving possible incoming
+ * packets from other devices. Outside the listen period, the node device turns
+ * off its radio to conserve power.
+ *
+ * ## Phase-lock scheme
+ * LWMAC adopts the phase-lock scheme to further reduce power consumption. Each
+ * node device in LWMAC will try to record/track its Tx-neighbor's wake-up phase.
+ * This is called phase-lock. After phase-locking, the sender node will (likely)
+ * spend less preamble packets (also called WR packet, i.e., wake-up-request, in
+ * LWMAC) for initiating a hand-shaking procedure for transmitting a data packet,
+ * compared to the first time it talks to the receiver.
+ *
+ * ## Burst transmission
+ * LWMAC adopts pending-bit technique to enhance its throughput. Namely, in case
+ * of having multi packets for the receiver, a sender uses the pending-bit flag
+ * embedded in the MAC header to instruct this situation, and the buffered packets
+ * will be transmitted in a continuous sequence, back to back, to the receiver in
+ * one shot.
+ *
+ * ## Auto wake-up extension
+ * LWMAC adopts auto wake-up extension scheme based on timeout (like T-MAC). In short,
+ * when a packet is successfully received at the receiver side, the receiver will
+ * reset the wake-up timeout to extend its wake-up period for receiving more potential
+ * incoming packets. This is to be compatible with the pending-bit technique to allow
+ * the receiver to absorb more packets when needed, thus boosts the throughput.
+ *
+ * ## Simple retransmission scheme
+ * LWMAC adopts a simple retransmission scheme to enhance link reliability. The data
+ * packet will only be dropped in case the retransmission counter gets larger than
+ * @ref GNRC_LWMAC_MAX_DATA_TX_RETRIES.
+ *
+ * ## Automatic phase backoff scheme
+ * LWMAC adopts an automatic phase backoff scheme to reduce WR (preamble) collision
+ * probability. In multi-hop scenarios, let's say, nodes A <---B <----C (which is
+ * common in multi-hop data collection networks), in which B has packets for A, and
+ * C has packets for B. In case A and B's wake-up phases are too close (overlapping).
+ * Then, especially in high traffic conditions, B and C may initiate transmissions
+ * at the same time (B sends to A, and C sends to B), a link of either will be
+ * definitely interfered, leading to collisions and link throughput reduction. To
+ * this end, by using the automatic phase backoff scheme, if a sender finds its
+ * receiver's phase is too close to its own phase, it will run a backoff scheme to
+ * randomly reselect a new wake-up phase for itself.
+ *
+ * @{
+ *
+ * @file
+ * @brief       Interface definition for the LWMAC protocol
+ *
+ * @author      Daniel Krebs <github@daniel-krebs.net>
+ * @author      Shuguo Zhuo  <shuguo.zhuo@inria.fr>
+ */
+
+#ifndef NET_GNRC_LWMAC_LWMAC_H
+#define NET_GNRC_LWMAC_LWMAC_H
+
+#include "kernel_types.h"
+#include "net/gnrc/netdev.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Time between consecutive wake-ups.
+ *
+ * This macro governs power consumption, latency and throughput!
+ * In LWMAC, devices adopt duty-cycle scheme to conserve power. That is,
+ * time is divided into repeated cycles (or, superframes), and in each
+ * cycle, a node only wakes up for a 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 LWMAC. If the wake-up interval
+ * is short, nodes will wake up more frequently, which also increases
+ * the chances for receiving packets from neighbors (i.e., leads to higher
+ * throughput), but also results in higher power consumption.
+ * In LWMAC, by default, we regard the wake-up period as the beginning of a cycle.
+ */
+#ifndef GNRC_LWMAC_WAKEUP_INTERVAL_US
+#define GNRC_LWMAC_WAKEUP_INTERVAL_US        (100LU * US_PER_MS)
+#endif
+
+/**
+ * @brief The Maximum WR (preamble packet @ref gnrc_lwmac_frame_wr_t) duration time.
+ *
+ * Since LWMAC adopts duty-cycle scheme, a node only wakes up for a short
+ * period in each cycle. Thus, to probe where is the wake-up period of the
+ * receiver, a sender sends WR (preamble) packets to notice the receiver for
+ * communication. To ensure that the receiver will catch at least one WR
+ * packet in one cycle, the sender repeatedly broadcasts a stream of WR packets
+ * with the broadcast duration (preamble duration) slightly longer period than
+ * @ref GNRC_LWMAC_WAKEUP_INTERVAL_US.
+ */
+#ifndef GNRC_LWMAC_PREAMBLE_DURATION_US
+#define GNRC_LWMAC_PREAMBLE_DURATION_US      ((13LU * GNRC_LWMAC_WAKEUP_INTERVAL_US) / 10)
+#endif
+
+/**
+ * @brief Timeout to send the next WR in case no WA has been received during that
+ *        time.
+ *
+ * In LWMAC, when a sender initiates a transmission to a receiver, it starts with
+ * sending a stream of repeated WR packets with @ref GNRC_LWMAC_TIME_BETWEEN_WR_US interval
+ * between two consecutive WRs. After sending one WR (preamble) packet, the sender turns
+ * to the listen mode to receive the potential incoming WA (preamble-ACK) packet with
+ * a timeout of @ref GNRC_LWMAC_TIME_BETWEEN_WR_US. If no WA is received during
+ * @ref GNRC_LWMAC_TIME_BETWEEN_WR_US, the sender starts sending the next WR.
+ * It is referenced to the beginning of both WRs, but due to internal
+ * overhead, the exact spacing is slightly higher.
+ * The minimum possible value depends on the time it takes to completely
+ * send a WR with the given hardware (including processor) and data rate.
+ */
+#ifndef GNRC_LWMAC_TIME_BETWEEN_WR_US
+#define GNRC_LWMAC_TIME_BETWEEN_WR_US        (5U * US_PER_MS)
+#endif
+
+/**
+ * @brief How long a node in LWMAC should keep awake and listen on the channel in one cycle.
+ *
+ * LWMAC adopts the duty-cycle scheme that a node only wakes up for a short
+ * period of @ref GNRC_LWMAC_WAKEUP_DURATION_US in each cycle. In the rest of the cycle, the node
+ * turns off the radio to conserve power. @ref GNRC_LWMAC_WAKEUP_DURATION_US is set to twice the
+ * duration of @ref GNRC_LWMAC_TIME_BETWEEN_WR_US, to guarantee that the wake-up period is long
+ * enough that receiver will not miss the WR (preamble) packet.
+ * Receiver needs to support @ref NETDEV_EVENT_RX_STARTED event in order to use time-between-WR
+ * as a sensible default here. Otherwise the duration of WRs as well as longest
+ * possible data broadcasts need to be taken into account.
+ */
+#ifndef GNRC_LWMAC_WAKEUP_DURATION_US
+#define GNRC_LWMAC_WAKEUP_DURATION_US        (GNRC_LWMAC_TIME_BETWEEN_WR_US * 2)
+#endif
+
+/**
+ * @brief How long broadcast packets @ref gnrc_lwmac_frame_broadcast_t will be sent to make sure
+ *        every participant has received at least one copy.
+ *
+ * Since LWMAC adopts duty-cycle scheme, a node only wakes up for a short period in
+ * each cycle. Thus, when a node wants to broadcast a packet, it repeatedly broadcasts the
+ * packet for one @ref GNRC_LWMAC_BROADCAST_DURATION_US duration which is slightly longer
+ * than @ref GNRC_LWMAC_WAKEUP_INTERVAL_US. This is to ensure that all neighbors will not miss
+ * the broadcast procedure of the sender and catch at least one copy of the broadcast packet.
+ */
+#ifndef GNRC_LWMAC_BROADCAST_DURATION_US
+#define GNRC_LWMAC_BROADCAST_DURATION_US     ((GNRC_LWMAC_WAKEUP_INTERVAL_US * 11) / 10)
+#endif
+
+/**
+ * @brief Time to idle between two successive broadcast packets, referenced to the
+ *        start of the packet.
+ *
+ * The same limitation as for @ref GNRC_LWMAC_TIME_BETWEEN_WR_US apply here.
+ * In LWMAC, when a sender initiates a broadcast, it starts with sending a stream of
+ * repeated broadcast packets with @ref GNRC_LWMAC_TIME_BETWEEN_BROADCAST_US interval
+ * between two consecutive broadcast packets. After sending one broadcast packet, the sender
+ * turns to the listen mode with a timeout of @ref GNRC_LWMAC_TIME_BETWEEN_BROADCAST_US. When this
+ * timeout expires, the sender sends the next broadcast packet until reaching the maximum
+ * broadcast duration of @ref GNRC_LWMAC_BROADCAST_DURATION_US.
+ */
+#ifndef GNRC_LWMAC_TIME_BETWEEN_BROADCAST_US
+#define GNRC_LWMAC_TIME_BETWEEN_BROADCAST_US (GNRC_LWMAC_TIME_BETWEEN_WR_US)
+#endif
+
+/**
+ * @brief WR preparation overhead before it can be sent (higher with debugging output).
+ *
+ * In LWMAC, when a sender wants to send a data packet to the receiver, it starts
+ * sending the WR stream a little bit earlier (advance) to the beginning edge
+ * of destination's wake-up phase over time. The idea is not to miss the wake-up
+ * period of the receiver, otherwise will lead to a long WR procedure.
+ */
+#ifndef GNRC_LWMAC_WR_PREPARATION_US
+#define GNRC_LWMAC_WR_PREPARATION_US         ((3U * US_PER_MS))
+#endif
+
+/**
+ * @brief How long to wait after a WA for data to come in.
+ *
+ * When a node in LWMAC gets a WR during its wake-up period, it immediately
+ * replies a WA packet to the sender for acknowledging the sender's transmission
+ * request. After sending the WA, the receiver waits for the data packet from the
+ * sender, with a timeout of @ref GNRC_LWMAC_DATA_DELAY_US duration. In case no data will be
+ * received in this period, the receiver regards reception failed and go back to
+ * normal listen mode. However, in case the receiver receives other unintended packets,
+ * like WR/WA packets from other neighbor communication pairs, the receiver resets
+ * this timeout and continues to wait for the data packet, with the consideration that
+ * the sender's data transmission might be delayed due to other ongoing transmissions
+ * (the data packet is transmitted with CSMA/CA).
+ * This data timeout is long enough to catch the beginning of the packet if the transceiver
+ * supports @ref NETDEV_EVENT_RX_STARTED event (this can be important for big packets).
+ */
+#ifndef GNRC_LWMAC_DATA_DELAY_US
+#define GNRC_LWMAC_DATA_DELAY_US             (10U * US_PER_MS)
+#endif
+
+/**
+ * @brief CSMA retries for DATA packet after WR->WA was successful.
+ *
+ * After receiving the WA packet @ref gnrc_lwmac_frame_wa_t from the receiver, the sender
+ * starts sending the data packet using CSMA/CA. This macro defines how many CSMA retries
+ * a sender will be allowed to execute for sending its data, before the data is successfully
+ * sent (gets data ACK from the receiver).
+ */
+#ifndef GNRC_LWMAC_DATA_CSMA_RETRIES
+#define GNRC_LWMAC_DATA_CSMA_RETRIES         (3U)
+#endif
+
+/**
+ * @brief Maximum TX transmission retries for DATA packet in case of no response from the receiver.
+ *
+ * When a data packet is scheduled for transmission, i.e., pushed into TX for sending,
+ * LWMAC defines a maximum of @ref GNRC_LWMAC_MAX_DATA_TX_RETRIES retries for transmission of the
+ * packet. That is, in case of transmission failure in TX due to no WA from the receiver,
+ * the sender will not drop the packet, but keeps it and retries to send the data packet
+ * in the following cycles, until the sender reaches the maximum retries limit defined here.
+ * Then, the packet will be dropped.
+ */
+#ifndef GNRC_LWMAC_MAX_DATA_TX_RETRIES
+#define GNRC_LWMAC_MAX_DATA_TX_RETRIES       (3U)
+#endif
+
+/**
+ * @brief MAX burst transmission packet number in one shot.
+ *
+ * LWMAC supports burst transmission based on the pending-bit technique, and this macro
+ * here defines the largest number of packets allowed to be sent in one consecutive
+ * sequence. In case a sender has multi packets for one receiver,the burst transmission
+ * procedure is as follow:
+ * 1. The sender first uses WR stream to locate the receiver's wake-up period (if the
+ * sender has already phase-locked the receiver's phase, normally the sender only cost
+ * one WR to get the first WA from the receiver) and then sends its first data.
+ * 2. After the transmission of the first data, the sender immediately sends a WR to
+ * the receiver for starting the second round of transmission of the second data. The
+ * receiver should also immediately reply WA for continue receiving data packets. In
+ * case the sender doesn't receive WA during @ref GNRC_LWMAC_TIME_BETWEEN_WR_US, it regards the
+ * consecutive (burst) transmission failed and quits TX procedure (the data will be queued
+ * back to the transmission queue for normal transmission attempt in following cycles).
+ * 3. In case the second transmission succeeds, the sender repeats step (2) to send all the
+ * following pending packets.
+ * In short, in burst transmission mode, the sender doesn't tolerate no-WA event. ALl the
+ * pending data packets should be sent with only one WR cost for leading the transmission.
+ */
+#ifndef GNRC_LWMAC_MAX_TX_BURST_PKT_NUM
+#define GNRC_LWMAC_MAX_TX_BURST_PKT_NUM      (GNRC_LWMAC_WAKEUP_INTERVAL_US / GNRC_LWMAC_WAKEUP_DURATION_US)
+#endif
+
+/**
+ * @brief MAX bad Listen period extensions a node can tolerate.
+ *
+ * In LWMAC, to allow burst transmissions, when in the wake-up period and by default, a node
+ * will extend its wake-up period to another @ref GNRC_LWMAC_WAKEUP_DURATION_US after each packet
+ * reception (except for broadcast packet). However, in some cases, a receiver may
+ * overhear other unintended packets, e.g., WR or WA packets for other nodes, these are
+ * called bad extensions for the receiver. If a receiver reaches the maximum bad listen
+ * extension limit defined here, it goes to sleep mode with the consideration that the
+ * channel is currently unavailable/busy.
+ */
+#ifndef GNRC_LWMAC_MAX_RX_EXTENSION_NUM
+#define GNRC_LWMAC_MAX_RX_EXTENSION_NUM      (3U)
+#endif
+
+/**
+ * @brief CSMA retries for broadcast packet.
+ *
+ * Currently, each broadcast packet is sent with CSMA/CA for collision avoidance.
+ * Too many CSMA retries may lead to running out of destinations wake-up period.
+ */
+#ifndef GNRC_LWMAC_BROADCAST_CSMA_RETRIES
+#define GNRC_LWMAC_BROADCAST_CSMA_RETRIES    (3U)
+#endif
+
+/**
+ * @brief Default message queue size to use for the LWMAC thread.
+ *
+ * The value of this macro should be enough for supporting the manipulation of
+ * LWMAC.
+ *
+ */
+#ifndef GNRC_LWMAC_IPC_MSG_QUEUE_SIZE
+#define GNRC_LWMAC_IPC_MSG_QUEUE_SIZE        (8U)
+#endif
+
+/**
+ * @brief Initialize an instance of the LWMAC layer
+ *
+ * The initialization starts a new thread that connects to the given netdev
+ * device and starts a link layer event loop.
+ *
+ * @param[in] stack         stack for the control thread
+ * @param[in] stacksize     size of *stack*
+ * @param[in] priority      priority for the thread housing the LWMAC instance
+ * @param[in] name          name of the thread housing the LWMAC instance
+ * @param[in] dev           netdev device, needs to be already initialized
+ *
+ * @return                  PID of LWMAC thread on success
+ * @return                  -EINVAL if creation of thread fails
+ * @return                  -ENODEV if *dev* is invalid
+ */
+kernel_pid_t gnrc_lwmac_init(char *stack, int stacksize, char priority,
+                             const char *name, gnrc_netdev_t *dev);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NET_GNRC_LWMAC_LWMAC_H */
+/** @} */
diff --git a/sys/include/net/gnrc/lwmac/timeout.h b/sys/include/net/gnrc/lwmac/timeout.h
new file mode 100644
index 0000000000000000000000000000000000000000..0a664b9deadcfc4a8273453b60fd9e35f90c52f1
--- /dev/null
+++ b/sys/include/net/gnrc/lwmac/timeout.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2015 Daniel Krebs
+ *               2016 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_lwmac
+ * @{
+ *
+ * @file
+ * @brief       Timeout handling of LWMAC
+ *
+ *
+ * @author      Daniel Krebs <github@daniel-krebs.net>
+ * @author      Shuguo Zhuo  <shuguo.zhuo@inria.fr>
+ */
+
+#ifndef NET_GNRC_LWMAC_TIMEOUT_H
+#define NET_GNRC_LWMAC_TIMEOUT_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "net/gnrc/netdev.h"
+#include "net/gnrc/lwmac/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief   Static initializer for @ref gnrc_lwmac_timeout_t.
+ */
+#define GNRC_LWMAC_TIMEOUT_INITIAL  { {}, {}, false, TIMEOUT_DISABLED }
+
+/**
+ * @brief   Set LWMAC timeout of type @p type of offset @p offset.
+ *
+ * @param[in,out] gnrc_netdev  gnrc_netdev structure
+ * @param[in]     type         LWMAC timeout type
+ * @param[in]     offset       timeout offset
+ */
+void gnrc_lwmac_set_timeout(gnrc_netdev_t *gnrc_netdev,
+                            gnrc_lwmac_timeout_type_t type,
+                            uint32_t offset);
+
+/**
+ * @brief   Clear LWMAC timeout of type @p type.
+ *
+ * @param[in,out] gnrc_netdev  gnrc_netdev structure
+ * @param[in]     type         LWMAC timeout type
+ */
+void gnrc_lwmac_clear_timeout(gnrc_netdev_t *gnrc_netdev, gnrc_lwmac_timeout_type_t type);
+
+/**
+ * @brief   Check whether LWMAC timeout of type @p type is running.
+ *
+ * @param[in]     gnrc_netdev  gnrc_netdev structure
+ * @param[in]     type         LWMAC 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_lwmac_timeout_is_running(gnrc_netdev_t *gnrc_netdev,
+                                   gnrc_lwmac_timeout_type_t type);
+
+/**
+ * @brief   Check whether LWMAC timeout of type @p type is expired. It will clear
+ *          the timeout once it is found expired.
+ *
+ * @param[in,out] gnrc_netdev  gnrc_netdev structure
+ * @param[in]     type         LWMAC 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_lwmac_timeout_is_expired(gnrc_netdev_t *gnrc_netdev, gnrc_lwmac_timeout_type_t type);
+
+/**
+ * @brief   Reset all LWMAC timeouts.
+ *
+ * @param[in,out] gnrc_netdev  gnrc_netdev structure
+ */
+void gnrc_lwmac_reset_timeouts(gnrc_netdev_t *gnrc_netdev);
+
+/**
+ * @brief   Make a specific LWMAC timeout expired.
+ *
+ * @param[in,out] timeout   LWMAC tiemout
+ */
+void gnrc_lwmac_timeout_make_expire(gnrc_lwmac_timeout_t *timeout);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NET_GNRC_LWMAC_TIMEOUT_H */
+/** @} */
diff --git a/sys/include/net/gnrc/lwmac/types.h b/sys/include/net/gnrc/lwmac/types.h
new file mode 100644
index 0000000000000000000000000000000000000000..4d2e03815ed8220414d4c84fa9cb1e9804bb5ba8
--- /dev/null
+++ b/sys/include/net/gnrc/lwmac/types.h
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2015 Daniel Krebs
+ *               2016 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_lwmac
+ * @{
+ *
+ * @file
+ * @brief       Definition of internal types used by LWMAC
+ *
+ *
+ * @author      Daniel Krebs <github@daniel-krebs.net>
+ * @author      Shuguo Zhuo  <shuguo.zhuo@inria.fr>
+ */
+
+#ifndef NET_GNRC_LWMAC_TYPES_H
+#define NET_GNRC_LWMAC_TYPES_H
+
+#include "msg.h"
+#include "xtimer.h"
+#include "net/gnrc/lwmac/hdr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief   LWMAC RTT event type.
+ */
+#define GNRC_LWMAC_EVENT_RTT_TYPE            (0x4300)
+
+/**
+ * @brief   LWMAC RTT start event type.
+ */
+#define GNRC_LWMAC_EVENT_RTT_START           (0x4301)
+
+/**
+ * @brief   LWMAC RTT stop event type.
+ */
+#define GNRC_LWMAC_EVENT_RTT_STOP            (0x4302)
+
+/**
+ * @brief   LWMAC RTT pause event type.
+ */
+#define GNRC_LWMAC_EVENT_RTT_PAUSE           (0x4303)
+
+/**
+ * @brief   LWMAC RTT resume event type.
+ */
+#define GNRC_LWMAC_EVENT_RTT_RESUME          (0x4304)
+
+/**
+ * @brief   LWMAC RTT wakeup pending event type.
+ */
+#define GNRC_LWMAC_EVENT_RTT_WAKEUP_PENDING  (0x4305)
+
+/**
+ * @brief   LWMAC RTT sleep pending event type.
+ */
+#define GNRC_LWMAC_EVENT_RTT_SLEEP_PENDING   (0x4306)
+
+/**
+ * @brief   LWMAC timeout event type.
+ */
+#define GNRC_LWMAC_EVENT_TIMEOUT_TYPE        (0x4400)
+
+/**
+ * @brief   LWMAC duty-cycle active flag.
+ *
+ * Keep track of duty cycling to avoid late RTT events after stopping.
+ */
+#define GNRC_LWMAC_DUTYCYCLE_ACTIVE          (0x01)
+
+/**
+ * @brief   LWMAC needs reschedule flag.
+ *
+ * Used internally for rescheduling state machine update, e.g. after state
+ * transition caused in update.
+ */
+#define GNRC_LWMAC_NEEDS_RESCHEDULE          (0x02)
+
+/**
+ * @brief   LWMAC check radio's on/off state flag.
+ */
+#define GNRC_LWMAC_RADIO_IS_ON               (0x04)
+
+/**
+ * @brief   Enable/disable duty-cycle record and print out.
+ *          Set "1" to enable, set "0" to disable.
+ */
+#ifndef GNRC_LWMAC_ENABLE_DUTYCYLE_RECORD
+#define GNRC_LWMAC_ENABLE_DUTYCYLE_RECORD    (0U)
+#endif
+
+/**
+ * @brief The default largest number of parallel timeouts in LWMAC
+ */
+#ifndef GNRC_LWMAC_TIMEOUT_COUNT
+#define GNRC_LWMAC_TIMEOUT_COUNT             (3U)
+#endif
+
+/**
+ * @brief   Internal states of LWMAC
+ */
+typedef enum {
+    GNRC_LWMAC_UNDEF = -1,     /**< Undefined state of LWMAC */
+    GNRC_LWMAC_STOPPED,        /**< LWMAC's main state machine has been stopped */
+    GNRC_LWMAC_START,          /**< Start LWMAC's main state machine */
+    GNRC_LWMAC_STOP,           /**< Stop LWMAC's main state machine */
+    GNRC_LWMAC_RESET,          /**< Reset LWMAC's main state machine */
+    GNRC_LWMAC_LISTENING,      /**< Listen the channel for receiving packets */
+    GNRC_LWMAC_RECEIVING,      /**< RX is handled in own state machine */
+    GNRC_LWMAC_TRANSMITTING,   /**< TX is handled in own state machine */
+    GNRC_LWMAC_SLEEPING,       /**< Turn off radio to conserve power */
+    GNRC_LWMAC_STATE_COUNT     /**< Count of LWMAC's states */
+} gnrc_lwmac_state_t;
+
+/**
+ * @brief   TX states of LWMAC
+ */
+typedef enum {
+    GNRC_LWMAC_TX_STATE_STOPPED,           /**< Tx schedule stopped, stop sending packet */
+    GNRC_LWMAC_TX_STATE_INIT,              /**< Initiate transmission */
+    GNRC_LWMAC_TX_STATE_SEND_BROADCAST,    /**< directly goes to SUCCESSFUL or FAILED when finished */
+    GNRC_LWMAC_TX_STATE_SEND_WR,           /**< Send a wakeup request */
+    GNRC_LWMAC_TX_STATE_WAIT_WR_SENT,      /**< Wait until WR sent to set timeout */
+    GNRC_LWMAC_TX_STATE_WAIT_FOR_WA,       /**< Wait for dest node's wakeup ackknowledge */
+    GNRC_LWMAC_TX_STATE_SEND_DATA,         /**< Send the actual payload data */
+    GNRC_LWMAC_TX_STATE_WAIT_FEEDBACK,     /**< Wait if packet was ACKed */
+    GNRC_LWMAC_TX_STATE_SUCCESSFUL,        /**< Transmission has finished successfully */
+    GNRC_LWMAC_TX_STATE_FAILED             /**< Payload data couldn't be delivered to dest */
+} gnrc_lwmac_tx_state_t;
+
+/**
+ * @brief   Static initializer for gnrc_lwmac_tx_state_t.
+ */
+#define GNRC_LWMAC_TX_STATE_INITIAL GNRC_LWMAC_TX_STATE_STOPPED
+
+/**
+ * @brief   RX states of LWMAC
+ */
+typedef enum {
+    GNRC_LWMAC_RX_STATE_STOPPED,       /**< Rx schedule stopped */
+    GNRC_LWMAC_RX_STATE_INIT,          /**< Initiate reception */
+    GNRC_LWMAC_RX_STATE_WAIT_FOR_WR,   /**< Wait for a wakeup request */
+    GNRC_LWMAC_RX_STATE_SEND_WA,       /**< Send wakeup ackknowledge to requesting node */
+    GNRC_LWMAC_RX_STATE_WAIT_WA_SENT,  /**< Wait until WA sent to set timeout */
+    GNRC_LWMAC_RX_STATE_WAIT_FOR_DATA, /**< Wait for actual payload data */
+    GNRC_LWMAC_RX_STATE_SUCCESSFUL,    /**< Recption has finished successfully */
+    GNRC_LWMAC_RX_STATE_FAILED         /**< Reception over, but nothing received */
+} gnrc_lwmac_rx_state_t;
+
+/**
+ * @brief   Static initializer for gnrc_lwmac_rx_state_t.
+ */
+#define GNRC_LWMAC_RX_STATE_INITIAL GNRC_LWMAC_RX_STATE_STOPPED
+
+/**
+ * @brief   LWMAC uninitialized phase value
+ */
+#define GNRC_LWMAC_PHASE_UNINITIALIZED   (0)
+
+/**
+ * @brief   LWMAC max phase value
+ */
+#define GNRC_LWMAC_PHASE_MAX             (-1)
+
+/**
+ * @brief   LWMAC timeout types
+ */
+typedef enum {
+    GNRC_LWMAC_TIMEOUT_DISABLED,              /**< Timeout is diabled */
+    GNRC_LWMAC_TIMEOUT_WR,                    /**< WR timeout, waiting WA */
+    GNRC_LWMAC_TIMEOUT_NO_RESPONSE,           /**< Maximum WR duration timeout awaiting WA */
+    GNRC_LWMAC_TIMEOUT_DATA,                  /**< Timeout awaiting data packet from receiver */
+    GNRC_LWMAC_TIMEOUT_WAIT_DEST_WAKEUP,      /**< Timeout for waiting receiver's wake-up phase */
+    GNRC_LWMAC_TIMEOUT_WAKEUP_PERIOD,         /**< Wake up period timeout for going to sleep */
+    GNRC_LWMAC_TIMEOUT_NEXT_BROADCAST,        /**< Timeout for waiting to send the next broadcast packet */
+    GNRC_LWMAC_TIMEOUT_BROADCAST_END,         /**< Timeout awaiting the end of the whole broadcast period */
+} gnrc_lwmac_timeout_type_t;
+
+/**
+ * @brief   LWMAC timeout structure
+ */
+typedef struct {
+    xtimer_t timer;                 /**< xtimer entity */
+    msg_t msg;                      /**< msg entity */
+    bool expired;                   /**< If type != DISABLED, this indicates if timeout has expired */
+    gnrc_lwmac_timeout_type_t type; /**< timeout type */
+} gnrc_lwmac_timeout_t;
+
+/**
+ * @brief   LWMAC specific structure for storing internal states.
+ */
+typedef struct lwmac {
+    gnrc_lwmac_state_t state;                                /**< Internal state of MAC layer */
+    uint32_t last_wakeup;                                    /**< Used to calculate wakeup times */
+    uint8_t lwmac_info;                                      /**< LWMAC's internal informations (flags) */
+    gnrc_lwmac_timeout_t timeouts[GNRC_LWMAC_TIMEOUT_COUNT]; /**< Store timeouts used for protocol */
+
+#if (GNRC_LWMAC_ENABLE_DUTYCYLE_RECORD == 1)
+    /* Parameters for recording duty-cycle */
+    uint32_t last_radio_on_time_ticks;                       /**< The last time in ticks when radio is on */
+    uint32_t radio_off_time_ticks;                           /**< The time in ticks when radio is off */
+    uint32_t system_start_time_ticks;                        /**< The time in ticks when chip is started */
+    uint32_t awake_duration_sum_ticks;                       /**< The sum of time in ticks when radio is on */
+    uint32_t pkt_start_sending_time_ticks;                   /**< The time in ticks when the packet is started
+                                                                  to be sent */
+#endif
+} gnrc_lwmac_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NET_GNRC_LWMAC_TYPES_H */
+/** @} */
diff --git a/sys/include/net/gnrc/mac/types.h b/sys/include/net/gnrc/mac/types.h
index 63ac0e25dccf7283494797692ab1ad3a2dab8e9f..ac844a9ce3ed4958adc24a289cf956fdc361a5f0 100644
--- a/sys/include/net/gnrc/mac/types.h
+++ b/sys/include/net/gnrc/mac/types.h
@@ -29,6 +29,7 @@
 #include "net/gnrc/priority_pktqueue.h"
 #include "net/ieee802154.h"
 #include "net/gnrc/mac/mac.h"
+#include "net/gnrc/lwmac/types.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -66,6 +67,12 @@ typedef struct {
 #if (GNRC_MAC_DISPATCH_BUFFER_SIZE != 0) || defined(DOXYGEN)
     gnrc_pktsnip_t *dispatch_buffer[GNRC_MAC_DISPATCH_BUFFER_SIZE];      /**< dispatch packet buffer */
 #endif /* (GNRC_MAC_DISPATCH_BUFFER_SIZE != 0) || defined(DOXYGEN) */
+
+#ifdef MODULE_GNRC_LWMAC
+    gnrc_lwmac_l2_addr_t l2_addr; /**< Records the sender's address */
+    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
 } gnrc_mac_rx_t;
 
 /**
@@ -157,6 +164,15 @@ typedef struct {
     gnrc_priority_pktqueue_node_t _queue_nodes[GNRC_MAC_TX_QUEUE_SIZE]; /**< Shared buffer for TX queue nodes */
     gnrc_pktsnip_t *packet;                                             /**< currently scheduled packet for sending */
 #endif /* (GNRC_MAC_TX_QUEUE_SIZE != 0) || defined(DOXYGEN) */
+
+#ifdef MODULE_GNRC_LWMAC
+    gnrc_lwmac_tx_state_t state;       /**< LWMAC specific internal transmission state */
+    uint32_t wr_sent;                  /**< Count how many WRs were sent until WA received */
+    uint32_t timestamp;                /**< Records the receiver's current phase */
+    uint8_t bcast_seqnr;               /**< Sequence number for broadcast data to filter at receiver */
+    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
 } gnrc_mac_tx_t;
 
 /**
diff --git a/sys/include/net/gnrc/netdev.h b/sys/include/net/gnrc/netdev.h
index c6882b53d32f6cc734b684a9e9b2c55c697a510d..b49dff0c70b62920b26332f18fc73854364a69a9 100644
--- a/sys/include/net/gnrc/netdev.h
+++ b/sys/include/net/gnrc/netdev.h
@@ -152,6 +152,14 @@ typedef struct gnrc_netdev {
      */
     gnrc_mac_tx_t tx;
 #endif /* ((GNRC_MAC_TX_QUEUE_SIZE != 0) || (GNRC_MAC_NEIGHBOR_COUNT == 0)) || defined(DOXYGEN) */
+
+#ifdef MODULE_GNRC_LWMAC
+    /**
+     * @brief LWMAC specific structure object for storing LWMAC internal states.
+     */
+    gnrc_lwmac_t lwmac;
+#endif
+
 #endif /* MODULE_GNRC_MAC */
 } gnrc_netdev_t;
 
diff --git a/sys/include/net/gnrc/nettype.h b/sys/include/net/gnrc/nettype.h
index a455d24026cf3a182968243010fdc9099b2909a0..40d84fa9a536378ac92e74b5618862dceadfad89 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_LWMAC
+    GNRC_NETTYPE_LWMAC,          /**< Protocol is lwMAC */
+#endif
+    /**
+     * @}
+     */
+
     /**
      * @{
      * @name Network layer
diff --git a/sys/net/gnrc/Makefile b/sys/net/gnrc/Makefile
index e03f6f26d661acd1e07dfeec6d223c68ca55c8c9..de9e238a2ae01bcf86fca60f7902f0b473315e31 100644
--- a/sys/net/gnrc/Makefile
+++ b/sys/net/gnrc/Makefile
@@ -67,6 +67,9 @@ endif
 ifneq (,$(filter gnrc_pkt,$(USEMODULE)))
     DIRS += pkt
 endif
+ifneq (,$(filter gnrc_lwmac,$(USEMODULE)))
+    DIRS += link_layer/lwmac
+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 1279438e254fb4e7309ce7041fcbdc388ae16a0e..8e4fb09c293cfff10f12133ace25b7f81491e6ef 100644
--- a/sys/net/gnrc/link_layer/gnrc_mac/internal.c
+++ b/sys/net/gnrc/link_layer/gnrc_mac/internal.c
@@ -241,6 +241,17 @@ void gnrc_mac_dispatch(gnrc_mac_rx_t *rx)
 
     for (unsigned i = 0; i < GNRC_MAC_DISPATCH_BUFFER_SIZE; i++) {
         if (rx->dispatch_buffer[i]) {
+#ifdef MODULE_GNRC_LWMAC
+            /* save pointer to netif header */
+            gnrc_pktsnip_t *netif = rx->dispatch_buffer[i]->next->next;
+
+            /* remove lwmac 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/lwmac/Makefile b/sys/net/gnrc/link_layer/lwmac/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..4c7540701604ca78a00ec691b0a4362fa772dc75
--- /dev/null
+++ b/sys/net/gnrc/link_layer/lwmac/Makefile
@@ -0,0 +1,3 @@
+MODULE = gnrc_lwmac
+
+include $(RIOTBASE)/Makefile.base
diff --git a/sys/net/gnrc/link_layer/lwmac/include/lwmac_internal.h b/sys/net/gnrc/link_layer/lwmac/include/lwmac_internal.h
new file mode 100644
index 0000000000000000000000000000000000000000..88a5a1e397be1e5ad09f457c1020393c20d4c10c
--- /dev/null
+++ b/sys/net/gnrc/link_layer/lwmac/include/lwmac_internal.h
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2015 Daniel Krebs
+ *               2016 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_lwmac
+ * @{
+ *
+ * @file
+ * @brief       Interface definition for internal functions of LWMAC protocol
+ *
+ * @author      Daniel Krebs <github@daniel-krebs.net>
+ * @author      Shuguo Zhuo  <shuguo.zhuo@inria.fr>
+ */
+
+#ifndef LWMAC_INTERNAL_H
+#define LWMAC_INTERNAL_H
+
+#include <stdint.h>
+
+#include "periph/rtt.h"
+#include "net/gnrc/netdev.h"
+#include "net/gnrc/mac/types.h"
+#include "net/gnrc/lwmac/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief   Flag to track if the sender can continue to transmit packet to
+ *          the receiver in its TX procedure.
+ *
+ * LWMAC supports burst transmission based on the pending-bit technique.
+ * Namely, if the sender has multi packets for the same receiver, it can
+ * successively transmit its packets back to back with this flag set up,
+ * with the awareness that the receiver will also keep awake for receptions.
+ */
+#define GNRC_NETDEV_LWMAC_TX_CONTINUE          (0x0008U)
+
+/**
+ * @brief   Flag to track if the sender should quit Tx in current cycle.
+ *
+ * This flag is mainly for collision avoidance. In case a node overhears
+ * ongoing broadcast packets stream or other ongoing transmissions of
+ * other communication pairs during its wake-up period, it sets up this
+ * flag, which quits all its potential transmission attempts in this current
+ * cycle (started by the wake-up period), thus not to collide with other
+ * (neighbor) nodes' transmissions.
+ */
+#define GNRC_NETDEV_LWMAC_QUIT_TX              (0x0010U)
+
+/**
+ * @brief   Flag to track if the device need to reselect a new wake-up phase.
+ *
+ * This flag is mainly for potential collision avoidance. In multi-hop scenario,
+ * it could be dangerous that a sender's wake-up phase is close to its receiver's,
+ * which may lead to collisions when the sender is sending to the receiver while
+ * the sender's son nodes are also sending to the sender. To avoid this, in case a
+ * sender finds its phase close to its receiver's, it sets up this flag and then
+ * randomly reselects a new wake-up phase.
+ */
+#define GNRC_NETDEV_LWMAC_PHASE_BACKOFF        (0x0020U)
+
+/**
+ * @brief   Flag to track if the device needs to quit the wake-up (listening) procedure.
+ *
+ * LWMAC adopts an auto wake-up extension scheme. That is, normally, after each data
+ * reception in the wake-up period, it extends the wake-up period to another basic
+ * duration, thus to receive more potential incoming packets, which is also correlated to
+ * the pending-bit transmission scheme to support burst transmissions to boost throughput.
+ * However, in some situations, like receiving broadcast (stream) packet, the receiver
+ * should immediately goto sleep (by setting up this flag) after one reception, thus not
+ * to receive duplicate broadcast packets.
+ */
+#define GNRC_NETDEV_LWMAC_QUIT_RX              (0x0040U)
+
+/**
+ * @brief Type to pass information about parsing.
+ */
+typedef struct {
+    gnrc_lwmac_hdr_t *header;      /**< LWMAC header of packet */
+    gnrc_lwmac_l2_addr_t src_addr; /**< copied source address of packet  */
+    gnrc_lwmac_l2_addr_t dst_addr; /**< copied destination address of packet */
+} gnrc_lwmac_packet_info_t;
+
+/**
+ * @brief Next RTT event must be at least this far in the future.
+ *
+ * When setting an RTT alarm to short in the future it could be possible that
+ * the counter already passed the calculated alarm before it could be set.
+ */
+#define GNRC_LWMAC_RTT_EVENT_MARGIN_TICKS    (RTT_MS_TO_TICKS(2))
+
+/**
+ * @brief set the TX-continue flag of the device
+ *
+ * @param[in] dev          ptr to netdev device
+ * @param[in] tx_continue  value for LWMAC tx-continue flag
+ *
+ */
+static inline void gnrc_netdev_lwmac_set_tx_continue(gnrc_netdev_t *dev, bool tx_continue)
+{
+    if (tx_continue) {
+        dev->mac_info |= GNRC_NETDEV_LWMAC_TX_CONTINUE;
+    }
+    else {
+        dev->mac_info &= ~GNRC_NETDEV_LWMAC_TX_CONTINUE;
+    }
+}
+
+/**
+ * @brief get the TX-continue flag of the device
+ *
+ * @param[in] dev          ptr to netdev device
+ *
+ * @return                 true if tx continue
+ * @return                 false if tx will continue
+ */
+static inline bool gnrc_netdev_lwmac_get_tx_continue(gnrc_netdev_t *dev)
+{
+    return (dev->mac_info & GNRC_NETDEV_LWMAC_TX_CONTINUE);
+}
+
+/**
+ * @brief set the quit-TX flag of the device
+ *
+ * @param[in] dev          ptr to netdev device
+ * @param[in] quit_tx      value for LWMAC quit-TX flag
+ *
+ */
+static inline void gnrc_netdev_lwmac_set_quit_tx(gnrc_netdev_t *dev, bool quit_tx)
+{
+    if (quit_tx) {
+        dev->mac_info |= GNRC_NETDEV_LWMAC_QUIT_TX;
+    }
+    else {
+        dev->mac_info &= ~GNRC_NETDEV_LWMAC_QUIT_TX;
+    }
+}
+
+/**
+ * @brief get the quit-TX flag of the device
+ *
+ * @param[in] dev          ptr to netdev device
+ *
+ * @return                 true if quit tx
+ * @return                 false if will not quit tx
+ */
+static inline bool gnrc_netdev_lwmac_get_quit_tx(gnrc_netdev_t *dev)
+{
+    return (dev->mac_info & GNRC_NETDEV_LWMAC_QUIT_TX);
+}
+
+/**
+ * @brief set the phase-backoff flag of the device
+ *
+ * @param[in] dev          ptr to netdev device
+ * @param[in] backoff      value for LWMAC phase-backoff flag
+ *
+ */
+static inline void gnrc_netdev_lwmac_set_phase_backoff(gnrc_netdev_t *dev, bool backoff)
+{
+    if (backoff) {
+        dev->mac_info |= GNRC_NETDEV_LWMAC_PHASE_BACKOFF;
+    }
+    else {
+        dev->mac_info &= ~GNRC_NETDEV_LWMAC_PHASE_BACKOFF;
+    }
+}
+
+/**
+ * @brief get the phase-backoff of the device
+ *
+ * @param[in] dev          ptr to netdev device
+ *
+ * @return                 true if will run phase-backoff
+ * @return                 false if will not run phase-backoff
+ */
+static inline bool gnrc_netdev_lwmac_get_phase_backoff(gnrc_netdev_t *dev)
+{
+    return (dev->mac_info & GNRC_NETDEV_LWMAC_PHASE_BACKOFF);
+}
+
+/**
+ * @brief set the quit-RX flag of the device
+ *
+ * @param[in] dev          ptr to netdev device
+ * @param[in] quit_rx      value for LWMAC quit-Rx flag
+ *
+ */
+static inline void gnrc_netdev_lwmac_set_quit_rx(gnrc_netdev_t *dev, bool quit_rx)
+{
+    if (quit_rx) {
+        dev->mac_info |= GNRC_NETDEV_LWMAC_QUIT_RX;
+    }
+    else {
+        dev->mac_info &= ~GNRC_NETDEV_LWMAC_QUIT_RX;
+    }
+}
+
+/**
+ * @brief get the quit-RX flag of the device
+ *
+ * @param[in] dev          ptr to netdev device
+ *
+ * @return                 true if will quit rx
+ * @return                 false if will not quit rx
+ */
+static inline bool gnrc_netdev_lwmac_get_quit_rx(gnrc_netdev_t *dev)
+{
+    return (dev->mac_info & GNRC_NETDEV_LWMAC_QUIT_RX);
+}
+
+/**
+ * @brief set the duty-cycle-active flag of LWMAC
+ *
+ * @param[in] dev          ptr to netdev device
+ * @param[in] active       value for LWMAC duty-cycle-active flag
+ *
+ */
+static inline void gnrc_netdev_lwmac_set_dutycycle_active(gnrc_netdev_t *dev, bool active)
+{
+    if (active) {
+        dev->lwmac.lwmac_info |= GNRC_LWMAC_DUTYCYCLE_ACTIVE;
+    }
+    else {
+        dev->lwmac.lwmac_info &= ~GNRC_LWMAC_DUTYCYCLE_ACTIVE;
+    }
+}
+
+/**
+ * @brief get the duty-cycle-active flag of LWMAC
+ *
+ * @param[in] dev          ptr to netdev device
+ *
+ * @return                 true if active
+ * @return                 false if not active
+ */
+static inline bool gnrc_netdev_lwmac_get_dutycycle_active(gnrc_netdev_t *dev)
+{
+    return (dev->lwmac.lwmac_info & GNRC_LWMAC_DUTYCYCLE_ACTIVE);
+}
+
+/**
+ * @brief set the needs-rescheduling flag of LWMAC
+ *
+ * @param[in] dev          ptr to netdev device
+ * @param[in] reschedule   value for LWMAC needs-rescheduling flag
+ *
+ */
+static inline void gnrc_netdev_lwmac_set_reschedule(gnrc_netdev_t *dev, bool reschedule)
+{
+    if (reschedule) {
+        dev->lwmac.lwmac_info |= GNRC_LWMAC_NEEDS_RESCHEDULE;
+    }
+    else {
+        dev->lwmac.lwmac_info &= ~GNRC_LWMAC_NEEDS_RESCHEDULE;
+    }
+}
+
+/**
+ * @brief get the needs-rescheduling flag of LWMAC
+ *
+ * @param[in] dev          ptr to netdev device
+ *
+ * @return                 true if needs rescheduling
+ * @return                 false if no need for rescheduling
+ */
+static inline bool gnrc_netdev_lwmac_get_reschedule(gnrc_netdev_t *dev)
+{
+    return (dev->lwmac.lwmac_info & GNRC_LWMAC_NEEDS_RESCHEDULE);
+}
+
+/**
+ * @brief Parse an incoming packet and extract important information.
+ *
+ *        Copies addresses into @p info, but header points inside @p pkt.
+ *
+ * @param[in]   pkt             packet that will be parsed
+ * @param[out]  info            structure that will hold parsed information
+ *
+ * @return                      0 if correctly parsed
+ * @return                      <0 on error
+ */
+int _gnrc_lwmac_parse_packet(gnrc_pktsnip_t *pkt, gnrc_lwmac_packet_info_t *info);
+
+/**
+ * @brief Shortcut to get the state of netdev.
+ *
+ * @param[in]   gnrc_netdev    gnrc_netdev structure
+ *
+ * @return                     state of netdev
+ */
+netopt_state_t _gnrc_lwmac_get_netdev_state(gnrc_netdev_t *gnrc_netdev);
+
+/**
+ * @brief Shortcut to set the state of netdev
+ *
+ * @param[in]   gnrc_netdev    gnrc_netdev structure
+ * @param[in]   devstate       new state for netdev
+ */
+void _gnrc_lwmac_set_netdev_state(gnrc_netdev_t *gnrc_netdev, netopt_state_t devstate);
+
+/**
+ * @brief Convert RTT ticks to device phase
+ *
+ * @param[in]   ticks    RTT ticks
+ *
+ * @return               device phase
+ */
+static inline uint32_t _gnrc_lwmac_ticks_to_phase(uint32_t ticks)
+{
+    assert(GNRC_LWMAC_WAKEUP_INTERVAL_US != 0);
+
+    return (ticks % RTT_US_TO_TICKS(GNRC_LWMAC_WAKEUP_INTERVAL_US));
+}
+
+/**
+ * @brief Get device's current phase
+ *
+ * @return               device phase
+ */
+static inline uint32_t _gnrc_lwmac_phase_now(void)
+{
+    return _gnrc_lwmac_ticks_to_phase(rtt_get_counter());
+}
+
+/**
+ * @brief Calculate how many ticks remaining to the targeted phase in the future
+ *
+ * @param[in]   phase    device phase
+ *
+ * @return               RTT ticks
+ */
+static inline uint32_t _gnrc_lwmac_ticks_until_phase(uint32_t phase)
+{
+    long int tmp = phase - _gnrc_lwmac_phase_now();
+
+    if (tmp < 0) {
+        /* Phase in next interval */
+        tmp += RTT_US_TO_TICKS(GNRC_LWMAC_WAKEUP_INTERVAL_US);
+    }
+
+    return (uint32_t)tmp;
+}
+
+/**
+ * @brief Store the received packet to the dispatch buffer and remove possible
+ *        duplicate packets.
+ *
+ * @param[in,out]   buffer      RX dispatch packet buffer
+ * @param[in]       pkt         received packet
+ *
+ * @return                      0 if correctly stored
+ * @return                      <0 on error
+ */
+int _gnrc_lwmac_dispatch_defer(gnrc_pktsnip_t * buffer[], gnrc_pktsnip_t * pkt);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWMAC_INTERNAL_H */
+/** @} */
diff --git a/sys/net/gnrc/link_layer/lwmac/include/rx_state_machine.h b/sys/net/gnrc/link_layer/lwmac/include/rx_state_machine.h
new file mode 100644
index 0000000000000000000000000000000000000000..4d585e0dade0d7cee9adcf3ebfac0ddcaee0ba1d
--- /dev/null
+++ b/sys/net/gnrc/link_layer/lwmac/include/rx_state_machine.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2015 Daniel Krebs
+ *               2016 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_lwmac
+ * @{
+ *
+ * @file
+ * @brief       Implementation of RX state machine
+ *
+ * @author      Daniel Krebs <github@daniel-krebs.net>
+ * @author      Shuguo Zhuo  <shuguo.zhuo@inria.fr>
+ * @}
+ */
+
+#ifndef RX_STATE_MACHINE_H
+#define RX_STATE_MACHINE_H
+
+#include "net/gnrc/pkt.h"
+#include "net/gnrc/netdev.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Start LWMAC RX procedure to receive packet
+ *
+ * @param[in,out]   gnrc_netdev   gnrc_netdev structure
+ *
+ */
+void gnrc_lwmac_rx_start(gnrc_netdev_t *gnrc_netdev);
+
+/**
+ * @brief Stop LWMAC RX procedure
+ *
+ * @param[in,out]   gnrc_netdev   gnrc_netdev structure
+ *
+ */
+void gnrc_lwmac_rx_stop(gnrc_netdev_t *gnrc_netdev);
+
+/**
+ * @brief Update LWMAC RX procedure for packet reception
+ *
+ * @param[in,out]   gnrc_netdev   gnrc_netdev structure
+ *
+ */
+void gnrc_lwmac_rx_update(gnrc_netdev_t *gnrc_netdev);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* RX_STATE_MACHINE_H */
diff --git a/sys/net/gnrc/link_layer/lwmac/include/tx_state_machine.h b/sys/net/gnrc/link_layer/lwmac/include/tx_state_machine.h
new file mode 100644
index 0000000000000000000000000000000000000000..4787c23444664d1742c9beed7d4300fb90628ee7
--- /dev/null
+++ b/sys/net/gnrc/link_layer/lwmac/include/tx_state_machine.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2015 Daniel Krebs
+ *               2016 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_lwmac
+ * @{
+ *
+ * @file
+ * @brief       Implementation of TX state machine
+ *
+ * @author      Daniel Krebs <github@daniel-krebs.net>
+ * @author      Shuguo Zhuo  <shuguo.zhuo@inria.fr>
+ * @}
+ */
+
+#ifndef TX_STATE_MACHINE_H
+#define TX_STATE_MACHINE_H
+
+#include "net/gnrc/pkt.h"
+#include "net/gnrc/netdev.h"
+#include "net/gnrc/mac/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Start LWMAC TX procedure to transmit packet @p pkt to @p neighbor
+ *
+ * @param[in,out]   gnrc_netdev   gnrc_netdev structure
+ * @param[in]       pkt           packet to transmit
+ * @param[in]       neighbor      Tx neighbor
+ *
+ */
+void gnrc_lwmac_tx_start(gnrc_netdev_t *gnrc_netdev,
+                         gnrc_pktsnip_t *pkt,
+                         gnrc_mac_tx_neighbor_t *neighbor);
+
+/**
+ * @brief Stop LWMAC TX procedure
+ *
+ * @param[in,out]   gnrc_netdev   gnrc_netdev structure
+ *
+ */
+void gnrc_lwmac_tx_stop(gnrc_netdev_t *gnrc_netdev);
+
+/**
+ * @brief Update LWMAC TX procedure for transmission
+ *
+ * @param[in,out]   gnrc_netdev   gnrc_netdev structure
+ *
+ */
+void gnrc_lwmac_tx_update(gnrc_netdev_t *gnrc_netdev);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* TX_STATE_MACHINE_H */
diff --git a/sys/net/gnrc/link_layer/lwmac/lwmac.c b/sys/net/gnrc/link_layer/lwmac/lwmac.c
new file mode 100644
index 0000000000000000000000000000000000000000..db855afcc1a8d200d7f001cc21c93b4e3b518acf
--- /dev/null
+++ b/sys/net/gnrc/link_layer/lwmac/lwmac.c
@@ -0,0 +1,920 @@
+/*
+ * Copyright (C) 2015 Daniel Krebs
+ *               2016 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_lwmac
+ * @{
+ *
+ * @file
+ * @brief       Implementation of the LWMAC protocol
+ *
+ * @author      Daniel Krebs <github@daniel-krebs.net>
+ * @author      Shuguo Zhuo  <shuguo.zhuo@inria.fr>
+ * @}
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "kernel_types.h"
+#include "msg.h"
+#include "thread.h"
+#include "timex.h"
+#include "random.h"
+#include "periph/rtt.h"
+#include "net/gnrc.h"
+#include "net/netdev.h"
+#include "net/gnrc/netdev.h"
+#include "net/gnrc/lwmac/types.h"
+#include "net/gnrc/lwmac/lwmac.h"
+#include "net/gnrc/mac/internal.h"
+#include "net/gnrc/lwmac/timeout.h"
+#include "include/tx_state_machine.h"
+#include "include/rx_state_machine.h"
+#include "include/lwmac_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  LWMAC thread's PID
+ */
+kernel_pid_t lwmac_pid;
+
+static void rtt_cb(void *arg);
+static void lwmac_set_state(gnrc_netdev_t *gnrc_netdev, gnrc_lwmac_state_t newstate);
+static void lwmac_schedule_update(gnrc_netdev_t *gnrc_netdev);
+static void rtt_handler(uint32_t event, gnrc_netdev_t *gnrc_netdev);
+
+static gnrc_mac_tx_neighbor_t *_next_tx_neighbor(gnrc_netdev_t *gnrc_netdev)
+{
+    int next = -1;
+
+    uint32_t phase_nearest = GNRC_LWMAC_PHASE_MAX;
+
+    for (int i = 0; i < GNRC_MAC_NEIGHBOR_COUNT; i++) {
+        if (gnrc_priority_pktqueue_length(&gnrc_netdev->tx.neighbors[i].queue) > 0) {
+            /* Unknown destinations are initialized with their phase at the end
+             * of the local interval, so known destinations that still wakeup
+             * in this interval will be preferred. */
+            uint32_t phase_check = _gnrc_lwmac_ticks_until_phase(gnrc_netdev->tx.neighbors[i].phase);
+
+            if (phase_check <= phase_nearest) {
+                next = i;
+                phase_nearest = phase_check;
+                DEBUG("[LWMAC-int] Advancing queue #%d\n", i);
+            }
+        }
+    }
+
+    return (next < 0) ? NULL : &(gnrc_netdev->tx.neighbors[next]);
+}
+
+static uint32_t _next_inphase_event(uint32_t last, uint32_t interval)
+{
+    /* Counter did overflow since last wakeup */
+    if (rtt_get_counter() < last) {
+        /* TODO: Not sure if this was tested :) */
+        uint32_t tmp = -last;
+        tmp /= interval;
+        tmp++;
+        last += tmp * interval;
+    }
+
+    /* Add margin to next wakeup so that it will be at least 2ms in the future */
+    while (last < (rtt_get_counter() + GNRC_LWMAC_RTT_EVENT_MARGIN_TICKS)) {
+        last += interval;
+    }
+
+    return last;
+}
+
+inline void lwmac_schedule_update(gnrc_netdev_t *gnrc_netdev)
+{
+    gnrc_netdev_lwmac_set_reschedule(gnrc_netdev, true);
+}
+
+void lwmac_set_state(gnrc_netdev_t *gnrc_netdev, gnrc_lwmac_state_t newstate)
+{
+    gnrc_lwmac_state_t oldstate = gnrc_netdev->lwmac.state;
+
+    if (newstate == oldstate) {
+        return;
+    }
+
+    if (newstate >= GNRC_LWMAC_STATE_COUNT) {
+        LOG_ERROR("ERROR: [LWMAC] Trying to set invalid state %u\n", newstate);
+        return;
+    }
+
+    /* Already change state, but might be reverted to oldstate when needed */
+    gnrc_netdev->lwmac.state = newstate;
+
+    /* Actions when leaving old state */
+    switch (oldstate) {
+        case GNRC_LWMAC_RECEIVING:
+        case GNRC_LWMAC_TRANSMITTING: {
+            /* Enable duty cycling again */
+            rtt_handler(GNRC_LWMAC_EVENT_RTT_RESUME, gnrc_netdev);
+#if (GNRC_LWMAC_ENABLE_DUTYCYLE_RECORD == 1)
+            /* Output duty-cycle ratio */
+            uint64_t duty;
+            duty = (uint64_t) rtt_get_counter();
+            duty = ((uint64_t) gnrc_netdev->lwmac.awake_duration_sum_ticks) * 100 /
+                   (duty - (uint64_t)gnrc_netdev->lwmac.system_start_time_ticks);
+            printf("[LWMAC]: achieved duty-cycle: %lu %% \n", (uint32_t)duty);
+#endif
+            break;
+        }
+        case GNRC_LWMAC_SLEEPING: {
+            gnrc_lwmac_clear_timeout(gnrc_netdev, GNRC_LWMAC_TIMEOUT_WAKEUP_PERIOD);
+            break;
+        }
+        default:
+            break;
+    }
+
+    /* Actions when entering new state */
+    switch (newstate) {
+        /*********************** Operation states *********************************/
+        case GNRC_LWMAC_LISTENING: {
+            _gnrc_lwmac_set_netdev_state(gnrc_netdev, NETOPT_STATE_IDLE);
+            break;
+        }
+        case GNRC_LWMAC_SLEEPING: {
+            /* Put transceiver to sleep */
+            _gnrc_lwmac_set_netdev_state(gnrc_netdev, NETOPT_STATE_SLEEP);
+            /* We may have come here through RTT handler, so timeout may still be active */
+            gnrc_lwmac_clear_timeout(gnrc_netdev, GNRC_LWMAC_TIMEOUT_WAKEUP_PERIOD);
+
+            if (gnrc_netdev_lwmac_get_phase_backoff(gnrc_netdev)) {
+                gnrc_netdev_lwmac_set_phase_backoff(gnrc_netdev, false);
+                uint32_t alarm;
+
+                rtt_clear_alarm();
+                alarm = random_uint32_range(RTT_US_TO_TICKS((3 * GNRC_LWMAC_WAKEUP_DURATION_US / 2)),
+                                            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));
+                gnrc_netdev->lwmac.last_wakeup = gnrc_netdev->lwmac.last_wakeup + alarm;
+                alarm = _next_inphase_event(gnrc_netdev->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);
+            }
+
+            /* Return immediately, so no rescheduling */
+            return;
+        }
+        /* Trying to send data */
+        case GNRC_LWMAC_TRANSMITTING: {
+            rtt_handler(GNRC_LWMAC_EVENT_RTT_PAUSE, gnrc_netdev);    /**< No duty cycling while RXing */
+            _gnrc_lwmac_set_netdev_state(gnrc_netdev, NETOPT_STATE_IDLE);  /**< Power up netdev */
+            break;
+        }
+        /* Receiving incoming data */
+        case GNRC_LWMAC_RECEIVING: {
+            rtt_handler(GNRC_LWMAC_EVENT_RTT_PAUSE, gnrc_netdev);    /**< No duty cycling while TXing */
+            _gnrc_lwmac_set_netdev_state(gnrc_netdev, NETOPT_STATE_IDLE);  /**< Power up netdev */
+            break;
+        }
+        case GNRC_LWMAC_STOPPED: {
+            _gnrc_lwmac_set_netdev_state(gnrc_netdev, NETOPT_STATE_OFF);
+            break;
+        }
+        /*********************** Control states ***********************************/
+        case GNRC_LWMAC_START: {
+            rtt_handler(GNRC_LWMAC_EVENT_RTT_START, gnrc_netdev);
+            lwmac_set_state(gnrc_netdev, GNRC_LWMAC_LISTENING);
+            break;
+        }
+        case GNRC_LWMAC_STOP: {
+            rtt_handler(GNRC_LWMAC_EVENT_RTT_STOP, gnrc_netdev);
+            lwmac_set_state(gnrc_netdev, GNRC_LWMAC_STOPPED);
+            break;
+        }
+        case GNRC_LWMAC_RESET: {
+            LOG_WARNING("WARNING: [LWMAC] Reset not yet implemented\n");
+            lwmac_set_state(gnrc_netdev, GNRC_LWMAC_STOP);
+            lwmac_set_state(gnrc_netdev, GNRC_LWMAC_START);
+            break;
+        }
+        /**************************************************************************/
+        default: {
+            LOG_DEBUG("[LWMAC] No actions for entering state %u\n", newstate);
+            return;
+        }
+    }
+
+    lwmac_schedule_update(gnrc_netdev);
+}
+
+static void _sleep_management(gnrc_netdev_t *gnrc_netdev)
+{
+    /* If a packet is scheduled, no other (possible earlier) packet can be
+     * sent before the first one is handled, even no broadcast
+     */
+    if (!gnrc_lwmac_timeout_is_running(gnrc_netdev, GNRC_LWMAC_TIMEOUT_WAIT_DEST_WAKEUP)) {
+        gnrc_mac_tx_neighbor_t *neighbour;
+
+        /* Check if there is packet remaining for retransmission */
+        if (gnrc_netdev->tx.current_neighbor != NULL) {
+            neighbour = gnrc_netdev->tx.current_neighbor;
+        }
+        else {
+            /* Check if there are broadcasts to send and transmit immediately */
+            if (gnrc_priority_pktqueue_length(&(gnrc_netdev->tx.neighbors[0].queue)) > 0) {
+                gnrc_netdev->tx.current_neighbor = &(gnrc_netdev->tx.neighbors[0]);
+                lwmac_set_state(gnrc_netdev, GNRC_LWMAC_TRANSMITTING);
+                return;
+            }
+            neighbour = _next_tx_neighbor(gnrc_netdev);
+        }
+
+        if (neighbour != NULL) {
+            /* if phase is unknown, send immediately. */
+            if (neighbour->phase > RTT_TICKS_TO_US(GNRC_LWMAC_WAKEUP_INTERVAL_US)) {
+                gnrc_netdev->tx.current_neighbor = neighbour;
+                gnrc_netdev_lwmac_set_tx_continue(gnrc_netdev, false);
+                gnrc_netdev->tx.tx_burst_count = 0;
+                lwmac_set_state(gnrc_netdev, GNRC_LWMAC_TRANSMITTING);
+                return;
+            }
+
+            /* Offset in microseconds when the earliest (phase) destination
+             * node wakes up that we have packets for. */
+            int time_until_tx = RTT_TICKS_TO_US(_gnrc_lwmac_ticks_until_phase(neighbour->phase));
+
+            /* If there's not enough time to prepare a WR to catch the phase
+             * postpone to next interval */
+            if (time_until_tx < GNRC_LWMAC_WR_PREPARATION_US) {
+                time_until_tx += GNRC_LWMAC_WAKEUP_INTERVAL_US;
+            }
+            time_until_tx -= GNRC_LWMAC_WR_PREPARATION_US;
+
+            /* add a random time before goto TX, for avoiding one node for
+             * always holding the medium (if the receiver's phase is recorded earlier in this
+             * particular node) */
+            uint32_t random_backoff;
+            random_backoff = random_uint32_range(0, GNRC_LWMAC_TIME_BETWEEN_WR_US);
+            time_until_tx = time_until_tx + random_backoff;
+
+            gnrc_lwmac_set_timeout(gnrc_netdev, GNRC_LWMAC_TIMEOUT_WAIT_DEST_WAKEUP, time_until_tx);
+
+            /* Register neighbour to be the next */
+            gnrc_netdev->tx.current_neighbor = neighbour;
+
+            /* Stop dutycycling, we're preparing to send. This prevents the
+             * timeout arriving late, so that the destination phase would
+             * be missed. */
+            /* TODO: bad for power savings */
+            rtt_handler(GNRC_LWMAC_EVENT_RTT_PAUSE, gnrc_netdev);
+        }
+    }
+    else if (gnrc_lwmac_timeout_is_expired(gnrc_netdev, GNRC_LWMAC_TIMEOUT_WAIT_DEST_WAKEUP)) {
+        LOG_DEBUG("[LWMAC] Got timeout for dest wakeup, ticks: %" PRIu32 "\n", rtt_get_counter());
+        gnrc_netdev_lwmac_set_tx_continue(gnrc_netdev, false);
+        gnrc_netdev->tx.tx_burst_count = 0;
+        lwmac_set_state(gnrc_netdev, GNRC_LWMAC_TRANSMITTING);
+    }
+}
+
+static void _rx_management_failed(gnrc_netdev_t *gnrc_netdev)
+{
+    /* This may happen frequently because we'll receive WA from
+     * every node in range. */
+    LOG_DEBUG("[LWMAC] Reception was NOT successful\n");
+    gnrc_lwmac_rx_stop(gnrc_netdev);
+
+    if (gnrc_netdev->rx.rx_bad_exten_count >= GNRC_LWMAC_MAX_RX_EXTENSION_NUM) {
+        gnrc_netdev_lwmac_set_quit_rx(gnrc_netdev, true);
+    }
+
+    /* 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 < gnrc_netdev->lwmac.last_wakeup) {
+        phase = (RTT_US_TO_TICKS(GNRC_LWMAC_PHASE_MAX) - gnrc_netdev->lwmac.last_wakeup) +
+                 phase;
+    }
+    else {
+        phase = phase - gnrc_netdev->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)) {
+        gnrc_netdev_lwmac_set_quit_rx(gnrc_netdev, true);
+    }
+
+    if (gnrc_netdev_lwmac_get_quit_rx(gnrc_netdev)) {
+        lwmac_set_state(gnrc_netdev, GNRC_LWMAC_SLEEPING);
+    }
+    else {
+        /* Go back to LISTENING for keep hearing on the channel */
+        lwmac_set_state(gnrc_netdev, GNRC_LWMAC_LISTENING);
+    }
+}
+
+static void _rx_management_success(gnrc_netdev_t *gnrc_netdev)
+{
+    LOG_DEBUG("[LWMAC] Reception was successful\n");
+    gnrc_lwmac_rx_stop(gnrc_netdev);
+    /* Dispatch received packets, timing is not critical anymore */
+    gnrc_mac_dispatch(&gnrc_netdev->rx);
+
+    /* 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 < gnrc_netdev->lwmac.last_wakeup) {
+        phase = (RTT_US_TO_TICKS(GNRC_LWMAC_PHASE_MAX) - gnrc_netdev->lwmac.last_wakeup) +
+                 phase;
+    }
+    else {
+        phase = phase - gnrc_netdev->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)) {
+        gnrc_netdev_lwmac_set_quit_rx(gnrc_netdev, true);
+    }
+
+    if (gnrc_netdev_lwmac_get_quit_rx(gnrc_netdev)) {
+        lwmac_set_state(gnrc_netdev, GNRC_LWMAC_SLEEPING);
+    }
+    else {
+        /* Go back to LISTENING after successful reception */
+        lwmac_set_state(gnrc_netdev, GNRC_LWMAC_LISTENING);
+    }
+}
+static void _rx_management(gnrc_netdev_t *gnrc_netdev)
+{
+    gnrc_lwmac_rx_state_t state_rx = gnrc_netdev->rx.state;
+
+    switch (state_rx) {
+        case GNRC_LWMAC_RX_STATE_STOPPED: {
+            gnrc_lwmac_rx_start(gnrc_netdev);
+            gnrc_lwmac_rx_update(gnrc_netdev);
+            break;
+        }
+        case GNRC_LWMAC_RX_STATE_FAILED: {
+            _rx_management_failed(gnrc_netdev);
+            break;
+        }
+        case GNRC_LWMAC_RX_STATE_SUCCESSFUL: {
+            _rx_management_success(gnrc_netdev);
+            break;
+        }
+        default:
+            gnrc_lwmac_rx_update(gnrc_netdev);
+    }
+
+    /* If state has changed, reschedule main state machine */
+    if (state_rx != gnrc_netdev->rx.state) {
+        lwmac_schedule_update(gnrc_netdev);
+    }
+}
+
+static void _tx_management_stopped(gnrc_netdev_t *gnrc_netdev)
+{
+    gnrc_pktsnip_t *pkt;
+
+    /* If there is packet remaining for retransmission,
+     * retransmit it (i.e., the retransmission scheme of LWMAC). */
+    if (gnrc_netdev->tx.packet != NULL) {
+        LOG_WARNING("WARNING: [LWMAC] TX %d times retry\n",
+                         gnrc_netdev->tx.tx_retry_count);
+        gnrc_netdev->tx.state = GNRC_LWMAC_TX_STATE_INIT;
+        gnrc_netdev->tx.wr_sent = 0;
+        gnrc_lwmac_tx_update(gnrc_netdev);
+    }
+    else {
+        if ((pkt = gnrc_priority_pktqueue_pop(
+                 &gnrc_netdev->tx.current_neighbor->queue))) {
+            gnrc_netdev->tx.tx_retry_count = 0;
+            gnrc_lwmac_tx_start(gnrc_netdev, pkt, gnrc_netdev->tx.current_neighbor);
+            gnrc_lwmac_tx_update(gnrc_netdev);
+        }
+        else {
+            /* Shouldn't happen, but never observed this case */
+            lwmac_set_state(gnrc_netdev, GNRC_LWMAC_SLEEPING);
+        }
+    }
+}
+
+static void _tx_management_success(gnrc_netdev_t *gnrc_netdev)
+{
+    if (gnrc_netdev->tx.current_neighbor == &(gnrc_netdev->tx.neighbors[0])) {
+        LOG_INFO("[LWMAC] Broadcast transmission done\n");
+    }
+
+    gnrc_lwmac_tx_stop(gnrc_netdev);
+
+    /* In case have pending packets for the same receiver, continue to
+     * send immediately, before the maximum transmit-limit */
+    if ((gnrc_netdev_lwmac_get_tx_continue(gnrc_netdev)) &&
+        (gnrc_netdev->tx.tx_burst_count < GNRC_LWMAC_MAX_TX_BURST_PKT_NUM)) {
+        lwmac_schedule_update(gnrc_netdev);
+    }
+    else {
+        lwmac_set_state(gnrc_netdev, GNRC_LWMAC_SLEEPING);
+    }
+}
+
+static void _tx_management(gnrc_netdev_t *gnrc_netdev)
+{
+    gnrc_lwmac_tx_state_t state_tx = gnrc_netdev->tx.state;
+
+    switch (state_tx) {
+        case GNRC_LWMAC_TX_STATE_STOPPED: {
+            _tx_management_stopped(gnrc_netdev);
+            break;
+        }
+        case GNRC_LWMAC_TX_STATE_FAILED: {
+            /* If transmission failure, do not try burst transmissions and quit other
+             * transmission attempts in this cycle for collision avoidance */
+            gnrc_netdev_lwmac_set_tx_continue(gnrc_netdev, false);
+            gnrc_netdev_lwmac_set_quit_tx(gnrc_netdev, true);
+            /* falls through */
+            /* TX packet will therefore be dropped. No automatic resending here,
+             * we did our best.
+             */
+        }
+        case GNRC_LWMAC_TX_STATE_SUCCESSFUL: {
+            _tx_management_success(gnrc_netdev);
+            break;
+        }
+        default:
+            gnrc_lwmac_tx_update(gnrc_netdev);
+    }
+
+    /* If state has changed, reschedule main state machine */
+    if (state_tx != gnrc_netdev->tx.state) {
+        lwmac_schedule_update(gnrc_netdev);
+    }
+}
+
+static void _lwmac_update_listening(gnrc_netdev_t *gnrc_netdev)
+{
+    /* In case has pending packet to send, clear rtt alarm thus to goto
+     * transmission initialization (in SLEEPING management) right after the
+     * listening period */
+    if ((_next_tx_neighbor(gnrc_netdev) != NULL) ||
+        (gnrc_netdev->tx.current_neighbor != NULL)) {
+        rtt_handler(GNRC_LWMAC_EVENT_RTT_PAUSE, gnrc_netdev);
+    }
+
+    /* Set timeout for if there's no successful rx transaction that will
+     * change state to SLEEPING. */
+    if (!gnrc_lwmac_timeout_is_running(gnrc_netdev, GNRC_LWMAC_TIMEOUT_WAKEUP_PERIOD)) {
+        gnrc_lwmac_set_timeout(gnrc_netdev, GNRC_LWMAC_TIMEOUT_WAKEUP_PERIOD, GNRC_LWMAC_WAKEUP_DURATION_US);
+    }
+    else if (gnrc_lwmac_timeout_is_expired(gnrc_netdev, GNRC_LWMAC_TIMEOUT_WAKEUP_PERIOD)) {
+        /* Dispatch first as there still may be broadcast packets. */
+        gnrc_mac_dispatch(&gnrc_netdev->rx);
+
+        gnrc_netdev->lwmac.state = GNRC_LWMAC_SLEEPING;
+        /* Enable duty cycling again */
+        rtt_handler(GNRC_LWMAC_EVENT_RTT_RESUME, gnrc_netdev);
+
+        _gnrc_lwmac_set_netdev_state(gnrc_netdev, NETOPT_STATE_SLEEP);
+        gnrc_lwmac_clear_timeout(gnrc_netdev, GNRC_LWMAC_TIMEOUT_WAKEUP_PERIOD);
+
+        /* if there is a packet for transmission, schedule update to start
+         * transmission initialization immediately. */
+        gnrc_mac_tx_neighbor_t *neighbour = _next_tx_neighbor(gnrc_netdev);
+        if ((neighbour != NULL) || (gnrc_netdev->tx.current_neighbor != NULL)) {
+            /* This triggers packet sending procedure in sleeping immediately. */
+            lwmac_schedule_update(gnrc_netdev);
+            return;
+        }
+    }
+
+    if (gnrc_priority_pktqueue_length(&gnrc_netdev->rx.queue) > 0) {
+        /* Do wake-up extension in each packet reception. */
+        gnrc_lwmac_clear_timeout(gnrc_netdev, GNRC_LWMAC_TIMEOUT_WAKEUP_PERIOD);
+        lwmac_set_state(gnrc_netdev, GNRC_LWMAC_RECEIVING);
+    }
+}
+
+/* Main state machine. Call whenever something happens */
+static bool lwmac_update(gnrc_netdev_t *gnrc_netdev)
+{
+    gnrc_netdev_lwmac_set_reschedule(gnrc_netdev, false);
+
+    switch (gnrc_netdev->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
+             * neighbor nodes */
+            if (gnrc_netdev_lwmac_get_quit_tx(gnrc_netdev)) {
+                return false;
+            }
+
+            _sleep_management(gnrc_netdev);
+            break;
+        }
+        case GNRC_LWMAC_LISTENING: {
+            _lwmac_update_listening(gnrc_netdev);
+            break;
+        }
+        case GNRC_LWMAC_RECEIVING: {
+            _rx_management(gnrc_netdev);
+            break;
+        }
+        case GNRC_LWMAC_TRANSMITTING: {
+            _tx_management(gnrc_netdev);
+            break;
+        }
+        default:
+            LOG_DEBUG("[LWMAC] No actions in state %u\n", gnrc_netdev->lwmac.state);
+    }
+
+    return gnrc_netdev_lwmac_get_reschedule(gnrc_netdev);
+}
+
+static void rtt_cb(void *arg)
+{
+    msg_t msg;
+
+    msg.content.value = ((uint32_t) arg) & 0xffff;
+    msg.type = GNRC_LWMAC_EVENT_RTT_TYPE;
+    msg_send(&msg, lwmac_pid);
+
+    if (sched_context_switch_request) {
+        thread_yield();
+    }
+}
+
+void rtt_handler(uint32_t event, gnrc_netdev_t *gnrc_netdev)
+{
+    uint32_t alarm;
+
+    switch (event & 0xffff) {
+        case GNRC_LWMAC_EVENT_RTT_WAKEUP_PENDING: {
+            /* A new cycle starts, set sleep timing and initialize related MAC-info flags. */
+            gnrc_netdev->lwmac.last_wakeup = rtt_get_alarm();
+            alarm = _next_inphase_event(gnrc_netdev->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_netdev_lwmac_set_quit_tx(gnrc_netdev, false);
+            gnrc_netdev_lwmac_set_quit_rx(gnrc_netdev, false);
+            gnrc_netdev_lwmac_set_phase_backoff(gnrc_netdev, false);
+            gnrc_netdev->rx.rx_bad_exten_count = 0;
+            lwmac_set_state(gnrc_netdev, GNRC_LWMAC_LISTENING);
+            break;
+        }
+        case GNRC_LWMAC_EVENT_RTT_SLEEP_PENDING: {
+            /* Set next wake-up timing. */
+            alarm = _next_inphase_event(gnrc_netdev->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(gnrc_netdev, GNRC_LWMAC_SLEEPING);
+            break;
+        }
+        /* Set initial wake-up alarm that starts the cycle */
+        case GNRC_LWMAC_EVENT_RTT_START: {
+            LOG_DEBUG("[LWMAC] RTT: Initialize duty cycling\n");
+            alarm = rtt_get_counter() + RTT_US_TO_TICKS(GNRC_LWMAC_WAKEUP_DURATION_US);
+            rtt_set_alarm(alarm, rtt_cb, (void *) GNRC_LWMAC_EVENT_RTT_SLEEP_PENDING);
+            gnrc_netdev_lwmac_set_dutycycle_active(gnrc_netdev, true);
+            break;
+        }
+        case GNRC_LWMAC_EVENT_RTT_STOP:
+        case GNRC_LWMAC_EVENT_RTT_PAUSE: {
+            rtt_clear_alarm();
+            LOG_DEBUG("[LWMAC] RTT: Stop duty cycling, now in state %u\n",
+                           gnrc_netdev->lwmac.state);
+            gnrc_netdev_lwmac_set_dutycycle_active(gnrc_netdev, false);
+            break;
+        }
+        case GNRC_LWMAC_EVENT_RTT_RESUME: {
+            LOG_DEBUG("[LWMAC] RTT: Resume duty cycling\n");
+            rtt_clear_alarm();
+            alarm = _next_inphase_event(gnrc_netdev->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_netdev_lwmac_set_dutycycle_active(gnrc_netdev, true);
+            break;
+        }
+        default:
+            break;
+    }
+}
+
+/**
+ * @brief   Function called by the device driver on device events
+ *
+ * @param[in] event         type of event
+ * @param[in] data          optional parameter
+ */
+static void _event_cb(netdev_t *dev, netdev_event_t event)
+{
+    gnrc_netdev_t *gnrc_netdev = (gnrc_netdev_t *) dev->context;
+
+    if (event == NETDEV_EVENT_ISR) {
+        msg_t msg;
+
+        msg.type = NETDEV_MSG_TYPE_EVENT;
+        msg.content.ptr = (void *) gnrc_netdev;
+
+        if (msg_send(&msg, gnrc_netdev->pid) <= 0) {
+            LOG_WARNING("WARNING: [LWMAC] gnrc_netdev: possibly lost interrupt.\n");
+        }
+    }
+    else {
+        DEBUG("gnrc_netdev: event triggered -> %i\n", event);
+        switch (event) {
+            case NETDEV_EVENT_RX_STARTED: {
+                LOG_DEBUG("[LWMAC] NETDEV_EVENT_RX_STARTED\n");
+                gnrc_netdev_set_rx_started(gnrc_netdev, true);
+                break;
+            }
+            case NETDEV_EVENT_RX_COMPLETE: {
+                LOG_DEBUG("[LWMAC] NETDEV_EVENT_RX_COMPLETE\n");
+
+                gnrc_pktsnip_t *pkt = gnrc_netdev->recv(gnrc_netdev);
+
+                /* Prevent packet corruption when a packet is sent before the previous
+                 * received packet has been downloaded. This happens e.g. when a timeout
+                 * expires that causes the tx state machine to send a packet. When a
+                 * packet arrives after the timeout, the notification is queued but the
+                 * tx state machine continues to send and then destroys the received
+                 * packet in the frame buffer. After completion, the queued notification
+                 * will be handled a corrupted packet will be downloaded. Therefore
+                 * keep track that RX_STARTED is followed by RX_COMPLETE.
+                 *
+                 * TODO: transceivers might have 2 frame buffers, so make this optional
+                 */
+                if (pkt == NULL) {
+                    gnrc_netdev_set_rx_started(gnrc_netdev, false);
+                    break;
+                }
+
+                gnrc_netdev_set_rx_started(gnrc_netdev, false);
+
+                if (!gnrc_mac_queue_rx_packet(&gnrc_netdev->rx, 0, pkt)) {
+                    LOG_ERROR("ERROR: [LWMAC] Can't push RX packet @ %p, memory full?\n", pkt);
+                    gnrc_pktbuf_release(pkt);
+                    break;
+                }
+                lwmac_schedule_update(gnrc_netdev);
+                break;
+            }
+            case NETDEV_EVENT_TX_STARTED: {
+                gnrc_netdev_set_tx_feedback(gnrc_netdev, TX_FEEDBACK_UNDEF);
+                gnrc_netdev_set_rx_started(gnrc_netdev, false);
+                break;
+            }
+            case NETDEV_EVENT_TX_COMPLETE: {
+                gnrc_netdev_set_tx_feedback(gnrc_netdev, TX_FEEDBACK_SUCCESS);
+                gnrc_netdev_set_rx_started(gnrc_netdev, false);
+                lwmac_schedule_update(gnrc_netdev);
+                break;
+            }
+            case NETDEV_EVENT_TX_NOACK: {
+                gnrc_netdev_set_tx_feedback(gnrc_netdev, TX_FEEDBACK_NOACK);
+                gnrc_netdev_set_rx_started(gnrc_netdev, false);
+                lwmac_schedule_update(gnrc_netdev);
+                break;
+            }
+            case NETDEV_EVENT_TX_MEDIUM_BUSY: {
+                gnrc_netdev_set_tx_feedback(gnrc_netdev, TX_FEEDBACK_BUSY);
+                gnrc_netdev_set_rx_started(gnrc_netdev, false);
+                lwmac_schedule_update(gnrc_netdev);
+                break;
+            }
+            default:
+                LOG_WARNING("WARNING: [LWMAC] Unhandled netdev event: %u\n", event);
+        }
+    }
+}
+
+/**
+ * @brief   Startup code and event loop of the LWMAC layer
+ *
+ * @param[in] args          expects a pointer to the underlying netdev device
+ *
+ * @return                  never returns
+ */
+static void *_lwmac_thread(void *args)
+{
+    gnrc_netdev_t *gnrc_netdev = (gnrc_netdev_t *)args;
+    netdev_t *dev = gnrc_netdev->dev;
+
+    gnrc_netdev->pid = thread_getpid();
+
+    gnrc_netapi_opt_t *opt;
+    int res;
+    msg_t msg, reply, msg_queue[GNRC_LWMAC_IPC_MSG_QUEUE_SIZE];
+
+    LOG_INFO("[LWMAC] Starting LWMAC\n");
+
+    /* RTT is used for scheduling wakeup */
+    rtt_init();
+
+    /* Store pid globally, so that IRQ can use it to send msg */
+    lwmac_pid = thread_getpid();
+
+    /* setup the MAC layers message queue */
+    msg_init_queue(msg_queue, GNRC_LWMAC_IPC_MSG_QUEUE_SIZE);
+
+    /* register the event callback with the device driver */
+    dev->event_callback = _event_cb;
+    dev->context = (void *) gnrc_netdev;
+
+    /* register the device to the network stack*/
+    gnrc_netif_add(thread_getpid());
+
+    /* initialize low-level driver */
+    dev->driver->init(dev);
+
+    /* Enable RX- and TX-started interrupts  */
+    netopt_enable_t enable = NETOPT_ENABLE;
+    dev->driver->set(dev, NETOPT_RX_START_IRQ, &enable, sizeof(enable));
+    dev->driver->set(dev, NETOPT_TX_START_IRQ, &enable, sizeof(enable));
+    dev->driver->set(dev, NETOPT_TX_END_IRQ, &enable, sizeof(enable));
+
+    uint16_t src_len = 8;
+    dev->driver->set(dev, NETOPT_SRC_LEN, &src_len, sizeof(src_len));
+
+    /* Get own address from netdev */
+    gnrc_netdev->l2_addr_len = dev->driver->get(dev, NETOPT_ADDRESS_LONG,
+                                                &gnrc_netdev->l2_addr,
+                                                IEEE802154_LONG_ADDRESS_LEN);
+    assert(gnrc_netdev->l2_addr_len > 0);
+
+    /* Initialize broadcast sequence number. This at least differs from board
+     * to board */
+    gnrc_netdev->tx.bcast_seqnr = gnrc_netdev->l2_addr[0];
+
+    /* Reset all timeouts just to be sure */
+    gnrc_lwmac_reset_timeouts(gnrc_netdev);
+
+    /* Start duty cycling */
+    lwmac_set_state(gnrc_netdev, GNRC_LWMAC_START);
+
+#if (GNRC_LWMAC_ENABLE_DUTYCYLE_RECORD == 1)
+    /* Start duty cycle recording */
+    gnrc_netdev->lwmac.system_start_time_ticks = rtt_get_counter();
+    gnrc_netdev->lwmac.last_radio_on_time_ticks = gnrc_netdev->lwmac.system_start_time_ticks;
+    gnrc_netdev->lwmac.awake_duration_sum_ticks = 0;
+    gnrc_netdev->lwmac.lwmac_info |= GNRC_LWMAC_RADIO_IS_ON;
+#endif
+
+    /* start the event loop */
+    while (1) {
+        msg_receive(&msg);
+
+        /* Handle NETDEV, NETAPI, RTT and TIMEOUT messages */
+        switch (msg.type) {
+            /* RTT raised an interrupt */
+            case GNRC_LWMAC_EVENT_RTT_TYPE: {
+                if (gnrc_netdev_lwmac_get_dutycycle_active(gnrc_netdev)) {
+                    rtt_handler(msg.content.value, gnrc_netdev);
+                    lwmac_schedule_update(gnrc_netdev);
+                }
+                else {
+                    LOG_DEBUG("[LWMAC] Ignoring late RTT event while dutycycling is off\n");
+                }
+                break;
+            }
+            /* An LWMAC timeout occured */
+            case GNRC_LWMAC_EVENT_TIMEOUT_TYPE: {
+                gnrc_lwmac_timeout_make_expire((gnrc_lwmac_timeout_t *) msg.content.ptr);
+                lwmac_schedule_update(gnrc_netdev);
+                break;
+            }
+            /* Transceiver raised an interrupt */
+            case NETDEV_MSG_TYPE_EVENT: {
+                LOG_DEBUG("[LWMAC] GNRC_NETDEV_MSG_TYPE_EVENT received\n");
+                /* Forward event back to driver */
+                dev->driver->isr(dev);
+                break;
+            }
+            /* TX: Queue for sending */
+            case GNRC_NETAPI_MSG_TYPE_SND: {
+                /* TODO: how to announce failure to upper layers? */
+                LOG_DEBUG("[LWMAC] GNRC_NETAPI_MSG_TYPE_SND received\n");
+                gnrc_pktsnip_t *pkt = (gnrc_pktsnip_t *) msg.content.ptr;
+
+                if (!gnrc_mac_queue_tx_packet(&gnrc_netdev->tx, 0, pkt)) {
+                    gnrc_pktbuf_release(pkt);
+                    LOG_WARNING("WARNING: [LWMAC] TX queue full, drop packet\n");
+                }
+
+                lwmac_schedule_update(gnrc_netdev);
+                break;
+            }
+            /* NETAPI set/get. Can't this be refactored away from here? */
+            case GNRC_NETAPI_MSG_TYPE_SET: {
+                LOG_DEBUG("[LWMAC] GNRC_NETAPI_MSG_TYPE_SET received\n");
+                opt = (gnrc_netapi_opt_t *)msg.content.ptr;
+
+                /* Depending on option forward to NETDEV or handle here */
+                switch (opt->opt) {
+                    /* Handle state change requests */
+                    case NETOPT_STATE: {
+                        netopt_state_t *state = (netopt_state_t *) opt->data;
+                        res = opt->data_len;
+                        switch (*state) {
+                            case NETOPT_STATE_OFF: {
+                                lwmac_set_state(gnrc_netdev, GNRC_LWMAC_STOP);
+                                break;
+                            }
+                            case NETOPT_STATE_IDLE: {
+                                lwmac_set_state(gnrc_netdev, GNRC_LWMAC_START);
+                                break;
+                            }
+                            case NETOPT_STATE_RESET: {
+                                lwmac_set_state(gnrc_netdev, GNRC_LWMAC_RESET);
+                                break;
+                            }
+                            default:
+                                res = -EINVAL;
+                                LOG_ERROR("ERROR: [LWMAC] NETAPI tries to set unsupported"
+                                               " state %u\n",*state);
+                        }
+                        lwmac_schedule_update(gnrc_netdev);
+                        break;
+                    }
+                    /* Forward to netdev by default*/
+                    default:
+                        /* set option for device driver */
+                        res = dev->driver->set(dev, opt->opt, opt->data, opt->data_len);
+                        LOG_DEBUG("[LWMAC] Response of netdev->set: %i\n", res);
+                }
+
+                /* send reply to calling thread */
+                reply.type = GNRC_NETAPI_MSG_TYPE_ACK;
+                reply.content.value = (uint32_t)res;
+                msg_reply(&msg, &reply);
+                break;
+            }
+            case GNRC_NETAPI_MSG_TYPE_GET: {
+                /* TODO: filter out MAC layer options -> for now forward
+                         everything to the device driver */
+                LOG_DEBUG("[LWMAC] GNRC_NETAPI_MSG_TYPE_GET received\n");
+                /* read incoming options */
+                opt = (gnrc_netapi_opt_t *)msg.content.ptr;
+                /* get option from device driver */
+                res = dev->driver->get(dev, opt->opt, opt->data, opt->data_len);
+                LOG_DEBUG("[LWMAC] Response of netdev->get: %i\n", res);
+                /* send reply to calling thread */
+                reply.type = GNRC_NETAPI_MSG_TYPE_ACK;
+                reply.content.value = (uint32_t)res;
+                msg_reply(&msg, &reply);
+                break;
+            }
+            default:
+                LOG_ERROR("ERROR: [LWMAC] Unknown command %" PRIu16 "\n", msg.type);
+                break;
+        }
+
+        /* Execute main state machine because something just happend*/
+        while (gnrc_netdev_lwmac_get_reschedule(gnrc_netdev)) {
+            lwmac_update(gnrc_netdev);
+        }
+    }
+
+    LOG_ERROR("ERROR: [LWMAC] terminated\n");
+
+    /* never reached */
+    return NULL;
+}
+
+kernel_pid_t gnrc_lwmac_init(char *stack, int stacksize, char priority,
+                             const char *name, gnrc_netdev_t *dev)
+{
+    kernel_pid_t res;
+
+    /* check if given netdev device is defined and the driver is set */
+    if (dev == NULL || dev->dev == NULL) {
+        LOG_ERROR("ERROR: [LWMAC] No netdev supplied or driver not set\n");
+        return -ENODEV;
+    }
+
+    /* create new LWMAC thread */
+    res = thread_create(stack, stacksize, priority, THREAD_CREATE_STACKTEST,
+                        _lwmac_thread, (void *)dev, name);
+    if (res <= 0) {
+        LOG_ERROR("ERROR: [LWMAC] Couldn't create thread\n");
+        return -EINVAL;
+    }
+
+    return res;
+}
diff --git a/sys/net/gnrc/link_layer/lwmac/lwmac_internal.c b/sys/net/gnrc/link_layer/lwmac/lwmac_internal.c
new file mode 100644
index 0000000000000000000000000000000000000000..5ec668601333aaec4cb0522f16c61f02825a6bbf
--- /dev/null
+++ b/sys/net/gnrc/link_layer/lwmac/lwmac_internal.c
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2015 Daniel Krebs
+ *               2016 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_lwmac
+ * @{
+ *
+ * @file
+ * @brief       Implementation of internal functions of LWMAC
+ *
+ * @author      Daniel Krebs <github@daniel-krebs.net>
+ * @author      Shuguo Zhuo  <shuguo.zhuo@inria.fr>
+ * @}
+ */
+
+#include <stdbool.h>
+
+#include "periph/rtt.h"
+#include "net/gnrc.h"
+#include "net/gnrc/mac/mac.h"
+#include "net/gnrc/netdev.h"
+#include "net/gnrc/lwmac/lwmac.h"
+#include "include/lwmac_internal.h"
+
+#define ENABLE_DEBUG    (0)
+#include "debug.h"
+
+int _gnrc_lwmac_parse_packet(gnrc_pktsnip_t *pkt, gnrc_lwmac_packet_info_t *info)
+{
+    gnrc_netif_hdr_t *netif_hdr;
+    gnrc_pktsnip_t *lwmac_snip;
+    gnrc_lwmac_hdr_t *lwmac_hdr;
+
+    assert(info != NULL);
+    assert(pkt != NULL);
+
+    netif_hdr = (gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_NETIF))->data;
+    if (netif_hdr == NULL) {
+        return -1;
+    }
+
+    /* Dissect LWMAC header, Every frame has header as first member */
+    lwmac_hdr = (gnrc_lwmac_hdr_t *) pkt->data;
+    switch (lwmac_hdr->type) {
+        case GNRC_LWMAC_FRAMETYPE_WR: {
+            lwmac_snip = gnrc_pktbuf_mark(pkt, sizeof(gnrc_lwmac_frame_wr_t),
+                                          GNRC_NETTYPE_LWMAC);
+            break;
+        }
+        case GNRC_LWMAC_FRAMETYPE_WA: {
+            lwmac_snip = gnrc_pktbuf_mark(pkt, sizeof(gnrc_lwmac_frame_wa_t),
+                                          GNRC_NETTYPE_LWMAC);
+            break;
+        }
+        case GNRC_LWMAC_FRAMETYPE_DATA_PENDING:
+        case GNRC_LWMAC_FRAMETYPE_DATA: {
+            lwmac_snip = gnrc_pktbuf_mark(pkt, sizeof(gnrc_lwmac_frame_data_t),
+                                          GNRC_NETTYPE_LWMAC);
+            break;
+        }
+        case GNRC_LWMAC_FRAMETYPE_BROADCAST: {
+            lwmac_snip = gnrc_pktbuf_mark(pkt, sizeof(gnrc_lwmac_frame_broadcast_t),
+                                          GNRC_NETTYPE_LWMAC);
+            break;
+        }
+        default: {
+            return -2;
+        }
+    }
+
+    /* Memory location may have changed while marking */
+    lwmac_hdr = lwmac_snip->data;
+
+    if (lwmac_hdr->type == GNRC_LWMAC_FRAMETYPE_WA) {
+        /* WA is broadcast, so get dst address out of header instead of netif */
+        info->dst_addr = ((gnrc_lwmac_frame_wa_t *)lwmac_hdr)->dst_addr;
+    }
+    else if (lwmac_hdr->type == GNRC_LWMAC_FRAMETYPE_WR) {
+        /* WR is broadcast, so get dst address out of header instead of netif */
+        info->dst_addr = ((gnrc_lwmac_frame_wr_t *)lwmac_hdr)->dst_addr;
+    }
+    else 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);
+    }
+
+    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 = lwmac_hdr;
+    return 0;
+}
+
+void _gnrc_lwmac_set_netdev_state(gnrc_netdev_t *gnrc_netdev, netopt_state_t devstate)
+{
+    gnrc_netdev->dev->driver->set(gnrc_netdev->dev,
+                                  NETOPT_STATE,
+                                  &devstate,
+                                  sizeof(devstate));
+
+#if (GNRC_LWMAC_ENABLE_DUTYCYLE_RECORD == 1)
+    if (devstate == NETOPT_STATE_IDLE) {
+        if (!(gnrc_netdev->lwmac.lwmac_info & GNRC_LWMAC_RADIO_IS_ON)) {
+            gnrc_netdev->lwmac.last_radio_on_time_ticks = rtt_get_counter();
+            gnrc_netdev->lwmac.lwmac_info |= GNRC_LWMAC_RADIO_IS_ON;
+        }
+        return;
+    }
+    else if ((devstate == NETOPT_STATE_SLEEP) &&
+             (gnrc_netdev->lwmac.lwmac_info & GNRC_LWMAC_RADIO_IS_ON)) {
+        gnrc_netdev->lwmac.radio_off_time_ticks = rtt_get_counter();
+
+        gnrc_netdev->lwmac.awake_duration_sum_ticks +=
+            (gnrc_netdev->lwmac.radio_off_time_ticks -
+             gnrc_netdev->lwmac.last_radio_on_time_ticks);
+
+        gnrc_netdev->lwmac.lwmac_info &= ~GNRC_LWMAC_RADIO_IS_ON;
+    }
+#endif
+}
+
+netopt_state_t _gnrc_lwmac_get_netdev_state(gnrc_netdev_t *gnrc_netdev)
+{
+    netopt_state_t state;
+
+    if (0 < gnrc_netdev->dev->driver->get(gnrc_netdev->dev,
+                                          NETOPT_STATE,
+                                          &state,
+                                          sizeof(state))) {
+        return state;
+    }
+    return -1;
+}
+
+int _gnrc_lwmac_dispatch_defer(gnrc_pktsnip_t *buffer[], gnrc_pktsnip_t *pkt)
+{
+    assert(buffer != NULL);
+    assert(pkt != NULL);
+
+    /* We care about speed here, so assume packet structure */
+    assert(pkt->next->type == GNRC_NETTYPE_LWMAC);
+    assert(pkt->next->next->type == GNRC_NETTYPE_NETIF);
+
+    gnrc_lwmac_frame_broadcast_t *bcast = NULL;
+    if (((gnrc_lwmac_hdr_t *)pkt->next->data)->type == GNRC_LWMAC_FRAMETYPE_BROADCAST) {
+        bcast = pkt->next->data;
+    }
+
+    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;
+        }
+        else if (bcast &&
+                (((gnrc_lwmac_hdr_t *)buffer[i]->next->data)->type == GNRC_LWMAC_FRAMETYPE_BROADCAST) &&
+                (bcast->seq_nr == ((gnrc_lwmac_frame_broadcast_t *)buffer[i]->next->data)->seq_nr)) {
+                /* Filter same broadcasts, compare sequence number */
+                gnrc_netif_hdr_t *hdr_queued, *hdr_new;
+                hdr_new = pkt->next->next->data;
+                hdr_queued = buffer[i]->next->next->data;
+
+                /* Sequence numbers match, compare source addresses */
+                if ((hdr_new->src_l2addr_len == hdr_queued->src_l2addr_len) &&
+                    (memcmp(gnrc_netif_hdr_get_src_addr(hdr_new),
+                            gnrc_netif_hdr_get_src_addr(hdr_queued),
+                            hdr_new->src_l2addr_len) == 0)) {
+                    /* Source addresses match, same packet */
+                    DEBUG("[LWMAC] Found duplicate broadcast packet, dropping\n");
+                    gnrc_pktbuf_release(pkt);
+                    return -2;
+                }
+        }
+    }
+
+    DEBUG("[LWMAC] Dispatch buffer full, dropping packet\n");
+    gnrc_pktbuf_release(pkt);
+
+    return -1;
+}
diff --git a/sys/net/gnrc/link_layer/lwmac/rx_state_machine.c b/sys/net/gnrc/link_layer/lwmac/rx_state_machine.c
new file mode 100644
index 0000000000000000000000000000000000000000..3f35ad7e688798d7153757e23a616b1357b1c3df
--- /dev/null
+++ b/sys/net/gnrc/link_layer/lwmac/rx_state_machine.c
@@ -0,0 +1,437 @@
+/*
+ * Copyright (C) 2015 Daniel Krebs
+ *               2016 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_lwmac
+ * @{
+ *
+ * @file
+ * @brief       Implementation of RX state machine of LWMAC protocol
+ *
+ * @author      Daniel Krebs <github@daniel-krebs.net>
+ * @author      Shuguo Zhuo  <shuguo.zhuo@inria.fr>
+ * @}
+ */
+
+#include "net/gnrc.h"
+#include "net/gnrc/lwmac/lwmac.h"
+#include "net/gnrc/mac/internal.h"
+#include "net/gnrc/lwmac/timeout.h"
+#include "include/rx_state_machine.h"
+#include "include/lwmac_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   Flag to track if the receiver has got a broadcast packet
+ */
+#define GNRC_LWMAC_RX_FOUND_BROADCAST         (0x01U)
+
+/**
+ * @brief   Flag to track if the receiver has got a WR packet
+ */
+#define GNRC_LWMAC_RX_FOUND_WR                (0x02U)
+
+/**
+ * @brief   Flag to track if the receiver has got a data packet
+ */
+#define GNRC_LWMAC_RX_FOUND_DATA              (0x04U)
+
+static uint8_t _packet_process_in_wait_for_wr(gnrc_netdev_t *gnrc_netdev)
+{
+    uint8_t rx_info = 0;
+    gnrc_pktsnip_t *pkt;
+
+    assert(gnrc_netdev != NULL);
+
+    while ((pkt = gnrc_priority_pktqueue_pop(&gnrc_netdev->rx.queue)) != NULL) {
+        LOG_DEBUG("[LWMAC-rx] Inspecting pkt @ %p\n", pkt);
+
+        /* Parse packet */
+        gnrc_lwmac_packet_info_t info;
+
+        if (_gnrc_lwmac_parse_packet(pkt, &info) != 0) {
+            LOG_DEBUG("[LWMAC-rx] Packet could not be parsed\n");
+            gnrc_pktbuf_release(pkt);
+            continue;
+        }
+
+        if (info.header->type == GNRC_LWMAC_FRAMETYPE_BROADCAST) {
+            _gnrc_lwmac_dispatch_defer(gnrc_netdev->rx.dispatch_buffer, pkt);
+            gnrc_mac_dispatch(&gnrc_netdev->rx);
+            rx_info |= GNRC_LWMAC_RX_FOUND_BROADCAST;
+            /* quit listening period to avoid receiving duplicate broadcast packets */
+            gnrc_netdev_lwmac_set_quit_rx(gnrc_netdev, true);
+            /* quit TX in this cycle to avoid collisions with broadcast packets */
+            gnrc_netdev_lwmac_set_quit_tx(gnrc_netdev, true);
+            break;
+        }
+
+        if (info.header->type != GNRC_LWMAC_FRAMETYPE_WR) {
+            LOG_DEBUG("[LWMAC-rx] Packet is not WR: 0x%02x\n", info.header->type);
+            gnrc_pktbuf_release(pkt);
+            continue;
+        }
+
+        /* No need to keep pkt anymore */
+        gnrc_pktbuf_release(pkt);
+
+        if (!(memcmp(&info.dst_addr.addr, &gnrc_netdev->l2_addr,
+                     gnrc_netdev->l2_addr_len) == 0)) {
+            LOG_DEBUG("[LWMAC-rx] Packet is WR but not for us\n");
+            /* quit TX in this cycle to avoid collisions with other senders, since
+             * found ongoing WR (preamble) stream */
+            gnrc_netdev_lwmac_set_quit_tx(gnrc_netdev, true);
+            continue;
+        }
+
+        /* If reach here, the node gets a WR for itself. */
+        /* Save source address for later addressing */
+        gnrc_netdev->rx.l2_addr = info.src_addr;
+
+        rx_info |= GNRC_LWMAC_RX_FOUND_WR;
+        break;
+    }
+
+    return rx_info;
+}
+
+/* return false if send wa failed, otherwise return true */
+static bool _send_wa(gnrc_netdev_t *gnrc_netdev)
+{
+    gnrc_pktsnip_t *pkt;
+    gnrc_pktsnip_t *pkt_lwmac;
+    gnrc_netif_hdr_t *nethdr_wa;
+
+    assert(gnrc_netdev != NULL);
+    assert(gnrc_netdev->rx.l2_addr.len != 0);
+
+    /* if found ongoing transmission,
+     * quit sending WA for collision avoidance. */
+    if (_gnrc_lwmac_get_netdev_state(gnrc_netdev) == NETOPT_STATE_RX) {
+        gnrc_netdev->rx.rx_bad_exten_count++;
+        return false;
+    }
+
+    /* Assemble WA packet */
+    gnrc_lwmac_frame_wa_t lwmac_hdr;
+    lwmac_hdr.header.type = GNRC_LWMAC_FRAMETYPE_WA;
+    lwmac_hdr.dst_addr = gnrc_netdev->rx.l2_addr;
+
+    uint32_t phase_now = _gnrc_lwmac_phase_now();
+
+    /* 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(gnrc_netdev->lwmac.last_wakeup)) {
+        lwmac_hdr.current_phase = (phase_now -
+                                   _gnrc_lwmac_ticks_to_phase(gnrc_netdev->lwmac.last_wakeup));
+    }
+    else {
+        lwmac_hdr.current_phase = (phase_now + RTT_US_TO_TICKS(GNRC_LWMAC_WAKEUP_INTERVAL_US)) -
+                                  _gnrc_lwmac_ticks_to_phase(gnrc_netdev->lwmac.last_wakeup);
+    }
+
+    pkt = gnrc_pktbuf_add(NULL, &lwmac_hdr, sizeof(lwmac_hdr), GNRC_NETTYPE_LWMAC);
+    if (pkt == NULL) {
+        LOG_ERROR("ERROR: [LWMAC-rx] Cannot allocate pktbuf of type GNRC_NETTYPE_LWMAC\n");
+        gnrc_netdev_lwmac_set_quit_rx(gnrc_netdev, true);
+        return false;
+    }
+    pkt_lwmac = pkt;
+
+    pkt = gnrc_pktbuf_add(pkt, NULL,
+                          sizeof(gnrc_netif_hdr_t) + gnrc_netdev->rx.l2_addr.len,
+                          GNRC_NETTYPE_NETIF);
+    if (pkt == NULL) {
+        LOG_ERROR("ERROR: [LWMAC-rx] Cannot allocate pktbuf of type GNRC_NETTYPE_NETIF\n");
+        gnrc_pktbuf_release(pkt_lwmac);
+        gnrc_netdev_lwmac_set_quit_rx(gnrc_netdev, true);
+        return false;
+    }
+
+    /* We wouldn't get here if add the NETIF header had failed, so no
+       sanity checks needed */
+    nethdr_wa = (gnrc_netif_hdr_t *)(gnrc_pktsnip_search_type(pkt,
+                                                              GNRC_NETTYPE_NETIF)->data);
+    /* Construct NETIF header and insert address for WA packet */
+    gnrc_netif_hdr_init(nethdr_wa, 0, gnrc_netdev->rx.l2_addr.len);
+
+    /* Send WA as broadcast*/
+    nethdr_wa->flags |= GNRC_NETIF_HDR_FLAGS_BROADCAST;
+
+    /* Disable Auto ACK */
+    netopt_enable_t autoack = NETOPT_DISABLE;
+    gnrc_netdev->dev->driver->set(gnrc_netdev->dev, NETOPT_AUTOACK, &autoack,
+                                  sizeof(autoack));
+
+    /* Send WA */
+    if (gnrc_netdev->send(gnrc_netdev, pkt) < 0) {
+        LOG_ERROR("ERROR: [LWMAC-rx] Send WA failed.");
+        if (pkt != NULL) {
+            gnrc_pktbuf_release(pkt);
+        }
+        gnrc_netdev_lwmac_set_quit_rx(gnrc_netdev, true);
+        return false;
+    }
+
+    /* Enable Auto ACK again for data reception */
+    autoack = NETOPT_ENABLE;
+    gnrc_netdev->dev->driver->set(gnrc_netdev->dev, NETOPT_AUTOACK, &autoack,
+                                  sizeof(autoack));
+
+    return true;
+}
+
+static uint8_t _packet_process_in_wait_for_data(gnrc_netdev_t *gnrc_netdev)
+{
+    uint8_t rx_info = 0;
+    gnrc_pktsnip_t *pkt;
+
+    assert(gnrc_netdev != NULL);
+
+    pkt = NULL;
+
+    while ((pkt = gnrc_priority_pktqueue_pop(&gnrc_netdev->rx.queue)) != NULL) {
+        LOG_DEBUG("[LWMAC-rx] Inspecting pkt @ %p\n", pkt);
+
+        /* Parse packet */
+        gnrc_lwmac_packet_info_t info;
+
+        if (_gnrc_lwmac_parse_packet(pkt, &info) != 0) {
+            LOG_DEBUG("[LWMAC-rx] Packet could not be parsed\n");
+            gnrc_pktbuf_release(pkt);
+            continue;
+        }
+
+        if (info.header->type == GNRC_LWMAC_FRAMETYPE_BROADCAST) {
+            _gnrc_lwmac_dispatch_defer(gnrc_netdev->rx.dispatch_buffer, pkt);
+            gnrc_mac_dispatch(&gnrc_netdev->rx);
+            /* quit listening period to avoid receiving duplicate broadcast packets */
+            gnrc_netdev_lwmac_set_quit_rx(gnrc_netdev, true);
+            continue;
+        }
+
+        if (!(memcmp(&info.src_addr.addr, &gnrc_netdev->rx.l2_addr.addr,
+                     gnrc_netdev->rx.l2_addr.len) == 0)) {
+            LOG_DEBUG("[LWMAC-rx] Packet is not from destination\n");
+            gnrc_pktbuf_release(pkt);
+            /* Reset timeout to wait for the data packet */
+            gnrc_lwmac_clear_timeout(gnrc_netdev, GNRC_LWMAC_TIMEOUT_DATA);
+            gnrc_lwmac_set_timeout(gnrc_netdev, GNRC_LWMAC_TIMEOUT_DATA, GNRC_LWMAC_DATA_DELAY_US);
+            continue;
+        }
+
+        if (!(memcmp(&info.dst_addr.addr, &gnrc_netdev->l2_addr,
+                     gnrc_netdev->l2_addr_len) == 0)) {
+            LOG_DEBUG("[LWMAC-rx] Packet is not for us\n");
+            gnrc_pktbuf_release(pkt);
+            /* Reset timeout to wait for the data packet */
+            gnrc_lwmac_clear_timeout(gnrc_netdev, GNRC_LWMAC_TIMEOUT_DATA);
+            gnrc_lwmac_set_timeout(gnrc_netdev, GNRC_LWMAC_TIMEOUT_DATA, GNRC_LWMAC_DATA_DELAY_US);
+            continue;
+        }
+
+        /* Sender maybe didn't get the WA */
+        if (info.header->type == GNRC_LWMAC_FRAMETYPE_WR) {
+            LOG_DEBUG("[LWMAC-rx] Found a WR while waiting for DATA\n");
+            gnrc_lwmac_clear_timeout(gnrc_netdev, GNRC_LWMAC_TIMEOUT_DATA);
+            rx_info |= GNRC_LWMAC_RX_FOUND_WR;
+            /* Push WR back to rx queue */
+            gnrc_mac_queue_rx_packet(&gnrc_netdev->rx, 0, pkt);
+            break;
+        }
+
+        switch (info.header->type) {
+            case GNRC_LWMAC_FRAMETYPE_DATA:
+            case GNRC_LWMAC_FRAMETYPE_DATA_PENDING: {
+                /* Receiver gets the data packet */
+                _gnrc_lwmac_dispatch_defer(gnrc_netdev->rx.dispatch_buffer, pkt);
+                gnrc_mac_dispatch(&gnrc_netdev->rx);
+                LOG_DEBUG("[LWMAC-rx] Found DATA!\n");
+                gnrc_lwmac_clear_timeout(gnrc_netdev, GNRC_LWMAC_TIMEOUT_DATA);
+                rx_info |= GNRC_LWMAC_RX_FOUND_DATA;
+                return rx_info;
+            }
+            default: {
+                gnrc_pktbuf_release(pkt);
+            }
+        }
+    }
+
+    return rx_info;
+}
+
+void gnrc_lwmac_rx_start(gnrc_netdev_t *gnrc_netdev)
+{
+    if (gnrc_netdev == NULL) {
+        return;
+    }
+
+    /* RX address should have been reset, probably not stopped then */
+    assert(gnrc_netdev->rx.l2_addr.len == 0);
+
+    /* Don't attempt to send a WA if channel is busy to get timings right */
+    gnrc_netdev->mac_info &= ~GNRC_NETDEV_MAC_INFO_CSMA_ENABLED;
+    netopt_enable_t csma_disable = NETOPT_DISABLE;
+    gnrc_netdev->dev->driver->set(gnrc_netdev->dev, NETOPT_CSMA, &csma_disable,
+                                  sizeof(csma_disable));
+
+    gnrc_netdev->rx.state = GNRC_LWMAC_RX_STATE_INIT;
+}
+
+void gnrc_lwmac_rx_stop(gnrc_netdev_t *gnrc_netdev)
+{
+    if (!gnrc_netdev) {
+        return;
+    }
+
+    gnrc_lwmac_clear_timeout(gnrc_netdev, GNRC_LWMAC_TIMEOUT_DATA);
+    gnrc_netdev->rx.state = GNRC_LWMAC_RX_STATE_STOPPED;
+    gnrc_netdev->rx.l2_addr.len = 0;
+}
+
+/* Returns whether rescheduling is needed or not */
+static bool _lwmac_rx_update(gnrc_netdev_t *gnrc_netdev)
+{
+    bool reschedule = false;
+
+    if (!gnrc_netdev) {
+        return reschedule;
+    }
+
+    switch (gnrc_netdev->rx.state) {
+        case GNRC_LWMAC_RX_STATE_INIT: {
+            gnrc_lwmac_clear_timeout(gnrc_netdev, GNRC_LWMAC_TIMEOUT_DATA);
+            gnrc_netdev->rx.state = GNRC_LWMAC_RX_STATE_WAIT_FOR_WR;
+            reschedule = true;
+            break;
+        }
+        case GNRC_LWMAC_RX_STATE_WAIT_FOR_WR: {
+            LOG_DEBUG("[LWMAC-rx] GNRC_LWMAC_RX_STATE_WAIT_FOR_WR\n");
+
+            uint8_t rx_info = _packet_process_in_wait_for_wr(gnrc_netdev);
+
+            /* if found broadcast packet, goto rx successful */
+            if (rx_info & GNRC_LWMAC_RX_FOUND_BROADCAST) {
+                gnrc_netdev->rx.state = GNRC_LWMAC_RX_STATE_SUCCESSFUL;
+                reschedule = true;
+                break;
+            }
+
+            if (!(rx_info & GNRC_LWMAC_RX_FOUND_WR)) {
+                LOG_DEBUG("[LWMAC-rx] No WR found, stop RX\n");
+                gnrc_netdev->rx.rx_bad_exten_count++;
+                gnrc_netdev->rx.state = GNRC_LWMAC_RX_STATE_FAILED;
+                reschedule = true;
+                break;
+            }
+
+            gnrc_priority_pktqueue_flush(&gnrc_netdev->rx.queue);
+            /* Found WR packet (preamble), goto next state to send WA (preamble-ACK) */
+            gnrc_netdev->rx.state = GNRC_LWMAC_RX_STATE_SEND_WA;
+            reschedule = true;
+            break;
+        }
+        case GNRC_LWMAC_RX_STATE_SEND_WA: {
+            LOG_DEBUG("[LWMAC-rx] GNRC_LWMAC_RX_STATE_SEND_WA\n");
+
+            if (!_send_wa(gnrc_netdev)) {
+                gnrc_netdev->rx.state = GNRC_LWMAC_RX_STATE_FAILED;
+                reschedule = true;
+                break;
+            }
+
+            gnrc_netdev->rx.state = GNRC_LWMAC_RX_STATE_WAIT_WA_SENT;
+            reschedule = false;
+            break;
+        }
+        case GNRC_LWMAC_RX_STATE_WAIT_WA_SENT: {
+            LOG_DEBUG("[LWMAC-rx] GNRC_LWMAC_RX_STATE_WAIT_WA_SENT\n");
+
+            if (gnrc_netdev_get_tx_feedback(gnrc_netdev) == TX_FEEDBACK_UNDEF) {
+                LOG_DEBUG("[LWMAC-rx] WA not yet completely sent\n");
+                break;
+            }
+
+            /* When reach here, WA has been sent, set timeout for expected data arrival */
+            gnrc_lwmac_set_timeout(gnrc_netdev, GNRC_LWMAC_TIMEOUT_DATA, GNRC_LWMAC_DATA_DELAY_US);
+
+            _gnrc_lwmac_set_netdev_state(gnrc_netdev, NETOPT_STATE_IDLE);
+            gnrc_netdev->rx.state = GNRC_LWMAC_RX_STATE_WAIT_FOR_DATA;
+            reschedule = false;
+            break;
+        }
+        case GNRC_LWMAC_RX_STATE_WAIT_FOR_DATA: {
+            LOG_DEBUG("[LWMAC-rx] GNRC_LWMAC_RX_STATE_WAIT_FOR_DATA\n");
+
+            uint8_t rx_info = _packet_process_in_wait_for_data(gnrc_netdev);
+
+            /* If WA got lost we wait for data but we will be hammered with WR
+             * packets. So a WR indicates a lost WA => reset RX state machine. */
+            if (rx_info & GNRC_LWMAC_RX_FOUND_WR) {
+                LOG_INFO("[LWMAC-rx] WA probably got lost, reset RX state machine\n");
+                /* Start over again */
+                gnrc_netdev->rx.state = GNRC_LWMAC_RX_STATE_INIT;
+                reschedule = true;
+                break;
+            }
+
+            /* Only timeout if no packet (presumably the expected data) is being
+             * received. This won't be blocked by WRs as they restart the state
+             * machine (see above).
+             */
+            if (gnrc_lwmac_timeout_is_expired(gnrc_netdev, GNRC_LWMAC_TIMEOUT_DATA)) {
+                if (!gnrc_netdev_get_rx_started(gnrc_netdev)) {
+                    LOG_INFO("[LWMAC-rx] DATA timed out\n");
+                    gnrc_netdev->rx.rx_bad_exten_count++;
+                    gnrc_netdev->rx.state = GNRC_LWMAC_RX_STATE_FAILED;
+                    reschedule = true;
+                }
+                else {
+                    /* If radio is receiving packet, reset wait data timeout */
+                    gnrc_lwmac_set_timeout(gnrc_netdev, GNRC_LWMAC_TIMEOUT_DATA, GNRC_LWMAC_DATA_DELAY_US);
+                }
+                break;
+            }
+
+            if (!(rx_info & GNRC_LWMAC_RX_FOUND_DATA)) {
+                LOG_DEBUG("[LWMAC-rx] No DATA yet\n");
+                break;
+            }
+
+            gnrc_netdev->rx.state = GNRC_LWMAC_RX_STATE_SUCCESSFUL;
+            reschedule = true;
+            break;
+        }
+        case GNRC_LWMAC_RX_STATE_SUCCESSFUL:
+        case GNRC_LWMAC_RX_STATE_FAILED: {
+            break;
+        }
+        case GNRC_LWMAC_RX_STATE_STOPPED: {
+            LOG_DEBUG("[LWMAC-rx] Reception state machine is stopped\n");
+        }
+    }
+    return reschedule;
+}
+
+void gnrc_lwmac_rx_update(gnrc_netdev_t *gnrc_netdev)
+{
+    /* Update until no rescheduling needed */
+    while (_lwmac_rx_update(gnrc_netdev)) {}
+}
diff --git a/sys/net/gnrc/link_layer/lwmac/timeout.c b/sys/net/gnrc/link_layer/lwmac/timeout.c
new file mode 100644
index 0000000000000000000000000000000000000000..6291f2ee930b05f83df38a97f871c80479f02db4
--- /dev/null
+++ b/sys/net/gnrc/link_layer/lwmac/timeout.c
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2015 Daniel Krebs
+ *               2016 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_lwmac
+ * @{
+ *
+ * @file
+ * @brief       Timeout handling of LWMAC protocol
+ *
+ * @author      Daniel Krebs <github@daniel-krebs.net>
+ * @author      Shuguo Zhuo  <shuguo.zhuo@inria.fr>
+ * @}
+ */
+
+#include <errno.h>
+
+#include "net/gnrc/lwmac/timeout.h"
+
+#define ENABLE_DEBUG    (0)
+#include "debug.h"
+
+#if ENABLE_DEBUG
+char *lwmac_timeout_names[] = {
+    [GNRC_LWMAC_TIMEOUT_DISABLED]              = "DISABLED",
+    [GNRC_LWMAC_TIMEOUT_WR]                    = "WR",
+    [GNRC_LWMAC_TIMEOUT_NO_RESPONSE]           = "NO_RESPONSE",
+    [GNRC_LWMAC_TIMEOUT_DATA]                  = "DATA",
+    [GNRC_LWMAC_TIMEOUT_WAIT_DEST_WAKEUP]      = "WAIT_FOR_DEST_WAKEUP",
+    [GNRC_LWMAC_TIMEOUT_WAKEUP_PERIOD]         = "WAKEUP_PERIOD",
+};
+#endif
+
+static inline void _lwmac_clear_timeout(gnrc_lwmac_timeout_t *timeout)
+{
+    assert(timeout);
+
+    xtimer_remove(&(timeout->timer));
+    timeout->type = GNRC_LWMAC_TIMEOUT_DISABLED;
+}
+
+/* Return index >= 0 if found, -ENONENT if not found */
+static int _lwmac_find_timeout(gnrc_lwmac_t *lwmac, gnrc_lwmac_timeout_type_t type)
+{
+    assert(lwmac);
+
+    for (unsigned i = 0; i < GNRC_LWMAC_TIMEOUT_COUNT; i++) {
+        if (lwmac->timeouts[i].type == type) {
+            return i;
+        }
+    }
+    return -ENOENT;
+}
+
+inline bool gnrc_lwmac_timeout_is_running(gnrc_netdev_t *gnrc_netdev,
+                                          gnrc_lwmac_timeout_type_t type)
+{
+    assert(gnrc_netdev);
+    return (_lwmac_find_timeout(&gnrc_netdev->lwmac, type) >= 0);
+}
+
+bool gnrc_lwmac_timeout_is_expired(gnrc_netdev_t *gnrc_netdev, gnrc_lwmac_timeout_type_t type)
+{
+    assert(gnrc_netdev);
+
+    int index = _lwmac_find_timeout(&gnrc_netdev->lwmac, type);
+    if (index >= 0) {
+        if (gnrc_netdev->lwmac.timeouts[index].expired) {
+            _lwmac_clear_timeout(&gnrc_netdev->lwmac.timeouts[index]);
+        }
+        return gnrc_netdev->lwmac.timeouts[index].expired;
+    }
+    return false;
+}
+
+gnrc_lwmac_timeout_t *_lwmac_acquire_timeout(gnrc_netdev_t *gnrc_netdev,
+                                             gnrc_lwmac_timeout_type_t type)
+{
+    assert(gnrc_netdev);
+
+    if (gnrc_lwmac_timeout_is_running(gnrc_netdev, type)) {
+        return NULL;
+    }
+
+    for (unsigned i = 0; i < GNRC_LWMAC_TIMEOUT_COUNT; i++) {
+        if (gnrc_netdev->lwmac.timeouts[i].type == GNRC_LWMAC_TIMEOUT_DISABLED) {
+            gnrc_netdev->lwmac.timeouts[i].type = type;
+            return &gnrc_netdev->lwmac.timeouts[i];
+        }
+    }
+    return NULL;
+}
+
+void gnrc_lwmac_timeout_make_expire(gnrc_lwmac_timeout_t *timeout)
+{
+    assert(timeout);
+
+    timeout->expired = true;
+}
+
+void gnrc_lwmac_clear_timeout(gnrc_netdev_t *gnrc_netdev, gnrc_lwmac_timeout_type_t type)
+{
+    assert(gnrc_netdev);
+
+    int index = _lwmac_find_timeout(&gnrc_netdev->lwmac, type);
+    if (index >= 0) {
+        _lwmac_clear_timeout(&gnrc_netdev->lwmac.timeouts[index]);
+    }
+}
+
+void gnrc_lwmac_set_timeout(gnrc_netdev_t *gnrc_netdev,
+                            gnrc_lwmac_timeout_type_t type,
+                            uint32_t offset)
+{
+    assert(gnrc_netdev);
+
+    gnrc_lwmac_timeout_t *timeout;
+    if ((timeout = _lwmac_acquire_timeout(gnrc_netdev, type))) {
+        DEBUG("[LWMAC] Set timeout %s in %" PRIu32 " us\n",
+              lwmac_timeout_names[type], offset);
+        timeout->expired = false;
+        timeout->msg.type = GNRC_LWMAC_EVENT_TIMEOUT_TYPE;
+        timeout->msg.content.ptr = (void *) timeout;
+        xtimer_set_msg(&(timeout->timer), offset,
+                       &(timeout->msg), gnrc_netdev->pid);
+    }
+    else {
+        DEBUG("[LWMAC] Cannot set timeout %s, too many concurrent timeouts\n",
+              lwmac_timeout_names[type]);
+    }
+}
+
+void gnrc_lwmac_reset_timeouts(gnrc_netdev_t *gnrc_netdev)
+{
+    assert(gnrc_netdev);
+
+    for (unsigned i = 0; i < GNRC_LWMAC_TIMEOUT_COUNT; i++) {
+        if (gnrc_netdev->lwmac.timeouts[i].type != GNRC_LWMAC_TIMEOUT_DISABLED) {
+            _lwmac_clear_timeout(&gnrc_netdev->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
new file mode 100644
index 0000000000000000000000000000000000000000..44df3bd520eb095cfc9d150b6106dffae9035c7f
--- /dev/null
+++ b/sys/net/gnrc/link_layer/lwmac/tx_state_machine.c
@@ -0,0 +1,810 @@
+/*
+ * Copyright (C) 2015 Daniel Krebs
+ *               2016 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_lwmac
+ * @{
+ *
+ * @file
+ * @brief       Implementation of TX state machine of LWMAC protocol
+ *
+ * @author      Daniel Krebs <github@daniel-krebs.net>
+ * @author      Shuguo Zhuo  <shuguo.zhuo@inria.fr>
+ * @}
+ */
+
+#include "periph/rtt.h"
+#include "net/gnrc.h"
+#include "net/gnrc/lwmac/lwmac.h"
+#include "random.h"
+#include "net/gnrc/mac/internal.h"
+#include "net/gnrc/lwmac/timeout.h"
+#include "include/tx_state_machine.h"
+#include "include/lwmac_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   Flag to track if send packet success
+ */
+#define GNRC_LWMAC_TX_SUCCESS         (0x01U)
+
+/**
+ * @brief   Flag to track if send packet fail
+ */
+#define GNRC_LWMAC_TX_FAIL            (0x02U)
+
+static uint8_t _send_bcast(gnrc_netdev_t *gnrc_netdev)
+{
+    assert(gnrc_netdev != NULL);
+
+    uint8_t tx_info = 0;
+    gnrc_pktsnip_t *pkt = gnrc_netdev->tx.packet;
+    bool first = false;
+
+    if (gnrc_lwmac_timeout_is_running(gnrc_netdev, GNRC_LWMAC_TIMEOUT_BROADCAST_END)) {
+        if (gnrc_lwmac_timeout_is_expired(gnrc_netdev, GNRC_LWMAC_TIMEOUT_BROADCAST_END)) {
+            gnrc_lwmac_clear_timeout(gnrc_netdev, GNRC_LWMAC_TIMEOUT_NEXT_BROADCAST);
+            gnrc_pktbuf_release(pkt);
+            gnrc_netdev->tx.packet = NULL;
+            tx_info |= GNRC_LWMAC_TX_SUCCESS;
+            return tx_info;
+        }
+    }
+    else {
+        LOG_INFO("[LWMAC-tx] Initialize broadcasting\n");
+        gnrc_lwmac_set_timeout(gnrc_netdev, GNRC_LWMAC_TIMEOUT_BROADCAST_END,
+                          GNRC_LWMAC_BROADCAST_DURATION_US);
+
+        gnrc_pktsnip_t *pkt_payload;
+
+        /* Prepare packet with LWMAC header*/
+        gnrc_lwmac_frame_broadcast_t hdr = {};
+        hdr.header.type = GNRC_LWMAC_FRAMETYPE_BROADCAST;
+        hdr.seq_nr = gnrc_netdev->tx.bcast_seqnr++;
+
+        pkt_payload = pkt->next;
+        pkt->next = gnrc_pktbuf_add(pkt->next, &hdr, sizeof(hdr), GNRC_NETTYPE_LWMAC);
+        if (pkt->next == NULL) {
+            LOG_ERROR("ERROR: [LWMAC-tx] Cannot allocate pktbuf of type FRAMETYPE_BROADCAST\n");
+            gnrc_netdev->tx.packet->next = pkt_payload;
+            /* Drop the broadcast packet */
+            LOG_ERROR("ERROR: [LWMAC-tx] Memory maybe full, drop the broadcast packet\n");
+            gnrc_pktbuf_release(gnrc_netdev->tx.packet);
+            /* clear packet point to avoid TX retry */
+            gnrc_netdev->tx.packet = NULL;
+            tx_info |= GNRC_LWMAC_TX_FAIL;
+            return tx_info;
+        }
+
+        /* No Auto-ACK for broadcast packets */
+        netopt_enable_t autoack = NETOPT_DISABLE;
+        gnrc_netdev->dev->driver->set(gnrc_netdev->dev, NETOPT_AUTOACK, &autoack,
+                                      sizeof(autoack));
+        first = true;
+    }
+
+    if (gnrc_lwmac_timeout_is_expired(gnrc_netdev, GNRC_LWMAC_TIMEOUT_NEXT_BROADCAST) ||
+        first) {
+        /* if found ongoing transmission, quit this cycle for collision avoidance.
+         * Broadcast packet will be re-queued and try to send in the next cycle. */
+        if (_gnrc_lwmac_get_netdev_state(gnrc_netdev) == NETOPT_STATE_RX) {
+            /* save pointer to netif header */
+            gnrc_pktsnip_t *netif = pkt->next->next;
+
+            /* remove LWMAC header */
+            pkt->next->next = NULL;
+            gnrc_pktbuf_release(pkt->next);
+
+            /* make append netif header after payload again */
+            pkt->next = netif;
+
+            if (!gnrc_mac_queue_tx_packet(&gnrc_netdev->tx, 0, gnrc_netdev->tx.packet)) {
+                gnrc_pktbuf_release(gnrc_netdev->tx.packet);
+                LOG_WARNING("WARNING: [LWMAC-tx] TX queue full, drop packet\n");
+            }
+            /* drop pointer so it wont be free'd */
+            gnrc_netdev->tx.packet = NULL;
+            tx_info |= GNRC_LWMAC_TX_FAIL;
+            return tx_info;
+        }
+
+        /* Don't let the packet be released yet, we want to send it again */
+        gnrc_pktbuf_hold(pkt, 1);
+
+        int res = gnrc_netdev->send(gnrc_netdev, pkt);
+        if (res < 0) {
+            LOG_ERROR("ERROR: [LWMAC-tx] Send broadcast pkt failed.");
+            tx_info |= GNRC_LWMAC_TX_FAIL;
+            return tx_info;
+        }
+
+        gnrc_lwmac_set_timeout(gnrc_netdev, GNRC_LWMAC_TIMEOUT_NEXT_BROADCAST,
+                          GNRC_LWMAC_TIME_BETWEEN_BROADCAST_US);
+        LOG_INFO("[LWMAC-tx] Broadcast sent\n");
+    }
+
+    return tx_info;
+}
+
+static uint8_t _send_wr(gnrc_netdev_t *gnrc_netdev)
+{
+    assert(gnrc_netdev != NULL);
+
+    uint8_t tx_info = 0;
+    gnrc_pktsnip_t *pkt;
+    gnrc_pktsnip_t *pkt_lwmac;
+    gnrc_netif_hdr_t *nethdr;
+
+    /* if found ongoing transmission, quit this cycle for collision avoidance.
+     * Data packet will be re-queued and try to send in the next cycle. */
+    if (_gnrc_lwmac_get_netdev_state(gnrc_netdev) == NETOPT_STATE_RX) {
+        if (!gnrc_mac_queue_tx_packet(&gnrc_netdev->tx, 0, gnrc_netdev->tx.packet)) {
+            gnrc_pktbuf_release(gnrc_netdev->tx.packet);
+            LOG_WARNING("WARNING: [LWMAC-tx] TX queue full, drop packet\n");
+        }
+        /* drop pointer so it wont be free'd */
+        gnrc_netdev->tx.packet = NULL;
+        tx_info |= GNRC_LWMAC_TX_FAIL;
+        return tx_info;
+    }
+
+    /* Assemble WR */
+    gnrc_lwmac_frame_wr_t wr_hdr = {};
+    wr_hdr.header.type = GNRC_LWMAC_FRAMETYPE_WR;
+    memcpy(&(wr_hdr.dst_addr.addr), gnrc_netdev->tx.current_neighbor->l2_addr,
+           gnrc_netdev->tx.current_neighbor->l2_addr_len);
+    wr_hdr.dst_addr.len = gnrc_netdev->tx.current_neighbor->l2_addr_len;
+
+    pkt = gnrc_pktbuf_add(NULL, &wr_hdr, sizeof(wr_hdr), GNRC_NETTYPE_LWMAC);
+    if (pkt == NULL) {
+        LOG_ERROR("ERROR: [LWMAC-tx] Cannot allocate pktbuf of type GNRC_NETTYPE_LWMAC\n");
+        gnrc_pktbuf_release(gnrc_netdev->tx.packet);
+        LOG_ERROR("ERROR: [LWMAC-tx] Memory maybe full, drop the data packet\n");
+        /* clear packet point to avoid TX retry */
+        gnrc_netdev->tx.packet = NULL;
+        tx_info |= GNRC_LWMAC_TX_FAIL;
+        return tx_info;
+    }
+
+    /* track the location of this lwmac_frame_wr_t header */
+    pkt_lwmac = pkt;
+
+    pkt = gnrc_pktbuf_add(pkt, NULL, sizeof(gnrc_netif_hdr_t), GNRC_NETTYPE_NETIF);
+    if (pkt == NULL) {
+        LOG_ERROR("ERROR: [LWMAC-tx] Cannot allocate pktbuf of type GNRC_NETTYPE_NETIF\n");
+        gnrc_pktbuf_release(pkt_lwmac);
+        LOG_ERROR("ERROR: [LWMAC-tx] Memory maybe full, drop the data packet\n");
+        gnrc_pktbuf_release(gnrc_netdev->tx.packet);
+        /* clear packet point to avoid TX retry */
+        gnrc_netdev->tx.packet = NULL;
+        tx_info |= GNRC_LWMAC_TX_FAIL;
+        return tx_info;
+    }
+
+    /* We wouldn't get here if adding the NETIF header had failed, so no
+     * sanity checks needed */
+    nethdr = (gnrc_netif_hdr_t *) (gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_NETIF))->data;
+
+    /* Construct NETIF header and insert address for WR packet */
+    gnrc_netif_hdr_init(nethdr, 0, 0);
+
+    /* Send WR as broadcast*/
+    nethdr->flags |= GNRC_NETIF_HDR_FLAGS_BROADCAST;
+
+    /* Disable Auto ACK */
+    netopt_enable_t autoack = NETOPT_DISABLE;
+    gnrc_netdev->dev->driver->set(gnrc_netdev->dev, NETOPT_AUTOACK, &autoack,
+                                  sizeof(autoack));
+
+    /* Prepare WR, this will discard any frame in the transceiver that has
+     * possibly arrived in the meantime but we don't care at this point. */
+    int res = gnrc_netdev->send(gnrc_netdev, pkt);
+    if (res < 0) {
+        LOG_ERROR("ERROR: [LWMAC-tx] Send WR failed.");
+        if (pkt != NULL) {
+            gnrc_pktbuf_release(pkt);
+        }
+        tx_info |= GNRC_LWMAC_TX_FAIL;
+        return tx_info;
+    }
+
+    gnrc_priority_pktqueue_flush(&gnrc_netdev->rx.queue);
+    return tx_info;
+}
+
+static uint8_t _packet_process_in_wait_for_wa(gnrc_netdev_t *gnrc_netdev)
+{
+    assert(gnrc_netdev != NULL);
+
+    uint8_t tx_info = 0;
+    gnrc_pktsnip_t *pkt;
+    bool found_wa = false;
+    bool postponed = false;
+    bool from_expected_destination = false;
+
+    while ((pkt = gnrc_priority_pktqueue_pop(&gnrc_netdev->rx.queue)) != NULL) {
+        LOG_DEBUG("[LWMAC-tx] Inspecting pkt @ %p\n", pkt);
+
+        /* Parse packet */
+        gnrc_lwmac_packet_info_t info;
+        int ret = _gnrc_lwmac_parse_packet(pkt, &info);
+
+        if (ret != 0) {
+            LOG_DEBUG("[LWMAC-tx] Packet could not be parsed: %i\n", ret);
+            gnrc_pktbuf_release(pkt);
+            continue;
+        }
+
+        if (memcmp(&info.src_addr.addr, &gnrc_netdev->tx.current_neighbor->l2_addr,
+                   gnrc_netdev->tx.current_neighbor->l2_addr_len) == 0) {
+            from_expected_destination = true;
+        }
+
+        if (info.header->type == GNRC_LWMAC_FRAMETYPE_BROADCAST) {
+            _gnrc_lwmac_dispatch_defer(gnrc_netdev->rx.dispatch_buffer, pkt);
+            gnrc_mac_dispatch(&gnrc_netdev->rx);
+            /* Drop pointer to it can't get released */
+            pkt = NULL;
+            continue;
+        }
+
+        /* Check if destination is talking to another node. It will sleep
+         * after a finished transaction so there's no point in trying any
+         * further now. */
+        if (!(memcmp(&info.dst_addr.addr, &gnrc_netdev->l2_addr,
+                     gnrc_netdev->l2_addr_len) == 0) && from_expected_destination) {
+            if (!gnrc_mac_queue_tx_packet(&gnrc_netdev->tx, 0, gnrc_netdev->tx.packet)) {
+                gnrc_pktbuf_release(gnrc_netdev->tx.packet);
+                LOG_WARNING("WARNING: [LWMAC-tx] TX queue full, drop packet\n");
+            }
+            /* drop pointer so it wont be free'd */
+            gnrc_netdev->tx.packet = NULL;
+            postponed = true;
+            gnrc_pktbuf_release(pkt);
+            break;
+        }
+
+        /* if found anther node is also trying to send data,
+         * quit this cycle for collision avoidance. */
+        if (info.header->type == GNRC_LWMAC_FRAMETYPE_WR) {
+            if (!gnrc_mac_queue_tx_packet(&gnrc_netdev->tx, 0, gnrc_netdev->tx.packet)) {
+                gnrc_pktbuf_release(gnrc_netdev->tx.packet);
+                LOG_WARNING("WARNING: [LWMAC-tx] TX queue full, drop packet\n");
+            }
+            /* drop pointer so it wont be free'd */
+            gnrc_netdev->tx.packet = NULL;
+            postponed = true;
+            gnrc_pktbuf_release(pkt);
+            break;
+        }
+
+        if (info.header->type != GNRC_LWMAC_FRAMETYPE_WA) {
+            LOG_DEBUG("[LWMAC-tx] Packet is not WA: 0x%02x\n", info.header->type);
+            gnrc_pktbuf_release(pkt);
+            continue;
+        }
+
+        if (from_expected_destination) {
+            /* calculate the phase of the receiver based on WA */
+            gnrc_netdev->tx.timestamp = _gnrc_lwmac_phase_now();
+            gnrc_lwmac_frame_wa_t *wa_hdr;
+            wa_hdr = (gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_LWMAC))->data;
+
+            if (gnrc_netdev->tx.timestamp >= wa_hdr->current_phase) {
+                gnrc_netdev->tx.timestamp = gnrc_netdev->tx.timestamp -
+                                            wa_hdr->current_phase;
+            }
+            else {
+                gnrc_netdev->tx.timestamp += RTT_US_TO_TICKS(GNRC_LWMAC_WAKEUP_INTERVAL_US);
+                gnrc_netdev->tx.timestamp -= wa_hdr->current_phase;
+            }
+
+            uint32_t own_phase;
+            own_phase = _gnrc_lwmac_ticks_to_phase(gnrc_netdev->lwmac.last_wakeup);
+
+            if (own_phase >= gnrc_netdev->tx.timestamp) {
+                own_phase = own_phase - gnrc_netdev->tx.timestamp;
+            }
+            else {
+                own_phase = gnrc_netdev->tx.timestamp - own_phase;
+            }
+
+            if ((own_phase < RTT_US_TO_TICKS((3 * GNRC_LWMAC_WAKEUP_DURATION_US / 2))) ||
+                (own_phase > RTT_US_TO_TICKS(GNRC_LWMAC_WAKEUP_INTERVAL_US -
+                                             (3 * GNRC_LWMAC_WAKEUP_DURATION_US / 2)))) {
+                gnrc_netdev_lwmac_set_phase_backoff(gnrc_netdev, true);
+                LOG_WARNING("WARNING: [LWMAC-tx] phase close\n");
+            }
+        }
+
+        /* No need to keep pkt anymore */
+        gnrc_pktbuf_release(pkt);
+
+        if (!from_expected_destination) {
+            LOG_DEBUG("[LWMAC-tx] Packet is not from expected destination\n");
+            break;
+        }
+
+        /* All checks passed so this must be a valid WA */
+        gnrc_lwmac_clear_timeout(gnrc_netdev, GNRC_LWMAC_TIMEOUT_WR);
+
+        found_wa = true;
+        break;
+    }
+
+    if (postponed) {
+        LOG_INFO("[LWMAC-tx] Destination is talking to another node, postpone\n");
+        tx_info |= GNRC_LWMAC_TX_FAIL;
+        return tx_info;
+    }
+
+    if (!found_wa) {
+        LOG_DEBUG("[LWMAC-tx] No WA yet\n");
+        return tx_info;
+    }
+
+    /* Save newly calculated phase for destination */
+    gnrc_netdev->tx.current_neighbor->phase = gnrc_netdev->tx.timestamp;
+    LOG_INFO("[LWMAC-tx] New phase: %" PRIu32 "\n", gnrc_netdev->tx.timestamp);
+
+    /* We've got our WA, so discard the rest, TODO: no flushing */
+    gnrc_priority_pktqueue_flush(&gnrc_netdev->rx.queue);
+
+    tx_info |= GNRC_LWMAC_TX_SUCCESS;
+    return tx_info;
+}
+
+/* return false if send data failed, otherwise return true */
+static bool _send_data(gnrc_netdev_t *gnrc_netdev)
+{
+    assert(gnrc_netdev != NULL);
+
+    gnrc_pktsnip_t *pkt = gnrc_netdev->tx.packet;
+    gnrc_pktsnip_t *pkt_payload;
+
+    /* Enable Auto ACK again */
+    netopt_enable_t autoack = NETOPT_ENABLE;
+    gnrc_netdev->dev->driver->set(gnrc_netdev->dev, NETOPT_AUTOACK,
+                                  &autoack, sizeof(autoack));
+
+    /* It's okay to retry sending DATA. Timing doesn't matter anymore and
+     * destination is waiting for a certain amount of time. */
+    uint8_t csma_retries = GNRC_LWMAC_DATA_CSMA_RETRIES;
+    gnrc_netdev->dev->driver->set(gnrc_netdev->dev, NETOPT_CSMA_RETRIES,
+                                  &csma_retries, sizeof(csma_retries));
+
+    gnrc_netdev->mac_info |= GNRC_NETDEV_MAC_INFO_CSMA_ENABLED;
+    netopt_enable_t csma_enable = NETOPT_ENABLE;
+    gnrc_netdev->dev->driver->set(gnrc_netdev->dev, NETOPT_CSMA,
+                                  &csma_enable, sizeof(csma_enable));
+
+    pkt_payload = pkt->next;
+
+    /* Insert LWMAC header above NETIF header. The burst (consecutive) transmission
+     * scheme works here (sender side). If the sender finds it has pending packets
+     * for the receiver (and under burst limit), it sets the packet type to
+     * FRAMETYPE_DATA_PENDING, to notice the receiver for next incoming packet.
+     * In case the sender has no more packet for the receiver, it simply sets the
+     * data type to FRAMETYPE_DATA. */
+    gnrc_lwmac_hdr_t hdr;
+    if ((gnrc_priority_pktqueue_length(&gnrc_netdev->tx.current_neighbor->queue) > 0) &&
+        (gnrc_netdev->tx.tx_burst_count < GNRC_LWMAC_MAX_TX_BURST_PKT_NUM)) {
+        hdr.type = GNRC_LWMAC_FRAMETYPE_DATA_PENDING;
+        gnrc_netdev_lwmac_set_tx_continue(gnrc_netdev, true);
+        gnrc_netdev->tx.tx_burst_count++;
+    }
+    else {
+        hdr.type = GNRC_LWMAC_FRAMETYPE_DATA;
+        gnrc_netdev_lwmac_set_tx_continue(gnrc_netdev, false);
+    }
+
+    pkt->next = gnrc_pktbuf_add(pkt->next, &hdr, sizeof(hdr), GNRC_NETTYPE_LWMAC);
+    if (pkt->next == NULL) {
+        LOG_ERROR("ERROR: [LWMAC-tx] Cannot allocate pktbuf of type GNRC_NETTYPE_LWMAC\n");
+        LOG_ERROR("ERROR: [LWMAC-tx] Memory maybe full, drop the data packet\n");
+        gnrc_netdev->tx.packet->next = pkt_payload;
+        gnrc_pktbuf_release(gnrc_netdev->tx.packet);
+        /* clear packet point to avoid TX retry */
+        gnrc_netdev->tx.packet = NULL;
+        return false;
+    }
+
+    /* if found ongoing transmission, quit this cycle for collision avoidance.
+     * Data packet will be re-queued and try to send in the next cycle. */
+    if (_gnrc_lwmac_get_netdev_state(gnrc_netdev) == NETOPT_STATE_RX) {
+        /* save pointer to netif header */
+        gnrc_pktsnip_t *netif = pkt->next->next;
+
+        /* remove LWMAC header */
+        pkt->next->next = NULL;
+        gnrc_pktbuf_release(pkt->next);
+
+        /* make append netif header after payload again */
+        pkt->next = netif;
+
+        if (!gnrc_mac_queue_tx_packet(&gnrc_netdev->tx, 0, gnrc_netdev->tx.packet)) {
+            gnrc_pktbuf_release(gnrc_netdev->tx.packet);
+            LOG_WARNING("WARNING: [LWMAC-tx] TX queue full, drop packet\n");
+        }
+        /* drop pointer so it wont be free'd */
+        gnrc_netdev->tx.packet = NULL;
+        return false;
+    }
+
+    /* Send data */
+    int res = gnrc_netdev->send(gnrc_netdev, pkt);
+    if (res < 0) {
+        LOG_ERROR("ERROR: [LWMAC-tx] Send data failed.");
+        if (pkt != NULL) {
+            gnrc_pktbuf_release(pkt);
+        }
+        /* clear packet point to avoid TX retry */
+        gnrc_netdev->tx.packet = NULL;
+        return false;
+    }
+
+    /* Packet has been released by netdev, so drop pointer */
+    gnrc_netdev->tx.packet = NULL;
+
+    DEBUG("[LWMAC-tx]: spent %lu WR in TX\n", gnrc_netdev->tx.wr_sent);
+
+#if (LWMAC_ENABLE_DUTYCYLE_RECORD == 1)
+    gnrc_netdev->lwmac.pkt_start_sending_time_ticks =
+        rtt_get_counter() - gnrc_netdev->lwmac.pkt_start_sending_time_ticks;
+    DEBUG("[LWMAC-tx]: pkt sending delay in TX: %lu us\n",
+          RTT_TICKS_TO_US(gnrc_netdev->lwmac.pkt_start_sending_time_ticks));
+#endif
+
+    return true;
+}
+
+void gnrc_lwmac_tx_start(gnrc_netdev_t *gnrc_netdev,
+                         gnrc_pktsnip_t *pkt,
+                         gnrc_mac_tx_neighbor_t *neighbor)
+{
+    assert(gnrc_netdev != NULL);
+    assert(pkt != NULL);
+    assert(neighbor != NULL);
+
+    if (gnrc_netdev->tx.packet) {
+        LOG_WARNING("WARNING: [LWMAC-tx] Starting but tx.packet is still set\n");
+        gnrc_pktbuf_release(gnrc_netdev->tx.packet);
+    }
+
+    gnrc_netdev->tx.packet = pkt;
+    gnrc_netdev->tx.current_neighbor = neighbor;
+    gnrc_netdev->tx.state = GNRC_LWMAC_TX_STATE_INIT;
+    gnrc_netdev->tx.wr_sent = 0;
+
+#if (LWMAC_ENABLE_DUTYCYLE_RECORD == 1)
+    gnrc_netdev->lwmac.pkt_start_sending_time_ticks = rtt_get_counter();
+#endif
+}
+
+void gnrc_lwmac_tx_stop(gnrc_netdev_t *gnrc_netdev)
+{
+    assert(gnrc_netdev != NULL);
+
+    gnrc_lwmac_clear_timeout(gnrc_netdev, GNRC_LWMAC_TIMEOUT_WR);
+    gnrc_lwmac_clear_timeout(gnrc_netdev, GNRC_LWMAC_TIMEOUT_NO_RESPONSE);
+    gnrc_lwmac_clear_timeout(gnrc_netdev, GNRC_LWMAC_TIMEOUT_NEXT_BROADCAST);
+    gnrc_lwmac_clear_timeout(gnrc_netdev, GNRC_LWMAC_TIMEOUT_BROADCAST_END);
+    gnrc_netdev->tx.state = GNRC_LWMAC_TX_STATE_STOPPED;
+
+    /* Release packet in case of failure */
+    if (gnrc_netdev->tx.packet) {
+        if (gnrc_netdev->tx.tx_retry_count >= GNRC_LWMAC_MAX_DATA_TX_RETRIES) {
+            gnrc_netdev->tx.tx_retry_count = 0;
+            gnrc_pktbuf_release(gnrc_netdev->tx.packet);
+            gnrc_netdev->tx.packet = NULL;
+            LOG_WARNING("WARNING: [LWMAC-tx] Drop TX packet\n");
+        }
+        else {
+            gnrc_netdev->tx.tx_retry_count++;
+            return;
+        }
+    }
+
+    if (!gnrc_netdev_lwmac_get_tx_continue(gnrc_netdev)) {
+        gnrc_netdev->tx.current_neighbor = NULL;
+    }
+}
+
+/* Returns whether rescheduling is needed or not */
+static bool _lwmac_tx_update(gnrc_netdev_t *gnrc_netdev)
+{
+    assert(gnrc_netdev != NULL);
+
+    bool reschedule = false;
+
+    switch (gnrc_netdev->tx.state) {
+        case GNRC_LWMAC_TX_STATE_INIT: {
+            gnrc_lwmac_clear_timeout(gnrc_netdev, GNRC_LWMAC_TIMEOUT_WR);
+            gnrc_lwmac_clear_timeout(gnrc_netdev, GNRC_LWMAC_TIMEOUT_NO_RESPONSE);
+            gnrc_lwmac_clear_timeout(gnrc_netdev, GNRC_LWMAC_TIMEOUT_NEXT_BROADCAST);
+            gnrc_lwmac_clear_timeout(gnrc_netdev, GNRC_LWMAC_TIMEOUT_BROADCAST_END);
+
+            /* if found ongoing transmission,
+             * quit this cycle for collision avoidance. */
+            if (_gnrc_lwmac_get_netdev_state(gnrc_netdev) == NETOPT_STATE_RX) {
+                if (!gnrc_mac_queue_tx_packet(&gnrc_netdev->tx, 0, gnrc_netdev->tx.packet)) {
+                    gnrc_pktbuf_release(gnrc_netdev->tx.packet);
+                    LOG_WARNING("WARNING: [LWMAC-tx] TX queue full, drop packet\n");
+                }
+                /* drop pointer so it wont be free'd */
+                gnrc_netdev->tx.packet = NULL;
+                gnrc_netdev->tx.state = GNRC_LWMAC_TX_STATE_FAILED;
+                reschedule = true;
+                break;
+            }
+
+            /* check if the packet is for broadcast */
+            if (gnrc_netif_hdr_get_flag(gnrc_netdev->tx.packet) &
+                (GNRC_NETIF_HDR_FLAGS_BROADCAST | GNRC_NETIF_HDR_FLAGS_MULTICAST)) {
+                /* Set CSMA retries as configured and enable */
+                uint8_t csma_retries = GNRC_LWMAC_BROADCAST_CSMA_RETRIES;
+                gnrc_netdev->dev->driver->set(gnrc_netdev->dev, NETOPT_CSMA_RETRIES,
+                                              &csma_retries, sizeof(csma_retries));
+                gnrc_netdev->mac_info |= GNRC_NETDEV_MAC_INFO_CSMA_ENABLED;
+                netopt_enable_t csma_enable = NETOPT_ENABLE;
+                gnrc_netdev->dev->driver->set(gnrc_netdev->dev, NETOPT_CSMA,
+                                              &csma_enable, sizeof(csma_enable));
+
+                gnrc_netdev->tx.state = GNRC_LWMAC_TX_STATE_SEND_BROADCAST;
+                reschedule = true;
+                break;
+            }
+            else {
+                /* Use CSMA for the first WR */
+                gnrc_netdev->mac_info |= GNRC_NETDEV_MAC_INFO_CSMA_ENABLED;
+                netopt_enable_t csma_disable = NETOPT_ENABLE;
+                gnrc_netdev->dev->driver->set(gnrc_netdev->dev, NETOPT_CSMA,
+                                              &csma_disable, sizeof(csma_disable));
+                /* Set a timeout for the maximum transmission procedure */
+                gnrc_lwmac_set_timeout(gnrc_netdev, GNRC_LWMAC_TIMEOUT_NO_RESPONSE, GNRC_LWMAC_PREAMBLE_DURATION_US);
+
+                gnrc_netdev->tx.state = GNRC_LWMAC_TX_STATE_SEND_WR;
+                reschedule = true;
+                break;
+            }
+        }
+        case GNRC_LWMAC_TX_STATE_SEND_BROADCAST: {
+            uint8_t tx_info = _send_bcast(gnrc_netdev);
+
+            if (tx_info & GNRC_LWMAC_TX_SUCCESS) {
+                gnrc_netdev->tx.state = GNRC_LWMAC_TX_STATE_SUCCESSFUL;
+                reschedule = true;
+                break;
+            }
+
+            if (tx_info & GNRC_LWMAC_TX_FAIL) {
+                gnrc_netdev->tx.state = GNRC_LWMAC_TX_STATE_FAILED;
+                reschedule = true;
+                break;
+            }
+
+            break;
+        }
+        case GNRC_LWMAC_TX_STATE_SEND_WR: {
+            /* In case of no Tx-isr error (e.g., no Tx-isr), goto TX failure. */
+            if (gnrc_lwmac_timeout_is_expired(gnrc_netdev, GNRC_LWMAC_TIMEOUT_NO_RESPONSE)) {
+                LOG_WARNING("WARNING: [LWMAC-tx] No response from destination, "
+                                 "probably no TX-ISR\n");
+                gnrc_netdev->tx.state = GNRC_LWMAC_TX_STATE_FAILED;
+                reschedule = true;
+                break;
+            }
+            LOG_DEBUG("[LWMAC-tx] GNRC_LWMAC_TX_STATE_SEND_WR\n");
+            uint8_t tx_info = _send_wr(gnrc_netdev);
+
+            if (tx_info & GNRC_LWMAC_TX_FAIL) {
+                gnrc_netdev->tx.state = GNRC_LWMAC_TX_STATE_FAILED;
+                reschedule = true;
+                break;
+            }
+
+            gnrc_netdev->tx.state = GNRC_LWMAC_TX_STATE_WAIT_WR_SENT;
+            reschedule = false;
+            break;
+        }
+        case GNRC_LWMAC_TX_STATE_WAIT_WR_SENT: {
+            LOG_DEBUG("[LWMAC-tx] GNRC_LWMAC_TX_STATE_WAIT_WR_SENT\n");
+
+            /* In case of no Tx-isr error (e.g., no Tx-isr), goto TX failure. */
+            if (gnrc_lwmac_timeout_is_expired(gnrc_netdev, GNRC_LWMAC_TIMEOUT_NO_RESPONSE)) {
+                LOG_WARNING("WARNING: [LWMAC-tx] No response from destination\n");
+                gnrc_netdev->tx.state = GNRC_LWMAC_TX_STATE_FAILED;
+                reschedule = true;
+                break;
+            }
+
+            if (gnrc_netdev_get_tx_feedback(gnrc_netdev) == TX_FEEDBACK_UNDEF) {
+                LOG_DEBUG("[LWMAC-tx] WR not yet completely sent\n");
+                break;
+            }
+
+            /* If found ongoing transmission, goto TX failure, i.e., postpone transmission to
+             * next cycle. This is mainly for collision avoidance. */
+            if (gnrc_netdev_get_tx_feedback(gnrc_netdev) == TX_FEEDBACK_BUSY) {
+                if (!gnrc_mac_queue_tx_packet(&gnrc_netdev->tx, 0, gnrc_netdev->tx.packet)) {
+                    gnrc_pktbuf_release(gnrc_netdev->tx.packet);
+                    LOG_WARNING("WARNING: [LWMAC-tx] TX queue full, drop packet\n");
+                }
+                /* clear packet point to avoid TX retry */
+                gnrc_netdev->tx.packet = NULL;
+                gnrc_netdev->tx.state = GNRC_LWMAC_TX_STATE_FAILED;
+                reschedule = true;
+                break;
+            }
+
+            if (gnrc_netdev->tx.wr_sent == 0) {
+                /* Only the first WR use CSMA */
+                gnrc_netdev->mac_info &= ~GNRC_NETDEV_MAC_INFO_CSMA_ENABLED;
+                netopt_enable_t csma_disable = NETOPT_DISABLE;
+                gnrc_netdev->dev->driver->set(gnrc_netdev->dev, NETOPT_CSMA,
+                                              &csma_disable, sizeof(csma_disable));
+            }
+
+            gnrc_netdev->tx.wr_sent++;
+
+            /* Set timeout for next WR in case no WA will be received */
+            gnrc_lwmac_set_timeout(gnrc_netdev, GNRC_LWMAC_TIMEOUT_WR, GNRC_LWMAC_TIME_BETWEEN_WR_US);
+
+            /* Debug WR timing */
+            LOG_DEBUG("[LWMAC-tx] Destination phase was: %" PRIu32 "\n",
+                           gnrc_netdev->tx.current_neighbor->phase);
+            LOG_DEBUG("[LWMAC-tx] Phase when sent was:   %" PRIu32 "\n",
+                           _gnrc_lwmac_ticks_to_phase(gnrc_netdev->tx.timestamp));
+            LOG_DEBUG("[LWMAC-tx] Ticks when sent was:   %" PRIu32 "\n",
+                           gnrc_netdev->tx.timestamp);
+            _gnrc_lwmac_set_netdev_state(gnrc_netdev, NETOPT_STATE_IDLE);
+            gnrc_netdev->tx.state = GNRC_LWMAC_TX_STATE_WAIT_FOR_WA;
+            reschedule = false;
+            break;
+        }
+        case GNRC_LWMAC_TX_STATE_WAIT_FOR_WA: {
+            LOG_DEBUG("[LWMAC-tx] GNRC_LWMAC_TX_STATE_WAIT_FOR_WA\n");
+
+            if (gnrc_lwmac_timeout_is_expired(gnrc_netdev, GNRC_LWMAC_TIMEOUT_NO_RESPONSE)) {
+                LOG_WARNING("WARNING: [LWMAC-tx] No response from destination\n");
+                gnrc_netdev->tx.state = GNRC_LWMAC_TX_STATE_FAILED;
+                reschedule = true;
+                break;
+            }
+
+            if (gnrc_lwmac_timeout_is_expired(gnrc_netdev, GNRC_LWMAC_TIMEOUT_WR)) {
+                /* In case the sender is in consecutive (burst) transmission to the receiver,
+                 * meaning that the sender has already successfully sent at least one data to
+                 * the receiver, then the sender will only spend one WR for triggering the next
+                 * transmission procedure. And, if this WR doesn't work (no WA replied), the
+                 * sender regards consecutive transmission failed.
+                 */
+                if (gnrc_netdev_lwmac_get_tx_continue(gnrc_netdev)) {
+                    LOG_DEBUG("[LWMAC-tx] Tx burst fail\n");
+                    if (!gnrc_mac_queue_tx_packet(&gnrc_netdev->tx, 0, gnrc_netdev->tx.packet)) {
+                        gnrc_pktbuf_release(gnrc_netdev->tx.packet);
+                        LOG_WARNING("WARNING: [LWMAC-tx] TX queue full, drop packet\n");
+                    }
+                    /* drop pointer so it wont be free'd */
+                    gnrc_netdev->tx.packet = NULL;
+
+                    gnrc_netdev->tx.state = GNRC_LWMAC_TX_STATE_FAILED;
+                    reschedule = true;
+                    break;
+                }
+                else {
+                    /* If this is the first transmission to the receiver for locating the
+                     * latter's wake-up period, the sender just keep sending WRs until it
+                     * finds the WA.
+                     */
+                    gnrc_netdev->tx.state = GNRC_LWMAC_TX_STATE_SEND_WR;
+                    reschedule = true;
+                    break;
+                }
+            }
+
+            if (_gnrc_lwmac_get_netdev_state(gnrc_netdev) == NETOPT_STATE_RX) {
+                /* Wait for completion of frame reception */
+                break;
+            }
+
+            uint8_t tx_info = _packet_process_in_wait_for_wa(gnrc_netdev);
+
+            if (tx_info & GNRC_LWMAC_TX_FAIL) {
+                gnrc_netdev->tx.state = GNRC_LWMAC_TX_STATE_FAILED;
+                reschedule = true;
+                break;
+            }
+
+            if (tx_info & GNRC_LWMAC_TX_SUCCESS) {
+                gnrc_netdev->tx.state = GNRC_LWMAC_TX_STATE_SEND_DATA;
+                reschedule = true;
+                break;
+            }
+            else {
+                /* No WA yet */
+                break;
+            }
+        }
+        case GNRC_LWMAC_TX_STATE_SEND_DATA: {
+            LOG_DEBUG("[LWMAC-tx] GNRC_LWMAC_TX_STATE_SEND_DATA\n");
+
+            if (!_send_data(gnrc_netdev)) {
+                gnrc_netdev->tx.state = GNRC_LWMAC_TX_STATE_FAILED;
+                reschedule = true;
+                break;
+            }
+
+            gnrc_netdev->tx.state = GNRC_LWMAC_TX_STATE_WAIT_FEEDBACK;
+            reschedule = false;
+            break;
+        }
+        case GNRC_LWMAC_TX_STATE_WAIT_FEEDBACK: {
+            /* In case of no Tx-isr error, goto TX failure. */
+            if (gnrc_lwmac_timeout_is_expired(gnrc_netdev, GNRC_LWMAC_TIMEOUT_NO_RESPONSE)) {
+                gnrc_netdev->tx.state = GNRC_LWMAC_TX_STATE_FAILED;
+                reschedule = true;
+                break;
+            }
+
+            LOG_DEBUG("[LWMAC-tx] GNRC_LWMAC_TX_STATE_WAIT_FEEDBACK\n");
+            if (gnrc_netdev_get_tx_feedback(gnrc_netdev) == TX_FEEDBACK_UNDEF) {
+                break;
+            }
+            else if (gnrc_netdev_get_tx_feedback(gnrc_netdev) == TX_FEEDBACK_SUCCESS) {
+                gnrc_netdev->tx.state = GNRC_LWMAC_TX_STATE_SUCCESSFUL;
+                reschedule = true;
+                break;
+            }
+            else if (gnrc_netdev_get_tx_feedback(gnrc_netdev) == TX_FEEDBACK_NOACK) {
+                LOG_ERROR("ERROR: [LWMAC-tx] Not ACKED\n");
+                gnrc_netdev->tx.state = GNRC_LWMAC_TX_STATE_FAILED;
+                reschedule = true;
+                break;
+            }
+            else if (gnrc_netdev_get_tx_feedback(gnrc_netdev) == TX_FEEDBACK_BUSY) {
+                LOG_ERROR("ERROR: [LWMAC-tx] Channel busy \n");
+                gnrc_netdev->tx.state = GNRC_LWMAC_TX_STATE_FAILED;
+                reschedule = true;
+                break;
+            }
+
+            LOG_ERROR("ERROR: [LWMAC-tx] Tx feedback unhandled: %i\n",
+                           gnrc_netdev_get_tx_feedback(gnrc_netdev));
+            gnrc_netdev->tx.state = GNRC_LWMAC_TX_STATE_FAILED;
+            reschedule = true;
+            break;
+        }
+        case GNRC_LWMAC_TX_STATE_SUCCESSFUL:
+        case GNRC_LWMAC_TX_STATE_FAILED: {
+            break;
+        }
+        case GNRC_LWMAC_TX_STATE_STOPPED: {
+            LOG_DEBUG("[LWMAC-tx] Transmission state machine is stopped\n");
+        }
+    }
+
+    return reschedule;
+}
+
+void gnrc_lwmac_tx_update(gnrc_netdev_t *gnrc_netdev)
+{
+    /* Update until no rescheduling needed */
+    while (_lwmac_tx_update(gnrc_netdev)) {}
+}