Browse Source

Merge pull request #6554 from zhuoshuguo/lwmac_retry

LWMAC:  A simple duty cycling 802.15.4 MAC protocol (2nd try).
master
Sebastian Meiling 5 years ago committed by GitHub
parent
commit
9ce9dd601c
  1. 6
      Makefile.dep
  2. 61
      examples/gnrc_networking_mac/Makefile
  3. 79
      examples/gnrc_networking_mac/README.md
  4. 50
      examples/gnrc_networking_mac/main.c
  5. 174
      examples/gnrc_networking_mac/udp.c
  6. 9
      sys/auto_init/netif/auto_init_at86rf2xx.c
  7. 115
      sys/include/net/gnrc/lwmac/hdr.h
  8. 324
      sys/include/net/gnrc/lwmac/lwmac.h
  9. 102
      sys/include/net/gnrc/lwmac/timeout.h
  10. 223
      sys/include/net/gnrc/lwmac/types.h
  11. 16
      sys/include/net/gnrc/mac/types.h
  12. 8
      sys/include/net/gnrc/netdev.h
  13. 11
      sys/include/net/gnrc/nettype.h
  14. 3
      sys/net/gnrc/Makefile
  15. 11
      sys/net/gnrc/link_layer/gnrc_mac/internal.c
  16. 3
      sys/net/gnrc/link_layer/lwmac/Makefile
  17. 370
      sys/net/gnrc/link_layer/lwmac/include/lwmac_internal.h
  18. 60
      sys/net/gnrc/link_layer/lwmac/include/rx_state_machine.h
  19. 65
      sys/net/gnrc/link_layer/lwmac/include/tx_state_machine.h
  20. 920
      sys/net/gnrc/link_layer/lwmac/lwmac.c
  21. 192
      sys/net/gnrc/link_layer/lwmac/lwmac_internal.c
  22. 437
      sys/net/gnrc/link_layer/lwmac/rx_state_machine.c
  23. 148
      sys/net/gnrc/link_layer/lwmac/timeout.c
  24. 810
      sys/net/gnrc/link_layer/lwmac/tx_state_machine.c
  25. 70
      tests/lwmac/Makefile
  26. 53
      tests/lwmac/README.md
  27. 67
      tests/lwmac/main.c

6
Makefile.dep

@ -524,6 +524,12 @@ ifneq (,$(filter netstats_%, $(USEMODULE)))
USEMODULE += netstats
endif
ifneq (,$(filter gnrc_lwmac,$(USEMODULE)))
USEMODULE += gnrc_mac
USEMODULE += gnrc_netdev
FEATURES_REQUIRED += periph_rtt
endif
ifneq (,$(filter pthread,$(USEMODULE)))
USEMODULE += xtimer
USEMODULE += timex

61
examples/gnrc_networking_mac/Makefile

@ -0,0 +1,61 @@
# name of your application
APPLICATION = gnrc_networking_mac
# If no BOARD is found in the environment, use this default:
BOARD ?= samr21-xpro
# Currently, LWMAC is only tested and evaluated through on samr21-xpro.
# Once LWMAC has also been tested through on other boards, the whitelist should be
# then accordingly extended.
BOARD_WHITELIST := samr21-xpro
# This has to be the absolute path to the RIOT base directory:
RIOTBASE ?= $(CURDIR)/../..
# Include packages that pull up and auto-init the link layer.
# NOTE: 6LoWPAN will be included if IEEE802.15.4 devices are present
USEMODULE += gnrc_netdev_default
USEMODULE += auto_init_gnrc_netif
# Specify the mandatory networking modules for IPv6 and UDP
USEMODULE += gnrc_ipv6_router_default
USEMODULE += gnrc_udp
# Add a routing protocol
USEMODULE += gnrc_rpl
USEMODULE += auto_init_gnrc_rpl
# This application dumps received packets to STDIO using the pktdump module
USEMODULE += gnrc_pktdump
# Additional networking modules that can be dropped if not needed
USEMODULE += gnrc_icmpv6_echo
# Add also the shell, some shell commands
USEMODULE += shell
USEMODULE += shell_commands
USEMODULE += ps
USEMODULE += netstats_l2
USEMODULE += netstats_ipv6
USEMODULE += netstats_rpl
# Use LWMAC as the MAC layer protocol
USEMODULE += gnrc_lwmac
# Comment this out to disable code in RIOT that does safety checking
# which is not needed in a production environment but helps in the
# development process:
CFLAGS += -DDEVELHELP
# Uncomment the following 2 lines to specify static link lokal IPv6 address
# this might be useful for testing, in cases where you cannot or do not want to
# run a shell with ifconfig to get the real link lokal address.
#IPV6_STATIC_LLADDR ?= '"fe80::cafe:cafe:cafe:1"'
#CFLAGS += -DGNRC_IPV6_STATIC_LLADDR=$(IPV6_STATIC_LLADDR)
# Uncomment this to join RPL DODAGs even if DIOs do not contain
# DODAG Configuration Options (see the doc for more info)
# CFLAGS += -DGNRC_RPL_DODAG_CONF_OPTIONAL_ON_JOIN
# Change this to 0 show compiler invocation lines by default:
QUIET ?= 1
include $(RIOTBASE)/Makefile.include
# Set a custom channel
DEFAULT_CHANNEL ?= 26
CFLAGS += -DIEEE802154_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL)

79
examples/gnrc_networking_mac/README.md

