From 5b76fdf46e9d03398a924927a76398990d5fcb4b Mon Sep 17 00:00:00 2001
From: Alexandre Abadie <alexandre.abadie@inria.fr>
Date: Mon, 19 Mar 2018 08:27:16 +0100
Subject: [PATCH] pkg/semtech-loramac: refactor API to make it thread-safe

---
 pkg/semtech-loramac/contrib/semtech_loramac.c | 344 ++++++++++--------
 .../contrib/semtech_loramac_getset.c          | 217 +++++++----
 .../contrib/semtech_loramac_timer.c           |   4 +
 pkg/semtech-loramac/include/semtech_loramac.h | 246 +++++++++----
 4 files changed, 533 insertions(+), 278 deletions(-)

diff --git a/pkg/semtech-loramac/contrib/semtech_loramac.c b/pkg/semtech-loramac/contrib/semtech_loramac.c
index 55dc026850..5212b30af7 100644
--- a/pkg/semtech-loramac/contrib/semtech_loramac.c
+++ b/pkg/semtech-loramac/contrib/semtech_loramac.c
@@ -28,6 +28,7 @@
 #include <string.h>
 
 #include "msg.h"
+#include "mutex.h"
 
 #include "net/netdev.h"
 #include "net/loramac.h"
@@ -63,30 +64,21 @@
 
 #define SEMTECH_LORAMAC_MSG_QUEUE                   (16U)
 #define SEMTECH_LORAMAC_LORAMAC_STACKSIZE           (THREAD_STACKSIZE_DEFAULT)
+static msg_t _semtech_loramac_msg_queue[SEMTECH_LORAMAC_MSG_QUEUE];
 static char _semtech_loramac_stack[SEMTECH_LORAMAC_LORAMAC_STACKSIZE];
 kernel_pid_t semtech_loramac_pid;
-kernel_pid_t semtech_loramac_handler_pid;
 
+sx127x_t sx127x;
 RadioEvents_t semtech_loramac_radio_events;
-uint8_t semtech_loramac_dev_eui[LORAMAC_DEVEUI_LEN];
-uint8_t semtech_loramac_app_eui[LORAMAC_APPEUI_LEN];
-uint8_t semtech_loramac_app_key[LORAMAC_APPKEY_LEN];
-uint8_t semtech_loramac_nwk_skey[LORAMAC_NWKSKEY_LEN];
-uint8_t semtech_loramac_app_skey[LORAMAC_APPSKEY_LEN];
-uint8_t semtech_loramac_dev_addr[LORAMAC_DEVADDR_LEN];
-
-static uint8_t _semtech_loramac_radio_payload[SX127X_RX_BUFFER_SIZE];
-static semtech_loramac_rx_data_t _semtech_loramac_rx_data;
+LoRaMacPrimitives_t semtech_loramac_primitives;
+LoRaMacCallback_t semtech_loramac_callbacks;
 
 typedef struct {
-    uint8_t port;
-    uint8_t cnf;
-    uint8_t dr;
     uint8_t *payload;
     uint8_t len;
 } loramac_send_params_t;
 
-typedef void (*semtech_loramac_func_t)(void *);
+typedef void (*semtech_loramac_func_t)(semtech_loramac_t *, void *);
 
 /**
  * @brief   Struct containing a semtech loramac function call
@@ -99,12 +91,13 @@ typedef struct {
 } semtech_loramac_call_t;
 
 /* Prepares the payload of the frame */
