1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

gnrc_lorawan: remove GNRC specific dependencies

This commit is contained in:
Jose Alamos 2020-05-08 14:49:00 +02:00
parent 0162a16d82
commit 71bda3bcd8
10 changed files with 330 additions and 293 deletions

View File

@ -89,7 +89,6 @@ ifneq (,$(filter gnrc_lorawan,$(USEMODULE)))
USEMODULE += hashes
USEMODULE += crypto_aes
USEMODULE += netdev_layer
USEMODULE += gnrc_neterr
USEMODULE += gnrc_nettype_lorawan
endif

View File

@ -7,6 +7,7 @@ USEMODULE += gnrc_netdev_default
USEMODULE += auto_init_gnrc_netif
USEMODULE += gnrc_lorawan
USEMODULE += gnrc_pktdump
USEMODULE += gnrc_neterr
BOARD ?= b-l072z-lrwan1
RIOTBASE ?= ../../

View File

@ -35,7 +35,8 @@
#include "net/gnrc/pktdump.h"
#include "net/gnrc/netreg.h"
#define LORAWAN_PORT (2U)
#define LORAWAN_PORT (2U)
#define LORAWAN_QUEUE_SIZE (4U)
static void _usage(void)
{
@ -75,16 +76,17 @@ int tx_cmd(int argc, char **argv)
gnrc_netapi_set(interface, NETOPT_LORAWAN_TX_PORT, 0, &port, sizeof(port));
gnrc_netif_send(gnrc_netif_get_by_pid(interface), pkt);
msg_t msg;
/* wait for packet status and check */
msg_t msg;
msg_receive(&msg);
if ((msg.type != GNRC_NETERR_MSG_TYPE) ||
(msg.content.value != GNRC_NETERR_SUCCESS)) {
puts("Error sending packet (not joined?)");
printf("Error sending packet: (status: %d\n)", (int) msg.content.value);
}
else {
puts("Successfully sent packet");
}
return 0;
}

View File