@ -0,0 +1,79 @@
# gnrc_networking_mac example
This example shows you how to try out communications between RIOT instances with LWMAC as the MAC layer ptotocol for IEEE 802.15.4 devices.
This example is generally based on `gnrc_networking` but embeds LWMAC to support low duty-cycle operation to conserve power. Also, it intends to show that the duty-cycled LWMAC can support popular upper layer protocols like UDP and RPL.
Currently, it seems that you can only use the samr21-xpro board to test this MAC, since some certain features of the protocol are only available on that platform. Also, the current implementation of LWMAC uses RTT as the underlying timer source. So, currently, LWMAC cannot run on nodes that don't have RTT. But, as a long-term plan, we will replace RTT by a general timer API as the underlying timer to make LWMAC available for more devices, when the related implementations are ready.
## Usage
Build, flash and start the application:
```
export BOARD=your_board
make
make flash
make term
```
## Print out the achieved duty-cyle of LWMAC
You can print out the radio duty-cyle (a roughly one) of LWMAC by setting the `LWMAC_ENABLE_DUTYCYLE_RECORD` flag in `sys/include/net/gnrc/lwmac/types.h` to "1". By doing so, each time when a device sends or receives a packet, it will print out its radio duty-cycle value.
Also, by further enabling the debug flag in `sys/net/gnrc/link_layer/lwmac/tx_state_machine.c`, you will get the printout of how many preamble (WR) and time (sending delay) cost for sending this packet in the TX procedure of LWMAC.
## Try UDP transmissions with LWMAC
In the RIOT shell, get to know the IP address of one node:
2017-06-06 15:05:48,279 - INFO # ifconfig
2017-06-06 15:05:48,284 - INFO # Iface 7 HWaddr: 79:f6 Channel: 26 Page: 0 NID: 0x23
2017-06-06 15:05:48,288 - INFO # Long HWaddr: 79:67:35:7e:54:3a:79:f6
2017-06-06 15:05:48,297 - INFO # TX-Power: 0dBm State: SLEEP max. Retrans.: 3 CSMA Retries: 4
2017-06-06 15:05:48,303 - INFO # CSMA MTU:1280 HL:64 6LO RTR IPHC
2017-06-06 15:05:48,306 - INFO # Source address length: 8
2017-06-06 15:05:48,309 - INFO # Link type: wireless
2017-06-06 15:05:48,314 - INFO # inet6 addr: ff02::1/128 scope: local [multicast]
2017-06-06 15:05:48,320 - INFO # inet6 addr: fe80::7b67:357e:543a:79f6/64 scope: local
2017-06-06 15:05:48,326 - INFO # inet6 addr: ff02::1:ff3a:79f6/128 scope: local [multicast]
2017-06-06 15:05:48,331 - INFO # inet6 addr: ff02::1a/128 scope: local [multicast]
and start a UDP server.
> udp server start 8808
This node is now ready to receive data on port `8808`.
In a second terminal, start a second RIOT instance, in the RIOT shell, you can now send a message to the first RIOT instance:
> udp send fe80::7b67:357e:543a:79f6 8808 testmessage
In your first terminal (the receiver side), you should now see output that looks like this:
2017-06-06 15:00:06,894 - INFO # [LWMAC]: achieved duty-cycle: 10 %
2017-06-06 15:00:06,896 - INFO # PKTDUMP: data received:
2017-06-06 15:00:06,901 - INFO # ~~ SNIP 0 - size: 11 byte, type: NETTYPE_UNDEF (0)
2017-06-06 15:00:06,907 - INFO # 00000000 74 65 73 74 6D 65 73 73 61 67 65
2017-06-06 15:00:06,911 - INFO # ~~ SNIP 1 - size: 8 byte, type: NETTYPE_UDP (5)
2017-06-06 15:00:06,914 - INFO # src-port: 8808 dst-port: 8808
2017-06-06 15:00:06,917 - INFO # length: 19 cksum: 0xf729
2017-06-06 15:00:06,921 - INFO # ~~ SNIP 2 - size: 40 byte, type: NETTYPE_IPV6 (3)
2017-06-06 15:00:06,925 - INFO # traffic class: 0x00 (ECN: 0x0, DSCP: 0x00)
2017-06-06 15:00:06,927 - INFO # flow label: 0x00000
2017-06-06 15:00:06,930 - INFO # length: 19 next header: 17 hop limit: 64
2017-06-06 15:00:06,934 - INFO # source address: fe80::7b67:877:19f:331e
2017-06-06 15:00:06,938 - INFO # destination address: fe80::7b67:357e:543a:79f6
2017-06-06 15:00:06,943 - INFO # ~~ SNIP 3 - size: 24 byte, type: NETTYPE_NETIF (-1)
2017-06-06 15:00:06,945 - INFO # if_pid: 7 rssi: 51 lqi: 255
2017-06-06 15:00:06,946 - INFO # flags: 0x0
2017-06-06 15:00:06,949 - INFO # src_l2addr: 79:67:08:77:01:9f:33:1e
2017-06-06 15:00:06,952 - INFO # dst_l2addr: 79:67:35:7e:54:3a:79:f6
2017-06-06 15:00:06,956 - INFO # ~~ PKT - 4 snips, total size: 83 byte
In your second terminal (the sender side), you should now see output that looks like this:
2017-06-06 15:00:06,871 - INFO # udp send fe80::7b67:357e:543a:79f6 8808 testmessage
2017-06-06 15:00:06,877 - INFO # Success: sent 11 byte(s) to [fe80::7b67:357e:543a:79f6]:8808
2017-06-06 15:00:06,890 - INFO # [LWMAC-tx]: spent 1 WR in TX
2017-06-06 15:00:06,894 - INFO # [LWMAC-tx]: pkt sending delay in TX: 8422 us
2017-06-06 15:00:06,898 - INFO # [LWMAC]: achieved duty-cycle: 10 %

50
examples/gnrc_networking_mac/main.c

@ -0,0 +1,50 @@
/*
* Copyright (C) 2015 Freie Universität Berlin
*
* 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 examples
* @{
*
* @file
* @brief Example application for demonstrating the RIOT network stack
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*
* @}
*/
#include <stdio.h>
#include "shell.h"
#include "msg.h"
#define MAIN_QUEUE_SIZE (8)
static msg_t _main_msg_queue[MAIN_QUEUE_SIZE];
extern int udp_cmd(int argc, char **argv);
static const shell_command_t shell_commands[] = {
{ "udp", "send data over UDP and listen on UDP ports", udp_cmd },
{ NULL, NULL, NULL }
};
int main(void)
{
/* we need a message queue for the thread running the shell in order to
* receive potentially fast incoming networking packets */
msg_init_queue(_main_msg_queue, MAIN_QUEUE_SIZE);
puts("RIOT network stack example application");
/* start shell */
puts("All up, running the shell now");
char line_buf[SHELL_DEFAULT_BUFSIZE];
shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE);
/* should be never reached */
return 0;
}

174
examples/gnrc_networking_mac/udp.c

