mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2025-01-17 05:12:57 +01:00
LWMAC: a simple duty cycling 802.15.4 MAC protocol.
This commit is contained in:
parent
b70a4885f4
commit
a54655890e
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
115
sys/include/net/gnrc/lwmac/hdr.h
Normal file
115
sys/include/net/gnrc/lwmac/hdr.h
Normal file
@ -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 */
|
||||
/** @} */
|
324
sys/include/net/gnrc/lwmac/lwmac.h
Normal file
324
sys/include/net/gnrc/lwmac/lwmac.h
Normal file
@ -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 */
|
||||
/** @} */
|
102
sys/include/net/gnrc/lwmac/timeout.h
Normal file
102
sys/include/net/gnrc/lwmac/timeout.h
Normal file
@ -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 */
|
||||
/** @} */
|
223
sys/include/net/gnrc/lwmac/types.h
Normal file
223
sys/include/net/gnrc/lwmac/types.h
Normal file
@ -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 */
|
||||
/** @} */
|
@ -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;
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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])) {
|
||||
|
3
sys/net/gnrc/link_layer/lwmac/Makefile
Normal file
3
sys/net/gnrc/link_layer/lwmac/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
MODULE = gnrc_lwmac
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
370
sys/net/gnrc/link_layer/lwmac/include/lwmac_internal.h
Normal file
370
sys/net/gnrc/link_layer/lwmac/include/lwmac_internal.h
Normal file
@ -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 */
|
||||
/** @} */
|
60
sys/net/gnrc/link_layer/lwmac/include/rx_state_machine.h
Normal file
60
sys/net/gnrc/link_layer/lwmac/include/rx_state_machine.h
Normal file
@ -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 */
|
65
sys/net/gnrc/link_layer/lwmac/include/tx_state_machine.h
Normal file
65
sys/net/gnrc/link_layer/lwmac/include/tx_state_machine.h
Normal file
@ -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 */
|
920
sys/net/gnrc/link_layer/lwmac/lwmac.c
Normal file
920
sys/net/gnrc/link_layer/lwmac/lwmac.c
Normal file
@ -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;
|
||||
}
|
192
sys/net/gnrc/link_layer/lwmac/lwmac_internal.c
Normal file
192
sys/net/gnrc/link_layer/lwmac/lwmac_internal.c
Normal file
@ -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;
|
||||
}
|
437
sys/net/gnrc/link_layer/lwmac/rx_state_machine.c
Normal file
437
sys/net/gnrc/link_layer/lwmac/rx_state_machine.c
Normal file
@ -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)) {}
|
||||
}
|
148
sys/net/gnrc/link_layer/lwmac/timeout.c
Normal file
148
sys/net/gnrc/link_layer/lwmac/timeout.c
Normal file
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
810
sys/net/gnrc/link_layer/lwmac/tx_state_machine.c
Normal file
810
sys/net/gnrc/link_layer/lwmac/tx_state_machine.c
Normal file
@ -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)) {}
|
||||
}
|
Loading…
Reference in New Issue
Block a user