@ -63,7 +63,6 @@ extern "C" {
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;
/**
@ -153,9 +152,9 @@ typedef struct {
* @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 */
iolist_t *msdu; /**< pointer to the msdu */
} mcps_confirm_t;
/**
@ -230,9 +229,11 @@ void gnrc_lorawan_mcps_request(gnrc_lorawan_t *mac, const mcps_request_t *mcps_r
* To be called on radio RX done event.
*
* @param[in] mac pointer to the MAC descriptor
* @param[in] pkt pointer to the packet
* @param[in] data pointer to the psdu. Pass NULL if the packet was wrong (or
* allocation failed)
* @param[in] size size of the PSDU
*/
void gnrc_lorawan_radio_rx_done_cb(gnrc_lorawan_t *mac, gnrc_pktsnip_t *pkt);
void gnrc_lorawan_radio_rx_done_cb(gnrc_lorawan_t *mac, uint8_t *data, size_t size);
/**
* @brief MCPS indication callback

View File

@ -175,10 +175,8 @@ void gnrc_lorawan_radio_rx_timeout_cb(gnrc_lorawan_t *mac)
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);
gnrc_lorawan_event_no_rx(mac);
mac->state = LORAWAN_STATE_IDLE;
gnrc_lorawan_mac_release(mac);
break;
default:
assert(false);
@ -222,57 +220,44 @@ static uint32_t lora_time_on_air(size_t payload_size, uint8_t dr, uint8_t cr)
return t_preamble + t_payload;
}
void gnrc_lorawan_send_pkt(gnrc_lorawan_t *mac, gnrc_pktsnip_t *pkt, uint8_t dr)
void gnrc_lorawan_send_pkt(gnrc_lorawan_t *mac, iolist_t *psdu, uint8_t dr)
{
netdev_t *dev = gnrc_lorawan_get_netdev(mac);
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;
dev->driver->get(dev, NETOPT_CODING_RATE, &cr, sizeof(cr));
mac->toa = lora_time_on_air(gnrc_pkt_len(pkt), dr, cr + 4);
mac->toa = lora_time_on_air(iolist_size(psdu), dr, cr + 4);
if (dev->driver->send(dev, &iolist) == -ENOTSUP) {
if (dev->driver->send(dev, psdu) == -ENOTSUP) {
DEBUG("gnrc_lorawan: Cannot send: radio is still transmitting");
}
}
void gnrc_lorawan_radio_rx_done_cb(gnrc_lorawan_t *mac, gnrc_pktsnip_t *pkt)
void gnrc_lorawan_radio_rx_done_cb(gnrc_lorawan_t *mac, uint8_t *psdu, size_t size)
{
_sleep_radio(mac);
if (pkt == NULL) {
if (psdu == NULL) {
return;
}
mac->state = LORAWAN_STATE_IDLE;
xtimer_remove(&mac->rx);
uint8_t *p = pkt->data;
uint8_t mtype = (*p & MTYPE_MASK) >> 5;
uint8_t mtype = (*psdu & MTYPE_MASK) >> 5;
switch (mtype) {
case MTYPE_JOIN_ACCEPT:
gnrc_lorawan_mlme_process_join(mac, pkt);
gnrc_lorawan_mlme_process_join(mac, psdu, size);
break;
case MTYPE_CNF_DOWNLINK:
case MTYPE_UNCNF_DOWNLINK:
gnrc_lorawan_mcps_process_downlink(mac, pkt);
gnrc_lorawan_mcps_process_downlink(mac, psdu, size);
break;
default:
gnrc_pktbuf_release(pkt);
break;
}
gnrc_lorawan_mac_release(mac);
}

View File

@ -45,20 +45,17 @@ typedef struct __attribute__((packed)) {
uint8_t len;
} lorawan_block_t;
void gnrc_lorawan_calculate_join_mic(const iolist_t *io, const uint8_t *key, le_uint32_t *out)
void gnrc_lorawan_calculate_join_mic(const uint8_t *buf, size_t len, 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_update(&CmacContext, buf, len);
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)
uint8_t dir, iolist_t *frame, const uint8_t *nwkskey, le_uint32_t *out)
{
lorawan_block_t block;
@ -72,11 +69,16 @@ void gnrc_lorawan_calculate_mic(const le_uint32_t *dev_addr, uint32_t fcnt,
block.u32_pad = 0;
block.len = iolist_size(pkt);
block.len = iolist_size(frame);
iolist_t io = { .iol_base = &block, .iol_len = sizeof(block),
.iol_next = pkt };
gnrc_lorawan_calculate_join_mic(&io, nwkskey, out);
cmac_init(&CmacContext, nwkskey, LORAMAC_APPKEY_LEN);
cmac_update(&CmacContext, &block, sizeof(block));
for (iolist_t *io = frame; io != NULL; io = io->iol_next) {
cmac_update(&CmacContext, io->iol_base, io->iol_len);
}
cmac_final(&CmacContext, digest);
memcpy(out, digest, sizeof(le_uint32_t));
}
void gnrc_lorawan_encrypt_payload(iolist_t *iolist, const le_uint32_t *dev_addr, uint32_t fcnt, uint8_t dir, const uint8_t *appskey)

View File

@ -30,17 +30,19 @@
#define _16_UPPER_BITMASK 0xFFFF0000
#define _16_LOWER_BITMASK 0xFFFF
int gnrc_lorawan_mic_is_valid(gnrc_pktsnip_t *mic, uint8_t *nwkskey)
static void _end_of_tx(gnrc_lorawan_t *mac, int type, int status);
static int gnrc_lorawan_mic_is_valid(uint8_t *buf, size_t len, 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;
lorawan_hdr_t *lw_hdr = (lorawan_hdr_t *) buf;
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;
iolist_t iol = {.iol_base = buf, .iol_len = len - MIC_SIZE, .iol_next = NULL};
gnrc_lorawan_calculate_mic(&lw_hdr->addr, fcnt, GNRC_LORAWAN_DIR_DOWNLINK,
&iol, nwkskey, &calc_mic);
return calc_mic.u32 == ((le_uint32_t *) (buf+len-MIC_SIZE))->u32;
}
uint32_t gnrc_lorawan_fcnt_stol(uint32_t fcnt_down, uint16_t s_fcnt)
@ -54,120 +56,139 @@ uint32_t gnrc_lorawan_fcnt_stol(uint32_t fcnt_down, uint16_t s_fcnt)
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;
/**
* @brief holder of parsed packet
*/
struct parsed_packet {
uint32_t fcnt_down; /**< frame counter */
lorawan_hdr_t *hdr; /**< pointer to the LoRaWAN header */
bool ack_req; /**< whether an ACK was requested or not */
iolist_t fopts; /**< iolist with Fopts information */
iolist_t enc_payload; /**< iolist with encrypted payload */
uint8_t port; /**< Fport of the packet */
bool ack; /**< whether the ACK bit was set or not */
bool frame_pending; /**< whether there's pending data or not */
};
/* 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;
int gnrc_lorawan_parse_dl(gnrc_lorawan_t *mac, uint8_t *buf, size_t len,
struct parsed_packet *pkt)
{
memset(pkt, 0, sizeof(struct parsed_packet));
lorawan_hdr_t *_hdr = (lorawan_hdr_t*) buf;
uint8_t *p_mic = buf + len - MIC_SIZE;
pkt->hdr = _hdr;
buf += sizeof(lorawan_hdr_t);
/* Validate header */
if (_hdr->addr.u32 != mac->dev_addr.u32) {
DEBUG("gnrc_lorawan: received packet with wrong dev addr. Drop\n");
return -1;
}
uint32_t _fcnt = gnrc_lorawan_fcnt_stol(mac->mcps.fcnt_down, _hdr->fcnt.u16);
if (mac->mcps.fcnt_down > _fcnt || mac->mcps.fcnt_down +
LORAMAC_DEFAULT_MAX_FCNT_GAP < _fcnt) {
DEBUG("gnrc_lorawan: wrong frame counter\n");
return -1;
}
pkt->fcnt_down = _fcnt;
int fopts_length = lorawan_hdr_get_frame_opts_len(_hdr);
if(fopts_length) {
pkt->fopts.iol_base = buf;
pkt->fopts.iol_len = fopts_length;
buf += fopts_length;
}
if(buf < p_mic) {
pkt->port = *(buf++);
if (!pkt->port && fopts_length) {
DEBUG("gnrc_lorawan: packet with fopts and port == 0. Drop\n");
return -1;
}
if (buf < p_mic) {
pkt->enc_payload.iol_base = buf;
pkt->enc_payload.iol_len = p_mic - buf;
}
}
pkt->ack_req = lorawan_hdr_get_mtype(_hdr) == MTYPE_CNF_DOWNLINK;
pkt->ack = lorawan_hdr_get_ack(_hdr);
pkt->frame_pending = lorawan_hdr_get_frame_pending(_hdr);
return 0;
}
void gnrc_lorawan_mcps_process_downlink(gnrc_lorawan_t *mac, uint8_t *psdu, size_t size)
{
struct parsed_packet _pkt;
/* NOTE: MIC is in pkt */
if (!gnrc_lorawan_mic_is_valid(pkt, mac->nwkskey)) {
if (!gnrc_lorawan_mic_is_valid(psdu, size, mac->nwkskey)) {
DEBUG("gnrc_lorawan: invalid MIC\n");
goto out;
gnrc_lorawan_event_no_rx(mac);
return;
}
/* 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;
if (gnrc_lorawan_parse_dl(mac, psdu, size, &_pkt) < 0) {
DEBUG("gnrc_lorawan: couldn't parse packet\n");
gnrc_lorawan_event_no_rx(mac);
return;
}
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;
iolist_t *fopts = NULL;
if(_pkt.fopts.iol_base) {
fopts = &_pkt.fopts;
}
assert(pkt != NULL);
int fopts_in_payload = 0;
/* only for download frames with payload the FPort must be present */
if (pkt->size) {
if ((fport = gnrc_pktbuf_mark(pkt, 1, GNRC_NETTYPE_UNDEF)) == NULL) {
DEBUG("gnrc_lorawan: failed to allocate fport\n");
goto out;
if(_pkt.enc_payload.iol_base) {
uint8_t *key;
if(_pkt.port) {
key = mac->appskey;
}
assert(fport->data);
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;
else {
key = mac->nwkskey;
fopts = &_pkt.enc_payload;
}
gnrc_lorawan_encrypt_payload(&_pkt.enc_payload, &_pkt.hdr->addr, byteorder_ntohs(byteorder_ltobs(_pkt.hdr->fcnt)), GNRC_LORAWAN_DIR_DOWNLINK, key);
}
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;
mac->mcps.fcnt_down = _pkt.fcnt_down;
if (mac->mcps.waiting_for_ack && !_pkt.ack) {
DEBUG("gnrc_lorawan: expected ACK packet\n");
gnrc_lorawan_event_no_rx(mac);
return;
}
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) {
if (_pkt.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);
DEBUG("gnrc_lorawan: processing fopts\n");
gnrc_lorawan_process_fopts(mac, fopts->iol_base, fopts->iol_len);
}
gnrc_lorawan_mcps_event(mac, MCPS_EVENT_RX, lorawan_hdr_get_ack(lw_hdr));
if (pkt->data && fport && *((uint8_t *) fport->data) != 0) {
pkt->type = GNRC_NETTYPE_LORAWAN;
release = false;
_end_of_tx(mac, MCPS_CONFIRMED, GNRC_LORAWAN_REQ_STATUS_SUCCESS);
mcps_indication_t mcps_indication;
mcps_indication.type = ack_req;
mcps_indication.data.pkt = pkt;
mcps_indication.data.port = *((uint8_t *) fport->data);
gnrc_lorawan_mcps_indication(mac, &mcps_indication);
}
if (lorawan_hdr_get_frame_pending(lw_hdr)) {
if (_pkt.frame_pending) {
mlme_indication_t mlme_indication;
mlme_indication.type = MLME_SCHEDULE_UPLINK;
gnrc_lorawan_mlme_indication(mac, &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);
if (_pkt.port) {
mcps_indication_t mcps_indication;
mcps_indication.type = _pkt.ack_req;
mcps_indication.data.pkt = &_pkt.enc_payload;
mcps_indication.data.port = _pkt.port;
gnrc_lorawan_mcps_indication(mac, &mcps_indication);
}
}
@ -193,95 +214,130 @@ size_t gnrc_lorawan_build_hdr(uint8_t mtype, le_uint32_t *dev_addr, uint32_t fcn
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)
size_t gnrc_lorawan_build_uplink(gnrc_lorawan_t *mac, iolist_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,
.data = (uint8_t *) mac->mcps.mhdr_mic,
.size = sizeof(mac->mcps.mhdr_mic),
.index = 0
};
lorawan_hdr_t *lw_hdr = (lorawan_hdr_t *) buf.data;
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);
lw_hdr->mt_maj = 0;
lorawan_hdr_set_mtype(lw_hdr, confirmed_data ? MTYPE_CNF_UPLINK : MTYPE_UNCNF_UPLINK);
lorawan_hdr_set_maj(lw_hdr, MAJOR_LRWAN_R1);
gnrc_lorawan_build_options(mac, &buf);
lw_hdr->addr = mac->dev_addr;
lw_hdr->fctrl = 0;
assert(buf.index == mac_hdr->size - 1);
lorawan_hdr_set_ack(lw_hdr, mac->mcps.ack_requested);
lw_hdr->fcnt = byteorder_btols(byteorder_htons(mac->mcps.fcnt));
buf.index += sizeof(lorawan_hdr_t);
int fopts_length = gnrc_lorawan_build_options(mac, &buf);
assert(fopts_length < 16);
lorawan_hdr_set_frame_opts_len(lw_hdr, fopts_length);
buf.data[buf.index++] = port;
gnrc_lorawan_encrypt_payload(payload, &mac->dev_addr, mac->mcps.fcnt, GNRC_LORAWAN_DIR_UPLINK, port ? mac->appskey : mac->nwkskey);
iolist_t iol = {.iol_base = buf.data, .iol_len = buf.index, .iol_next = payload};
gnrc_lorawan_calculate_mic(&mac->dev_addr, mac->mcps.fcnt, GNRC_LORAWAN_DIR_UPLINK,
(iolist_t *) mac_hdr, mac->nwkskey, mic->data);
&iol, mac->nwkskey, (le_uint32_t*) &buf.data[buf.index]);
LL_APPEND(payload, mic);
return mac_hdr;
return buf.index;
}
static void _end_of_tx(gnrc_lorawan_t *mac, int type, int status)
{
mlme_confirm_t mlme_confirm;
mcps_confirm_t mcps_confirm;
mac->mcps.waiting_for_ack = false;
mcps_confirm_t mcps_confirm;
mac->mcps.fcnt++;
gnrc_lorawan_mac_release(mac);
if (mac->mlme.pending_mlme_opts & GNRC_LORAWAN_MLME_OPTS_LINK_CHECK_REQ) {
mlme_confirm.type = MLME_LINK_CHECK;
mlme_confirm.status = -ETIMEDOUT;
mac->mlme.pending_mlme_opts &= ~GNRC_LORAWAN_MLME_OPTS_LINK_CHECK_REQ;
gnrc_lorawan_mlme_confirm(mac, &mlme_confirm);
}
mcps_confirm.type = type;
mcps_confirm.status = status;
mcps_confirm.msdu = mac->mcps.msdu;
mac->mcps.msdu = NULL;
gnrc_lorawan_mcps_confirm(mac, &mcps_confirm);
mac->mcps.fcnt += 1;
}
void gnrc_lorawan_mcps_event(gnrc_lorawan_t *mac, int event, int data)
static void _transmit_pkt(gnrc_lorawan_t *mac)
{
size_t mhdr_size = sizeof(lorawan_hdr_t) + 1 + lorawan_hdr_get_frame_opts_len((void*) mac->mcps.mhdr_mic);
iolist_t header = {.iol_base = mac->mcps.mhdr_mic, .iol_len = mhdr_size, .iol_next = mac->mcps.msdu};
iolist_t footer = {.iol_base = mac->mcps.mhdr_mic + header.iol_len, .iol_len = MIC_SIZE, .iol_next = NULL};
iolist_t *last_snip = mac->mcps.msdu;
while (last_snip->iol_next != NULL) {
last_snip = last_snip->iol_next;
}
last_snip->iol_next = &footer;
gnrc_lorawan_send_pkt(mac, &header, mac->last_dr);
/* cppcheck-suppress redundantAssignment
* (reason: cppcheck bug. The pointer is temporally modified to add a footer.
* The `gnrc_lorawan_send_pkt` function uses this hack to append
* the MIC independently of `gnrc_pktsnip_t` structures) */
last_snip->iol_next = NULL;
}
void gnrc_lorawan_event_ack_timeout(gnrc_lorawan_t *mac)
{
_transmit_pkt(mac);
}
static void _handle_retransmissions(gnrc_lorawan_t *mac)
{
if (mac->mcps.nb_trials-- == 0) {
_end_of_tx(mac, MCPS_CONFIRMED, -ETIMEDOUT);
} else {
mac->msg.type = MSG_TYPE_MCPS_ACK_TIMEOUT;
xtimer_set_msg(&mac->rx, 1000000 + random_uint32_range(0, 2000000), &mac->msg, thread_getpid());
}
}
void gnrc_lorawan_event_no_rx(gnrc_lorawan_t *mac)
{
mlme_confirm_t mlme_confirm;
if (mac->mlme.activation == MLME_ACTIVATION_NONE) {
/* This was a Join Request */
mlme_confirm.type = MLME_JOIN;
mlme_confirm.status = -ETIMEDOUT;
gnrc_lorawan_mac_release(mac);
gnrc_lorawan_mlme_confirm(mac, &mlme_confirm);
return;
}
if (event == MCPS_EVENT_ACK_TIMEOUT) {
gnrc_lorawan_send_pkt(mac, mac->mcps.outgoing_pkt, mac->last_dr);
/* Otherwise check if retransmission should be handled */
if (mac->mcps.waiting_for_ack) {
_handle_retransmissions(mac);
}
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());
}
_end_of_tx(mac, MCPS_UNCONFIRMED, GNRC_LORAWAN_REQ_STATUS_SUCCESS);
}
}
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;
iolist_t *pkt = mcps_request->data.pkt;
if (mac->mlme.activation == MLME_ACTIVATION_NONE) {
DEBUG("gnrc_lorawan_mcps: LoRaWAN not activated\n");
@ -305,39 +361,35 @@ void gnrc_lorawan_mcps_request(gnrc_lorawan_t *mac, const mcps_request_t *mcps_r
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;
}
uint8_t fopts_length = gnrc_lorawan_build_options(mac, NULL);
/* We don't include the port because `MACPayload` doesn't consider
* the MHDR...*/
size_t mac_payload_size = sizeof(lorawan_hdr_t) + fopts_length +
iolist_size(pkt);
if ((gnrc_pkt_len(pkt) - MIC_SIZE - 1) > gnrc_lorawan_region_mac_payload_max(mcps_request->data.dr)) {
if (mac_payload_size > gnrc_lorawan_region_mac_payload_max(mcps_request->data.dr)) {
mcps_confirm->status = -EMSGSIZE;
goto out;
}
release = false;
int waiting_for_ack = mcps_request->type == MCPS_CONFIRMED;
gnrc_lorawan_build_uplink(mac, pkt, waiting_for_ack, mcps_request->data.port);
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);
mac->mcps.msdu = pkt;
mac->last_dr = mcps_request->data.dr;
_transmit_pkt(mac);
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);
}
}
/** @} */