@ -0,0 +1,174 @@
/*
* Copyright (C) 2015 Freie Universität Berlin
*
* 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 examples
* @{
*
* @file
* @brief Demonstrating the sending and receiving of UDP data
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*
* @}
*/
#include <stdio.h>
#include <inttypes.h>
#include "net/gnrc.h"
#include "net/gnrc/ipv6.h"
#include "net/gnrc/udp.h"
#include "net/gnrc/pktdump.h"
#include "timex.h"
#include "xtimer.h"
static gnrc_netreg_entry_t server = GNRC_NETREG_ENTRY_INIT_PID(GNRC_NETREG_DEMUX_CTX_ALL,
KERNEL_PID_UNDEF);
static void send(char *addr_str, char *port_str, char *data, unsigned int num,
unsigned int delay)
{
uint16_t port;
ipv6_addr_t addr;
/* parse destination address */
if (ipv6_addr_from_str(&addr, addr_str) == NULL) {
puts("Error: unable to parse destination address");
return;
}
/* parse port */
port = (uint16_t)atoi(port_str);
if (port == 0) {
puts("Error: unable to parse destination port");
return;
}
for (unsigned int i = 0; i < num; i++) {
gnrc_pktsnip_t *payload, *udp, *ip;
unsigned payload_size;
/* allocate payload */
payload = gnrc_pktbuf_add(NULL, data, strlen(data), GNRC_NETTYPE_UNDEF);
if (payload == NULL) {
puts("Error: unable to copy data to packet buffer");
return;
}
/* store size for output */
payload_size = (unsigned)payload->size;
/* allocate UDP header, set source port := destination port */
udp = gnrc_udp_hdr_build(payload, port, port);
if (udp == NULL) {
puts("Error: unable to allocate UDP header");
gnrc_pktbuf_release(payload);
return;
}
/* allocate IPv6 header */
ip = gnrc_ipv6_hdr_build(udp, NULL, &addr);
if (ip == NULL) {
puts("Error: unable to allocate IPv6 header");
gnrc_pktbuf_release(udp);
return;
}
/* send packet */
if (!gnrc_netapi_dispatch_send(GNRC_NETTYPE_UDP, GNRC_NETREG_DEMUX_CTX_ALL, ip)) {
puts("Error: unable to locate UDP thread");
gnrc_pktbuf_release(ip);
return;
}
/* access to `payload` was implicitly given up with the send operation above
* => use temporary variable for output */
printf("Success: sent %u byte(s) to [%s]:%u\n", payload_size, addr_str,
port);
xtimer_usleep(delay);
}
}
static void start_server(char *port_str)
{
uint16_t port;
/* check if server is already running */
if (server.target.pid != KERNEL_PID_UNDEF) {
printf("Error: server already running on port %" PRIu32 "\n",
server.demux_ctx);
return;
}
/* parse port */
port = (uint16_t)atoi(port_str);
if (port == 0) {
puts("Error: invalid port specified");
return;
}
/* start server (which means registering pktdump for the chosen port) */
server.target.pid = gnrc_pktdump_pid;
server.demux_ctx = (uint32_t)port;
gnrc_netreg_register(GNRC_NETTYPE_UDP, &server);
printf("Success: started UDP server on port %" PRIu16 "\n", port);
}
static void stop_server(void)
{
/* check if server is running at all */
if (server.target.pid == KERNEL_PID_UNDEF) {
printf("Error: server was not running\n");
return;
}
/* stop server */
gnrc_netreg_unregister(GNRC_NETTYPE_UDP, &server);
server.target.pid = KERNEL_PID_UNDEF;
puts("Success: stopped UDP server");
}
int udp_cmd(int argc, char **argv)
{
if (argc < 2) {
printf("usage: %s [send|server]\n", argv[0]);
return 1;
}
if (strcmp(argv[1], "send") == 0) {
uint32_t num = 1;
uint32_t delay = 1000000;
if (argc < 5) {
printf("usage: %s send <addr> <port> <data> [<num> [<delay in us>]]\n",
argv[0]);
return 1;
}
if (argc > 5) {
num = (uint32_t)atoi(argv[5]);
}
if (argc > 6) {
delay = (uint32_t)atoi(argv[6]);
}
send(argv[2], argv[3], argv[4], num, delay);
}
else if (strcmp(argv[1], "server") == 0) {
if (argc < 3) {
printf("usage: %s server [start|stop]\n", argv[0]);
return 1;
}
if (strcmp(argv[2], "start") == 0) {
if (argc < 4) {
printf("usage %s server start <port>\n", argv[0]);
return 1;
}
start_server(argv[3]);
}
else if (strcmp(argv[2], "stop") == 0) {
stop_server();
}
else {
puts("error: invalid command");
}
}
else {
puts("error: invalid command");
}
return 0;
}

9
sys/auto_init/netif/auto_init_at86rf2xx.c

@ -23,6 +23,7 @@
#include "board.h"
#include "net/gnrc/netdev.h"
#include "net/gnrc/netdev/ieee802154.h"
#include "net/gnrc/lwmac/lwmac.h"
#include "net/gnrc.h"
#include "at86rf2xx.h"
@ -58,11 +59,19 @@ void auto_init_at86rf2xx(void)
LOG_ERROR("[auto_init_netif] error initializing at86rf2xx radio #%u\n", i);
}
else {
#ifdef MODULE_GNRC_LWMAC
gnrc_lwmac_init(_at86rf2xx_stacks[i],
AT86RF2XX_MAC_STACKSIZE,
AT86RF2XX_MAC_PRIO,
"at86rf2xx-lwmac",
&gnrc_adpt[i]);
#else
gnrc_netdev_init(_at86rf2xx_stacks[i],
AT86RF2XX_MAC_STACKSIZE,
AT86RF2XX_MAC_PRIO,
"at86rf2xx",
&gnrc_adpt[i]);
#endif
}
}
}

115
sys/include/net/gnrc/lwmac/hdr.h

@ -0,0 +1,115 @@
/*
* Copyright (C) 2015 Daniel Krebs
* 2016 INRIA
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup net_gnrc_lwmac
* @{
*
* @file
* @brief Header definition LWMAC
* @internal
* @author Daniel Krebs <github@daniel-krebs.net>
* @author Shuguo Zhuo <shuguo.zhuo@inria.fr>
*/
#ifndef NET_GNRC_LWMAC_HDR_H
#define NET_GNRC_LWMAC_HDR_H
#include <stdint.h>
#include <stdbool.h>
#include "net/ieee802154.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief LWMAC WR (wake-up request packet, i.e., preamble packet) frame type
*/
#define GNRC_LWMAC_FRAMETYPE_WR (0x01U)
/**
* @brief LWMAC WA (wake-up answer packet, i.e., preamble-ACK packet) frame type
*/
#define GNRC_LWMAC_FRAMETYPE_WA (0x02U)
/**
* @brief LWMAC data frame type
*/
#define GNRC_LWMAC_FRAMETYPE_DATA (0x03U)
/**
* @brief LWMAC data frame type with pending data transmission request
*/
#define GNRC_LWMAC_FRAMETYPE_DATA_PENDING (0x04U)
/**
* @brief LWMAC broadcast frame type
*/
#define GNRC_LWMAC_FRAMETYPE_BROADCAST (0x05U)
/**
* @brief LWMAC internal L2 address structure
*/
typedef struct {
uint8_t addr[IEEE802154_LONG_ADDRESS_LEN]; /**< address of node */
uint8_t len; /**< address */
} gnrc_lwmac_l2_addr_t;
/**
* @brief Static initializer for l2_addr_t.
*/
#define GNRC_LWMAC_L2_ADDR_INITIAL { { 0 }, 0 }
/**
* @brief LWMAC header
*/
typedef struct {
uint8_t type; /**< type of frame */
} gnrc_lwmac_hdr_t;
/**
* @brief LWMAC WR (wake-up request packet, i.e., preamble packet) frame
*/
typedef struct __attribute__((packed)) {
gnrc_lwmac_hdr_t header; /**< WR packet header type */
gnrc_lwmac_l2_addr_t dst_addr; /**< WR is broadcast, so destination address needed */
} gnrc_lwmac_frame_wr_t;
/**
* @brief LWMAC WA (wake-up answer packet, i.e., preamble-ACK packet) frame
*/
typedef struct __attribute__((packed)) {
gnrc_lwmac_hdr_t header; /**< WA packet header type */
gnrc_lwmac_l2_addr_t dst_addr; /**< WA is broadcast, so destination address needed */
uint32_t current_phase; /**< Node's current phase value */
} gnrc_lwmac_frame_wa_t;
/**
* @brief LWMAC broadcast data frame
*/
typedef struct __attribute__((packed)) {
gnrc_lwmac_hdr_t header; /**< Broadcast packet header type */
uint8_t seq_nr; /**< Broadcast sequence */
} gnrc_lwmac_frame_broadcast_t;
/**
* @brief LWMAC unicast data frame
*/
typedef struct __attribute__((packed)) {
gnrc_lwmac_hdr_t header; /**< Data packet header type */
} gnrc_lwmac_frame_data_t;
#ifdef __cplusplus
}
#endif
#endif /* NET_GNRC_LWMAC_HDR_H */
/** @} */

