Merge pull request #6554 from zhuoshuguo/lwmac_retry
LWMAC: A simple duty cycling 802.15.4 MAC protocol (2nd try).
This commit is contained in:
commit
9ce9dd601c
@ -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
Normal file
61
examples/gnrc_networking_mac/Makefile
Normal file
@ -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
Normal file
79
examples/gnrc_networking_mac/README.md
Normal file
@ -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
Normal file
50
examples/gnrc_networking_mac/main.c
Normal file
@ -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
Normal file
174
examples/gnrc_networking_mac/udp.c
Normal file
@ -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;
|
||||
}
|
||||
@ -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)) {}
|
||||
}
|
||||
70
tests/lwmac/Makefile
Normal file
70
tests/lwmac/Makefile
Normal file
@ -0,0 +1,70 @@
|
||||
# name of your application
|
||||
APPLICATION = lwmac
|
||||
|
||||
# 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)/../..
|
||||
|
||||
# Uncomment these lines if you want to use platform support from external
|
||||
# repositories:
|
||||
#RIOTCPU ?= $(CURDIR)/../../RIOT/thirdparty_cpu
|
||||
#RIOTBOARD ?= $(CURDIR)/../../RIOT/thirdparty_boards
|
||||
|
||||
# Uncomment this to enable scheduler statistics for ps:
|
||||
#CFLAGS += -DSCHEDSTATISTICS
|
||||
|
||||
# If you want to use native with valgrind, you should recompile native
|
||||
# with the target all-valgrind instead of all:
|
||||
# make -B clean all-valgrind
|
||||
|
||||
# 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
|
||||
|
||||
# Change this to 0 show compiler invocation lines by default:
|
||||
QUIET ?= 1
|
||||
|
||||
# Modules to include:
|
||||
USEMODULE += shell
|
||||
USEMODULE += shell_commands
|
||||
USEMODULE += ps
|
||||
# include and auto-initialize all available sensors
|
||||
USEMODULE += saul_default
|
||||
|
||||
BOARD_PROVIDES_NETIF := airfy-beacon cc2538dk fox iotlab-m3 iotlab-a8-m3 mulle \
|
||||
microbit native nrf51dongle nrf52dk nrf6310 openmote-cc2538 pba-d-01-kw2x \
|
||||
pca10000 pca10005 remote-pa remote-reva saml21-xpro samr21-xpro \
|
||||
spark-core telosb yunjia-nrf51822 z1
|
||||
|
||||
# Use modules for networking
|
||||
# gnrc is a meta module including all required, basic gnrc networking modules
|
||||
USEMODULE += gnrc
|
||||
# use the default network interface for the board
|
||||
USEMODULE += gnrc_netdev_default
|
||||
# automatically initialize the network interface
|
||||
USEMODULE += auto_init_gnrc_netif
|
||||
# shell command to send L2 packets with a simple string
|
||||
USEMODULE += gnrc_txtsnd
|
||||
# the application dumps received packets to stdout
|
||||
USEMODULE += gnrc_pktdump
|
||||
# Use LWMAC
|
||||
USEMODULE += gnrc_lwmac
|
||||
# We use only the lower layers of the GNRC network stack, hence, we can
|
||||
# reduce the size of the packet buffer a bit
|
||||
CFLAGS += -DGNRC_PKTBUF_SIZE=512
|
||||
|
||||
FEATURES_OPTIONAL += config
|
||||
|
||||
include $(RIOTBASE)/Makefile.include
|
||||
|
||||
# Set a custom channel if needed
|
||||
DEFAULT_CHANNEL ?= 26
|
||||
CFLAGS += -DIEEE802154_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL)
|
||||
53
tests/lwmac/README.md
Normal file
53
tests/lwmac/README.md
Normal file
@ -0,0 +1,53 @@
|
||||
examples/default
|
||||
================
|
||||
This application is a showcase for testing LWMAC communications. Using it
|
||||
for your board, you should be able to interactively use any hardware
|
||||
that is supported for communications among devices based on LWMAC.
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
Build, flash and start the application:
|
||||
```
|
||||
export BOARD=your_board
|
||||
make
|
||||
make flash
|
||||
make term
|
||||
```
|
||||
|
||||
The `term` make target starts a terminal emulator for your board. It
|
||||
connects to a default port so you can interact with the shell, usually
|
||||
that is `/dev/ttyUSB0`. If your port is named differently, the
|
||||
`PORT=/dev/yourport` variable can be used to override this.
|
||||
|
||||
|
||||
Example output
|
||||
==========
|
||||
|
||||
The `ifconfig` command will help you to configure all available network
|
||||
interfaces. On an samr21 board it will print something like:
|
||||
```
|
||||
2015-09-16 16:58:37,762 - INFO # ifconfig
|
||||
2015-09-16 16:58:37,766 - INFO # Iface 4 HWaddr: 9e:72 Channel: 26 NID: 0x23 TX-Power: 0dBm State: IDLE CSMA Retries: 4
|
||||
2015-09-16 16:58:37,768 - INFO # Long HWaddr: 36:32:48:33:46:da:9e:72
|
||||
2015-09-16 16:58:37,769 - INFO # AUTOACK CSMA
|
||||
2015-09-16 16:58:37,770 - INFO # Source address length: 2
|
||||
```
|
||||
|
||||
The `txtsnd` command allows you to send a simple string directly over the link
|
||||
layer (here, it is LWMAC) using unicast or broadcast. The application will also automatically print
|
||||
information about any received packet over the serial. This will look like:
|
||||
```
|
||||
2015-09-16 16:59:29,187 - INFO # PKTDUMP: data received:
|
||||
2015-09-16 16:59:29,189 - INFO # ~~ SNIP 0 - size: 28 byte, type:
|
||||
NETTYPE_UNDEF (0)
|
||||
2015-09-16 16:59:29,190 - INFO # 000000 7b 3b 3a 02 85 00 e7 fb 00 00 00 00 01
|
||||
02 5a 55
|
||||
2015-09-16 16:59:29,192 - INFO # 000010 40 42 3e 62 f2 1a 00 00 00 00 00 00
|
||||
2015-09-16 16:59:29,194 - INFO # ~~ SNIP 1 - size: 18 byte, type:
|
||||
NETTYPE_NETIF (-1)
|
||||
2015-09-16 16:59:29,195 - INFO # if_pid: 4 rssi: 49 lqi: 78
|
||||
2015-09-16 16:59:29,196 - INFO # src_l2addr: 5a:55:40:42:3e:62:f2:1a
|
||||
2015-09-16 16:59:29,197 - INFO # dst_l2addr: ff:ff
|
||||
2015-09-16 16:59:29,198 - INFO # ~~ PKT - 2 snips, total size: 46 byte
|
||||
```
|
||||
67
tests/lwmac/main.c
Normal file
67
tests/lwmac/main.c
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (C) 2008, 2009, 2010 Kaspar Schleiser <kaspar@schleiser.de>
|
||||
* Copyright (C) 2013 INRIA
|
||||
* Copyright (C) 2013 Ludwig Knüpfer <ludwig.knuepfer@fu-berlin.de>
|
||||
*
|
||||
* 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 Default application that shows a lot of functionality of RIOT
|
||||
*
|
||||
* @author Kaspar Schleiser <kaspar@schleiser.de>
|
||||
* @author Oliver Hahm <oliver.hahm@inria.fr>
|
||||
* @author Ludwig Knüpfer <ludwig.knuepfer@fu-berlin.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "thread.h"
|
||||
#include "shell.h"
|
||||
#include "shell_commands.h"
|
||||
|
||||
#if FEATURE_PERIPH_RTC
|
||||
#include "periph/rtc.h"
|
||||
#endif
|
||||
|
||||
#ifdef MODULE_LTC4150
|
||||
#include "ltc4150.h"
|
||||
#endif
|
||||
|
||||
#ifdef MODULE_NETIF
|
||||
#include "net/gnrc/pktdump.h"
|
||||
#include "net/gnrc.h"
|
||||
#endif
|
||||
|
||||
int main(void)
|
||||
{
|
||||
#ifdef MODULE_LTC4150
|
||||
ltc4150_start();
|
||||
#endif
|
||||
|
||||
#ifdef FEATURE_PERIPH_RTC
|
||||
rtc_init();
|
||||
#endif
|
||||
|
||||
#ifdef MODULE_NETIF
|
||||
gnrc_netreg_entry_t dump = GNRC_NETREG_ENTRY_INIT_PID(GNRC_NETREG_DEMUX_CTX_ALL,
|
||||
gnrc_pktdump_pid);
|
||||
gnrc_netreg_register(GNRC_NETTYPE_UNDEF, &dump);
|
||||
#endif
|
||||
|
||||
(void) puts("Welcome to RIOT!");
|
||||
|
||||
char line_buf[SHELL_DEFAULT_BUFSIZE];
|
||||
shell_run(NULL, line_buf, SHELL_DEFAULT_BUFSIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user