-static bool _semtech_loramac_send(uint8_t cnf, uint8_t port, uint8_t dr,
+static bool _semtech_loramac_send(semtech_loramac_t *mac,
                                   uint8_t *payload, uint8_t len)
 {
     DEBUG("[semtech-loramac] send frame %s\n", (char *)payload);
     McpsReq_t mcpsReq;
     LoRaMacTxInfo_t txInfo;
+    uint8_t dr = semtech_loramac_get_dr(mac);
 
     if (LoRaMacQueryTxPossible(len, &txInfo) != LORAMAC_STATUS_OK) {
         DEBUG("[semtech-loramac] empty frame in order to flush MAC commands\n");
@@ -115,10 +108,10 @@ static bool _semtech_loramac_send(uint8_t cnf, uint8_t port, uint8_t dr,
         mcpsReq.Req.Unconfirmed.Datarate = (int8_t)dr;
     }
     else {
-        if (cnf == LORAMAC_TX_UNCNF) {
+        if (mac->cnf == LORAMAC_TX_UNCNF) {
             DEBUG("[semtech-loramac] MCPS_UNCONFIRMED\n");
             mcpsReq.Type = MCPS_UNCONFIRMED;
-            mcpsReq.Req.Unconfirmed.fPort = port;
+            mcpsReq.Req.Unconfirmed.fPort = mac->port;
             mcpsReq.Req.Unconfirmed.fBuffer = payload;
             mcpsReq.Req.Unconfirmed.fBufferSize = len;
             mcpsReq.Req.Unconfirmed.Datarate = (int8_t)dr;
@@ -126,7 +119,7 @@ static bool _semtech_loramac_send(uint8_t cnf, uint8_t port, uint8_t dr,
         else {
             DEBUG("[semtech-loramac] MCPS_CONFIRMED\n");
             mcpsReq.Type = MCPS_CONFIRMED;
-            mcpsReq.Req.Confirmed.fPort = port;
+            mcpsReq.Req.Confirmed.fPort = mac->port;
             mcpsReq.Req.Confirmed.fBuffer = payload;
             mcpsReq.Req.Confirmed.fBufferSize = len;
             mcpsReq.Req.Confirmed.NbTrials = 3;
@@ -156,9 +149,8 @@ static void mcps_confirm(McpsConfirm_t *confirm)
                    Check TxPower */
                 DEBUG("[semtech-loramac] MCPS confirm event UNCONFIRMED\n");
                 msg_t msg;
-                msg.type = MSG_TYPE_LORAMAC_NOTIFY;
-                msg.content.value = SEMTECH_LORAMAC_TX_DONE;
-                msg_send(&msg, semtech_loramac_handler_pid);
+                msg.type = MSG_TYPE_LORAMAC_TX_DONE;
+                msg_send(&msg, semtech_loramac_pid);
                 break;
             }
 
@@ -214,28 +206,15 @@ static void mcps_indication(McpsIndication_t *indication)
     }
 
     msg_t msg;
-    msg.type = MSG_TYPE_LORAMAC_NOTIFY;
     if (indication->RxData) {
-        indication->Buffer[indication->BufferSize] = '\0';
-        memcpy(_semtech_loramac_rx_data.payload, indication->Buffer,
-               indication->BufferSize);
-        _semtech_loramac_rx_data.payload[indication->BufferSize] = 0;
-        _semtech_loramac_rx_data.payload_len = indication->BufferSize;
-        _semtech_loramac_rx_data.port = indication->Port;
-        DEBUG("[semtech-loramac] MCPS indication:\n"
-              "  - Payload: %s\n"
-              "  - Size: %d\n"
-              "  - Port: %d\n",
-              (char *)_semtech_loramac_rx_data.payload,
-              _semtech_loramac_rx_data.payload_len,
-              _semtech_loramac_rx_data.port
-              );
-        msg.content.value = SEMTECH_LORAMAC_RX_DATA;
+        DEBUG("[semtech-loramac] MCPS indication: data received\n");
+        msg.type = MSG_TYPE_LORAMAC_RX;
+        msg.content.ptr = indication;
     }
     else {
-        msg.content.value = SEMTECH_LORAMAC_TX_DONE;
+        msg.type = MSG_TYPE_LORAMAC_TX_DONE;
     }
-    msg_send(&msg, semtech_loramac_handler_pid);
+    msg_send(&msg, semtech_loramac_pid);
 }
 
 /*MLME-Confirm event function */
@@ -248,17 +227,17 @@ static void mlme_confirm(MlmeConfirm_t *confirm)
                 /* Status is OK, node has joined the network */
                 DEBUG("[semtech-loramac] join succeeded\n");
                 msg_t msg;
-                msg.type = MSG_TYPE_LORAMAC_NOTIFY;
+                msg.type = MSG_TYPE_LORAMAC_JOIN;
                 msg.content.value = SEMTECH_LORAMAC_JOIN_SUCCEEDED;
-                msg_send(&msg, semtech_loramac_handler_pid);
+                msg_send(&msg, semtech_loramac_pid);
             }
             else {
                 DEBUG("[semtech-loramac] join not successful\n");
                 /* Join was not successful. */
                 msg_t msg;
-                msg.type = MSG_TYPE_LORAMAC_NOTIFY;
+                msg.type = MSG_TYPE_LORAMAC_JOIN;
                 msg.content.value = SEMTECH_LORAMAC_JOIN_FAILED;
-                msg_send(&msg, semtech_loramac_handler_pid);
+                msg_send(&msg, semtech_loramac_pid);
             }
             break;
 
@@ -267,24 +246,10 @@ static void mlme_confirm(MlmeConfirm_t *confirm)
     }
 }
 
-void _loramac_set_rx2_params(uint32_t freq, uint8_t dr)
-{
-    Rx2ChannelParams_t params;
-    params.Frequency = freq;
-    params.Datarate = dr;
-
-    MibRequestConfirm_t mibReq;
-    mibReq.Type = MIB_RX2_DEFAULT_CHANNEL;
-    mibReq.Param.Rx2DefaultChannel = params;
-    LoRaMacMibSetRequestConfirm(&mibReq);
-
-    mibReq.Type = MIB_RX2_CHANNEL;
-    mibReq.Param.Rx2Channel = params;
-    LoRaMacMibSetRequestConfirm(&mibReq);
-}
-
-void _init_loramac(LoRaMacPrimitives_t * primitives, LoRaMacCallback_t *callbacks)
+void _init_loramac(semtech_loramac_t *mac,
+                   LoRaMacPrimitives_t * primitives, LoRaMacCallback_t *callbacks)
 {
+    mutex_lock(&mac->lock);
     DEBUG("[semtech-loramac] initializing loramac\n");
     primitives->MacMcpsConfirm = mcps_confirm;
     primitives->MacMcpsIndication = mcps_indication;
@@ -324,9 +289,11 @@ void _init_loramac(LoRaMacPrimitives_t * primitives, LoRaMacCallback_t *callback
 #else
 #error "Please define a region in the compiler options."
 #endif
+    mutex_unlock(&mac->lock);
 
 #if defined(REGION_EU868) && USE_SEMTECH_DEFAULT_CHANNEL_LINEUP
     DEBUG("[semtech-loramac] EU868 region: use default channels\n");
+    mutex_lock(&mac->lock);
     LoRaMacChannelAdd(3, (ChannelParams_t)LC4);
     LoRaMacChannelAdd(4, (ChannelParams_t)LC5);
     LoRaMacChannelAdd(5, (ChannelParams_t)LC6);
@@ -334,15 +301,25 @@ void _init_loramac(LoRaMacPrimitives_t * primitives, LoRaMacCallback_t *callback
     LoRaMacChannelAdd(7, (ChannelParams_t)LC8);
     LoRaMacChannelAdd(8, (ChannelParams_t)LC9);
     LoRaMacChannelAdd(9, (ChannelParams_t)LC10);
+    mutex_unlock(&mac->lock);
 
-    _loramac_set_rx2_params(LORAMAC_DEFAULT_RX2_FREQ, LORAMAC_DEFAULT_RX2_DR);
+    semtech_loramac_set_rx2_dr(mac, LORAMAC_DEFAULT_RX2_DR);
+    semtech_loramac_set_rx2_freq(mac, LORAMAC_DEFAULT_RX2_FREQ);
 #endif
+
+    semtech_loramac_set_dr(mac, LORAMAC_DEFAULT_DR);
+    semtech_loramac_set_adr(mac, LORAMAC_DEFAULT_ADR);
+    semtech_loramac_set_public_network(mac, LORAMAC_DEFAULT_PUBLIC_NETWORK);
+    semtech_loramac_set_class(mac, LORAMAC_DEFAULT_DEVICE_CLASS);
+    semtech_loramac_set_tx_port(mac, LORAMAC_DEFAULT_TX_PORT);
+    semtech_loramac_set_tx_mode(mac, LORAMAC_DEFAULT_TX_MODE);
 }
 
-static void _join_otaa(void)
+static void _join_otaa(semtech_loramac_t *mac)
 {
     DEBUG("[semtech-loramac] starting OTAA join\n");
 
+    mutex_lock(&mac->lock);
     MibRequestConfirm_t mibReq;
     mibReq.Type = MIB_NETWORK_JOINED;
     mibReq.Param.IsNetworkJoined = false;
@@ -350,65 +327,69 @@ static void _join_otaa(void)
 
     MlmeReq_t mlmeReq;
     mlmeReq.Type = MLME_JOIN;
-    mlmeReq.Req.Join.DevEui = semtech_loramac_dev_eui;
-    mlmeReq.Req.Join.AppEui = semtech_loramac_app_eui;
-    mlmeReq.Req.Join.AppKey = semtech_loramac_app_key;
+    mlmeReq.Req.Join.DevEui = mac->deveui;
+    mlmeReq.Req.Join.AppEui = mac->appeui;
+    mlmeReq.Req.Join.AppKey = mac->appkey;
     mlmeReq.Req.Join.NbTrials = LORAWAN_MAX_JOIN_RETRIES;
     LoRaMacMlmeRequest(&mlmeReq);
+    mutex_unlock(&mac->lock);
 }
 
-static void _join_abp(void)
+static void _join_abp(semtech_loramac_t *mac)
 {
     DEBUG("[semtech-loramac] starting ABP join\n");
 
+    semtech_loramac_set_netid(mac, LORAMAC_DEFAULT_NETID);
+
+    mutex_lock(&mac->lock);
     MibRequestConfirm_t mibReq;
     mibReq.Type = MIB_NETWORK_JOINED;
     mibReq.Param.IsNetworkJoined = false;
     LoRaMacMibSetRequestConfirm(&mibReq);
 
-    semtech_loramac_set_netid(LORAMAC_DEFAULT_NETID);
-
     mibReq.Type = MIB_DEV_ADDR;
-    mibReq.Param.DevAddr = ((uint32_t)semtech_loramac_dev_addr[0] << 24 |
-                            (uint32_t)semtech_loramac_dev_addr[1] << 16 |
-                            (uint32_t)semtech_loramac_dev_addr[2] << 8 |
-                            (uint32_t)semtech_loramac_dev_addr[3]);
+    mibReq.Param.DevAddr = ((uint32_t)mac->devaddr[0] << 24 |
+                            (uint32_t)mac->devaddr[1] << 16 |
+                            (uint32_t)mac->devaddr[2] << 8 |
+                            (uint32_t)mac->devaddr[3]);
     LoRaMacMibSetRequestConfirm(&mibReq);
 
     mibReq.Type = MIB_NWK_SKEY;
-    mibReq.Param.NwkSKey = semtech_loramac_nwk_skey;
+    mibReq.Param.NwkSKey = mac->nwkskey;
     LoRaMacMibSetRequestConfirm(&mibReq);
 
     mibReq.Type = MIB_APP_SKEY;
-    mibReq.Param.AppSKey = semtech_loramac_app_skey;
+    mibReq.Param.AppSKey = mac->appskey;
     LoRaMacMibSetRequestConfirm(&mibReq);
 
     mibReq.Type = MIB_NETWORK_JOINED;
     mibReq.Param.IsNetworkJoined = true;
     LoRaMacMibSetRequestConfirm(&mibReq);
+
+    /* switch back to idle state now*/
+    mac->state = SEMTECH_LORAMAC_STATE_IDLE;
+    mutex_unlock(&mac->lock);
 }
 
-static void _join(void *arg)
+static void _join(semtech_loramac_t *mac, void *arg)
 {
-    (void) arg;
     uint8_t join_type = *(uint8_t *)arg;
 
     switch (join_type) {
         case LORAMAC_JOIN_OTAA:
-            _join_otaa();
+            _join_otaa(mac);
             break;
 
         case LORAMAC_JOIN_ABP:
-            _join_abp();
+            _join_abp(mac);
             break;
     }
 }
 
-static void _send(void *arg)
+static void _send(semtech_loramac_t *mac, void *arg)
 {
     loramac_send_params_t params = *(loramac_send_params_t *)arg;
-    _semtech_loramac_send(params.cnf, params.port, params.dr,
-                          params.payload, params.len);
+    _semtech_loramac_send(mac, params.payload, params.len);
 }
 
 static void _semtech_loramac_call(semtech_loramac_func_t func, void *arg)
@@ -417,10 +398,10 @@ static void _semtech_loramac_call(semtech_loramac_func_t func, void *arg)
     call.func = func;
     call.arg = arg;
 
-    msg_t msg;
+    msg_t msg, msg_resp;
     msg.type = MSG_TYPE_LORAMAC_CMD;
     msg.content.ptr = &call;
-    msg_send(&msg, semtech_loramac_pid);
+    msg_send_receive(&msg, &msg_resp, semtech_loramac_pid);
 }
 
 static void _semtech_loramac_event_cb(netdev_t *dev, netdev_event_t event)
@@ -454,12 +435,12 @@ static void _semtech_loramac_event_cb(netdev_t *dev, netdev_event_t event)
         case NETDEV_EVENT_RX_COMPLETE:
         {
             size_t len;
+            uint8_t radio_payload[SX127X_RX_BUFFER_SIZE];
             len = dev->driver->recv(dev, NULL, 0, 0);
-            dev->driver->recv(dev, _semtech_loramac_radio_payload, len, &packet_info);
-            semtech_loramac_radio_events.RxDone(_semtech_loramac_radio_payload,
-                                                 len,
-                                                 packet_info.rssi,
-                                                 packet_info.snr);
+            dev->driver->recv(dev, radio_payload, len, &packet_info);
+            semtech_loramac_radio_events.RxDone(radio_payload,
+                                                len, packet_info.rssi,
+                                                packet_info.snr);
             break;
         }
         case NETDEV_EVENT_RX_TIMEOUT:
@@ -492,69 +473,105 @@ static void _semtech_loramac_event_cb(netdev_t *dev, netdev_event_t event)
 
 void *_semtech_loramac_event_loop(void *arg)
 {
-    (void) arg;
-    static msg_t _msg_q[SEMTECH_LORAMAC_MSG_QUEUE];
-    msg_init_queue(_msg_q, SEMTECH_LORAMAC_MSG_QUEUE);
-    LoRaMacPrimitives_t primitives;
-    LoRaMacCallback_t callbacks;
-
-    _init_loramac(&primitives, &callbacks);
-    semtech_loramac_set_dr(LORAMAC_DEFAULT_DR);
-    semtech_loramac_set_adr(LORAMAC_DEFAULT_ADR);
-    semtech_loramac_set_public_network(LORAMAC_DEFAULT_PUBLIC_NETWORK);
-    semtech_loramac_set_class(LORAMAC_DEFAULT_DEVICE_CLASS);
+    msg_init_queue(_semtech_loramac_msg_queue, SEMTECH_LORAMAC_MSG_QUEUE);
+    semtech_loramac_t *mac = (semtech_loramac_t *)arg;
 
     while (1) {
         msg_t msg;
         msg_receive(&msg);
-        switch (msg.type) {
-            case MSG_TYPE_ISR:
-            {
-                netdev_t *dev = msg.content.ptr;
-                dev->driver->isr(dev);
-                break;
-            }
-            case MSG_TYPE_RX_TIMEOUT:
-                DEBUG("[semtech-loramac] RX timer timeout\n");
-                semtech_loramac_radio_events.RxTimeout();
-                break;
-
-            case MSG_TYPE_TX_TIMEOUT:
-                DEBUG("[semtech-loramac] TX timer timeout\n");
-                semtech_loramac_radio_events.TxTimeout();
-                break;
-
-            case MSG_TYPE_MAC_TIMEOUT:
-            {
-                DEBUG("[semtech-loramac] MAC timer timeout\n");
-                void (*callback)(void) = msg.content.ptr;
-                callback();
-                break;
-            }
-            case MSG_TYPE_LORAMAC_CMD:
-            {
-                DEBUG("[semtech-loramac] loramac cmd\n");
-                semtech_loramac_call_t *call = msg.content.ptr;
-                call->func(call->arg);
-                break;
+        if (msg.type == MSG_TYPE_ISR) {
+            netdev_t *dev = msg.content.ptr;
+            dev->driver->isr(dev);
+        }
+        else {
+            switch (msg.type) {
+                case MSG_TYPE_RX_TIMEOUT:
+                    DEBUG("[semtech-loramac] RX timer timeout\n");
+                    semtech_loramac_radio_events.RxTimeout();
+                    break;
+
+                case MSG_TYPE_TX_TIMEOUT:
+                    DEBUG("[semtech-loramac] TX timer timeout\n");
+                    semtech_loramac_radio_events.TxTimeout();
+                    break;
+
+                case MSG_TYPE_MAC_TIMEOUT:
+                {
+                    DEBUG("[semtech-loramac] MAC timer timeout\n");
+                    void (*callback)(void) = msg.content.ptr;
+                    callback();
+                    break;
+                }
+                case MSG_TYPE_LORAMAC_CMD:
+                {
+                    msg_t msg_resp;
+                    DEBUG("[semtech-loramac] loramac cmd\n");
+                    mac->state = SEMTECH_LORAMAC_STATE_BUSY;
+                    semtech_loramac_call_t *call = msg.content.ptr;
+                    call->func(mac, call->arg);
+                    msg_reply(&msg, &msg_resp);
+                    break;
+                }
+                case MSG_TYPE_LORAMAC_JOIN:
+                {
+                    DEBUG("[semtech-loramac] loramac join notification\n");
+                    msg_t msg_ret;
+                    msg_ret.content.value = msg.content.value;
+                    msg_send(&msg_ret, mac->caller_pid);
+                    /* switch back to idle state now*/
+                    mac->state = SEMTECH_LORAMAC_STATE_IDLE;
+                    break;
+                }
+                case MSG_TYPE_LORAMAC_TX_DONE:
+                {
+                    DEBUG("[semtech-loramac] loramac TX done\n");
+                    msg_t msg_ret;
+                    msg_ret.type = MSG_TYPE_LORAMAC_TX_DONE;
+                    msg_send(&msg_ret, mac->caller_pid);
+                    /* switch back to idle state now*/
+                    mac->state = SEMTECH_LORAMAC_STATE_IDLE;
+                    break;
+                }
+                case MSG_TYPE_LORAMAC_RX:
+                {
+                    msg_t msg_ret;
+                    msg_ret.type = MSG_TYPE_LORAMAC_RX;
+                    McpsIndication_t *indication = (McpsIndication_t *)msg.content.ptr;
+                    memcpy(mac->rx_data.payload,
+                           indication->Buffer, indication->BufferSize);
+                    mac->rx_data.payload_len = indication->BufferSize;
+                    mac->rx_data.port = indication->Port;
+                    DEBUG("[semtech-loramac] loramac RX data:\n"
+                          "  - Payload: %s\n"
+                          "  - Size: %d\n"
+                          "  - Port: %d\n",
+                          (char *)mac->rx_data.payload,
+                          mac->rx_data.payload_len,
+                          mac->rx_data.port);
+                    msg_send(&msg_ret, mac->caller_pid);
+                    /* switch back to idle state now*/
+                    mac->state = SEMTECH_LORAMAC_STATE_IDLE;
+                    break;
+                }
+                default:
+                    DEBUG("[semtech-loramac] Unexpected msg type '%04x'\n",
+                          msg.type);
             }
-            default:
-                DEBUG("[semtech-loramac] Unexpected msg type '%04x'\n", msg.type);
         }
     }
 }
 
-int semtech_loramac_init(sx127x_t *dev)
+int semtech_loramac_init(semtech_loramac_t *mac)
 {
-    dev->netdev.driver = &sx127x_driver;
-    dev->netdev.event_callback = _semtech_loramac_event_cb;
+    sx127x_setup(&sx127x, &sx127x_params[0]);
+    sx127x.netdev.driver = &sx127x_driver;
+    sx127x.netdev.event_callback = _semtech_loramac_event_cb;
 
-    semtech_loramac_handler_pid = thread_getpid();
     semtech_loramac_pid = thread_create(_semtech_loramac_stack,
                                         sizeof(_semtech_loramac_stack),
                                         THREAD_PRIORITY_MAIN - 1,
                                         THREAD_CREATE_STACKTEST,
-                                        _semtech_loramac_event_loop, NULL,
+                                        _semtech_loramac_event_loop, mac,
                                         "recv_thread");
 
     if (semtech_loramac_pid <= KERNEL_PID_UNDEF) {
@@ -562,17 +579,29 @@ int semtech_loramac_init(sx127x_t *dev)
         return -1;
     }
 
+    _init_loramac(mac, &semtech_loramac_primitives, &semtech_loramac_callbacks);
+
     return 0;
 }
 
-uint8_t semtech_loramac_join(uint8_t type)
+uint8_t semtech_loramac_join(semtech_loramac_t *mac, uint8_t type)
 {
+    DEBUG("Starting join procedure: %d\n", type);
+
+    if (mac->state != SEMTECH_LORAMAC_STATE_IDLE) {
+        DEBUG("[semtech-loramac] internal mac is busy\n");
+        return SEMTECH_LORAMAC_BUSY;
+    }
+
+    mac->caller_pid = thread_getpid();
+
     _semtech_loramac_call(_join, &type);
 
     if (type == LORAMAC_JOIN_OTAA) {
         /* Wait until the OTAA join procedure is complete */
         msg_t msg;
         msg_receive(&msg);
+        mac->state = SEMTECH_LORAMAC_STATE_IDLE;
         return (uint8_t)msg.content.value;
     }
 
@@ -580,36 +609,47 @@ uint8_t semtech_loramac_join(uint8_t type)
     return SEMTECH_LORAMAC_JOIN_SUCCEEDED;
 }
 
-uint8_t semtech_loramac_send(uint8_t cnf, uint8_t port,
-                             uint8_t *tx_buf, uint8_t tx_len,
-                             semtech_loramac_rx_data_t *rx_data)
+uint8_t semtech_loramac_send(semtech_loramac_t *mac, uint8_t *data, uint8_t len)
 {
+    mutex_lock(&mac->lock);
     MibRequestConfirm_t mibReq;
     mibReq.Type = MIB_NETWORK_JOINED;
     LoRaMacMibGetRequestConfirm(&mibReq);
+    bool is_joined = mibReq.Param.IsNetworkJoined;
+    mutex_unlock(&mac->lock);
 
-    if (!mibReq.Param.IsNetworkJoined) {
+    if (!is_joined) {
         DEBUG("[semtech-loramac] network is not joined\n");
         return SEMTECH_LORAMAC_NOT_JOINED;
     }
 
+    if (mac->state != SEMTECH_LORAMAC_STATE_IDLE) {
+        DEBUG("[semtech-loramac] internal mac is busy\n");
+        return SEMTECH_LORAMAC_BUSY;
+    }
+
     loramac_send_params_t params;
-    params.cnf = cnf;
-    params.port = port;
-    params.dr = semtech_loramac_get_dr();
-    params.payload = tx_buf;
-    params.len = tx_len;
+    params.payload = data;
+    params.len = len;
 
     _semtech_loramac_call(_send, &params);
 
-    /* Wait until sending is fully done */
+    return SEMTECH_LORAMAC_TX_SCHEDULED;
+}
+
+uint8_t semtech_loramac_recv(semtech_loramac_t *mac)
+{
+    mac->caller_pid = thread_getpid();
+
+    /* Wait until the mac receive some information */
     msg_t msg;
     msg_receive(&msg);
-    uint8_t status = (uint8_t)msg.content.value;
-    if (status == SEMTECH_LORAMAC_RX_DATA) {
-        memcpy(rx_data, &_semtech_loramac_rx_data,
-               sizeof(semtech_loramac_rx_data_t));
+    uint8_t ret = SEMTECH_LORAMAC_TX_DONE;
+    if (msg.type == MSG_TYPE_LORAMAC_RX) {
+        ret = SEMTECH_LORAMAC_DATA_RECEIVED;
     }
 
-    return status;
+    DEBUG("[semtech-loramac] MAC reply received: %d\n", ret);
+
+    return ret;
 }
diff --git a/pkg/semtech-loramac/contrib/semtech_loramac_getset.c b/pkg/semtech-loramac/contrib/semtech_loramac_getset.c
index 6e1e418202..5659ffb5e6 100644
--- a/pkg/semtech-loramac/contrib/semtech_loramac_getset.c
+++ b/pkg/semtech-loramac/contrib/semtech_loramac_getset.c
@@ -19,6 +19,8 @@
 
 #include <string.h>
 
+#include "mutex.h"
+
 #include "net/loramac.h"
 
 #include "semtech-loramac/board.h"
@@ -27,217 +29,302 @@
 #define ENABLE_DEBUG (0)
 #include "debug.h"
 
-extern uint8_t semtech_loramac_dev_eui[LORAMAC_DEVEUI_LEN];
-extern uint8_t semtech_loramac_app_eui[LORAMAC_APPEUI_LEN];
-extern uint8_t semtech_loramac_app_key[LORAMAC_APPKEY_LEN];
-extern uint8_t semtech_loramac_nwk_skey[LORAMAC_NWKSKEY_LEN];
-extern uint8_t semtech_loramac_app_skey[LORAMAC_APPSKEY_LEN];
-extern uint8_t semtech_loramac_dev_addr[LORAMAC_DEVADDR_LEN];
-
-extern void _loramac_set_rx2_params(uint32_t freq, uint8_t dr);
-
-void semtech_loramac_set_deveui(const uint8_t *eui)
+void semtech_loramac_set_deveui(semtech_loramac_t *mac, const uint8_t *eui)
 {
-    memcpy(semtech_loramac_dev_eui, eui, LORAMAC_DEVEUI_LEN);
+    memcpy(mac->deveui, eui, LORAMAC_DEVEUI_LEN);
 }
 
-void semtech_loramac_get_deveui(uint8_t *eui)
+void semtech_loramac_get_deveui(const semtech_loramac_t *mac, uint8_t *eui)
 {
-    memcpy(eui, semtech_loramac_dev_eui, LORAMAC_DEVEUI_LEN);
+    memcpy(eui, mac->deveui, LORAMAC_DEVEUI_LEN);
 }
 
-void semtech_loramac_set_appeui(const uint8_t *eui)
+void semtech_loramac_set_appeui(semtech_loramac_t *mac, const uint8_t *eui)
 {
-    memcpy(semtech_loramac_app_eui, eui, LORAMAC_APPEUI_LEN);
+    memcpy(mac->appeui, eui, LORAMAC_APPEUI_LEN);
 }
 
-void semtech_loramac_get_appeui(uint8_t *eui)
+void semtech_loramac_get_appeui(const semtech_loramac_t *mac, uint8_t *eui)
 {
-    memcpy(eui, semtech_loramac_app_eui, LORAMAC_APPEUI_LEN);
+    memcpy(eui, mac->appeui, LORAMAC_APPEUI_LEN);
 }
 
-void semtech_loramac_set_appkey(const uint8_t *key)
+void semtech_loramac_set_appkey(semtech_loramac_t *mac, const uint8_t *key)
 {
-    memcpy(semtech_loramac_app_key, key, LORAMAC_APPKEY_LEN);
+    memcpy(mac->appkey, key, LORAMAC_APPKEY_LEN);
 }
 
-void semtech_loramac_get_appkey(uint8_t *key)
+void semtech_loramac_get_appkey(const semtech_loramac_t *mac, uint8_t *key)
 {
-    memcpy(key, semtech_loramac_app_key, LORAMAC_APPKEY_LEN);
+    memcpy(key, mac->appkey, LORAMAC_APPKEY_LEN);
 }
 
-void semtech_loramac_set_appskey(const uint8_t *skey)
+void semtech_loramac_set_appskey(semtech_loramac_t *mac, const uint8_t *skey)
 {
-    memcpy(semtech_loramac_app_skey, skey, LORAMAC_APPSKEY_LEN);
+    memcpy(mac->appskey, skey, LORAMAC_APPSKEY_LEN);
 }
 
-void semtech_loramac_get_appskey(uint8_t *skey)
+void semtech_loramac_get_appskey(const semtech_loramac_t *mac, uint8_t *skey)
 {
-    memcpy(skey, semtech_loramac_app_skey, LORAMAC_APPSKEY_LEN);
+    memcpy(skey, mac->appskey, LORAMAC_APPSKEY_LEN);
 }
 
-void semtech_loramac_set_nwkskey(const uint8_t *skey)
+void semtech_loramac_set_nwkskey(semtech_loramac_t *mac, const uint8_t *skey)
 {
-    memcpy(semtech_loramac_nwk_skey, skey, LORAMAC_NWKSKEY_LEN);
+    memcpy(mac->nwkskey, skey, LORAMAC_NWKSKEY_LEN);
 }
 
-void semtech_loramac_get_nwkskey(uint8_t *skey)
+void semtech_loramac_get_nwkskey(const semtech_loramac_t *mac, uint8_t *skey)
 {
-    memcpy(skey, semtech_loramac_nwk_skey, LORAMAC_NWKSKEY_LEN);
+    memcpy(skey, mac->nwkskey, LORAMAC_NWKSKEY_LEN);
 }
 
-void semtech_loramac_set_devaddr(const uint8_t *addr)
+void semtech_loramac_set_devaddr(semtech_loramac_t *mac, const uint8_t *addr)
 {
-    memcpy(semtech_loramac_dev_addr, addr, LORAMAC_DEVADDR_LEN);
+    memcpy(mac->devaddr, addr, LORAMAC_DEVADDR_LEN);
 }
 
-void semtech_loramac_get_devaddr(uint8_t *addr)
+void semtech_loramac_get_devaddr(const semtech_loramac_t *mac, uint8_t *addr)
 {
-    memcpy(addr, semtech_loramac_dev_addr, LORAMAC_DEVADDR_LEN);
+    memcpy(addr, mac->devaddr, LORAMAC_DEVADDR_LEN);
 }
 
-void semtech_loramac_set_class(loramac_class_t cls)
+void semtech_loramac_set_class(semtech_loramac_t *mac, loramac_class_t cls)
 {
+    mutex_lock(&mac->lock);
     DEBUG("[semtech-loramac] set class %d\n", cls);
     MibRequestConfirm_t mibReq;
     mibReq.Type = MIB_DEVICE_CLASS;
     mibReq.Param.Class = (DeviceClass_t)cls;
     LoRaMacMibSetRequestConfirm(&mibReq);
+    mutex_unlock(&mac->lock);
 }
 
-loramac_class_t semtech_loramac_get_class(void)
+loramac_class_t semtech_loramac_get_class(semtech_loramac_t *mac)
 {
+    mutex_lock(&mac->lock);
+    loramac_class_t cls;
     DEBUG("[semtech-loramac] get device class\n");
     MibRequestConfirm_t mibReq;
     mibReq.Type = MIB_DEVICE_CLASS;
     LoRaMacMibGetRequestConfirm(&mibReq);
-    return (loramac_class_t)mibReq.Param.Class;
+    cls = (loramac_class_t)mibReq.Param.Class;
+    mutex_unlock(&mac->lock);
+    return cls;
 }
 
-void semtech_loramac_set_dr(uint8_t dr)
+void semtech_loramac_set_dr(semtech_loramac_t *mac, uint8_t dr)
 {
+    mutex_lock(&mac->lock);
     DEBUG("[semtech-loramac] set dr %d\n", dr);
     MibRequestConfirm_t mibReq;
     mibReq.Type = MIB_CHANNELS_DEFAULT_DATARATE;
     mibReq.Param.ChannelsDatarate = dr;
     LoRaMacMibSetRequestConfirm(&mibReq);
+    mutex_unlock(&mac->lock);
 }
 
-uint8_t semtech_loramac_get_dr(void)
+uint8_t semtech_loramac_get_dr(semtech_loramac_t *mac)
 {
+    mutex_lock(&mac->lock);
     DEBUG("[semtech-loramac] get dr\n");
+    uint8_t datarate;
     MibRequestConfirm_t mibReq;
     mibReq.Type = MIB_CHANNELS_DEFAULT_DATARATE;
     LoRaMacMibGetRequestConfirm(&mibReq);
-    return (uint8_t)mibReq.Param.ChannelsDatarate;
+    datarate = (uint8_t)mibReq.Param.ChannelsDatarate;
+    mutex_unlock(&mac->lock);
+    return datarate;
 }
 
-void semtech_loramac_set_adr(bool adr)
+void semtech_loramac_set_adr(semtech_loramac_t *mac, bool adr)
 {
+    mutex_lock(&mac->lock);
     DEBUG("[semtech-loramac] set adr %d\n", adr);
     MibRequestConfirm_t mibReq;
     mibReq.Type = MIB_ADR;
     mibReq.Param.AdrEnable = adr;
     LoRaMacMibSetRequestConfirm(&mibReq);
+    mutex_unlock(&mac->lock);
 }
 
-bool semtech_loramac_get_adr(void)
+bool semtech_loramac_get_adr(semtech_loramac_t *mac)
 {
+    mutex_lock(&mac->lock);
+    bool enable;
     DEBUG("[semtech-loramac] get adr\n");
     MibRequestConfirm_t mibReq;
     mibReq.Type = MIB_ADR;
     LoRaMacMibGetRequestConfirm(&mibReq);
-    return mibReq.Param.AdrEnable;
+    enable = mibReq.Param.AdrEnable;
+    mutex_unlock(&mac->lock);
+    return enable;
 }
 
-void semtech_loramac_set_public_network(bool public)
+void semtech_loramac_set_public_network(semtech_loramac_t *mac, bool public)
 {
+    mutex_lock(&mac->lock);
     DEBUG("[semtech-loramac] set public network %d\n", public);
     MibRequestConfirm_t mibReq;
     mibReq.Type = MIB_PUBLIC_NETWORK;
     mibReq.Param.EnablePublicNetwork = public;
     LoRaMacMibSetRequestConfirm(&mibReq);
+    mutex_unlock(&mac->lock);
 }
 
-bool semtech_loramac_get_public_network(void)
+bool semtech_loramac_get_public_network(semtech_loramac_t *mac)
 {
+    mutex_lock(&mac->lock);
+    bool enable;
     DEBUG("[semtech-loramac] get public network\n");
     MibRequestConfirm_t mibReq;
     mibReq.Type = MIB_PUBLIC_NETWORK;
     LoRaMacMibGetRequestConfirm(&mibReq);
-    return mibReq.Param.EnablePublicNetwork;
+    enable = mibReq.Param.EnablePublicNetwork;
+    mutex_unlock(&mac->lock);
+    return enable;
 }
 
-void semtech_loramac_set_netid(uint32_t netid)
+void semtech_loramac_set_netid(semtech_loramac_t *mac, uint32_t netid)
 {
+    mutex_lock(&mac->lock);
     DEBUG("[semtech-loramac] set NetID %lu\n", netid);
     MibRequestConfirm_t mibReq;
     mibReq.Type = MIB_NET_ID;
     mibReq.Param.NetID = netid;
     LoRaMacMibSetRequestConfirm(&mibReq);
+    mutex_unlock(&mac->lock);
 }
 
-uint32_t semtech_loramac_get_netid(void)
+uint32_t semtech_loramac_get_netid(semtech_loramac_t *mac)
 {
+    mutex_lock(&mac->lock);
+    uint32_t netid;
     DEBUG("[semtech-loramac] get NetID\n");
     MibRequestConfirm_t mibReq;
     mibReq.Type = MIB_NET_ID;
     LoRaMacMibGetRequestConfirm(&mibReq);
-    return mibReq.Param.NetID;
+    netid = mibReq.Param.NetID;
+    mutex_unlock(&mac->lock);
+    return netid;
 }
 
-void semtech_loramac_set_tx_power(uint8_t power)
+void semtech_loramac_set_tx_power(semtech_loramac_t *mac, uint8_t power)
 {
+    mutex_lock(&mac->lock);
     DEBUG("[semtech-loramac] set TX power %d\n", power);
     MibRequestConfirm_t mibReq;
     mibReq.Type = MIB_CHANNELS_TX_POWER;
     mibReq.Param.ChannelsTxPower = power;
     LoRaMacMibSetRequestConfirm(&mibReq);
+    mutex_unlock(&mac->lock);
 }
 
-uint8_t semtech_loramac_get_tx_power(void)
+uint8_t semtech_loramac_get_tx_power(semtech_loramac_t *mac)
 {
+    mutex_lock(&mac->lock);
+    uint8_t tx_power;
     DEBUG("[semtech-loramac] get TX power\n");
     MibRequestConfirm_t mibReq;
     mibReq.Type = MIB_CHANNELS_TX_POWER;
     LoRaMacMibGetRequestConfirm(&mibReq);
-    return (uint8_t)mibReq.Param.ChannelsTxPower;
+    tx_power = (uint8_t)mibReq.Param.ChannelsTxPower;
+    mutex_unlock(&mac->lock);
+    return tx_power;
+}
+
+void semtech_loramac_set_tx_port(semtech_loramac_t *mac, uint8_t port)
+{
+    mac->port = port;
+}
+
+uint8_t semtech_loramac_get_tx_port(semtech_loramac_t *mac)
+{
+    return mac->port;
+}
+
+void semtech_loramac_set_tx_mode(semtech_loramac_t *mac, uint8_t mode)
+{
+    mac->cnf = mode;
+}
+
+uint8_t semtech_loramac_get_tx_mode(semtech_loramac_t *mac)
+{
+    return mac->cnf;
+}
+
+static void _semtech_loramac_set_rx2_params(semtech_loramac_channel_params_t params)
+{
+    Rx2ChannelParams_t p;
+    p.Frequency = params.frequency;
+    p.Datarate = params.datarate;
+
+    MibRequestConfirm_t mibReq;
+    mibReq.Type = MIB_RX2_DEFAULT_CHANNEL;
+    mibReq.Param.Rx2DefaultChannel = p;
+    LoRaMacMibSetRequestConfirm(&mibReq);
+
+    mibReq.Type = MIB_RX2_CHANNEL;
+    mibReq.Param.Rx2Channel = p;
+    LoRaMacMibSetRequestConfirm(&mibReq);
 }
 
-void semtech_loramac_set_rx2_freq(uint8_t freq)
+void semtech_loramac_set_rx2_freq(semtech_loramac_t *mac, uint32_t freq)
 {
-    Rx2ChannelParams_t params;
+    mutex_lock(&mac->lock);
+    DEBUG("[semtech-loramac] setting RX2 freq to %lu\n", freq);
+    Rx2ChannelParams_t p;
     MibRequestConfirm_t mibReq;
     mibReq.Type = MIB_RX2_DEFAULT_CHANNEL;
     LoRaMacMibGetRequestConfirm(&mibReq);
-    params.Frequency = freq;
-    params.Datarate = mibReq.Param.Rx2DefaultChannel.Datarate;
-    _loramac_set_rx2_params(params.Frequency, params.Datarate);
+    p.Frequency = freq;
+    p.Datarate = mibReq.Param.Rx2DefaultChannel.Datarate;
+    semtech_loramac_channel_params_t params;
+    params.frequency = freq;
+    params.datarate = p.Datarate;
+    _semtech_loramac_set_rx2_params(params);
+    mutex_unlock(&mac->lock);
 }
 
-uint32_t semtech_loramac_get_rx2_freq(void)
+uint32_t semtech_loramac_get_rx2_freq(semtech_loramac_t *mac)
 {
+    mutex_lock(&mac->lock);
+    uint32_t freq;
+    DEBUG("[semtech-loramac] getting RX2 freq\n");
     MibRequestConfirm_t mibReq;
     mibReq.Type = MIB_RX2_DEFAULT_CHANNEL;
     LoRaMacMibGetRequestConfirm(&mibReq);
-    return mibReq.Param.Rx2DefaultChannel.Frequency;
+    freq = mibReq.Param.Rx2DefaultChannel.Frequency;
+    mutex_unlock(&mac->lock);
+    return freq;
 }
 
-void semtech_loramac_set_rx2_dr(uint8_t dr)
+void semtech_loramac_set_rx2_dr(semtech_loramac_t *mac, uint8_t dr)
 {
-    Rx2ChannelParams_t params;
+    mutex_lock(&mac->lock);
+    DEBUG("[semtech-loramac] setting RX2 datarate to %d\n", dr);
+    Rx2ChannelParams_t p;
     MibRequestConfirm_t mibReq;
     mibReq.Type = MIB_RX2_DEFAULT_CHANNEL;
     LoRaMacMibGetRequestConfirm(&mibReq);
-    params.Datarate = dr;
-    params.Frequency = mibReq.Param.Rx2DefaultChannel.Frequency;
-    _loramac_set_rx2_params(params.Frequency, params.Datarate);
+    p.Datarate = dr;
+    p.Frequency = mibReq.Param.Rx2DefaultChannel.Frequency;
+    semtech_loramac_channel_params_t params;
+    params.datarate = dr;
+    params.frequency = p.Frequency;
+    _semtech_loramac_set_rx2_params(params);
+    mutex_unlock(&mac->lock);
 }
 
-uint8_t semtech_loramac_get_rx2_dr(void)
+uint8_t semtech_loramac_get_rx2_dr(semtech_loramac_t *mac)
 {
+    mutex_lock(&mac->lock);
+    uint8_t datarate;
+    DEBUG("[semtech-loramac] getting RX2 datarate\n");
     MibRequestConfirm_t mibReq;
     mibReq.Type = MIB_RX2_DEFAULT_CHANNEL;
     LoRaMacMibGetRequestConfirm(&mibReq);
-    return mibReq.Param.Rx2DefaultChannel.Datarate;
+    datarate = mibReq.Param.Rx2DefaultChannel.Datarate;
+    mutex_unlock(&mac->lock);
+    return datarate;
 }
diff --git a/pkg/semtech-loramac/contrib/semtech_loramac_timer.c b/pkg/semtech-loramac/contrib/semtech_loramac_timer.c
index 885e16ca53..0c62722c7d 100644
--- a/pkg/semtech-loramac/contrib/semtech_loramac_timer.c
+++ b/pkg/semtech-loramac/contrib/semtech_loramac_timer.c
@@ -60,6 +60,10 @@ void TimerSetValue(TimerEvent_t *obj, uint32_t value)
         xtimer_remove(&(obj->dev));
     }
 
+    /* According to the lorawan specifications, the data sent from the gateway
+       could arrive with a short shift in time of +/- 20ms. Here the timeout is
+       triggered 50ms in advance to make sure the radio switches to RX mode on
+       time and doesn't miss any downlink messages. */
     obj->timeout = (value - 50) * 1000;
 }
 
diff --git a/pkg/semtech-loramac/include/semtech_loramac.h b/pkg/semtech-loramac/include/semtech_loramac.h
index 66897dac0b..321d626d32 100644
--- a/pkg/semtech-loramac/include/semtech_loramac.h
+++ b/pkg/semtech-loramac/include/semtech_loramac.h
@@ -24,6 +24,10 @@ extern "C" {
 #endif
 
 #include <inttypes.h>
+
+#include "mutex.h"
+
+#include "net/netdev.h"
 #include "net/loramac.h"
 
 #include "sx127x.h"
@@ -37,7 +41,9 @@ extern "C" {
 #define MSG_TYPE_TX_TIMEOUT            (0x3458)  /**< radio driver TX timeout */
 #define MSG_TYPE_MAC_TIMEOUT           (0x3459)  /**< MAC timers timeout */
 #define MSG_TYPE_LORAMAC_CMD           (0x3460)  /**< Command sent to the MAC */
-#define MSG_TYPE_LORAMAC_NOTIFY        (0x3461)  /**< MAC notifications */
+#define MSG_TYPE_LORAMAC_JOIN          (0x3461)  /**< MAC join event */
+#define MSG_TYPE_LORAMAC_TX_DONE       (0x3462)  /**< MAC TX completes */
+#define MSG_TYPE_LORAMAC_RX            (0x3463)  /**< Some data received */
 /** @} */
 
 /**
@@ -46,16 +52,34 @@ extern "C" {
 #define LORAWAN_APP_DATA_MAX_SIZE      (242U)
 
 /**
- * @brief   LoRaMAC status
+ * @brief   LoRaMAC return status
  */
 enum {
     SEMTECH_LORAMAC_JOIN_SUCCEEDED,              /**< Join procedure succeeded */
     SEMTECH_LORAMAC_JOIN_FAILED,                 /**< Join procedure failed */
     SEMTECH_LORAMAC_NOT_JOINED,                  /**< MAC is not joined */
+    SEMTECH_LORAMAC_TX_SCHEDULED,                /**< TX data scheduled */
     SEMTECH_LORAMAC_TX_DONE,                     /**< Transmission completed */
-    SEMTECH_LORAMAC_RX_DATA,                     /**< Data received */
+    SEMTECH_LORAMAC_DATA_RECEIVED,               /**< Data received */
+    SEMTECH_LORAMAC_BUSY                         /**< Internal MAC is busy */
+};
+
+/**
+ * @brief   LoRaMAC internal state
+ */
+enum {
+    SEMTECH_LORAMAC_STATE_IDLE = 0,
+    SEMTECH_LORAMAC_STATE_BUSY
 };
 
+/**
+ * @brief   LoRaMAC channel radio parameters
+ */
+typedef struct {
+    uint32_t frequency;                          /**< channel center frequency */
+    uint8_t datarate;                            /**< channel datarate */
+} semtech_loramac_channel_params_t;
+
 /**
  * @brief   Structure containing LoRaWAN RX data
  */
@@ -66,237 +90,337 @@ typedef struct {
 } semtech_loramac_rx_data_t;
 
 /**
- * @brief   Initializes semtech loramac
+ * @brief   Semtech LoRaMAC descriptor
+ */
+typedef struct {
+    mutex_t lock;                                /**< loramac access lock */
+    uint8_t state;                               /**< internal loramac state */
+    uint8_t caller_pid;                          /**< pid of caller thread */
+    uint8_t port;                                /**< application TX port */
+    uint8_t cnf;                                 /**< enable/disable confirmable messages */
+    uint8_t deveui[LORAMAC_DEVEUI_LEN];          /**< device EUI */
+    uint8_t appeui[LORAMAC_APPEUI_LEN];          /**< application EUI */
+    uint8_t appkey[LORAMAC_APPKEY_LEN];          /**< application key */
+    uint8_t appskey[LORAMAC_APPSKEY_LEN];        /**< application session key */
+    uint8_t nwkskey[LORAMAC_NWKSKEY_LEN];        /**< network session key */
+    uint8_t devaddr[LORAMAC_DEVADDR_LEN];        /**< device address */
+    semtech_loramac_rx_data_t rx_data;           /**< struct handling the RX data */
+} semtech_loramac_t;
+
+/**
+ * @brief   Initializes the semtech loramac mac
  *
- * @param[in] dev          pointer to the radio device
+ * @param[in] mac          Pointer to loramac descriptor
  *
  * @return  0 on success
  * @return -1 on failure
  */
-int semtech_loramac_init(sx127x_t *dev);
+int semtech_loramac_init(semtech_loramac_t *mac);
 
 /**
  * @brief   Starts a LoRaWAN network join procedure
  *
+ * This function blocks until the join procedure succeeds or fails.
+ *
+ * @param[in] mac          Pointer to the mac
  * @param[in] type         The type of join procedure (otaa or abp)
  *
  * @return SEMTECH_LORAMAC_JOIN_SUCCEEDED on success
  * @return SEMTECH_LORAMAC_JOIN_FAILED on failure
+ * @return SEMTECH_LORAMAC_BUSY when the mac is already active (join or tx in progress)
  */
-uint8_t semtech_loramac_join(uint8_t type);
+uint8_t semtech_loramac_join(semtech_loramac_t *mac, uint8_t type);
 
 /**
- * @brief   Sends data to LoRaWAN
+ * @brief   Sends data to the LoRaWAN network
+ *
+ * This function returns immediately and leave the mac in busy state until a
+ * message is received from the network (with RX1 and RX2 receive windows).
+ * @see semtech_loramac_recv
  *
- * @param[in] cnf          Use confirmable/unconfirmable send type
- * @param[in] port         The send port to use (between 1 and 223)
- * @param[in] tx_buf       The TX buffer
- * @param[in] tx_len       The length of the TX buffer
- * @param[out] rx_data     The RX data descriptor
+ * @param[in] mac          Pointer to the mac
+ * @param[in] data         The TX data
+ * @param[in] len          The length of the TX data
  *
  * @return SEMTECH_LORAMAC_NOT_JOINED when the network is not joined
+ * @return SEMTECH_LORAMAC_BUSY when the mac is already active (join or tx in progress)
+ * @return SEMTECH_LORAMAC_TX_SCHEDULED when the TX is scheduled in the mac
+ */
+uint8_t semtech_loramac_send(semtech_loramac_t *mac, uint8_t *data, uint8_t len);
+
+/**
+ * @brief   Wait for a message sent by the LoRaWAN network
+ *
+ * This function blocks until a single message is received by the mac (RX1 and
+ * RX2 windows).
+ * With a class A device, a message can only be received after a send. With a
+ * class C device, a message can be received at any time. In this case, this
+ * function can be used in a dedicated listener thread.
+ *
+ * @see semtech_loramac_send
+ *
+ * @param[in] mac          Pointer to the mac
+ *
  * @return SEMTECH_LORAMAC_TX_DONE when TX has completed, no data received
- * @return SEMTECH_LORAMAC_RX_DATA when TX has completed and data is received
+ * @return SEMTECH_LORAMAC_DATA_RECEIVED when TX has completed and data is received
  */
-uint8_t semtech_loramac_send(uint8_t cnf, uint8_t port,
-                             uint8_t *tx_buf, uint8_t tx_len,
-                             semtech_loramac_rx_data_t *rx_data);
+uint8_t semtech_loramac_recv(semtech_loramac_t *mac);
 
 /**
  * @brief   Sets the device EUI
  *
- * @param[in] eui           The device EUI
+ * @param[in] mac          Pointer to the mac
+ * @param[in] eui          The device EUI
  */
-void semtech_loramac_set_deveui(const uint8_t *eui);
+void semtech_loramac_set_deveui(semtech_loramac_t *mac, const uint8_t *eui);
 
 /**
  * @brief   Gets the device EUI
  *
- * @param[out] eui           The device EUI
+ * @param[in] mac          Pointer to the mac
+ * @param[out] eui         The device EUI
  */
-void semtech_loramac_get_deveui(uint8_t *eui);
+void semtech_loramac_get_deveui(const semtech_loramac_t *mac, uint8_t *eui);
 
 /**
  * @brief   Sets the application EUI
  *
- * @param[in] eui           The application EUI
+ * @param[in] mac          Pointer to the mac
+ * @param[in] eui          The application EUI
  */
-void semtech_loramac_set_appeui(const uint8_t *eui);
+void semtech_loramac_set_appeui(semtech_loramac_t *mac, const uint8_t *eui);
 
 /**
  * @brief   Gets the application EUI
  *
- * @param[out] eui           The application EUI
+ * @param[in] mac          Pointer to the mac
+ * @param[out] eui         The application EUI
  */
-void semtech_loramac_get_appeui(uint8_t *eui);
+void semtech_loramac_get_appeui(const semtech_loramac_t *mac, uint8_t *eui);
 
 /**
  * @brief   Sets the application key
  *
- * @param[in] key           The application key
+ * @param[in] mac          Pointer to the mac
+ * @param[in] key          The application key
  */
-void semtech_loramac_set_appkey(const uint8_t *key);
+void semtech_loramac_set_appkey(semtech_loramac_t *mac, const uint8_t *key);
 
 /**
  * @brief   Gets the application key
  *
- * @param[in] key           The application key
+ * @param[in] mac          Pointer to the mac
+ * @param[in] key          The application key
  */
-void semtech_loramac_get_appkey(uint8_t *key);
+void semtech_loramac_get_appkey(const semtech_loramac_t *mac, uint8_t *key);
 
 /**
  * @brief   Sets the application session key
  *
- * @param[in] skey          The application session key
+ * @param[in] mac          Pointer to the mac
+ * @param[in] skey         The application session key
  */
-void semtech_loramac_set_appskey(const uint8_t *skey);
+void semtech_loramac_set_appskey(semtech_loramac_t *mac, const uint8_t *skey);
 
 /**
  * @brief   Gets the application session key
  *
- * @param[in] skey          The application session key
+ * @param[in] mac          Pointer to the mac
+ * @param[in] skey         The application session key
  */
-void semtech_loramac_get_appskey(uint8_t *skey);
+void semtech_loramac_get_appskey(const semtech_loramac_t *mac, uint8_t *skey);
 
 /**
  * @brief   Sets the network session key
  *
- * @param[in] skey          The network session key
+ * @param[in] mac          Pointer to the mac
+ * @param[in] skey         The network session key
  */
-void semtech_loramac_set_nwkskey(const uint8_t *skey);
+void semtech_loramac_set_nwkskey(semtech_loramac_t *mac, const uint8_t *skey);
 
 /**
  * @brief   Gets the network session key
  *
- * @param[in] skey          The network session key
+ * @param[in] mac          Pointer to the mac
+ * @param[in] skey         The network session key
  */
-void semtech_loramac_get_nwkskey(uint8_t *skey);
+void semtech_loramac_get_nwkskey(const semtech_loramac_t *mac, uint8_t *skey);
 
 /**
  * @brief   Sets the device address
  *
- * @param[in] addr          The device address
+ * @param[in] mac          Pointer to the mac
+ * @param[in] addr         The device address
  */
-void semtech_loramac_set_devaddr(const uint8_t *addr);
+void semtech_loramac_set_devaddr(semtech_loramac_t *mac, const uint8_t *addr);
 
 /**
  * @brief   Gets the device address
  *
- * @param[in] addr          The device address
+ * @param[in] mac          Pointer to the mac
+ * @param[in] addr         The device address
  */
-void semtech_loramac_get_devaddr(uint8_t *addr);
+void semtech_loramac_get_devaddr(const semtech_loramac_t *mac, uint8_t *addr);
 
 /**
  * @brief   Sets the device class
  *
- * @param[in] cls           The device class
+ * @param[in] mac          Pointer to the mac
+ * @param[in] cls          The device class
  */
-void semtech_loramac_set_class(loramac_class_t cls);
+void semtech_loramac_set_class(semtech_loramac_t *mac, loramac_class_t cls);
 
 /**
  * @brief   Gets the device class
  *
+ * @param[in] mac          Pointer to the mac
  * @return                 The device class
  */
-loramac_class_t semtech_loramac_get_class(void);
+loramac_class_t semtech_loramac_get_class(semtech_loramac_t *mac);
 
 /**
  * @brief   Sets the channels datarate
  *
+ * @param[in] mac          Pointer to the mac
  * @param[in] dr           The datarate (from 1 to 16)
  */
-void semtech_loramac_set_dr(uint8_t dr);
+void semtech_loramac_set_dr(semtech_loramac_t *mac, uint8_t dr);
 
 /**
  * @brief   Gets the channels datarate
  *
+ * @param[in] mac          Pointer to the mac
  * @return                 The datarate (from 1 to 16)
  */
-uint8_t semtech_loramac_get_dr(void);
+uint8_t semtech_loramac_get_dr(semtech_loramac_t *mac);
 
 /**
  * @brief   Enables/disable adaptive datarate
  *
- * @param[in] adr           Adaptive datarate mode
+ * @param[in] mac          Pointer to the mac
+ * @param[in] adr          Adaptive datarate mode
  */
-void semtech_loramac_set_adr(bool adr);
+void semtech_loramac_set_adr(semtech_loramac_t *mac, bool adr);
 
 /**
  * @brief   Checks if adaptive datarate is set
  *
- * @return                  true if adr is on, false otherwise
+ * @param[in] mac          Pointer to the mac
+ * @return                 true if adr is on, false otherwise
  */
-bool semtech_loramac_get_adr(void);
+bool semtech_loramac_get_adr(semtech_loramac_t *mac);
 
 /**
  * @brief   Enable/disable the public network mode
  *
- * @param[in] public        The public network mode
+ * @param[in] mac          Pointer to the mac
+ * @param[in] public       The public network mode
  */
-void semtech_loramac_set_public_network(bool public);
+void semtech_loramac_set_public_network(semtech_loramac_t *mac, bool public);
 
 /**
  * @brief   Checks if public network is set
  *
+ * @param[in] mac          Pointer to the mac
  * @return                 true if public network is on, false otherwise
  */
-bool semtech_loramac_get_public_network(void);
+bool semtech_loramac_get_public_network(semtech_loramac_t *mac);
 
 /**
  * @brief   Sets the NetID (only useful with ABP join procedure)
  *
+ * @param[in] mac          Pointer to the mac
  * @param[in] netid        The NetID
  */
-void semtech_loramac_set_netid(uint32_t netid);
+void semtech_loramac_set_netid(semtech_loramac_t *mac, uint32_t netid);
 
 /**
  * @brief   Gets the NetID
  *
+ * @param[in] mac          Pointer to the mac
  * @return                 The NetID
  */
-uint32_t semtech_loramac_get_netid(void);
+uint32_t semtech_loramac_get_netid(semtech_loramac_t *mac);
 
 /**
  * @brief   Sets the channels TX power index
  *
+ * @param[in] mac          Pointer to the mac
  * @param[in] power        The TX power index (from 1 to 16)
  */
-void semtech_loramac_set_tx_power(uint8_t power);
+void semtech_loramac_set_tx_power(semtech_loramac_t *mac, uint8_t power);
 
 /**
  * @brief   Gets the channels TX power index
  *
+ * @param[in] mac          Pointer to the mac
  * @return                 The TX power index (from 1 to 16)
  */
-uint8_t semtech_loramac_get_tx_power(void);
+uint8_t semtech_loramac_get_tx_power(semtech_loramac_t *mac);
+
+/**
+ * @brief   Sets the TX application port
+ *
+ * @param[in] mac          Pointer to the mac
+ * @param[in] port         The TX application port
+ */
+void semtech_loramac_set_tx_port(semtech_loramac_t *mac, uint8_t port);
+
+/**
+ * @brief   Gets the TX application port
+ *
+ * @param[in] mac          Pointer to the mac
+ * @return                 The TX application port
+ */
+uint8_t semtech_loramac_get_tx_port(semtech_loramac_t *mac);
+
+/**
+ * @brief   Sets the TX confirmable mode
+ *
+ * @param[in] mac          Pointer to the mac
+ * @param[in] mode          The TX mode (confirmable or not confirmable)
+ */
+void semtech_loramac_set_tx_mode(semtech_loramac_t *mac, uint8_t mode);
+
+/**
+ * @brief   Gets the TX confirmable mode
+ *
+ * @param[in] mac          Pointer to the mac
+ * @return                 The TX mode (confirmable or not confirmable)
+ */
+uint8_t semtech_loramac_get_tx_mode(semtech_loramac_t *mac);
 
 /**
  * @brief   Sets the RX2 frequency
  *
+ * @param[in] mac          Pointer to the mac
  * @param[in] freq         The RX2 frequency
  */
-void semtech_loramac_set_rx2_freq(uint8_t freq);
+void semtech_loramac_set_rx2_freq(semtech_loramac_t *mac, uint32_t freq);
 
 /**
  * @brief   Gets the RX2 frequency
  *
+ * @param[in] mac          Pointer to the mac
  * @return                 The RX2 frequency
  */
-uint32_t semtech_loramac_get_rx2_freq(void);
+uint32_t semtech_loramac_get_rx2_freq(semtech_loramac_t *mac);
 
 /**
  * @brief   Sets the RX2 datarate
  *
+ * @param[in] mac          Pointer to the mac
  * @param[in] dr           The RX2 datarate
  */
-void semtech_loramac_set_rx2_dr(uint8_t dr);
+void semtech_loramac_set_rx2_dr(semtech_loramac_t *mac, uint8_t dr);
 
 /**
  * @brief   Gets the RX2 datarate
  *
+ * @param[in] mac          Pointer to the mac
  * @return                 The RX2 datarate
  */
-uint8_t semtech_loramac_get_rx2_dr(void);
+uint8_t semtech_loramac_get_rx2_dr(semtech_loramac_t *mac);
 
 #ifdef __cplusplus
 }
-- 
GitLab