324
sys/include/net/gnrc/lwmac/lwmac.h

@ -0,0 +1,324 @@
/*
* Copyright (C) 2015 Daniel Krebs
* 2016 INRIA
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @defgroup net_gnrc_lwmac Simplest possible MAC layer
* @ingroup net_gnrc
* @brief Lightweight MAC protocol that allows for duty cycling to save
* energy.
*
* ## LWMAC implementation
*
* ## Radio duty cycling
* LWMAC adopts the radio duty-cycle scheme to conserve power. Namely, in each
* cycle period (MAC superframe), a node device wakes up for a short period of
* time (called listen period or wake-up period) for receiving possible incoming
* packets from other devices. Outside the listen period, the node device turns
* off its radio to conserve power.
*
* ## Phase-lock scheme
* LWMAC adopts the phase-lock scheme to further reduce power consumption. Each
* node device in LWMAC will try to record/track its Tx-neighbor's wake-up phase.
* This is called phase-lock. After phase-locking, the sender node will (likely)
* spend less preamble packets (also called WR packet, i.e., wake-up-request, in
* LWMAC) for initiating a hand-shaking procedure for transmitting a data packet,
* compared to the first time it talks to the receiver.
*
* ## Burst transmission
* LWMAC adopts pending-bit technique to enhance its throughput. Namely, in case
* of having multi packets for the receiver, a sender uses the pending-bit flag
* embedded in the MAC header to instruct this situation, and the buffered packets
* will be transmitted in a continuous sequence, back to back, to the receiver in
* one shot.
*
* ## Auto wake-up extension
* LWMAC adopts auto wake-up extension scheme based on timeout (like T-MAC). In short,
* when a packet is successfully received at the receiver side, the receiver will
* reset the wake-up timeout to extend its wake-up period for receiving more potential
* incoming packets. This is to be compatible with the pending-bit technique to allow
* the receiver to absorb more packets when needed, thus boosts the throughput.
*
* ## Simple retransmission scheme
* LWMAC adopts a simple retransmission scheme to enhance link reliability. The data
* packet will only be dropped in case the retransmission counter gets larger than
* @ref GNRC_LWMAC_MAX_DATA_TX_RETRIES.
*
* ## Automatic phase backoff scheme
* LWMAC adopts an automatic phase backoff scheme to reduce WR (preamble) collision
* probability. In multi-hop scenarios, let's say, nodes A <---B <----C (which is
* common in multi-hop data collection networks), in which B has packets for A, and
* C has packets for B. In case A and B's wake-up phases are too close (overlapping).
* Then, especially in high traffic conditions, B and C may initiate transmissions
* at the same time (B sends to A, and C sends to B), a link of either will be
* definitely interfered, leading to collisions and link throughput reduction. To
* this end, by using the automatic phase backoff scheme, if a sender finds its
* receiver's phase is too close to its own phase, it will run a backoff scheme to
* randomly reselect a new wake-up phase for itself.
*
* @{
*
* @file
* @brief Interface definition for the LWMAC protocol
*
* @author Daniel Krebs <github@daniel-krebs.net>
* @author Shuguo Zhuo <shuguo.zhuo@inria.fr>
*/
#ifndef NET_GNRC_LWMAC_LWMAC_H
#define NET_GNRC_LWMAC_LWMAC_H
#include "kernel_types.h"
#include "net/gnrc/netdev.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Time between consecutive wake-ups.
*
* This macro governs power consumption, latency and throughput!
* In LWMAC, devices adopt duty-cycle scheme to conserve power. That is,
* time is divided into repeated cycles (or, superframes), and in each
* cycle, a node only wakes up for a period of time for receiving potential
* incoming packets for itself. This macro defines the wake-up interval, or,
* in other words, defines the cycle duration used in LWMAC. If the wake-up interval
* is short, nodes will wake up more frequently, which also increases
* the chances for receiving packets from neighbors (i.e., leads to higher
* throughput), but also results in higher power consumption.
* In LWMAC, by default, we regard the wake-up period as the beginning of a cycle.
*/
#ifndef GNRC_LWMAC_WAKEUP_INTERVAL_US
#define GNRC_LWMAC_WAKEUP_INTERVAL_US (100LU * US_PER_MS)
#endif
/**
* @brief The Maximum WR (preamble packet @ref gnrc_lwmac_frame_wr_t) duration time.
*
* Since LWMAC adopts duty-cycle scheme, a node only wakes up for a short
* period in each cycle. Thus, to probe where is the wake-up period of the
* receiver, a sender sends WR (preamble) packets to notice the receiver for
* communication. To ensure that the receiver will catch at least one WR
* packet in one cycle, the sender repeatedly broadcasts a stream of WR packets
* with the broadcast duration (preamble duration) slightly longer period than
* @ref GNRC_LWMAC_WAKEUP_INTERVAL_US.
*/
#ifndef GNRC_LWMAC_PREAMBLE_DURATION_US
#define GNRC_LWMAC_PREAMBLE_DURATION_US ((13LU * GNRC_LWMAC_WAKEUP_INTERVAL_US) / 10)
#endif
/**
* @brief Timeout to send the next WR in case no WA has been received during that
* time.
*
* In LWMAC, when a sender initiates a transmission to a receiver, it starts with
* sending a stream of repeated WR packets with @ref GNRC_LWMAC_TIME_BETWEEN_WR_US interval
* between two consecutive WRs. After sending one WR (preamble) packet, the sender turns
* to the listen mode to receive the potential incoming WA (preamble-ACK) packet with
* a timeout of @ref GNRC_LWMAC_TIME_BETWEEN_WR_US. If no WA is received during
* @ref GNRC_LWMAC_TIME_BETWEEN_WR_US, the sender starts sending the next WR.
* It is referenced to the beginning of both WRs, but due to internal
* overhead, the exact spacing is slightly higher.
* The minimum possible value depends on the time it takes to completely
* send a WR with the given hardware (including processor) and data rate.
*/
#ifndef GNRC_LWMAC_TIME_BETWEEN_WR_US
#define GNRC_LWMAC_TIME_BETWEEN_WR_US (5U * US_PER_MS)
#endif
/**
* @brief How long a node in LWMAC should keep awake and listen on the channel in one cycle.
*
* LWMAC adopts the duty-cycle scheme that a node only wakes up for a short
* period of @ref GNRC_LWMAC_WAKEUP_DURATION_US in each cycle. In the rest of the cycle, the node
* turns off the radio to conserve power. @ref GNRC_LWMAC_WAKEUP_DURATION_US is set to twice the
* duration of @ref GNRC_LWMAC_TIME_BETWEEN_WR_US, to guarantee that the wake-up period is long
* enough that receiver will not miss the WR (preamble) packet.
* Receiver needs to support @ref NETDEV_EVENT_RX_STARTED event in order to use time-between-WR
* as a sensible default here. Otherwise the duration of WRs as well as longest
* possible data broadcasts need to be taken into account.
*/
#ifndef GNRC_LWMAC_WAKEUP_DURATION_US
#define GNRC_LWMAC_WAKEUP_DURATION_US (GNRC_LWMAC_TIME_BETWEEN_WR_US * 2)
#endif
/**
* @brief How long broadcast packets @ref gnrc_lwmac_frame_broadcast_t will be sent to make sure
* every participant has received at least one copy.
*
* Since LWMAC adopts duty-cycle scheme, a node only wakes up for a short period in
* each cycle. Thus, when a node wants to broadcast a packet, it repeatedly broadcasts the
* packet for one @ref GNRC_LWMAC_BROADCAST_DURATION_US duration which is slightly longer
* than @ref GNRC_LWMAC_WAKEUP_INTERVAL_US. This is to ensure that all neighbors will not miss
* the broadcast procedure of the sender and catch at least one copy of the broadcast packet.
*/
#ifndef GNRC_LWMAC_BROADCAST_DURATION_US
#define GNRC_LWMAC_BROADCAST_DURATION_US ((GNRC_LWMAC_WAKEUP_INTERVAL_US * 11) / 10)
#endif
/**
* @brief Time to idle between two successive broadcast packets, referenced to the
* start of the packet.
*
* The same limitation as for @ref GNRC_LWMAC_TIME_BETWEEN_WR_US apply here.
* In LWMAC, when a sender initiates a broadcast, it starts with sending a stream of
* repeated broadcast packets with @ref GNRC_LWMAC_TIME_BETWEEN_BROADCAST_US interval
* between two consecutive broadcast packets. After sending one broadcast packet, the sender
* turns to the listen mode with a timeout of @ref GNRC_LWMAC_TIME_BETWEEN_BROADCAST_US. When this
* timeout expires, the sender sends the next broadcast packet until reaching the maximum
* broadcast duration of @ref GNRC_LWMAC_BROADCAST_DURATION_US.
*/
#ifndef GNRC_LWMAC_TIME_BETWEEN_BROADCAST_US
#define GNRC_LWMAC_TIME_BETWEEN_BROADCAST_US (GNRC_LWMAC_TIME_BETWEEN_WR_US)
#endif
/**
* @brief WR preparation overhead before it can be sent (higher with debugging output).
*
* In LWMAC, when a sender wants to send a data packet to the receiver, it starts
* sending the WR stream a little bit earlier (advance) to the beginning edge
* of destination's wake-up phase over time. The idea is not to miss the wake-up
* period of the receiver, otherwise will lead to a long WR procedure.
*/
#ifndef GNRC_LWMAC_WR_PREPARATION_US
#define GNRC_LWMAC_WR_PREPARATION_US ((3U * US_PER_MS))
#endif
/**
* @brief How long to wait after a WA for data to come in.
*
* When a node in LWMAC gets a WR during its wake-up period, it immediately
* replies a WA packet to the sender for acknowledging the sender's transmission
* request. After sending the WA, the receiver waits for the data packet from the
* sender, with a timeout of @ref GNRC_LWMAC_DATA_DELAY_US duration. In case no data will be
* received in this period, the receiver regards reception failed and go back to
* normal listen mode. However, in case the receiver receives other unintended packets,
* like WR/WA packets from other neighbor communication pairs, the receiver resets
* this timeout and continues to wait for the data packet, with the consideration that
* the sender's data transmission might be delayed due to other ongoing transmissions
* (the data packet is transmitted with CSMA/CA).
* This data timeout is long enough to catch the beginning of the packet if the transceiver
* supports @ref NETDEV_EVENT_RX_STARTED event (this can be important for big packets).
*/
#ifndef GNRC_LWMAC_DATA_DELAY_US
#define GNRC_LWMAC_DATA_DELAY_US (10U * US_PER_MS)
#endif
/**
* @brief CSMA retries for DATA packet after WR->WA was successful.
*
* After receiving the WA packet @ref gnrc_lwmac_frame_wa_t from the receiver, the sender
* starts sending the data packet using CSMA/CA. This macro defines how many CSMA retries
* a sender will be allowed to execute for sending its data, before the data is successfully
* sent (gets data ACK from the receiver).
*/
#ifndef GNRC_LWMAC_DATA_CSMA_RETRIES
#define GNRC_LWMAC_DATA_CSMA_RETRIES (3U)
#endif
/**
* @brief Maximum TX transmission retries for DATA packet in case of no response from the receiver.
*
* When a data packet is scheduled for transmission, i.e., pushed into TX for sending,
* LWMAC defines a maximum of @ref GNRC_LWMAC_MAX_DATA_TX_RETRIES retries for transmission of the
* packet. That is, in case of transmission failure in TX due to no WA from the receiver,
* the sender will not drop the packet, but keeps it and retries to send the data packet
* in the following cycles, until the sender reaches the maximum retries limit defined here.
* Then, the packet will be dropped.
*/
#ifndef GNRC_LWMAC_MAX_DATA_TX_RETRIES
#define GNRC_LWMAC_MAX_DATA_TX_RETRIES (3U)
#endif
/**
* @brief MAX burst transmission packet number in one shot.
*
* LWMAC supports burst transmission based on the pending-bit technique, and this macro
* here defines the largest number of packets allowed to be sent in one consecutive
* sequence. In case a sender has multi packets for one receiver,the burst transmission
* procedure is as follow:
* 1. The sender first uses WR stream to locate the receiver's wake-up period (if the
* sender has already phase-locked the receiver's phase, normally the sender only cost
* one WR to get the first WA from the receiver) and then sends its first data.
* 2. After the transmission of the first data, the sender immediately sends a WR to
* the receiver for starting the second round of transmission of the second data. The
* receiver should also immediately reply WA for continue receiving data packets. In
* case the sender doesn't receive WA during @ref GNRC_LWMAC_TIME_BETWEEN_WR_US, it regards the
* consecutive (burst) transmission failed and quits TX procedure (the data will be queued
* back to the transmission queue for normal transmission attempt in following cycles).
* 3. In case the second transmission succeeds, the sender repeats step (2) to send all the
* following pending packets.
* In short, in burst transmission mode, the sender doesn't tolerate no-WA event. ALl the
* pending data packets should be sent with only one WR cost for leading the transmission.
*/
#ifndef GNRC_LWMAC_MAX_TX_BURST_PKT_NUM
#define GNRC_LWMAC_MAX_TX_BURST_PKT_NUM (GNRC_LWMAC_WAKEUP_INTERVAL_US / GNRC_LWMAC_WAKEUP_DURATION_US)
#endif
/**
* @brief MAX bad Listen period extensions a node can tolerate.
*
* In LWMAC, to allow burst transmissions, when in the wake-up period and by default, a node
* will extend its wake-up period to another @ref GNRC_LWMAC_WAKEUP_DURATION_US after each packet
* reception (except for broadcast packet). However, in some cases, a receiver may
* overhear other unintended packets, e.g., WR or WA packets for other nodes, these are
* called bad extensions for the receiver. If a receiver reaches the maximum bad listen
* extension limit defined here, it goes to sleep mode with the consideration that the
* channel is currently unavailable/busy.
*/
#ifndef GNRC_LWMAC_MAX_RX_EXTENSION_NUM
#define GNRC_LWMAC_MAX_RX_EXTENSION_NUM (3U)
#endif
/**
* @brief CSMA retries for broadcast packet.
*
* Currently, each broadcast packet is sent with CSMA/CA for collision avoidance.
* Too many CSMA retries may lead to running out of destinations wake-up period.
*/
#ifndef GNRC_LWMAC_BROADCAST_CSMA_RETRIES
#define GNRC_LWMAC_BROADCAST_CSMA_RETRIES (3U)
#endif
/**
* @brief Default message queue size to use for the LWMAC thread.
*
* The value of this macro should be enough for supporting the manipulation of
* LWMAC.
*
*/
#ifndef GNRC_LWMAC_IPC_MSG_QUEUE_SIZE
#define GNRC_LWMAC_IPC_MSG_QUEUE_SIZE (8U)
#endif
/**
* @brief Initialize an instance of the LWMAC layer
*
* The initialization starts a new thread that connects to the given netdev
* device and starts a link layer event loop.
*
* @param[in] stack stack for the control thread
* @param[in] stacksize size of *stack*
* @param[in] priority priority for the thread housing the LWMAC instance
* @param[in] name name of the thread housing the LWMAC instance
* @param[in] dev netdev device, needs to be already initialized
*
* @return PID of LWMAC thread on success
* @return -EINVAL if creation of thread fails
* @return -ENODEV if *dev* is invalid
*/
kernel_pid_t gnrc_lwmac_init(char *stack, int stacksize, char priority,
const char *name, gnrc_netdev_t *dev);
#ifdef __cplusplus
}
#endif
#endif /* NET_GNRC_LWMAC_LWMAC_H */
/** @} */