View File

@ -28,32 +28,24 @@
#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)
static void _build_join_req_pkt(uint8_t *appeui, uint8_t *deveui, uint8_t *appkey, uint8_t *dev_nonce, uint8_t *psdu)
{
gnrc_pktsnip_t *pkt = gnrc_pktbuf_add(NULL, NULL, sizeof(lorawan_join_request_t), GNRC_NETTYPE_UNDEF);
lorawan_join_request_t *hdr = (lorawan_join_request_t *) psdu;
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);
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);
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;
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;
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;
gnrc_lorawan_calculate_join_mic(psdu, JOIN_REQUEST_SIZE - MIC_SIZE, appkey, &hdr->mic);
}
static int gnrc_lorawan_send_join_request(gnrc_lorawan_t *mac, uint8_t *deveui,
@ -69,56 +61,54 @@ static int gnrc_lorawan_send_join_request(gnrc_lorawan_t *mac, uint8_t *deveui,
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;
}
uint8_t psdu[sizeof(lorawan_join_request_t)];
iolist_t pkt = {.iol_base = &psdu, .iol_len = sizeof(psdu), .iol_next = NULL};
_build_join_req_pkt(appeui, deveui, appkey, mac->mlme.dev_nonce, psdu);
/* 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);
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)
void gnrc_lorawan_mlme_process_join(gnrc_lorawan_t *mac, uint8_t *data, size_t size)
{
int status;
mlme_confirm_t mlme_confirm;
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) {
if (size != GNRC_LORAWAN_JOIN_ACCEPT_MAX_SIZE - CFLIST_SIZE &&
size != GNRC_LORAWAN_JOIN_ACCEPT_MAX_SIZE) {
status = -EBADMSG;
goto out;
}
/* Subtract 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,
uint8_t has_cflist = (size - 1) >= CFLIST_SIZE;
gnrc_lorawan_decrypt_join_accept(mac->appskey, data + 1,
has_cflist, out);
memcpy(((uint8_t *) pkt->data) + 1, out, pkt->size - 1);
memcpy(data + 1, out, 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);
le_uint32_t *expected_mic = (le_uint32_t *) (data + size - MIC_SIZE);
gnrc_lorawan_calculate_join_mic(data, size - MIC_SIZE, 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;
lorawan_join_accept_t *ja_hdr = (lorawan_join_accept_t *) 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;
@ -138,11 +128,10 @@ void gnrc_lorawan_mlme_process_join(gnrc_lorawan_t *mac, gnrc_pktsnip_t *pkt)
status = GNRC_LORAWAN_REQ_STATUS_SUCCESS;
out:
gnrc_pktbuf_release(pkt);
mlme_confirm_t mlme_confirm;
mlme_confirm.type = MLME_JOIN;
mlme_confirm.status = status;
gnrc_lorawan_mac_release(mac);
gnrc_lorawan_mlme_confirm(mac, &mlme_confirm);
}
@ -321,20 +310,3 @@ uint8_t gnrc_lorawan_build_options(gnrc_lorawan_t *mac, lorawan_buffer_t *buf)
return size;
}
void gnrc_lorawan_mlme_no_rx(gnrc_lorawan_t *mac)
{
mlme_confirm_t mlme_confirm;
mlme_confirm.status = -ETIMEDOUT;
if (mac->mlme.activation == MLME_ACTIVATION_NONE) {
mlme_confirm.type = MLME_JOIN;
gnrc_lorawan_mlme_confirm(mac, &mlme_confirm);
}
else if (mac->mlme.pending_mlme_opts & GNRC_LORAWAN_MLME_OPTS_LINK_CHECK_REQ) {
mlme_confirm.type = MLME_LINK_CHECK;
gnrc_lorawan_mlme_confirm(mac, &mlme_confirm);
mac->mlme.pending_mlme_opts &= ~GNRC_LORAWAN_MLME_OPTS_LINK_CHECK_REQ;
}
}

View File

@ -99,6 +99,17 @@ extern "C" {
#define GNRC_LORAWAN_NET_ID_SIZE (3U) /**< Net ID size */
#define GNRC_LORAWAN_DEV_NONCE_SIZE (2U) /**< Dev Nonce size */
#define GNRC_LORAWAN_FOPTS_MAX_SIZE (15U) /**< Maximum size of Fopts field */
#define GNRC_LORAWAN_FPORT_SIZE (1U) /**< Size of the Fport field */
/**
* @brief Size of the internal MHDR-MIC buffer
*/
#define MHDR_MIC_BUF_SIZE (sizeof(lorawan_hdr_t) + \
GNRC_LORAWAN_FOPTS_MAX_SIZE + \
GNRC_LORAWAN_FPORT_SIZE + \
MIC_SIZE)
/**
* @brief buffer helper for parsing and constructing LoRaWAN packets.
*/
@ -130,7 +141,7 @@ typedef struct {
* @brief MCPS data
*/
typedef struct {
gnrc_pktsnip_t *pkt; /**< packet of the request */
iolist_t *pkt; /**< packet of the request */
uint8_t port; /**< port of the request */
uint8_t dr; /**< datarate of the request */
} mcps_data_t;
@ -141,10 +152,11 @@ typedef struct {
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 */
iolist_t *msdu; /**< current MSDU */
int nb_trials; /**< holds the remaining number of retransmissions */
int ack_requested; /**< whether the network server requested an ACK */
int waiting_for_ack; /**< true if the MAC layer is waiting for an ACK */
char mhdr_mic[MHDR_MIC_BUF_SIZE]; /**< internal retransmissions buffer */
} gnrc_lorawan_mcps_t;
/**
@ -189,7 +201,7 @@ typedef struct {
*
* @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] iolist pointer to the MSDU frame
* @param[in] dev_addr device address
* @param[in] fcnt frame counter
* @param[in] dir direction of the packet (0 if uplink, 1 if downlink)
@ -243,7 +255,7 @@ int gnrc_lorawan_set_dr(gnrc_lorawan_t *mac, uint8_t datarate);
* @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);
size_t gnrc_lorawan_build_uplink(gnrc_lorawan_t *mac, iolist_t *payload, int confirmed_data, uint8_t port);
/**
* @brief pick a random available LoRaWAN channel
@ -277,11 +289,12 @@ 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] buf pointer to the frame
* @param[in] len length of the frame
* @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);
void gnrc_lorawan_calculate_join_mic(const uint8_t *buf, size_t len, const uint8_t *key, le_uint32_t *out);
/**
* @brief Calculate Message Integrity Code for a MCPS message
@ -289,13 +302,12 @@ void gnrc_lorawan_calculate_join_mic(const iolist_t *io, const uint8_t *key, le
* @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] frame pointer to the PSDU frame (witout MIC)
* @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);
uint8_t dir, iolist_t *frame, const uint8_t *nwkskey, le_uint32_t *out);
/**
* @brief Build a MCPS LoRaWAN header
*
@ -314,9 +326,10 @@ size_t gnrc_lorawan_build_hdr(uint8_t mtype, le_uint32_t *dev_addr, uint32_t fcn
* @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
* @param[in] psdu pointer to the downlink PSDU
* @param[in] size size of the PSDU
*/
void gnrc_lorawan_mcps_process_downlink(gnrc_lorawan_t *mac, gnrc_pktsnip_t *pkt);
void gnrc_lorawan_mcps_process_downlink(gnrc_lorawan_t *mac, uint8_t *psdu, size_t size);
/**
* @brief Init regional channel settings.
@ -340,18 +353,19 @@ 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] psdu the psdu frame 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);
void gnrc_lorawan_send_pkt(gnrc_lorawan_t *mac, iolist_t *psdu, uint8_t dr);
/**
* @brief Process join accept message
*
* @param[in] mac pointer to the MAC descriptor
* @param[in] pkt the Join Accept packet
* @param[in] data the Join Accept packet
* @param[in] size size of the Join Accept packet
*/
void gnrc_lorawan_mlme_process_join(gnrc_lorawan_t *mac, gnrc_pktsnip_t *pkt);
void gnrc_lorawan_mlme_process_join(gnrc_lorawan_t *mac, uint8_t *data, size_t size);
/**
* @brief Inform the MAC layer that no packet was received during reception.
@ -364,13 +378,18 @@ void gnrc_lorawan_mlme_process_join(gnrc_lorawan_t *mac, gnrc_pktsnip_t *pkt);
void gnrc_lorawan_mlme_no_rx(gnrc_lorawan_t *mac);
/**
* @brief Trigger a MCPS event
* @brief Mac callback for no RX
*
* @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);
void gnrc_lorawan_event_no_rx(gnrc_lorawan_t *mac);
/**
* @brief Mac callback for ACK timeout event
*
* @param[in] mac pointer to the MAC descriptor
*/
void gnrc_lorawan_event_ack_timeout(gnrc_lorawan_t *mac);
/**
* @brief Get the maximum MAC payload (M value) for a given datarate.
@ -400,7 +419,7 @@ void gnrc_lorawan_mlme_backoff_expire(gnrc_lorawan_t *mac);
* @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);
void gnrc_lorawan_process_pkt(gnrc_lorawan_t *mac, iolist_t *pkt);
/**
* @brief Open a reception window

View File

@ -85,9 +85,10 @@ static inline void _set_be_addr(gnrc_lorawan_t *mac, uint8_t *be_addr)
void gnrc_lorawan_mcps_indication(gnrc_lorawan_t *mac, mcps_indication_t *ind)
{
(void) mac;
gnrc_pktsnip_t *pkt = gnrc_pktbuf_add(NULL, ind->data.pkt->iol_base, ind->data.pkt->iol_len, GNRC_NETTYPE_LORAWAN);
if (!gnrc_netapi_dispatch_receive(GNRC_NETTYPE_LORAWAN, ind->data.port,
ind->data.pkt)) {
gnrc_pktbuf_release(ind->data.pkt);
pkt)) {
gnrc_pktbuf_release(pkt);
}
}
@ -99,13 +100,12 @@ void gnrc_lorawan_mlme_indication(gnrc_lorawan_t *mac, mlme_indication_t *ind)
void gnrc_lorawan_mcps_confirm(gnrc_lorawan_t *mac, mcps_confirm_t *confirm)
{
if (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;
(void)mac;
gnrc_pktbuf_release_error((gnrc_pktsnip_t *)confirm->msdu, confirm->status);
DEBUG("gnrc_lorawan: transmission finished with status %i\n",
confirm->status);
}
static void _rx_done(gnrc_lorawan_t *mac)
@ -116,20 +116,21 @@ static void _rx_done(gnrc_lorawan_t *mac)
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");
DEBUG("_recv_lorawan: cannot allocate pktsnip.\n");
/* Discard packet on netdev device */
dev->driver->recv(dev, NULL, bytes_expected, NULL);
gnrc_lorawan_radio_rx_done_cb(mac, NULL);
gnrc_lorawan_radio_rx_done_cb(mac, NULL, 0);
return;
}
nread = dev->driver->recv(dev, pkt->data, bytes_expected, &rx_info);
if (nread <= 0) {
gnrc_pktbuf_release(pkt);
gnrc_lorawan_radio_rx_done_cb(mac, NULL);
gnrc_lorawan_radio_rx_done_cb(mac, NULL, 0);
return;
}
gnrc_lorawan_radio_rx_done_cb(mac, pkt);
gnrc_lorawan_radio_rx_done_cb(mac, pkt->data, pkt->size);
gnrc_pktbuf_release(pkt);
}
static void _driver_cb(netdev_t *dev, netdev_event_t event)
@ -229,10 +230,13 @@ static int _send(gnrc_netif_t *netif, gnrc_pktsnip_t *payload)
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,
.data = { .pkt = (iolist_t*) payload, .port = netif->lorawan.port,
.dr = netif->lorawan.datarate } };
mcps_confirm_t conf;
gnrc_lorawan_mcps_request(&netif->lorawan.mac, &req, &conf);
if (conf.status < 0) {
gnrc_pktbuf_release_error(payload, conf.status);
}
return conf.status;
}
@ -245,7 +249,7 @@ static void _msg_handler(gnrc_netif_t *netif, msg_t *msg)
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);
gnrc_lorawan_event_ack_timeout(&netif->lorawan.mac);
break;
case MSG_TYPE_MLME_BACKOFF_EXPIRE:
gnrc_lorawan_mlme_backoff_expire(&netif->lorawan.mac);