mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
Merge pull request #11022 from jia200x/pr/gnrc_lorawan
gnrc_lorawan: add support for GNRC based LoRaWAN stack
This commit is contained in:
commit
bf676d4728
12
Makefile.dep
12
Makefile.dep
@ -43,6 +43,15 @@ ifneq (,$(filter gnrc_gomach,$(USEMODULE)))
|
||||
FEATURES_REQUIRED += periph_rtt
|
||||
endif
|
||||
|
||||
ifneq (,$(filter gnrc_lorawan,$(USEMODULE)))
|
||||
USEMODULE += xtimer
|
||||
USEMODULE += random
|
||||
USEMODULE += hashes
|
||||
USEMODULE += crypto
|
||||
USEMODULE += netdev_layer
|
||||
USEMODULE += gnrc_neterr
|
||||
endif
|
||||
|
||||
ifneq (,$(filter nhdp,$(USEMODULE)))
|
||||
USEMODULE += sock_udp
|
||||
USEMODULE += xtimer
|
||||
@ -158,6 +167,9 @@ ifneq (,$(filter gnrc_netif,$(USEMODULE)))
|
||||
ifneq (,$(filter netdev_eth,$(USEMODULE)))
|
||||
USEMODULE += gnrc_netif_ethernet
|
||||
endif
|
||||
ifneq (,$(filter gnrc_lorawan,$(USEMODULE)))
|
||||
USEMODULE += gnrc_netif_lorawan
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq (,$(filter ieee802154 nrfmin esp_now cc110x gnrc_sixloenc,$(USEMODULE)))
|
||||
|
@ -246,6 +246,12 @@ typedef enum {
|
||||
NETDEV_EVENT_CRC_ERROR, /**< wrong CRC */
|
||||
NETDEV_EVENT_FHSS_CHANGE_CHANNEL, /**< channel changed */
|
||||
NETDEV_EVENT_CAD_DONE, /**< channel activity detection done */
|
||||
NETDEV_EVENT_MLME_CONFIRM, /**< MAC MLME confirm event */
|
||||
NETDEV_EVENT_MLME_INDICATION, /**< MAC MLME indication event */
|
||||
NETDEV_EVENT_MCPS_CONFIRM, /**< MAC MCPS confirm event */
|
||||
NETDEV_EVENT_MCPS_INDICATION, /**< MAC MCPS indication event */
|
||||
NETDEV_EVENT_MLME_GET_BUFFER, /**< MAC layer requests MLME buffer */
|
||||
NETDEV_EVENT_MCPS_GET_BUFFER, /**< MAC layer requests MCPS buffer */
|
||||
/* expand this list if needed */
|
||||
} netdev_event_t;
|
||||
|
||||
|
60
examples/gnrc_lorawan/Makefile
Normal file
60
examples/gnrc_lorawan/Makefile
Normal file
@ -0,0 +1,60 @@
|
||||
# name of your application
|
||||
APPLICATION = gnrc_lorawan
|
||||
|
||||
USEMODULE += shell
|
||||
USEMODULE += shell_commands
|
||||
USEMODULE += gnrc_netdev_default
|
||||
USEMODULE += auto_init_gnrc_netif
|
||||
USEMODULE += gnrc_lorawan
|
||||
USEMODULE += gnrc_pktdump
|
||||
|
||||
BOARD ?= b-l072z-lrwan1
|
||||
RIOTBASE ?= ../../
|
||||
|
||||
# Turn on developer helpers
|
||||
DEVELHELP ?= 1
|
||||
|
||||
# use SX1276 by default
|
||||
DRIVER ?= sx1276
|
||||
|
||||
USEMODULE += $(DRIVER)
|
||||
|
||||
# Required for the cipher module */
|
||||
CFLAGS += -DCRYPTO_AES
|
||||
#
|
||||
# We can reduce the size of the packet buffer for LoRaWAN, since there's no IP
|
||||
# support. This will reduce RAM consumption.
|
||||
CFLAGS += -DGNRC_PKTBUF_SIZE=512
|
||||
|
||||
|
||||
########################### COMPILE TIME CONFIGURATION ########################
|
||||
# NOTE: The following options can be configured on runtime as well using
|
||||
# `ifconfig`
|
||||
|
||||
# OTAA compile time configuration keys
|
||||
CFLAGS += -DLORAMAC_APP_KEY_DEFAULT=\{0x00\,0x00\,0x00\,0x00\,0x00\,0x00\,0x00\,0x00\,0x00\,0x00\,0x00\,0x00\,0x00\,0x00\,0x00\,0x00\}
|
||||
CFLAGS += -DLORAMAC_APP_EUI_DEFAULT=\{0x00\,0x00\,0x00\,0x00\,0x00\,0x00\,0x00\,0x00\}
|
||||
CFLAGS += -DLORAMAC_DEV_EUI_DEFAULT=\{0x00\,0x00\,0x00\,0x00\,0x00\,0x00\,0x00\,0x00\}
|
||||
|
||||
# Uncomment and replace with proper keys for joining with ABP
|
||||
# NOTE: This values will be overriten in case of OTAA.
|
||||
#CFLAGS += -DLORAMAC_DEV_ADDR_DEFAULT=\{0x00\,0x00\,0x00\,0x00\}
|
||||
#CFLAGS += -DLORAMAC_NWK_SKEY_DEFAULT=\{0x00\,0x00\,0x00\,0x00\,0x00\,0x00\,0x00\,0x00\,0x00\,0x00\,0x00\,0x00\,0x00\,0x00\,0x00\,0x00\}
|
||||
#CFLAGS += -DLORAMAC_APP_SKEY_DEFAULT=\{0x00\,0x00\,0x00\,0x00\,0x00\,0x00\,0x00\,0x00\,0x00\,0x00\,0x00\,0x00\,0x00\,0x00\,0x00\,0x00\}
|
||||
|
||||
# Comment/uncomment as necessary
|
||||
CFLAGS += -DLORAMAC_DEFAULT_JOIN_PROCEDURE=LORAMAC_JOIN_OTAA
|
||||
#CFLAGS += -DLORAMAC_DEFAULT_JOIN_PROCEDURE=LORAMAC_JOIN_ABP
|
||||
|
||||
# Uncomment to set the highest DR for the EU868 in order to maximize throughput.
|
||||
# If uncommented, the default value (DR0) is used.
|
||||
# Note this value is also used for the OTAA.
|
||||
#CFLAGS += -DLORAMAC_DEFAULT_DR=LORAMAC_DR_5
|
||||
|
||||
# Set the default RX2 datarate to DR3 (used by The Things Network)
|
||||
CFLAGS += -DLORAMAC_DEFAULT_RX2_DR=LORAMAC_DR_3
|
||||
|
||||
# Set default messages to unconfirmable
|
||||
CFLAGS += -DLORAMAC_DEFAULT_TX_MODE=LORAMAC_TX_CNF
|
||||
|
||||
include $(RIOTBASE)/Makefile.include
|
20
examples/gnrc_lorawan/Makefile.ci
Normal file
20
examples/gnrc_lorawan/Makefile.ci
Normal file
@ -0,0 +1,20 @@
|
||||
BOARD_INSUFFICIENT_MEMORY := \
|
||||
arduino-duemilanove \
|
||||
arduino-leonardo \
|
||||
arduino-mega2560 \
|
||||
arduino-nano \
|
||||
arduino-uno \
|
||||
atmega328p \
|
||||
nucleo-f031k6 \
|
||||
nucleo-f042k6 \
|
||||
nucleo-l031k6 \
|
||||
stm32f030f4-demo \
|
||||
stm32f0discovery \
|
||||
msb-430 \
|
||||
msb-430h \
|
||||
telosb \
|
||||
waspmote-pro \
|
||||
wsn430-v1_3b \
|
||||
wsn430-v1_4 \
|
||||
z1 \
|
||||
#
|
145
examples/gnrc_lorawan/README.md
Normal file
145
examples/gnrc_lorawan/README.md
Normal file
@ -0,0 +1,145 @@
|
||||
GNRC LoRaWAN application
|
||||
=============================
|
||||
|
||||
This application is a showcase for testing GNRC LoRaWAN stack. You should be
|
||||
able to send and receive LoRaWAN packets and perform basic LoRaWAN commands
|
||||
(Link Check).
|
||||
|
||||
The MAC layers still doesn't implement any duty cycle restriction mechanism.
|
||||
However, it respects the retransmission procedure.
|
||||
|
||||
Only Class A and EU868 region are supported so far.
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
It's necessary to join the LoRaWAN network either via OTAA or ABP.
|
||||
All keys, addresses and EUIs are in network endian (big endian).
|
||||
|
||||
## OTAA
|
||||
|
||||
Join by OTAA is set by default.
|
||||
Set the Application Key, Device EUI and Application EUI using ifconfig. Assuming
|
||||
the interface pid is 3:
|
||||
|
||||
```
|
||||
ifconfig 3 set deveui AAAAAAAAAAAAAAAA
|
||||
ifconfig 3 set appeui BBBBBBBBBBBBBBBB
|
||||
ifconfig 3 set appkey CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
|
||||
ifconfig 3 up
|
||||
```
|
||||
|
||||
Wait for 5-6 seconds. Type `ifconfig`. The link status should be `up`:
|
||||
|
||||
```
|
||||
Iface 3 HWaddr: 26:01:27:2F Frequency: 868500000Hz BW: 125kHz SF: 7
|
||||
CR: 4/5 Link: up
|
||||
TX-Power: 14dBm State: SLEEP Demod margin.: 0 Num gateways.: 0
|
||||
IQ_INVERT
|
||||
RX_SINGLE OTAA
|
||||
|
||||
```
|
||||
|
||||
## ABP
|
||||
|
||||
Deactivate OTAA using ifconfig and set the AppSKey, NwkSKey and DevAddr;
|
||||
|
||||
```
|
||||
ifconfig 3 -otaa
|
||||
ifconfig 3 set appskey DDDDDDDDDDDDDDDD
|
||||
ifconfig 3 set nwkskey EEEEEEEEEEEEEEEE
|
||||
ifconfig 3 set addr FFFFFFFF
|
||||
ifconfig 3 up
|
||||
```
|
||||
|
||||
The join by ABP occurs immediately.
|
||||
|
||||
Alternatively all keys can be set using CFLAGS so it's only required to
|
||||
select join mode and type `ifconfig <if_pid> up`.
|
||||
|
||||
E.g in the application Makefile:
|
||||
|
||||
```
|
||||
CFLAGS += -DLORAMAC_DEV_EUI_DEFAULT=\{0xAA\,0xAA\,0xAA\,0xAA\,0xAA\,0xAA\,0xAA\,0xAA\}
|
||||
CFLAGS += -DLORAMAC_APP_EUI_DEFAULT=\{0xBB\,0xBB\,0xBB\,0xBB\,0xBB\,0xBB\,0xBB\,0xBB\}
|
||||
CFLAGS += -DLORAMAC_APP_KEY_DEFAULT=\{0xCC\,0xCC\,0xCC\,0xCC\,0xCC\,0xCC\,0xCC\,0xCC\,0xCC\,0xCC\,0xCC\,0xCC\,0xCC\,0xCC\,0xCC\,0xCC\}
|
||||
CFLAGS += -DLORAMAC_APP_SKEY_DEFAULT=\{0xDD\,0xDD\,0xDD\,0xDD\,0xDD\,0xDD\,0xDD\,0xDD\,0xDD\,0xDD\,0xDD\,0xDD\,0xDD\,0xDD\,0xDD\,0xDD\}
|
||||
CFLAGS += -DLORAMAC_NWK_SKEY_DEFAULT=\{0xEE\,0xEE\,0xEE\,0xEE\,0xEE\,0xEE\,0xEE\,0xEE\,0xEE\,0xEE\,0xEE\,0xEE\,0xEE\,0xEE\,0xEE\,0xEE\}
|
||||
CFLAGS += -DLORAMAC_DEV_ADDR_DEFAULT=\{0xFF\,0xFF\,0xFF\,0xFF\}
|
||||
```
|
||||
|
||||
## Send data
|
||||
|
||||
After join, send data using `send` command. E.g to send "Hello RIOT!" to port 2:
|
||||
|
||||
```
|
||||
send 3 "Hello RIOT!" 2
|
||||
```
|
||||
|
||||
## Changing datarate of transmission
|
||||
Use `ifconfig` to change the datarate of the transmission. E.g to set the DR to
|
||||
2:
|
||||
|
||||
```
|
||||
ifconfig 3 set dr 2
|
||||
```
|
||||
|
||||
## Perform a Link Check
|
||||
|
||||
Use `ifconfig` to request a Link Check on the next transmission:
|
||||
|
||||
```
|
||||
ifconfig 3 link_check
|
||||
```
|
||||
|
||||
Send some data. The result of the Link Check request can be seen with
|
||||
`ifconfig`.
|
||||
|
||||
```
|
||||
ifconfig 3 link_check
|
||||
send 3 "Join the RIOT!"
|
||||
```
|
||||
|
||||
Check demodulation margin and number of gateways using `ifconfig`
|
||||
|
||||
```
|
||||
ifconfig
|
||||
Iface 3 HWaddr: 26:01:2C:EA Frequency: 867500000Hz BW: 125kHz SF: 7
|
||||
CR: 4/5 Link: up
|
||||
TX-Power: 14dBm State: SLEEP Demod margin.: 14 Num gateways.: 2
|
||||
IQ_INVERT
|
||||
RX_SINGLE OTAA
|
||||
|
||||
```
|
||||
|
||||
## Confirmable and unconfirmable messages
|
||||
|
||||
Use `ifconfig` to set the `ack_req` flag. With this flag on, messages are
|
||||
confirmable.
|
||||
|
||||
E.g send confirmable messages:
|
||||
|
||||
```
|
||||
ifconfig 3 ack_req
|
||||
send "My confirmable message"
|
||||
```
|
||||
|
||||
And unconfirmable messages:
|
||||
|
||||
```
|
||||
ifconfig 3 -ack_req
|
||||
send "My unconfirmable message"
|
||||
```
|
||||
|
||||
Current state and future plans
|
||||
============
|
||||
|
||||
The current GNRC LoRaWAN stack is still in an experimental state. It's still
|
||||
not compliant with the LoRaWAN specification because some features like duty
|
||||
cycle restrictions and some FOps are missing. Work in progress.
|
||||
|
||||
Next steps:
|
||||
- Add other regions (US915, etc)
|
||||
- Add Adaptive Data Rate
|
||||
- Add Duty Cycle restrictions
|
||||
- Add support for RTC
|
109
examples/gnrc_lorawan/main.c
Normal file
109
examples/gnrc_lorawan/main.c
Normal file
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright (C) 2019 HAW Hamburg
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup tests
|
||||
* @{
|
||||
* @file
|
||||
* @brief Test application for GNRC LoRaWAN
|
||||
*
|
||||
* @author José Ignacio Alamos <jose.alamos@haw-hamburg.de>
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "thread.h"
|
||||
#include "xtimer.h"
|
||||
#include "shell.h"
|
||||
#include "shell_commands.h"
|
||||
|
||||
#include "board.h"
|
||||
|
||||
#include "net/gnrc/netapi.h"
|
||||
#include "net/gnrc/netif.h"
|
||||
|
||||
#include "net/gnrc/pktbuf.h"
|
||||
#include "net/gnrc/pktdump.h"
|
||||
#include "net/gnrc/netreg.h"
|
||||
|
||||
#define LORAWAN_PORT (2U)
|
||||
|
||||
static void _usage(void)
|
||||
{
|
||||
puts("usage: send <if_pid> <payload> [port]");
|
||||
}
|
||||
|
||||
int tx_cmd(int argc, char **argv)
|
||||
{
|
||||
gnrc_pktsnip_t *pkt;
|
||||
uint8_t port = LORAWAN_PORT; /* Default: 2 */
|
||||
int interface;
|
||||
|
||||
if(argc < 3) {
|
||||
_usage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
interface = atoi(argv[1]);
|
||||
/* handle optional parameters */
|
||||
if (argc > 3) {
|
||||
port = atoi(argv[3]);
|
||||
if (port == 0 || port >= 224) {
|
||||
printf("error: invalid port given '%d', "
|
||||
"port can only be between 1 and 223\n", port);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
pkt = gnrc_pktbuf_add(NULL, argv[2], strlen(argv[2]), GNRC_NETTYPE_UNDEF);
|
||||
|
||||
/* register for returned packet status */
|
||||
if (gnrc_neterr_reg(pkt) != 0) {
|
||||
puts("Can not register for error reporting");
|
||||
return 0;
|
||||
}
|
||||
|
||||
gnrc_netapi_set(interface, NETOPT_LORAWAN_TX_PORT, 0, &port, sizeof(port));
|
||||
gnrc_netapi_send(interface, pkt);
|
||||
|
||||
msg_t msg;
|
||||
/* wait for packet status and check */
|
||||
msg_receive(&msg);
|
||||
if ((msg.type != GNRC_NETERR_MSG_TYPE) ||
|
||||
(msg.content.value != GNRC_NETERR_SUCCESS)) {
|
||||
puts("Error sending packet (not joined?)");
|
||||
}
|
||||
else {
|
||||
puts("Successfully sent packet");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const shell_command_t shell_commands[] = {
|
||||
{ "send", "Send LoRaWAN data", tx_cmd},
|
||||
{ NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
|
||||
int main(void)
|
||||
{
|
||||
/* start the shell */
|
||||
puts("Initialization successful - starting the shell now");
|
||||
gnrc_netreg_entry_t dump = GNRC_NETREG_ENTRY_INIT_PID(LORAWAN_PORT,
|
||||
gnrc_pktdump_pid);
|
||||
gnrc_netreg_register(GNRC_NETTYPE_LORAWAN, &dump);
|
||||
char line_buf[SHELL_DEFAULT_BUFSIZE];
|
||||
|
||||
shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE);
|
||||
|
||||
return 0;
|
||||
}
|
@ -6,6 +6,10 @@ ifneq (,$(filter gnrc_sixlowpan_frag_rb,$(USEMODULE)))
|
||||
USEMODULE_INCLUDES += $(RIOTBASE)/sys/net/gnrc/network_layer/sixlowpan/frag
|
||||
endif
|
||||
|
||||
ifneq (,$(filter gnrc_lorawan,$(USEMODULE)))
|
||||
USEMODULE_INCLUDES += $(RIOTBASE)/sys/net/gnrc/link_layer/lorawan/include
|
||||
endif
|
||||
|
||||
ifneq (,$(filter gnrc_sock,$(USEMODULE)))
|
||||
USEMODULE_INCLUDES += $(RIOTBASE)/sys/net/gnrc/sock/include
|
||||
ifneq (,$(filter gnrc_ipv6,$(USEMODULE)))
|
||||
|
@ -21,7 +21,11 @@
|
||||
|
||||
#include "log.h"
|
||||
#include "board.h"
|
||||
#ifdef MODULE_GNRC_LORAWAN
|
||||
#include "net/gnrc/netif/lorawan_base.h"
|
||||
#else
|
||||
#include "net/gnrc/netif/raw.h"
|
||||
#endif
|
||||
#include "net/gnrc.h"
|
||||
|
||||
#include "sx127x.h"
|
||||
@ -56,8 +60,16 @@ void auto_init_sx127x(void)
|
||||
#endif
|
||||
|
||||
sx127x_setup(&sx127x_devs[i], &sx127x_params[i]);
|
||||
#ifdef MODULE_GNRC_LORAWAN
|
||||
/* Currently only one lora device is supported */
|
||||
assert(SX127X_NUMOF == 1);
|
||||
|
||||
gnrc_netif_lorawan_create(sx127x_stacks[i], SX127X_STACKSIZE, SX127X_PRIO,
|
||||
"sx127x", (netdev_t *)&sx127x_devs[i]);
|
||||
#else
|
||||
gnrc_netif_raw_create(sx127x_stacks[i], SX127X_STACKSIZE, SX127X_PRIO,
|
||||
"sx127x", (netdev_t *)&sx127x_devs[i]);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
239
sys/include/net/gnrc/lorawan.h
Normal file
239
sys/include/net/gnrc/lorawan.h
Normal file
@ -0,0 +1,239 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Fundación Inria Chile
|
||||
* Copyright (C) 2019 HAW Hamburg
|
||||
*
|
||||
* 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_lorawan GNRC LoRaWAN
|
||||
* @ingroup net_gnrc
|
||||
* @brief GNRC LoRaWAN stack implementation
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief GNRC LoRaWAN API definition
|
||||
*
|
||||
* @author José Ignacio Alamos <jose.alamos@haw-hamburg.de>
|
||||
* @author Francisco Molina <femolina@uc.cl>
|
||||
*/
|
||||
#ifndef NET_GNRC_LORAWAN_H
|
||||
#define NET_GNRC_LORAWAN_H
|
||||
|
||||
#include "gnrc_lorawan_internal.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief maximum timer drift in percentage
|
||||
*
|
||||
* @note this is only a workaround to compensate inaccurate timers.
|
||||
*
|
||||
* E.g a value of 0.1 means there's a positive drift of 0.1% (set timeout to
|
||||
* 1000 ms => triggers after 1001 ms)
|
||||
*/
|
||||
#ifndef CONFIG_GNRC_LORAWAN_TIMER_DRIFT
|
||||
#define CONFIG_GNRC_LORAWAN_TIMER_DRIFT 1
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief the minimum symbols to detect a LoRa preamble
|
||||
*/
|
||||
#ifndef CONFIG_GNRC_LORAWAN_MIN_SYMBOLS_TIMEOUT
|
||||
#define CONFIG_GNRC_LORAWAN_MIN_SYMBOLS_TIMEOUT 30
|
||||
#endif
|
||||
|
||||
#define GNRC_LORAWAN_REQ_STATUS_SUCCESS (0) /**< MLME or MCPS request successful status */
|
||||
#define GNRC_LORAWAN_REQ_STATUS_DEFERRED (1) /**< the MLME or MCPS confirm message is asynchronous */
|
||||
|
||||
/**
|
||||
* @brief MCPS events
|
||||
*/
|
||||
typedef enum {
|
||||
MCPS_EVENT_RX, /**< MCPS RX event */
|
||||
MCPS_EVENT_NO_RX, /**< MCPS no RX event */
|
||||
MCPS_EVENT_ACK_TIMEOUT /**< MCPS retrans event */
|
||||
} mcps_event_t;
|
||||
|
||||
/**
|
||||
* @brief LoRaWAN activation mechanism
|
||||
*/
|
||||
typedef enum {
|
||||
MLME_ACTIVATION_NONE, /**< MAC layer is not activated */
|
||||
MLME_ACTIVATION_ABP, /**< MAC layer activated by ABP */
|
||||
MLME_ACTIVATION_OTAA /**< MAC layer activated by OTAA */
|
||||
} mlme_activation_t;
|
||||
|
||||
/**
|
||||
* @brief MAC Information Base attributes
|
||||
*/
|
||||
typedef enum {
|
||||
MIB_ACTIVATION_METHOD /**< type is activation method */
|
||||
} mlme_mib_type_t;
|
||||
|
||||
/**
|
||||
* @brief MLME primitive types
|
||||
*/
|
||||
typedef enum {
|
||||
MLME_JOIN, /**< join a LoRaWAN network */
|
||||
MLME_LINK_CHECK, /**< perform a Link Check */
|
||||
MLME_RESET, /**< reset the MAC layer */
|
||||
MLME_SET, /**< set the MIB */
|
||||
MLME_GET, /**< get the MIB */
|
||||
MLME_SCHEDULE_UPLINK /**< schedule uplink indication */
|
||||
} mlme_type_t;
|
||||
|
||||
/**
|
||||
* @brief MCPS primitive types
|
||||
*/
|
||||
typedef enum {
|
||||
MCPS_CONFIRMED, /**< confirmed data */
|
||||
MCPS_UNCONFIRMED /**< unconfirmed data */
|
||||
} mcps_type_t;
|
||||
|
||||
/**
|
||||
* @brief MAC Information Base descriptor for MLME Request-Confirm
|
||||
*/
|
||||
typedef struct {
|
||||
mlme_mib_type_t type; /**< MIB attribute identifier */
|
||||
union {
|
||||
mlme_activation_t activation; /**< holds activation mechanism */
|
||||
};
|
||||
} mlme_mib_t;
|
||||
|
||||
/**
|
||||
* @brief MAC (sub) Layer Management Entity (MLME) request representation
|
||||
*/
|
||||
typedef struct {
|
||||
union {
|
||||
mlme_lorawan_join_t join; /**< Join Data holder */
|
||||
mlme_mib_t mib; /**< MIB holder */
|
||||
};
|
||||
mlme_type_t type; /**< type of the MLME request */
|
||||
} mlme_request_t;
|
||||
|
||||
/**
|
||||
* @brief Mac Common Part Sublayer (MCPS) request representation
|
||||
*/
|
||||
typedef struct {
|
||||
union {
|
||||
mcps_data_t data; /**< MCPS data holder */
|
||||
};
|
||||
mcps_type_t type; /**< type of the MCPS request */
|
||||
} mcps_request_t;
|
||||
|
||||
/**
|
||||
* @brief MAC (sub) Layer Management Entity (MLME) confirm representation
|
||||
*/
|
||||
typedef struct {
|
||||
int16_t status; /**< status of the MLME confirm */
|
||||
mlme_type_t type; /**< type of the MLME confirm */
|
||||
union {
|
||||
mlme_link_req_confirm_t link_req; /**< Link Check confirmation data */
|
||||
mlme_mib_t mib; /**< MIB confirmation data */
|
||||
};
|
||||
} mlme_confirm_t;
|
||||
|
||||
/**
|
||||
* @brief Mac Common Part Sublayer (MCPS) confirm representation
|
||||
*/
|
||||
typedef struct {
|
||||
void *data; /**< data of the MCPS confirm */
|
||||
int16_t status; /**< status of the MCPS confirm */
|
||||
mcps_type_t type; /**< type of the MCPS confirm */
|
||||
} mcps_confirm_t;
|
||||
|
||||
/**
|
||||
* @brief Mac Common Part Sublayer (MCPS) indication representation
|
||||
*/
|
||||
typedef struct {
|
||||
mcps_type_t type; /**< type of the MCPS indication */
|
||||
union {
|
||||
mcps_data_t data; /**< MCPS Data holder */
|
||||
};
|
||||
} mcps_indication_t;
|
||||
|
||||
/**
|
||||
* @brief MAC (sub) Layer Management Entity (MLME) indication representation
|
||||
*/
|
||||
typedef struct {
|
||||
mlme_type_t type; /**< type of the MLME indication */
|
||||
} mlme_indication_t;
|
||||
|
||||
/**
|
||||
* @brief Indicate the MAC layer there was a timeout event
|
||||
*
|
||||
* @param[in] mac pointer to the MAC descriptor
|
||||
*/
|
||||
void gnrc_lorawan_event_timeout(gnrc_lorawan_t *mac);
|
||||
|
||||
/**
|
||||
* @brief Indicate the MAC layer when the transmission finished
|
||||
*
|
||||
* @param[in] mac pointer to the MAC descriptor
|
||||
*/
|
||||
void gnrc_lorawan_event_tx_complete(gnrc_lorawan_t *mac);
|
||||
|
||||
/**
|
||||
* @brief Init GNRC LoRaWAN
|
||||
*
|
||||
* @param[in] mac pointer to the MAC descriptor
|
||||
* @param[in] nwkskey buffer to store the NwkSKey. Should be at least 16 bytes long
|
||||
* @param[in] appskey buffer to store the AppsKey. Should be at least 16 bytes long
|
||||
*/
|
||||
void gnrc_lorawan_init(gnrc_lorawan_t *mac, uint8_t *nwkskey, uint8_t *appskey);
|
||||
|
||||
/**
|
||||
* @brief Perform a MLME request
|
||||
*
|
||||
* @param[in] mac pointer to the MAC descriptor
|
||||
* @param[in] mlme_request the MLME request
|
||||
* @param[out] mlme_confirm the MLME confirm. `mlme_confirm->status` could either
|
||||
* be GNRC_LORAWAN_REQ_STATUS_SUCCESS if the request was OK,
|
||||
* GNRC_LORAWAN_REQ_STATUS_DEFERRED if the confirmation is deferred
|
||||
* or an standard error number
|
||||
*/
|
||||
void gnrc_lorawan_mlme_request(gnrc_lorawan_t *mac, const mlme_request_t *mlme_request,
|
||||
mlme_confirm_t *mlme_confirm);
|
||||
|
||||
/**
|
||||
* @brief Perform a MCPS request
|
||||
*
|
||||
* @param[in] mac pointer to the MAC descriptor
|
||||
* @param[in] mcps_request the MCPS request
|
||||
* @param[out] mcps_confirm the MCPS confirm. `mlme_confirm->status` could either
|
||||
* be GNRC_LORAWAN_REQ_STATUS_SUCCESS if the request was OK,
|
||||
* GNRC_LORAWAN_REQ_STATUS_DEFERRED if the confirmation is deferred
|
||||
* or an standard error number
|
||||
*/
|
||||
void gnrc_lorawan_mcps_request(gnrc_lorawan_t *mac, const mcps_request_t *mcps_request,
|
||||
mcps_confirm_t *mcps_confirm);
|
||||
|
||||
/**
|
||||
* @brief Fetch a LoRaWAN packet from the radio.
|
||||
*
|
||||
* To be called on radio RX done event.
|
||||
*
|
||||
* @param[in] mac pointer to the MAC descriptor
|
||||
*/
|
||||
void gnrc_lorawan_recv(gnrc_lorawan_t *mac);
|
||||
|
||||
/**
|
||||
* @brief Setup GNRC LoRaWAN netdev layers
|
||||
*
|
||||
* @param mac pointer to the MAC descriptor
|
||||
* @param lower pointer to the lower netdev device (radio)
|
||||
*/
|
||||
void gnrc_lorawan_setup(gnrc_lorawan_t *mac, netdev_t *lower);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* NET_GNRC_LORAWAN_H */
|
||||
/** @} */
|
72
sys/include/net/gnrc/lorawan/region.h
Normal file
72
sys/include/net/gnrc/lorawan/region.h
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (C) 2019 HAW Hamburg
|
||||
*
|
||||
* 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_lorawan
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief GNRC LoRaWAN region specific functions
|
||||
*
|
||||
* @author José Ignacio Alamos <jose.alamos@haw-hamburg.de>
|
||||
*/
|
||||
#ifndef NET_GNRC_LORAWAN_REGION_H
|
||||
#define NET_GNRC_LORAWAN_REGION_H
|
||||
|
||||
#include "kernel_defines.h"
|
||||
#include "net/gnrc/lorawan.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Default LoRaWAN channels for current region (EU868)
|
||||
*/
|
||||
static const uint32_t gnrc_lorawan_default_channels[] = {
|
||||
868100000UL,
|
||||
868300000UL,
|
||||
868500000UL
|
||||
};
|
||||
|
||||
#define GNRC_LORAWAN_DEFAULT_CHANNELS_NUMOF \
|
||||
ARRAY_SIZE(gnrc_lorawan_default_channels) /**< Number of default channels */
|
||||
|
||||
/**
|
||||
* @brief Process Channel Frequency list frame
|
||||
*
|
||||
* @param[in] mac pointer to the MAC descriptor
|
||||
* @param[in] cflist the CFList to be processed
|
||||
*/
|
||||
void gnrc_lorawan_process_cflist(gnrc_lorawan_t *mac, uint8_t *cflist);
|
||||
|
||||
/**
|
||||
* @brief Get the datarate of the first reception windows
|
||||
*
|
||||
* @param[in] dr_up the datarate of the transmission
|
||||
* @param[in] dr_offset the offset of the first reception window
|
||||
*
|
||||
* @return datarate
|
||||
*/
|
||||
uint8_t gnrc_lorawan_rx1_get_dr_offset(uint8_t dr_up, uint8_t dr_offset);
|
||||
|
||||
/**
|
||||
* @brief Check if a datarate is valid in the current region
|
||||
*
|
||||
* @param[in] dr the datarate to be checked
|
||||
*
|
||||
* @return true if datarate is valid
|
||||
* @return false otherwise
|
||||
*/
|
||||
bool gnrc_lorawan_validate_dr(uint8_t dr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* NET_GNRC_LORAWAN_REGION_H */
|
@ -34,6 +34,9 @@
|
||||
#include "net/gnrc/netapi.h"
|
||||
#include "net/gnrc/pkt.h"
|
||||
#include "net/gnrc/netif/conf.h"
|
||||
#ifdef MODULE_GNRC_LORAWAN
|
||||
#include "net/gnrc/netif/lorawan.h"
|
||||
#endif
|
||||
#ifdef MODULE_GNRC_SIXLOWPAN
|
||||
#include "net/gnrc/netif/6lo.h"
|
||||
#endif
|
||||
@ -76,6 +79,9 @@ typedef struct {
|
||||
#ifdef MODULE_NETSTATS_L2
|
||||
netstats_t stats; /**< transceiver's statistics */
|
||||
#endif
|
||||
#if defined(MODULE_GNRC_LORAWAN) || DOXYGEN
|
||||
gnrc_netif_lorawan_t lorawan; /**< LoRaWAN component */
|
||||
#endif
|
||||
#if defined(MODULE_GNRC_IPV6) || DOXYGEN
|
||||
gnrc_netif_ipv6_t ipv6; /**< IPv6 component */
|
||||
#endif
|
||||
|
56
sys/include/net/gnrc/netif/lorawan.h
Normal file
56
sys/include/net/gnrc/netif/lorawan.h
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2019 HAW Hamburg
|
||||
*
|
||||
* 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_netif
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief LoRaWAN adaption for @ref net_gnrc_netif
|
||||
*
|
||||
* @author Jose Ignacio Alamos <jose.alamos@haw-hamburg.de>
|
||||
*/
|
||||
#ifndef NET_GNRC_NETIF_LORAWAN_H
|
||||
#define NET_GNRC_NETIF_LORAWAN_H
|
||||
|
||||
#include "net/gnrc/lorawan.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief A Link Check request was scheduled
|
||||
*/
|
||||
#define GNRC_NETIF_LORAWAN_FLAGS_LINK_CHECK (0x1U)
|
||||
|
||||
/**
|
||||
* @brief GNRC LoRaWAN interface descriptor
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t nwkskey[LORAMAC_NWKSKEY_LEN]; /**< network SKey buffer */
|
||||
uint8_t appskey[LORAMAC_APPSKEY_LEN]; /**< App SKey buffer */
|
||||
uint8_t appkey[LORAMAC_APPKEY_LEN]; /**< App Key buffer */
|
||||
uint8_t deveui[LORAMAC_DEVEUI_LEN]; /**< Device EUI buffer */
|
||||
uint8_t appeui[LORAMAC_APPEUI_LEN]; /**< App EUI buffer */
|
||||
gnrc_lorawan_t mac; /**< gnrc lorawan mac descriptor */
|
||||
uint8_t flags; /**< flags for the LoRaWAN interface */
|
||||
uint8_t demod_margin; /**< value of last demodulation margin */
|
||||
uint8_t num_gateways; /**< number of gateways of last link check */
|
||||
uint8_t datarate; /**< LoRaWAN datarate for the next transmission */
|
||||
uint8_t port; /**< LoRaWAN port for the next transmission */
|
||||
uint8_t ack_req; /**< Request ACK in the next transmission */
|
||||
uint8_t otaa; /**< wether the next transmission is OTAA or not */
|
||||
} gnrc_netif_lorawan_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* NET_GNRC_NETIF_LORAWAN_H */
|
||||
/** @} */
|
49
sys/include/net/gnrc/netif/lorawan_base.h
Normal file
49
sys/include/net/gnrc/netif/lorawan_base.h
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2019 HAW Hamburg
|
||||
*
|
||||
* 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_netif
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief LoRaWAN base @ref net_gnrc_netif header
|
||||
*
|
||||
* @author Jose Ignacio Alamos <jose.alamos@haw-hamburg.de>
|
||||
*/
|
||||
#ifndef NET_GNRC_NETIF_LORAWAN_BASE_H
|
||||
#define NET_GNRC_NETIF_LORAWAN_BASE_H
|
||||
|
||||
#include "net/gnrc/netif.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Creates a raw network interface
|
||||
*
|
||||
* @param[in] stack The stack for the network interface's thread.
|
||||
* @param[in] stacksize Size of @p stack.
|
||||
* @param[in] priority Priority for the network interface's thread.
|
||||
* @param[in] name Name for the network interface. May be NULL.
|
||||
* @param[in] dev Device for the interface.
|
||||
*
|
||||
* @see @ref gnrc_netif_create()
|
||||
*
|
||||
* @return The network interface on success.
|
||||
* @return NULL, on error.
|
||||
*/
|
||||
gnrc_netif_t *gnrc_netif_lorawan_create(char *stack, int stacksize, char priority,
|
||||
char *name, netdev_t *dev);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* NET_GNRC_NETIF_LORAWAN_BASE_H */
|
||||
/** @} */
|
@ -119,6 +119,10 @@ typedef enum {
|
||||
GNRC_NETTYPE_NDN, /**< Protocol is NDN */
|
||||
#endif
|
||||
|
||||
#ifdef MODULE_GNRC_LORAWAN
|
||||
GNRC_NETTYPE_LORAWAN, /**< Protocol is LoRaWAN */
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @{
|
||||
* @name Testing
|
||||
|
@ -670,6 +670,29 @@ typedef enum {
|
||||
|
||||
/* add more options if needed */
|
||||
|
||||
/**
|
||||
* @brief (@ref netopt_enable_t) Enable or disable OTAA activation (LoRaWAN)
|
||||
*/
|
||||
NETOPT_OTAA,
|
||||
|
||||
/**
|
||||
* @brief (uint8_t) Get the demodulation margin of the last Link Check request.
|
||||
*/
|
||||
NETOPT_DEMOD_MARGIN,
|
||||
|
||||
/**
|
||||
* @brief (uint8_t) Get the number of gateways of the last Link Check request.
|
||||
*/
|
||||
NETOPT_NUM_GATEWAYS,
|
||||
|
||||
/**
|
||||
* @brief (@ref netopt_enable_t) Perform a Link Check request (LoRaWAN)
|
||||
*
|
||||
* When set, the next transmission will request a Link Check and will
|
||||
* be received on the next downlink
|
||||
*/
|
||||
NETOPT_LINK_CHECK,
|
||||
|
||||
/**
|
||||
* @brief maximum number of options defined here.
|
||||
*
|
||||
|
@ -109,6 +109,10 @@ static const char *_netopt_strmap[] = {
|
||||
[NETOPT_SYNCWORD] = "NETOPT_SYNCWORD",
|
||||
[NETOPT_RANDOM] = "NETOPT_RANDOM",
|
||||
[NETOPT_RX_SYMBOL_TIMEOUT] = "NETOPT_RX_SYMBOL_TIMEOUT",
|
||||
[NETOPT_OTAA] = "NETOPT_OTAA",
|
||||
[NETOPT_DEMOD_MARGIN] = "NETOPT_DEMOD_MARGIN",
|
||||
[NETOPT_NUM_GATEWAYS] = "NETOPT_NUM_GATEWAYS",
|
||||
[NETOPT_LINK_CHECK] = "NETOPT_LINK_CHECK",
|
||||
[NETOPT_NUMOF] = "NETOPT_NUMOF",
|
||||
};
|
||||
|
||||
|
@ -58,6 +58,9 @@ endif
|
||||
ifneq (,$(filter gnrc_pktbuf_malloc,$(USEMODULE)))
|
||||
DIRS += pktbuf_malloc
|
||||
endif
|
||||
ifneq (,$(filter gnrc_lorawan,$(USEMODULE)))
|
||||
DIRS += link_layer/lorawan
|
||||
endif
|
||||
ifneq (,$(filter gnrc_gomach,$(USEMODULE)))
|
||||
DIRS += link_layer/gomach
|
||||
endif
|
||||
|
3
sys/net/gnrc/link_layer/lorawan/Makefile
Normal file
3
sys/net/gnrc/link_layer/lorawan/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
MODULE = gnrc_lorawan
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
352
sys/net/gnrc/link_layer/lorawan/gnrc_lorawan.c
Normal file
352
sys/net/gnrc/link_layer/lorawan/gnrc_lorawan.c
Normal file
@ -0,0 +1,352 @@
|
||||
/*
|
||||
* Copyright (C) 2019 HAW Hamburg
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @author José Ignacio Alamos <jose.alamos@haw-hamburg.de>
|
||||
* @}
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "net/lora.h"
|
||||
#include "net/gnrc/lorawan.h"
|
||||
#include "errno.h"
|
||||
#include "net/gnrc/pktbuf.h"
|
||||
|
||||
#include "net/lorawan/hdr.h"
|
||||
#include "net/loramac.h"
|
||||
#include "net/gnrc/lorawan/region.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
/* This factor is used for converting "real" seconds into microcontroller
|
||||
* microseconds. This is done in order to correct timer drift.
|
||||
*/
|
||||
#define _DRIFT_FACTOR (int) (US_PER_SEC * 100 / (100 + CONFIG_GNRC_LORAWAN_TIMER_DRIFT))
|
||||
|
||||
#define GNRC_LORAWAN_DL_RX2_DR_MASK (0x0F) /**< DL Settings DR Offset mask */
|
||||
#define GNRC_LORAWAN_DL_RX2_DR_POS (0) /**< DL Settings DR Offset pos */
|
||||
#define GNRC_LORAWAN_DL_DR_OFFSET_MASK (0x70) /**< DL Settings RX2 DR mask */
|
||||
#define GNRC_LORAWAN_DL_DR_OFFSET_POS (4) /**< DL Settings RX2 DR pos */
|
||||
|
||||
static inline void gnrc_lorawan_mlme_reset(gnrc_lorawan_t *mac)
|
||||
{
|
||||
mac->mlme.activation = MLME_ACTIVATION_NONE;
|
||||
mac->mlme.pending_mlme_opts = 0;
|
||||
mac->rx_delay = (LORAMAC_DEFAULT_RX1_DELAY/MS_PER_SEC);
|
||||
mac->mlme.nid = LORAMAC_DEFAULT_NETID;
|
||||
}
|
||||
|
||||
static inline void gnrc_lorawan_mlme_backoff_init(gnrc_lorawan_t *mac)
|
||||
{
|
||||
mac->mlme.backoff_msg.type = MSG_TYPE_MLME_BACKOFF_EXPIRE;
|
||||
mac->mlme.backoff_state = 0;
|
||||
|
||||
gnrc_lorawan_mlme_backoff_expire(mac);
|
||||
}
|
||||
|
||||
static inline void gnrc_lorawan_mcps_reset(gnrc_lorawan_t *mac)
|
||||
{
|
||||
mac->mcps.ack_requested = false;
|
||||
mac->mcps.waiting_for_ack = false;
|
||||
mac->mcps.fcnt = 0;
|
||||
mac->mcps.fcnt_down = 0;
|
||||
}
|
||||
|
||||
static inline void _set_rx2_dr(gnrc_lorawan_t *mac, uint8_t rx2_dr)
|
||||
{
|
||||
mac->dl_settings &= ~GNRC_LORAWAN_DL_RX2_DR_MASK;
|
||||
mac->dl_settings |= (rx2_dr << GNRC_LORAWAN_DL_RX2_DR_POS) &
|
||||
GNRC_LORAWAN_DL_RX2_DR_MASK;
|
||||
}
|
||||
|
||||
static void _sleep_radio(gnrc_lorawan_t *mac)
|
||||
{
|
||||
netopt_state_t state = NETOPT_STATE_SLEEP;
|
||||
|
||||
netdev_set_pass((netdev_t *) mac, NETOPT_STATE, &state, sizeof(state));
|
||||
}
|
||||
|
||||
void gnrc_lorawan_init(gnrc_lorawan_t *mac, uint8_t *nwkskey, uint8_t *appskey)
|
||||
{
|
||||
mac->nwkskey = nwkskey;
|
||||
mac->appskey = appskey;
|
||||
mac->busy = false;
|
||||
gnrc_lorawan_mlme_backoff_init(mac);
|
||||
gnrc_lorawan_reset(mac);
|
||||
}
|
||||
|
||||
void gnrc_lorawan_reset(gnrc_lorawan_t *mac)
|
||||
{
|
||||
uint8_t cr = LORA_CR_4_5;
|
||||
|
||||
netdev_set_pass(&mac->netdev, NETOPT_CODING_RATE, &cr, sizeof(cr));
|
||||
|
||||
uint8_t syncword = LORAMAC_DEFAULT_PUBLIC_NETWORK ? LORA_SYNCWORD_PUBLIC
|
||||
: LORA_SYNCWORD_PRIVATE;
|
||||
netdev_set_pass(&mac->netdev, NETOPT_SYNCWORD, &syncword, sizeof(syncword));
|
||||
|
||||
/* Continuous reception */
|
||||
uint32_t rx_timeout = 0;
|
||||
netdev_set_pass(&mac->netdev, NETOPT_RX_TIMEOUT, &rx_timeout, sizeof(rx_timeout));
|
||||
|
||||
_set_rx2_dr(mac, LORAMAC_DEFAULT_RX2_DR);
|
||||
|
||||
mac->toa = 0;
|
||||
gnrc_lorawan_mcps_reset(mac);
|
||||
gnrc_lorawan_mlme_reset(mac);
|
||||
gnrc_lorawan_channels_init(mac);
|
||||
}
|
||||
|
||||
static void _config_radio(gnrc_lorawan_t *mac, uint32_t channel_freq, uint8_t dr, int rx)
|
||||
{
|
||||
if (channel_freq != 0) {
|
||||
netdev_set_pass(&mac->netdev, NETOPT_CHANNEL_FREQUENCY, &channel_freq, sizeof(channel_freq));
|
||||
}
|
||||
|
||||
netopt_enable_t iq_invert = rx;
|
||||
netdev_set_pass(&mac->netdev, NETOPT_IQ_INVERT, &iq_invert, sizeof(iq_invert));
|
||||
|
||||
gnrc_lorawan_set_dr(mac, dr);
|
||||
|
||||
if (rx) {
|
||||
/* Switch to single listen mode */
|
||||
const netopt_enable_t single = true;
|
||||
netdev_set_pass(&mac->netdev, NETOPT_SINGLE_RECEIVE, &single, sizeof(single));
|
||||
const uint16_t timeout = CONFIG_GNRC_LORAWAN_MIN_SYMBOLS_TIMEOUT;
|
||||
netdev_set_pass(&mac->netdev, NETOPT_RX_SYMBOL_TIMEOUT, &timeout, sizeof(timeout));
|
||||
}
|
||||
}
|
||||
|
||||
static void _configure_rx_window(gnrc_lorawan_t *mac, uint32_t channel_freq, uint8_t dr)
|
||||
{
|
||||
_config_radio(mac, channel_freq, dr, true);
|
||||
}
|
||||
|
||||
void gnrc_lorawan_open_rx_window(gnrc_lorawan_t *mac)
|
||||
{
|
||||
mac->msg.type = MSG_TYPE_TIMEOUT;
|
||||
/* Switch to RX state */
|
||||
if (mac->state == LORAWAN_STATE_RX_1) {
|
||||
xtimer_set_msg(&mac->rx, _DRIFT_FACTOR, &mac->msg, thread_getpid());
|
||||
}
|
||||
uint8_t state = NETOPT_STATE_RX;
|
||||
netdev_set_pass(&mac->netdev, NETOPT_STATE, &state, sizeof(state));
|
||||
}
|
||||
|
||||
void gnrc_lorawan_event_tx_complete(gnrc_lorawan_t *mac)
|
||||
{
|
||||
mac->msg.type = MSG_TYPE_TIMEOUT;
|
||||
mac->state = LORAWAN_STATE_RX_1;
|
||||
|
||||
int rx_1;
|
||||
/* if the MAC is not activated, then this is a Join Request */
|
||||
rx_1 = mac->mlme.activation == MLME_ACTIVATION_NONE ?
|
||||
LORAMAC_DEFAULT_JOIN_DELAY1 : mac->rx_delay;
|
||||
|
||||
xtimer_set_msg(&mac->rx, rx_1 * _DRIFT_FACTOR, &mac->msg, thread_getpid());
|
||||
|
||||
uint8_t dr_offset = (mac->dl_settings & GNRC_LORAWAN_DL_DR_OFFSET_MASK) >>
|
||||
GNRC_LORAWAN_DL_DR_OFFSET_POS;
|
||||
_configure_rx_window(mac, 0, gnrc_lorawan_rx1_get_dr_offset(mac->last_dr, dr_offset));
|
||||
|
||||
_sleep_radio(mac);
|
||||
}
|
||||
|
||||
void gnrc_lorawan_event_timeout(gnrc_lorawan_t *mac)
|
||||
{
|
||||
(void) mac;
|
||||
switch (mac->state) {
|
||||
case LORAWAN_STATE_RX_1:
|
||||
_configure_rx_window(mac, LORAMAC_DEFAULT_RX2_FREQ, mac->dl_settings & GNRC_LORAWAN_DL_RX2_DR_MASK);
|
||||
mac->state = LORAWAN_STATE_RX_2;
|
||||
break;
|
||||
case LORAWAN_STATE_RX_2:
|
||||
gnrc_lorawan_mlme_no_rx(mac);
|
||||
gnrc_lorawan_mcps_event(mac, MCPS_EVENT_NO_RX, 0);
|
||||
mac->state = LORAWAN_STATE_IDLE;
|
||||
gnrc_lorawan_mac_release(mac);
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
_sleep_radio(mac);
|
||||
}
|
||||
|
||||
/* This function uses a precomputed table to calculate time on air without
|
||||
* using floating point arithmetics */
|
||||
static uint32_t lora_time_on_air(size_t payload_size, uint8_t dr, uint8_t cr)
|
||||
{
|
||||
assert(dr <= LORAMAC_DR_6);
|
||||
uint8_t _K[6][4] = { { 0, 1, 5, 5 },
|
||||
{ 0, 1, 4, 5 },
|
||||
{ 1, 5, 5, 5 },
|
||||
{ 1, 4, 5, 4 },
|
||||
{ 1, 3, 4, 4 },
|
||||
{ 1, 2, 4, 3 } };
|
||||
|
||||
uint32_t t_sym = 1 << (15 - dr);
|
||||
uint32_t t_preamble = (t_sym << 3) + (t_sym << 2) + (t_sym >> 2);
|
||||
|
||||
int index = (dr < LORAMAC_DR_6) ? dr : LORAMAC_DR_5;
|
||||
uint8_t n0 = _K[index][0];
|
||||
int nb_symbols;
|
||||
|
||||
uint8_t offset = _K[index][1];
|
||||
if (payload_size < offset) {
|
||||
nb_symbols = 8 + n0 * cr;
|
||||
}
|
||||
else {
|
||||
uint8_t c1 = _K[index][2];
|
||||
uint8_t c2 = _K[index][3];
|
||||
uint8_t pos = (payload_size - offset) % (c1 + c2);
|
||||
uint8_t cycle = (payload_size - offset) / (c1 + c2);
|
||||
nb_symbols = 8 + (n0 + 2 * cycle + 1 + (pos > (c1 - 1))) * cr;
|
||||
}
|
||||
|
||||
uint32_t t_payload = t_sym * nb_symbols;
|
||||
return t_preamble + t_payload;
|
||||
}
|
||||
|
||||
void gnrc_lorawan_send_pkt(gnrc_lorawan_t *mac, gnrc_pktsnip_t *pkt, uint8_t dr)
|
||||
{
|
||||
mac->state = LORAWAN_STATE_TX;
|
||||
|
||||
iolist_t iolist = {
|
||||
.iol_base = pkt->data,
|
||||
.iol_len = pkt->size,
|
||||
.iol_next = (iolist_t *) pkt->next
|
||||
};
|
||||
|
||||
uint32_t chan = gnrc_lorawan_pick_channel(mac);
|
||||
_config_radio(mac, chan, dr, false);
|
||||
|
||||
mac->last_dr = dr;
|
||||
|
||||
uint8_t cr;
|
||||
netdev_get_pass(&mac->netdev, NETOPT_CODING_RATE, &cr, sizeof(cr));
|
||||
|
||||
mac->toa = lora_time_on_air(gnrc_pkt_len(pkt), dr, cr + 4);
|
||||
|
||||
if (netdev_send_pass(&mac->netdev, &iolist) == -ENOTSUP) {
|
||||
DEBUG("gnrc_lorawan: Cannot send: radio is still transmitting");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void gnrc_lorawan_process_pkt(gnrc_lorawan_t *mac, gnrc_pktsnip_t *pkt)
|
||||
{
|
||||
mac->state = LORAWAN_STATE_IDLE;
|
||||
xtimer_remove(&mac->rx);
|
||||
|
||||
uint8_t *p = pkt->data;
|
||||
|
||||
uint8_t mtype = (*p & MTYPE_MASK) >> 5;
|
||||
switch (mtype) {
|
||||
case MTYPE_JOIN_ACCEPT:
|
||||
gnrc_lorawan_mlme_process_join(mac, pkt);
|
||||
break;
|
||||
case MTYPE_CNF_DOWNLINK:
|
||||
case MTYPE_UNCNF_DOWNLINK:
|
||||
gnrc_lorawan_mcps_process_downlink(mac, pkt);
|
||||
break;
|
||||
default:
|
||||
gnrc_pktbuf_release(pkt);
|
||||
break;
|
||||
}
|
||||
|
||||
gnrc_lorawan_mac_release(mac);
|
||||
}
|
||||
|
||||
int gnrc_lorawan_netdev_get(netdev_t *dev, netopt_t opt, void *value, size_t max_len)
|
||||
{
|
||||
int res = 0;
|
||||
gnrc_lorawan_t *mac = (gnrc_lorawan_t *) dev;
|
||||
uint32_t tmp;
|
||||
|
||||
switch (opt) {
|
||||
case NETOPT_ADDRESS:
|
||||
assert(max_len >= sizeof(mac->dev_addr));
|
||||
tmp = byteorder_swapl(mac->dev_addr.u32);
|
||||
memcpy(value, &tmp, sizeof(mac->dev_addr));
|
||||
res = sizeof(mac->dev_addr);
|
||||
break;
|
||||
default:
|
||||
res = netdev_get_pass(dev, opt, value, max_len);
|
||||
break;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int gnrc_lorawan_netdev_set(netdev_t *dev, netopt_t opt, const void *value, size_t len)
|
||||
{
|
||||
gnrc_lorawan_t *mac = (gnrc_lorawan_t *) dev;
|
||||
uint32_t tmp;
|
||||
|
||||
if (mac->busy) {
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
switch (opt) {
|
||||
case NETOPT_ADDRESS:
|
||||
assert(len == sizeof(uint32_t));
|
||||
tmp = byteorder_swapl(*((uint32_t *) value));
|
||||
memcpy(&mac->dev_addr, &tmp, sizeof(uint32_t));
|
||||
break;
|
||||
case NETOPT_LORAWAN_RX2_DR:
|
||||
assert(len == sizeof(uint8_t));
|
||||
_set_rx2_dr(mac, *((uint8_t *) value));
|
||||
break;
|
||||
default:
|
||||
netdev_set_pass(dev, opt, value, len);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const netdev_driver_t gnrc_lorawan_driver = {
|
||||
.init = netdev_init_pass,
|
||||
.send = netdev_send_pass,
|
||||
.recv = netdev_recv_pass,
|
||||
.get = gnrc_lorawan_netdev_get,
|
||||
.set = gnrc_lorawan_netdev_set,
|
||||
.isr = netdev_isr_pass,
|
||||
};
|
||||
|
||||
void gnrc_lorawan_setup(gnrc_lorawan_t *mac, netdev_t *lower)
|
||||
{
|
||||
mac->netdev.driver = &gnrc_lorawan_driver;
|
||||
mac->netdev.lower = lower;
|
||||
lower->context = mac;
|
||||
}
|
||||
|
||||
void gnrc_lorawan_recv(gnrc_lorawan_t *mac)
|
||||
{
|
||||
int bytes_expected = netdev_recv_pass((netdev_t *) mac, NULL, 0, 0);
|
||||
int nread;
|
||||
struct netdev_radio_rx_info rx_info;
|
||||
gnrc_pktsnip_t *pkt = gnrc_pktbuf_add(NULL, NULL, bytes_expected, GNRC_NETTYPE_UNDEF);
|
||||
if (pkt == NULL) {
|
||||
DEBUG("_recv_ieee802154: cannot allocate pktsnip.\n");
|
||||
/* Discard packet on netdev device */
|
||||
netdev_recv_pass((netdev_t *) mac, NULL, bytes_expected, NULL);
|
||||
return;
|
||||
}
|
||||
nread = netdev_recv_pass((netdev_t *) mac, pkt->data, bytes_expected, &rx_info);
|
||||
_sleep_radio(mac);
|
||||
if (nread <= 0) {
|
||||
gnrc_pktbuf_release(pkt);
|
||||
return;
|
||||
}
|
||||
|
||||
gnrc_lorawan_process_pkt(mac, pkt);
|
||||
}
|
151
sys/net/gnrc/link_layer/lorawan/gnrc_lorawan_crypto.c
Normal file
151
sys/net/gnrc/link_layer/lorawan/gnrc_lorawan_crypto.c
Normal file
@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (C) 2019 HAW Hamburg
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @author José Ignacio Alamos <jose.alamos@haw-hamburg.de>
|
||||
* @author Francisco Molina <femolina@uc.cl>
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "hashes/cmac.h"
|
||||
#include "crypto/ciphers.h"
|
||||
|
||||
#include "net/gnrc/lorawan.h"
|
||||
#include "byteorder.h"
|
||||
#include "net/lorawan/hdr.h"
|
||||
|
||||
#define MIC_B0_START (0x49)
|
||||
#define CRYPT_B0_START (0x01)
|
||||
#define DIR_MASK (0x1)
|
||||
#define SBIT_MASK (0xF)
|
||||
|
||||
#define APP_SKEY_B0_START (0x1)
|
||||
#define NWK_SKEY_B0_START (0x2)
|
||||
|
||||
static cmac_context_t CmacContext;
|
||||
static uint8_t digest[LORAMAC_APPKEY_LEN];
|
||||
static cipher_t AesContext;
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint8_t fb;
|
||||
uint32_t u8_pad;
|
||||
uint8_t dir;
|
||||
le_uint32_t dev_addr;
|
||||
le_uint32_t fcnt;
|
||||
uint8_t u32_pad;
|
||||
uint8_t len;
|
||||
} lorawan_block_t;
|
||||
|
||||
void gnrc_lorawan_calculate_join_mic(const iolist_t *io, const uint8_t *key, le_uint32_t *out)
|
||||
{
|
||||
cmac_init(&CmacContext, key, LORAMAC_APPKEY_LEN);
|
||||
while (io != NULL) {
|
||||
cmac_update(&CmacContext, io->iol_base, io->iol_len);
|
||||
io = io->iol_next;
|
||||
}
|
||||
cmac_final(&CmacContext, digest);
|
||||
|
||||
memcpy(out, digest, sizeof(le_uint32_t));
|
||||
}
|
||||
|
||||
void gnrc_lorawan_calculate_mic(const le_uint32_t *dev_addr, uint32_t fcnt,
|
||||
uint8_t dir, iolist_t *pkt, const uint8_t *nwkskey, le_uint32_t *out)
|
||||
{
|
||||
lorawan_block_t block;
|
||||
|
||||
block.fb = MIC_B0_START;
|
||||
block.u8_pad = 0;
|
||||
block.dir = dir & DIR_MASK;
|
||||
|
||||
memcpy(&block.dev_addr, dev_addr, sizeof(le_uint32_t));
|
||||
|
||||
block.fcnt = byteorder_btoll(byteorder_htonl(fcnt));
|
||||
|
||||
block.u32_pad = 0;
|
||||
|
||||
block.len = iolist_size(pkt);
|
||||
|
||||
iolist_t io = { .iol_base = &block, .iol_len = sizeof(block),
|
||||
.iol_next = pkt };
|
||||
gnrc_lorawan_calculate_join_mic(&io, nwkskey, out);
|
||||
}
|
||||
|
||||
void gnrc_lorawan_encrypt_payload(iolist_t *iolist, const le_uint32_t *dev_addr, uint32_t fcnt, uint8_t dir, const uint8_t *appskey)
|
||||
{
|
||||
uint8_t s_block[16];
|
||||
uint8_t a_block[16];
|
||||
|
||||
memset(s_block, 0, sizeof(s_block));
|
||||
memset(a_block, 0, sizeof(a_block));
|
||||
|
||||
lorawan_block_t *block = (lorawan_block_t *) a_block;
|
||||
|
||||
cipher_init(&AesContext, CIPHER_AES_128, appskey, LORAMAC_APPKEY_LEN);
|
||||
|
||||
block->fb = CRYPT_B0_START;
|
||||
|
||||
block->u8_pad = 0;
|
||||
block->dir = dir & DIR_MASK;
|
||||
|
||||
block->dev_addr = *dev_addr;
|
||||
block->fcnt = byteorder_btoll(byteorder_htonl(fcnt));
|
||||
|
||||
block->u32_pad = 0;
|
||||
|
||||
int c = 0;
|
||||
for (iolist_t *io = iolist; io != NULL; io = io->iol_next) {
|
||||
for (unsigned i = 0; i < io->iol_len; i++) {
|
||||
uint8_t *v = io->iol_base;
|
||||
|
||||
if ((c & SBIT_MASK) == 0) {
|
||||
block->len = (c >> 4) + 1;
|
||||
cipher_encrypt(&AesContext, a_block, s_block);
|
||||
}
|
||||
|
||||
v[i] = v[i] ^ s_block[c & SBIT_MASK];
|
||||
c++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gnrc_lorawan_decrypt_join_accept(const uint8_t *key, uint8_t *pkt, int has_clist, uint8_t *out)
|
||||
{
|
||||
cipher_init(&AesContext, CIPHER_AES_128, key, LORAMAC_APPKEY_LEN);
|
||||
cipher_encrypt(&AesContext, pkt, out);
|
||||
|
||||
if (has_clist) {
|
||||
cipher_encrypt(&AesContext, pkt + LORAMAC_APPKEY_LEN, out + LORAMAC_APPKEY_LEN);
|
||||
}
|
||||
}
|
||||
|
||||
void gnrc_lorawan_generate_session_keys(const uint8_t *app_nonce, const uint8_t *dev_nonce, const uint8_t *appkey, uint8_t *nwkskey, uint8_t *appskey)
|
||||
{
|
||||
uint8_t buf[LORAMAC_APPSKEY_LEN];
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
cipher_init(&AesContext, CIPHER_AES_128, appkey, LORAMAC_APPSKEY_LEN);
|
||||
|
||||
/* net_id comes right after app_nonce */
|
||||
memcpy(buf + 1, app_nonce, GNRC_LORAWAN_APP_NONCE_SIZE + GNRC_LORAWAN_NET_ID_SIZE);
|
||||
memcpy(buf + 1 + GNRC_LORAWAN_APP_NONCE_SIZE + GNRC_LORAWAN_NET_ID_SIZE, dev_nonce, GNRC_LORAWAN_DEV_NONCE_SIZE);
|
||||
|
||||
/* Calculate Application Session Key */
|
||||
buf[0] = APP_SKEY_B0_START;
|
||||
cipher_encrypt(&AesContext, buf, nwkskey);
|
||||
|
||||
/* Calculate Network Session Key */
|
||||
buf[0] = NWK_SKEY_B0_START;
|
||||
cipher_encrypt(&AesContext, buf, appskey);
|
||||
}
|
||||
|
||||
/** @} */
|
334
sys/net/gnrc/link_layer/lorawan/gnrc_lorawan_mcps.c
Normal file
334
sys/net/gnrc/link_layer/lorawan/gnrc_lorawan_mcps.c
Normal file
@ -0,0 +1,334 @@
|
||||
/*
|
||||
* Copyright (C) 2019 HAW Hamburg
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @author José Ignacio Alamos <jose.alamos@haw-hamburg.de>
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "net/lora.h"
|
||||
#include "net/gnrc/lorawan.h"
|
||||
#include "net/gnrc/lorawan/region.h"
|
||||
#include "errno.h"
|
||||
#include "net/gnrc/pktbuf.h"
|
||||
|
||||
#include "net/lorawan/hdr.h"
|
||||
|
||||
#include "random.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
#define _16_UPPER_BITMASK 0xFFFF0000
|
||||
#define _16_LOWER_BITMASK 0xFFFF
|
||||
|
||||
int gnrc_lorawan_mic_is_valid(gnrc_pktsnip_t *mic, uint8_t *nwkskey)
|
||||
{
|
||||
le_uint32_t calc_mic;
|
||||
|
||||
assert(mic->size == MIC_SIZE);
|
||||
assert(mic->next->data);
|
||||
lorawan_hdr_t *lw_hdr = (lorawan_hdr_t *) mic->next->data;
|
||||
|
||||
uint32_t fcnt = byteorder_ntohs(byteorder_ltobs(lw_hdr->fcnt));
|
||||
gnrc_lorawan_calculate_mic(&lw_hdr->addr, fcnt, GNRC_LORAWAN_DIR_DOWNLINK, (iolist_t *) mic->next, nwkskey, &calc_mic);
|
||||
return calc_mic.u32 == ((le_uint32_t *) mic->data)->u32;
|
||||
}
|
||||
|
||||
uint32_t gnrc_lorawan_fcnt_stol(uint32_t fcnt_down, uint16_t s_fcnt)
|
||||
{
|
||||
uint32_t u32_fcnt = (fcnt_down & _16_UPPER_BITMASK) | s_fcnt;
|
||||
|
||||
if (fcnt_down + LORAMAC_DEFAULT_MAX_FCNT_GAP >= _16_LOWER_BITMASK
|
||||
&& s_fcnt < (fcnt_down & _16_LOWER_BITMASK)) {
|
||||
u32_fcnt += _16_LOWER_BITMASK;
|
||||
}
|
||||
return u32_fcnt;
|
||||
}
|
||||
|
||||
void gnrc_lorawan_mcps_process_downlink(gnrc_lorawan_t *mac, gnrc_pktsnip_t *pkt)
|
||||
{
|
||||
gnrc_pktsnip_t *hdr, *data, *fopts = NULL, *fport = NULL;
|
||||
int release = true;
|
||||
int error = true;
|
||||
|
||||
/* mark MIC */
|
||||
if (!(data = gnrc_pktbuf_mark(pkt, (pkt->size - MIC_SIZE > 0) ? pkt->size - MIC_SIZE : 0, GNRC_NETTYPE_UNDEF))) {
|
||||
DEBUG("gnrc_lorawan: failed to mark MIC\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* NOTE: MIC is in pkt */
|
||||
if (!gnrc_lorawan_mic_is_valid(pkt, mac->nwkskey)) {
|
||||
DEBUG("gnrc_lorawan: invalid MIC\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* remove snip */
|
||||
pkt = gnrc_pktbuf_remove_snip(pkt, pkt);
|
||||
|
||||
if (!(hdr = gnrc_pktbuf_mark(pkt, sizeof(lorawan_hdr_t), GNRC_NETTYPE_UNDEF))) {
|
||||
DEBUG("gnrc_lorawan: failed to allocate hdr\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
int _fopts_length = lorawan_hdr_get_frame_opts_len((lorawan_hdr_t *) hdr->data);
|
||||
if (_fopts_length && !(fopts = gnrc_pktbuf_mark(pkt, _fopts_length, GNRC_NETTYPE_UNDEF))) {
|
||||
DEBUG("gnrc_lorawan: failed to allocate fopts\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (pkt->size && !(fport = gnrc_pktbuf_mark(pkt, 1, GNRC_NETTYPE_UNDEF))) {
|
||||
DEBUG("gnrc_lorawan: failed to allocate fport\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
assert(pkt != NULL && fport->data);
|
||||
|
||||
int fopts_in_payload = *((uint8_t *) fport->data) == 0;
|
||||
if (fopts && fopts_in_payload) {
|
||||
DEBUG("gnrc_lorawan: packet with fopts and port == 0. Drop\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
lorawan_hdr_t *lw_hdr = hdr->data;
|
||||
|
||||
if (lw_hdr->addr.u32 != mac->dev_addr.u32) {
|
||||
DEBUG("gnrc_lorawan: received packet with wrong dev addr. Drop\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
uint32_t fcnt = gnrc_lorawan_fcnt_stol(mac->mcps.fcnt_down, lw_hdr->fcnt.u16);
|
||||
if (mac->mcps.fcnt_down > fcnt || mac->mcps.fcnt_down +
|
||||
LORAMAC_DEFAULT_MAX_FCNT_GAP < fcnt) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
mac->mcps.fcnt_down = fcnt;
|
||||
error = false;
|
||||
|
||||
int ack_req = lorawan_hdr_get_mtype(lw_hdr) == MTYPE_CNF_DOWNLINK;
|
||||
if (ack_req) {
|
||||
mac->mcps.ack_requested = true;
|
||||
}
|
||||
|
||||
iolist_t payload = { .iol_base = pkt->data, .iol_len = pkt->size };
|
||||
if (pkt->data) {
|
||||
gnrc_lorawan_encrypt_payload(&payload, &lw_hdr->addr, byteorder_ntohs(byteorder_ltobs(lw_hdr->fcnt)), GNRC_LORAWAN_DIR_DOWNLINK, fopts_in_payload ? mac->nwkskey : mac->appskey);
|
||||
}
|
||||
|
||||
/* if there are fopts, it's either an empty packet or application payload */
|
||||
if (fopts) {
|
||||
gnrc_lorawan_process_fopts(mac, fopts->data, fopts->size);
|
||||
}
|
||||
else if (fopts_in_payload) {
|
||||
gnrc_lorawan_process_fopts(mac, pkt->data, pkt->size);
|
||||
}
|
||||
|
||||
gnrc_lorawan_mcps_event(mac, MCPS_EVENT_RX, lorawan_hdr_get_ack(lw_hdr));
|
||||
if (pkt->data && *((uint8_t *) fport->data) != 0) {
|
||||
pkt->type = GNRC_NETTYPE_LORAWAN;
|
||||
release = false;
|
||||
|
||||
mcps_indication_t *mcps_indication = gnrc_lorawan_mcps_allocate(mac);
|
||||
mcps_indication->type = ack_req;
|
||||
mcps_indication->data.pkt = pkt;
|
||||
mcps_indication->data.port = *((uint8_t *) fport->data);
|
||||
mac->netdev.event_callback((netdev_t *) mac, NETDEV_EVENT_MCPS_INDICATION);
|
||||
}
|
||||
|
||||
if (lorawan_hdr_get_frame_pending(lw_hdr)) {
|
||||
mlme_indication_t *mlme_indication = gnrc_lorawan_mlme_allocate(mac);
|
||||
mlme_indication->type = MLME_SCHEDULE_UPLINK;
|
||||
mac->netdev.event_callback((netdev_t *) mac, NETDEV_EVENT_MLME_INDICATION);
|
||||
}
|
||||
|
||||
out:
|
||||
if (error) {
|
||||
gnrc_lorawan_mcps_event(mac, MCPS_EVENT_NO_RX, 0);
|
||||
}
|
||||
|
||||
if (release) {
|
||||
DEBUG("gnrc_lorawan: release packet\n");
|
||||
gnrc_pktbuf_release(pkt);
|
||||
}
|
||||
}
|
||||
|
||||
size_t gnrc_lorawan_build_hdr(uint8_t mtype, le_uint32_t *dev_addr, uint32_t fcnt, uint8_t ack, uint8_t fopts_length, lorawan_buffer_t *buf)
|
||||
{
|
||||
assert(fopts_length < 16);
|
||||
lorawan_hdr_t *lw_hdr = (lorawan_hdr_t *) buf->data;
|
||||
|
||||
lw_hdr->mt_maj = 0;
|
||||
lorawan_hdr_set_mtype(lw_hdr, mtype);
|
||||
lorawan_hdr_set_maj(lw_hdr, MAJOR_LRWAN_R1);
|
||||
|
||||
lw_hdr->addr = *dev_addr;
|
||||
lw_hdr->fctrl = 0;
|
||||
|
||||
lorawan_hdr_set_ack(lw_hdr, ack);
|
||||
lorawan_hdr_set_frame_opts_len(lw_hdr, fopts_length);
|
||||
|
||||
lw_hdr->fcnt = byteorder_btols(byteorder_htons(fcnt));
|
||||
|
||||
buf->index += sizeof(lorawan_hdr_t);
|
||||
|
||||
return sizeof(lorawan_hdr_t);
|
||||
}
|
||||
|
||||
gnrc_pktsnip_t *gnrc_lorawan_build_uplink(gnrc_lorawan_t *mac, gnrc_pktsnip_t *payload, int confirmed_data, uint8_t port)
|
||||
{
|
||||
/* Encrypt payload (it's block encryption so we can use the same buffer!) */
|
||||
gnrc_lorawan_encrypt_payload((iolist_t *) payload, &mac->dev_addr, mac->mcps.fcnt, GNRC_LORAWAN_DIR_UPLINK, port ? mac->appskey : mac->nwkskey);
|
||||
|
||||
/* We try to allocate the whole header with fopts at once */
|
||||
uint8_t fopts_length = gnrc_lorawan_build_options(mac, NULL);
|
||||
|
||||
gnrc_pktsnip_t *mac_hdr = gnrc_pktbuf_add(payload, NULL, sizeof(lorawan_hdr_t) + fopts_length + 1, GNRC_NETTYPE_UNDEF);
|
||||
|
||||
if (!mac_hdr) {
|
||||
gnrc_pktbuf_release_error(payload, -ENOBUFS);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gnrc_pktsnip_t *mic = gnrc_pktbuf_add(NULL, NULL, MIC_SIZE, GNRC_NETTYPE_UNDEF);
|
||||
if (!mic) {
|
||||
gnrc_pktbuf_release_error(mac_hdr, -ENOBUFS);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lorawan_buffer_t buf = {
|
||||
.data = (uint8_t *) mac_hdr->data,
|
||||
.size = mac_hdr->size,
|
||||
.index = 0
|
||||
};
|
||||
|
||||
gnrc_lorawan_build_hdr(confirmed_data ? MTYPE_CNF_UPLINK : MTYPE_UNCNF_UPLINK,
|
||||
&mac->dev_addr, mac->mcps.fcnt, mac->mcps.ack_requested, fopts_length, &buf);
|
||||
|
||||
gnrc_lorawan_build_options(mac, &buf);
|
||||
|
||||
assert(buf.index == mac_hdr->size - 1);
|
||||
|
||||
buf.data[buf.index++] = port;
|
||||
|
||||
gnrc_lorawan_calculate_mic(&mac->dev_addr, mac->mcps.fcnt, GNRC_LORAWAN_DIR_UPLINK,
|
||||
(iolist_t *) mac_hdr, mac->nwkskey, mic->data);
|
||||
|
||||
LL_APPEND(payload, mic);
|
||||
|
||||
return mac_hdr;
|
||||
}
|
||||
|
||||
static void _end_of_tx(gnrc_lorawan_t *mac, int type, int status)
|
||||
{
|
||||
mac->mcps.waiting_for_ack = false;
|
||||
|
||||
mcps_confirm_t *mcps_confirm = gnrc_lorawan_mcps_allocate(mac);
|
||||
|
||||
mcps_confirm->type = type;
|
||||
mcps_confirm->status = status;
|
||||
mac->netdev.event_callback((netdev_t *) mac, NETDEV_EVENT_MCPS_CONFIRM);
|
||||
|
||||
mac->mcps.fcnt += 1;
|
||||
}
|
||||
|
||||
void gnrc_lorawan_mcps_event(gnrc_lorawan_t *mac, int event, int data)
|
||||
{
|
||||
if (mac->mlme.activation == MLME_ACTIVATION_NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event == MCPS_EVENT_ACK_TIMEOUT) {
|
||||
gnrc_lorawan_send_pkt(mac, mac->mcps.outgoing_pkt, mac->last_dr);
|
||||
}
|
||||
else {
|
||||
int state = mac->mcps.waiting_for_ack ? MCPS_CONFIRMED : MCPS_UNCONFIRMED;
|
||||
if (state == MCPS_CONFIRMED && ((event == MCPS_EVENT_RX && !data) ||
|
||||
event == MCPS_EVENT_NO_RX)) {
|
||||
if (mac->mcps.nb_trials-- == 0) {
|
||||
_end_of_tx(mac, MCPS_CONFIRMED, -ETIMEDOUT);
|
||||
}
|
||||
}
|
||||
else {
|
||||
_end_of_tx(mac, state, GNRC_LORAWAN_REQ_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
mac->msg.type = MSG_TYPE_MCPS_ACK_TIMEOUT;
|
||||
if (mac->mcps.outgoing_pkt) {
|
||||
xtimer_set_msg(&mac->rx, 1000000 + random_uint32_range(0, 2000000), &mac->msg, thread_getpid());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gnrc_lorawan_mcps_request(gnrc_lorawan_t *mac, const mcps_request_t *mcps_request, mcps_confirm_t *mcps_confirm)
|
||||
{
|
||||
int release = true;
|
||||
gnrc_pktsnip_t *pkt = mcps_request->data.pkt;
|
||||
|
||||
if (mac->mlme.activation == MLME_ACTIVATION_NONE) {
|
||||
DEBUG("gnrc_lorawan_mcps: LoRaWAN not activated\n");
|
||||
mcps_confirm->status = -ENOTCONN;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!gnrc_lorawan_mac_acquire(mac)) {
|
||||
mcps_confirm->status = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (mcps_request->data.port < LORAMAC_PORT_MIN ||
|
||||
mcps_request->data.port > LORAMAC_PORT_MAX) {
|
||||
mcps_confirm->status = -EBADMSG;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!gnrc_lorawan_validate_dr(mcps_request->data.dr)) {
|
||||
mcps_confirm->status = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
int waiting_for_ack = mcps_request->type == MCPS_CONFIRMED;
|
||||
if (!(pkt = gnrc_lorawan_build_uplink(mac, pkt, waiting_for_ack, mcps_request->data.port))) {
|
||||
/* This function releases the pkt if fails */
|
||||
release = false;
|
||||
mcps_confirm->status = -ENOBUFS;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((gnrc_pkt_len(pkt) - MIC_SIZE - 1) > gnrc_lorawan_region_mac_payload_max(mcps_request->data.dr)) {
|
||||
mcps_confirm->status = -EMSGSIZE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
release = false;
|
||||
mac->mcps.waiting_for_ack = waiting_for_ack;
|
||||
mac->mcps.ack_requested = false;
|
||||
|
||||
mac->mcps.nb_trials = LORAMAC_DEFAULT_RETX;
|
||||
|
||||
assert(mac->mcps.outgoing_pkt == NULL);
|
||||
mac->mcps.outgoing_pkt = pkt;
|
||||
|
||||
gnrc_lorawan_send_pkt(mac, pkt, mcps_request->data.dr);
|
||||
mcps_confirm->status = GNRC_LORAWAN_REQ_STATUS_DEFERRED;
|
||||
out:
|
||||
|
||||
if (mcps_confirm->status != GNRC_LORAWAN_REQ_STATUS_DEFERRED) {
|
||||
gnrc_lorawan_mac_release(mac);
|
||||
}
|
||||
|
||||
if (release) {
|
||||
gnrc_pktbuf_release_error(pkt, mcps_confirm->status);
|
||||
}
|
||||
}
|
||||
|
||||
/** @} */
|
328
sys/net/gnrc/link_layer/lorawan/gnrc_lorawan_mlme.c
Normal file
328
sys/net/gnrc/link_layer/lorawan/gnrc_lorawan_mlme.c
Normal file
@ -0,0 +1,328 @@
|
||||
/*
|
||||
* Copyright (C) 2019 HAW Hamburg
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @author José Ignacio Alamos <jose.alamos@haw-hamburg.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "net/lora.h"
|
||||
#include "net/gnrc/lorawan.h"
|
||||
#include "net/gnrc/lorawan/region.h"
|
||||
#include "errno.h"
|
||||
#include "net/gnrc/pktbuf.h"
|
||||
#include "random.h"
|
||||
|
||||
#include "net/lorawan/hdr.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
static gnrc_pktsnip_t *_build_join_req_pkt(uint8_t *appeui, uint8_t *deveui, uint8_t *appkey, uint8_t *dev_nonce)
|
||||
{
|
||||
gnrc_pktsnip_t *pkt = gnrc_pktbuf_add(NULL, NULL, sizeof(lorawan_join_request_t), GNRC_NETTYPE_UNDEF);
|
||||
|
||||
if (pkt) {
|
||||
lorawan_join_request_t *hdr = (lorawan_join_request_t *) pkt->data;
|
||||
|
||||
hdr->mt_maj = 0;
|
||||
lorawan_hdr_set_mtype((lorawan_hdr_t *) hdr, MTYPE_JOIN_REQUEST);
|
||||
lorawan_hdr_set_maj((lorawan_hdr_t *) hdr, MAJOR_LRWAN_R1);
|
||||
|
||||
le_uint64_t l_appeui = *((le_uint64_t *) appeui);
|
||||
le_uint64_t l_deveui = *((le_uint64_t *) deveui);
|
||||
|
||||
hdr->app_eui = l_appeui;
|
||||
hdr->dev_eui = l_deveui;
|
||||
|
||||
le_uint16_t l_dev_nonce = *((le_uint16_t *) dev_nonce);
|
||||
hdr->dev_nonce = l_dev_nonce;
|
||||
|
||||
iolist_t io = { .iol_base = pkt->data, .iol_len = JOIN_REQUEST_SIZE - MIC_SIZE,
|
||||
.iol_next = NULL };
|
||||
gnrc_lorawan_calculate_join_mic(&io, appkey, &hdr->mic);
|
||||
}
|
||||
|
||||
return pkt;
|
||||
}
|
||||
|
||||
static int gnrc_lorawan_send_join_request(gnrc_lorawan_t *mac, uint8_t *deveui,
|
||||
uint8_t *appeui, uint8_t *appkey, uint8_t dr)
|
||||
{
|
||||
netdev_t *dev = mac->netdev.lower;
|
||||
|
||||
/* Dev Nonce */
|
||||
uint32_t random_number;
|
||||
dev->driver->get(dev, NETOPT_RANDOM, &random_number, sizeof(random_number));
|
||||
|
||||
mac->mlme.dev_nonce[0] = random_number & 0xFF;
|
||||
mac->mlme.dev_nonce[1] = (random_number >> 8) & 0xFF;
|
||||
|
||||
/* build join request */
|
||||
gnrc_pktsnip_t *pkt = _build_join_req_pkt(appeui, deveui, appkey, mac->mlme.dev_nonce);
|
||||
if (!pkt) {
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
/* We need a random delay for join request. Otherwise there might be
|
||||
* network congestion if a group of nodes start at the same time */
|
||||
xtimer_usleep(random_uint32() & GNRC_LORAWAN_JOIN_DELAY_U32_MASK);
|
||||
gnrc_lorawan_send_pkt(mac, pkt, dr);
|
||||
|
||||
mac->mlme.backoff_budget -= mac->toa;
|
||||
gnrc_pktbuf_release(pkt);
|
||||
|
||||
return GNRC_LORAWAN_REQ_STATUS_DEFERRED;
|
||||
}
|
||||
|
||||
void gnrc_lorawan_mlme_process_join(gnrc_lorawan_t *mac, gnrc_pktsnip_t *pkt)
|
||||
{
|
||||
int status;
|
||||
|
||||
if (mac->mlme.activation != MLME_ACTIVATION_NONE) {
|
||||
status = -EBADMSG;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (pkt->size != GNRC_LORAWAN_JOIN_ACCEPT_MAX_SIZE - CFLIST_SIZE &&
|
||||
pkt->size != GNRC_LORAWAN_JOIN_ACCEPT_MAX_SIZE) {
|
||||
status = -EBADMSG;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Substract 1 from join accept max size, since the MHDR was already read */
|
||||
uint8_t out[GNRC_LORAWAN_JOIN_ACCEPT_MAX_SIZE - 1];
|
||||
uint8_t has_cflist = (pkt->size - 1) >= CFLIST_SIZE;
|
||||
gnrc_lorawan_decrypt_join_accept(mac->appskey, ((uint8_t *) pkt->data) + 1,
|
||||
has_cflist, out);
|
||||
memcpy(((uint8_t *) pkt->data) + 1, out, pkt->size - 1);
|
||||
|
||||
iolist_t io = { .iol_base = pkt->data, .iol_len = pkt->size - MIC_SIZE,
|
||||
.iol_next = NULL };
|
||||
le_uint32_t mic;
|
||||
le_uint32_t *expected_mic = (le_uint32_t *) (((uint8_t *) pkt->data) + pkt->size - MIC_SIZE);
|
||||
gnrc_lorawan_calculate_join_mic(&io, mac->appskey, &mic);
|
||||
if (mic.u32 != expected_mic->u32) {
|
||||
DEBUG("gnrc_lorawan_mlme: wrong MIC.\n");
|
||||
status = -EBADMSG;
|
||||
goto out;
|
||||
}
|
||||
|
||||
lorawan_join_accept_t *ja_hdr = (lorawan_join_accept_t *) pkt->data;
|
||||
gnrc_lorawan_generate_session_keys(ja_hdr->app_nonce, mac->mlme.dev_nonce, mac->appskey, mac->nwkskey, mac->appskey);
|
||||
|
||||
le_uint32_t le_nid;
|
||||
le_nid.u32 = 0;
|
||||
memcpy(&le_nid, ja_hdr->net_id, 3);
|
||||
mac->mlme.nid = byteorder_ntohl(byteorder_ltobl(le_nid));
|
||||
/* Copy devaddr */
|
||||
memcpy(&mac->dev_addr, ja_hdr->dev_addr, sizeof(mac->dev_addr));
|
||||
|
||||
mac->dl_settings = ja_hdr->dl_settings;
|
||||
|
||||
/* delay 0 maps to 1 second */
|
||||
mac->rx_delay = ja_hdr->rx_delay ? ja_hdr->rx_delay : 1;
|
||||
|
||||
gnrc_lorawan_process_cflist(mac, out + sizeof(lorawan_join_accept_t) - 1);
|
||||
mac->mlme.activation = MLME_ACTIVATION_OTAA;
|
||||
status = GNRC_LORAWAN_REQ_STATUS_SUCCESS;
|
||||
|
||||
out:
|
||||
gnrc_pktbuf_release(pkt);
|
||||
mlme_confirm_t *mlme_confirm = gnrc_lorawan_mlme_allocate(mac);
|
||||
mlme_confirm->type = MLME_JOIN;
|
||||
mlme_confirm->status = status;
|
||||
|
||||
mac->netdev.event_callback((netdev_t *) mac, NETDEV_EVENT_MLME_CONFIRM);
|
||||
}
|
||||
|
||||
void gnrc_lorawan_mlme_backoff_expire(gnrc_lorawan_t *mac)
|
||||
{
|
||||
uint8_t counter = mac->mlme.backoff_state & 0x1F;
|
||||
uint8_t state = mac->mlme.backoff_state >> 5;
|
||||
|
||||
if (counter == 0) {
|
||||
switch (state) {
|
||||
case GNRC_LORAWAN_BACKOFF_STATE_1:
|
||||
counter = GNRC_LORAWAN_BACKOFF_TIME_1;
|
||||
state = GNRC_LORAWAN_BACKOFF_STATE_2;
|
||||
mac->mlme.backoff_budget = GNRC_LORAWAN_BACKOFF_BUDGET_1;
|
||||
break;
|
||||
case GNRC_LORAWAN_BACKOFF_STATE_2:
|
||||
counter = GNRC_LORAWAN_BACKOFF_TIME_2;
|
||||
state = GNRC_LORAWAN_BACKOFF_STATE_3;
|
||||
mac->mlme.backoff_budget = GNRC_LORAWAN_BACKOFF_BUDGET_2;
|
||||
break;
|
||||
case GNRC_LORAWAN_BACKOFF_STATE_3:
|
||||
default:
|
||||
counter = GNRC_LORAWAN_BACKOFF_TIME_3;
|
||||
mac->mlme.backoff_budget = GNRC_LORAWAN_BACKOFF_BUDGET_3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
counter--;
|
||||
mac->mlme.backoff_state = state << 5 | (counter & 0x1F);
|
||||
xtimer_set_msg(&mac->mlme.backoff_timer,
|
||||
GNRC_LORAWAN_BACKOFF_WINDOW_TICK,
|
||||
&mac->mlme.backoff_msg, thread_getpid());
|
||||
}
|
||||
|
||||
static void _mlme_set(gnrc_lorawan_t *mac, const mlme_request_t *mlme_request,
|
||||
mlme_confirm_t *mlme_confirm)
|
||||
{
|
||||
mlme_confirm->status = -EINVAL;
|
||||
switch(mlme_request->mib.type) {
|
||||
case MIB_ACTIVATION_METHOD:
|
||||
if(mlme_request->mib.activation != MLME_ACTIVATION_OTAA) {
|
||||
mlme_confirm->status = GNRC_LORAWAN_REQ_STATUS_SUCCESS;
|
||||
mac->mlme.activation = mlme_request->mib.activation;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void _mlme_get(gnrc_lorawan_t *mac, const mlme_request_t *mlme_request,
|
||||
mlme_confirm_t *mlme_confirm)
|
||||
{
|
||||
switch(mlme_request->mib.type) {
|
||||
case MIB_ACTIVATION_METHOD:
|
||||
mlme_confirm->status = GNRC_LORAWAN_REQ_STATUS_SUCCESS;
|
||||
mlme_confirm->mib.activation = mac->mlme.activation;
|
||||
break;
|
||||
default:
|
||||
mlme_confirm->status = -EINVAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void gnrc_lorawan_mlme_request(gnrc_lorawan_t *mac, const mlme_request_t *mlme_request,
|
||||
mlme_confirm_t *mlme_confirm)
|
||||
{
|
||||
switch (mlme_request->type) {
|
||||
case MLME_JOIN:
|
||||
if(mac->mlme.activation != MLME_ACTIVATION_NONE) {
|
||||
mlme_confirm->status = -EINVAL;
|
||||
break;
|
||||
}
|
||||
if (!gnrc_lorawan_mac_acquire(mac)) {
|
||||
mlme_confirm->status = -EBUSY;
|
||||
break;
|
||||
}
|
||||
|
||||
if (mac->mlme.backoff_budget < 0) {
|
||||
mlme_confirm->status = -EDQUOT;
|
||||
break;
|
||||
}
|
||||
memcpy(mac->appskey, mlme_request->join.appkey, LORAMAC_APPKEY_LEN);
|
||||
mlme_confirm->status = gnrc_lorawan_send_join_request(mac, mlme_request->join.deveui,
|
||||
mlme_request->join.appeui, mlme_request->join.appkey, mlme_request->join.dr);
|
||||
break;
|
||||
case MLME_LINK_CHECK:
|
||||
mac->mlme.pending_mlme_opts |= GNRC_LORAWAN_MLME_OPTS_LINK_CHECK_REQ;
|
||||
mlme_confirm->status = GNRC_LORAWAN_REQ_STATUS_DEFERRED;
|
||||
break;
|
||||
case MLME_SET:
|
||||
_mlme_set(mac, mlme_request, mlme_confirm);
|
||||
break;
|
||||
case MLME_GET:
|
||||
_mlme_get(mac, mlme_request, mlme_confirm);
|
||||
break;
|
||||
case MLME_RESET:
|
||||
gnrc_lorawan_reset(mac);
|
||||
mlme_confirm->status = GNRC_LORAWAN_REQ_STATUS_SUCCESS;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int _fopts_mlme_link_check_req(lorawan_buffer_t *buf)
|
||||
{
|
||||
if (buf) {
|
||||
assert(buf->index + GNRC_LORAWAN_CID_SIZE <= buf->size);
|
||||
buf->data[buf->index++] = GNRC_LORAWAN_CID_LINK_CHECK_ANS;
|
||||
}
|
||||
|
||||
return GNRC_LORAWAN_CID_SIZE;
|
||||
}
|
||||
|
||||
static void _mlme_link_check_ans(gnrc_lorawan_t *mac, uint8_t *p)
|
||||
{
|
||||
mlme_confirm_t *mlme_confirm = gnrc_lorawan_mlme_allocate(mac);
|
||||
mlme_confirm->link_req.margin = p[1];
|
||||
mlme_confirm->link_req.num_gateways = p[2];
|
||||
|
||||
mlme_confirm->type = MLME_LINK_CHECK;
|
||||
mlme_confirm->status = GNRC_LORAWAN_REQ_STATUS_SUCCESS;
|
||||
mac->netdev.event_callback(&mac->netdev, NETDEV_EVENT_MLME_CONFIRM);
|
||||
|
||||
mac->mlme.pending_mlme_opts &= ~GNRC_LORAWAN_MLME_OPTS_LINK_CHECK_REQ;
|
||||
}
|
||||
|
||||
void gnrc_lorawan_process_fopts(gnrc_lorawan_t *mac, uint8_t *fopts, size_t size)
|
||||
{
|
||||
if (!fopts || !size) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t ret = 0;
|
||||
void (*cb)(gnrc_lorawan_t*, uint8_t *p) = NULL;
|
||||
|
||||
for(uint8_t pos = 0; pos < size; pos += ret) {
|
||||
switch (fopts[pos]) {
|
||||
case GNRC_LORAWAN_CID_LINK_CHECK_ANS:
|
||||
ret += GNRC_LORAWAN_FOPT_LINK_CHECK_ANS_SIZE;
|
||||
cb = _mlme_link_check_ans;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if(pos + ret > size) {
|
||||
return;
|
||||
}
|
||||
|
||||
cb(mac, &fopts[pos]);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t gnrc_lorawan_build_options(gnrc_lorawan_t *mac, lorawan_buffer_t *buf)
|
||||
{
|
||||
size_t size = 0;
|
||||
|
||||
if(mac->mlme.pending_mlme_opts & GNRC_LORAWAN_MLME_OPTS_LINK_CHECK_REQ) {
|
||||
size += _fopts_mlme_link_check_req(buf);
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void gnrc_lorawan_mlme_no_rx(gnrc_lorawan_t *mac)
|
||||
{
|
||||
mlme_confirm_t *mlme_confirm = gnrc_lorawan_mlme_allocate(mac);
|
||||
|
||||
mlme_confirm->status = -ETIMEDOUT;
|
||||
|
||||
if (mac->mlme.activation == MLME_ACTIVATION_NONE) {
|
||||
mlme_confirm->type = MLME_JOIN;
|
||||
mac->netdev.event_callback(&mac->netdev, NETDEV_EVENT_MLME_CONFIRM);
|
||||
}
|
||||
else if (mac->mlme.pending_mlme_opts & GNRC_LORAWAN_MLME_OPTS_LINK_CHECK_REQ) {
|
||||
mlme_confirm->type = MLME_LINK_CHECK;
|
||||
mac->netdev.event_callback(&mac->netdev, NETDEV_EVENT_MLME_CONFIRM);
|
||||
mac->mlme.pending_mlme_opts &= ~GNRC_LORAWAN_MLME_OPTS_LINK_CHECK_REQ;
|
||||
}
|
||||
}
|
130
sys/net/gnrc/link_layer/lorawan/gnrc_lorawan_region.c
Normal file
130
sys/net/gnrc/link_layer/lorawan/gnrc_lorawan_region.c
Normal file
@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright (C) 2019 HAW Hamburg
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @author José Ignacio Alamos <jose.alamos@haw-hamburg.de>
|
||||
*/
|
||||
#include "net/gnrc/lorawan/region.h"
|
||||
|
||||
#define GNRC_LORAWAN_DATARATES_NUMOF (6U)
|
||||
|
||||
static uint8_t dr_sf[GNRC_LORAWAN_DATARATES_NUMOF] =
|
||||
{ LORA_SF12, LORA_SF11, LORA_SF10, LORA_SF9, LORA_SF8, LORA_SF7 };
|
||||
static uint8_t dr_bw[GNRC_LORAWAN_DATARATES_NUMOF] =
|
||||
{ LORA_BW_125_KHZ, LORA_BW_125_KHZ, LORA_BW_125_KHZ, LORA_BW_125_KHZ,
|
||||
LORA_BW_125_KHZ, LORA_BW_125_KHZ };
|
||||
|
||||
int gnrc_lorawan_set_dr(gnrc_lorawan_t *mac, uint8_t datarate)
|
||||
{
|
||||
netdev_t *dev = mac->netdev.lower;
|
||||
|
||||
if (!gnrc_lorawan_validate_dr(datarate)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
uint8_t bw = dr_bw[datarate];
|
||||
uint8_t sf = dr_sf[datarate];
|
||||
|
||||
dev->driver->set(dev, NETOPT_BANDWIDTH, &bw, sizeof(bw));
|
||||
dev->driver->set(dev, NETOPT_SPREADING_FACTOR, &sf, sizeof(sf));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t gnrc_lorawan_rx1_get_dr_offset(uint8_t dr_up, uint8_t dr_offset)
|
||||
{
|
||||
return (dr_up > dr_offset) ? (dr_up - dr_offset) : 0;
|
||||
}
|
||||
|
||||
static size_t _get_num_used_channels(gnrc_lorawan_t *mac)
|
||||
{
|
||||
size_t count = 0;
|
||||
|
||||
for (unsigned i = 0; i < GNRC_LORAWAN_MAX_CHANNELS; i++) {
|
||||
if (mac->channel[i]) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static uint32_t _get_nth_channel(gnrc_lorawan_t *mac, size_t n)
|
||||
{
|
||||
int i = 0;
|
||||
uint32_t channel = 0;
|
||||
|
||||
while (n) {
|
||||
if (mac->channel[i]) {
|
||||
n--;
|
||||
channel = mac->channel[i];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return channel;
|
||||
}
|
||||
|
||||
void gnrc_lorawan_channels_init(gnrc_lorawan_t *mac)
|
||||
{
|
||||
for (unsigned i = 0; i < GNRC_LORAWAN_DEFAULT_CHANNELS_NUMOF; i++) {
|
||||
mac->channel[i] = gnrc_lorawan_default_channels[i];
|
||||
}
|
||||
|
||||
for (unsigned i = GNRC_LORAWAN_DEFAULT_CHANNELS_NUMOF;
|
||||
i < GNRC_LORAWAN_MAX_CHANNELS; i++) {
|
||||
mac->channel[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t gnrc_lorawan_pick_channel(gnrc_lorawan_t *mac)
|
||||
{
|
||||
netdev_t *netdev = mac->netdev.lower;
|
||||
uint32_t random_number;
|
||||
|
||||
netdev->driver->get(netdev, NETOPT_RANDOM, &random_number,
|
||||
sizeof(random_number));
|
||||
|
||||
return _get_nth_channel(mac,
|
||||
1 + (random_number % _get_num_used_channels(mac)));
|
||||
}
|
||||
|
||||
void gnrc_lorawan_process_cflist(gnrc_lorawan_t *mac, uint8_t *cflist)
|
||||
{
|
||||
/* TODO: Check CFListType to 0 */
|
||||
for (unsigned i = GNRC_LORAWAN_DEFAULT_CHANNELS_NUMOF; i < 8; i++) {
|
||||
le_uint32_t cl;
|
||||
cl.u32 = 0;
|
||||
memcpy(&cl, cflist, GNRC_LORAWAN_CFLIST_ENTRY_SIZE);
|
||||
mac->channel[i] = byteorder_ntohl(byteorder_ltobl(cl)) * 100;
|
||||
cflist += GNRC_LORAWAN_CFLIST_ENTRY_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t gnrc_lorawan_region_mac_payload_max(uint8_t datarate)
|
||||
{
|
||||
if (datarate < 3) {
|
||||
return GNRC_LORAWAN_MAX_PAYLOAD_1;
|
||||
}
|
||||
else if (datarate == 3) {
|
||||
return GNRC_LORAWAN_MAX_PAYLOAD_2;
|
||||
}
|
||||
else {
|
||||
return GNRC_LORAWAN_MAX_PAYLOAD_3;
|
||||
}
|
||||
}
|
||||
|
||||
bool gnrc_lorawan_validate_dr(uint8_t dr)
|
||||
{
|
||||
if (dr < GNRC_LORAWAN_DATARATES_NUMOF) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @} */
|
481
sys/net/gnrc/link_layer/lorawan/include/gnrc_lorawan_internal.h
Normal file
481
sys/net/gnrc/link_layer/lorawan/include/gnrc_lorawan_internal.h
Normal file
@ -0,0 +1,481 @@
|
||||
/*
|
||||
* Copyright (C) 2019 HAW Hamburg
|
||||
*
|
||||
* 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_lorawan
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief GNRC LoRaWAN internal header
|
||||
*
|
||||
* @author Jose Ignacio Alamos <jose.alamos@haw-hamburg.de>
|
||||
*/
|
||||
#ifndef GNRC_LORAWAN_INTERNAL_H
|
||||
#define GNRC_LORAWAN_INTERNAL_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "iolist.h"
|
||||
#include "net/lora.h"
|
||||
#include "net/lorawan/hdr.h"
|
||||
#include "net/gnrc/pktbuf.h"
|
||||
#include "xtimer.h"
|
||||
#include "msg.h"
|
||||
#include "net/netdev.h"
|
||||
#include "net/netdev/layer.h"
|
||||
#include "net/loramac.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define MSG_TYPE_TIMEOUT (0x3457) /**< Timeout message type */
|
||||
#define MSG_TYPE_MCPS_ACK_TIMEOUT (0x3458) /**< ACK timeout message type */
|
||||
#define MSG_TYPE_MLME_BACKOFF_EXPIRE (0x3459) /**< Backoff timer expiration message type */
|
||||
|
||||
#define MTYPE_MASK 0xE0 /**< MHDR mtype mask */
|
||||
#define MTYPE_JOIN_REQUEST 0x0 /**< Join Request type */
|
||||
#define MTYPE_JOIN_ACCEPT 0x1 /**< Join Accept type */
|
||||
#define MTYPE_UNCNF_UPLINK 0x2 /**< Unconfirmed uplink type */
|
||||
#define MTYPE_UNCNF_DOWNLINK 0x3 /**< Unconfirmed downlink type */
|
||||
#define MTYPE_CNF_UPLINK 0x4 /**< Confirmed uplink type */
|
||||
#define MTYPE_CNF_DOWNLINK 0x5 /**< Confirmed downlink type */
|
||||
#define MTYPE_REJOIN_REQ 0x6 /**< Re-join request type */
|
||||
#define MTYPE_PROPIETARY 0x7 /**< Propietary frame type */
|
||||
|
||||
#define MAJOR_MASK 0x3 /**< Major mtype mask */
|
||||
#define MAJOR_LRWAN_R1 0x0 /**< LoRaWAN R1 version type */
|
||||
|
||||
#define JOIN_REQUEST_SIZE (23U) /**< Join Request size in bytes */
|
||||
#define MIC_SIZE (4U) /**< MIC size in bytes */
|
||||
#define CFLIST_SIZE (16U) /**< Channel Frequency list size in bytes */
|
||||
|
||||
#define GNRC_LORAWAN_MAX_CHANNELS (16U) /**< Maximum number of channels */
|
||||
|
||||
#define LORAWAN_STATE_IDLE (0) /**< MAC state machine in idle */
|
||||
#define LORAWAN_STATE_RX_1 (1) /**< MAC state machine in RX1 */
|
||||
#define LORAWAN_STATE_RX_2 (2) /**< MAC state machine in RX2 */
|
||||
#define LORAWAN_STATE_TX (3) /**< MAC state machine in TX */
|
||||
|
||||
#define GNRC_LORAWAN_DIR_UPLINK (0U) /**< uplink frame direction */
|
||||
#define GNRC_LORAWAN_DIR_DOWNLINK (1U) /**< downlink frame direction */
|
||||
|
||||
#define GNRC_LORAWAN_BACKOFF_WINDOW_TICK (3600000000LL) /**< backoff expire tick in usecs (set to 1 second) */
|
||||
|
||||
#define GNRC_LORAWAN_BACKOFF_BUDGET_1 (36000000LL) /**< budget of time on air during the first hour */
|
||||
#define GNRC_LORAWAN_BACKOFF_BUDGET_2 (36000000LL) /**< budget of time on air between 1-10 hours after boot */
|
||||
#define GNRC_LORAWAN_BACKOFF_BUDGET_3 (8700000LL) /**< budget of time on air every 24 hours */
|
||||
|
||||
#define GNRC_LORAWAN_MLME_OPTS_LINK_CHECK_REQ (1 << 0) /**< Internal Link Check request flag */
|
||||
|
||||
#define GNRC_LORAWAN_CID_SIZE (1U) /**< size of Command ID in FOps */
|
||||
#define GNRC_LORAWAN_CID_LINK_CHECK_ANS (0x02) /**< Link Check CID */
|
||||
|
||||
#define GNRC_LORAWAN_FOPT_LINK_CHECK_ANS_SIZE (3U) /**< size of Link check answer */
|
||||
|
||||
#define GNRC_LORAWAN_JOIN_DELAY_U32_MASK (0x1FFFFF) /**< mask for detecting overflow in frame counter */
|
||||
|
||||
#define GNRC_LORAWAN_MAX_PAYLOAD_1 (59U) /**< max MAC payload in DR0, DR1 and DR2 */
|
||||
#define GNRC_LORAWAN_MAX_PAYLOAD_2 (123U) /**< max MAC payload in DR3 */
|
||||
#define GNRC_LORAWAN_MAX_PAYLOAD_3 (250U) /**< max MAC payload above DR3 */
|
||||
|
||||
#define GNRC_LORAWAN_CFLIST_ENTRY_SIZE (3U) /**< size of Channel Frequency list */
|
||||
#define GNRC_LORAWAN_JOIN_ACCEPT_MAX_SIZE (33U) /**< max size of Join Accept frame */
|
||||
|
||||
#define GNRC_LORAWAN_BACKOFF_STATE_1 (0U) /**< backoff state during the first hour after boot */
|
||||
#define GNRC_LORAWAN_BACKOFF_STATE_2 (1U) /**< backoff state between 1-10 hours after boot */
|
||||
#define GNRC_LORAWAN_BACKOFF_STATE_3 (2U) /**< backoff state past 11 hours after boot */
|
||||
|
||||
#define GNRC_LORAWAN_BACKOFF_TIME_1 (1U) /**< duration of first backoff state (in hours) */
|
||||
#define GNRC_LORAWAN_BACKOFF_TIME_2 (10U) /**< duration of second backoff state (in hours) */
|
||||
#define GNRC_LORAWAN_BACKOFF_TIME_3 (24U) /**< duration of third backoff state (in hours) */
|
||||
|
||||
#define GNRC_LORAWAN_APP_NONCE_SIZE (3U) /**< App Nonce size */
|
||||
#define GNRC_LORAWAN_NET_ID_SIZE (3U) /**< Net ID size */
|
||||
#define GNRC_LORAWAN_DEV_NONCE_SIZE (2U) /**< Dev Nonce size */
|
||||
|
||||
/**
|
||||
* @brief buffer helper for parsing and constructing LoRaWAN packets.
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t *data; /**< pointer to the beginning of the buffer holding data */
|
||||
uint8_t size; /**< size of the buffer */
|
||||
uint8_t index; /**< current inxed in the buffer */
|
||||
} lorawan_buffer_t;
|
||||
|
||||
/**
|
||||
* @brief MLME Join Request data
|
||||
*/
|
||||
typedef struct {
|
||||
void *deveui; /**< pointer to the Device EUI */
|
||||
void *appeui; /**< pointer to the Application EUI */
|
||||
void *appkey; /**< pointer to the Application Key */
|
||||
uint8_t dr; /**< datarate for the Join Request */
|
||||
} mlme_lorawan_join_t;
|
||||
|
||||
/**
|
||||
* @brief MLME Link Check confirmation data
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t margin; /**< demodulation margin (in dB) */
|
||||
uint8_t num_gateways; /**< number of gateways */
|
||||
} mlme_link_req_confirm_t;
|
||||
|
||||
/**
|
||||
* @brief MCPS data
|
||||
*/
|
||||
typedef struct {
|
||||
gnrc_pktsnip_t *pkt; /**< packet of the request */
|
||||
uint8_t port; /**< port of the request */
|
||||
uint8_t dr; /**< datarate of the request */
|
||||
} mcps_data_t;
|
||||
|
||||
/**
|
||||
* @brief MCPS service access point descriptor
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t fcnt; /**< uplink framecounter */
|
||||
uint32_t fcnt_down; /**< downlink frame counter */
|
||||
gnrc_pktsnip_t *outgoing_pkt; /**< holds the outgoing packet in case of retransmissions */
|
||||
int nb_trials; /**< holds the remaining number of retransmissions */
|
||||
int ack_requested; /**< wether the network server requested an ACK */
|
||||
int waiting_for_ack; /**< true if the MAC layer is waiting for an ACK */
|
||||
} gnrc_lorawan_mcps_t;
|
||||
|
||||
/**
|
||||
* @brief MLME service access point descriptor
|
||||
*/
|
||||
typedef struct {
|
||||
xtimer_t backoff_timer; /**< timer used for backoff expiration */
|
||||
msg_t backoff_msg; /**< msg for backoff expiration */
|
||||
uint8_t activation; /**< Activation mechanism of the MAC layer */
|
||||
int pending_mlme_opts; /**< holds pending mlme opts */
|
||||
uint32_t nid; /**< current Network ID */
|
||||
int32_t backoff_budget; /**< remaining Time On Air budget */
|
||||
uint8_t dev_nonce[2]; /**< Device Nonce */
|
||||
uint8_t backoff_state; /**< state in the backoff state machine */
|
||||
} gnrc_lorawan_mlme_t;
|
||||
|
||||
/**
|
||||
* @brief GNRC LoRaWAN mac descriptor */
|
||||
typedef struct {
|
||||
netdev_t netdev; /**< netdev for the MAC layer */
|
||||
xtimer_t rx; /**< RX timer */
|
||||
msg_t msg; /**< MAC layer message descriptor */
|
||||
gnrc_lorawan_mcps_t mcps; /**< MCPS descriptor */
|
||||
gnrc_lorawan_mlme_t mlme; /**< MLME descriptor */
|
||||
void *mlme_buf; /**< pointer to MLME buffer */
|
||||
void *mcps_buf; /**< pointer to MCPS buffer */
|
||||
uint8_t *nwkskey; /**< pointer to Network SKey buffer */
|
||||
uint8_t *appskey; /**< pointer to Application SKey buffer */
|
||||
uint32_t channel[GNRC_LORAWAN_MAX_CHANNELS]; /**< channel array */
|
||||
uint32_t toa; /**< Time on Air of the last transmission */
|
||||
int busy; /**< MAC busy */
|
||||
int shutdown_req; /**< MAC Shutdown request */
|
||||
le_uint32_t dev_addr; /**< Device address */
|
||||
int state; /**< state of MAC layer */
|
||||
uint8_t dl_settings; /**< downlink settings */
|
||||
uint8_t rx_delay; /**< Delay of first reception window */
|
||||
uint8_t dr_range[GNRC_LORAWAN_MAX_CHANNELS]; /**< Datarate Range for all channels */
|
||||
uint8_t last_dr; /**< datarate of the last transmission */
|
||||
} gnrc_lorawan_t;
|
||||
|
||||
/**
|
||||
* @brief Encrypts LoRaWAN payload
|
||||
*
|
||||
* @note This function is also used for decrypting a LoRaWAN packet. The LoRaWAN server encrypts the packet using decryption, so the end device only needs to implement encryption
|
||||
*
|
||||
* @param[in] iolist packet iolist representation
|
||||
* @param[in] dev_addr device address
|
||||
* @param[in] fcnt frame counter
|
||||
* @param[in] dir direction of the packet (0 if uplink, 1 if downlink)
|
||||
* @param[in] appskey pointer to the Application Session Key
|
||||
*/
|
||||
void gnrc_lorawan_encrypt_payload(iolist_t *iolist, const le_uint32_t *dev_addr, uint32_t fcnt, uint8_t dir, const uint8_t *appskey);
|
||||
|
||||
/**
|
||||
* @brief Decrypts join accept message
|
||||
*
|
||||
* @param[in] key key to be used in the decryption
|
||||
* @param[in] pkt pointer to Join Accept MAC component (next byte after the MHDR)
|
||||
* @param[in] has_clist true if the Join Accept frame has CFList
|
||||
* @param[out] out buffer where the decryption is stored
|
||||
*/
|
||||
void gnrc_lorawan_decrypt_join_accept(const uint8_t *key, uint8_t *pkt, int has_clist, uint8_t *out);
|
||||
|
||||
/**
|
||||
* @brief Generate LoRaWAN session keys
|
||||
*
|
||||
* Intended to be called after a successfull Join Request in order to generate
|
||||
* NwkSKey and AppSKey
|
||||
*
|
||||
* @param[in] app_nonce pointer to the app_nonce of the Join Accept message
|
||||
* @param[in] dev_nonce pointer to the dev_nonce buffer
|
||||
* @param[in] appkey pointer to eh AppKey
|
||||
* @param[out] nwkskey pointer to the NwkSKey
|
||||
* @param[out] appskey pointer to the AppSKey
|
||||
*/
|
||||
void gnrc_lorawan_generate_session_keys(const uint8_t *app_nonce, const uint8_t *dev_nonce, const uint8_t *appkey, uint8_t *nwkskey, uint8_t *appskey);
|
||||
|
||||
/**
|
||||
* @brief Set datarate for the next transmission
|
||||
*
|
||||
* @param[in] mac pointer to the MAC descriptor
|
||||
* @param[in] datarate desired datarate
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -EINVAL if datarate is not available in the current region
|
||||
*/
|
||||
int gnrc_lorawan_set_dr(gnrc_lorawan_t *mac, uint8_t datarate);
|
||||
|
||||
/**
|
||||
* @brief build uplink frame
|
||||
*
|
||||
* @param[in] mac pointer to MAC descriptor
|
||||
* @param[in] payload packet containing payload
|
||||
* @param[in] confirmed_data true if confirmed frame
|
||||
* @param[in] port MAC port
|
||||
*
|
||||
* @return full LoRaWAN frame including payload
|
||||
* @return NULL if packet buffer is full. `payload` is released
|
||||
*/
|
||||
gnrc_pktsnip_t *gnrc_lorawan_build_uplink(gnrc_lorawan_t *mac, gnrc_pktsnip_t *payload, int confirmed_data, uint8_t port);
|
||||
|
||||
/**
|
||||
* @brief pick a random available LoRaWAN channel
|
||||
*
|
||||
* @param[in] mac pointer to the MAC descriptor
|
||||
*
|
||||
* @return a free channel
|
||||
*/
|
||||
uint32_t gnrc_lorawan_pick_channel(gnrc_lorawan_t *mac);
|
||||
|
||||
/**
|
||||
* @brief Build fopts header
|
||||
*
|
||||
* @param[in] mac pointer to MAC descriptor
|
||||
* @param[out] buf destination buffer of fopts. If NULL, this function just returns
|
||||
* the size of the expected fopts frame.
|
||||
*
|
||||
* @return size of the fopts frame
|
||||
*/
|
||||
uint8_t gnrc_lorawan_build_options(gnrc_lorawan_t *mac, lorawan_buffer_t *buf);
|
||||
|
||||
/**
|
||||
* @brief Process an fopts frame
|
||||
*
|
||||
* @param[in] mac pointer to MAC descriptor
|
||||
* @param[in] fopts pointer to fopts frame
|
||||
* @param[in] size size of fopts frame
|
||||
*/
|
||||
void gnrc_lorawan_process_fopts(gnrc_lorawan_t *mac, uint8_t *fopts, size_t size);
|
||||
|
||||
/**
|
||||
* @brief calculate join Message Integrity Code
|
||||
*
|
||||
* @param[in] io iolist representation of the packet
|
||||
* @param[in] key key used to calculate the MIC
|
||||
* @param[out] out calculated MIC
|
||||
*/
|
||||
void gnrc_lorawan_calculate_join_mic(const iolist_t *io, const uint8_t *key, le_uint32_t *out);
|
||||
|
||||
/**
|
||||
* @brief Calculate Message Integrity Code for a MCPS message
|
||||
*
|
||||
* @param[in] dev_addr the Device Address
|
||||
* @param[in] fcnt frame counter
|
||||
* @param[in] dir direction of the packet (0 is uplink, 1 is downlink)
|
||||
* @param[in] pkt the pkt
|
||||
* @param[in] nwkskey pointer to the Network Session Key
|
||||
* @param[out] out calculated MIC
|
||||
*/
|
||||
void gnrc_lorawan_calculate_mic(const le_uint32_t *dev_addr, uint32_t fcnt,
|
||||
uint8_t dir, iolist_t *pkt, const uint8_t *nwkskey, le_uint32_t *out);
|
||||
|
||||
/**
|
||||
* @brief Build a MCPS LoRaWAN header
|
||||
*
|
||||
* @param[in] mtype the MType of the header
|
||||
* @param[in] dev_addr the Device Address
|
||||
* @param[in] fcnt frame counter
|
||||
* @param[in] ack true if ACK bit is set
|
||||
* @param[in] fopts_length the length of the FOpts field
|
||||
* @param[out] buf destination buffer of the hdr
|
||||
*
|
||||
* @return the size of the header
|
||||
*/
|
||||
size_t gnrc_lorawan_build_hdr(uint8_t mtype, le_uint32_t *dev_addr, uint32_t fcnt, uint8_t ack, uint8_t fopts_length, lorawan_buffer_t *buf);
|
||||
|
||||
/**
|
||||
* @brief Process an MCPS downlink message (confirmable or non comfirmable)
|
||||
*
|
||||
* @param[in] mac pointer to the MAC descriptor
|
||||
* @param[in] pkt pointer to the downlink message
|
||||
*/
|
||||
void gnrc_lorawan_mcps_process_downlink(gnrc_lorawan_t *mac, gnrc_pktsnip_t *pkt);
|
||||
|
||||
/**
|
||||
* @brief Init regional channel settings.
|
||||
*
|
||||
* Intended to be called upon initialization
|
||||
*
|
||||
* @param[in] mac pointer to the MAC descriptor
|
||||
*/
|
||||
void gnrc_lorawan_channels_init(gnrc_lorawan_t *mac);
|
||||
|
||||
/**
|
||||
* @brief Reset MAC parameters
|
||||
*
|
||||
* @note This doesn't affect backoff timers variables.
|
||||
*
|
||||
* @param[in] mac pointer to the MAC layer
|
||||
*/
|
||||
void gnrc_lorawan_reset(gnrc_lorawan_t *mac);
|
||||
|
||||
/**
|
||||
* @brief Send a LoRaWAN packet
|
||||
*
|
||||
* @param[in] mac pointer to the MAC descriptor
|
||||
* @param[in] pkt the packet to be sent
|
||||
* @param[in] dr the datarate used for the transmission
|
||||
*/
|
||||
void gnrc_lorawan_send_pkt(gnrc_lorawan_t *mac, gnrc_pktsnip_t *pkt, uint8_t dr);
|
||||
|
||||
/**
|
||||
* @brief Process join accept message
|
||||
*
|
||||
* @param[in] mac pointer to the MAC descriptor
|
||||
* @param[in] pkt the Join Accept packet
|
||||
*/
|
||||
void gnrc_lorawan_mlme_process_join(gnrc_lorawan_t *mac, gnrc_pktsnip_t *pkt);
|
||||
|
||||
/**
|
||||
* @brief Inform the MAC layer that no packet was received during reception.
|
||||
*
|
||||
* To be called when the radio reports "NO RX" after the second reception
|
||||
* window
|
||||
*
|
||||
* @param[in] mac pointer to the MAC descriptor
|
||||
*/
|
||||
void gnrc_lorawan_mlme_no_rx(gnrc_lorawan_t *mac);
|
||||
|
||||
/**
|
||||
* @brief Trigger a MCPS event
|
||||
*
|
||||
* @param[in] mac pointer to the MAC descriptor
|
||||
* @param[in] event the event to be processed.
|
||||
* @param[in] data set to true if the packet contains payload
|
||||
*/
|
||||
void gnrc_lorawan_mcps_event(gnrc_lorawan_t *mac, int event, int data);
|
||||
|
||||
/**
|
||||
* @brief Get the maximum MAC payload (M value) for a given datarate.
|
||||
*
|
||||
* @note This function is region specific
|
||||
*
|
||||
* @param[in] datarate datarate
|
||||
*
|
||||
* @return the maximum allowed size of the packet
|
||||
*/
|
||||
uint8_t gnrc_lorawan_region_mac_payload_max(uint8_t datarate);
|
||||
|
||||
/**
|
||||
* @brief MLME Backoff expiration tick
|
||||
*
|
||||
* Should be called every hour in order to maintain the Time On Air budget.
|
||||
*
|
||||
* @param[in] mac pointer to the MAC descriptor
|
||||
*/
|
||||
void gnrc_lorawan_mlme_backoff_expire(gnrc_lorawan_t *mac);
|
||||
|
||||
/**
|
||||
* @brief Process and dispatch a full LoRaWAN packet
|
||||
*
|
||||
* Intended to be called right after reception from the radio
|
||||
*
|
||||
* @param[in] mac pointer to the MAC descriptor
|
||||
* @param[in] pkt the received packet
|
||||
*/
|
||||
void gnrc_lorawan_process_pkt(gnrc_lorawan_t *mac, gnrc_pktsnip_t *pkt);
|
||||
|
||||
/**
|
||||
* @brief Open a reception window
|
||||
*
|
||||
* This is called by the MAC layer on timeout event.
|
||||
*
|
||||
* @param[in] mac pointer to the MAC descriptor
|
||||
*/
|
||||
void gnrc_lorawan_open_rx_window(gnrc_lorawan_t *mac);
|
||||
|
||||
/**
|
||||
* @brief save internal MAC state in non-volatile storage and shutdown
|
||||
* the MAC layer gracefully.
|
||||
*
|
||||
* @param mac
|
||||
*/
|
||||
void gnrc_lorawan_perform_save(gnrc_lorawan_t *mac);
|
||||
|
||||
/**
|
||||
* @brief Acquire the MAC layer
|
||||
*
|
||||
* @param[in] mac pointer to the MAC descriptor
|
||||
*
|
||||
* @return true on success
|
||||
* @return false if MAC is already acquired
|
||||
*/
|
||||
static inline int gnrc_lorawan_mac_acquire(gnrc_lorawan_t *mac)
|
||||
{
|
||||
int _c = mac->busy;
|
||||
|
||||
mac->busy = true;
|
||||
return !_c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Release the MAC layer
|
||||
*
|
||||
* @param[in] mac pointer to the MAC descriptor
|
||||
*/
|
||||
static inline void gnrc_lorawan_mac_release(gnrc_lorawan_t *mac)
|
||||
{
|
||||
mac->busy = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Allocate memory to hold a GNRC LoRaWAN MCPS request
|
||||
*
|
||||
* @param[in] mac pointer to the MAC descriptor
|
||||
*
|
||||
* @return pointer the allocated buffer
|
||||
*/
|
||||
static inline void *gnrc_lorawan_mcps_allocate(gnrc_lorawan_t *mac)
|
||||
{
|
||||
mac->netdev.event_callback((netdev_t *) mac, NETDEV_EVENT_MCPS_GET_BUFFER);
|
||||
return mac->mcps_buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Allocate memory to hold a GNRC LoRaWAN MLME request
|
||||
*
|
||||
* @param[in] mac pointer to the MAC descriptor
|
||||
*
|
||||
* @return pointer the allocated buffer
|
||||
*/
|
||||
static inline void *gnrc_lorawan_mlme_allocate(gnrc_lorawan_t *mac)
|
||||
{
|
||||
mac->netdev.event_callback((netdev_t *) mac, NETDEV_EVENT_MLME_GET_BUFFER);
|
||||
return mac->mlme_buf;
|
||||
}
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GNRC_LORAWAN_INTERNAL_H */
|
||||
/** @} */
|
@ -9,5 +9,8 @@ endif
|
||||
ifneq (,$(filter gnrc_netif_hdr,$(USEMODULE)))
|
||||
DIRS += hdr
|
||||
endif
|
||||
ifneq (,$(filter gnrc_netif_lorawan,$(USEMODULE)))
|
||||
DIRS += lorawan
|
||||
endif
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
||||
|
3
sys/net/gnrc/netif/lorawan/Makefile
Normal file
3
sys/net/gnrc/netif/lorawan/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
MODULE := gnrc_netif_lorawan
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
356
sys/net/gnrc/netif/lorawan/gnrc_netif_lorawan.c
Normal file
356
sys/net/gnrc/netif/lorawan/gnrc_netif_lorawan.c
Normal file
@ -0,0 +1,356 @@
|
||||
/*
|
||||
* Copyright (C) 2018 HAW Hamburg
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @author Jose Ignacio Alamos <jose.alamos@haw-hamburg.de>
|
||||
*/
|
||||
|
||||
#include "net/gnrc/pktbuf.h"
|
||||
#include "net/gnrc/netif.h"
|
||||
#include "net/gnrc/netif/lorawan.h"
|
||||
#include "net/gnrc/netif/internal.h"
|
||||
#include "net/gnrc/lorawan.h"
|
||||
#include "net/netdev.h"
|
||||
#include "net/lora.h"
|
||||
#include "net/loramac.h"
|
||||
#include "net/gnrc/netreg.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
static uint8_t _nwkskey[LORAMAC_NWKSKEY_LEN] = LORAMAC_NWK_SKEY_DEFAULT;
|
||||
static uint8_t _appskey[LORAMAC_APPSKEY_LEN] = LORAMAC_APP_SKEY_DEFAULT;
|
||||
static uint8_t _appkey[LORAMAC_APPKEY_LEN] = LORAMAC_APP_KEY_DEFAULT;
|
||||
static uint8_t _deveui[LORAMAC_DEVEUI_LEN] = LORAMAC_DEV_EUI_DEFAULT;
|
||||
static uint8_t _appeui[LORAMAC_APPEUI_LEN] = LORAMAC_APP_EUI_DEFAULT;
|
||||
static uint8_t _devaddr[LORAMAC_DEVADDR_LEN] = LORAMAC_DEV_ADDR_DEFAULT;
|
||||
|
||||
static int _send(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt);
|
||||
static gnrc_pktsnip_t *_recv(gnrc_netif_t *netif);
|
||||
static void _msg_handler(gnrc_netif_t *netif, msg_t *msg);
|
||||
static int _get(gnrc_netif_t *netif, gnrc_netapi_opt_t *opt);
|
||||
static int _set(gnrc_netif_t *netif, const gnrc_netapi_opt_t *opt);
|
||||
static void _init(gnrc_netif_t *netif);
|
||||
|
||||
static const gnrc_netif_ops_t lorawan_ops = {
|
||||
.init = _init,
|
||||
.send = _send,
|
||||
.recv = _recv,
|
||||
.get = _get,
|
||||
.set = _set,
|
||||
.msg_handler = _msg_handler
|
||||
};
|
||||
|
||||
static uint8_t _mcps_buffer[sizeof(mcps_confirm_t) > sizeof(mcps_indication_t) ?
|
||||
sizeof(mcps_confirm_t) : sizeof(mcps_indication_t)];
|
||||
static uint8_t _mlme_buffer[sizeof(mlme_confirm_t) > sizeof(mlme_indication_t) ?
|
||||
sizeof(mlme_confirm_t) : sizeof(mlme_indication_t)];
|
||||
|
||||
static void _mlme_confirm(gnrc_netif_t *netif, mlme_confirm_t *confirm)
|
||||
{
|
||||
if (confirm->type == MLME_JOIN) {
|
||||
if (confirm->status == 0) {
|
||||
DEBUG("gnrc_lorawan: join succeeded\n");
|
||||
}
|
||||
else {
|
||||
DEBUG("gnrc_lorawan: join failed\n");
|
||||
}
|
||||
}
|
||||
else if (confirm->type == MLME_LINK_CHECK) {
|
||||
netif->lorawan.flags &= ~GNRC_NETIF_LORAWAN_FLAGS_LINK_CHECK;
|
||||
netif->lorawan.demod_margin = confirm->link_req.margin;
|
||||
netif->lorawan.num_gateways = confirm->link_req.num_gateways;
|
||||
}
|
||||
}
|
||||
|
||||
static void _mac_cb(netdev_t *dev, netdev_event_t event)
|
||||
{
|
||||
gnrc_lorawan_t *mac = (gnrc_lorawan_t *) dev;
|
||||
|
||||
mcps_confirm_t *mcps_confirm;
|
||||
mcps_indication_t *mcps_indication;
|
||||
|
||||
switch (event) {
|
||||
case NETDEV_EVENT_MLME_INDICATION:
|
||||
/* ignore */
|
||||
break;
|
||||
case NETDEV_EVENT_MCPS_INDICATION:
|
||||
mcps_indication = mac->mcps_buf;
|
||||
if (!gnrc_netapi_dispatch_receive(GNRC_NETTYPE_LORAWAN, mcps_indication->data.port, mcps_indication->data.pkt)) {
|
||||
gnrc_pktbuf_release(mcps_indication->data.pkt);
|
||||
}
|
||||
break;
|
||||
case NETDEV_EVENT_MLME_CONFIRM:
|
||||
_mlme_confirm((gnrc_netif_t *) mac->netdev.context, mac->mlme_buf);
|
||||
break;
|
||||
case NETDEV_EVENT_MCPS_CONFIRM:
|
||||
mcps_confirm = mac->mcps_buf;
|
||||
if (mcps_confirm->status == 0) {
|
||||
gnrc_pktbuf_release(mac->mcps.outgoing_pkt);
|
||||
}
|
||||
else {
|
||||
gnrc_pktbuf_release_error(mac->mcps.outgoing_pkt, 1);
|
||||
}
|
||||
mac->mcps.outgoing_pkt = NULL;
|
||||
break;
|
||||
case NETDEV_EVENT_MLME_GET_BUFFER:
|
||||
mac->mlme_buf = _mlme_buffer;
|
||||
break;
|
||||
case NETDEV_EVENT_MCPS_GET_BUFFER:
|
||||
mac->mcps_buf = _mcps_buffer;
|
||||
break;
|
||||
default:
|
||||
netdev_event_cb_pass(dev, event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void _driver_cb(netdev_t *dev, netdev_event_t event)
|
||||
{
|
||||
gnrc_lorawan_t *mac = (gnrc_lorawan_t *) dev->context;
|
||||
gnrc_netif_t *netif = (gnrc_netif_t *) mac->netdev.context;
|
||||
|
||||
if (event == NETDEV_EVENT_ISR) {
|
||||
msg_t msg = { .type = NETDEV_MSG_TYPE_EVENT,
|
||||
.content = { .ptr = netif } };
|
||||
|
||||
if (msg_send(&msg, netif->pid) <= 0) {
|
||||
DEBUG("gnrc_netif: possibly lost interrupt.\n");
|
||||
}
|
||||
}
|
||||
else {
|
||||
DEBUG("gnrc_netif: event triggered -> %i\n", event);
|
||||
switch (event) {
|
||||
case NETDEV_EVENT_RX_COMPLETE:
|
||||
gnrc_lorawan_recv(mac);
|
||||
break;
|
||||
case NETDEV_EVENT_TX_COMPLETE:
|
||||
gnrc_lorawan_event_tx_complete(mac);
|
||||
break;
|
||||
case NETDEV_EVENT_RX_TIMEOUT:
|
||||
gnrc_lorawan_event_timeout(mac);
|
||||
break;
|
||||
default:
|
||||
DEBUG("gnrc_netif: warning: unhandled event %u.\n", event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void _reset(gnrc_netif_t *netif)
|
||||
{
|
||||
netif->lorawan.otaa = LORAMAC_DEFAULT_JOIN_PROCEDURE == LORAMAC_JOIN_OTAA ? NETOPT_ENABLE : NETOPT_DISABLE;
|
||||
netif->lorawan.datarate = LORAMAC_DEFAULT_DR;
|
||||
netif->lorawan.demod_margin = 0;
|
||||
netif->lorawan.num_gateways = 0;
|
||||
netif->lorawan.port = LORAMAC_DEFAULT_TX_PORT;
|
||||
netif->lorawan.ack_req = LORAMAC_DEFAULT_TX_MODE == LORAMAC_TX_CNF;
|
||||
netif->lorawan.flags = 0;
|
||||
}
|
||||
|
||||
static void _memcpy_reversed(uint8_t *dst, uint8_t *src, size_t size)
|
||||
{
|
||||
for(size_t i=0;i<size;i++) {
|
||||
dst[size-i-1] = src[i];
|
||||
}
|
||||
}
|
||||
|
||||
static void _init(gnrc_netif_t *netif)
|
||||
{
|
||||
gnrc_netif_default_init(netif);
|
||||
netif->dev->event_callback = _driver_cb;
|
||||
netif->lorawan.mac.netdev.event_callback = _mac_cb;
|
||||
netif->lorawan.mac.netdev.context = netif;
|
||||
_reset(netif);
|
||||
|
||||
/* Initialize default keys, address and EUIs */
|
||||
memcpy(netif->lorawan.nwkskey, _nwkskey, sizeof(_nwkskey));
|
||||
memcpy(netif->lorawan.appskey, _appskey, sizeof(_appskey));
|
||||
_memcpy_reversed(netif->lorawan.deveui, _deveui, sizeof(_deveui));
|
||||
memcpy(netif->lorawan.appkey, _appkey, sizeof(_appkey));
|
||||
_memcpy_reversed(netif->lorawan.appeui, _appeui, sizeof(_appeui));
|
||||
|
||||
gnrc_lorawan_setup(&netif->lorawan.mac, netif->dev);
|
||||
netif->lorawan.mac.netdev.driver->set(&netif->lorawan.mac.netdev, NETOPT_ADDRESS, _devaddr, sizeof(_devaddr));
|
||||
gnrc_lorawan_init(&netif->lorawan.mac, netif->lorawan.nwkskey, netif->lorawan.appskey);
|
||||
}
|
||||
|
||||
gnrc_netif_t *gnrc_netif_lorawan_create(char *stack, int stacksize,
|
||||
char priority, char *name,
|
||||
netdev_t *dev)
|
||||
{
|
||||
return gnrc_netif_create(stack, stacksize, priority, name, dev,
|
||||
&lorawan_ops);
|
||||
}
|
||||
|
||||
static gnrc_pktsnip_t *_recv(gnrc_netif_t *netif)
|
||||
{
|
||||
(void) netif;
|
||||
/* Unused */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _send(gnrc_netif_t *netif, gnrc_pktsnip_t *payload)
|
||||
{
|
||||
mlme_request_t mlme_request;
|
||||
mlme_confirm_t mlme_confirm;
|
||||
|
||||
if (netif->lorawan.flags & GNRC_NETIF_LORAWAN_FLAGS_LINK_CHECK) {
|
||||
mlme_request.type = MLME_LINK_CHECK;
|
||||
gnrc_lorawan_mlme_request(&netif->lorawan.mac, &mlme_request, &mlme_confirm);
|
||||
}
|
||||
mcps_request_t req = { .type = netif->lorawan.ack_req ? MCPS_CONFIRMED : MCPS_UNCONFIRMED,
|
||||
.data = { .pkt = payload, .port = netif->lorawan.port,
|
||||
.dr = netif->lorawan.datarate } };
|
||||
mcps_confirm_t conf;
|
||||
gnrc_lorawan_mcps_request(&netif->lorawan.mac, &req, &conf);
|
||||
return conf.status;
|
||||
}
|
||||
|
||||
static void _msg_handler(gnrc_netif_t *netif, msg_t *msg)
|
||||
{
|
||||
(void) netif;
|
||||
(void) msg;
|
||||
switch (msg->type) {
|
||||
case MSG_TYPE_TIMEOUT:
|
||||
gnrc_lorawan_open_rx_window(&netif->lorawan.mac);
|
||||
break;
|
||||
case MSG_TYPE_MCPS_ACK_TIMEOUT:
|
||||
gnrc_lorawan_mcps_event(&netif->lorawan.mac, MCPS_EVENT_ACK_TIMEOUT, 0);
|
||||
break;
|
||||
case MSG_TYPE_MLME_BACKOFF_EXPIRE:
|
||||
gnrc_lorawan_mlme_backoff_expire(&netif->lorawan.mac);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int _get(gnrc_netif_t *netif, gnrc_netapi_opt_t *opt)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
mlme_confirm_t mlme_confirm;
|
||||
mlme_request_t mlme_request;
|
||||
switch (opt->opt) {
|
||||
case NETOPT_OTAA:
|
||||
assert(opt->data_len >= sizeof(netopt_enable_t));
|
||||
*((netopt_enable_t *) opt->data) = netif->lorawan.otaa;
|
||||
break;
|
||||
case NETOPT_LINK_CONNECTED:
|
||||
mlme_request.type = MLME_GET;
|
||||
mlme_request.mib.type = MIB_ACTIVATION_METHOD;
|
||||
gnrc_lorawan_mlme_request(&netif->lorawan.mac, &mlme_request, &mlme_confirm);
|
||||
*((netopt_enable_t *) opt->data) = mlme_confirm.mib.activation != MLME_ACTIVATION_NONE;
|
||||
break;
|
||||
case NETOPT_LINK_CHECK:
|
||||
assert(opt->data_len == sizeof(netopt_enable_t));
|
||||
*((netopt_enable_t *) opt->data) = (netif->lorawan.flags & GNRC_NETIF_LORAWAN_FLAGS_LINK_CHECK) ?
|
||||
NETOPT_ENABLE : NETOPT_DISABLE;
|
||||
break;
|
||||
case NETOPT_NUM_GATEWAYS:
|
||||
assert(opt->data_len == sizeof(uint8_t));
|
||||
*((uint8_t *) opt->data) = netif->lorawan.num_gateways;
|
||||
break;
|
||||
case NETOPT_DEMOD_MARGIN:
|
||||
assert(opt->data_len == sizeof(uint8_t));
|
||||
*((uint8_t *) opt->data) = netif->lorawan.demod_margin;
|
||||
break;
|
||||
default:
|
||||
res = netif->lorawan.mac.netdev.driver->get(&netif->lorawan.mac.netdev, opt->opt, opt->data, opt->data_len);
|
||||
break;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int _set(gnrc_netif_t *netif, const gnrc_netapi_opt_t *opt)
|
||||
{
|
||||
int res = 0;
|
||||
mlme_confirm_t mlme_confirm;
|
||||
mlme_request_t mlme_request;
|
||||
|
||||
gnrc_netif_acquire(netif);
|
||||
switch (opt->opt) {
|
||||
case NETOPT_LORAWAN_DR:
|
||||
assert(opt->data_len == sizeof(uint8_t));
|
||||
netif->lorawan.datarate = *((uint8_t *) opt->data);
|
||||
break;
|
||||
case NETOPT_LORAWAN_TX_PORT:
|
||||
assert(opt->data_len == sizeof(uint8_t));
|
||||
netif->lorawan.port = *((uint8_t *) opt->data);
|
||||
break;
|
||||
case NETOPT_ACK_REQ:
|
||||
assert(opt->data_len == sizeof(netopt_enable_t));
|
||||
netif->lorawan.ack_req = *((netopt_enable_t *) opt->data);
|
||||
break;
|
||||
case NETOPT_LORAWAN_APPKEY:
|
||||
assert(opt->data_len == LORAMAC_APPKEY_LEN);
|
||||
memcpy(netif->lorawan.appkey, opt->data, LORAMAC_APPKEY_LEN);
|
||||
break;
|
||||
case NETOPT_ADDRESS_LONG:
|
||||
assert(opt->data_len == LORAMAC_DEVEUI_LEN);
|
||||
_memcpy_reversed(netif->lorawan.deveui, opt->data, LORAMAC_DEVEUI_LEN);
|
||||
break;
|
||||
case NETOPT_LORAWAN_APPEUI:
|
||||
assert(opt->data_len == LORAMAC_APPEUI_LEN);
|
||||
_memcpy_reversed(netif->lorawan.appeui, opt->data, LORAMAC_APPEUI_LEN);
|
||||
break;
|
||||
case NETOPT_OTAA:
|
||||
assert(opt->data_len == sizeof(netopt_enable_t));
|
||||
netif->lorawan.otaa = *((netopt_enable_t *) opt->data);
|
||||
break;
|
||||
case NETOPT_LORAWAN_APPSKEY:
|
||||
assert(opt->data_len >= LORAMAC_APPSKEY_LEN);
|
||||
memcpy(netif->lorawan.appskey, opt->data, LORAMAC_APPSKEY_LEN);
|
||||
break;
|
||||
case NETOPT_LORAWAN_NWKSKEY:
|
||||
assert(opt->data_len >= LORAMAC_NWKSKEY_LEN);
|
||||
memcpy(netif->lorawan.nwkskey, opt->data, LORAMAC_NWKSKEY_LEN);
|
||||
break;
|
||||
case NETOPT_LINK_CONNECTED:
|
||||
{
|
||||
netopt_enable_t en = *((netopt_enable_t *) opt->data);
|
||||
if (en) {
|
||||
if(netif->lorawan.otaa) {
|
||||
mlme_request.type = MLME_JOIN;
|
||||
mlme_request.join.deveui = netif->lorawan.deveui;
|
||||
mlme_request.join.appeui = netif->lorawan.appeui;
|
||||
mlme_request.join.appkey = netif->lorawan.appkey;
|
||||
mlme_request.join.dr = netif->lorawan.datarate;
|
||||
gnrc_lorawan_mlme_request(&netif->lorawan.mac, &mlme_request, &mlme_confirm);
|
||||
}
|
||||
else {
|
||||
mlme_request.type = MLME_SET;
|
||||
mlme_request.mib.activation = MLME_ACTIVATION_ABP;
|
||||
gnrc_lorawan_mlme_request(&netif->lorawan.mac, &mlme_request, &mlme_confirm);
|
||||
}
|
||||
}
|
||||
else {
|
||||
mlme_request.type = MLME_RESET;
|
||||
gnrc_lorawan_mlme_request(&netif->lorawan.mac, &mlme_request, &mlme_confirm);
|
||||
res = mlme_confirm.status;
|
||||
if (mlme_confirm.status == 0) {
|
||||
/* reset netif as well */
|
||||
_reset(netif);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NETOPT_LINK_CHECK:
|
||||
netif->lorawan.flags |= GNRC_NETIF_LORAWAN_FLAGS_LINK_CHECK;
|
||||
break;
|
||||
default:
|
||||
res = netif->lorawan.mac.netdev.driver->set(&netif->lorawan.mac.netdev, opt->opt, opt->data, opt->data_len);
|
||||
break;
|
||||
}
|
||||
gnrc_netif_release(netif);
|
||||
return res;
|
||||
}
|
||||
/** @} */
|
@ -112,6 +112,12 @@ static void _dump_snip(gnrc_pktsnip_t *pkt)
|
||||
od_hex_dump(pkt->data, pkt->size, OD_WIDTH_DEFAULT);
|
||||
break;
|
||||
#endif
|
||||
#ifdef MODULE_GNRC_LORAWAN
|
||||
case GNRC_NETTYPE_LORAWAN:
|
||||
printf("NETTYPE_LORAWAN (%i)\n", pkt->type);
|
||||
od_hex_dump(pkt->data, pkt->size, OD_WIDTH_DEFAULT);
|
||||
break;
|
||||
#endif
|
||||
#ifdef TEST_SUITES
|
||||
case GNRC_NETTYPE_TEST:
|
||||
printf("NETTYPE_TEST (%i)\n", pkt->type);
|
||||
|
@ -26,6 +26,8 @@
|
||||
#include "net/gnrc/netif.h"
|
||||
#include "net/gnrc/netif/hdr.h"
|
||||
#include "net/lora.h"
|
||||
#include "net/loramac.h"
|
||||
#include "fmt.h"
|
||||
|
||||
#ifdef MODULE_NETSTATS
|
||||
#include "net/netstats.h"
|
||||
@ -70,6 +72,8 @@ static const struct {
|
||||
{ "rx_single", NETOPT_SINGLE_RECEIVE },
|
||||
{ "chan_hop", NETOPT_CHANNEL_HOP },
|
||||
{ "checksum", NETOPT_CHECKSUM },
|
||||
{ "otaa", NETOPT_OTAA },
|
||||
{ "link_check", NETOPT_LINK_CHECK },
|
||||
};
|
||||
|
||||
/* utility functions */
|
||||
@ -169,6 +173,13 @@ static void _set_usage(char *cmd_name)
|
||||
" * \"bw\" - alias for channel bandwidth\n"
|
||||
" * \"sf\" - alias for spreading factor\n"
|
||||
" * \"cr\" - alias for coding rate\n"
|
||||
" * \"appeui\" - sets Application EUI\n"
|
||||
" * \"appkey\" - sets Application key\n"
|
||||
" * \"appskey\" - sets Application session key\n"
|
||||
" * \"deveui\" - sets Device EUI\n"
|
||||
" * \"dr\" - sets datarate\n"
|
||||
" * \"rx2_dr\" - sets datarate of RX2 (lorawan)\n"
|
||||
" * \"nwkskey\" - sets Network Session Key\n"
|
||||
#endif
|
||||
" * \"power\" - TX power in dBm\n"
|
||||
" * \"retrans\" - max. number of retransmissions\n"
|
||||
@ -219,6 +230,22 @@ static void _print_netopt(netopt_t opt)
|
||||
printf("long address");
|
||||
break;
|
||||
|
||||
case NETOPT_LORAWAN_APPKEY:
|
||||
printf("AppKey");
|
||||
break;
|
||||
|
||||
case NETOPT_LORAWAN_APPSKEY:
|
||||
printf("AppSKey");
|
||||
break;
|
||||
|
||||
case NETOPT_LORAWAN_NWKSKEY:
|
||||
printf("NwkSKey");
|
||||
break;
|
||||
|
||||
case NETOPT_LORAWAN_APPEUI:
|
||||
printf("AppEUI");
|
||||
break;
|
||||
|
||||
case NETOPT_SRC_LEN:
|
||||
printf("source address length");
|
||||
break;
|
||||
@ -288,10 +315,26 @@ static void _print_netopt(netopt_t opt)
|
||||
printf("checksum");
|
||||
break;
|
||||
|
||||
case NETOPT_OTAA:
|
||||
printf("otaa");
|
||||
break;
|
||||
|
||||
case NETOPT_LINK_CHECK:
|
||||
printf("link check");
|
||||
break;
|
||||
|
||||
case NETOPT_PHY_BUSY:
|
||||
printf("PHY busy");
|
||||
break;
|
||||
|
||||
case NETOPT_LORAWAN_DR:
|
||||
printf("datarate");
|
||||
break;
|
||||
|
||||
case NETOPT_LORAWAN_RX2_DR:
|
||||
printf("RX2 datarate");
|
||||
break;
|
||||
|
||||
default:
|
||||
/* we don't serve these options here */
|
||||
break;
|
||||
@ -491,6 +534,18 @@ static void _netif_list(kernel_pid_t iface)
|
||||
}
|
||||
line_thresh++;
|
||||
}
|
||||
#ifdef MODULE_GNRC_NETIF_CMD_LORA
|
||||
res = gnrc_netapi_get(iface, NETOPT_DEMOD_MARGIN, 0, &u8, sizeof(u8));
|
||||
if (res >= 0) {
|
||||
printf(" Demod margin.: %u ", (unsigned) u8);
|
||||
line_thresh++;
|
||||
}
|
||||
res = gnrc_netapi_get(iface, NETOPT_NUM_GATEWAYS, 0, &u8, sizeof(u8));
|
||||
if (res >= 0) {
|
||||
printf(" Num gateways.: %u ", (unsigned) u8);
|
||||
line_thresh++;
|
||||
}
|
||||
#endif
|
||||
line_thresh = _newline(0U, line_thresh);
|
||||
line_thresh = _netif_list_flag(iface, NETOPT_PROMISCUOUSMODE, "PROMISC ",
|
||||
line_thresh);
|
||||
@ -507,13 +562,15 @@ static void _netif_list(kernel_pid_t iface)
|
||||
line_thresh = _netif_list_flag(iface, NETOPT_CSMA, "CSMA ",
|
||||
line_thresh);
|
||||
line_thresh += _LINE_THRESHOLD + 1; /* enforce linebreak after this option */
|
||||
line_thresh = _netif_list_flag(iface, NETOPT_AUTOCCA, "AUTOCCA",
|
||||
line_thresh = _netif_list_flag(iface, NETOPT_AUTOCCA, "AUTOCCA ",
|
||||
line_thresh);
|
||||
line_thresh = _netif_list_flag(iface, NETOPT_IQ_INVERT, "IQ_INVERT",
|
||||
line_thresh = _netif_list_flag(iface, NETOPT_IQ_INVERT, "IQ_INVERT ",
|
||||
line_thresh);
|
||||
line_thresh = _netif_list_flag(iface, NETOPT_SINGLE_RECEIVE, "RX_SINGLE",
|
||||
line_thresh = _netif_list_flag(iface, NETOPT_SINGLE_RECEIVE, "RX_SINGLE ",
|
||||
line_thresh);
|
||||
line_thresh = _netif_list_flag(iface, NETOPT_CHANNEL_HOP, "CHAN_HOP",
|
||||
line_thresh = _netif_list_flag(iface, NETOPT_CHANNEL_HOP, "CHAN_HOP ",
|
||||
line_thresh);
|
||||
line_thresh = _netif_list_flag(iface, NETOPT_OTAA, "OTAA ",
|
||||
line_thresh);
|
||||
res = gnrc_netapi_get(iface, NETOPT_MAX_PDU_SIZE, 0, &u16, sizeof(u16));
|
||||
if (res > 0) {
|
||||
@ -823,6 +880,38 @@ static int _netif_set_flag(kernel_pid_t iface, netopt_t opt,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef MODULE_GNRC_NETIF_CMD_LORA
|
||||
static int _netif_set_lw_key(kernel_pid_t iface, netopt_t opt, char *key_str)
|
||||
{
|
||||
/* This is the longest key */
|
||||
uint8_t key[LORAMAC_APPKEY_LEN];
|
||||
|
||||
size_t key_len = fmt_hex_bytes(key, key_str);
|
||||
size_t expected_len;
|
||||
switch(opt) {
|
||||
case NETOPT_LORAWAN_APPKEY:
|
||||
case NETOPT_LORAWAN_APPSKEY:
|
||||
case NETOPT_LORAWAN_NWKSKEY:
|
||||
/* All keys have the same length as the APP KEY */
|
||||
expected_len = LORAMAC_APPKEY_LEN;
|
||||
break;
|
||||
default:
|
||||
/* Same rationale here */
|
||||
expected_len = LORAMAC_DEVEUI_LEN;
|
||||
}
|
||||
if (!key_len || key_len != expected_len) {
|
||||
puts("error: unable to parse key.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
gnrc_netapi_set(iface, opt, 0, &key, expected_len);
|
||||
printf("success: set ");
|
||||
_print_netopt(opt);
|
||||
printf(" on interface %" PRIkernel_pid " to %s\n", iface, key_str);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int _netif_set_addr(kernel_pid_t iface, netopt_t opt, char *addr_str)
|
||||
{
|
||||
uint8_t addr[GNRC_NETIF_L2ADDR_MAXLEN];
|
||||
@ -1040,6 +1129,27 @@ static int _netif_set(char *cmd_name, kernel_pid_t iface, char *key, char *value
|
||||
else if ((strcmp("coding_rate", key) == 0) || (strcmp("cr", key) == 0)) {
|
||||
return _netif_set_coding_rate(iface, value);
|
||||
}
|
||||
else if (strcmp("appeui", key) == 0) {
|
||||
return _netif_set_lw_key(iface, NETOPT_LORAWAN_APPEUI, value);
|
||||
}
|
||||
else if (strcmp("appkey", key) == 0) {
|
||||
return _netif_set_lw_key(iface, NETOPT_LORAWAN_APPKEY, value);
|
||||
}
|
||||
else if (strcmp("deveui", key) == 0) {
|
||||
return _netif_set_addr(iface, NETOPT_ADDRESS_LONG, value);
|
||||
}
|
||||
else if (strcmp("appskey", key) == 0) {
|
||||
return _netif_set_addr(iface, NETOPT_LORAWAN_APPSKEY, value);
|
||||
}
|
||||
else if (strcmp("nwkskey", key) == 0) {
|
||||
return _netif_set_addr(iface, NETOPT_LORAWAN_NWKSKEY, value);
|
||||
}
|
||||
else if (strcmp("dr", key) == 0) {
|
||||
return _netif_set_u8(iface, NETOPT_LORAWAN_DR, 0, value);
|
||||
}
|
||||
else if (strcmp("rx2_dr", key) == 0) {
|
||||
return _netif_set_u8(iface, NETOPT_LORAWAN_RX2_DR, 0, value);
|
||||
}
|
||||
#endif
|
||||
else if ((strcmp("channel", key) == 0) || (strcmp("chan", key) == 0)) {
|
||||
return _netif_set_u16(iface, NETOPT_CHANNEL, 0, value);
|
||||
|
Loading…
Reference in New Issue
Block a user