102
sys/include/net/gnrc/lwmac/timeout.h

@ -0,0 +1,102 @@
/*
* Copyright (C) 2015 Daniel Krebs
* 2016 INRIA
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup net_gnrc_lwmac
* @{
*
* @file
* @brief Timeout handling of LWMAC
*
*
* @author Daniel Krebs <github@daniel-krebs.net>
* @author Shuguo Zhuo <shuguo.zhuo@inria.fr>
*/
#ifndef NET_GNRC_LWMAC_TIMEOUT_H
#define NET_GNRC_LWMAC_TIMEOUT_H
#include <stdint.h>
#include <stdbool.h>
#include "net/gnrc/netdev.h"
#include "net/gnrc/lwmac/types.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Static initializer for @ref gnrc_lwmac_timeout_t.
*/
#define GNRC_LWMAC_TIMEOUT_INITIAL { {}, {}, false, TIMEOUT_DISABLED }
/**
* @brief Set LWMAC timeout of type @p type of offset @p offset.
*
* @param[in,out] gnrc_netdev gnrc_netdev structure
* @param[in] type LWMAC timeout type
* @param[in] offset timeout offset
*/
void gnrc_lwmac_set_timeout(gnrc_netdev_t *gnrc_netdev,
gnrc_lwmac_timeout_type_t type,
uint32_t offset);
/**
* @brief Clear LWMAC timeout of type @p type.
*
* @param[in,out] gnrc_netdev gnrc_netdev structure
* @param[in] type LWMAC timeout type
*/
void gnrc_lwmac_clear_timeout(gnrc_netdev_t *gnrc_netdev, gnrc_lwmac_timeout_type_t type);
/**
* @brief Check whether LWMAC timeout of type @p type is running.
*
* @param[in] gnrc_netdev gnrc_netdev structure
* @param[in] type LWMAC timeout type
*
* @return true, if timeout of type @p type is running.
* @return false, if timeout of type @p type is not running.
*/
bool gnrc_lwmac_timeout_is_running(gnrc_netdev_t *gnrc_netdev,
gnrc_lwmac_timeout_type_t type);
/**
* @brief Check whether LWMAC timeout of type @p type is expired. It will clear
* the timeout once it is found expired.
*
* @param[in,out] gnrc_netdev gnrc_netdev structure
* @param[in] type LWMAC timeout type
*
* @return true, if timeout of type @p type is expired.
* @return false, if timeout of type @p type is not expired, or not exist.
*/
bool gnrc_lwmac_timeout_is_expired(gnrc_netdev_t *gnrc_netdev, gnrc_lwmac_timeout_type_t type);
/**
* @brief Reset all LWMAC timeouts.
*
* @param[in,out] gnrc_netdev gnrc_netdev structure
*/
void gnrc_lwmac_reset_timeouts(gnrc_netdev_t *gnrc_netdev);
/**
* @brief Make a specific LWMAC timeout expired.
*
* @param[in,out] timeout LWMAC tiemout
*/
void gnrc_lwmac_timeout_make_expire(gnrc_lwmac_timeout_t *timeout);
#ifdef __cplusplus
}
#endif
#endif /* NET_GNRC_LWMAC_TIMEOUT_H */
/** @} */

223
sys/include/net/gnrc/lwmac/types.h

@ -0,0 +1,223 @@
/*
* Copyright (C) 2015 Daniel Krebs
* 2016 INRIA
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup net_gnrc_lwmac
* @{
*
* @file
* @brief Definition of internal types used by LWMAC
*
*
* @author Daniel Krebs <github@daniel-krebs.net>
* @author Shuguo Zhuo <shuguo.zhuo@inria.fr>
*/
#ifndef NET_GNRC_LWMAC_TYPES_H
#define NET_GNRC_LWMAC_TYPES_H
#include "msg.h"
#include "xtimer.h"
#include "net/gnrc/lwmac/hdr.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief LWMAC RTT event type.
*/
#define GNRC_LWMAC_EVENT_RTT_TYPE (0x4300)
/**
* @brief LWMAC RTT start event type.
*/
#define GNRC_LWMAC_EVENT_RTT_START (0x4301)
/**
* @brief LWMAC RTT stop event type.
*/
#define GNRC_LWMAC_EVENT_RTT_STOP (0x4302)
/**
* @brief LWMAC RTT pause event type.
*/
#define GNRC_LWMAC_EVENT_RTT_PAUSE (0x4303)
/**
* @brief LWMAC RTT resume event type.
*/
#define GNRC_LWMAC_EVENT_RTT_RESUME (0x4304)
/**
* @brief LWMAC RTT wakeup pending event type.
*/
#define GNRC_LWMAC_EVENT_RTT_WAKEUP_PENDING (0x4305)
/**
* @brief LWMAC RTT sleep pending event type.
*/
#define GNRC_LWMAC_EVENT_RTT_SLEEP_PENDING (0x4306)
/**
* @brief LWMAC timeout event type.
*/
#define GNRC_LWMAC_EVENT_TIMEOUT_TYPE (0x4400)
/**
* @brief LWMAC duty-cycle active flag.
*
* Keep track of duty cycling to avoid late RTT events after stopping.
*/
#define GNRC_LWMAC_DUTYCYCLE_ACTIVE (0x01)
/**
* @brief LWMAC needs reschedule flag.
*
* Used internally for rescheduling state machine update, e.g. after state
* transition caused in update.
*/
#define GNRC_LWMAC_NEEDS_RESCHEDULE (0x02)
/**
* @brief LWMAC check radio's on/off state flag.
*/
#define GNRC_LWMAC_RADIO_IS_ON (0x04)
/**
* @brief Enable/disable duty-cycle record and print out.
* Set "1" to enable, set "0" to disable.
*/
#ifndef GNRC_LWMAC_ENABLE_DUTYCYLE_RECORD
#define GNRC_LWMAC_ENABLE_DUTYCYLE_RECORD (0U)
#endif
/**
* @brief The default largest number of parallel timeouts in LWMAC
*/
#ifndef GNRC_LWMAC_TIMEOUT_COUNT
#define GNRC_LWMAC_TIMEOUT_COUNT (3U)
#endif
/**
* @brief Internal states of LWMAC
*/
typedef enum {
GNRC_LWMAC_UNDEF = -1, /**< Undefined state of LWMAC */
GNRC_LWMAC_STOPPED, /**< LWMAC's main state machine has been stopped */
GNRC_LWMAC_START, /**< Start LWMAC's main state machine */
GNRC_LWMAC_STOP, /**< Stop LWMAC's main state machine */
GNRC_LWMAC_RESET, /**< Reset LWMAC's main state machine */
GNRC_LWMAC_LISTENING, /**< Listen the channel for receiving packets */
GNRC_LWMAC_RECEIVING, /**< RX is handled in own state machine */
GNRC_LWMAC_TRANSMITTING, /**< TX is handled in own state machine */
GNRC_LWMAC_SLEEPING, /**< Turn off radio to conserve power */
GNRC_LWMAC_STATE_COUNT /**< Count of LWMAC's states */
} gnrc_lwmac_state_t;
/**
* @brief TX states of LWMAC
*/
typedef enum {
GNRC_LWMAC_TX_STATE_STOPPED, /**< Tx schedule stopped, stop sending packet */
GNRC_LWMAC_TX_STATE_INIT, /**< Initiate transmission */
GNRC_LWMAC_TX_STATE_SEND_BROADCAST, /**< directly goes to SUCCESSFUL or FAILED when finished */
GNRC_LWMAC_TX_STATE_SEND_WR, /**< Send a wakeup request */
GNRC_LWMAC_TX_STATE_WAIT_WR_SENT, /**< Wait until WR sent to set timeout */
GNRC_LWMAC_TX_STATE_WAIT_FOR_WA, /**< Wait for dest node's wakeup ackknowledge */
GNRC_LWMAC_TX_STATE_SEND_DATA, /**< Send the actual payload data */
GNRC_LWMAC_TX_STATE_WAIT_FEEDBACK, /**< Wait if packet was ACKed */
GNRC_LWMAC_TX_STATE_SUCCESSFUL, /**< Transmission has finished successfully */
GNRC_LWMAC_TX_STATE_FAILED /**< Payload data couldn't be delivered to dest */
} gnrc_lwmac_tx_state_t;
/**
* @brief Static initializer for gnrc_lwmac_tx_state_t.
*/
#define GNRC_LWMAC_TX_STATE_INITIAL GNRC_LWMAC_TX_STATE_STOPPED
/**
* @brief RX states of LWMAC
*/
typedef enum {
GNRC_LWMAC_RX_STATE_STOPPED, /**< Rx schedule stopped */
GNRC_LWMAC_RX_STATE_INIT, /**< Initiate reception */
GNRC_LWMAC_RX_STATE_WAIT_FOR_WR, /**< Wait for a wakeup request */
GNRC_LWMAC_RX_STATE_SEND_WA, /**< Send wakeup ackknowledge to requesting node */
GNRC_LWMAC_RX_STATE_WAIT_WA_SENT, /**< Wait until WA sent to set timeout */
GNRC_LWMAC_RX_STATE_WAIT_FOR_DATA, /**< Wait for actual payload data */
GNRC_LWMAC_RX_STATE_SUCCESSFUL, /**< Recption has finished successfully */
GNRC_LWMAC_RX_STATE_FAILED /**< Reception over, but nothing received */
} gnrc_lwmac_rx_state_t;
/**
* @brief Static initializer for gnrc_lwmac_rx_state_t.
*/
#define GNRC_LWMAC_RX_STATE_INITIAL GNRC_LWMAC_RX_STATE_STOPPED
/**
* @brief LWMAC uninitialized phase value
*/
#define GNRC_LWMAC_PHASE_UNINITIALIZED (0)
/**
* @brief LWMAC max phase value
*/
#define GNRC_LWMAC_PHASE_MAX (-1)
/**
* @brief LWMAC timeout types
*/
typedef enum {
GNRC_LWMAC_TIMEOUT_DISABLED, /**< Timeout is diabled */
GNRC_LWMAC_TIMEOUT_WR, /**< WR timeout, waiting WA */
GNRC_LWMAC_TIMEOUT_NO_RESPONSE, /**< Maximum WR duration timeout awaiting WA */
GNRC_LWMAC_TIMEOUT_DATA, /**< Timeout awaiting data packet from receiver */
GNRC_LWMAC_TIMEOUT_WAIT_DEST_WAKEUP, /**< Timeout for waiting receiver's wake-up phase */
GNRC_LWMAC_TIMEOUT_WAKEUP_PERIOD, /**< Wake up period timeout for going to sleep */
GNRC_LWMAC_TIMEOUT_NEXT_BROADCAST, /**< Timeout for waiting to send the next broadcast packet */
GNRC_LWMAC_TIMEOUT_BROADCAST_END, /**< Timeout awaiting the end of the whole broadcast period */
} gnrc_lwmac_timeout_type_t;
/**
* @brief LWMAC timeout structure
*/
typedef struct {
xtimer_t timer; /**< xtimer entity */
msg_t msg; /**< msg entity */
bool expired; /**< If type != DISABLED, this indicates if timeout has expired */
gnrc_lwmac_timeout_type_t type; /**< timeout type */
} gnrc_lwmac_timeout_t;
/**
* @brief LWMAC specific structure for storing internal states.
*/
typedef struct lwmac {
gnrc_lwmac_state_t state; /**< Internal state of MAC layer */
uint32_t last_wakeup; /**< Used to calculate wakeup times */
uint8_t lwmac_info; /**< LWMAC's internal informations (flags) */
gnrc_lwmac_timeout_t timeouts[GNRC_LWMAC_TIMEOUT_COUNT]; /**< Store timeouts used for protocol */
#if (GNRC_LWMAC_ENABLE_DUTYCYLE_RECORD == 1)
/* Parameters for recording duty-cycle */
uint32_t last_radio_on_time_ticks; /**< The last time in ticks when radio is on */
uint32_t radio_off_time_ticks; /**< The time in ticks when radio is off */
uint32_t system_start_time_ticks; /**< The time in ticks when chip is started */
uint32_t awake_duration_sum_ticks; /**< The sum of time in ticks when radio is on */
uint32_t pkt_start_sending_time_ticks; /**< The time in ticks when the packet is started
to be sent */
#endif
} gnrc_lwmac_t;
#ifdef __cplusplus
}
#endif
#endif /* NET_GNRC_LWMAC_TYPES_H */
/** @} */

16
sys/include/net/gnrc/mac/types.h

@ -29,6 +29,7 @@
#include "net/gnrc/priority_pktqueue.h"
#include "net/ieee802154.h"
#include "net/gnrc/mac/mac.h"
#include "net/gnrc/lwmac/types.h"
#ifdef __cplusplus
extern "C" {
@ -66,6 +67,12 @@ typedef struct {
#if (GNRC_MAC_DISPATCH_BUFFER_SIZE != 0) || defined(DOXYGEN)
gnrc_pktsnip_t *dispatch_buffer[GNRC_MAC_DISPATCH_BUFFER_SIZE]; /**< dispatch packet buffer */
#endif /* (GNRC_MAC_DISPATCH_BUFFER_SIZE != 0) || defined(DOXYGEN) */
#ifdef MODULE_GNRC_LWMAC
gnrc_lwmac_l2_addr_t l2_addr; /**< Records the sender's address */
gnrc_lwmac_rx_state_t state; /**< LWMAC specific internal reception state */
uint8_t rx_bad_exten_count; /**< Count how many unnecessary RX extensions have been executed */
#endif
} gnrc_mac_rx_t;
/**
@ -157,6 +164,15 @@ typedef struct {
gnrc_priority_pktqueue_node_t _queue_nodes[GNRC_MAC_TX_QUEUE_SIZE]; /**< Shared buffer for TX queue nodes */
gnrc_pktsnip_t *packet; /**< currently scheduled packet for sending */
#endif /* (GNRC_MAC_TX_QUEUE_SIZE != 0) || defined(DOXYGEN) */
#ifdef MODULE_GNRC_LWMAC
gnrc_lwmac_tx_state_t state; /**< LWMAC specific internal transmission state */
uint32_t wr_sent; /**< Count how many WRs were sent until WA received */
uint32_t timestamp; /**< Records the receiver's current phase */
uint8_t bcast_seqnr; /**< Sequence number for broadcast data to filter at receiver */
uint8_t tx_burst_count; /**< Count how many consecutive packets have been transmitted */
uint8_t tx_retry_count; /**< Count how many Tx-retrials have been executed before packet drop */
#endif
} gnrc_mac_tx_t;
/**

8
sys/include/net/gnrc/netdev.h

@ -152,6 +152,14 @@ typedef struct gnrc_netdev {
*/
gnrc_mac_tx_t tx;
#endif /* ((GNRC_MAC_TX_QUEUE_SIZE != 0) || (GNRC_MAC_NEIGHBOR_COUNT == 0)) || defined(DOXYGEN) */
#ifdef MODULE_GNRC_LWMAC
/**
* @brief LWMAC specific structure object for storing LWMAC internal states.
*/
gnrc_lwmac_t lwmac;
#endif
#endif /* MODULE_GNRC_MAC */
} gnrc_netdev_t;

11
sys/include/net/gnrc/nettype.h

@ -56,6 +56,17 @@ typedef enum {
GNRC_NETTYPE_SIXLOWPAN, /**< Protocol is 6LoWPAN */
#endif
/**
* @{
* @name Link layer
*/
#ifdef MODULE_GNRC_LWMAC
GNRC_NETTYPE_LWMAC, /**< Protocol is lwMAC */
#endif
/**
* @}
*/
/**
* @{
* @name Network layer

3
sys/net/gnrc/Makefile

@ -67,6 +67,9 @@ endif
ifneq (,$(filter gnrc_pkt,$(USEMODULE)))
DIRS += pkt
endif
ifneq (,$(filter gnrc_lwmac,$(USEMODULE)))
DIRS += link_layer/lwmac
endif
ifneq (,$(filter gnrc_pktbuf_static,$(USEMODULE)))
DIRS += pktbuf_static
endif

11
sys/net/gnrc/link_layer/gnrc_mac/internal.c

@ -241,6 +241,17 @@ void gnrc_mac_dispatch(gnrc_mac_rx_t *rx)
for (unsigned i = 0; i < GNRC_MAC_DISPATCH_BUFFER_SIZE; i++) {
if (rx->dispatch_buffer[i]) {
#ifdef MODULE_GNRC_LWMAC
/* save pointer to netif header */
gnrc_pktsnip_t *netif = rx->dispatch_buffer[i]->next->next;
/* remove lwmac header */
rx->dispatch_buffer[i]->next->next = NULL;
gnrc_pktbuf_release(rx->dispatch_buffer[i]->next);
/* make append netif header after payload again */
rx->dispatch_buffer[i]->next = netif;
#endif
if (!gnrc_netapi_dispatch_receive(rx->dispatch_buffer[i]->type,
GNRC_NETREG_DEMUX_CTX_ALL,
rx->dispatch_buffer[i])) {

3
sys/net/gnrc/link_layer/lwmac/Makefile

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

370
sys/net/gnrc/link_layer/lwmac/include/lwmac_internal.h

@ -0,0 +1,370 @@
/*
* Copyright (C) 2015 Daniel Krebs
* 2016 INRIA
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup net_gnrc_lwmac
* @{
*
* @file
* @brief Interface definition for internal functions of LWMAC protocol
*
* @author Daniel Krebs <github@daniel-krebs.net>
* @author Shuguo Zhuo <shuguo.zhuo@inria.fr>
*/
#ifndef LWMAC_INTERNAL_H
#define LWMAC_INTERNAL_H
#include <stdint.h>
#include "periph/rtt.h"
#include "net/gnrc/netdev.h"
#include "net/gnrc/mac/types.h"
#include "net/gnrc/lwmac/types.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Flag to track if the sender can continue to transmit packet to
* the receiver in its TX procedure.
*
* LWMAC supports burst transmission based on the pending-bit technique.
* Namely, if the sender has multi packets for the same receiver, it can
* successively transmit its packets back to back with this flag set up,
* with the awareness that the receiver will also keep awake for receptions.
*/
#define GNRC_NETDEV_LWMAC_TX_CONTINUE (0x0008U)
/**
* @brief Flag to track if the sender should quit Tx in current cycle.
*
* This flag is mainly for collision avoidance. In case a node overhears
* ongoing broadcast packets stream or other ongoing transmissions of
* other communication pairs during its wake-up period, it sets up this
* flag, which quits all its potential transmission attempts in this current
* cycle (started by the wake-up period), thus not to collide with other
* (neighbor) nodes' transmissions.
*/
#define GNRC_NETDEV_LWMAC_QUIT_TX (0x0010U)
/**
* @brief Flag to track if the device need to reselect a new wake-up phase.
*
* This flag is mainly for potential collision avoidance. In multi-hop scenario,
* it could be dangerous that a sender's wake-up phase is close to its receiver's,
* which may lead to collisions when the sender is sending to the receiver while
* the sender's son nodes are also sending to the sender. To avoid this, in case a
* sender finds its phase close to its receiver's, it sets up this flag and then
* randomly reselects a new wake-up phase.
*/
#define GNRC_NETDEV_LWMAC_PHASE_BACKOFF (0x0020U)
/**