Fork of Semtech LoRaWAN stack

Fork of LoRaWAN-lib by canuck lehead

Files at this revision

API Documentation at this revision

Comitter:
Shaun Nelson
Date:
Wed Aug 09 16:20:21 2017 -0400
Branch:
class_b
Parent:
37:a592e8eeaa1e
Child:
39:ca51084123b8
Commit message:
Synchronized with https://github.com/Senetco/LoRaMac-node/commit/6cb21de99eaa65caad9d911318df8875867a6e60

Changed in this revision

LoRaMac-definitions.h Show diff for this revision Revisions of this file
LoRaMac.cpp Show annotated file Show diff for this revision Revisions of this file
LoRaMac.h Show annotated file Show diff for this revision Revisions of this file
LoRaMacClassB.cpp Show annotated file Show diff for this revision Revisions of this file
LoRaMacClassB.h Show annotated file Show diff for this revision Revisions of this file
LoRaMacCrypto.cpp Show annotated file Show diff for this revision Revisions of this file
LoRaMacCrypto.h Show annotated file Show diff for this revision Revisions of this file
LoRaMacTest.h Show annotated file Show diff for this revision Revisions of this file
region/Region.cpp Show annotated file Show diff for this revision Revisions of this file
region/Region.h Show annotated file Show diff for this revision Revisions of this file
region/RegionAS923.cpp Show annotated file Show diff for this revision Revisions of this file
region/RegionAS923.h Show annotated file Show diff for this revision Revisions of this file
region/RegionAU915.cpp Show annotated file Show diff for this revision Revisions of this file
region/RegionAU915.h Show annotated file Show diff for this revision Revisions of this file
region/RegionCN470.cpp Show annotated file Show diff for this revision Revisions of this file
region/RegionCN470.h Show annotated file Show diff for this revision Revisions of this file
region/RegionCN779.cpp Show annotated file Show diff for this revision Revisions of this file
region/RegionCN779.h Show annotated file Show diff for this revision Revisions of this file
region/RegionCommon.cpp Show annotated file Show diff for this revision Revisions of this file
region/RegionCommon.h Show annotated file Show diff for this revision Revisions of this file
region/RegionEU433.cpp Show annotated file Show diff for this revision Revisions of this file
region/RegionEU433.h Show annotated file Show diff for this revision Revisions of this file
region/RegionEU868.cpp Show annotated file Show diff for this revision Revisions of this file
region/RegionEU868.h Show annotated file Show diff for this revision Revisions of this file
region/RegionIN865.cpp Show annotated file Show diff for this revision Revisions of this file
region/RegionIN865.h Show annotated file Show diff for this revision Revisions of this file
region/RegionKR920.cpp Show annotated file Show diff for this revision Revisions of this file
region/RegionKR920.h Show annotated file Show diff for this revision Revisions of this file
region/RegionUS915-Hybrid.cpp Show annotated file Show diff for this revision Revisions of this file
region/RegionUS915-Hybrid.h Show annotated file Show diff for this revision Revisions of this file
region/RegionUS915.cpp Show annotated file Show diff for this revision Revisions of this file
region/RegionUS915.h Show annotated file Show diff for this revision Revisions of this file
--- a/LoRaMac-definitions.h	Tue Aug 08 19:27:17 2017 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,608 +0,0 @@
-/*
- / _____)             _              | |
-( (____  _____ ____ _| |_ _____  ____| |__
- \____ \| ___ |    (_   _) ___ |/ ___)  _ \
- _____) ) ____| | | || |_| ____( (___| | | |
-(______/|_____)_|_|_| \__)_____)\____)_| |_|
-    (C)2013 Semtech
-
-Description: LoRa MAC layer global definitions
-
-License: Revised BSD License, see LICENSE.TXT file include in the project
-
-Maintainer: Miguel Luis and Gregory Cristian
-*/
-#ifndef __LORAMAC_BOARD_H__
-#define __LORAMAC_BOARD_H__
-
-/*!
- * Returns individual channel mask
- *
- * \param[IN] channelIndex Channel index 1 based
- * \retval channelMask
- */
-#define LC( channelIndex )            ( uint16_t )( 1 << ( channelIndex - 1 ) )
-
-#if defined( USE_BAND_433 )
-
-/*!
- * LoRaMac maximum number of channels
- */
-#define LORA_MAX_NB_CHANNELS                        16
-
-/*!
- * Minimal datarate that can be used by the node
- */
-#define LORAMAC_TX_MIN_DATARATE                     DR_0
-
-/*!
- * Maximal datarate that can be used by the node
- */
-#define LORAMAC_TX_MAX_DATARATE                     DR_7
-
-/*!
- * Minimal datarate that can be used by the node
- */
-#define LORAMAC_RX_MIN_DATARATE                     DR_0
-
-/*!
- * Maximal datarate that can be used by the node
- */
-#define LORAMAC_RX_MAX_DATARATE                     DR_7
-
-/*!
- * Default datarate used by the node
- */
-#define LORAMAC_DEFAULT_DATARATE                    DR_0
-
-/*!
- * Minimal Rx1 receive datarate offset
- */
-#define LORAMAC_MIN_RX1_DR_OFFSET                   0
-
-/*!
- * Maximal Rx1 receive datarate offset
- */
-#define LORAMAC_MAX_RX1_DR_OFFSET                   5
-
-/*!
- * Minimal Tx output power that can be used by the node
- */
-#define LORAMAC_MIN_TX_POWER                        TX_POWER_M5_DBM
-
-/*!
- * Maximal Tx output power that can be used by the node
- */
-#define LORAMAC_MAX_TX_POWER                        TX_POWER_10_DBM
-
-/*!
- * Default Tx output power used by the node
- */
-#define LORAMAC_DEFAULT_TX_POWER                    TX_POWER_10_DBM
-
-/*!
- * LoRaMac TxPower definition
- */
-#define TX_POWER_10_DBM                             0
-#define TX_POWER_07_DBM                             1
-#define TX_POWER_04_DBM                             2
-#define TX_POWER_01_DBM                             3
-#define TX_POWER_M2_DBM                             4
-#define TX_POWER_M5_DBM                             5
-
-/*!
- * LoRaMac datarates definition
- */
-#define DR_0                                        0  // SF12 - BW125
-#define DR_1                                        1  // SF11 - BW125
-#define DR_2                                        2  // SF10 - BW125
-#define DR_3                                        3  // SF9  - BW125
-#define DR_4                                        4  // SF8  - BW125
-#define DR_5                                        5  // SF7  - BW125
-#define DR_6                                        6  // SF7  - BW250
-#define DR_7                                        7  // FSK
-
-/*!
- * Verification of default datarate
- */
-#if ( LORAMAC_DEFAULT_DATARATE > DR_5 )
-#error "A default DR higher than DR_5 may lead to connectivity loss."
-#endif
-
-/*!
- * Second reception window channel definition.
- */
-// Channel = { Frequency [Hz], Datarate }
-#define RX_WND_2_CHANNEL                                  { 434665000, DR_0 }
-
-/*!
- * LoRaMac maximum number of bands
- */
-#define LORA_MAX_NB_BANDS                           1
-
-// Band = { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff }
-#define BAND0              { 100, TX_POWER_10_DBM, 0,  0 } //  1.0 %
-
-/*!
- * LoRaMac default channels
- */
-// Channel = { Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
-#define LC1                { 433175000, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
-#define LC2                { 433375000, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
-#define LC3                { 433575000, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
-
-/*!
- * LoRaMac channels which are allowed for the join procedure
- */
-#define JOIN_CHANNELS      ( uint16_t )( LC( 1 ) | LC( 2 ) | LC( 3 ) )
-
-#elif defined( USE_BAND_470 )
-
-/*!
- * LoRaMac maximum number of channels
- */
-#define LORA_MAX_NB_CHANNELS                        96
-
-/*!
- * Minimal datarate that can be used by the node
- */
-#define LORAMAC_TX_MIN_DATARATE                     DR_0
-
-/*!
- * Maximal datarate that can be used by the node
- */
-#define LORAMAC_TX_MAX_DATARATE                     DR_5
-
-/*!
- * Minimal datarate that can be used by the node
- */
-#define LORAMAC_RX_MIN_DATARATE                     DR_0
-
-/*!
- * Maximal datarate that can be used by the node
- */
-#define LORAMAC_RX_MAX_DATARATE                     DR_5
-
-/*!
- * Default datarate used by the node
- */
-#define LORAMAC_DEFAULT_DATARATE                    DR_0
-
-/*!
- * Minimal Rx1 receive datarate offset
- */
-#define LORAMAC_MIN_RX1_DR_OFFSET                   0
-
-/*!
- * Maximal Rx1 receive datarate offset
- */
-#define LORAMAC_MAX_RX1_DR_OFFSET                   3
-
-/*!
- * Minimal Tx output power that can be used by the node
- */
-#define LORAMAC_MIN_TX_POWER                        TX_POWER_2_DBM
-
-/*!
- * Maximal Tx output power that can be used by the node
- */
-#define LORAMAC_MAX_TX_POWER                        TX_POWER_17_DBM
-
-/*!
- * Default Tx output power used by the node
- */
-#define LORAMAC_DEFAULT_TX_POWER                    TX_POWER_14_DBM
-
-/*!
- * LoRaMac TxPower definition
- */
-#define TX_POWER_17_DBM                             0
-#define TX_POWER_16_DBM                             1
-#define TX_POWER_14_DBM                             2
-#define TX_POWER_12_DBM                             3
-#define TX_POWER_10_DBM                             4
-#define TX_POWER_7_DBM                              5
-#define TX_POWER_5_DBM                              6
-#define TX_POWER_2_DBM                              7
-
-
-/*!
- * LoRaMac datarates definition
- */
-#define DR_0                                        0  // SF12 - BW125 |
-#define DR_1                                        1  // SF11 - BW125 |
-#define DR_2                                        2  // SF10 - BW125 |
-#define DR_3                                        3  // SF9  - BW125 |
-#define DR_4                                        4  // SF8  - BW125 |
-#define DR_5                                        5  // SF7  - BW125 |
-
-/*!
- * Second reception window channel definition.
- */
-// Channel = { Frequency [Hz], Datarate }
-#define RX_WND_2_CHANNEL                                  { 505300000, DR_0 }
-
-/*!
- * LoRaMac maximum number of bands
- */
-#define LORA_MAX_NB_BANDS                           1
-
-// Band = { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff }
-#define BAND0              { 1, TX_POWER_17_DBM, 0,  0 } //  100.0 %
-
-#elif defined( USE_BAND_780 )
-
-/*!
- * LoRaMac maximum number of channels
- */
-#define LORA_MAX_NB_CHANNELS                        16
-
-/*!
- * Minimal datarate that can be used by the node
- */
-#define LORAMAC_TX_MIN_DATARATE                     DR_0
-
-/*!
- * Maximal datarate that can be used by the node
- */
-#define LORAMAC_TX_MAX_DATARATE                     DR_7
-
-/*!
- * Minimal datarate that can be used by the node
- */
-#define LORAMAC_RX_MIN_DATARATE                     DR_0
-
-/*!
- * Maximal datarate that can be used by the node
- */
-#define LORAMAC_RX_MAX_DATARATE                     DR_7
-
-/*!
- * Default datarate used by the node
- */
-#define LORAMAC_DEFAULT_DATARATE                    DR_0
-
-/*!
- * Minimal Rx1 receive datarate offset
- */
-#define LORAMAC_MIN_RX1_DR_OFFSET                   0
-
-/*!
- * Maximal Rx1 receive datarate offset
- */
-#define LORAMAC_MAX_RX1_DR_OFFSET                   5
-
-/*!
- * Minimal Tx output power that can be used by the node
- */
-#define LORAMAC_MIN_TX_POWER                        TX_POWER_M5_DBM
-
-/*!
- * Maximal Tx output power that can be used by the node
- */
-#define LORAMAC_MAX_TX_POWER                        TX_POWER_10_DBM
-
-/*!
- * Default Tx output power used by the node
- */
-#define LORAMAC_DEFAULT_TX_POWER                    TX_POWER_10_DBM
-
-/*!
- * LoRaMac TxPower definition
- */
-#define TX_POWER_10_DBM                             0
-#define TX_POWER_07_DBM                             1
-#define TX_POWER_04_DBM                             2
-#define TX_POWER_01_DBM                             3
-#define TX_POWER_M2_DBM                             4
-#define TX_POWER_M5_DBM                             5
-
-/*!
- * LoRaMac datarates definition
- */
-#define DR_0                                        0  // SF12 - BW125
-#define DR_1                                        1  // SF11 - BW125
-#define DR_2                                        2  // SF10 - BW125
-#define DR_3                                        3  // SF9  - BW125
-#define DR_4                                        4  // SF8  - BW125
-#define DR_5                                        5  // SF7  - BW125
-#define DR_6                                        6  // SF7  - BW250
-#define DR_7                                        7  // FSK
-
-/*!
- * Verification of default datarate
- */
-#if ( LORAMAC_DEFAULT_DATARATE > DR_5 )
-#error "A default DR higher than DR_5 may lead to connectivity loss."
-#endif
-
-/*!
- * Second reception window channel definition.
- */
-// Channel = { Frequency [Hz], Datarate }
-#define RX_WND_2_CHANNEL                                  { 786000000, DR_0 }
-
-/*!
- * LoRaMac maximum number of bands
- */
-#define LORA_MAX_NB_BANDS                           1
-
-// Band = { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff }
-#define BAND0              { 100, TX_POWER_10_DBM, 0,  0 } //  1.0 %
-
-/*!
- * LoRaMac default channels
- */
-// Channel = { Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
-#define LC1                { 779500000, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
-#define LC2                { 779700000, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
-#define LC3                { 779900000, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
-
-/*!
- * LoRaMac channels which are allowed for the join procedure
- */
-#define JOIN_CHANNELS      ( uint16_t )( LC( 1 ) | LC( 2 ) | LC( 3 ) )
-
-#elif defined( USE_BAND_868 )
-
-/*!
- * LoRaMac maximum number of channels
- */
-#define LORA_MAX_NB_CHANNELS                        16
-
-/*!
- * Minimal datarate that can be used by the node
- */
-#define LORAMAC_TX_MIN_DATARATE                     DR_0
-
-/*!
- * Maximal datarate that can be used by the node
- */
-#define LORAMAC_TX_MAX_DATARATE                     DR_7
-
-/*!
- * Minimal datarate that can be used by the node
- */
-#define LORAMAC_RX_MIN_DATARATE                     DR_0
-
-/*!
- * Maximal datarate that can be used by the node
- */
-#define LORAMAC_RX_MAX_DATARATE                     DR_7
-
-/*!
- * Default datarate used by the node
- */
-#define LORAMAC_DEFAULT_DATARATE                    DR_0
-
-/*!
- * Minimal Rx1 receive datarate offset
- */
-#define LORAMAC_MIN_RX1_DR_OFFSET                   0
-
-/*!
- * Maximal Rx1 receive datarate offset
- */
-#define LORAMAC_MAX_RX1_DR_OFFSET                   5
-
-/*!
- * Minimal Tx output power that can be used by the node
- */
-#define LORAMAC_MIN_TX_POWER                        TX_POWER_02_DBM
-
-/*!
- * Maximal Tx output power that can be used by the node
- */
-#define LORAMAC_MAX_TX_POWER                        TX_POWER_20_DBM
-
-/*!
- * Default Tx output power used by the node
- */
-#define LORAMAC_DEFAULT_TX_POWER                    TX_POWER_14_DBM
-
-/*!
- * LoRaMac TxPower definition
- */
-#define TX_POWER_20_DBM                             0
-#define TX_POWER_14_DBM                             1
-#define TX_POWER_11_DBM                             2
-#define TX_POWER_08_DBM                             3
-#define TX_POWER_05_DBM                             4
-#define TX_POWER_02_DBM                             5
-
-/*!
- * LoRaMac datarates definition
- */
-#define DR_0                                        0  // SF12 - BW125
-#define DR_1                                        1  // SF11 - BW125
-#define DR_2                                        2  // SF10 - BW125
-#define DR_3                                        3  // SF9  - BW125
-#define DR_4                                        4  // SF8  - BW125
-#define DR_5                                        5  // SF7  - BW125
-#define DR_6                                        6  // SF7  - BW250
-#define DR_7                                        7  // FSK
-
-/*!
- * Verification of default datarate
- */
-#if ( LORAMAC_DEFAULT_DATARATE > DR_5 )
-#error "A default DR higher than DR_5 may lead to connectivity loss."
-#endif
-
-/*!
- * Second reception window channel definition.
- */
-// Channel = { Frequency [Hz], Datarate }
-#define RX_WND_2_CHANNEL                                  { 869525000, DR_0 }
-
-/*!
- * LoRaMac maximum number of bands
- */
-#define LORA_MAX_NB_BANDS                           5
-
-/*!
- * LoRaMac EU868 default bands
- */
-typedef enum
-{
-    BAND_G1_0,
-    BAND_G1_1,
-    BAND_G1_2,
-    BAND_G1_3,
-    BAND_G1_4,
-}BandId_t;
-
-// Band = { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff }
-#define BAND0              { 100 , TX_POWER_14_DBM, 0,  0 } //  1.0 %
-#define BAND1              { 100 , TX_POWER_14_DBM, 0,  0 } //  1.0 %
-#define BAND2              { 1000, TX_POWER_14_DBM, 0,  0 } //  0.1 %
-#define BAND3              { 10  , TX_POWER_14_DBM, 0,  0 } // 10.0 %
-#define BAND4              { 100 , TX_POWER_14_DBM, 0,  0 } //  1.0 %
-
-/*!
- * LoRaMac default channels
- */
-// Channel = { Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
-#define LC1                { 868100000, { ( ( DR_5 << 4 ) | DR_0 ) }, 1 }
-#define LC2                { 868300000, { ( ( DR_5 << 4 ) | DR_0 ) }, 1 }
-#define LC3                { 868500000, { ( ( DR_5 << 4 ) | DR_0 ) }, 1 }
-
-/*!
- * LoRaMac channels which are allowed for the join procedure
- */
-#define JOIN_CHANNELS      ( uint16_t )( LC( 1 ) | LC( 2 ) | LC( 3 ) )
-
-#elif defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
-
-/*!
- * LoRaMac maximum number of channels
- */
-#define LORA_MAX_NB_CHANNELS                        72
-
-/*!
- * Minimal datarate that can be used by the node
- */
-#define LORAMAC_TX_MIN_DATARATE                     DR_0
-
-/*!
- * Maximal datarate that can be used by the node
- */
-#define LORAMAC_TX_MAX_DATARATE                     DR_4
-
-/*!
- * Minimal datarate that can be used by the node
- */
-#define LORAMAC_RX_MIN_DATARATE                     DR_8
-
-/*!
- * Maximal datarate that can be used by the node
- */
-#define LORAMAC_RX_MAX_DATARATE                     DR_13
-
-/*!
- * Default datarate used by the node
- */
-#define LORAMAC_DEFAULT_DATARATE                    DR_0
-
-/*!
- * Minimal Rx1 receive datarate offset
- */
-#define LORAMAC_MIN_RX1_DR_OFFSET                   0
-
-/*!
- * Maximal Rx1 receive datarate offset
- */
-#define LORAMAC_MAX_RX1_DR_OFFSET                   3
-
-/*!
- * Minimal Tx output power that can be used by the node
- */
-#define LORAMAC_MIN_TX_POWER                        TX_POWER_10_DBM
-
-/*!
- * Maximal Tx output power that can be used by the node
- */
-#define LORAMAC_MAX_TX_POWER                        TX_POWER_30_DBM
-
-/*!
- * Default Tx output power used by the node
- */
-#define LORAMAC_DEFAULT_TX_POWER                    TX_POWER_20_DBM
-
-/*!
- * LoRaMac TxPower definition
- */
-#define TX_POWER_30_DBM                             0
-#define TX_POWER_28_DBM                             1
-#define TX_POWER_26_DBM                             2
-#define TX_POWER_24_DBM                             3
-#define TX_POWER_22_DBM                             4
-#define TX_POWER_20_DBM                             5
-#define TX_POWER_18_DBM                             6
-#define TX_POWER_16_DBM                             7
-#define TX_POWER_14_DBM                             8
-#define TX_POWER_12_DBM                             9
-#define TX_POWER_10_DBM                             10
-
-/*!
- * LoRaMac datarates definition
- */
-#define DR_0                                        0  // SF10 - BW125 |
-#define DR_1                                        1  // SF9  - BW125 |
-#define DR_2                                        2  // SF8  - BW125 +-> Up link
-#define DR_3                                        3  // SF7  - BW125 |
-#define DR_4                                        4  // SF8  - BW500 |
-#define DR_5                                        5  // RFU
-#define DR_6                                        6  // RFU
-#define DR_7                                        7  // RFU
-#define DR_8                                        8  // SF12 - BW500 |
-#define DR_9                                        9  // SF11 - BW500 |
-#define DR_10                                       10 // SF10 - BW500 |
-#define DR_11                                       11 // SF9  - BW500 |
-#define DR_12                                       12 // SF8  - BW500 +-> Down link
-#define DR_13                                       13 // SF7  - BW500 |
-#define DR_14                                       14 // RFU          |
-#define DR_15                                       15 // RFU          |
-
-/*!
- * Second reception window channel definition.
- */
-// Channel = { Frequency [Hz], Datarate }
-#define RX_WND_2_CHANNEL                                  { 923300000, DR_8 }
-
-/*!
- * LoRaMac maximum number of bands
- */
-#define LORA_MAX_NB_BANDS                           1
-
-// Band = { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff }
-#define BAND0              { 1, TX_POWER_20_DBM, 0,  0 } //  100.0 %
-
-/*!
- * LoRaMac default channels
- */
-// Channel = { Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
-/*
- * US band channels are initialized using a loop in LoRaMacInit function
- * \code
- * // 125 kHz channels
- * for( uint8_t i = 0; i < LORA_MAX_NB_CHANNELS - 8; i++ )
- * {
- *     Channels[i].Frequency = 902.3e6 + i * 200e3;
- *     Channels[i].DrRange.Value = ( DR_3 << 4 ) | DR_0;
- *     Channels[i].Band = 0;
- * }
- * // 500 kHz channels
- * for( uint8_t i = LORA_MAX_NB_CHANNELS - 8; i < LORA_MAX_NB_CHANNELS; i++ )
- * {
- *     Channels[i].Frequency = 903.0e6 + ( i - ( LORA_MAX_NB_CHANNELS - 8 ) ) * 1.6e6;
- *     Channels[i].DrRange.Value = ( DR_4 << 4 ) | DR_4;
- *     Channels[i].Band = 0;
- * }
- * \endcode
- */
-#else
-    #error "Please define a frequency band in the compiler options."
-#endif
-
-#endif // __LORAMAC_BOARD_H__
--- a/LoRaMac.cpp	Tue Aug 08 19:27:17 2017 -0400
+++ b/LoRaMac.cpp	Wed Aug 09 16:20:21 2017 -0400
@@ -15,15 +15,18 @@
 
 License: Revised BSD License, see LICENSE.TXT file include in the project
 
-Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jäckle ( STACKFORCE )
+Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE )
 */
-#include <math.h>
 #include "board.h"
 
+#include "LoRaMac.h"
+#include "LoRaMacClassB.h"
+#include "region/Region.h"
 #include "LoRaMacCrypto.h"
-#include "LoRaMac.h"
 #include "LoRaMacTest.h"
 
+
+
 /*!
  * Maximum PHY layer payload size
  */
@@ -32,14 +35,12 @@
 /*!
  * Maximum MAC commands buffer size
  */
-#define LORA_MAC_COMMAND_MAX_LENGTH                 15
+#define LORA_MAC_COMMAND_MAX_LENGTH                 128
 
 /*!
- * FRMPayload overhead to be used when setting the Radio.SetMaxPayloadLength
- * in RxWindowSetup function.
- * Maximum PHYPayload = MaxPayloadOfDatarate/MaxPayloadOfDatarateRepeater + LORA_MAC_FRMPAYLOAD_OVERHEAD
+ * Maximum length of the fOpts field
  */
-#define LORA_MAC_FRMPAYLOAD_OVERHEAD                13 // MHDR(1) + FHDR(7) + Port(1) + MIC(4)
+#define LORA_MAC_COMMAND_MAX_FOPTS_LENGTH           15
 
 /*!
  * LoRaMac duty cycle for the back-off procedure during the first hour.
@@ -57,6 +58,11 @@
 #define BACKOFF_DC_24_HOURS                         10000
 
 /*!
+ * LoRaMac region.
+ */
+static LoRaMacRegion_t LoRaMacRegion;
+
+/*!
  * Device IEEE EUI
  */
 static uint8_t *LoRaMacDevEui;
@@ -121,11 +127,6 @@
 static bool PublicNetwork;
 
 /*!
- * Indicates if the node supports repeaters
- */
-static bool RepeaterSupport;
-
-/*!
  * Buffer containing the data to be sent or received.
  */
 static uint8_t LoRaMacBuffer[LORAMAC_PHY_MAXPAYLOAD];
@@ -220,269 +221,6 @@
  */
 static uint8_t MacCommandsBufferToRepeat[LORA_MAC_COMMAND_MAX_LENGTH];
 
-#if defined( USE_BAND_433 )
-/*!
- * Data rates table definition
- */
-const uint8_t Datarates[]  = { 12, 11, 10,  9,  8,  7,  7, 50 };
-
-/*!
- * Bandwidths table definition in Hz
- */
-const uint32_t Bandwidths[] = { 125e3, 125e3, 125e3, 125e3, 125e3, 125e3, 250e3, 0 };
-
-/*!
- * Maximum payload with respect to the datarate index. Cannot operate with repeater.
- */
-const uint8_t MaxPayloadOfDatarate[] = { 51, 51, 51, 115, 242, 242, 242, 242 };
-
-/*!
- * Maximum payload with respect to the datarate index. Can operate with repeater.
- */
-const uint8_t MaxPayloadOfDatarateRepeater[] = { 51, 51, 51, 115, 222, 222, 222, 222 };
-
-/*!
- * Tx output powers table definition
- */
-const int8_t TxPowers[]    = { 10, 7, 4, 1, -2, -5 };
-
-/*!
- * LoRaMac bands
- */
-static Band_t Bands[LORA_MAX_NB_BANDS] =
-{
-    BAND0,
-};
-
-/*!
- * LoRaMAC channels
- */
-static ChannelParams_t Channels[LORA_MAX_NB_CHANNELS] =
-{
-    LC1,
-    LC2,
-    LC3,
-};
-#elif defined( USE_BAND_470 )
-
-/*!
- * Data rates table definition
- */
-const uint8_t Datarates[]  = { 12, 11, 10,  9,  8,  7 };
-
-/*!
- * Bandwidths table definition in Hz
- */
-const uint32_t Bandwidths[] = { 125e3, 125e3, 125e3, 125e3, 125e3, 125e3 };
-
-/*!
- * Maximum payload with respect to the datarate index. Cannot operate with repeater.
- */
-const uint8_t MaxPayloadOfDatarate[] = { 51, 51, 51, 115, 222, 222 };
-
-/*!
- * Maximum payload with respect to the datarate index. Can operate with repeater.
- */
-const uint8_t MaxPayloadOfDatarateRepeater[] = { 51, 51, 51, 115, 222, 222 };
-
-/*!
- * Tx output powers table definition
- */
-const int8_t TxPowers[]    = { 17, 16, 14, 12, 10, 7, 5, 2 };
-
-/*!
- * LoRaMac bands
- */
-static Band_t Bands[LORA_MAX_NB_BANDS] =
-{
-    BAND0,
-};
-
-/*!
- * LoRaMAC channels
- */
-static ChannelParams_t Channels[LORA_MAX_NB_CHANNELS];
-
-/*!
- * Defines the first channel for RX window 1 for CN470 band
- */
-#define LORAMAC_FIRST_RX1_CHANNEL           ( (uint32_t) 500.3e6 )
-
-/*!
- * Defines the last channel for RX window 1 for CN470 band
- */
-#define LORAMAC_LAST_RX1_CHANNEL            ( (uint32_t) 509.7e6 )
-
-/*!
- * Defines the step width of the channels for RX window 1
- */
-#define LORAMAC_STEPWIDTH_RX1_CHANNEL       ( (uint32_t) 200e3 )
-
-#elif defined( USE_BAND_780 )
-/*!
- * Data rates table definition
- */
-const uint8_t Datarates[]  = { 12, 11, 10,  9,  8,  7,  7, 50 };
-
-/*!
- * Bandwidths table definition in Hz
- */
-const uint32_t Bandwidths[] = { 125e3, 125e3, 125e3, 125e3, 125e3, 125e3, 250e3, 0 };
-
-/*!
- * Maximum payload with respect to the datarate index. Cannot operate with repeater.
- */
-const uint8_t MaxPayloadOfDatarate[] = { 51, 51, 51, 115, 242, 242, 242, 242 };
-
-/*!
- * Maximum payload with respect to the datarate index. Can operate with repeater.
- */
-const uint8_t MaxPayloadOfDatarateRepeater[] = { 51, 51, 51, 115, 222, 222, 222, 222 };
-
-/*!
- * Tx output powers table definition
- */
-const int8_t TxPowers[]    = { 10, 7, 4, 1, -2, -5 };
-
-/*!
- * LoRaMac bands
- */
-static Band_t Bands[LORA_MAX_NB_BANDS] =
-{
-    BAND0,
-};
-
-/*!
- * LoRaMAC channels
- */
-static ChannelParams_t Channels[LORA_MAX_NB_CHANNELS] =
-{
-    LC1,
-    LC2,
-    LC3,
-};
-#elif defined( USE_BAND_868 )
-/*!
- * Data rates table definition
- */
-const uint8_t Datarates[]  = { 12, 11, 10,  9,  8,  7,  7, 50 };
-
-/*!
- * Bandwidths table definition in Hz
- */
-const uint32_t Bandwidths[] = { 125e3, 125e3, 125e3, 125e3, 125e3, 125e3, 250e3, 0 };
-
-/*!
- * Maximum payload with respect to the datarate index. Cannot operate with repeater.
- */
-const uint8_t MaxPayloadOfDatarate[] = { 51, 51, 51, 115, 242, 242, 242, 242 };
-
-/*!
- * Maximum payload with respect to the datarate index. Can operate with repeater.
- */
-const uint8_t MaxPayloadOfDatarateRepeater[] = { 51, 51, 51, 115, 222, 222, 222, 222 };
-
-/*!
- * Tx output powers table definition
- */
-const int8_t TxPowers[]    = { 20, 14, 11,  8,  5,  2 };
-
-/*!
- * LoRaMac bands
- */
-static Band_t Bands[LORA_MAX_NB_BANDS] =
-{
-    BAND0,
-    BAND1,
-    BAND2,
-    BAND3,
-    BAND4,
-};
-
-/*!
- * LoRaMAC channels
- */
-static ChannelParams_t Channels[LORA_MAX_NB_CHANNELS] =
-{
-    LC1,
-    LC2,
-    LC3,
-};
-#elif defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
-/*!
- * Data rates table definition
- */
-const uint8_t Datarates[]  = { 10, 9, 8,  7,  8,  0,  0, 0, 12, 11, 10, 9, 8, 7, 0, 0 };
-
-/*!
- * Bandwidths table definition in Hz
- */
-const uint32_t Bandwidths[] = { 125e3, 125e3, 125e3, 125e3, 500e3, 0, 0, 0, 500e3, 500e3, 500e3, 500e3, 500e3, 500e3, 0, 0 };
-
-/*!
- * Up/Down link data rates offset definition
- */
-const int8_t DatarateOffsets[5][4] =
-{
-    { DR_10, DR_9 , DR_8 , DR_8  }, // DR_0
-    { DR_11, DR_10, DR_9 , DR_8  }, // DR_1
-    { DR_12, DR_11, DR_10, DR_9  }, // DR_2
-    { DR_13, DR_12, DR_11, DR_10 }, // DR_3
-    { DR_13, DR_13, DR_12, DR_11 }, // DR_4
-};
-
-/*!
- * Maximum payload with respect to the datarate index. Cannot operate with repeater.
- */
-const uint8_t MaxPayloadOfDatarate[] = { 11, 53, 125, 242, 242, 0, 0, 0, 53, 129, 242, 242, 242, 242, 0, 0 };
-
-/*!
- * Maximum payload with respect to the datarate index. Can operate with repeater.
- */
-const uint8_t MaxPayloadOfDatarateRepeater[] = { 11, 53, 125, 242, 242, 0, 0, 0, 33, 109, 222, 222, 222, 222, 0, 0 };
-
-/*!
- * Tx output powers table definition
- */
-const int8_t TxPowers[]    = { 30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10 };
-
-/*!
- * LoRaMac bands
- */
-static Band_t Bands[LORA_MAX_NB_BANDS] =
-{
-    BAND0,
-};
-
-/*!
- * LoRaMAC channels
- */
-static ChannelParams_t Channels[LORA_MAX_NB_CHANNELS];
-
-/*!
- * Contains the channels which remain to be applied.
- */
-static uint16_t ChannelsMaskRemaining[6];
-
-/*!
- * Defines the first channel for RX window 1 for US band
- */
-#define LORAMAC_FIRST_RX1_CHANNEL           ( (uint32_t) 923.3e6 )
-
-/*!
- * Defines the last channel for RX window 1 for US band
- */
-#define LORAMAC_LAST_RX1_CHANNEL            ( (uint32_t) 927.5e6 )
-
-/*!
- * Defines the step width of the channels for RX window 1
- */
-#define LORAMAC_STEPWIDTH_RX1_CHANNEL       ( (uint32_t) 600e3 )
-
-#else
-    #error "Please define a frequency band in the compiler options."
-#endif
-
 /*!
  * LoRaMac parameters
  */
@@ -522,6 +260,16 @@
 static uint8_t Channel;
 
 /*!
+ * Current channel index
+ */
+static uint8_t LastTxChannel;
+
+/*!
+ * Set to true, if the last uplink was a join request
+ */
+static bool LastTxIsJoinRequest;
+
+/*!
  * Stores the time at LoRaMac initialization.
  *
  * \remark Used for the BACKOFF_DC computation.
@@ -588,20 +336,10 @@
 static uint32_t RxWindow2Delay;
 
 /*!
- * Rx window parameters
+ * LoRaMac Rx windows configuration
  */
-typedef struct
-{
-    int8_t Datarate;
-    uint8_t Bandwidth;
-    uint32_t RxWindowTimeout;
-    int32_t RxOffset;
-}RxConfigParams_t;
-
-/*!
- * Rx windows params
- */
-static RxConfigParams_t RxWindowsParams[2];
+static RxConfigParams_t RxWindow1Config;
+static RxConfigParams_t RxWindow2Config;
 
 /*!
  * Acknowledge timeout timer. Used for packet retransmissions.
@@ -649,11 +387,26 @@
 static McpsConfirm_t McpsConfirm;
 
 /*!
+ * Structure to hold MLME indication data.
+ */
+static MlmeIndication_t MlmeIndication;
+
+/*!
  * Structure to hold MLME confirm data.
  */
 static MlmeConfirm_t MlmeConfirm;
 
 /*!
+ * MlmeConfirm queue data structure
+ */
+static MlmeConfirmQueue_t MlmeConfirmQueue[LORA_MAC_MLME_CONFIRM_QUEUE_LEN];
+
+/*!
+ * Counts the number of MlmeConfirms to process
+ */
+static uint8_t MlmeConfirmQueueCnt;
+
+/*!
  * Holds the current rx window slot
  */
 static uint8_t RxSlot = 0;
@@ -720,36 +473,19 @@
 static void OnAckTimeoutTimerEvent( void );
 
 /*!
- * \brief Searches and set the next random available channel
- *
- * \param [OUT] Time to wait for the next transmission according to the duty
- *              cycle.
- *
- * \retval status  Function status [1: OK, 0: Unable to find a channel on the
- *                                  current datarate]
- */
-static bool SetNextChannel( TimerTime_t* time );
-
-/*!
  * \brief Initializes and opens the reception window
  *
- * \param [IN] freq window channel frequency
- * \param [IN] datarate window channel datarate
- * \param [IN] bandwidth window channel bandwidth
- * \param [IN] timeout window channel timeout
- *
- * \retval status Operation status [true: Success, false: Fail]
+ * \param [IN] rxContinuous Set to true, if the RX is in continuous mode
+ * \param [IN] maxRxWindow Maximum RX window timeout
  */
-static bool RxWindowSetup( uint32_t freq, int8_t datarate, uint32_t bandwidth, uint16_t timeout, bool rxContinuous );
+static void RxWindowSetup( bool rxContinuous, uint32_t maxRxWindow );
 
 /*!
- * \brief Verifies if the RX window 2 frequency is in range
- *
- * \param [IN] freq window channel frequency
+ * \brief Switches the device class
  *
- * \retval status  Function status [1: OK, 0: Frequency not applicable]
+ * \param [IN] deviceClass Device class to switch to
  */
-static bool Rx2FreqInRange( uint32_t freq );
+static LoRaMacStatus_t SwitchClass( DeviceClass_t deviceClass );
 
 /*!
  * \brief Adds a new MAC command to be sent.
@@ -803,105 +539,6 @@
 static bool ValidatePayloadLength( uint8_t lenN, int8_t datarate, uint8_t fOptsLen );
 
 /*!
- * \brief Counts the number of bits in a mask.
- *
- * \param [IN] mask A mask from which the function counts the active bits.
- * \param [IN] nbBits The number of bits to check.
- *
- * \retval Number of enabled bits in the mask.
- */
-static uint8_t CountBits( uint16_t mask, uint8_t nbBits );
-
-#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
-/*!
- * \brief Counts the number of enabled 125 kHz channels in the channel mask.
- *        This function can only be applied to US915 band.
- *
- * \param [IN] channelsMask Pointer to the first element of the channel mask
- *
- * \retval Number of enabled channels in the channel mask
- */
-static uint8_t CountNbEnabled125kHzChannels( uint16_t *channelsMask );
-
-#if defined( USE_BAND_915_HYBRID )
-/*!
- * \brief Validates the correctness of the channel mask for US915, hybrid mode.
- *
- * \param [IN] mask Block definition to set.
- * \param [OUT] channelsMask Pointer to the first element of the channel mask
- */
-static void ReenableChannels( uint16_t mask, uint16_t* channelsMask );
-
-/*!
- * \brief Validates the correctness of the channel mask for US915, hybrid mode.
- *
- * \param [IN] channelsMask Pointer to the first element of the channel mask
- *
- * \retval [true: channel mask correct, false: channel mask not correct]
- */
-static bool ValidateChannelMask( uint16_t* channelsMask );
-#endif
-
-#endif
-
-/*!
- * \brief Validates the correctness of the datarate against the enable channels.
- *
- * \param [IN] datarate Datarate to be check
- * \param [IN] channelsMask Pointer to the first element of the channel mask
- *
- * \retval [true: datarate can be used, false: datarate can not be used]
- */
-static bool ValidateDatarate( int8_t datarate, uint16_t* channelsMask );
-
-/*!
- * \brief Limits the Tx power according to the number of enabled channels
- *
- * \param [IN] txPower txPower to limit
- * \param [IN] maxBandTxPower Maximum band allowed TxPower
- *
- * \retval Returns the maximum valid tx power
- */
-static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower );
-
-/*!
- * \brief Verifies, if a value is in a given range.
- *
- * \param value Value to verify, if it is in range
- *
- * \param min Minimum possible value
- *
- * \param max Maximum possible value
- *
- * \retval Returns the maximum valid tx power
- */
-static bool ValueInRange( int8_t value, int8_t min, int8_t max );
-
-/*!
- * \brief Calculates the next datarate to set, when ADR is on or off
- *
- * \param [IN] adrEnabled Specify whether ADR is on or off
- *
- * \param [IN] updateChannelMask Set to true, if the channel masks shall be updated
- *
- * \param [OUT] datarateOut Reports the datarate which will be used next
- *
- * \retval Returns the state of ADR ack request
- */
-static bool AdrNextDr( bool adrEnabled, bool updateChannelMask, int8_t* datarateOut );
-
-/*!
- * \brief Disables channel in a specified channel mask
- *
- * \param [IN] id - Id of the channel
- *
- * \param [IN] mask - Pointer to the channel mask to edit
- *
- * \retval [true, if disable was successful, false if not]
- */
-static bool DisableChannelInMask( uint8_t id, uint16_t* mask );
-
-/*!
  * \brief Decodes MAC commands in the fOpts field and in the payload
  */
 static void ProcessMacCommands( uint8_t *payload, uint8_t macIndex, uint8_t commandsSize, uint8_t snr );
@@ -938,13 +575,6 @@
 static LoRaMacStatus_t ScheduleTx( void );
 
 /*
- * \brief Sets the duty cycle for the join procedure.
- *
- * \retval Duty cycle
- */
-static uint16_t JoinDutyCycle( void );
-
-/*
  * \brief Calculates the back-off time for the band of a channel.
  *
  * \param [IN] channel     The last Tx channel index
@@ -952,12 +582,21 @@
 static void CalculateBackOff( uint8_t channel );
 
 /*
- * \brief Alternates the datarate of the channel for the join request.
+ * \brief Gets the index of the confirm queue of a specific MLME-request
  *
- * \param [IN] nbTrials    Number of performed join requests.
- * \retval Datarate to apply
+ * \param [IN] queue        MLME-Confirm queue pointer
+ * \param [IN] req          MLME-Request to validate
+ * \retval Index of the MLME-Confirm. 0xFF is no entry found.
  */
-static int8_t AlternateDatarate( uint16_t nbTrials );
+static uint8_t GetMlmeConfirmIndex( MlmeConfirmQueue_t* queue, Mlme_t req );
+
+/*
+ * \brief Sets the status of all MLME requests in the queue to the provided status
+ *
+ * \param [IN] queue        MLME-Confirm queue pointer
+ * \param [IN] status       MLME-Status to set
+ */
+static void SetEveryMlmeConfirmStatus( MlmeConfirmQueue_t* queue, LoRaMacEventInfoStatus_t status );
 
 /*!
  * \brief LoRaMAC layer prepared frame buffer transmission with channel specification
@@ -965,10 +604,10 @@
  * \remark PrepareFrame must be called at least once before calling this
  *         function.
  *
- * \param [IN] channel     Channel parameters
- * \retval status          Status of the operation.
+ * \param [IN] channel     Channel to transmit on
+ * \retval Time delay      Returns a time which shall delay the next TX.
  */
-LoRaMacStatus_t SendFrameOnChannel( ChannelParams_t channel );
+TimerTime_t SendFrameOnChannel( uint8_t channel );
 
 /*!
  * \brief Sets the radio in continuous transmission mode
@@ -987,7 +626,7 @@
  *
  * \param [IN] timeout     Time in seconds while the radio is kept in continuous wave mode
  * \param [IN] frequency   RF frequency to be set.
- * \param [IN] power       RF ouptput power to be set.
+ * \param [IN] power       RF output power to be set.
  * \retval status          Status of the operation.
  */
 LoRaMacStatus_t SetTxContinuousWave1( uint16_t timeout, uint32_t frequency, uint8_t power );
@@ -997,58 +636,11 @@
  */
 static void ResetMacParameters( void );
 
-/*
- * Rx window precise timing
- *
- * For more details please consult the following document, chapter 3.1.2.
- * http://www.semtech.com/images/datasheet/SX1272_settings_for_LoRaWAN_v2.0.pdf
- * or
- * http://www.semtech.com/images/datasheet/SX1276_settings_for_LoRaWAN_v2.0.pdf
- *
- *                 Downlink start: T = Tx + 1s (+/- 20 us)
- *                            |
- *             TRxEarly       |        TRxLate
- *                |           |           |
- *                |           |           +---+---+---+---+---+---+---+---+
- *                |           |           |       Latest Rx window        |
- *                |           |           +---+---+---+---+---+---+---+---+
- *                |           |           |
- *                +---+---+---+---+---+---+---+---+
- *                |       Earliest Rx window      |
- *                +---+---+---+---+---+---+---+---+
- *                            |
- *                            +---+---+---+---+---+---+---+---+
- *Downlink preamble 8 symbols |   |   |   |   |   |   |   |   |
- *                            +---+---+---+---+---+---+---+---+
- *
- *                     Worst case Rx window timings
- *
- * TRxLate  = DEFAULT_MIN_RX_SYMBOLS * tSymbol - RADIO_WAKEUP_TIME
- * TRxEarly = 8 - DEFAULT_MIN_RX_SYMBOLS * tSymbol - RxWindowTimeout - RADIO_WAKEUP_TIME
- *
- * TRxLate - TRxEarly = 2 * DEFAULT_SYSTEM_MAX_RX_ERROR
- *
- * RxOffset = ( TRxLate + TRxEarly ) / 2
- *
- * RxWindowTimeout = ( 2 * DEFAULT_MIN_RX_SYMBOLS - 8 ) * tSymbol + 2 * DEFAULT_SYSTEM_MAX_RX_ERROR
- * RxOffset = 4 * tSymbol - RxWindowTimeout / 2 - RADIO_WAKE_UP_TIME
- *
- * Minimal value of RxWindowTimeout must be 5 symbols which implies that the system always tolerates at least an error of 1.5 * tSymbol
- */
-/*!
- * Computes the Rx window parameters.
- *
- * \param [IN] datarate     Rx window datarate to be used
- * \param [IN] rxError      Maximum timing error of the receiver. in milliseconds
- *                          The receiver will turn on in a [-rxError : +rxError] ms
- *                          interval around RxOffset
- *
- * \retval rxConfigParams   Returns a RxConfigParams_t structure.
- */
-static RxConfigParams_t ComputeRxWindowParameters( int8_t datarate, uint32_t rxError );
-
 static void OnRadioTxDone( void )
 {
+    GetPhyParams_t getPhy;
+    PhyParam_t phyParam;
+    SetBandTxDoneParams_t txDone;
     TimerTime_t curTime = TimerGetCurrentTime( );
 
     if( LoRaMacDeviceClass != CLASS_C )
@@ -1072,15 +664,16 @@
         }
         if( ( LoRaMacDeviceClass == CLASS_C ) || ( NodeAckRequested == true ) )
         {
-            TimerSetValue( &AckTimeoutTimer, RxWindow2Delay + ACK_TIMEOUT +
-                                             randr( -ACK_TIMEOUT_RND, ACK_TIMEOUT_RND ) );
+            getPhy.Attribute = PHY_ACK_TIMEOUT;
+            phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+            TimerSetValue( &AckTimeoutTimer, RxWindow2Delay + phyParam.Value );
             TimerStart( &AckTimeoutTimer );
         }
     }
     else
     {
         McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_OK;
-        MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT;
+        SetEveryMlmeConfirmStatus( MlmeConfirmQueue, LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT );
 
         if( LoRaMacFlags.Value == 0 )
         {
@@ -1089,12 +682,25 @@
         LoRaMacFlags.Bits.MacDone = 1;
     }
 
+    // Verify if the last uplink was a join request
+    if( ( LoRaMacFlags.Bits.MlmeReq == 1 ) && ( MlmeConfirm.MlmeRequest == MLME_JOIN ) )
+    {
+        LastTxIsJoinRequest = true;
+    }
+    else
+    {
+        LastTxIsJoinRequest = false;
+    }
+
+    // Store last Tx channel
+    LastTxChannel = Channel;
     // Update last tx done time for the current channel
-    Bands[Channels[Channel].Band].LastTxDoneTime = curTime;
+    txDone.Channel = Channel;
+    txDone.Joined = IsLoRaMacNetworkJoined;
+    txDone.LastTxDoneTime = curTime;
+    RegionSetBandTxDone( LoRaMacRegion, &txDone );
     // Update Aggregated last tx done time
     AggregatedLastTxDoneTime = curTime;
-    // Update Backoff
-    CalculateBackOff( Channel );
 
     if( NodeAckRequested == false )
     {
@@ -1124,8 +730,13 @@
 {
     LoRaMacHeader_t macHdr;
     LoRaMacFrameCtrl_t fCtrl;
+    ApplyCFListParams_t applyCFList;
+    GetPhyParams_t getPhy;
+    PhyParam_t phyParam;
     bool skipIndication = false;
 
+    uint8_t index = 0;
+
     uint8_t pktHeaderLen = 0;
     uint32_t address = 0;
     uint8_t appPayloadStartIndex = 0;
@@ -1164,6 +775,21 @@
     Radio.Sleep( );
     TimerStop( &RxWindowTimer2 );
 
+    // This function must be called even if we are not in class b mode yet.
+    if( LoRaMacClassBRxBeacon( payload, size ) == true )
+    {
+        return;
+    }
+    // Check if we expect a ping slot.
+    if( LoRaMacDeviceClass == CLASS_B )
+    {
+        if( LoRaMacClassBIsPingExpected( ) == true )
+        {
+            LoRaMacClassBSetPingSlotState( PINGSLOT_STATE_SET_TIMER );
+            LoRaMacClassBPingSlotTimerEvent( );
+        }
+    }
+
     macHdr.Value = payload[pktHeaderLen++];
 
     switch( macHdr.Bits.MType )
@@ -1186,72 +812,80 @@
             micRx |= ( ( uint32_t )LoRaMacRxPayload[size - LORAMAC_MFR_LEN + 2] << 16 );
             micRx |= ( ( uint32_t )LoRaMacRxPayload[size - LORAMAC_MFR_LEN + 3] << 24 );
 
-            if( micRx == mic )
+            index = GetMlmeConfirmIndex( MlmeConfirmQueue, MLME_JOIN );
+            if( index < LORA_MAC_MLME_CONFIRM_QUEUE_LEN )
             {
-                LoRaMacJoinComputeSKeys( LoRaMacAppKey, LoRaMacRxPayload + 1, LoRaMacDevNonce, LoRaMacNwkSKey, LoRaMacAppSKey );
-
-                LoRaMacNetID = ( uint32_t )LoRaMacRxPayload[4];
-                LoRaMacNetID |= ( ( uint32_t )LoRaMacRxPayload[5] << 8 );
-                LoRaMacNetID |= ( ( uint32_t )LoRaMacRxPayload[6] << 16 );
-
-                LoRaMacDevAddr = ( uint32_t )LoRaMacRxPayload[7];
-                LoRaMacDevAddr |= ( ( uint32_t )LoRaMacRxPayload[8] << 8 );
-                LoRaMacDevAddr |= ( ( uint32_t )LoRaMacRxPayload[9] << 16 );
-                LoRaMacDevAddr |= ( ( uint32_t )LoRaMacRxPayload[10] << 24 );
-
-                // DLSettings
-                LoRaMacParams.Rx1DrOffset = ( LoRaMacRxPayload[11] >> 4 ) & 0x07;
-                LoRaMacParams.Rx2Channel.Datarate = LoRaMacRxPayload[11] & 0x0F;
-
-                // RxDelay
-                LoRaMacParams.ReceiveDelay1 = ( LoRaMacRxPayload[12] & 0x0F );
-                if( LoRaMacParams.ReceiveDelay1 == 0 )
+                if( micRx == mic )
                 {
-                    LoRaMacParams.ReceiveDelay1 = 1;
+                    LoRaMacJoinComputeSKeys( LoRaMacAppKey, LoRaMacRxPayload + 1, LoRaMacDevNonce, LoRaMacNwkSKey, LoRaMacAppSKey );
+
+                    LoRaMacNetID = ( uint32_t )LoRaMacRxPayload[4];
+                    LoRaMacNetID |= ( ( uint32_t )LoRaMacRxPayload[5] << 8 );
+                    LoRaMacNetID |= ( ( uint32_t )LoRaMacRxPayload[6] << 16 );
+
+                    LoRaMacDevAddr = ( uint32_t )LoRaMacRxPayload[7];
+                    LoRaMacDevAddr |= ( ( uint32_t )LoRaMacRxPayload[8] << 8 );
+                    LoRaMacDevAddr |= ( ( uint32_t )LoRaMacRxPayload[9] << 16 );
+                    LoRaMacDevAddr |= ( ( uint32_t )LoRaMacRxPayload[10] << 24 );
+
+                    // DLSettings
+                    LoRaMacParams.Rx1DrOffset = ( LoRaMacRxPayload[11] >> 4 ) & 0x07;
+                    LoRaMacParams.Rx2Channel.Datarate = LoRaMacRxPayload[11] & 0x0F;
+
+                    // RxDelay
+                    LoRaMacParams.ReceiveDelay1 = ( LoRaMacRxPayload[12] & 0x0F );
+                    if( LoRaMacParams.ReceiveDelay1 == 0 )
+                    {
+                        LoRaMacParams.ReceiveDelay1 = 1;
+                    }
+                    LoRaMacParams.ReceiveDelay1 *= 1000;
+                    LoRaMacParams.ReceiveDelay2 = LoRaMacParams.ReceiveDelay1 + 1000;
+
+                    // Apply CF list
+                    applyCFList.Payload = &LoRaMacRxPayload[13];
+                    // Size of the regular payload is 12. Plus 1 byte MHDR and 4 bytes MIC
+                    applyCFList.Size = size - 17;
+
+                    RegionApplyCFList( LoRaMacRegion, &applyCFList );
+
+                    MlmeConfirmQueue[index].Status = LORAMAC_EVENT_INFO_STATUS_OK;
+                    IsLoRaMacNetworkJoined = true;
+                    LoRaMacParams.ChannelsDatarate = LoRaMacParamsDefaults.ChannelsDatarate;
                 }
-                LoRaMacParams.ReceiveDelay1 *= 1e3;
-                LoRaMacParams.ReceiveDelay2 = LoRaMacParams.ReceiveDelay1 + 1e3;
-
-#if !( defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) )
-                //CFList
-                if( ( size - 1 ) > 16 )
+                else
                 {
-                    ChannelParams_t param;
-                    param.DrRange.Value = ( DR_5 << 4 ) | DR_0;
-
-                    LoRaMacState |= LORAMAC_TX_CONFIG;
-                    for( uint8_t i = 3, j = 0; i < ( 5 + 3 ); i++, j += 3 )
-                    {
-                        param.Frequency = ( ( uint32_t )LoRaMacRxPayload[13 + j] | ( ( uint32_t )LoRaMacRxPayload[14 + j] << 8 ) | ( ( uint32_t )LoRaMacRxPayload[15 + j] << 16 ) ) * 100;
-                        if( param.Frequency != 0 )
-                        {
-                            LoRaMacChannelAdd( i, param );
-                        }
-                        else
-                        {
-                            LoRaMacChannelRemove( i );
-                        }
-                    }
-                    LoRaMacState &= ~LORAMAC_TX_CONFIG;
+                    MlmeConfirmQueue[index].Status = LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL;
                 }
-#endif
-                MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_OK;
-                IsLoRaMacNetworkJoined = true;
-                LoRaMacParams.ChannelsDatarate = LoRaMacParamsDefaults.ChannelsDatarate;
-            }
-            else
-            {
-                MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL;
             }
             break;
         case FRAME_TYPE_DATA_CONFIRMED_DOWN:
         case FRAME_TYPE_DATA_UNCONFIRMED_DOWN:
             {
+                // Check if the received payload size is valid
+                getPhy.UplinkDwellTime = LoRaMacParams.DownlinkDwellTime;
+                getPhy.Datarate = McpsIndication.RxDatarate;
+                getPhy.Attribute = PHY_MAX_PAYLOAD;
+
+                // Get the maximum payload length
+                if( LoRaMacParams.RepeaterSupport == true )
+                {
+                    getPhy.Attribute = PHY_MAX_PAYLOAD_REPEATER;
+                }
+                phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+                if( (uint32_t)MAX( (int16_t)0, ( int16_t )( ( int16_t )size - ( int16_t )LORA_MAC_FRMPAYLOAD_OVERHEAD ) ) > phyParam.Value )
+                {
+                    McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
+                    PrepareRxDoneAbort( );
+                    return;
+                }
+
                 address = payload[pktHeaderLen++];
                 address |= ( (uint32_t)payload[pktHeaderLen++] << 8 );
                 address |= ( (uint32_t)payload[pktHeaderLen++] << 16 );
                 address |= ( (uint32_t)payload[pktHeaderLen++] << 24 );
 
+                fCtrl.Value = payload[pktHeaderLen++];
+
                 if( address != LoRaMacDevAddr )
                 {
                     curMulticastParams = MulticastChannels;
@@ -1274,6 +908,15 @@
                         PrepareRxDoneAbort( );
                         return;
                     }
+                    if( ( macHdr.Bits.MType != FRAME_TYPE_DATA_UNCONFIRMED_DOWN ) ||
+                        ( fCtrl.Bits.Ack == 1 ) ||
+                        ( fCtrl.Bits.AdrAckReq == 1 ) )
+                    {
+                        // Wrong multicast message format. Refer to chapter 11.2.2 of the specification
+                        McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_MULTICAST_FAIL;
+                        PrepareRxDoneAbort( );
+                        return;
+                    }
                 }
                 else
                 {
@@ -1283,8 +926,6 @@
                     downLinkCounter = DownLinkCounter;
                 }
 
-                fCtrl.Value = payload[pktHeaderLen++];
-
                 sequenceCounter = ( uint16_t )payload[pktHeaderLen++];
                 sequenceCounter |= ( uint16_t )payload[pktHeaderLen++] << 8;
 
@@ -1320,7 +961,9 @@
                 }
 
                 // Check for a the maximum allowed counter difference
-                if( sequenceCounterDiff >= MAX_FCNT_GAP )
+                getPhy.Attribute = PHY_MAX_FCNT_GAP;
+                phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+                if( sequenceCounterDiff >= phyParam.Value )
                 {
                     McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_DOWNLINK_TOO_MANY_FRAMES_LOSS;
                     McpsIndication.DownLinkCounter = downLinkCounter;
@@ -1394,7 +1037,7 @@
 
                     // This must be done before parsing the payload and the MAC commands.
                     // We need to reset the MacCommandsBufferIndex here, since we need
-                    // to take retransmissions and repititions into account. Error cases
+                    // to take retransmissions and repetitions into account. Error cases
                     // will be handled in function OnMacStateCheckTimerEvent.
                     if( McpsConfirm.McpsRequest == MCPS_CONFIRMED )
                     {
@@ -1418,8 +1061,7 @@
 
                         if( port == 0 )
                         {
-                            // Only allow frames which do not have fOpts
-                            if( fCtrl.Bits.FOptsLen == 0 )
+                            if( ( fCtrl.Bits.FOptsLen == 0 ) && ( multicast == 0 ) )
                             {
                                 LoRaMacPayloadDecrypt( payload + appPayloadStartIndex,
                                                        frameLen,
@@ -1439,7 +1081,7 @@
                         }
                         else
                         {
-                            if( fCtrl.Bits.FOptsLen > 0 )
+                            if( ( fCtrl.Bits.FOptsLen > 0 ) && ( multicast == 0 ) )
                             {
                                 // Decode Options field MAC commands. Omit the fPort.
                                 ProcessMacCommands( payload, 8, appPayloadStartIndex - 1, snr );
@@ -1544,12 +1186,14 @@
     }
 
     McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT;
-    MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT;
+    SetEveryMlmeConfirmStatus( MlmeConfirmQueue, LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT );
     LoRaMacFlags.Bits.MacDone = 1;
 }
 
 static void OnRadioRxError( void )
 {
+    bool classBRx = false;
+
     if( LoRaMacDeviceClass != CLASS_C )
     {
         Radio.Sleep( );
@@ -1559,32 +1203,52 @@
         OnRxWindow2TimerEvent( );
     }
 
-    if( RxSlot == 0 )
+    if( LoRaMacDeviceClass == CLASS_B )
     {
-        if( NodeAckRequested == true )
+        if( LoRaMacClassBIsBeaconExpected( ) == true )
+        {
+            LoRaMacClassBSetBeaconState( BEACON_STATE_TIMEOUT );
+            LoRaMacClassBBeaconTimerEvent( );
+            classBRx = true;
+        }
+        if( LoRaMacClassBIsPingExpected( ) == true )
         {
-            McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX1_ERROR;
+            LoRaMacClassBSetPingSlotState( PINGSLOT_STATE_SET_TIMER );
+            LoRaMacClassBPingSlotTimerEvent( );
+            classBRx = true;
         }
-        MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX1_ERROR;
-
-        if( TimerGetElapsedTime( AggregatedLastTxDoneTime ) >= RxWindow2Delay )
+    }
+
+    if( classBRx == false )
+    {
+        if( RxSlot == 0 )
         {
+            if( NodeAckRequested == true )
+            {
+                McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX1_ERROR;
+            }
+            SetEveryMlmeConfirmStatus( MlmeConfirmQueue, LORAMAC_EVENT_INFO_STATUS_RX1_ERROR );
+            if( TimerGetElapsedTime( AggregatedLastTxDoneTime ) >= RxWindow2Delay )
+            {
+                LoRaMacFlags.Bits.MacDone = 1;
+            }
+        }
+        else
+        {
+            if( NodeAckRequested == true )
+            {
+                McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX2_ERROR;
+            }
+            SetEveryMlmeConfirmStatus( MlmeConfirmQueue, LORAMAC_EVENT_INFO_STATUS_RX2_ERROR );
             LoRaMacFlags.Bits.MacDone = 1;
         }
     }
-    else
-    {
-        if( NodeAckRequested == true )
-        {
-            McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX2_ERROR;
-        }
-        MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX2_ERROR;
-        LoRaMacFlags.Bits.MacDone = 1;
-    }
 }
 
 static void OnRadioRxTimeout( void )
 {
+    bool classBRx = false;
+
     if( LoRaMacDeviceClass != CLASS_C )
     {
         Radio.Sleep( );
@@ -1594,21 +1258,64 @@
         OnRxWindow2TimerEvent( );
     }
 
-    if( RxSlot == 1 )
+    if( LoRaMacDeviceClass == CLASS_B )
     {
-        if( NodeAckRequested == true )
+        if( LoRaMacClassBIsBeaconExpected( ) == true )
+        {
+            LoRaMacClassBSetBeaconState( BEACON_STATE_TIMEOUT );
+            LoRaMacClassBBeaconTimerEvent( );
+            classBRx = true;
+        }
+
+        if( LoRaMacClassBIsPingExpected( ) == true )
+        {
+            LoRaMacClassBSetPingSlotState( PINGSLOT_STATE_SET_TIMER );
+            LoRaMacClassBPingSlotTimerEvent( );
+            classBRx = true;
+        }
+    }
+
+    if( classBRx == false )
+    {
+        if( RxSlot == 0 )
         {
-            McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT;
+            if( NodeAckRequested == true )
+            {
+                McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX1_TIMEOUT;
+            }
+            SetEveryMlmeConfirmStatus( MlmeConfirmQueue, LORAMAC_EVENT_INFO_STATUS_RX1_TIMEOUT );
+
+            if( TimerGetElapsedTime( AggregatedLastTxDoneTime ) >= RxWindow2Delay )
+            {
+                LoRaMacFlags.Bits.MacDone = 1;
+            }
         }
-        MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT;
-        LoRaMacFlags.Bits.MacDone = 1;
+        else
+        {
+            if( NodeAckRequested == true )
+            {
+                McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT;
+            }
+            SetEveryMlmeConfirmStatus( MlmeConfirmQueue, LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT );
+
+            if( LoRaMacDeviceClass != CLASS_C )
+            {
+                LoRaMacFlags.Bits.MacDone = 1;
+            }
+        }
     }
 }
 
 static void OnMacStateCheckTimerEvent( void )
 {
+    GetPhyParams_t getPhy;
+    PhyParam_t phyParam;
+    uint8_t index = 0;
+    bool noTx = false;
+    uint8_t i, j = 0;
+
+
     TimerStop( &MacStateCheckTimer );
-    bool txTimeout = false;
 
     if( LoRaMacFlags.Bits.MacDone == 1 )
     {
@@ -1620,6 +1327,9 @@
 
         if( ( LoRaMacFlags.Bits.MlmeReq == 1 ) || ( ( LoRaMacFlags.Bits.McpsReq == 1 ) ) )
         {
+            // Get a status of any request and check if we have a TX timeout
+            MlmeConfirm.Status = MlmeConfirmQueue[0].Status;
+
             if( ( McpsConfirm.Status == LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT ) ||
                 ( MlmeConfirm.Status == LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT ) )
             {
@@ -1629,19 +1339,30 @@
                 McpsConfirm.NbRetries = AckTimeoutRetriesCounter;
                 McpsConfirm.AckReceived = false;
                 McpsConfirm.TxTimeOnAir = 0;
-                txTimeout = true;
+                noTx = true;
+            }
+
+            index = GetMlmeConfirmIndex( MlmeConfirmQueue, MLME_BEACON_ACQUISITION );
+            if( ( index < LORA_MAC_MLME_CONFIRM_QUEUE_LEN ) && ( LoRaMacFlags.Bits.McpsReq == 0 ) )
+            {
+                if( LoRaMacFlags.Bits.MlmeReq == 1 )
+                {
+                    noTx = true;
+                    LoRaMacState &= ~LORAMAC_TX_RUNNING;
+                }
             }
         }
 
-        if( ( NodeAckRequested == false ) && ( txTimeout == false ) )
+        if( ( NodeAckRequested == false ) && ( noTx == false ) )
         {
             if( ( LoRaMacFlags.Bits.MlmeReq == 1 ) || ( ( LoRaMacFlags.Bits.McpsReq == 1 ) ) )
             {
-                if( ( LoRaMacFlags.Bits.MlmeReq == 1 ) && ( MlmeConfirm.MlmeRequest == MLME_JOIN ) )
+                index = GetMlmeConfirmIndex( MlmeConfirmQueue, MLME_JOIN );
+                if( ( LoRaMacFlags.Bits.MlmeReq == 1 ) && ( index < LORA_MAC_MLME_CONFIRM_QUEUE_LEN ) )
                 {// Procedure for the join request
                     MlmeConfirm.NbRetries = JoinRequestTrials;
 
-                    if( MlmeConfirm.Status == LORAMAC_EVENT_INFO_STATUS_OK )
+                    if( MlmeConfirmQueue[index].Status == LORAMAC_EVENT_INFO_STATUS_OK )
                     {// Node joined successfully
                         UpLinkCounter = 0;
                         ChannelsNbRepCounter = 0;
@@ -1666,7 +1387,7 @@
                     if( ( ChannelsNbRepCounter >= LoRaMacParams.ChannelsNbRep ) || ( LoRaMacFlags.Bits.McpsInd == 1 ) )
                     {
                         if( LoRaMacFlags.Bits.McpsInd == 0 )
-                        {   // Maximum repititions without downlink. Reset MacCommandsBufferIndex. Increase ADR Ack counter.
+                        {   // Maximum repetitions without downlink. Reset MacCommandsBufferIndex. Increase ADR Ack counter.
                             // Only process the case when the MAC did not receive a downlink.
                             MacCommandsBufferIndex = 0;
                             AdrAckCounter++;
@@ -1716,7 +1437,11 @@
 
                 if( ( AckTimeoutRetriesCounter % 2 ) == 1 )
                 {
-                    LoRaMacParams.ChannelsDatarate = MAX( LoRaMacParams.ChannelsDatarate - 1, LORAMAC_TX_MIN_DATARATE );
+                    getPhy.Attribute = PHY_NEXT_LOWER_TX_DR;
+                    getPhy.UplinkDwellTime = LoRaMacParams.UplinkDwellTime;
+                    getPhy.Datarate = LoRaMacParams.ChannelsDatarate;
+                    phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+                    LoRaMacParams.ChannelsDatarate = phyParam.Value;
                 }
                 // Try to send the frame again
                 if( ScheduleTx( ) == LORAMAC_STATUS_OK )
@@ -1742,21 +1467,8 @@
             }
             else
             {
-#if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 )
-                // Re-enable default channels LC1, LC2, LC3
-                LoRaMacParams.ChannelsMask[0] = LoRaMacParams.ChannelsMask[0] | ( LC( 1 ) + LC( 2 ) + LC( 3 ) );
-#elif defined( USE_BAND_470 )
-                // Re-enable default channels
-                memcpy1( ( uint8_t* )LoRaMacParams.ChannelsMask, ( uint8_t* )LoRaMacParamsDefaults.ChannelsMask, sizeof( LoRaMacParams.ChannelsMask ) );
-#elif defined( USE_BAND_915 )
-                // Re-enable default channels
-                memcpy1( ( uint8_t* )LoRaMacParams.ChannelsMask, ( uint8_t* )LoRaMacParamsDefaults.ChannelsMask, sizeof( LoRaMacParams.ChannelsMask ) );
-#elif defined( USE_BAND_915_HYBRID )
-                // Re-enable default channels
-                ReenableChannels( LoRaMacParamsDefaults.ChannelsMask[4], LoRaMacParams.ChannelsMask );
-#else
-    #error "Please define a frequency band in the compiler options."
-#endif
+                RegionInitDefaults( LoRaMacRegion, INIT_TYPE_RESTORE );
+
                 LoRaMacState &= ~LORAMAC_TX_RUNNING;
 
                 MacCommandsBufferIndex = 0;
@@ -1785,12 +1497,44 @@
 
         if( LoRaMacFlags.Bits.MlmeReq == 1 )
         {
-            LoRaMacPrimitives->MacMlmeConfirm( &MlmeConfirm );
-            LoRaMacFlags.Bits.MlmeReq = 0;
+            j = MlmeConfirmQueueCnt;
+            for( i = 0; i < MlmeConfirmQueueCnt; i++ )
+            {
+                if( MlmeConfirmQueue[i].MlmeRequest == MLME_BEACON_ACQUISITION )
+                {
+                    if( LoRaMacClassBIsAcquisitionPending( ) == true )
+                    {
+                        MlmeConfirmQueue[0].MlmeRequest = MLME_BEACON_ACQUISITION;
+                        MlmeConfirmQueue[0].Status = MlmeConfirmQueue[i].Status;
+                        continue;
+                    }
+                }
+                j--;
+                MlmeConfirm.Status = MlmeConfirmQueue[i].Status;
+                MlmeConfirm.MlmeRequest = MlmeConfirmQueue[i].MlmeRequest;
+                LoRaMacPrimitives->MacMlmeConfirm( &MlmeConfirm );
+            }
+            MlmeConfirmQueueCnt = j;
+
+            if( MlmeConfirmQueueCnt == 0 )
+            {
+                LoRaMacFlags.Bits.MlmeReq = 0;
+            }
+        }
+
+        if( LoRaMacFlags.Bits.MlmeInd == 1 )
+        {
+            LoRaMacPrimitives->MacMlmeIndication( &MlmeIndication );
+            LoRaMacFlags.Bits.MlmeInd = 0;
         }
 
         // Procedure done. Reset variables.
         LoRaMacFlags.Bits.MacDone = 0;
+
+        if( LoRaMacDeviceClass == CLASS_B )
+        {
+            LoRaMacClassBResumeBeaconing( );
+        }
     }
     else
     {
@@ -1818,15 +1562,20 @@
 {
     LoRaMacHeader_t macHdr;
     LoRaMacFrameCtrl_t fCtrl;
+    AlternateDrParams_t altDr;
+    uint8_t index = 0;
 
     TimerStop( &TxDelayedTimer );
     LoRaMacState &= ~LORAMAC_TX_DELAYED;
 
-    if( ( LoRaMacFlags.Bits.MlmeReq == 1 ) && ( MlmeConfirm.MlmeRequest == MLME_JOIN ) )
+    index = GetMlmeConfirmIndex( MlmeConfirmQueue, MLME_JOIN );
+
+    if( ( LoRaMacFlags.Bits.MlmeReq == 1 ) && ( index < LORA_MAC_MLME_CONFIRM_QUEUE_LEN ) )
     {
         ResetMacParameters( );
-        // Add a +1, since we start to count from 0
-        LoRaMacParams.ChannelsDatarate = AlternateDatarate( JoinRequestTrials + 1 );
+
+        altDr.NbTrials = JoinRequestTrials + 1;
+        LoRaMacParams.ChannelsDatarate = RegionAlternateDr( LoRaMacRegion, &altDr );
 
         macHdr.Value = 0;
         macHdr.Bits.MType = FRAME_TYPE_JOIN_REQ;
@@ -1848,35 +1597,45 @@
     TimerStop( &RxWindowTimer1 );
     RxSlot = 0;
 
+    RxWindow1Config.Channel = Channel;
+    RxWindow1Config.DrOffset = LoRaMacParams.Rx1DrOffset;
+    RxWindow1Config.DownlinkDwellTime = LoRaMacParams.DownlinkDwellTime;
+    RxWindow1Config.RepeaterSupport = LoRaMacParams.RepeaterSupport;
+    RxWindow1Config.RxContinuous = false;
+    RxWindow1Config.Window = RxSlot;
+
     if( LoRaMacDeviceClass == CLASS_C )
     {
         Radio.Standby( );
     }
 
-#if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 )
-    RxWindowSetup( Channels[Channel].Frequency, RxWindowsParams[0].Datarate, RxWindowsParams[0].Bandwidth, RxWindowsParams[0].RxWindowTimeout, false );
-#elif defined( USE_BAND_470 )
-    RxWindowSetup( LORAMAC_FIRST_RX1_CHANNEL + ( Channel % 48 ) * LORAMAC_STEPWIDTH_RX1_CHANNEL, RxWindowsParams[0].Datarate, RxWindowsParams[0].Bandwidth, RxWindowsParams[0].RxWindowTimeout, false );
-#elif ( defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) )
-    RxWindowSetup( LORAMAC_FIRST_RX1_CHANNEL + ( Channel % 8 ) * LORAMAC_STEPWIDTH_RX1_CHANNEL, RxWindowsParams[0].Datarate, RxWindowsParams[0].Bandwidth, RxWindowsParams[0].RxWindowTimeout, false );
-#else
-    #error "Please define a frequency band in the compiler options."
-#endif
+    RegionRxConfig( LoRaMacRegion, &RxWindow1Config, ( int8_t* )&McpsIndication.RxDatarate );
+    RxWindowSetup( RxWindow1Config.RxContinuous, LoRaMacParams.MaxRxWindow );
 }
 
 static void OnRxWindow2TimerEvent( void )
 {
-    bool rxContinuousMode = false;
-
     TimerStop( &RxWindowTimer2 );
 
-    if( LoRaMacDeviceClass == CLASS_C )
+    RxWindow2Config.Channel = Channel;
+    RxWindow2Config.Frequency = LoRaMacParams.Rx2Channel.Frequency;
+    RxWindow2Config.DownlinkDwellTime = LoRaMacParams.DownlinkDwellTime;
+    RxWindow2Config.RepeaterSupport = LoRaMacParams.RepeaterSupport;
+    RxWindow2Config.Window = 1;
+
+    if( LoRaMacDeviceClass != CLASS_C )
     {
-        rxContinuousMode = true;
+        RxWindow2Config.RxContinuous = false;
+    }
+    else
+    {
+        RxWindow2Config.RxContinuous = true;
     }
-    if( RxWindowSetup( LoRaMacParams.Rx2Channel.Frequency, RxWindowsParams[1].Datarate, RxWindowsParams[1].Bandwidth, RxWindowsParams[1].RxWindowTimeout, rxContinuousMode ) == true )
+
+    if( RegionRxConfig( LoRaMacRegion, &RxWindow2Config, ( int8_t* )&McpsIndication.RxDatarate ) == true )
     {
-        RxSlot = 1;
+        RxWindowSetup( RxWindow2Config.RxContinuous, LoRaMacParams.MaxRxWindow );
+        RxSlot = RxWindow2Config.Window;
     }
 }
 
@@ -1895,221 +1654,93 @@
     }
 }
 
-static bool SetNextChannel( TimerTime_t* time )
+static void RxWindowSetup( bool rxContinuous, uint32_t maxRxWindow )
 {
-    uint8_t nbEnabledChannels = 0;
-    uint8_t delayTx = 0;
-    uint8_t enabledChannels[LORA_MAX_NB_CHANNELS];
-    TimerTime_t nextTxDelay = ( TimerTime_t )( -1 );
-
-    memset1( enabledChannels, 0, LORA_MAX_NB_CHANNELS );
-
-#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
-    if( CountNbEnabled125kHzChannels( ChannelsMaskRemaining ) == 0 )
-    { // Restore default channels
-        memcpy1( ( uint8_t* ) ChannelsMaskRemaining, ( uint8_t* ) LoRaMacParams.ChannelsMask, 8 );
-    }
-    if( ( LoRaMacParams.ChannelsDatarate >= DR_4 ) && ( ( ChannelsMaskRemaining[4] & 0x00FF ) == 0 ) )
-    { // Make sure, that the channels are activated
-        ChannelsMaskRemaining[4] = LoRaMacParams.ChannelsMask[4];
-    }
-#elif defined( USE_BAND_470 )
-    if( ( CountBits( LoRaMacParams.ChannelsMask[0], 16 ) == 0 ) &&
-        ( CountBits( LoRaMacParams.ChannelsMask[1], 16 ) == 0 ) &&
-        ( CountBits( LoRaMacParams.ChannelsMask[2], 16 ) == 0 ) &&
-        ( CountBits( LoRaMacParams.ChannelsMask[3], 16 ) == 0 ) &&
-        ( CountBits( LoRaMacParams.ChannelsMask[4], 16 ) == 0 ) &&
-        ( CountBits( LoRaMacParams.ChannelsMask[5], 16 ) == 0 ) )
-    {
-        memcpy1( ( uint8_t* )LoRaMacParams.ChannelsMask, ( uint8_t* )LoRaMacParamsDefaults.ChannelsMask, sizeof( LoRaMacParams.ChannelsMask ) );
-    }
-#else
-    if( CountBits( LoRaMacParams.ChannelsMask[0], 16 ) == 0 )
-    {
-        // Re-enable default channels, if no channel is enabled
-        LoRaMacParams.ChannelsMask[0] = LoRaMacParams.ChannelsMask[0] | ( LC( 1 ) + LC( 2 ) + LC( 3 ) );
-    }
-#endif
-
-    // Update Aggregated duty cycle
-    if( AggregatedTimeOff <= TimerGetElapsedTime( AggregatedLastTxDoneTime ) )
+    if( rxContinuous == false )
     {
-        AggregatedTimeOff = 0;
-
-        // Update bands Time OFF
-        for( uint8_t i = 0; i < LORA_MAX_NB_BANDS; i++ )
-        {
-            if( ( IsLoRaMacNetworkJoined == false ) || ( DutyCycleOn == true ) )
-            {
-                if( Bands[i].TimeOff <= TimerGetElapsedTime( Bands[i].LastTxDoneTime ) )
-                {
-                    Bands[i].TimeOff = 0;
-                }
-                if( Bands[i].TimeOff != 0 )
-                {
-                    nextTxDelay = MIN( Bands[i].TimeOff - TimerGetElapsedTime( Bands[i].LastTxDoneTime ), nextTxDelay );
-                }
-            }
-            else
-            {
-                if( DutyCycleOn == false )
-                {
-                    Bands[i].TimeOff = 0;
-                }
-            }
-        }
-
-        // Search how many channels are enabled
-        for( uint8_t i = 0, k = 0; i < LORA_MAX_NB_CHANNELS; i += 16, k++ )
-        {
-            for( uint8_t j = 0; j < 16; j++ )
-            {
-#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
-                if( ( ChannelsMaskRemaining[k] & ( 1 << j ) ) != 0 )
-#else
-                if( ( LoRaMacParams.ChannelsMask[k] & ( 1 << j ) ) != 0 )
-#endif
-                {
-                    if( Channels[i + j].Frequency == 0 )
-                    { // Check if the channel is enabled
-                        continue;
-                    }
-#if defined( USE_BAND_868 ) || defined( USE_BAND_433 ) || defined( USE_BAND_780 )
-                    if( IsLoRaMacNetworkJoined == false )
-                    {
-                        if( ( JOIN_CHANNELS & ( 1 << j ) ) == 0 )
-                        {
-                            continue;
-                        }
-                    }
-#endif
-                    if( ( ( Channels[i + j].DrRange.Fields.Min <= LoRaMacParams.ChannelsDatarate ) &&
-                          ( LoRaMacParams.ChannelsDatarate <= Channels[i + j].DrRange.Fields.Max ) ) == false )
-                    { // Check if the current channel selection supports the given datarate
-                        continue;
-                    }
-                    if( Bands[Channels[i + j].Band].TimeOff > 0 )
-                    { // Check if the band is available for transmission
-                        delayTx++;
-                        continue;
-                    }
-                    enabledChannels[nbEnabledChannels++] = i + j;
-                }
-            }
-        }
+        Radio.Rx( maxRxWindow );
     }
     else
     {
-        delayTx++;
-        nextTxDelay = AggregatedTimeOff - TimerGetElapsedTime( AggregatedLastTxDoneTime );
-    }
-
-    if( nbEnabledChannels > 0 )
-    {
-        Channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )];
-#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
-        if( Channel < ( LORA_MAX_NB_CHANNELS - 8 ) )
-        {
-            DisableChannelInMask( Channel, ChannelsMaskRemaining );
-        }
-#endif
-        *time = 0;
-        return true;
-    }
-    else
-    {
-        if( delayTx > 0 )
-        {
-            // Delay transmission due to AggregatedTimeOff or to a band time off
-            *time = nextTxDelay;
-            return true;
-        }
-        // Datarate not supported by any channel
-        *time = 0;
-        return false;
+        Radio.Rx( 0 ); // Continuous mode
     }
 }
 
-static bool RxWindowSetup( uint32_t freq, int8_t datarate, uint32_t bandwidth, uint16_t timeout, bool rxContinuous )
+static LoRaMacStatus_t SwitchClass( DeviceClass_t deviceClass )
 {
-    uint8_t downlinkDatarate = Datarates[datarate];
-    RadioModems_t modem;
-
-    if( Radio.GetStatus( ) == RF_IDLE )
+    LoRaMacStatus_t status = LORAMAC_STATUS_PARAMETER_INVALID;
+
+    switch( LoRaMacDeviceClass )
     {
-        Radio.SetChannel( freq );
-
-        // Store downlink datarate
-        McpsIndication.RxDatarate = ( uint8_t ) datarate;
-
-#if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 )
-        if( datarate == DR_7 )
+        case CLASS_A:
         {
-            modem = MODEM_FSK;
-            Radio.SetRxConfig( modem, 50e3, downlinkDatarate * 1e3, 0, 83.333e3, 5, timeout, false, 0, true, 0, 0, false, rxContinuous );
-        }
-        else
-        {
-            modem = MODEM_LORA;
-            Radio.SetRxConfig( modem, bandwidth, downlinkDatarate, 1, 0, 8, timeout, false, 0, false, 0, 0, true, rxContinuous );
-        }
-#elif defined( USE_BAND_470 ) || defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
-        modem = MODEM_LORA;
-        Radio.SetRxConfig( modem, bandwidth, downlinkDatarate, 1, 0, 8, timeout, false, 0, false, 0, 0, true, rxContinuous );
-#endif
-
-        if( RepeaterSupport == true )
-        {
-            Radio.SetMaxPayloadLength( modem, MaxPayloadOfDatarateRepeater[datarate] + LORA_MAC_FRMPAYLOAD_OVERHEAD );
+            if( deviceClass == CLASS_B )
+            {
+                status = LoRaMacClassBSwitchClass( deviceClass );
+                if( status == LORAMAC_STATUS_OK )
+                {
+                    LoRaMacDeviceClass = deviceClass;
+                }
+            }
+
+            if( deviceClass == CLASS_C )
+            {
+                LoRaMacDeviceClass = deviceClass;
+
+                // Set the NodeAckRequested indicator to default
+                NodeAckRequested = false;
+                OnRxWindow2TimerEvent( );
+
+                status = LORAMAC_STATUS_OK;
+            }
+            break;
         }
-        else
+        case CLASS_B:
         {
-            Radio.SetMaxPayloadLength( modem, MaxPayloadOfDatarate[datarate] + LORA_MAC_FRMPAYLOAD_OVERHEAD );
+            status = LoRaMacClassBSwitchClass( deviceClass );
+            if( status == LORAMAC_STATUS_OK )
+            {
+                LoRaMacDeviceClass = deviceClass;
+            }
+            break;
         }
-
-        if( rxContinuous == false )
+        case CLASS_C:
         {
-            Radio.Rx( LoRaMacParams.MaxRxWindow );
+            if( deviceClass == CLASS_A )
+            {
+                LoRaMacDeviceClass = deviceClass;
+
+                // Set the radio into sleep to setup a defined state
+                Radio.Sleep( );
+
+                status = LORAMAC_STATUS_OK;
+            }
+            break;
         }
-        else
-        {
-            Radio.Rx( 0 ); // Continuous mode
-        }
-        return true;
     }
-    return false;
-}
-
-static bool Rx2FreqInRange( uint32_t freq )
-{
-#if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 )
-    if( Radio.CheckRfFrequency( freq ) == true )
-#elif defined( USE_BAND_470 ) || defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
-    if( ( Radio.CheckRfFrequency( freq ) == true ) &&
-        ( freq >= LORAMAC_FIRST_RX1_CHANNEL ) &&
-        ( freq <= LORAMAC_LAST_RX1_CHANNEL ) &&
-        ( ( ( freq - ( uint32_t ) LORAMAC_FIRST_RX1_CHANNEL ) % ( uint32_t ) LORAMAC_STEPWIDTH_RX1_CHANNEL ) == 0 ) )
-#endif
-    {
-        return true;
-    }
-    return false;
+
+    return status;
 }
 
 static bool ValidatePayloadLength( uint8_t lenN, int8_t datarate, uint8_t fOptsLen )
 {
+    GetPhyParams_t getPhy;
+    PhyParam_t phyParam;
     uint16_t maxN = 0;
     uint16_t payloadSize = 0;
 
+    // Setup PHY request
+    getPhy.UplinkDwellTime = LoRaMacParams.UplinkDwellTime;
+    getPhy.Datarate = datarate;
+    getPhy.Attribute = PHY_MAX_PAYLOAD;
+
     // Get the maximum payload length
-    if( RepeaterSupport == true )
+    if( LoRaMacParams.RepeaterSupport == true )
     {
-        maxN = MaxPayloadOfDatarateRepeater[datarate];
+        getPhy.Attribute = PHY_MAX_PAYLOAD_REPEATER;
     }
-    else
-    {
-        maxN = MaxPayloadOfDatarate[datarate];
-    }
+    phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+    maxN = phyParam.Value;
 
     // Calculate the resulting payload size
     payloadSize = ( lenN + fOptsLen );
@@ -2122,258 +1753,6 @@
     return false;
 }
 
-static uint8_t CountBits( uint16_t mask, uint8_t nbBits )
-{
-    uint8_t nbActiveBits = 0;
-
-    for( uint8_t j = 0; j < nbBits; j++ )
-    {
-        if( ( mask & ( 1 << j ) ) == ( 1 << j ) )
-        {
-            nbActiveBits++;
-        }
-    }
-    return nbActiveBits;
-}
-
-#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
-static uint8_t CountNbEnabled125kHzChannels( uint16_t *channelsMask )
-{
-    uint8_t nb125kHzChannels = 0;
-
-    for( uint8_t i = 0, k = 0; i < LORA_MAX_NB_CHANNELS - 8; i += 16, k++ )
-    {
-        nb125kHzChannels += CountBits( channelsMask[k], 16 );
-    }
-
-    return nb125kHzChannels;
-}
-
-#if defined( USE_BAND_915_HYBRID )
-static void ReenableChannels( uint16_t mask, uint16_t* channelsMask )
-{
-    uint16_t blockMask = mask;
-
-    for( uint8_t i = 0, j = 0; i < 4; i++, j += 2 )
-    {
-        channelsMask[i] = 0;
-        if( ( blockMask & ( 1 << j ) ) != 0 )
-        {
-            channelsMask[i] |= 0x00FF;
-        }
-        if( ( blockMask & ( 1 << ( j + 1 ) ) ) != 0 )
-        {
-            channelsMask[i] |= 0xFF00;
-        }
-    }
-    channelsMask[4] = blockMask;
-    channelsMask[5] = 0x0000;
-}
-
-static bool ValidateChannelMask( uint16_t* channelsMask )
-{
-    bool chanMaskState = false;
-    uint16_t block1 = 0;
-    uint16_t block2 = 0;
-    uint8_t index = 0;
-
-    for( uint8_t i = 0; i < 4; i++ )
-    {
-        block1 = channelsMask[i] & 0x00FF;
-        block2 = channelsMask[i] & 0xFF00;
-
-        if( ( CountBits( block1, 16 ) > 5 ) && ( chanMaskState == false ) )
-        {
-            channelsMask[i] &= block1;
-            channelsMask[4] = 1 << ( i * 2 );
-            chanMaskState = true;
-            index = i;
-        }
-        else if( ( CountBits( block2, 16 ) > 5 ) && ( chanMaskState == false ) )
-        {
-            channelsMask[i] &= block2;
-            channelsMask[4] = 1 << ( i * 2 + 1 );
-            chanMaskState = true;
-            index = i;
-        }
-    }
-
-    // Do only change the channel mask, if we have found a valid block.
-    if( chanMaskState == true )
-    {
-        for( uint8_t i = 0; i < 4; i++ )
-        {
-            if( i != index )
-            {
-                channelsMask[i] = 0;
-            }
-        }
-    }
-    return chanMaskState;
-}
-#endif
-#endif
-
-static bool ValidateDatarate( int8_t datarate, uint16_t* channelsMask )
-{
-    if( ValueInRange( datarate, LORAMAC_TX_MIN_DATARATE, LORAMAC_TX_MAX_DATARATE ) == false )
-    {
-        return false;
-    }
-    for( uint8_t i = 0, k = 0; i < LORA_MAX_NB_CHANNELS; i += 16, k++ )
-    {
-        for( uint8_t j = 0; j < 16; j++ )
-        {
-            if( ( ( channelsMask[k] & ( 1 << j ) ) != 0 ) )
-            {// Check datarate validity for enabled channels
-                if( ValueInRange( datarate, Channels[i + j].DrRange.Fields.Min, Channels[i + j].DrRange.Fields.Max ) == true )
-                {
-                    // At least 1 channel has been found we can return OK.
-                    return true;
-                }
-            }
-        }
-    }
-    return false;
-}
-
-static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower )
-{
-    int8_t resultTxPower = txPower;
-
-    // Limit tx power to the band max
-    resultTxPower =  MAX( txPower, maxBandTxPower );
-
-#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
-    if( ( LoRaMacParams.ChannelsDatarate == DR_4 ) ||
-        ( ( LoRaMacParams.ChannelsDatarate >= DR_8 ) && ( LoRaMacParams.ChannelsDatarate <= DR_13 ) ) )
-    {// Limit tx power to max 26dBm
-        resultTxPower =  MAX( txPower, TX_POWER_26_DBM );
-    }
-    else
-    {
-        if( CountNbEnabled125kHzChannels( LoRaMacParams.ChannelsMask ) < 50 )
-        {// Limit tx power to max 21dBm
-            resultTxPower = MAX( txPower, TX_POWER_20_DBM );
-        }
-    }
-#endif
-    return resultTxPower;
-}
-
-static bool ValueInRange( int8_t value, int8_t min, int8_t max )
-{
-    if( ( value >= min ) && ( value <= max ) )
-    {
-        return true;
-    }
-    return false;
-}
-
-static bool DisableChannelInMask( uint8_t id, uint16_t* mask )
-{
-    uint8_t index = 0;
-    index = id / 16;
-
-    if( ( index > 4 ) || ( id >= LORA_MAX_NB_CHANNELS ) )
-    {
-        return false;
-    }
-
-    // Deactivate channel
-    mask[index] &= ~( 1 << ( id % 16 ) );
-
-    return true;
-}
-
-static bool AdrNextDr( bool adrEnabled, bool updateChannelMask, int8_t* datarateOut )
-{
-    bool adrAckReq = false;
-    int8_t datarate = LoRaMacParams.ChannelsDatarate;
-
-    if( adrEnabled == true )
-    {
-        if( datarate == LORAMAC_TX_MIN_DATARATE )
-        {
-            AdrAckCounter = 0;
-            adrAckReq = false;
-        }
-        else
-        {
-            if( AdrAckCounter >= ADR_ACK_LIMIT )
-            {
-                adrAckReq = true;
-                LoRaMacParams.ChannelsTxPower = LORAMAC_MAX_TX_POWER;
-            }
-            else
-            {
-                adrAckReq = false;
-            }
-            if( AdrAckCounter >= ( ADR_ACK_LIMIT + ADR_ACK_DELAY ) )
-            {
-                if( ( AdrAckCounter % ADR_ACK_DELAY ) == 1 )
-                {
-#if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 )
-                    if( datarate > LORAMAC_TX_MIN_DATARATE )
-                    {
-                        datarate--;
-                    }
-                    if( datarate == LORAMAC_TX_MIN_DATARATE )
-                    {
-                        if( updateChannelMask == true )
-                        {
-                            // Re-enable default channels LC1, LC2, LC3
-                            LoRaMacParams.ChannelsMask[0] = LoRaMacParams.ChannelsMask[0] | ( LC( 1 ) + LC( 2 ) + LC( 3 ) );
-                        }
-                    }
-#elif defined( USE_BAND_470 )
-                    if( datarate > LORAMAC_TX_MIN_DATARATE )
-                    {
-                        datarate--;
-                    }
-                    if( datarate == LORAMAC_TX_MIN_DATARATE )
-                    {
-                        if( updateChannelMask == true )
-                        {
-                            // Re-enable default channels
-                            memcpy1( ( uint8_t* )LoRaMacParams.ChannelsMask, ( uint8_t* )LoRaMacParamsDefaults.ChannelsMask, sizeof( LoRaMacParams.ChannelsMask ) );
-                        }
-                    }
-#elif defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
-                    if( ( datarate > LORAMAC_TX_MIN_DATARATE ) && ( datarate == DR_8 ) )
-                    {
-                        datarate = DR_4;
-                    }
-                    else if( datarate > LORAMAC_TX_MIN_DATARATE )
-                    {
-                        datarate--;
-                    }
-                    if( datarate == LORAMAC_TX_MIN_DATARATE )
-                    {
-                        if( updateChannelMask == true )
-                        {
-#if defined( USE_BAND_915 )
-                            // Re-enable default channels
-                            memcpy1( ( uint8_t* )LoRaMacParams.ChannelsMask, ( uint8_t* )LoRaMacParamsDefaults.ChannelsMask, sizeof( LoRaMacParams.ChannelsMask ) );
-#else // defined( USE_BAND_915_HYBRID )
-                            // Re-enable default channels
-                            ReenableChannels( LoRaMacParamsDefaults.ChannelsMask[4], LoRaMacParams.ChannelsMask );
-#endif
-                        }
-                    }
-#else
-#error "Please define a frequency band in the compiler options."
-#endif
-                }
-            }
-        }
-    }
-
-    *datarateOut = datarate;
-
-    return adrAckReq;
-}
-
 static LoRaMacStatus_t AddMacCommand( uint8_t cmd, uint8_t p1, uint8_t p2 )
 {
     LoRaMacStatus_t status = LORAMAC_STATUS_BUSY;
@@ -2444,6 +1823,57 @@
                 status = LORAMAC_STATUS_OK;
             }
             break;
+        case MOTE_MAC_TX_PARAM_SETUP_ANS:
+            if( MacCommandsBufferIndex < bufLen )
+            {
+                MacCommandsBuffer[MacCommandsBufferIndex++] = cmd;
+                // No payload for this answer
+                status = LORAMAC_STATUS_OK;
+            }
+            break;
+        case MOTE_MAC_DL_CHANNEL_ANS:
+            if( MacCommandsBufferIndex < bufLen )
+            {
+                MacCommandsBuffer[MacCommandsBufferIndex++] = cmd;
+                // Status: Uplink frequency exists, Channel frequency OK
+                MacCommandsBuffer[MacCommandsBufferIndex++] = p1;
+                status = LORAMAC_STATUS_OK;
+            }
+            break;
+        case MOTE_MAC_PING_SLOT_INFO_REQ:
+            if( MacCommandsBufferIndex < ( LORA_MAC_COMMAND_MAX_LENGTH - 1 ) )
+            {
+                MacCommandsBuffer[MacCommandsBufferIndex++] = cmd;
+                // Status: Periodicity and Datarate
+                MacCommandsBuffer[MacCommandsBufferIndex++] = p1;
+                status = LORAMAC_STATUS_OK;
+            }
+            break;
+        case MOTE_MAC_PING_SLOT_FREQ_ANS:
+            if( MacCommandsBufferIndex < LORA_MAC_COMMAND_MAX_LENGTH )
+            {
+                MacCommandsBuffer[MacCommandsBufferIndex++] = cmd;
+                // Status: Datarate range OK, Channel frequency OK
+                MacCommandsBuffer[MacCommandsBufferIndex++] = p1;
+                status = LORAMAC_STATUS_OK;
+            }
+            break;
+        case MOTE_MAC_BEACON_TIMING_REQ:
+            if( MacCommandsBufferIndex < LORA_MAC_COMMAND_MAX_LENGTH )
+            {
+                MacCommandsBuffer[MacCommandsBufferIndex++] = cmd;
+                // No payload for this answer
+                status = LORAMAC_STATUS_OK;
+            }
+            break;
+        case MOTE_MAC_BEACON_FREQ_ANS:
+            if( MacCommandsBufferIndex < LORA_MAC_COMMAND_MAX_LENGTH )
+            {
+                MacCommandsBuffer[MacCommandsBufferIndex++] = cmd;
+                // No payload for this answer
+                status = LORAMAC_STATUS_OK;
+            }
+            break;
         default:
             return LORAMAC_STATUS_SERVICE_UNKNOWN;
     }
@@ -2469,14 +1899,15 @@
         switch( cmdBufIn[i] )
         {
             // STICKY
+            case MOTE_MAC_DL_CHANNEL_ANS:
             case MOTE_MAC_RX_PARAM_SETUP_ANS:
-            {
+            { // 1 byte payload
                 cmdBufOut[cmdCount++] = cmdBufIn[i++];
                 cmdBufOut[cmdCount++] = cmdBufIn[i];
                 break;
             }
             case MOTE_MAC_RX_TIMING_SETUP_ANS:
-            {
+            { // 0 byte payload
                 cmdBufOut[cmdCount++] = cmdBufIn[i];
                 break;
             }
@@ -2492,6 +1923,7 @@
                 i++;
                 break;
             }
+            case MOTE_MAC_TX_PARAM_SETUP_ANS:
             case MOTE_MAC_DUTY_CYCLE_ANS:
             case MOTE_MAC_LINK_CHECK_REQ:
             { // 0 byte payload
@@ -2507,198 +1939,58 @@
 
 static void ProcessMacCommands( uint8_t *payload, uint8_t macIndex, uint8_t commandsSize, uint8_t snr )
 {
+    uint8_t status = 0;
+    uint8_t index = 0;
+
     while( macIndex < commandsSize )
     {
         // Decode Frame MAC commands
         switch( payload[macIndex++] )
         {
             case SRV_MAC_LINK_CHECK_ANS:
-                MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_OK;
-                MlmeConfirm.DemodMargin = payload[macIndex++];
-                MlmeConfirm.NbGateways = payload[macIndex++];
+                index = GetMlmeConfirmIndex( MlmeConfirmQueue, MLME_LINK_CHECK );
+                if( index < LORA_MAC_MLME_CONFIRM_QUEUE_LEN )
+                {
+                    MlmeConfirmQueue[index].Status = LORAMAC_EVENT_INFO_STATUS_OK;
+                    MlmeConfirm.DemodMargin = payload[macIndex++];
+                    MlmeConfirm.NbGateways = payload[macIndex++];
+                }
                 break;
             case SRV_MAC_LINK_ADR_REQ:
                 {
-                    uint8_t i;
-                    uint8_t status = 0x07;
-                    uint16_t chMask;
-                    int8_t txPower = 0;
-                    int8_t datarate = 0;
-                    uint8_t nbRep = 0;
-                    uint8_t chMaskCntl = 0;
-                    uint16_t channelsMask[6] = { 0, 0, 0, 0, 0, 0 };
-
-                    // Initialize local copy of the channels mask array
-                    for( i = 0; i < 6; i++ )
-                    {
-                        channelsMask[i] = LoRaMacParams.ChannelsMask[i];
-                    }
-                    datarate = payload[macIndex++];
-                    txPower = datarate & 0x0F;
-                    datarate = ( datarate >> 4 ) & 0x0F;
-
-                    if( ( AdrCtrlOn == false ) &&
-                        ( ( LoRaMacParams.ChannelsDatarate != datarate ) || ( LoRaMacParams.ChannelsTxPower != txPower ) ) )
-                    { // ADR disabled don't handle ADR requests if server tries to change datarate or txpower
-                        // Answer the server with fail status
-                        // Power ACK     = 0
-                        // Data rate ACK = 0
-                        // Channel mask  = 0
-                        AddMacCommand( MOTE_MAC_LINK_ADR_ANS, 0, 0 );
-                        macIndex += 3;  // Skip over the remaining bytes of the request
-                        break;
-                    }
-                    chMask = ( uint16_t )payload[macIndex++];
-                    chMask |= ( uint16_t )payload[macIndex++] << 8;
-
-                    nbRep = payload[macIndex++];
-                    chMaskCntl = ( nbRep >> 4 ) & 0x07;
-                    nbRep &= 0x0F;
-                    if( nbRep == 0 )
-                    {
-                        nbRep = 1;
-                    }
-#if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 )
-                    if( ( chMaskCntl == 0 ) && ( chMask == 0 ) )
-                    {
-                        status &= 0xFE; // Channel mask KO
-                    }
-                    else if( ( ( chMaskCntl >= 1 ) && ( chMaskCntl <= 5 )) ||
-                             ( chMaskCntl >= 7 ) )
-                    {
-                        // RFU
-                        status &= 0xFE; // Channel mask KO
-                    }
-                    else
-                    {
-                        for( i = 0; i < LORA_MAX_NB_CHANNELS; i++ )
-                        {
-                            if( chMaskCntl == 6 )
-                            {
-                                if( Channels[i].Frequency != 0 )
-                                {
-                                    chMask |= 1 << i;
-                                }
-                            }
-                            else
-                            {
-                                if( ( ( chMask & ( 1 << i ) ) != 0 ) &&
-                                    ( Channels[i].Frequency == 0 ) )
-                                {// Trying to enable an undefined channel
-                                    status &= 0xFE; // Channel mask KO
-                                }
-                            }
-                        }
-                        channelsMask[0] = chMask;
-                    }
-#elif defined( USE_BAND_470 )
-                    if( chMaskCntl == 6 )
-                    {
-                        // Enable all 125 kHz channels
-                        for( uint8_t i = 0, k = 0; i < LORA_MAX_NB_CHANNELS; i += 16, k++ )
-                        {
-                            for( uint8_t j = 0; j < 16; j++ )
-                            {
-                                if( Channels[i + j].Frequency != 0 )
-                                {
-                                    channelsMask[k] |= 1 << j;
-                                }
-                            }
-                        }
-                    }
-                    else if( chMaskCntl == 7 )
-                    {
-                        status &= 0xFE; // Channel mask KO
-                    }
-                    else
-                    {
-                        for( uint8_t i = 0; i < 16; i++ )
-                        {
-                            if( ( ( chMask & ( 1 << i ) ) != 0 ) &&
-                                ( Channels[chMaskCntl * 16 + i].Frequency == 0 ) )
-                            {// Trying to enable an undefined channel
-                                status &= 0xFE; // Channel mask KO
-                            }
-                        }
-                        channelsMask[chMaskCntl] = chMask;
-                    }
-#elif defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
-                    if( chMaskCntl == 6 )
-                    {
-                        // Enable all 125 kHz channels
-                        channelsMask[0] = 0xFFFF;
-                        channelsMask[1] = 0xFFFF;
-                        channelsMask[2] = 0xFFFF;
-                        channelsMask[3] = 0xFFFF;
-                        // Apply chMask to channels 64 to 71
-                        channelsMask[4] = chMask;
-                    }
-                    else if( chMaskCntl == 7 )
-                    {
-                        // Disable all 125 kHz channels
-                        channelsMask[0] = 0x0000;
-                        channelsMask[1] = 0x0000;
-                        channelsMask[2] = 0x0000;
-                        channelsMask[3] = 0x0000;
-                        // Apply chMask to channels 64 to 71
-                        channelsMask[4] = chMask;
-                    }
-                    else if( chMaskCntl == 5 )
-                    {
-                        // RFU
-                        status &= 0xFE; // Channel mask KO
-                    }
-                    else
-                    {
-                        channelsMask[chMaskCntl] = chMask;
-
-                        // FCC 15.247 paragraph F mandates to hop on at least 2 125 kHz channels
-                        if( ( datarate < DR_4 ) && ( CountNbEnabled125kHzChannels( channelsMask ) < 2 ) )
-                        {
-                            status &= 0xFE; // Channel mask KO
-                        }
-
-#if defined( USE_BAND_915_HYBRID )
-                        if( ValidateChannelMask( channelsMask ) == false )
-                        {
-                            status &= 0xFE; // Channel mask KO
-                        }
-#endif
-                    }
-#else
-    #error "Please define a frequency band in the compiler options."
-#endif
-                    if( ValidateDatarate( datarate, channelsMask ) == false )
-                    {
-                        status &= 0xFD; // Datarate KO
-                    }
-
-                    //
-                    // Remark MaxTxPower = 0 and MinTxPower = 5
-                    //
-                    if( ValueInRange( txPower, LORAMAC_MAX_TX_POWER, LORAMAC_MIN_TX_POWER ) == false )
-                    {
-                        status &= 0xFB; // TxPower KO
-                    }
+                    LinkAdrReqParams_t linkAdrReq;
+                    int8_t linkAdrDatarate = DR_0;
+                    int8_t linkAdrTxPower = TX_POWER_0;
+                    uint8_t linkAdrNbRep = 0;
+                    uint8_t linkAdrNbBytesParsed = 0;
+
+                    // Fill parameter structure
+                    linkAdrReq.Payload = &payload[macIndex - 1];
+                    linkAdrReq.PayloadSize = commandsSize - ( macIndex - 1 );
+                    linkAdrReq.AdrEnabled = AdrCtrlOn;
+                    linkAdrReq.UplinkDwellTime = LoRaMacParams.UplinkDwellTime;
+                    linkAdrReq.CurrentDatarate = LoRaMacParams.ChannelsDatarate;
+                    linkAdrReq.CurrentTxPower = LoRaMacParams.ChannelsTxPower;
+                    linkAdrReq.CurrentNbRep = LoRaMacParams.ChannelsNbRep;
+
+                    // Process the ADR requests
+                    status = RegionLinkAdrReq( LoRaMacRegion, &linkAdrReq, &linkAdrDatarate,
+                                               &linkAdrTxPower, &linkAdrNbRep, &linkAdrNbBytesParsed );
+
                     if( ( status & 0x07 ) == 0x07 )
                     {
-                        LoRaMacParams.ChannelsDatarate = datarate;
-                        LoRaMacParams.ChannelsTxPower = txPower;
-
-                        memcpy1( ( uint8_t* )LoRaMacParams.ChannelsMask, ( uint8_t* )channelsMask, sizeof( LoRaMacParams.ChannelsMask ) );
-
-                        LoRaMacParams.ChannelsNbRep = nbRep;
-#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
-                        // Reset ChannelsMaskRemaining to the new ChannelsMask
-                        ChannelsMaskRemaining[0] &= channelsMask[0];
-                        ChannelsMaskRemaining[1] &= channelsMask[1];
-                        ChannelsMaskRemaining[2] &= channelsMask[2];
-                        ChannelsMaskRemaining[3] &= channelsMask[3];
-                        ChannelsMaskRemaining[4] = channelsMask[4];
-                        ChannelsMaskRemaining[5] = channelsMask[5];
-#endif
+                        LoRaMacParams.ChannelsDatarate = linkAdrDatarate;
+                        LoRaMacParams.ChannelsTxPower = linkAdrTxPower;
+                        LoRaMacParams.ChannelsNbRep = linkAdrNbRep;
                     }
-                    AddMacCommand( MOTE_MAC_LINK_ADR_ANS, status, 0 );
+
+                    // Add the answers to the buffer
+                    for( uint8_t i = 0; i < ( linkAdrNbBytesParsed / 5 ); i++ )
+                    {
+                        AddMacCommand( MOTE_MAC_LINK_ADR_ANS, status, 0 );
+                    }
+                    // Update MAC index
+                    macIndex += linkAdrNbBytesParsed - 1;
                 }
                 break;
             case SRV_MAC_DUTY_CYCLE_REQ:
@@ -2708,46 +2000,26 @@
                 break;
             case SRV_MAC_RX_PARAM_SETUP_REQ:
                 {
-                    uint8_t status = 0x07;
-                    int8_t datarate = 0;
-                    int8_t drOffset = 0;
-                    uint32_t freq = 0;
-
-                    drOffset = ( payload[macIndex] >> 4 ) & 0x07;
-                    datarate = payload[macIndex] & 0x0F;
+                    RxParamSetupReqParams_t rxParamSetupReq;
+                    status = 0x07;
+
+                    rxParamSetupReq.DrOffset = ( payload[macIndex] >> 4 ) & 0x07;
+                    rxParamSetupReq.Datarate = payload[macIndex] & 0x0F;
                     macIndex++;
 
-                    freq =  ( uint32_t )payload[macIndex++];
-                    freq |= ( uint32_t )payload[macIndex++] << 8;
-                    freq |= ( uint32_t )payload[macIndex++] << 16;
-                    freq *= 100;
-
-                    if( Rx2FreqInRange( freq ) == false )
-                    {
-                        status &= 0xFE; // Channel frequency KO
-                    }
-
-                    if( ValueInRange( datarate, LORAMAC_RX_MIN_DATARATE, LORAMAC_RX_MAX_DATARATE ) == false )
-                    {
-                        status &= 0xFD; // Datarate KO
-                    }
-#if ( defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) )
-                    if( ( ValueInRange( datarate, DR_5, DR_7 ) == true ) ||
-                        ( datarate > DR_13 ) )
-                    {
-                        status &= 0xFD; // Datarate KO
-                    }
-#endif
-                    if( ValueInRange( drOffset, LORAMAC_MIN_RX1_DR_OFFSET, LORAMAC_MAX_RX1_DR_OFFSET ) == false )
-                    {
-                        status &= 0xFB; // Rx1DrOffset range KO
-                    }
+                    rxParamSetupReq.Frequency =  ( uint32_t )payload[macIndex++];
+                    rxParamSetupReq.Frequency |= ( uint32_t )payload[macIndex++] << 8;
+                    rxParamSetupReq.Frequency |= ( uint32_t )payload[macIndex++] << 16;
+                    rxParamSetupReq.Frequency *= 100;
+
+                    // Perform request on region
+                    status = RegionRxParamSetupReq( LoRaMacRegion, &rxParamSetupReq );
 
                     if( ( status & 0x07 ) == 0x07 )
                     {
-                        LoRaMacParams.Rx2Channel.Datarate = datarate;
-                        LoRaMacParams.Rx2Channel.Frequency = freq;
-                        LoRaMacParams.Rx1DrOffset = drOffset;
+                        LoRaMacParams.Rx2Channel.Datarate = rxParamSetupReq.Datarate;
+                        LoRaMacParams.Rx2Channel.Frequency = rxParamSetupReq.Frequency;
+                        LoRaMacParams.Rx1DrOffset = rxParamSetupReq.DrOffset;
                     }
                     AddMacCommand( MOTE_MAC_RX_PARAM_SETUP_ANS, status, 0 );
                 }
@@ -2764,69 +2036,22 @@
                 }
             case SRV_MAC_NEW_CHANNEL_REQ:
                 {
-                    uint8_t status = 0x03;
-
-#if defined( USE_BAND_470 ) || defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
-                    status &= 0xFC; // Channel frequency and datarate KO
-                    macIndex += 5;
-#else
-                    int8_t channelIndex = 0;
+                    NewChannelReqParams_t newChannelReq;
                     ChannelParams_t chParam;
-
-                    channelIndex = payload[macIndex++];
+                    status = 0x03;
+
+                    newChannelReq.ChannelId = payload[macIndex++];
+                    newChannelReq.NewChannel = &chParam;
+
                     chParam.Frequency = ( uint32_t )payload[macIndex++];
                     chParam.Frequency |= ( uint32_t )payload[macIndex++] << 8;
                     chParam.Frequency |= ( uint32_t )payload[macIndex++] << 16;
                     chParam.Frequency *= 100;
+                    chParam.Rx1Frequency = 0;
                     chParam.DrRange.Value = payload[macIndex++];
 
-                    LoRaMacState |= LORAMAC_TX_CONFIG;
-                    if( chParam.Frequency == 0 )
-                    {
-                        if( channelIndex < 3 )
-                        {
-                            status &= 0xFC;
-                        }
-                        else
-                        {
-                            if( LoRaMacChannelRemove( channelIndex ) != LORAMAC_STATUS_OK )
-                            {
-                                status &= 0xFC;
-                            }
-                        }
-                    }
-                    else
-                    {
-                        switch( LoRaMacChannelAdd( channelIndex, chParam ) )
-                        {
-                            case LORAMAC_STATUS_OK:
-                            {
-                                break;
-                            }
-                            case LORAMAC_STATUS_FREQUENCY_INVALID:
-                            {
-                                status &= 0xFE;
-                                break;
-                            }
-                            case LORAMAC_STATUS_DATARATE_INVALID:
-                            {
-                                status &= 0xFD;
-                                break;
-                            }
-                            case LORAMAC_STATUS_FREQ_AND_DR_INVALID:
-                            {
-                                status &= 0xFC;
-                                break;
-                            }
-                            default:
-                            {
-                                status &= 0xFC;
-                                break;
-                            }
-                        }
-                    }
-                    LoRaMacState &= ~LORAMAC_TX_CONFIG;
-#endif
+                    status = RegionNewChannelReq( LoRaMacRegion, &newChannelReq );
+
                     AddMacCommand( MOTE_MAC_NEW_CHANNEL_ANS, status, 0 );
                 }
                 break;
@@ -2838,11 +2063,109 @@
                     {
                         delay++;
                     }
-                    LoRaMacParams.ReceiveDelay1 = delay * 1e3;
-                    LoRaMacParams.ReceiveDelay2 = LoRaMacParams.ReceiveDelay1 + 1e3;
+                    LoRaMacParams.ReceiveDelay1 = delay * 1000;
+                    LoRaMacParams.ReceiveDelay2 = LoRaMacParams.ReceiveDelay1 + 1000;
                     AddMacCommand( MOTE_MAC_RX_TIMING_SETUP_ANS, 0, 0 );
                 }
                 break;
+            case SRV_MAC_TX_PARAM_SETUP_REQ:
+                {
+                    TxParamSetupReqParams_t txParamSetupReq;
+                    uint8_t eirpDwellTime = payload[macIndex++];
+
+                    txParamSetupReq.UplinkDwellTime = 0;
+                    txParamSetupReq.DownlinkDwellTime = 0;
+
+                    if( ( eirpDwellTime & 0x20 ) == 0x20 )
+                    {
+                        txParamSetupReq.DownlinkDwellTime = 1;
+                    }
+                    if( ( eirpDwellTime & 0x10 ) == 0x10 )
+                    {
+                        txParamSetupReq.UplinkDwellTime = 1;
+                    }
+                    txParamSetupReq.MaxEirp = eirpDwellTime & 0x0F;
+
+                    // Check the status for correctness
+                    if( RegionTxParamSetupReq( LoRaMacRegion, &txParamSetupReq ) != -1 )
+                    {
+                        // Accept command
+                        LoRaMacParams.UplinkDwellTime = txParamSetupReq.UplinkDwellTime;
+                        LoRaMacParams.DownlinkDwellTime = txParamSetupReq.DownlinkDwellTime;
+                        LoRaMacParams.MaxEirp = LoRaMacMaxEirpTable[txParamSetupReq.MaxEirp];
+                        // Add command response
+                        AddMacCommand( MOTE_MAC_TX_PARAM_SETUP_ANS, 0, 0 );
+                    }
+                }
+                break;
+            case SRV_MAC_DL_CHANNEL_REQ:
+                {
+                    DlChannelReqParams_t dlChannelReq;
+                    status = 0x03;
+
+                    dlChannelReq.ChannelId = payload[macIndex++];
+                    dlChannelReq.Rx1Frequency = ( uint32_t )payload[macIndex++];
+                    dlChannelReq.Rx1Frequency |= ( uint32_t )payload[macIndex++] << 8;
+                    dlChannelReq.Rx1Frequency |= ( uint32_t )payload[macIndex++] << 16;
+                    dlChannelReq.Rx1Frequency *= 100;
+
+                    status = RegionDlChannelReq( LoRaMacRegion, &dlChannelReq );
+
+                    AddMacCommand( MOTE_MAC_DL_CHANNEL_ANS, status, 0 );
+                }
+                break;
+            case SRV_MAC_PING_SLOT_INFO_ANS:
+                {
+                    LoRaMacClassBPingSlotInfoAns( );
+                }
+                break;
+            case SRV_MAC_PING_SLOT_CHANNEL_REQ:
+                {
+                    uint8_t status = 0x03;
+                    uint32_t frequency = 0;
+                    uint8_t datarate;
+
+                    frequency = ( uint32_t )payload[macIndex++];
+                    frequency |= ( uint32_t )payload[macIndex++] << 8;
+                    frequency |= ( uint32_t )payload[macIndex++] << 16;
+                    frequency *= 100;
+                    datarate = payload[macIndex++] & 0x0F;
+
+                    status = LoRaMacClassBPingSlotChannelReq( datarate, frequency );
+                    AddMacCommand( MOTE_MAC_PING_SLOT_FREQ_ANS, status, 0 );
+                }
+                break;
+            case SRV_MAC_BEACON_TIMING_ANS:
+                {
+                    uint16_t beaconTimingDelay = 0;
+                    uint8_t beaconTimingChannel = 0;
+
+                    beaconTimingDelay = ( uint16_t )payload[macIndex++];
+                    beaconTimingDelay |= ( uint16_t )payload[macIndex++] << 8;
+                    beaconTimingChannel = payload[macIndex++];
+
+                    LoRaMacClassBBeaconTimingAns( beaconTimingDelay, beaconTimingChannel );
+                }
+                break;
+            case SRV_MAC_BEACON_FREQ_REQ:
+                {
+                    uint32_t frequency = 0;
+
+                    frequency = ( uint32_t )payload[macIndex++];
+                    frequency |= ( uint32_t )payload[macIndex++] << 8;
+                    frequency |= ( uint32_t )payload[macIndex++] << 16;
+                    frequency *= 100;
+
+                    if( LoRaMacClassBBeaconFreqReq( frequency ) == true )
+                    {
+                        AddMacCommand( MOTE_MAC_BEACON_FREQ_ANS, 1, 0 );
+                    }
+                    else
+                    {
+                        AddMacCommand( MOTE_MAC_BEACON_FREQ_ANS, 0, 0 );
+                    }
+                }
+                break;
             default:
                 // Unknown command. ABORT MAC commands processing
                 return;
@@ -2857,7 +2180,14 @@
 
     fCtrl.Value = 0;
     fCtrl.Bits.FOptsLen      = 0;
-    fCtrl.Bits.FPending      = 0;
+    if( LoRaMacDeviceClass == CLASS_B )
+    {
+        fCtrl.Bits.FPending      = 1;
+    }
+    else
+    {
+        fCtrl.Bits.FPending      = 0;
+    }
     fCtrl.Bits.Ack           = false;
     fCtrl.Bits.AdrAckReq     = false;
     fCtrl.Bits.Adr           = AdrCtrlOn;
@@ -2884,6 +2214,9 @@
 static LoRaMacStatus_t ScheduleTx( void )
 {
     TimerTime_t dutyCycleTimeOff = 0;
+    TimerTime_t mutexTimeLock = 0;
+    TimerTime_t timeOff = 0;
+    NextChanParams_t nextChan;
 
     // Check if the device is off
     if( MaxDCycle == 255 )
@@ -2895,31 +2228,41 @@
         AggregatedTimeOff = 0;
     }
 
+    // Update Backoff
+    CalculateBackOff( LastTxChannel );
+
+    nextChan.AggrTimeOff = AggregatedTimeOff;
+    nextChan.Datarate = LoRaMacParams.ChannelsDatarate;
+    nextChan.DutyCycleEnabled = DutyCycleOn;
+    nextChan.Joined = IsLoRaMacNetworkJoined;
+    nextChan.LastAggrTx = AggregatedLastTxDoneTime;
+
     // Select channel
-    while( SetNextChannel( &dutyCycleTimeOff ) == false )
+    while( RegionNextChannel( LoRaMacRegion, &nextChan, &Channel, &dutyCycleTimeOff, &AggregatedTimeOff ) == false )
     {
         // Set the default datarate
         LoRaMacParams.ChannelsDatarate = LoRaMacParamsDefaults.ChannelsDatarate;
-
-#if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 )
-        // Re-enable default channels LC1, LC2, LC3
-        LoRaMacParams.ChannelsMask[0] = LoRaMacParams.ChannelsMask[0] | ( LC( 1 ) + LC( 2 ) + LC( 3 ) );
-#endif
+        // Update datarate in the function parameters
+        nextChan.Datarate = LoRaMacParams.ChannelsDatarate;
     }
 
     // Compute Rx1 windows parameters
-#if ( defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) )
-    RxWindowsParams[0] = ComputeRxWindowParameters( DatarateOffsets[LoRaMacParams.ChannelsDatarate][LoRaMacParams.Rx1DrOffset], LoRaMacParams.SystemMaxRxError );
-#else
-    RxWindowsParams[0] = ComputeRxWindowParameters( MAX( DR_0, LoRaMacParams.ChannelsDatarate - LoRaMacParams.Rx1DrOffset ), LoRaMacParams.SystemMaxRxError );
-#endif
+    RegionComputeRxWindowParameters( LoRaMacRegion,
+                                     RegionApplyDrOffset( LoRaMacRegion, LoRaMacParams.DownlinkDwellTime, LoRaMacParams.ChannelsDatarate, LoRaMacParams.Rx1DrOffset ),
+                                     LoRaMacParams.MinRxSymbols,
+                                     LoRaMacParams.SystemMaxRxError,
+                                     &RxWindow1Config );
     // Compute Rx2 windows parameters
-    RxWindowsParams[1] = ComputeRxWindowParameters( LoRaMacParams.Rx2Channel.Datarate, LoRaMacParams.SystemMaxRxError );
+    RegionComputeRxWindowParameters( LoRaMacRegion,
+                                     LoRaMacParams.Rx2Channel.Datarate,
+                                     LoRaMacParams.MinRxSymbols,
+                                     LoRaMacParams.SystemMaxRxError,
+                                     &RxWindow2Config );
 
     if( IsLoRaMacNetworkJoined == false )
     {
-        RxWindow1Delay = LoRaMacParams.JoinAcceptDelay1 + RxWindowsParams[0].RxOffset;
-        RxWindow2Delay = LoRaMacParams.JoinAcceptDelay2 + RxWindowsParams[1].RxOffset;
+        RxWindow1Delay = LoRaMacParams.JoinAcceptDelay1 + RxWindow1Config.WindowOffset;
+        RxWindow2Delay = LoRaMacParams.JoinAcceptDelay2 + RxWindow2Config.WindowOffset;
     }
     else
     {
@@ -2927,123 +2270,68 @@
         {
             return LORAMAC_STATUS_LENGTH_ERROR;
         }
-        RxWindow1Delay = LoRaMacParams.ReceiveDelay1 + RxWindowsParams[0].RxOffset;
-        RxWindow2Delay = LoRaMacParams.ReceiveDelay2 + RxWindowsParams[1].RxOffset;
+        RxWindow1Delay = LoRaMacParams.ReceiveDelay1 + RxWindow1Config.WindowOffset;
+        RxWindow2Delay = LoRaMacParams.ReceiveDelay2 + RxWindow2Config.WindowOffset;
     }
 
     // Schedule transmission of frame
     if( dutyCycleTimeOff == 0 )
     {
         // Try to send now
-        return SendFrameOnChannel( Channels[Channel] );
+        mutexTimeLock =  SendFrameOnChannel( Channel );
     }
-    else
+
+    timeOff = MAX( dutyCycleTimeOff, mutexTimeLock );
+
+    if( timeOff > 0 )
     {
         // Send later - prepare timer
         LoRaMacState |= LORAMAC_TX_DELAYED;
-        TimerSetValue( &TxDelayedTimer, dutyCycleTimeOff );
+        TimerSetValue( &TxDelayedTimer, timeOff );
         TimerStart( &TxDelayedTimer );
-
-        return LORAMAC_STATUS_OK;
     }
-}
-
-static uint16_t JoinDutyCycle( void )
-{
-    uint16_t dutyCycle = 0;
-    TimerTime_t timeElapsed = TimerGetElapsedTime( LoRaMacInitializationTime );
-
-    if( timeElapsed < 3600e3 )
-    {
-        dutyCycle = BACKOFF_DC_1_HOUR;
-    }
-    else if( timeElapsed < ( 3600e3 + 36000e3 ) )
-    {
-        dutyCycle = BACKOFF_DC_10_HOURS;
-    }
-    else
-    {
-        dutyCycle = BACKOFF_DC_24_HOURS;
-    }
-    return dutyCycle;
+    return LORAMAC_STATUS_OK;
 }
 
 static void CalculateBackOff( uint8_t channel )
 {
-    uint16_t dutyCycle = Bands[Channels[channel].Band].DCycle;
-    uint16_t joinDutyCycle = 0;
-
-    // Reset time-off to initial value.
-    Bands[Channels[channel].Band].TimeOff = 0;
-
-    if( IsLoRaMacNetworkJoined == false )
-    {
-        // The node has not joined yet. Apply join duty cycle to all regions.
-        joinDutyCycle = JoinDutyCycle( );
-        dutyCycle = MAX( dutyCycle, joinDutyCycle );
-
-        // Update Band time-off.
-        Bands[Channels[channel].Band].TimeOff = TxTimeOnAir * dutyCycle - TxTimeOnAir;
-    }
-    else
-    {
-        if( DutyCycleOn == true )
-        {
-            Bands[Channels[channel].Band].TimeOff = TxTimeOnAir * dutyCycle - TxTimeOnAir;
-        }
-    }
-
-    // Update Aggregated Time OFF
+    CalcBackOffParams_t calcBackOff;
+
+    calcBackOff.Joined = IsLoRaMacNetworkJoined;
+    calcBackOff.DutyCycleEnabled = DutyCycleOn;
+    calcBackOff.Channel = channel;
+    calcBackOff.ElapsedTime = TimerGetElapsedTime( LoRaMacInitializationTime );
+    calcBackOff.TxTimeOnAir = TxTimeOnAir;
+    calcBackOff.LastTxIsJoinRequest = LastTxIsJoinRequest;
+
+    // Update regional back-off
+    RegionCalcBackOff( LoRaMacRegion, &calcBackOff );
+
+    // Update aggregated time-off
     AggregatedTimeOff = AggregatedTimeOff + ( TxTimeOnAir * AggregatedDCycle - TxTimeOnAir );
 }
 
-static int8_t AlternateDatarate( uint16_t nbTrials )
+static uint8_t GetMlmeConfirmIndex( MlmeConfirmQueue_t* queue, Mlme_t req )
 {
-    int8_t datarate = LORAMAC_TX_MIN_DATARATE;
-#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
-#if defined( USE_BAND_915 )
-    // Re-enable 500 kHz default channels
-    LoRaMacParams.ChannelsMask[4] = 0x00FF;
-#else // defined( USE_BAND_915_HYBRID )
-    // Re-enable 500 kHz default channels
-    ReenableChannels( LoRaMacParamsDefaults.ChannelsMask[4], LoRaMacParams.ChannelsMask );
-#endif
-
-    if( ( nbTrials & 0x01 ) == 0x01 )
-    {
-        datarate = DR_4;
-    }
-    else
-    {
-        datarate = DR_0;
-    }
-#else
-    if( ( nbTrials % 48 ) == 0 )
+    for( uint8_t i = 0; i < MlmeConfirmQueueCnt; i++ )
     {
-        datarate = DR_0;
-    }
-    else if( ( nbTrials % 32 ) == 0 )
-    {
-        datarate = DR_1;
+        if( queue->MlmeRequest == req )
+        {
+            return i;
+        }
+        queue++;
     }
-    else if( ( nbTrials % 24 ) == 0 )
-    {
-        datarate = DR_2;
-    }
-    else if( ( nbTrials % 16 ) == 0 )
+
+    // Out of band
+    return LORA_MAC_MLME_CONFIRM_QUEUE_LEN;
+}
+
+static void SetEveryMlmeConfirmStatus( MlmeConfirmQueue_t* queue, LoRaMacEventInfoStatus_t status )
+{
+    for( uint8_t i = 0; i < MlmeConfirmQueueCnt; i++ )
     {
-        datarate = DR_3;
-    }
-    else if( ( nbTrials % 8 ) == 0 )
-    {
-        datarate = DR_4;
+        queue[i].Status = status;
     }
-    else
-    {
-        datarate = DR_5;
-    }
-#endif
-    return datarate;
 }
 
 static void ResetMacParameters( void )
@@ -3071,16 +2359,12 @@
 
     LoRaMacParams.ChannelsTxPower = LoRaMacParamsDefaults.ChannelsTxPower;
     LoRaMacParams.ChannelsDatarate = LoRaMacParamsDefaults.ChannelsDatarate;
-
     LoRaMacParams.Rx1DrOffset = LoRaMacParamsDefaults.Rx1DrOffset;
     LoRaMacParams.Rx2Channel = LoRaMacParamsDefaults.Rx2Channel;
-
-    memcpy1( ( uint8_t* ) LoRaMacParams.ChannelsMask, ( uint8_t* ) LoRaMacParamsDefaults.ChannelsMask, sizeof( LoRaMacParams.ChannelsMask ) );
-
-#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
-    memcpy1( ( uint8_t* ) ChannelsMaskRemaining, ( uint8_t* ) LoRaMacParamsDefaults.ChannelsMask, sizeof( LoRaMacParams.ChannelsMask ) );
-#endif
-
+    LoRaMacParams.UplinkDwellTime = LoRaMacParamsDefaults.UplinkDwellTime;
+    LoRaMacParams.DownlinkDwellTime = LoRaMacParamsDefaults.DownlinkDwellTime;
+    LoRaMacParams.MaxEirp = LoRaMacParamsDefaults.MaxEirp;
+    LoRaMacParams.AntennaGain = LoRaMacParamsDefaults.AntennaGain;
 
     NodeAckRequested = false;
     SrvAckRequested = false;
@@ -3095,11 +2379,13 @@
     }
 
     // Initialize channel index.
-    Channel = LORA_MAX_NB_CHANNELS;
+    Channel = 0;
+    LastTxChannel = Channel;
 }
 
 LoRaMacStatus_t PrepareFrame( LoRaMacHeader_t *macHdr, LoRaMacFrameCtrl_t *fCtrl, uint8_t fPort, void *fBuffer, uint16_t fBufferSize )
 {
+    AdrNextParams_t adrNext;
     uint16_t i;
     uint8_t pktHeaderLen = 0;
     uint32_t mic = 0;
@@ -3151,8 +2437,17 @@
                 return LORAMAC_STATUS_NO_NETWORK_JOINED; // No network has been joined yet
             }
 
-            fCtrl->Bits.AdrAckReq = AdrNextDr( fCtrl->Bits.Adr, true, &LoRaMacParams.ChannelsDatarate );
-            
+            // Adr next request
+            adrNext.UpdateChanMask = true;
+            adrNext.AdrEnabled = fCtrl->Bits.Adr;
+            adrNext.AdrAckCounter = AdrAckCounter;
+            adrNext.Datarate = LoRaMacParams.ChannelsDatarate;
+            adrNext.TxPower = LoRaMacParams.ChannelsTxPower;
+            adrNext.UplinkDwellTime = LoRaMacParams.UplinkDwellTime;
+
+            fCtrl->Bits.AdrAckReq = RegionAdrNext( LoRaMacRegion, &adrNext,
+                                                   &LoRaMacParams.ChannelsDatarate, &LoRaMacParams.ChannelsTxPower, &AdrAckCounter );
+
             if( SrvAckRequested == true )
             {
                 SrvAckRequested = false;
@@ -3175,21 +2470,30 @@
 
             if( ( payload != NULL ) && ( LoRaMacTxPayloadLen > 0 ) )
             {
-                if( ( MacCommandsBufferIndex <= LORA_MAC_COMMAND_MAX_LENGTH ) && ( MacCommandsInNextTx == true ) )
+                if( MacCommandsInNextTx == true )
                 {
-                    fCtrl->Bits.FOptsLen += MacCommandsBufferIndex;
-
-                    // Update FCtrl field with new value of OptionsLength
-                    LoRaMacBuffer[0x05] = fCtrl->Value;
-                    for( i = 0; i < MacCommandsBufferIndex; i++ )
+                    if( MacCommandsBufferIndex <= LORA_MAC_COMMAND_MAX_FOPTS_LENGTH )
                     {
-                        LoRaMacBuffer[pktHeaderLen++] = MacCommandsBuffer[i];
+                        fCtrl->Bits.FOptsLen += MacCommandsBufferIndex;
+
+                        // Update FCtrl field with new value of OptionsLength
+                        LoRaMacBuffer[0x05] = fCtrl->Value;
+                        for( i = 0; i < MacCommandsBufferIndex; i++ )
+                        {
+                            LoRaMacBuffer[pktHeaderLen++] = MacCommandsBuffer[i];
+                        }
+                    }
+                    else
+                    {
+                        LoRaMacTxPayloadLen = MacCommandsBufferIndex;
+                        payload = MacCommandsBuffer;
+                        framePort = 0;
                     }
                 }
             }
             else
             {
-                if( ( MacCommandsBufferIndex > 0 ) && ( MacCommandsInNextTx ) )
+                if( ( MacCommandsBufferIndex > 0 ) && ( MacCommandsInNextTx == true ) )
                 {
                     LoRaMacTxPayloadLen = MacCommandsBufferIndex;
                     payload = MacCommandsBuffer;
@@ -3210,6 +2514,8 @@
 
                 if( framePort == 0 )
                 {
+                    // Reset buffer index as the mac commands are being sent on port 0
+                    MacCommandsBufferIndex = 0;
                     LoRaMacPayloadEncrypt( (uint8_t* ) payload, LoRaMacTxPayloadLen, LoRaMacNwkSKey, LoRaMacDevAddr, UP_LINK, UpLinkCounter, &LoRaMacBuffer[pktHeaderLen] );
                 }
                 else
@@ -3243,67 +2549,55 @@
     return LORAMAC_STATUS_OK;
 }
 
-LoRaMacStatus_t SendFrameOnChannel( ChannelParams_t channel )
+TimerTime_t SendFrameOnChannel( uint8_t channel )
 {
-    int8_t datarate = Datarates[LoRaMacParams.ChannelsDatarate];
-    int8_t txPowerIndex = 0;
+    TxConfigParams_t txConfig;
     int8_t txPower = 0;
 
-    txPowerIndex = LimitTxPower( LoRaMacParams.ChannelsTxPower, Bands[channel.Band].TxMaxPower );
-    txPower = TxPowers[txPowerIndex];
-
-    MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
+    txConfig.Channel = channel;
+    txConfig.Datarate = LoRaMacParams.ChannelsDatarate;
+    txConfig.TxPower = LoRaMacParams.ChannelsTxPower;
+    txConfig.MaxEirp = LoRaMacParams.MaxEirp;
+    txConfig.AntennaGain = LoRaMacParams.AntennaGain;
+    txConfig.PktLen = LoRaMacBufferPktLen;
+
+    if( LoRaMacDeviceClass == CLASS_B )
+    {
+        if( LoRaMacClassBIsBeaconExpected( ) == true )
+        {
+            return LoRaMacClassBGetBeaconReservedTime( );
+        }
+        if( LoRaMacClassBIsPingExpected( ) == true )
+        {
+            return LoRaMacClassBGetPingSlotWinTime( );
+        }
+    }
+    RegionTxConfig( LoRaMacRegion, &txConfig, &txPower, &TxTimeOnAir );
+
+    SetEveryMlmeConfirmStatus( MlmeConfirmQueue, LORAMAC_EVENT_INFO_STATUS_ERROR );
     McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
     McpsConfirm.Datarate = LoRaMacParams.ChannelsDatarate;
-    McpsConfirm.TxPower = txPowerIndex;
-    McpsConfirm.UpLinkFrequency = channel.Frequency;
-
-    Radio.SetChannel( channel.Frequency );
-
-#if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 )
-    if( LoRaMacParams.ChannelsDatarate == DR_7 )
-    { // High Speed FSK channel
-        Radio.SetMaxPayloadLength( MODEM_FSK, LoRaMacBufferPktLen );
-        Radio.SetTxConfig( MODEM_FSK, txPower, 25e3, 0, datarate * 1e3, 0, 5, false, true, 0, 0, false, 3e3 );
-        TxTimeOnAir = Radio.TimeOnAir( MODEM_FSK, LoRaMacBufferPktLen );
-
-    }
-    else if( LoRaMacParams.ChannelsDatarate == DR_6 )
-    { // High speed LoRa channel
-        Radio.SetMaxPayloadLength( MODEM_LORA, LoRaMacBufferPktLen );
-        Radio.SetTxConfig( MODEM_LORA, txPower, 0, 1, datarate, 1, 8, false, true, 0, 0, false, 3e3 );
-        TxTimeOnAir = Radio.TimeOnAir( MODEM_LORA, LoRaMacBufferPktLen );
-    }
-    else
-    { // Normal LoRa channel
-        Radio.SetMaxPayloadLength( MODEM_LORA, LoRaMacBufferPktLen );
-        Radio.SetTxConfig( MODEM_LORA, txPower, 0, 0, datarate, 1, 8, false, true, 0, 0, false, 3e3 );
-        TxTimeOnAir = Radio.TimeOnAir( MODEM_LORA, LoRaMacBufferPktLen );
-    }
-#elif defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
-    Radio.SetMaxPayloadLength( MODEM_LORA, LoRaMacBufferPktLen );
-    if( LoRaMacParams.ChannelsDatarate >= DR_4 )
-    { // High speed LoRa channel BW500 kHz
-        Radio.SetTxConfig( MODEM_LORA, txPower, 0, 2, datarate, 1, 8, false, true, 0, 0, false, 3e3 );
-        TxTimeOnAir = Radio.TimeOnAir( MODEM_LORA, LoRaMacBufferPktLen );
-    }
-    else
-    { // Normal LoRa channel
-        Radio.SetTxConfig( MODEM_LORA, txPower, 0, 0, datarate, 1, 8, false, true, 0, 0, false, 3e3 );
-        TxTimeOnAir = Radio.TimeOnAir( MODEM_LORA, LoRaMacBufferPktLen );
-    }
-#elif defined( USE_BAND_470 )
-    Radio.SetMaxPayloadLength( MODEM_LORA, LoRaMacBufferPktLen );
-    Radio.SetTxConfig( MODEM_LORA, txPower, 0, 0, datarate, 1, 8, false, true, 0, 0, false, 3e3 );
-    TxTimeOnAir = Radio.TimeOnAir( MODEM_LORA, LoRaMacBufferPktLen );
-#else
-    #error "Please define a frequency band in the compiler options."
-#endif
+    McpsConfirm.TxPower = txPower;
 
     // Store the time on air
     McpsConfirm.TxTimeOnAir = TxTimeOnAir;
     MlmeConfirm.TxTimeOnAir = TxTimeOnAir;
 
+    if( ( LoRaMacDeviceClass == CLASS_B ) || ( LoRaMacClassBIsBeaconModeActive( ) == true ) )
+    {
+        TimerTime_t collisionTime = LoRaMacClassBIsUplinkCollision( TxTimeOnAir );
+
+        if( collisionTime > 0 )
+        {
+            return collisionTime;
+        }
+    }
+
+    if( LoRaMacDeviceClass == CLASS_B )
+    {
+        LoRaMacClassBHaltBeaconing( );
+    }
+
     // Starts the MAC layer status check timer
     TimerSetValue( &MacStateCheckTimer, MAC_STATE_CHECK_TIMEOUT );
     TimerStart( &MacStateCheckTimer );
@@ -3313,28 +2607,33 @@
         JoinRequestTrials++;
     }
 
+    LoRaMacState |= LORAMAC_TX_RUNNING;
+
     // Send now
     Radio.Send( LoRaMacBuffer, LoRaMacBufferPktLen );
 
     LoRaMacState |= LORAMAC_TX_RUNNING;
 
-    return LORAMAC_STATUS_OK;
+    return 0;
 }
 
 LoRaMacStatus_t SetTxContinuousWave( uint16_t timeout )
 {
-    int8_t txPowerIndex = 0;
-    int8_t txPower = 0;
-
-    txPowerIndex = LimitTxPower( LoRaMacParams.ChannelsTxPower, Bands[Channels[Channel].Band].TxMaxPower );
-    txPower = TxPowers[txPowerIndex];
+    ContinuousWaveParams_t continuousWave;
+
+    continuousWave.Channel = Channel;
+    continuousWave.Datarate = LoRaMacParams.ChannelsDatarate;
+    continuousWave.TxPower = LoRaMacParams.ChannelsTxPower;
+    continuousWave.MaxEirp = LoRaMacParams.MaxEirp;
+    continuousWave.AntennaGain = LoRaMacParams.AntennaGain;
+    continuousWave.Timeout = timeout;
+
+    RegionSetContinuousWave( LoRaMacRegion, &continuousWave );
 
     // Starts the MAC layer status check timer
     TimerSetValue( &MacStateCheckTimer, MAC_STATE_CHECK_TIMEOUT );
     TimerStart( &MacStateCheckTimer );
 
-    Radio.SetTxContinuousWave( Channels[Channel].Frequency, txPower, timeout );
-
     LoRaMacState |= LORAMAC_TX_RUNNING;
 
     return LORAMAC_STATUS_OK;
@@ -3353,8 +2652,13 @@
     return LORAMAC_STATUS_OK;
 }
 
-LoRaMacStatus_t LoRaMacInitialization( LoRaMacPrimitives_t *primitives, LoRaMacCallback_t *callbacks )
+LoRaMacStatus_t LoRaMacInitialization( LoRaMacPrimitives_t *primitives, LoRaMacCallback_t *callbacks, LoRaMacRegion_t region )
 {
+    GetPhyParams_t getPhy;
+    PhyParam_t phyParam;
+    LoRaMacClassBCallback_t classBCallbacks;
+    LoRaMacClassBParams_t classBParams;
+
     if( primitives == NULL )
     {
         return LORAMAC_STATUS_PARAMETER_INVALID;
@@ -3362,13 +2666,20 @@
 
     if( ( primitives->MacMcpsConfirm == NULL ) ||
         ( primitives->MacMcpsIndication == NULL ) ||
-        ( primitives->MacMlmeConfirm == NULL ) )
+        ( primitives->MacMlmeConfirm == NULL ) ||
+        ( primitives->MacMlmeIndication == NULL ) )
     {
         return LORAMAC_STATUS_PARAMETER_INVALID;
     }
+    // Verify if the region is supported
+    if( RegionIsActive( region ) == false )
+    {
+        return LORAMAC_STATUS_REGION_NOT_SUPPORTED;
+    }
 
     LoRaMacPrimitives = primitives;
     LoRaMacCallbacks = callbacks;
+    LoRaMacRegion = region;
 
     LoRaMacFlags.Value = 0;
 
@@ -3377,102 +2688,80 @@
 
     JoinRequestTrials = 0;
     MaxJoinRequestTrials = 1;
-    RepeaterSupport = false;
 
     // Reset duty cycle times
     AggregatedLastTxDoneTime = 0;
     AggregatedTimeOff = 0;
 
-    // Duty cycle
-#if defined( USE_BAND_433 )
-    DutyCycleOn = true;
-#elif defined( USE_BAND_470 )
-    DutyCycleOn = false;
-#elif defined( USE_BAND_780 )
-    DutyCycleOn = true;
-#elif defined( USE_BAND_868 )
-    DutyCycleOn = true;
-#elif defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
-    DutyCycleOn = false;
-#else
-    #error "Please define a frequency band in the compiler options."
-#endif
-
     // Reset to defaults
-    LoRaMacParamsDefaults.ChannelsTxPower = LORAMAC_DEFAULT_TX_POWER;
-    LoRaMacParamsDefaults.ChannelsDatarate = LORAMAC_DEFAULT_DATARATE;
-
+    getPhy.Attribute = PHY_DUTY_CYCLE;
+    phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+    DutyCycleOn = ( bool ) phyParam.Value;
+
+    getPhy.Attribute = PHY_DEF_TX_POWER;
+    phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+    LoRaMacParamsDefaults.ChannelsTxPower = phyParam.Value;
+
+    getPhy.Attribute = PHY_DEF_TX_DR;
+    phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+    LoRaMacParamsDefaults.ChannelsDatarate = phyParam.Value;
+
+    getPhy.Attribute = PHY_MAX_RX_WINDOW;
+    phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+    LoRaMacParamsDefaults.MaxRxWindow = phyParam.Value;
+
+    getPhy.Attribute = PHY_RECEIVE_DELAY1;
+    phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+    LoRaMacParamsDefaults.ReceiveDelay1 = phyParam.Value;
+
+    getPhy.Attribute = PHY_RECEIVE_DELAY2;
+    phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+    LoRaMacParamsDefaults.ReceiveDelay2 = phyParam.Value;
+
+    getPhy.Attribute = PHY_JOIN_ACCEPT_DELAY1;
+    phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+    LoRaMacParamsDefaults.JoinAcceptDelay1 = phyParam.Value;
+
+    getPhy.Attribute = PHY_JOIN_ACCEPT_DELAY2;
+    phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+    LoRaMacParamsDefaults.JoinAcceptDelay2 = phyParam.Value;
+
+    getPhy.Attribute = PHY_DEF_DR1_OFFSET;
+    phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+    LoRaMacParamsDefaults.Rx1DrOffset = phyParam.Value;
+
+    getPhy.Attribute = PHY_DEF_RX2_FREQUENCY;
+    phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+    LoRaMacParamsDefaults.Rx2Channel.Frequency = phyParam.Value;
+
+    getPhy.Attribute = PHY_DEF_RX2_DR;
+    phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+    LoRaMacParamsDefaults.Rx2Channel.Datarate = phyParam.Value;
+
+    getPhy.Attribute = PHY_DEF_UPLINK_DWELL_TIME;
+    phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+    LoRaMacParamsDefaults.UplinkDwellTime = phyParam.Value;
+
+    getPhy.Attribute = PHY_DEF_DOWNLINK_DWELL_TIME;
+    phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+    LoRaMacParamsDefaults.DownlinkDwellTime = phyParam.Value;
+
+    getPhy.Attribute = PHY_DEF_MAX_EIRP;
+    phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+    LoRaMacParamsDefaults.MaxEirp = phyParam.fValue;
+
+    getPhy.Attribute = PHY_DEF_ANTENNA_GAIN;
+    phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+    LoRaMacParamsDefaults.AntennaGain = phyParam.fValue;
+
+    RegionInitDefaults( LoRaMacRegion, INIT_TYPE_INIT );
+
+    // Init parameters which are not set in function ResetMacParameters
+    LoRaMacParams.RepeaterSupport = false;
+    LoRaMacParamsDefaults.ChannelsNbRep = 1;
     LoRaMacParamsDefaults.SystemMaxRxError = 10;
     LoRaMacParamsDefaults.MinRxSymbols = 6;
-    LoRaMacParamsDefaults.MaxRxWindow = MAX_RX_WINDOW;
-    LoRaMacParamsDefaults.ReceiveDelay1 = RECEIVE_DELAY1;
-    LoRaMacParamsDefaults.ReceiveDelay2 = RECEIVE_DELAY2;
-    LoRaMacParamsDefaults.JoinAcceptDelay1 = JOIN_ACCEPT_DELAY1;
-    LoRaMacParamsDefaults.JoinAcceptDelay2 = JOIN_ACCEPT_DELAY2;
-
-    LoRaMacParamsDefaults.ChannelsNbRep = 1;
-    LoRaMacParamsDefaults.Rx1DrOffset = 0;
-
-    LoRaMacParamsDefaults.Rx2Channel = ( Rx2ChannelParams_t )RX_WND_2_CHANNEL;
-
-    // Channel mask
-#if defined( USE_BAND_433 )
-    LoRaMacParamsDefaults.ChannelsMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 );
-#elif defined ( USE_BAND_470 )
-    LoRaMacParamsDefaults.ChannelsMask[0] = 0xFFFF;
-    LoRaMacParamsDefaults.ChannelsMask[1] = 0xFFFF;
-    LoRaMacParamsDefaults.ChannelsMask[2] = 0xFFFF;
-    LoRaMacParamsDefaults.ChannelsMask[3] = 0xFFFF;
-    LoRaMacParamsDefaults.ChannelsMask[4] = 0xFFFF;
-    LoRaMacParamsDefaults.ChannelsMask[5] = 0xFFFF;
-#elif defined( USE_BAND_780 )
-    LoRaMacParamsDefaults.ChannelsMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 );
-#elif defined( USE_BAND_868 )
-    LoRaMacParamsDefaults.ChannelsMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 );
-#elif defined( USE_BAND_915 )
-    LoRaMacParamsDefaults.ChannelsMask[0] = 0xFFFF;
-    LoRaMacParamsDefaults.ChannelsMask[1] = 0xFFFF;
-    LoRaMacParamsDefaults.ChannelsMask[2] = 0xFFFF;
-    LoRaMacParamsDefaults.ChannelsMask[3] = 0xFFFF;
-    LoRaMacParamsDefaults.ChannelsMask[4] = 0x00FF;
-    LoRaMacParamsDefaults.ChannelsMask[5] = 0x0000;
-#elif defined( USE_BAND_915_HYBRID )
-    LoRaMacParamsDefaults.ChannelsMask[0] = 0x00FF;
-    LoRaMacParamsDefaults.ChannelsMask[1] = 0x0000;
-    LoRaMacParamsDefaults.ChannelsMask[2] = 0x0000;
-    LoRaMacParamsDefaults.ChannelsMask[3] = 0x0000;
-    LoRaMacParamsDefaults.ChannelsMask[4] = 0x0001;
-    LoRaMacParamsDefaults.ChannelsMask[5] = 0x0000;
-#else
-    #error "Please define a frequency band in the compiler options."
-#endif
-
-#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
-    // 125 kHz channels
-    for( uint8_t i = 0; i < LORA_MAX_NB_CHANNELS - 8; i++ )
-    {
-        Channels[i].Frequency = 902.3e6 + i * 200e3;
-        Channels[i].DrRange.Value = ( DR_3 << 4 ) | DR_0;
-        Channels[i].Band = 0;
-    }
-    // 500 kHz channels
-    for( uint8_t i = LORA_MAX_NB_CHANNELS - 8; i < LORA_MAX_NB_CHANNELS; i++ )
-    {
-        Channels[i].Frequency = 903.0e6 + ( i - ( LORA_MAX_NB_CHANNELS - 8 ) ) * 1.6e6;
-        Channels[i].DrRange.Value = ( DR_4 << 4 ) | DR_4;
-        Channels[i].Band = 0;
-    }
-#elif defined( USE_BAND_470 )
-    // 125 kHz channels
-    for( uint8_t i = 0; i < LORA_MAX_NB_CHANNELS; i++ )
-    {
-        Channels[i].Frequency = 470.3e6 + i * 200e3;
-        Channels[i].DrRange.Value = ( DR_5 << 4 ) | DR_0;
-        Channels[i].Band = 0;
-    }
-#endif
-
-    // Init parameters which are not set in function ResetMacParameters
+
     LoRaMacParams.SystemMaxRxError = LoRaMacParamsDefaults.SystemMaxRxError;
     LoRaMacParams.MinRxSymbols = LoRaMacParamsDefaults.MinRxSymbols;
     LoRaMacParams.MaxRxWindow = LoRaMacParamsDefaults.MaxRxWindow;
@@ -3511,12 +2800,39 @@
     Radio.SetPublicNetwork( PublicNetwork );
     Radio.Sleep( );
 
+    // Initialize class b
+    // Apply callback
+    classBCallbacks.GetTemperatureLevel = NULL;
+    if( callbacks != NULL )
+    {
+        classBCallbacks.GetTemperatureLevel = callbacks->GetTemperatureLevel;
+    }
+    classBCallbacks.GetMlmeConfrimIndex = GetMlmeConfirmIndex;
+
+    // Must all be static. Don't use local references.
+    classBParams.MlmeIndication = &MlmeIndication;
+    classBParams.McpsIndication = &McpsIndication;
+    classBParams.MlmeConfirm = &MlmeConfirm;
+    classBParams.LoRaMacFlags = &LoRaMacFlags;
+    classBParams.LoRaMacDevAddr = &LoRaMacDevAddr;
+    classBParams.MlmeConfirmQueue = MlmeConfirmQueue;
+    classBParams.LoRaMacRegion = &LoRaMacRegion;
+    classBParams.MacStateCheckTimer = &MacStateCheckTimer;
+    classBParams.LoRaMacParams = &LoRaMacParams;
+    classBParams.MulticastChannels = MulticastChannels;
+
+    LoRaMacClassBInit( &classBParams, &classBCallbacks );
+
     return LORAMAC_STATUS_OK;
 }
 
 LoRaMacStatus_t LoRaMacQueryTxPossible( uint8_t size, LoRaMacTxInfo_t* txInfo )
 {
+    AdrNextParams_t adrNext;
+    GetPhyParams_t getPhy;
+    PhyParam_t phyParam;
     int8_t datarate = LoRaMacParamsDefaults.ChannelsDatarate;
+    int8_t txPower = LoRaMacParamsDefaults.ChannelsTxPower;
     uint8_t fOptLen = MacCommandsBufferIndex + MacCommandsBufferToRepeatIndex;
 
     if( txInfo == NULL )
@@ -3524,42 +2840,59 @@
         return LORAMAC_STATUS_PARAMETER_INVALID;
     }
 
-    AdrNextDr( AdrCtrlOn, false, &datarate );
-
-    if( RepeaterSupport == true )
+    // Setup ADR request
+    adrNext.UpdateChanMask = false;
+    adrNext.AdrEnabled = AdrCtrlOn;
+    adrNext.AdrAckCounter = AdrAckCounter;
+    adrNext.Datarate = LoRaMacParams.ChannelsDatarate;
+    adrNext.TxPower = LoRaMacParams.ChannelsTxPower;
+    adrNext.UplinkDwellTime = LoRaMacParams.UplinkDwellTime;
+
+    // We call the function for information purposes only. We don't want to
+    // apply the datarate, the tx power and the ADR ack counter.
+    RegionAdrNext( LoRaMacRegion, &adrNext, &datarate, &txPower, &AdrAckCounter );
+
+    // Setup PHY request
+    getPhy.UplinkDwellTime = LoRaMacParams.UplinkDwellTime;
+    getPhy.Datarate = datarate;
+    getPhy.Attribute = PHY_MAX_PAYLOAD;
+
+    // Change request in case repeater is supported
+    if( LoRaMacParams.RepeaterSupport == true )
     {
-        txInfo->CurrentPayloadSize = MaxPayloadOfDatarateRepeater[datarate];
+        getPhy.Attribute = PHY_MAX_PAYLOAD_REPEATER;
     }
-    else
-    {
-        txInfo->CurrentPayloadSize = MaxPayloadOfDatarate[datarate];
-    }
-
+    phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+    txInfo->CurrentPayloadSize = phyParam.Value;
+
+    // Verify if the fOpts fit into the maximum payload
     if( txInfo->CurrentPayloadSize >= fOptLen )
     {
         txInfo->MaxPossiblePayload = txInfo->CurrentPayloadSize - fOptLen;
     }
     else
     {
-        return LORAMAC_STATUS_MAC_CMD_LENGTH_ERROR;
+        txInfo->MaxPossiblePayload = txInfo->CurrentPayloadSize;
+        // The fOpts don't fit into the maximum payload. Omit the MAC commands to
+        // ensure that another uplink is possible.
+        fOptLen = 0;
+        MacCommandsBufferIndex = 0;
+        MacCommandsBufferToRepeatIndex = 0;
     }
 
-    if( ValidatePayloadLength( size, datarate, 0 ) == false )
+    // Verify if the fOpts and the payload fit into the maximum payload
+    if( ValidatePayloadLength( size, datarate, fOptLen ) == false )
     {
         return LORAMAC_STATUS_LENGTH_ERROR;
     }
-
-    if( ValidatePayloadLength( size, datarate, fOptLen ) == false )
-    {
-        return LORAMAC_STATUS_MAC_CMD_LENGTH_ERROR;
-    }
-
     return LORAMAC_STATUS_OK;
 }
 
 LoRaMacStatus_t LoRaMacMibGetRequestConfirm( MibRequestConfirm_t *mibGet )
 {
     LoRaMacStatus_t status = LORAMAC_STATUS_OK;
+    GetPhyParams_t getPhy;
+    PhyParam_t phyParam;
 
     if( mibGet == NULL )
     {
@@ -3610,12 +2943,15 @@
         }
         case MIB_REPEATER_SUPPORT:
         {
-            mibGet->Param.EnableRepeaterSupport = RepeaterSupport;
+            mibGet->Param.EnableRepeaterSupport = LoRaMacParams.RepeaterSupport;
             break;
         }
         case MIB_CHANNELS:
         {
-            mibGet->Param.ChannelList = Channels;
+            getPhy.Attribute = PHY_CHANNELS;
+            phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+
+            mibGet->Param.ChannelList = phyParam.Channels;
             break;
         }
         case MIB_RX2_CHANNEL:
@@ -3630,12 +2966,18 @@
         }
         case MIB_CHANNELS_DEFAULT_MASK:
         {
-            mibGet->Param.ChannelsDefaultMask = LoRaMacParamsDefaults.ChannelsMask;
+            getPhy.Attribute = PHY_CHANNELS_DEFAULT_MASK;
+            phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+
+            mibGet->Param.ChannelsDefaultMask = phyParam.ChannelsMask;
             break;
         }
         case MIB_CHANNELS_MASK:
         {
-            mibGet->Param.ChannelsMask = LoRaMacParams.ChannelsMask;
+            getPhy.Attribute = PHY_CHANNELS_MASK;
+            phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+
+            mibGet->Param.ChannelsMask = phyParam.ChannelsMask;
             break;
         }
         case MIB_CHANNELS_NB_REP:
@@ -3713,17 +3055,32 @@
             mibGet->Param.MinRxSymbols = LoRaMacParams.MinRxSymbols;
             break;
         }
-        default:
-            status = LORAMAC_STATUS_SERVICE_UNKNOWN;
+        case MIB_ANTENNA_GAIN:
+        {
+            mibGet->Param.AntennaGain = LoRaMacParams.AntennaGain;
             break;
+        }
+        default:
+        {
+            if( LoRaMacDeviceClass == CLASS_B )
+            {
+                status = LoRaMacClassBMibGetRequestConfirm( mibGet );
+            }
+            else
+            {
+                status = LORAMAC_STATUS_SERVICE_UNKNOWN;
+            }
+            break;
+        }
     }
-
     return status;
 }
 
 LoRaMacStatus_t LoRaMacMibSetRequestConfirm( MibRequestConfirm_t *mibSet )
 {
     LoRaMacStatus_t status = LORAMAC_STATUS_OK;
+    ChanMaskSetParams_t chanMaskSet;
+    VerifyParams_t verify;
 
     if( mibSet == NULL )
     {
@@ -3738,27 +3095,7 @@
     {
         case MIB_DEVICE_CLASS:
         {
-            LoRaMacDeviceClass = mibSet->Param.Class;
-            switch( LoRaMacDeviceClass )
-            {
-                case CLASS_A:
-                {
-                    // Set the radio into sleep to setup a defined state
-                    Radio.Sleep( );
-                    break;
-                }
-                case CLASS_B:
-                {
-                    break;
-                }
-                case CLASS_C:
-                {
-                    // Set the NodeAckRequested indicator to default
-                    NodeAckRequested = false;
-                    OnRxWindow2TimerEvent( );
-                    break;
-                }
-            }
+            status = SwitchClass( mibSet->Param.Class );
             break;
         }
         case MIB_NETWORK_JOINED:
@@ -3815,58 +3152,44 @@
         }
         case MIB_REPEATER_SUPPORT:
         {
-             RepeaterSupport = mibSet->Param.EnableRepeaterSupport;
+             LoRaMacParams.RepeaterSupport = mibSet->Param.EnableRepeaterSupport;
             break;
         }
         case MIB_RX2_CHANNEL:
         {
-            LoRaMacParams.Rx2Channel = mibSet->Param.Rx2Channel;
-            break;
-        }
-        case MIB_RX2_DEFAULT_CHANNEL:
-        {
-            LoRaMacParamsDefaults.Rx2Channel = mibSet->Param.Rx2DefaultChannel;
-            break;
-        }
-        case MIB_CHANNELS_DEFAULT_MASK:
-        {
-            if( mibSet->Param.ChannelsDefaultMask )
+            verify.DatarateParams.Datarate = mibSet->Param.Rx2Channel.Datarate;
+            verify.DatarateParams.DownlinkDwellTime = LoRaMacParams.DownlinkDwellTime;
+
+            if( RegionVerify( LoRaMacRegion, &verify, PHY_RX_DR ) == true )
             {
-#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
-                bool chanMaskState = true;
-
-#if defined( USE_BAND_915_HYBRID )
-                chanMaskState = ValidateChannelMask( mibSet->Param.ChannelsDefaultMask );
-#endif
-                if( chanMaskState == true )
+                LoRaMacParams.Rx2Channel = mibSet->Param.Rx2Channel;
+
+                if( ( LoRaMacDeviceClass == CLASS_C ) && ( IsLoRaMacNetworkJoined == true ) )
                 {
-                    if( ( CountNbEnabled125kHzChannels( mibSet->Param.ChannelsMask ) < 2 ) &&
-                        ( CountNbEnabled125kHzChannels( mibSet->Param.ChannelsMask ) > 0 ) )
+                    // Compute Rx2 windows parameters
+                    RegionComputeRxWindowParameters( LoRaMacRegion,
+                                                     LoRaMacParams.Rx2Channel.Datarate,
+                                                     LoRaMacParams.MinRxSymbols,
+                                                     LoRaMacParams.SystemMaxRxError,
+                                                     &RxWindow2Config );
+
+                    RxWindow2Config.Channel = Channel;
+                    RxWindow2Config.Frequency = LoRaMacParams.Rx2Channel.Frequency;
+                    RxWindow2Config.DownlinkDwellTime = LoRaMacParams.DownlinkDwellTime;
+                    RxWindow2Config.RepeaterSupport = LoRaMacParams.RepeaterSupport;
+                    RxWindow2Config.Window = 1;
+                    RxWindow2Config.RxContinuous = true;
+
+                    if( RegionRxConfig( LoRaMacRegion, &RxWindow2Config, ( int8_t* )&McpsIndication.RxDatarate ) == true )
+                    {
+                        RxWindowSetup( RxWindow2Config.RxContinuous, LoRaMacParams.MaxRxWindow );
+                        RxSlot = RxWindow2Config.Window;
+                    }
+                    else
                     {
                         status = LORAMAC_STATUS_PARAMETER_INVALID;
                     }
-                    else
-                    {
-                        memcpy1( ( uint8_t* ) LoRaMacParamsDefaults.ChannelsMask,
-                                 ( uint8_t* ) mibSet->Param.ChannelsDefaultMask, sizeof( LoRaMacParamsDefaults.ChannelsMask ) );
-                        for ( uint8_t i = 0; i < sizeof( LoRaMacParamsDefaults.ChannelsMask ) / 2; i++ )
-                        {
-                            // Disable channels which are no longer available
-                            ChannelsMaskRemaining[i] &= LoRaMacParamsDefaults.ChannelsMask[i];
-                        }
-                    }
                 }
-                else
-                {
-                    status = LORAMAC_STATUS_PARAMETER_INVALID;
-                }
-#elif defined( USE_BAND_470 )
-                memcpy1( ( uint8_t* ) LoRaMacParamsDefaults.ChannelsMask,
-                         ( uint8_t* ) mibSet->Param.ChannelsDefaultMask, sizeof( LoRaMacParamsDefaults.ChannelsMask ) );
-#else
-                memcpy1( ( uint8_t* ) LoRaMacParamsDefaults.ChannelsMask,
-                         ( uint8_t* ) mibSet->Param.ChannelsDefaultMask, 2 );
-#endif
             }
             else
             {
@@ -3874,47 +3197,38 @@
             }
             break;
         }
+        case MIB_RX2_DEFAULT_CHANNEL:
+        {
+            verify.DatarateParams.Datarate = mibSet->Param.Rx2Channel.Datarate;
+            verify.DatarateParams.DownlinkDwellTime = LoRaMacParams.DownlinkDwellTime;
+
+            if( RegionVerify( LoRaMacRegion, &verify, PHY_RX_DR ) == true )
+            {
+                LoRaMacParamsDefaults.Rx2Channel = mibSet->Param.Rx2DefaultChannel;
+            }
+            else
+            {
+                status = LORAMAC_STATUS_PARAMETER_INVALID;
+            }
+            break;
+        }
+        case MIB_CHANNELS_DEFAULT_MASK:
+        {
+            chanMaskSet.ChannelsMaskIn = mibSet->Param.ChannelsMask;
+            chanMaskSet.ChannelsMaskType = CHANNELS_DEFAULT_MASK;
+
+            if( RegionChanMaskSet( LoRaMacRegion, &chanMaskSet ) == false )
+            {
+                status = LORAMAC_STATUS_PARAMETER_INVALID;
+            }
+            break;
+        }
         case MIB_CHANNELS_MASK:
         {
-            if( mibSet->Param.ChannelsMask )
-            {
-#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
-                bool chanMaskState = true;
-
-#if defined( USE_BAND_915_HYBRID )
-                chanMaskState = ValidateChannelMask( mibSet->Param.ChannelsMask );
-#endif
-                if( chanMaskState == true )
-                {
-                    if( ( CountNbEnabled125kHzChannels( mibSet->Param.ChannelsMask ) < 2 ) &&
-                        ( CountNbEnabled125kHzChannels( mibSet->Param.ChannelsMask ) > 0 ) )
-                    {
-                        status = LORAMAC_STATUS_PARAMETER_INVALID;
-                    }
-                    else
-                    {
-                        memcpy1( ( uint8_t* ) LoRaMacParams.ChannelsMask,
-                                 ( uint8_t* ) mibSet->Param.ChannelsMask, sizeof( LoRaMacParams.ChannelsMask ) );
-                        for ( uint8_t i = 0; i < sizeof( LoRaMacParams.ChannelsMask ) / 2; i++ )
-                        {
-                            // Disable channels which are no longer available
-                            ChannelsMaskRemaining[i] &= LoRaMacParams.ChannelsMask[i];
-                        }
-                    }
-                }
-                else
-                {
-                    status = LORAMAC_STATUS_PARAMETER_INVALID;
-                }
-#elif defined( USE_BAND_470 )
-                memcpy1( ( uint8_t* ) LoRaMacParams.ChannelsMask,
-                         ( uint8_t* ) mibSet->Param.ChannelsMask, sizeof( LoRaMacParams.ChannelsMask ) );
-#else
-                memcpy1( ( uint8_t* ) LoRaMacParams.ChannelsMask,
-                         ( uint8_t* ) mibSet->Param.ChannelsMask, 2 );
-#endif
-            }
-            else
+            chanMaskSet.ChannelsMaskIn = mibSet->Param.ChannelsMask;
+            chanMaskSet.ChannelsMaskType = CHANNELS_MASK;
+
+            if( RegionChanMaskSet( LoRaMacRegion, &chanMaskSet ) == false )
             {
                 status = LORAMAC_STATUS_PARAMETER_INVALID;
             }
@@ -3960,19 +3274,12 @@
         }
         case MIB_CHANNELS_DEFAULT_DATARATE:
         {
-#if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 )
-            if( ValueInRange( mibSet->Param.ChannelsDefaultDatarate,
-                              DR_0, DR_5 ) )
+            verify.DatarateParams.Datarate = mibSet->Param.ChannelsDefaultDatarate;
+
+            if( RegionVerify( LoRaMacRegion, &verify, PHY_DEF_TX_DR ) == true )
             {
-                LoRaMacParamsDefaults.ChannelsDatarate = mibSet->Param.ChannelsDefaultDatarate;
+                LoRaMacParamsDefaults.ChannelsDatarate = verify.DatarateParams.Datarate;
             }
-#else
-            if( ValueInRange( mibSet->Param.ChannelsDefaultDatarate,
-                              LORAMAC_TX_MIN_DATARATE, LORAMAC_TX_MAX_DATARATE ) )
-            {
-                LoRaMacParamsDefaults.ChannelsDatarate = mibSet->Param.ChannelsDefaultDatarate;
-            }
-#endif
             else
             {
                 status = LORAMAC_STATUS_PARAMETER_INVALID;
@@ -3981,10 +3288,11 @@
         }
         case MIB_CHANNELS_DATARATE:
         {
-            if( ValueInRange( mibSet->Param.ChannelsDatarate,
-                              LORAMAC_TX_MIN_DATARATE, LORAMAC_TX_MAX_DATARATE ) )
+            verify.DatarateParams.Datarate = mibSet->Param.ChannelsDatarate;
+
+            if( RegionVerify( LoRaMacRegion, &verify, PHY_TX_DR ) == true )
             {
-                LoRaMacParams.ChannelsDatarate = mibSet->Param.ChannelsDatarate;
+                LoRaMacParams.ChannelsDatarate = verify.DatarateParams.Datarate;
             }
             else
             {
@@ -3994,10 +3302,11 @@
         }
         case MIB_CHANNELS_DEFAULT_TX_POWER:
         {
-            if( ValueInRange( mibSet->Param.ChannelsDefaultTxPower,
-                              LORAMAC_MAX_TX_POWER, LORAMAC_MIN_TX_POWER ) )
+            verify.TxPower = mibSet->Param.ChannelsDefaultTxPower;
+
+            if( RegionVerify( LoRaMacRegion, &verify, PHY_DEF_TX_POWER ) == true )
             {
-                LoRaMacParamsDefaults.ChannelsTxPower = mibSet->Param.ChannelsDefaultTxPower;
+                LoRaMacParamsDefaults.ChannelsTxPower = verify.TxPower;
             }
             else
             {
@@ -4007,10 +3316,11 @@
         }
         case MIB_CHANNELS_TX_POWER:
         {
-            if( ValueInRange( mibSet->Param.ChannelsTxPower,
-                              LORAMAC_MAX_TX_POWER, LORAMAC_MIN_TX_POWER ) )
+            verify.TxPower = mibSet->Param.ChannelsTxPower;
+
+            if( RegionVerify( LoRaMacRegion, &verify, PHY_TX_POWER ) == true )
             {
-                LoRaMacParams.ChannelsTxPower = mibSet->Param.ChannelsTxPower;
+                LoRaMacParams.ChannelsTxPower = verify.TxPower;
             }
             else
             {
@@ -4038,9 +3348,23 @@
             LoRaMacParams.MinRxSymbols = LoRaMacParamsDefaults.MinRxSymbols = mibSet->Param.MinRxSymbols;
             break;
         }
+        case MIB_ANTENNA_GAIN:
+        {
+            LoRaMacParams.AntennaGain = mibSet->Param.AntennaGain;
+            break;
+        }
         default:
-            status = LORAMAC_STATUS_SERVICE_UNKNOWN;
+        {
+            if( LoRaMacDeviceClass == CLASS_B )
+            {
+                status = LoRaMacMibClassBSetRequestConfirm( mibSet );
+            }
+            else
+            {
+                status = LORAMAC_STATUS_SERVICE_UNKNOWN;
+            }
             break;
+        }
     }
 
     return status;
@@ -4048,18 +3372,8 @@
 
 LoRaMacStatus_t LoRaMacChannelAdd( uint8_t id, ChannelParams_t params )
 {
-#if defined( USE_BAND_470 ) || defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
-    return LORAMAC_STATUS_PARAMETER_INVALID;
-#else
-    bool datarateInvalid = false;
-    bool frequencyInvalid = false;
-    uint8_t band = 0;
-
-    // The id must not exceed LORA_MAX_NB_CHANNELS
-    if( id >= LORA_MAX_NB_CHANNELS )
-    {
-        return LORAMAC_STATUS_PARAMETER_INVALID;
-    }
+    ChannelAddParams_t channelAdd;
+
     // Validate if the MAC is in a correct state
     if( ( LoRaMacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING )
     {
@@ -4068,99 +3382,17 @@
             return LORAMAC_STATUS_BUSY;
         }
     }
-    // Validate the datarate
-    if( ( params.DrRange.Fields.Min > params.DrRange.Fields.Max ) ||
-        ( ValueInRange( params.DrRange.Fields.Min, LORAMAC_TX_MIN_DATARATE,
-                        LORAMAC_TX_MAX_DATARATE ) == false ) ||
-        ( ValueInRange( params.DrRange.Fields.Max, LORAMAC_TX_MIN_DATARATE,
-                        LORAMAC_TX_MAX_DATARATE ) == false ) )
-    {
-        datarateInvalid = true;
-    }
-
-#if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 )
-    if( id < 3 )
-    {
-        if( params.Frequency != Channels[id].Frequency )
-        {
-            frequencyInvalid = true;
-        }
-
-        if( params.DrRange.Fields.Min > DR_0 )
-        {
-            datarateInvalid = true;
-        }
-        if( ValueInRange( params.DrRange.Fields.Max, DR_5, LORAMAC_TX_MAX_DATARATE ) == false )
-        {
-            datarateInvalid = true;
-        }
-    }
-#endif
-
-    // Validate the frequency
-    if( ( Radio.CheckRfFrequency( params.Frequency ) == true ) && ( params.Frequency > 0 ) && ( frequencyInvalid == false ) )
-    {
-#if defined( USE_BAND_868 )
-        if( ( params.Frequency >= 863000000 ) && ( params.Frequency < 865000000 ) )
-        {
-            band = BAND_G1_2;
-        }
-        else if( ( params.Frequency >= 865000000 ) && ( params.Frequency <= 868000000 ) )
-        {
-            band = BAND_G1_0;
-        }
-        else if( ( params.Frequency > 868000000 ) && ( params.Frequency <= 868600000 ) )
-        {
-            band = BAND_G1_1;
-        }
-        else if( ( params.Frequency >= 868700000 ) && ( params.Frequency <= 869200000 ) )
-        {
-            band = BAND_G1_2;
-        }
-        else if( ( params.Frequency >= 869400000 ) && ( params.Frequency <= 869650000 ) )
-        {
-            band = BAND_G1_3;
-        }
-        else if( ( params.Frequency >= 869700000 ) && ( params.Frequency <= 870000000 ) )
-        {
-            band = BAND_G1_4;
-        }
-        else
-        {
-            frequencyInvalid = true;
-        }
-#endif
-    }
-    else
-    {
-        frequencyInvalid = true;
-    }
-
-    if( ( datarateInvalid == true ) && ( frequencyInvalid == true ) )
-    {
-        return LORAMAC_STATUS_FREQ_AND_DR_INVALID;
-    }
-    if( datarateInvalid == true )
-    {
-        return LORAMAC_STATUS_DATARATE_INVALID;
-    }
-    if( frequencyInvalid == true )
-    {
-        return LORAMAC_STATUS_FREQUENCY_INVALID;
-    }
-
-    // Every parameter is valid, activate the channel
-    Channels[id] = params;
-    Channels[id].Band = band;
-    LoRaMacParams.ChannelsMask[0] |= ( 1 << id );
-
-    return LORAMAC_STATUS_OK;
-#endif
+
+    channelAdd.NewChannel = &params;
+    channelAdd.ChannelId = id;
+
+    return RegionChannelAdd( LoRaMacRegion, &channelAdd );
 }
 
 LoRaMacStatus_t LoRaMacChannelRemove( uint8_t id )
 {
-#if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 )
+    ChannelRemoveParams_t channelRemove;
+
     if( ( LoRaMacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING )
     {
         if( ( LoRaMacState & LORAMAC_TX_CONFIG ) != LORAMAC_TX_CONFIG )
@@ -4169,25 +3401,13 @@
         }
     }
 
-    if( ( id < 3 ) || ( id >= LORA_MAX_NB_CHANNELS ) )
+    channelRemove.ChannelId = id;
+
+    if( RegionChannelsRemove( LoRaMacRegion, &channelRemove ) == false )
     {
         return LORAMAC_STATUS_PARAMETER_INVALID;
     }
-    else
-    {
-        // Remove the channel from the list of channels
-        Channels[id] = ( ChannelParams_t ){ 0, { 0 }, 0 };
-
-        // Disable the channel as it doesn't exist anymore
-        if( DisableChannelInMask( id, LoRaMacParams.ChannelsMask ) == false )
-        {
-            return LORAMAC_STATUS_PARAMETER_INVALID;
-        }
-    }
     return LORAMAC_STATUS_OK;
-#elif ( defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) || defined( USE_BAND_470 ) )
-    return LORAMAC_STATUS_PARAMETER_INVALID;
-#endif
 }
 
 LoRaMacStatus_t LoRaMacMulticastChannelLink( MulticastParams_t *channelParam )
@@ -4203,6 +3423,7 @@
 
     // Reset downlink counter
     channelParam->DownLinkCounter = 0;
+    channelParam->Next = NULL;
 
     if( MulticastChannels == NULL )
     {
@@ -4268,6 +3489,10 @@
 {
     LoRaMacStatus_t status = LORAMAC_STATUS_SERVICE_UNKNOWN;
     LoRaMacHeader_t macHdr;
+    AlternateDrParams_t altDr;
+    VerifyParams_t verify;
+    GetPhyParams_t getPhy;
+    PhyParam_t phyParam;
 
     if( mlmeRequest == NULL )
     {
@@ -4277,11 +3502,18 @@
     {
         return LORAMAC_STATUS_BUSY;
     }
-
-    memset1( ( uint8_t* ) &MlmeConfirm, 0, sizeof( MlmeConfirm ) );
-
-    MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
-
+    if( LORA_MAC_MLME_CONFIRM_QUEUE_LEN <= MlmeConfirmQueueCnt )
+    {
+        return LORAMAC_STATUS_BUSY;
+    }
+
+    // Reset the confirm queue if it is empty
+    if( MlmeConfirmQueueCnt == 0 )
+    {
+        memset1( ( uint8_t* ) &MlmeConfirm, 0, sizeof( MlmeConfirm ) );
+    }
+
+    // Switch requests
     switch( mlmeRequest->Type )
     {
         case MLME_JOIN:
@@ -4299,22 +3531,21 @@
                 return LORAMAC_STATUS_PARAMETER_INVALID;
             }
 
-#if ( defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) )
-            // Enables at least the usage of the 2 datarates.
-            if( mlmeRequest->Req.Join.NbTrials < 2 )
+            // Apply the request
+            LoRaMacFlags.Bits.MlmeReq = 1;
+            MlmeConfirmQueue[MlmeConfirmQueueCnt].MlmeRequest = mlmeRequest->Type;
+            MlmeConfirmQueueCnt++;
+
+            // Verify the parameter NbTrials for the join procedure
+            verify.NbJoinTrials = mlmeRequest->Req.Join.NbTrials;
+
+            if( RegionVerify( LoRaMacRegion, &verify, PHY_NB_JOIN_TRIALS ) == false )
             {
-                mlmeRequest->Req.Join.NbTrials = 2;
+                // Value not supported, get default
+                getPhy.Attribute = PHY_DEF_NB_JOIN_TRIALS;
+                phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+                mlmeRequest->Req.Join.NbTrials = ( uint8_t ) phyParam.Value;
             }
-#else
-            // Enables at least the usage of all datarates.
-            if( mlmeRequest->Req.Join.NbTrials < 48 )
-            {
-                mlmeRequest->Req.Join.NbTrials = 48;
-            }
-#endif
-
-            LoRaMacFlags.Bits.MlmeReq = 1;
-            MlmeConfirm.MlmeRequest = mlmeRequest->Type;
 
             LoRaMacDevEui = mlmeRequest->Req.Join.DevEui;
             LoRaMacAppEui = mlmeRequest->Req.Join.AppEui;
@@ -4330,33 +3561,85 @@
 
             ResetMacParameters( );
 
-            // Add a +1, since we start to count from 0
-            LoRaMacParams.ChannelsDatarate = AlternateDatarate( JoinRequestTrials + 1 );
+            altDr.NbTrials = JoinRequestTrials + 1;
+
+            LoRaMacParams.ChannelsDatarate = RegionAlternateDr( LoRaMacRegion, &altDr );
 
             status = Send( &macHdr, 0, NULL, 0 );
             break;
         }
         case MLME_LINK_CHECK:
         {
+            // Apply the request
             LoRaMacFlags.Bits.MlmeReq = 1;
+            MlmeConfirmQueue[MlmeConfirmQueueCnt].MlmeRequest = mlmeRequest->Type;
+            MlmeConfirmQueueCnt++;
+
             // LoRaMac will send this command piggy-pack
-            MlmeConfirm.MlmeRequest = mlmeRequest->Type;
-
             status = AddMacCommand( MOTE_MAC_LINK_CHECK_REQ, 0, 0 );
             break;
         }
         case MLME_TXCW:
         {
-            MlmeConfirm.MlmeRequest = mlmeRequest->Type;
+            // Apply the request
             LoRaMacFlags.Bits.MlmeReq = 1;
+            MlmeConfirmQueue[MlmeConfirmQueueCnt].MlmeRequest = mlmeRequest->Type;
+            MlmeConfirmQueueCnt++;
             status = SetTxContinuousWave( mlmeRequest->Req.TxCw.Timeout );
             break;
         }
         case MLME_TXCW_1:
         {
-            MlmeConfirm.MlmeRequest = mlmeRequest->Type;
+            // Apply the request
+            LoRaMacFlags.Bits.MlmeReq = 1;
+            MlmeConfirmQueue[MlmeConfirmQueueCnt].MlmeRequest = mlmeRequest->Type;
+            MlmeConfirmQueueCnt++;
+            status = SetTxContinuousWave1( mlmeRequest->Req.TxCw.Timeout, mlmeRequest->Req.TxCw.Frequency, mlmeRequest->Req.TxCw.Power );
+            break;
+        }
+        case MLME_PING_SLOT_INFO:
+        {
+            uint8_t value = mlmeRequest->Req.PingSlotInfo.PingSlot.Value;
+
+            // Apply the request
+            LoRaMacFlags.Bits.MlmeReq = 1;
+            MlmeConfirmQueue[MlmeConfirmQueueCnt].MlmeRequest = mlmeRequest->Type;
+            MlmeConfirmQueueCnt++;
+            // LoRaMac will send this command piggy-pack
+            LoRaMacClassBSetPingSlotInfo( mlmeRequest->Req.PingSlotInfo.PingSlot.Fields.Periodicity );
+
+            status = AddMacCommand( MOTE_MAC_PING_SLOT_INFO_REQ, value, 0 );
+            break;
+        }
+        case MLME_BEACON_TIMING:
+        {
+            // Apply the request
             LoRaMacFlags.Bits.MlmeReq = 1;
-            status = SetTxContinuousWave1( mlmeRequest->Req.TxCw.Timeout, mlmeRequest->Req.TxCw.Frequency, mlmeRequest->Req.TxCw.Power );
+            MlmeConfirmQueue[MlmeConfirmQueueCnt].MlmeRequest = mlmeRequest->Type;
+            MlmeConfirmQueueCnt++;
+            // LoRaMac will send this command piggy-pack
+            status = AddMacCommand( MOTE_MAC_BEACON_TIMING_REQ, 0, 0 );
+            break;
+        }
+        case MLME_BEACON_ACQUISITION:
+        {
+            // Apply the request
+            LoRaMacFlags.Bits.MlmeReq = 1;
+            MlmeConfirmQueue[MlmeConfirmQueueCnt].MlmeRequest = mlmeRequest->Type;
+            MlmeConfirmQueueCnt++;
+
+            if( ( LoRaMacClassBIsAcquisitionPending( ) == false ) && ( LoRaMacClassBIsAcquisitionTimerSet( ) == false ) )
+            {
+                // Start class B algorithm
+                LoRaMacClassBSetBeaconState( BEACON_STATE_ACQUISITION );
+                LoRaMacClassBBeaconTimerEvent( );
+
+                status = LORAMAC_STATUS_OK;
+            }
+            else
+            {
+                status = LORAMAC_STATUS_BUSY;
+            }
             break;
         }
         default:
@@ -4366,7 +3649,11 @@
     if( status != LORAMAC_STATUS_OK )
     {
         NodeAckRequested = false;
-        LoRaMacFlags.Bits.MlmeReq = 0;
+        MlmeConfirmQueueCnt--;
+        if( MlmeConfirmQueueCnt == 0 )
+        {
+            LoRaMacFlags.Bits.MlmeReq = 0;
+        }
     }
 
     return status;
@@ -4374,8 +3661,11 @@
 
 LoRaMacStatus_t LoRaMacMcpsRequest( McpsReq_t *mcpsRequest )
 {
+    GetPhyParams_t getPhy;
+    PhyParam_t phyParam;
     LoRaMacStatus_t status = LORAMAC_STATUS_SERVICE_UNKNOWN;
     LoRaMacHeader_t macHdr;
+    VerifyParams_t verify;
     uint8_t fPort = 0;
     void *fBuffer;
     uint16_t fBufferSize;
@@ -4396,6 +3686,9 @@
     memset1 ( ( uint8_t* ) &McpsConfirm, 0, sizeof( McpsConfirm ) );
     McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
 
+    // AckTimeoutRetriesCounter must be reset every time a new request (unconfirmed or confirmed) is performed.
+    AckTimeoutRetriesCounter = 1;
+
     switch( mcpsRequest->Type )
     {
         case MCPS_UNCONFIRMED:
@@ -4413,7 +3706,6 @@
         case MCPS_CONFIRMED:
         {
             readyToSend = true;
-            AckTimeoutRetriesCounter = 1;
             AckTimeoutRetries = mcpsRequest->Req.Confirmed.NbTrials;
 
             macHdr.Bits.MType = FRAME_TYPE_DATA_CONFIRMED_UP;
@@ -4438,13 +3730,24 @@
             break;
     }
 
+    // Get the minimum possible datarate
+    getPhy.Attribute = PHY_MIN_TX_DR;
+    getPhy.UplinkDwellTime = LoRaMacParams.UplinkDwellTime;
+    phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
+    // Apply the minimum possible datarate.
+    // Some regions have limitations for the minimum datarate.
+    datarate = MAX( datarate, phyParam.Value );
+
     if( readyToSend == true )
     {
         if( AdrCtrlOn == false )
         {
-            if( ValueInRange( datarate, LORAMAC_TX_MIN_DATARATE, LORAMAC_TX_MAX_DATARATE ) == true )
+            verify.DatarateParams.Datarate = datarate;
+            verify.DatarateParams.UplinkDwellTime = LoRaMacParams.UplinkDwellTime;
+
+            if( RegionVerify( LoRaMacRegion, &verify, PHY_TX_DR ) == true )
             {
-                LoRaMacParams.ChannelsDatarate = datarate;
+                LoRaMacParams.ChannelsDatarate = verify.DatarateParams.Datarate;
             }
             else
             {
@@ -4480,52 +3783,17 @@
 
 void LoRaMacTestSetDutyCycleOn( bool enable )
 {
-#if ( defined( USE_BAND_868 ) || defined( USE_BAND_433 ) || defined( USE_BAND_780 ) )
-    DutyCycleOn = enable;
-#else
-    DutyCycleOn = false;
-#endif
+    VerifyParams_t verify;
+
+    verify.DutyCycle = enable;
+
+    if( RegionVerify( LoRaMacRegion, &verify, PHY_DUTY_CYCLE ) == true )
+    {
+        DutyCycleOn = enable;
+    }
 }
 
 void LoRaMacTestSetChannel( uint8_t channel )
 {
     Channel = channel;
 }
-
-static RxConfigParams_t ComputeRxWindowParameters( int8_t datarate, uint32_t rxError )
-{
-    RxConfigParams_t rxConfigParams = { 0, 0, 0, 0 };
-    double tSymbol = 0.0;
-
-    rxConfigParams.Datarate = datarate;
-    switch( Bandwidths[datarate] )
-    {
-        default:
-        case 125000:
-            rxConfigParams.Bandwidth = 0;
-            break;
-        case 250000:
-            rxConfigParams.Bandwidth = 1;
-            break;
-        case 500000:
-            rxConfigParams.Bandwidth = 2;
-            break;
-    }
-
-#if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 )
-    if( datarate == DR_7 )
-    { // FSK
-        tSymbol = ( 1.0 / ( double )Datarates[datarate] ) * 8.0; // 1 symbol equals 1 byte
-    }
-    else
-#endif
-    { // LoRa
-        tSymbol = ( ( double )( 1 << Datarates[datarate] ) / ( double )Bandwidths[datarate] ) * 1e3;
-    }
-
-    rxConfigParams.RxWindowTimeout = MAX( ( uint32_t )ceil( ( ( 2 * LoRaMacParams.MinRxSymbols - 8 ) * tSymbol + 2 * rxError ) / tSymbol ), LoRaMacParams.MinRxSymbols ); // Computed number of symbols
-
-    rxConfigParams.RxOffset = ( int32_t )ceil( ( 4.0 * tSymbol ) - ( ( rxConfigParams.RxWindowTimeout * tSymbol ) / 2.0 ) - RADIO_WAKEUP_TIME );
-
-    return rxConfigParams;
-}
--- a/LoRaMac.h	Tue Aug 08 19:27:17 2017 -0400
+++ b/LoRaMac.h	Wed Aug 09 16:20:21 2017 -0400
@@ -26,7 +26,7 @@
  *
  * \author    Gregory Cristian ( Semtech )
  *
- * \author    Daniel Jäckle ( STACKFORCE )
+ * \author    Daniel Jaeckle ( STACKFORCE )
  *
  * \defgroup  LORAMAC LoRa MAC layer implementation
  *            This module specifies the API implementation of the LoRaMAC layer.
@@ -42,79 +42,51 @@
  *
  * \example   classC/LoRaMote/main.c
  *            LoRaWAN class C application example for the LoRaMote.
+ *
+ * \example   classA/MoteII/main.c
+ *            LoRaWAN class A application example for the MoteII.
+ *
+ * \example   classB/MoteII/main.c
+ *            LoRaWAN class B application example for the MoteII.
+ *
+ * \example   classC/MoteII/main.c
+ *            LoRaWAN class C application example for the MoteII.
+ *
+ * \example   classA/NAMote72/main.c
+ *            LoRaWAN class A application example for the NAMote72.
+ *
+ * \example   classB/NAMote72/main.c
+ *            LoRaWAN class B application example for the NAMote72.
+ *
+ * \example   classC/NAMote72/main.c
+ *            LoRaWAN class C application example for the NAMote72.
+ *
+ * \example   classA/SensorNode/main.c
+ *            LoRaWAN class A application example for the SensorNode.
+ *
+ * \example   classB/SensorNode/main.c
+ *            LoRaWAN class B application example for the SensorNode.
+ *
+ * \example   classC/SensorNode/main.c
+ *            LoRaWAN class C application example for the SensorNode.
+ *
+ * \example   classA/SK-iM880A/main.c
+ *            LoRaWAN class A application example for the SK-iM880A.
+ *
+ * \example   classB/SK-iM880A/main.c
+ *            LoRaWAN class B application example for the SK-iM880A.
+ *
+ * \example   classC/SK-iM880A/main.c
+ *            LoRaWAN class C application example for the SK-iM880A.
  */
 #ifndef __LORAMAC_H__
 #define __LORAMAC_H__
-
-// Includes board dependent definitions such as channels frequencies
-#include "LoRaMac-definitions.h"
-
 /*!
  * Stack Version Information
  */
 #define LORAMAC_MAJOR_VERSION                       4
-#define LORAMAC_MINOR_VERSION                       3
-#define LORAMAC_POINT_VERSION                       2
-
-/*!
- * Beacon interval in ms
- */
-#define BEACON_INTERVAL                             128000
-
-/*!
- * Class A&B receive delay 1 in ms
- */
-#define RECEIVE_DELAY1                              1000
-
-/*!
- * Class A&B receive delay 2 in ms
- */
-#define RECEIVE_DELAY2                              2000
-
-/*!
- * Join accept receive delay 1 in ms
- */
-#define JOIN_ACCEPT_DELAY1                          5000
-
-/*!
- * Join accept receive delay 2 in ms
- */
-#define JOIN_ACCEPT_DELAY2                          6000
-
-/*!
- * Class A&B maximum receive window delay in ms
- */
-#define MAX_RX_WINDOW                               3000
-
-/*!
- * Maximum allowed gap for the FCNT field
- */
-#define MAX_FCNT_GAP                                16384
-
-/*!
- * ADR acknowledgement counter limit
- */
-#define ADR_ACK_LIMIT                               64
-
-/*!
- * Number of ADR acknowledgement requests before returning to default datarate
- */
-#define ADR_ACK_DELAY                               32
-
-/*!
- * Number of seconds after the start of the second reception window without
- * receiving an acknowledge.
- * AckTimeout = \ref ACK_TIMEOUT + Random( -\ref ACK_TIMEOUT_RND, \ref ACK_TIMEOUT_RND )
- */
-#define ACK_TIMEOUT                                 2000
-
-/*!
- * Random number of seconds after the start of the second reception window without
- * receiving an acknowledge
- * AckTimeout = \ref ACK_TIMEOUT + Random( -\ref ACK_TIMEOUT_RND, \ref ACK_TIMEOUT_RND )
- */
-#define ACK_TIMEOUT_RND                             1000
-
+#define LORAMAC_MINOR_VERSION                       4
+#define LORAMAC_POINT_VERSION                       0
 /*!
  * Check the Mac layer state every MAC_STATE_CHECK_TIMEOUT in ms
  */
@@ -126,11 +98,6 @@
 #define MAX_ACK_RETRIES                             8
 
 /*!
- * RSSI free threshold [dBm]
- */
-#define RSSI_FREE_TH                                ( int8_t )( -90 )
-
-/*!
  * Frame direction definition for up-link communications
  */
 #define UP_LINK                                     0
@@ -147,26 +114,40 @@
 #define LORAMAC_MFR_LEN                             4
 
 /*!
+ * LoRaMac MLME-Confirm queue length
+ */
+#define LORA_MAC_MLME_CONFIRM_QUEUE_LEN             5
+
+/*!
+ * FRMPayload overhead to be used when setting the Radio.SetMaxPayloadLength
+ * in RxWindowSetup function.
+ * Maximum PHYPayload = MaxPayloadOfDatarate/MaxPayloadOfDatarateRepeater + LORA_MAC_FRMPAYLOAD_OVERHEAD
+ */
+#define LORA_MAC_FRMPAYLOAD_OVERHEAD                13 // MHDR(1) + FHDR(7) + Port(1) + MIC(4)
+
+/*!
  * LoRaWAN devices classes definition
+ *
+ * LoRaWAN Specification V1.0.2, chapter 2.1
  */
 typedef enum eDeviceClass
 {
     /*!
      * LoRaWAN device class A
      *
-     * LoRaWAN Specification V1.0.1, chapter 3ff
+     * LoRaWAN Specification V1.0.2, chapter 3
      */
     CLASS_A,
     /*!
      * LoRaWAN device class B
      *
-     * LoRaWAN Specification V1.0.1, chapter 8ff
+     * LoRaWAN Specification V1.0.2, chapter 8
      */
     CLASS_B,
     /*!
      * LoRaWAN device class C
      *
-     * LoRaWAN Specification V1.0.1, chapter 17ff
+     * LoRaWAN Specification V1.0.2, chapter 17
      */
     CLASS_C,
 }DeviceClass_t;
@@ -188,17 +169,17 @@
          /*!
          * Minimum data rate
          *
-         * EU868 - [DR_0, DR_1, DR_2, DR_3, DR_4, DR_5, DR_6, DR_7]
+         * LoRaWAN Regional Parameters V1.0.2rB
          *
-         * US915 - [DR_0, DR_1, DR_2, DR_3, DR_4]
+         * The allowed ranges are region specific. Please refer to \ref DR_0 to \ref DR_15 for details.
          */
         int8_t Min : 4;
         /*!
          * Maximum data rate
          *
-         * EU868 - [DR_0, DR_1, DR_2, DR_3, DR_4, DR_5, DR_6, DR_7]
+         * LoRaWAN Regional Parameters V1.0.2rB
          *
-         * US915 - [DR_0, DR_1, DR_2, DR_3, DR_4]
+         * The allowed ranges are region specific. Please refer to \ref DR_0 to \ref DR_15 for details.
          */
         int8_t Max : 4;
     }Fields;
@@ -218,6 +199,10 @@
      */
     int8_t TxMaxPower;
     /*!
+     * Time stamp of the last JoinReq Tx frame.
+     */
+    TimerTime_t LastJoinTxDoneTime;
+    /*!
      * Time stamp of the last Tx frame
      */
     TimerTime_t LastTxDoneTime;
@@ -237,6 +222,10 @@
      */
     uint32_t Frequency;
     /*!
+     * Alternative frequency for RX window 1
+     */
+    uint32_t Rx1Frequency;
+    /*!
      * Data rate definition
      */
     DrRange_t DrRange;
@@ -258,9 +247,9 @@
     /*!
      * Data rate
      *
-     * EU868 - [DR_0, DR_1, DR_2, DR_3, DR_4, DR_5, DR_6, DR_7]
+     * LoRaWAN Regional Parameters V1.0.2rB
      *
-     * US915 - [DR_8, DR_9, DR_10, DR_11, DR_12, DR_13]
+     * The allowed ranges are region specific. Please refer to \ref DR_0 to \ref DR_15 for details.
      */
     uint8_t  Datarate;
 }Rx2ChannelParams_t;
@@ -322,9 +311,25 @@
      */
     Rx2ChannelParams_t Rx2Channel;
     /*!
-     * Mask indicating which channels are enabled
+     * Uplink dwell time configuration. 0: No limit, 1: 400ms
+     */
+    uint8_t UplinkDwellTime;
+    /*!
+     * Downlink dwell time configuration. 0: No limit, 1: 400ms
+     */
+    uint8_t DownlinkDwellTime;
+    /*!
+     * Maximum possible EIRP
      */
-    uint16_t ChannelsMask[6];
+    float MaxEirp;
+    /*!
+     * Antenna gain of the node
+     */
+    float AntennaGain;
+    /*!
+     * Indicates if the node supports repeaters
+     */
+    bool RepeaterSupport;
 }LoRaMacParams_t;
 
 /*!
@@ -349,6 +354,10 @@
      */
     uint32_t DownLinkCounter;
     /*!
+     * Ping offset of the muilticast channel for Class B
+     */
+    uint16_t PingOffset;
+    /*!
      * Reference pointer to the next multicast channel parameters in the list
      */
     struct sMulticastParams *Next;
@@ -357,7 +366,7 @@
 /*!
  * LoRaMAC frame types
  *
- * LoRaWAN Specification V1.0.1, chapter 4.2.1, table 1
+ * LoRaWAN Specification V1.0.2, chapter 4.2.1, table 1
  */
 typedef enum eLoRaMacFrameType
 {
@@ -398,7 +407,7 @@
 /*!
  * LoRaMAC mote MAC commands
  *
- * LoRaWAN Specification V1.0.1, chapter 5, table 4
+ * LoRaWAN Specification V1.0.2, chapter 5, table 4
  */
 typedef enum eLoRaMacMoteCmd
 {
@@ -430,12 +439,36 @@
      * RXTimingSetupAns
      */
     MOTE_MAC_RX_TIMING_SETUP_ANS     = 0x08,
+    /*!
+     * TXParamSetupAns
+     */
+    MOTE_MAC_TX_PARAM_SETUP_ANS      = 0x09,
+    /*!
+     * DlChannelAns
+     */
+    MOTE_MAC_DL_CHANNEL_ANS          = 0x0A,
+    /*!
+     * PingSlotInfoReq
+     */
+    MOTE_MAC_PING_SLOT_INFO_REQ      = 0x10,
+    /*!
+     * PingSlotFreqAns
+     */
+    MOTE_MAC_PING_SLOT_FREQ_ANS      = 0x11,
+    /*!
+     * BeaconTimingReq
+     */
+    MOTE_MAC_BEACON_TIMING_REQ       = 0x12,
+    /*!
+     * BeaconFreqAns
+     */
+    MOTE_MAC_BEACON_FREQ_ANS         = 0x13,
 }LoRaMacMoteCmd_t;
 
 /*!
  * LoRaMAC server MAC commands
  *
- * LoRaWAN Specification V1.0.1 chapter 5, table 4
+ * LoRaWAN Specification V1.0.2 chapter 5, table 4
  */
 typedef enum eLoRaMacSrvCmd
 {
@@ -467,6 +500,30 @@
      * RXTimingSetupReq
      */
     SRV_MAC_RX_TIMING_SETUP_REQ      = 0x08,
+    /*!
+     * NewChannelReq
+     */
+    SRV_MAC_TX_PARAM_SETUP_REQ       = 0x09,
+    /*!
+     * DlChannelReq
+     */
+    SRV_MAC_DL_CHANNEL_REQ           = 0x0A,
+    /*!
+     * PingSlotInfoAns
+     */
+    SRV_MAC_PING_SLOT_INFO_ANS       = 0x10,
+    /*!
+     * PingSlotChannelReq
+     */
+    SRV_MAC_PING_SLOT_CHANNEL_REQ    = 0x11,
+    /*!
+     * BeaconTimingAns
+     */
+    SRV_MAC_BEACON_TIMING_ANS        = 0x12,
+    /*!
+     * BeaconFreqReq
+     */
+    SRV_MAC_BEACON_FREQ_REQ          = 0x13,
 }LoRaMacSrvCmd_t;
 
 /*!
@@ -495,7 +552,7 @@
 /*!
  * LoRaMAC header field definition (MHDR field)
  *
- * LoRaWAN Specification V1.0.1, chapter 4.2
+ * LoRaWAN Specification V1.0.2, chapter 4.2
  */
 typedef union uLoRaMacHeader
 {
@@ -526,7 +583,7 @@
 /*!
  * LoRaMAC frame control field definition (FCtrl)
  *
- * LoRaWAN Specification V1.0.1, chapter 4.3.1
+ * LoRaWAN Specification V1.0.2, chapter 4.3.1
  */
 typedef union uLoRaMacFrameCtrl
 {
@@ -562,6 +619,26 @@
     }Bits;
 }LoRaMacFrameCtrl_t;
 
+typedef union uPingSlotInfo
+{
+    uint8_t Value;
+    struct sInfoFields
+    {
+        uint8_t Periodicity     : 3;
+        uint8_t RFU             : 5;
+    }Fields;
+}PingSlotInfo_t;
+
+typedef struct sBeaconInfo
+{
+    uint32_t Time;
+    struct sGwSpecific
+    {
+        uint8_t InfoDesc;
+        uint8_t Info[6];
+    }GwSpecific;
+}BeaconInfo_t;
+
 /*!
  * Enumeration containing the status of the operation of a MAC service
  */
@@ -580,6 +657,10 @@
      */
     LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT,
     /*!
+     * An Rx timeout occurred on receive window 1
+     */
+    LORAMAC_EVENT_INFO_STATUS_RX1_TIMEOUT,
+    /*!
      * An Rx timeout occurred on receive window 2
      */
     LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT,
@@ -596,9 +677,8 @@
      */
     LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL,
     /*!
-     * A frame with an invalid downlink counter was received. The
-     * downlink counter of the frame was equal to the local copy
-     * of the downlink counter of the node.
+     * The downlink counter of the frame was equal to the local copy of the
+     * downlink counter of the node
      */
     LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED,
     /*!
@@ -615,9 +695,25 @@
      */
     LORAMAC_EVENT_INFO_STATUS_ADDRESS_FAIL,
     /*!
-     * message integrity check failure
+     * Message integrity check failure
      */
     LORAMAC_EVENT_INFO_STATUS_MIC_FAIL,
+    /*!
+     * ToDo
+     */
+    LORAMAC_EVENT_INFO_STATUS_MULTICAST_FAIL,
+    /*!
+     * ToDo
+     */
+    LORAMAC_EVENT_INFO_STATUS_BEACON_LOCKED,
+    /*!
+     * ToDo
+     */
+    LORAMAC_EVENT_INFO_STATUS_BEACON_LOST,
+    /*!
+     * ToDo
+     */
+    LORAMAC_EVENT_INFO_STATUS_BEACON_NOT_FOUND,
 }LoRaMacEventInfoStatus_t;
 
 /*!
@@ -651,6 +747,10 @@
          */
         uint8_t MlmeReq         : 1;
         /*!
+         * MLME-Ind pending
+         */
+        uint8_t MlmeInd         : 1;
+        /*!
          * MAC cycle done
          */
         uint8_t MacDone         : 1;
@@ -709,7 +809,7 @@
      * Frame port field. Must be set if the payload is not empty. Use the
      * application specific frame port values: [1...223]
      *
-     * LoRaWAN Specification V1.0.1, chapter 4.3.2
+     * LoRaWAN Specification V1.0.2, chapter 4.3.2
      */
     uint8_t fPort;
     /*!
@@ -735,7 +835,7 @@
      * Frame port field. Must be set if the payload is not empty. Use the
      * application specific frame port values: [1...223]
      *
-     * LoRaWAN Specification V1.0.1, chapter 4.3.2
+     * LoRaWAN Specification V1.0.2, chapter 4.3.2
      */
     uint8_t fPort;
     /*!
@@ -753,7 +853,7 @@
     /*!
      * Number of trials to transmit the frame, if the LoRaMAC layer did not
      * receive an acknowledgment. The MAC performs a datarate adaptation,
-     * according to the LoRaWAN Specification V1.0.1, chapter 19.4, according
+     * according to the LoRaWAN Specification V1.0.2, chapter 18.4, according
      * to the following table:
      *
      * Transmission nb | Data Rate
@@ -955,15 +1055,20 @@
     /*!
      * Initiates the Over-the-Air activation
      *
-     * LoRaWAN Specification V1.0.1, chapter 6.2
+     * LoRaWAN Specification V1.0.2, chapter 6.2
      */
     MLME_JOIN,
     /*!
      * LinkCheckReq - Connectivity validation
      *
-     * LoRaWAN Specification V1.0.1, chapter 5, table 4
+     * LoRaWAN Specification V1.0.2, chapter 5, table 4
      */
     MLME_LINK_CHECK,
+    MLME_PING_SLOT_INFO,
+    MLME_BEACON_TIMING,
+    MLME_SWITCH_CLASS,
+    MLME_BEACON_ACQUISITION,
+    MLME_BEACON,
     /*!
      * Sets Tx continuous wave mode
      *
@@ -986,19 +1091,19 @@
     /*!
      * Globally unique end-device identifier
      *
-     * LoRaWAN Specification V1.0.1, chapter 6.2.1
+     * LoRaWAN Specification V1.0.2, chapter 6.2.1
      */
     uint8_t *DevEui;
     /*!
      * Application identifier
      *
-     * LoRaWAN Specification V1.0.1, chapter 6.1.2
+     * LoRaWAN Specification V1.0.2, chapter 6.1.2
      */
     uint8_t *AppEui;
     /*!
      * AES-128 application key
      *
-     * LoRaWAN Specification V1.0.1, chapter 6.2.2
+     * LoRaWAN Specification V1.0.2, chapter 6.2.2
      */
     uint8_t *AppKey;
     /*!
@@ -1008,6 +1113,22 @@
 }MlmeReqJoin_t;
 
 /*!
+ * LoRaMAC MLME-Request for the ping slot info service
+ */
+typedef struct sMlmeReqPingSlotInfo
+{
+    PingSlotInfo_t PingSlot;
+}MlmeReqPingSlotInfo_t;
+
+/*!
+ * LoRaMAC MLME-Request for the switch class service
+ */
+typedef struct sMlmeReqSwitchClass
+{
+    DeviceClass_t Class;
+}MlmeReqSwitchClass_t;
+
+/*!
  * LoRaMAC MLME-Request for Tx continuous wave mode
  */
 typedef struct sMlmeReqTxCw
@@ -1046,6 +1167,14 @@
          */
         MlmeReqJoin_t Join;
         /*!
+         * MLME-Request parameters for a ping slot info request
+         */
+        MlmeReqPingSlotInfo_t PingSlotInfo;
+        /*!
+         * MLME-Request parameters for a switch class request
+         */
+        MlmeReqSwitchClass_t SwitchClass;
+        /*!
          * MLME-Request parameters for Tx continuous mode request
          */
         MlmeReqTxCw_t TxCw;
@@ -1078,47 +1207,76 @@
      * Number of gateways which received the last LinkCheckReq
      */
     uint8_t NbGateways;
+    TimerTime_t BeaconTimingDelay;
+    uint8_t BeaconTimingChannel;
     /*!
      * Provides the number of retransmissions
      */
     uint8_t NbRetries;
 }MlmeConfirm_t;
 
+typedef struct sMlmeIndication
+{
+    /*!
+     * Holds the previously performed MLME-Request
+     */
+    Mlme_t MlmeIndication;
+    /*!
+     * Status of the operation
+     */
+    LoRaMacEventInfoStatus_t Status;
+
+    BeaconInfo_t BeaconInfo;
+}MlmeIndication_t;
+
 /*!
  * LoRa Mac Information Base (MIB)
  *
  * The following table lists the MIB parameters and the related attributes:
  *
- * Attribute                         | Get | Set
- * --------------------------------- | :-: | :-:
- * \ref MIB_DEVICE_CLASS             | YES | YES
- * \ref MIB_NETWORK_JOINED           | YES | YES
- * \ref MIB_ADR                      | YES | YES
- * \ref MIB_NET_ID                   | YES | YES
- * \ref MIB_DEV_ADDR                 | YES | YES
- * \ref MIB_NWK_SKEY                 | YES | YES
- * \ref MIB_APP_SKEY                 | YES | YES
- * \ref MIB_PUBLIC_NETWORK           | YES | YES
- * \ref MIB_REPEATER_SUPPORT         | YES | YES
- * \ref MIB_CHANNELS                 | YES | NO
- * \ref MIB_RX2_CHANNEL              | YES | YES
- * \ref MIB_CHANNELS_MASK            | YES | YES
+ * Attribute                                     | Get | Set
+ * ----------------------------------------------| :-: | :-:
+ * \ref MIB_DEVICE_CLASS                         | YES | YES
+ * \ref MIB_NETWORK_JOINED                       | YES | YES
+ * \ref MIB_ADR                                  | YES | YES
+ * \ref MIB_NET_ID                               | YES | YES
+ * \ref MIB_DEV_ADDR                             | YES | YES
+ * \ref MIB_NWK_SKEY                             | YES | YES
+ * \ref MIB_APP_SKEY                             | YES | YES
+ * \ref MIB_PUBLIC_NETWORK                       | YES | YES
+ * \ref MIB_REPEATER_SUPPORT                     | YES | YES
+ * \ref MIB_CHANNELS                             | YES | NO
+ * \ref MIB_RX2_CHANNEL                          | YES | YES
+ * \ref MIB_CHANNELS_MASK                        | YES | YES
  * \ref MIB_CHANNELS_DEFAULT_MASK    | YES | YES
- * \ref MIB_CHANNELS_NB_REP          | YES | YES
- * \ref MIB_MAX_RX_WINDOW_DURATION   | YES | YES
- * \ref MIB_RECEIVE_DELAY_1          | YES | YES
- * \ref MIB_RECEIVE_DELAY_2          | YES | YES
- * \ref MIB_JOIN_ACCEPT_DELAY_1      | YES | YES
- * \ref MIB_JOIN_ACCEPT_DELAY_2      | YES | YES
- * \ref MIB_CHANNELS_DATARATE        | YES | YES
- * \ref MIB_CHANNELS_DEFAULT_DATARATE| YES | YES
- * \ref MIB_CHANNELS_TX_POWER        | YES | YES
- * \ref MIB_CHANNELS_DEFAULT_TX_POWER| YES | YES
- * \ref MIB_UPLINK_COUNTER           | YES | YES
- * \ref MIB_DOWNLINK_COUNTER         | YES | YES
- * \ref MIB_MULTICAST_CHANNEL        | YES | NO
- * \ref MIB_SYSTEM_MAX_RX_ERROR      | YES | YES
- * \ref MIB_MIN_RX_SYMBOLS           | YES | YES
+ * \ref MIB_CHANNELS_NB_REP                      | YES | YES
+ * \ref MIB_MAX_RX_WINDOW_DURATION               | YES | YES
+ * \ref MIB_RECEIVE_DELAY_1                      | YES | YES
+ * \ref MIB_RECEIVE_DELAY_2                      | YES | YES
+ * \ref MIB_JOIN_ACCEPT_DELAY_1                  | YES | YES
+ * \ref MIB_JOIN_ACCEPT_DELAY_2                  | YES | YES
+ * \ref MIB_CHANNELS_DATARATE                    | YES | YES
+ * \ref MIB_CHANNELS_DEFAULT_DATARATE            | YES | YES
+ * \ref MIB_CHANNELS_TX_POWER                    | YES | YES
+ * \ref MIB_CHANNELS_DEFAULT_TX_POWER            | YES | YES
+ * \ref MIB_UPLINK_COUNTER                       | YES | YES
+ * \ref MIB_DOWNLINK_COUNTER                     | YES | YES
+ * \ref MIB_MULTICAST_CHANNEL                    | YES | NO
+ * \ref MIB_SYSTEM_MAX_RX_ERROR                  | YES | YES
+ * \ref MIB_MIN_RX_SYMBOLS                       | YES | YES
+ * \ref MIB_BEACON_INTERVAL                      | YES | YES
+ * \ref MIB_BEACON_RESERVED                      | YES | YES
+ * \ref MIB_BEACON_GUARD                         | YES | YES
+ * \ref MIB_BEACON_WINDOW                        | YES | YES
+ * \ref MIB_BEACON_WINDOW_SLOTS                  | YES | YES
+ * \ref MIB_PING_SLOT_WINDOW                     | YES | YES
+ * \ref MIB_BEACON_SYMBOL_TO_DEFAULT             | YES | YES
+ * \ref MIB_BEACON_SYMBOL_TO_EXPANSION_MAX       | YES | YES
+ * \ref MIB_PING_SLOT_SYMBOL_TO_EXPANSION_MAX    | YES | YES
+ * \ref MIB_BEACON_SYMBOL_TO_EXPANSION_FACTOR    | YES | YES
+ * \ref MIB_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR | YES | YES
+ * \ref MIB_MAX_BEACON_LESS_PERIOD               | YES | YES
+ * \ref MIB_ANTENNA_GAIN             | YES | YES
  *
  * The following table provides links to the function implementations of the
  * related MIB primitives:
@@ -1133,19 +1291,19 @@
     /*!
      * LoRaWAN device class
      *
-     * LoRaWAN Specification V1.0.1
+     * LoRaWAN Specification V1.0.2
      */
     MIB_DEVICE_CLASS,
     /*!
      * LoRaWAN Network joined attribute
      *
-     * LoRaWAN Specification V1.0.1
+     * LoRaWAN Specification V1.0.2
      */
     MIB_NETWORK_JOINED,
     /*!
      * Adaptive data rate
      *
-     * LoRaWAN Specification V1.0.1, chapter 4.3.1.1
+     * LoRaWAN Specification V1.0.2, chapter 4.3.1.1
      *
      * [true: ADR enabled, false: ADR disabled]
      */
@@ -1153,31 +1311,31 @@
     /*!
      * Network identifier
      *
-     * LoRaWAN Specification V1.0.1, chapter 6.1.1
+     * LoRaWAN Specification V1.0.2, chapter 6.1.1
      */
     MIB_NET_ID,
     /*!
      * End-device address
      *
-     * LoRaWAN Specification V1.0.1, chapter 6.1.1
+     * LoRaWAN Specification V1.0.2, chapter 6.1.1
      */
     MIB_DEV_ADDR,
     /*!
      * Network session key
      *
-     * LoRaWAN Specification V1.0.1, chapter 6.1.3
+     * LoRaWAN Specification V1.0.2, chapter 6.1.3
      */
     MIB_NWK_SKEY,
     /*!
      * Application session key
      *
-     * LoRaWAN Specification V1.0.1, chapter 6.1.4
+     * LoRaWAN Specification V1.0.2, chapter 6.1.4
      */
     MIB_APP_SKEY,
     /*!
      * Set the network type to public or private
      *
-     * LoRaWAN Specification V1.0.1, chapter 7
+     * LoRaWAN Regional Parameters V1.0.2rB
      *
      * [true: public network, false: private network]
      */
@@ -1185,7 +1343,7 @@
     /*!
      * Support the operation with repeaters
      *
-     * LoRaWAN Specification V1.0.1, chapter 7
+     * LoRaWAN Regional Parameters V1.0.2rB
      *
      * [true: repeater support enabled, false: repeater support disabled]
      */
@@ -1195,127 +1353,111 @@
      * pointer which references the first entry of the channel list. The
      * list is of size LORA_MAX_NB_CHANNELS
      *
-     * LoRaWAN Specification V1.0.1, chapter 7
+     * LoRaWAN Regional Parameters V1.0.2rB
      */
     MIB_CHANNELS,
     /*!
      * Set receive window 2 channel
      *
-     * LoRaWAN Specification V1.0.1, chapter 3.3.2
+     * LoRaWAN Specification V1.0.2, chapter 3.3.1
      */
     MIB_RX2_CHANNEL,
     /*!
      * Set receive window 2 channel
      *
-     * LoRaWAN Specification V1.0.1, chapter 3.3.2
+     * LoRaWAN Specification V1.0.2, chapter 3.3.2
      */
     MIB_RX2_DEFAULT_CHANNEL,
     /*!
      * LoRaWAN channels mask
      *
-     * LoRaWAN Specification V1.0.1, chapter 7
+     * LoRaWAN Regional Parameters V1.0.2rB
      */
     MIB_CHANNELS_MASK,
     /*!
      * LoRaWAN default channels mask
      *
-     * LoRaWAN Specification V1.0.1, chapter 7
+     * LoRaWAN Regional Parameters V1.0.2rB
      */
     MIB_CHANNELS_DEFAULT_MASK,
     /*!
      * Set the number of repetitions on a channel
      *
-     * LoRaWAN Specification V1.0.1, chapter 5.2
+     * LoRaWAN Specification V1.0.2, chapter 5.2
      */
     MIB_CHANNELS_NB_REP,
     /*!
      * Maximum receive window duration in [ms]
      *
-     * LoRaWAN Specification V1.0.1, chapter 3.3.3
+     * LoRaWAN Specification V1.0.2, chapter 3.3.3
      */
     MIB_MAX_RX_WINDOW_DURATION,
     /*!
      * Receive delay 1 in [ms]
      *
-     * LoRaWAN Specification V1.0.1, chapter 7
+     * LoRaWAN Regional Parameters V1.0.2rB
      */
     MIB_RECEIVE_DELAY_1,
     /*!
      * Receive delay 2 in [ms]
      *
-     * LoRaWAN Specification V1.0.1, chapter 7
+     * LoRaWAN Regional Parameters V1.0.2rB
      */
     MIB_RECEIVE_DELAY_2,
     /*!
      * Join accept delay 1 in [ms]
      *
-     * LoRaWAN Specification V1.0.1, chapter 7
+     * LoRaWAN Regional Parameters V1.0.2rB
      */
     MIB_JOIN_ACCEPT_DELAY_1,
     /*!
      * Join accept delay 2 in [ms]
      *
-     * LoRaWAN Specification V1.0.1, chapter 7
+     * LoRaWAN Regional Parameters V1.0.2rB
      */
     MIB_JOIN_ACCEPT_DELAY_2,
     /*!
      * Default Data rate of a channel
      *
-     * LoRaWAN Specification V1.0.1, chapter 7
+     * LoRaWAN Regional Parameters V1.0.2rB
      *
-     * EU868 - [DR_0, DR_1, DR_2, DR_3, DR_4, DR_5, DR_6, DR_7]
-     *
-     * US915 - [DR_0, DR_1, DR_2, DR_3, DR_4, DR_8, DR_9, DR_10, DR_11, DR_12, DR_13]
+     * The allowed ranges are region specific. Please refer to \ref DR_0 to \ref DR_15 for details.
      */
     MIB_CHANNELS_DEFAULT_DATARATE,
     /*!
      * Data rate of a channel
      *
-     * LoRaWAN Specification V1.0.1, chapter 7
+     * LoRaWAN Regional Parameters V1.0.2rB
      *
-     * EU868 - [DR_0, DR_1, DR_2, DR_3, DR_4, DR_5, DR_6, DR_7]
-     *
-     * US915 - [DR_0, DR_1, DR_2, DR_3, DR_4, DR_8, DR_9, DR_10, DR_11, DR_12, DR_13]
+     * The allowed ranges are region specific. Please refer to \ref DR_0 to \ref DR_15 for details.
      */
     MIB_CHANNELS_DATARATE,
     /*!
      * Transmission power of a channel
      *
-     * LoRaWAN Specification V1.0.1, chapter 7
-     *
-     * EU868 - [TX_POWER_20_DBM, TX_POWER_14_DBM, TX_POWER_11_DBM,
-     *          TX_POWER_08_DBM, TX_POWER_05_DBM, TX_POWER_02_DBM]
+     * LoRaWAN Regional Parameters V1.0.2rB
      *
-     * US915 - [TX_POWER_30_DBM, TX_POWER_28_DBM, TX_POWER_26_DBM,
-     *          TX_POWER_24_DBM, TX_POWER_22_DBM, TX_POWER_20_DBM,
-     *          TX_POWER_18_DBM, TX_POWER_14_DBM, TX_POWER_12_DBM,
-     *          TX_POWER_10_DBM]
+     * The allowed ranges are region specific. Please refer to \ref TX_POWER_0 to \ref TX_POWER_15 for details.
      */
     MIB_CHANNELS_TX_POWER,
     /*!
      * Transmission power of a channel
      *
-     * LoRaWAN Specification V1.0.1, chapter 7
-     *
-     * EU868 - [TX_POWER_20_DBM, TX_POWER_14_DBM, TX_POWER_11_DBM,
-     *          TX_POWER_08_DBM, TX_POWER_05_DBM, TX_POWER_02_DBM]
+     * LoRaWAN Regional Parameters V1.0.2rB
      *
-     * US915 - [TX_POWER_30_DBM, TX_POWER_28_DBM, TX_POWER_26_DBM,
-     *          TX_POWER_24_DBM, TX_POWER_22_DBM, TX_POWER_20_DBM,
-     *          TX_POWER_18_DBM, TX_POWER_14_DBM, TX_POWER_12_DBM,
-     *          TX_POWER_10_DBM]
+     * The allowed ranges are region specific. Please refer to \ref TX_POWER_0 to \ref TX_POWER_15 for details.
      */
     MIB_CHANNELS_DEFAULT_TX_POWER,
     /*!
      * LoRaWAN Up-link counter
      *
-     * LoRaWAN Specification V1.0.1, chapter 4.3.1.5
+     * LoRaWAN Specification V1.0.2, chapter 4.3.1.5
      */
     MIB_UPLINK_COUNTER,
     /*!
      * LoRaWAN Down-link counter
      *
-     * LoRaWAN Specification V1.0.1, chapter 4.3.1.5
+     * LoRaWAN Specification V1.0.2, chapter 4.3.1.5
      */
     MIB_DOWNLINK_COUNTER,
     /*!
@@ -1335,6 +1477,63 @@
      * Default: 6 symbols
      */
     MIB_MIN_RX_SYMBOLS,
+    /*!
+     * Antenna gain of the node. Default value is region specific.
+     * The antenna gain is used to calculate the TX power of the node.
+     * The formula is:
+     * radioTxPower = ( int8_t )floor( maxEirp - antennaGain )
+     */
+    MIB_ANTENNA_GAIN,
+    /*!
+     * Beacon interval in ms
+     */
+    MIB_BEACON_INTERVAL,
+    /*!
+     * Beacon reserved time in ms
+     */
+    MIB_BEACON_RESERVED,
+    /*!
+     * Beacon guard time in ms
+     */
+    MIB_BEACON_GUARD,
+    /*!
+     * Beacon window time in ms
+     */
+    MIB_BEACON_WINDOW,
+    /*!
+     * Beacon window time in number of slots
+     */
+    MIB_BEACON_WINDOW_SLOTS,
+    /*!
+     * Ping slot length time in ms
+     */
+    MIB_PING_SLOT_WINDOW,
+    /*!
+     * Default symbol timeout for beacons and ping slot windows
+     */
+    MIB_BEACON_SYMBOL_TO_DEFAULT,
+    /*!
+     * Maximum symbol timeout for beacons
+     */
+    MIB_BEACON_SYMBOL_TO_EXPANSION_MAX,
+    /*!
+     * Maximum symbol timeout for ping slots
+     */
+    MIB_PING_SLOT_SYMBOL_TO_EXPANSION_MAX,
+    /*!
+     * Symbol expansion value for beacon windows in case of beacon
+     * loss in symbols
+     */
+    MIB_BEACON_SYMBOL_TO_EXPANSION_FACTOR,
+    /*!
+     * Symbol expansion value for ping slot windows in case of beacon
+     * loss in symbols
+     */
+    MIB_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR,
+    /*!
+     * Maximum allowed beacon less time in ms
+     */
+    MIB_MAX_BEACON_LESS_PERIOD
 }Mib_t;
 
 /*!
@@ -1516,6 +1715,86 @@
      * Related MIB type: \ref MIB_MIN_RX_SYMBOLS
      */
     uint8_t MinRxSymbols;
+    /*!
+     * Antenna gain
+     *
+     * Related MIB type: \ref MIB_ANTENNA_GAIN
+     */
+    float AntennaGain;
+    /*!
+     * Beacon interval in ms
+     *
+     * Related MIB type: \ref MIB_BEACON_INTERVAL
+     */
+    uint32_t BeaconInterval;
+    /*!
+     * Beacon reserved time in ms
+     *
+     * Related MIB type: \ref MIB_BEACON_RESERVED
+     */
+    uint32_t BeaconReserved;
+    /*!
+     * Beacon guard time in ms
+     *
+     * Related MIB type: \ref MIB_BEACON_GUARD
+     */
+    uint32_t BeaconGuard;
+    /*!
+     * Beacon window time in ms
+     *
+     * Related MIB type: \ref MIB_BEACON_WINDOW
+     */
+    uint32_t BeaconWindow;
+    /*!
+     * Beacon window time in number of slots
+     *
+     * Related MIB type: \ref MIB_BEACON_WINDOW_SLOTS
+     */
+    uint32_t BeaconWindowSlots;
+    /*!
+     * Ping slot length time in ms
+     *
+     * Related MIB type: \ref MIB_PING_SLOT_WINDOW
+     */
+    uint32_t PingSlotWindow;
+    /*!
+     * Default symbol timeout for beacons and ping slot windows
+     *
+     * Related MIB type: \ref MIB_BEACON_SYMBOL_TO_DEFAULT
+     */
+    uint32_t BeaconSymbolToDefault;
+    /*!
+     * Maximum symbol timeout for beacons
+     *
+     * Related MIB type: \ref MIB_BEACON_SYMBOL_TO_EXPANSION_MAX
+     */
+    uint32_t BeaconSymbolToExpansionMax;
+    /*!
+     * Maximum symbol timeout for ping slots
+     *
+     * Related MIB type: \ref MIB_PING_SLOT_SYMBOL_TO_EXPANSION_MAX
+     */
+    uint32_t PingSlotSymbolToExpansionMax;
+    /*!
+     * Symbol expansion value for beacon windows in case of beacon
+     * loss in symbols
+     *
+     * Related MIB type: \ref MIB_BEACON_SYMBOL_TO_EXPANSION_FACTOR
+     */
+    uint32_t BeaconSymbolToExpansionFactor;
+    /*!
+     * Symbol expansion value for ping slot windows in case of beacon
+     * loss in symbols
+     *
+     * Related MIB type: \ref MIB_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR
+     */
+    uint32_t PingSlotSymbolToExpansionFactor;
+    /*!
+     * Maximum allowed beacon less time in ms
+     *
+     * Related MIB type: \ref MIB_MAX_BEACON_LESS_PERIOD
+     */
+    uint32_t MaxBeaconLessPeriod;
 }MibParam_t;
 
 /*!
@@ -1550,6 +1829,21 @@
 }LoRaMacTxInfo_t;
 
 /*!
+ * Structure to hold multiple MLME request confirm data
+ */
+typedef struct sMlmeConfirmQueue
+{
+    /*!
+     * Holds the previously performed MLME-Request
+     */
+    Mlme_t MlmeRequest;
+    /*!
+     * Status of the operation
+     */
+    LoRaMacEventInfoStatus_t Status;
+}MlmeConfirmQueue_t;
+
+/*!
  * LoRaMAC Status
  */
 typedef enum eLoRaMacStatus
@@ -1587,20 +1881,68 @@
      */
     LORAMAC_STATUS_NO_NETWORK_JOINED,
     /*!
-     * Service not started - payload lenght error
+     * Service not started - payload length error
      */
     LORAMAC_STATUS_LENGTH_ERROR,
     /*!
-     * Service not started - payload lenght error
-     */
-    LORAMAC_STATUS_MAC_CMD_LENGTH_ERROR,
-    /*!
      * Service not started - the device is switched off
      */
     LORAMAC_STATUS_DEVICE_OFF,
+    /*!
+     * Service not started - the specified region is not supported
+     * or not activated with preprocessor definitions.
+     */
+    LORAMAC_STATUS_REGION_NOT_SUPPORTED
 }LoRaMacStatus_t;
 
 /*!
+ * LoRaMAC region enumeration
+ */
+typedef enum eLoRaMacRegion_t
+{
+    /*!
+     * AS band on 923MHz
+     */
+    LORAMAC_REGION_AS923,
+    /*!
+     * Australian band on 915MHz
+     */
+    LORAMAC_REGION_AU915,
+    /*!
+     * Chinese band on 470MHz
+     */
+    LORAMAC_REGION_CN470,
+    /*!
+     * Chinese band on 779MHz
+     */
+    LORAMAC_REGION_CN779,
+    /*!
+     * European band on 433MHz
+     */
+    LORAMAC_REGION_EU433,
+    /*!
+     * European band on 868MHz
+     */
+    LORAMAC_REGION_EU868,
+    /*!
+     * South korean band on 920MHz
+     */
+    LORAMAC_REGION_KR920,
+    /*!
+     * India band on 865MHz
+     */
+    LORAMAC_REGION_IN865,
+    /*!
+     * North american band on 915MHz
+     */
+    LORAMAC_REGION_US915,
+    /*!
+     * North american band on 915MHz with a maximum of 16 channels
+     */
+    LORAMAC_REGION_US915_HYBRID,
+}LoRaMacRegion_t;
+
+/*!
  * LoRaMAC events structure
  * Used to notify upper layers of MAC events
  */
@@ -1624,8 +1966,17 @@
      * \param   [OUT] MLME-Confirm parameters
      */
     void ( *MacMlmeConfirm )( MlmeConfirm_t *MlmeConfirm );
+    /*!
+     * \brief   MLME-Indication primitive
+     *
+     * \param   [OUT] MLME-Indication parameters
+     */
+    void ( *MacMlmeIndication )( MlmeIndication_t *MlmeIndication );
 }LoRaMacPrimitives_t;
 
+/*!
+ * LoRaMAC callback structure
+ */
 typedef struct sLoRaMacCallback
 {
     /*!
@@ -1637,9 +1988,22 @@
      *          to measure the battery level]
      */
     uint8_t ( *GetBatteryLevel )( void );
+    /*!
+     * \brief   Measures the temperature level
+     *
+     * \retval  Temperature level
+     */
+    float ( *GetTemperatureLevel )( void );
 }LoRaMacCallback_t;
 
 /*!
+ * LoRaMAC Max EIRP (dBm) table
+ */
+static const uint8_t LoRaMacMaxEirpTable[] = { 8, 10, 12, 13, 14, 16, 18, 20, 21, 24, 26, 27, 29, 30, 33, 36 };
+
+
+
+/*!
  * \brief   LoRaMAC layer initialization
  *
  * \details In addition to the initialization of the LoRaMAC layer, this
@@ -1647,18 +2011,21 @@
  *          MLME services. Every data field of \ref LoRaMacPrimitives_t must be
  *          set to a valid callback function.
  *
- * \param   [IN] events - Pointer to a structure defining the LoRaMAC
- *                        event functions. Refer to \ref LoRaMacPrimitives_t.
+ * \param   [IN] primitives - Pointer to a structure defining the LoRaMAC
+ *                            event functions. Refer to \ref LoRaMacPrimitives_t.
  *
  * \param   [IN] events - Pointer to a structure defining the LoRaMAC
  *                        callback functions. Refer to \ref LoRaMacCallback_t.
  *
+ * \param   [IN] region - The region to start.
+ *
  * \retval  LoRaMacStatus_t Status of the operation. Possible returns are:
  *          returns are:
  *          \ref LORAMAC_STATUS_OK,
- *          \ref LORAMAC_STATUS_PARAMETER_INVALID.
+ *          \ref LORAMAC_STATUS_PARAMETER_INVALID,
+ *          \ref LORAMAC_STATUS_REGION_NOT_SUPPORTED.
  */
-LoRaMacStatus_t LoRaMacInitialization( LoRaMacPrimitives_t *primitives, LoRaMacCallback_t *callbacks );
+LoRaMacStatus_t LoRaMacInitialization( LoRaMacPrimitives_t *primitives, LoRaMacCallback_t *callbacks, LoRaMacRegion_t region );
 
 /*!
  * \brief   Queries the LoRaMAC if it is possible to send the next frame with
@@ -1675,11 +2042,13 @@
  *
  * \retval  LoRaMacStatus_t Status of the operation. When the parameters are
  *          not valid, the function returns \ref LORAMAC_STATUS_PARAMETER_INVALID.
- *          In case of a length error caused by the applicative payload size, the
- *          function returns LORAMAC_STATUS_LENGTH_ERROR. In case of a length error
- *          due to additional MAC commands in the queue, the function returns
- *          LORAMAC_STATUS_MAC_CMD_LENGTH_ERROR. In case the query is valid, and
- *          the LoRaMAC is able to send the frame, the function returns LORAMAC_STATUS_OK. *
+ *          In case of a length error caused by the applicative payload in combination
+ *          with the MAC commands, the function returns \ref LORAMAC_STATUS_LENGTH_ERROR.
+ *          Please note that if the size of the MAC commands which are in the queue do
+ *          not fit into the payload size on the related datarate, the LoRaMAC will
+ *          omit the MAC commands.
+ *          In case the query is valid, and the LoRaMAC is able to send the frame,
+ *          the function returns \ref LORAMAC_STATUS_OK.
  */
 LoRaMacStatus_t LoRaMacQueryTxPossible( uint8_t size, LoRaMacTxInfo_t* txInfo );
 
@@ -1687,14 +2056,10 @@
  * \brief   LoRaMAC channel add service
  *
  * \details Adds a new channel to the channel list and activates the id in
- *          the channel mask. For the US915 band, all channels are enabled
- *          by default. It is not possible to activate less than 6 125 kHz
- *          channels.
+ *          the channel mask. Please note that this functionality is not available
+ *          on all regions. Information about allowed ranges are available at the LoRaWAN Regional Parameters V1.0.2rB
  *
- * \param   [IN] id - Id of the channel. Possible values are:
- *
- *          0-15 for EU868
- *          0-72 for US915
+ * \param   [IN] id - Id of the channel.
  *
  * \param   [IN] params - Channel parameters to set.
  *
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/LoRaMacClassB.cpp	Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,1345 @@
+/*
+ / _____)             _              | |
+( (____  _____ ____ _| |_ _____  ____| |__
+ \____ \| ___ |    (_   _) ___ |/ ___)  _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+    (C)2013 Semtech
+ ___ _____ _   ___ _  _____ ___  ___  ___ ___
+/ __|_   _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+\__ \ | |/ _ \ (__| ' <| _| (_) |   / (__| _|
+|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+embedded.connectivity.solutions===============
+
+Description: LoRa MAC Class B layer implementation
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+
+Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE )
+*/
+#include "board.h"
+#include "LoRaMac.h"
+#include "region/Region.h"
+#include "LoRaMacClassB.h"
+#include "LoRaMacCrypto.h"
+
+#ifdef LORAMAC_CLASSB_ENABLED
+/*!
+ * State of the beaconing mechanism
+ */
+static BeaconState_t BeaconState;
+
+/*!
+ * State of the ping slot mechanism
+ */
+static PingSlotState_t PingSlotState;
+
+/*!
+ * State of the multicast slot mechanism
+ */
+static PingSlotState_t MulticastSlotState;
+
+/*!
+ * Class B ping slot context
+ */
+static PingSlotContext_t PingSlotCtx;
+
+/*!
+ * Class B beacon context
+ */
+static BeaconContext_t BeaconCtx;
+
+/*!
+ * Timer for CLASS B beacon acquisition and tracking.
+ */
+static TimerEvent_t BeaconTimer;
+
+/*!
+ * Timer for CLASS B ping slot timer.
+ */
+static TimerEvent_t PingSlotTimer;
+
+/*!
+ * Timer for CLASS B multicast ping slot timer.
+ */
+static TimerEvent_t MulticastSlotTimer;
+
+/*!
+ * Container for the callbacks related to class b.
+ */
+static LoRaMacClassBCallback_t LoRaMacClassBCallbacks;
+
+/*!
+ * Data structure which holds the parameters which needs to be set
+ * in class b operation.
+ */
+static LoRaMacClassBParams_t LoRaMacClassBParams;
+
+
+/*!
+ * \brief Calculates the next ping slot time.
+ *
+ * \param [IN] slotOffset The ping slot offset
+ * \param [IN] pingPeriod The ping period
+ * \param [OUT] timeOffset Time offset of the next slot, based on current time
+ *
+ * \retval [true: ping slot found, false: no ping slot found]
+ */
+static bool CalcNextSlotTime( uint16_t slotOffset, uint16_t pingPeriod, TimerTime_t* timeOffset )
+{
+    uint8_t currentPingSlot = 0;
+    TimerTime_t slotTime = 0;
+    TimerTime_t currentTime = TimerGetCurrentTime( );
+
+    // Calculate the point in time of the last beacon even if we missed it
+    slotTime = ( ( currentTime - BeaconCtx.LastBeaconRx ) % BeaconCtx.Cfg.Interval );
+    slotTime = currentTime - slotTime;
+
+    // Add the reserved time and the ping offset
+    slotTime += BeaconCtx.Cfg.Reserved;
+    slotTime += slotOffset * PingSlotCtx.Cfg.PingSlotWindow;
+
+    if( slotTime < currentTime )
+    {
+        currentPingSlot = ( ( currentTime - slotTime ) /
+                          ( pingPeriod * PingSlotCtx.Cfg.PingSlotWindow ) ) + 1;
+        slotTime += ( ( TimerTime_t )( currentPingSlot * pingPeriod ) *
+                    PingSlotCtx.Cfg.PingSlotWindow );
+    }
+
+    if( currentPingSlot < PingSlotCtx.PingNb )
+    {
+        if( slotTime <= ( BeaconCtx.NextBeaconRx - BeaconCtx.Cfg.Guard - PingSlotCtx.Cfg.PingSlotWindow ) )
+        {
+            // Calculate the relative ping slot time
+            slotTime -= currentTime;
+            slotTime -= RADIO_WAKEUP_TIME;
+            slotTime = TimerTempCompensation( slotTime, BeaconCtx.Temperature );
+            *timeOffset = slotTime;
+            return true;
+        }
+    }
+    return false;
+}
+
+/*!
+ * \brief Calculates CRC's of the beacon frame
+ *
+ * \param [IN] buffer Pointer to the data
+ * \param [IN] length Length of the data
+ *
+ * \retval CRC
+ */
+static uint16_t BeaconCrc( uint8_t *buffer, uint16_t length )
+{
+    // The CRC calculation follows CCITT
+    const uint16_t polynom = 0x1021;
+    // CRC initial value
+    uint16_t crc = 0x0000;
+
+    if( buffer == NULL )
+    {
+        return 0;
+    }
+
+    for( uint16_t i = 0; i < length; ++i )
+    {
+        crc ^= ( uint16_t ) buffer[i] << 8;
+        for( uint16_t j = 0; j < 8; ++j )
+        {
+            crc = ( crc & 0x8000 ) ? ( crc << 1 ) ^ polynom : ( crc << 1 );
+        }
+    }
+
+    return crc;
+}
+
+static void GetTemperatureLevel( LoRaMacClassBCallback_t *callbacks, BeaconContext_t *beaconCtx )
+{
+    // Measure temperature, if available
+    if( ( callbacks != NULL ) && ( callbacks->GetTemperatureLevel != NULL ) )
+    {
+        beaconCtx->Temperature = callbacks->GetTemperatureLevel( );
+    }
+}
+
+#endif // LORAMAC_CLASSB_ENABLED
+
+void LoRaMacClassBInit( LoRaMacClassBParams_t *classBParams, LoRaMacClassBCallback_t *callbacks )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+    GetPhyParams_t getPhy;
+    PhyParam_t phyParam;
+
+    // Init variables to default
+    memset1( ( uint8_t* ) &BeaconCtx, 0, sizeof( BeaconContext_t ) );
+    memset1( ( uint8_t* ) &PingSlotCtx, 0, sizeof( PingSlotCtx ) );
+
+    // Store callbacks
+    LoRaMacClassBCallbacks = *callbacks;
+
+    // Store parameter pointers
+    LoRaMacClassBParams = *classBParams;
+
+    // Setup default temperature
+    BeaconCtx.Temperature = 25.0;
+    GetTemperatureLevel( &LoRaMacClassBCallbacks, &BeaconCtx );
+
+    // Initialize timers
+    TimerInit( &BeaconTimer, LoRaMacClassBBeaconTimerEvent );
+    TimerInit( &PingSlotTimer, LoRaMacClassBPingSlotTimerEvent );
+    TimerInit( &MulticastSlotTimer, LoRaMacClassBMulticastSlotTimerEvent );
+
+    // Setup default states
+    BeaconState = BEACON_STATE_ACQUISITION;
+    PingSlotState = PINGSLOT_STATE_SET_TIMER;
+    MulticastSlotState = PINGSLOT_STATE_SET_TIMER;
+
+    // Get phy parameters
+    getPhy.Attribute = PHY_BEACON_INTERVAL;
+    phyParam = RegionGetPhyParam( *LoRaMacClassBParams.LoRaMacRegion, &getPhy );
+    BeaconCtx.Cfg.Interval = phyParam.Value;
+
+    getPhy.Attribute = PHY_BEACON_RESERVED;
+    phyParam = RegionGetPhyParam( *LoRaMacClassBParams.LoRaMacRegion, &getPhy );
+    BeaconCtx.Cfg.Reserved = phyParam.Value;
+
+    getPhy.Attribute = PHY_BEACON_GUARD;
+    phyParam = RegionGetPhyParam( *LoRaMacClassBParams.LoRaMacRegion, &getPhy );
+    BeaconCtx.Cfg.Guard = phyParam.Value;
+
+    getPhy.Attribute = PHY_BEACON_WINDOW;
+    phyParam = RegionGetPhyParam( *LoRaMacClassBParams.LoRaMacRegion, &getPhy );
+    BeaconCtx.Cfg.Window = phyParam.Value;
+
+    getPhy.Attribute = PHY_BEACON_WINDOW_SLOTS;
+    phyParam = RegionGetPhyParam( *LoRaMacClassBParams.LoRaMacRegion, &getPhy );
+    BeaconCtx.Cfg.WindowSlots = phyParam.Value;
+
+    getPhy.Attribute = PHY_BEACON_SYMBOL_TO_DEFAULT;
+    phyParam = RegionGetPhyParam( *LoRaMacClassBParams.LoRaMacRegion, &getPhy );
+    BeaconCtx.Cfg.SymbolToDefault = phyParam.Value;
+
+    getPhy.Attribute = PHY_BEACON_SYMBOL_TO_EXPANSION_MAX;
+    phyParam = RegionGetPhyParam( *LoRaMacClassBParams.LoRaMacRegion, &getPhy );
+    BeaconCtx.Cfg.SymbolToExpansionMax = phyParam.Value;
+
+    getPhy.Attribute = PHY_BEACON_SYMBOL_TO_EXPANSION_FACTOR;
+    phyParam = RegionGetPhyParam( *LoRaMacClassBParams.LoRaMacRegion, &getPhy );
+    BeaconCtx.Cfg.SymbolToExpansionFactor = phyParam.Value;
+
+    getPhy.Attribute = PHY_MAX_BEACON_LESS_PERIOD;
+    phyParam = RegionGetPhyParam( *LoRaMacClassBParams.LoRaMacRegion, &getPhy );
+    BeaconCtx.Cfg.MaxBeaconLessPeriod = phyParam.Value;
+
+    getPhy.Attribute = PHY_BEACON_DELAY_BEACON_TIMING_ANS;
+    phyParam = RegionGetPhyParam( *LoRaMacClassBParams.LoRaMacRegion, &getPhy );
+    BeaconCtx.Cfg.DelayBeaconTimingAns = phyParam.Value;
+
+    getPhy.Attribute = PHY_PING_SLOT_WINDOW;
+    phyParam = RegionGetPhyParam( *LoRaMacClassBParams.LoRaMacRegion, &getPhy );
+    PingSlotCtx.Cfg.PingSlotWindow = phyParam.Value;
+
+    getPhy.Attribute = PHY_PING_SLOT_SYMBOL_TO_EXPANSION_MAX;
+    phyParam = RegionGetPhyParam( *LoRaMacClassBParams.LoRaMacRegion, &getPhy );
+    PingSlotCtx.Cfg.SymbolToExpansionMax = phyParam.Value;
+
+    getPhy.Attribute = PHY_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR;
+    phyParam = RegionGetPhyParam( *LoRaMacClassBParams.LoRaMacRegion, &getPhy );
+    PingSlotCtx.Cfg.SymbolToExpansionFactor = phyParam.Value;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+void LoRaMacClassBSetBeaconState( BeaconState_t beaconState )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+    BeaconState = beaconState;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+void LoRaMacClassBSetPingSlotState( PingSlotState_t pingSlotState )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+    PingSlotState = pingSlotState;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+void LoRaMacClassBBeaconTimerEvent( void )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+    RxBeaconSetup_t rxBeaconSetup;
+    bool beaconChannelSet = false;
+    uint8_t index = 0;
+    bool activateTimer = false;
+    TimerTime_t beaconEventTime = 1;
+    TimerTime_t currentTime = TimerGetCurrentTime( );
+
+    TimerStop( &BeaconTimer );
+
+    // Beacon state machine
+    switch( BeaconState )
+    {
+        case BEACON_STATE_ACQUISITION:
+        {
+            activateTimer = true;
+
+            if( BeaconCtx.Ctrl.AcquisitionPending == 1 )
+            {
+                Radio.Sleep();
+                BeaconState = BEACON_STATE_SWITCH_CLASS;
+            }
+            else
+            {
+                // Default symbol timeouts
+                BeaconCtx.SymbolTimeout = BeaconCtx.Cfg.SymbolToDefault;
+                PingSlotCtx.SymbolTimeout = BeaconCtx.Cfg.SymbolToDefault;
+
+                if( BeaconCtx.Ctrl.BeaconDelaySet == 1 )
+                {
+                    if( BeaconCtx.BeaconTimingDelay > 0 )
+                    {
+                        if( BeaconCtx.NextBeaconRx > currentTime )
+                        {
+                            BeaconCtx.Ctrl.AcquisitionTimerSet = 1;
+                            beaconEventTime = TimerTempCompensation( BeaconCtx.NextBeaconRx - currentTime, BeaconCtx.Temperature );
+                        }
+                        else
+                        {
+                            BeaconCtx.Ctrl.BeaconDelaySet = 0;
+                            BeaconCtx.Ctrl.AcquisitionPending = 1;
+                            BeaconCtx.Ctrl.AcquisitionTimerSet = 0;
+                            beaconEventTime = BeaconCtx.Cfg.Interval;
+
+                            rxBeaconSetup.SymbolTimeout = BeaconCtx.SymbolTimeout;
+                            rxBeaconSetup.RxTime = 0;
+                            rxBeaconSetup.DeviceAddress = *LoRaMacClassBParams.LoRaMacDevAddr;
+                            rxBeaconSetup.BeaconTimingChannel = BeaconCtx.BeaconTimingChannel;
+                            rxBeaconSetup.CustomFrequencyEnabled = BeaconCtx.Ctrl.CustomFreq;
+                            rxBeaconSetup.BeaconChannelSet = BeaconCtx.Ctrl.BeaconChannelSet;
+                            rxBeaconSetup.CustomFrequency = BeaconCtx.Frequency;
+                            rxBeaconSetup.BeaconTime = BeaconCtx.BeaconTime;
+                            rxBeaconSetup.BeaconInterval = BeaconCtx.Cfg.Interval;
+
+                            RegionRxBeaconSetup( *LoRaMacClassBParams.LoRaMacRegion, &rxBeaconSetup, &LoRaMacClassBParams.McpsIndication->RxDatarate, &beaconChannelSet );
+                            BeaconCtx.Ctrl.BeaconChannelSet = beaconChannelSet;
+                        }
+                        BeaconCtx.NextBeaconRx = 0;
+                        BeaconCtx.BeaconTimingDelay = 0;
+                    }
+                    else
+                    {
+                        BeaconCtx.Ctrl.BeaconDelaySet = 0;
+                        BeaconCtx.Ctrl.AcquisitionPending = 0;
+                        BeaconCtx.Ctrl.AcquisitionTimerSet = 1;
+                        beaconEventTime = BeaconCtx.Cfg.DelayBeaconTimingAns;
+
+                        rxBeaconSetup.SymbolTimeout = BeaconCtx.SymbolTimeout;
+                        rxBeaconSetup.RxTime = 0;
+                        rxBeaconSetup.DeviceAddress = *LoRaMacClassBParams.LoRaMacDevAddr;
+                        rxBeaconSetup.BeaconTimingChannel = BeaconCtx.BeaconTimingChannel;
+                        rxBeaconSetup.CustomFrequencyEnabled = BeaconCtx.Ctrl.CustomFreq;
+                        rxBeaconSetup.BeaconChannelSet = BeaconCtx.Ctrl.BeaconChannelSet;
+                        rxBeaconSetup.CustomFrequency = BeaconCtx.Frequency;
+                        rxBeaconSetup.BeaconTime = BeaconCtx.BeaconTime;
+                        rxBeaconSetup.BeaconInterval = BeaconCtx.Cfg.Interval;
+
+                        RegionRxBeaconSetup( *LoRaMacClassBParams.LoRaMacRegion, &rxBeaconSetup, &LoRaMacClassBParams.McpsIndication->RxDatarate, &beaconChannelSet );
+                        BeaconCtx.Ctrl.BeaconChannelSet = beaconChannelSet;
+                    }
+                }
+                else
+                {
+                    BeaconCtx.Ctrl.AcquisitionPending = 1;
+                    beaconEventTime = BeaconCtx.Cfg.Interval;
+                    if( BeaconCtx.Ctrl.AcquisitionTimerSet == 0 )
+                    {
+                        rxBeaconSetup.SymbolTimeout = BeaconCtx.SymbolTimeout;
+                        rxBeaconSetup.RxTime = 0;
+                        rxBeaconSetup.DeviceAddress = *LoRaMacClassBParams.LoRaMacDevAddr;
+                        rxBeaconSetup.BeaconTimingChannel = BeaconCtx.BeaconTimingChannel;
+                        rxBeaconSetup.CustomFrequencyEnabled = BeaconCtx.Ctrl.CustomFreq;
+                        rxBeaconSetup.BeaconChannelSet = BeaconCtx.Ctrl.BeaconChannelSet;
+                        rxBeaconSetup.CustomFrequency = BeaconCtx.Frequency;
+                        rxBeaconSetup.BeaconTime = BeaconCtx.BeaconTime;
+                        rxBeaconSetup.BeaconInterval = BeaconCtx.Cfg.Interval;
+
+                        RegionRxBeaconSetup( *LoRaMacClassBParams.LoRaMacRegion, &rxBeaconSetup, &LoRaMacClassBParams.McpsIndication->RxDatarate, &beaconChannelSet );
+                        BeaconCtx.Ctrl.BeaconChannelSet = beaconChannelSet;
+                    }
+                    BeaconCtx.Ctrl.AcquisitionTimerSet = 0;
+                }
+            }
+            break;
+        }
+        case BEACON_STATE_TIMEOUT:
+        {
+            // Store listen time
+            BeaconCtx.ListenTime = currentTime - BeaconCtx.NextBeaconRx;
+            // Setup next state
+            BeaconState = BEACON_STATE_BEACON_MISSED;
+            // no break here
+        }
+        case BEACON_STATE_BEACON_MISSED:
+        {
+            // We have to update the beacon time, since we missed a beacon
+            BeaconCtx.BeaconTime += ( BeaconCtx.Cfg.Interval / 1000 );
+
+            // Update symbol timeout
+            BeaconCtx.SymbolTimeout *= BeaconCtx.Cfg.SymbolToExpansionFactor;
+            if( BeaconCtx.SymbolTimeout > BeaconCtx.Cfg.SymbolToExpansionMax )
+            {
+                BeaconCtx.SymbolTimeout = BeaconCtx.Cfg.SymbolToExpansionMax;
+            }
+            PingSlotCtx.SymbolTimeout *= PingSlotCtx.Cfg.SymbolToExpansionFactor;
+            if( PingSlotCtx.SymbolTimeout > PingSlotCtx.Cfg.SymbolToExpansionMax )
+            {
+                PingSlotCtx.SymbolTimeout = PingSlotCtx.Cfg.SymbolToExpansionMax;
+            }
+            // Setup next state
+            BeaconState = BEACON_STATE_REACQUISITION;
+            // no break here
+        }
+        case BEACON_STATE_REACQUISITION:
+        {
+            if( ( currentTime - BeaconCtx.LastBeaconRx ) > BeaconCtx.Cfg.MaxBeaconLessPeriod )
+            {
+                activateTimer = true;
+                BeaconState = BEACON_STATE_SWITCH_CLASS;
+            }
+            else
+            {
+                activateTimer = true;
+                // Calculate the point in time of the next beacon
+                beaconEventTime = ( ( currentTime - BeaconCtx.LastBeaconRx ) % BeaconCtx.Cfg.Interval );
+                beaconEventTime = BeaconCtx.Cfg.Interval - beaconEventTime;
+                // Take window enlargement into account
+                beaconEventTime -= ( ( BeaconCtx.ListenTime * BeaconCtx.Cfg.SymbolToExpansionFactor ) >> 1 );
+                beaconEventTime = TimerTempCompensation( beaconEventTime, BeaconCtx.Temperature );
+                BeaconCtx.NextBeaconRx = currentTime + beaconEventTime;
+
+                // Make sure to transit to the correct state
+                if( ( currentTime + BeaconCtx.Cfg.Guard ) < BeaconCtx.NextBeaconRx )
+                {
+                    beaconEventTime -= BeaconCtx.Cfg.Guard;
+                    BeaconState = BEACON_STATE_IDLE;
+                }
+                else
+                {
+                    BeaconState = BEACON_STATE_GUARD;
+                }
+
+                if( PingSlotCtx.Ctrl.Assigned == 1 )
+                {
+                    PingSlotState = PINGSLOT_STATE_CALC_PING_OFFSET;
+                    TimerSetValue( &PingSlotTimer, 1 );
+                    TimerStart( &PingSlotTimer );
+
+                    MulticastSlotState = PINGSLOT_STATE_CALC_PING_OFFSET;
+                    TimerSetValue( &MulticastSlotTimer, 1 );
+                    TimerStart( &MulticastSlotTimer );
+                }
+            }
+            BeaconCtx.Ctrl.BeaconAcquired = 0;
+
+            if( BeaconCtx.Ctrl.ResumeBeaconing == 0 )
+            {
+                LoRaMacClassBParams.MlmeIndication->MlmeIndication = MLME_BEACON;
+                LoRaMacClassBParams.MlmeIndication->Status = LORAMAC_EVENT_INFO_STATUS_BEACON_LOST;
+                LoRaMacClassBParams.LoRaMacFlags->Bits.MlmeInd = 1;
+
+                TimerSetValue( LoRaMacClassBParams.MacStateCheckTimer, 1 );
+                TimerStart( LoRaMacClassBParams.MacStateCheckTimer );
+                LoRaMacClassBParams.LoRaMacFlags->Bits.MacDone = 1;
+            }
+            BeaconCtx.Ctrl.ResumeBeaconing = 0;
+            break;
+        }
+        case BEACON_STATE_LOCKED:
+        {
+            activateTimer = true;
+            // Calculate the point in time of the next beacon
+            beaconEventTime = ( ( currentTime - BeaconCtx.LastBeaconRx ) % BeaconCtx.Cfg.Interval );
+            beaconEventTime = BeaconCtx.Cfg.Interval - beaconEventTime;
+            beaconEventTime = TimerTempCompensation( beaconEventTime, BeaconCtx.Temperature );
+            BeaconCtx.NextBeaconRx = currentTime + beaconEventTime;
+
+            // Make sure to transit to the correct state
+            if( ( currentTime + BeaconCtx.Cfg.Guard ) < BeaconCtx.NextBeaconRx )
+            {
+                beaconEventTime -= BeaconCtx.Cfg.Guard;
+                BeaconState = BEACON_STATE_IDLE;
+            }
+            else
+            {
+                BeaconState = BEACON_STATE_GUARD;
+            }
+
+            if( LoRaMacClassBParams.LoRaMacFlags->Bits.MlmeReq == 1 )
+            {
+                // index = GetMlmeConfirmIndex( MlmeConfirmQueue, MLME_BEACON_ACQUISITION, MlmeConfirmQueueCnt );
+                index = LoRaMacClassBCallbacks.GetMlmeConfrimIndex( LoRaMacClassBParams.MlmeConfirmQueue, MLME_BEACON_ACQUISITION );
+                if( index < LORA_MAC_MLME_CONFIRM_QUEUE_LEN )
+                {
+                    LoRaMacClassBParams.MlmeConfirmQueue[index].Status = LORAMAC_EVENT_INFO_STATUS_OK;
+                    LoRaMacClassBParams.MlmeConfirm->TxTimeOnAir = 0;
+                }
+            }
+
+            if( PingSlotCtx.Ctrl.Assigned == 1 )
+            {
+                PingSlotState = PINGSLOT_STATE_CALC_PING_OFFSET;
+                TimerSetValue( &PingSlotTimer, 1 );
+                TimerStart( &PingSlotTimer );
+
+                MulticastSlotState = PINGSLOT_STATE_CALC_PING_OFFSET;
+                TimerSetValue( &MulticastSlotTimer, 1 );
+                TimerStart( &MulticastSlotTimer );
+            }
+            BeaconCtx.Ctrl.AcquisitionPending = 0;
+
+            if( BeaconCtx.Ctrl.ResumeBeaconing == 0 )
+            {
+                LoRaMacClassBParams.MlmeIndication->MlmeIndication = MLME_BEACON;
+                LoRaMacClassBParams.MlmeIndication->Status = LORAMAC_EVENT_INFO_STATUS_BEACON_LOCKED;
+                LoRaMacClassBParams.LoRaMacFlags->Bits.MlmeInd = 1;
+
+                TimerSetValue( LoRaMacClassBParams.MacStateCheckTimer, 1 );
+                TimerStart( LoRaMacClassBParams.MacStateCheckTimer );
+                LoRaMacClassBParams.LoRaMacFlags->Bits.MacDone = 1;
+            }
+            BeaconCtx.Ctrl.ResumeBeaconing = 0;
+            break;
+        }
+        case BEACON_STATE_IDLE:
+        {
+            activateTimer = true;
+            GetTemperatureLevel( &LoRaMacClassBCallbacks, &BeaconCtx );
+            beaconEventTime = BeaconCtx.NextBeaconRx - RADIO_WAKEUP_TIME;
+            currentTime = TimerGetCurrentTime( );
+
+            if( beaconEventTime > currentTime )
+            {
+                BeaconState = BEACON_STATE_GUARD;
+                beaconEventTime -= currentTime;
+                beaconEventTime = TimerTempCompensation( beaconEventTime, BeaconCtx.Temperature );
+            }
+            else
+            {
+                BeaconState = BEACON_STATE_REACQUISITION;
+                beaconEventTime = 1;
+            }
+            break;
+        }
+        case BEACON_STATE_GUARD:
+        {
+            BeaconState = BEACON_STATE_RX;
+
+            rxBeaconSetup.SymbolTimeout = BeaconCtx.SymbolTimeout;
+            rxBeaconSetup.RxTime = BeaconCtx.Cfg.Reserved;
+            rxBeaconSetup.DeviceAddress = *LoRaMacClassBParams.LoRaMacDevAddr;
+            rxBeaconSetup.BeaconTimingChannel = BeaconCtx.BeaconTimingChannel;
+            rxBeaconSetup.CustomFrequencyEnabled = BeaconCtx.Ctrl.CustomFreq;
+            rxBeaconSetup.BeaconChannelSet = BeaconCtx.Ctrl.BeaconChannelSet;
+            rxBeaconSetup.CustomFrequency = BeaconCtx.Frequency;
+            rxBeaconSetup.BeaconTime = BeaconCtx.BeaconTime;
+            rxBeaconSetup.BeaconInterval = BeaconCtx.Cfg.Interval;
+
+            RegionRxBeaconSetup( *LoRaMacClassBParams.LoRaMacRegion, &rxBeaconSetup, &LoRaMacClassBParams.McpsIndication->RxDatarate, &beaconChannelSet );
+            BeaconCtx.Ctrl.BeaconChannelSet = beaconChannelSet;
+            break;
+        }
+        case BEACON_STATE_SWITCH_CLASS:
+        {
+            if( LoRaMacClassBParams.LoRaMacFlags->Bits.MlmeReq == 1 )
+            {
+                // index = GetMlmeConfirmIndex( MlmeConfirmQueue, MLME_BEACON_ACQUISITION, MlmeConfirmQueueCnt );
+                index = LoRaMacClassBCallbacks.GetMlmeConfrimIndex( LoRaMacClassBParams.MlmeConfirmQueue, MLME_BEACON_ACQUISITION );
+                if( index < LORA_MAC_MLME_CONFIRM_QUEUE_LEN )
+                {
+                    LoRaMacClassBParams.MlmeConfirmQueue[index].Status = LORAMAC_EVENT_INFO_STATUS_BEACON_NOT_FOUND;
+                }
+            }
+            else
+            {
+                LoRaMacClassBParams.MlmeIndication->MlmeIndication = MLME_SWITCH_CLASS;
+                LoRaMacClassBParams.MlmeIndication->Status = LORAMAC_EVENT_INFO_STATUS_OK;
+                PingSlotCtx.Ctrl.Assigned = 0;
+                LoRaMacClassBParams.LoRaMacFlags->Bits.MlmeInd = 1;
+            }
+            BeaconState = BEACON_STATE_ACQUISITION;
+
+            BeaconCtx.Ctrl.BeaconMode = 0;
+            BeaconCtx.Ctrl.AcquisitionPending = 0;
+            BeaconCtx.Ctrl.AcquisitionTimerSet = 0;
+            LoRaMacClassBParams.LoRaMacFlags->Bits.MacDone = 1;
+
+            TimerSetValue( LoRaMacClassBParams.MacStateCheckTimer, beaconEventTime );
+            TimerStart( LoRaMacClassBParams.MacStateCheckTimer );
+            break;
+        }
+        default:
+        {
+            BeaconState = BEACON_STATE_ACQUISITION;
+            break;
+        }
+    }
+
+    if( activateTimer == true )
+    {
+        TimerSetValue( &BeaconTimer, beaconEventTime );
+        TimerStart( &BeaconTimer );
+    }
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+void LoRaMacClassBPingSlotTimerEvent( void )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+    GetPhyParams_t getPhy;
+    PhyParam_t phyParam;
+    RxConfigParams_t pingSlotRxConfig;
+    TimerTime_t pingSlotTime = 0;
+
+    TimerStop( &PingSlotTimer );
+
+    switch( PingSlotState )
+    {
+        case PINGSLOT_STATE_CALC_PING_OFFSET:
+        {
+            LoRaMacBeaconComputePingOffset( BeaconCtx.BeaconTime,
+                                            *LoRaMacClassBParams.LoRaMacDevAddr,
+                                            PingSlotCtx.PingPeriod,
+                                            &( PingSlotCtx.PingOffset ) );
+            PingSlotState = PINGSLOT_STATE_SET_TIMER;
+            // no break
+        }
+        case PINGSLOT_STATE_SET_TIMER:
+        {
+            if( CalcNextSlotTime( PingSlotCtx.PingOffset, PingSlotCtx.PingPeriod, &pingSlotTime ) == true )
+            {
+                // Start the timer if the ping slot time is in range
+                PingSlotState = PINGSLOT_STATE_IDLE;
+                TimerSetValue( &PingSlotTimer, pingSlotTime );
+                TimerStart( &PingSlotTimer );
+            }
+            break;
+        }
+        case PINGSLOT_STATE_IDLE:
+        {
+            uint32_t frequency = PingSlotCtx.Frequency;
+
+            if( PingSlotCtx.Ctrl.CustomFreq == 0 )
+            {
+                // Restore floor plan
+                getPhy.Attribute = PHY_PINGSLOT_CHANNEL_FREQ;
+                getPhy.DeviceAddress = *LoRaMacClassBParams.LoRaMacDevAddr;
+                phyParam = RegionGetPhyParam( *LoRaMacClassBParams.LoRaMacRegion, &getPhy );
+                frequency = phyParam.Value;
+            }
+
+            if( MulticastSlotState != PINGSLOT_STATE_RX )
+            {
+                if( BeaconCtx.Ctrl.BeaconAcquired == 1 )
+                {
+                    RegionComputeRxWindowParameters( *LoRaMacClassBParams.LoRaMacRegion,
+                                                     PingSlotCtx.Datarate,
+                                                     LoRaMacClassBParams.LoRaMacParams->MinRxSymbols,
+                                                     LoRaMacClassBParams.LoRaMacParams->SystemMaxRxError,
+                                                     &pingSlotRxConfig );
+                    PingSlotCtx.SymbolTimeout = pingSlotRxConfig.WindowTimeout;
+                }
+                PingSlotState = PINGSLOT_STATE_RX;
+
+                pingSlotRxConfig.Datarate = PingSlotCtx.Datarate;
+                pingSlotRxConfig.DownlinkDwellTime = LoRaMacClassBParams.LoRaMacParams->DownlinkDwellTime;
+                pingSlotRxConfig.RepeaterSupport = LoRaMacClassBParams.LoRaMacParams->RepeaterSupport;
+                pingSlotRxConfig.Frequency = frequency;
+                pingSlotRxConfig.RxContinuous = false;
+                pingSlotRxConfig.Window = 1;
+
+                RegionRxConfig( *LoRaMacClassBParams.LoRaMacRegion, &pingSlotRxConfig, ( int8_t* )&LoRaMacClassBParams.McpsIndication->RxDatarate );
+
+                if( pingSlotRxConfig.RxContinuous == false )
+                {
+                    Radio.Rx( LoRaMacClassBParams.LoRaMacParams->MaxRxWindow );
+                }
+                else
+                {
+                    Radio.Rx( 0 ); // Continuous mode
+                }
+            }
+            else
+            {
+                // Multicast slots have priority. Skip Rx
+                PingSlotState = PINGSLOT_STATE_SET_TIMER;
+                TimerSetValue( &PingSlotTimer, PingSlotCtx.Cfg.PingSlotWindow );
+                TimerStart( &PingSlotTimer );
+            }
+            break;
+        }
+        default:
+        {
+            PingSlotState = PINGSLOT_STATE_SET_TIMER;
+            break;
+        }
+    }
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+void LoRaMacClassBMulticastSlotTimerEvent( void )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+    GetPhyParams_t getPhy;
+    PhyParam_t phyParam;
+    RxConfigParams_t multicastSlotRxConfig;
+    TimerTime_t multicastSlotTime = 0;
+    TimerTime_t slotTime = 0;
+    MulticastParams_t *cur = LoRaMacClassBParams.MulticastChannels;
+
+    TimerStop( &MulticastSlotTimer );
+
+    if( cur == NULL )
+    {
+        return;
+    }
+
+    switch( MulticastSlotState )
+    {
+        case PINGSLOT_STATE_CALC_PING_OFFSET:
+        {
+            while( cur != NULL )
+            {
+                LoRaMacBeaconComputePingOffset( BeaconCtx.BeaconTime,
+                                                cur->Address,
+                                                PingSlotCtx.PingPeriod,
+                                                &( cur->PingOffset ) );
+                cur = cur->Next;
+            }
+            MulticastSlotState = PINGSLOT_STATE_SET_TIMER;
+            // no break
+        }
+        case PINGSLOT_STATE_SET_TIMER:
+        {
+            cur = LoRaMacClassBParams.MulticastChannels;
+            PingSlotCtx.NextMulticastChannel = NULL;
+
+            while( cur != NULL )
+            {
+                if( CalcNextSlotTime( cur->PingOffset, PingSlotCtx.PingPeriod, &slotTime ) == true )
+                {
+                    if( ( multicastSlotTime == 0 ) || ( multicastSlotTime > slotTime ) )
+                    {
+                        // Update the slot time and the next multicast channel
+                        multicastSlotTime = slotTime;
+                        PingSlotCtx.NextMulticastChannel = cur;
+                    }
+                }
+                cur = cur->Next;
+            }
+
+            if( PingSlotCtx.NextMulticastChannel != NULL )
+            {
+                // Start the timer if the ping slot time is in range
+                MulticastSlotState = PINGSLOT_STATE_IDLE;
+                TimerSetValue( &MulticastSlotTimer, multicastSlotTime );
+                TimerStart( &MulticastSlotTimer );
+            }
+            break;
+        }
+        case PINGSLOT_STATE_IDLE:
+        {
+            uint32_t frequency = PingSlotCtx.Frequency;
+
+            if( PingSlotCtx.NextMulticastChannel == NULL )
+            {
+                MulticastSlotState = PINGSLOT_STATE_SET_TIMER;
+                TimerSetValue( &MulticastSlotTimer, 1 );
+                TimerStart( &MulticastSlotTimer );
+                break;
+            }
+
+            if( PingSlotCtx.Ctrl.CustomFreq == 0 )
+            {
+                // Restore floor plan
+                getPhy.Attribute = PHY_PINGSLOT_CHANNEL_FREQ;
+                getPhy.DeviceAddress = PingSlotCtx.NextMulticastChannel->Address;
+                phyParam = RegionGetPhyParam( *LoRaMacClassBParams.LoRaMacRegion, &getPhy );
+                frequency = phyParam.Value;
+            }
+
+            if( BeaconCtx.Ctrl.BeaconAcquired == 1 )
+            {
+                RegionComputeRxWindowParameters( *LoRaMacClassBParams.LoRaMacRegion,
+                                                 PingSlotCtx.Datarate,
+                                                 LoRaMacClassBParams.LoRaMacParams->MinRxSymbols,
+                                                 LoRaMacClassBParams.LoRaMacParams->SystemMaxRxError,
+                                                 &multicastSlotRxConfig );
+                PingSlotCtx.SymbolTimeout = multicastSlotRxConfig.WindowTimeout;
+            }
+            MulticastSlotState = PINGSLOT_STATE_RX;
+
+            multicastSlotRxConfig.Datarate = PingSlotCtx.Datarate;
+            multicastSlotRxConfig.DownlinkDwellTime = LoRaMacClassBParams.LoRaMacParams->DownlinkDwellTime;
+            multicastSlotRxConfig.RepeaterSupport = LoRaMacClassBParams.LoRaMacParams->RepeaterSupport;
+            multicastSlotRxConfig.Frequency = frequency;
+            multicastSlotRxConfig.RxContinuous = false;
+            multicastSlotRxConfig.Window = 1;
+
+            RegionRxConfig( *LoRaMacClassBParams.LoRaMacRegion, &multicastSlotRxConfig, ( int8_t* )&LoRaMacClassBParams.McpsIndication->RxDatarate );
+
+            if( multicastSlotRxConfig.RxContinuous == false )
+            {
+                Radio.Rx( LoRaMacClassBParams.LoRaMacParams->MaxRxWindow );
+            }
+            else
+            {
+                Radio.Rx( 0 ); // Continuous mode
+            }
+            break;
+        }
+        default:
+        {
+            MulticastSlotState = PINGSLOT_STATE_SET_TIMER;
+            break;
+        }
+    }
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+bool LoRaMacClassBRxBeacon( uint8_t *payload, uint16_t size )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+    GetPhyParams_t getPhy;
+    PhyParam_t phyParam;
+    bool beaconReceived = false;
+    uint16_t crc0 = 0;
+    uint16_t crc1 = 0;
+    uint16_t beaconCrc0 = 0;
+    uint16_t beaconCrc1 = 0;
+    uint8_t rfuOffset1 = 0;
+    uint8_t rfuOffset2 = 0;
+
+    getPhy.Attribute = PHY_BEACON_SIZE;
+    phyParam = RegionGetPhyParam( *LoRaMacClassBParams.LoRaMacRegion, &getPhy );
+
+    // For beacon payload sizes > 17 we need to apply an offset
+    if( phyParam.Value > 17 )
+    {
+        rfuOffset1 = 1;
+        rfuOffset2 = 1;
+    }
+
+    // Verify if we are in the state where we expect a beacon
+    if( ( BeaconState == BEACON_STATE_RX ) || ( BeaconCtx.Ctrl.AcquisitionPending == 1 ) )
+    {
+        if( size == phyParam.Value )
+        {
+            beaconCrc0 = ( ( uint16_t ) payload[6 + rfuOffset1] ) & 0x00FF;
+            beaconCrc0 |= ( ( uint16_t ) payload[7 + rfuOffset1] << 8 ) & 0xFF00;
+            crc0 = BeaconCrc( payload, 6 + rfuOffset1 );
+
+            // Validate the first crc of the beacon frame
+            if( crc0 == beaconCrc0 )
+            {
+                BeaconCtx.BeaconTime  = ( ( uint32_t ) payload[2 + rfuOffset1] ) & 0x000000FF;
+                BeaconCtx.BeaconTime |= ( ( uint32_t ) ( payload[3 + rfuOffset1] << 8 ) ) & 0x0000FF00;
+                BeaconCtx.BeaconTime |= ( ( uint32_t ) ( payload[4 + rfuOffset1] << 16 ) ) & 0x00FF0000;
+                BeaconCtx.BeaconTime |= ( ( uint32_t ) ( payload[5 + rfuOffset1] << 24 ) ) & 0xFF000000;
+                LoRaMacClassBParams.MlmeIndication->BeaconInfo.Time = BeaconCtx.BeaconTime;
+                beaconReceived = true;
+            }
+
+            beaconCrc1 = ( ( uint16_t ) payload[15 + rfuOffset1 + rfuOffset2] ) & 0x00FF;
+            beaconCrc1 |= ( ( uint16_t ) payload[16 + rfuOffset1 + rfuOffset2] << 8 ) & 0xFF00;
+            crc1 = BeaconCrc( &payload[8 + rfuOffset1], 7 + rfuOffset2 );
+
+            // Validate the second crc of the beacon frame
+            if( crc1 == beaconCrc1 )
+            {
+                // Beacon valid, apply data
+                LoRaMacClassBParams.MlmeIndication->BeaconInfo.GwSpecific.InfoDesc = payload[8 + rfuOffset1];
+                memcpy1( LoRaMacClassBParams.MlmeIndication->BeaconInfo.GwSpecific.Info, &payload[9 + rfuOffset1], 7 );
+                beaconReceived = true;
+            }
+
+            // Reset beacon variables, if one of the crc is valid
+            if( beaconReceived == true )
+            {
+                BeaconCtx.LastBeaconRx = TimerGetCurrentTime( ) - Radio.TimeOnAir( MODEM_LORA, size );
+                BeaconCtx.Ctrl.BeaconAcquired = 1;
+                BeaconCtx.Ctrl.BeaconMode = 1;
+                BeaconCtx.SymbolTimeout = BeaconCtx.Cfg.SymbolToDefault;
+                BeaconState = BEACON_STATE_LOCKED;
+
+                LoRaMacClassBBeaconTimerEvent( );
+            }
+        }
+
+        if( BeaconState == BEACON_STATE_RX )
+        {
+            BeaconState = BEACON_STATE_TIMEOUT;
+            LoRaMacClassBBeaconTimerEvent( );
+        }
+        // Return always true, when we expect a beacon.
+        beaconReceived = true;
+    }
+
+    return beaconReceived;
+#else
+    return false;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+bool LoRaMacClassBIsBeaconExpected( void )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+    if( ( BeaconCtx.Ctrl.AcquisitionPending == 1 ) ||
+        ( BeaconCtx.Ctrl.AcquisitionTimerSet == 1 ) ||
+        ( BeaconState == BEACON_STATE_RX ) )
+    {
+        return true;
+    }
+    return false;
+#else
+    return false;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+bool LoRaMacClassBIsPingExpected( void )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+    if( PingSlotState == PINGSLOT_STATE_RX )
+    {
+        return true;
+    }
+    return false;
+#else
+    return false;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+bool LoRaMacClassBIsAcquisitionPending( void )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+    if( BeaconCtx.Ctrl.AcquisitionPending == 1 )
+    {
+        return true;
+    }
+    return false;
+#else
+    return false;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+bool LoRaMacClassBIsAcquisitionTimerSet( void )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+    if( BeaconCtx.Ctrl.AcquisitionTimerSet == 1 )
+    {
+        return true;
+    }
+    return false;
+#else
+    return false;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+bool LoRaMacClassBIsBeaconModeActive( void )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+    if( BeaconCtx.Ctrl.BeaconMode == 1 )
+    {
+        return true;
+    }
+    return false;
+#else
+    return false;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+void LoRaMacClassBSetPingSlotInfo( uint8_t periodicity )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+    PingSlotCtx.PingNb = 128 / ( 1 << periodicity );
+    PingSlotCtx.PingPeriod = BeaconCtx.Cfg.WindowSlots / PingSlotCtx.PingNb;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+void LoRaMacClassBHaltBeaconing( void )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+    if( ( BeaconState == BEACON_STATE_TIMEOUT ) ||
+        ( BeaconState == BEACON_STATE_SWITCH_CLASS ) )
+    {
+        // Update the state machine before halt
+        LoRaMacClassBBeaconTimerEvent( );
+    }
+
+    // Halt beacon state machine
+    BeaconState = BEACON_STATE_HALT;
+
+    // Halt ping slot state machine
+    TimerStop( &BeaconTimer );
+
+    // Halt ping slot state machine
+    TimerStop( &PingSlotTimer );
+
+    // Halt multicast ping slot state machine
+    TimerStop( &MulticastSlotTimer );
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+void LoRaMacClassBResumeBeaconing( void )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+    if( BeaconState == BEACON_STATE_HALT )
+    {
+        BeaconCtx.Ctrl.ResumeBeaconing = 1;
+
+        // Set default state
+        BeaconState = BEACON_STATE_LOCKED;
+
+        if( BeaconCtx.Ctrl.BeaconAcquired == 0 )
+        {
+            // Set the default state for beacon less operation
+            BeaconState = BEACON_STATE_REACQUISITION;
+        }
+        TimerSetValue( &BeaconTimer, 1 );
+        TimerStart( &BeaconTimer );
+    }
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+LoRaMacStatus_t LoRaMacClassBSwitchClass( DeviceClass_t nextClass )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+    if( nextClass == CLASS_B )
+    {// Switch to from class a to class b
+        if( ( BeaconCtx.Ctrl.BeaconMode == 1 ) && ( PingSlotCtx.Ctrl.Assigned == 1 ) )
+        {
+            return LORAMAC_STATUS_OK;
+        }
+    }
+    if( nextClass == CLASS_A )
+    {// Switch from class b to class a
+        BeaconState = BEACON_STATE_ACQUISITION;
+        return LORAMAC_STATUS_OK;
+    }
+    return LORAMAC_STATUS_SERVICE_UNKNOWN;
+#else
+    return LORAMAC_STATUS_SERVICE_UNKNOWN;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+LoRaMacStatus_t LoRaMacClassBMibGetRequestConfirm( MibRequestConfirm_t *mibGet )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+    LoRaMacStatus_t status;
+
+    switch( mibGet->Type )
+    {
+        case MIB_BEACON_INTERVAL:
+        {
+            mibGet->Param.BeaconInterval = BeaconCtx.Cfg.Interval;
+            break;
+        }
+        case MIB_BEACON_RESERVED:
+        {
+            mibGet->Param.BeaconReserved = BeaconCtx.Cfg.Reserved;
+            break;
+        }
+        case MIB_BEACON_GUARD:
+        {
+            mibGet->Param.BeaconGuard = BeaconCtx.Cfg.Guard;
+            break;
+        }
+        case MIB_BEACON_WINDOW:
+        {
+            mibGet->Param.BeaconWindow = BeaconCtx.Cfg.Window;
+            break;
+        }
+        case MIB_BEACON_WINDOW_SLOTS:
+        {
+            mibGet->Param.BeaconWindowSlots = BeaconCtx.Cfg.WindowSlots;
+            break;
+        }
+        case MIB_PING_SLOT_WINDOW:
+        {
+            mibGet->Param.PingSlotWindow = PingSlotCtx.Cfg.PingSlotWindow;
+            break;
+        }
+        case MIB_BEACON_SYMBOL_TO_DEFAULT:
+        {
+            mibGet->Param.BeaconSymbolToDefault = BeaconCtx.Cfg.SymbolToDefault;
+            break;
+        }
+        case MIB_BEACON_SYMBOL_TO_EXPANSION_MAX:
+        {
+            mibGet->Param.BeaconSymbolToExpansionMax = BeaconCtx.Cfg.SymbolToExpansionMax;
+            break;
+        }
+        case MIB_PING_SLOT_SYMBOL_TO_EXPANSION_MAX:
+        {
+            mibGet->Param.PingSlotSymbolToExpansionMax = PingSlotCtx.Cfg.SymbolToExpansionMax;
+            break;
+        }
+        case MIB_BEACON_SYMBOL_TO_EXPANSION_FACTOR:
+        {
+            mibGet->Param.BeaconSymbolToExpansionFactor = BeaconCtx.Cfg.SymbolToExpansionFactor;
+            break;
+        }
+        case MIB_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR:
+        {
+            mibGet->Param.PingSlotSymbolToExpansionFactor = PingSlotCtx.Cfg.SymbolToExpansionFactor;
+            break;
+        }
+        case MIB_MAX_BEACON_LESS_PERIOD:
+        {
+            mibGet->Param.MaxBeaconLessPeriod = BeaconCtx.Cfg.MaxBeaconLessPeriod;
+            break;
+        }
+        default:
+        {
+            status = LORAMAC_STATUS_SERVICE_UNKNOWN;
+            break;
+        }
+    }
+    return status;
+#else
+    return LORAMAC_STATUS_SERVICE_UNKNOWN;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+LoRaMacStatus_t LoRaMacMibClassBSetRequestConfirm( MibRequestConfirm_t *mibSet )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+    LoRaMacStatus_t status;
+
+    switch( mibSet->Type )
+    {
+        case MIB_BEACON_INTERVAL:
+        {
+            BeaconCtx.Cfg.Interval = mibSet->Param.BeaconInterval;
+            break;
+        }
+        case MIB_BEACON_RESERVED:
+        {
+            BeaconCtx.Cfg.Reserved = mibSet->Param.BeaconReserved;
+            break;
+        }
+        case MIB_BEACON_GUARD:
+        {
+            BeaconCtx.Cfg.Guard = mibSet->Param.BeaconGuard;
+            break;
+        }
+        case MIB_BEACON_WINDOW:
+        {
+            BeaconCtx.Cfg.Window = mibSet->Param.BeaconWindow;
+            break;
+        }
+        case MIB_BEACON_WINDOW_SLOTS:
+        {
+            BeaconCtx.Cfg.WindowSlots = mibSet->Param.BeaconWindowSlots;
+            break;
+        }
+        case MIB_PING_SLOT_WINDOW:
+        {
+            PingSlotCtx.Cfg.PingSlotWindow = mibSet->Param.PingSlotWindow;
+            break;
+        }
+        case MIB_BEACON_SYMBOL_TO_DEFAULT:
+        {
+            BeaconCtx.Cfg.SymbolToDefault = mibSet->Param.BeaconSymbolToDefault;
+            break;
+        }
+        case MIB_BEACON_SYMBOL_TO_EXPANSION_MAX:
+        {
+            BeaconCtx.Cfg.SymbolToExpansionMax = mibSet->Param.BeaconSymbolToExpansionMax;
+            break;
+        }
+        case MIB_PING_SLOT_SYMBOL_TO_EXPANSION_MAX:
+        {
+            PingSlotCtx.Cfg.SymbolToExpansionMax = mibSet->Param.PingSlotSymbolToExpansionMax;
+            break;
+        }
+        case MIB_BEACON_SYMBOL_TO_EXPANSION_FACTOR:
+        {
+            BeaconCtx.Cfg.SymbolToExpansionFactor = mibSet->Param.BeaconSymbolToExpansionFactor;
+            break;
+        }
+        case MIB_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR:
+        {
+            PingSlotCtx.Cfg.SymbolToExpansionFactor = mibSet->Param.PingSlotSymbolToExpansionFactor;
+            break;
+        }
+        case MIB_MAX_BEACON_LESS_PERIOD:
+        {
+            BeaconCtx.Cfg.MaxBeaconLessPeriod = mibSet->Param.MaxBeaconLessPeriod;
+            break;
+        }
+        default:
+        {
+            status = LORAMAC_STATUS_SERVICE_UNKNOWN;
+            break;
+        }
+    }
+    return status;
+#else
+    return LORAMAC_STATUS_SERVICE_UNKNOWN;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+void LoRaMacClassBPingSlotInfoAns( void )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+    uint8_t index = LORA_MAC_MLME_CONFIRM_QUEUE_LEN;
+
+    index = LoRaMacClassBCallbacks.GetMlmeConfrimIndex( LoRaMacClassBParams.MlmeConfirmQueue, MLME_PING_SLOT_INFO );
+    if( index < LORA_MAC_MLME_CONFIRM_QUEUE_LEN )
+    {
+        LoRaMacClassBParams.MlmeConfirmQueue[index].Status = LORAMAC_EVENT_INFO_STATUS_OK;
+        PingSlotCtx.Ctrl.Assigned = 1;
+    }
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+uint8_t LoRaMacClassBPingSlotChannelReq( uint8_t datarate, uint32_t frequency )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+    uint8_t status = 0x03;
+    VerifyParams_t verify;
+    GetPhyParams_t getPhy;
+    PhyParam_t phyParam;
+
+    if( frequency != 0 )
+    {
+        if( Radio.CheckRfFrequency( frequency ) == false )
+        {
+            status &= 0xFE; // Channel frequency KO
+        }
+
+        verify.DatarateParams.Datarate = datarate;
+        verify.DatarateParams.DownlinkDwellTime = LoRaMacClassBParams.LoRaMacParams->DownlinkDwellTime;
+
+        if( RegionVerify( *LoRaMacClassBParams.LoRaMacRegion, &verify, PHY_RX_DR ) == false )
+        {
+            status &= 0xFD; // Datarate range KO
+        }
+
+        if( status == 0x03 )
+        {
+            PingSlotCtx.Ctrl.CustomFreq = 1;
+            PingSlotCtx.Frequency = frequency;
+            PingSlotCtx.Datarate = datarate;
+        }
+    }
+    else
+    {
+        getPhy.Attribute = PHY_BEACON_CHANNEL_DR;
+        phyParam = RegionGetPhyParam( *LoRaMacClassBParams.LoRaMacRegion, &getPhy );
+
+        PingSlotCtx.Ctrl.CustomFreq = 0;
+        PingSlotCtx.Datarate = phyParam.Value;
+    }
+    return status;
+#else
+    return 0;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+void LoRaMacClassBBeaconTimingAns( uint16_t beaconTimingDelay, uint8_t beaconTimingChannel )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+    uint8_t index = LORA_MAC_MLME_CONFIRM_QUEUE_LEN;
+    TimerTime_t currentTime = TimerGetCurrentTime( );
+
+    BeaconCtx.BeaconTimingDelay = ( BeaconCtx.Cfg.DelayBeaconTimingAns * beaconTimingDelay );
+    BeaconCtx.BeaconTimingChannel = beaconTimingChannel;
+
+    index = LoRaMacClassBCallbacks.GetMlmeConfrimIndex( LoRaMacClassBParams.MlmeConfirmQueue, MLME_BEACON_TIMING );
+    if( index < LORA_MAC_MLME_CONFIRM_QUEUE_LEN )
+    {
+        if( BeaconCtx.BeaconTimingDelay > BeaconCtx.Cfg.Interval )
+        {
+            // We missed the beacon already
+            BeaconCtx.BeaconTimingDelay = 0;
+            BeaconCtx.BeaconTimingChannel = 0;
+            LoRaMacClassBParams.MlmeConfirmQueue[index].Status = LORAMAC_EVENT_INFO_STATUS_BEACON_NOT_FOUND;
+        }
+        else
+        {
+            BeaconCtx.Ctrl.BeaconDelaySet = 1;
+            BeaconCtx.Ctrl.BeaconChannelSet = 1;
+            BeaconCtx.NextBeaconRx = currentTime + BeaconCtx.BeaconTimingDelay;
+            LoRaMacClassBParams.MlmeConfirmQueue[index].Status = LORAMAC_EVENT_INFO_STATUS_OK;
+        }
+
+        LoRaMacClassBParams.MlmeConfirm->BeaconTimingDelay = BeaconCtx.BeaconTimingDelay;
+        LoRaMacClassBParams.MlmeConfirm->BeaconTimingChannel = BeaconCtx.BeaconTimingChannel;
+    }
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+bool LoRaMacClassBBeaconFreqReq( uint32_t frequency )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+    if( frequency != 0 )
+    {
+        if( Radio.CheckRfFrequency( frequency ) == true )
+        {
+            BeaconCtx.Ctrl.CustomFreq = 1;
+            BeaconCtx.Frequency = frequency;
+            return true;
+        }
+    }
+    else
+    {
+        BeaconCtx.Ctrl.CustomFreq = 0;
+        return true;
+    }
+    return false;
+#else
+    return false;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+TimerTime_t LoRaMacClassBGetBeaconReservedTime( void )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+    return BeaconCtx.Cfg.Reserved;
+#else
+    return 0;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+TimerTime_t LoRaMacClassBGetPingSlotWinTime( void )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+    return PingSlotCtx.Cfg.PingSlotWindow;
+#else
+    return 0;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+TimerTime_t LoRaMacClassBIsUplinkCollision( TimerTime_t txTimeOnAir )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+    TimerTime_t currentTime = TimerGetCurrentTime( );
+    TimerTime_t beaconReserved = 0;
+
+    beaconReserved = BeaconCtx.NextBeaconRx -
+                     BeaconCtx.Cfg.Guard -
+                     LoRaMacClassBParams.LoRaMacParams->ReceiveDelay1 -
+                     LoRaMacClassBParams.LoRaMacParams->ReceiveDelay2 -
+                     txTimeOnAir;
+
+    // Check if the next beacon will be received during the next uplink.
+    if( ( currentTime >= beaconReserved ) && ( currentTime < ( BeaconCtx.NextBeaconRx + BeaconCtx.Cfg.Reserved ) ) )
+    {// Next beacon will be sent during the next uplink.
+        return BeaconCtx.Cfg.Reserved;
+    }
+    return 0;
+#else
+    return 0;
+#endif // LORAMAC_CLASSB_ENABLED
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/LoRaMacClassB.h	Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,565 @@
+/*!
+ * \file      LoRaMacClassB.h
+ *
+ * \brief     LoRa MAC Class B layer implementation
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ *                ______                              _
+ *               / _____)             _              | |
+ *              ( (____  _____ ____ _| |_ _____  ____| |__
+ *               \____ \| ___ |    (_   _) ___ |/ ___)  _ \
+ *               _____) ) ____| | | || |_| ____( (___| | | |
+ *              (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ *              (C)2013 Semtech
+ *
+ *               ___ _____ _   ___ _  _____ ___  ___  ___ ___
+ *              / __|_   _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ *              \__ \ | |/ _ \ (__| ' <| _| (_) |   / (__| _|
+ *              |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ *              embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author    Miguel Luis ( Semtech )
+ *
+ * \author    Gregory Cristian ( Semtech )
+ *
+ * \author    Daniel Jaeckle ( STACKFORCE )
+ *
+ * \defgroup  LORAMACCLASSB LoRa MAC Class B layer implementation
+ *            This module specifies the API implementation of the LoRaMAC Class B layer.
+ *            This is a placeholder for a detailed description of the LoRaMac
+ *            layer and the supported features.
+ * \{
+ */
+#ifndef __LORAMACCLASSB_H__
+#define __LORAMACCLASSB_H__
+
+ /*!
+  * States of the class B beacon acquisition and tracking
+  */
+typedef enum eBeaconState
+{
+    /*!
+     * Initial state to acquire the beacon
+     */
+    BEACON_STATE_ACQUISITION,
+    /*!
+     * Handles the state when the beacon reception fails
+     */
+    BEACON_STATE_TIMEOUT,
+    /*!
+     * Handles the state when the beacon was missed due to an uplink
+     */
+    BEACON_STATE_BEACON_MISSED,
+    /*!
+     * Reacquisition state which applies the algorithm to enlarge the reception
+     * windows
+     */
+    BEACON_STATE_REACQUISITION,
+    /*!
+     * The node has locked a beacon successfully
+     */
+    BEACON_STATE_LOCKED,
+    /*!
+     * The beacon state machine is stopped due to operations with higher priority
+     */
+    BEACON_STATE_HALT,
+    /*!
+     * The node currently operates in the beacon window and is idle. In this
+     * state, the temperature measurement takes place
+     */
+    BEACON_STATE_IDLE,
+    /*!
+     * The node operates in the guard time of class B
+     */
+    BEACON_STATE_GUARD,
+    /*!
+     * The node is in receive mode to lock a beacon
+     */
+    BEACON_STATE_RX,
+    /*!
+     * The nodes switches the device class
+     */
+    BEACON_STATE_SWITCH_CLASS,
+}BeaconState_t;
+
+/*!
+ * States of the class B ping slot mechanism
+ */
+typedef enum ePingSlotState
+{
+    /*!
+     * Calculation of the ping slot offset
+     */
+    PINGSLOT_STATE_CALC_PING_OFFSET,
+    /*!
+     * State to set the timer to open the next ping slot
+     */
+    PINGSLOT_STATE_SET_TIMER,
+    /*!
+     * The node is in idle state
+     */
+    PINGSLOT_STATE_IDLE,
+    /*!
+     * The node opens up a ping slot window
+     */
+    PINGSLOT_STATE_RX,
+}PingSlotState_t;
+
+/*!
+ * Class B ping slot context structure
+ */
+typedef struct sPingSlotContext
+{
+    struct sPingSlotCtrl
+    {
+        /*!
+         * Set when the server assigned a ping slot to the node
+         */
+        uint8_t Assigned         : 1;
+        /*!
+         * Set when a custom frequency is used
+         */
+        uint8_t CustomFreq       : 1;
+    }Ctrl;
+
+    struct sPingSlotCfg
+    {
+        /*!
+         * Ping slot length time in ms
+         */
+        uint32_t PingSlotWindow;
+        /*!
+         * Maximum symbol timeout for ping slots
+         */
+        uint32_t SymbolToExpansionMax;
+        /*!
+         * Symbol expansion value for ping slot windows in case of beacon
+         * loss in symbols
+         */
+        uint32_t SymbolToExpansionFactor;
+    }Cfg;
+    /*!
+     * Number of ping slots
+     */
+    uint8_t PingNb;
+    /*!
+     * Period of the ping slots
+     */
+    uint16_t PingPeriod;
+    /*!
+     * Ping offset
+     */
+    uint16_t PingOffset;
+    /*!
+     * Reception frequency of the ping slot windows
+     */
+    uint32_t Frequency;
+    /*!
+     * Datarate of the ping slot
+     */
+    int8_t Datarate;
+    /*!
+     * Current symbol timeout. The node enlarges this variable in case of beacon
+     * loss.
+     */
+    uint16_t SymbolTimeout;
+    /*!
+     * The multicast channel which will be enabled next.
+     */
+    MulticastParams_t *NextMulticastChannel;
+}PingSlotContext_t;
+
+
+/*!
+ * Class B beacon context structure
+ */
+typedef struct sBeaconContext
+{
+    struct sBeaconCtrl
+    {
+        /*!
+         * Set if the node receives beacons
+         */
+        uint8_t BeaconMode          : 1;
+        /*!
+         * Set if the node has acquired the beacon
+         */
+        uint8_t BeaconAcquired      : 1;
+        /*!
+         * Set if the node has a custom frequency for beaconing and ping slots
+         */
+        uint8_t CustomFreq          : 1;
+        /*!
+         * Set if a beacon delay was set for the beacon acquisition
+         */
+        uint8_t BeaconDelaySet      : 1;
+        /*!
+         * Set if a beacon channel was set for the beacon acquisition
+         */
+        uint8_t BeaconChannelSet    : 1;
+        /*!
+         * Set if beacon acquisition is pending
+         */
+        uint8_t AcquisitionPending  : 1;
+        /*!
+         * Set if beacon timer is set to acquire a beacon
+         */
+        uint8_t AcquisitionTimerSet  : 1;
+        /*!
+         * Set if the beacon state machine will be resumed
+         */
+        uint8_t ResumeBeaconing      : 1;
+    }Ctrl;
+
+    struct sBeaconCfg
+    {
+        /*!
+         * Beacon interval in ms
+         */
+        uint32_t Interval;
+        /*!
+         * Beacon reserved time in ms
+         */
+        uint32_t Reserved;
+        /*!
+         * Beacon guard time in ms
+         */
+        uint32_t Guard;
+        /*!
+         * Beacon window time in ms
+         */
+        uint32_t Window;
+        /*!
+         * Beacon window time in numer of slots
+         */
+        uint32_t WindowSlots;
+        /*!
+         * Default symbol timeout for beacons and ping slot windows
+         */
+        uint32_t SymbolToDefault;
+        /*!
+         * Maximum symbol timeout for beacons
+         */
+        uint32_t SymbolToExpansionMax;
+        /*!
+         * Symbol expansion value for beacon windows in case of beacon
+         * loss in symbols
+         */
+        uint32_t SymbolToExpansionFactor;
+        /*!
+         * Symbol expansion value for ping slot windows in case of beacon
+         * loss in symbols
+         */
+        uint32_t MaxBeaconLessPeriod;
+        /*!
+         * Delay time for the BeaconTimingAns in ms
+         */
+        uint32_t DelayBeaconTimingAns;
+    }Cfg;
+    /*!
+     * Beacon reception frequency
+     */
+    uint32_t Frequency;
+    /*!
+     * Current temperature
+     */
+    float Temperature;
+    /*!
+     * Beacon time received with the beacon frame
+     */
+    TimerTime_t BeaconTime;
+    /*!
+     * Time when the last beacon was received
+     */
+    TimerTime_t LastBeaconRx;
+    /*!
+     * Time when the next beacon will be received
+     */
+    TimerTime_t NextBeaconRx;
+    /*!
+     * Current symbol timeout. The node enlarges this variable in case of beacon
+     * loss.
+     */
+    uint16_t SymbolTimeout;
+    /*!
+     * Listen time for the beacon in case of reception timeout
+     */
+    TimerTime_t ListenTime;
+    /*!
+     * Beacon timing channel for next beacon
+     */
+    uint8_t BeaconTimingChannel;
+    /*!
+     * Delay for next beacon in ms
+     */
+    TimerTime_t BeaconTimingDelay;
+}BeaconContext_t;
+
+/*!
+ * Data structure which contains the callbacks
+ */
+typedef struct sLoRaMacClassBCallback
+{
+    /*!
+     * \brief   Measures the temperature level
+     *
+     * \retval  Temperature level
+     */
+    float ( *GetTemperatureLevel )( void );
+    /*!
+     * \brief Gets the index of the confirm queue of a specific MLME-request
+     *
+     * \param [IN] queue        MLME-Confirm queue pointer
+     * \param [IN] req          MLME-Request to validate
+     * \retval Index of the MLME-Confirm. 0xFF is no entry found.
+     */
+    uint8_t ( *GetMlmeConfrimIndex )( MlmeConfirmQueue_t* queue, Mlme_t req );
+}LoRaMacClassBCallback_t;
+
+/*!
+ * Data structure which pointers to the properties LoRaMAC
+ */
+typedef struct sLoRaMacClassBParams
+{
+    /*!
+     * Pointer to the MlmeIndication structure
+     */
+    MlmeIndication_t *MlmeIndication;
+    /*!
+     * Pointer to the McpsIndication structure
+     */
+    McpsIndication_t *McpsIndication;
+    /*!
+     * Pointer to the MlmeConfirm structure
+     */
+    MlmeConfirm_t *MlmeConfirm;
+    /*!
+     * Pointer to the LoRaMacFlags structure
+     */
+    LoRaMacFlags_t *LoRaMacFlags;
+    /*!
+     * Pointer to the LoRaMac device address
+     */
+    uint32_t *LoRaMacDevAddr;
+    /*!
+     * Pointer to the MLME confirmation queue
+     */
+    MlmeConfirmQueue_t *MlmeConfirmQueue;
+    /*!
+     * Pointer to the LoRaMac region definition
+     */
+    LoRaMacRegion_t *LoRaMacRegion;
+    /*!
+     * Pointer to the MacStateCheckTimer timer
+     */
+    TimerEvent_t *MacStateCheckTimer;
+    /*!
+     * Pointer to the LoRaMacParams structure
+     */
+    LoRaMacParams_t *LoRaMacParams;
+    /*!
+     * Pointer to the multicast channel list
+     */
+    MulticastParams_t *MulticastChannels;
+}LoRaMacClassBParams_t;
+
+/*!
+ * \brief Initialize LoRaWAN Class B
+ *
+ * \param [IN] classBParams Information and feedback parameter
+ * \param [IN] callbacks Contains the callback which the Class B implementation needs
+ */
+void LoRaMacClassBInit( LoRaMacClassBParams_t *classBParams, LoRaMacClassBCallback_t *callbacks );
+
+/*!
+ * \brief Set the state of the beacon state machine
+ *
+ * \param [IN] beaconState Beacon state.
+ */
+void LoRaMacClassBSetBeaconState( BeaconState_t beaconState );
+
+/*!
+ * \brief Set the state of the ping slot state machine
+ *
+ * \param [IN] pingSlotState Ping slot state.
+ */
+void LoRaMacClassBSetPingSlotState( PingSlotState_t pingSlotState );
+
+/*!
+ * \brief State machine of the Class B for beaconing
+ */
+void LoRaMacClassBBeaconTimerEvent( void );
+
+/*!
+ * \brief State machine of the Class B for ping slots
+ */
+void LoRaMacClassBPingSlotTimerEvent( void );
+
+/*!
+ * \brief State machine of the Class B for multicast slots
+ */
+void LoRaMacClassBMulticastSlotTimerEvent( void );
+
+/*!
+ * \brief Receives and decodes the beacon frame
+ *
+ * \param [IN] payload Pointer to the payload
+ * \param [IN] size Size of the payload
+ * \retval [true, if the node has received a beacon; false, if not]
+ */
+bool LoRaMacClassBRxBeacon( uint8_t *payload, uint16_t size );
+
+/*!
+ * \brief The function validates, if the node expects a beacon
+ *        at the current time.
+ *
+ * \retval [true, if the node expects a beacon; false, if not]
+ */
+bool LoRaMacClassBIsBeaconExpected( void );
+
+/*!
+ * \brief The function validates, if the node expects a ping slot
+ *        at the current time.
+ *
+ * \retval [true, if the node expects a ping slot; false, if not]
+ */
+bool LoRaMacClassBIsPingExpected( void );
+
+/*!
+ * \brief Verifies if the acquisition pending bit is set
+ *
+ * \retval [true, if the bit is set; false, if not]
+ */
+bool LoRaMacClassBIsAcquisitionPending( void );
+
+/*!
+ * \brief Verifies if the acquisition timer bit is set
+ *
+ * \retval [true, if the bit is set; false, if not]
+ */
+bool LoRaMacClassBIsAcquisitionTimerSet( void );
+
+/*!
+ * \brief Verifies if the beacon mode active bit is set
+ *
+ * \retval [true, if the bit is set; false, if not]
+ */
+bool LoRaMacClassBIsBeaconModeActive( void );
+
+/*!
+ * \brief Stops the beacon and ping slot operation
+ */
+void LoRaMacClassBHaltBeaconing( void );
+
+/*!
+ * \brief Resumes the beacon and ping slot operation
+ */
+void LoRaMacClassBResumeBeaconing( void );
+
+/*!
+ * \brief Sets the periodicity of the ping slots
+ *
+ * \param [IN] periodicity Periodicity
+ */
+void LoRaMacClassBSetPingSlotInfo( uint8_t periodicity );
+
+/*!
+ * \brief Switches the device class
+ *
+ * \param [IN] nextClass Device class to switch to
+ *
+ * \retval LoRaMacStatus_t Status of the operation.
+ */
+LoRaMacStatus_t LoRaMacClassBSwitchClass( DeviceClass_t nextClass );
+
+/*!
+ * \brief   LoRaMAC ClassB MIB-Get
+ *
+ * \details The mac information base service to get attributes of the LoRaMac
+ *          Class B layer.
+ *
+ * \param   [IN] mibRequest - MIB-GET-Request to perform. Refer to \ref MibRequestConfirm_t.
+ *
+ * \retval  LoRaMacStatus_t Status of the operation. Possible returns are:
+ *          \ref LORAMAC_STATUS_OK,
+ *          \ref LORAMAC_STATUS_SERVICE_UNKNOWN,
+ *          \ref LORAMAC_STATUS_PARAMETER_INVALID.
+ */
+LoRaMacStatus_t LoRaMacClassBMibGetRequestConfirm( MibRequestConfirm_t *mibGet );
+
+/*!
+ * \brief   LoRaMAC Class B MIB-Set
+ *
+ * \details The mac information base service to set attributes of the LoRaMac
+ *          Class B layer.
+ *
+ * \param   [IN] mibRequest - MIB-SET-Request to perform. Refer to \ref MibRequestConfirm_t.
+ *
+ * \retval  LoRaMacStatus_t Status of the operation. Possible returns are:
+ *          \ref LORAMAC_STATUS_OK,
+ *          \ref LORAMAC_STATUS_BUSY,
+ *          \ref LORAMAC_STATUS_SERVICE_UNKNOWN,
+ *          \ref LORAMAC_STATUS_PARAMETER_INVALID.
+ */
+LoRaMacStatus_t LoRaMacMibClassBSetRequestConfirm( MibRequestConfirm_t *mibSet );
+
+/*!
+ * \brief This function handles the PING_SLOT_FREQ_ANS
+ */
+void LoRaMacClassBPingSlotInfoAns( void );
+
+/*!
+ * \brief This function handles the PING_SLOT_CHANNEL_REQ
+ *
+ * \param [IN] datarate Device class to switch to
+ * \param [IN] frequency Device class to switch to
+ *
+ * \retval Status for the MAC answer.
+ */
+uint8_t LoRaMacClassBPingSlotChannelReq( uint8_t datarate, uint32_t frequency );
+
+/*!
+ * \brief This function handles the BEACON_TIMING_ANS
+ *
+ * \param [IN] beaconTimingDelay The beacon timing delay
+ * \param [IN] beaconTimingChannel The beacon timing channel
+ */
+void LoRaMacClassBBeaconTimingAns( uint16_t beaconTimingDelay, uint8_t beaconTimingChannel );
+
+/*!
+ * \brief This function handles the BEACON_FREQ_REQ
+ *
+ * \param [IN] frequency Frequency to set
+ *
+ * \retval [true, if MAC shall send an answer; false, if not]
+ */
+bool LoRaMacClassBBeaconFreqReq( uint32_t frequency );
+
+/*!
+ * \brief Queries the beacon reserved time
+ *
+ * \retval Beacon reserved time
+ */
+TimerTime_t LoRaMacClassBGetBeaconReservedTime( void );
+
+/*!
+ * \brief Queries the ping slot window time
+ *
+ * \retval Ping slot window time
+ */
+TimerTime_t LoRaMacClassBGetPingSlotWinTime( void );
+
+/*!
+ * \brief Queries the ping slot window time
+ *
+ * \param [IN] txTimeOnAir TX time on air for the next uplink
+ *
+ * \retval Returns the time the uplink should be delayed
+ */
+TimerTime_t LoRaMacClassBIsUplinkCollision( TimerTime_t txTimeOnAir );
+
+#endif // __LORAMACCLASSB_H__
--- a/LoRaMacCrypto.cpp	Tue Aug 08 19:27:17 2017 -0400
+++ b/LoRaMacCrypto.cpp	Wed Aug 09 16:20:21 2017 -0400
@@ -15,7 +15,7 @@
 
 License: Revised BSD License, see LICENSE.TXT file include in the project
 
-Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jäckle ( STACKFORCE )
+Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE )
 */
 #include <stdlib.h>
 #include <stdint.h>
@@ -200,3 +200,37 @@
     memcpy1( nonce + 7, pDevNonce, 2 );
     aes_encrypt( nonce, appSKey, &AesContext );
 }
+
+void LoRaMacBeaconComputePingOffset( uint64_t beaconTime, uint32_t address, uint16_t pingPeriod, uint16_t *pingOffset )
+{
+    uint8_t zeroKey[16];
+    uint8_t buffer[16];
+    uint8_t cipher[16];
+    uint32_t result = 0;
+    /* Refer to chapter 15.2 of the LoRaWAN specification v1.1. The beacon time
+     * GPS time in seconds modulo 2^32
+     */
+    uint32_t time = ( beaconTime % ( ( ( uint64_t ) 1 ) << 32 ) );
+
+    memset1( zeroKey, 0, 16 );
+    memset1( buffer, 0, 16 );
+    memset1( cipher, 0, 16 );
+    memset1( AesContext.ksch, '\0', 240 );
+
+    buffer[0] = ( time ) & 0xFF;
+    buffer[1] = ( time >> 8 ) & 0xFF;
+    buffer[2] = ( time >> 16 ) & 0xFF;
+    buffer[3] = ( time >> 24 ) & 0xFF;
+
+    buffer[4] = ( address ) & 0xFF;
+    buffer[5] = ( address >> 8 ) & 0xFF;
+    buffer[6] = ( address >> 16 ) & 0xFF;
+    buffer[7] = ( address >> 24 ) & 0xFF;
+
+    aes_set_key( zeroKey, 16, &AesContext );
+    aes_encrypt( buffer, cipher, &AesContext );
+
+    result = ( ( ( uint32_t ) cipher[0] ) + ( ( ( uint32_t ) cipher[1] ) * 256 ) );
+
+    *pingOffset = ( uint16_t )( result % pingPeriod );
+}
--- a/LoRaMacCrypto.h	Tue Aug 08 19:27:17 2017 -0400
+++ b/LoRaMacCrypto.h	Wed Aug 09 16:20:21 2017 -0400
@@ -26,7 +26,7 @@
  *
  * \author    Gregory Cristian ( Semtech )
  *
- * \author    Daniel Jäckle ( STACKFORCE )
+ * \author    Daniel Jaeckle ( STACKFORCE )
  *
  * \defgroup    LORAMAC_CRYPTO  LoRa MAC layer cryptography implementation
  *              This module covers the implementation of cryptographic functions
@@ -106,6 +106,16 @@
  */
 void LoRaMacJoinComputeSKeys( const uint8_t *key, const uint8_t *appNonce, uint16_t devNonce, uint8_t *nwkSKey, uint8_t *appSKey );
 
+/*!
+ * Computes the LoRaMAC join frame decryption
+ *
+ * \param [IN]  beaconTime      - Time of the recent received beacon
+ * \param [IN]  address         - Frame address
+ * \param [IN]  pingPeriod      - Ping period of the node
+ * \param [OUT] pingOffset      - Pseudo random ping offset
+ */
+void LoRaMacBeaconComputePingOffset( uint64_t beaconTime, uint32_t address, uint16_t pingPeriod, uint16_t *pingOffset );
+
 /*! \} defgroup LORAMAC */
 
 #endif // __LORAMAC_CRYPTO_H__
--- a/LoRaMacTest.h	Tue Aug 08 19:27:17 2017 -0400
+++ b/LoRaMacTest.h	Wed Aug 09 16:20:21 2017 -0400
@@ -26,7 +26,7 @@
  *
  * \author    Gregory Cristian ( Semtech )
  *
- * \author    Daniel Jäckle ( STACKFORCE )
+ * \author    Daniel Jaeckle ( STACKFORCE )
  *
  * \defgroup  LORAMACTEST LoRa MAC layer test function implementation
  *            This module specifies the API implementation of test function of the LoRaMAC layer.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/region/Region.cpp	Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,1078 @@
+/*
+ / _____)             _              | |
+( (____  _____ ____ _| |_ _____  ____| |__
+ \____ \| ___ |    (_   _) ___ |/ ___)  _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+    (C)2013 Semtech
+ ___ _____ _   ___ _  _____ ___  ___  ___ ___
+/ __|_   _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+\__ \ | |/ _ \ (__| ' <| _| (_) |   / (__| _|
+|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+embedded.connectivity.solutions===============
+
+Description: LoRa MAC region implementation
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+
+Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE )
+*/
+#include <stdbool.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "timer.h"
+#include "LoRaMac.h"
+
+
+
+// Regional includes
+#include "Region.h"
+
+
+
+// Setup regions
+#ifdef REGION_AS923
+#include "RegionAS923.h"
+#define AS923_CASE                                 case LORAMAC_REGION_AS923:
+#define AS923_IS_ACTIVE( )                         AS923_CASE { return true; }
+#define AS923_GET_PHY_PARAM( )                     AS923_CASE { return RegionAS923GetPhyParam( getPhy ); }
+#define AS923_SET_BAND_TX_DONE( )                  AS923_CASE { RegionAS923SetBandTxDone( txDone ); break; }
+#define AS923_INIT_DEFAULTS( )                     AS923_CASE { RegionAS923InitDefaults( type ); break; }
+#define AS923_VERIFY( )                            AS923_CASE { return RegionAS923Verify( verify, phyAttribute ); }
+#define AS923_APPLY_CF_LIST( )                     AS923_CASE { RegionAS923ApplyCFList( applyCFList ); break; }
+#define AS923_CHAN_MASK_SET( )                     AS923_CASE { return RegionAS923ChanMaskSet( chanMaskSet ); }
+#define AS923_ADR_NEXT( )                          AS923_CASE { return RegionAS923AdrNext( adrNext, drOut, txPowOut, adrAckCounter ); }
+#define AS923_COMPUTE_RX_WINDOW_PARAMETERS( )      AS923_CASE { RegionAS923ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; }
+#define AS923_RX_CONFIG( )                         AS923_CASE { return RegionAS923RxConfig( rxConfig, datarate ); }
+#define AS923_TX_CONFIG( )                         AS923_CASE { return RegionAS923TxConfig( txConfig, txPower, txTimeOnAir ); }
+#define AS923_LINK_ADR_REQ( )                      AS923_CASE { return RegionAS923LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); }
+#define AS923_RX_PARAM_SETUP_REQ( )                AS923_CASE { return RegionAS923RxParamSetupReq( rxParamSetupReq ); }
+#define AS923_NEW_CHANNEL_REQ( )                   AS923_CASE { return RegionAS923NewChannelReq( newChannelReq ); }
+#define AS923_TX_PARAM_SETUP_REQ( )                AS923_CASE { return RegionAS923TxParamSetupReq( txParamSetupReq ); }
+#define AS923_DL_CHANNEL_REQ( )                    AS923_CASE { return RegionAS923DlChannelReq( dlChannelReq ); }
+#define AS923_ALTERNATE_DR( )                      AS923_CASE { return RegionAS923AlternateDr( alternateDr ); }
+#define AS923_CALC_BACKOFF( )                      AS923_CASE { RegionAS923CalcBackOff( calcBackOff ); break; }
+#define AS923_NEXT_CHANNEL( )                      AS923_CASE { return RegionAS923NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); }
+#define AS923_CHANNEL_ADD( )                       AS923_CASE { return RegionAS923ChannelAdd( channelAdd ); }
+#define AS923_CHANNEL_REMOVE( )                    AS923_CASE { return RegionAS923ChannelsRemove( channelRemove ); }
+#define AS923_SET_CONTINUOUS_WAVE( )               AS923_CASE { RegionAS923SetContinuousWave( continuousWave ); break; }
+#define AS923_APPLY_DR_OFFSET( )                   AS923_CASE { return RegionAS923ApplyDrOffset( downlinkDwellTime, dr, drOffset ); }
+#define AS923_RX_BEACON_SETUP( )
+#else
+#define AS923_IS_ACTIVE( )
+#define AS923_GET_PHY_PARAM( )
+#define AS923_SET_BAND_TX_DONE( )
+#define AS923_INIT_DEFAULTS( )
+#define AS923_VERIFY( )
+#define AS923_APPLY_CF_LIST( )
+#define AS923_CHAN_MASK_SET( )
+#define AS923_ADR_NEXT( )
+#define AS923_COMPUTE_RX_WINDOW_PARAMETERS( )
+#define AS923_RX_CONFIG( )
+#define AS923_TX_CONFIG( )
+#define AS923_LINK_ADR_REQ( )
+#define AS923_RX_PARAM_SETUP_REQ( )
+#define AS923_NEW_CHANNEL_REQ( )
+#define AS923_TX_PARAM_SETUP_REQ( )
+#define AS923_DL_CHANNEL_REQ( )
+#define AS923_ALTERNATE_DR( )
+#define AS923_CALC_BACKOFF( )
+#define AS923_NEXT_CHANNEL( )
+#define AS923_CHANNEL_ADD( )
+#define AS923_CHANNEL_REMOVE( )
+#define AS923_SET_CONTINUOUS_WAVE( )
+#define AS923_APPLY_DR_OFFSET( )
+#define AS923_RX_BEACON_SETUP( )
+#endif
+
+#ifdef REGION_AU915
+#include "RegionAU915.h"
+#define AU915_CASE                                 case LORAMAC_REGION_AU915:
+#define AU915_IS_ACTIVE( )                         AU915_CASE { return true; }
+#define AU915_GET_PHY_PARAM( )                     AU915_CASE { return RegionAU915GetPhyParam( getPhy ); }
+#define AU915_SET_BAND_TX_DONE( )                  AU915_CASE { RegionAU915SetBandTxDone( txDone ); break; }
+#define AU915_INIT_DEFAULTS( )                     AU915_CASE { RegionAU915InitDefaults( type ); break; }
+#define AU915_VERIFY( )                            AU915_CASE { return RegionAU915Verify( verify, phyAttribute ); }
+#define AU915_APPLY_CF_LIST( )                     AU915_CASE { RegionAU915ApplyCFList( applyCFList ); break; }
+#define AU915_CHAN_MASK_SET( )                     AU915_CASE { return RegionAU915ChanMaskSet( chanMaskSet ); }
+#define AU915_ADR_NEXT( )                          AU915_CASE { return RegionAU915AdrNext( adrNext, drOut, txPowOut, adrAckCounter ); }
+#define AU915_COMPUTE_RX_WINDOW_PARAMETERS( )      AU915_CASE { RegionAU915ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; }
+#define AU915_RX_CONFIG( )                         AU915_CASE { return RegionAU915RxConfig( rxConfig, datarate ); }
+#define AU915_TX_CONFIG( )                         AU915_CASE { return RegionAU915TxConfig( txConfig, txPower, txTimeOnAir ); }
+#define AU915_LINK_ADR_REQ( )                      AU915_CASE { return RegionAU915LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); }
+#define AU915_RX_PARAM_SETUP_REQ( )                AU915_CASE { return RegionAU915RxParamSetupReq( rxParamSetupReq ); }
+#define AU915_NEW_CHANNEL_REQ( )                   AU915_CASE { return RegionAU915NewChannelReq( newChannelReq ); }
+#define AU915_TX_PARAM_SETUP_REQ( )                AU915_CASE { return RegionAU915TxParamSetupReq( txParamSetupReq ); }
+#define AU915_DL_CHANNEL_REQ( )                    AU915_CASE { return RegionAU915DlChannelReq( dlChannelReq ); }
+#define AU915_ALTERNATE_DR( )                      AU915_CASE { return RegionAU915AlternateDr( alternateDr ); }
+#define AU915_CALC_BACKOFF( )                      AU915_CASE { RegionAU915CalcBackOff( calcBackOff ); break; }
+#define AU915_NEXT_CHANNEL( )                      AU915_CASE { return RegionAU915NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); }
+#define AU915_CHANNEL_ADD( )                       AU915_CASE { return RegionAU915ChannelAdd( channelAdd ); }
+#define AU915_CHANNEL_REMOVE( )                    AU915_CASE { return RegionAU915ChannelsRemove( channelRemove ); }
+#define AU915_SET_CONTINUOUS_WAVE( )               AU915_CASE { RegionAU915SetContinuousWave( continuousWave ); break; }
+#define AU915_APPLY_DR_OFFSET( )                   AU915_CASE { return RegionAU915ApplyDrOffset( downlinkDwellTime, dr, drOffset ); }
+#define AU915_RX_BEACON_SETUP( )
+#else
+#define AU915_IS_ACTIVE( )
+#define AU915_GET_PHY_PARAM( )
+#define AU915_SET_BAND_TX_DONE( )
+#define AU915_INIT_DEFAULTS( )
+#define AU915_VERIFY( )
+#define AU915_APPLY_CF_LIST( )
+#define AU915_CHAN_MASK_SET( )
+#define AU915_ADR_NEXT( )
+#define AU915_COMPUTE_RX_WINDOW_PARAMETERS( )
+#define AU915_RX_CONFIG( )
+#define AU915_TX_CONFIG( )
+#define AU915_LINK_ADR_REQ( )
+#define AU915_RX_PARAM_SETUP_REQ( )
+#define AU915_NEW_CHANNEL_REQ( )
+#define AU915_TX_PARAM_SETUP_REQ( )
+#define AU915_DL_CHANNEL_REQ( )
+#define AU915_ALTERNATE_DR( )
+#define AU915_CALC_BACKOFF( )
+#define AU915_NEXT_CHANNEL( )
+#define AU915_CHANNEL_ADD( )
+#define AU915_CHANNEL_REMOVE( )
+#define AU915_SET_CONTINUOUS_WAVE( )
+#define AU915_APPLY_DR_OFFSET( )
+#define AU915_RX_BEACON_SETUP( )
+#endif
+
+#ifdef REGION_CN470
+#include "RegionCN470.h"
+#define CN470_CASE                                 case LORAMAC_REGION_CN470:
+#define CN470_IS_ACTIVE( )                         CN470_CASE { return true; }
+#define CN470_GET_PHY_PARAM( )                     CN470_CASE { return RegionCN470GetPhyParam( getPhy ); }
+#define CN470_SET_BAND_TX_DONE( )                  CN470_CASE { RegionCN470SetBandTxDone( txDone ); break; }
+#define CN470_INIT_DEFAULTS( )                     CN470_CASE { RegionCN470InitDefaults( type ); break; }
+#define CN470_VERIFY( )                            CN470_CASE { return RegionCN470Verify( verify, phyAttribute ); }
+#define CN470_APPLY_CF_LIST( )                     CN470_CASE { RegionCN470ApplyCFList( applyCFList ); break; }
+#define CN470_CHAN_MASK_SET( )                     CN470_CASE { return RegionCN470ChanMaskSet( chanMaskSet ); }
+#define CN470_ADR_NEXT( )                          CN470_CASE { return RegionCN470AdrNext( adrNext, drOut, txPowOut, adrAckCounter ); }
+#define CN470_COMPUTE_RX_WINDOW_PARAMETERS( )      CN470_CASE { RegionCN470ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; }
+#define CN470_RX_CONFIG( )                         CN470_CASE { return RegionCN470RxConfig( rxConfig, datarate ); }
+#define CN470_TX_CONFIG( )                         CN470_CASE { return RegionCN470TxConfig( txConfig, txPower, txTimeOnAir ); }
+#define CN470_LINK_ADR_REQ( )                      CN470_CASE { return RegionCN470LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); }
+#define CN470_RX_PARAM_SETUP_REQ( )                CN470_CASE { return RegionCN470RxParamSetupReq( rxParamSetupReq ); }
+#define CN470_NEW_CHANNEL_REQ( )                   CN470_CASE { return RegionCN470NewChannelReq( newChannelReq ); }
+#define CN470_TX_PARAM_SETUP_REQ( )                CN470_CASE { return RegionCN470TxParamSetupReq( txParamSetupReq ); }
+#define CN470_DL_CHANNEL_REQ( )                    CN470_CASE { return RegionCN470DlChannelReq( dlChannelReq ); }
+#define CN470_ALTERNATE_DR( )                      CN470_CASE { return RegionCN470AlternateDr( alternateDr ); }
+#define CN470_CALC_BACKOFF( )                      CN470_CASE { RegionCN470CalcBackOff( calcBackOff ); break; }
+#define CN470_NEXT_CHANNEL( )                      CN470_CASE { return RegionCN470NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); }
+#define CN470_CHANNEL_ADD( )                       CN470_CASE { return RegionCN470ChannelAdd( channelAdd ); }
+#define CN470_CHANNEL_REMOVE( )                    CN470_CASE { return RegionCN470ChannelsRemove( channelRemove ); }
+#define CN470_SET_CONTINUOUS_WAVE( )               CN470_CASE { RegionCN470SetContinuousWave( continuousWave ); break; }
+#define CN470_APPLY_DR_OFFSET( )                   CN470_CASE { return RegionCN470ApplyDrOffset( downlinkDwellTime, dr, drOffset ); }
+#define CN470_RX_BEACON_SETUP( )
+#else
+#define CN470_IS_ACTIVE( )
+#define CN470_GET_PHY_PARAM( )
+#define CN470_SET_BAND_TX_DONE( )
+#define CN470_INIT_DEFAULTS( )
+#define CN470_VERIFY( )
+#define CN470_APPLY_CF_LIST( )
+#define CN470_CHAN_MASK_SET( )
+#define CN470_ADR_NEXT( )
+#define CN470_COMPUTE_RX_WINDOW_PARAMETERS( )
+#define CN470_RX_CONFIG( )
+#define CN470_TX_CONFIG( )
+#define CN470_LINK_ADR_REQ( )
+#define CN470_RX_PARAM_SETUP_REQ( )
+#define CN470_NEW_CHANNEL_REQ( )
+#define CN470_TX_PARAM_SETUP_REQ( )
+#define CN470_DL_CHANNEL_REQ( )
+#define CN470_ALTERNATE_DR( )
+#define CN470_CALC_BACKOFF( )
+#define CN470_NEXT_CHANNEL( )
+#define CN470_CHANNEL_ADD( )
+#define CN470_CHANNEL_REMOVE( )
+#define CN470_SET_CONTINUOUS_WAVE( )
+#define CN470_APPLY_DR_OFFSET( )
+#define CN470_RX_BEACON_SETUP( )
+#endif
+
+#ifdef REGION_CN779
+#include "RegionCN779.h"
+#define CN779_CASE                                 case LORAMAC_REGION_CN779:
+#define CN779_IS_ACTIVE( )                         CN779_CASE { return true; }
+#define CN779_GET_PHY_PARAM( )                     CN779_CASE { return RegionCN779GetPhyParam( getPhy ); }
+#define CN779_SET_BAND_TX_DONE( )                  CN779_CASE { RegionCN779SetBandTxDone( txDone ); break; }
+#define CN779_INIT_DEFAULTS( )                     CN779_CASE { RegionCN779InitDefaults( type ); break; }
+#define CN779_VERIFY( )                            CN779_CASE { return RegionCN779Verify( verify, phyAttribute ); }
+#define CN779_APPLY_CF_LIST( )                     CN779_CASE { RegionCN779ApplyCFList( applyCFList ); break; }
+#define CN779_CHAN_MASK_SET( )                     CN779_CASE { return RegionCN779ChanMaskSet( chanMaskSet ); }
+#define CN779_ADR_NEXT( )                          CN779_CASE { return RegionCN779AdrNext( adrNext, drOut, txPowOut, adrAckCounter ); }
+#define CN779_COMPUTE_RX_WINDOW_PARAMETERS( )      CN779_CASE { RegionCN779ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; }
+#define CN779_RX_CONFIG( )                         CN779_CASE { return RegionCN779RxConfig( rxConfig, datarate ); }
+#define CN779_TX_CONFIG( )                         CN779_CASE { return RegionCN779TxConfig( txConfig, txPower, txTimeOnAir ); }
+#define CN779_LINK_ADR_REQ( )                      CN779_CASE { return RegionCN779LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); }
+#define CN779_RX_PARAM_SETUP_REQ( )                CN779_CASE { return RegionCN779RxParamSetupReq( rxParamSetupReq ); }
+#define CN779_NEW_CHANNEL_REQ( )                   CN779_CASE { return RegionCN779NewChannelReq( newChannelReq ); }
+#define CN779_TX_PARAM_SETUP_REQ( )                CN779_CASE { return RegionCN779TxParamSetupReq( txParamSetupReq ); }
+#define CN779_DL_CHANNEL_REQ( )                    CN779_CASE { return RegionCN779DlChannelReq( dlChannelReq ); }
+#define CN779_ALTERNATE_DR( )                      CN779_CASE { return RegionCN779AlternateDr( alternateDr ); }
+#define CN779_CALC_BACKOFF( )                      CN779_CASE { RegionCN779CalcBackOff( calcBackOff ); break; }
+#define CN779_NEXT_CHANNEL( )                      CN779_CASE { return RegionCN779NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); }
+#define CN779_CHANNEL_ADD( )                       CN779_CASE { return RegionCN779ChannelAdd( channelAdd ); }
+#define CN779_CHANNEL_REMOVE( )                    CN779_CASE { return RegionCN779ChannelsRemove( channelRemove ); }
+#define CN779_SET_CONTINUOUS_WAVE( )               CN779_CASE { RegionCN779SetContinuousWave( continuousWave ); break; }
+#define CN779_APPLY_DR_OFFSET( )                   CN779_CASE { return RegionCN779ApplyDrOffset( downlinkDwellTime, dr, drOffset ); }
+#define CN779_RX_BEACON_SETUP( )
+#else
+#define CN779_IS_ACTIVE( )
+#define CN779_GET_PHY_PARAM( )
+#define CN779_SET_BAND_TX_DONE( )
+#define CN779_INIT_DEFAULTS( )
+#define CN779_VERIFY( )
+#define CN779_APPLY_CF_LIST( )
+#define CN779_CHAN_MASK_SET( )
+#define CN779_ADR_NEXT( )
+#define CN779_COMPUTE_RX_WINDOW_PARAMETERS( )
+#define CN779_RX_CONFIG( )
+#define CN779_TX_CONFIG( )
+#define CN779_LINK_ADR_REQ( )
+#define CN779_RX_PARAM_SETUP_REQ( )
+#define CN779_NEW_CHANNEL_REQ( )
+#define CN779_TX_PARAM_SETUP_REQ( )
+#define CN779_DL_CHANNEL_REQ( )
+#define CN779_ALTERNATE_DR( )
+#define CN779_CALC_BACKOFF( )
+#define CN779_NEXT_CHANNEL( )
+#define CN779_CHANNEL_ADD( )
+#define CN779_CHANNEL_REMOVE( )
+#define CN779_SET_CONTINUOUS_WAVE( )
+#define CN779_APPLY_DR_OFFSET( )
+#define CN779_RX_BEACON_SETUP( )
+#endif
+
+#ifdef REGION_EU433
+#include "RegionEU433.h"
+#define EU433_CASE                                 case LORAMAC_REGION_EU433:
+#define EU433_IS_ACTIVE( )                         EU433_CASE { return true; }
+#define EU433_GET_PHY_PARAM( )                     EU433_CASE { return RegionEU433GetPhyParam( getPhy ); }
+#define EU433_SET_BAND_TX_DONE( )                  EU433_CASE { RegionEU433SetBandTxDone( txDone ); break; }
+#define EU433_INIT_DEFAULTS( )                     EU433_CASE { RegionEU433InitDefaults( type ); break; }
+#define EU433_VERIFY( )                            EU433_CASE { return RegionEU433Verify( verify, phyAttribute ); }
+#define EU433_APPLY_CF_LIST( )                     EU433_CASE { RegionEU433ApplyCFList( applyCFList ); break; }
+#define EU433_CHAN_MASK_SET( )                     EU433_CASE { return RegionEU433ChanMaskSet( chanMaskSet ); }
+#define EU433_ADR_NEXT( )                          EU433_CASE { return RegionEU433AdrNext( adrNext, drOut, txPowOut, adrAckCounter ); }
+#define EU433_COMPUTE_RX_WINDOW_PARAMETERS( )      EU433_CASE { RegionEU433ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; }
+#define EU433_RX_CONFIG( )                         EU433_CASE { return RegionEU433RxConfig( rxConfig, datarate ); }
+#define EU433_TX_CONFIG( )                         EU433_CASE { return RegionEU433TxConfig( txConfig, txPower, txTimeOnAir ); }
+#define EU433_LINK_ADR_REQ( )                      EU433_CASE { return RegionEU433LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); }
+#define EU433_RX_PARAM_SETUP_REQ( )                EU433_CASE { return RegionEU433RxParamSetupReq( rxParamSetupReq ); }
+#define EU433_NEW_CHANNEL_REQ( )                   EU433_CASE { return RegionEU433NewChannelReq( newChannelReq ); }
+#define EU433_TX_PARAM_SETUP_REQ( )                EU433_CASE { return RegionEU433TxParamSetupReq( txParamSetupReq ); }
+#define EU433_DL_CHANNEL_REQ( )                    EU433_CASE { return RegionEU433DlChannelReq( dlChannelReq ); }
+#define EU433_ALTERNATE_DR( )                      EU433_CASE { return RegionEU433AlternateDr( alternateDr ); }
+#define EU433_CALC_BACKOFF( )                      EU433_CASE { RegionEU433CalcBackOff( calcBackOff ); break; }
+#define EU433_NEXT_CHANNEL( )                      EU433_CASE { return RegionEU433NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); }
+#define EU433_CHANNEL_ADD( )                       EU433_CASE { return RegionEU433ChannelAdd( channelAdd ); }
+#define EU433_CHANNEL_REMOVE( )                    EU433_CASE { return RegionEU433ChannelsRemove( channelRemove ); }
+#define EU433_SET_CONTINUOUS_WAVE( )               EU433_CASE { RegionEU433SetContinuousWave( continuousWave ); break; }
+#define EU433_APPLY_DR_OFFSET( )                   EU433_CASE { return RegionEU433ApplyDrOffset( downlinkDwellTime, dr, drOffset ); }
+#define EU433_RX_BEACON_SETUP( )
+#else
+#define EU433_IS_ACTIVE( )
+#define EU433_GET_PHY_PARAM( )
+#define EU433_SET_BAND_TX_DONE( )
+#define EU433_INIT_DEFAULTS( )
+#define EU433_VERIFY( )
+#define EU433_APPLY_CF_LIST( )
+#define EU433_CHAN_MASK_SET( )
+#define EU433_ADR_NEXT( )
+#define EU433_COMPUTE_RX_WINDOW_PARAMETERS( )
+#define EU433_RX_CONFIG( )
+#define EU433_TX_CONFIG( )
+#define EU433_LINK_ADR_REQ( )
+#define EU433_RX_PARAM_SETUP_REQ( )
+#define EU433_NEW_CHANNEL_REQ( )
+#define EU433_TX_PARAM_SETUP_REQ( )
+#define EU433_DL_CHANNEL_REQ( )
+#define EU433_ALTERNATE_DR( )
+#define EU433_CALC_BACKOFF( )
+#define EU433_NEXT_CHANNEL( )
+#define EU433_CHANNEL_ADD( )
+#define EU433_CHANNEL_REMOVE( )
+#define EU433_SET_CONTINUOUS_WAVE( )
+#define EU433_APPLY_DR_OFFSET( )
+#define EU433_RX_BEACON_SETUP( )
+#endif
+
+#ifdef REGION_EU868
+#include "RegionEU868.h"
+#define EU868_CASE                                 case LORAMAC_REGION_EU868:
+#define EU868_IS_ACTIVE( )                         EU868_CASE { return true; }
+#define EU868_GET_PHY_PARAM( )                     EU868_CASE { return RegionEU868GetPhyParam( getPhy ); }
+#define EU868_SET_BAND_TX_DONE( )                  EU868_CASE { RegionEU868SetBandTxDone( txDone ); break; }
+#define EU868_INIT_DEFAULTS( )                     EU868_CASE { RegionEU868InitDefaults( type ); break; }
+#define EU868_VERIFY( )                            EU868_CASE { return RegionEU868Verify( verify, phyAttribute ); }
+#define EU868_APPLY_CF_LIST( )                     EU868_CASE { RegionEU868ApplyCFList( applyCFList ); break; }
+#define EU868_CHAN_MASK_SET( )                     EU868_CASE { return RegionEU868ChanMaskSet( chanMaskSet ); }
+#define EU868_ADR_NEXT( )                          EU868_CASE { return RegionEU868AdrNext( adrNext, drOut, txPowOut, adrAckCounter ); }
+#define EU868_COMPUTE_RX_WINDOW_PARAMETERS( )      EU868_CASE { RegionEU868ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; }
+#define EU868_RX_CONFIG( )                         EU868_CASE { return RegionEU868RxConfig( rxConfig, datarate ); }
+#define EU868_TX_CONFIG( )                         EU868_CASE { return RegionEU868TxConfig( txConfig, txPower, txTimeOnAir ); }
+#define EU868_LINK_ADR_REQ( )                      EU868_CASE { return RegionEU868LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); }
+#define EU868_RX_PARAM_SETUP_REQ( )                EU868_CASE { return RegionEU868RxParamSetupReq( rxParamSetupReq ); }
+#define EU868_NEW_CHANNEL_REQ( )                   EU868_CASE { return RegionEU868NewChannelReq( newChannelReq ); }
+#define EU868_TX_PARAM_SETUP_REQ( )                EU868_CASE { return RegionEU868TxParamSetupReq( txParamSetupReq ); }
+#define EU868_DL_CHANNEL_REQ( )                    EU868_CASE { return RegionEU868DlChannelReq( dlChannelReq ); }
+#define EU868_ALTERNATE_DR( )                      EU868_CASE { return RegionEU868AlternateDr( alternateDr ); }
+#define EU868_CALC_BACKOFF( )                      EU868_CASE { RegionEU868CalcBackOff( calcBackOff ); break; }
+#define EU868_NEXT_CHANNEL( )                      EU868_CASE { return RegionEU868NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); }
+#define EU868_CHANNEL_ADD( )                       EU868_CASE { return RegionEU868ChannelAdd( channelAdd ); }
+#define EU868_CHANNEL_REMOVE( )                    EU868_CASE { return RegionEU868ChannelsRemove( channelRemove ); }
+#define EU868_SET_CONTINUOUS_WAVE( )               EU868_CASE { RegionEU868SetContinuousWave( continuousWave ); break; }
+#define EU868_APPLY_DR_OFFSET( )                   EU868_CASE { return RegionEU868ApplyDrOffset( downlinkDwellTime, dr, drOffset ); }
+#define EU868_RX_BEACON_SETUP( )                   EU868_CASE { RegionEU868RxBeaconSetup( rxBeaconSetup, outDr, beaconChannelSet ); }
+#else
+#define EU868_IS_ACTIVE( )
+#define EU868_GET_PHY_PARAM( )
+#define EU868_SET_BAND_TX_DONE( )
+#define EU868_INIT_DEFAULTS( )
+#define EU868_VERIFY( )
+#define EU868_APPLY_CF_LIST( )
+#define EU868_CHAN_MASK_SET( )
+#define EU868_ADR_NEXT( )
+#define EU868_COMPUTE_RX_WINDOW_PARAMETERS( )
+#define EU868_RX_CONFIG( )
+#define EU868_TX_CONFIG( )
+#define EU868_LINK_ADR_REQ( )
+#define EU868_RX_PARAM_SETUP_REQ( )
+#define EU868_NEW_CHANNEL_REQ( )
+#define EU868_TX_PARAM_SETUP_REQ( )
+#define EU868_DL_CHANNEL_REQ( )
+#define EU868_ALTERNATE_DR( )
+#define EU868_CALC_BACKOFF( )
+#define EU868_NEXT_CHANNEL( )
+#define EU868_CHANNEL_ADD( )
+#define EU868_CHANNEL_REMOVE( )
+#define EU868_SET_CONTINUOUS_WAVE( )
+#define EU868_APPLY_DR_OFFSET( )
+#define EU868_RX_BEACON_SETUP( )
+#endif
+
+#ifdef REGION_KR920
+#include "RegionKR920.h"
+#define KR920_CASE                                 case LORAMAC_REGION_KR920:
+#define KR920_IS_ACTIVE( )                         KR920_CASE { return true; }
+#define KR920_GET_PHY_PARAM( )                     KR920_CASE { return RegionKR920GetPhyParam( getPhy ); }
+#define KR920_SET_BAND_TX_DONE( )                  KR920_CASE { RegionKR920SetBandTxDone( txDone ); break; }
+#define KR920_INIT_DEFAULTS( )                     KR920_CASE { RegionKR920InitDefaults( type ); break; }
+#define KR920_VERIFY( )                            KR920_CASE { return RegionKR920Verify( verify, phyAttribute ); }
+#define KR920_APPLY_CF_LIST( )                     KR920_CASE { RegionKR920ApplyCFList( applyCFList ); break; }
+#define KR920_CHAN_MASK_SET( )                     KR920_CASE { return RegionKR920ChanMaskSet( chanMaskSet ); }
+#define KR920_ADR_NEXT( )                          KR920_CASE { return RegionKR920AdrNext( adrNext, drOut, txPowOut, adrAckCounter ); }
+#define KR920_COMPUTE_RX_WINDOW_PARAMETERS( )      KR920_CASE { RegionKR920ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; }
+#define KR920_RX_CONFIG( )                         KR920_CASE { return RegionKR920RxConfig( rxConfig, datarate ); }
+#define KR920_TX_CONFIG( )                         KR920_CASE { return RegionKR920TxConfig( txConfig, txPower, txTimeOnAir ); }
+#define KR920_LINK_ADR_REQ( )                      KR920_CASE { return RegionKR920LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); }
+#define KR920_RX_PARAM_SETUP_REQ( )                KR920_CASE { return RegionKR920RxParamSetupReq( rxParamSetupReq ); }
+#define KR920_NEW_CHANNEL_REQ( )                   KR920_CASE { return RegionKR920NewChannelReq( newChannelReq ); }
+#define KR920_TX_PARAM_SETUP_REQ( )                KR920_CASE { return RegionKR920TxParamSetupReq( txParamSetupReq ); }
+#define KR920_DL_CHANNEL_REQ( )                    KR920_CASE { return RegionKR920DlChannelReq( dlChannelReq ); }
+#define KR920_ALTERNATE_DR( )                      KR920_CASE { return RegionKR920AlternateDr( alternateDr ); }
+#define KR920_CALC_BACKOFF( )                      KR920_CASE { RegionKR920CalcBackOff( calcBackOff ); break; }
+#define KR920_NEXT_CHANNEL( )                      KR920_CASE { return RegionKR920NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); }
+#define KR920_CHANNEL_ADD( )                       KR920_CASE { return RegionKR920ChannelAdd( channelAdd ); }
+#define KR920_CHANNEL_REMOVE( )                    KR920_CASE { return RegionKR920ChannelsRemove( channelRemove ); }
+#define KR920_SET_CONTINUOUS_WAVE( )               KR920_CASE { RegionKR920SetContinuousWave( continuousWave ); break; }
+#define KR920_APPLY_DR_OFFSET( )                   KR920_CASE { return RegionKR920ApplyDrOffset( downlinkDwellTime, dr, drOffset ); }
+#define KR920_RX_BEACON_SETUP( )
+#else
+#define KR920_IS_ACTIVE( )
+#define KR920_GET_PHY_PARAM( )
+#define KR920_SET_BAND_TX_DONE( )
+#define KR920_INIT_DEFAULTS( )
+#define KR920_VERIFY( )
+#define KR920_APPLY_CF_LIST( )
+#define KR920_CHAN_MASK_SET( )
+#define KR920_ADR_NEXT( )
+#define KR920_COMPUTE_RX_WINDOW_PARAMETERS( )
+#define KR920_RX_CONFIG( )
+#define KR920_TX_CONFIG( )
+#define KR920_LINK_ADR_REQ( )
+#define KR920_RX_PARAM_SETUP_REQ( )
+#define KR920_NEW_CHANNEL_REQ( )
+#define KR920_TX_PARAM_SETUP_REQ( )
+#define KR920_DL_CHANNEL_REQ( )
+#define KR920_ALTERNATE_DR( )
+#define KR920_CALC_BACKOFF( )
+#define KR920_NEXT_CHANNEL( )
+#define KR920_CHANNEL_ADD( )
+#define KR920_CHANNEL_REMOVE( )
+#define KR920_SET_CONTINUOUS_WAVE( )
+#define KR920_APPLY_DR_OFFSET( )
+#define KR920_RX_BEACON_SETUP( )
+#endif
+
+#ifdef REGION_IN865
+#include "RegionIN865.h"
+#define IN865_CASE                                 case LORAMAC_REGION_IN865:
+#define IN865_IS_ACTIVE( )                         IN865_CASE { return true; }
+#define IN865_GET_PHY_PARAM( )                     IN865_CASE { return RegionIN865GetPhyParam( getPhy ); }
+#define IN865_SET_BAND_TX_DONE( )                  IN865_CASE { RegionIN865SetBandTxDone( txDone ); break; }
+#define IN865_INIT_DEFAULTS( )                     IN865_CASE { RegionIN865InitDefaults( type ); break; }
+#define IN865_VERIFY( )                            IN865_CASE { return RegionIN865Verify( verify, phyAttribute ); }
+#define IN865_APPLY_CF_LIST( )                     IN865_CASE { RegionIN865ApplyCFList( applyCFList ); break; }
+#define IN865_CHAN_MASK_SET( )                     IN865_CASE { return RegionIN865ChanMaskSet( chanMaskSet ); }
+#define IN865_ADR_NEXT( )                          IN865_CASE { return RegionIN865AdrNext( adrNext, drOut, txPowOut, adrAckCounter ); }
+#define IN865_COMPUTE_RX_WINDOW_PARAMETERS( )      IN865_CASE { RegionIN865ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; }
+#define IN865_RX_CONFIG( )                         IN865_CASE { return RegionIN865RxConfig( rxConfig, datarate ); }
+#define IN865_TX_CONFIG( )                         IN865_CASE { return RegionIN865TxConfig( txConfig, txPower, txTimeOnAir ); }
+#define IN865_LINK_ADR_REQ( )                      IN865_CASE { return RegionIN865LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); }
+#define IN865_RX_PARAM_SETUP_REQ( )                IN865_CASE { return RegionIN865RxParamSetupReq( rxParamSetupReq ); }
+#define IN865_NEW_CHANNEL_REQ( )                   IN865_CASE { return RegionIN865NewChannelReq( newChannelReq ); }
+#define IN865_TX_PARAM_SETUP_REQ( )                IN865_CASE { return RegionIN865TxParamSetupReq( txParamSetupReq ); }
+#define IN865_DL_CHANNEL_REQ( )                    IN865_CASE { return RegionIN865DlChannelReq( dlChannelReq ); }
+#define IN865_ALTERNATE_DR( )                      IN865_CASE { return RegionIN865AlternateDr( alternateDr ); }
+#define IN865_CALC_BACKOFF( )                      IN865_CASE { RegionIN865CalcBackOff( calcBackOff ); break; }
+#define IN865_NEXT_CHANNEL( )                      IN865_CASE { return RegionIN865NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); }
+#define IN865_CHANNEL_ADD( )                       IN865_CASE { return RegionIN865ChannelAdd( channelAdd ); }
+#define IN865_CHANNEL_REMOVE( )                    IN865_CASE { return RegionIN865ChannelsRemove( channelRemove ); }
+#define IN865_SET_CONTINUOUS_WAVE( )               IN865_CASE { RegionIN865SetContinuousWave( continuousWave ); break; }
+#define IN865_APPLY_DR_OFFSET( )                   IN865_CASE { return RegionIN865ApplyDrOffset( downlinkDwellTime, dr, drOffset ); }
+#define IN865_RX_BEACON_SETUP( )
+#else
+#define IN865_IS_ACTIVE( )
+#define IN865_GET_PHY_PARAM( )
+#define IN865_SET_BAND_TX_DONE( )
+#define IN865_INIT_DEFAULTS( )
+#define IN865_VERIFY( )
+#define IN865_APPLY_CF_LIST( )
+#define IN865_CHAN_MASK_SET( )
+#define IN865_ADR_NEXT( )
+#define IN865_COMPUTE_RX_WINDOW_PARAMETERS( )
+#define IN865_RX_CONFIG( )
+#define IN865_TX_CONFIG( )
+#define IN865_LINK_ADR_REQ( )
+#define IN865_RX_PARAM_SETUP_REQ( )
+#define IN865_NEW_CHANNEL_REQ( )
+#define IN865_TX_PARAM_SETUP_REQ( )
+#define IN865_DL_CHANNEL_REQ( )
+#define IN865_ALTERNATE_DR( )
+#define IN865_CALC_BACKOFF( )
+#define IN865_NEXT_CHANNEL( )
+#define IN865_CHANNEL_ADD( )
+#define IN865_CHANNEL_REMOVE( )
+#define IN865_SET_CONTINUOUS_WAVE( )
+#define IN865_APPLY_DR_OFFSET( )
+#define IN865_RX_BEACON_SETUP( )
+#endif
+
+#ifdef REGION_US915
+#include "RegionUS915.h"
+#define US915_CASE                                 case LORAMAC_REGION_US915:
+#define US915_IS_ACTIVE( )                         US915_CASE { return true; }
+#define US915_GET_PHY_PARAM( )                     US915_CASE { return RegionUS915GetPhyParam( getPhy ); }
+#define US915_SET_BAND_TX_DONE( )                  US915_CASE { RegionUS915SetBandTxDone( txDone ); break; }
+#define US915_INIT_DEFAULTS( )                     US915_CASE { RegionUS915InitDefaults( type ); break; }
+#define US915_VERIFY( )                            US915_CASE { return RegionUS915Verify( verify, phyAttribute ); }
+#define US915_APPLY_CF_LIST( )                     US915_CASE { RegionUS915ApplyCFList( applyCFList ); break; }
+#define US915_CHAN_MASK_SET( )                     US915_CASE { return RegionUS915ChanMaskSet( chanMaskSet ); }
+#define US915_ADR_NEXT( )                          US915_CASE { return RegionUS915AdrNext( adrNext, drOut, txPowOut, adrAckCounter ); }
+#define US915_COMPUTE_RX_WINDOW_PARAMETERS( )      US915_CASE { RegionUS915ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; }
+#define US915_RX_CONFIG( )                         US915_CASE { return RegionUS915RxConfig( rxConfig, datarate ); }
+#define US915_TX_CONFIG( )                         US915_CASE { return RegionUS915TxConfig( txConfig, txPower, txTimeOnAir ); }
+#define US915_LINK_ADR_REQ( )                      US915_CASE { return RegionUS915LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); }
+#define US915_RX_PARAM_SETUP_REQ( )                US915_CASE { return RegionUS915RxParamSetupReq( rxParamSetupReq ); }
+#define US915_NEW_CHANNEL_REQ( )                   US915_CASE { return RegionUS915NewChannelReq( newChannelReq ); }
+#define US915_TX_PARAM_SETUP_REQ( )                US915_CASE { return RegionUS915TxParamSetupReq( txParamSetupReq ); }
+#define US915_DL_CHANNEL_REQ( )                    US915_CASE { return RegionUS915DlChannelReq( dlChannelReq ); }
+#define US915_ALTERNATE_DR( )                      US915_CASE { return RegionUS915AlternateDr( alternateDr ); }
+#define US915_CALC_BACKOFF( )                      US915_CASE { RegionUS915CalcBackOff( calcBackOff ); break; }
+#define US915_NEXT_CHANNEL( )                      US915_CASE { return RegionUS915NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); }
+#define US915_CHANNEL_ADD( )                       US915_CASE { return RegionUS915ChannelAdd( channelAdd ); }
+#define US915_CHANNEL_REMOVE( )                    US915_CASE { return RegionUS915ChannelsRemove( channelRemove ); }
+#define US915_SET_CONTINUOUS_WAVE( )               US915_CASE { RegionUS915SetContinuousWave( continuousWave ); break; }
+#define US915_APPLY_DR_OFFSET( )                   US915_CASE { return RegionUS915ApplyDrOffset( downlinkDwellTime, dr, drOffset ); }
+#define US915_RX_BEACON_SETUP( )
+#else
+#define US915_IS_ACTIVE( )
+#define US915_GET_PHY_PARAM( )
+#define US915_SET_BAND_TX_DONE( )
+#define US915_INIT_DEFAULTS( )
+#define US915_VERIFY( )
+#define US915_APPLY_CF_LIST( )
+#define US915_CHAN_MASK_SET( )
+#define US915_ADR_NEXT( )
+#define US915_COMPUTE_RX_WINDOW_PARAMETERS( )
+#define US915_RX_CONFIG( )
+#define US915_TX_CONFIG( )
+#define US915_LINK_ADR_REQ( )
+#define US915_RX_PARAM_SETUP_REQ( )
+#define US915_NEW_CHANNEL_REQ( )
+#define US915_TX_PARAM_SETUP_REQ( )
+#define US915_DL_CHANNEL_REQ( )
+#define US915_ALTERNATE_DR( )
+#define US915_CALC_BACKOFF( )
+#define US915_NEXT_CHANNEL( )
+#define US915_CHANNEL_ADD( )
+#define US915_CHANNEL_REMOVE( )
+#define US915_SET_CONTINUOUS_WAVE( )
+#define US915_APPLY_DR_OFFSET( )
+#define US915_RX_BEACON_SETUP( )
+#endif
+
+#ifdef REGION_US915_HYBRID
+#include "RegionUS915-Hybrid.h"
+#define US915_HYBRID_CASE                                 case LORAMAC_REGION_US915_HYBRID:
+#define US915_HYBRID_IS_ACTIVE( )                         US915_HYBRID_CASE { return true; }
+#define US915_HYBRID_GET_PHY_PARAM( )                     US915_HYBRID_CASE { return RegionUS915HybridGetPhyParam( getPhy ); }
+#define US915_HYBRID_SET_BAND_TX_DONE( )                  US915_HYBRID_CASE { RegionUS915HybridSetBandTxDone( txDone ); break; }
+#define US915_HYBRID_INIT_DEFAULTS( )                     US915_HYBRID_CASE { RegionUS915HybridInitDefaults( type ); break; }
+#define US915_HYBRID_VERIFY( )                            US915_HYBRID_CASE { return RegionUS915HybridVerify( verify, phyAttribute ); }
+#define US915_HYBRID_APPLY_CF_LIST( )                     US915_HYBRID_CASE { RegionUS915HybridApplyCFList( applyCFList ); break; }
+#define US915_HYBRID_CHAN_MASK_SET( )                     US915_HYBRID_CASE { return RegionUS915HybridChanMaskSet( chanMaskSet ); }
+#define US915_HYBRID_ADR_NEXT( )                          US915_HYBRID_CASE { return RegionUS915HybridAdrNext( adrNext, drOut, txPowOut, adrAckCounter ); }
+#define US915_HYBRID_COMPUTE_RX_WINDOW_PARAMETERS( )      US915_HYBRID_CASE { RegionUS915HybridComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; }
+#define US915_HYBRID_RX_CONFIG( )                         US915_HYBRID_CASE { return RegionUS915HybridRxConfig( rxConfig, datarate ); }
+#define US915_HYBRID_TX_CONFIG( )                         US915_HYBRID_CASE { return RegionUS915HybridTxConfig( txConfig, txPower, txTimeOnAir ); }
+#define US915_HYBRID_LINK_ADR_REQ( )                      US915_HYBRID_CASE { return RegionUS915HybridLinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); }
+#define US915_HYBRID_RX_PARAM_SETUP_REQ( )                US915_HYBRID_CASE { return RegionUS915HybridRxParamSetupReq( rxParamSetupReq ); }
+#define US915_HYBRID_NEW_CHANNEL_REQ( )                   US915_HYBRID_CASE { return RegionUS915HybridNewChannelReq( newChannelReq ); }
+#define US915_HYBRID_TX_PARAM_SETUP_REQ( )                US915_HYBRID_CASE { return RegionUS915HybridTxParamSetupReq( txParamSetupReq ); }
+#define US915_HYBRID_DL_CHANNEL_REQ( )                    US915_HYBRID_CASE { return RegionUS915HybridDlChannelReq( dlChannelReq ); }
+#define US915_HYBRID_ALTERNATE_DR( )                      US915_HYBRID_CASE { return RegionUS915HybridAlternateDr( alternateDr ); }
+#define US915_HYBRID_CALC_BACKOFF( )                      US915_HYBRID_CASE { RegionUS915HybridCalcBackOff( calcBackOff ); break; }
+#define US915_HYBRID_NEXT_CHANNEL( )                      US915_HYBRID_CASE { return RegionUS915HybridNextChannel( nextChanParams, channel, time, aggregatedTimeOff ); }
+#define US915_HYBRID_CHANNEL_ADD( )                       US915_HYBRID_CASE { return RegionUS915HybridChannelAdd( channelAdd ); }
+#define US915_HYBRID_CHANNEL_REMOVE( )                    US915_HYBRID_CASE { return RegionUS915HybridChannelsRemove( channelRemove ); }
+#define US915_HYBRID_SET_CONTINUOUS_WAVE( )               US915_HYBRID_CASE { RegionUS915HybridSetContinuousWave( continuousWave ); break; }
+#define US915_HYBRID_APPLY_DR_OFFSET( )                   US915_HYBRID_CASE { return RegionUS915HybridApplyDrOffset( downlinkDwellTime, dr, drOffset ); }
+#define US915_HYBRID_RX_BEACON_SETUP( )
+#else
+#define US915_HYBRID_IS_ACTIVE( )
+#define US915_HYBRID_GET_PHY_PARAM( )
+#define US915_HYBRID_SET_BAND_TX_DONE( )
+#define US915_HYBRID_INIT_DEFAULTS( )
+#define US915_HYBRID_VERIFY( )
+#define US915_HYBRID_APPLY_CF_LIST( )
+#define US915_HYBRID_CHAN_MASK_SET( )
+#define US915_HYBRID_ADR_NEXT( )
+#define US915_HYBRID_COMPUTE_RX_WINDOW_PARAMETERS( )
+#define US915_HYBRID_RX_CONFIG( )
+#define US915_HYBRID_TX_CONFIG( )
+#define US915_HYBRID_LINK_ADR_REQ( )
+#define US915_HYBRID_RX_PARAM_SETUP_REQ( )
+#define US915_HYBRID_NEW_CHANNEL_REQ( )
+#define US915_HYBRID_TX_PARAM_SETUP_REQ( )
+#define US915_HYBRID_DL_CHANNEL_REQ( )
+#define US915_HYBRID_ALTERNATE_DR( )
+#define US915_HYBRID_CALC_BACKOFF( )
+#define US915_HYBRID_NEXT_CHANNEL( )
+#define US915_HYBRID_CHANNEL_ADD( )
+#define US915_HYBRID_CHANNEL_REMOVE( )
+#define US915_HYBRID_SET_CONTINUOUS_WAVE( )
+#define US915_HYBRID_APPLY_DR_OFFSET( )
+#define US915_HYBRID_RX_BEACON_SETUP( )
+#endif
+
+bool RegionIsActive( LoRaMacRegion_t region )
+{
+    switch( region )
+    {
+        AS923_IS_ACTIVE( );
+        AU915_IS_ACTIVE( );
+        CN470_IS_ACTIVE( );
+        CN779_IS_ACTIVE( );
+        EU433_IS_ACTIVE( );
+        EU868_IS_ACTIVE( );
+        KR920_IS_ACTIVE( );
+        IN865_IS_ACTIVE( );
+        US915_IS_ACTIVE( );
+        US915_HYBRID_IS_ACTIVE( );
+        default:
+        {
+            return false;
+        }
+    }
+}
+
+PhyParam_t RegionGetPhyParam( LoRaMacRegion_t region, GetPhyParams_t* getPhy )
+{
+    PhyParam_t phyParam = { 0 };
+    switch( region )
+    {
+        AS923_GET_PHY_PARAM( );
+        AU915_GET_PHY_PARAM( );
+        CN470_GET_PHY_PARAM( );
+        CN779_GET_PHY_PARAM( );
+        EU433_GET_PHY_PARAM( );
+        EU868_GET_PHY_PARAM( );
+        KR920_GET_PHY_PARAM( );
+        IN865_GET_PHY_PARAM( );
+        US915_GET_PHY_PARAM( );
+        US915_HYBRID_GET_PHY_PARAM( );
+        default:
+        {
+            return phyParam;
+        }
+    }
+}
+
+void RegionSetBandTxDone( LoRaMacRegion_t region, SetBandTxDoneParams_t* txDone )
+{
+    switch( region )
+    {
+        AS923_SET_BAND_TX_DONE( );
+        AU915_SET_BAND_TX_DONE( );
+        CN470_SET_BAND_TX_DONE( );
+        CN779_SET_BAND_TX_DONE( );
+        EU433_SET_BAND_TX_DONE( );
+        EU868_SET_BAND_TX_DONE( );
+        KR920_SET_BAND_TX_DONE( );
+        IN865_SET_BAND_TX_DONE( );
+        US915_SET_BAND_TX_DONE( );
+        US915_HYBRID_SET_BAND_TX_DONE( );
+        default:
+        {
+            return;
+        }
+    }
+}
+
+void RegionInitDefaults( LoRaMacRegion_t region, InitType_t type )
+{
+    switch( region )
+    {
+        AS923_INIT_DEFAULTS( );
+        AU915_INIT_DEFAULTS( );
+        CN470_INIT_DEFAULTS( );
+        CN779_INIT_DEFAULTS( );
+        EU433_INIT_DEFAULTS( );
+        EU868_INIT_DEFAULTS( );
+        KR920_INIT_DEFAULTS( );
+        IN865_INIT_DEFAULTS( );
+        US915_INIT_DEFAULTS( );
+        US915_HYBRID_INIT_DEFAULTS( );
+        default:
+        {
+            break;
+        }
+    }
+}
+
+bool RegionVerify( LoRaMacRegion_t region, VerifyParams_t* verify, PhyAttribute_t phyAttribute )
+{
+    switch( region )
+    {
+        AS923_VERIFY( );
+        AU915_VERIFY( );
+        CN470_VERIFY( );
+        CN779_VERIFY( );
+        EU433_VERIFY( );
+        EU868_VERIFY( );
+        KR920_VERIFY( );
+        IN865_VERIFY( );
+        US915_VERIFY( );
+        US915_HYBRID_VERIFY( );
+        default:
+        {
+            return false;
+        }
+    }
+}
+
+void RegionApplyCFList( LoRaMacRegion_t region, ApplyCFListParams_t* applyCFList )
+{
+    switch( region )
+    {
+        AS923_APPLY_CF_LIST( );
+        AU915_APPLY_CF_LIST( );
+        CN470_APPLY_CF_LIST( );
+        CN779_APPLY_CF_LIST( );
+        EU433_APPLY_CF_LIST( );
+        EU868_APPLY_CF_LIST( );
+        KR920_APPLY_CF_LIST( );
+        IN865_APPLY_CF_LIST( );
+        US915_APPLY_CF_LIST( );
+        US915_HYBRID_APPLY_CF_LIST( );
+        default:
+        {
+            break;
+        }
+    }
+}
+
+bool RegionChanMaskSet( LoRaMacRegion_t region, ChanMaskSetParams_t* chanMaskSet )
+{
+    switch( region )
+    {
+        AS923_CHAN_MASK_SET( );
+        AU915_CHAN_MASK_SET( );
+        CN470_CHAN_MASK_SET( );
+        CN779_CHAN_MASK_SET( );
+        EU433_CHAN_MASK_SET( );
+        EU868_CHAN_MASK_SET( );
+        KR920_CHAN_MASK_SET( );
+        IN865_CHAN_MASK_SET( );
+        US915_CHAN_MASK_SET( );
+        US915_HYBRID_CHAN_MASK_SET( );
+        default:
+        {
+            return false;
+        }
+    }
+}
+
+bool RegionAdrNext( LoRaMacRegion_t region, AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter )
+{
+    switch( region )
+    {
+        AS923_ADR_NEXT( );
+        AU915_ADR_NEXT( );
+        CN470_ADR_NEXT( );
+        CN779_ADR_NEXT( );
+        EU433_ADR_NEXT( );
+        EU868_ADR_NEXT( );
+        KR920_ADR_NEXT( );
+        IN865_ADR_NEXT( );
+        US915_ADR_NEXT( );
+        US915_HYBRID_ADR_NEXT( );
+        default:
+        {
+            return false;
+        }
+    }
+}
+
+void RegionComputeRxWindowParameters( LoRaMacRegion_t region, int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams )
+{
+    switch( region )
+    {
+        AS923_COMPUTE_RX_WINDOW_PARAMETERS( );
+        AU915_COMPUTE_RX_WINDOW_PARAMETERS( );
+        CN470_COMPUTE_RX_WINDOW_PARAMETERS( );
+        CN779_COMPUTE_RX_WINDOW_PARAMETERS( );
+        EU433_COMPUTE_RX_WINDOW_PARAMETERS( );
+        EU868_COMPUTE_RX_WINDOW_PARAMETERS( );
+        KR920_COMPUTE_RX_WINDOW_PARAMETERS( );
+        IN865_COMPUTE_RX_WINDOW_PARAMETERS( );
+        US915_COMPUTE_RX_WINDOW_PARAMETERS( );
+        US915_HYBRID_COMPUTE_RX_WINDOW_PARAMETERS( );
+        default:
+        {
+            break;
+        }
+    }
+}
+
+bool RegionRxConfig( LoRaMacRegion_t region, RxConfigParams_t* rxConfig, int8_t* datarate )
+{
+    switch( region )
+    {
+        AS923_RX_CONFIG( );
+        AU915_RX_CONFIG( );
+        CN470_RX_CONFIG( );
+        CN779_RX_CONFIG( );
+        EU433_RX_CONFIG( );
+        EU868_RX_CONFIG( );
+        KR920_RX_CONFIG( );
+        IN865_RX_CONFIG( );
+        US915_RX_CONFIG( );
+        US915_HYBRID_RX_CONFIG( );
+        default:
+        {
+            return false;
+        }
+    }
+}
+
+bool RegionTxConfig( LoRaMacRegion_t region, TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir )
+{
+    switch( region )
+    {
+        AS923_TX_CONFIG( );
+        AU915_TX_CONFIG( );
+        CN470_TX_CONFIG( );
+        CN779_TX_CONFIG( );
+        EU433_TX_CONFIG( );
+        EU868_TX_CONFIG( );
+        KR920_TX_CONFIG( );
+        IN865_TX_CONFIG( );
+        US915_TX_CONFIG( );
+        US915_HYBRID_TX_CONFIG( );
+        default:
+        {
+            return false;
+        }
+    }
+}
+
+uint8_t RegionLinkAdrReq( LoRaMacRegion_t region, LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed )
+{
+    switch( region )
+    {
+        AS923_LINK_ADR_REQ( );
+        AU915_LINK_ADR_REQ( );
+        CN470_LINK_ADR_REQ( );
+        CN779_LINK_ADR_REQ( );
+        EU433_LINK_ADR_REQ( );
+        EU868_LINK_ADR_REQ( );
+        KR920_LINK_ADR_REQ( );
+        IN865_LINK_ADR_REQ( );
+        US915_LINK_ADR_REQ( );
+        US915_HYBRID_LINK_ADR_REQ( );
+        default:
+        {
+            return 0;
+        }
+    }
+}
+
+uint8_t RegionRxParamSetupReq( LoRaMacRegion_t region, RxParamSetupReqParams_t* rxParamSetupReq )
+{
+    switch( region )
+    {
+        AS923_RX_PARAM_SETUP_REQ( );
+        AU915_RX_PARAM_SETUP_REQ( );
+        CN470_RX_PARAM_SETUP_REQ( );
+        CN779_RX_PARAM_SETUP_REQ( );
+        EU433_RX_PARAM_SETUP_REQ( );
+        EU868_RX_PARAM_SETUP_REQ( );
+        KR920_RX_PARAM_SETUP_REQ( );
+        IN865_RX_PARAM_SETUP_REQ( );
+        US915_RX_PARAM_SETUP_REQ( );
+        US915_HYBRID_RX_PARAM_SETUP_REQ( );
+        default:
+        {
+            return 0;
+        }
+    }
+}
+
+uint8_t RegionNewChannelReq( LoRaMacRegion_t region, NewChannelReqParams_t* newChannelReq )
+{
+    switch( region )
+    {
+        AS923_NEW_CHANNEL_REQ( );
+        AU915_NEW_CHANNEL_REQ( );
+        CN470_NEW_CHANNEL_REQ( );
+        CN779_NEW_CHANNEL_REQ( );
+        EU433_NEW_CHANNEL_REQ( );
+        EU868_NEW_CHANNEL_REQ( );
+        KR920_NEW_CHANNEL_REQ( );
+        IN865_NEW_CHANNEL_REQ( );
+        US915_NEW_CHANNEL_REQ( );
+        US915_HYBRID_NEW_CHANNEL_REQ( );
+        default:
+        {
+            return 0;
+        }
+    }
+}
+
+int8_t RegionTxParamSetupReq( LoRaMacRegion_t region, TxParamSetupReqParams_t* txParamSetupReq )
+{
+    switch( region )
+    {
+        AS923_TX_PARAM_SETUP_REQ( );
+        AU915_TX_PARAM_SETUP_REQ( );
+        CN470_TX_PARAM_SETUP_REQ( );
+        CN779_TX_PARAM_SETUP_REQ( );
+        EU433_TX_PARAM_SETUP_REQ( );
+        EU868_TX_PARAM_SETUP_REQ( );
+        KR920_TX_PARAM_SETUP_REQ( );
+        IN865_TX_PARAM_SETUP_REQ( );
+        US915_TX_PARAM_SETUP_REQ( );
+        US915_HYBRID_TX_PARAM_SETUP_REQ( );
+        default:
+        {
+            return 0;
+        }
+    }
+}
+
+uint8_t RegionDlChannelReq( LoRaMacRegion_t region, DlChannelReqParams_t* dlChannelReq )
+{
+    switch( region )
+    {
+        AS923_DL_CHANNEL_REQ( );
+        AU915_DL_CHANNEL_REQ( );
+        CN470_DL_CHANNEL_REQ( );
+        CN779_DL_CHANNEL_REQ( );
+        EU433_DL_CHANNEL_REQ( );
+        EU868_DL_CHANNEL_REQ( );
+        KR920_DL_CHANNEL_REQ( );
+        IN865_DL_CHANNEL_REQ( );
+        US915_DL_CHANNEL_REQ( );
+        US915_HYBRID_DL_CHANNEL_REQ( );
+        default:
+        {
+            return 0;
+        }
+    }
+}
+
+int8_t RegionAlternateDr( LoRaMacRegion_t region, AlternateDrParams_t* alternateDr )
+{
+    switch( region )
+    {
+        AS923_ALTERNATE_DR( );
+        AU915_ALTERNATE_DR( );
+        CN470_ALTERNATE_DR( );
+        CN779_ALTERNATE_DR( );
+        EU433_ALTERNATE_DR( );
+        EU868_ALTERNATE_DR( );
+        KR920_ALTERNATE_DR( );
+        IN865_ALTERNATE_DR( );
+        US915_ALTERNATE_DR( );
+        US915_HYBRID_ALTERNATE_DR( );
+        default:
+        {
+            return 0;
+        }
+    }
+}
+
+void RegionCalcBackOff( LoRaMacRegion_t region, CalcBackOffParams_t* calcBackOff )
+{
+    switch( region )
+    {
+        AS923_CALC_BACKOFF( );
+        AU915_CALC_BACKOFF( );
+        CN470_CALC_BACKOFF( );
+        CN779_CALC_BACKOFF( );
+        EU433_CALC_BACKOFF( );
+        EU868_CALC_BACKOFF( );
+        KR920_CALC_BACKOFF( );
+        IN865_CALC_BACKOFF( );
+        US915_CALC_BACKOFF( );
+        US915_HYBRID_CALC_BACKOFF( );
+        default:
+        {
+            break;
+        }
+    }
+}
+
+bool RegionNextChannel( LoRaMacRegion_t region, NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff )
+{
+    switch( region )
+    {
+        AS923_NEXT_CHANNEL( );
+        AU915_NEXT_CHANNEL( );
+        CN470_NEXT_CHANNEL( );
+        CN779_NEXT_CHANNEL( );
+        EU433_NEXT_CHANNEL( );
+        EU868_NEXT_CHANNEL( );
+        KR920_NEXT_CHANNEL( );
+        IN865_NEXT_CHANNEL( );
+        US915_NEXT_CHANNEL( );
+        US915_HYBRID_NEXT_CHANNEL( );
+        default:
+        {
+            return false;
+        }
+    }
+}
+
+LoRaMacStatus_t RegionChannelAdd( LoRaMacRegion_t region, ChannelAddParams_t* channelAdd )
+{
+    switch( region )
+    {
+        AS923_CHANNEL_ADD( );
+        AU915_CHANNEL_ADD( );
+        CN470_CHANNEL_ADD( );
+        CN779_CHANNEL_ADD( );
+        EU433_CHANNEL_ADD( );
+        EU868_CHANNEL_ADD( );
+        KR920_CHANNEL_ADD( );
+        IN865_CHANNEL_ADD( );
+        US915_CHANNEL_ADD( );
+        US915_HYBRID_CHANNEL_ADD( );
+        default:
+        {
+            return LORAMAC_STATUS_PARAMETER_INVALID;
+        }
+    }
+}
+
+bool RegionChannelsRemove( LoRaMacRegion_t region, ChannelRemoveParams_t* channelRemove )
+{
+    switch( region )
+    {
+        AS923_CHANNEL_REMOVE( );
+        AU915_CHANNEL_REMOVE( );
+        CN470_CHANNEL_REMOVE( );
+        CN779_CHANNEL_REMOVE( );
+        EU433_CHANNEL_REMOVE( );
+        EU868_CHANNEL_REMOVE( );
+        KR920_CHANNEL_REMOVE( );
+        IN865_CHANNEL_REMOVE( );
+        US915_CHANNEL_REMOVE( );
+        US915_HYBRID_CHANNEL_REMOVE( );
+        default:
+        {
+            return false;
+        }
+    }
+}
+
+void RegionSetContinuousWave( LoRaMacRegion_t region, ContinuousWaveParams_t* continuousWave )
+{
+    switch( region )
+    {
+        AS923_SET_CONTINUOUS_WAVE( );
+        AU915_SET_CONTINUOUS_WAVE( );
+        CN470_SET_CONTINUOUS_WAVE( );
+        CN779_SET_CONTINUOUS_WAVE( );
+        EU433_SET_CONTINUOUS_WAVE( );
+        EU868_SET_CONTINUOUS_WAVE( );
+        KR920_SET_CONTINUOUS_WAVE( );
+        IN865_SET_CONTINUOUS_WAVE( );
+        US915_SET_CONTINUOUS_WAVE( );
+        US915_HYBRID_SET_CONTINUOUS_WAVE( );
+        default:
+        {
+            break;
+        }
+    }
+}
+
+uint8_t RegionApplyDrOffset( LoRaMacRegion_t region, uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset )
+{
+    switch( region )
+    {
+        AS923_APPLY_DR_OFFSET( );
+        AU915_APPLY_DR_OFFSET( );
+        CN470_APPLY_DR_OFFSET( );
+        CN779_APPLY_DR_OFFSET( );
+        EU433_APPLY_DR_OFFSET( );
+        EU868_APPLY_DR_OFFSET( );
+        KR920_APPLY_DR_OFFSET( );
+        IN865_APPLY_DR_OFFSET( );
+        US915_APPLY_DR_OFFSET( );
+        US915_HYBRID_APPLY_DR_OFFSET( );
+        default:
+        {
+            return dr;
+        }
+    }
+}
+
+void RegionRxBeaconSetup( LoRaMacRegion_t region, RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr, bool *beaconChannelSet )
+{
+    switch( region )
+    {
+        AS923_RX_BEACON_SETUP( );
+        AU915_RX_BEACON_SETUP( );
+        CN470_RX_BEACON_SETUP( );
+        CN779_RX_BEACON_SETUP( );
+        EU433_RX_BEACON_SETUP( );
+        EU868_RX_BEACON_SETUP( );
+        KR920_RX_BEACON_SETUP( );
+        IN865_RX_BEACON_SETUP( );
+        US915_RX_BEACON_SETUP( );
+        US915_HYBRID_RX_BEACON_SETUP( );
+        default:
+        {
+            break;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/region/Region.h	Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,1612 @@
+/*!
+ * \file      Region.h
+ *
+ * \brief     Region implementation.
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ *                ______                              _
+ *               / _____)             _              | |
+ *              ( (____  _____ ____ _| |_ _____  ____| |__
+ *               \____ \| ___ |    (_   _) ___ |/ ___)  _ \
+ *               _____) ) ____| | | || |_| ____( (___| | | |
+ *              (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ *              (C)2013 Semtech
+ *
+ *               ___ _____ _   ___ _  _____ ___  ___  ___ ___
+ *              / __|_   _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ *              \__ \ | |/ _ \ (__| ' <| _| (_) |   / (__| _|
+ *              |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ *              embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author    Miguel Luis ( Semtech )
+ *
+ * \author    Gregory Cristian ( Semtech )
+ *
+ * \author    Daniel Jaeckle ( STACKFORCE )
+ *
+ * \defgroup  REGION Region implementation
+ *            This is the common API to access the specific
+ *            regional implementations.
+ *
+ *            Preprocessor options:
+ *            - LoRaWAN regions can be activated by defining the related preprocessor
+ *              definition. It is possible to define more than one region.
+ *              The following regions are supported:
+ *              - #define REGION_AS923
+ *              - #define REGION_AU915
+ *              - #define REGION_CN470
+ *              - #define REGION_CN779
+ *              - #define REGION_EU433
+ *              - #define REGION_EU868
+ *              - #define REGION_KR920
+ *              - #define REGION_IN865
+ *              - #define REGION_US915
+ *              - #define REGION_US915_HYBRID
+ *
+ * \{
+ */
+#ifndef __REGION_H__
+#define __REGION_H__
+
+
+
+
+/*!
+ * Macro to compute bit of a channel index.
+ */
+#define LC( channelIndex )                          ( uint16_t )( 1 << ( channelIndex - 1 ) )
+
+/*!
+ * Region       | SF
+ * ------------ | :-----:
+ * AS923        | SF12 - BW125
+ * AU915        | SF10 - BW125
+ * CN470        | SF12 - BW125
+ * CN779        | SF12 - BW125
+ * EU433        | SF12 - BW125
+ * EU868        | SF12 - BW125
+ * IN865        | SF12 - BW125
+ * KR920        | SF12 - BW125
+ * US915        | SF10 - BW125
+ * US915_HYBRID | SF10 - BW125
+ */
+#define DR_0                                        0
+
+/*!
+ * Region       | SF
+ * ------------ | :-----:
+ * AS923        | SF11 - BW125
+ * AU915        | SF9  - BW125
+ * CN470        | SF11 - BW125
+ * CN779        | SF11 - BW125
+ * EU433        | SF11 - BW125
+ * EU868        | SF11 - BW125
+ * IN865        | SF11 - BW125
+ * KR920        | SF11 - BW125
+ * US915        | SF9  - BW125
+ * US915_HYBRID | SF9  - BW125
+ */
+#define DR_1                                        1
+
+/*!
+ * Region       | SF
+ * ------------ | :-----:
+ * AS923        | SF10 - BW125
+ * AU915        | SF8  - BW125
+ * CN470        | SF10 - BW125
+ * CN779        | SF10 - BW125
+ * EU433        | SF10 - BW125
+ * EU868        | SF10 - BW125
+ * IN865        | SF10 - BW125
+ * KR920        | SF10 - BW125
+ * US915        | SF8  - BW125
+ * US915_HYBRID | SF8  - BW125
+ */
+#define DR_2                                        2
+
+/*!
+ * Region       | SF
+ * ------------ | :-----:
+ * AS923        | SF9  - BW125
+ * AU915        | SF7  - BW125
+ * CN470        | SF9  - BW125
+ * CN779        | SF9  - BW125
+ * EU433        | SF9  - BW125
+ * EU868        | SF9  - BW125
+ * IN865        | SF9  - BW125
+ * KR920        | SF9  - BW125
+ * US915        | SF7  - BW125
+ * US915_HYBRID | SF7  - BW125
+ */
+#define DR_3                                        3
+
+/*!
+ * Region       | SF
+ * ------------ | :-----:
+ * AS923        | SF8  - BW125
+ * AU915        | SF8  - BW500
+ * CN470        | SF8  - BW125
+ * CN779        | SF8  - BW125
+ * EU433        | SF8  - BW125
+ * EU868        | SF8  - BW125
+ * IN865        | SF8  - BW125
+ * KR920        | SF8  - BW125
+ * US915        | SF8  - BW500
+ * US915_HYBRID | SF8  - BW500
+ */
+#define DR_4                                        4
+
+/*!
+ * Region       | SF
+ * ------------ | :-----:
+ * AS923        | SF7  - BW125
+ * AU915        | RFU
+ * CN470        | SF7  - BW125
+ * CN779        | SF7  - BW125
+ * EU433        | SF7  - BW125
+ * EU868        | SF7  - BW125
+ * IN865        | SF7  - BW125
+ * KR920        | SF7  - BW125
+ * US915        | RFU
+ * US915_HYBRID | RFU
+ */
+#define DR_5                                        5
+
+/*!
+ * Region       | SF
+ * ------------ | :-----:
+ * AS923        | SF7  - BW250
+ * AU915        | RFU
+ * CN470        | SF12 - BW125
+ * CN779        | SF7  - BW250
+ * EU433        | SF7  - BW250
+ * EU868        | SF7  - BW250
+ * IN865        | SF7  - BW250
+ * KR920        | RFU
+ * US915        | RFU
+ * US915_HYBRID | RFU
+ */
+#define DR_6                                        6
+
+/*!
+ * Region       | SF
+ * ------------ | :-----:
+ * AS923        | FSK
+ * AU915        | RFU
+ * CN470        | SF12 - BW125
+ * CN779        | FSK
+ * EU433        | FSK
+ * EU868        | FSK
+ * IN865        | FSK
+ * KR920        | RFU
+ * US915        | RFU
+ * US915_HYBRID | RFU
+ */
+#define DR_7                                        7
+
+/*!
+ * Region       | SF
+ * ------------ | :-----:
+ * AS923        | RFU
+ * AU915        | SF12 - BW500
+ * CN470        | RFU
+ * CN779        | RFU
+ * EU433        | RFU
+ * EU868        | RFU
+ * IN865        | RFU
+ * KR920        | RFU
+ * US915        | SF12 - BW500
+ * US915_HYBRID | SF12 - BW500
+ */
+#define DR_8                                        8
+
+/*!
+ * Region       | SF
+ * ------------ | :-----:
+ * AS923        | RFU
+ * AU915        | SF11 - BW500
+ * CN470        | RFU
+ * CN779        | RFU
+ * EU433        | RFU
+ * EU868        | RFU
+ * IN865        | RFU
+ * KR920        | RFU
+ * US915        | SF11 - BW500
+ * US915_HYBRID | SF11 - BW500
+ */
+#define DR_9                                        9
+
+/*!
+ * Region       | SF
+ * ------------ | :-----:
+ * AS923        | RFU
+ * AU915        | SF10 - BW500
+ * CN470        | RFU
+ * CN779        | RFU
+ * EU433        | RFU
+ * EU868        | RFU
+ * IN865        | RFU
+ * KR920        | RFU
+ * US915        | SF10 - BW500
+ * US915_HYBRID | SF10 - BW500
+ */
+#define DR_10                                       10
+
+/*!
+ * Region       | SF
+ * ------------ | :-----:
+ * AS923        | RFU
+ * AU915        | SF9  - BW500
+ * CN470        | RFU
+ * CN779        | RFU
+ * EU433        | RFU
+ * EU868        | RFU
+ * IN865        | RFU
+ * KR920        | RFU
+ * US915        | SF9  - BW500
+ * US915_HYBRID | SF9  - BW500
+ */
+#define DR_11                                       11
+
+/*!
+ * Region       | SF
+ * ------------ | :-----:
+ * AS923        | RFU
+ * AU915        | SF8  - BW500
+ * CN470        | RFU
+ * CN779        | RFU
+ * EU433        | RFU
+ * EU868        | RFU
+ * IN865        | RFU
+ * KR920        | RFU
+ * US915        | SF8  - BW500
+ * US915_HYBRID | SF8  - BW500
+ */
+#define DR_12                                       12
+
+/*!
+ * Region       | SF
+ * ------------ | :-----:
+ * AS923        | RFU
+ * AU915        | SF7  - BW500
+ * CN470        | RFU
+ * CN779        | RFU
+ * EU433        | RFU
+ * EU868        | RFU
+ * IN865        | RFU
+ * KR920        | RFU
+ * US915        | SF7  - BW500
+ * US915_HYBRID | SF7  - BW500
+ */
+#define DR_13                                       13
+
+/*!
+ * Region       | SF
+ * ------------ | :-----:
+ * AS923        | RFU
+ * AU915        | RFU
+ * CN470        | RFU
+ * CN779        | RFU
+ * EU433        | RFU
+ * EU868        | RFU
+ * IN865        | RFU
+ * KR920        | RFU
+ * US915        | RFU
+ * US915_HYBRID | RFU
+ */
+#define DR_14                                       14
+
+/*!
+ * Region       | SF
+ * ------------ | :-----:
+ * AS923        | RFU
+ * AU915        | RFU
+ * CN470        | RFU
+ * CN779        | RFU
+ * EU433        | RFU
+ * EU868        | RFU
+ * IN865        | RFU
+ * KR920        | RFU
+ * US915        | RFU
+ * US915_HYBRID | RFU
+ */
+#define DR_15                                       15
+
+
+
+/*!
+ * Region       | dBM
+ * ------------ | :-----:
+ * AS923        | Max EIRP
+ * AU915        | Max EIRP
+ * CN470        | Max EIRP
+ * CN779        | Max EIRP
+ * EU433        | Max EIRP
+ * EU868        | Max EIRP
+ * IN865        | Max EIRP
+ * KR920        | Max EIRP
+ * US915        | Max ERP
+ * US915_HYBRID | Max ERP
+ */
+#define TX_POWER_0                                  0
+
+/*!
+ * Region       | dBM
+ * ------------ | :-----:
+ * AS923        | Max EIRP - 2
+ * AU915        | Max EIRP - 2
+ * CN470        | Max EIRP - 2
+ * CN779        | Max EIRP - 2
+ * EU433        | Max EIRP - 2
+ * EU868        | Max EIRP - 2
+ * IN865        | Max EIRP - 2
+ * KR920        | Max EIRP - 2
+ * US915        | Max ERP - 2
+ * US915_HYBRID | Max ERP - 2
+ */
+#define TX_POWER_1                                  1
+
+/*!
+ * Region       | dBM
+ * ------------ | :-----:
+ * AS923        | Max EIRP - 4
+ * AU915        | Max EIRP - 4
+ * CN470        | Max EIRP - 4
+ * CN779        | Max EIRP - 4
+ * EU433        | Max EIRP - 4
+ * EU868        | Max EIRP - 4
+ * IN865        | Max EIRP - 4
+ * KR920        | Max EIRP - 4
+ * US915        | Max ERP - 4
+ * US915_HYBRID | Max ERP - 4
+ */
+#define TX_POWER_2                                  2
+
+/*!
+ * Region       | dBM
+ * ------------ | :-----:
+ * AS923        | Max EIRP - 6
+ * AU915        | Max EIRP - 6
+ * CN470        | Max EIRP - 6
+ * CN779        | Max EIRP - 6
+ * EU433        | Max EIRP - 6
+ * EU868        | Max EIRP - 6
+ * IN865        | Max EIRP - 6
+ * KR920        | Max EIRP - 6
+ * US915        | Max ERP - 6
+ * US915_HYBRID | Max ERP - 6
+ */
+#define TX_POWER_3                                  3
+
+/*!
+ * Region       | dBM
+ * ------------ | :-----:
+ * AS923        | Max EIRP - 8
+ * AU915        | Max EIRP - 8
+ * CN470        | Max EIRP - 8
+ * CN779        | Max EIRP - 8
+ * EU433        | Max EIRP - 8
+ * EU868        | Max EIRP - 8
+ * IN865        | Max EIRP - 8
+ * KR920        | Max EIRP - 8
+ * US915        | Max ERP - 8
+ * US915_HYBRID | Max ERP - 8
+ */
+#define TX_POWER_4                                  4
+
+/*!
+ * Region       | dBM
+ * ------------ | :-----:
+ * AS923        | Max EIRP - 10
+ * AU915        | Max EIRP - 10
+ * CN470        | Max EIRP - 10
+ * CN779        | Max EIRP - 10
+ * EU433        | Max EIRP - 10
+ * EU868        | Max EIRP - 10
+ * IN865        | Max EIRP - 10
+ * KR920        | Max EIRP - 10
+ * US915        | Max ERP - 10
+ * US915_HYBRID | Max ERP - 10
+ */
+#define TX_POWER_5                                  5
+
+/*!
+ * Region       | dBM
+ * ------------ | :-----:
+ * AS923        | Max EIRP - 12
+ * AU915        | Max EIRP - 12
+ * CN470        | Max EIRP - 12
+ * CN779        | -
+ * EU433        | -
+ * EU868        | Max EIRP - 12
+ * IN865        | Max EIRP - 12
+ * KR920        | Max EIRP - 12
+ * US915        | Max ERP - 12
+ * US915_HYBRID | Max ERP - 12
+ */
+#define TX_POWER_6                                  6
+
+/*!
+ * Region       | dBM
+ * ------------ | :-----:
+ * AS923        | Max EIRP - 14
+ * AU915        | Max EIRP - 14
+ * CN470        | Max EIRP - 14
+ * CN779        | -
+ * EU433        | -
+ * EU868        | Max EIRP - 14
+ * IN865        | Max EIRP - 14
+ * KR920        | Max EIRP - 14
+ * US915        | Max ERP - 14
+ * US915_HYBRID | Max ERP - 14
+ */
+#define TX_POWER_7                                  7
+
+/*!
+ * Region       | dBM
+ * ------------ | :-----:
+ * AS923        | -
+ * AU915        | Max EIRP - 16
+ * CN470        | -
+ * CN779        | -
+ * EU433        | -
+ * EU868        | -
+ * IN865        | Max EIRP - 16
+ * KR920        | -
+ * US915        | Max ERP - 16
+ * US915_HYBRID | Max ERP -16
+ */
+#define TX_POWER_8                                  8
+
+/*!
+ * Region       | dBM
+ * ------------ | :-----:
+ * AS923        | -
+ * AU915        | Max EIRP - 18
+ * CN470        | -
+ * CN779        | -
+ * EU433        | -
+ * EU868        | -
+ * IN865        | Max EIRP - 18
+ * KR920        | -
+ * US915        | Max ERP - 16
+ * US915_HYBRID | Max ERP - 16
+ */
+#define TX_POWER_9                                  9
+
+/*!
+ * Region       | dBM
+ * ------------ | :-----:
+ * AS923        | -
+ * AU915        | Max EIRP - 20
+ * CN470        | -
+ * CN779        | -
+ * EU433        | -
+ * EU868        | -
+ * IN865        | Max EIRP - 20
+ * KR920        | -
+ * US915        | Max ERP - 10
+ * US915_HYBRID | Max ERP - 10
+ */
+#define TX_POWER_10                                 10
+
+/*!
+ * RFU
+ */
+#define TX_POWER_11                                 11
+
+/*!
+ * RFU
+ */
+#define TX_POWER_12                                 12
+
+/*!
+ * RFU
+ */
+#define TX_POWER_13                                 13
+
+/*!
+ * RFU
+ */
+#define TX_POWER_14                                 14
+
+/*!
+ * RFU
+ */
+#define TX_POWER_15                                 15
+
+
+
+/*!
+ * Enumeration of phy attributes.
+ */
+typedef enum ePhyAttribute
+{
+    /*!
+     * Minimum RX datarate.
+     */
+    PHY_MIN_RX_DR,
+    /*!
+     * Minimum TX datarate.
+     */
+    PHY_MIN_TX_DR,
+    /*!
+     * Maximum RX datarate.
+     */
+    PHY_MAX_RX_DR,
+    /*!
+     * Maximum TX datarate.
+     */
+    PHY_MAX_TX_DR,
+    /*!
+     * TX datarate.
+     */
+    PHY_TX_DR,
+    /*!
+     * Default TX datarate.
+     */
+    PHY_DEF_TX_DR,
+    /*!
+     * RX datarate.
+     */
+    PHY_RX_DR,
+    /*!
+     * TX power.
+     */
+    PHY_TX_POWER,
+    /*!
+     * Default TX power.
+     */
+    PHY_DEF_TX_POWER,
+    /*!
+     * Maximum payload possible.
+     */
+    PHY_MAX_PAYLOAD,
+    /*!
+     * Maximum payload possible when repeater support is enabled.
+     */
+    PHY_MAX_PAYLOAD_REPEATER,
+    /*!
+     * Duty cycle.
+     */
+    PHY_DUTY_CYCLE,
+    /*!
+     * Maximum receive window duration.
+     */
+    PHY_MAX_RX_WINDOW,
+    /*!
+     * Receive delay for window 1.
+     */
+    PHY_RECEIVE_DELAY1,
+    /*!
+     * Receive delay for window 2.
+     */
+    PHY_RECEIVE_DELAY2,
+    /*!
+     * Join accept delay for window 1.
+     */
+    PHY_JOIN_ACCEPT_DELAY1,
+    /*!
+     * Join accept delay for window 2.
+     */
+    PHY_JOIN_ACCEPT_DELAY2,
+    /*!
+     * Maximum frame counter gap.
+     */
+    PHY_MAX_FCNT_GAP,
+    /*!
+     * Acknowledgement time out.
+     */
+    PHY_ACK_TIMEOUT,
+    /*!
+     * Default datarate offset for window 1.
+     */
+    PHY_DEF_DR1_OFFSET,
+    /*!
+     * Default receive window 2 frequency.
+     */
+    PHY_DEF_RX2_FREQUENCY,
+    /*!
+     * Default receive window 2 datarate.
+     */
+    PHY_DEF_RX2_DR,
+    /*!
+     * Channels mask.
+     */
+    PHY_CHANNELS_MASK,
+    /*!
+     * Channels default mask.
+     */
+    PHY_CHANNELS_DEFAULT_MASK,
+    /*!
+     * Maximum number of supported channels
+     */
+    PHY_MAX_NB_CHANNELS,
+    /*!
+     * Channels.
+     */
+    PHY_CHANNELS,
+    /*!
+     * Default value of the uplink dwell time.
+     */
+    PHY_DEF_UPLINK_DWELL_TIME,
+    /*!
+     * Default value of the downlink dwell time.
+     */
+    PHY_DEF_DOWNLINK_DWELL_TIME,
+    /*!
+     * Default value of the MaxEIRP.
+     */
+    PHY_DEF_MAX_EIRP,
+    /*!
+     * Default value of the antenna gain.
+     */
+    PHY_DEF_ANTENNA_GAIN,
+    /*!
+     * Value for the number of join trials.
+     */
+    PHY_NB_JOIN_TRIALS,
+    /*!
+     * Next lower datarate.
+     */
+    PHY_NEXT_LOWER_TX_DR,
+    /*!
+     * Default value for the number of join trials.
+     */
+    PHY_DEF_NB_JOIN_TRIALS,
+    /*!
+     * Beacon interval in ms.
+     */
+    PHY_BEACON_INTERVAL,
+    /*!
+     * Beacon reserved time in ms.
+     */
+    PHY_BEACON_RESERVED,
+    /*!
+     * Beacon guard time in ms.
+     */
+    PHY_BEACON_GUARD,
+    /*!
+     * Beacon window time in ms.
+     */
+    PHY_BEACON_WINDOW,
+    /*!
+     * Beacon window time in numer of slots.
+     */
+    PHY_BEACON_WINDOW_SLOTS,
+    /*!
+     * Ping slot length time in ms.
+     */
+    PHY_PING_SLOT_WINDOW,
+    /*!
+     * Default symbol timeout for beacons and ping slot windows.
+     */
+    PHY_BEACON_SYMBOL_TO_DEFAULT,
+    /*!
+     * Maximum symbol timeout for beacons.
+     */
+    PHY_BEACON_SYMBOL_TO_EXPANSION_MAX,
+    /*!
+     * Maximum symbol timeout for ping slots.
+     */
+    PHY_PING_SLOT_SYMBOL_TO_EXPANSION_MAX,
+    /*!
+     * Symbol expansion value for beacon windows in case of beacon
+     * loss in symbols.
+     */
+    PHY_BEACON_SYMBOL_TO_EXPANSION_FACTOR,
+    /*!
+     * Symbol expansion value for ping slot windows in case of beacon
+     * loss in symbols.
+     */
+    PHY_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR,
+    /*!
+     * Maximum allowed beacon less time in ms.
+     */
+    PHY_MAX_BEACON_LESS_PERIOD,
+    /*!
+     * Delay time for the BeaconTimingAns in ms.
+     */
+    PHY_BEACON_DELAY_BEACON_TIMING_ANS,
+    /*!
+     * Delay time for the BeaconTimingAns in ms.
+     */
+    PHY_BEACON_CHANNEL_FREQ,
+    /*!
+     * Delay time for the BeaconTimingAns in ms.
+     */
+    PHY_BEACON_CHANNEL_FREQ_IDX,
+    /*!
+     * Delay time for the BeaconTimingAns in ms.
+     */
+    PHY_PINGSLOT_CHANNEL_FREQ,
+    PHY_BEACON_SIZE,
+    PHY_BEACON_CHANNEL_DR,
+}PhyAttribute_t;
+
+/*!
+ * Enumeration of initialization types.
+ */
+typedef enum eInitType
+{
+    /*!
+     * Performs an initialization and overwrites all existing data.
+     */
+    INIT_TYPE_INIT,
+    /*!
+     * Restores default channels only.
+     */
+    INIT_TYPE_RESTORE
+}InitType_t;
+
+typedef enum eChannelsMask
+{
+    /*!
+     * The channels mask.
+     */
+    CHANNELS_MASK,
+    /*!
+     * The channels default mask.
+     */
+    CHANNELS_DEFAULT_MASK
+}ChannelsMask_t;
+
+/*!
+ * Union for the structure uGetPhyParams
+ */
+typedef union uPhyParam
+{
+    /*!
+     * A parameter value.
+     */
+    uint32_t Value;
+    /*!
+     * A floating point value.
+     */
+    float fValue;
+    /*!
+     * Pointer to the channels mask.
+     */
+    uint16_t* ChannelsMask;
+    /*!
+     * Pointer to the channels.
+     */
+    ChannelParams_t* Channels;
+}PhyParam_t;
+
+/*!
+ * Parameter structure for the function RegionGetPhyParam.
+ */
+typedef struct sGetPhyParams
+{
+    /*!
+     * Setup the parameter to get.
+     */
+    PhyAttribute_t Attribute;
+    /*!
+     * Datarate.
+     * The parameter is needed for the following queries:
+     * PHY_MAX_PAYLOAD, PHY_MAX_PAYLOAD_REPEATER, PHY_NEXT_LOWER_TX_DR.
+     */
+    int8_t Datarate;
+    /*!
+     * Uplink dwell time. This parameter must be set to query:
+     * PHY_MAX_PAYLOAD, PHY_MAX_PAYLOAD_REPEATER, PHY_MIN_TX_DR.
+     * The parameter is needed for the following queries:
+     * PHY_MIN_TX_DR, PHY_MAX_PAYLOAD, PHY_MAX_PAYLOAD_REPEATER, PHY_NEXT_LOWER_TX_DR.
+     */
+    uint8_t UplinkDwellTime;
+    /*!
+     * Downlink dwell time.This parameter must be set to query:
+     * PHY_MAX_PAYLOAD, PHY_MAX_PAYLOAD_REPEATER, PHY_MIN_RX_DR.
+     * The parameter is needed for the following queries:
+     * PHY_MIN_RX_DR, PHY_MAX_PAYLOAD, PHY_MAX_PAYLOAD_REPEATER.
+     */
+    uint8_t DownlinkDwellTime;
+    /*!
+     * LoRaMAC Device Address.This parameter must be set to query:
+     * PHY_PINGSLOT_CHANNEL_FREQ.
+     */
+    uint32_t DeviceAddress;
+}GetPhyParams_t;
+
+/*!
+ * Parameter structure for the function RegionSetBandTxDone.
+ */
+typedef struct sSetBandTxDoneParams
+{
+    /*!
+     * Channel to update.
+     */
+    uint8_t Channel;
+    /*!
+     * Joined Set to true, if the node has joined the network
+     */
+    bool Joined;
+    /*!
+     * Last TX done time.
+     */
+    TimerTime_t LastTxDoneTime;
+}SetBandTxDoneParams_t;
+
+/*!
+ * Parameter structure for the function RegionVerify.
+ */
+typedef union uVerifyParams
+{
+    /*!
+     * TX power to verify.
+     */
+    int8_t TxPower;
+    /*!
+     * Set to true, if the duty cycle is enabled, otherwise false.
+     */
+    bool DutyCycle;
+    /*!
+     * The number of join trials.
+     */
+    uint8_t NbJoinTrials;
+    /*!
+     * Datarate to verify.
+     */
+    struct sDatarateParams
+    {
+        /*!
+        * Datarate to verify.
+        */
+        int8_t Datarate;
+        /*!
+        * The downlink dwell time.
+        */
+        uint8_t DownlinkDwellTime;
+        /*!
+        * The up link dwell time.
+        */
+        uint8_t UplinkDwellTime;
+    }DatarateParams;
+}VerifyParams_t;
+
+/*!
+ * Parameter structure for the function RegionApplyCFList.
+ */
+typedef struct sApplyCFListParams
+{
+    /*!
+     * Payload which contains the CF list.
+     */
+    uint8_t* Payload;
+    /*!
+     * Size of the payload.
+     */
+    uint8_t Size;
+}ApplyCFListParams_t;
+
+/*!
+ * Parameter structure for the function RegionChanMaskSet.
+ */
+typedef struct sChanMaskSetParams
+{
+    /*!
+     * Pointer to the channels mask which should be set.
+     */
+    uint16_t* ChannelsMaskIn;
+    /*!
+     * Pointer to the channels mask which should be set.
+     */
+    ChannelsMask_t ChannelsMaskType;
+}ChanMaskSetParams_t;
+
+/*!
+ * Parameter structure for the function RegionAdrNext.
+ */
+typedef struct sAdrNextParams
+{
+    /*!
+     * Set to true, if the function should update the channels mask.
+     */
+    bool UpdateChanMask;
+    /*!
+     * Set to true, if ADR is enabled.
+     */
+    bool AdrEnabled;
+    /*!
+     * ADR ack counter.
+     */
+    uint32_t AdrAckCounter;
+    /*!
+     * Datarate used currently.
+     */
+    int8_t Datarate;
+    /*!
+     * TX power used currently.
+     */
+    int8_t TxPower;
+    /*!
+     * UplinkDwellTime
+     */
+    uint8_t UplinkDwellTime;
+}AdrNextParams_t;
+
+/*!
+ * Parameter structure for the function RegionRxConfig.
+ */
+typedef struct sRxConfigParams
+{
+    /*!
+     * The RX channel.
+     */
+    uint8_t Channel;
+    /*!
+     * RX datarate.
+     */
+    int8_t Datarate;
+    /*!
+     * RX bandwidth.
+     */
+    uint8_t Bandwidth;
+    /*!
+     * RX datarate offset.
+     */
+    int8_t DrOffset;
+    /*!
+     * RX frequency.
+     */
+    uint32_t Frequency;
+    /*!
+     * RX window timeout
+     */
+     uint32_t WindowTimeout;
+    /*!
+     * RX window offset
+     */
+    int32_t WindowOffset;
+    /*!
+     * Downlink dwell time.
+     */
+    uint8_t DownlinkDwellTime;
+    /*!
+     * Set to true, if a repeater is supported.
+     */
+    bool RepeaterSupport;
+    /*!
+     * Set to true, if RX should be continuous.
+     */
+    bool RxContinuous;
+    /*!
+     * Sets the RX window. 0: RX window 1, 1: RX window 2.
+     */
+    bool Window;
+}RxConfigParams_t;
+
+/*!
+ * Parameter structure for the function RegionTxConfig.
+ */
+typedef struct sTxConfigParams
+{
+    /*!
+     * The TX channel.
+     */
+    uint8_t Channel;
+    /*!
+     * The TX datarate.
+     */
+    int8_t Datarate;
+    /*!
+     * The TX power.
+     */
+    int8_t TxPower;
+    /*!
+     * The Max EIRP, if applicable.
+     */
+    float MaxEirp;
+    /*!
+     * The antenna gain, if applicable.
+     */
+    float AntennaGain;
+    /*!
+     * Frame length to setup.
+     */
+    uint16_t PktLen;
+}TxConfigParams_t;
+
+/*!
+ * Parameter structure for the function RegionLinkAdrReq.
+ */
+typedef struct sLinkAdrReqParams
+{
+    /*!
+     * Pointer to the payload which contains the MAC commands.
+     */
+    uint8_t* Payload;
+    /*!
+     * Size of the payload.
+     */
+    uint8_t PayloadSize;
+    /*!
+     * Uplink dwell time.
+     */
+    uint8_t UplinkDwellTime;
+    /*!
+     * Set to true, if ADR is enabled.
+     */
+    bool AdrEnabled;
+    /*!
+     * The current datarate.
+     */
+    int8_t CurrentDatarate;
+    /*!
+     * The current TX power.
+     */
+    int8_t CurrentTxPower;
+    /*!
+     * The current number of repetitions.
+     */
+    uint8_t CurrentNbRep;
+}LinkAdrReqParams_t;
+
+/*!
+ * Parameter structure for the function RegionRxParamSetupReq.
+ */
+typedef struct sRxParamSetupReqParams
+{
+    /*!
+     * The datarate to setup.
+     */
+    int8_t Datarate;
+    /*!
+     * Datarate offset.
+     */
+    int8_t DrOffset;
+    /*!
+     * The frequency to setup.
+     */
+    uint32_t Frequency;
+}RxParamSetupReqParams_t;
+
+/*!
+ * Parameter structure for the function RegionNewChannelReq.
+ */
+typedef struct sNewChannelReqParams
+{
+    /*!
+     * Pointer to the new channels.
+     */
+    ChannelParams_t* NewChannel;
+    /*!
+     * Channel id.
+     */
+    int8_t ChannelId;
+}NewChannelReqParams_t;
+
+/*!
+ * Parameter structure for the function RegionTxParamSetupReq.
+ */
+typedef struct sTxParamSetupReqParams
+{
+    /*!
+     * Uplink dwell time.
+     */
+    uint8_t UplinkDwellTime;
+    /*!
+     * Downlink dwell time.
+     */
+    uint8_t DownlinkDwellTime;
+    /*!
+     * Max EIRP.
+     */
+    uint8_t MaxEirp;
+}TxParamSetupReqParams_t;
+
+/*!
+ * Parameter structure for the function RegionDlChannelReq.
+ */
+typedef struct sDlChannelReqParams
+{
+    /*!
+     * Channel Id to add the frequency.
+     */
+    uint8_t ChannelId;
+    /*!
+     * Alternative frequency for the Rx1 window.
+     */
+    uint32_t Rx1Frequency;
+}DlChannelReqParams_t;
+
+/*!
+ * Parameter structure for the function RegionAlternateDr.
+ */
+typedef struct sAlternateDrParams
+{
+    /*!
+     * Number of trials.
+     */
+    uint16_t NbTrials;
+}AlternateDrParams_t;
+
+/*!
+ * Parameter structure for the function RegionCalcBackOff.
+ */
+typedef struct sCalcBackOffParams
+{
+    /*!
+     * Set to true, if the node has already joined a network, otherwise false.
+     */
+    bool Joined;
+    /*!
+     * Joined Set to true, if the last uplink was a join request
+     */
+    bool LastTxIsJoinRequest;
+    /*!
+     * Set to true, if the duty cycle is enabled, otherwise false.
+     */
+    bool DutyCycleEnabled;
+    /*!
+     * Current channel index.
+     */
+    uint8_t Channel;
+    /*!
+     * Elapsed time since the start of the node.
+     */
+    TimerTime_t ElapsedTime;
+    /*!
+     * Time-on-air of the last transmission.
+     */
+    TimerTime_t TxTimeOnAir;
+}CalcBackOffParams_t;
+
+/*!
+ * Parameter structure for the function RegionNextChannel.
+ */
+typedef struct sNextChanParams
+{
+    /*!
+     * Aggregated time-off time.
+     */
+    TimerTime_t AggrTimeOff;
+    /*!
+     * Time of the last aggregated TX.
+     */
+    TimerTime_t LastAggrTx;
+    /*!
+     * Current datarate.
+     */
+    int8_t Datarate;
+    /*!
+     * Set to true, if the node has already joined a network, otherwise false.
+     */
+    bool Joined;
+    /*!
+     * Set to true, if the duty cycle is enabled, otherwise false.
+     */
+    bool DutyCycleEnabled;
+}NextChanParams_t;
+
+/*!
+ * Parameter structure for the function RegionChannelsAdd.
+ */
+typedef struct sChannelAddParams
+{
+    /*!
+     * Pointer to the new channel to add.
+     */
+    ChannelParams_t* NewChannel;
+    /*!
+     * Channel id to add.
+     */
+    uint8_t ChannelId;
+}ChannelAddParams_t;
+
+/*!
+ * Parameter structure for the function RegionChannelsRemove.
+ */
+typedef struct sChannelRemoveParams
+{
+    /*!
+     * Channel id to remove.
+     */
+    uint8_t ChannelId;
+}ChannelRemoveParams_t;
+
+/*!
+ * Parameter structure for the function RegionContinuousWave.
+ */
+typedef struct sContinuousWaveParams
+{
+    /*!
+     * Current channel index.
+     */
+    uint8_t Channel;
+    /*!
+     * Datarate. Used to limit the TX power.
+     */
+    int8_t Datarate;
+    /*!
+     * The TX power to setup.
+     */
+    int8_t TxPower;
+    /*!
+     * Max EIRP, if applicable.
+     */
+    float MaxEirp;
+    /*!
+     * The antenna gain, if applicable.
+     */
+    float AntennaGain;
+    /*!
+     * Specifies the time the radio will stay in CW mode.
+     */
+    uint16_t Timeout;
+}ContinuousWaveParams_t;
+
+/*!
+ * Parameter structure for the function RegionRxBeaconSetup
+ */
+typedef struct sRxBeaconSetupParams
+{
+    /*!
+     * Symbol timeout.
+     */
+    uint16_t SymbolTimeout;
+    /*!
+     * Receive time.
+     */
+    uint32_t RxTime;
+    /*!
+     * The address of the node.
+     */
+    uint32_t DeviceAddress;
+    /*!
+     * The beacon timing channel.
+     */
+    uint8_t BeaconTimingChannel;
+    /*!
+     * Set to true if a custom frequency is enabled.
+     */
+    bool CustomFrequencyEnabled;
+    /*!
+     * Set to true if the beacon channel is set.
+     */
+    bool BeaconChannelSet;
+    /*!
+     * Custom frequency. Only valid of CustomFrequencyEnabled is set.
+     */
+    uint32_t CustomFrequency;
+    /*!
+     * THe beacon time.
+     */
+    TimerTime_t BeaconTime;
+    /*!
+     * The beacon interval.
+     */
+    TimerTime_t BeaconInterval;
+}RxBeaconSetup_t;
+
+
+
+/*!
+ * \brief The function verifies if a region is active or not. If a region
+ *        is not active, it cannot be used.
+ *
+ * \param [IN] region LoRaWAN region.
+ *
+ * \retval Return true, if the region is supported.
+ */
+bool RegionIsActive( LoRaMacRegion_t region );
+
+/*!
+ * \brief The function gets a value of a specific phy attribute.
+ *
+ * \param [IN] region LoRaWAN region.
+ *
+ * \param [IN] getPhy Pointer to the function parameters.
+ *
+ * \retval Returns a structure containing the PHY parameter.
+ */
+PhyParam_t RegionGetPhyParam( LoRaMacRegion_t region, GetPhyParams_t* getPhy );
+
+/*!
+ * \brief Updates the last TX done parameters of the current channel.
+ *
+ * \param [IN] region LoRaWAN region.
+ *
+ * \param [IN] txDone Pointer to the function parameters.
+ */
+void RegionSetBandTxDone( LoRaMacRegion_t region, SetBandTxDoneParams_t* txDone );
+
+/*!
+ * \brief Initializes the channels masks and the channels.
+ *
+ * \param [IN] region LoRaWAN region.
+ *
+ * \param [IN] type Sets the initialization type.
+ */
+void RegionInitDefaults( LoRaMacRegion_t region, InitType_t type );
+
+/*!
+ * \brief Verifies a parameter.
+ *
+ * \param [IN] region LoRaWAN region.
+ *
+ * \param [IN] verify Pointer to the function parameters.
+ *
+ * \param [IN] type Sets the initialization type.
+ *
+ * \retval Returns true, if the parameter is valid.
+ */
+bool RegionVerify( LoRaMacRegion_t region, VerifyParams_t* verify, PhyAttribute_t phyAttribute );
+
+/*!
+ * \brief The function parses the input buffer and sets up the channels of the
+ *        CF list.
+ *
+ * \param [IN] region LoRaWAN region.
+ *
+ * \param [IN] applyCFList Pointer to the function parameters.
+ */
+void RegionApplyCFList( LoRaMacRegion_t region, ApplyCFListParams_t* applyCFList );
+
+/*!
+ * \brief Sets a channels mask.
+ *
+ * \param [IN] region LoRaWAN region.
+ *
+ * \param [IN] chanMaskSet Pointer to the function parameters.
+ *
+ * \retval Returns true, if the channels mask could be set.
+ */
+bool RegionChanMaskSet( LoRaMacRegion_t region, ChanMaskSetParams_t* chanMaskSet );
+
+/*!
+ * \brief Calculates the next datarate to set, when ADR is on or off.
+ *
+ * \param [IN] region LoRaWAN region.
+ *
+ * \param [IN] adrNext Pointer to the function parameters.
+ *
+ * \param [OUT] drOut The calculated datarate for the next TX.
+ *
+ * \param [OUT] txPowOut The TX power for the next TX.
+ *
+ * \param [OUT] adrAckCounter The calculated ADR acknowledgement counter.
+ *
+ * \retval Returns true, if an ADR request should be performed.
+ */
+bool RegionAdrNext( LoRaMacRegion_t region, AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter );
+
+/*!
+ * \brief Configuration of the RX windows.
+ *
+ * \param [IN] region LoRaWAN region.
+ *
+ * \param [IN] rxConfig Pointer to the function parameters.
+ *
+ * \param [OUT] datarate The datarate index which was set.
+ *
+ * \retval Returns true, if the configuration was applied successfully.
+ */
+bool RegionRxConfig( LoRaMacRegion_t region, RxConfigParams_t* rxConfig, int8_t* datarate );
+
+/*
+ * Rx window precise timing
+ *
+ * For more details please consult the following document, chapter 3.1.2.
+ * http://www.semtech.com/images/datasheet/SX1272_settings_for_LoRaWAN_v2.0.pdf
+ * or
+ * http://www.semtech.com/images/datasheet/SX1276_settings_for_LoRaWAN_v2.0.pdf
+ *
+ *                 Downlink start: T = Tx + 1s (+/- 20 us)
+ *                            |
+ *             TRxEarly       |        TRxLate
+ *                |           |           |
+ *                |           |           +---+---+---+---+---+---+---+---+
+ *                |           |           |       Latest Rx window        |
+ *                |           |           +---+---+---+---+---+---+---+---+
+ *                |           |           |
+ *                +---+---+---+---+---+---+---+---+
+ *                |       Earliest Rx window      |
+ *                +---+---+---+---+---+---+---+---+
+ *                            |
+ *                            +---+---+---+---+---+---+---+---+
+ *Downlink preamble 8 symbols |   |   |   |   |   |   |   |   |
+ *                            +---+---+---+---+---+---+---+---+
+ *
+ *                     Worst case Rx window timings
+ *
+ * TRxLate  = DEFAULT_MIN_RX_SYMBOLS * tSymbol - RADIO_WAKEUP_TIME
+ * TRxEarly = 8 - DEFAULT_MIN_RX_SYMBOLS * tSymbol - RxWindowTimeout - RADIO_WAKEUP_TIME
+ *
+ * TRxLate - TRxEarly = 2 * DEFAULT_SYSTEM_MAX_RX_ERROR
+ *
+ * RxOffset = ( TRxLate + TRxEarly ) / 2
+ *
+ * RxWindowTimeout = ( 2 * DEFAULT_MIN_RX_SYMBOLS - 8 ) * tSymbol + 2 * DEFAULT_SYSTEM_MAX_RX_ERROR
+ * RxOffset = 4 * tSymbol - RxWindowTimeout / 2 - RADIO_WAKE_UP_TIME
+ *
+ * Minimal value of RxWindowTimeout must be 5 symbols which implies that the system always tolerates at least an error of 1.5 * tSymbol
+ */
+/*!
+ * Computes the Rx window timeout and offset.
+ *
+ * \param [IN] region       LoRaWAN region.
+ *
+ * \param [IN] datarate     Rx window datarate index to be used
+ *
+ * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame.
+ *
+ * \param [IN] rxError      System maximum timing error of the receiver. In milliseconds
+ *                          The receiver will turn on in a [-rxError : +rxError] ms
+ *                          interval around RxOffset
+ *
+ * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields.
+ */
+void RegionComputeRxWindowParameters( LoRaMacRegion_t region, int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams );
+
+/*!
+ * \brief TX configuration.
+ *
+ * \param [IN] region LoRaWAN region.
+ *
+ * \param [IN] txConfig Pointer to the function parameters.
+ *
+ * \param [OUT] txPower The tx power index which was set.
+ *
+ * \param [OUT] txTimeOnAir The time-on-air of the frame.
+ *
+ * \retval Returns true, if the configuration was applied successfully.
+ */
+bool RegionTxConfig( LoRaMacRegion_t region, TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir );
+
+/*!
+ * \brief The function processes a Link ADR Request.
+ *
+ * \param [IN] region LoRaWAN region.
+ *
+ * \param [IN] linkAdrReq Pointer to the function parameters.
+ *
+ * \param [OUT] drOut The datarate which was applied.
+ *
+ * \param [OUT] txPowOut The TX power which was applied.
+ *
+ * \param [OUT] nbRepOut The number of repetitions to apply.
+ *
+ * \param [OUT] nbBytesParsed The number bytes which were parsed.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionLinkAdrReq( LoRaMacRegion_t region, LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed );
+
+/*!
+ * \brief The function processes a RX Parameter Setup Request.
+ *
+ * \param [IN] region LoRaWAN region.
+ *
+ * \param [IN] rxParamSetupReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionRxParamSetupReq( LoRaMacRegion_t region, RxParamSetupReqParams_t* rxParamSetupReq );
+
+/*!
+ * \brief The function processes a New Channel Request.
+ *
+ * \param [IN] region LoRaWAN region.
+ *
+ * \param [IN] newChannelReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionNewChannelReq( LoRaMacRegion_t region, NewChannelReqParams_t* newChannelReq );
+
+/*!
+ * \brief The function processes a TX ParamSetup Request.
+ *
+ * \param [IN] region LoRaWAN region.
+ *
+ * \param [IN] txParamSetupReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ *         Returns -1, if the functionality is not implemented. In this case, the end node
+ *         shall ignore the command.
+ */
+int8_t RegionTxParamSetupReq( LoRaMacRegion_t region, TxParamSetupReqParams_t* txParamSetupReq );
+
+/*!
+ * \brief The function processes a DlChannel Request.
+ *
+ * \param [IN] region LoRaWAN region.
+ *
+ * \param [IN] dlChannelReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionDlChannelReq( LoRaMacRegion_t region, DlChannelReqParams_t* dlChannelReq );
+
+/*!
+ * \brief Alternates the datarate of the channel for the join request.
+ *
+ * \param [IN] region LoRaWAN region.
+ *
+ * \param [IN] alternateDr Pointer to the function parameters.
+ *
+ * \retval Datarate to apply.
+ */
+int8_t RegionAlternateDr( LoRaMacRegion_t region, AlternateDrParams_t* alternateDr );
+
+/*!
+ * \brief Calculates the back-off time.
+ *
+ * \param [IN] region LoRaWAN region.
+ *
+ * \param [IN] calcBackOff Pointer to the function parameters.
+ */
+void RegionCalcBackOff( LoRaMacRegion_t region, CalcBackOffParams_t* calcBackOff );
+
+/*!
+ * \brief Searches and set the next random available channel
+ *
+ * \param [IN] region LoRaWAN region.
+ *
+ * \param [OUT] channel Next channel to use for TX.
+ *
+ * \param [OUT] time Time to wait for the next transmission according to the duty
+ *              cycle.
+ *
+ * \param [OUT] aggregatedTimeOff Updates the aggregated time off.
+ *
+ * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate].
+ */
+bool RegionNextChannel( LoRaMacRegion_t region, NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff );
+
+/*!
+ * \brief Adds a channel.
+ *
+ * \param [IN] region LoRaWAN region.
+ *
+ * \param [IN] channelAdd Pointer to the function parameters.
+ *
+ * \retval Status of the operation.
+ */
+LoRaMacStatus_t RegionChannelAdd( LoRaMacRegion_t region, ChannelAddParams_t* channelAdd );
+
+/*!
+ * \brief Removes a channel.
+ *
+ * \param [IN] region LoRaWAN region.
+ *
+ * \param [IN] channelRemove Pointer to the function parameters.
+ *
+ * \retval Returns true, if the channel was removed successfully.
+ */
+bool RegionChannelsRemove( LoRaMacRegion_t region, ChannelRemoveParams_t* channelRemove );
+
+/*!
+ * \brief Sets the radio into continuous wave mode.
+ *
+ * \param [IN] region LoRaWAN region.
+ *
+ * \param [IN] continuousWave Pointer to the function parameters.
+ */
+void RegionSetContinuousWave( LoRaMacRegion_t region, ContinuousWaveParams_t* continuousWave );
+
+/*!
+ * \brief Computes new datarate according to the given offset
+ *
+ * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms
+ *
+ * \param [IN] dr Current datarate
+ *
+ * \param [IN] drOffset Offset to be applied
+ *
+ * \retval newDr Computed datarate.
+ */
+uint8_t RegionApplyDrOffset( LoRaMacRegion_t region, uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset );
+
+void RegionRxBeaconSetup( LoRaMacRegion_t region, RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr, bool *beaconChannelSet );
+
+/*! \} defgroup REGION */
+
+#endif // __REGION_H__
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/region/RegionAS923.cpp	Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,1203 @@
+/*
+ / _____)             _              | |
+( (____  _____ ____ _| |_ _____  ____| |__
+ \____ \| ___ |    (_   _) ___ |/ ___)  _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+    (C)2013 Semtech
+ ___ _____ _   ___ _  _____ ___  ___  ___ ___
+/ __|_   _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+\__ \ | |/ _ \ (__| ' <| _| (_) |   / (__| _|
+|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+embedded.connectivity.solutions===============
+
+Description: LoRa MAC region AS923 implementation
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+
+Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE )
+*/
+#include <stdbool.h>
+#include <string.h>
+#include <stdint.h>
+#include <math.h>
+
+#include "board.h"
+#include "LoRaMac.h"
+
+#include "utilities.h"
+
+#include "Region.h"
+#include "RegionCommon.h"
+#include "RegionAS923.h"
+
+// Definitions
+#define CHANNELS_MASK_SIZE              1
+
+// Global attributes
+/*!
+ * LoRaMAC channels
+ */
+static ChannelParams_t Channels[AS923_MAX_NB_CHANNELS];
+
+/*!
+ * LoRaMac bands
+ */
+static Band_t Bands[AS923_MAX_NB_BANDS] =
+{
+    AS923_BAND0
+};
+
+/*!
+ * LoRaMac channels mask
+ */
+static uint16_t ChannelsMask[CHANNELS_MASK_SIZE];
+
+/*!
+ * LoRaMac channels default mask
+ */
+static uint16_t ChannelsDefaultMask[CHANNELS_MASK_SIZE];
+
+// Static functions
+static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr )
+{
+    uint8_t nextLowerDr = 0;
+
+    if( dr == minDr )
+    {
+        nextLowerDr = minDr;
+    }
+    else
+    {
+        nextLowerDr = dr - 1;
+    }
+    return nextLowerDr;
+}
+
+static uint32_t GetBandwidth( uint32_t drIndex )
+{
+    switch( BandwidthsAS923[drIndex] )
+    {
+        default:
+        case 125000:
+            return 0;
+        case 250000:
+            return 1;
+        case 500000:
+            return 2;
+    }
+}
+
+static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask )
+{
+    int8_t txPowerResult = txPower;
+
+    // Limit tx power to the band max
+    txPowerResult =  MAX( txPower, maxBandTxPower );
+
+    return txPowerResult;
+}
+
+static bool VerifyTxFreq( uint32_t freq )
+{
+    // Check radio driver support
+    if( Radio.CheckRfFrequency( freq ) == false )
+    {
+        return false;
+    }
+
+    if( ( freq < 915000000 ) || ( freq > 928000000 ) )
+    {
+        return false;
+    }
+    return true;
+}
+
+static uint8_t CountNbOfEnabledChannels( bool joined, uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx )
+{
+    uint8_t nbEnabledChannels = 0;
+    uint8_t delayTransmission = 0;
+
+    for( uint8_t i = 0, k = 0; i < AS923_MAX_NB_CHANNELS; i += 16, k++ )
+    {
+        for( uint8_t j = 0; j < 16; j++ )
+        {
+            if( ( channelsMask[k] & ( 1 << j ) ) != 0 )
+            {
+                if( channels[i + j].Frequency == 0 )
+                { // Check if the channel is enabled
+                    continue;
+                }
+                if( joined == false )
+                {
+                    if( ( AS923_JOIN_CHANNELS & ( 1 << j ) ) == 0 )
+                    {
+                        continue;
+                    }
+                }
+                if( RegionCommonValueInRange( datarate, channels[i + j].DrRange.Fields.Min,
+                                              channels[i + j].DrRange.Fields.Max ) == false )
+                { // Check if the current channel selection supports the given datarate
+                    continue;
+                }
+                if( bands[channels[i + j].Band].TimeOff > 0 )
+                { // Check if the band is available for transmission
+                    delayTransmission++;
+                    continue;
+                }
+                enabledChannels[nbEnabledChannels++] = i + j;
+            }
+        }
+    }
+
+    *delayTx = delayTransmission;
+    return nbEnabledChannels;
+}
+
+PhyParam_t RegionAS923GetPhyParam( GetPhyParams_t* getPhy )
+{
+    PhyParam_t phyParam = { 0 };
+
+    switch( getPhy->Attribute )
+    {
+        case PHY_MIN_RX_DR:
+        {
+            if( getPhy->DownlinkDwellTime == 0 )
+            {
+                phyParam.Value = AS923_RX_MIN_DATARATE;
+            }
+            else
+            {
+                phyParam.Value = AS923_DWELL_LIMIT_DATARATE;
+            }
+            break;
+        }
+        case PHY_MIN_TX_DR:
+        {
+            if( getPhy->UplinkDwellTime == 0 )
+            {
+                phyParam.Value = AS923_TX_MIN_DATARATE;
+            }
+            else
+            {
+                phyParam.Value = AS923_DWELL_LIMIT_DATARATE;
+            }
+            break;
+        }
+        case PHY_DEF_TX_DR:
+        {
+            phyParam.Value = AS923_DEFAULT_DATARATE;
+            break;
+        }
+        case PHY_NEXT_LOWER_TX_DR:
+        {
+            if( getPhy->UplinkDwellTime == 0 )
+            {
+                phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, AS923_TX_MIN_DATARATE );
+            }
+            else
+            {
+                phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, AS923_DWELL_LIMIT_DATARATE );
+            }
+            break;
+        }
+        case PHY_DEF_TX_POWER:
+        {
+            phyParam.Value = AS923_DEFAULT_TX_POWER;
+            break;
+        }
+        case PHY_MAX_PAYLOAD:
+        {
+            if( getPhy->UplinkDwellTime == 0 )
+            {
+                phyParam.Value = MaxPayloadOfDatarateDwell0AS923[getPhy->Datarate];
+            }
+            else
+            {
+                phyParam.Value = MaxPayloadOfDatarateDwell1UpAS923[getPhy->Datarate];
+            }
+            break;
+        }
+        case PHY_MAX_PAYLOAD_REPEATER:
+        {
+            if( getPhy->UplinkDwellTime == 0 )
+            {
+                phyParam.Value = MaxPayloadOfDatarateRepeaterDwell0AS923[getPhy->Datarate];
+            }
+            else
+            {
+                phyParam.Value = MaxPayloadOfDatarateDwell1UpAS923[getPhy->Datarate];
+            }
+            break;
+        }
+        case PHY_DUTY_CYCLE:
+        {
+            phyParam.Value = AS923_DUTY_CYCLE_ENABLED;
+            break;
+        }
+        case PHY_MAX_RX_WINDOW:
+        {
+            phyParam.Value = AS923_MAX_RX_WINDOW;
+            break;
+        }
+        case PHY_RECEIVE_DELAY1:
+        {
+            phyParam.Value = AS923_RECEIVE_DELAY1;
+            break;
+        }
+        case PHY_RECEIVE_DELAY2:
+        {
+            phyParam.Value = AS923_RECEIVE_DELAY2;
+            break;
+        }
+        case PHY_JOIN_ACCEPT_DELAY1:
+        {
+            phyParam.Value = AS923_JOIN_ACCEPT_DELAY1;
+            break;
+        }
+        case PHY_JOIN_ACCEPT_DELAY2:
+        {
+            phyParam.Value = AS923_JOIN_ACCEPT_DELAY2;
+            break;
+        }
+        case PHY_MAX_FCNT_GAP:
+        {
+            phyParam.Value = AS923_MAX_FCNT_GAP;
+            break;
+        }
+        case PHY_ACK_TIMEOUT:
+        {
+            phyParam.Value = ( AS923_ACKTIMEOUT + randr( -AS923_ACK_TIMEOUT_RND, AS923_ACK_TIMEOUT_RND ) );
+            break;
+        }
+        case PHY_DEF_DR1_OFFSET:
+        {
+            phyParam.Value = AS923_DEFAULT_RX1_DR_OFFSET;
+            break;
+        }
+        case PHY_DEF_RX2_FREQUENCY:
+        {
+            phyParam.Value = AS923_RX_WND_2_FREQ;
+            break;
+        }
+        case PHY_DEF_RX2_DR:
+        {
+            phyParam.Value = AS923_RX_WND_2_DR;
+            break;
+        }
+        case PHY_CHANNELS_MASK:
+        {
+            phyParam.ChannelsMask = ChannelsMask;
+            break;
+        }
+        case PHY_CHANNELS_DEFAULT_MASK:
+        {
+            phyParam.ChannelsMask = ChannelsDefaultMask;
+            break;
+        }
+        case PHY_MAX_NB_CHANNELS:
+        {
+            phyParam.Value = AS923_MAX_NB_CHANNELS;
+            break;
+        }
+        case PHY_CHANNELS:
+        {
+            phyParam.Channels = Channels;
+            break;
+        }
+        case PHY_DEF_UPLINK_DWELL_TIME:
+        {
+            phyParam.Value = AS923_DEFAULT_UPLINK_DWELL_TIME;
+            break;
+        }
+        case PHY_DEF_DOWNLINK_DWELL_TIME:
+        {
+            phyParam.Value = AS923_DEFAULT_DOWNLINK_DWELL_TIME;
+            break;
+        }
+        case PHY_DEF_MAX_EIRP:
+        {
+            phyParam.fValue = AS923_DEFAULT_MAX_EIRP;
+            break;
+        }
+        case PHY_DEF_ANTENNA_GAIN:
+        {
+            phyParam.fValue = AS923_DEFAULT_ANTENNA_GAIN;
+            break;
+        }
+        case PHY_NB_JOIN_TRIALS:
+        case PHY_DEF_NB_JOIN_TRIALS:
+        {
+            phyParam.Value = 1;
+            break;
+        }
+        case PHY_BEACON_INTERVAL:
+        {
+            phyParam.Value = AS923_BEACON_INTERVAL;
+            break;
+        }
+        case PHY_BEACON_RESERVED:
+        {
+            phyParam.Value = AS923_BEACON_RESERVED;
+            break;
+        }
+        case PHY_BEACON_GUARD:
+        {
+            phyParam.Value = AS923_BEACON_GUARD;
+            break;
+        }
+        case PHY_BEACON_WINDOW:
+        {
+            phyParam.Value = AS923_BEACON_WINDOW;
+            break;
+        }
+        case PHY_BEACON_WINDOW_SLOTS:
+        {
+            phyParam.Value = AS923_BEACON_WINDOW_SLOTS;
+            break;
+        }
+        case PHY_PING_SLOT_WINDOW:
+        {
+            phyParam.Value = AS923_PING_SLOT_WINDOW;
+            break;
+        }
+        case PHY_BEACON_SYMBOL_TO_DEFAULT:
+        {
+            phyParam.Value = AS923_BEACON_SYMBOL_TO_DEFAULT;
+            break;
+        }
+        case PHY_BEACON_SYMBOL_TO_EXPANSION_MAX:
+        {
+            phyParam.Value = AS923_BEACON_SYMBOL_TO_EXPANSION_MAX;
+            break;
+        }
+        case PHY_PING_SLOT_SYMBOL_TO_EXPANSION_MAX:
+        {
+            phyParam.Value = AS923_PING_SLOT_SYMBOL_TO_EXPANSION_MAX;
+            break;
+        }
+        case PHY_BEACON_SYMBOL_TO_EXPANSION_FACTOR:
+        {
+            phyParam.Value = AS923_BEACON_SYMBOL_TO_EXPANSION_FACTOR;
+            break;
+        }
+        case PHY_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR:
+        {
+            phyParam.Value = AS923_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR;
+            break;
+        }
+        case PHY_MAX_BEACON_LESS_PERIOD:
+        {
+            phyParam.Value = AS923_MAX_BEACON_LESS_PERIOD;
+            break;
+        }
+        case PHY_BEACON_DELAY_BEACON_TIMING_ANS:
+        {
+            phyParam.Value = AS923_BEACON_DELAY_BEACON_TIMING_ANS;
+            break;
+        }
+        case PHY_BEACON_CHANNEL_FREQ:
+        {
+            phyParam.Value = AS923_BEACON_CHANNEL_FREQ;
+            break;
+        }
+        case PHY_PINGSLOT_CHANNEL_FREQ:
+        {
+            phyParam.Value = AS923_BEACON_CHANNEL_FREQ;
+            break;
+        }
+        case PHY_BEACON_SIZE:
+        {
+            phyParam.Value = AS923_BEACON_SIZE;
+            break;
+        }
+        case PHY_BEACON_CHANNEL_DR:
+        {
+            phyParam.Value = AS923_BEACON_CHANNEL_DR;
+            break;
+        }
+        default:
+        {
+            break;
+        }
+    }
+
+    return phyParam;
+}
+
+void RegionAS923SetBandTxDone( SetBandTxDoneParams_t* txDone )
+{
+    RegionCommonSetBandTxDone( txDone->Joined, &Bands[Channels[txDone->Channel].Band], txDone->LastTxDoneTime );
+}
+
+void RegionAS923InitDefaults( InitType_t type )
+{
+    switch( type )
+    {
+        case INIT_TYPE_INIT:
+        {
+            // Channels
+            Channels[0] = ( ChannelParams_t ) AS923_LC1;
+            Channels[1] = ( ChannelParams_t ) AS923_LC2;
+
+            // Initialize the channels default mask
+            ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 );
+            // Update the channels mask
+            RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 1 );
+            break;
+        }
+        case INIT_TYPE_RESTORE:
+        {
+            // Restore channels default mask
+            ChannelsMask[0] |= ChannelsDefaultMask[0];
+            break;
+        }
+        default:
+        {
+            break;
+        }
+    }
+}
+
+bool RegionAS923Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute )
+{
+    switch( phyAttribute )
+    {
+        case PHY_TX_DR:
+        {
+            if( verify->DatarateParams.UplinkDwellTime == 0 )
+            {
+                return RegionCommonValueInRange( verify->DatarateParams.Datarate, AS923_TX_MIN_DATARATE, AS923_TX_MAX_DATARATE );
+            }
+            else
+            {
+                return RegionCommonValueInRange( verify->DatarateParams.Datarate, AS923_DWELL_LIMIT_DATARATE, AS923_TX_MAX_DATARATE );
+            }
+        }
+        case PHY_DEF_TX_DR:
+        {
+            return RegionCommonValueInRange( verify->DatarateParams.Datarate, DR_0, DR_5 );
+        }
+        case PHY_RX_DR:
+        {
+            if( verify->DatarateParams.DownlinkDwellTime == 0 )
+            {
+                return RegionCommonValueInRange( verify->DatarateParams.Datarate, AS923_RX_MIN_DATARATE, AS923_RX_MAX_DATARATE );
+            }
+            else
+            {
+                return RegionCommonValueInRange( verify->DatarateParams.Datarate, AS923_DWELL_LIMIT_DATARATE, AS923_RX_MAX_DATARATE );
+            }
+        }
+        case PHY_DEF_TX_POWER:
+        case PHY_TX_POWER:
+        {
+            // Remark: switched min and max!
+            return RegionCommonValueInRange( verify->TxPower, AS923_MAX_TX_POWER, AS923_MIN_TX_POWER );
+        }
+        case PHY_DUTY_CYCLE:
+        {
+            return AS923_DUTY_CYCLE_ENABLED;
+        }
+        case PHY_NB_JOIN_TRIALS:
+        {
+            return true;
+        }
+        default:
+            return false;
+    }
+}
+
+void RegionAS923ApplyCFList( ApplyCFListParams_t* applyCFList )
+{
+    ChannelParams_t newChannel;
+    ChannelAddParams_t channelAdd;
+    ChannelRemoveParams_t channelRemove;
+
+    // Setup default datarate range
+    newChannel.DrRange.Value = ( DR_5 << 4 ) | DR_0;
+
+    // Size of the optional CF list
+    if( applyCFList->Size != 16 )
+    {
+        return;
+    }
+
+    // Last byte is RFU, don't take it into account
+    for( uint8_t i = 0, chanIdx = AS923_NUMB_DEFAULT_CHANNELS; chanIdx < AS923_MAX_NB_CHANNELS; i+=3, chanIdx++ )
+    {
+        if( chanIdx < ( AS923_NUMB_CHANNELS_CF_LIST + AS923_NUMB_DEFAULT_CHANNELS ) )
+        {
+            // Channel frequency
+            newChannel.Frequency = (uint32_t) applyCFList->Payload[i];
+            newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 1] << 8 );
+            newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 2] << 16 );
+            newChannel.Frequency *= 100;
+
+            // Initialize alternative frequency to 0
+            newChannel.Rx1Frequency = 0;
+        }
+        else
+        {
+            newChannel.Frequency = 0;
+            newChannel.DrRange.Value = 0;
+            newChannel.Rx1Frequency = 0;
+        }
+
+        if( newChannel.Frequency != 0 )
+        {
+            channelAdd.NewChannel = &newChannel;
+            channelAdd.ChannelId = chanIdx;
+
+            // Try to add all channels
+            RegionAS923ChannelAdd( &channelAdd );
+        }
+        else
+        {
+            channelRemove.ChannelId = chanIdx;
+
+            RegionAS923ChannelsRemove( &channelRemove );
+        }
+    }
+}
+
+bool RegionAS923ChanMaskSet( ChanMaskSetParams_t* chanMaskSet )
+{
+    switch( chanMaskSet->ChannelsMaskType )
+    {
+        case CHANNELS_MASK:
+        {
+            RegionCommonChanMaskCopy( ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 );
+            break;
+        }
+        case CHANNELS_DEFAULT_MASK:
+        {
+            RegionCommonChanMaskCopy( ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 );
+            break;
+        }
+        default:
+            return false;
+    }
+    return true;
+}
+
+bool RegionAS923AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter )
+{
+    bool adrAckReq = false;
+    int8_t datarate = adrNext->Datarate;
+    int8_t minTxDatarate = 0;
+    int8_t txPower = adrNext->TxPower;
+    GetPhyParams_t getPhy;
+    PhyParam_t phyParam;
+
+    // Get the minimum possible datarate
+    getPhy.Attribute = PHY_MIN_TX_DR;
+    getPhy.UplinkDwellTime = adrNext->UplinkDwellTime;
+    phyParam = RegionAS923GetPhyParam( &getPhy );
+    minTxDatarate = phyParam.Value;
+
+    // Report back the adr ack counter
+    *adrAckCounter = adrNext->AdrAckCounter;
+
+    // Apply the minimum possible datarate.
+    datarate = MAX( datarate, minTxDatarate );
+
+    if( adrNext->AdrEnabled == true )
+    {
+        if( datarate == minTxDatarate )
+        {
+            *adrAckCounter = 0;
+            adrAckReq = false;
+        }
+        else
+        {
+            if( adrNext->AdrAckCounter >= AS923_ADR_ACK_LIMIT )
+            {
+                adrAckReq = true;
+                txPower = AS923_MAX_TX_POWER;
+            }
+            else
+            {
+                adrAckReq = false;
+            }
+            if( adrNext->AdrAckCounter >= ( AS923_ADR_ACK_LIMIT + AS923_ADR_ACK_DELAY ) )
+            {
+                if( ( adrNext->AdrAckCounter % AS923_ADR_ACK_DELAY ) == 1 )
+                {
+                    // Decrease the datarate
+                    getPhy.Attribute = PHY_NEXT_LOWER_TX_DR;
+                    getPhy.Datarate = datarate;
+                    getPhy.UplinkDwellTime = adrNext->UplinkDwellTime;
+                    phyParam = RegionAS923GetPhyParam( &getPhy );
+                    datarate = phyParam.Value;
+
+                    if( datarate == minTxDatarate )
+                    {
+                        // We must set adrAckReq to false as soon as we reach the lowest datarate
+                        adrAckReq = false;
+                        if( adrNext->UpdateChanMask == true )
+                        {
+                            // Re-enable default channels
+                            ChannelsMask[0] |= LC( 1 ) + LC( 2 );
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    *drOut = datarate;
+    *txPowOut = txPower;
+    return adrAckReq;
+}
+
+void RegionAS923ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams )
+{
+    double tSymbol = 0.0;
+
+    // Get the datarate, perform a boundary check
+    rxConfigParams->Datarate = MIN( datarate, AS923_RX_MAX_DATARATE );
+    rxConfigParams->Bandwidth = GetBandwidth( rxConfigParams->Datarate );
+
+    if( rxConfigParams->Datarate == DR_7 )
+    { // FSK
+        tSymbol = RegionCommonComputeSymbolTimeFsk( DataratesAS923[rxConfigParams->Datarate] );
+    }
+    else
+    { // LoRa
+        tSymbol = RegionCommonComputeSymbolTimeLoRa( DataratesAS923[rxConfigParams->Datarate], BandwidthsAS923[rxConfigParams->Datarate] );
+    }
+
+    RegionCommonComputeRxWindowParameters( tSymbol, minRxSymbols, rxError, RADIO_WAKEUP_TIME, &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset );
+}
+
+bool RegionAS923RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate )
+{
+    RadioModems_t modem;
+    int8_t dr = rxConfig->Datarate;
+    uint8_t maxPayload = 0;
+    int8_t phyDr = 0;
+    uint32_t frequency = rxConfig->Frequency;
+
+    if( Radio.GetStatus( ) != RF_IDLE )
+    {
+        return false;
+    }
+
+    if( rxConfig->Window == 0 )
+    {
+        // Apply window 1 frequency
+        frequency = Channels[rxConfig->Channel].Frequency;
+        // Apply the alternative RX 1 window frequency, if it is available
+        if( Channels[rxConfig->Channel].Rx1Frequency != 0 )
+        {
+            frequency = Channels[rxConfig->Channel].Rx1Frequency;
+        }
+    }
+
+    // Read the physical datarate from the datarates table
+    phyDr = DataratesAS923[dr];
+
+    Radio.SetChannel( frequency );
+
+    // Radio configuration
+    if( dr == DR_7 )
+    {
+        modem = MODEM_FSK;
+        Radio.SetRxConfig( modem, 50000, phyDr * 1000, 0, 83333, 5, rxConfig->WindowTimeout, false, 0, true, 0, 0, false, rxConfig->RxContinuous );
+    }
+    else
+    {
+        modem = MODEM_LORA;
+        Radio.SetRxConfig( modem, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous );
+    }
+
+    // Check for repeater support
+    if( rxConfig->RepeaterSupport == true )
+    {
+        maxPayload = MaxPayloadOfDatarateRepeaterDwell0AS923[dr];
+    }
+    else
+    {
+        maxPayload = MaxPayloadOfDatarateDwell0AS923[dr];
+    }
+
+    Radio.SetMaxPayloadLength( modem, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD );
+
+    *datarate = (uint8_t) dr;
+    return true;
+}
+
+bool RegionAS923TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir )
+{
+    RadioModems_t modem;
+    int8_t phyDr = DataratesAS923[txConfig->Datarate];
+    int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, Bands[Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, ChannelsMask );
+    uint32_t bandwidth = GetBandwidth( txConfig->Datarate );
+    int8_t phyTxPower = 0;
+
+    // Calculate physical TX power
+    phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain );
+
+    // Setup the radio frequency
+    Radio.SetChannel( Channels[txConfig->Channel].Frequency );
+
+    if( txConfig->Datarate == DR_7 )
+    { // High Speed FSK channel
+        modem = MODEM_FSK;
+        Radio.SetTxConfig( modem, phyTxPower, 25000, bandwidth, phyDr * 1000, 0, 5, false, true, 0, 0, false, 3000 );
+    }
+    else
+    {
+        modem = MODEM_LORA;
+        Radio.SetTxConfig( modem, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 3000 );
+    }
+
+    // Setup maximum payload lenght of the radio driver
+    Radio.SetMaxPayloadLength( modem, txConfig->PktLen );
+    // Get the time-on-air of the next tx frame
+    *txTimeOnAir = Radio.TimeOnAir( modem, txConfig->PktLen );
+
+    *txPower = txPowerLimited;
+    return true;
+}
+
+uint8_t RegionAS923LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed )
+{
+    uint8_t status = 0x07;
+    RegionCommonLinkAdrParams_t linkAdrParams;
+    uint8_t nextIndex = 0;
+    uint8_t bytesProcessed = 0;
+    uint16_t chMask = 0;
+    GetPhyParams_t getPhy;
+    PhyParam_t phyParam;
+    RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams;
+
+    while( bytesProcessed < linkAdrReq->PayloadSize )
+    {
+        // Get ADR request parameters
+        nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams );
+
+        if( nextIndex == 0 )
+            break; // break loop, since no more request has been found
+
+        // Update bytes processed
+        bytesProcessed += nextIndex;
+
+        // Revert status, as we only check the last ADR request for the channel mask KO
+        status = 0x07;
+
+        // Setup temporary channels mask
+        chMask = linkAdrParams.ChMask;
+
+        // Verify channels mask
+        if( ( linkAdrParams.ChMaskCtrl == 0 ) && ( chMask == 0 ) )
+        {
+            status &= 0xFE; // Channel mask KO
+        }
+        else if( ( ( linkAdrParams.ChMaskCtrl >= 1 ) && ( linkAdrParams.ChMaskCtrl <= 5 )) ||
+                ( linkAdrParams.ChMaskCtrl >= 7 ) )
+        {
+            // RFU
+            status &= 0xFE; // Channel mask KO
+        }
+        else
+        {
+            for( uint8_t i = 0; i < AS923_MAX_NB_CHANNELS; i++ )
+            {
+                if( linkAdrParams.ChMaskCtrl == 6 )
+                {
+                    if( Channels[i].Frequency != 0 )
+                    {
+                        chMask |= 1 << i;
+                    }
+                }
+                else
+                {
+                    if( ( ( chMask & ( 1 << i ) ) != 0 ) &&
+                        ( Channels[i].Frequency == 0 ) )
+                    {// Trying to enable an undefined channel
+                        status &= 0xFE; // Channel mask KO
+                    }
+                }
+            }
+        }
+    }
+
+    // Get the minimum possible datarate
+    getPhy.Attribute = PHY_MIN_TX_DR;
+    getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime;
+    phyParam = RegionAS923GetPhyParam( &getPhy );
+
+    linkAdrVerifyParams.Status = status;
+    linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled;
+    linkAdrVerifyParams.Datarate = linkAdrParams.Datarate;
+    linkAdrVerifyParams.TxPower = linkAdrParams.TxPower;
+    linkAdrVerifyParams.NbRep = linkAdrParams.NbRep;
+    linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate;
+    linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower;
+    linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep;
+    linkAdrVerifyParams.NbChannels = AS923_MAX_NB_CHANNELS;
+    linkAdrVerifyParams.ChannelsMask = &chMask;
+    linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value;
+    linkAdrVerifyParams.MaxDatarate = AS923_TX_MAX_DATARATE;
+    linkAdrVerifyParams.Channels = Channels;
+    linkAdrVerifyParams.MinTxPower = AS923_MIN_TX_POWER;
+    linkAdrVerifyParams.MaxTxPower = AS923_MAX_TX_POWER;
+
+    // Verify the parameters and update, if necessary
+    status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep );
+
+    // Update channelsMask if everything is correct
+    if( status == 0x07 )
+    {
+        // Set the channels mask to a default value
+        memset( ChannelsMask, 0, sizeof( ChannelsMask ) );
+        // Update the channels mask
+        ChannelsMask[0] = chMask;
+    }
+
+    // Update status variables
+    *drOut = linkAdrParams.Datarate;
+    *txPowOut = linkAdrParams.TxPower;
+    *nbRepOut = linkAdrParams.NbRep;
+    *nbBytesParsed = bytesProcessed;
+
+    return status;
+}
+
+uint8_t RegionAS923RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq )
+{
+    uint8_t status = 0x07;
+
+    // Verify radio frequency
+    if( Radio.CheckRfFrequency( rxParamSetupReq->Frequency ) == false )
+    {
+        status &= 0xFE; // Channel frequency KO
+    }
+
+    // Verify datarate
+    if( RegionCommonValueInRange( rxParamSetupReq->Datarate, AS923_RX_MIN_DATARATE, AS923_RX_MAX_DATARATE ) == false )
+    {
+        status &= 0xFD; // Datarate KO
+    }
+
+    // Verify datarate offset
+    if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, AS923_MIN_RX1_DR_OFFSET, AS923_MAX_RX1_DR_OFFSET ) == false )
+    {
+        status &= 0xFB; // Rx1DrOffset range KO
+    }
+
+    return status;
+}
+
+uint8_t RegionAS923NewChannelReq( NewChannelReqParams_t* newChannelReq )
+{
+    uint8_t status = 0x03;
+    ChannelAddParams_t channelAdd;
+    ChannelRemoveParams_t channelRemove;
+
+    if( newChannelReq->NewChannel->Frequency == 0 )
+    {
+        channelRemove.ChannelId = newChannelReq->ChannelId;
+
+        // Remove
+        if( RegionAS923ChannelsRemove( &channelRemove ) == false )
+        {
+            status &= 0xFC;
+        }
+    }
+    else
+    {
+        channelAdd.NewChannel = newChannelReq->NewChannel;
+        channelAdd.ChannelId = newChannelReq->ChannelId;
+
+        switch( RegionAS923ChannelAdd( &channelAdd ) )
+        {
+            case LORAMAC_STATUS_OK:
+            {
+                break;
+            }
+            case LORAMAC_STATUS_FREQUENCY_INVALID:
+            {
+                status &= 0xFE;
+                break;
+            }
+            case LORAMAC_STATUS_DATARATE_INVALID:
+            {
+                status &= 0xFD;
+                break;
+            }
+            case LORAMAC_STATUS_FREQ_AND_DR_INVALID:
+            {
+                status &= 0xFC;
+                break;
+            }
+            default:
+            {
+                status &= 0xFC;
+                break;
+            }
+        }
+    }
+
+    return status;
+}
+
+int8_t RegionAS923TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq )
+{
+    // Accept the request
+    return 0;
+}
+
+uint8_t RegionAS923DlChannelReq( DlChannelReqParams_t* dlChannelReq )
+{
+    uint8_t status = 0x03;
+
+    // Verify if the frequency is supported
+    if( VerifyTxFreq( dlChannelReq->Rx1Frequency ) == false )
+    {
+        status &= 0xFE;
+    }
+
+    // Verify if an uplink frequency exists
+    if( Channels[dlChannelReq->ChannelId].Frequency == 0 )
+    {
+        status &= 0xFD;
+    }
+
+    // Apply Rx1 frequency, if the status is OK
+    if( status == 0x03 )
+    {
+        Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency;
+    }
+
+    return status;
+}
+
+int8_t RegionAS923AlternateDr( AlternateDrParams_t* alternateDr )
+{
+    // Only AS923_DWELL_LIMIT_DATARATE is supported
+    return AS923_DWELL_LIMIT_DATARATE;
+}
+
+void RegionAS923CalcBackOff( CalcBackOffParams_t* calcBackOff )
+{
+    RegionCommonCalcBackOffParams_t calcBackOffParams;
+
+    calcBackOffParams.Channels = Channels;
+    calcBackOffParams.Bands = Bands;
+    calcBackOffParams.LastTxIsJoinRequest = calcBackOff->LastTxIsJoinRequest;
+    calcBackOffParams.Joined = calcBackOff->Joined;
+    calcBackOffParams.DutyCycleEnabled = calcBackOff->DutyCycleEnabled;
+    calcBackOffParams.Channel = calcBackOff->Channel;
+    calcBackOffParams.ElapsedTime = calcBackOff->ElapsedTime;
+    calcBackOffParams.TxTimeOnAir = calcBackOff->TxTimeOnAir;
+
+    RegionCommonCalcBackOff( &calcBackOffParams );
+}
+
+bool RegionAS923NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff )
+{
+    uint8_t channelNext = 0;
+    uint8_t nbEnabledChannels = 0;
+    uint8_t delayTx = 0;
+    uint8_t enabledChannels[AS923_MAX_NB_CHANNELS] = { 0 };
+    TimerTime_t nextTxDelay = 0;
+
+    if( RegionCommonCountChannels( ChannelsMask, 0, 1 ) == 0 )
+    { // Reactivate default channels
+        ChannelsMask[0] |= LC( 1 ) + LC( 2 );
+    }
+
+    if( nextChanParams->AggrTimeOff <= TimerGetElapsedTime( nextChanParams->LastAggrTx ) )
+    {
+        // Reset Aggregated time off
+        *aggregatedTimeOff = 0;
+
+        // Update bands Time OFF
+        nextTxDelay = RegionCommonUpdateBandTimeOff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, Bands, AS923_MAX_NB_BANDS );
+
+        // Search how many channels are enabled
+        nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Joined, nextChanParams->Datarate,
+                                                      ChannelsMask, Channels,
+                                                      Bands, enabledChannels, &delayTx );
+    }
+    else
+    {
+        delayTx++;
+        nextTxDelay = nextChanParams->AggrTimeOff - TimerGetElapsedTime( nextChanParams->LastAggrTx );
+    }
+
+    if( nbEnabledChannels > 0 )
+    {
+        for( uint8_t  i = 0, j = randr( 0, nbEnabledChannels - 1 ); i < AS923_MAX_NB_CHANNELS; i++ )
+        {
+            channelNext = enabledChannels[j];
+            j = ( j + 1 ) % nbEnabledChannels;
+
+            // Perform carrier sense for AS923_CARRIER_SENSE_TIME
+            // If the channel is free, we can stop the LBT mechanism
+            if( Radio.IsChannelFree( MODEM_LORA, Channels[channelNext].Frequency, AS923_RSSI_FREE_TH, AS923_CARRIER_SENSE_TIME ) == true )
+            {
+                // Free channel found
+                *channel = channelNext;
+                *time = 0;
+                return true;
+            }
+        }
+        return false;
+    }
+    else
+    {
+        if( delayTx > 0 )
+        {
+            // Delay transmission due to AggregatedTimeOff or to a band time off
+            *time = nextTxDelay;
+            return true;
+        }
+        // Datarate not supported by any channel, restore defaults
+        ChannelsMask[0] |= LC( 1 ) + LC( 2 );
+        *time = 0;
+        return false;
+    }
+}
+
+LoRaMacStatus_t RegionAS923ChannelAdd( ChannelAddParams_t* channelAdd )
+{
+    uint8_t band = 0;
+    bool drInvalid = false;
+    bool freqInvalid = false;
+    uint8_t id = channelAdd->ChannelId;
+
+    if( id >= AS923_MAX_NB_CHANNELS )
+    {
+        return LORAMAC_STATUS_PARAMETER_INVALID;
+    }
+
+    // Validate the datarate range
+    if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Min, AS923_TX_MIN_DATARATE, AS923_TX_MAX_DATARATE ) == false )
+    {
+        drInvalid = true;
+    }
+    if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, AS923_TX_MIN_DATARATE, AS923_TX_MAX_DATARATE ) == false )
+    {
+        drInvalid = true;
+    }
+    if( channelAdd->NewChannel->DrRange.Fields.Min > channelAdd->NewChannel->DrRange.Fields.Max )
+    {
+        drInvalid = true;
+    }
+
+    // Default channels don't accept all values
+    if( id < AS923_NUMB_DEFAULT_CHANNELS )
+    {
+        // Validate the datarate range for min: must be DR_0
+        if( channelAdd->NewChannel->DrRange.Fields.Min > DR_0 )
+        {
+            drInvalid = true;
+        }
+        // Validate the datarate range for max: must be DR_5 <= Max <= TX_MAX_DATARATE
+        if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, DR_5, AS923_TX_MAX_DATARATE ) == false )
+        {
+            drInvalid = true;
+        }
+        // We are not allowed to change the frequency
+        if( channelAdd->NewChannel->Frequency != Channels[id].Frequency )
+        {
+            freqInvalid = true;
+        }
+    }
+
+    // Check frequency
+    if( freqInvalid == false )
+    {
+        if( VerifyTxFreq( channelAdd->NewChannel->Frequency ) == false )
+        {
+            freqInvalid = true;
+        }
+    }
+
+    // Check status
+    if( ( drInvalid == true ) && ( freqInvalid == true ) )
+    {
+        return LORAMAC_STATUS_FREQ_AND_DR_INVALID;
+    }
+    if( drInvalid == true )
+    {
+        return LORAMAC_STATUS_DATARATE_INVALID;
+    }
+    if( freqInvalid == true )
+    {
+        return LORAMAC_STATUS_FREQUENCY_INVALID;
+    }
+
+    memcpy( &(Channels[id]), channelAdd->NewChannel, sizeof( Channels[id] ) );
+    Channels[id].Band = band;
+    ChannelsMask[0] |= ( 1 << id );
+    return LORAMAC_STATUS_OK;
+}
+
+bool RegionAS923ChannelsRemove( ChannelRemoveParams_t* channelRemove  )
+{
+    uint8_t id = channelRemove->ChannelId;
+
+    if( id < AS923_NUMB_DEFAULT_CHANNELS )
+    {
+        return false;
+    }
+
+    // Remove the channel from the list of channels
+    Channels[id] = ( ChannelParams_t ){ 0, 0, { 0 }, 0 };
+
+    return RegionCommonChanDisable( ChannelsMask, id, AS923_MAX_NB_CHANNELS );
+}
+
+void RegionAS923SetContinuousWave( ContinuousWaveParams_t* continuousWave )
+{
+    int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, Bands[Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, ChannelsMask );
+    int8_t phyTxPower = 0;
+    uint32_t frequency = Channels[continuousWave->Channel].Frequency;
+
+    // Calculate physical TX power
+    phyTxPower = RegionCommonComputeTxPower( txPowerLimited, continuousWave->MaxEirp, continuousWave->AntennaGain );
+
+    Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout );
+}
+
+uint8_t RegionAS923ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset )
+{
+    // Initialize minDr for a downlink dwell time configuration of 0
+    int8_t minDr = DR_0;
+
+    // Update the minDR for a downlink dwell time configuration of 1
+    if( downlinkDwellTime == 1 )
+    {
+        minDr = AS923_DWELL_LIMIT_DATARATE;
+    }
+
+    // Apply offset formula
+    return MIN( DR_5, MAX( minDr, dr - EffectiveRx1DrOffsetAS923[drOffset] ) );
+}
+
+void RegionAS923RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr, bool *beaconChannelSet )
+{
+    RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup;
+
+    regionCommonRxBeaconSetup.Datarates = DataratesAS923;
+    regionCommonRxBeaconSetup.ChannelPlanFrequency = AS923_BEACON_CHANNEL_FREQ;
+    regionCommonRxBeaconSetup.BeaconTimingAnsFrequency = AS923_BEACON_CHANNEL_FREQ;
+    regionCommonRxBeaconSetup.BeaconSize = AS923_BEACON_SIZE;
+    regionCommonRxBeaconSetup.BeaconDatarate = AS923_BEACON_CHANNEL_DR;
+    regionCommonRxBeaconSetup.BeaconChannelBW = AS923_BEACON_CHANNEL_BW;
+    regionCommonRxBeaconSetup.CustomFrequency = rxBeaconSetup->CustomFrequency;
+    regionCommonRxBeaconSetup.CustomFrequencyEnabled = rxBeaconSetup->CustomFrequencyEnabled;
+    regionCommonRxBeaconSetup.BeaconChannelSet = rxBeaconSetup->BeaconChannelSet;
+    regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime;
+    regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout;
+
+    RegionCommonRxBeaconSetup( &regionCommonRxBeaconSetup, beaconChannelSet );
+
+    // Store downlink datarate
+    *outDr = AS923_BEACON_CHANNEL_DR;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/region/RegionAS923.h	Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,593 @@
+/*!
+ * \file      RegionAS923.h
+ *
+ * \brief     Region definition for AS923
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ *                ______                              _
+ *               / _____)             _              | |
+ *              ( (____  _____ ____ _| |_ _____  ____| |__
+ *               \____ \| ___ |    (_   _) ___ |/ ___)  _ \
+ *               _____) ) ____| | | || |_| ____( (___| | | |
+ *              (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ *              (C)2013 Semtech
+ *
+ *               ___ _____ _   ___ _  _____ ___  ___  ___ ___
+ *              / __|_   _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ *              \__ \ | |/ _ \ (__| ' <| _| (_) |   / (__| _|
+ *              |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ *              embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author    Miguel Luis ( Semtech )
+ *
+ * \author    Gregory Cristian ( Semtech )
+ *
+ * \author    Daniel Jaeckle ( STACKFORCE )
+ *
+ * \defgroup  REGIONAS923 Region AS923
+ *            Implementation according to LoRaWAN Specification v1.0.2.
+ * \{
+ */
+#ifndef __REGION_AS923_H__
+#define __REGION_AS923_H__
+
+/*!
+ * LoRaMac maximum number of channels
+ */
+#define AS923_MAX_NB_CHANNELS                       16
+
+/*!
+ * Number of default channels
+ */
+#define AS923_NUMB_DEFAULT_CHANNELS                 2
+
+/*!
+ * Number of channels to apply for the CF list
+ */
+#define AS923_NUMB_CHANNELS_CF_LIST                 5
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define AS923_TX_MIN_DATARATE                       DR_0
+
+/*!
+ * Maximal datarate that can be used by the node
+ */
+#define AS923_TX_MAX_DATARATE                       DR_7
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define AS923_RX_MIN_DATARATE                       DR_0
+
+/*!
+ * Maximal datarate that can be used by the node
+ */
+#define AS923_RX_MAX_DATARATE                       DR_7
+
+/*!
+ * Default datarate used by the node
+ */
+#define AS923_DEFAULT_DATARATE                      DR_2
+
+/*!
+ * The minimum datarate which is used when the
+ * dwell time is limited.
+ */
+#define AS923_DWELL_LIMIT_DATARATE                  DR_2
+
+/*!
+ * Minimal Rx1 receive datarate offset
+ */
+#define AS923_MIN_RX1_DR_OFFSET                     0
+
+/*!
+ * Maximal Rx1 receive datarate offset
+ */
+#define AS923_MAX_RX1_DR_OFFSET                     7
+
+/*!
+ * Default Rx1 receive datarate offset
+ */
+#define AS923_DEFAULT_RX1_DR_OFFSET                 0
+
+/*!
+ * Minimal Tx output power that can be used by the node
+ */
+#define AS923_MIN_TX_POWER                          TX_POWER_7
+
+/*!
+ * Maximal Tx output power that can be used by the node
+ */
+#define AS923_MAX_TX_POWER                          TX_POWER_0
+
+/*!
+ * Default Tx output power used by the node
+ */
+#define AS923_DEFAULT_TX_POWER                      TX_POWER_0
+
+/*!
+ * Default uplink dwell time configuration
+ */
+#define AS923_DEFAULT_UPLINK_DWELL_TIME             1
+
+/*!
+ * Default downlink dwell time configuration
+ */
+#define AS923_DEFAULT_DOWNLINK_DWELL_TIME           1
+
+/*!
+ * Default Max EIRP
+ */
+#define AS923_DEFAULT_MAX_EIRP                      16.0f
+
+/*!
+ * Default antenna gain
+ */
+#define AS923_DEFAULT_ANTENNA_GAIN                  2.15f
+
+/*!
+ * ADR Ack limit
+ */
+#define AS923_ADR_ACK_LIMIT                         64
+
+/*!
+ * ADR Ack delay
+ */
+#define AS923_ADR_ACK_DELAY                         32
+
+/*!
+ * Enabled or disabled the duty cycle
+ */
+#define AS923_DUTY_CYCLE_ENABLED                    0
+
+/*!
+ * Maximum RX window duration
+ */
+#define AS923_MAX_RX_WINDOW                         3000
+
+/*!
+ * Receive delay 1
+ */
+#define AS923_RECEIVE_DELAY1                        1000
+
+/*!
+ * Receive delay 2
+ */
+#define AS923_RECEIVE_DELAY2                        2000
+
+/*!
+ * Join accept delay 1
+ */
+#define AS923_JOIN_ACCEPT_DELAY1                    5000
+
+/*!
+ * Join accept delay 2
+ */
+#define AS923_JOIN_ACCEPT_DELAY2                    6000
+
+/*!
+ * Maximum frame counter gap
+ */
+#define AS923_MAX_FCNT_GAP                          16384
+
+/*!
+ * Ack timeout
+ */
+#define AS923_ACKTIMEOUT                            2000
+
+/*!
+ * Random ack timeout limits
+ */
+#define AS923_ACK_TIMEOUT_RND                       1000
+
+#if ( AS923_DEFAULT_DATARATE > DR_5 )
+#error "A default DR higher than DR_5 may lead to connectivity loss."
+#endif
+
+/*!
+ * Second reception window channel frequency definition.
+ */
+#define AS923_RX_WND_2_FREQ                         923200000
+
+/*!
+ * Second reception window channel datarate definition.
+ */
+#define AS923_RX_WND_2_DR                           DR_2
+
+/*
+ * CLASS B
+ */
+/*!
+ * Beacon interval in ms
+ */
+#define AS923_BEACON_INTERVAL                       128000
+
+/*!
+ * Beacon reserved time in ms
+ */
+#define AS923_BEACON_RESERVED                       2120
+
+/*!
+ * Beacon guard time in ms
+ */
+#define AS923_BEACON_GUARD                          3000
+
+/*!
+ * Beacon window time in ms
+ */
+#define AS923_BEACON_WINDOW                         122880
+
+/*!
+ * Beacon window time in numer of slots
+ */
+#define AS923_BEACON_WINDOW_SLOTS                   4096
+
+/*!
+ * Ping slot length time in ms
+ */
+#define AS923_PING_SLOT_WINDOW                      30
+
+/*!
+ * Default symbol timeout for beacons and ping slot windows
+ */
+#define AS923_BEACON_SYMBOL_TO_DEFAULT              8
+
+/*!
+ * Maximum symbol timeout for beacons
+ */
+#define AS923_BEACON_SYMBOL_TO_EXPANSION_MAX        400
+
+/*!
+ * Maximum symbol timeout for ping slots
+ */
+#define AS923_PING_SLOT_SYMBOL_TO_EXPANSION_MAX     40
+
+/*!
+ * Symbol expansion value for beacon windows in case of beacon
+ * loss in symbols
+ */
+#define AS923_BEACON_SYMBOL_TO_EXPANSION_FACTOR     2
+
+/*!
+ * Symbol expansion value for ping slot windows in case of beacon
+ * loss in symbols
+ */
+#define AS923_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR  2
+
+/*!
+ * Maximum allowed beacon less time in ms
+ */
+#define AS923_MAX_BEACON_LESS_PERIOD                7200000
+
+/*!
+ * Delay time for the BeaconTimingAns in ms
+ */
+#define AS923_BEACON_DELAY_BEACON_TIMING_ANS        30
+
+/*!
+ * Beacon frequency
+ */
+#define AS923_BEACON_CHANNEL_FREQ                   923400000
+
+/*!
+ * Payload size of a beacon frame
+ */
+#define AS923_BEACON_SIZE                           17
+
+/*!
+ * Datarate of the beacon channel
+ */
+#define AS923_BEACON_CHANNEL_DR                     DR_3
+
+/*!
+ * Bandwith of the beacon channel
+ */
+#define AS923_BEACON_CHANNEL_BW                     0
+
+/*!
+ * Maximum number of bands
+ */
+#define AS923_MAX_NB_BANDS                          1
+
+/*!
+ * Band 0 definition
+ * { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff }
+ */
+#define AS923_BAND0                                 { 100, AS923_MAX_TX_POWER, 0,  0 } //  1.0 %
+
+/*!
+ * LoRaMac default channel 1
+ * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
+ */
+#define AS923_LC1                                   { 923200000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
+
+/*!
+ * LoRaMac default channel 2
+ * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
+ */
+#define AS923_LC2                                   { 923400000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
+
+/*!
+ * LoRaMac channels which are allowed for the join procedure
+ */
+#define AS923_JOIN_CHANNELS                         ( uint16_t )( LC( 1 ) | LC( 2 ) )
+
+/*!
+ * RSSI threshold for a free channel [dBm]
+ */
+#define AS923_RSSI_FREE_TH                          -85
+
+/*!
+ * Specifies the time the node performs a carrier sense
+ */
+#define AS923_CARRIER_SENSE_TIME                    6
+
+/*!
+ * Data rates table definition
+ */
+static const uint8_t DataratesAS923[]  = { 12, 11, 10,  9,  8,  7, 7, 50 };
+
+/*!
+ * Bandwidths table definition in Hz
+ */
+static const uint32_t BandwidthsAS923[] = { 125000, 125000, 125000, 125000, 125000, 125000, 250000, 0 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Cannot operate with repeater.
+ * The table is valid for the dwell time configuration of 0 for uplinks and downlinks.
+ */
+static const uint8_t MaxPayloadOfDatarateDwell0AS923[] = { 51, 51, 51, 115, 242, 242, 242, 242 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Can operate with repeater.
+ * The table is valid for the dwell time configuration of 0 for uplinks and downlinks. The table provides
+ * repeater support.
+ */
+static const uint8_t MaxPayloadOfDatarateRepeaterDwell0AS923[] = { 51, 51, 51, 115, 222, 222, 222, 222 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Can operate with and without repeater.
+ * The table proides repeater support. The table is only valid for uplinks.
+ */
+static const uint8_t MaxPayloadOfDatarateDwell1UpAS923[] = { 0, 0, 11, 53, 125, 242, 242, 242 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Can operate with and without repeater.
+ * The table proides repeater support. The table is only valid for downlinks.
+ */
+static const uint8_t MaxPayloadOfDatarateDwell1DownAS923[] = { 0, 0, 11, 53, 126, 242, 242, 242 };
+
+/*!
+ * Effective datarate offsets for receive window 1.
+ */
+static const int8_t EffectiveRx1DrOffsetAS923[] = { 0, 1, 2, 3, 4, 5, -1, -2 };
+
+/*!
+ * \brief The function gets a value of a specific phy attribute.
+ *
+ * \param [IN] getPhy Pointer to the function parameters.
+ *
+ * \retval Returns a structure containing the PHY parameter.
+ */
+PhyParam_t RegionAS923GetPhyParam( GetPhyParams_t* getPhy );
+
+/*!
+ * \brief Updates the last TX done parameters of the current channel.
+ *
+ * \param [IN] txDone Pointer to the function parameters.
+ */
+void RegionAS923SetBandTxDone( SetBandTxDoneParams_t* txDone );
+
+/*!
+ * \brief Initializes the channels masks and the channels.
+ *
+ * \param [IN] type Sets the initialization type.
+ */
+void RegionAS923InitDefaults( InitType_t type );
+
+/*!
+ * \brief Verifies a parameter.
+ *
+ * \param [IN] verify Pointer to the function parameters.
+ *
+ * \param [IN] type Sets the initialization type.
+ *
+ * \retval Returns true, if the parameter is valid.
+ */
+bool RegionAS923Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute );
+
+/*!
+ * \brief The function parses the input buffer and sets up the channels of the
+ *        CF list.
+ *
+ * \param [IN] applyCFList Pointer to the function parameters.
+ */
+void RegionAS923ApplyCFList( ApplyCFListParams_t* applyCFList );
+
+/*!
+ * \brief Sets a channels mask.
+ *
+ * \param [IN] chanMaskSet Pointer to the function parameters.
+ *
+ * \retval Returns true, if the channels mask could be set.
+ */
+bool RegionAS923ChanMaskSet( ChanMaskSetParams_t* chanMaskSet );
+
+/*!
+ * \brief Calculates the next datarate to set, when ADR is on or off.
+ *
+ * \param [IN] adrNext Pointer to the function parameters.
+ *
+ * \param [OUT] drOut The calculated datarate for the next TX.
+ *
+ * \param [OUT] txPowOut The TX power for the next TX.
+ *
+ * \param [OUT] adrAckCounter The calculated ADR acknowledgement counter.
+ *
+ * \retval Returns true, if an ADR request should be performed.
+ */
+bool RegionAS923AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter );
+
+/*!
+ * Computes the Rx window timeout and offset.
+ *
+ * \param [IN] datarate     Rx window datarate index to be used
+ *
+ * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame.
+ *
+ * \param [IN] rxError      System maximum timing error of the receiver. In milliseconds
+ *                          The receiver will turn on in a [-rxError : +rxError] ms
+ *                          interval around RxOffset
+ *
+ * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields.
+ */
+void RegionAS923ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams );
+
+/*!
+ * \brief Configuration of the RX windows.
+ *
+ * \param [IN] rxConfig Pointer to the function parameters.
+ *
+ * \param [OUT] datarate The datarate index which was set.
+ *
+ * \retval Returns true, if the configuration was applied successfully.
+ */
+bool RegionAS923RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate );
+
+/*!
+ * \brief TX configuration.
+ *
+ * \param [IN] txConfig Pointer to the function parameters.
+ *
+ * \param [OUT] txPower The tx power index which was set.
+ *
+ * \param [OUT] txTimeOnAir The time-on-air of the frame.
+ *
+ * \retval Returns true, if the configuration was applied successfully.
+ */
+bool RegionAS923TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir );
+
+/*!
+ * \brief The function processes a Link ADR Request.
+ *
+ * \param [IN] linkAdrReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionAS923LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed );
+
+/*!
+ * \brief The function processes a RX Parameter Setup Request.
+ *
+ * \param [IN] rxParamSetupReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionAS923RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq );
+
+/*!
+ * \brief The function processes a Channel Request.
+ *
+ * \param [IN] newChannelReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionAS923NewChannelReq( NewChannelReqParams_t* newChannelReq );
+
+/*!
+ * \brief The function processes a TX ParamSetup Request.
+ *
+ * \param [IN] txParamSetupReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ *         Returns -1, if the functionality is not implemented. In this case, the end node
+ *         shall not process the command.
+ */
+int8_t RegionAS923TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq );
+
+/*!
+ * \brief The function processes a DlChannel Request.
+ *
+ * \param [IN] dlChannelReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionAS923DlChannelReq( DlChannelReqParams_t* dlChannelReq );
+
+/*!
+ * \brief Alternates the datarate of the channel for the join request.
+ *
+ * \param [IN] alternateDr Pointer to the function parameters.
+ *
+ * \retval Datarate to apply.
+ */
+int8_t RegionAS923AlternateDr( AlternateDrParams_t* alternateDr );
+
+/*!
+ * \brief Calculates the back-off time.
+ *
+ * \param [IN] calcBackOff Pointer to the function parameters.
+ */
+void RegionAS923CalcBackOff( CalcBackOffParams_t* calcBackOff );
+
+/*!
+ * \brief Searches and set the next random available channel
+ *
+ * \param [OUT] channel Next channel to use for TX.
+ *
+ * \param [OUT] time Time to wait for the next transmission according to the duty
+ *              cycle.
+ *
+ * \param [OUT] aggregatedTimeOff Updates the aggregated time off.
+ *
+ * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate]
+ */
+bool RegionAS923NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff );
+
+/*!
+ * \brief Adds a channel.
+ *
+ * \param [IN] channelAdd Pointer to the function parameters.
+ *
+ * \retval Status of the operation.
+ */
+LoRaMacStatus_t RegionAS923ChannelAdd( ChannelAddParams_t* channelAdd );
+
+/*!
+ * \brief Removes a channel.
+ *
+ * \param [IN] channelRemove Pointer to the function parameters.
+ *
+ * \retval Returns true, if the channel was removed successfully.
+ */
+bool RegionAS923ChannelsRemove( ChannelRemoveParams_t* channelRemove  );
+
+/*!
+ * \brief Sets the radio into continuous wave mode.
+ *
+ * \param [IN] continuousWave Pointer to the function parameters.
+ */
+void RegionAS923SetContinuousWave( ContinuousWaveParams_t* continuousWave );
+
+/*!
+ * \brief Computes new datarate according to the given offset
+ *
+ * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms
+ *
+ * \param [IN] dr Current datarate
+ *
+ * \param [IN] drOffset Offset to be applied
+ *
+ * \retval newDr Computed datarate.
+ */
+uint8_t RegionAS923ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset );
+
+/*! \} defgroup REGIONAS923 */
+
+#endif // __REGION_AS923_H__
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/region/RegionAU915.cpp	Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,968 @@
+/*
+ / _____)             _              | |
+( (____  _____ ____ _| |_ _____  ____| |__
+ \____ \| ___ |    (_   _) ___ |/ ___)  _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+    (C)2013 Semtech
+ ___ _____ _   ___ _  _____ ___  ___  ___ ___
+/ __|_   _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+\__ \ | |/ _ \ (__| ' <| _| (_) |   / (__| _|
+|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+embedded.connectivity.solutions===============
+
+Description: LoRa MAC region AU915 implementation
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+
+Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE )
+*/
+#include <stdbool.h>
+#include <string.h>
+#include <stdint.h>
+#include <math.h>
+
+#include "board.h"
+#include "LoRaMac.h"
+
+#include "utilities.h"
+
+#include "Region.h"
+#include "RegionCommon.h"
+#include "RegionAU915.h"
+
+// Definitions
+#define CHANNELS_MASK_SIZE              6
+
+// Global attributes
+/*!
+ * LoRaMAC channels
+ */
+static ChannelParams_t Channels[AU915_MAX_NB_CHANNELS];
+
+/*!
+ * LoRaMac bands
+ */
+static Band_t Bands[AU915_MAX_NB_BANDS] =
+{
+    AU915_BAND0
+};
+
+/*!
+ * LoRaMac channels mask
+ */
+static uint16_t ChannelsMask[CHANNELS_MASK_SIZE];
+
+/*!
+ * LoRaMac channels remaining
+ */
+static uint16_t ChannelsMaskRemaining[CHANNELS_MASK_SIZE];
+
+/*!
+ * LoRaMac channels default mask
+ */
+static uint16_t ChannelsDefaultMask[CHANNELS_MASK_SIZE];
+
+// Static functions
+static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr )
+{
+    uint8_t nextLowerDr = 0;
+
+    if( dr == minDr )
+    {
+        nextLowerDr = minDr;
+    }
+    else
+    {
+        nextLowerDr = dr - 1;
+    }
+    return nextLowerDr;
+}
+
+static uint32_t GetBandwidth( uint32_t drIndex )
+{
+    switch( BandwidthsAU915[drIndex] )
+    {
+        default:
+        case 125000:
+            return 0;
+        case 250000:
+            return 1;
+        case 500000:
+            return 2;
+    }
+}
+
+static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask )
+{
+    int8_t txPowerResult = txPower;
+
+    // Limit tx power to the band max
+    txPowerResult =  MAX( txPower, maxBandTxPower );
+
+    return txPowerResult;
+}
+
+static uint8_t CountNbOfEnabledChannels( uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx )
+{
+    uint8_t nbEnabledChannels = 0;
+    uint8_t delayTransmission = 0;
+
+    for( uint8_t i = 0, k = 0; i < AU915_MAX_NB_CHANNELS; i += 16, k++ )
+    {
+        for( uint8_t j = 0; j < 16; j++ )
+        {
+            if( ( channelsMask[k] & ( 1 << j ) ) != 0 )
+            {
+                if( channels[i + j].Frequency == 0 )
+                { // Check if the channel is enabled
+                    continue;
+                }
+                if( RegionCommonValueInRange( datarate, channels[i + j].DrRange.Fields.Min,
+                                              channels[i + j].DrRange.Fields.Max ) == false )
+                { // Check if the current channel selection supports the given datarate
+                    continue;
+                }
+                if( bands[channels[i + j].Band].TimeOff > 0 )
+                { // Check if the band is available for transmission
+                    delayTransmission++;
+                    continue;
+                }
+                enabledChannels[nbEnabledChannels++] = i + j;
+            }
+        }
+    }
+
+    *delayTx = delayTransmission;
+    return nbEnabledChannels;
+}
+
+static uint8_t BeaconChannel( uint32_t devAddr, TimerTime_t beaconTime, TimerTime_t beaconInterval )
+{
+    uint32_t frequency = 0;
+
+    frequency = devAddr + ( beaconTime / ( beaconInterval / 1000 ) );
+
+    return ( ( uint8_t )( frequency % 8 ) );
+}
+
+PhyParam_t RegionAU915GetPhyParam( GetPhyParams_t* getPhy )
+{
+    PhyParam_t phyParam = { 0 };
+
+    switch( getPhy->Attribute )
+    {
+        case PHY_MIN_RX_DR:
+        {
+            phyParam.Value = AU915_RX_MIN_DATARATE;
+            break;
+        }
+        case PHY_MIN_TX_DR:
+        {
+            phyParam.Value = AU915_TX_MIN_DATARATE;
+            break;
+        }
+        case PHY_DEF_TX_DR:
+        {
+            phyParam.Value = AU915_DEFAULT_DATARATE;
+            break;
+        }
+        case PHY_NEXT_LOWER_TX_DR:
+        {
+            phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, AU915_TX_MIN_DATARATE );
+            break;
+        }
+        case PHY_DEF_TX_POWER:
+        {
+            phyParam.Value = AU915_DEFAULT_TX_POWER;
+            break;
+        }
+        case PHY_MAX_PAYLOAD:
+        {
+            phyParam.Value = MaxPayloadOfDatarateAU915[getPhy->Datarate];
+            break;
+        }
+        case PHY_MAX_PAYLOAD_REPEATER:
+        {
+            phyParam.Value = MaxPayloadOfDatarateRepeaterAU915[getPhy->Datarate];
+            break;
+        }
+        case PHY_DUTY_CYCLE:
+        {
+            phyParam.Value = AU915_DUTY_CYCLE_ENABLED;
+            break;
+        }
+        case PHY_MAX_RX_WINDOW:
+        {
+            phyParam.Value = AU915_MAX_RX_WINDOW;
+            break;
+        }
+        case PHY_RECEIVE_DELAY1:
+        {
+            phyParam.Value = AU915_RECEIVE_DELAY1;
+            break;
+        }
+        case PHY_RECEIVE_DELAY2:
+        {
+            phyParam.Value = AU915_RECEIVE_DELAY2;
+            break;
+        }
+        case PHY_JOIN_ACCEPT_DELAY1:
+        {
+            phyParam.Value = AU915_JOIN_ACCEPT_DELAY1;
+            break;
+        }
+        case PHY_JOIN_ACCEPT_DELAY2:
+        {
+            phyParam.Value = AU915_JOIN_ACCEPT_DELAY2;
+            break;
+        }
+        case PHY_MAX_FCNT_GAP:
+        {
+            phyParam.Value = AU915_MAX_FCNT_GAP;
+            break;
+        }
+        case PHY_ACK_TIMEOUT:
+        {
+            phyParam.Value = ( AU915_ACKTIMEOUT + randr( -AU915_ACK_TIMEOUT_RND, AU915_ACK_TIMEOUT_RND ) );
+            break;
+        }
+        case PHY_DEF_DR1_OFFSET:
+        {
+            phyParam.Value = AU915_DEFAULT_RX1_DR_OFFSET;
+            break;
+        }
+        case PHY_DEF_RX2_FREQUENCY:
+        {
+            phyParam.Value = AU915_RX_WND_2_FREQ;
+            break;
+        }
+        case PHY_DEF_RX2_DR:
+        {
+            phyParam.Value = AU915_RX_WND_2_DR;
+            break;
+        }
+        case PHY_CHANNELS_MASK:
+        {
+            phyParam.ChannelsMask = ChannelsMask;
+            break;
+        }
+        case PHY_CHANNELS_DEFAULT_MASK:
+        {
+            phyParam.ChannelsMask = ChannelsDefaultMask;
+            break;
+        }
+        case PHY_MAX_NB_CHANNELS:
+        {
+            phyParam.Value = AU915_MAX_NB_CHANNELS;
+            break;
+        }
+        case PHY_CHANNELS:
+        {
+            phyParam.Channels = Channels;
+            break;
+        }
+        case PHY_DEF_UPLINK_DWELL_TIME:
+        case PHY_DEF_DOWNLINK_DWELL_TIME:
+        {
+            phyParam.Value = 0;
+            break;
+        }
+        case PHY_DEF_MAX_EIRP:
+        {
+            phyParam.fValue = AU915_DEFAULT_MAX_EIRP;
+            break;
+        }
+        case PHY_DEF_ANTENNA_GAIN:
+        {
+            phyParam.fValue = AU915_DEFAULT_ANTENNA_GAIN;
+            break;
+        }
+        case PHY_NB_JOIN_TRIALS:
+        case PHY_DEF_NB_JOIN_TRIALS:
+        {
+            phyParam.Value = 2;
+            break;
+        }
+        case PHY_BEACON_INTERVAL:
+        {
+            phyParam.Value = AU915_BEACON_INTERVAL;
+            break;
+        }
+        case PHY_BEACON_RESERVED:
+        {
+            phyParam.Value = AU915_BEACON_RESERVED;
+            break;
+        }
+        case PHY_BEACON_GUARD:
+        {
+            phyParam.Value = AU915_BEACON_GUARD;
+            break;
+        }
+        case PHY_BEACON_WINDOW:
+        {
+            phyParam.Value = AU915_BEACON_WINDOW;
+            break;
+        }
+        case PHY_BEACON_WINDOW_SLOTS:
+        {
+            phyParam.Value = AU915_BEACON_WINDOW_SLOTS;
+            break;
+        }
+        case PHY_PING_SLOT_WINDOW:
+        {
+            phyParam.Value = AU915_PING_SLOT_WINDOW;
+            break;
+        }
+        case PHY_BEACON_SYMBOL_TO_DEFAULT:
+        {
+            phyParam.Value = AU915_BEACON_SYMBOL_TO_DEFAULT;
+            break;
+        }
+        case PHY_BEACON_SYMBOL_TO_EXPANSION_MAX:
+        {
+            phyParam.Value = AU915_BEACON_SYMBOL_TO_EXPANSION_MAX;
+            break;
+        }
+        case PHY_PING_SLOT_SYMBOL_TO_EXPANSION_MAX:
+        {
+            phyParam.Value = AU915_PING_SLOT_SYMBOL_TO_EXPANSION_MAX;
+            break;
+        }
+        case PHY_BEACON_SYMBOL_TO_EXPANSION_FACTOR:
+        {
+            phyParam.Value = AU915_BEACON_SYMBOL_TO_EXPANSION_FACTOR;
+            break;
+        }
+        case PHY_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR:
+        {
+            phyParam.Value = AU915_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR;
+            break;
+        }
+        case PHY_MAX_BEACON_LESS_PERIOD:
+        {
+            phyParam.Value = AU915_MAX_BEACON_LESS_PERIOD;
+            break;
+        }
+        case PHY_BEACON_DELAY_BEACON_TIMING_ANS:
+        {
+            phyParam.Value = AU915_BEACON_DELAY_BEACON_TIMING_ANS;
+            break;
+        }
+        case PHY_PINGSLOT_CHANNEL_FREQ:
+        {
+            phyParam.Value = AU915_BEACON_CHANNEL_FREQ;
+            break;
+        }
+        case PHY_BEACON_SIZE:
+        {
+            phyParam.Value = AU915_BEACON_SIZE;
+            break;
+        }
+        case PHY_BEACON_CHANNEL_DR:
+        {
+            phyParam.Value = AU915_BEACON_CHANNEL_DR;
+            break;
+        }
+        default:
+        {
+            break;
+        }
+    }
+
+    return phyParam;
+}
+
+void RegionAU915SetBandTxDone( SetBandTxDoneParams_t* txDone )
+{
+    RegionCommonSetBandTxDone( txDone->Joined, &Bands[Channels[txDone->Channel].Band], txDone->LastTxDoneTime );
+}
+
+void RegionAU915InitDefaults( InitType_t type )
+{
+    switch( type )
+    {
+        case INIT_TYPE_INIT:
+        {
+            // Channels
+            // 125 kHz channels
+            for( uint8_t i = 0; i < AU915_MAX_NB_CHANNELS - 8; i++ )
+            {
+                Channels[i].Frequency = 915200000 + i * 200000;
+                Channels[i].DrRange.Value = ( DR_5 << 4 ) | DR_0;
+                Channels[i].Band = 0;
+            }
+            // 500 kHz channels
+            for( uint8_t i = AU915_MAX_NB_CHANNELS - 8; i < AU915_MAX_NB_CHANNELS; i++ )
+            {
+                Channels[i].Frequency = 915900000 + ( i - ( AU915_MAX_NB_CHANNELS - 8 ) ) * 1600000;
+                Channels[i].DrRange.Value = ( DR_6 << 4 ) | DR_6;
+                Channels[i].Band = 0;
+            }
+
+            // Initialize channels default mask
+            ChannelsDefaultMask[0] = 0xFFFF;
+            ChannelsDefaultMask[1] = 0xFFFF;
+            ChannelsDefaultMask[2] = 0xFFFF;
+            ChannelsDefaultMask[3] = 0xFFFF;
+            ChannelsDefaultMask[4] = 0x00FF;
+            ChannelsDefaultMask[5] = 0x0000;
+
+            // Copy channels default mask
+            RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 6 );
+
+            // Copy into channels mask remaining
+            RegionCommonChanMaskCopy( ChannelsMaskRemaining, ChannelsMask, 6 );
+            break;
+        }
+        case INIT_TYPE_RESTORE:
+        {
+            // Copy channels default mask
+            RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 6 );
+
+            for( uint8_t i = 0; i < 6; i++ )
+            { // Copy-And the channels mask
+                ChannelsMaskRemaining[i] &= ChannelsMask[i];
+            }
+            break;
+        }
+        default:
+        {
+            break;
+        }
+    }
+}
+
+bool RegionAU915Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute )
+{
+    switch( phyAttribute )
+    {
+        case PHY_TX_DR:
+        case PHY_DEF_TX_DR:
+        {
+            return RegionCommonValueInRange( verify->DatarateParams.Datarate, AU915_TX_MIN_DATARATE, AU915_TX_MAX_DATARATE );
+        }
+        case PHY_RX_DR:
+        {
+            return RegionCommonValueInRange( verify->DatarateParams.Datarate, AU915_RX_MIN_DATARATE, AU915_RX_MAX_DATARATE );
+        }
+        case PHY_DEF_TX_POWER:
+        case PHY_TX_POWER:
+        {
+            // Remark: switched min and max!
+            return RegionCommonValueInRange( verify->TxPower, AU915_MAX_TX_POWER, AU915_MIN_TX_POWER );
+        }
+        case PHY_DUTY_CYCLE:
+        {
+            return AU915_DUTY_CYCLE_ENABLED;
+        }
+        case PHY_NB_JOIN_TRIALS:
+        {
+            if( verify->NbJoinTrials < 2 )
+            {
+                return false;
+            }
+            break;
+        }
+        default:
+            return false;
+    }
+    return true;
+}
+
+void RegionAU915ApplyCFList( ApplyCFListParams_t* applyCFList )
+{
+    return;
+}
+
+bool RegionAU915ChanMaskSet( ChanMaskSetParams_t* chanMaskSet )
+{
+    uint8_t nbChannels = RegionCommonCountChannels( chanMaskSet->ChannelsMaskIn, 0, 4 );
+
+    // Check the number of active channels
+    // According to ACMA regulation, we require at least 20 125KHz channels, if
+    // the node shall utilize 125KHz channels.
+    if( ( nbChannels < 20 ) &&
+        ( nbChannels > 0 ) )
+    {
+        return false;
+    }
+
+    switch( chanMaskSet->ChannelsMaskType )
+    {
+        case CHANNELS_MASK:
+        {
+            RegionCommonChanMaskCopy( ChannelsMask, chanMaskSet->ChannelsMaskIn, 6 );
+
+            for( uint8_t i = 0; i < 6; i++ )
+            { // Copy-And the channels mask
+                ChannelsMaskRemaining[i] &= ChannelsMask[i];
+            }
+            break;
+        }
+        case CHANNELS_DEFAULT_MASK:
+        {
+            RegionCommonChanMaskCopy( ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 6 );
+            break;
+        }
+        default:
+            return false;
+    }
+    return true;
+}
+
+bool RegionAU915AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter )
+{
+    bool adrAckReq = false;
+    int8_t datarate = adrNext->Datarate;
+    int8_t txPower = adrNext->TxPower;
+    GetPhyParams_t getPhy;
+    PhyParam_t phyParam;
+
+    // Report back the adr ack counter
+    *adrAckCounter = adrNext->AdrAckCounter;
+
+    if( adrNext->AdrEnabled == true )
+    {
+        if( datarate == AU915_TX_MIN_DATARATE )
+        {
+            *adrAckCounter = 0;
+            adrAckReq = false;
+        }
+        else
+        {
+            if( adrNext->AdrAckCounter >= AU915_ADR_ACK_LIMIT )
+            {
+                adrAckReq = true;
+                txPower = AU915_MAX_TX_POWER;
+            }
+            else
+            {
+                adrAckReq = false;
+            }
+            if( adrNext->AdrAckCounter >= ( AU915_ADR_ACK_LIMIT + AU915_ADR_ACK_DELAY ) )
+            {
+                if( ( adrNext->AdrAckCounter % AU915_ADR_ACK_DELAY ) == 1 )
+                {
+                    // Decrease the datarate
+                    getPhy.Attribute = PHY_NEXT_LOWER_TX_DR;
+                    getPhy.Datarate = datarate;
+                    getPhy.UplinkDwellTime = adrNext->UplinkDwellTime;
+                    phyParam = RegionAU915GetPhyParam( &getPhy );
+                    datarate = phyParam.Value;
+
+                    if( datarate == AU915_TX_MIN_DATARATE )
+                    {
+                        // We must set adrAckReq to false as soon as we reach the lowest datarate
+                        adrAckReq = false;
+                        if( adrNext->UpdateChanMask == true )
+                        {
+                            // Re-enable default channels
+                            ChannelsMask[0] = 0xFFFF;
+                            ChannelsMask[1] = 0xFFFF;
+                            ChannelsMask[2] = 0xFFFF;
+                            ChannelsMask[3] = 0xFFFF;
+                            ChannelsMask[4] = 0x00FF;
+                            ChannelsMask[5] = 0x0000;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    *drOut = datarate;
+    *txPowOut = txPower;
+    return adrAckReq;
+}
+
+void RegionAU915ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams )
+{
+    double tSymbol = 0.0;
+
+    // Get the datarate, perform a boundary check
+    rxConfigParams->Datarate = MIN( datarate, AU915_RX_MAX_DATARATE );
+    rxConfigParams->Bandwidth = GetBandwidth( rxConfigParams->Datarate );
+
+    tSymbol = RegionCommonComputeSymbolTimeLoRa( DataratesAU915[rxConfigParams->Datarate], BandwidthsAU915[rxConfigParams->Datarate] );
+
+    RegionCommonComputeRxWindowParameters( tSymbol, minRxSymbols, rxError, RADIO_WAKEUP_TIME, &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset );
+}
+
+bool RegionAU915RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate )
+{
+    int8_t dr = rxConfig->Datarate;
+    uint8_t maxPayload = 0;
+    int8_t phyDr = 0;
+    uint32_t frequency = rxConfig->Frequency;
+
+    if( Radio.GetStatus( ) != RF_IDLE )
+    {
+        return false;
+    }
+
+    if( rxConfig->Window == 0 )
+    {
+        // Apply window 1 frequency
+        frequency = AU915_FIRST_RX1_CHANNEL + ( rxConfig->Channel % 8 ) * AU915_STEPWIDTH_RX1_CHANNEL;
+    }
+
+    // Read the physical datarate from the datarates table
+    phyDr = DataratesAU915[dr];
+
+    Radio.SetChannel( frequency );
+
+    // Radio configuration
+    Radio.SetRxConfig( MODEM_LORA, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous );
+
+    if( rxConfig->RepeaterSupport == true )
+    {
+        maxPayload = MaxPayloadOfDatarateRepeaterAU915[dr];
+    }
+    else
+    {
+        maxPayload = MaxPayloadOfDatarateAU915[dr];
+    }
+    Radio.SetMaxPayloadLength( MODEM_LORA, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD );
+
+    *datarate = (uint8_t) dr;
+    return true;
+}
+
+bool RegionAU915TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir )
+{
+    int8_t phyDr = DataratesAU915[txConfig->Datarate];
+    int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, Bands[Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, ChannelsMask );
+    uint32_t bandwidth = GetBandwidth( txConfig->Datarate );
+    int8_t phyTxPower = 0;
+
+    // Calculate physical TX power
+    phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain );
+
+    // Setup the radio frequency
+    Radio.SetChannel( Channels[txConfig->Channel].Frequency );
+
+    Radio.SetTxConfig( MODEM_LORA, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 3000 );
+
+    // Setup maximum payload lenght of the radio driver
+    Radio.SetMaxPayloadLength( MODEM_LORA, txConfig->PktLen );
+
+    *txTimeOnAir = Radio.TimeOnAir( MODEM_LORA, txConfig->PktLen );
+    *txPower = txPowerLimited;
+
+    return true;
+}
+
+uint8_t RegionAU915LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed )
+{
+    uint8_t status = 0x07;
+    RegionCommonLinkAdrParams_t linkAdrParams;
+    uint8_t nextIndex = 0;
+    uint8_t bytesProcessed = 0;
+    uint16_t channelsMask[6] = { 0, 0, 0, 0, 0, 0 };
+    GetPhyParams_t getPhy;
+    PhyParam_t phyParam;
+    RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams;
+
+    // Initialize local copy of channels mask
+    RegionCommonChanMaskCopy( channelsMask, ChannelsMask, 6 );
+
+    while( bytesProcessed < linkAdrReq->PayloadSize )
+    {
+        nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams );
+
+        if( nextIndex == 0 )
+            break; // break loop, since no more request has been found
+
+        // Update bytes processed
+        bytesProcessed += nextIndex;
+
+        // Revert status, as we only check the last ADR request for the channel mask KO
+        status = 0x07;
+
+        if( linkAdrParams.ChMaskCtrl == 6 )
+        {
+            // Enable all 125 kHz channels
+            channelsMask[0] = 0xFFFF;
+            channelsMask[1] = 0xFFFF;
+            channelsMask[2] = 0xFFFF;
+            channelsMask[3] = 0xFFFF;
+            // Apply chMask to channels 64 to 71
+            channelsMask[4] = linkAdrParams.ChMask;
+        }
+        else if( linkAdrParams.ChMaskCtrl == 7 )
+        {
+            // Disable all 125 kHz channels
+            channelsMask[0] = 0x0000;
+            channelsMask[1] = 0x0000;
+            channelsMask[2] = 0x0000;
+            channelsMask[3] = 0x0000;
+            // Apply chMask to channels 64 to 71
+            channelsMask[4] = linkAdrParams.ChMask;
+        }
+        else if( linkAdrParams.ChMaskCtrl == 5 )
+        {
+            // RFU
+            status &= 0xFE; // Channel mask KO
+        }
+        else
+        {
+            channelsMask[linkAdrParams.ChMaskCtrl] = linkAdrParams.ChMask;
+        }
+    }
+
+    // FCC 15.247 paragraph F mandates to hop on at least 2 125 kHz channels
+    if( ( linkAdrParams.Datarate < DR_6 ) && ( RegionCommonCountChannels( channelsMask, 0, 4 ) < 2 ) )
+    {
+        status &= 0xFE; // Channel mask KO
+    }
+
+    // Get the minimum possible datarate
+    getPhy.Attribute = PHY_MIN_TX_DR;
+    getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime;
+    phyParam = RegionAU915GetPhyParam( &getPhy );
+
+    linkAdrVerifyParams.Status = status;
+    linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled;
+    linkAdrVerifyParams.Datarate = linkAdrParams.Datarate;
+    linkAdrVerifyParams.TxPower = linkAdrParams.TxPower;
+    linkAdrVerifyParams.NbRep = linkAdrParams.NbRep;
+    linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate;
+    linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower;
+    linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep;
+    linkAdrVerifyParams.NbChannels = AU915_MAX_NB_CHANNELS;
+    linkAdrVerifyParams.ChannelsMask = channelsMask;
+    linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value;
+    linkAdrVerifyParams.MaxDatarate = AU915_TX_MAX_DATARATE;
+    linkAdrVerifyParams.Channels = Channels;
+    linkAdrVerifyParams.MinTxPower = AU915_MIN_TX_POWER;
+    linkAdrVerifyParams.MaxTxPower = AU915_MAX_TX_POWER;
+
+    // Verify the parameters and update, if necessary
+    status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep );
+
+    // Update channelsMask if everything is correct
+    if( status == 0x07 )
+    {
+        // Copy Mask
+        RegionCommonChanMaskCopy( ChannelsMask, channelsMask, 6 );
+
+        ChannelsMaskRemaining[0] &= ChannelsMask[0];
+        ChannelsMaskRemaining[1] &= ChannelsMask[1];
+        ChannelsMaskRemaining[2] &= ChannelsMask[2];
+        ChannelsMaskRemaining[3] &= ChannelsMask[3];
+        ChannelsMaskRemaining[4] = ChannelsMask[4];
+        ChannelsMaskRemaining[5] = ChannelsMask[5];
+    }
+
+    // Update status variables
+    *drOut = linkAdrParams.Datarate;
+    *txPowOut = linkAdrParams.TxPower;
+    *nbRepOut = linkAdrParams.NbRep;
+    *nbBytesParsed = bytesProcessed;
+
+    return status;
+}
+
+uint8_t RegionAU915RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq )
+{
+    uint8_t status = 0x07;
+    uint32_t freq = rxParamSetupReq->Frequency;
+
+    // Verify radio frequency
+    if( ( Radio.CheckRfFrequency( freq ) == false ) ||
+        ( freq < AU915_FIRST_RX1_CHANNEL ) ||
+        ( freq > AU915_LAST_RX1_CHANNEL ) ||
+        ( ( ( freq - ( uint32_t ) AU915_FIRST_RX1_CHANNEL ) % ( uint32_t ) AU915_STEPWIDTH_RX1_CHANNEL ) != 0 ) )
+    {
+        status &= 0xFE; // Channel frequency KO
+    }
+
+    // Verify datarate
+    if( RegionCommonValueInRange( rxParamSetupReq->Datarate, AU915_RX_MIN_DATARATE, AU915_RX_MAX_DATARATE ) == false )
+    {
+        status &= 0xFD; // Datarate KO
+    }
+    if( ( rxParamSetupReq->Datarate == DR_7 ) ||
+        ( rxParamSetupReq->Datarate > DR_13 ) )
+    {
+        status &= 0xFD; // Datarate KO
+    }
+
+    // Verify datarate offset
+    if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, AU915_MIN_RX1_DR_OFFSET, AU915_MAX_RX1_DR_OFFSET ) == false )
+    {
+        status &= 0xFB; // Rx1DrOffset range KO
+    }
+
+    return status;
+}
+
+uint8_t RegionAU915NewChannelReq( NewChannelReqParams_t* newChannelReq )
+{
+    // Datarate and frequency KO
+    return 0;
+}
+
+int8_t RegionAU915TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq )
+{
+    return -1;
+}
+
+uint8_t RegionAU915DlChannelReq( DlChannelReqParams_t* dlChannelReq )
+{
+    return 0;
+}
+
+int8_t RegionAU915AlternateDr( AlternateDrParams_t* alternateDr )
+{
+    int8_t datarate = 0;
+
+    // Re-enable 500 kHz default channels
+    ChannelsMask[4] = 0x00FF;
+
+    if( ( alternateDr->NbTrials & 0x01 ) == 0x01 )
+    {
+        datarate = DR_6;
+    }
+    else
+    {
+        datarate = DR_0;
+    }
+    return datarate;
+}
+
+void RegionAU915CalcBackOff( CalcBackOffParams_t* calcBackOff )
+{
+    RegionCommonCalcBackOffParams_t calcBackOffParams;
+
+    calcBackOffParams.Channels = Channels;
+    calcBackOffParams.Bands = Bands;
+    calcBackOffParams.LastTxIsJoinRequest = calcBackOff->LastTxIsJoinRequest;
+    calcBackOffParams.Joined = calcBackOff->Joined;
+    calcBackOffParams.DutyCycleEnabled = calcBackOff->DutyCycleEnabled;
+    calcBackOffParams.Channel = calcBackOff->Channel;
+    calcBackOffParams.ElapsedTime = calcBackOff->ElapsedTime;
+    calcBackOffParams.TxTimeOnAir = calcBackOff->TxTimeOnAir;
+
+    RegionCommonCalcBackOff( &calcBackOffParams );
+}
+
+bool RegionAU915NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff )
+{
+    uint8_t nbEnabledChannels = 0;
+    uint8_t delayTx = 0;
+    uint8_t enabledChannels[AU915_MAX_NB_CHANNELS] = { 0 };
+    TimerTime_t nextTxDelay = 0;
+
+    // Count 125kHz channels
+    if( RegionCommonCountChannels( ChannelsMaskRemaining, 0, 4 ) == 0 )
+    { // Reactivate default channels
+        RegionCommonChanMaskCopy( ChannelsMaskRemaining, ChannelsMask, 4  );
+    }
+    // Check other channels
+    if( nextChanParams->Datarate >= DR_6 )
+    {
+        if( ( ChannelsMaskRemaining[4] & 0x00FF ) == 0 )
+        {
+            ChannelsMaskRemaining[4] = ChannelsMask[4];
+        }
+    }
+
+    if( nextChanParams->AggrTimeOff <= TimerGetElapsedTime( nextChanParams->LastAggrTx ) )
+    {
+        // Reset Aggregated time off
+        *aggregatedTimeOff = 0;
+
+        // Update bands Time OFF
+        nextTxDelay = RegionCommonUpdateBandTimeOff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, Bands, AU915_MAX_NB_BANDS );
+
+        // Search how many channels are enabled
+        nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Datarate,
+                                                      ChannelsMaskRemaining, Channels,
+                                                      Bands, enabledChannels, &delayTx );
+    }
+    else
+    {
+        delayTx++;
+        nextTxDelay = nextChanParams->AggrTimeOff - TimerGetElapsedTime( nextChanParams->LastAggrTx );
+    }
+
+    if( nbEnabledChannels > 0 )
+    {
+        // We found a valid channel
+        *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )];
+        // Disable the channel in the mask
+        RegionCommonChanDisable( ChannelsMaskRemaining, *channel, AU915_MAX_NB_CHANNELS - 8 );
+
+        *time = 0;
+        return true;
+    }
+    else
+    {
+        if( delayTx > 0 )
+        {
+            // Delay transmission due to AggregatedTimeOff or to a band time off
+            *time = nextTxDelay;
+            return true;
+        }
+        // Datarate not supported by any channel
+        *time = 0;
+        return false;
+    }
+}
+
+LoRaMacStatus_t RegionAU915ChannelAdd( ChannelAddParams_t* channelAdd )
+{
+    return LORAMAC_STATUS_PARAMETER_INVALID;
+}
+
+bool RegionAU915ChannelsRemove( ChannelRemoveParams_t* channelRemove  )
+{
+    return LORAMAC_STATUS_PARAMETER_INVALID;
+}
+
+void RegionAU915SetContinuousWave( ContinuousWaveParams_t* continuousWave )
+{
+    int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, Bands[Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, ChannelsMask );
+    int8_t phyTxPower = 0;
+    uint32_t frequency = Channels[continuousWave->Channel].Frequency;
+
+    // Calculate physical TX power
+    phyTxPower = RegionCommonComputeTxPower( txPowerLimited, continuousWave->MaxEirp, continuousWave->AntennaGain );
+
+    Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout );
+}
+
+uint8_t RegionAU915ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset )
+{
+    int8_t datarate = DatarateOffsetsAU915[dr][drOffset];
+
+    if( datarate < 0 )
+    {
+        datarate = DR_0;
+    }
+    return datarate;
+}
+
+void RegionAU915RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr, bool *beaconChannelSet )
+{
+    RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup;
+
+    regionCommonRxBeaconSetup.Datarates = DataratesAU915;
+    regionCommonRxBeaconSetup.ChannelPlanFrequency = ( 923.3e6 + ( BeaconChannel( 0, rxBeaconSetup->BeaconTime, rxBeaconSetup->BeaconInterval ) * 600e3 ) );
+    regionCommonRxBeaconSetup.BeaconTimingAnsFrequency = ( 923.3e6 + ( rxBeaconSetup->BeaconTimingChannel * 600e3 ) );
+    regionCommonRxBeaconSetup.BeaconSize = AU915_BEACON_SIZE;
+    regionCommonRxBeaconSetup.BeaconDatarate = AU915_BEACON_CHANNEL_DR;
+    regionCommonRxBeaconSetup.BeaconChannelBW = AU915_BEACON_CHANNEL_BW;
+    regionCommonRxBeaconSetup.CustomFrequency = rxBeaconSetup->CustomFrequency;
+    regionCommonRxBeaconSetup.CustomFrequencyEnabled = rxBeaconSetup->CustomFrequencyEnabled;
+    regionCommonRxBeaconSetup.BeaconChannelSet = rxBeaconSetup->BeaconChannelSet;
+    regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime;
+    regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout;
+
+    RegionCommonRxBeaconSetup( &regionCommonRxBeaconSetup, beaconChannelSet );
+
+    // Store downlink datarate
+    *outDr = AU915_BEACON_CHANNEL_DR;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/region/RegionAU915.h	Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,545 @@
+/*!
+ * \file      RegionAU915.h
+ *
+ * \brief     Region definition for AU915
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ *                ______                              _
+ *               / _____)             _              | |
+ *              ( (____  _____ ____ _| |_ _____  ____| |__
+ *               \____ \| ___ |    (_   _) ___ |/ ___)  _ \
+ *               _____) ) ____| | | || |_| ____( (___| | | |
+ *              (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ *              (C)2013 Semtech
+ *
+ *               ___ _____ _   ___ _  _____ ___  ___  ___ ___
+ *              / __|_   _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ *              \__ \ | |/ _ \ (__| ' <| _| (_) |   / (__| _|
+ *              |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ *              embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author    Miguel Luis ( Semtech )
+ *
+ * \author    Gregory Cristian ( Semtech )
+ *
+ * \author    Daniel Jaeckle ( STACKFORCE )
+ *
+ * \defgroup  REGIONAU915 Region AU915
+ *            Implementation according to LoRaWAN Specification v1.0.2.
+ * \{
+ */
+#ifndef __REGION_AU915_H__
+#define __REGION_AU915_H__
+
+/*!
+ * LoRaMac maximum number of channels
+ */
+#define AU915_MAX_NB_CHANNELS                       72
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define AU915_TX_MIN_DATARATE                       DR_0
+
+/*!
+ * Maximal datarate that can be used by the node
+ */
+#define AU915_TX_MAX_DATARATE                       DR_6
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define AU915_RX_MIN_DATARATE                       DR_8
+
+/*!
+ * Maximal datarate that can be used by the node
+ */
+#define AU915_RX_MAX_DATARATE                       DR_13
+
+/*!
+ * Default datarate used by the node
+ */
+#define AU915_DEFAULT_DATARATE                      DR_0
+
+/*!
+ * Minimal Rx1 receive datarate offset
+ */
+#define AU915_MIN_RX1_DR_OFFSET                     0
+
+/*!
+ * Maximal Rx1 receive datarate offset
+ */
+#define AU915_MAX_RX1_DR_OFFSET                     6
+
+/*!
+ * Default Rx1 receive datarate offset
+ */
+#define AU915_DEFAULT_RX1_DR_OFFSET                 0
+
+/*!
+ * Minimal Tx output power that can be used by the node
+ */
+#define AU915_MIN_TX_POWER                          TX_POWER_10
+
+/*!
+ * Maximal Tx output power that can be used by the node
+ */
+#define AU915_MAX_TX_POWER                          TX_POWER_0
+
+/*!
+ * Default Tx output power used by the node
+ */
+#define AU915_DEFAULT_TX_POWER                      TX_POWER_0
+
+/*!
+ * Default Max EIRP
+ */
+#define AU915_DEFAULT_MAX_EIRP                      30.0f
+
+/*!
+ * Default antenna gain
+ */
+#define AU915_DEFAULT_ANTENNA_GAIN                  2.15f
+
+/*!
+ * ADR Ack limit
+ */
+#define AU915_ADR_ACK_LIMIT                         64
+
+/*!
+ * ADR Ack delay
+ */
+#define AU915_ADR_ACK_DELAY                         32
+
+/*!
+ * Enabled or disabled the duty cycle
+ */
+#define AU915_DUTY_CYCLE_ENABLED                    0
+
+/*!
+ * Maximum RX window duration
+ */
+#define AU915_MAX_RX_WINDOW                         3000
+
+/*!
+ * Receive delay 1
+ */
+#define AU915_RECEIVE_DELAY1                        1000
+
+/*!
+ * Receive delay 2
+ */
+#define AU915_RECEIVE_DELAY2                        2000
+
+/*!
+ * Join accept delay 1
+ */
+#define AU915_JOIN_ACCEPT_DELAY1                    5000
+
+/*!
+ * Join accept delay 2
+ */
+#define AU915_JOIN_ACCEPT_DELAY2                    6000
+
+/*!
+ * Maximum frame counter gap
+ */
+#define AU915_MAX_FCNT_GAP                          16384
+
+/*!
+ * Ack timeout
+ */
+#define AU915_ACKTIMEOUT                            2000
+
+/*!
+ * Random ack timeout limits
+ */
+#define AU915_ACK_TIMEOUT_RND                       1000
+
+/*!
+ * Second reception window channel frequency definition.
+ */
+#define AU915_RX_WND_2_FREQ                         923300000
+
+/*!
+ * Second reception window channel datarate definition.
+ */
+#define AU915_RX_WND_2_DR                           DR_8
+
+/*
+ * CLASS B
+ */
+/*!
+ * Beacon interval in ms
+ */
+#define AU915_BEACON_INTERVAL                       128000
+
+/*!
+ * Beacon reserved time in ms
+ */
+#define AU915_BEACON_RESERVED                       2120
+
+/*!
+ * Beacon guard time in ms
+ */
+#define AU915_BEACON_GUARD                          3000
+
+/*!
+ * Beacon window time in ms
+ */
+#define AU915_BEACON_WINDOW                         122880
+
+/*!
+ * Beacon window time in numer of slots
+ */
+#define AU915_BEACON_WINDOW_SLOTS                   4096
+
+/*!
+ * Ping slot length time in ms
+ */
+#define AU915_PING_SLOT_WINDOW                      30
+
+/*!
+ * Beacon frequency
+ */
+#define AU915_BEACON_CHANNEL_FREQ                  923300000
+
+/*!
+ * Default symbol timeout for beacons and ping slot windows
+ */
+#define AU915_BEACON_SYMBOL_TO_DEFAULT              8
+
+/*!
+ * Maximum symbol timeout for beacons
+ */
+#define AU915_BEACON_SYMBOL_TO_EXPANSION_MAX        400
+
+/*!
+ * Maximum symbol timeout for ping slots
+ */
+#define AU915_PING_SLOT_SYMBOL_TO_EXPANSION_MAX     40
+
+/*!
+ * Symbol expansion value for beacon windows in case of beacon
+ * loss in symbols
+ */
+#define AU915_BEACON_SYMBOL_TO_EXPANSION_FACTOR     2
+
+/*!
+ * Symbol expansion value for ping slot windows in case of beacon
+ * loss in symbols
+ */
+#define AU915_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR  2
+
+/*!
+ * Maximum allowed beacon less time in ms
+ */
+#define AU915_MAX_BEACON_LESS_PERIOD                7200000
+
+/*!
+ * Delay time for the BeaconTimingAns in ms
+ */
+#define AU915_BEACON_DELAY_BEACON_TIMING_ANS        30
+
+/*!
+ * Payload size of a beacon frame
+ */
+#define AU915_BEACON_SIZE                           19
+
+/*!
+ * Datarate of the beacon channel
+ */
+#define AU915_BEACON_CHANNEL_DR                     DR_10
+
+/*!
+ * Bandwith of the beacon channel
+ */
+#define AU915_BEACON_CHANNEL_BW                     2
+
+/*!
+ * LoRaMac maximum number of bands
+ */
+#define AU915_MAX_NB_BANDS                          1
+
+/*!
+ * Band 0 definition
+ * { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff }
+ */
+#define AU915_BAND0                                 { 1, AU915_MAX_TX_POWER, 0,  0 } //  100.0 %
+
+/*!
+ * Defines the first channel for RX window 1 for US band
+ */
+#define AU915_FIRST_RX1_CHANNEL                     ( (uint32_t) 923300000 )
+
+/*!
+ * Defines the last channel for RX window 1 for US band
+ */
+#define AU915_LAST_RX1_CHANNEL                      ( (uint32_t) 927500000 )
+
+/*!
+ * Defines the step width of the channels for RX window 1
+ */
+#define AU915_STEPWIDTH_RX1_CHANNEL                 ( (uint32_t) 600000 )
+
+/*!
+ * Data rates table definition
+ */
+static const uint8_t DataratesAU915[]  = { 12, 11, 10, 9, 8, 7, 8, 0, 12, 11, 10, 9, 8, 7, 0, 0 };
+
+/*!
+ * Bandwidths table definition in Hz
+ */
+static const uint32_t BandwidthsAU915[] = { 125000, 125000, 125000, 125000, 125000, 125000, 500000, 0, 500000, 500000, 500000, 500000, 500000, 500000, 0, 0 };
+
+/*!
+ * Up/Down link data rates offset definition
+ */
+static const int8_t DatarateOffsetsAU915[7][6] =
+{
+    { DR_8 , DR_8 , DR_8 , DR_8 , DR_8 , DR_8  }, // DR_0
+    { DR_9 , DR_8 , DR_8 , DR_8 , DR_8 , DR_8  }, // DR_1
+    { DR_10, DR_9 , DR_8 , DR_8 , DR_8 , DR_8  }, // DR_2
+    { DR_11, DR_10, DR_9 , DR_8 , DR_8 , DR_8  }, // DR_3
+    { DR_12, DR_11, DR_10, DR_9 , DR_8 , DR_8  }, // DR_4
+    { DR_13, DR_12, DR_11, DR_10, DR_9 , DR_8  }, // DR_5
+    { DR_13, DR_13, DR_12, DR_11, DR_10, DR_9  }, // DR_6
+};
+
+/*!
+ * Maximum payload with respect to the datarate index. Cannot operate with repeater.
+ */
+static const uint8_t MaxPayloadOfDatarateAU915[] = { 51, 51, 51, 115, 242, 242, 242, 0, 53, 129, 242, 242, 242, 242, 0, 0 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Can operate with repeater.
+ */
+static const uint8_t MaxPayloadOfDatarateRepeaterAU915[] = { 51, 51, 51, 115, 222, 222, 222, 0, 33, 109, 222, 222, 222, 222, 0, 0 };
+
+/*!
+ * \brief The function gets a value of a specific phy attribute.
+ *
+ * \param [IN] getPhy Pointer to the function parameters.
+ *
+ * \retval Returns a structure containing the PHY parameter.
+ */
+PhyParam_t RegionAU915GetPhyParam( GetPhyParams_t* getPhy );
+
+/*!
+ * \brief Updates the last TX done parameters of the current channel.
+ *
+ * \param [IN] txDone Pointer to the function parameters.
+ */
+void RegionAU915SetBandTxDone( SetBandTxDoneParams_t* txDone );
+
+/*!
+ * \brief Initializes the channels masks and the channels.
+ *
+ * \param [IN] type Sets the initialization type.
+ */
+void RegionAU915InitDefaults( InitType_t type );
+
+/*!
+ * \brief Verifies a parameter.
+ *
+ * \param [IN] verify Pointer to the function parameters.
+ *
+ * \param [IN] type Sets the initialization type.
+ *
+ * \retval Returns true, if the parameter is valid.
+ */
+bool RegionAU915Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute );
+
+/*!
+ * \brief The function parses the input buffer and sets up the channels of the
+ *        CF list.
+ *
+ * \param [IN] applyCFList Pointer to the function parameters.
+ */
+void RegionAU915ApplyCFList( ApplyCFListParams_t* applyCFList );
+
+/*!
+ * \brief Sets a channels mask.
+ *
+ * \param [IN] chanMaskSet Pointer to the function parameters.
+ *
+ * \retval Returns true, if the channels mask could be set.
+ */
+bool RegionAU915ChanMaskSet( ChanMaskSetParams_t* chanMaskSet );
+
+/*!
+ * \brief Calculates the next datarate to set, when ADR is on or off.
+ *
+ * \param [IN] adrNext Pointer to the function parameters.
+ *
+ * \param [OUT] drOut The calculated datarate for the next TX.
+ *
+ * \param [OUT] txPowOut The TX power for the next TX.
+ *
+ * \param [OUT] adrAckCounter The calculated ADR acknowledgement counter.
+ *
+ * \retval Returns true, if an ADR request should be performed.
+ */
+bool RegionAU915AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter );
+
+/*!
+ * Computes the Rx window timeout and offset.
+ *
+ * \param [IN] datarate     Rx window datarate index to be used
+ *
+ * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame.
+ *
+ * \param [IN] rxError      System maximum timing error of the receiver. In milliseconds
+ *                          The receiver will turn on in a [-rxError : +rxError] ms
+ *                          interval around RxOffset
+ *
+ * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields.
+ */
+void RegionAU915ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams );
+
+/*!
+ * \brief Configuration of the RX windows.
+ *
+ * \param [IN] rxConfig Pointer to the function parameters.
+ *
+ * \param [OUT] datarate The datarate index which was set.
+ *
+ * \retval Returns true, if the configuration was applied successfully.
+ */
+bool RegionAU915RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate );
+
+/*!
+ * \brief TX configuration.
+ *
+ * \param [IN] txConfig Pointer to the function parameters.
+ *
+ * \param [OUT] txPower The tx power index which was set.
+ *
+ * \param [OUT] txTimeOnAir The time-on-air of the frame.
+ *
+ * \retval Returns true, if the configuration was applied successfully.
+ */
+bool RegionAU915TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir );
+
+/*!
+ * \brief The function processes a Link ADR Request.
+ *
+ * \param [IN] linkAdrReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionAU915LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed );
+
+/*!
+ * \brief The function processes a RX Parameter Setup Request.
+ *
+ * \param [IN] rxParamSetupReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionAU915RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq );
+
+/*!
+ * \brief The function processes a Channel Request.
+ *
+ * \param [IN] newChannelReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionAU915NewChannelReq( NewChannelReqParams_t* newChannelReq );
+
+/*!
+ * \brief The function processes a TX ParamSetup Request.
+ *
+ * \param [IN] txParamSetupReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ *         Returns -1, if the functionality is not implemented. In this case, the end node
+ *         shall not process the command.
+ */
+int8_t RegionAU915TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq );
+
+/*!
+ * \brief The function processes a DlChannel Request.
+ *
+ * \param [IN] dlChannelReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionAU915DlChannelReq( DlChannelReqParams_t* dlChannelReq );
+
+/*!
+ * \brief Alternates the datarate of the channel for the join request.
+ *
+ * \param [IN] alternateDr Pointer to the function parameters.
+ *
+ * \retval Datarate to apply.
+ */
+int8_t RegionAU915AlternateDr( AlternateDrParams_t* alternateDr );
+
+/*!
+ * \brief Calculates the back-off time.
+ *
+ * \param [IN] calcBackOff Pointer to the function parameters.
+ */
+void RegionAU915CalcBackOff( CalcBackOffParams_t* calcBackOff );
+
+/*!
+ * \brief Searches and set the next random available channel
+ *
+ * \param [OUT] channel Next channel to use for TX.
+ *
+ * \param [OUT] time Time to wait for the next transmission according to the duty
+ *              cycle.
+ *
+ * \param [OUT] aggregatedTimeOff Updates the aggregated time off.
+ *
+ * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate]
+ */
+bool RegionAU915NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff );
+
+/*!
+ * \brief Adds a channel.
+ *
+ * \param [IN] channelAdd Pointer to the function parameters.
+ *
+ * \retval Status of the operation.
+ */
+LoRaMacStatus_t RegionAU915ChannelAdd( ChannelAddParams_t* channelAdd );
+
+/*!
+ * \brief Removes a channel.
+ *
+ * \param [IN] channelRemove Pointer to the function parameters.
+ *
+ * \retval Returns true, if the channel was removed successfully.
+ */
+bool RegionAU915ChannelsRemove( ChannelRemoveParams_t* channelRemove  );
+
+/*!
+ * \brief Sets the radio into continuous wave mode.
+ *
+ * \param [IN] continuousWave Pointer to the function parameters.
+ */
+void RegionAU915SetContinuousWave( ContinuousWaveParams_t* continuousWave );
+
+/*!
+ * \brief Computes new datarate according to the given offset
+ *
+ * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms
+ *
+ * \param [IN] dr Current datarate
+ *
+ * \param [IN] drOffset Offset to be applied
+ *
+ * \retval newDr Computed datarate.
+ */
+uint8_t RegionAU915ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset );
+
+/*! \} defgroup REGIONAU915 */
+
+#endif // __REGION_AU915_H__
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/region/RegionCN470.cpp	Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,918 @@
+/*
+ / _____)             _              | |
+( (____  _____ ____ _| |_ _____  ____| |__
+ \____ \| ___ |    (_   _) ___ |/ ___)  _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+    (C)2013 Semtech
+ ___ _____ _   ___ _  _____ ___  ___  ___ ___
+/ __|_   _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+\__ \ | |/ _ \ (__| ' <| _| (_) |   / (__| _|
+|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+embedded.connectivity.solutions===============
+
+Description: LoRa MAC region CN470 implementation
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+
+Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE )
+*/
+#include <stdbool.h>
+#include <string.h>
+#include <stdint.h>
+#include <math.h>
+
+#include "board.h"
+#include "LoRaMac.h"
+
+#include "utilities.h"
+
+#include "Region.h"
+#include "RegionCommon.h"
+#include "RegionCN470.h"
+
+// Definitions
+#define CHANNELS_MASK_SIZE              6
+
+// Global attributes
+/*!
+ * LoRaMAC channels
+ */
+static ChannelParams_t Channels[CN470_MAX_NB_CHANNELS];
+
+/*!
+ * LoRaMac bands
+ */
+static Band_t Bands[CN470_MAX_NB_BANDS] =
+{
+    CN470_BAND0
+};
+
+/*!
+ * LoRaMac channels mask
+ */
+static uint16_t ChannelsMask[CHANNELS_MASK_SIZE];
+
+/*!
+ * LoRaMac channels default mask
+ */
+static uint16_t ChannelsDefaultMask[CHANNELS_MASK_SIZE];
+
+// Static functions
+static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr )
+{
+    uint8_t nextLowerDr = 0;
+
+    if( dr == minDr )
+    {
+        nextLowerDr = minDr;
+    }
+    else
+    {
+        nextLowerDr = dr - 1;
+    }
+    return nextLowerDr;
+}
+
+static uint32_t GetBandwidth( uint32_t drIndex )
+{
+    switch( BandwidthsCN470[drIndex] )
+    {
+        default:
+        case 125000:
+            return 0;
+        case 250000:
+            return 1;
+        case 500000:
+            return 2;
+    }
+}
+
+static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask )
+{
+    int8_t txPowerResult = txPower;
+
+    // Limit tx power to the band max
+    txPowerResult =  MAX( txPower, maxBandTxPower );
+
+    return txPowerResult;
+}
+
+static uint8_t CountNbOfEnabledChannels( uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx )
+{
+    uint8_t nbEnabledChannels = 0;
+    uint8_t delayTransmission = 0;
+
+    for( uint8_t i = 0, k = 0; i < CN470_MAX_NB_CHANNELS; i += 16, k++ )
+    {
+        for( uint8_t j = 0; j < 16; j++ )
+        {
+            if( ( channelsMask[k] & ( 1 << j ) ) != 0 )
+            {
+                if( channels[i + j].Frequency == 0 )
+                { // Check if the channel is enabled
+                    continue;
+                }
+                if( RegionCommonValueInRange( datarate, channels[i + j].DrRange.Fields.Min,
+                                              channels[i + j].DrRange.Fields.Max ) == false )
+                { // Check if the current channel selection supports the given datarate
+                    continue;
+                }
+                if( bands[channels[i + j].Band].TimeOff > 0 )
+                { // Check if the band is available for transmission
+                    delayTransmission++;
+                    continue;
+                }
+                enabledChannels[nbEnabledChannels++] = i + j;
+            }
+        }
+    }
+
+    *delayTx = delayTransmission;
+    return nbEnabledChannels;
+}
+
+static uint8_t BeaconChannel( uint32_t devAddr, TimerTime_t beaconTime, TimerTime_t beaconInterval )
+{
+    uint32_t frequency = 0;
+
+    frequency = devAddr + ( beaconTime / ( beaconInterval / 1000 ) );
+
+    return ( ( uint8_t )( frequency % 8 ) );
+}
+
+PhyParam_t RegionCN470GetPhyParam( GetPhyParams_t* getPhy )
+{
+    PhyParam_t phyParam = { 0 };
+
+    switch( getPhy->Attribute )
+    {
+        case PHY_MIN_RX_DR:
+        {
+            phyParam.Value = CN470_RX_MIN_DATARATE;
+            break;
+        }
+        case PHY_MIN_TX_DR:
+        {
+            phyParam.Value = CN470_TX_MIN_DATARATE;
+            break;
+        }
+        case PHY_DEF_TX_DR:
+        {
+            phyParam.Value = CN470_DEFAULT_DATARATE;
+            break;
+        }
+        case PHY_NEXT_LOWER_TX_DR:
+        {
+            phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, CN470_TX_MIN_DATARATE );
+            break;
+        }
+        case PHY_DEF_TX_POWER:
+        {
+            phyParam.Value = CN470_DEFAULT_TX_POWER;
+            break;
+        }
+        case PHY_MAX_PAYLOAD:
+        {
+            phyParam.Value = MaxPayloadOfDatarateCN470[getPhy->Datarate];
+            break;
+        }
+        case PHY_MAX_PAYLOAD_REPEATER:
+        {
+            phyParam.Value = MaxPayloadOfDatarateRepeaterCN470[getPhy->Datarate];
+            break;
+        }
+        case PHY_DUTY_CYCLE:
+        {
+            phyParam.Value = CN470_DUTY_CYCLE_ENABLED;
+            break;
+        }
+        case PHY_MAX_RX_WINDOW:
+        {
+            phyParam.Value = CN470_MAX_RX_WINDOW;
+            break;
+        }
+        case PHY_RECEIVE_DELAY1:
+        {
+            phyParam.Value = CN470_RECEIVE_DELAY1;
+            break;
+        }
+        case PHY_RECEIVE_DELAY2:
+        {
+            phyParam.Value = CN470_RECEIVE_DELAY2;
+            break;
+        }
+        case PHY_JOIN_ACCEPT_DELAY1:
+        {
+            phyParam.Value = CN470_JOIN_ACCEPT_DELAY1;
+            break;
+        }
+        case PHY_JOIN_ACCEPT_DELAY2:
+        {
+            phyParam.Value = CN470_JOIN_ACCEPT_DELAY2;
+            break;
+        }
+        case PHY_MAX_FCNT_GAP:
+        {
+            phyParam.Value = CN470_MAX_FCNT_GAP;
+            break;
+        }
+        case PHY_ACK_TIMEOUT:
+        {
+            phyParam.Value = ( CN470_ACKTIMEOUT + randr( -CN470_ACK_TIMEOUT_RND, CN470_ACK_TIMEOUT_RND ) );
+            break;
+        }
+        case PHY_DEF_DR1_OFFSET:
+        {
+            phyParam.Value = CN470_DEFAULT_RX1_DR_OFFSET;
+            break;
+        }
+        case PHY_DEF_RX2_FREQUENCY:
+        {
+            phyParam.Value = CN470_RX_WND_2_FREQ;
+            break;
+        }
+        case PHY_DEF_RX2_DR:
+        {
+            phyParam.Value = CN470_RX_WND_2_DR;
+            break;
+        }
+        case PHY_CHANNELS_MASK:
+        {
+            phyParam.ChannelsMask = ChannelsMask;
+            break;
+        }
+        case PHY_CHANNELS_DEFAULT_MASK:
+        {
+            phyParam.ChannelsMask = ChannelsDefaultMask;
+            break;
+        }
+        case PHY_MAX_NB_CHANNELS:
+        {
+            phyParam.Value = CN470_MAX_NB_CHANNELS;
+            break;
+        }
+        case PHY_CHANNELS:
+        {
+            phyParam.Channels = Channels;
+            break;
+        }
+        case PHY_DEF_UPLINK_DWELL_TIME:
+        case PHY_DEF_DOWNLINK_DWELL_TIME:
+        {
+            phyParam.Value = 0;
+            break;
+        }
+        case PHY_DEF_MAX_EIRP:
+        {
+            phyParam.fValue = CN470_DEFAULT_MAX_EIRP;
+            break;
+        }
+        case PHY_DEF_ANTENNA_GAIN:
+        {
+            phyParam.fValue = CN470_DEFAULT_ANTENNA_GAIN;
+            break;
+        }
+        case PHY_NB_JOIN_TRIALS:
+        case PHY_DEF_NB_JOIN_TRIALS:
+        {
+            phyParam.Value = 48;
+            break;
+        }
+        case PHY_BEACON_INTERVAL:
+        {
+            phyParam.Value = CN470_BEACON_INTERVAL;
+            break;
+        }
+        case PHY_BEACON_RESERVED:
+        {
+            phyParam.Value = CN470_BEACON_RESERVED;
+            break;
+        }
+        case PHY_BEACON_GUARD:
+        {
+            phyParam.Value = CN470_BEACON_GUARD;
+            break;
+        }
+        case PHY_BEACON_WINDOW:
+        {
+            phyParam.Value = CN470_BEACON_WINDOW;
+            break;
+        }
+        case PHY_BEACON_WINDOW_SLOTS:
+        {
+            phyParam.Value = CN470_BEACON_WINDOW_SLOTS;
+            break;
+        }
+        case PHY_PING_SLOT_WINDOW:
+        {
+            phyParam.Value = CN470_PING_SLOT_WINDOW;
+            break;
+        }
+        case PHY_BEACON_SYMBOL_TO_DEFAULT:
+        {
+            phyParam.Value = CN470_BEACON_SYMBOL_TO_DEFAULT;
+            break;
+        }
+        case PHY_BEACON_SYMBOL_TO_EXPANSION_MAX:
+        {
+            phyParam.Value = CN470_BEACON_SYMBOL_TO_EXPANSION_MAX;
+            break;
+        }
+        case PHY_PING_SLOT_SYMBOL_TO_EXPANSION_MAX:
+        {
+            phyParam.Value = CN470_PING_SLOT_SYMBOL_TO_EXPANSION_MAX;
+            break;
+        }
+        case PHY_BEACON_SYMBOL_TO_EXPANSION_FACTOR:
+        {
+            phyParam.Value = CN470_BEACON_SYMBOL_TO_EXPANSION_FACTOR;
+            break;
+        }
+        case PHY_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR:
+        {
+            phyParam.Value = CN470_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR;
+            break;
+        }
+        case PHY_MAX_BEACON_LESS_PERIOD:
+        {
+            phyParam.Value = CN470_MAX_BEACON_LESS_PERIOD;
+            break;
+        }
+        case PHY_BEACON_DELAY_BEACON_TIMING_ANS:
+        {
+            phyParam.Value = CN470_BEACON_DELAY_BEACON_TIMING_ANS;
+            break;
+        }
+        case PHY_PINGSLOT_CHANNEL_FREQ:
+        {
+            phyParam.Value = CN470_BEACON_CHANNEL_FREQ;
+            break;
+        }
+        case PHY_BEACON_SIZE:
+        {
+            phyParam.Value = CN470_BEACON_SIZE;
+            break;
+        }
+        case PHY_BEACON_CHANNEL_DR:
+        {
+            phyParam.Value = CN470_BEACON_CHANNEL_DR;
+            break;
+        }
+        default:
+        {
+            break;
+        }
+    }
+
+    return phyParam;
+}
+
+void RegionCN470SetBandTxDone( SetBandTxDoneParams_t* txDone )
+{
+    RegionCommonSetBandTxDone( txDone->Joined, &Bands[Channels[txDone->Channel].Band], txDone->LastTxDoneTime );
+}
+
+void RegionCN470InitDefaults( InitType_t type )
+{
+    switch( type )
+    {
+        case INIT_TYPE_INIT:
+        {
+            // Channels
+            // 125 kHz channels
+            for( uint8_t i = 0; i < CN470_MAX_NB_CHANNELS; i++ )
+            {
+                Channels[i].Frequency = 470300000 + i * 200000;
+                Channels[i].DrRange.Value = ( DR_5 << 4 ) | DR_0;
+                Channels[i].Band = 0;
+            }
+
+            // Initialize the channels default mask
+            ChannelsDefaultMask[0] = 0xFFFF;
+            ChannelsDefaultMask[1] = 0xFFFF;
+            ChannelsDefaultMask[2] = 0xFFFF;
+            ChannelsDefaultMask[3] = 0xFFFF;
+            ChannelsDefaultMask[4] = 0xFFFF;
+            ChannelsDefaultMask[5] = 0xFFFF;
+
+            // Update the channels mask
+            RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 6 );
+            break;
+        }
+        case INIT_TYPE_RESTORE:
+        {
+            // Restore channels default mask
+            RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 6 );
+            break;
+        }
+        default:
+        {
+            break;
+        }
+    }
+}
+
+bool RegionCN470Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute )
+{
+    switch( phyAttribute )
+    {
+        case PHY_TX_DR:
+        case PHY_DEF_TX_DR:
+        {
+            return RegionCommonValueInRange( verify->DatarateParams.Datarate, CN470_TX_MIN_DATARATE, CN470_TX_MAX_DATARATE );
+        }
+        case PHY_RX_DR:
+        {
+            return RegionCommonValueInRange( verify->DatarateParams.Datarate, CN470_RX_MIN_DATARATE, CN470_RX_MAX_DATARATE );
+        }
+        case PHY_DEF_TX_POWER:
+        case PHY_TX_POWER:
+        {
+            // Remark: switched min and max!
+            return RegionCommonValueInRange( verify->TxPower, CN470_MAX_TX_POWER, CN470_MIN_TX_POWER );
+        }
+        case PHY_DUTY_CYCLE:
+        {
+            return CN470_DUTY_CYCLE_ENABLED;
+        }
+        case PHY_NB_JOIN_TRIALS:
+        {
+            if( verify->NbJoinTrials < 48 )
+            {
+                return false;
+            }
+            break;
+        }
+        default:
+            return false;
+    }
+    return true;
+}
+
+void RegionCN470ApplyCFList( ApplyCFListParams_t* applyCFList )
+{
+    return;
+}
+
+bool RegionCN470ChanMaskSet( ChanMaskSetParams_t* chanMaskSet )
+{
+    switch( chanMaskSet->ChannelsMaskType )
+    {
+        case CHANNELS_MASK:
+        {
+            RegionCommonChanMaskCopy( ChannelsMask, chanMaskSet->ChannelsMaskIn, 6 );
+            break;
+        }
+        case CHANNELS_DEFAULT_MASK:
+        {
+            RegionCommonChanMaskCopy( ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 6 );
+            break;
+        }
+        default:
+            return false;
+    }
+    return true;
+}
+
+bool RegionCN470AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter )
+{
+    bool adrAckReq = false;
+    int8_t datarate = adrNext->Datarate;
+    int8_t txPower = adrNext->TxPower;
+    GetPhyParams_t getPhy;
+    PhyParam_t phyParam;
+
+    // Report back the adr ack counter
+    *adrAckCounter = adrNext->AdrAckCounter;
+
+    if( adrNext->AdrEnabled == true )
+    {
+        if( datarate == CN470_TX_MIN_DATARATE )
+        {
+            *adrAckCounter = 0;
+            adrAckReq = false;
+        }
+        else
+        {
+            if( adrNext->AdrAckCounter >= CN470_ADR_ACK_LIMIT )
+            {
+                adrAckReq = true;
+                txPower = CN470_MAX_TX_POWER;
+            }
+            else
+            {
+                adrAckReq = false;
+            }
+            if( adrNext->AdrAckCounter >= ( CN470_ADR_ACK_LIMIT + CN470_ADR_ACK_DELAY ) )
+            {
+                if( ( adrNext->AdrAckCounter % CN470_ADR_ACK_DELAY ) == 1 )
+                {
+                    // Decrease the datarate
+                    getPhy.Attribute = PHY_NEXT_LOWER_TX_DR;
+                    getPhy.Datarate = datarate;
+                    getPhy.UplinkDwellTime = adrNext->UplinkDwellTime;
+                    phyParam = RegionCN470GetPhyParam( &getPhy );
+                    datarate = phyParam.Value;
+
+                    if( datarate == CN470_TX_MIN_DATARATE )
+                    {
+                        // We must set adrAckReq to false as soon as we reach the lowest datarate
+                        adrAckReq = false;
+                        if( adrNext->UpdateChanMask == true )
+                        {
+                            // Re-enable default channels
+                            ChannelsMask[0] = 0xFFFF;
+                            ChannelsMask[1] = 0xFFFF;
+                            ChannelsMask[2] = 0xFFFF;
+                            ChannelsMask[3] = 0xFFFF;
+                            ChannelsMask[4] = 0xFFFF;
+                            ChannelsMask[5] = 0xFFFF;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    *drOut = datarate;
+    *txPowOut = txPower;
+    return adrAckReq;
+}
+
+void RegionCN470ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams )
+{
+    double tSymbol = 0.0;
+
+    // Get the datarate, perform a boundary check
+    rxConfigParams->Datarate = MIN( datarate, CN470_RX_MAX_DATARATE );
+    rxConfigParams->Bandwidth = GetBandwidth( rxConfigParams->Datarate );
+
+    tSymbol = RegionCommonComputeSymbolTimeLoRa( DataratesCN470[rxConfigParams->Datarate], BandwidthsCN470[rxConfigParams->Datarate] );
+
+    RegionCommonComputeRxWindowParameters( tSymbol, minRxSymbols, rxError, RADIO_WAKEUP_TIME, &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset );
+}
+
+bool RegionCN470RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate )
+{
+    int8_t dr = rxConfig->Datarate;
+    uint8_t maxPayload = 0;
+    int8_t phyDr = 0;
+    uint32_t frequency = rxConfig->Frequency;
+
+    if( Radio.GetStatus( ) != RF_IDLE )
+    {
+        return false;
+    }
+
+    if( rxConfig->Window == 0 )
+    {
+        // Apply window 1 frequency
+        frequency = CN470_FIRST_RX1_CHANNEL + ( rxConfig->Channel % 48 ) * CN470_STEPWIDTH_RX1_CHANNEL;
+    }
+
+    // Read the physical datarate from the datarates table
+    phyDr = DataratesCN470[dr];
+
+    Radio.SetChannel( frequency );
+
+    // Radio configuration
+    Radio.SetRxConfig( MODEM_LORA, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous );
+
+    if( rxConfig->RepeaterSupport == true )
+    {
+        maxPayload = MaxPayloadOfDatarateRepeaterCN470[dr];
+    }
+    else
+    {
+        maxPayload = MaxPayloadOfDatarateCN470[dr];
+    }
+    Radio.SetMaxPayloadLength( MODEM_LORA, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD );
+
+    *datarate = (uint8_t) dr;
+    return true;
+}
+
+bool RegionCN470TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir )
+{
+    int8_t phyDr = DataratesCN470[txConfig->Datarate];
+    int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, Bands[Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, ChannelsMask );
+    int8_t phyTxPower = 0;
+
+    // Calculate physical TX power
+    phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain );
+
+    // Setup the radio frequency
+    Radio.SetChannel( Channels[txConfig->Channel].Frequency );
+
+    Radio.SetTxConfig( MODEM_LORA, phyTxPower, 0, 0, phyDr, 1, 8, false, true, 0, 0, false, 3000 );
+    // Setup maximum payload lenght of the radio driver
+    Radio.SetMaxPayloadLength( MODEM_LORA, txConfig->PktLen );
+    // Get the time-on-air of the next tx frame
+    *txTimeOnAir = Radio.TimeOnAir( MODEM_LORA, txConfig->PktLen );
+    *txPower = txPowerLimited;
+
+    return true;
+}
+
+uint8_t RegionCN470LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed )
+{
+    uint8_t status = 0x07;
+    RegionCommonLinkAdrParams_t linkAdrParams;
+    uint8_t nextIndex = 0;
+    uint8_t bytesProcessed = 0;
+    uint16_t channelsMask[6] = { 0, 0, 0, 0, 0, 0 };
+    GetPhyParams_t getPhy;
+    PhyParam_t phyParam;
+    RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams;
+
+    // Initialize local copy of channels mask
+    RegionCommonChanMaskCopy( channelsMask, ChannelsMask, 6 );
+
+    while( bytesProcessed < linkAdrReq->PayloadSize )
+    {
+        // Get ADR request parameters
+        nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams );
+
+        if( nextIndex == 0 )
+            break; // break loop, since no more request has been found
+
+        // Update bytes processed
+        bytesProcessed += nextIndex;
+
+        // Revert status, as we only check the last ADR request for the channel mask KO
+        status = 0x07;
+
+        if( linkAdrParams.ChMaskCtrl == 6 )
+        {
+            // Enable all 125 kHz channels
+            channelsMask[0] = 0xFFFF;
+            channelsMask[1] = 0xFFFF;
+            channelsMask[2] = 0xFFFF;
+            channelsMask[3] = 0xFFFF;
+            channelsMask[4] = 0xFFFF;
+            channelsMask[5] = 0xFFFF;
+        }
+        else if( linkAdrParams.ChMaskCtrl == 7 )
+        {
+            status &= 0xFE; // Channel mask KO
+        }
+        else
+        {
+            for( uint8_t i = 0; i < 16; i++ )
+            {
+                if( ( ( linkAdrParams.ChMask & ( 1 << i ) ) != 0 ) &&
+                    ( Channels[linkAdrParams.ChMaskCtrl * 16 + i].Frequency == 0 ) )
+                {// Trying to enable an undefined channel
+                    status &= 0xFE; // Channel mask KO
+                }
+            }
+            channelsMask[linkAdrParams.ChMaskCtrl] = linkAdrParams.ChMask;
+        }
+    }
+
+    // Get the minimum possible datarate
+    getPhy.Attribute = PHY_MIN_TX_DR;
+    getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime;
+    phyParam = RegionCN470GetPhyParam( &getPhy );
+
+    linkAdrVerifyParams.Status = status;
+    linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled;
+    linkAdrVerifyParams.Datarate = linkAdrParams.Datarate;
+    linkAdrVerifyParams.TxPower = linkAdrParams.TxPower;
+    linkAdrVerifyParams.NbRep = linkAdrParams.NbRep;
+    linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate;
+    linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower;
+    linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep;
+    linkAdrVerifyParams.NbChannels = CN470_MAX_NB_CHANNELS;
+    linkAdrVerifyParams.ChannelsMask = channelsMask;
+    linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value;
+    linkAdrVerifyParams.MaxDatarate = CN470_TX_MAX_DATARATE;
+    linkAdrVerifyParams.Channels = Channels;
+    linkAdrVerifyParams.MinTxPower = CN470_MIN_TX_POWER;
+    linkAdrVerifyParams.MaxTxPower = CN470_MAX_TX_POWER;
+
+    // Verify the parameters and update, if necessary
+    status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep );
+
+    // Update channelsMask if everything is correct
+    if( status == 0x07 )
+    {
+        // Copy Mask
+        RegionCommonChanMaskCopy( ChannelsMask, channelsMask, 6 );
+    }
+
+    // Update status variables
+    *drOut = linkAdrParams.Datarate;
+    *txPowOut = linkAdrParams.TxPower;
+    *nbRepOut = linkAdrParams.NbRep;
+    *nbBytesParsed = bytesProcessed;
+
+    return status;
+}
+
+uint8_t RegionCN470RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq )
+{
+    uint8_t status = 0x07;
+    uint32_t freq = rxParamSetupReq->Frequency;
+
+    // Verify radio frequency
+    if( ( Radio.CheckRfFrequency( freq ) == false ) ||
+        ( freq < CN470_FIRST_RX1_CHANNEL ) ||
+        ( freq > CN470_LAST_RX1_CHANNEL ) ||
+        ( ( ( freq - ( uint32_t ) CN470_FIRST_RX1_CHANNEL ) % ( uint32_t ) CN470_STEPWIDTH_RX1_CHANNEL ) != 0 ) )
+    {
+        status &= 0xFE; // Channel frequency KO
+    }
+
+    // Verify datarate
+    if( RegionCommonValueInRange( rxParamSetupReq->Datarate, CN470_RX_MIN_DATARATE, CN470_RX_MAX_DATARATE ) == false )
+    {
+        status &= 0xFD; // Datarate KO
+    }
+
+    // Verify datarate offset
+    if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, CN470_MIN_RX1_DR_OFFSET, CN470_MAX_RX1_DR_OFFSET ) == false )
+    {
+        status &= 0xFB; // Rx1DrOffset range KO
+    }
+
+    return status;
+}
+
+uint8_t RegionCN470NewChannelReq( NewChannelReqParams_t* newChannelReq )
+{
+    // Datarate and frequency KO
+    return 0;
+}
+
+int8_t RegionCN470TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq )
+{
+    return -1;
+}
+
+uint8_t RegionCN470DlChannelReq( DlChannelReqParams_t* dlChannelReq )
+{
+    return 0;
+}
+
+int8_t RegionCN470AlternateDr( AlternateDrParams_t* alternateDr )
+{
+    int8_t datarate = 0;
+
+    if( ( alternateDr->NbTrials % 48 ) == 0 )
+    {
+        datarate = DR_0;
+    }
+    else if( ( alternateDr->NbTrials % 32 ) == 0 )
+    {
+        datarate = DR_1;
+    }
+    else if( ( alternateDr->NbTrials % 24 ) == 0 )
+    {
+        datarate = DR_2;
+    }
+    else if( ( alternateDr->NbTrials % 16 ) == 0 )
+    {
+        datarate = DR_3;
+    }
+    else if( ( alternateDr->NbTrials % 8 ) == 0 )
+    {
+        datarate = DR_4;
+    }
+    else
+    {
+        datarate = DR_5;
+    }
+    return datarate;
+}
+
+void RegionCN470CalcBackOff( CalcBackOffParams_t* calcBackOff )
+{
+    RegionCommonCalcBackOffParams_t calcBackOffParams;
+
+    calcBackOffParams.Channels = Channels;
+    calcBackOffParams.Bands = Bands;
+    calcBackOffParams.LastTxIsJoinRequest = calcBackOff->LastTxIsJoinRequest;
+    calcBackOffParams.Joined = calcBackOff->Joined;
+    calcBackOffParams.DutyCycleEnabled = calcBackOff->DutyCycleEnabled;
+    calcBackOffParams.Channel = calcBackOff->Channel;
+    calcBackOffParams.ElapsedTime = calcBackOff->ElapsedTime;
+    calcBackOffParams.TxTimeOnAir = calcBackOff->TxTimeOnAir;
+
+    RegionCommonCalcBackOff( &calcBackOffParams );
+}
+
+bool RegionCN470NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff )
+{
+    uint8_t nbEnabledChannels = 0;
+    uint8_t delayTx = 0;
+    uint8_t enabledChannels[CN470_MAX_NB_CHANNELS] = { 0 };
+    TimerTime_t nextTxDelay = 0;
+
+    // Count 125kHz channels
+    if( RegionCommonCountChannels( ChannelsMask, 0, 6 ) == 0 )
+    { // Reactivate default channels
+        ChannelsMask[0] = 0xFFFF;
+        ChannelsMask[1] = 0xFFFF;
+        ChannelsMask[2] = 0xFFFF;
+        ChannelsMask[3] = 0xFFFF;
+        ChannelsMask[4] = 0xFFFF;
+        ChannelsMask[5] = 0xFFFF;
+    }
+
+    if( nextChanParams->AggrTimeOff <= TimerGetElapsedTime( nextChanParams->LastAggrTx ) )
+    {
+        // Reset Aggregated time off
+        *aggregatedTimeOff = 0;
+
+        // Update bands Time OFF
+        nextTxDelay = RegionCommonUpdateBandTimeOff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, Bands, CN470_MAX_NB_BANDS );
+
+        // Search how many channels are enabled
+        nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Datarate,
+                                                      ChannelsMask, Channels,
+                                                      Bands, enabledChannels, &delayTx );
+    }
+    else
+    {
+        delayTx++;
+        nextTxDelay = nextChanParams->AggrTimeOff - TimerGetElapsedTime( nextChanParams->LastAggrTx );
+    }
+
+    if( nbEnabledChannels > 0 )
+    {
+        // We found a valid channel
+        *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )];
+
+        *time = 0;
+        return true;
+    }
+    else
+    {
+        if( delayTx > 0 )
+        {
+            // Delay transmission due to AggregatedTimeOff or to a band time off
+            *time = nextTxDelay;
+            return true;
+        }
+        // Datarate not supported by any channel
+        *time = 0;
+        return false;
+    }
+}
+
+LoRaMacStatus_t RegionCN470ChannelAdd( ChannelAddParams_t* channelAdd )
+{
+    return LORAMAC_STATUS_PARAMETER_INVALID;
+}
+
+bool RegionCN470ChannelsRemove( ChannelRemoveParams_t* channelRemove  )
+{
+    return LORAMAC_STATUS_PARAMETER_INVALID;
+}
+
+void RegionCN470SetContinuousWave( ContinuousWaveParams_t* continuousWave )
+{
+    int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, Bands[Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, ChannelsMask );
+    int8_t phyTxPower = 0;
+    uint32_t frequency = Channels[continuousWave->Channel].Frequency;
+
+    // Calculate physical TX power
+    phyTxPower = RegionCommonComputeTxPower( txPowerLimited, continuousWave->MaxEirp, continuousWave->AntennaGain );
+
+    Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout );
+}
+
+uint8_t RegionCN470ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset )
+{
+    int8_t datarate = dr - drOffset;
+
+    if( datarate < 0 )
+    {
+        datarate = DR_0;
+    }
+    return datarate;
+}
+
+void RegionCN470RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr, bool *beaconChannelSet )
+{
+    RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup;
+
+    regionCommonRxBeaconSetup.Datarates = DataratesCN470;
+    regionCommonRxBeaconSetup.ChannelPlanFrequency = ( 508.3e6 + ( BeaconChannel( 0, rxBeaconSetup->BeaconTime, rxBeaconSetup->BeaconInterval ) * 200e3 ) );
+    regionCommonRxBeaconSetup.BeaconTimingAnsFrequency = ( 508.3e6 + ( rxBeaconSetup->BeaconTimingChannel * 200e3 ) );
+    regionCommonRxBeaconSetup.BeaconSize = CN470_BEACON_SIZE;
+    regionCommonRxBeaconSetup.BeaconDatarate = CN470_BEACON_CHANNEL_DR;
+    regionCommonRxBeaconSetup.BeaconChannelBW = CN470_BEACON_CHANNEL_BW;
+    regionCommonRxBeaconSetup.CustomFrequency = rxBeaconSetup->CustomFrequency;
+    regionCommonRxBeaconSetup.CustomFrequencyEnabled = rxBeaconSetup->CustomFrequencyEnabled;
+    regionCommonRxBeaconSetup.BeaconChannelSet = rxBeaconSetup->BeaconChannelSet;
+    regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime;
+    regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout;
+
+    RegionCommonRxBeaconSetup( &regionCommonRxBeaconSetup, beaconChannelSet );
+
+    // Store downlink datarate
+    *outDr = CN470_BEACON_CHANNEL_DR;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/region/RegionCN470.h	Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,531 @@
+/*!
+ * \file      RegionCN470.h
+ *
+ * \brief     Region definition for CN470
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ *                ______                              _
+ *               / _____)             _              | |
+ *              ( (____  _____ ____ _| |_ _____  ____| |__
+ *               \____ \| ___ |    (_   _) ___ |/ ___)  _ \
+ *               _____) ) ____| | | || |_| ____( (___| | | |
+ *              (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ *              (C)2013 Semtech
+ *
+ *               ___ _____ _   ___ _  _____ ___  ___  ___ ___
+ *              / __|_   _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ *              \__ \ | |/ _ \ (__| ' <| _| (_) |   / (__| _|
+ *              |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ *              embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author    Miguel Luis ( Semtech )
+ *
+ * \author    Gregory Cristian ( Semtech )
+ *
+ * \author    Daniel Jaeckle ( STACKFORCE )
+ *
+ * \defgroup  REGIONCN470 Region CN470
+ *            Implementation according to LoRaWAN Specification v1.0.2.
+ * \{
+ */
+#ifndef __REGION_CN470_H__
+#define __REGION_CN470_H__
+
+/*!
+ * LoRaMac maximum number of channels
+ */
+#define CN470_MAX_NB_CHANNELS                        96
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define CN470_TX_MIN_DATARATE                       DR_0
+
+/*!
+ * Maximal datarate that can be used by the node
+ */
+#define CN470_TX_MAX_DATARATE                       DR_5
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define CN470_RX_MIN_DATARATE                       DR_0
+
+/*!
+ * Maximal datarate that can be used by the node
+ */
+#define CN470_RX_MAX_DATARATE                       DR_5
+
+/*!
+ * Default datarate used by the node
+ */
+#define CN470_DEFAULT_DATARATE                      DR_0
+
+/*!
+ * Minimal Rx1 receive datarate offset
+ */
+#define CN470_MIN_RX1_DR_OFFSET                     0
+
+/*!
+ * Maximal Rx1 receive datarate offset
+ */
+#define CN470_MAX_RX1_DR_OFFSET                     3
+
+/*!
+ * Default Rx1 receive datarate offset
+ */
+#define CN470_DEFAULT_RX1_DR_OFFSET                 0
+
+/*!
+ * Minimal Tx output power that can be used by the node
+ */
+#define CN470_MIN_TX_POWER                        TX_POWER_7
+
+/*!
+ * Maximal Tx output power that can be used by the node
+ */
+#define CN470_MAX_TX_POWER                        TX_POWER_0
+
+/*!
+ * Default Tx output power used by the node
+ */
+#define CN470_DEFAULT_TX_POWER                    TX_POWER_0
+
+/*!
+ * Default Max EIRP
+ */
+#define CN470_DEFAULT_MAX_EIRP                      19.15f
+
+/*!
+ * Default antenna gain
+ */
+#define CN470_DEFAULT_ANTENNA_GAIN                  2.15f
+
+/*!
+ * ADR Ack limit
+ */
+#define CN470_ADR_ACK_LIMIT                         64
+
+/*!
+ * ADR Ack delay
+ */
+#define CN470_ADR_ACK_DELAY                         32
+
+/*!
+ * Enabled or disabled the duty cycle
+ */
+#define CN470_DUTY_CYCLE_ENABLED                    0
+
+/*!
+ * Maximum RX window duration
+ */
+#define CN470_MAX_RX_WINDOW                         3000
+
+/*!
+ * Receive delay 1
+ */
+#define CN470_RECEIVE_DELAY1                        1000
+
+/*!
+ * Receive delay 2
+ */
+#define CN470_RECEIVE_DELAY2                        2000
+
+/*!
+ * Join accept delay 1
+ */
+#define CN470_JOIN_ACCEPT_DELAY1                    5000
+
+/*!
+ * Join accept delay 2
+ */
+#define CN470_JOIN_ACCEPT_DELAY2                    6000
+
+/*!
+ * Maximum frame counter gap
+ */
+#define CN470_MAX_FCNT_GAP                          16384
+
+/*!
+ * Ack timeout
+ */
+#define CN470_ACKTIMEOUT                            2000
+
+/*!
+ * Random ack timeout limits
+ */
+#define CN470_ACK_TIMEOUT_RND                       1000
+
+/*!
+ * Second reception window channel frequency definition.
+ */
+#define CN470_RX_WND_2_FREQ                         505300000
+
+/*!
+ * Second reception window channel datarate definition.
+ */
+#define CN470_RX_WND_2_DR                           DR_0
+
+/*
+ * CLASS B
+ */
+/*!
+ * Beacon interval in ms
+ */
+#define CN470_BEACON_INTERVAL                       128000
+
+/*!
+ * Beacon reserved time in ms
+ */
+#define CN470_BEACON_RESERVED                       2120
+
+/*!
+ * Beacon guard time in ms
+ */
+#define CN470_BEACON_GUARD                          3000
+
+/*!
+ * Beacon window time in ms
+ */
+#define CN470_BEACON_WINDOW                         122880
+
+/*!
+ * Beacon window time in numer of slots
+ */
+#define CN470_BEACON_WINDOW_SLOTS                   4096
+
+/*!
+ * Ping slot length time in ms
+ */
+#define CN470_PING_SLOT_WINDOW                      30
+
+/*!
+ * Beacon frequency
+ */
+#define CN470_BEACON_CHANNEL_FREQ                  508300000
+
+/*!
+ * Default symbol timeout for beacons and ping slot windows
+ */
+#define CN470_BEACON_SYMBOL_TO_DEFAULT              8
+
+/*!
+ * Maximum symbol timeout for beacons
+ */
+#define CN470_BEACON_SYMBOL_TO_EXPANSION_MAX        400
+
+/*!
+ * Maximum symbol timeout for ping slots
+ */
+#define CN470_PING_SLOT_SYMBOL_TO_EXPANSION_MAX     40
+
+/*!
+ * Symbol expansion value for beacon windows in case of beacon
+ * loss in symbols
+ */
+#define CN470_BEACON_SYMBOL_TO_EXPANSION_FACTOR     2
+
+/*!
+ * Symbol expansion value for ping slot windows in case of beacon
+ * loss in symbols
+ */
+#define CN470_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR  2
+
+/*!
+ * Maximum allowed beacon less time in ms
+ */
+#define CN470_MAX_BEACON_LESS_PERIOD                7200000
+
+/*!
+ * Delay time for the BeaconTimingAns in ms
+ */
+#define CN470_BEACON_DELAY_BEACON_TIMING_ANS        30
+
+/*!
+ * Payload size of a beacon frame
+ */
+#define CN470_BEACON_SIZE                           19
+
+/*!
+ * Datarate of the beacon channel
+ */
+#define CN470_BEACON_CHANNEL_DR                     DR_2
+
+/*!
+ * Bandwith of the beacon channel
+ */
+#define CN470_BEACON_CHANNEL_BW                     0
+
+/*!
+ * LoRaMac maximum number of bands
+ */
+#define CN470_MAX_NB_BANDS                          1
+
+/*!
+ * Band 0 definition
+ * { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff }
+ */
+#define CN470_BAND0                                 { 1, CN470_MAX_TX_POWER, 0,  0 } //  100.0 %
+
+/*!
+ * Defines the first channel for RX window 1 for CN470 band
+ */
+#define CN470_FIRST_RX1_CHANNEL                     ( (uint32_t) 500300000 )
+
+/*!
+ * Defines the last channel for RX window 1 for CN470 band
+ */
+#define CN470_LAST_RX1_CHANNEL                      ( (uint32_t) 509700000 )
+
+/*!
+ * Defines the step width of the channels for RX window 1
+ */
+#define CN470_STEPWIDTH_RX1_CHANNEL                 ( (uint32_t) 200000 )
+
+/*!
+ * Data rates table definition
+ */
+static const uint8_t DataratesCN470[]  = { 12, 11, 10,  9,  8,  7 };
+
+/*!
+ * Bandwidths table definition in Hz
+ */
+static const uint32_t BandwidthsCN470[] = { 125000, 125000, 125000, 125000, 125000, 125000 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Cannot operate with repeater.
+ */
+static const uint8_t MaxPayloadOfDatarateCN470[] = { 51, 51, 51, 115, 222, 222 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Can operate with repeater.
+ */
+static const uint8_t MaxPayloadOfDatarateRepeaterCN470[] = { 51, 51, 51, 115, 222, 222 };
+
+/*!
+ * \brief The function gets a value of a specific phy attribute.
+ *
+ * \param [IN] getPhy Pointer to the function parameters.
+ *
+ * \retval Returns a structure containing the PHY parameter.
+ */
+PhyParam_t RegionCN470GetPhyParam( GetPhyParams_t* getPhy );
+
+/*!
+ * \brief Updates the last TX done parameters of the current channel.
+ *
+ * \param [IN] txDone Pointer to the function parameters.
+ */
+void RegionCN470SetBandTxDone( SetBandTxDoneParams_t* txDone );
+
+/*!
+ * \brief Initializes the channels masks and the channels.
+ *
+ * \param [IN] type Sets the initialization type.
+ */
+void RegionCN470InitDefaults( InitType_t type );
+
+/*!
+ * \brief Verifies a parameter.
+ *
+ * \param [IN] verify Pointer to the function parameters.
+ *
+ * \param [IN] type Sets the initialization type.
+ *
+ * \retval Returns true, if the parameter is valid.
+ */
+bool RegionCN470Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute );
+
+/*!
+ * \brief The function parses the input buffer and sets up the channels of the
+ *        CF list.
+ *
+ * \param [IN] applyCFList Pointer to the function parameters.
+ */
+void RegionCN470ApplyCFList( ApplyCFListParams_t* applyCFList );
+
+/*!
+ * \brief Sets a channels mask.
+ *
+ * \param [IN] chanMaskSet Pointer to the function parameters.
+ *
+ * \retval Returns true, if the channels mask could be set.
+ */
+bool RegionCN470ChanMaskSet( ChanMaskSetParams_t* chanMaskSet );
+
+/*!
+ * \brief Calculates the next datarate to set, when ADR is on or off.
+ *
+ * \param [IN] adrNext Pointer to the function parameters.
+ *
+ * \param [OUT] drOut The calculated datarate for the next TX.
+ *
+ * \param [OUT] txPowOut The TX power for the next TX.
+ *
+ * \param [OUT] adrAckCounter The calculated ADR acknowledgement counter.
+ *
+ * \retval Returns true, if an ADR request should be performed.
+ */
+bool RegionCN470AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter );
+
+/*!
+ * Computes the Rx window timeout and offset.
+ *
+ * \param [IN] datarate     Rx window datarate index to be used
+ *
+ * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame.
+ *
+ * \param [IN] rxError      System maximum timing error of the receiver. In milliseconds
+ *                          The receiver will turn on in a [-rxError : +rxError] ms
+ *                          interval around RxOffset
+ *
+ * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields.
+ */
+void RegionCN470ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams );
+
+/*!
+ * \brief Configuration of the RX windows.
+ *
+ * \param [IN] rxConfig Pointer to the function parameters.
+ *
+ * \param [OUT] datarate The datarate index which was set.
+ *
+ * \retval Returns true, if the configuration was applied successfully.
+ */
+bool RegionCN470RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate );
+
+/*!
+ * \brief TX configuration.
+ *
+ * \param [IN] txConfig Pointer to the function parameters.
+ *
+ * \param [OUT] txPower The tx power index which was set.
+ *
+ * \param [OUT] txTimeOnAir The time-on-air of the frame.
+ *
+ * \retval Returns true, if the configuration was applied successfully.
+ */
+bool RegionCN470TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir );
+
+/*!
+ * \brief The function processes a Link ADR Request.
+ *
+ * \param [IN] linkAdrReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionCN470LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed );
+
+/*!
+ * \brief The function processes a RX Parameter Setup Request.
+ *
+ * \param [IN] rxParamSetupReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionCN470RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq );
+
+/*!
+ * \brief The function processes a Channel Request.
+ *
+ * \param [IN] newChannelReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionCN470NewChannelReq( NewChannelReqParams_t* newChannelReq );
+
+/*!
+ * \brief The function processes a TX ParamSetup Request.
+ *
+ * \param [IN] txParamSetupReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ *         Returns -1, if the functionality is not implemented. In this case, the end node
+ *         shall not process the command.
+ */
+int8_t RegionCN470TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq );
+
+/*!
+ * \brief The function processes a DlChannel Request.
+ *
+ * \param [IN] dlChannelReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionCN470DlChannelReq( DlChannelReqParams_t* dlChannelReq );
+
+/*!
+ * \brief Alternates the datarate of the channel for the join request.
+ *
+ * \param [IN] alternateDr Pointer to the function parameters.
+ *
+ * \retval Datarate to apply.
+ */
+int8_t RegionCN470AlternateDr( AlternateDrParams_t* alternateDr );
+
+/*!
+ * \brief Calculates the back-off time.
+ *
+ * \param [IN] calcBackOff Pointer to the function parameters.
+ */
+void RegionCN470CalcBackOff( CalcBackOffParams_t* calcBackOff );
+
+/*!
+ * \brief Searches and set the next random available channel
+ *
+ * \param [OUT] channel Next channel to use for TX.
+ *
+ * \param [OUT] time Time to wait for the next transmission according to the duty
+ *              cycle.
+ *
+ * \param [OUT] aggregatedTimeOff Updates the aggregated time off.
+ *
+ * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate]
+ */
+bool RegionCN470NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff );
+
+/*!
+ * \brief Adds a channel.
+ *
+ * \param [IN] channelAdd Pointer to the function parameters.
+ *
+ * \retval Status of the operation.
+ */
+LoRaMacStatus_t RegionCN470ChannelAdd( ChannelAddParams_t* channelAdd );
+
+/*!
+ * \brief Removes a channel.
+ *
+ * \param [IN] channelRemove Pointer to the function parameters.
+ *
+ * \retval Returns true, if the channel was removed successfully.
+ */
+bool RegionCN470ChannelsRemove( ChannelRemoveParams_t* channelRemove  );
+
+/*!
+ * \brief Sets the radio into continuous wave mode.
+ *
+ * \param [IN] continuousWave Pointer to the function parameters.
+ */
+void RegionCN470SetContinuousWave( ContinuousWaveParams_t* continuousWave );
+
+/*!
+ * \brief Computes new datarate according to the given offset
+ *
+ * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms
+ *
+ * \param [IN] dr Current datarate
+ *
+ * \param [IN] drOffset Offset to be applied
+ *
+ * \retval newDr Computed datarate.
+ */
+uint8_t RegionCN470ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset );
+
+/*! \} defgroup REGIONCN470 */
+
+#endif // __REGION_CN470_H__
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/region/RegionCN779.cpp	Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,1152 @@
+/*
+ / _____)             _              | |
+( (____  _____ ____ _| |_ _____  ____| |__
+ \____ \| ___ |    (_   _) ___ |/ ___)  _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+    (C)2013 Semtech
+ ___ _____ _   ___ _  _____ ___  ___  ___ ___
+/ __|_   _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+\__ \ | |/ _ \ (__| ' <| _| (_) |   / (__| _|
+|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+embedded.connectivity.solutions===============
+
+Description: LoRa MAC region CN779 implementation
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+
+Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE )
+*/
+#include <stdbool.h>
+#include <string.h>
+#include <stdint.h>
+#include <math.h>
+
+#include "board.h"
+#include "LoRaMac.h"
+
+#include "utilities.h"
+
+#include "Region.h"
+#include "RegionCommon.h"
+#include "RegionCN779.h"
+
+// Definitions
+#define CHANNELS_MASK_SIZE              1
+
+// Global attributes
+/*!
+ * LoRaMAC channels
+ */
+static ChannelParams_t Channels[CN779_MAX_NB_CHANNELS];
+
+/*!
+ * LoRaMac bands
+ */
+static Band_t Bands[CN779_MAX_NB_BANDS] =
+{
+    CN779_BAND0
+};
+
+/*!
+ * LoRaMac channels mask
+ */
+static uint16_t ChannelsMask[CHANNELS_MASK_SIZE];
+
+/*!
+ * LoRaMac channels default mask
+ */
+static uint16_t ChannelsDefaultMask[CHANNELS_MASK_SIZE];
+
+// Static functions
+static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr )
+{
+    uint8_t nextLowerDr = 0;
+
+    if( dr == minDr )
+    {
+        nextLowerDr = minDr;
+    }
+    else
+    {
+        nextLowerDr = dr - 1;
+    }
+    return nextLowerDr;
+}
+
+static uint32_t GetBandwidth( uint32_t drIndex )
+{
+    switch( BandwidthsCN779[drIndex] )
+    {
+        default:
+        case 125000:
+            return 0;
+        case 250000:
+            return 1;
+        case 500000:
+            return 2;
+    }
+}
+
+static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask )
+{
+    int8_t txPowerResult = txPower;
+
+    // Limit tx power to the band max
+    txPowerResult =  MAX( txPower, maxBandTxPower );
+
+    return txPowerResult;
+}
+
+static bool VerifyTxFreq( uint32_t freq )
+{
+    // Check radio driver support
+    if( Radio.CheckRfFrequency( freq ) == false )
+    {
+        return false;
+    }
+
+    if( ( freq < 779500000 ) || ( freq > 786500000 ) )
+    {
+        return false;
+    }
+    return true;
+}
+
+static uint8_t CountNbOfEnabledChannels( bool joined, uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx )
+{
+    uint8_t nbEnabledChannels = 0;
+    uint8_t delayTransmission = 0;
+
+    for( uint8_t i = 0, k = 0; i < CN779_MAX_NB_CHANNELS; i += 16, k++ )
+    {
+        for( uint8_t j = 0; j < 16; j++ )
+        {
+            if( ( channelsMask[k] & ( 1 << j ) ) != 0 )
+            {
+                if( channels[i + j].Frequency == 0 )
+                { // Check if the channel is enabled
+                    continue;
+                }
+                if( joined == false )
+                {
+                    if( ( CN779_JOIN_CHANNELS & ( 1 << j ) ) == 0 )
+                    {
+                        continue;
+                    }
+                }
+                if( RegionCommonValueInRange( datarate, channels[i + j].DrRange.Fields.Min,
+                                              channels[i + j].DrRange.Fields.Max ) == false )
+                { // Check if the current channel selection supports the given datarate
+                    continue;
+                }
+                if( bands[channels[i + j].Band].TimeOff > 0 )
+                { // Check if the band is available for transmission
+                    delayTransmission++;
+                    continue;
+                }
+                enabledChannels[nbEnabledChannels++] = i + j;
+            }
+        }
+    }
+
+    *delayTx = delayTransmission;
+    return nbEnabledChannels;
+}
+
+PhyParam_t RegionCN779GetPhyParam( GetPhyParams_t* getPhy )
+{
+    PhyParam_t phyParam = { 0 };
+
+    switch( getPhy->Attribute )
+    {
+        case PHY_MIN_RX_DR:
+        {
+            phyParam.Value = CN779_RX_MIN_DATARATE;
+            break;
+        }
+        case PHY_MIN_TX_DR:
+        {
+            phyParam.Value = CN779_TX_MIN_DATARATE;
+            break;
+        }
+        case PHY_DEF_TX_DR:
+        {
+            phyParam.Value = CN779_DEFAULT_DATARATE;
+            break;
+        }
+        case PHY_NEXT_LOWER_TX_DR:
+        {
+            phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, CN779_TX_MIN_DATARATE );
+            break;
+        }
+        case PHY_DEF_TX_POWER:
+        {
+            phyParam.Value = CN779_DEFAULT_TX_POWER;
+            break;
+        }
+        case PHY_MAX_PAYLOAD:
+        {
+            phyParam.Value = MaxPayloadOfDatarateCN779[getPhy->Datarate];
+            break;
+        }
+        case PHY_MAX_PAYLOAD_REPEATER:
+        {
+            phyParam.Value = MaxPayloadOfDatarateRepeaterCN779[getPhy->Datarate];
+            break;
+        }
+        case PHY_DUTY_CYCLE:
+        {
+            phyParam.Value = CN779_DUTY_CYCLE_ENABLED;
+            break;
+        }
+        case PHY_MAX_RX_WINDOW:
+        {
+            phyParam.Value = CN779_MAX_RX_WINDOW;
+            break;
+        }
+        case PHY_RECEIVE_DELAY1:
+        {
+            phyParam.Value = CN779_RECEIVE_DELAY1;
+            break;
+        }
+        case PHY_RECEIVE_DELAY2:
+        {
+            phyParam.Value = CN779_RECEIVE_DELAY2;
+            break;
+        }
+        case PHY_JOIN_ACCEPT_DELAY1:
+        {
+            phyParam.Value = CN779_JOIN_ACCEPT_DELAY1;
+            break;
+        }
+        case PHY_JOIN_ACCEPT_DELAY2:
+        {
+            phyParam.Value = CN779_JOIN_ACCEPT_DELAY2;
+            break;
+        }
+        case PHY_MAX_FCNT_GAP:
+        {
+            phyParam.Value = CN779_MAX_FCNT_GAP;
+            break;
+        }
+        case PHY_ACK_TIMEOUT:
+        {
+            phyParam.Value = ( CN779_ACKTIMEOUT + randr( -CN779_ACK_TIMEOUT_RND, CN779_ACK_TIMEOUT_RND ) );
+            break;
+        }
+        case PHY_DEF_DR1_OFFSET:
+        {
+            phyParam.Value = CN779_DEFAULT_RX1_DR_OFFSET;
+            break;
+        }
+        case PHY_DEF_RX2_FREQUENCY:
+        {
+            phyParam.Value = CN779_RX_WND_2_FREQ;
+            break;
+        }
+        case PHY_DEF_RX2_DR:
+        {
+            phyParam.Value = CN779_RX_WND_2_DR;
+            break;
+        }
+        case PHY_CHANNELS_MASK:
+        {
+            phyParam.ChannelsMask = ChannelsMask;
+            break;
+        }
+        case PHY_CHANNELS_DEFAULT_MASK:
+        {
+            phyParam.ChannelsMask = ChannelsDefaultMask;
+            break;
+        }
+        case PHY_MAX_NB_CHANNELS:
+        {
+            phyParam.Value = CN779_MAX_NB_CHANNELS;
+            break;
+        }
+        case PHY_CHANNELS:
+        {
+            phyParam.Channels = Channels;
+            break;
+        }
+        case PHY_DEF_UPLINK_DWELL_TIME:
+        case PHY_DEF_DOWNLINK_DWELL_TIME:
+        {
+            phyParam.Value = 0;
+            break;
+        }
+        case PHY_DEF_MAX_EIRP:
+        {
+            phyParam.fValue = CN779_DEFAULT_MAX_EIRP;
+            break;
+        }
+        case PHY_DEF_ANTENNA_GAIN:
+        {
+            phyParam.fValue = CN779_DEFAULT_ANTENNA_GAIN;
+            break;
+        }
+        case PHY_NB_JOIN_TRIALS:
+        case PHY_DEF_NB_JOIN_TRIALS:
+        {
+            phyParam.Value = 48;
+            break;
+        }
+        case PHY_BEACON_INTERVAL:
+        {
+            phyParam.Value = CN779_BEACON_INTERVAL;
+            break;
+        }
+        case PHY_BEACON_RESERVED:
+        {
+            phyParam.Value = CN779_BEACON_RESERVED;
+            break;
+        }
+        case PHY_BEACON_GUARD:
+        {
+            phyParam.Value = CN779_BEACON_GUARD;
+            break;
+        }
+        case PHY_BEACON_WINDOW:
+        {
+            phyParam.Value = CN779_BEACON_WINDOW;
+            break;
+        }
+        case PHY_BEACON_WINDOW_SLOTS:
+        {
+            phyParam.Value = CN779_BEACON_WINDOW_SLOTS;
+            break;
+        }
+        case PHY_PING_SLOT_WINDOW:
+        {
+            phyParam.Value = CN779_PING_SLOT_WINDOW;
+            break;
+        }
+        case PHY_BEACON_SYMBOL_TO_DEFAULT:
+        {
+            phyParam.Value = CN779_BEACON_SYMBOL_TO_DEFAULT;
+            break;
+        }
+        case PHY_BEACON_SYMBOL_TO_EXPANSION_MAX:
+        {
+            phyParam.Value = CN779_BEACON_SYMBOL_TO_EXPANSION_MAX;
+            break;
+        }
+        case PHY_PING_SLOT_SYMBOL_TO_EXPANSION_MAX:
+        {
+            phyParam.Value = CN779_PING_SLOT_SYMBOL_TO_EXPANSION_MAX;
+            break;
+        }
+        case PHY_BEACON_SYMBOL_TO_EXPANSION_FACTOR:
+        {
+            phyParam.Value = CN779_BEACON_SYMBOL_TO_EXPANSION_FACTOR;
+            break;
+        }
+        case PHY_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR:
+        {
+            phyParam.Value = CN779_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR;
+            break;
+        }
+        case PHY_MAX_BEACON_LESS_PERIOD:
+        {
+            phyParam.Value = CN779_MAX_BEACON_LESS_PERIOD;
+            break;
+        }
+        case PHY_BEACON_DELAY_BEACON_TIMING_ANS:
+        {
+            phyParam.Value = CN779_BEACON_DELAY_BEACON_TIMING_ANS;
+            break;
+        }
+        case PHY_BEACON_CHANNEL_FREQ:
+        {
+            phyParam.Value = CN779_BEACON_CHANNEL_FREQ;
+            break;
+        }
+        case PHY_PINGSLOT_CHANNEL_FREQ:
+        {
+            phyParam.Value = CN779_BEACON_CHANNEL_FREQ;
+            break;
+        }
+        case PHY_BEACON_SIZE:
+        {
+            phyParam.Value = CN779_BEACON_SIZE;
+            break;
+        }
+        case PHY_BEACON_CHANNEL_DR:
+        {
+            phyParam.Value = CN779_BEACON_CHANNEL_DR;
+            break;
+        }
+        default:
+        {
+            break;
+        }
+    }
+
+    return phyParam;
+}
+
+void RegionCN779SetBandTxDone( SetBandTxDoneParams_t* txDone )
+{
+    RegionCommonSetBandTxDone( txDone->Joined, &Bands[Channels[txDone->Channel].Band], txDone->LastTxDoneTime );
+}
+
+void RegionCN779InitDefaults( InitType_t type )
+{
+    switch( type )
+    {
+        case INIT_TYPE_INIT:
+        {
+            // Channels
+            Channels[0] = ( ChannelParams_t ) CN779_LC1;
+            Channels[1] = ( ChannelParams_t ) CN779_LC2;
+            Channels[2] = ( ChannelParams_t ) CN779_LC3;
+
+            // Initialize the channels default mask
+            ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 );
+            // Update the channels mask
+            RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 1 );
+            break;
+        }
+        case INIT_TYPE_RESTORE:
+        {
+            // Restore channels default mask
+            ChannelsMask[0] |= ChannelsDefaultMask[0];
+            break;
+        }
+        default:
+        {
+            break;
+        }
+    }
+}
+
+bool RegionCN779Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute )
+{
+    switch( phyAttribute )
+    {
+        case PHY_TX_DR:
+        {
+            return RegionCommonValueInRange( verify->DatarateParams.Datarate, CN779_TX_MIN_DATARATE, CN779_TX_MAX_DATARATE );
+        }
+        case PHY_DEF_TX_DR:
+        {
+            return RegionCommonValueInRange( verify->DatarateParams.Datarate, DR_0, DR_5 );
+        }
+        case PHY_RX_DR:
+        {
+            return RegionCommonValueInRange( verify->DatarateParams.Datarate, CN779_RX_MIN_DATARATE, CN779_RX_MAX_DATARATE );
+        }
+        case PHY_DEF_TX_POWER:
+        case PHY_TX_POWER:
+        {
+            // Remark: switched min and max!
+            return RegionCommonValueInRange( verify->TxPower, CN779_MAX_TX_POWER, CN779_MIN_TX_POWER );
+        }
+        case PHY_DUTY_CYCLE:
+        {
+            return CN779_DUTY_CYCLE_ENABLED;
+        }
+        case PHY_NB_JOIN_TRIALS:
+        {
+            if( verify->NbJoinTrials < 48 )
+            {
+                return false;
+            }
+            break;
+        }
+        default:
+            return false;
+    }
+    return true;
+}
+
+void RegionCN779ApplyCFList( ApplyCFListParams_t* applyCFList )
+{
+    ChannelParams_t newChannel;
+    ChannelAddParams_t channelAdd;
+    ChannelRemoveParams_t channelRemove;
+
+    // Setup default datarate range
+    newChannel.DrRange.Value = ( DR_5 << 4 ) | DR_0;
+
+    // Size of the optional CF list
+    if( applyCFList->Size != 16 )
+    {
+        return;
+    }
+
+    // Last byte is RFU, don't take it into account
+    for( uint8_t i = 0, chanIdx = CN779_NUMB_DEFAULT_CHANNELS; chanIdx < CN779_MAX_NB_CHANNELS; i+=3, chanIdx++ )
+    {
+        if( chanIdx < ( CN779_NUMB_CHANNELS_CF_LIST + CN779_NUMB_DEFAULT_CHANNELS ) )
+        {
+            // Channel frequency
+            newChannel.Frequency = (uint32_t) applyCFList->Payload[i];
+            newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 1] << 8 );
+            newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 2] << 16 );
+            newChannel.Frequency *= 100;
+
+            // Initialize alternative frequency to 0
+            newChannel.Rx1Frequency = 0;
+        }
+        else
+        {
+            newChannel.Frequency = 0;
+            newChannel.DrRange.Value = 0;
+            newChannel.Rx1Frequency = 0;
+        }
+
+        if( newChannel.Frequency != 0 )
+        {
+            channelAdd.NewChannel = &newChannel;
+            channelAdd.ChannelId = chanIdx;
+
+            // Try to add all channels
+            RegionCN779ChannelAdd( &channelAdd );
+        }
+        else
+        {
+            channelRemove.ChannelId = chanIdx;
+
+            RegionCN779ChannelsRemove( &channelRemove );
+        }
+    }
+}
+
+bool RegionCN779ChanMaskSet( ChanMaskSetParams_t* chanMaskSet )
+{
+    switch( chanMaskSet->ChannelsMaskType )
+    {
+        case CHANNELS_MASK:
+        {
+            RegionCommonChanMaskCopy( ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 );
+            break;
+        }
+        case CHANNELS_DEFAULT_MASK:
+        {
+            RegionCommonChanMaskCopy( ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 );
+            break;
+        }
+        default:
+            return false;
+    }
+    return true;
+}
+
+bool RegionCN779AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter )
+{
+    bool adrAckReq = false;
+    int8_t datarate = adrNext->Datarate;
+    int8_t txPower = adrNext->TxPower;
+    GetPhyParams_t getPhy;
+    PhyParam_t phyParam;
+
+    // Report back the adr ack counter
+    *adrAckCounter = adrNext->AdrAckCounter;
+
+    if( adrNext->AdrEnabled == true )
+    {
+        if( datarate == CN779_TX_MIN_DATARATE )
+        {
+            *adrAckCounter = 0;
+            adrAckReq = false;
+        }
+        else
+        {
+            if( adrNext->AdrAckCounter >= CN779_ADR_ACK_LIMIT )
+            {
+                adrAckReq = true;
+                txPower = CN779_MAX_TX_POWER;
+            }
+            else
+            {
+                adrAckReq = false;
+            }
+            if( adrNext->AdrAckCounter >= ( CN779_ADR_ACK_LIMIT + CN779_ADR_ACK_DELAY ) )
+            {
+                if( ( adrNext->AdrAckCounter % CN779_ADR_ACK_DELAY ) == 1 )
+                {
+                    // Decrease the datarate
+                    getPhy.Attribute = PHY_NEXT_LOWER_TX_DR;
+                    getPhy.Datarate = datarate;
+                    getPhy.UplinkDwellTime = adrNext->UplinkDwellTime;
+                    phyParam = RegionCN779GetPhyParam( &getPhy );
+                    datarate = phyParam.Value;
+
+                    if( datarate == CN779_TX_MIN_DATARATE )
+                    {
+                        // We must set adrAckReq to false as soon as we reach the lowest datarate
+                        adrAckReq = false;
+                        if( adrNext->UpdateChanMask == true )
+                        {
+                            // Re-enable default channels
+                            ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 );
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    *drOut = datarate;
+    *txPowOut = txPower;
+    return adrAckReq;
+}
+
+void RegionCN779ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams )
+{
+    double tSymbol = 0.0;
+
+    // Get the datarate, perform a boundary check
+    rxConfigParams->Datarate = MIN( datarate, CN779_RX_MAX_DATARATE );
+    rxConfigParams->Bandwidth = GetBandwidth( rxConfigParams->Datarate );
+
+    if( rxConfigParams->Datarate == DR_7 )
+    { // FSK
+        tSymbol = RegionCommonComputeSymbolTimeFsk( DataratesCN779[rxConfigParams->Datarate] );
+    }
+    else
+    { // LoRa
+        tSymbol = RegionCommonComputeSymbolTimeLoRa( DataratesCN779[rxConfigParams->Datarate], BandwidthsCN779[rxConfigParams->Datarate] );
+    }
+
+    RegionCommonComputeRxWindowParameters( tSymbol, minRxSymbols, rxError, RADIO_WAKEUP_TIME, &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset );
+}
+
+bool RegionCN779RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate )
+{
+    RadioModems_t modem;
+    int8_t dr = rxConfig->Datarate;
+    uint8_t maxPayload = 0;
+    int8_t phyDr = 0;
+    uint32_t frequency = rxConfig->Frequency;
+
+    if( Radio.GetStatus( ) != RF_IDLE )
+    {
+        return false;
+    }
+
+    if( rxConfig->Window == 0 )
+    {
+        // Apply window 1 frequency
+        frequency = Channels[rxConfig->Channel].Frequency;
+        // Apply the alternative RX 1 window frequency, if it is available
+        if( Channels[rxConfig->Channel].Rx1Frequency != 0 )
+        {
+            frequency = Channels[rxConfig->Channel].Rx1Frequency;
+        }
+    }
+
+    // Read the physical datarate from the datarates table
+    phyDr = DataratesCN779[dr];
+
+    Radio.SetChannel( frequency );
+
+    // Radio configuration
+    if( dr == DR_7 )
+    {
+        modem = MODEM_FSK;
+        Radio.SetRxConfig( modem, 50000, phyDr * 1000, 0, 83333, 5, rxConfig->WindowTimeout, false, 0, true, 0, 0, false, rxConfig->RxContinuous );
+    }
+    else
+    {
+        modem = MODEM_LORA;
+        Radio.SetRxConfig( modem, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous );
+    }
+
+    if( rxConfig->RepeaterSupport == true )
+    {
+        maxPayload = MaxPayloadOfDatarateRepeaterCN779[dr];
+    }
+    else
+    {
+        maxPayload = MaxPayloadOfDatarateCN779[dr];
+    }
+    Radio.SetMaxPayloadLength( modem, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD );
+
+    *datarate = (uint8_t) dr;
+    return true;
+}
+
+bool RegionCN779TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir )
+{
+    RadioModems_t modem;
+    int8_t phyDr = DataratesCN779[txConfig->Datarate];
+    int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, Bands[Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, ChannelsMask );
+    uint32_t bandwidth = GetBandwidth( txConfig->Datarate );
+    int8_t phyTxPower = 0;
+
+    // Calculate physical TX power
+    phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain );
+
+    // Setup the radio frequency
+    Radio.SetChannel( Channels[txConfig->Channel].Frequency );
+
+    if( txConfig->Datarate == DR_7 )
+    { // High Speed FSK channel
+        modem = MODEM_FSK;
+        Radio.SetTxConfig( modem, phyTxPower, 25000, bandwidth, phyDr * 1000, 0, 5, false, true, 0, 0, false, 3000 );
+    }
+    else
+    {
+        modem = MODEM_LORA;
+        Radio.SetTxConfig( modem, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 3000 );
+    }
+
+    // Setup maximum payload lenght of the radio driver
+    Radio.SetMaxPayloadLength( modem, txConfig->PktLen );
+    // Get the time-on-air of the next tx frame
+    *txTimeOnAir = Radio.TimeOnAir( modem, txConfig->PktLen );
+
+    *txPower = txPowerLimited;
+    return true;
+}
+
+uint8_t RegionCN779LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed )
+{
+    uint8_t status = 0x07;
+    RegionCommonLinkAdrParams_t linkAdrParams;
+    uint8_t nextIndex = 0;
+    uint8_t bytesProcessed = 0;
+    uint16_t chMask = 0;
+    GetPhyParams_t getPhy;
+    PhyParam_t phyParam;
+    RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams;
+
+    while( bytesProcessed < linkAdrReq->PayloadSize )
+    {
+        // Get ADR request parameters
+        nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams );
+
+        if( nextIndex == 0 )
+            break; // break loop, since no more request has been found
+
+        // Update bytes processed
+        bytesProcessed += nextIndex;
+
+        // Revert status, as we only check the last ADR request for the channel mask KO
+        status = 0x07;
+
+        // Setup temporary channels mask
+        chMask = linkAdrParams.ChMask;
+
+        // Verify channels mask
+        if( ( linkAdrParams.ChMaskCtrl == 0 ) && ( chMask == 0 ) )
+        {
+            status &= 0xFE; // Channel mask KO
+        }
+        else if( ( ( linkAdrParams.ChMaskCtrl >= 1 ) && ( linkAdrParams.ChMaskCtrl <= 5 )) ||
+                ( linkAdrParams.ChMaskCtrl >= 7 ) )
+        {
+            // RFU
+            status &= 0xFE; // Channel mask KO
+        }
+        else
+        {
+            for( uint8_t i = 0; i < CN779_MAX_NB_CHANNELS; i++ )
+            {
+                if( linkAdrParams.ChMaskCtrl == 6 )
+                {
+                    if( Channels[i].Frequency != 0 )
+                    {
+                        chMask |= 1 << i;
+                    }
+                }
+                else
+                {
+                    if( ( ( chMask & ( 1 << i ) ) != 0 ) &&
+                        ( Channels[i].Frequency == 0 ) )
+                    {// Trying to enable an undefined channel
+                        status &= 0xFE; // Channel mask KO
+                    }
+                }
+            }
+        }
+    }
+
+    // Get the minimum possible datarate
+    getPhy.Attribute = PHY_MIN_TX_DR;
+    getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime;
+    phyParam = RegionCN779GetPhyParam( &getPhy );
+
+    linkAdrVerifyParams.Status = status;
+    linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled;
+    linkAdrVerifyParams.Datarate = linkAdrParams.Datarate;
+    linkAdrVerifyParams.TxPower = linkAdrParams.TxPower;
+    linkAdrVerifyParams.NbRep = linkAdrParams.NbRep;
+    linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate;
+    linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower;
+    linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep;
+    linkAdrVerifyParams.NbChannels = CN779_MAX_NB_CHANNELS;
+    linkAdrVerifyParams.ChannelsMask = &chMask;
+    linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value;
+    linkAdrVerifyParams.MaxDatarate = CN779_TX_MAX_DATARATE;
+    linkAdrVerifyParams.Channels = Channels;
+    linkAdrVerifyParams.MinTxPower = CN779_MIN_TX_POWER;
+    linkAdrVerifyParams.MaxTxPower = CN779_MAX_TX_POWER;
+
+    // Verify the parameters and update, if necessary
+    status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep );
+
+    // Update channelsMask if everything is correct
+    if( status == 0x07 )
+    {
+        // Set the channels mask to a default value
+        memset( ChannelsMask, 0, sizeof( ChannelsMask ) );
+        // Update the channels mask
+        ChannelsMask[0] = chMask;
+    }
+
+    // Update status variables
+    *drOut = linkAdrParams.Datarate;
+    *txPowOut = linkAdrParams.TxPower;
+    *nbRepOut = linkAdrParams.NbRep;
+    *nbBytesParsed = bytesProcessed;
+
+    return status;
+}
+
+uint8_t RegionCN779RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq )
+{
+    uint8_t status = 0x07;
+
+    // Verify radio frequency
+    if( Radio.CheckRfFrequency( rxParamSetupReq->Frequency ) == false )
+    {
+        status &= 0xFE; // Channel frequency KO
+    }
+
+    // Verify datarate
+    if( RegionCommonValueInRange( rxParamSetupReq->Datarate, CN779_RX_MIN_DATARATE, CN779_RX_MAX_DATARATE ) == false )
+    {
+        status &= 0xFD; // Datarate KO
+    }
+
+    // Verify datarate offset
+    if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, CN779_MIN_RX1_DR_OFFSET, CN779_MAX_RX1_DR_OFFSET ) == false )
+    {
+        status &= 0xFB; // Rx1DrOffset range KO
+    }
+
+    return status;
+}
+
+uint8_t RegionCN779NewChannelReq( NewChannelReqParams_t* newChannelReq )
+{
+    uint8_t status = 0x03;
+    ChannelAddParams_t channelAdd;
+    ChannelRemoveParams_t channelRemove;
+
+    if( newChannelReq->NewChannel->Frequency == 0 )
+    {
+        channelRemove.ChannelId = newChannelReq->ChannelId;
+
+        // Remove
+        if( RegionCN779ChannelsRemove( &channelRemove ) == false )
+        {
+            status &= 0xFC;
+        }
+    }
+    else
+    {
+        channelAdd.NewChannel = newChannelReq->NewChannel;
+        channelAdd.ChannelId = newChannelReq->ChannelId;
+
+        switch( RegionCN779ChannelAdd( &channelAdd ) )
+        {
+            case LORAMAC_STATUS_OK:
+            {
+                break;
+            }
+            case LORAMAC_STATUS_FREQUENCY_INVALID:
+            {
+                status &= 0xFE;
+                break;
+            }
+            case LORAMAC_STATUS_DATARATE_INVALID:
+            {
+                status &= 0xFD;
+                break;
+            }
+            case LORAMAC_STATUS_FREQ_AND_DR_INVALID:
+            {
+                status &= 0xFC;
+                break;
+            }
+            default:
+            {
+                status &= 0xFC;
+                break;
+            }
+        }
+    }
+
+    return status;
+}
+
+int8_t RegionCN779TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq )
+{
+    return -1;
+}
+
+uint8_t RegionCN779DlChannelReq( DlChannelReqParams_t* dlChannelReq )
+{
+    uint8_t status = 0x03;
+
+    // Verify if the frequency is supported
+    if( VerifyTxFreq( dlChannelReq->Rx1Frequency ) == false )
+    {
+        status &= 0xFE;
+    }
+
+    // Verify if an uplink frequency exists
+    if( Channels[dlChannelReq->ChannelId].Frequency == 0 )
+    {
+        status &= 0xFD;
+    }
+
+    // Apply Rx1 frequency, if the status is OK
+    if( status == 0x03 )
+    {
+        Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency;
+    }
+
+    return status;
+}
+
+int8_t RegionCN779AlternateDr( AlternateDrParams_t* alternateDr )
+{
+    int8_t datarate = 0;
+
+    if( ( alternateDr->NbTrials % 48 ) == 0 )
+    {
+        datarate = DR_0;
+    }
+    else if( ( alternateDr->NbTrials % 32 ) == 0 )
+    {
+        datarate = DR_1;
+    }
+    else if( ( alternateDr->NbTrials % 24 ) == 0 )
+    {
+        datarate = DR_2;
+    }
+    else if( ( alternateDr->NbTrials % 16 ) == 0 )
+    {
+        datarate = DR_3;
+    }
+    else if( ( alternateDr->NbTrials % 8 ) == 0 )
+    {
+        datarate = DR_4;
+    }
+    else
+    {
+        datarate = DR_5;
+    }
+    return datarate;
+}
+
+void RegionCN779CalcBackOff( CalcBackOffParams_t* calcBackOff )
+{
+    RegionCommonCalcBackOffParams_t calcBackOffParams;
+
+    calcBackOffParams.Channels = Channels;
+    calcBackOffParams.Bands = Bands;
+    calcBackOffParams.LastTxIsJoinRequest = calcBackOff->LastTxIsJoinRequest;
+    calcBackOffParams.Joined = calcBackOff->Joined;
+    calcBackOffParams.DutyCycleEnabled = calcBackOff->DutyCycleEnabled;
+    calcBackOffParams.Channel = calcBackOff->Channel;
+    calcBackOffParams.ElapsedTime = calcBackOff->ElapsedTime;
+    calcBackOffParams.TxTimeOnAir = calcBackOff->TxTimeOnAir;
+
+    RegionCommonCalcBackOff( &calcBackOffParams );
+}
+
+bool RegionCN779NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff )
+{
+    uint8_t nbEnabledChannels = 0;
+    uint8_t delayTx = 0;
+    uint8_t enabledChannels[CN779_MAX_NB_CHANNELS] = { 0 };
+    TimerTime_t nextTxDelay = 0;
+
+    if( RegionCommonCountChannels( ChannelsMask, 0, 1 ) == 0 )
+    { // Reactivate default channels
+        ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 );
+    }
+
+    if( nextChanParams->AggrTimeOff <= TimerGetElapsedTime( nextChanParams->LastAggrTx ) )
+    {
+        // Reset Aggregated time off
+        *aggregatedTimeOff = 0;
+
+        // Update bands Time OFF
+        nextTxDelay = RegionCommonUpdateBandTimeOff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, Bands, CN779_MAX_NB_BANDS );
+
+        // Search how many channels are enabled
+        nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Joined, nextChanParams->Datarate,
+                                                      ChannelsMask, Channels,
+                                                      Bands, enabledChannels, &delayTx );
+    }
+    else
+    {
+        delayTx++;
+        nextTxDelay = nextChanParams->AggrTimeOff - TimerGetElapsedTime( nextChanParams->LastAggrTx );
+    }
+
+    if( nbEnabledChannels > 0 )
+    {
+        // We found a valid channel
+        *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )];
+
+        *time = 0;
+        return true;
+    }
+    else
+    {
+        if( delayTx > 0 )
+        {
+            // Delay transmission due to AggregatedTimeOff or to a band time off
+            *time = nextTxDelay;
+            return true;
+        }
+        // Datarate not supported by any channel, restore defaults
+        ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 );
+        *time = 0;
+        return false;
+    }
+}
+
+LoRaMacStatus_t RegionCN779ChannelAdd( ChannelAddParams_t* channelAdd )
+{
+    uint8_t band = 0;
+    bool drInvalid = false;
+    bool freqInvalid = false;
+    uint8_t id = channelAdd->ChannelId;
+
+    if( id >= CN779_MAX_NB_CHANNELS )
+    {
+        return LORAMAC_STATUS_PARAMETER_INVALID;
+    }
+
+    // Validate the datarate range
+    if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Min, CN779_TX_MIN_DATARATE, CN779_TX_MAX_DATARATE ) == false )
+    {
+        drInvalid = true;
+    }
+    if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, CN779_TX_MIN_DATARATE, CN779_TX_MAX_DATARATE ) == false )
+    {
+        drInvalid = true;
+    }
+    if( channelAdd->NewChannel->DrRange.Fields.Min > channelAdd->NewChannel->DrRange.Fields.Max )
+    {
+        drInvalid = true;
+    }
+
+    // Default channels don't accept all values
+    if( id < CN779_NUMB_DEFAULT_CHANNELS )
+    {
+        // Validate the datarate range for min: must be DR_0
+        if( channelAdd->NewChannel->DrRange.Fields.Min > DR_0 )
+        {
+            drInvalid = true;
+        }
+        // Validate the datarate range for max: must be DR_5 <= Max <= TX_MAX_DATARATE
+        if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, DR_5, CN779_TX_MAX_DATARATE ) == false )
+        {
+            drInvalid = true;
+        }
+        // We are not allowed to change the frequency
+        if( channelAdd->NewChannel->Frequency != Channels[id].Frequency )
+        {
+            freqInvalid = true;
+        }
+    }
+
+    // Check frequency
+    if( freqInvalid == false )
+    {
+        if( VerifyTxFreq( channelAdd->NewChannel->Frequency ) == false )
+        {
+            freqInvalid = true;
+        }
+    }
+
+    // Check status
+    if( ( drInvalid == true ) && ( freqInvalid == true ) )
+    {
+        return LORAMAC_STATUS_FREQ_AND_DR_INVALID;
+    }
+    if( drInvalid == true )
+    {
+        return LORAMAC_STATUS_DATARATE_INVALID;
+    }
+    if( freqInvalid == true )
+    {
+        return LORAMAC_STATUS_FREQUENCY_INVALID;
+    }
+
+    memcpy( &(Channels[id]), channelAdd->NewChannel, sizeof( Channels[id] ) );
+    Channels[id].Band = band;
+    ChannelsMask[0] |= ( 1 << id );
+    return LORAMAC_STATUS_OK;
+}
+
+bool RegionCN779ChannelsRemove( ChannelRemoveParams_t* channelRemove  )
+{
+    uint8_t id = channelRemove->ChannelId;
+
+    if( id < CN779_NUMB_DEFAULT_CHANNELS )
+    {
+        return false;
+    }
+
+    // Remove the channel from the list of channels
+    Channels[id] = ( ChannelParams_t ){ 0, 0, { 0 }, 0 };
+
+    return RegionCommonChanDisable( ChannelsMask, id, CN779_MAX_NB_CHANNELS );
+}
+
+void RegionCN779SetContinuousWave( ContinuousWaveParams_t* continuousWave )
+{
+    int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, Bands[Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, ChannelsMask );
+    int8_t phyTxPower = 0;
+    uint32_t frequency = Channels[continuousWave->Channel].Frequency;
+
+    // Calculate physical TX power
+    phyTxPower = RegionCommonComputeTxPower( txPowerLimited, continuousWave->MaxEirp, continuousWave->AntennaGain );
+
+    Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout );
+}
+
+uint8_t RegionCN779ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset )
+{
+    int8_t datarate = dr - drOffset;
+
+    if( datarate < 0 )
+    {
+        datarate = DR_0;
+    }
+    return datarate;
+}
+
+void RegionCN779RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr, bool *beaconChannelSet )
+{
+    RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup;
+
+    regionCommonRxBeaconSetup.Datarates = DataratesCN779;
+    regionCommonRxBeaconSetup.ChannelPlanFrequency = CN779_BEACON_CHANNEL_FREQ;
+    regionCommonRxBeaconSetup.BeaconTimingAnsFrequency = CN779_BEACON_CHANNEL_FREQ;
+    regionCommonRxBeaconSetup.BeaconSize = CN779_BEACON_SIZE;
+    regionCommonRxBeaconSetup.BeaconDatarate = CN779_BEACON_CHANNEL_DR;
+    regionCommonRxBeaconSetup.BeaconChannelBW = CN779_BEACON_CHANNEL_BW;
+    regionCommonRxBeaconSetup.CustomFrequency = rxBeaconSetup->CustomFrequency;
+    regionCommonRxBeaconSetup.CustomFrequencyEnabled = rxBeaconSetup->CustomFrequencyEnabled;
+    regionCommonRxBeaconSetup.BeaconChannelSet = rxBeaconSetup->BeaconChannelSet;
+    regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime;
+    regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout;
+
+    RegionCommonRxBeaconSetup( &regionCommonRxBeaconSetup, beaconChannelSet );
+
+    // Store downlink datarate
+    *outDr = CN779_BEACON_CHANNEL_DR;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/region/RegionCN779.h	Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,555 @@
+/*!
+ * \file      RegionCN779.h
+ *
+ * \brief     Region definition for CN779
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ *                ______                              _
+ *               / _____)             _              | |
+ *              ( (____  _____ ____ _| |_ _____  ____| |__
+ *               \____ \| ___ |    (_   _) ___ |/ ___)  _ \
+ *               _____) ) ____| | | || |_| ____( (___| | | |
+ *              (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ *              (C)2013 Semtech
+ *
+ *               ___ _____ _   ___ _  _____ ___  ___  ___ ___
+ *              / __|_   _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ *              \__ \ | |/ _ \ (__| ' <| _| (_) |   / (__| _|
+ *              |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ *              embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author    Miguel Luis ( Semtech )
+ *
+ * \author    Gregory Cristian ( Semtech )
+ *
+ * \author    Daniel Jaeckle ( STACKFORCE )
+ *
+ * \defgroup  REGIONCN779 Region CN779
+ *            Implementation according to LoRaWAN Specification v1.0.2.
+ * \{
+ */
+#ifndef __REGION_CN779_H__
+#define __REGION_CN779_H__
+
+/*!
+ * LoRaMac maximum number of channels
+ */
+#define CN779_MAX_NB_CHANNELS                       16
+
+/*!
+ * Number of default channels
+ */
+#define CN779_NUMB_DEFAULT_CHANNELS                 3
+
+/*!
+ * Number of channels to apply for the CF list
+ */
+#define CN779_NUMB_CHANNELS_CF_LIST                 5
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define CN779_TX_MIN_DATARATE                       DR_0
+
+/*!
+ * Maximal datarate that can be used by the node
+ */
+#define CN779_TX_MAX_DATARATE                       DR_7
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define CN779_RX_MIN_DATARATE                       DR_0
+
+/*!
+ * Maximal datarate that can be used by the node
+ */
+#define CN779_RX_MAX_DATARATE                       DR_7
+
+/*!
+ * Default datarate used by the node
+ */
+#define CN779_DEFAULT_DATARATE                      DR_0
+
+/*!
+ * Minimal Rx1 receive datarate offset
+ */
+#define CN779_MIN_RX1_DR_OFFSET                     0
+
+/*!
+ * Maximal Rx1 receive datarate offset
+ */
+#define CN779_MAX_RX1_DR_OFFSET                     5
+
+/*!
+ * Default Rx1 receive datarate offset
+ */
+#define CN779_DEFAULT_RX1_DR_OFFSET                 0
+
+/*!
+ * Minimal Tx output power that can be used by the node
+ */
+#define CN779_MIN_TX_POWER                          TX_POWER_5
+
+/*!
+ * Maximal Tx output power that can be used by the node
+ */
+#define CN779_MAX_TX_POWER                          TX_POWER_0
+
+/*!
+ * Default Tx output power used by the node
+ */
+#define CN779_DEFAULT_TX_POWER                      TX_POWER_0
+
+/*!
+ * Default Max EIRP
+ */
+#define CN779_DEFAULT_MAX_EIRP                      12.15f
+
+/*!
+ * Default antenna gain
+ */
+#define CN779_DEFAULT_ANTENNA_GAIN                  2.15f
+
+/*!
+ * ADR Ack limit
+ */
+#define CN779_ADR_ACK_LIMIT                         64
+
+/*!
+ * ADR Ack delay
+ */
+#define CN779_ADR_ACK_DELAY                         32
+
+/*!
+ * Enabled or disabled the duty cycle
+ */
+#define CN779_DUTY_CYCLE_ENABLED                    1
+
+/*!
+ * Maximum RX window duration
+ */
+#define CN779_MAX_RX_WINDOW                         3000
+
+/*!
+ * Receive delay 1
+ */
+#define CN779_RECEIVE_DELAY1                        1000
+
+/*!
+ * Receive delay 2
+ */
+#define CN779_RECEIVE_DELAY2                        2000
+
+/*!
+ * Join accept delay 1
+ */
+#define CN779_JOIN_ACCEPT_DELAY1                    5000
+
+/*!
+ * Join accept delay 2
+ */
+#define CN779_JOIN_ACCEPT_DELAY2                    6000
+
+/*!
+ * Maximum frame counter gap
+ */
+#define CN779_MAX_FCNT_GAP                          16384
+
+/*!
+ * Ack timeout
+ */
+#define CN779_ACKTIMEOUT                            2000
+
+/*!
+ * Random ack timeout limits
+ */
+#define CN779_ACK_TIMEOUT_RND                       1000
+
+/*!
+ * Verification of default datarate
+ */
+#if ( CN779_DEFAULT_DATARATE > DR_5 )
+#error "A default DR higher than DR_5 may lead to connectivity loss."
+#endif
+
+/*!
+ * Second reception window channel frequency definition.
+ */
+#define CN779_RX_WND_2_FREQ                         786000000
+
+/*!
+ * Second reception window channel datarate definition.
+ */
+#define CN779_RX_WND_2_DR                           DR_0
+
+/*
+ * CLASS B
+ */
+/*!
+ * Beacon interval in ms
+ */
+#define CN779_BEACON_INTERVAL                       128000
+
+/*!
+ * Beacon reserved time in ms
+ */
+#define CN779_BEACON_RESERVED                       2120
+
+/*!
+ * Beacon guard time in ms
+ */
+#define CN779_BEACON_GUARD                          3000
+
+/*!
+ * Beacon window time in ms
+ */
+#define CN779_BEACON_WINDOW                         122880
+
+/*!
+ * Beacon window time in numer of slots
+ */
+#define CN779_BEACON_WINDOW_SLOTS                   4096
+
+/*!
+ * Ping slot length time in ms
+ */
+#define CN779_PING_SLOT_WINDOW                      30
+
+/*!
+ * Default symbol timeout for beacons and ping slot windows
+ */
+#define CN779_BEACON_SYMBOL_TO_DEFAULT              8
+
+/*!
+ * Maximum symbol timeout for beacons
+ */
+#define CN779_BEACON_SYMBOL_TO_EXPANSION_MAX        400
+
+/*!
+ * Maximum symbol timeout for ping slots
+ */
+#define CN779_PING_SLOT_SYMBOL_TO_EXPANSION_MAX     40
+
+/*!
+ * Symbol expansion value for beacon windows in case of beacon
+ * loss in symbols
+ */
+#define CN779_BEACON_SYMBOL_TO_EXPANSION_FACTOR     2
+
+/*!
+ * Symbol expansion value for ping slot windows in case of beacon
+ * loss in symbols
+ */
+#define CN779_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR  2
+
+/*!
+ * Maximum allowed beacon less time in ms
+ */
+#define CN779_MAX_BEACON_LESS_PERIOD                7200000
+
+/*!
+ * Delay time for the BeaconTimingAns in ms
+ */
+#define CN779_BEACON_DELAY_BEACON_TIMING_ANS        30
+
+/*!
+ * Beacon frequency
+ */
+#define CN779_BEACON_CHANNEL_FREQ                   785000000
+
+/*!
+ * Payload size of a beacon frame
+ */
+#define CN779_BEACON_SIZE                           17
+
+/*!
+ * Datarate of the beacon channel
+ */
+#define CN779_BEACON_CHANNEL_DR                     DR_3
+
+/*!
+ * Bandwith of the beacon channel
+ */
+#define CN779_BEACON_CHANNEL_BW                     0
+
+/*!
+ * LoRaMac maximum number of bands
+ */
+#define CN779_MAX_NB_BANDS                           1
+
+/*!
+ * Band 0 definition
+ * { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff }
+ */
+#define CN779_BAND0                                 { 100, CN779_MAX_TX_POWER, 0,  0 } //  1.0 %
+
+/*!
+ * LoRaMac default channel 1
+ * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
+ */
+#define CN779_LC1                                   { 779500000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
+/*!
+ * LoRaMac default channel 2
+ * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
+ */
+#define CN779_LC2                                   { 779700000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
+
+/*!
+ * LoRaMac default channel 3
+ * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
+ */
+#define CN779_LC3                                   { 779900000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
+
+/*!
+ * LoRaMac channels which are allowed for the join procedure
+ */
+#define CN779_JOIN_CHANNELS                         ( uint16_t )( LC( 1 ) | LC( 2 ) | LC( 3 ) )
+
+/*!
+ * Data rates table definition
+ */
+static const uint8_t DataratesCN779[]  = { 12, 11, 10,  9,  8,  7,  7, 50 };
+
+/*!
+ * Bandwidths table definition in Hz
+ */
+static const uint32_t BandwidthsCN779[] = { 125000, 125000, 125000, 125000, 125000, 125000, 250000, 0 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Cannot operate with repeater.
+ */
+static const uint8_t MaxPayloadOfDatarateCN779[] = { 51, 51, 51, 115, 242, 242, 242, 242 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Can operate with repeater.
+ */
+static const uint8_t MaxPayloadOfDatarateRepeaterCN779[] = { 51, 51, 51, 115, 222, 222, 222, 222 };
+
+/*!
+ * \brief The function gets a value of a specific phy attribute.
+ *
+ * \param [IN] getPhy Pointer to the function parameters.
+ *
+ * \retval Returns a structure containing the PHY parameter.
+ */
+PhyParam_t RegionCN779GetPhyParam( GetPhyParams_t* getPhy );
+
+/*!
+ * \brief Updates the last TX done parameters of the current channel.
+ *
+ * \param [IN] txDone Pointer to the function parameters.
+ */
+void RegionCN779SetBandTxDone( SetBandTxDoneParams_t* txDone );
+
+/*!
+ * \brief Initializes the channels masks and the channels.
+ *
+ * \param [IN] type Sets the initialization type.
+ */
+void RegionCN779InitDefaults( InitType_t type );
+
+/*!
+ * \brief Verifies a parameter.
+ *
+ * \param [IN] verify Pointer to the function parameters.
+ *
+ * \param [IN] type Sets the initialization type.
+ *
+ * \retval Returns true, if the parameter is valid.
+ */
+bool RegionCN779Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute );
+
+/*!
+ * \brief The function parses the input buffer and sets up the channels of the
+ *        CF list.
+ *
+ * \param [IN] applyCFList Pointer to the function parameters.
+ */
+void RegionCN779ApplyCFList( ApplyCFListParams_t* applyCFList );
+
+/*!
+ * \brief Sets a channels mask.
+ *
+ * \param [IN] chanMaskSet Pointer to the function parameters.
+ *
+ * \retval Returns true, if the channels mask could be set.
+ */
+bool RegionCN779ChanMaskSet( ChanMaskSetParams_t* chanMaskSet );
+
+/*!
+ * \brief Calculates the next datarate to set, when ADR is on or off.
+ *
+ * \param [IN] adrNext Pointer to the function parameters.
+ *
+ * \param [OUT] drOut The calculated datarate for the next TX.
+ *
+ * \param [OUT] txPowOut The TX power for the next TX.
+ *
+ * \param [OUT] adrAckCounter The calculated ADR acknowledgement counter.
+ *
+ * \retval Returns true, if an ADR request should be performed.
+ */
+bool RegionCN779AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter );
+
+/*!
+ * Computes the Rx window timeout and offset.
+ *
+ * \param [IN] datarate     Rx window datarate index to be used
+ *
+ * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame.
+ *
+ * \param [IN] rxError      System maximum timing error of the receiver. In milliseconds
+ *                          The receiver will turn on in a [-rxError : +rxError] ms
+ *                          interval around RxOffset
+ *
+ * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields.
+ */
+void RegionCN779ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams );
+
+/*!
+ * \brief Configuration of the RX windows.
+ *
+ * \param [IN] rxConfig Pointer to the function parameters.
+ *
+ * \param [OUT] datarate The datarate index which was set.
+ *
+ * \retval Returns true, if the configuration was applied successfully.
+ */
+bool RegionCN779RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate );
+
+/*!
+ * \brief TX configuration.
+ *
+ * \param [IN] txConfig Pointer to the function parameters.
+ *
+ * \param [OUT] txPower The tx power index which was set.
+ *
+ * \param [OUT] txTimeOnAir The time-on-air of the frame.
+ *
+ * \retval Returns true, if the configuration was applied successfully.
+ */
+bool RegionCN779TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir );
+
+/*!
+ * \brief The function processes a Link ADR Request.
+ *
+ * \param [IN] linkAdrReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionCN779LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed );
+
+/*!
+ * \brief The function processes a RX Parameter Setup Request.
+ *
+ * \param [IN] rxParamSetupReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionCN779RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq );
+
+/*!
+ * \brief The function processes a Channel Request.
+ *
+ * \param [IN] newChannelReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionCN779NewChannelReq( NewChannelReqParams_t* newChannelReq );
+
+/*!
+ * \brief The function processes a TX ParamSetup Request.
+ *
+ * \param [IN] txParamSetupReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ *         Returns -1, if the functionality is not implemented. In this case, the end node
+ *         shall not process the command.
+ */
+int8_t RegionCN779TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq );
+
+/*!
+ * \brief The function processes a DlChannel Request.
+ *
+ * \param [IN] dlChannelReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionCN779DlChannelReq( DlChannelReqParams_t* dlChannelReq );
+
+/*!
+ * \brief Alternates the datarate of the channel for the join request.
+ *
+ * \param [IN] alternateDr Pointer to the function parameters.
+ *
+ * \retval Datarate to apply.
+ */
+int8_t RegionCN779AlternateDr( AlternateDrParams_t* alternateDr );
+
+/*!
+ * \brief Calculates the back-off time.
+ *
+ * \param [IN] calcBackOff Pointer to the function parameters.
+ */
+void RegionCN779CalcBackOff( CalcBackOffParams_t* calcBackOff );
+
+/*!
+ * \brief Searches and set the next random available channel
+ *
+ * \param [OUT] channel Next channel to use for TX.
+ *
+ * \param [OUT] time Time to wait for the next transmission according to the duty
+ *              cycle.
+ *
+ * \param [OUT] aggregatedTimeOff Updates the aggregated time off.
+ *
+ * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate]
+ */
+bool RegionCN779NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff );
+
+/*!
+ * \brief Adds a channel.
+ *
+ * \param [IN] channelAdd Pointer to the function parameters.
+ *
+ * \retval Status of the operation.
+ */
+LoRaMacStatus_t RegionCN779ChannelAdd( ChannelAddParams_t* channelAdd );
+
+/*!
+ * \brief Removes a channel.
+ *
+ * \param [IN] channelRemove Pointer to the function parameters.
+ *
+ * \retval Returns true, if the channel was removed successfully.
+ */
+bool RegionCN779ChannelsRemove( ChannelRemoveParams_t* channelRemove  );
+
+/*!
+ * \brief Sets the radio into continuous wave mode.
+ *
+ * \param [IN] continuousWave Pointer to the function parameters.
+ */
+void RegionCN779SetContinuousWave( ContinuousWaveParams_t* continuousWave );
+
+/*!
+ * \brief Computes new datarate according to the given offset
+ *
+ * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms
+ *
+ * \param [IN] dr Current datarate
+ *
+ * \param [IN] drOffset Offset to be applied
+ *
+ * \retval newDr Computed datarate.
+ */
+uint8_t RegionCN779ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset );
+
+/*! \} defgroup REGIONCN779 */
+
+#endif // __REGION_CN779_H__
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/region/RegionCommon.cpp	Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,409 @@
+/*
+ / _____)             _              | |
+( (____  _____ ____ _| |_ _____  ____| |__
+ \____ \| ___ |    (_   _) ___ |/ ___)  _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+    (C)2013 Semtech
+ ___ _____ _   ___ _  _____ ___  ___  ___ ___
+/ __|_   _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+\__ \ | |/ _ \ (__| ' <| _| (_) |   / (__| _|
+|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+embedded.connectivity.solutions===============
+
+Description: LoRa MAC common region implementation
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+
+Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE )
+*/
+
+#include <stdbool.h>
+#include <string.h>
+#include <stdint.h>
+#include <math.h>
+
+#include "board.h"
+#include "radio.h"
+#include "timer.h"
+#include "utilities.h"
+#include "LoRaMac.h"
+#include "RegionCommon.h"
+
+
+
+#define BACKOFF_DC_1_HOUR       100
+#define BACKOFF_DC_10_HOURS     1000
+#define BACKOFF_DC_24_HOURS     10000
+
+
+
+static uint8_t CountChannels( uint16_t mask, uint8_t nbBits )
+{
+    uint8_t nbActiveBits = 0;
+
+    for( uint8_t j = 0; j < nbBits; j++ )
+    {
+        if( ( mask & ( 1 << j ) ) == ( 1 << j ) )
+        {
+            nbActiveBits++;
+        }
+    }
+    return nbActiveBits;
+}
+
+
+
+uint16_t RegionCommonGetJoinDc( TimerTime_t elapsedTime )
+{
+    uint16_t dutyCycle = 0;
+
+    if( elapsedTime < 3600000 )
+    {
+        dutyCycle = BACKOFF_DC_1_HOUR;
+    }
+    else if( elapsedTime < ( 3600000 + 36000000 ) )
+    {
+        dutyCycle = BACKOFF_DC_10_HOURS;
+    }
+    else
+    {
+        dutyCycle = BACKOFF_DC_24_HOURS;
+    }
+    return dutyCycle;
+}
+
+bool RegionCommonChanVerifyDr( uint8_t nbChannels, uint16_t* channelsMask, int8_t dr, int8_t minDr, int8_t maxDr, ChannelParams_t* channels )
+{
+    if( RegionCommonValueInRange( dr, minDr, maxDr ) == 0 )
+    {
+        return false;
+    }
+
+    for( uint8_t i = 0, k = 0; i < nbChannels; i += 16, k++ )
+    {
+        for( uint8_t j = 0; j < 16; j++ )
+        {
+            if( ( ( channelsMask[k] & ( 1 << j ) ) != 0 ) )
+            {// Check datarate validity for enabled channels
+                if( RegionCommonValueInRange( dr, ( channels[i + j].DrRange.Fields.Min & 0x0F ),
+                                                  ( channels[i + j].DrRange.Fields.Max & 0x0F ) ) == 1 )
+                {
+                    // At least 1 channel has been found we can return OK.
+                    return true;
+                }
+            }
+        }
+    }
+    return false;
+}
+
+uint8_t RegionCommonValueInRange( int8_t value, int8_t min, int8_t max )
+{
+    if( ( value >= min ) && ( value <= max ) )
+    {
+        return 1;
+    }
+    return 0;
+}
+
+bool RegionCommonChanDisable( uint16_t* channelsMask, uint8_t id, uint8_t maxChannels )
+{
+    uint8_t index = id / 16;
+
+    if( ( index > ( maxChannels / 16 ) ) || ( id >= maxChannels ) )
+    {
+        return false;
+    }
+
+    // Deactivate channel
+    channelsMask[index] &= ~( 1 << ( id % 16 ) );
+
+    return true;
+}
+
+uint8_t RegionCommonCountChannels( uint16_t* channelsMask, uint8_t startIdx, uint8_t stopIdx )
+{
+    uint8_t nbChannels = 0;
+
+    if( channelsMask == NULL )
+    {
+        return 0;
+    }
+
+    for( uint8_t i = startIdx; i < stopIdx; i++ )
+    {
+        nbChannels += CountChannels( channelsMask[i], 16 );
+    }
+
+    return nbChannels;
+}
+
+void RegionCommonChanMaskCopy( uint16_t* channelsMaskDest, uint16_t* channelsMaskSrc, uint8_t len )
+{
+    if( ( channelsMaskDest != NULL ) && ( channelsMaskSrc != NULL ) )
+    {
+        for( uint8_t i = 0; i < len; i++ )
+        {
+            channelsMaskDest[i] = channelsMaskSrc[i];
+        }
+    }
+}
+
+void RegionCommonSetBandTxDone( bool joined, Band_t* band, TimerTime_t lastTxDone )
+{
+    if( joined == true )
+    {
+        band->LastTxDoneTime = lastTxDone;
+    }
+    else
+    {
+        band->LastTxDoneTime = lastTxDone;
+        band->LastJoinTxDoneTime = lastTxDone;
+    }
+}
+
+TimerTime_t RegionCommonUpdateBandTimeOff( bool joined, bool dutyCycle, Band_t* bands, uint8_t nbBands )
+{
+    TimerTime_t nextTxDelay = ( TimerTime_t )( -1 );
+
+    // Update bands Time OFF
+    for( uint8_t i = 0; i < nbBands; i++ )
+    {
+        if( joined == false )
+        {
+            uint32_t txDoneTime =  MAX( TimerGetElapsedTime( bands[i].LastJoinTxDoneTime ),
+                                        ( dutyCycle == true ) ? TimerGetElapsedTime( bands[i].LastTxDoneTime ) : 0 );
+
+            if( bands[i].TimeOff <= txDoneTime )
+            {
+                bands[i].TimeOff = 0;
+            }
+            if( bands[i].TimeOff != 0 )
+            {
+                nextTxDelay = MIN( bands[i].TimeOff - txDoneTime, nextTxDelay );
+            }
+        }
+        else
+        {
+            if( dutyCycle == true )
+            {
+                if( bands[i].TimeOff <= TimerGetElapsedTime( bands[i].LastTxDoneTime ) )
+                {
+                    bands[i].TimeOff = 0;
+                }
+                if( bands[i].TimeOff != 0 )
+                {
+                    nextTxDelay = MIN( bands[i].TimeOff - TimerGetElapsedTime( bands[i].LastTxDoneTime ),
+                                       nextTxDelay );
+                }
+            }
+            else
+            {
+                nextTxDelay = 0;
+                bands[i].TimeOff = 0;
+            }
+        }
+    }
+    return nextTxDelay;
+}
+
+uint8_t RegionCommonParseLinkAdrReq( uint8_t* payload, RegionCommonLinkAdrParams_t* linkAdrParams )
+{
+    uint8_t retIndex = 0;
+
+    if( payload[0] == SRV_MAC_LINK_ADR_REQ )
+    {
+        // Parse datarate and tx power
+        linkAdrParams->Datarate = payload[1];
+        linkAdrParams->TxPower = linkAdrParams->Datarate & 0x0F;
+        linkAdrParams->Datarate = ( linkAdrParams->Datarate >> 4 ) & 0x0F;
+        // Parse ChMask
+        linkAdrParams->ChMask = ( uint16_t )payload[2];
+        linkAdrParams->ChMask |= ( uint16_t )payload[3] << 8;
+        // Parse ChMaskCtrl and nbRep
+        linkAdrParams->NbRep = payload[4];
+        linkAdrParams->ChMaskCtrl = ( linkAdrParams->NbRep >> 4 ) & 0x07;
+        linkAdrParams->NbRep &= 0x0F;
+
+        // LinkAdrReq has 4 bytes length + 1 byte CMD
+        retIndex = 5;
+    }
+    return retIndex;
+}
+
+uint8_t RegionCommonLinkAdrReqVerifyParams( RegionCommonLinkAdrReqVerifyParams_t* verifyParams, int8_t* dr, int8_t* txPow, uint8_t* nbRep )
+{
+    uint8_t status = verifyParams->Status;
+    int8_t datarate = verifyParams->Datarate;
+    int8_t txPower = verifyParams->TxPower;
+    int8_t nbRepetitions = verifyParams->NbRep;
+
+    // Handle the case when ADR is off.
+    if( verifyParams->AdrEnabled == false )
+    {
+        // When ADR is off, we are allowed to change the channels mask and the NbRep,
+        // if the datarate and the TX power of the LinkAdrReq are set to 0x0F.
+        if( ( verifyParams->Datarate != 0x0F ) || ( verifyParams->TxPower != 0x0F ) )
+        {
+            status = 0;
+            nbRepetitions = verifyParams->CurrentNbRep;
+        }
+        // Get the current datarate and tx power
+        datarate = verifyParams->CurrentDatarate;
+        txPower = verifyParams->CurrentTxPower;
+    }
+
+    if( status != 0 )
+    {
+        // Verify datarate. The variable phyParam. Value contains the minimum allowed datarate.
+        if( RegionCommonChanVerifyDr( verifyParams->NbChannels, verifyParams->ChannelsMask, datarate,
+                                      verifyParams->MinDatarate, verifyParams->MaxDatarate, verifyParams->Channels  ) == false )
+        {
+            status &= 0xFD; // Datarate KO
+        }
+
+        // Verify tx power
+        if( RegionCommonValueInRange( txPower, verifyParams->MaxTxPower, verifyParams->MinTxPower ) == 0 )
+        {
+            // Verify if the maximum TX power is exceeded
+            if( verifyParams->MaxTxPower > txPower )
+            { // Apply maximum TX power. Accept TX power.
+                txPower = verifyParams->MaxTxPower;
+            }
+            else
+            {
+                status &= 0xFB; // TxPower KO
+            }
+        }
+    }
+
+    // If the status is ok, verify the NbRep
+    if( status == 0x07 )
+    {
+        if( nbRepetitions == 0 )
+        { // Keep the current one
+            nbRepetitions = verifyParams->CurrentNbRep;
+        }
+    }
+
+    // Apply changes
+    *dr = datarate;
+    *txPow = txPower;
+    *nbRep = nbRepetitions;
+
+    return status;
+}
+
+double RegionCommonComputeSymbolTimeLoRa( uint8_t phyDr, uint32_t bandwidth )
+{
+    return ( ( double )( 1 << phyDr ) / ( double )bandwidth ) * 1000;
+}
+
+double RegionCommonComputeSymbolTimeFsk( uint8_t phyDr )
+{
+    return ( 8.0 / ( double )phyDr ); // 1 symbol equals 1 byte
+}
+
+void RegionCommonComputeRxWindowParameters( double tSymbol, uint8_t minRxSymbols, uint32_t rxError, uint32_t wakeUpTime, uint32_t* windowTimeout, int32_t* windowOffset )
+{
+    *windowTimeout = MAX( ( uint32_t )ceil( ( ( 2 * minRxSymbols - 8 ) * tSymbol + 2 * rxError ) / tSymbol ), minRxSymbols ); // Computed number of symbols
+    *windowOffset = ( int32_t )ceil( ( 4.0 * tSymbol ) - ( ( *windowTimeout * tSymbol ) / 2.0 ) - wakeUpTime );
+}
+
+int8_t RegionCommonComputeTxPower( int8_t txPowerIndex, float maxEirp, float antennaGain )
+{
+    int8_t phyTxPower = 0;
+
+    phyTxPower = ( int8_t )floor( ( maxEirp - ( txPowerIndex * 2U ) ) - antennaGain );
+
+    return phyTxPower;
+}
+
+void RegionCommonCalcBackOff( RegionCommonCalcBackOffParams_t* calcBackOffParams )
+{
+    uint8_t bandIdx = calcBackOffParams->Channels[calcBackOffParams->Channel].Band;
+    uint16_t dutyCycle = calcBackOffParams->Bands[bandIdx].DCycle;
+    uint16_t joinDutyCycle = 0;
+
+    // Reset time-off to initial value.
+    calcBackOffParams->Bands[bandIdx].TimeOff = 0;
+
+    if( calcBackOffParams->Joined == false )
+    {
+        // Get the join duty cycle
+        joinDutyCycle = RegionCommonGetJoinDc( calcBackOffParams->ElapsedTime );
+        // Apply the most restricting duty cycle
+        dutyCycle = MAX( dutyCycle, joinDutyCycle );
+        // Reset the timeoff if the last frame was not a join request and when the duty cycle is not enabled
+        if( ( calcBackOffParams->DutyCycleEnabled == false ) && ( calcBackOffParams->LastTxIsJoinRequest == false ) )
+        {
+            // This is the case when the duty cycle is off and the last uplink frame was not a join.
+            // This could happen in case of a rejoin, e.g. in compliance test mode.
+            // In this special case we have to set the time off to 0, since the join duty cycle shall only
+            // be applied after the first join request.
+            calcBackOffParams->Bands[bandIdx].TimeOff = 0;
+        }
+        else
+        {
+            // Apply band time-off.
+            calcBackOffParams->Bands[bandIdx].TimeOff = calcBackOffParams->TxTimeOnAir * dutyCycle - calcBackOffParams->TxTimeOnAir;
+        }
+    }
+    else
+    {
+        if( calcBackOffParams->DutyCycleEnabled == true )
+        {
+            calcBackOffParams->Bands[bandIdx].TimeOff = calcBackOffParams->TxTimeOnAir * dutyCycle - calcBackOffParams->TxTimeOnAir;
+        }
+        else
+        {
+            calcBackOffParams->Bands[bandIdx].TimeOff = 0;
+        }
+    }
+}
+
+
+void RegionCommonRxBeaconSetup( RegionCommonRxBeaconSetupParams_t* rxBeaconSetupParams, bool *beaconChannelSet )
+{
+    bool rxContinuous = true;
+    uint32_t frequency = rxBeaconSetupParams->CustomFrequency;
+    uint8_t datarate;
+
+    // Set the radio into sleep mode
+    Radio.Sleep( );
+
+    // If no custom frequency is enabled, use the default floor plan frequency
+    if( rxBeaconSetupParams->CustomFrequencyEnabled == false )
+    {
+        // Restore floor plan
+        frequency = rxBeaconSetupParams->ChannelPlanFrequency;
+    }
+
+    // Set the frequency which was provided by BeaconTimingAns MAC command
+    if( rxBeaconSetupParams->BeaconChannelSet == true )
+    {
+        // Take the frequency of the next beacon
+        *beaconChannelSet = false;
+        frequency = rxBeaconSetupParams->BeaconTimingAnsFrequency;
+    }
+
+    // Setup frequency and payload length
+    Radio.SetChannel( frequency );
+    Radio.SetMaxPayloadLength( MODEM_LORA, rxBeaconSetupParams->BeaconSize );
+
+    // Check the RX continuous mode
+    if( rxBeaconSetupParams->RxTime != 0 )
+    {
+        rxContinuous = false;
+    }
+
+    // Get region specific datarate
+    datarate = rxBeaconSetupParams->Datarates[rxBeaconSetupParams->BeaconDatarate];
+
+    // Setup radio
+    Radio.SetRxConfig( MODEM_LORA, rxBeaconSetupParams->BeaconChannelBW, datarate,
+                       1, 0, 10, rxBeaconSetupParams->SymbolTimeout, true, rxBeaconSetupParams->BeaconSize, false, 0, 0, false, rxContinuous );
+
+    Radio.Rx( rxBeaconSetupParams->RxTime );
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/region/RegionCommon.h	Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,424 @@
+/*!
+ * \file      RegionCommon.h
+ *
+ * \brief     Region independent implementations which are common to all regions.
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ *                ______                              _
+ *               / _____)             _              | |
+ *              ( (____  _____ ____ _| |_ _____  ____| |__
+ *               \____ \| ___ |    (_   _) ___ |/ ___)  _ \
+ *               _____) ) ____| | | || |_| ____( (___| | | |
+ *              (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ *              (C)2013 Semtech
+ *
+ *               ___ _____ _   ___ _  _____ ___  ___  ___ ___
+ *              / __|_   _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ *              \__ \ | |/ _ \ (__| ' <| _| (_) |   / (__| _|
+ *              |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ *              embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author    Miguel Luis ( Semtech )
+ *
+ * \author    Gregory Cristian ( Semtech )
+ *
+ * \author    Daniel Jaeckle ( STACKFORCE )
+ *
+ * \defgroup  REGIONCOMMON Common region implementation
+ *            Region independent implementations which are common to all regions.
+ * \{
+ */
+#ifndef __REGIONCOMMON_H__
+#define __REGIONCOMMON_H__
+
+typedef struct sRegionCommonLinkAdrParams
+{
+    /*!
+     * Number of repetitions.
+     */
+    uint8_t NbRep;
+    /*!
+     * Datarate.
+     */
+    int8_t Datarate;
+    /*!
+     * Tx power.
+     */
+    int8_t TxPower;
+    /*!
+     * Channels mask control field.
+     */
+    uint8_t ChMaskCtrl;
+    /*!
+     * Channels mask field.
+     */
+    uint16_t ChMask;
+}RegionCommonLinkAdrParams_t;
+
+typedef struct sRegionCommonLinkAdrReqVerifyParams
+{
+    /*!
+     * The current status of the AdrLinkRequest.
+     */
+    uint8_t Status;
+    /*!
+     * Set to true, if ADR is enabled.
+     */
+    bool AdrEnabled;
+    /*!
+     * The datarate the AdrLinkRequest wants to set.
+     */
+    int8_t Datarate;
+    /*!
+     * The TX power the AdrLinkRequest wants to set.
+     */
+    int8_t TxPower;
+    /*!
+     * The number of repetitions the AdrLinkRequest wants to set.
+     */
+    uint8_t NbRep;
+    /*!
+     * The current datarate the node is using.
+     */
+    int8_t CurrentDatarate;
+    /*!
+     * The current TX power the node is using.
+     */
+    int8_t CurrentTxPower;
+    /*!
+     * The current number of repetitions the node is using.
+     */
+    int8_t CurrentNbRep;
+    /*!
+     * The number of channels.
+     */
+    uint8_t NbChannels;
+    /*!
+     * Pointer to the first element of the channels mask.
+     */
+    uint16_t* ChannelsMask;
+    /*!
+     * The minimum possible datarate.
+     */
+    int8_t MinDatarate;
+    /*!
+     * The maximum possible datarate.
+     */
+    int8_t MaxDatarate;
+    /*!
+     * Pointer to the channels.
+     */
+    ChannelParams_t* Channels;
+    /*!
+     * The minimum possible TX power.
+     */
+    int8_t MinTxPower;
+    /*!
+     * The maximum possible TX power.
+     */
+    int8_t MaxTxPower;
+}RegionCommonLinkAdrReqVerifyParams_t;
+
+typedef struct sRegionCommonCalcBackOffParams
+{
+    /*!
+     * A pointer to region specific channels.
+     */
+    ChannelParams_t* Channels;
+    /*!
+     * A pointer to region specific bands.
+     */
+    Band_t* Bands;
+    /*!
+     * Set to true, if the last uplink was a join request.
+     */
+    bool LastTxIsJoinRequest;
+    /*!
+     * Set to true, if the node is joined.
+     */
+    bool Joined;
+    /*!
+     * Set to true, if the duty cycle is enabled.
+     */
+    bool DutyCycleEnabled;
+    /*!
+     * The current channel.
+     */
+    uint8_t Channel;
+    /*!
+     * The elapsed time since initialization.
+     */
+    TimerTime_t ElapsedTime;
+    /*!
+     * The time on air of the last Tx frame.
+     */
+    TimerTime_t TxTimeOnAir;
+}RegionCommonCalcBackOffParams_t;
+
+typedef struct sRegionCommonRxBeaconSetupParams
+{
+    /*!
+     * A pointer to the available datarates.
+     */
+    const uint8_t* Datarates;
+    /*!
+     * The default channel floor plan frequency.
+     */
+    uint32_t ChannelPlanFrequency;
+    /*!
+     * The frequency to use when a BeaconTimingAns was performed.
+     */
+    uint32_t BeaconTimingAnsFrequency;
+    /*!
+     * The size of the beacon frame.
+     */
+    uint8_t BeaconSize;
+    /*!
+     * The datarate of the beacon.
+     */
+    uint8_t BeaconDatarate;
+    /*!
+     * The channel bandwidth of the beacon.
+     */
+    uint8_t BeaconChannelBW;
+    /*!
+     * A custom frequency.
+     */
+    uint32_t CustomFrequency;
+    /*!
+     * Set to true, if a custom frequency shall be used.
+     */
+    bool CustomFrequencyEnabled;
+    /*!
+     * Set to true, if a BeaconTimingAns MAC command was performed.
+     */
+    bool BeaconChannelSet;
+    /*!
+     * The RX time.
+     */
+    uint32_t RxTime;
+    /*!
+     * The symbol timeout of the RX procedure.
+     */
+    uint16_t SymbolTimeout;
+}RegionCommonRxBeaconSetupParams_t;
+
+/*!
+ * \brief Calculates the join duty cycle.
+ *        This is a generic function and valid for all regions.
+ *
+ * \param [IN] elapsedTime Elapsed time since the start of the device.
+ *
+ * \retval Duty cycle restriction.
+ */
+uint16_t RegionCommonGetJoinDc( TimerTime_t elapsedTime );
+
+/*!
+ * \brief Verifies, if a value is in a given range.
+ *        This is a generic function and valid for all regions.
+ *
+ * \param [IN] value Value to verify, if it is in range.
+ *
+ * \param [IN] min Minimum possible value.
+ *
+ * \param [IN] max Maximum possible value.
+ *
+ * \retval Returns 1 if the value is in range, otherwise 0.
+ */
+uint8_t RegionCommonValueInRange( int8_t value, int8_t min, int8_t max );
+
+/*!
+ * \brief Verifies, if a datarate is available on an active channel.
+ *        This is a generic function and valid for all regions.
+ *
+ * \param [IN] nbChannels Number of channels.
+ *
+ * \param [IN] channelsMask The channels mask of the region.
+ *
+ * \param [IN] dr The datarate to verify.
+ *
+ * \param [IN] minDr Minimum datarate.
+ *
+ * \param [IN] maxDr Maximum datarate.
+ *
+ * \param [IN] channels The channels of the region.
+ *
+ * \retval Returns true if the datarate is supported, false if not.
+ */
+bool RegionCommonChanVerifyDr( uint8_t nbChannels, uint16_t* channelsMask, int8_t dr,
+                            int8_t minDr, int8_t maxDr, ChannelParams_t* channels );
+
+/*!
+ * \brief Disables a channel in a given channels mask.
+ *        This is a generic function and valid for all regions.
+ *
+ * \param [IN] channelsMask The channels mask of the region.
+ *
+ * \param [IN] id The id of the channels mask to disable.
+ *
+ * \param [IN] maxChannels Maximum number of channels.
+ *
+ * \retval Returns true if the channel could be disabled, false if not.
+ */
+bool RegionCommonChanDisable( uint16_t* channelsMask, uint8_t id, uint8_t maxChannels );
+
+/*!
+ * \brief Counts the number of active channels in a given channels mask.
+ *        This is a generic function and valid for all regions.
+ *
+ * \param [IN] channelsMask The channels mask of the region.
+ *
+ * \param [IN] startIdx Start index.
+ *
+ * \param [IN] stopIdx Stop index ( the channels of this index will not be counted ).
+ *
+ * \retval Returns the number of active channels.
+ */
+uint8_t RegionCommonCountChannels( uint16_t* channelsMask, uint8_t startIdx, uint8_t stopIdx );
+
+/*!
+ * \brief Copy a channels mask.
+ *        This is a generic function and valid for all regions.
+ *
+ * \param [IN] channelsMaskDest The destination channels mask.
+ *
+ * \param [IN] channelsMaskSrc The source channels mask.
+ *
+ * \param [IN] len The index length to copy.
+ */
+void RegionCommonChanMaskCopy( uint16_t* channelsMaskDest, uint16_t* channelsMaskSrc, uint8_t len );
+
+/*!
+ * \brief Sets the last tx done property.
+ *        This is a generic function and valid for all regions.
+ *
+ * \param [IN] joined Set to true, if the node has joined the network
+ *
+ * \param [IN] band The band to be updated.
+ *
+ * \param [IN] lastTxDone The time of the last TX done.
+ */
+void RegionCommonSetBandTxDone( bool joined, Band_t* band, TimerTime_t lastTxDone );
+
+/*!
+ * \brief Updates the time-offs of the bands.
+ *        This is a generic function and valid for all regions.
+ *
+ * \param [IN] joined Set to true, if the node has joined the network
+ *
+ * \param [IN] dutyCycle Set to true, if the duty cycle is enabled.
+ *
+ * \param [IN] bands A pointer to the bands.
+ *
+ * \param [IN] nbBands The number of bands available.
+ *
+ * \retval Returns the time which must be waited to perform the next uplink.
+ */
+TimerTime_t RegionCommonUpdateBandTimeOff( bool joined, bool dutyCycle, Band_t* bands, uint8_t nbBands );
+
+/*!
+ * \brief Parses the parameter of an LinkAdrRequest.
+ *        This is a generic function and valid for all regions.
+ *
+ * \param [IN] payload Pointer to the payload containing the MAC commands. The payload
+ *                     must contain the CMD identifier, following by the parameters.
+ *
+ * \param [OUT] parseLinkAdr The function fills the structure with the ADR parameters.
+ *
+ * \retval Returns the length of the ADR request, if a request was found. Otherwise, the
+ *         function returns 0.
+ */
+uint8_t RegionCommonParseLinkAdrReq( uint8_t* payload, RegionCommonLinkAdrParams_t* parseLinkAdr );
+
+/*!
+ * \brief Verifies and updates the datarate, the TX power and the number of repetitions
+ *        of a LinkAdrRequest. This depends on the configuration of ADR also.
+ *
+ * \param [IN] verifyParams Pointer to a structure containing input parameters.
+ *
+ * \param [OUT] dr The updated datarate.
+ *
+ * \param [OUT] txPow The updated TX power.
+ *
+ * \param [OUT] nbRep The updated number of repetitions.
+ *
+ * \retval Returns the status according to the LinkAdrRequest definition.
+ */
+uint8_t RegionCommonLinkAdrReqVerifyParams( RegionCommonLinkAdrReqVerifyParams_t* verifyParams, int8_t* dr, int8_t* txPow, uint8_t* nbRep );
+
+/*!
+ * \brief Computes the symbol time for LoRa modulation.
+ *
+ * \param [IN] phyDr Physical datarate to use.
+ *
+ * \param [IN] bandwidth Bandwidth to use.
+ *
+ * \retval Returns the symbol time.
+ */
+double RegionCommonComputeSymbolTimeLoRa( uint8_t phyDr, uint32_t bandwidth );
+
+/*!
+ * \brief Computes the symbol time for FSK modulation.
+ *
+ * \param [IN] phyDr Physical datarate to use.
+ *
+ * \param [IN] bandwidth Bandwidth to use.
+ *
+ * \retval Returns the symbol time.
+ */
+double RegionCommonComputeSymbolTimeFsk( uint8_t phyDr );
+
+/*!
+ * \brief Computes the RX window timeout and the RX window offset.
+ *
+ * \param [IN] tSymbol Symbol timeout.
+ *
+ * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame.
+ *
+ * \param [IN] rxError System maximum timing error of the receiver. In milliseconds
+ *                     The receiver will turn on in a [-rxError : +rxError] ms interval around RxOffset.
+ *
+ * \param [IN] wakeUpTime Wakeup time of the system.
+ *
+ * \param [OUT] windowTimeout RX window timeout.
+ *
+ * \param [OUT] windowOffset RX window time offset to be applied to the RX delay.
+ */
+void RegionCommonComputeRxWindowParameters( double tSymbol, uint8_t minRxSymbols, uint32_t rxError, uint32_t wakeUpTime, uint32_t* windowTimeout, int32_t* windowOffset );
+
+/*!
+ * \brief Computes the txPower, based on the max EIRP and the antenna gain.
+ *
+ * \param [IN] txPower TX power index.
+ *
+ * \param [IN] maxEirp Maximum EIRP.
+ *
+ * \param [IN] antennaGain Antenna gain.
+ *
+ * \retval Returns the physical TX power.
+ */
+int8_t RegionCommonComputeTxPower( int8_t txPowerIndex, float maxEirp, float antennaGain );
+
+/*!
+ * \brief Calculates the duty cycle for the current band.
+ *
+ * \param [IN] calcBackOffParams A pointer to the input parameters.
+ */
+void RegionCommonCalcBackOff( RegionCommonCalcBackOffParams_t* calcBackOffParams );
+
+/*!
+ * \brief Sets up the radio into RX beacon mode.
+ *
+ * \param [IN] rxBeaconSetupParams A pointer to the input parameters.
+ *
+ * \param [OUT] beaconChannelSet A pointer ot the beaconChannelSet variable.
+ */
+void RegionCommonRxBeaconSetup( RegionCommonRxBeaconSetupParams_t* rxBeaconSetupParams, bool *beaconChannelSet );
+
+/*! \} defgroup REGIONCOMMON */
+
+#endif // __REGIONCOMMON_H__
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/region/RegionEU433.cpp	Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,1152 @@
+/*
+ / _____)             _              | |
+( (____  _____ ____ _| |_ _____  ____| |__
+ \____ \| ___ |    (_   _) ___ |/ ___)  _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+    (C)2013 Semtech
+ ___ _____ _   ___ _  _____ ___  ___  ___ ___
+/ __|_   _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+\__ \ | |/ _ \ (__| ' <| _| (_) |   / (__| _|
+|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+embedded.connectivity.solutions===============
+
+Description: LoRa MAC region EU433 implementation
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+
+Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE )
+*/
+#include <stdbool.h>
+#include <string.h>
+#include <stdint.h>
+#include <math.h>
+
+#include "board.h"
+#include "LoRaMac.h"
+
+#include "utilities.h"
+
+#include "Region.h"
+#include "RegionCommon.h"
+#include "RegionEU433.h"
+
+// Definitions
+#define CHANNELS_MASK_SIZE              1
+
+// Global attributes
+/*!
+ * LoRaMAC channels
+ */
+static ChannelParams_t Channels[EU433_MAX_NB_CHANNELS];
+
+/*!
+ * LoRaMac bands
+ */
+static Band_t Bands[EU433_MAX_NB_BANDS] =
+{
+    EU433_BAND0
+};
+
+/*!
+ * LoRaMac channels mask
+ */
+static uint16_t ChannelsMask[CHANNELS_MASK_SIZE];
+
+/*!
+ * LoRaMac channels default mask
+ */
+static uint16_t ChannelsDefaultMask[CHANNELS_MASK_SIZE];
+
+// Static functions
+static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr )
+{
+    uint8_t nextLowerDr = 0;
+
+    if( dr == minDr )
+    {
+        nextLowerDr = minDr;
+    }
+    else
+    {
+        nextLowerDr = dr - 1;
+    }
+    return nextLowerDr;
+}
+
+static uint32_t GetBandwidth( uint32_t drIndex )
+{
+    switch( BandwidthsEU433[drIndex] )
+    {
+        default:
+        case 125000:
+            return 0;
+        case 250000:
+            return 1;
+        case 500000:
+            return 2;
+    }
+}
+
+static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask )
+{
+    int8_t txPowerResult = txPower;
+
+    // Limit tx power to the band max
+    txPowerResult =  MAX( txPower, maxBandTxPower );
+
+    return txPowerResult;
+}
+
+static bool VerifyTxFreq( uint32_t freq )
+{
+    // Check radio driver support
+    if( Radio.CheckRfFrequency( freq ) == false )
+    {
+        return false;
+    }
+
+    if( ( freq < 433175000 ) || ( freq > 434665000 ) )
+    {
+        return false;
+    }
+    return true;
+}
+
+static uint8_t CountNbOfEnabledChannels( bool joined, uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx )
+{
+    uint8_t nbEnabledChannels = 0;
+    uint8_t delayTransmission = 0;
+
+    for( uint8_t i = 0, k = 0; i < EU433_MAX_NB_CHANNELS; i += 16, k++ )
+    {
+        for( uint8_t j = 0; j < 16; j++ )
+        {
+            if( ( channelsMask[k] & ( 1 << j ) ) != 0 )
+            {
+                if( channels[i + j].Frequency == 0 )
+                { // Check if the channel is enabled
+                    continue;
+                }
+                if( joined == false )
+                {
+                    if( ( EU433_JOIN_CHANNELS & ( 1 << j ) ) == 0 )
+                    {
+                        continue;
+                    }
+                }
+                if( RegionCommonValueInRange( datarate, channels[i + j].DrRange.Fields.Min,
+                                              channels[i + j].DrRange.Fields.Max ) == false )
+                { // Check if the current channel selection supports the given datarate
+                    continue;
+                }
+                if( bands[channels[i + j].Band].TimeOff > 0 )
+                { // Check if the band is available for transmission
+                    delayTransmission++;
+                    continue;
+                }
+                enabledChannels[nbEnabledChannels++] = i + j;
+            }
+        }
+    }
+
+    *delayTx = delayTransmission;
+    return nbEnabledChannels;
+}
+
+PhyParam_t RegionEU433GetPhyParam( GetPhyParams_t* getPhy )
+{
+    PhyParam_t phyParam = { 0 };
+
+    switch( getPhy->Attribute )
+    {
+        case PHY_MIN_RX_DR:
+        {
+            phyParam.Value = EU433_RX_MIN_DATARATE;
+            break;
+        }
+        case PHY_MIN_TX_DR:
+        {
+            phyParam.Value = EU433_TX_MIN_DATARATE;
+            break;
+        }
+        case PHY_DEF_TX_DR:
+        {
+            phyParam.Value = EU433_DEFAULT_DATARATE;
+            break;
+        }
+        case PHY_NEXT_LOWER_TX_DR:
+        {
+            phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, EU433_TX_MIN_DATARATE );
+            break;
+        }
+        case PHY_DEF_TX_POWER:
+        {
+            phyParam.Value = EU433_DEFAULT_TX_POWER;
+            break;
+        }
+        case PHY_MAX_PAYLOAD:
+        {
+            phyParam.Value = MaxPayloadOfDatarateEU433[getPhy->Datarate];
+            break;
+        }
+        case PHY_MAX_PAYLOAD_REPEATER:
+        {
+            phyParam.Value = MaxPayloadOfDatarateRepeaterEU433[getPhy->Datarate];
+            break;
+        }
+        case PHY_DUTY_CYCLE:
+        {
+            phyParam.Value = EU433_DUTY_CYCLE_ENABLED;
+            break;
+        }
+        case PHY_MAX_RX_WINDOW:
+        {
+            phyParam.Value = EU433_MAX_RX_WINDOW;
+            break;
+        }
+        case PHY_RECEIVE_DELAY1:
+        {
+            phyParam.Value = EU433_RECEIVE_DELAY1;
+            break;
+        }
+        case PHY_RECEIVE_DELAY2:
+        {
+            phyParam.Value = EU433_RECEIVE_DELAY2;
+            break;
+        }
+        case PHY_JOIN_ACCEPT_DELAY1:
+        {
+            phyParam.Value = EU433_JOIN_ACCEPT_DELAY1;
+            break;
+        }
+        case PHY_JOIN_ACCEPT_DELAY2:
+        {
+            phyParam.Value = EU433_JOIN_ACCEPT_DELAY2;
+            break;
+        }
+        case PHY_MAX_FCNT_GAP:
+        {
+            phyParam.Value = EU433_MAX_FCNT_GAP;
+            break;
+        }
+        case PHY_ACK_TIMEOUT:
+        {
+            phyParam.Value = ( EU433_ACKTIMEOUT + randr( -EU433_ACK_TIMEOUT_RND, EU433_ACK_TIMEOUT_RND ) );
+            break;
+        }
+        case PHY_DEF_DR1_OFFSET:
+        {
+            phyParam.Value = EU433_DEFAULT_RX1_DR_OFFSET;
+            break;
+        }
+        case PHY_DEF_RX2_FREQUENCY:
+        {
+            phyParam.Value = EU433_RX_WND_2_FREQ;
+            break;
+        }
+        case PHY_DEF_RX2_DR:
+        {
+            phyParam.Value = EU433_RX_WND_2_DR;
+            break;
+        }
+        case PHY_CHANNELS_MASK:
+        {
+            phyParam.ChannelsMask = ChannelsMask;
+            break;
+        }
+        case PHY_CHANNELS_DEFAULT_MASK:
+        {
+            phyParam.ChannelsMask = ChannelsDefaultMask;
+            break;
+        }
+        case PHY_MAX_NB_CHANNELS:
+        {
+            phyParam.Value = EU433_MAX_NB_CHANNELS;
+            break;
+        }
+        case PHY_CHANNELS:
+        {
+            phyParam.Channels = Channels;
+            break;
+        }
+        case PHY_DEF_UPLINK_DWELL_TIME:
+        case PHY_DEF_DOWNLINK_DWELL_TIME:
+        {
+            phyParam.Value = 0;
+            break;
+        }
+        case PHY_DEF_MAX_EIRP:
+        {
+            phyParam.fValue = EU433_DEFAULT_MAX_EIRP;
+            break;
+        }
+        case PHY_DEF_ANTENNA_GAIN:
+        {
+            phyParam.fValue = EU433_DEFAULT_ANTENNA_GAIN;
+            break;
+        }
+        case PHY_NB_JOIN_TRIALS:
+        case PHY_DEF_NB_JOIN_TRIALS:
+        {
+            phyParam.Value = 48;
+            break;
+        }
+        case PHY_BEACON_INTERVAL:
+        {
+            phyParam.Value = EU433_BEACON_INTERVAL;
+            break;
+        }
+        case PHY_BEACON_RESERVED:
+        {
+            phyParam.Value = EU433_BEACON_RESERVED;
+            break;
+        }
+        case PHY_BEACON_GUARD:
+        {
+            phyParam.Value = EU433_BEACON_GUARD;
+            break;
+        }
+        case PHY_BEACON_WINDOW:
+        {
+            phyParam.Value = EU433_BEACON_WINDOW;
+            break;
+        }
+        case PHY_BEACON_WINDOW_SLOTS:
+        {
+            phyParam.Value = EU433_BEACON_WINDOW_SLOTS;
+            break;
+        }
+        case PHY_PING_SLOT_WINDOW:
+        {
+            phyParam.Value = EU433_PING_SLOT_WINDOW;
+            break;
+        }
+        case PHY_BEACON_SYMBOL_TO_DEFAULT:
+        {
+            phyParam.Value = EU433_BEACON_SYMBOL_TO_DEFAULT;
+            break;
+        }
+        case PHY_BEACON_SYMBOL_TO_EXPANSION_MAX:
+        {
+            phyParam.Value = EU433_BEACON_SYMBOL_TO_EXPANSION_MAX;
+            break;
+        }
+        case PHY_PING_SLOT_SYMBOL_TO_EXPANSION_MAX:
+        {
+            phyParam.Value = EU433_PING_SLOT_SYMBOL_TO_EXPANSION_MAX;
+            break;
+        }
+        case PHY_BEACON_SYMBOL_TO_EXPANSION_FACTOR:
+        {
+            phyParam.Value = EU433_BEACON_SYMBOL_TO_EXPANSION_FACTOR;
+            break;
+        }
+        case PHY_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR:
+        {
+            phyParam.Value = EU433_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR;
+            break;
+        }
+        case PHY_MAX_BEACON_LESS_PERIOD:
+        {
+            phyParam.Value = EU433_MAX_BEACON_LESS_PERIOD;
+            break;
+        }
+        case PHY_BEACON_DELAY_BEACON_TIMING_ANS:
+        {
+            phyParam.Value = EU433_BEACON_DELAY_BEACON_TIMING_ANS;
+            break;
+        }
+        case PHY_BEACON_CHANNEL_FREQ:
+        {
+            phyParam.Value = EU433_BEACON_CHANNEL_FREQ;
+            break;
+        }
+        case PHY_PINGSLOT_CHANNEL_FREQ:
+        {
+            phyParam.Value = EU433_BEACON_CHANNEL_FREQ;
+            break;
+        }
+        case PHY_BEACON_SIZE:
+        {
+            phyParam.Value = EU433_BEACON_SIZE;
+            break;
+        }
+        case PHY_BEACON_CHANNEL_DR:
+        {
+            phyParam.Value = EU433_BEACON_CHANNEL_DR;
+            break;
+        }
+        default:
+        {
+            break;
+        }
+    }
+
+    return phyParam;
+}
+
+void RegionEU433SetBandTxDone( SetBandTxDoneParams_t* txDone )
+{
+    RegionCommonSetBandTxDone( txDone->Joined, &Bands[Channels[txDone->Channel].Band], txDone->LastTxDoneTime );
+}
+
+void RegionEU433InitDefaults( InitType_t type )
+{
+    switch( type )
+    {
+        case INIT_TYPE_INIT:
+        {
+            // Channels
+            Channels[0] = ( ChannelParams_t ) EU433_LC1;
+            Channels[1] = ( ChannelParams_t ) EU433_LC2;
+            Channels[2] = ( ChannelParams_t ) EU433_LC3;
+
+            // Initialize the channels default mask
+            ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 );
+            // Update the channels mask
+            RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 1 );
+            break;
+        }
+        case INIT_TYPE_RESTORE:
+        {
+            // Restore channels default mask
+            ChannelsMask[0] |= ChannelsDefaultMask[0];
+            break;
+        }
+        default:
+        {
+            break;
+        }
+    }
+}
+
+bool RegionEU433Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute )
+{
+    switch( phyAttribute )
+    {
+        case PHY_TX_DR:
+        {
+            return RegionCommonValueInRange( verify->DatarateParams.Datarate, EU433_TX_MIN_DATARATE, EU433_TX_MAX_DATARATE );
+        }
+        case PHY_DEF_TX_DR:
+        {
+            return RegionCommonValueInRange( verify->DatarateParams.Datarate, DR_0, DR_5 );
+        }
+        case PHY_RX_DR:
+        {
+            return RegionCommonValueInRange( verify->DatarateParams.Datarate, EU433_RX_MIN_DATARATE, EU433_RX_MAX_DATARATE );
+        }
+        case PHY_DEF_TX_POWER:
+        case PHY_TX_POWER:
+        {
+            // Remark: switched min and max!
+            return RegionCommonValueInRange( verify->TxPower, EU433_MAX_TX_POWER, EU433_MIN_TX_POWER );
+        }
+        case PHY_DUTY_CYCLE:
+        {
+            return EU433_DUTY_CYCLE_ENABLED;
+        }
+        case PHY_NB_JOIN_TRIALS:
+        {
+            if( verify->NbJoinTrials < 48 )
+            {
+                return false;
+            }
+            break;
+        }
+        default:
+            return false;
+    }
+    return true;
+}
+
+void RegionEU433ApplyCFList( ApplyCFListParams_t* applyCFList )
+{
+    ChannelParams_t newChannel;
+    ChannelAddParams_t channelAdd;
+    ChannelRemoveParams_t channelRemove;
+
+    // Setup default datarate range
+    newChannel.DrRange.Value = ( DR_5 << 4 ) | DR_0;
+
+    // Size of the optional CF list
+    if( applyCFList->Size != 16 )
+    {
+        return;
+    }
+
+    // Last byte is RFU, don't take it into account
+    for( uint8_t i = 0, chanIdx = EU433_NUMB_DEFAULT_CHANNELS; chanIdx < EU433_MAX_NB_CHANNELS; i+=3, chanIdx++ )
+    {
+        if( chanIdx < ( EU433_NUMB_CHANNELS_CF_LIST + EU433_NUMB_DEFAULT_CHANNELS ) )
+        {
+            // Channel frequency
+            newChannel.Frequency = (uint32_t) applyCFList->Payload[i];
+            newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 1] << 8 );
+            newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 2] << 16 );
+            newChannel.Frequency *= 100;
+
+            // Initialize alternative frequency to 0
+            newChannel.Rx1Frequency = 0;
+        }
+        else
+        {
+            newChannel.Frequency = 0;
+            newChannel.DrRange.Value = 0;
+            newChannel.Rx1Frequency = 0;
+        }
+
+        if( newChannel.Frequency != 0 )
+        {
+            channelAdd.NewChannel = &newChannel;
+            channelAdd.ChannelId = chanIdx;
+
+            // Try to add all channels
+            RegionEU433ChannelAdd( &channelAdd );
+        }
+        else
+        {
+            channelRemove.ChannelId = chanIdx;
+
+            RegionEU433ChannelsRemove( &channelRemove );
+        }
+    }
+}
+
+bool RegionEU433ChanMaskSet( ChanMaskSetParams_t* chanMaskSet )
+{
+    switch( chanMaskSet->ChannelsMaskType )
+    {
+        case CHANNELS_MASK:
+        {
+            RegionCommonChanMaskCopy( ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 );
+            break;
+        }
+        case CHANNELS_DEFAULT_MASK:
+        {
+            RegionCommonChanMaskCopy( ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 );
+            break;
+        }
+        default:
+            return false;
+    }
+    return true;
+}
+
+bool RegionEU433AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter )
+{
+    bool adrAckReq = false;
+    int8_t datarate = adrNext->Datarate;
+    int8_t txPower = adrNext->TxPower;
+    GetPhyParams_t getPhy;
+    PhyParam_t phyParam;
+
+    // Report back the adr ack counter
+    *adrAckCounter = adrNext->AdrAckCounter;
+
+    if( adrNext->AdrEnabled == true )
+    {
+        if( datarate == EU433_TX_MIN_DATARATE )
+        {
+            *adrAckCounter = 0;
+            adrAckReq = false;
+        }
+        else
+        {
+            if( adrNext->AdrAckCounter >= EU433_ADR_ACK_LIMIT )
+            {
+                adrAckReq = true;
+                txPower = EU433_MAX_TX_POWER;
+            }
+            else
+            {
+                adrAckReq = false;
+            }
+            if( adrNext->AdrAckCounter >= ( EU433_ADR_ACK_LIMIT + EU433_ADR_ACK_DELAY ) )
+            {
+                if( ( adrNext->AdrAckCounter % EU433_ADR_ACK_DELAY ) == 1 )
+                {
+                    // Decrease the datarate
+                    getPhy.Attribute = PHY_NEXT_LOWER_TX_DR;
+                    getPhy.Datarate = datarate;
+                    getPhy.UplinkDwellTime = adrNext->UplinkDwellTime;
+                    phyParam = RegionEU433GetPhyParam( &getPhy );
+                    datarate = phyParam.Value;
+
+                    if( datarate == EU433_TX_MIN_DATARATE )
+                    {
+                        // We must set adrAckReq to false as soon as we reach the lowest datarate
+                        adrAckReq = false;
+                        if( adrNext->UpdateChanMask == true )
+                        {
+                            // Re-enable default channels
+                            ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 );
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    *drOut = datarate;
+    *txPowOut = txPower;
+    return adrAckReq;
+}
+
+void RegionEU433ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams )
+{
+    double tSymbol = 0.0;
+
+    // Get the datarate, perform a boundary check
+    rxConfigParams->Datarate = MIN( datarate, EU433_RX_MAX_DATARATE );
+    rxConfigParams->Bandwidth = GetBandwidth( rxConfigParams->Datarate );
+
+    if( rxConfigParams->Datarate == DR_7 )
+    { // FSK
+        tSymbol = RegionCommonComputeSymbolTimeFsk( DataratesEU433[rxConfigParams->Datarate] );
+    }
+    else
+    { // LoRa
+        tSymbol = RegionCommonComputeSymbolTimeLoRa( DataratesEU433[rxConfigParams->Datarate], BandwidthsEU433[rxConfigParams->Datarate] );
+    }
+
+    RegionCommonComputeRxWindowParameters( tSymbol, minRxSymbols, rxError, RADIO_WAKEUP_TIME, &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset );
+}
+
+bool RegionEU433RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate )
+{
+    RadioModems_t modem;
+    int8_t dr = rxConfig->Datarate;
+    uint8_t maxPayload = 0;
+    int8_t phyDr = 0;
+    uint32_t frequency = rxConfig->Frequency;
+
+    if( Radio.GetStatus( ) != RF_IDLE )
+    {
+        return false;
+    }
+
+    if( rxConfig->Window == 0 )
+    {
+        // Apply window 1 frequency
+        frequency = Channels[rxConfig->Channel].Frequency;
+        // Apply the alternative RX 1 window frequency, if it is available
+        if( Channels[rxConfig->Channel].Rx1Frequency != 0 )
+        {
+            frequency = Channels[rxConfig->Channel].Rx1Frequency;
+        }
+    }
+
+    // Read the physical datarate from the datarates table
+    phyDr = DataratesEU433[dr];
+
+    Radio.SetChannel( frequency );
+
+    // Radio configuration
+    if( dr == DR_7 )
+    {
+        modem = MODEM_FSK;
+        Radio.SetRxConfig( modem, 50000, phyDr * 1000, 0, 83333, 5, rxConfig->WindowTimeout, false, 0, true, 0, 0, false, rxConfig->RxContinuous );
+    }
+    else
+    {
+        modem = MODEM_LORA;
+        Radio.SetRxConfig( modem, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous );
+    }
+
+    if( rxConfig->RepeaterSupport == true )
+    {
+        maxPayload = MaxPayloadOfDatarateRepeaterEU433[dr];
+    }
+    else
+    {
+        maxPayload = MaxPayloadOfDatarateEU433[dr];
+    }
+    Radio.SetMaxPayloadLength( modem, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD );
+
+    *datarate = (uint8_t) dr;
+    return true;
+}
+
+bool RegionEU433TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir )
+{
+    RadioModems_t modem;
+    int8_t phyDr = DataratesEU433[txConfig->Datarate];
+    int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, Bands[Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, ChannelsMask );
+    uint32_t bandwidth = GetBandwidth( txConfig->Datarate );
+    int8_t phyTxPower = 0;
+
+    // Calculate physical TX power
+    phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain );
+
+    // Setup the radio frequency
+    Radio.SetChannel( Channels[txConfig->Channel].Frequency );
+
+    if( txConfig->Datarate == DR_7 )
+    { // High Speed FSK channel
+        modem = MODEM_FSK;
+        Radio.SetTxConfig( modem, phyTxPower, 25000, bandwidth, phyDr * 1000, 0, 5, false, true, 0, 0, false, 3000 );
+    }
+    else
+    {
+        modem = MODEM_LORA;
+        Radio.SetTxConfig( modem, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 3000 );
+    }
+
+    // Setup maximum payload lenght of the radio driver
+    Radio.SetMaxPayloadLength( modem, txConfig->PktLen );
+    // Get the time-on-air of the next tx frame
+    *txTimeOnAir = Radio.TimeOnAir( modem, txConfig->PktLen );
+
+    *txPower = txPowerLimited;
+    return true;
+}
+
+uint8_t RegionEU433LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed )
+{
+    uint8_t status = 0x07;
+    RegionCommonLinkAdrParams_t linkAdrParams;
+    uint8_t nextIndex = 0;
+    uint8_t bytesProcessed = 0;
+    uint16_t chMask = 0;
+    GetPhyParams_t getPhy;
+    PhyParam_t phyParam;
+    RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams;
+
+    while( bytesProcessed < linkAdrReq->PayloadSize )
+    {
+        // Get ADR request parameters
+        nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams );
+
+        if( nextIndex == 0 )
+            break; // break loop, since no more request has been found
+
+        // Update bytes processed
+        bytesProcessed += nextIndex;
+
+        // Revert status, as we only check the last ADR request for the channel mask KO
+        status = 0x07;
+
+        // Setup temporary channels mask
+        chMask = linkAdrParams.ChMask;
+
+        // Verify channels mask
+        if( ( linkAdrParams.ChMaskCtrl == 0 ) && ( chMask == 0 ) )
+        {
+            status &= 0xFE; // Channel mask KO
+        }
+        else if( ( ( linkAdrParams.ChMaskCtrl >= 1 ) && ( linkAdrParams.ChMaskCtrl <= 5 )) ||
+                ( linkAdrParams.ChMaskCtrl >= 7 ) )
+        {
+            // RFU
+            status &= 0xFE; // Channel mask KO
+        }
+        else
+        {
+            for( uint8_t i = 0; i < EU433_MAX_NB_CHANNELS; i++ )
+            {
+                if( linkAdrParams.ChMaskCtrl == 6 )
+                {
+                    if( Channels[i].Frequency != 0 )
+                    {
+                        chMask |= 1 << i;
+                    }
+                }
+                else
+                {
+                    if( ( ( chMask & ( 1 << i ) ) != 0 ) &&
+                        ( Channels[i].Frequency == 0 ) )
+                    {// Trying to enable an undefined channel
+                        status &= 0xFE; // Channel mask KO
+                    }
+                }
+            }
+        }
+    }
+
+        // Get the minimum possible datarate
+    getPhy.Attribute = PHY_MIN_TX_DR;
+    getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime;
+    phyParam = RegionEU433GetPhyParam( &getPhy );
+
+    linkAdrVerifyParams.Status = status;
+    linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled;
+    linkAdrVerifyParams.Datarate = linkAdrParams.Datarate;
+    linkAdrVerifyParams.TxPower = linkAdrParams.TxPower;
+    linkAdrVerifyParams.NbRep = linkAdrParams.NbRep;
+    linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate;
+    linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower;
+    linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep;
+    linkAdrVerifyParams.NbChannels = EU433_MAX_NB_CHANNELS;
+    linkAdrVerifyParams.ChannelsMask = &chMask;
+    linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value;
+    linkAdrVerifyParams.MaxDatarate = EU433_TX_MAX_DATARATE;
+    linkAdrVerifyParams.Channels = Channels;
+    linkAdrVerifyParams.MinTxPower = EU433_MIN_TX_POWER;
+    linkAdrVerifyParams.MaxTxPower = EU433_MAX_TX_POWER;
+
+    // Verify the parameters and update, if necessary
+    status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep );
+
+    // Update channelsMask if everything is correct
+    if( status == 0x07 )
+    {
+        // Set the channels mask to a default value
+        memset( ChannelsMask, 0, sizeof( ChannelsMask ) );
+        // Update the channels mask
+        ChannelsMask[0] = chMask;
+    }
+
+    // Update status variables
+    *drOut = linkAdrParams.Datarate;
+    *txPowOut = linkAdrParams.TxPower;
+    *nbRepOut = linkAdrParams.NbRep;
+    *nbBytesParsed = bytesProcessed;
+
+    return status;
+}
+
+uint8_t RegionEU433RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq )
+{
+    uint8_t status = 0x07;
+
+    // Verify radio frequency
+    if( Radio.CheckRfFrequency( rxParamSetupReq->Frequency ) == false )
+    {
+        status &= 0xFE; // Channel frequency KO
+    }
+
+    // Verify datarate
+    if( RegionCommonValueInRange( rxParamSetupReq->Datarate, EU433_RX_MIN_DATARATE, EU433_RX_MAX_DATARATE ) == false )
+    {
+        status &= 0xFD; // Datarate KO
+    }
+
+    // Verify datarate offset
+    if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, EU433_MIN_RX1_DR_OFFSET, EU433_MAX_RX1_DR_OFFSET ) == false )
+    {
+        status &= 0xFB; // Rx1DrOffset range KO
+    }
+
+    return status;
+}
+
+uint8_t RegionEU433NewChannelReq( NewChannelReqParams_t* newChannelReq )
+{
+    uint8_t status = 0x03;
+    ChannelAddParams_t channelAdd;
+    ChannelRemoveParams_t channelRemove;
+
+    if( newChannelReq->NewChannel->Frequency == 0 )
+    {
+        channelRemove.ChannelId = newChannelReq->ChannelId;
+
+        // Remove
+        if( RegionEU433ChannelsRemove( &channelRemove ) == false )
+        {
+            status &= 0xFC;
+        }
+    }
+    else
+    {
+        channelAdd.NewChannel = newChannelReq->NewChannel;
+        channelAdd.ChannelId = newChannelReq->ChannelId;
+
+        switch( RegionEU433ChannelAdd( &channelAdd ) )
+        {
+            case LORAMAC_STATUS_OK:
+            {
+                break;
+            }
+            case LORAMAC_STATUS_FREQUENCY_INVALID:
+            {
+                status &= 0xFE;
+                break;
+            }
+            case LORAMAC_STATUS_DATARATE_INVALID:
+            {
+                status &= 0xFD;
+                break;
+            }
+            case LORAMAC_STATUS_FREQ_AND_DR_INVALID:
+            {
+                status &= 0xFC;
+                break;
+            }
+            default:
+            {
+                status &= 0xFC;
+                break;
+            }
+        }
+    }
+
+    return status;
+}
+
+int8_t RegionEU433TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq )
+{
+    return -1;
+}
+
+uint8_t RegionEU433DlChannelReq( DlChannelReqParams_t* dlChannelReq )
+{
+    uint8_t status = 0x03;
+
+    // Verify if the frequency is supported
+    if( VerifyTxFreq( dlChannelReq->Rx1Frequency ) == false )
+    {
+        status &= 0xFE;
+    }
+
+    // Verify if an uplink frequency exists
+    if( Channels[dlChannelReq->ChannelId].Frequency == 0 )
+    {
+        status &= 0xFD;
+    }
+
+    // Apply Rx1 frequency, if the status is OK
+    if( status == 0x03 )
+    {
+        Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency;
+    }
+
+    return status;
+}
+
+int8_t RegionEU433AlternateDr( AlternateDrParams_t* alternateDr )
+{
+    int8_t datarate = 0;
+
+    if( ( alternateDr->NbTrials % 48 ) == 0 )
+    {
+        datarate = DR_0;
+    }
+    else if( ( alternateDr->NbTrials % 32 ) == 0 )
+    {
+        datarate = DR_1;
+    }
+    else if( ( alternateDr->NbTrials % 24 ) == 0 )
+    {
+        datarate = DR_2;
+    }
+    else if( ( alternateDr->NbTrials % 16 ) == 0 )
+    {
+        datarate = DR_3;
+    }
+    else if( ( alternateDr->NbTrials % 8 ) == 0 )
+    {
+        datarate = DR_4;
+    }
+    else
+    {
+        datarate = DR_5;
+    }
+    return datarate;
+}
+
+void RegionEU433CalcBackOff( CalcBackOffParams_t* calcBackOff )
+{
+    RegionCommonCalcBackOffParams_t calcBackOffParams;
+
+    calcBackOffParams.Channels = Channels;
+    calcBackOffParams.Bands = Bands;
+    calcBackOffParams.LastTxIsJoinRequest = calcBackOff->LastTxIsJoinRequest;
+    calcBackOffParams.Joined = calcBackOff->Joined;
+    calcBackOffParams.DutyCycleEnabled = calcBackOff->DutyCycleEnabled;
+    calcBackOffParams.Channel = calcBackOff->Channel;
+    calcBackOffParams.ElapsedTime = calcBackOff->ElapsedTime;
+    calcBackOffParams.TxTimeOnAir = calcBackOff->TxTimeOnAir;
+
+    RegionCommonCalcBackOff( &calcBackOffParams );
+}
+
+bool RegionEU433NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff )
+{
+    uint8_t nbEnabledChannels = 0;
+    uint8_t delayTx = 0;
+    uint8_t enabledChannels[EU433_MAX_NB_CHANNELS] = { 0 };
+    TimerTime_t nextTxDelay = 0;
+
+    if( RegionCommonCountChannels( ChannelsMask, 0, 1 ) == 0 )
+    { // Reactivate default channels
+        ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 );
+    }
+
+    if( nextChanParams->AggrTimeOff <= TimerGetElapsedTime( nextChanParams->LastAggrTx ) )
+    {
+        // Reset Aggregated time off
+        *aggregatedTimeOff = 0;
+
+        // Update bands Time OFF
+        nextTxDelay = RegionCommonUpdateBandTimeOff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, Bands, EU433_MAX_NB_BANDS );
+
+        // Search how many channels are enabled
+        nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Joined, nextChanParams->Datarate,
+                                                      ChannelsMask, Channels,
+                                                      Bands, enabledChannels, &delayTx );
+    }
+    else
+    {
+        delayTx++;
+        nextTxDelay = nextChanParams->AggrTimeOff - TimerGetElapsedTime( nextChanParams->LastAggrTx );
+    }
+
+    if( nbEnabledChannels > 0 )
+    {
+        // We found a valid channel
+        *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )];
+
+        *time = 0;
+        return true;
+    }
+    else
+    {
+        if( delayTx > 0 )
+        {
+            // Delay transmission due to AggregatedTimeOff or to a band time off
+            *time = nextTxDelay;
+            return true;
+        }
+        // Datarate not supported by any channel, restore defaults
+        ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 );
+        *time = 0;
+        return false;
+    }
+}
+
+LoRaMacStatus_t RegionEU433ChannelAdd( ChannelAddParams_t* channelAdd )
+{
+    uint8_t band = 0;
+    bool drInvalid = false;
+    bool freqInvalid = false;
+    uint8_t id = channelAdd->ChannelId;
+
+    if( id >= EU433_MAX_NB_CHANNELS )
+    {
+        return LORAMAC_STATUS_PARAMETER_INVALID;
+    }
+
+    // Validate the datarate range
+    if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Min, EU433_TX_MIN_DATARATE, EU433_TX_MAX_DATARATE ) == false )
+    {
+        drInvalid = true;
+    }
+    if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, EU433_TX_MIN_DATARATE, EU433_TX_MAX_DATARATE ) == false )
+    {
+        drInvalid = true;
+    }
+    if( channelAdd->NewChannel->DrRange.Fields.Min > channelAdd->NewChannel->DrRange.Fields.Max )
+    {
+        drInvalid = true;
+    }
+
+    // Default channels don't accept all values
+    if( id < EU433_NUMB_DEFAULT_CHANNELS )
+    {
+        // Validate the datarate range for min: must be DR_0
+        if( channelAdd->NewChannel->DrRange.Fields.Min > DR_0 )
+        {
+            drInvalid = true;
+        }
+        // Validate the datarate range for max: must be DR_5 <= Max <= TX_MAX_DATARATE
+        if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, DR_5, EU433_TX_MAX_DATARATE ) == false )
+        {
+            drInvalid = true;
+        }
+        // We are not allowed to change the frequency
+        if( channelAdd->NewChannel->Frequency != Channels[id].Frequency )
+        {
+            freqInvalid = true;
+        }
+    }
+
+    // Check frequency
+    if( freqInvalid == false )
+    {
+        if( VerifyTxFreq( channelAdd->NewChannel->Frequency ) == false )
+        {
+            freqInvalid = true;
+        }
+    }
+
+    // Check status
+    if( ( drInvalid == true ) && ( freqInvalid == true ) )
+    {
+        return LORAMAC_STATUS_FREQ_AND_DR_INVALID;
+    }
+    if( drInvalid == true )
+    {
+        return LORAMAC_STATUS_DATARATE_INVALID;
+    }
+    if( freqInvalid == true )
+    {
+        return LORAMAC_STATUS_FREQUENCY_INVALID;
+    }
+
+    memcpy( &(Channels[id]), channelAdd->NewChannel, sizeof( Channels[id] ) );
+    Channels[id].Band = band;
+    ChannelsMask[0] |= ( 1 << id );
+    return LORAMAC_STATUS_OK;
+}
+
+bool RegionEU433ChannelsRemove( ChannelRemoveParams_t* channelRemove  )
+{
+    uint8_t id = channelRemove->ChannelId;
+
+    if( id < EU433_NUMB_DEFAULT_CHANNELS )
+    {
+        return false;
+    }
+
+    // Remove the channel from the list of channels
+    Channels[id] = ( ChannelParams_t ){ 0, 0, { 0 }, 0 };
+
+    return RegionCommonChanDisable( ChannelsMask, id, EU433_MAX_NB_CHANNELS );
+}
+
+void RegionEU433SetContinuousWave( ContinuousWaveParams_t* continuousWave )
+{
+    int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, Bands[Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, ChannelsMask );
+    int8_t phyTxPower = 0;
+    uint32_t frequency = Channels[continuousWave->Channel].Frequency;
+
+    // Calculate physical TX power
+    phyTxPower = RegionCommonComputeTxPower( txPowerLimited, continuousWave->MaxEirp, continuousWave->AntennaGain );
+
+    Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout );
+}
+
+uint8_t RegionEU433ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset )
+{
+    int8_t datarate = dr - drOffset;
+
+    if( datarate < 0 )
+    {
+        datarate = DR_0;
+    }
+    return datarate;
+}
+
+void RegionEU433RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr, bool *beaconChannelSet )
+{
+    RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup;
+
+    regionCommonRxBeaconSetup.Datarates = DataratesEU433;
+    regionCommonRxBeaconSetup.ChannelPlanFrequency = EU433_BEACON_CHANNEL_FREQ;
+    regionCommonRxBeaconSetup.BeaconTimingAnsFrequency = EU433_BEACON_CHANNEL_FREQ;
+    regionCommonRxBeaconSetup.BeaconSize = EU433_BEACON_SIZE;
+    regionCommonRxBeaconSetup.BeaconDatarate = EU433_BEACON_CHANNEL_DR;
+    regionCommonRxBeaconSetup.BeaconChannelBW = EU433_BEACON_CHANNEL_BW;
+    regionCommonRxBeaconSetup.CustomFrequency = rxBeaconSetup->CustomFrequency;
+    regionCommonRxBeaconSetup.CustomFrequencyEnabled = rxBeaconSetup->CustomFrequencyEnabled;
+    regionCommonRxBeaconSetup.BeaconChannelSet = rxBeaconSetup->BeaconChannelSet;
+    regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime;
+    regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout;
+
+    RegionCommonRxBeaconSetup( &regionCommonRxBeaconSetup, beaconChannelSet );
+
+    // Store downlink datarate
+    *outDr = EU433_BEACON_CHANNEL_DR;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/region/RegionEU433.h	Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,556 @@
+/*!
+ * \file      RegionEU433.h
+ *
+ * \brief     Region definition for EU433
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ *                ______                              _
+ *               / _____)             _              | |
+ *              ( (____  _____ ____ _| |_ _____  ____| |__
+ *               \____ \| ___ |    (_   _) ___ |/ ___)  _ \
+ *               _____) ) ____| | | || |_| ____( (___| | | |
+ *              (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ *              (C)2013 Semtech
+ *
+ *               ___ _____ _   ___ _  _____ ___  ___  ___ ___
+ *              / __|_   _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ *              \__ \ | |/ _ \ (__| ' <| _| (_) |   / (__| _|
+ *              |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ *              embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author    Miguel Luis ( Semtech )
+ *
+ * \author    Gregory Cristian ( Semtech )
+ *
+ * \author    Daniel Jaeckle ( STACKFORCE )
+ *
+ * \defgroup  REGIONEU433 Region EU433
+ *            Implementation according to LoRaWAN Specification v1.0.2.
+ * \{
+ */
+#ifndef __REGION_EU433_H__
+#define __REGION_EU433_H__
+
+/*!
+ * LoRaMac maximum number of channels
+ */
+#define EU433_MAX_NB_CHANNELS                       16
+
+/*!
+ * Number of default channels
+ */
+#define EU433_NUMB_DEFAULT_CHANNELS                 3
+
+/*!
+ * Number of channels to apply for the CF list
+ */
+#define EU433_NUMB_CHANNELS_CF_LIST                 5
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define EU433_TX_MIN_DATARATE                       DR_0
+
+/*!
+ * Maximal datarate that can be used by the node
+ */
+#define EU433_TX_MAX_DATARATE                       DR_7
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define EU433_RX_MIN_DATARATE                       DR_0
+
+/*!
+ * Maximal datarate that can be used by the node
+ */
+#define EU433_RX_MAX_DATARATE                       DR_7
+
+/*!
+ * Default datarate used by the node
+ */
+#define EU433_DEFAULT_DATARATE                      DR_0
+
+/*!
+ * Minimal Rx1 receive datarate offset
+ */
+#define EU433_MIN_RX1_DR_OFFSET                     0
+
+/*!
+ * Maximal Rx1 receive datarate offset
+ */
+#define EU433_MAX_RX1_DR_OFFSET                     5
+
+/*!
+ * Default Rx1 receive datarate offset
+ */
+#define EU433_DEFAULT_RX1_DR_OFFSET                 0
+
+/*!
+ * Minimal Tx output power that can be used by the node
+ */
+#define EU433_MIN_TX_POWER                          TX_POWER_5
+
+/*!
+ * Maximal Tx output power that can be used by the node
+ */
+#define EU433_MAX_TX_POWER                          TX_POWER_0
+
+/*!
+ * Default Tx output power used by the node
+ */
+#define EU433_DEFAULT_TX_POWER                      TX_POWER_0
+
+/*!
+ * Default Max EIRP
+ */
+#define EU433_DEFAULT_MAX_EIRP                      12.15f
+
+/*!
+ * Default antenna gain
+ */
+#define EU433_DEFAULT_ANTENNA_GAIN                  2.15f
+
+/*!
+ * ADR Ack limit
+ */
+#define EU433_ADR_ACK_LIMIT                         64
+
+/*!
+ * ADR Ack delay
+ */
+#define EU433_ADR_ACK_DELAY                         32
+
+/*!
+ * Enabled or disabled the duty cycle
+ */
+#define EU433_DUTY_CYCLE_ENABLED                    1
+
+/*!
+ * Maximum RX window duration
+ */
+#define EU433_MAX_RX_WINDOW                         3000
+
+/*!
+ * Receive delay 1
+ */
+#define EU433_RECEIVE_DELAY1                        1000
+
+/*!
+ * Receive delay 2
+ */
+#define EU433_RECEIVE_DELAY2                        2000
+
+/*!
+ * Join accept delay 1
+ */
+#define EU433_JOIN_ACCEPT_DELAY1                    5000
+
+/*!
+ * Join accept delay 2
+ */
+#define EU433_JOIN_ACCEPT_DELAY2                    6000
+
+/*!
+ * Maximum frame counter gap
+ */
+#define EU433_MAX_FCNT_GAP                          16384
+
+/*!
+ * Ack timeout
+ */
+#define EU433_ACKTIMEOUT                            2000
+
+/*!
+ * Random ack timeout limits
+ */
+#define EU433_ACK_TIMEOUT_RND                       1000
+
+/*!
+ * Verification of default datarate
+ */
+#if ( EU433_DEFAULT_DATARATE > DR_5 )
+#error "A default DR higher than DR_5 may lead to connectivity loss."
+#endif
+
+/*!
+ * Second reception window channel frequency definition.
+ */
+#define EU433_RX_WND_2_FREQ                         434665000
+
+/*!
+ * Second reception window channel datarate definition.
+ */
+#define EU433_RX_WND_2_DR                           DR_0
+
+/*!
+ * LoRaMac maximum number of bands
+ */
+#define EU433_MAX_NB_BANDS                          1
+
+/*
+ * CLASS B
+ */
+/*!
+ * Beacon interval in ms
+ */
+#define EU433_BEACON_INTERVAL                       128000
+
+/*!
+ * Beacon reserved time in ms
+ */
+#define EU433_BEACON_RESERVED                       2120
+
+/*!
+ * Beacon guard time in ms
+ */
+#define EU433_BEACON_GUARD                          3000
+
+/*!
+ * Beacon window time in ms
+ */
+#define EU433_BEACON_WINDOW                         122880
+
+/*!
+ * Beacon window time in numer of slots
+ */
+#define EU433_BEACON_WINDOW_SLOTS                   4096
+
+/*!
+ * Ping slot length time in ms
+ */
+#define EU433_PING_SLOT_WINDOW                      30
+
+/*!
+ * Default symbol timeout for beacons and ping slot windows
+ */
+#define EU433_BEACON_SYMBOL_TO_DEFAULT              8
+
+/*!
+ * Maximum symbol timeout for beacons
+ */
+#define EU433_BEACON_SYMBOL_TO_EXPANSION_MAX        400
+
+/*!
+ * Maximum symbol timeout for ping slots
+ */
+#define EU433_PING_SLOT_SYMBOL_TO_EXPANSION_MAX     40
+
+/*!
+ * Symbol expansion value for beacon windows in case of beacon
+ * loss in symbols
+ */
+#define EU433_BEACON_SYMBOL_TO_EXPANSION_FACTOR     2
+
+/*!
+ * Symbol expansion value for ping slot windows in case of beacon
+ * loss in symbols
+ */
+#define EU433_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR  2
+
+/*!
+ * Maximum allowed beacon less time in ms
+ */
+#define EU433_MAX_BEACON_LESS_PERIOD                7200000
+
+/*!
+ * Delay time for the BeaconTimingAns in ms
+ */
+#define EU433_BEACON_DELAY_BEACON_TIMING_ANS        30
+
+/*!
+ * Beacon frequency
+ */
+#define EU433_BEACON_CHANNEL_FREQ                   434665000
+
+/*!
+ * Payload size of a beacon frame
+ */
+#define EU433_BEACON_SIZE                           17
+
+/*!
+ * Datarate of the beacon channel
+ */
+#define EU433_BEACON_CHANNEL_DR                     DR_3
+
+/*!
+ * Bandwith of the beacon channel
+ */
+#define EU433_BEACON_CHANNEL_BW                     0
+
+/*!
+ * Band 0 definition
+ * { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff }
+ */
+#define EU433_BAND0                                 { 100, EU433_MAX_TX_POWER, 0,  0 } //  1.0 %
+
+/*!
+ * LoRaMac default channel 1
+ * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
+ */
+#define EU433_LC1                                   { 433175000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
+
+/*!
+ * LoRaMac default channel 2
+ * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
+ */
+#define EU433_LC2                                   { 433375000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
+
+/*!
+ * LoRaMac default channel 3
+ * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
+ */
+#define EU433_LC3                                   { 433575000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
+
+/*!
+ * LoRaMac channels which are allowed for the join procedure
+ */
+#define EU433_JOIN_CHANNELS                         ( uint16_t )( LC( 1 ) | LC( 2 ) | LC( 3 ) )
+
+/*!
+ * Data rates table definition
+ */
+static const uint8_t DataratesEU433[] = { 12, 11, 10,  9,  8,  7,  7, 50 };
+
+/*!
+ * Bandwidths table definition in Hz
+ */
+static const uint32_t BandwidthsEU433[] = { 125000, 125000, 125000, 125000, 125000, 125000, 250000, 0 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Cannot operate with repeater.
+ */
+static const uint8_t MaxPayloadOfDatarateEU433[] = { 51, 51, 51, 115, 242, 242, 242, 242 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Can operate with repeater.
+ */
+static const uint8_t MaxPayloadOfDatarateRepeaterEU433[] = { 51, 51, 51, 115, 222, 222, 222, 222 };
+
+/*!
+ * \brief The function gets a value of a specific phy attribute.
+ *
+ * \param [IN] getPhy Pointer to the function parameters.
+ *
+ * \retval Returns a structure containing the PHY parameter.
+ */
+PhyParam_t RegionEU433GetPhyParam( GetPhyParams_t* getPhy );
+
+/*!
+ * \brief Updates the last TX done parameters of the current channel.
+ *
+ * \param [IN] txDone Pointer to the function parameters.
+ */
+void RegionEU433SetBandTxDone( SetBandTxDoneParams_t* txDone );
+
+/*!
+ * \brief Initializes the channels masks and the channels.
+ *
+ * \param [IN] type Sets the initialization type.
+ */
+void RegionEU433InitDefaults( InitType_t type );
+
+/*!
+ * \brief Verifies a parameter.
+ *
+ * \param [IN] verify Pointer to the function parameters.
+ *
+ * \param [IN] type Sets the initialization type.
+ *
+ * \retval Returns true, if the parameter is valid.
+ */
+bool RegionEU433Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute );
+
+/*!
+ * \brief The function parses the input buffer and sets up the channels of the
+ *        CF list.
+ *
+ * \param [IN] applyCFList Pointer to the function parameters.
+ */
+void RegionEU433ApplyCFList( ApplyCFListParams_t* applyCFList );
+
+/*!
+ * \brief Sets a channels mask.
+ *
+ * \param [IN] chanMaskSet Pointer to the function parameters.
+ *
+ * \retval Returns true, if the channels mask could be set.
+ */
+bool RegionEU433ChanMaskSet( ChanMaskSetParams_t* chanMaskSet );
+
+/*!
+ * \brief Calculates the next datarate to set, when ADR is on or off.
+ *
+ * \param [IN] adrNext Pointer to the function parameters.
+ *
+ * \param [OUT] drOut The calculated datarate for the next TX.
+ *
+ * \param [OUT] txPowOut The TX power for the next TX.
+ *
+ * \param [OUT] adrAckCounter The calculated ADR acknowledgement counter.
+ *
+ * \retval Returns true, if an ADR request should be performed.
+ */
+bool RegionEU433AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter );
+
+/*!
+ * Computes the Rx window timeout and offset.
+ *
+ * \param [IN] datarate     Rx window datarate index to be used
+ *
+ * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame.
+ *
+ * \param [IN] rxError      System maximum timing error of the receiver. In milliseconds
+ *                          The receiver will turn on in a [-rxError : +rxError] ms
+ *                          interval around RxOffset
+ *
+ * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields.
+ */
+void RegionEU433ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams );
+
+/*!
+ * \brief Configuration of the RX windows.
+ *
+ * \param [IN] rxConfig Pointer to the function parameters.
+ *
+ * \param [OUT] datarate The datarate index which was set.
+ *
+ * \retval Returns true, if the configuration was applied successfully.
+ */
+bool RegionEU433RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate );
+
+/*!
+ * \brief TX configuration.
+ *
+ * \param [IN] txConfig Pointer to the function parameters.
+ *
+ * \param [OUT] txPower The tx power index which was set.
+ *
+ * \param [OUT] txTimeOnAir The time-on-air of the frame.
+ *
+ * \retval Returns true, if the configuration was applied successfully.
+ */
+bool RegionEU433TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir );
+
+/*!
+ * \brief The function processes a Link ADR Request.
+ *
+ * \param [IN] linkAdrReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionEU433LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed );
+
+/*!
+ * \brief The function processes a RX Parameter Setup Request.
+ *
+ * \param [IN] rxParamSetupReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionEU433RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq );
+
+/*!
+ * \brief The function processes a Channel Request.
+ *
+ * \param [IN] newChannelReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionEU433NewChannelReq( NewChannelReqParams_t* newChannelReq );
+
+/*!
+ * \brief The function processes a TX ParamSetup Request.
+ *
+ * \param [IN] txParamSetupReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ *         Returns -1, if the functionality is not implemented. In this case, the end node
+ *         shall not process the command.
+ */
+int8_t RegionEU433TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq );
+
+/*!
+ * \brief The function processes a DlChannel Request.
+ *
+ * \param [IN] dlChannelReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionEU433DlChannelReq( DlChannelReqParams_t* dlChannelReq );
+
+/*!
+ * \brief Alternates the datarate of the channel for the join request.
+ *
+ * \param [IN] alternateDr Pointer to the function parameters.
+ *
+ * \retval Datarate to apply.
+ */
+int8_t RegionEU433AlternateDr( AlternateDrParams_t* alternateDr );
+
+/*!
+ * \brief Calculates the back-off time.
+ *
+ * \param [IN] calcBackOff Pointer to the function parameters.
+ */
+void RegionEU433CalcBackOff( CalcBackOffParams_t* calcBackOff );
+
+/*!
+ * \brief Searches and set the next random available channel
+ *
+ * \param [OUT] channel Next channel to use for TX.
+ *
+ * \param [OUT] time Time to wait for the next transmission according to the duty
+ *              cycle.
+ *
+ * \param [OUT] aggregatedTimeOff Updates the aggregated time off.
+ *
+ * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate]
+ */
+bool RegionEU433NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff );
+
+/*!
+ * \brief Adds a channel.
+ *
+ * \param [IN] channelAdd Pointer to the function parameters.
+ *
+ * \retval Status of the operation.
+ */
+LoRaMacStatus_t RegionEU433ChannelAdd( ChannelAddParams_t* channelAdd );
+
+/*!
+ * \brief Removes a channel.
+ *
+ * \param [IN] channelRemove Pointer to the function parameters.
+ *
+ * \retval Returns true, if the channel was removed successfully.
+ */
+bool RegionEU433ChannelsRemove( ChannelRemoveParams_t* channelRemove  );
+
+/*!
+ * \brief Sets the radio into continuous wave mode.
+ *
+ * \param [IN] continuousWave Pointer to the function parameters.
+ */
+void RegionEU433SetContinuousWave( ContinuousWaveParams_t* continuousWave );
+
+/*!
+ * \brief Computes new datarate according to the given offset
+ *
+ * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms
+ *
+ * \param [IN] dr Current datarate
+ *
+ * \param [IN] drOffset Offset to be applied
+ *
+ * \retval newDr Computed datarate.
+ */
+uint8_t RegionEU433ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset );
+
+/*! \} defgroup REGIONEU433 */
+
+#endif // __REGION_EU433_H__
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/region/RegionEU868.cpp	Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,1183 @@
+/*
+ / _____)             _              | |
+( (____  _____ ____ _| |_ _____  ____| |__
+ \____ \| ___ |    (_   _) ___ |/ ___)  _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+    (C)2013 Semtech
+ ___ _____ _   ___ _  _____ ___  ___  ___ ___
+/ __|_   _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+\__ \ | |/ _ \ (__| ' <| _| (_) |   / (__| _|
+|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+embedded.connectivity.solutions===============
+
+Description: LoRa MAC region EU868 implementation
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+
+Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE )
+*/
+#include <stdbool.h>
+#include <string.h>
+#include <stdint.h>
+#include <math.h>
+
+#include "board.h"
+#include "LoRaMac.h"
+
+#include "utilities.h"
+
+#include "Region.h"
+#include "RegionCommon.h"
+#include "RegionEU868.h"
+
+// Definitions
+#define CHANNELS_MASK_SIZE              1
+
+// Global attributes
+/*!
+ * LoRaMAC channels
+ */
+static ChannelParams_t Channels[EU868_MAX_NB_CHANNELS];
+
+/*!
+ * LoRaMac bands
+ */
+static Band_t Bands[EU868_MAX_NB_BANDS] =
+{
+    EU868_BAND0,
+    EU868_BAND1,
+    EU868_BAND2,
+    EU868_BAND3,
+    EU868_BAND4,
+};
+
+/*!
+ * LoRaMac channels mask
+ */
+static uint16_t ChannelsMask[CHANNELS_MASK_SIZE];
+
+/*!
+ * LoRaMac channels default mask
+ */
+static uint16_t ChannelsDefaultMask[CHANNELS_MASK_SIZE];
+
+// Static functions
+static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr )
+{
+    uint8_t nextLowerDr = 0;
+
+    if( dr == minDr )
+    {
+        nextLowerDr = minDr;
+    }
+    else
+    {
+        nextLowerDr = dr - 1;
+    }
+    return nextLowerDr;
+}
+
+static uint32_t GetBandwidth( uint32_t drIndex )
+{
+    switch( BandwidthsEU868[drIndex] )
+    {
+        default:
+        case 125000:
+            return 0;
+        case 250000:
+            return 1;
+        case 500000:
+            return 2;
+    }
+}
+
+static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask )
+{
+    int8_t txPowerResult = txPower;
+
+    // Limit tx power to the band max
+    txPowerResult =  MAX( txPower, maxBandTxPower );
+
+    return txPowerResult;
+}
+
+static bool VerifyTxFreq( uint32_t freq, uint8_t *band )
+{
+    // Check radio driver support
+    if( Radio.CheckRfFrequency( freq ) == false )
+    {
+        return false;
+    }
+
+    // Check frequency bands
+    if( ( freq >= 863000000 ) && ( freq < 865000000 ) )
+    {
+        *band = 2;
+    }
+    else if( ( freq >= 865000000 ) && ( freq <= 868000000 ) )
+    {
+        *band = 0;
+    }
+    else if( ( freq > 868000000 ) && ( freq <= 868600000 ) )
+    {
+        *band = 1;
+    }
+    else if( ( freq >= 868700000 ) && ( freq <= 869200000 ) )
+    {
+        *band = 2;
+    }
+    else if( ( freq >= 869400000 ) && ( freq <= 869650000 ) )
+    {
+        *band = 3;
+    }
+    else if( ( freq >= 869700000 ) && ( freq <= 870000000 ) )
+    {
+        *band = 4;
+    }
+    else
+    {
+        return false;
+    }
+    return true;
+}
+
+static uint8_t CountNbOfEnabledChannels( bool joined, uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx )
+{
+    uint8_t nbEnabledChannels = 0;
+    uint8_t delayTransmission = 0;
+
+    for( uint8_t i = 0, k = 0; i < EU868_MAX_NB_CHANNELS; i += 16, k++ )
+    {
+        for( uint8_t j = 0; j < 16; j++ )
+        {
+            if( ( channelsMask[k] & ( 1 << j ) ) != 0 )
+            {
+                if( channels[i + j].Frequency == 0 )
+                { // Check if the channel is enabled
+                    continue;
+                }
+                if( joined == false )
+                {
+                    if( ( EU868_JOIN_CHANNELS & ( 1 << j ) ) == 0 )
+                    {
+                        continue;
+                    }
+                }
+                if( RegionCommonValueInRange( datarate, channels[i + j].DrRange.Fields.Min,
+                                              channels[i + j].DrRange.Fields.Max ) == false )
+                { // Check if the current channel selection supports the given datarate
+                    continue;
+                }
+                if( bands[channels[i + j].Band].TimeOff > 0 )
+                { // Check if the band is available for transmission
+                    delayTransmission++;
+                    continue;
+                }
+                enabledChannels[nbEnabledChannels++] = i + j;
+            }
+        }
+    }
+
+    *delayTx = delayTransmission;
+    return nbEnabledChannels;
+}
+
+PhyParam_t RegionEU868GetPhyParam( GetPhyParams_t* getPhy )
+{
+    PhyParam_t phyParam = { 0 };
+
+    switch( getPhy->Attribute )
+    {
+        case PHY_MIN_RX_DR:
+        {
+            phyParam.Value = EU868_RX_MIN_DATARATE;
+            break;
+        }
+        case PHY_MIN_TX_DR:
+        {
+            phyParam.Value = EU868_TX_MIN_DATARATE;
+            break;
+        }
+        case PHY_DEF_TX_DR:
+        {
+            phyParam.Value = EU868_DEFAULT_DATARATE;
+            break;
+        }
+        case PHY_NEXT_LOWER_TX_DR:
+        {
+            phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, EU868_TX_MIN_DATARATE );
+            break;
+        }
+        case PHY_DEF_TX_POWER:
+        {
+            phyParam.Value = EU868_DEFAULT_TX_POWER;
+            break;
+        }
+        case PHY_MAX_PAYLOAD:
+        {
+            phyParam.Value = MaxPayloadOfDatarateEU868[getPhy->Datarate];
+            break;
+        }
+        case PHY_MAX_PAYLOAD_REPEATER:
+        {
+            phyParam.Value = MaxPayloadOfDatarateRepeaterEU868[getPhy->Datarate];
+            break;
+        }
+        case PHY_DUTY_CYCLE:
+        {
+            phyParam.Value = EU868_DUTY_CYCLE_ENABLED;
+            break;
+        }
+        case PHY_MAX_RX_WINDOW:
+        {
+            phyParam.Value = EU868_MAX_RX_WINDOW;
+            break;
+        }
+        case PHY_RECEIVE_DELAY1:
+        {
+            phyParam.Value = EU868_RECEIVE_DELAY1;
+            break;
+        }
+        case PHY_RECEIVE_DELAY2:
+        {
+            phyParam.Value = EU868_RECEIVE_DELAY2;
+            break;
+        }
+        case PHY_JOIN_ACCEPT_DELAY1:
+        {
+            phyParam.Value = EU868_JOIN_ACCEPT_DELAY1;
+            break;
+        }
+        case PHY_JOIN_ACCEPT_DELAY2:
+        {
+            phyParam.Value = EU868_JOIN_ACCEPT_DELAY2;
+            break;
+        }
+        case PHY_MAX_FCNT_GAP:
+        {
+            phyParam.Value = EU868_MAX_FCNT_GAP;
+            break;
+        }
+        case PHY_ACK_TIMEOUT:
+        {
+            phyParam.Value = ( EU868_ACKTIMEOUT + randr( -EU868_ACK_TIMEOUT_RND, EU868_ACK_TIMEOUT_RND ) );
+            break;
+        }
+        case PHY_DEF_DR1_OFFSET:
+        {
+            phyParam.Value = EU868_DEFAULT_RX1_DR_OFFSET;
+            break;
+        }
+        case PHY_DEF_RX2_FREQUENCY:
+        {
+            phyParam.Value = EU868_RX_WND_2_FREQ;
+            break;
+        }
+        case PHY_DEF_RX2_DR:
+        {
+            phyParam.Value = EU868_RX_WND_2_DR;
+            break;
+        }
+        case PHY_CHANNELS_MASK:
+        {
+            phyParam.ChannelsMask = ChannelsMask;
+            break;
+        }
+        case PHY_CHANNELS_DEFAULT_MASK:
+        {
+            phyParam.ChannelsMask = ChannelsDefaultMask;
+            break;
+        }
+        case PHY_MAX_NB_CHANNELS:
+        {
+            phyParam.Value = EU868_MAX_NB_CHANNELS;
+            break;
+        }
+        case PHY_CHANNELS:
+        {
+            phyParam.Channels = Channels;
+            break;
+        }
+        case PHY_DEF_UPLINK_DWELL_TIME:
+        case PHY_DEF_DOWNLINK_DWELL_TIME:
+        {
+            phyParam.Value = 0;
+            break;
+        }
+        case PHY_DEF_MAX_EIRP:
+        {
+            phyParam.fValue = EU868_DEFAULT_MAX_EIRP;
+            break;
+        }
+        case PHY_DEF_ANTENNA_GAIN:
+        {
+            phyParam.fValue = EU868_DEFAULT_ANTENNA_GAIN;
+            break;
+        }
+        case PHY_NB_JOIN_TRIALS:
+        case PHY_DEF_NB_JOIN_TRIALS:
+        {
+            phyParam.Value = 48;
+            break;
+        }
+        case PHY_BEACON_INTERVAL:
+        {
+            phyParam.Value = EU868_BEACON_INTERVAL;
+            break;
+        }
+        case PHY_BEACON_RESERVED:
+        {
+            phyParam.Value = EU868_BEACON_RESERVED;
+            break;
+        }
+        case PHY_BEACON_GUARD:
+        {
+            phyParam.Value = EU868_BEACON_GUARD;
+            break;
+        }
+        case PHY_BEACON_WINDOW:
+        {
+            phyParam.Value = EU868_BEACON_WINDOW;
+            break;
+        }
+        case PHY_BEACON_WINDOW_SLOTS:
+        {
+            phyParam.Value = EU868_BEACON_WINDOW_SLOTS;
+            break;
+        }
+        case PHY_PING_SLOT_WINDOW:
+        {
+            phyParam.Value = EU868_PING_SLOT_WINDOW;
+            break;
+        }
+        case PHY_BEACON_SYMBOL_TO_DEFAULT:
+        {
+            phyParam.Value = EU868_BEACON_SYMBOL_TO_DEFAULT;
+            break;
+        }
+        case PHY_BEACON_SYMBOL_TO_EXPANSION_MAX:
+        {
+            phyParam.Value = EU868_BEACON_SYMBOL_TO_EXPANSION_MAX;
+            break;
+        }
+        case PHY_PING_SLOT_SYMBOL_TO_EXPANSION_MAX:
+        {
+            phyParam.Value = EU868_PING_SLOT_SYMBOL_TO_EXPANSION_MAX;
+            break;
+        }
+        case PHY_BEACON_SYMBOL_TO_EXPANSION_FACTOR:
+        {
+            phyParam.Value = EU868_BEACON_SYMBOL_TO_EXPANSION_FACTOR;
+            break;
+        }
+        case PHY_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR:
+        {
+            phyParam.Value = EU868_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR;
+            break;
+        }
+        case PHY_MAX_BEACON_LESS_PERIOD:
+        {
+            phyParam.Value = EU868_MAX_BEACON_LESS_PERIOD;
+            break;
+        }
+        case PHY_BEACON_DELAY_BEACON_TIMING_ANS:
+        {
+            phyParam.Value = EU868_BEACON_DELAY_BEACON_TIMING_ANS;
+            break;
+        }
+        case PHY_BEACON_CHANNEL_FREQ:
+        {
+            phyParam.Value = EU868_BEACON_CHANNEL_FREQ;
+            break;
+        }
+        case PHY_PINGSLOT_CHANNEL_FREQ:
+        {
+            phyParam.Value = EU868_BEACON_CHANNEL_FREQ;
+            break;
+        }
+        case PHY_BEACON_SIZE:
+        {
+            phyParam.Value = EU868_BEACON_SIZE;
+            break;
+        }
+        case PHY_BEACON_CHANNEL_DR:
+        {
+            phyParam.Value = EU868_BEACON_CHANNEL_DR;
+            break;
+        }
+        default:
+        {
+            break;
+        }
+    }
+
+    return phyParam;
+}
+
+void RegionEU868SetBandTxDone( SetBandTxDoneParams_t* txDone )
+{
+    RegionCommonSetBandTxDone( txDone->Joined, &Bands[Channels[txDone->Channel].Band], txDone->LastTxDoneTime );
+}
+
+void RegionEU868InitDefaults( InitType_t type )
+{
+    switch( type )
+    {
+        case INIT_TYPE_INIT:
+        {
+            // Channels
+            Channels[0] = ( ChannelParams_t ) EU868_LC1;
+            Channels[1] = ( ChannelParams_t ) EU868_LC2;
+            Channels[2] = ( ChannelParams_t ) EU868_LC3;
+
+            // Initialize the channels default mask
+            ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 );
+            // Update the channels mask
+            RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 1 );
+            break;
+        }
+        case INIT_TYPE_RESTORE:
+        {
+            // Restore channels default mask
+            ChannelsMask[0] |= ChannelsDefaultMask[0];
+            break;
+        }
+        default:
+        {
+            break;
+        }
+    }
+}
+
+bool RegionEU868Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute )
+{
+    switch( phyAttribute )
+    {
+        case PHY_TX_DR:
+        {
+            return RegionCommonValueInRange( verify->DatarateParams.Datarate, EU868_TX_MIN_DATARATE, EU868_TX_MAX_DATARATE );
+        }
+        case PHY_DEF_TX_DR:
+        {
+            return RegionCommonValueInRange( verify->DatarateParams.Datarate, DR_0, DR_5 );
+        }
+        case PHY_RX_DR:
+        {
+            return RegionCommonValueInRange( verify->DatarateParams.Datarate, EU868_RX_MIN_DATARATE, EU868_RX_MAX_DATARATE );
+        }
+        case PHY_DEF_TX_POWER:
+        case PHY_TX_POWER:
+        {
+            // Remark: switched min and max!
+            return RegionCommonValueInRange( verify->TxPower, EU868_MAX_TX_POWER, EU868_MIN_TX_POWER );
+        }
+        case PHY_DUTY_CYCLE:
+        {
+            return EU868_DUTY_CYCLE_ENABLED;
+        }
+        case PHY_NB_JOIN_TRIALS:
+        {
+            if( verify->NbJoinTrials < 48 )
+            {
+                return false;
+            }
+            break;
+        }
+        default:
+            return false;
+    }
+    return true;
+}
+
+void RegionEU868ApplyCFList( ApplyCFListParams_t* applyCFList )
+{
+    ChannelParams_t newChannel;
+    ChannelAddParams_t channelAdd;
+    ChannelRemoveParams_t channelRemove;
+
+    // Setup default datarate range
+    newChannel.DrRange.Value = ( DR_5 << 4 ) | DR_0;
+
+    // Size of the optional CF list
+    if( applyCFList->Size != 16 )
+    {
+        return;
+    }
+
+    // Last byte is RFU, don't take it into account
+    for( uint8_t i = 0, chanIdx = EU868_NUMB_DEFAULT_CHANNELS; chanIdx < EU868_MAX_NB_CHANNELS; i+=3, chanIdx++ )
+    {
+        if( chanIdx < ( EU868_NUMB_CHANNELS_CF_LIST + EU868_NUMB_DEFAULT_CHANNELS ) )
+        {
+            // Channel frequency
+            newChannel.Frequency = (uint32_t) applyCFList->Payload[i];
+            newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 1] << 8 );
+            newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 2] << 16 );
+            newChannel.Frequency *= 100;
+
+            // Initialize alternative frequency to 0
+            newChannel.Rx1Frequency = 0;
+        }
+        else
+        {
+            newChannel.Frequency = 0;
+            newChannel.DrRange.Value = 0;
+            newChannel.Rx1Frequency = 0;
+        }
+
+        if( newChannel.Frequency != 0 )
+        {
+            channelAdd.NewChannel = &newChannel;
+            channelAdd.ChannelId = chanIdx;
+
+            // Try to add all channels
+            RegionEU868ChannelAdd( &channelAdd );
+        }
+        else
+        {
+            channelRemove.ChannelId = chanIdx;
+
+            RegionEU868ChannelsRemove( &channelRemove );
+        }
+    }
+}
+
+bool RegionEU868ChanMaskSet( ChanMaskSetParams_t* chanMaskSet )
+{
+    switch( chanMaskSet->ChannelsMaskType )
+    {
+        case CHANNELS_MASK:
+        {
+            RegionCommonChanMaskCopy( ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 );
+            break;
+        }
+        case CHANNELS_DEFAULT_MASK:
+        {
+            RegionCommonChanMaskCopy( ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 );
+            break;
+        }
+        default:
+            return false;
+    }
+    return true;
+}
+
+bool RegionEU868AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter )
+{
+    bool adrAckReq = false;
+    int8_t datarate = adrNext->Datarate;
+    int8_t txPower = adrNext->TxPower;
+    GetPhyParams_t getPhy;
+    PhyParam_t phyParam;
+
+    // Report back the adr ack counter
+    *adrAckCounter = adrNext->AdrAckCounter;
+
+    if( adrNext->AdrEnabled == true )
+    {
+        if( datarate == EU868_TX_MIN_DATARATE )
+        {
+            *adrAckCounter = 0;
+            adrAckReq = false;
+        }
+        else
+        {
+            if( adrNext->AdrAckCounter >= EU868_ADR_ACK_LIMIT )
+            {
+                adrAckReq = true;
+                txPower = EU868_MAX_TX_POWER;
+            }
+            else
+            {
+                adrAckReq = false;
+            }
+            if( adrNext->AdrAckCounter >= ( EU868_ADR_ACK_LIMIT + EU868_ADR_ACK_DELAY ) )
+            {
+                if( ( adrNext->AdrAckCounter % EU868_ADR_ACK_DELAY ) == 1 )
+                {
+                    // Decrease the datarate
+                    getPhy.Attribute = PHY_NEXT_LOWER_TX_DR;
+                    getPhy.Datarate = datarate;
+                    getPhy.UplinkDwellTime = adrNext->UplinkDwellTime;
+                    phyParam = RegionEU868GetPhyParam( &getPhy );
+                    datarate = phyParam.Value;
+
+                    if( datarate == EU868_TX_MIN_DATARATE )
+                    {
+                        // We must set adrAckReq to false as soon as we reach the lowest datarate
+                        adrAckReq = false;
+                        if( adrNext->UpdateChanMask == true )
+                        {
+                            // Re-enable default channels
+                            ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 );
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    *drOut = datarate;
+    *txPowOut = txPower;
+    return adrAckReq;
+}
+
+void RegionEU868ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams )
+{
+    double tSymbol = 0.0;
+
+    // Get the datarate, perform a boundary check
+    rxConfigParams->Datarate = MIN( datarate, EU868_RX_MAX_DATARATE );
+    rxConfigParams->Bandwidth = GetBandwidth( rxConfigParams->Datarate );
+
+    if( rxConfigParams->Datarate == DR_7 )
+    { // FSK
+        tSymbol = RegionCommonComputeSymbolTimeFsk( DataratesEU868[rxConfigParams->Datarate] );
+    }
+    else
+    { // LoRa
+        tSymbol = RegionCommonComputeSymbolTimeLoRa( DataratesEU868[rxConfigParams->Datarate], BandwidthsEU868[rxConfigParams->Datarate] );
+    }
+
+    RegionCommonComputeRxWindowParameters( tSymbol, minRxSymbols, rxError, RADIO_WAKEUP_TIME, &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset );
+}
+
+bool RegionEU868RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate )
+{
+    RadioModems_t modem;
+    int8_t dr = rxConfig->Datarate;
+    uint8_t maxPayload = 0;
+    int8_t phyDr = 0;
+    uint32_t frequency = rxConfig->Frequency;
+
+    if( Radio.GetStatus( ) != RF_IDLE )
+    {
+        return false;
+    }
+
+    if( rxConfig->Window == 0 )
+    {
+        // Apply window 1 frequency
+        frequency = Channels[rxConfig->Channel].Frequency;
+        // Apply the alternative RX 1 window frequency, if it is available
+        if( Channels[rxConfig->Channel].Rx1Frequency != 0 )
+        {
+            frequency = Channels[rxConfig->Channel].Rx1Frequency;
+        }
+    }
+
+    // Read the physical datarate from the datarates table
+    phyDr = DataratesEU868[dr];
+
+    Radio.SetChannel( frequency );
+
+    // Radio configuration
+    if( dr == DR_7 )
+    {
+        modem = MODEM_FSK;
+        Radio.SetRxConfig( modem, 50000, phyDr * 1000, 0, 83333, 5, rxConfig->WindowTimeout, false, 0, true, 0, 0, false, rxConfig->RxContinuous );
+    }
+    else
+    {
+        modem = MODEM_LORA;
+        Radio.SetRxConfig( modem, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous );
+    }
+
+    if( rxConfig->RepeaterSupport == true )
+    {
+        maxPayload = MaxPayloadOfDatarateRepeaterEU868[dr];
+    }
+    else
+    {
+        maxPayload = MaxPayloadOfDatarateEU868[dr];
+    }
+
+    Radio.SetMaxPayloadLength( modem, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD );
+
+    *datarate = (uint8_t) dr;
+    return true;
+}
+
+bool RegionEU868TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir )
+{
+    RadioModems_t modem;
+    int8_t phyDr = DataratesEU868[txConfig->Datarate];
+    int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, Bands[Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, ChannelsMask );
+    uint32_t bandwidth = GetBandwidth( txConfig->Datarate );
+    int8_t phyTxPower = 0;
+
+    // Calculate physical TX power
+    phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain );
+
+    // Setup the radio frequency
+    Radio.SetChannel( Channels[txConfig->Channel].Frequency );
+
+    if( txConfig->Datarate == DR_7 )
+    { // High Speed FSK channel
+        modem = MODEM_FSK;
+        Radio.SetTxConfig( modem, phyTxPower, 25000, bandwidth, phyDr * 1000, 0, 5, false, true, 0, 0, false, 3000 );
+    }
+    else
+    {
+        modem = MODEM_LORA;
+        Radio.SetTxConfig( modem, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 3000 );
+    }
+
+    // Setup maximum payload lenght of the radio driver
+    Radio.SetMaxPayloadLength( modem, txConfig->PktLen );
+    // Get the time-on-air of the next tx frame
+    *txTimeOnAir = Radio.TimeOnAir( modem, txConfig->PktLen );
+
+    *txPower = txPowerLimited;
+    return true;
+}
+
+uint8_t RegionEU868LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed )
+{
+    uint8_t status = 0x07;
+    RegionCommonLinkAdrParams_t linkAdrParams;
+    uint8_t nextIndex = 0;
+    uint8_t bytesProcessed = 0;
+    uint16_t chMask = 0;
+    GetPhyParams_t getPhy;
+    PhyParam_t phyParam;
+    RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams;
+
+    while( bytesProcessed < linkAdrReq->PayloadSize )
+    {
+        // Get ADR request parameters
+        nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams );
+
+        if( nextIndex == 0 )
+            break; // break loop, since no more request has been found
+
+        // Update bytes processed
+        bytesProcessed += nextIndex;
+
+        // Revert status, as we only check the last ADR request for the channel mask KO
+        status = 0x07;
+
+        // Setup temporary channels mask
+        chMask = linkAdrParams.ChMask;
+
+        // Verify channels mask
+        if( ( linkAdrParams.ChMaskCtrl == 0 ) && ( chMask == 0 ) )
+        {
+            status &= 0xFE; // Channel mask KO
+        }
+        else if( ( ( linkAdrParams.ChMaskCtrl >= 1 ) && ( linkAdrParams.ChMaskCtrl <= 5 )) ||
+                ( linkAdrParams.ChMaskCtrl >= 7 ) )
+        {
+            // RFU
+            status &= 0xFE; // Channel mask KO
+        }
+        else
+        {
+            for( uint8_t i = 0; i < EU868_MAX_NB_CHANNELS; i++ )
+            {
+                if( linkAdrParams.ChMaskCtrl == 6 )
+                {
+                    if( Channels[i].Frequency != 0 )
+                    {
+                        chMask |= 1 << i;
+                    }
+                }
+                else
+                {
+                    if( ( ( chMask & ( 1 << i ) ) != 0 ) &&
+                        ( Channels[i].Frequency == 0 ) )
+                    {// Trying to enable an undefined channel
+                        status &= 0xFE; // Channel mask KO
+                    }
+                }
+            }
+        }
+    }
+
+    // Get the minimum possible datarate
+    getPhy.Attribute = PHY_MIN_TX_DR;
+    getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime;
+    phyParam = RegionEU868GetPhyParam( &getPhy );
+
+    linkAdrVerifyParams.Status = status;
+    linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled;
+    linkAdrVerifyParams.Datarate = linkAdrParams.Datarate;
+    linkAdrVerifyParams.TxPower = linkAdrParams.TxPower;
+    linkAdrVerifyParams.NbRep = linkAdrParams.NbRep;
+    linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate;
+    linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower;
+    linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep;
+    linkAdrVerifyParams.NbChannels = EU868_MAX_NB_CHANNELS;
+    linkAdrVerifyParams.ChannelsMask = &chMask;
+    linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value;
+    linkAdrVerifyParams.MaxDatarate = EU868_TX_MAX_DATARATE;
+    linkAdrVerifyParams.Channels = Channels;
+    linkAdrVerifyParams.MinTxPower = EU868_MIN_TX_POWER;
+    linkAdrVerifyParams.MaxTxPower = EU868_MAX_TX_POWER;
+
+    // Verify the parameters and update, if necessary
+    status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep );
+
+    // Update channelsMask if everything is correct
+    if( status == 0x07 )
+    {
+        // Set the channels mask to a default value
+        memset( ChannelsMask, 0, sizeof( ChannelsMask ) );
+        // Update the channels mask
+        ChannelsMask[0] = chMask;
+    }
+
+    // Update status variables
+    *drOut = linkAdrParams.Datarate;
+    *txPowOut = linkAdrParams.TxPower;
+    *nbRepOut = linkAdrParams.NbRep;
+    *nbBytesParsed = bytesProcessed;
+
+    return status;
+}
+
+uint8_t RegionEU868RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq )
+{
+    uint8_t status = 0x07;
+
+    // Verify radio frequency
+    if( Radio.CheckRfFrequency( rxParamSetupReq->Frequency ) == false )
+    {
+        status &= 0xFE; // Channel frequency KO
+    }
+
+    // Verify datarate
+    if( RegionCommonValueInRange( rxParamSetupReq->Datarate, EU868_RX_MIN_DATARATE, EU868_RX_MAX_DATARATE ) == false )
+    {
+        status &= 0xFD; // Datarate KO
+    }
+
+    // Verify datarate offset
+    if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, EU868_MIN_RX1_DR_OFFSET, EU868_MAX_RX1_DR_OFFSET ) == false )
+    {
+        status &= 0xFB; // Rx1DrOffset range KO
+    }
+
+    return status;
+}
+
+uint8_t RegionEU868NewChannelReq( NewChannelReqParams_t* newChannelReq )
+{
+    uint8_t status = 0x03;
+    ChannelAddParams_t channelAdd;
+    ChannelRemoveParams_t channelRemove;
+
+    if( newChannelReq->NewChannel->Frequency == 0 )
+    {
+        channelRemove.ChannelId = newChannelReq->ChannelId;
+
+        // Remove
+        if( RegionEU868ChannelsRemove( &channelRemove ) == false )
+        {
+            status &= 0xFC;
+        }
+    }
+    else
+    {
+        channelAdd.NewChannel = newChannelReq->NewChannel;
+        channelAdd.ChannelId = newChannelReq->ChannelId;
+
+        switch( RegionEU868ChannelAdd( &channelAdd ) )
+        {
+            case LORAMAC_STATUS_OK:
+            {
+                break;
+            }
+            case LORAMAC_STATUS_FREQUENCY_INVALID:
+            {
+                status &= 0xFE;
+                break;
+            }
+            case LORAMAC_STATUS_DATARATE_INVALID:
+            {
+                status &= 0xFD;
+                break;
+            }
+            case LORAMAC_STATUS_FREQ_AND_DR_INVALID:
+            {
+                status &= 0xFC;
+                break;
+            }
+            default:
+            {
+                status &= 0xFC;
+                break;
+            }
+        }
+    }
+
+    return status;
+}
+
+int8_t RegionEU868TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq )
+{
+    return -1;
+}
+
+uint8_t RegionEU868DlChannelReq( DlChannelReqParams_t* dlChannelReq )
+{
+    uint8_t status = 0x03;
+    uint8_t band = 0;
+
+    // Verify if the frequency is supported
+    if( VerifyTxFreq( dlChannelReq->Rx1Frequency, &band ) == false )
+    {
+        status &= 0xFE;
+    }
+
+    // Verify if an uplink frequency exists
+    if( Channels[dlChannelReq->ChannelId].Frequency == 0 )
+    {
+        status &= 0xFD;
+    }
+
+    // Apply Rx1 frequency, if the status is OK
+    if( status == 0x03 )
+    {
+        Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency;
+    }
+
+    return status;
+}
+
+int8_t RegionEU868AlternateDr( AlternateDrParams_t* alternateDr )
+{
+    int8_t datarate = 0;
+
+    if( ( alternateDr->NbTrials % 48 ) == 0 )
+    {
+        datarate = DR_0;
+    }
+    else if( ( alternateDr->NbTrials % 32 ) == 0 )
+    {
+        datarate = DR_1;
+    }
+    else if( ( alternateDr->NbTrials % 24 ) == 0 )
+    {
+        datarate = DR_2;
+    }
+    else if( ( alternateDr->NbTrials % 16 ) == 0 )
+    {
+        datarate = DR_3;
+    }
+    else if( ( alternateDr->NbTrials % 8 ) == 0 )
+    {
+        datarate = DR_4;
+    }
+    else
+    {
+        datarate = DR_5;
+    }
+    return datarate;
+}
+
+void RegionEU868CalcBackOff( CalcBackOffParams_t* calcBackOff )
+{
+    RegionCommonCalcBackOffParams_t calcBackOffParams;
+
+    calcBackOffParams.Channels = Channels;
+    calcBackOffParams.Bands = Bands;
+    calcBackOffParams.LastTxIsJoinRequest = calcBackOff->LastTxIsJoinRequest;
+    calcBackOffParams.Joined = calcBackOff->Joined;
+    calcBackOffParams.DutyCycleEnabled = calcBackOff->DutyCycleEnabled;
+    calcBackOffParams.Channel = calcBackOff->Channel;
+    calcBackOffParams.ElapsedTime = calcBackOff->ElapsedTime;
+    calcBackOffParams.TxTimeOnAir = calcBackOff->TxTimeOnAir;
+
+    RegionCommonCalcBackOff( &calcBackOffParams );
+}
+
+bool RegionEU868NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff )
+{
+    uint8_t nbEnabledChannels = 0;
+    uint8_t delayTx = 0;
+    uint8_t enabledChannels[EU868_MAX_NB_CHANNELS] = { 0 };
+    TimerTime_t nextTxDelay = 0;
+
+    if( RegionCommonCountChannels( ChannelsMask, 0, 1 ) == 0 )
+    { // Reactivate default channels
+        ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 );
+    }
+
+    if( nextChanParams->AggrTimeOff <= TimerGetElapsedTime( nextChanParams->LastAggrTx ) )
+    {
+        // Reset Aggregated time off
+        *aggregatedTimeOff = 0;
+
+        // Update bands Time OFF
+        nextTxDelay = RegionCommonUpdateBandTimeOff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, Bands, EU868_MAX_NB_BANDS );
+
+        // Search how many channels are enabled
+        nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Joined, nextChanParams->Datarate,
+                                                      ChannelsMask, Channels,
+                                                      Bands, enabledChannels, &delayTx );
+    }
+    else
+    {
+        delayTx++;
+        nextTxDelay = nextChanParams->AggrTimeOff - TimerGetElapsedTime( nextChanParams->LastAggrTx );
+    }
+
+    if( nbEnabledChannels > 0 )
+    {
+        // We found a valid channel
+        *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )];
+
+        *time = 0;
+        return true;
+    }
+    else
+    {
+        if( delayTx > 0 )
+        {
+            // Delay transmission due to AggregatedTimeOff or to a band time off
+            *time = nextTxDelay;
+            return true;
+        }
+        // Datarate not supported by any channel, restore defaults
+        ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 );
+        *time = 0;
+        return false;
+    }
+}
+
+LoRaMacStatus_t RegionEU868ChannelAdd( ChannelAddParams_t* channelAdd )
+{
+    uint8_t band = 0;
+    bool drInvalid = false;
+    bool freqInvalid = false;
+    uint8_t id = channelAdd->ChannelId;
+
+    if( id >= EU868_MAX_NB_CHANNELS )
+    {
+        return LORAMAC_STATUS_PARAMETER_INVALID;
+    }
+
+    // Validate the datarate range
+    if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Min, EU868_TX_MIN_DATARATE, EU868_TX_MAX_DATARATE ) == false )
+    {
+        drInvalid = true;
+    }
+    if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, EU868_TX_MIN_DATARATE, EU868_TX_MAX_DATARATE ) == false )
+    {
+        drInvalid = true;
+    }
+    if( channelAdd->NewChannel->DrRange.Fields.Min > channelAdd->NewChannel->DrRange.Fields.Max )
+    {
+        drInvalid = true;
+    }
+
+    // Default channels don't accept all values
+    if( id < EU868_NUMB_DEFAULT_CHANNELS )
+    {
+        // Validate the datarate range for min: must be DR_0
+        if( channelAdd->NewChannel->DrRange.Fields.Min > DR_0 )
+        {
+            drInvalid = true;
+        }
+        // Validate the datarate range for max: must be DR_5 <= Max <= TX_MAX_DATARATE
+        if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, DR_5, EU868_TX_MAX_DATARATE ) == false )
+        {
+            drInvalid = true;
+        }
+        // We are not allowed to change the frequency
+        if( channelAdd->NewChannel->Frequency != Channels[id].Frequency )
+        {
+            freqInvalid = true;
+        }
+    }
+
+    // Check frequency
+    if( freqInvalid == false )
+    {
+        if( VerifyTxFreq( channelAdd->NewChannel->Frequency, &band ) == false )
+        {
+            freqInvalid = true;
+        }
+    }
+
+    // Check status
+    if( ( drInvalid == true ) && ( freqInvalid == true ) )
+    {
+        return LORAMAC_STATUS_FREQ_AND_DR_INVALID;
+    }
+    if( drInvalid == true )
+    {
+        return LORAMAC_STATUS_DATARATE_INVALID;
+    }
+    if( freqInvalid == true )
+    {
+        return LORAMAC_STATUS_FREQUENCY_INVALID;
+    }
+
+    memcpy( &(Channels[id]), channelAdd->NewChannel, sizeof( Channels[id] ) );
+    Channels[id].Band = band;
+    ChannelsMask[0] |= ( 1 << id );
+    return LORAMAC_STATUS_OK;
+}
+
+bool RegionEU868ChannelsRemove( ChannelRemoveParams_t* channelRemove  )
+{
+    uint8_t id = channelRemove->ChannelId;
+
+    if( id < EU868_NUMB_DEFAULT_CHANNELS )
+    {
+        return false;
+    }
+
+    // Remove the channel from the list of channels
+    Channels[id] = ( ChannelParams_t ){ 0, 0, { 0 }, 0 };
+
+    return RegionCommonChanDisable( ChannelsMask, id, EU868_MAX_NB_CHANNELS );
+}
+
+void RegionEU868SetContinuousWave( ContinuousWaveParams_t* continuousWave )
+{
+    int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, Bands[Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, ChannelsMask );
+    int8_t phyTxPower = 0;
+    uint32_t frequency = Channels[continuousWave->Channel].Frequency;
+
+    // Calculate physical TX power
+    phyTxPower = RegionCommonComputeTxPower( txPowerLimited, continuousWave->MaxEirp, continuousWave->AntennaGain );
+
+    Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout );
+}
+
+uint8_t RegionEU868ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset )
+{
+    int8_t datarate = dr - drOffset;
+
+    if( datarate < 0 )
+    {
+        datarate = DR_0;
+    }
+    return datarate;
+}
+
+void RegionEU868RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr, bool *beaconChannelSet )
+{
+    RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup;
+
+    regionCommonRxBeaconSetup.Datarates = DataratesEU868;
+    regionCommonRxBeaconSetup.ChannelPlanFrequency = EU868_BEACON_CHANNEL_FREQ;
+    regionCommonRxBeaconSetup.BeaconTimingAnsFrequency = EU868_BEACON_CHANNEL_FREQ;
+    regionCommonRxBeaconSetup.BeaconSize = EU868_BEACON_SIZE;
+    regionCommonRxBeaconSetup.BeaconDatarate = EU868_BEACON_CHANNEL_DR;
+    regionCommonRxBeaconSetup.BeaconChannelBW = EU868_BEACON_CHANNEL_BW;
+    regionCommonRxBeaconSetup.CustomFrequency = rxBeaconSetup->CustomFrequency;
+    regionCommonRxBeaconSetup.CustomFrequencyEnabled = rxBeaconSetup->CustomFrequencyEnabled;
+    regionCommonRxBeaconSetup.BeaconChannelSet = rxBeaconSetup->BeaconChannelSet;
+    regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime;
+    regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout;
+
+    RegionCommonRxBeaconSetup( &regionCommonRxBeaconSetup, beaconChannelSet );
+
+    // Store downlink datarate
+    *outDr = EU868_BEACON_CHANNEL_DR;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/region/RegionEU868.h	Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,579 @@
+/*!
+ * \file      RegionEU868.h
+ *
+ * \brief     Region definition for EU868
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ *                ______                              _
+ *               / _____)             _              | |
+ *              ( (____  _____ ____ _| |_ _____  ____| |__
+ *               \____ \| ___ |    (_   _) ___ |/ ___)  _ \
+ *               _____) ) ____| | | || |_| ____( (___| | | |
+ *              (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ *              (C)2013 Semtech
+ *
+ *               ___ _____ _   ___ _  _____ ___  ___  ___ ___
+ *              / __|_   _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ *              \__ \ | |/ _ \ (__| ' <| _| (_) |   / (__| _|
+ *              |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ *              embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author    Miguel Luis ( Semtech )
+ *
+ * \author    Gregory Cristian ( Semtech )
+ *
+ * \author    Daniel Jaeckle ( STACKFORCE )
+ *
+ * \defgroup  REGIONEU868 Region EU868
+ *            Implementation according to LoRaWAN Specification v1.0.2.
+ * \{
+ */
+#ifndef __REGION_EU868_H__
+#define __REGION_EU868_H__
+
+/*!
+ * LoRaMac maximum number of channels
+ */
+#define EU868_MAX_NB_CHANNELS                       16
+
+/*!
+ * Number of default channels
+ */
+#define EU868_NUMB_DEFAULT_CHANNELS                 3
+
+/*!
+ * Number of channels to apply for the CF list
+ */
+#define EU868_NUMB_CHANNELS_CF_LIST                 5
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define EU868_TX_MIN_DATARATE                       DR_0
+
+/*!
+ * Maximal datarate that can be used by the node
+ */
+#define EU868_TX_MAX_DATARATE                       DR_7
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define EU868_RX_MIN_DATARATE                       DR_0
+
+/*!
+ * Maximal datarate that can be used by the node
+ */
+#define EU868_RX_MAX_DATARATE                       DR_7
+
+/*!
+ * Default datarate used by the node
+ */
+#define EU868_DEFAULT_DATARATE                      DR_0
+
+/*!
+ * Minimal Rx1 receive datarate offset
+ */
+#define EU868_MIN_RX1_DR_OFFSET                     0
+
+/*!
+ * Maximal Rx1 receive datarate offset
+ */
+#define EU868_MAX_RX1_DR_OFFSET                     5
+
+/*!
+ * Default Rx1 receive datarate offset
+ */
+#define EU868_DEFAULT_RX1_DR_OFFSET                 0
+
+/*!
+ * Minimal Tx output power that can be used by the node
+ */
+#define EU868_MIN_TX_POWER                          TX_POWER_7
+
+/*!
+ * Maximal Tx output power that can be used by the node
+ */
+#define EU868_MAX_TX_POWER                          TX_POWER_0
+
+/*!
+ * Default Tx output power used by the node
+ */
+#define EU868_DEFAULT_TX_POWER                      TX_POWER_0
+
+/*!
+ * Default Max EIRP
+ */
+#define EU868_DEFAULT_MAX_EIRP                      16.0f
+
+/*!
+ * Default antenna gain
+ */
+#define EU868_DEFAULT_ANTENNA_GAIN                  2.15f
+
+/*!
+ * ADR Ack limit
+ */
+#define EU868_ADR_ACK_LIMIT                         64
+
+/*!
+ * ADR Ack delay
+ */
+#define EU868_ADR_ACK_DELAY                         32
+
+/*!
+ * Enabled or disabled the duty cycle
+ */
+#define EU868_DUTY_CYCLE_ENABLED                    1
+
+/*!
+ * Maximum RX window duration
+ */
+#define EU868_MAX_RX_WINDOW                         3000
+
+/*!
+ * Receive delay 1
+ */
+#define EU868_RECEIVE_DELAY1                        1000
+
+/*!
+ * Receive delay 2
+ */
+#define EU868_RECEIVE_DELAY2                        2000
+
+/*!
+ * Join accept delay 1
+ */
+#define EU868_JOIN_ACCEPT_DELAY1                    5000
+
+/*!
+ * Join accept delay 2
+ */
+#define EU868_JOIN_ACCEPT_DELAY2                    6000
+
+/*!
+ * Maximum frame counter gap
+ */
+#define EU868_MAX_FCNT_GAP                          16384
+
+/*!
+ * Ack timeout
+ */
+#define EU868_ACKTIMEOUT                            2000
+
+/*!
+ * Random ack timeout limits
+ */
+#define EU868_ACK_TIMEOUT_RND                       1000
+
+#if ( EU868_DEFAULT_DATARATE > DR_5 )
+#error "A default DR higher than DR_5 may lead to connectivity loss."
+#endif
+
+/*!
+ * Second reception window channel frequency definition.
+ */
+#define EU868_RX_WND_2_FREQ                         869525000
+
+/*!
+ * Second reception window channel datarate definition.
+ */
+#define EU868_RX_WND_2_DR                           DR_0
+
+/*
+ * CLASS B
+ */
+/*!
+ * Beacon interval in ms
+ */
+#define EU868_BEACON_INTERVAL                       128000
+
+/*!
+ * Beacon reserved time in ms
+ */
+#define EU868_BEACON_RESERVED                       2120
+
+/*!
+ * Beacon guard time in ms
+ */
+#define EU868_BEACON_GUARD                          3000
+
+/*!
+ * Beacon window time in ms
+ */
+#define EU868_BEACON_WINDOW                         122880
+
+/*!
+ * Beacon window time in numer of slots
+ */
+#define EU868_BEACON_WINDOW_SLOTS                   4096
+
+/*!
+ * Ping slot length time in ms
+ */
+#define EU868_PING_SLOT_WINDOW                      30
+
+/*!
+ * Default symbol timeout for beacons and ping slot windows
+ */
+#define EU868_BEACON_SYMBOL_TO_DEFAULT              8
+
+/*!
+ * Maximum symbol timeout for beacons
+ */
+#define EU868_BEACON_SYMBOL_TO_EXPANSION_MAX        400
+
+/*!
+ * Maximum symbol timeout for ping slots
+ */
+#define EU868_PING_SLOT_SYMBOL_TO_EXPANSION_MAX     40
+
+/*!
+ * Symbol expansion value for beacon windows in case of beacon
+ * loss in symbols
+ */
+#define EU868_BEACON_SYMBOL_TO_EXPANSION_FACTOR     2
+
+/*!
+ * Symbol expansion value for ping slot windows in case of beacon
+ * loss in symbols
+ */
+#define EU868_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR  2
+
+/*!
+ * Maximum allowed beacon less time in ms
+ */
+#define EU868_MAX_BEACON_LESS_PERIOD                7200000
+
+/*!
+ * Delay time for the BeaconTimingAns in ms
+ */
+#define EU868_BEACON_DELAY_BEACON_TIMING_ANS        30
+
+/*!
+ * Beacon frequency
+ */
+#define EU868_BEACON_CHANNEL_FREQ                   869525000
+
+/*!
+ * Payload size of a beacon frame
+ */
+#define EU868_BEACON_SIZE                           17
+
+/*!
+ * Datarate of the beacon channel
+ */
+#define EU868_BEACON_CHANNEL_DR                     DR_3
+
+/*!
+ * Bandwith of the beacon channel
+ */
+#define EU868_BEACON_CHANNEL_BW                     0
+
+/*!
+ * Maximum number of bands
+ */
+#define EU868_MAX_NB_BANDS                          5
+
+/*!
+ * Band 0 definition
+ * { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff }
+ */
+#define EU868_BAND0                                 { 100 , EU868_MAX_TX_POWER, 0,  0 } //  1.0 %
+
+/*!
+ * Band 1 definition
+ * { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff }
+ */
+#define EU868_BAND1                                 { 100 , EU868_MAX_TX_POWER, 0,  0 } //  1.0 %
+
+/*!
+ * Band 2 definition
+ * Band = { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff }
+ */
+#define EU868_BAND2                                 { 1000, EU868_MAX_TX_POWER, 0,  0 } //  0.1 %
+
+/*!
+ * Band 2 definition
+ * Band = { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff }
+ */
+#define EU868_BAND3                                 { 10  , EU868_MAX_TX_POWER, 0,  0 } // 10.0 %
+
+/*!
+ * Band 2 definition
+ * Band = { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff }
+ */
+#define EU868_BAND4                                 { 100 , EU868_MAX_TX_POWER, 0,  0 } //  1.0 %
+
+/*!
+ * LoRaMac default channel 1
+ * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
+ */
+#define EU868_LC1                                   { 868100000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 1 }
+
+/*!
+ * LoRaMac default channel 2
+ * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
+ */
+#define EU868_LC2                                   { 868300000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 1 }
+
+/*!
+ * LoRaMac default channel 3
+ * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
+ */
+#define EU868_LC3                                   { 868500000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 1 }
+
+/*!
+ * LoRaMac channels which are allowed for the join procedure
+ */
+#define EU868_JOIN_CHANNELS                         ( uint16_t )( LC( 1 ) | LC( 2 ) | LC( 3 ) )
+
+/*!
+ * Data rates table definition
+ */
+static const uint8_t DataratesEU868[]  = { 12, 11, 10,  9,  8,  7,  7, 50 };
+
+/*!
+ * Bandwidths table definition in Hz
+ */
+static const uint32_t BandwidthsEU868[] = { 125000, 125000, 125000, 125000, 125000, 125000, 250000, 0 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Cannot operate with repeater.
+ */
+static const uint8_t MaxPayloadOfDatarateEU868[] = { 51, 51, 51, 115, 242, 242, 242, 242 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Can operate with repeater.
+ */
+static const uint8_t MaxPayloadOfDatarateRepeaterEU868[] = { 51, 51, 51, 115, 222, 222, 222, 222 };
+
+/*!
+ * \brief The function gets a value of a specific phy attribute.
+ *
+ * \param [IN] getPhy Pointer to the function parameters.
+ *
+ * \retval Returns a structure containing the PHY parameter.
+ */
+PhyParam_t RegionEU868GetPhyParam( GetPhyParams_t* getPhy );
+
+/*!
+ * \brief Updates the last TX done parameters of the current channel.
+ *
+ * \param [IN] txDone Pointer to the function parameters.
+ */
+void RegionEU868SetBandTxDone( SetBandTxDoneParams_t* txDone );
+
+/*!
+ * \brief Initializes the channels masks and the channels.
+ *
+ * \param [IN] type Sets the initialization type.
+ */
+void RegionEU868InitDefaults( InitType_t type );
+
+/*!
+ * \brief Verifies a parameter.
+ *
+ * \param [IN] verify Pointer to the function parameters.
+ *
+ * \param [IN] type Sets the initialization type.
+ *
+ * \retval Returns true, if the parameter is valid.
+ */
+bool RegionEU868Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute );
+
+/*!
+ * \brief The function parses the input buffer and sets up the channels of the
+ *        CF list.
+ *
+ * \param [IN] applyCFList Pointer to the function parameters.
+ */
+void RegionEU868ApplyCFList( ApplyCFListParams_t* applyCFList );
+
+/*!
+ * \brief Sets a channels mask.
+ *
+ * \param [IN] chanMaskSet Pointer to the function parameters.
+ *
+ * \retval Returns true, if the channels mask could be set.
+ */
+bool RegionEU868ChanMaskSet( ChanMaskSetParams_t* chanMaskSet );
+
+/*!
+ * \brief Calculates the next datarate to set, when ADR is on or off.
+ *
+ * \param [IN] adrNext Pointer to the function parameters.
+ *
+ * \param [OUT] drOut The calculated datarate for the next TX.
+ *
+ * \param [OUT] txPowOut The TX power for the next TX.
+ *
+ * \param [OUT] adrAckCounter The calculated ADR acknowledgement counter.
+ *
+ * \retval Returns true, if an ADR request should be performed.
+ */
+bool RegionEU868AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter );
+
+/*!
+ * Computes the Rx window timeout and offset.
+ *
+ * \param [IN] datarate     Rx window datarate index to be used
+ *
+ * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame.
+ *
+ * \param [IN] rxError      System maximum timing error of the receiver. In milliseconds
+ *                          The receiver will turn on in a [-rxError : +rxError] ms
+ *                          interval around RxOffset
+ *
+ * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields.
+ */
+void RegionEU868ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams );
+
+/*!
+ * \brief Configuration of the RX windows.
+ *
+ * \param [IN] rxConfig Pointer to the function parameters.
+ *
+ * \param [OUT] datarate The datarate index which was set.
+ *
+ * \retval Returns true, if the configuration was applied successfully.
+ */
+bool RegionEU868RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate );
+
+/*!
+ * \brief TX configuration.
+ *
+ * \param [IN] txConfig Pointer to the function parameters.
+ *
+ * \param [OUT] txPower The tx power index which was set.
+ *
+ * \param [OUT] txTimeOnAir The time-on-air of the frame.
+ *
+ * \retval Returns true, if the configuration was applied successfully.
+ */
+bool RegionEU868TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir );
+
+/*!
+ * \brief The function processes a Link ADR Request.
+ *
+ * \param [IN] linkAdrReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionEU868LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed );
+
+/*!
+ * \brief The function processes a RX Parameter Setup Request.
+ *
+ * \param [IN] rxParamSetupReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionEU868RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq );
+
+/*!
+ * \brief The function processes a Channel Request.
+ *
+ * \param [IN] newChannelReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionEU868NewChannelReq( NewChannelReqParams_t* newChannelReq );
+
+/*!
+ * \brief The function processes a TX ParamSetup Request.
+ *
+ * \param [IN] txParamSetupReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ *         Returns -1, if the functionality is not implemented. In this case, the end node
+ *         shall not process the command.
+ */
+int8_t RegionEU868TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq );
+
+/*!
+ * \brief The function processes a DlChannel Request.
+ *
+ * \param [IN] dlChannelReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionEU868DlChannelReq( DlChannelReqParams_t* dlChannelReq );
+
+/*!
+ * \brief Alternates the datarate of the channel for the join request.
+ *
+ * \param [IN] alternateDr Pointer to the function parameters.
+ *
+ * \retval Datarate to apply.
+ */
+int8_t RegionEU868AlternateDr( AlternateDrParams_t* alternateDr );
+
+/*!
+ * \brief Calculates the back-off time.
+ *
+ * \param [IN] calcBackOff Pointer to the function parameters.
+ */
+void RegionEU868CalcBackOff( CalcBackOffParams_t* calcBackOff );
+
+/*!
+ * \brief Searches and set the next random available channel
+ *
+ * \param [OUT] channel Next channel to use for TX.
+ *
+ * \param [OUT] time Time to wait for the next transmission according to the duty
+ *              cycle.
+ *
+ * \param [OUT] aggregatedTimeOff Updates the aggregated time off.
+ *
+ * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate]
+ */
+bool RegionEU868NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff );
+
+/*!
+ * \brief Adds a channel.
+ *
+ * \param [IN] channelAdd Pointer to the function parameters.
+ *
+ * \retval Status of the operation.
+ */
+LoRaMacStatus_t RegionEU868ChannelAdd( ChannelAddParams_t* channelAdd );
+
+/*!
+ * \brief Removes a channel.
+ *
+ * \param [IN] channelRemove Pointer to the function parameters.
+ *
+ * \retval Returns true, if the channel was removed successfully.
+ */
+bool RegionEU868ChannelsRemove( ChannelRemoveParams_t* channelRemove  );
+
+/*!
+ * \brief Sets the radio into continuous wave mode.
+ *
+ * \param [IN] continuousWave Pointer to the function parameters.
+ */
+void RegionEU868SetContinuousWave( ContinuousWaveParams_t* continuousWave );
+
+/*!
+ * \brief Computes new datarate according to the given offset
+ *
+ * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms
+ *
+ * \param [IN] dr Current datarate
+ *
+ * \param [IN] drOffset Offset to be applied
+ *
+ * \retval newDr Computed datarate.
+ */
+uint8_t RegionEU868ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset );
+
+void RegionEU868RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr, bool *beaconChannelSet );
+
+/*! \} defgroup REGIONEU868 */
+
+#endif // __REGION_EU868_H__
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/region/RegionIN865.cpp	Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,1152 @@
+/*
+ / _____)             _              | |
+( (____  _____ ____ _| |_ _____  ____| |__
+ \____ \| ___ |    (_   _) ___ |/ ___)  _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+    (C)2013 Semtech
+ ___ _____ _   ___ _  _____ ___  ___  ___ ___
+/ __|_   _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+\__ \ | |/ _ \ (__| ' <| _| (_) |   / (__| _|
+|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+embedded.connectivity.solutions===============
+
+Description: LoRa MAC region IN865 implementation
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+
+Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE )
+*/
+#include <stdbool.h>
+#include <string.h>
+#include <stdint.h>
+#include <math.h>
+
+#include "board.h"
+#include "LoRaMac.h"
+
+#include "utilities.h"
+
+#include "Region.h"
+#include "RegionCommon.h"
+#include "RegionIN865.h"
+
+// Definitions
+#define CHANNELS_MASK_SIZE              1
+
+// Global attributes
+/*!
+ * LoRaMAC channels
+ */
+static ChannelParams_t Channels[IN865_MAX_NB_CHANNELS];
+
+/*!
+ * LoRaMac bands
+ */
+static Band_t Bands[IN865_MAX_NB_BANDS] =
+{
+    IN865_BAND0
+};
+
+/*!
+ * LoRaMac channels mask
+ */
+static uint16_t ChannelsMask[CHANNELS_MASK_SIZE];
+
+/*!
+ * LoRaMac channels default mask
+ */
+static uint16_t ChannelsDefaultMask[CHANNELS_MASK_SIZE];
+
+// Static functions
+static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr )
+{
+    uint8_t nextLowerDr = 0;
+
+    if( dr == minDr )
+    {
+        nextLowerDr = minDr;
+    }
+    else if( dr == DR_7 )
+    {
+        nextLowerDr = DR_5;
+    }
+    else
+    {
+        nextLowerDr = dr - 1;
+    }
+    return nextLowerDr;
+}
+
+static uint32_t GetBandwidth( uint32_t drIndex )
+{
+    switch( BandwidthsIN865[drIndex] )
+    {
+        default:
+        case 125000:
+            return 0;
+        case 250000:
+            return 1;
+        case 500000:
+            return 2;
+    }
+}
+
+static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask )
+{
+    int8_t txPowerResult = txPower;
+
+    // Limit tx power to the band max
+    txPowerResult =  MAX( txPower, maxBandTxPower );
+
+    return txPowerResult;
+}
+
+static bool VerifyTxFreq( uint32_t freq, uint8_t *band )
+{
+    // Check radio driver support
+    if( Radio.CheckRfFrequency( freq ) == false )
+    {
+        return false;
+    }
+
+    if( ( freq < 865000000 ) || ( freq > 867000000 ) )
+    {
+        return false;
+    }
+    return true;
+}
+
+static uint8_t CountNbOfEnabledChannels( bool joined, uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx )
+{
+    uint8_t nbEnabledChannels = 0;
+    uint8_t delayTransmission = 0;
+
+    for( uint8_t i = 0, k = 0; i < IN865_MAX_NB_CHANNELS; i += 16, k++ )
+    {
+        for( uint8_t j = 0; j < 16; j++ )
+        {
+            if( ( channelsMask[k] & ( 1 << j ) ) != 0 )
+            {
+                if( channels[i + j].Frequency == 0 )
+                { // Check if the channel is enabled
+                    continue;
+                }
+                if( joined == false )
+                {
+                    if( ( IN865_JOIN_CHANNELS & ( 1 << j ) ) == 0 )
+                    {
+                        continue;
+                    }
+                }
+                if( RegionCommonValueInRange( datarate, channels[i + j].DrRange.Fields.Min,
+                                              channels[i + j].DrRange.Fields.Max ) == false )
+                { // Check if the current channel selection supports the given datarate
+                    continue;
+                }
+                if( bands[channels[i + j].Band].TimeOff > 0 )
+                { // Check if the band is available for transmission
+                    delayTransmission++;
+                    continue;
+                }
+                enabledChannels[nbEnabledChannels++] = i + j;
+            }
+        }
+    }
+
+    *delayTx = delayTransmission;
+    return nbEnabledChannels;
+}
+
+PhyParam_t RegionIN865GetPhyParam( GetPhyParams_t* getPhy )
+{
+    PhyParam_t phyParam = { 0 };
+
+    switch( getPhy->Attribute )
+    {
+        case PHY_MIN_RX_DR:
+        {
+            phyParam.Value = IN865_RX_MIN_DATARATE;
+            break;
+        }
+        case PHY_MIN_TX_DR:
+        {
+            phyParam.Value = IN865_TX_MIN_DATARATE;
+            break;
+        }
+        case PHY_DEF_TX_DR:
+        {
+            phyParam.Value = IN865_DEFAULT_DATARATE;
+            break;
+        }
+        case PHY_NEXT_LOWER_TX_DR:
+        {
+            phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, IN865_TX_MIN_DATARATE );
+            break;
+        }
+        case PHY_DEF_TX_POWER:
+        {
+            phyParam.Value = IN865_DEFAULT_TX_POWER;
+            break;
+        }
+        case PHY_MAX_PAYLOAD:
+        {
+            phyParam.Value = MaxPayloadOfDatarateIN865[getPhy->Datarate];
+            break;
+        }
+        case PHY_MAX_PAYLOAD_REPEATER:
+        {
+            phyParam.Value = MaxPayloadOfDatarateRepeaterIN865[getPhy->Datarate];
+            break;
+        }
+        case PHY_DUTY_CYCLE:
+        {
+            phyParam.Value = IN865_DUTY_CYCLE_ENABLED;
+            break;
+        }
+        case PHY_MAX_RX_WINDOW:
+        {
+            phyParam.Value = IN865_MAX_RX_WINDOW;
+            break;
+        }
+        case PHY_RECEIVE_DELAY1:
+        {
+            phyParam.Value = IN865_RECEIVE_DELAY1;
+            break;
+        }
+        case PHY_RECEIVE_DELAY2:
+        {
+            phyParam.Value = IN865_RECEIVE_DELAY2;
+            break;
+        }
+        case PHY_JOIN_ACCEPT_DELAY1:
+        {
+            phyParam.Value = IN865_JOIN_ACCEPT_DELAY1;
+            break;
+        }
+        case PHY_JOIN_ACCEPT_DELAY2:
+        {
+            phyParam.Value = IN865_JOIN_ACCEPT_DELAY2;
+            break;
+        }
+        case PHY_MAX_FCNT_GAP:
+        {
+            phyParam.Value = IN865_MAX_FCNT_GAP;
+            break;
+        }
+        case PHY_ACK_TIMEOUT:
+        {
+            phyParam.Value = ( IN865_ACKTIMEOUT + randr( -IN865_ACK_TIMEOUT_RND, IN865_ACK_TIMEOUT_RND ) );
+            break;
+        }
+        case PHY_DEF_DR1_OFFSET:
+        {
+            phyParam.Value = IN865_DEFAULT_RX1_DR_OFFSET;
+            break;
+        }
+        case PHY_DEF_RX2_FREQUENCY:
+        {
+            phyParam.Value = IN865_RX_WND_2_FREQ;
+            break;
+        }
+        case PHY_DEF_RX2_DR:
+        {
+            phyParam.Value = IN865_RX_WND_2_DR;
+            break;
+        }
+        case PHY_CHANNELS_MASK:
+        {
+            phyParam.ChannelsMask = ChannelsMask;
+            break;
+        }
+        case PHY_CHANNELS_DEFAULT_MASK:
+        {
+            phyParam.ChannelsMask = ChannelsDefaultMask;
+            break;
+        }
+        case PHY_MAX_NB_CHANNELS:
+        {
+            phyParam.Value = IN865_MAX_NB_CHANNELS;
+            break;
+        }
+        case PHY_CHANNELS:
+        {
+            phyParam.Channels = Channels;
+            break;
+        }
+        case PHY_DEF_UPLINK_DWELL_TIME:
+        case PHY_DEF_DOWNLINK_DWELL_TIME:
+        {
+            phyParam.Value = 0;
+            break;
+        }
+        case PHY_DEF_MAX_EIRP:
+        {
+            phyParam.fValue = IN865_DEFAULT_MAX_EIRP;
+            break;
+        }
+        case PHY_DEF_ANTENNA_GAIN:
+        {
+            phyParam.fValue = IN865_DEFAULT_ANTENNA_GAIN;
+            break;
+        }
+        case PHY_NB_JOIN_TRIALS:
+        case PHY_DEF_NB_JOIN_TRIALS:
+        {
+            phyParam.Value = 48;
+            break;
+        }
+        case PHY_BEACON_INTERVAL:
+        {
+            phyParam.Value = IN865_BEACON_INTERVAL;
+            break;
+        }
+        case PHY_BEACON_RESERVED:
+        {
+            phyParam.Value = IN865_BEACON_RESERVED;
+            break;
+        }
+        case PHY_BEACON_GUARD:
+        {
+            phyParam.Value = IN865_BEACON_GUARD;
+            break;
+        }
+        case PHY_BEACON_WINDOW:
+        {
+            phyParam.Value = IN865_BEACON_WINDOW;
+            break;
+        }
+        case PHY_BEACON_WINDOW_SLOTS:
+        {
+            phyParam.Value = IN865_BEACON_WINDOW_SLOTS;
+            break;
+        }
+        case PHY_PING_SLOT_WINDOW:
+        {
+            phyParam.Value = IN865_PING_SLOT_WINDOW;
+            break;
+        }
+        case PHY_BEACON_SYMBOL_TO_DEFAULT:
+        {
+            phyParam.Value = IN865_BEACON_SYMBOL_TO_DEFAULT;
+            break;
+        }
+        case PHY_BEACON_SYMBOL_TO_EXPANSION_MAX:
+        {
+            phyParam.Value = IN865_BEACON_SYMBOL_TO_EXPANSION_MAX;
+            break;
+        }
+        case PHY_PING_SLOT_SYMBOL_TO_EXPANSION_MAX:
+        {
+            phyParam.Value = IN865_PING_SLOT_SYMBOL_TO_EXPANSION_MAX;
+            break;
+        }
+        case PHY_BEACON_SYMBOL_TO_EXPANSION_FACTOR:
+        {
+            phyParam.Value = IN865_BEACON_SYMBOL_TO_EXPANSION_FACTOR;
+            break;
+        }
+        case PHY_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR:
+        {
+            phyParam.Value = IN865_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR;
+            break;
+        }
+        case PHY_MAX_BEACON_LESS_PERIOD:
+        {
+            phyParam.Value = IN865_MAX_BEACON_LESS_PERIOD;
+            break;
+        }
+        case PHY_BEACON_DELAY_BEACON_TIMING_ANS:
+        {
+            phyParam.Value = IN865_BEACON_DELAY_BEACON_TIMING_ANS;
+            break;
+        }
+        case PHY_BEACON_CHANNEL_FREQ:
+        {
+            phyParam.Value = IN865_BEACON_CHANNEL_FREQ;
+            break;
+        }
+        case PHY_PINGSLOT_CHANNEL_FREQ:
+        {
+            phyParam.Value = IN865_BEACON_CHANNEL_FREQ;
+            break;
+        }
+        case PHY_BEACON_SIZE:
+        {
+            phyParam.Value = IN865_BEACON_SIZE;
+            break;
+        }
+        case PHY_BEACON_CHANNEL_DR:
+        {
+            phyParam.Value = IN865_BEACON_CHANNEL_DR;
+            break;
+        }
+        default:
+        {
+            break;
+        }
+    }
+
+    return phyParam;
+}
+
+void RegionIN865SetBandTxDone( SetBandTxDoneParams_t* txDone )
+{
+    RegionCommonSetBandTxDone( txDone->Joined, &Bands[Channels[txDone->Channel].Band], txDone->LastTxDoneTime );
+}
+
+void RegionIN865InitDefaults( InitType_t type )
+{
+    switch( type )
+    {
+        case INIT_TYPE_INIT:
+        {
+            // Channels
+            Channels[0] = ( ChannelParams_t ) IN865_LC1;
+            Channels[1] = ( ChannelParams_t ) IN865_LC2;
+            Channels[2] = ( ChannelParams_t ) IN865_LC3;
+
+            // Initialize the channels default mask
+            ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 );
+            // Update the channels mask
+            RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 1 );
+            break;
+        }
+        case INIT_TYPE_RESTORE:
+        {
+            // Restore channels default mask
+            ChannelsMask[0] |= ChannelsDefaultMask[0];
+            break;
+        }
+        default:
+        {
+            break;
+        }
+    }
+}
+
+bool RegionIN865Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute )
+{
+    switch( phyAttribute )
+    {
+        case PHY_TX_DR:
+        {
+            return RegionCommonValueInRange( verify->DatarateParams.Datarate, IN865_TX_MIN_DATARATE, IN865_TX_MAX_DATARATE );
+        }
+        case PHY_DEF_TX_DR:
+        {
+            return RegionCommonValueInRange( verify->DatarateParams.Datarate, DR_0, DR_5 );
+        }
+        case PHY_RX_DR:
+        {
+            return RegionCommonValueInRange( verify->DatarateParams.Datarate, IN865_RX_MIN_DATARATE, IN865_RX_MAX_DATARATE );
+        }
+        case PHY_DEF_TX_POWER:
+        case PHY_TX_POWER:
+        {
+            // Remark: switched min and max!
+            return RegionCommonValueInRange( verify->TxPower, IN865_MAX_TX_POWER, IN865_MIN_TX_POWER );
+        }
+        case PHY_DUTY_CYCLE:
+        {
+            return IN865_DUTY_CYCLE_ENABLED;
+        }
+        case PHY_NB_JOIN_TRIALS:
+        {
+            if( verify->NbJoinTrials < 48 )
+            {
+                return false;
+            }
+            break;
+        }
+        default:
+            return false;
+    }
+    return true;
+}
+
+void RegionIN865ApplyCFList( ApplyCFListParams_t* applyCFList )
+{
+    ChannelParams_t newChannel;
+    ChannelAddParams_t channelAdd;
+    ChannelRemoveParams_t channelRemove;
+
+    // Setup default datarate range
+    newChannel.DrRange.Value = ( DR_5 << 4 ) | DR_0;
+
+    // Size of the optional CF list
+    if( applyCFList->Size != 16 )
+    {
+        return;
+    }
+
+    // Last byte is RFU, don't take it into account
+    for( uint8_t i = 0, chanIdx = IN865_NUMB_DEFAULT_CHANNELS; chanIdx < IN865_MAX_NB_CHANNELS; i+=3, chanIdx++ )
+    {
+        if( chanIdx < ( IN865_NUMB_CHANNELS_CF_LIST + IN865_NUMB_DEFAULT_CHANNELS ) )
+        {
+            // Channel frequency
+            newChannel.Frequency = (uint32_t) applyCFList->Payload[i];
+            newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 1] << 8 );
+            newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 2] << 16 );
+            newChannel.Frequency *= 100;
+
+            // Initialize alternative frequency to 0
+            newChannel.Rx1Frequency = 0;
+        }
+        else
+        {
+            newChannel.Frequency = 0;
+            newChannel.DrRange.Value = 0;
+            newChannel.Rx1Frequency = 0;
+        }
+
+        if( newChannel.Frequency != 0 )
+        {
+            channelAdd.NewChannel = &newChannel;
+            channelAdd.ChannelId = chanIdx;
+
+            // Try to add all channels
+            RegionIN865ChannelAdd( &channelAdd );
+        }
+        else
+        {
+            channelRemove.ChannelId = chanIdx;
+
+            RegionIN865ChannelsRemove( &channelRemove );
+        }
+    }
+}
+
+bool RegionIN865ChanMaskSet( ChanMaskSetParams_t* chanMaskSet )
+{
+    switch( chanMaskSet->ChannelsMaskType )
+    {
+        case CHANNELS_MASK:
+        {
+            RegionCommonChanMaskCopy( ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 );
+            break;
+        }
+        case CHANNELS_DEFAULT_MASK:
+        {
+            RegionCommonChanMaskCopy( ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 );
+            break;
+        }
+        default:
+            return false;
+    }
+    return true;
+}
+
+bool RegionIN865AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter )
+{
+    bool adrAckReq = false;
+    int8_t datarate = adrNext->Datarate;
+    int8_t txPower = adrNext->TxPower;
+    GetPhyParams_t getPhy;
+    PhyParam_t phyParam;
+
+    // Report back the adr ack counter
+    *adrAckCounter = adrNext->AdrAckCounter;
+
+    if( adrNext->AdrEnabled == true )
+    {
+        if( datarate == IN865_TX_MIN_DATARATE )
+        {
+            *adrAckCounter = 0;
+            adrAckReq = false;
+        }
+        else
+        {
+            if( adrNext->AdrAckCounter >= IN865_ADR_ACK_LIMIT )
+            {
+                adrAckReq = true;
+                txPower = IN865_MAX_TX_POWER;
+            }
+            else
+            {
+                adrAckReq = false;
+            }
+            if( adrNext->AdrAckCounter >= ( IN865_ADR_ACK_LIMIT + IN865_ADR_ACK_DELAY ) )
+            {
+                if( ( adrNext->AdrAckCounter % IN865_ADR_ACK_DELAY ) == 1 )
+                {
+                    // Decrease the datarate
+                    getPhy.Attribute = PHY_NEXT_LOWER_TX_DR;
+                    getPhy.Datarate = datarate;
+                    getPhy.UplinkDwellTime = adrNext->UplinkDwellTime;
+                    phyParam = RegionIN865GetPhyParam( &getPhy );
+                    datarate = phyParam.Value;
+
+                    if( datarate == IN865_TX_MIN_DATARATE )
+                    {
+                        // We must set adrAckReq to false as soon as we reach the lowest datarate
+                        adrAckReq = false;
+                        if( adrNext->UpdateChanMask == true )
+                        {
+                            // Re-enable default channels
+                            ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 );
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    *drOut = datarate;
+    *txPowOut = txPower;
+    return adrAckReq;
+}
+
+void RegionIN865ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams )
+{
+    double tSymbol = 0.0;
+
+    // Get the datarate, perform a boundary check
+    rxConfigParams->Datarate = MIN( datarate, IN865_RX_MAX_DATARATE );
+    rxConfigParams->Bandwidth = GetBandwidth( rxConfigParams->Datarate );
+
+    if( rxConfigParams->Datarate == DR_7 )
+    { // FSK
+        tSymbol = RegionCommonComputeSymbolTimeFsk( DataratesIN865[rxConfigParams->Datarate] );
+    }
+    else
+    { // LoRa
+        tSymbol = RegionCommonComputeSymbolTimeLoRa( DataratesIN865[rxConfigParams->Datarate], BandwidthsIN865[rxConfigParams->Datarate] );
+    }
+
+    RegionCommonComputeRxWindowParameters( tSymbol, minRxSymbols, rxError, RADIO_WAKEUP_TIME, &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset );
+}
+
+bool RegionIN865RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate )
+{
+    RadioModems_t modem;
+    int8_t dr = rxConfig->Datarate;
+    uint8_t maxPayload = 0;
+    int8_t phyDr = 0;
+    uint32_t frequency = rxConfig->Frequency;
+
+    if( Radio.GetStatus( ) != RF_IDLE )
+    {
+        return false;
+    }
+
+    if( rxConfig->Window == 0 )
+    {
+        // Apply window 1 frequency
+        frequency = Channels[rxConfig->Channel].Frequency;
+        // Apply the alternative RX 1 window frequency, if it is available
+        if( Channels[rxConfig->Channel].Rx1Frequency != 0 )
+        {
+            frequency = Channels[rxConfig->Channel].Rx1Frequency;
+        }
+    }
+
+    // Read the physical datarate from the datarates table
+    phyDr = DataratesIN865[dr];
+
+    Radio.SetChannel( frequency );
+
+    // Radio configuration
+    if( dr == DR_7 )
+    {
+        modem = MODEM_FSK;
+        Radio.SetRxConfig( modem, 50000, phyDr * 1000, 0, 83333, 5, rxConfig->WindowTimeout, false, 0, true, 0, 0, false, rxConfig->RxContinuous );
+    }
+    else
+    {
+        modem = MODEM_LORA;
+        Radio.SetRxConfig( modem, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous );
+    }
+
+    if( rxConfig->RepeaterSupport == true )
+    {
+        maxPayload = MaxPayloadOfDatarateRepeaterIN865[dr];
+    }
+    else
+    {
+        maxPayload = MaxPayloadOfDatarateIN865[dr];
+    }
+    Radio.SetMaxPayloadLength( modem, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD );
+
+    *datarate = (uint8_t) dr;
+    return true;
+}
+
+bool RegionIN865TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir )
+{
+    RadioModems_t modem;
+    int8_t phyDr = DataratesIN865[txConfig->Datarate];
+    int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, Bands[Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, ChannelsMask );
+    uint32_t bandwidth = GetBandwidth( txConfig->Datarate );
+    int8_t phyTxPower = 0;
+
+    // Calculate physical TX power
+    phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain );
+
+    // Setup the radio frequency
+    Radio.SetChannel( Channels[txConfig->Channel].Frequency );
+
+    if( txConfig->Datarate == DR_7 )
+    { // High Speed FSK channel
+        modem = MODEM_FSK;
+        Radio.SetTxConfig( modem, phyTxPower, 25000, bandwidth, phyDr * 1000, 0, 5, false, true, 0, 0, false, 3000 );
+    }
+    else
+    {
+        modem = MODEM_LORA;
+        Radio.SetTxConfig( modem, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 3000 );
+    }
+
+    // Setup maximum payload lenght of the radio driver
+    Radio.SetMaxPayloadLength( modem, txConfig->PktLen );
+    // Get the time-on-air of the next tx frame
+    *txTimeOnAir = Radio.TimeOnAir( modem, txConfig->PktLen );
+
+    *txPower = txPowerLimited;
+    return true;
+}
+
+uint8_t RegionIN865LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed )
+{
+    uint8_t status = 0x07;
+    RegionCommonLinkAdrParams_t linkAdrParams;
+    uint8_t nextIndex = 0;
+    uint8_t bytesProcessed = 0;
+    uint16_t chMask = 0;
+    GetPhyParams_t getPhy;
+    PhyParam_t phyParam;
+    RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams;
+
+    while( bytesProcessed < linkAdrReq->PayloadSize )
+    {
+        // Get ADR request parameters
+        nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams );
+
+        if( nextIndex == 0 )
+            break; // break loop, since no more request has been found
+
+        // Update bytes processed
+        bytesProcessed += nextIndex;
+
+        // Revert status, as we only check the last ADR request for the channel mask KO
+        status = 0x07;
+
+        // Setup temporary channels mask
+        chMask = linkAdrParams.ChMask;
+
+        // Verify channels mask
+        if( ( linkAdrParams.ChMaskCtrl == 0 ) && ( chMask == 0 ) )
+        {
+            status &= 0xFE; // Channel mask KO
+        }
+        else if( ( ( linkAdrParams.ChMaskCtrl >= 1 ) && ( linkAdrParams.ChMaskCtrl <= 5 )) ||
+                ( linkAdrParams.ChMaskCtrl >= 7 ) )
+        {
+            // RFU
+            status &= 0xFE; // Channel mask KO
+        }
+        else
+        {
+            for( uint8_t i = 0; i < IN865_MAX_NB_CHANNELS; i++ )
+            {
+                if( linkAdrParams.ChMaskCtrl == 6 )
+                {
+                    if( Channels[i].Frequency != 0 )
+                    {
+                        chMask |= 1 << i;
+                    }
+                }
+                else
+                {
+                    if( ( ( chMask & ( 1 << i ) ) != 0 ) &&
+                        ( Channels[i].Frequency == 0 ) )
+                    {// Trying to enable an undefined channel
+                        status &= 0xFE; // Channel mask KO
+                    }
+                }
+            }
+        }
+    }
+
+    // Get the minimum possible datarate
+    getPhy.Attribute = PHY_MIN_TX_DR;
+    getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime;
+    phyParam = RegionIN865GetPhyParam( &getPhy );
+
+    linkAdrVerifyParams.Status = status;
+    linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled;
+    linkAdrVerifyParams.Datarate = linkAdrParams.Datarate;
+    linkAdrVerifyParams.TxPower = linkAdrParams.TxPower;
+    linkAdrVerifyParams.NbRep = linkAdrParams.NbRep;
+    linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate;
+    linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower;
+    linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep;
+    linkAdrVerifyParams.NbChannels = IN865_MAX_NB_CHANNELS;
+    linkAdrVerifyParams.ChannelsMask = &chMask;
+    linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value;
+    linkAdrVerifyParams.MaxDatarate = IN865_TX_MAX_DATARATE;
+    linkAdrVerifyParams.Channels = Channels;
+    linkAdrVerifyParams.MinTxPower = IN865_MIN_TX_POWER;
+    linkAdrVerifyParams.MaxTxPower = IN865_MAX_TX_POWER;
+
+    // Verify the parameters and update, if necessary
+    status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep );
+
+    // Update channelsMask if everything is correct
+    if( status == 0x07 )
+    {
+        // Set the channels mask to a default value
+        memset( ChannelsMask, 0, sizeof( ChannelsMask ) );
+        // Update the channels mask
+        ChannelsMask[0] = chMask;
+    }
+
+    // Update status variables
+    *drOut = linkAdrParams.Datarate;
+    *txPowOut = linkAdrParams.TxPower;
+    *nbRepOut = linkAdrParams.NbRep;
+    *nbBytesParsed = bytesProcessed;
+
+    return status;
+}
+
+uint8_t RegionIN865RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq )
+{
+    uint8_t status = 0x07;
+
+    // Verify radio frequency
+    if( Radio.CheckRfFrequency( rxParamSetupReq->Frequency ) == false )
+    {
+        status &= 0xFE; // Channel frequency KO
+    }
+
+    // Verify datarate
+    if( RegionCommonValueInRange( rxParamSetupReq->Datarate, IN865_RX_MIN_DATARATE, IN865_RX_MAX_DATARATE ) == false )
+    {
+        status &= 0xFD; // Datarate KO
+    }
+
+    // Verify datarate offset
+    if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, IN865_MIN_RX1_DR_OFFSET, IN865_MAX_RX1_DR_OFFSET ) == false )
+    {
+        status &= 0xFB; // Rx1DrOffset range KO
+    }
+
+    return status;
+}
+
+uint8_t RegionIN865NewChannelReq( NewChannelReqParams_t* newChannelReq )
+{
+    uint8_t status = 0x03;
+    ChannelAddParams_t channelAdd;
+    ChannelRemoveParams_t channelRemove;
+
+    if( newChannelReq->NewChannel->Frequency == 0 )
+    {
+        channelRemove.ChannelId = newChannelReq->ChannelId;
+
+        // Remove
+        if( RegionIN865ChannelsRemove( &channelRemove ) == false )
+        {
+            status &= 0xFC;
+        }
+    }
+    else
+    {
+        channelAdd.NewChannel = newChannelReq->NewChannel;
+        channelAdd.ChannelId = newChannelReq->ChannelId;
+
+        switch( RegionIN865ChannelAdd( &channelAdd ) )
+        {
+            case LORAMAC_STATUS_OK:
+            {
+                break;
+            }
+            case LORAMAC_STATUS_FREQUENCY_INVALID:
+            {
+                status &= 0xFE;
+                break;
+            }
+            case LORAMAC_STATUS_DATARATE_INVALID:
+            {
+                status &= 0xFD;
+                break;
+            }
+            case LORAMAC_STATUS_FREQ_AND_DR_INVALID:
+            {
+                status &= 0xFC;
+                break;
+            }
+            default:
+            {
+                status &= 0xFC;
+                break;
+            }
+        }
+    }
+
+    return status;
+}
+
+int8_t RegionIN865TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq )
+{
+    return -1;
+}
+
+uint8_t RegionIN865DlChannelReq( DlChannelReqParams_t* dlChannelReq )
+{
+    uint8_t status = 0x03;
+    uint8_t band = 0;
+
+    // Verify if the frequency is supported
+    if( VerifyTxFreq( dlChannelReq->Rx1Frequency, &band ) == false )
+    {
+        status &= 0xFE;
+    }
+
+    // Verify if an uplink frequency exists
+    if( Channels[dlChannelReq->ChannelId].Frequency == 0 )
+    {
+        status &= 0xFD;
+    }
+
+    // Apply Rx1 frequency, if the status is OK
+    if( status == 0x03 )
+    {
+        Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency;
+    }
+
+    return status;
+}
+
+int8_t RegionIN865AlternateDr( AlternateDrParams_t* alternateDr )
+{
+    int8_t datarate = 0;
+
+    if( ( alternateDr->NbTrials % 48 ) == 0 )
+    {
+        datarate = DR_0;
+    }
+    else if( ( alternateDr->NbTrials % 32 ) == 0 )
+    {
+        datarate = DR_1;
+    }
+    else if( ( alternateDr->NbTrials % 24 ) == 0 )
+    {
+        datarate = DR_2;
+    }
+    else if( ( alternateDr->NbTrials % 16 ) == 0 )
+    {
+        datarate = DR_3;
+    }
+    else if( ( alternateDr->NbTrials % 8 ) == 0 )
+    {
+        datarate = DR_4;
+    }
+    else
+    {
+        datarate = DR_5;
+    }
+    return datarate;
+}
+
+void RegionIN865CalcBackOff( CalcBackOffParams_t* calcBackOff )
+{
+    RegionCommonCalcBackOffParams_t calcBackOffParams;
+
+    calcBackOffParams.Channels = Channels;
+    calcBackOffParams.Bands = Bands;
+    calcBackOffParams.LastTxIsJoinRequest = calcBackOff->LastTxIsJoinRequest;
+    calcBackOffParams.Joined = calcBackOff->Joined;
+    calcBackOffParams.DutyCycleEnabled = calcBackOff->DutyCycleEnabled;
+    calcBackOffParams.Channel = calcBackOff->Channel;
+    calcBackOffParams.ElapsedTime = calcBackOff->ElapsedTime;
+    calcBackOffParams.TxTimeOnAir = calcBackOff->TxTimeOnAir;
+
+    RegionCommonCalcBackOff( &calcBackOffParams );
+}
+
+bool RegionIN865NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff )
+{
+    uint8_t nbEnabledChannels = 0;
+    uint8_t delayTx = 0;
+    uint8_t enabledChannels[IN865_MAX_NB_CHANNELS] = { 0 };
+    TimerTime_t nextTxDelay = 0;
+
+    if( RegionCommonCountChannels( ChannelsMask, 0, 1 ) == 0 )
+    { // Reactivate default channels
+        ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 );
+    }
+
+    if( nextChanParams->AggrTimeOff <= TimerGetElapsedTime( nextChanParams->LastAggrTx ) )
+    {
+        // Reset Aggregated time off
+        *aggregatedTimeOff = 0;
+
+        // Update bands Time OFF
+        nextTxDelay = RegionCommonUpdateBandTimeOff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, Bands, IN865_MAX_NB_BANDS );
+
+        // Search how many channels are enabled
+        nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Joined, nextChanParams->Datarate,
+                                                      ChannelsMask, Channels,
+                                                      Bands, enabledChannels, &delayTx );
+    }
+    else
+    {
+        delayTx++;
+        nextTxDelay = nextChanParams->AggrTimeOff - TimerGetElapsedTime( nextChanParams->LastAggrTx );
+    }
+
+    if( nbEnabledChannels > 0 )
+    {
+        // We found a valid channel
+        *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )];
+
+        *time = 0;
+        return true;
+    }
+    else
+    {
+        if( delayTx > 0 )
+        {
+            // Delay transmission due to AggregatedTimeOff or to a band time off
+            *time = nextTxDelay;
+            return true;
+        }
+        // Datarate not supported by any channel, restore defaults
+        ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 );
+        *time = 0;
+        return false;
+    }
+}
+
+LoRaMacStatus_t RegionIN865ChannelAdd( ChannelAddParams_t* channelAdd )
+{
+    uint8_t band = 0;
+    bool drInvalid = false;
+    bool freqInvalid = false;
+    uint8_t id = channelAdd->ChannelId;
+
+    if( id >= IN865_MAX_NB_CHANNELS )
+    {
+        return LORAMAC_STATUS_PARAMETER_INVALID;
+    }
+
+    // Validate the datarate range
+    if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Min, IN865_TX_MIN_DATARATE, IN865_TX_MAX_DATARATE ) == false )
+    {
+        drInvalid = true;
+    }
+    if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, IN865_TX_MIN_DATARATE, IN865_TX_MAX_DATARATE ) == false )
+    {
+        drInvalid = true;
+    }
+    if( channelAdd->NewChannel->DrRange.Fields.Min > channelAdd->NewChannel->DrRange.Fields.Max )
+    {
+        drInvalid = true;
+    }
+
+    // Default channels don't accept all values
+    if( id < IN865_NUMB_DEFAULT_CHANNELS )
+    {
+        // Validate the datarate range for min: must be DR_0
+        if( channelAdd->NewChannel->DrRange.Fields.Min > DR_0 )
+        {
+            drInvalid = true;
+        }
+        // Validate the datarate range for max: must be DR_5 <= Max <= TX_MAX_DATARATE
+        if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, DR_5, IN865_TX_MAX_DATARATE ) == false )
+        {
+            drInvalid = true;
+        }
+        // We are not allowed to change the frequency
+        if( channelAdd->NewChannel->Frequency != Channels[id].Frequency )
+        {
+            freqInvalid = true;
+        }
+    }
+
+    // Check frequency
+    if( freqInvalid == false )
+    {
+        if( VerifyTxFreq( channelAdd->NewChannel->Frequency, &band ) == false )
+        {
+            freqInvalid = true;
+        }
+    }
+
+    // Check status
+    if( ( drInvalid == true ) && ( freqInvalid == true ) )
+    {
+        return LORAMAC_STATUS_FREQ_AND_DR_INVALID;
+    }
+    if( drInvalid == true )
+    {
+        return LORAMAC_STATUS_DATARATE_INVALID;
+    }
+    if( freqInvalid == true )
+    {
+        return LORAMAC_STATUS_FREQUENCY_INVALID;
+    }
+
+    memcpy( &(Channels[id]), channelAdd->NewChannel, sizeof( Channels[id] ) );
+    Channels[id].Band = band;
+    ChannelsMask[0] |= ( 1 << id );
+    return LORAMAC_STATUS_OK;
+}
+
+bool RegionIN865ChannelsRemove( ChannelRemoveParams_t* channelRemove  )
+{
+    uint8_t id = channelRemove->ChannelId;
+
+    if( id < IN865_NUMB_DEFAULT_CHANNELS )
+    {
+        return false;
+    }
+
+    // Remove the channel from the list of channels
+    Channels[id] = ( ChannelParams_t ){ 0, 0, { 0 }, 0 };
+
+    return RegionCommonChanDisable( ChannelsMask, id, IN865_MAX_NB_CHANNELS );
+}
+
+void RegionIN865SetContinuousWave( ContinuousWaveParams_t* continuousWave )
+{
+    int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, Bands[Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, ChannelsMask );
+    int8_t phyTxPower = 0;
+    uint32_t frequency = Channels[continuousWave->Channel].Frequency;
+
+    // Calculate physical TX power
+    phyTxPower = RegionCommonComputeTxPower( txPowerLimited, continuousWave->MaxEirp, continuousWave->AntennaGain );
+
+    Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout );
+}
+
+uint8_t RegionIN865ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset )
+{
+    // Apply offset formula
+    return MIN( DR_5, MAX( DR_0, dr - EffectiveRx1DrOffsetIN865[drOffset] ) );
+}
+
+void RegionIN865RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr, bool *beaconChannelSet )
+{
+    RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup;
+
+    regionCommonRxBeaconSetup.Datarates = DataratesIN865;
+    regionCommonRxBeaconSetup.ChannelPlanFrequency = IN865_BEACON_CHANNEL_FREQ;
+    regionCommonRxBeaconSetup.BeaconTimingAnsFrequency = IN865_BEACON_CHANNEL_FREQ;
+    regionCommonRxBeaconSetup.BeaconSize = IN865_BEACON_SIZE;
+    regionCommonRxBeaconSetup.BeaconDatarate = IN865_BEACON_CHANNEL_DR;
+    regionCommonRxBeaconSetup.BeaconChannelBW = IN865_BEACON_CHANNEL_BW;
+    regionCommonRxBeaconSetup.CustomFrequency = rxBeaconSetup->CustomFrequency;
+    regionCommonRxBeaconSetup.CustomFrequencyEnabled = rxBeaconSetup->CustomFrequencyEnabled;
+    regionCommonRxBeaconSetup.BeaconChannelSet = rxBeaconSetup->BeaconChannelSet;
+    regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime;
+    regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout;
+
+    RegionCommonRxBeaconSetup( &regionCommonRxBeaconSetup, beaconChannelSet );
+
+    // Store downlink datarate
+    *outDr = IN865_BEACON_CHANNEL_DR;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/region/RegionIN865.h	Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,558 @@
+/*!
+ * \file      RegionIN865.h
+ *
+ * \brief     Region definition for IN865
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ *                ______                              _
+ *               / _____)             _              | |
+ *              ( (____  _____ ____ _| |_ _____  ____| |__
+ *               \____ \| ___ |    (_   _) ___ |/ ___)  _ \
+ *               _____) ) ____| | | || |_| ____( (___| | | |
+ *              (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ *              (C)2013 Semtech
+ *
+ *               ___ _____ _   ___ _  _____ ___  ___  ___ ___
+ *              / __|_   _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ *              \__ \ | |/ _ \ (__| ' <| _| (_) |   / (__| _|
+ *              |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ *              embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author    Miguel Luis ( Semtech )
+ *
+ * \author    Gregory Cristian ( Semtech )
+ *
+ * \author    Daniel Jaeckle ( STACKFORCE )
+ *
+ * \defgroup  REGIONIN865 Region IN865
+ *            Implementation according to LoRaWAN Specification v1.0.2.
+ * \{
+ */
+#ifndef __REGION_IN865_H__
+#define __REGION_IN865_H__
+
+/*!
+ * LoRaMac maximum number of channels
+ */
+#define IN865_MAX_NB_CHANNELS                       16
+
+/*!
+ * Number of default channels
+ */
+#define IN865_NUMB_DEFAULT_CHANNELS                 3
+
+/*!
+ * Number of channels to apply for the CF list
+ */
+#define IN865_NUMB_CHANNELS_CF_LIST                 5
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define IN865_TX_MIN_DATARATE                       DR_0
+
+/*!
+ * Maximal datarate that can be used by the node
+ */
+#define IN865_TX_MAX_DATARATE                       DR_7
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define IN865_RX_MIN_DATARATE                       DR_0
+
+/*!
+ * Maximal datarate that can be used by the node
+ */
+#define IN865_RX_MAX_DATARATE                       DR_7
+
+/*!
+ * Default datarate used by the node
+ */
+#define IN865_DEFAULT_DATARATE                      DR_0
+
+/*!
+ * Minimal Rx1 receive datarate offset
+ */
+#define IN865_MIN_RX1_DR_OFFSET                     0
+
+/*!
+ * Maximal Rx1 receive datarate offset
+ */
+#define IN865_MAX_RX1_DR_OFFSET                     7
+
+/*!
+ * Default Rx1 receive datarate offset
+ */
+#define IN865_DEFAULT_RX1_DR_OFFSET                 0
+
+/*!
+ * Minimal Tx output power that can be used by the node
+ */
+#define IN865_MIN_TX_POWER                          TX_POWER_10
+
+/*!
+ * Maximal Tx output power that can be used by the node
+ */
+#define IN865_MAX_TX_POWER                          TX_POWER_0
+
+/*!
+ * Default Tx output power used by the node
+ */
+#define IN865_DEFAULT_TX_POWER                      TX_POWER_0
+
+/*!
+ * Default Max EIRP
+ */
+#define IN865_DEFAULT_MAX_EIRP                      30.0f
+
+/*!
+ * Default antenna gain
+ */
+#define IN865_DEFAULT_ANTENNA_GAIN                  2.15f
+
+/*!
+ * ADR Ack limit
+ */
+#define IN865_ADR_ACK_LIMIT                         64
+
+/*!
+ * ADR Ack delay
+ */
+#define IN865_ADR_ACK_DELAY                         32
+
+/*!
+ * Enabled or disabled the duty cycle
+ */
+#define IN865_DUTY_CYCLE_ENABLED                    1
+
+/*!
+ * Maximum RX window duration
+ */
+#define IN865_MAX_RX_WINDOW                         3000
+
+/*!
+ * Receive delay 1
+ */
+#define IN865_RECEIVE_DELAY1                        1000
+
+/*!
+ * Receive delay 2
+ */
+#define IN865_RECEIVE_DELAY2                        2000
+
+/*!
+ * Join accept delay 1
+ */
+#define IN865_JOIN_ACCEPT_DELAY1                    5000
+
+/*!
+ * Join accept delay 2
+ */
+#define IN865_JOIN_ACCEPT_DELAY2                    6000
+
+/*!
+ * Maximum frame counter gap
+ */
+#define IN865_MAX_FCNT_GAP                          16384
+
+/*!
+ * Ack timeout
+ */
+#define IN865_ACKTIMEOUT                            2000
+
+/*!
+ * Random ack timeout limits
+ */
+#define IN865_ACK_TIMEOUT_RND                       1000
+
+#if ( IN865_DEFAULT_DATARATE > DR_5 )
+#error "A default DR higher than DR_5 may lead to connectivity loss."
+#endif
+
+/*!
+ * Second reception window channel frequency definition.
+ */
+#define IN865_RX_WND_2_FREQ                         866550000
+
+/*!
+ * Second reception window channel datarate definition.
+ */
+#define IN865_RX_WND_2_DR                           DR_2
+
+/*
+ * CLASS B
+ */
+/*!
+ * Beacon interval in ms
+ */
+#define IN865_BEACON_INTERVAL                       128000
+
+/*!
+ * Beacon reserved time in ms
+ */
+#define IN865_BEACON_RESERVED                       2120
+
+/*!
+ * Beacon guard time in ms
+ */
+#define IN865_BEACON_GUARD                          3000
+
+/*!
+ * Beacon window time in ms
+ */
+#define IN865_BEACON_WINDOW                         122880
+
+/*!
+ * Beacon window time in numer of slots
+ */
+#define IN865_BEACON_WINDOW_SLOTS                   4096
+
+/*!
+ * Ping slot length time in ms
+ */
+#define IN865_PING_SLOT_WINDOW                      30
+
+/*!
+ * Default symbol timeout for beacons and ping slot windows
+ */
+#define IN865_BEACON_SYMBOL_TO_DEFAULT              8
+
+/*!
+ * Maximum symbol timeout for beacons
+ */
+#define IN865_BEACON_SYMBOL_TO_EXPANSION_MAX        400
+
+/*!
+ * Maximum symbol timeout for ping slots
+ */
+#define IN865_PING_SLOT_SYMBOL_TO_EXPANSION_MAX     40
+
+/*!
+ * Symbol expansion value for beacon windows in case of beacon
+ * loss in symbols
+ */
+#define IN865_BEACON_SYMBOL_TO_EXPANSION_FACTOR     2
+
+/*!
+ * Symbol expansion value for ping slot windows in case of beacon
+ * loss in symbols
+ */
+#define IN865_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR  2
+
+/*!
+ * Maximum allowed beacon less time in ms
+ */
+#define IN865_MAX_BEACON_LESS_PERIOD                7200000
+
+/*!
+ * Delay time for the BeaconTimingAns in ms
+ */
+#define IN865_BEACON_DELAY_BEACON_TIMING_ANS        30
+
+/*!
+ * Beacon frequency
+ */
+#define IN865_BEACON_CHANNEL_FREQ                   866550000
+
+/*!
+ * Payload size of a beacon frame
+ */
+#define IN865_BEACON_SIZE                           19
+
+/*!
+ * Datarate of the beacon channel
+ */
+#define IN865_BEACON_CHANNEL_DR                     DR_4
+
+/*!
+ * Bandwith of the beacon channel
+ */
+#define IN865_BEACON_CHANNEL_BW                     0
+
+/*!
+ * Maximum number of bands
+ */
+#define IN865_MAX_NB_BANDS                          1
+
+/*!
+ * Band 0 definition
+ * { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff }
+ */
+#define IN865_BAND0                                 { 1 , IN865_MAX_TX_POWER, 0,  0 } //  100.0 %
+
+/*!
+ * LoRaMac default channel 1
+ * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
+ */
+#define IN865_LC1                                   { 865062500, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
+
+/*!
+ * LoRaMac default channel 2
+ * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
+ */
+#define IN865_LC2                                   { 865402500, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
+
+/*!
+ * LoRaMac default channel 3
+ * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
+ */
+#define IN865_LC3                                   { 865985000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
+
+/*!
+ * LoRaMac channels which are allowed for the join procedure
+ */
+#define IN865_JOIN_CHANNELS                         ( uint16_t )( LC( 1 ) | LC( 2 ) | LC( 3 ) )
+
+/*!
+ * Data rates table definition
+ */
+static const uint8_t DataratesIN865[]  = { 12, 11, 10,  9,  8,  7,  7, 50 };
+
+/*!
+ * Bandwidths table definition in Hz
+ */
+static const uint32_t BandwidthsIN865[] = { 125000, 125000, 125000, 125000, 125000, 125000, 250000, 0 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Cannot operate with repeater.
+ */
+static const uint8_t MaxPayloadOfDatarateIN865[] = { 51, 51, 51, 115, 242, 242, 242, 242 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Can operate with repeater.
+ */
+static const uint8_t MaxPayloadOfDatarateRepeaterIN865[] = { 51, 51, 51, 115, 222, 222, 222, 222 };
+
+/*!
+ * Effective datarate offsets for receive window 1.
+ */
+static const int8_t EffectiveRx1DrOffsetIN865[] = { 0, 1, 2, 3, 4, 5, -1, -2 };
+
+/*!
+ * \brief The function gets a value of a specific phy attribute.
+ *
+ * \param [IN] getPhy Pointer to the function parameters.
+ *
+ * \retval Returns a structure containing the PHY parameter.
+ */
+PhyParam_t RegionIN865GetPhyParam( GetPhyParams_t* getPhy );
+
+/*!
+ * \brief Updates the last TX done parameters of the current channel.
+ *
+ * \param [IN] txDone Pointer to the function parameters.
+ */
+void RegionIN865SetBandTxDone( SetBandTxDoneParams_t* txDone );
+
+/*!
+ * \brief Initializes the channels masks and the channels.
+ *
+ * \param [IN] type Sets the initialization type.
+ */
+void RegionIN865InitDefaults( InitType_t type );
+
+/*!
+ * \brief Verifies a parameter.
+ *
+ * \param [IN] verify Pointer to the function parameters.
+ *
+ * \param [IN] type Sets the initialization type.
+ *
+ * \retval Returns true, if the parameter is valid.
+ */
+bool RegionIN865Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute );
+
+/*!
+ * \brief The function parses the input buffer and sets up the channels of the
+ *        CF list.
+ *
+ * \param [IN] applyCFList Pointer to the function parameters.
+ */
+void RegionIN865ApplyCFList( ApplyCFListParams_t* applyCFList );
+
+/*!
+ * \brief Sets a channels mask.
+ *
+ * \param [IN] chanMaskSet Pointer to the function parameters.
+ *
+ * \retval Returns true, if the channels mask could be set.
+ */
+bool RegionIN865ChanMaskSet( ChanMaskSetParams_t* chanMaskSet );
+
+/*!
+ * \brief Calculates the next datarate to set, when ADR is on or off.
+ *
+ * \param [IN] adrNext Pointer to the function parameters.
+ *
+ * \param [OUT] drOut The calculated datarate for the next TX.
+ *
+ * \param [OUT] txPowOut The TX power for the next TX.
+ *
+ * \param [OUT] adrAckCounter The calculated ADR acknowledgement counter.
+ *
+ * \retval Returns true, if an ADR request should be performed.
+ */
+bool RegionIN865AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter );
+
+/*!
+ * Computes the Rx window timeout and offset.
+ *
+ * \param [IN] datarate     Rx window datarate index to be used
+ *
+ * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame.
+ *
+ * \param [IN] rxError      System maximum timing error of the receiver. In milliseconds
+ *                          The receiver will turn on in a [-rxError : +rxError] ms
+ *                          interval around RxOffset
+ *
+ * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields.
+ */
+void RegionIN865ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams );
+
+/*!
+ * \brief Configuration of the RX windows.
+ *
+ * \param [IN] rxConfig Pointer to the function parameters.
+ *
+ * \param [OUT] datarate The datarate index which was set.
+ *
+ * \retval Returns true, if the configuration was applied successfully.
+ */
+bool RegionIN865RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate );
+
+/*!
+ * \brief TX configuration.
+ *
+ * \param [IN] txConfig Pointer to the function parameters.
+ *
+ * \param [OUT] txPower The tx power index which was set.
+ *
+ * \param [OUT] txTimeOnAir The time-on-air of the frame.
+ *
+ * \retval Returns true, if the configuration was applied successfully.
+ */
+bool RegionIN865TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir );
+
+/*!
+ * \brief The function processes a Link ADR Request.
+ *
+ * \param [IN] linkAdrReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionIN865LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed );
+
+/*!
+ * \brief The function processes a RX Parameter Setup Request.
+ *
+ * \param [IN] rxParamSetupReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionIN865RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq );
+
+/*!
+ * \brief The function processes a Channel Request.
+ *
+ * \param [IN] newChannelReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionIN865NewChannelReq( NewChannelReqParams_t* newChannelReq );
+
+/*!
+ * \brief The function processes a TX ParamSetup Request.
+ *
+ * \param [IN] txParamSetupReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ *         Returns -1, if the functionality is not implemented. In this case, the end node
+ *         shall not process the command.
+ */
+int8_t RegionIN865TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq );
+
+/*!
+ * \brief The function processes a DlChannel Request.
+ *
+ * \param [IN] dlChannelReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionIN865DlChannelReq( DlChannelReqParams_t* dlChannelReq );
+
+/*!
+ * \brief Alternates the datarate of the channel for the join request.
+ *
+ * \param [IN] alternateDr Pointer to the function parameters.
+ *
+ * \retval Datarate to apply.
+ */
+int8_t RegionIN865AlternateDr( AlternateDrParams_t* alternateDr );
+
+/*!
+ * \brief Calculates the back-off time.
+ *
+ * \param [IN] calcBackOff Pointer to the function parameters.
+ */
+void RegionIN865CalcBackOff( CalcBackOffParams_t* calcBackOff );
+
+/*!
+ * \brief Searches and set the next random available channel
+ *
+ * \param [OUT] channel Next channel to use for TX.
+ *
+ * \param [OUT] time Time to wait for the next transmission according to the duty
+ *              cycle.
+ *
+ * \param [OUT] aggregatedTimeOff Updates the aggregated time off.
+ *
+ * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate]
+ */
+bool RegionIN865NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff );
+
+/*!
+ * \brief Adds a channel.
+ *
+ * \param [IN] channelAdd Pointer to the function parameters.
+ *
+ * \retval Status of the operation.
+ */
+LoRaMacStatus_t RegionIN865ChannelAdd( ChannelAddParams_t* channelAdd );
+
+/*!
+ * \brief Removes a channel.
+ *
+ * \param [IN] channelRemove Pointer to the function parameters.
+ *
+ * \retval Returns true, if the channel was removed successfully.
+ */
+bool RegionIN865ChannelsRemove( ChannelRemoveParams_t* channelRemove  );
+
+/*!
+ * \brief Sets the radio into continuous wave mode.
+ *
+ * \param [IN] continuousWave Pointer to the function parameters.
+ */
+void RegionIN865SetContinuousWave( ContinuousWaveParams_t* continuousWave );
+
+/*!
+ * \brief Computes new datarate according to the given offset
+ *
+ * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms
+ *
+ * \param [IN] dr Current datarate
+ *
+ * \param [IN] drOffset Offset to be applied
+ *
+ * \retval newDr Computed datarate.
+ */
+uint8_t RegionIN865ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset );
+
+/*! \} defgroup REGIONIN865 */
+
+#endif // __REGION_IN865_H__
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/region/RegionKR920.cpp	Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,1153 @@
+/*
+ / _____)             _              | |
+( (____  _____ ____ _| |_ _____  ____| |__
+ \____ \| ___ |    (_   _) ___ |/ ___)  _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+    (C)2013 Semtech
+ ___ _____ _   ___ _  _____ ___  ___  ___ ___
+/ __|_   _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+\__ \ | |/ _ \ (__| ' <| _| (_) |   / (__| _|
+|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+embedded.connectivity.solutions===============
+
+Description: LoRa MAC region KR920 implementation
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+
+Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE )
+*/
+#include <stdbool.h>
+#include <string.h>
+#include <stdint.h>
+#include <math.h>
+
+#include "board.h"
+#include "LoRaMac.h"
+
+#include "utilities.h"
+
+#include "Region.h"
+#include "RegionCommon.h"
+#include "RegionKR920.h"
+
+// Definitions
+#define CHANNELS_MASK_SIZE              1
+
+// Global attributes
+/*!
+ * LoRaMAC channels
+ */
+static ChannelParams_t Channels[KR920_MAX_NB_CHANNELS];
+
+/*!
+ * LoRaMac bands
+ */
+static Band_t Bands[KR920_MAX_NB_BANDS] =
+{
+    KR920_BAND0
+};
+
+/*!
+ * LoRaMac channels mask
+ */
+static uint16_t ChannelsMask[CHANNELS_MASK_SIZE];
+
+/*!
+ * LoRaMac channels default mask
+ */
+static uint16_t ChannelsDefaultMask[CHANNELS_MASK_SIZE];
+
+// Static functions
+static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr )
+{
+    uint8_t nextLowerDr = 0;
+
+    if( dr == minDr )
+    {
+        nextLowerDr = minDr;
+    }
+    else
+    {
+        nextLowerDr = dr - 1;
+    }
+    return nextLowerDr;
+}
+
+static int8_t GetMaxEIRP( uint32_t freq )
+{
+    if( freq >= 922100000 )
+    {// Limit to 14dBm
+        return KR920_DEFAULT_MAX_EIRP_HIGH;
+    }
+    // Limit to 10dBm
+    return KR920_DEFAULT_MAX_EIRP_LOW;
+}
+
+static uint32_t GetBandwidth( uint32_t drIndex )
+{
+    switch( BandwidthsKR920[drIndex] )
+    {
+        default:
+        case 125000:
+            return 0;
+        case 250000:
+            return 1;
+        case 500000:
+            return 2;
+    }
+}
+
+static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask )
+{
+    int8_t txPowerResult = txPower;
+
+    // Limit tx power to the band max
+    txPowerResult =  MAX( txPower, maxBandTxPower );
+
+    return txPowerResult;
+}
+
+static bool VerifyTxFreq( uint32_t freq )
+{
+    uint32_t tmpFreq = freq;
+
+    // Check radio driver support
+    if( Radio.CheckRfFrequency( tmpFreq ) == false )
+    {
+        return false;
+    }
+
+    // Verify if the frequency is valid. The frequency must be in a specified
+    // range and can be set to specific values.
+    if( ( tmpFreq >= 920900000 ) && ( tmpFreq <=923300000 ) )
+    {
+        // Range ok, check for specific value
+        tmpFreq -= 920900000;
+        if( ( tmpFreq % 200000 ) == 0 )
+        {
+            return true;
+        }
+    }
+    return false;
+}
+
+static uint8_t CountNbOfEnabledChannels( bool joined, uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx )
+{
+    uint8_t nbEnabledChannels = 0;
+    uint8_t delayTransmission = 0;
+
+    for( uint8_t i = 0, k = 0; i < KR920_MAX_NB_CHANNELS; i += 16, k++ )
+    {
+        for( uint8_t j = 0; j < 16; j++ )
+        {
+            if( ( channelsMask[k] & ( 1 << j ) ) != 0 )
+            {
+                if( channels[i + j].Frequency == 0 )
+                { // Check if the channel is enabled
+                    continue;
+                }
+                if( joined == false )
+                {
+                    if( ( KR920_JOIN_CHANNELS & ( 1 << j ) ) == 0 )
+                    {
+                        continue;
+                    }
+                }
+                if( RegionCommonValueInRange( datarate, channels[i + j].DrRange.Fields.Min,
+                                              channels[i + j].DrRange.Fields.Max ) == false )
+                { // Check if the current channel selection supports the given datarate
+                    continue;
+                }
+                if( bands[channels[i + j].Band].TimeOff > 0 )
+                { // Check if the band is available for transmission
+                    delayTransmission++;
+                    continue;
+                }
+                enabledChannels[nbEnabledChannels++] = i + j;
+            }
+        }
+    }
+
+    *delayTx = delayTransmission;
+    return nbEnabledChannels;
+}
+
+PhyParam_t RegionKR920GetPhyParam( GetPhyParams_t* getPhy )
+{
+    PhyParam_t phyParam = { 0 };
+
+    switch( getPhy->Attribute )
+    {
+        case PHY_MIN_RX_DR:
+        {
+            phyParam.Value = KR920_RX_MIN_DATARATE;
+            break;
+        }
+        case PHY_MIN_TX_DR:
+        {
+            phyParam.Value = KR920_TX_MIN_DATARATE;
+            break;
+        }
+        case PHY_DEF_TX_DR:
+        {
+            phyParam.Value = KR920_DEFAULT_DATARATE;
+            break;
+        }
+        case PHY_NEXT_LOWER_TX_DR:
+        {
+            phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, KR920_TX_MIN_DATARATE );
+            break;
+        }
+        case PHY_DEF_TX_POWER:
+        {
+            phyParam.Value = KR920_DEFAULT_TX_POWER;
+            break;
+        }
+        case PHY_MAX_PAYLOAD:
+        {
+            phyParam.Value = MaxPayloadOfDatarateKR920[getPhy->Datarate];
+            break;
+        }
+        case PHY_MAX_PAYLOAD_REPEATER:
+        {
+            phyParam.Value = MaxPayloadOfDatarateRepeaterKR920[getPhy->Datarate];
+            break;
+        }
+        case PHY_DUTY_CYCLE:
+        {
+            phyParam.Value = KR920_DUTY_CYCLE_ENABLED;
+            break;
+        }
+        case PHY_MAX_RX_WINDOW:
+        {
+            phyParam.Value = KR920_MAX_RX_WINDOW;
+            break;
+        }
+        case PHY_RECEIVE_DELAY1:
+        {
+            phyParam.Value = KR920_RECEIVE_DELAY1;
+            break;
+        }
+        case PHY_RECEIVE_DELAY2:
+        {
+            phyParam.Value = KR920_RECEIVE_DELAY2;
+            break;
+        }
+        case PHY_JOIN_ACCEPT_DELAY1:
+        {
+            phyParam.Value = KR920_JOIN_ACCEPT_DELAY1;
+            break;
+        }
+        case PHY_JOIN_ACCEPT_DELAY2:
+        {
+            phyParam.Value = KR920_JOIN_ACCEPT_DELAY2;
+            break;
+        }
+        case PHY_MAX_FCNT_GAP:
+        {
+            phyParam.Value = KR920_MAX_FCNT_GAP;
+            break;
+        }
+        case PHY_ACK_TIMEOUT:
+        {
+            phyParam.Value = ( KR920_ACKTIMEOUT + randr( -KR920_ACK_TIMEOUT_RND, KR920_ACK_TIMEOUT_RND ) );
+            break;
+        }
+        case PHY_DEF_DR1_OFFSET:
+        {
+            phyParam.Value = KR920_DEFAULT_RX1_DR_OFFSET;
+            break;
+        }
+        case PHY_DEF_RX2_FREQUENCY:
+        {
+            phyParam.Value = KR920_RX_WND_2_FREQ;
+            break;
+        }
+        case PHY_DEF_RX2_DR:
+        {
+            phyParam.Value = KR920_RX_WND_2_DR;
+            break;
+        }
+        case PHY_CHANNELS_MASK:
+        {
+            phyParam.ChannelsMask = ChannelsMask;
+            break;
+        }
+        case PHY_CHANNELS_DEFAULT_MASK:
+        {
+            phyParam.ChannelsMask = ChannelsDefaultMask;
+            break;
+        }
+        case PHY_MAX_NB_CHANNELS:
+        {
+            phyParam.Value = KR920_MAX_NB_CHANNELS;
+            break;
+        }
+        case PHY_CHANNELS:
+        {
+            phyParam.Channels = Channels;
+            break;
+        }
+        case PHY_DEF_UPLINK_DWELL_TIME:
+        case PHY_DEF_DOWNLINK_DWELL_TIME:
+        {
+            phyParam.Value = 0;
+            break;
+        }
+        case PHY_DEF_MAX_EIRP:
+        {
+            // We set the higher maximum EIRP as default value.
+            // The reason for this is, that the frequency may
+            // change during a channel selection for the next uplink.
+            // The value has to be recalculated in the TX configuration.
+            phyParam.fValue = KR920_DEFAULT_MAX_EIRP_HIGH;
+            break;
+        }
+        case PHY_DEF_ANTENNA_GAIN:
+        {
+            phyParam.fValue = KR920_DEFAULT_ANTENNA_GAIN;
+            break;
+        }
+        case PHY_NB_JOIN_TRIALS:
+        case PHY_DEF_NB_JOIN_TRIALS:
+        {
+            phyParam.Value = 48;
+            break;
+        }
+        case PHY_BEACON_INTERVAL:
+        {
+            phyParam.Value = KR920_BEACON_INTERVAL;
+            break;
+        }
+        case PHY_BEACON_RESERVED:
+        {
+            phyParam.Value = KR920_BEACON_RESERVED;
+            break;
+        }
+        case PHY_BEACON_GUARD:
+        {
+            phyParam.Value = KR920_BEACON_GUARD;
+            break;
+        }
+        case PHY_BEACON_WINDOW:
+        {
+            phyParam.Value = KR920_BEACON_WINDOW;
+            break;
+        }
+        case PHY_BEACON_WINDOW_SLOTS:
+        {
+            phyParam.Value = KR920_BEACON_WINDOW_SLOTS;
+            break;
+        }
+        case PHY_PING_SLOT_WINDOW:
+        {
+            phyParam.Value = KR920_PING_SLOT_WINDOW;
+            break;
+        }
+        case PHY_BEACON_SYMBOL_TO_DEFAULT:
+        {
+            phyParam.Value = KR920_BEACON_SYMBOL_TO_DEFAULT;
+            break;
+        }
+        case PHY_BEACON_SYMBOL_TO_EXPANSION_MAX:
+        {
+            phyParam.Value = KR920_BEACON_SYMBOL_TO_EXPANSION_MAX;
+            break;
+        }
+        case PHY_PING_SLOT_SYMBOL_TO_EXPANSION_MAX:
+        {
+            phyParam.Value = KR920_PING_SLOT_SYMBOL_TO_EXPANSION_MAX;
+            break;
+        }
+        case PHY_BEACON_SYMBOL_TO_EXPANSION_FACTOR:
+        {
+            phyParam.Value = KR920_BEACON_SYMBOL_TO_EXPANSION_FACTOR;
+            break;
+        }
+        case PHY_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR:
+        {
+            phyParam.Value = KR920_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR;
+            break;
+        }
+        case PHY_MAX_BEACON_LESS_PERIOD:
+        {
+            phyParam.Value = KR920_MAX_BEACON_LESS_PERIOD;
+            break;
+        }
+        case PHY_BEACON_DELAY_BEACON_TIMING_ANS:
+        {
+            phyParam.Value = KR920_BEACON_DELAY_BEACON_TIMING_ANS;
+            break;
+        }
+        case PHY_BEACON_CHANNEL_FREQ:
+        {
+            phyParam.Value = KR920_BEACON_CHANNEL_FREQ;
+            break;
+        }
+        case PHY_PINGSLOT_CHANNEL_FREQ:
+        {
+            phyParam.Value = KR920_BEACON_CHANNEL_FREQ;
+            break;
+        }
+        case PHY_BEACON_SIZE:
+        {
+            phyParam.Value = KR920_BEACON_SIZE;
+            break;
+        }
+        case PHY_BEACON_CHANNEL_DR:
+        {
+            phyParam.Value = KR920_BEACON_CHANNEL_DR;
+            break;
+        }
+        default:
+        {
+            break;
+        }
+    }
+
+    return phyParam;
+}
+
+void RegionKR920SetBandTxDone( SetBandTxDoneParams_t* txDone )
+{
+    RegionCommonSetBandTxDone( txDone->Joined, &Bands[Channels[txDone->Channel].Band], txDone->LastTxDoneTime );
+}
+
+void RegionKR920InitDefaults( InitType_t type )
+{
+    switch( type )
+    {
+        case INIT_TYPE_INIT:
+        {
+            // Channels
+            Channels[0] = ( ChannelParams_t ) KR920_LC1;
+            Channels[1] = ( ChannelParams_t ) KR920_LC2;
+            Channels[2] = ( ChannelParams_t ) KR920_LC3;
+
+            // Initialize the channels default mask
+            ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 );
+            // Update the channels mask
+            RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 1 );
+            break;
+        }
+        case INIT_TYPE_RESTORE:
+        {
+            // Restore channels default mask
+            ChannelsMask[0] |= ChannelsDefaultMask[0];
+            break;
+        }
+        default:
+        {
+            break;
+        }
+    }
+}
+
+bool RegionKR920Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute )
+{
+    switch( phyAttribute )
+    {
+        case PHY_TX_DR:
+        {
+            return RegionCommonValueInRange( verify->DatarateParams.Datarate, KR920_TX_MIN_DATARATE, KR920_TX_MAX_DATARATE );
+        }
+        case PHY_DEF_TX_DR:
+        {
+            return RegionCommonValueInRange( verify->DatarateParams.Datarate, DR_0, DR_5 );
+        }
+        case PHY_RX_DR:
+        {
+            return RegionCommonValueInRange( verify->DatarateParams.Datarate, KR920_RX_MIN_DATARATE, KR920_RX_MAX_DATARATE );
+        }
+        case PHY_DEF_TX_POWER:
+        case PHY_TX_POWER:
+        {
+            // Remark: switched min and max!
+            return RegionCommonValueInRange( verify->TxPower, KR920_MAX_TX_POWER, KR920_MIN_TX_POWER );
+        }
+        case PHY_DUTY_CYCLE:
+        {
+            return KR920_DUTY_CYCLE_ENABLED;
+        }
+        case PHY_NB_JOIN_TRIALS:
+        {
+            if( verify->NbJoinTrials < 48 )
+            {
+                return false;
+            }
+            break;
+        }
+        default:
+            return false;
+    }
+    return true;
+}
+
+void RegionKR920ApplyCFList( ApplyCFListParams_t* applyCFList )
+{
+    ChannelParams_t newChannel;
+    ChannelAddParams_t channelAdd;
+    ChannelRemoveParams_t channelRemove;
+
+    // Setup default datarate range
+    newChannel.DrRange.Value = ( DR_5 << 4 ) | DR_0;
+
+    // Size of the optional CF list
+    if( applyCFList->Size != 16 )
+    {
+        return;
+    }
+
+    // Last byte is RFU, don't take it into account
+    for( uint8_t i = 0, chanIdx = KR920_NUMB_DEFAULT_CHANNELS; chanIdx < KR920_MAX_NB_CHANNELS; i+=3, chanIdx++ )
+    {
+        if( chanIdx < ( KR920_NUMB_CHANNELS_CF_LIST + KR920_NUMB_DEFAULT_CHANNELS ) )
+        {
+            // Channel frequency
+            newChannel.Frequency = (uint32_t) applyCFList->Payload[i];
+            newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 1] << 8 );
+            newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 2] << 16 );
+            newChannel.Frequency *= 100;
+
+            // Initialize alternative frequency to 0
+            newChannel.Rx1Frequency = 0;
+        }
+        else
+        {
+            newChannel.Frequency = 0;
+            newChannel.DrRange.Value = 0;
+            newChannel.Rx1Frequency = 0;
+        }
+
+        if( newChannel.Frequency != 0 )
+        {
+            channelAdd.NewChannel = &newChannel;
+            channelAdd.ChannelId = chanIdx;
+
+            // Try to add all channels
+            RegionKR920ChannelAdd( &channelAdd );
+        }
+        else
+        {
+            channelRemove.ChannelId = chanIdx;
+
+            RegionKR920ChannelsRemove( &channelRemove );
+        }
+    }
+}
+
+bool RegionKR920ChanMaskSet( ChanMaskSetParams_t* chanMaskSet )
+{
+    switch( chanMaskSet->ChannelsMaskType )
+    {
+        case CHANNELS_MASK:
+        {
+            RegionCommonChanMaskCopy( ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 );
+            break;
+        }
+        case CHANNELS_DEFAULT_MASK:
+        {
+            RegionCommonChanMaskCopy( ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 );
+            break;
+        }
+        default:
+            return false;
+    }
+    return true;
+}
+
+bool RegionKR920AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter )
+{
+    bool adrAckReq = false;
+    int8_t datarate = adrNext->Datarate;
+    int8_t txPower = adrNext->TxPower;
+    GetPhyParams_t getPhy;
+    PhyParam_t phyParam;
+
+    // Report back the adr ack counter
+    *adrAckCounter = adrNext->AdrAckCounter;
+
+    if( adrNext->AdrEnabled == true )
+    {
+        if( datarate == KR920_TX_MIN_DATARATE )
+        {
+            *adrAckCounter = 0;
+            adrAckReq = false;
+        }
+        else
+        {
+            if( adrNext->AdrAckCounter >= KR920_ADR_ACK_LIMIT )
+            {
+                adrAckReq = true;
+                txPower = KR920_MAX_TX_POWER;
+            }
+            else
+            {
+                adrAckReq = false;
+            }
+            if( adrNext->AdrAckCounter >= ( KR920_ADR_ACK_LIMIT + KR920_ADR_ACK_DELAY ) )
+            {
+                if( ( adrNext->AdrAckCounter % KR920_ADR_ACK_DELAY ) == 1 )
+                {
+                    // Decrease the datarate
+                    getPhy.Attribute = PHY_NEXT_LOWER_TX_DR;
+                    getPhy.Datarate = datarate;
+                    getPhy.UplinkDwellTime = adrNext->UplinkDwellTime;
+                    phyParam = RegionKR920GetPhyParam( &getPhy );
+                    datarate = phyParam.Value;
+
+                    if( datarate == KR920_TX_MIN_DATARATE )
+                    {
+                        // We must set adrAckReq to false as soon as we reach the lowest datarate
+                        adrAckReq = false;
+                        if( adrNext->UpdateChanMask == true )
+                        {
+                            // Re-enable default channels
+                            ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 );
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    *drOut = datarate;
+    *txPowOut = txPower;
+    return adrAckReq;
+}
+
+void RegionKR920ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams )
+{
+    double tSymbol = 0.0;
+
+    // Get the datarate, perform a boundary check
+    rxConfigParams->Datarate = MIN( datarate, KR920_RX_MAX_DATARATE );
+    rxConfigParams->Bandwidth = GetBandwidth( rxConfigParams->Datarate );
+
+    tSymbol = RegionCommonComputeSymbolTimeLoRa( DataratesKR920[rxConfigParams->Datarate], BandwidthsKR920[rxConfigParams->Datarate] );
+
+    RegionCommonComputeRxWindowParameters( tSymbol, minRxSymbols, rxError, RADIO_WAKEUP_TIME, &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset );
+}
+
+bool RegionKR920RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate )
+{
+    int8_t dr = rxConfig->Datarate;
+    uint8_t maxPayload = 0;
+    int8_t phyDr = 0;
+    uint32_t frequency = rxConfig->Frequency;
+
+    if( Radio.GetStatus( ) != RF_IDLE )
+    {
+        return false;
+    }
+
+    if( rxConfig->Window == 0 )
+    {
+        // Apply window 1 frequency
+        frequency = Channels[rxConfig->Channel].Frequency;
+        // Apply the alternative RX 1 window frequency, if it is available
+        if( Channels[rxConfig->Channel].Rx1Frequency != 0 )
+        {
+            frequency = Channels[rxConfig->Channel].Rx1Frequency;
+        }
+    }
+
+    // Read the physical datarate from the datarates table
+    phyDr = DataratesKR920[dr];
+
+    Radio.SetChannel( frequency );
+
+    // Radio configuration
+    Radio.SetRxConfig( MODEM_LORA, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous );
+    maxPayload = MaxPayloadOfDatarateKR920[dr];
+    Radio.SetMaxPayloadLength( MODEM_LORA, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD );
+
+    *datarate = (uint8_t) dr;
+    return true;
+}
+
+bool RegionKR920TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir )
+{
+    int8_t phyDr = DataratesKR920[txConfig->Datarate];
+    int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, Bands[Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, ChannelsMask );
+    uint32_t bandwidth = GetBandwidth( txConfig->Datarate );
+    float maxEIRP = GetMaxEIRP( Channels[txConfig->Channel].Frequency );
+    int8_t phyTxPower = 0;
+
+    // Take the minimum between the maxEIRP and txConfig->MaxEirp.
+    // The value of txConfig->MaxEirp could have changed during runtime, e.g. due to a MAC command.
+    maxEIRP = MIN( txConfig->MaxEirp, maxEIRP );
+
+    // Calculate physical TX power
+    phyTxPower = RegionCommonComputeTxPower( txPowerLimited, maxEIRP, txConfig->AntennaGain );
+
+    // Setup the radio frequency
+    Radio.SetChannel( Channels[txConfig->Channel].Frequency );
+
+    Radio.SetTxConfig( MODEM_LORA, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 3000 );
+
+    // Setup maximum payload lenght of the radio driver
+    Radio.SetMaxPayloadLength( MODEM_LORA, txConfig->PktLen );
+    // Get the time-on-air of the next tx frame
+    *txTimeOnAir = Radio.TimeOnAir( MODEM_LORA, txConfig->PktLen );
+
+    *txPower = txPowerLimited;
+    return true;
+}
+
+uint8_t RegionKR920LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed )
+{
+    uint8_t status = 0x07;
+    RegionCommonLinkAdrParams_t linkAdrParams;
+    uint8_t nextIndex = 0;
+    uint8_t bytesProcessed = 0;
+    uint16_t chMask = 0;
+    GetPhyParams_t getPhy;
+    PhyParam_t phyParam;
+    RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams;
+
+    while( bytesProcessed < linkAdrReq->PayloadSize )
+    {
+        // Get ADR request parameters
+        nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams );
+
+        if( nextIndex == 0 )
+            break; // break loop, since no more request has been found
+
+        // Update bytes processed
+        bytesProcessed += nextIndex;
+
+        // Revert status, as we only check the last ADR request for the channel mask KO
+        status = 0x07;
+
+        // Setup temporary channels mask
+        chMask = linkAdrParams.ChMask;
+
+        // Verify channels mask
+        if( ( linkAdrParams.ChMaskCtrl == 0 ) && ( chMask == 0 ) )
+        {
+            status &= 0xFE; // Channel mask KO
+        }
+        else if( ( ( linkAdrParams.ChMaskCtrl >= 1 ) && ( linkAdrParams.ChMaskCtrl <= 5 )) ||
+                ( linkAdrParams.ChMaskCtrl >= 7 ) )
+        {
+            // RFU
+            status &= 0xFE; // Channel mask KO
+        }
+        else
+        {
+            for( uint8_t i = 0; i < KR920_MAX_NB_CHANNELS; i++ )
+            {
+                if( linkAdrParams.ChMaskCtrl == 6 )
+                {
+                    if( Channels[i].Frequency != 0 )
+                    {
+                        chMask |= 1 << i;
+                    }
+                }
+                else
+                {
+                    if( ( ( chMask & ( 1 << i ) ) != 0 ) &&
+                        ( Channels[i].Frequency == 0 ) )
+                    {// Trying to enable an undefined channel
+                        status &= 0xFE; // Channel mask KO
+                    }
+                }
+            }
+        }
+    }
+
+    // Get the minimum possible datarate
+    getPhy.Attribute = PHY_MIN_TX_DR;
+    getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime;
+    phyParam = RegionKR920GetPhyParam( &getPhy );
+
+    linkAdrVerifyParams.Status = status;
+    linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled;
+    linkAdrVerifyParams.Datarate = linkAdrParams.Datarate;
+    linkAdrVerifyParams.TxPower = linkAdrParams.TxPower;
+    linkAdrVerifyParams.NbRep = linkAdrParams.NbRep;
+    linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate;
+    linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower;
+    linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep;
+    linkAdrVerifyParams.NbChannels = KR920_MAX_NB_CHANNELS;
+    linkAdrVerifyParams.ChannelsMask = &chMask;
+    linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value;
+    linkAdrVerifyParams.MaxDatarate = KR920_TX_MAX_DATARATE;
+    linkAdrVerifyParams.Channels = Channels;
+    linkAdrVerifyParams.MinTxPower = KR920_MIN_TX_POWER;
+    linkAdrVerifyParams.MaxTxPower = KR920_MAX_TX_POWER;
+
+    // Verify the parameters and update, if necessary
+    status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep );
+
+    // Update channelsMask if everything is correct
+    if( status == 0x07 )
+    {
+        // Set the channels mask to a default value
+        memset( ChannelsMask, 0, sizeof( ChannelsMask ) );
+        // Update the channels mask
+        ChannelsMask[0] = chMask;
+    }
+
+    // Update status variables
+    *drOut = linkAdrParams.Datarate;
+    *txPowOut = linkAdrParams.TxPower;
+    *nbRepOut = linkAdrParams.NbRep;
+    *nbBytesParsed = bytesProcessed;
+
+    return status;
+}
+
+uint8_t RegionKR920RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq )
+{
+    uint8_t status = 0x07;
+
+    // Verify radio frequency
+    if( Radio.CheckRfFrequency( rxParamSetupReq->Frequency ) == false )
+    {
+        status &= 0xFE; // Channel frequency KO
+    }
+
+    // Verify datarate
+    if( RegionCommonValueInRange( rxParamSetupReq->Datarate, KR920_RX_MIN_DATARATE, KR920_RX_MAX_DATARATE ) == false )
+    {
+        status &= 0xFD; // Datarate KO
+    }
+
+    // Verify datarate offset
+    if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, KR920_MIN_RX1_DR_OFFSET, KR920_MAX_RX1_DR_OFFSET ) == false )
+    {
+        status &= 0xFB; // Rx1DrOffset range KO
+    }
+
+    return status;
+}
+
+uint8_t RegionKR920NewChannelReq( NewChannelReqParams_t* newChannelReq )
+{
+    uint8_t status = 0x03;
+    ChannelAddParams_t channelAdd;
+    ChannelRemoveParams_t channelRemove;
+
+    if( newChannelReq->NewChannel->Frequency == 0 )
+    {
+        channelRemove.ChannelId = newChannelReq->ChannelId;
+
+        // Remove
+        if( RegionKR920ChannelsRemove( &channelRemove ) == false )
+        {
+            status &= 0xFC;
+        }
+    }
+    else
+    {
+        channelAdd.NewChannel = newChannelReq->NewChannel;
+        channelAdd.ChannelId = newChannelReq->ChannelId;
+
+        switch( RegionKR920ChannelAdd( &channelAdd ) )
+        {
+            case LORAMAC_STATUS_OK:
+            {
+                break;
+            }
+            case LORAMAC_STATUS_FREQUENCY_INVALID:
+            {
+                status &= 0xFE;
+                break;
+            }
+            case LORAMAC_STATUS_DATARATE_INVALID:
+            {
+                status &= 0xFD;
+                break;
+            }
+            case LORAMAC_STATUS_FREQ_AND_DR_INVALID:
+            {
+                status &= 0xFC;
+                break;
+            }
+            default:
+            {
+                status &= 0xFC;
+                break;
+            }
+        }
+    }
+
+    return status;
+}
+
+int8_t RegionKR920TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq )
+{
+    return -1;
+}
+
+uint8_t RegionKR920DlChannelReq( DlChannelReqParams_t* dlChannelReq )
+{
+    uint8_t status = 0x03;
+
+    // Verify if the frequency is supported
+    if( VerifyTxFreq( dlChannelReq->Rx1Frequency ) == false )
+    {
+        status &= 0xFE;
+    }
+
+    // Verify if an uplink frequency exists
+    if( Channels[dlChannelReq->ChannelId].Frequency == 0 )
+    {
+        status &= 0xFD;
+    }
+
+    // Apply Rx1 frequency, if the status is OK
+    if( status == 0x03 )
+    {
+        Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency;
+    }
+
+    return status;
+}
+
+int8_t RegionKR920AlternateDr( AlternateDrParams_t* alternateDr )
+{
+    int8_t datarate = 0;
+
+    if( ( alternateDr->NbTrials % 48 ) == 0 )
+    {
+        datarate = DR_0;
+    }
+    else if( ( alternateDr->NbTrials % 32 ) == 0 )
+    {
+        datarate = DR_1;
+    }
+    else if( ( alternateDr->NbTrials % 24 ) == 0 )
+    {
+        datarate = DR_2;
+    }
+    else if( ( alternateDr->NbTrials % 16 ) == 0 )
+    {
+        datarate = DR_3;
+    }
+    else if( ( alternateDr->NbTrials % 8 ) == 0 )
+    {
+        datarate = DR_4;
+    }
+    else
+    {
+        datarate = DR_5;
+    }
+    return datarate;
+}
+
+void RegionKR920CalcBackOff( CalcBackOffParams_t* calcBackOff )
+{
+    RegionCommonCalcBackOffParams_t calcBackOffParams;
+
+    calcBackOffParams.Channels = Channels;
+    calcBackOffParams.Bands = Bands;
+    calcBackOffParams.LastTxIsJoinRequest = calcBackOff->LastTxIsJoinRequest;
+    calcBackOffParams.Joined = calcBackOff->Joined;
+    calcBackOffParams.DutyCycleEnabled = calcBackOff->DutyCycleEnabled;
+    calcBackOffParams.Channel = calcBackOff->Channel;
+    calcBackOffParams.ElapsedTime = calcBackOff->ElapsedTime;
+    calcBackOffParams.TxTimeOnAir = calcBackOff->TxTimeOnAir;
+
+    RegionCommonCalcBackOff( &calcBackOffParams );
+}
+
+bool RegionKR920NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff )
+{
+    uint8_t channelNext = 0;
+    uint8_t nbEnabledChannels = 0;
+    uint8_t delayTx = 0;
+    uint8_t enabledChannels[KR920_MAX_NB_CHANNELS] = { 0 };
+    TimerTime_t nextTxDelay = 0;
+
+    if( RegionCommonCountChannels( ChannelsMask, 0, 1 ) == 0 )
+    { // Reactivate default channels
+        ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 );
+    }
+
+    if( nextChanParams->AggrTimeOff <= TimerGetElapsedTime( nextChanParams->LastAggrTx ) )
+    {
+        // Reset Aggregated time off
+        *aggregatedTimeOff = 0;
+
+        // Update bands Time OFF
+        nextTxDelay = RegionCommonUpdateBandTimeOff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, Bands, KR920_MAX_NB_BANDS );
+
+        // Search how many channels are enabled
+        nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Joined, nextChanParams->Datarate,
+                                                      ChannelsMask, Channels,
+                                                      Bands, enabledChannels, &delayTx );
+    }
+    else
+    {
+        delayTx++;
+        nextTxDelay = nextChanParams->AggrTimeOff - TimerGetElapsedTime( nextChanParams->LastAggrTx );
+    }
+
+    if( nbEnabledChannels > 0 )
+    {
+        for( uint8_t  i = 0, j = randr( 0, nbEnabledChannels - 1 ); i < KR920_MAX_NB_CHANNELS; i++ )
+        {
+            channelNext = enabledChannels[j];
+            j = ( j + 1 ) % nbEnabledChannels;
+
+            // Perform carrier sense for KR920_CARRIER_SENSE_TIME
+            // If the channel is free, we can stop the LBT mechanism
+            if( Radio.IsChannelFree( MODEM_LORA, Channels[channelNext].Frequency, KR920_RSSI_FREE_TH, KR920_CARRIER_SENSE_TIME ) == true )
+            {
+                // Free channel found
+                *channel = channelNext;
+                *time = 0;
+                return true;
+            }
+        }
+        return false;
+    }
+    else
+    {
+        if( delayTx > 0 )
+        {
+            // Delay transmission due to AggregatedTimeOff or to a band time off
+            *time = nextTxDelay;
+            return true;
+        }
+        // Datarate not supported by any channel, restore defaults
+        ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 );
+        *time = 0;
+        return false;
+    }
+}
+
+LoRaMacStatus_t RegionKR920ChannelAdd( ChannelAddParams_t* channelAdd )
+{
+    uint8_t band = 0;
+    bool drInvalid = false;
+    bool freqInvalid = false;
+    uint8_t id = channelAdd->ChannelId;
+
+    if( id >= KR920_MAX_NB_CHANNELS )
+    {
+        return LORAMAC_STATUS_PARAMETER_INVALID;
+    }
+
+    // Validate the datarate range
+    if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Min, KR920_TX_MIN_DATARATE, KR920_TX_MAX_DATARATE ) == false )
+    {
+        drInvalid = true;
+    }
+    if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, KR920_TX_MIN_DATARATE, KR920_TX_MAX_DATARATE ) == false )
+    {
+        drInvalid = true;
+    }
+    if( channelAdd->NewChannel->DrRange.Fields.Min > channelAdd->NewChannel->DrRange.Fields.Max )
+    {
+        drInvalid = true;
+    }
+
+    // Default channels don't accept all values
+    if( id < KR920_NUMB_DEFAULT_CHANNELS )
+    {
+        // All datarates are supported
+        // We are not allowed to change the frequency
+        if( channelAdd->NewChannel->Frequency != Channels[id].Frequency )
+        {
+            freqInvalid = true;
+        }
+    }
+
+    // Check frequency
+    if( freqInvalid == false )
+    {
+        if( VerifyTxFreq( channelAdd->NewChannel->Frequency ) == false )
+        {
+            freqInvalid = true;
+        }
+    }
+
+    // Check status
+    if( ( drInvalid == true ) && ( freqInvalid == true ) )
+    {
+        return LORAMAC_STATUS_FREQ_AND_DR_INVALID;
+    }
+    if( drInvalid == true )
+    {
+        return LORAMAC_STATUS_DATARATE_INVALID;
+    }
+    if( freqInvalid == true )
+    {
+        return LORAMAC_STATUS_FREQUENCY_INVALID;
+    }
+
+    memcpy( &(Channels[id]), channelAdd->NewChannel, sizeof( Channels[id] ) );
+    Channels[id].Band = band;
+    ChannelsMask[0] |= ( 1 << id );
+    return LORAMAC_STATUS_OK;
+}
+
+bool RegionKR920ChannelsRemove( ChannelRemoveParams_t* channelRemove  )
+{
+    uint8_t id = channelRemove->ChannelId;
+
+    if( id < KR920_NUMB_DEFAULT_CHANNELS )
+    {
+        return false;
+    }
+
+    // Remove the channel from the list of channels
+    Channels[id] = ( ChannelParams_t ){ 0, 0, { 0 }, 0 };
+
+    return RegionCommonChanDisable( ChannelsMask, id, KR920_MAX_NB_CHANNELS );
+}
+
+void RegionKR920SetContinuousWave( ContinuousWaveParams_t* continuousWave )
+{
+    int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, Bands[Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, ChannelsMask );
+    float maxEIRP = GetMaxEIRP( Channels[continuousWave->Channel].Frequency );
+    int8_t phyTxPower = 0;
+    uint32_t frequency = Channels[continuousWave->Channel].Frequency;
+
+    // Take the minimum between the maxEIRP and continuousWave->MaxEirp.
+    // The value of continuousWave->MaxEirp could have changed during runtime, e.g. due to a MAC command.
+    maxEIRP = MIN( continuousWave->MaxEirp, maxEIRP );
+
+    // Calculate physical TX power
+    phyTxPower = RegionCommonComputeTxPower( txPowerLimited, maxEIRP, continuousWave->AntennaGain );
+
+    Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout );
+}
+
+uint8_t RegionKR920ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset )
+{
+    int8_t datarate = dr - drOffset;
+
+    if( datarate < 0 )
+    {
+        datarate = DR_0;
+    }
+    return datarate;
+}
+
+void RegionKR920RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr, bool *beaconChannelSet )
+{
+    RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup;
+
+    regionCommonRxBeaconSetup.Datarates = DataratesKR920;
+    regionCommonRxBeaconSetup.ChannelPlanFrequency = KR920_BEACON_CHANNEL_FREQ;
+    regionCommonRxBeaconSetup.BeaconTimingAnsFrequency = KR920_BEACON_CHANNEL_FREQ;
+    regionCommonRxBeaconSetup.BeaconSize = KR920_BEACON_SIZE;
+    regionCommonRxBeaconSetup.BeaconDatarate = KR920_BEACON_CHANNEL_DR;
+    regionCommonRxBeaconSetup.BeaconChannelBW = KR920_BEACON_CHANNEL_BW;
+    regionCommonRxBeaconSetup.CustomFrequency = rxBeaconSetup->CustomFrequency;
+    regionCommonRxBeaconSetup.CustomFrequencyEnabled = rxBeaconSetup->CustomFrequencyEnabled;
+    regionCommonRxBeaconSetup.BeaconChannelSet = rxBeaconSetup->BeaconChannelSet;
+    regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime;
+    regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout;
+
+    RegionCommonRxBeaconSetup( &regionCommonRxBeaconSetup, beaconChannelSet );
+
+    // Store downlink datarate
+    *outDr = KR920_BEACON_CHANNEL_DR;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/region/RegionKR920.h	Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,568 @@
+/*!
+ * \file      RegionKR920.h
+ *
+ * \brief     Region definition for KR920
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ *                ______                              _
+ *               / _____)             _              | |
+ *              ( (____  _____ ____ _| |_ _____  ____| |__
+ *               \____ \| ___ |    (_   _) ___ |/ ___)  _ \
+ *               _____) ) ____| | | || |_| ____( (___| | | |
+ *              (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ *              (C)2013 Semtech
+ *
+ *               ___ _____ _   ___ _  _____ ___  ___  ___ ___
+ *              / __|_   _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ *              \__ \ | |/ _ \ (__| ' <| _| (_) |   / (__| _|
+ *              |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ *              embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author    Miguel Luis ( Semtech )
+ *
+ * \author    Gregory Cristian ( Semtech )
+ *
+ * \author    Daniel Jaeckle ( STACKFORCE )
+ *
+ * \defgroup  REGIONKR920 Region KR920
+ *            Implementation according to LoRaWAN Specification v1.0.2.
+ * \{
+ */
+#ifndef __REGION_KR920_H__
+#define __REGION_KR920_H__
+
+/*!
+ * LoRaMac maximum number of channels
+ */
+#define KR920_MAX_NB_CHANNELS                       16
+
+/*!
+ * Number of default channels
+ */
+#define KR920_NUMB_DEFAULT_CHANNELS                 3
+
+/*!
+ * Number of channels to apply for the CF list
+ */
+#define KR920_NUMB_CHANNELS_CF_LIST                 5
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define KR920_TX_MIN_DATARATE                       DR_0
+
+/*!
+ * Maximal datarate that can be used by the node
+ */
+#define KR920_TX_MAX_DATARATE                       DR_5
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define KR920_RX_MIN_DATARATE                       DR_0
+
+/*!
+ * Maximal datarate that can be used by the node
+ */
+#define KR920_RX_MAX_DATARATE                       DR_5
+
+/*!
+ * Default datarate used by the node
+ */
+#define KR920_DEFAULT_DATARATE                      DR_0
+
+/*!
+ * Minimal Rx1 receive datarate offset
+ */
+#define KR920_MIN_RX1_DR_OFFSET                     0
+
+/*!
+ * Maximal Rx1 receive datarate offset
+ */
+#define KR920_MAX_RX1_DR_OFFSET                     5
+
+/*!
+ * Default Rx1 receive datarate offset
+ */
+#define KR920_DEFAULT_RX1_DR_OFFSET                 0
+
+/*!
+ * Minimal Tx output power that can be used by the node
+ */
+#define KR920_MIN_TX_POWER                          TX_POWER_7
+
+/*!
+ * Maximal Tx output power that can be used by the node
+ */
+#define KR920_MAX_TX_POWER                          TX_POWER_0
+
+/*!
+ * Default Tx output power used by the node
+ */
+#define KR920_DEFAULT_TX_POWER                      TX_POWER_0
+
+/*!
+ * Default Max EIRP for frequency 920.9 MHz - 921.9 MHz
+ */
+#define KR920_DEFAULT_MAX_EIRP_LOW                  10.0f
+
+/*!
+ * Default Max EIRP for frequency 922.1 MHz - 923.3 MHz
+ */
+#define KR920_DEFAULT_MAX_EIRP_HIGH                 14.0f
+
+/*!
+ * Default antenna gain
+ */
+#define KR920_DEFAULT_ANTENNA_GAIN                  2.15f
+
+/*!
+ * ADR Ack limit
+ */
+#define KR920_ADR_ACK_LIMIT                         64
+
+/*!
+ * ADR Ack delay
+ */
+#define KR920_ADR_ACK_DELAY                         32
+
+/*!
+ * Enabled or disabled the duty cycle
+ */
+#define KR920_DUTY_CYCLE_ENABLED                    0
+
+/*!
+ * Maximum RX window duration
+ */
+#define KR920_MAX_RX_WINDOW                         4000
+
+/*!
+ * Receive delay 1
+ */
+#define KR920_RECEIVE_DELAY1                        1000
+
+/*!
+ * Receive delay 2
+ */
+#define KR920_RECEIVE_DELAY2                        2000
+
+/*!
+ * Join accept delay 1
+ */
+#define KR920_JOIN_ACCEPT_DELAY1                    5000
+
+/*!
+ * Join accept delay 2
+ */
+#define KR920_JOIN_ACCEPT_DELAY2                    6000
+
+/*!
+ * Maximum frame counter gap
+ */
+#define KR920_MAX_FCNT_GAP                          16384
+
+/*!
+ * Ack timeout
+ */
+#define KR920_ACKTIMEOUT                            2000
+
+/*!
+ * Random ack timeout limits
+ */
+#define KR920_ACK_TIMEOUT_RND                       1000
+
+#if ( KR920_DEFAULT_DATARATE > DR_5 )
+#error "A default DR higher than DR_5 may lead to connectivity loss."
+#endif
+
+/*!
+ * Second reception window channel frequency definition.
+ */
+#define KR920_RX_WND_2_FREQ                         921900000
+
+/*!
+ * Second reception window channel datarate definition.
+ */
+#define KR920_RX_WND_2_DR                           DR_0
+
+/*
+ * CLASS B
+ */
+/*!
+ * Beacon interval in ms
+ */
+#define KR920_BEACON_INTERVAL                       128000
+
+/*!
+ * Beacon reserved time in ms
+ */
+#define KR920_BEACON_RESERVED                       2120
+
+/*!
+ * Beacon guard time in ms
+ */
+#define KR920_BEACON_GUARD                          3000
+
+/*!
+ * Beacon window time in ms
+ */
+#define KR920_BEACON_WINDOW                         122880
+
+/*!
+ * Beacon window time in numer of slots
+ */
+#define KR920_BEACON_WINDOW_SLOTS                   4096
+
+/*!
+ * Ping slot length time in ms
+ */
+#define KR920_PING_SLOT_WINDOW                      30
+
+/*!
+ * Default symbol timeout for beacons and ping slot windows
+ */
+#define KR920_BEACON_SYMBOL_TO_DEFAULT              8
+
+/*!
+ * Maximum symbol timeout for beacons
+ */
+#define KR920_BEACON_SYMBOL_TO_EXPANSION_MAX        400
+
+/*!
+ * Maximum symbol timeout for ping slots
+ */
+#define KR920_PING_SLOT_SYMBOL_TO_EXPANSION_MAX     40
+
+/*!
+ * Symbol expansion value for beacon windows in case of beacon
+ * loss in symbols
+ */
+#define KR920_BEACON_SYMBOL_TO_EXPANSION_FACTOR     2
+
+/*!
+ * Symbol expansion value for ping slot windows in case of beacon
+ * loss in symbols
+ */
+#define KR920_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR  2
+
+/*!
+ * Maximum allowed beacon less time in ms
+ */
+#define KR920_MAX_BEACON_LESS_PERIOD                7200000
+
+/*!
+ * Delay time for the BeaconTimingAns in ms
+ */
+#define KR920_BEACON_DELAY_BEACON_TIMING_ANS        30
+
+/*!
+ * Beacon frequency
+ */
+#define KR920_BEACON_CHANNEL_FREQ                   923100000
+
+/*!
+ * Payload size of a beacon frame
+ */
+#define KR920_BEACON_SIZE                           17
+
+/*!
+ * Datarate of the beacon channel
+ */
+#define KR920_BEACON_CHANNEL_DR                     DR_3
+
+/*!
+ * Bandwith of the beacon channel
+ */
+#define KR920_BEACON_CHANNEL_BW                     0
+
+/*!
+ * Maximum number of bands
+ */
+#define KR920_MAX_NB_BANDS                          1
+
+/*!
+ * Band 0 definition
+ * { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff }
+ */
+#define KR920_BAND0                                 { 1 , KR920_MAX_TX_POWER, 0,  0 } //  100.0 %
+
+/*!
+ * LoRaMac default channel 1
+ * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
+ */
+#define KR920_LC1                                   { 922100000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
+
+/*!
+ * LoRaMac default channel 2
+ * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
+ */
+#define KR920_LC2                                   { 922300000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
+
+/*!
+ * LoRaMac default channel 3
+ * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
+ */
+#define KR920_LC3                                   { 922500000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
+
+/*!
+ * LoRaMac channels which are allowed for the join procedure
+ */
+#define KR920_JOIN_CHANNELS                         ( uint16_t )( LC( 1 ) | LC( 2 ) | LC( 3 ) )
+
+/*!
+ * RSSI threshold for a free channel [dBm]
+ */
+#define KR920_RSSI_FREE_TH                          -65
+
+/*!
+ * Specifies the time the node performs a carrier sense
+ */
+#define KR920_CARRIER_SENSE_TIME                    6
+
+/*!
+ * Data rates table definition
+ */
+static const uint8_t DataratesKR920[]  = { 12, 11, 10,  9,  8,  7 };
+
+/*!
+ * Bandwidths table definition in Hz
+ */
+static const uint32_t BandwidthsKR920[] = { 125000, 125000, 125000, 125000, 125000, 125000 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Can operate with and without a repeater.
+ */
+static const uint8_t MaxPayloadOfDatarateKR920[] = { 51, 51, 51, 115, 242, 242 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Can operate with repeater.
+ */
+static const uint8_t MaxPayloadOfDatarateRepeaterKR920[] = { 51, 51, 51, 115, 222, 222 };
+
+/*!
+ * \brief The function gets a value of a specific phy attribute.
+ *
+ * \param [IN] getPhy Pointer to the function parameters.
+ *
+ * \retval Returns a structure containing the PHY parameter.
+ */
+PhyParam_t RegionKR920GetPhyParam( GetPhyParams_t* getPhy );
+
+/*!
+ * \brief Updates the last TX done parameters of the current channel.
+ *
+ * \param [IN] txDone Pointer to the function parameters.
+ */
+void RegionKR920SetBandTxDone( SetBandTxDoneParams_t* txDone );
+
+/*!
+ * \brief Initializes the channels masks and the channels.
+ *
+ * \param [IN] type Sets the initialization type.
+ */
+void RegionKR920InitDefaults( InitType_t type );
+
+/*!
+ * \brief Verifies a parameter.
+ *
+ * \param [IN] verify Pointer to the function parameters.
+ *
+ * \param [IN] type Sets the initialization type.
+ *
+ * \retval Returns true, if the parameter is valid.
+ */
+bool RegionKR920Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute );
+
+/*!
+ * \brief The function parses the input buffer and sets up the channels of the
+ *        CF list.
+ *
+ * \param [IN] applyCFList Pointer to the function parameters.
+ */
+void RegionKR920ApplyCFList( ApplyCFListParams_t* applyCFList );
+
+/*!
+ * \brief Sets a channels mask.
+ *
+ * \param [IN] chanMaskSet Pointer to the function parameters.
+ *
+ * \retval Returns true, if the channels mask could be set.
+ */
+bool RegionKR920ChanMaskSet( ChanMaskSetParams_t* chanMaskSet );
+
+/*!
+ * \brief Calculates the next datarate to set, when ADR is on or off.
+ *
+ * \param [IN] adrNext Pointer to the function parameters.
+ *
+ * \param [OUT] drOut The calculated datarate for the next TX.
+ *
+ * \param [OUT] txPowOut The TX power for the next TX.
+ *
+ * \param [OUT] adrAckCounter The calculated ADR acknowledgement counter.
+ *
+ * \retval Returns true, if an ADR request should be performed.
+ */
+bool RegionKR920AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter );
+
+/*!
+ * Computes the Rx window timeout and offset.
+ *
+ * \param [IN] datarate     Rx window datarate index to be used
+ *
+ * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame.
+ *
+ * \param [IN] rxError      System maximum timing error of the receiver. In milliseconds
+ *                          The receiver will turn on in a [-rxError : +rxError] ms
+ *                          interval around RxOffset
+ *
+ * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields.
+ */
+void RegionKR920ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams );
+
+/*!
+ * \brief Configuration of the RX windows.
+ *
+ * \param [IN] rxConfig Pointer to the function parameters.
+ *
+ * \param [OUT] datarate The datarate index which was set.
+ *
+ * \retval Returns true, if the configuration was applied successfully.
+ */
+bool RegionKR920RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate );
+
+/*!
+ * \brief TX configuration.
+ *
+ * \param [IN] txConfig Pointer to the function parameters.
+ *
+ * \param [OUT] txPower The tx power index which was set.
+ *
+ * \param [OUT] txTimeOnAir The time-on-air of the frame.
+ *
+ * \retval Returns true, if the configuration was applied successfully.
+ */
+bool RegionKR920TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir );
+
+/*!
+ * \brief The function processes a Link ADR Request.
+ *
+ * \param [IN] linkAdrReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionKR920LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed );
+
+/*!
+ * \brief The function processes a RX Parameter Setup Request.
+ *
+ * \param [IN] rxParamSetupReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionKR920RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq );
+
+/*!
+ * \brief The function processes a Channel Request.
+ *
+ * \param [IN] newChannelReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionKR920NewChannelReq( NewChannelReqParams_t* newChannelReq );
+
+/*!
+ * \brief The function processes a TX ParamSetup Request.
+ *
+ * \param [IN] txParamSetupReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ *         Returns -1, if the functionality is not implemented. In this case, the end node
+ *         shall not process the command.
+ */
+int8_t RegionKR920TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq );
+
+/*!
+ * \brief The function processes a DlChannel Request.
+ *
+ * \param [IN] dlChannelReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionKR920DlChannelReq( DlChannelReqParams_t* dlChannelReq );
+
+/*!
+ * \brief Alternates the datarate of the channel for the join request.
+ *
+ * \param [IN] alternateDr Pointer to the function parameters.
+ *
+ * \retval Datarate to apply.
+ */
+int8_t RegionKR920AlternateDr( AlternateDrParams_t* alternateDr );
+
+/*!
+ * \brief Calculates the back-off time.
+ *
+ * \param [IN] calcBackOff Pointer to the function parameters.
+ */
+void RegionKR920CalcBackOff( CalcBackOffParams_t* calcBackOff );
+
+/*!
+ * \brief Searches and set the next random available channel
+ *
+ * \param [OUT] channel Next channel to use for TX.
+ *
+ * \param [OUT] time Time to wait for the next transmission according to the duty
+ *              cycle.
+ *
+ * \param [OUT] aggregatedTimeOff Updates the aggregated time off.
+ *
+ * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate]
+ */
+bool RegionKR920NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff );
+
+/*!
+ * \brief Adds a channel.
+ *
+ * \param [IN] channelAdd Pointer to the function parameters.
+ *
+ * \retval Status of the operation.
+ */
+LoRaMacStatus_t RegionKR920ChannelAdd( ChannelAddParams_t* channelAdd );
+
+/*!
+ * \brief Removes a channel.
+ *
+ * \param [IN] channelRemove Pointer to the function parameters.
+ *
+ * \retval Returns true, if the channel was removed successfully.
+ */
+bool RegionKR920ChannelsRemove( ChannelRemoveParams_t* channelRemove  );
+
+/*!
+ * \brief Sets the radio into continuous wave mode.
+ *
+ * \param [IN] continuousWave Pointer to the function parameters.
+ */
+void RegionKR920SetContinuousWave( ContinuousWaveParams_t* continuousWave );
+
+/*!
+ * \brief Computes new datarate according to the given offset
+ *
+ * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms
+ *
+ * \param [IN] dr Current datarate
+ *
+ * \param [IN] drOffset Offset to be applied
+ *
+ * \retval newDr Computed datarate.
+ */
+uint8_t RegionKR920ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset );
+
+/*! \} defgroup REGIONKR920 */
+
+#endif // __REGION_KR920_H__
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/region/RegionUS915-Hybrid.cpp	Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,958 @@
+/*
+ / _____)             _              | |
+( (____  _____ ____ _| |_ _____  ____| |__
+ \____ \| ___ |    (_   _) ___ |/ ___)  _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+    (C)2013 Semtech
+ ___ _____ _   ___ _  _____ ___  ___  ___ ___
+/ __|_   _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+\__ \ | |/ _ \ (__| ' <| _| (_) |   / (__| _|
+|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+embedded.connectivity.solutions===============
+
+Description: LoRa MAC region US915 Hybrid implementation
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+
+Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE )
+*/
+#include <stdbool.h>
+#include <string.h>
+#include <stdint.h>
+#include <math.h>
+
+#include "board.h"
+#include "LoRaMac.h"
+
+#include "utilities.h"
+
+#include "Region.h"
+#include "RegionCommon.h"
+#include "RegionUS915-Hybrid.h"
+
+// Definitions
+#define CHANNELS_MASK_SIZE              6
+
+// Global attributes
+/*!
+ * LoRaMAC channels
+ */
+static ChannelParams_t Channels[US915_HYBRID_MAX_NB_CHANNELS];
+
+/*!
+ * LoRaMac bands
+ */
+static Band_t Bands[US915_HYBRID_MAX_NB_BANDS] =
+{
+    US915_HYBRID_BAND0
+};
+
+/*!
+ * LoRaMac channels mask
+ */
+static uint16_t ChannelsMask[CHANNELS_MASK_SIZE];
+
+/*!
+ * LoRaMac channels remaining
+ */
+static uint16_t ChannelsMaskRemaining[CHANNELS_MASK_SIZE];
+
+/*!
+ * LoRaMac channels default mask
+ */
+static uint16_t ChannelsDefaultMask[CHANNELS_MASK_SIZE];
+
+// Static functions
+static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr )
+{
+    uint8_t nextLowerDr = 0;
+
+    if( dr == minDr )
+    {
+        nextLowerDr = minDr;
+    }
+    else
+    {
+        nextLowerDr = dr - 1;
+    }
+    return nextLowerDr;
+}
+
+static uint32_t GetBandwidth( uint32_t drIndex )
+{
+    switch( BandwidthsUS915_HYBRID[drIndex] )
+    {
+        default:
+        case 125000:
+            return 0;
+        case 250000:
+            return 1;
+        case 500000:
+            return 2;
+    }
+}
+
+static void ReenableChannels( uint16_t mask, uint16_t* channelsMask )
+{
+    uint16_t blockMask = mask;
+
+    for( uint8_t i = 0, j = 0; i < 4; i++, j += 2 )
+    {
+        channelsMask[i] = 0;
+        if( ( blockMask & ( 1 << j ) ) != 0 )
+        {
+            channelsMask[i] |= 0x00FF;
+        }
+        if( ( blockMask & ( 1 << ( j + 1 ) ) ) != 0 )
+        {
+            channelsMask[i] |= 0xFF00;
+        }
+    }
+    channelsMask[4] = blockMask;
+    channelsMask[5] = 0x0000;
+}
+
+static uint8_t CountBits( uint16_t mask, uint8_t nbBits )
+{
+    uint8_t nbActiveBits = 0;
+
+    for( uint8_t j = 0; j < nbBits; j++ )
+    {
+        if( ( mask & ( 1 << j ) ) == ( 1 << j ) )
+        {
+            nbActiveBits++;
+        }
+    }
+    return nbActiveBits;
+}
+
+static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask )
+{
+    int8_t txPowerResult = txPower;
+
+    // Limit tx power to the band max
+    txPowerResult =  MAX( txPower, maxBandTxPower );
+
+    if( datarate == DR_4 )
+    {// Limit tx power to max 26dBm
+        txPowerResult = MAX( txPower, TX_POWER_2 );
+    }
+    else
+    {
+        if( RegionCommonCountChannels( channelsMask, 0, 4 ) < 50 )
+        {// Limit tx power to max 21dBm
+            txPowerResult = MAX( txPower, TX_POWER_5 );
+        }
+    }
+    return txPowerResult;
+}
+
+static bool ValidateChannelsMask( uint16_t* channelsMask )
+{
+    bool chanMaskState = false;
+    uint16_t block1 = 0;
+    uint16_t block2 = 0;
+    uint8_t index = 0;
+    uint16_t channelsMaskCpy[6];
+
+    // Copy channels mask to not change the input
+    for( uint8_t i = 0; i < 4; i++ )
+    {
+        channelsMaskCpy[i] = channelsMask[i];
+    }
+
+    for( uint8_t i = 0; i < 4; i++ )
+    {
+        block1 = channelsMaskCpy[i] & 0x00FF;
+        block2 = channelsMaskCpy[i] & 0xFF00;
+
+        if( CountBits( block1, 16 ) > 5 )
+        {
+            channelsMaskCpy[i] &= block1;
+            channelsMaskCpy[4] = 1 << ( i * 2 );
+            chanMaskState = true;
+            index = i;
+            break;
+        }
+        else if( CountBits( block2, 16 ) > 5 )
+        {
+            channelsMaskCpy[i] &= block2;
+            channelsMaskCpy[4] = 1 << ( i * 2 + 1 );
+            chanMaskState = true;
+            index = i;
+            break;
+        }
+    }
+
+    // Do only change the channel mask, if we have found a valid block.
+    if( chanMaskState == true )
+    {
+        // Copy channels mask back again
+        for( uint8_t i = 0; i < 4; i++ )
+        {
+            channelsMask[i] = channelsMaskCpy[i];
+
+            if( i != index )
+            {
+                channelsMask[i] = 0;
+            }
+        }
+        channelsMask[4] = channelsMaskCpy[4];
+    }
+    return chanMaskState;
+}
+
+static uint8_t CountNbOfEnabledChannels( uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx )
+{
+    uint8_t nbEnabledChannels = 0;
+    uint8_t delayTransmission = 0;
+
+    for( uint8_t i = 0, k = 0; i < US915_HYBRID_MAX_NB_CHANNELS; i += 16, k++ )
+    {
+        for( uint8_t j = 0; j < 16; j++ )
+        {
+            if( ( channelsMask[k] & ( 1 << j ) ) != 0 )
+            {
+                if( channels[i + j].Frequency == 0 )
+                { // Check if the channel is enabled
+                    continue;
+                }
+                if( RegionCommonValueInRange( datarate, channels[i + j].DrRange.Fields.Min,
+                                              channels[i + j].DrRange.Fields.Max ) == false )
+                { // Check if the current channel selection supports the given datarate
+                    continue;
+                }
+                if( bands[channels[i + j].Band].TimeOff > 0 )
+                { // Check if the band is available for transmission
+                    delayTransmission++;
+                    continue;
+                }
+                enabledChannels[nbEnabledChannels++] = i + j;
+            }
+        }
+    }
+
+    *delayTx = delayTransmission;
+    return nbEnabledChannels;
+}
+
+PhyParam_t RegionUS915HybridGetPhyParam( GetPhyParams_t* getPhy )
+{
+    PhyParam_t phyParam = { 0 };
+
+    switch( getPhy->Attribute )
+    {
+        case PHY_MIN_RX_DR:
+        {
+            phyParam.Value = US915_HYBRID_RX_MIN_DATARATE;
+            break;
+        }
+        case PHY_MIN_TX_DR:
+        {
+            phyParam.Value = US915_HYBRID_TX_MIN_DATARATE;
+            break;
+        }
+        case PHY_DEF_TX_DR:
+        {
+            phyParam.Value = US915_HYBRID_DEFAULT_DATARATE;
+            break;
+        }
+        case PHY_NEXT_LOWER_TX_DR:
+        {
+            phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, US915_HYBRID_TX_MIN_DATARATE );
+            break;
+        }
+        case PHY_DEF_TX_POWER:
+        {
+            phyParam.Value = US915_HYBRID_DEFAULT_TX_POWER;
+            break;
+        }
+        case PHY_MAX_PAYLOAD:
+        {
+            phyParam.Value = MaxPayloadOfDatarateUS915_HYBRID[getPhy->Datarate];
+            break;
+        }
+        case PHY_MAX_PAYLOAD_REPEATER:
+        {
+            phyParam.Value = MaxPayloadOfDatarateRepeaterUS915_HYBRID[getPhy->Datarate];
+            break;
+        }
+        case PHY_DUTY_CYCLE:
+        {
+            phyParam.Value = US915_HYBRID_DUTY_CYCLE_ENABLED;
+            break;
+        }
+        case PHY_MAX_RX_WINDOW:
+        {
+            phyParam.Value = US915_HYBRID_MAX_RX_WINDOW;
+            break;
+        }
+        case PHY_RECEIVE_DELAY1:
+        {
+            phyParam.Value = US915_HYBRID_RECEIVE_DELAY1;
+            break;
+        }
+        case PHY_RECEIVE_DELAY2:
+        {
+            phyParam.Value = US915_HYBRID_RECEIVE_DELAY2;
+            break;
+        }
+        case PHY_JOIN_ACCEPT_DELAY1:
+        {
+            phyParam.Value = US915_HYBRID_JOIN_ACCEPT_DELAY1;
+            break;
+        }
+        case PHY_JOIN_ACCEPT_DELAY2:
+        {
+            phyParam.Value = US915_HYBRID_JOIN_ACCEPT_DELAY2;
+            break;
+        }
+        case PHY_MAX_FCNT_GAP:
+        {
+            phyParam.Value = US915_HYBRID_MAX_FCNT_GAP;
+            break;
+        }
+        case PHY_ACK_TIMEOUT:
+        {
+            phyParam.Value = ( US915_HYBRID_ACKTIMEOUT + randr( -US915_HYBRID_ACK_TIMEOUT_RND, US915_HYBRID_ACK_TIMEOUT_RND ) );
+            break;
+        }
+        case PHY_DEF_DR1_OFFSET:
+        {
+            phyParam.Value = US915_HYBRID_DEFAULT_RX1_DR_OFFSET;
+            break;
+        }
+        case PHY_DEF_RX2_FREQUENCY:
+        {
+            phyParam.Value = US915_HYBRID_RX_WND_2_FREQ;
+            break;
+        }
+        case PHY_DEF_RX2_DR:
+        {
+            phyParam.Value = US915_HYBRID_RX_WND_2_DR;
+            break;
+        }
+        case PHY_CHANNELS_MASK:
+        {
+            phyParam.ChannelsMask = ChannelsMask;
+            break;
+        }
+        case PHY_CHANNELS_DEFAULT_MASK:
+        {
+            phyParam.ChannelsMask = ChannelsDefaultMask;
+            break;
+        }
+        case PHY_MAX_NB_CHANNELS:
+        {
+            phyParam.Value = US915_HYBRID_MAX_NB_CHANNELS;
+            break;
+        }
+        case PHY_CHANNELS:
+        {
+            phyParam.Channels = Channels;
+            break;
+        }
+        case PHY_DEF_UPLINK_DWELL_TIME:
+        case PHY_DEF_DOWNLINK_DWELL_TIME:
+        {
+            phyParam.Value = 0;
+            break;
+        }
+        case PHY_DEF_MAX_EIRP:
+        case PHY_DEF_ANTENNA_GAIN:
+        {
+            phyParam.fValue = 0;
+            break;
+        }
+        case PHY_NB_JOIN_TRIALS:
+        case PHY_DEF_NB_JOIN_TRIALS:
+        {
+            phyParam.Value = 2;
+            break;
+        }
+        default:
+        {
+            break;
+        }
+    }
+
+    return phyParam;
+}
+
+void RegionUS915HybridSetBandTxDone( SetBandTxDoneParams_t* txDone )
+{
+    RegionCommonSetBandTxDone( txDone->Joined, &Bands[Channels[txDone->Channel].Band], txDone->LastTxDoneTime );
+}
+
+void RegionUS915HybridInitDefaults( InitType_t type )
+{
+    switch( type )
+    {
+        case INIT_TYPE_INIT:
+        {
+            // Channels
+            // 125 kHz channels
+            for( uint8_t i = 0; i < US915_HYBRID_MAX_NB_CHANNELS - 8; i++ )
+            {
+                Channels[i].Frequency = 902300000 + i * 200000;
+                Channels[i].DrRange.Value = ( DR_3 << 4 ) | DR_0;
+                Channels[i].Band = 0;
+            }
+            // 500 kHz channels
+            for( uint8_t i = US915_HYBRID_MAX_NB_CHANNELS - 8; i < US915_HYBRID_MAX_NB_CHANNELS; i++ )
+            {
+                Channels[i].Frequency = 903000000 + ( i - ( US915_HYBRID_MAX_NB_CHANNELS - 8 ) ) * 1600000;
+                Channels[i].DrRange.Value = ( DR_4 << 4 ) | DR_4;
+                Channels[i].Band = 0;
+            }
+
+            // ChannelsMask
+            ChannelsDefaultMask[0] = 0x00FF;
+            ChannelsDefaultMask[1] = 0x0000;
+            ChannelsDefaultMask[2] = 0x0000;
+            ChannelsDefaultMask[3] = 0x0000;
+            ChannelsDefaultMask[4] = 0x0001;
+            ChannelsDefaultMask[5] = 0x0000;
+
+            // Copy channels default mask
+            RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 6 );
+
+            // Copy into channels mask remaining
+            RegionCommonChanMaskCopy( ChannelsMaskRemaining, ChannelsMask, 6 );
+            break;
+        }
+        case INIT_TYPE_RESTORE:
+        {
+            ReenableChannels( ChannelsDefaultMask[4], ChannelsMask );
+
+            for( uint8_t i = 0; i < 6; i++ )
+            { // Copy-And the channels mask
+                ChannelsMaskRemaining[i] &= ChannelsMask[i];
+            }
+        }
+        default:
+        {
+            break;
+        }
+    }
+}
+
+bool RegionUS915HybridVerify( VerifyParams_t* verify, PhyAttribute_t phyAttribute )
+{
+    switch( phyAttribute )
+    {
+        case PHY_TX_DR:
+        {
+            return RegionCommonValueInRange( verify->DatarateParams.Datarate, US915_HYBRID_TX_MIN_DATARATE, US915_HYBRID_TX_MAX_DATARATE );
+        }
+        case PHY_DEF_TX_DR:
+        {
+            return RegionCommonValueInRange( verify->DatarateParams.Datarate, DR_0, DR_5 );
+        }
+        case PHY_RX_DR:
+        {
+            return RegionCommonValueInRange( verify->DatarateParams.Datarate, US915_HYBRID_RX_MIN_DATARATE, US915_HYBRID_RX_MAX_DATARATE );
+        }
+        case PHY_DEF_TX_POWER:
+        case PHY_TX_POWER:
+        {
+            // Remark: switched min and max!
+            return RegionCommonValueInRange( verify->TxPower, US915_HYBRID_MAX_TX_POWER, US915_HYBRID_MIN_TX_POWER );
+        }
+        case PHY_DUTY_CYCLE:
+        {
+            return US915_HYBRID_DUTY_CYCLE_ENABLED;
+        }
+        case PHY_NB_JOIN_TRIALS:
+        {
+            if( verify->NbJoinTrials < 2 )
+            {
+                return false;
+            }
+            break;
+        }
+        default:
+            return false;
+    }
+    return true;
+}
+
+void RegionUS915HybridApplyCFList( ApplyCFListParams_t* applyCFList )
+{
+    return;
+}
+
+bool RegionUS915HybridChanMaskSet( ChanMaskSetParams_t* chanMaskSet )
+{
+    uint8_t nbChannels = RegionCommonCountChannels( chanMaskSet->ChannelsMaskIn, 0, 4 );
+
+    // Check the number of active channels
+    if( ( nbChannels < 2 ) &&
+        ( nbChannels > 0 ) )
+    {
+        return false;
+    }
+
+    // Validate the channels mask
+    if( ValidateChannelsMask( chanMaskSet->ChannelsMaskIn ) == false  )
+    {
+        return false;
+    }
+
+    switch( chanMaskSet->ChannelsMaskType )
+    {
+        case CHANNELS_MASK:
+        {
+            RegionCommonChanMaskCopy( ChannelsMask, chanMaskSet->ChannelsMaskIn, 6 );
+
+            for( uint8_t i = 0; i < 6; i++ )
+            { // Copy-And the channels mask
+                ChannelsMaskRemaining[i] &= ChannelsMask[i];
+            }
+            break;
+        }
+        case CHANNELS_DEFAULT_MASK:
+        {
+            RegionCommonChanMaskCopy( ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 6 );
+            break;
+        }
+        default:
+            return false;
+    }
+    return true;
+}
+
+bool RegionUS915HybridAdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter )
+{
+    bool adrAckReq = false;
+    int8_t datarate = adrNext->Datarate;
+    int8_t txPower = adrNext->TxPower;
+    GetPhyParams_t getPhy;
+    PhyParam_t phyParam;
+
+    // Report back the adr ack counter
+    *adrAckCounter = adrNext->AdrAckCounter;
+
+    if( adrNext->AdrEnabled == true )
+    {
+        if( datarate == US915_HYBRID_TX_MIN_DATARATE )
+        {
+            *adrAckCounter = 0;
+            adrAckReq = false;
+        }
+        else
+        {
+            if( adrNext->AdrAckCounter >= US915_HYBRID_ADR_ACK_LIMIT )
+            {
+                adrAckReq = true;
+                txPower = US915_HYBRID_MAX_TX_POWER;
+            }
+            else
+            {
+                adrAckReq = false;
+            }
+            if( adrNext->AdrAckCounter >= ( US915_HYBRID_ADR_ACK_LIMIT + US915_HYBRID_ADR_ACK_DELAY ) )
+            {
+                if( ( adrNext->AdrAckCounter % US915_HYBRID_ADR_ACK_DELAY ) == 1 )
+                {
+                    // Decrease the datarate
+                    getPhy.Attribute = PHY_NEXT_LOWER_TX_DR;
+                    getPhy.Datarate = datarate;
+                    getPhy.UplinkDwellTime = adrNext->UplinkDwellTime;
+                    phyParam = RegionUS915HybridGetPhyParam( &getPhy );
+                    datarate = phyParam.Value;
+
+                    if( datarate == US915_HYBRID_TX_MIN_DATARATE )
+                    {
+                        // We must set adrAckReq to false as soon as we reach the lowest datarate
+                        adrAckReq = false;
+                        if( adrNext->UpdateChanMask == true )
+                        {
+                            // Re-enable default channels
+                            ReenableChannels( ChannelsMask[4], ChannelsMask );
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    *drOut = datarate;
+    *txPowOut = txPower;
+    return adrAckReq;
+}
+
+void RegionUS915HybridComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams )
+{
+    double tSymbol = 0.0;
+
+    // Get the datarate, perform a boundary check
+    rxConfigParams->Datarate = MIN( datarate, US915_HYBRID_RX_MAX_DATARATE );
+    rxConfigParams->Bandwidth = GetBandwidth( rxConfigParams->Datarate );
+
+    tSymbol = RegionCommonComputeSymbolTimeLoRa( DataratesUS915_HYBRID[rxConfigParams->Datarate], BandwidthsUS915_HYBRID[rxConfigParams->Datarate] );
+
+    RegionCommonComputeRxWindowParameters( tSymbol, minRxSymbols, rxError, RADIO_WAKEUP_TIME, &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset );
+}
+
+bool RegionUS915HybridRxConfig( RxConfigParams_t* rxConfig, int8_t* datarate )
+{
+    int8_t dr = rxConfig->Datarate;
+    uint8_t maxPayload = 0;
+    int8_t phyDr = 0;
+    uint32_t frequency = rxConfig->Frequency;
+
+    if( Radio.GetStatus( ) != RF_IDLE )
+    {
+        return false;
+    }
+
+    if( rxConfig->Window == 0 )
+    {
+        // Apply window 1 frequency
+        frequency = US915_HYBRID_FIRST_RX1_CHANNEL + ( rxConfig->Channel % 8 ) * US915_HYBRID_STEPWIDTH_RX1_CHANNEL;
+    }
+
+    // Read the physical datarate from the datarates table
+    phyDr = DataratesUS915_HYBRID[dr];
+
+    Radio.SetChannel( frequency );
+
+    // Radio configuration
+    Radio.SetRxConfig( MODEM_LORA, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous );
+
+    if( rxConfig->RepeaterSupport == true )
+    {
+        maxPayload = MaxPayloadOfDatarateRepeaterUS915_HYBRID[dr];
+    }
+    else
+    {
+        maxPayload = MaxPayloadOfDatarateUS915_HYBRID[dr];
+    }
+    Radio.SetMaxPayloadLength( MODEM_LORA, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD );
+
+    *datarate = (uint8_t) dr;
+    return true;
+}
+
+bool RegionUS915HybridTxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir )
+{
+    int8_t phyDr = DataratesUS915_HYBRID[txConfig->Datarate];
+    int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, Bands[Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, ChannelsMask );
+    uint32_t bandwidth = GetBandwidth( txConfig->Datarate );
+    int8_t phyTxPower = 0;
+
+    // Calculate physical TX power
+    phyTxPower = RegionCommonComputeTxPower( txPowerLimited, US915_HYBRID_DEFAULT_MAX_ERP, 0 );
+
+    // Setup the radio frequency
+    Radio.SetChannel( Channels[txConfig->Channel].Frequency );
+
+    Radio.SetTxConfig( MODEM_LORA, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 3000 );
+
+    // Setup maximum payload lenght of the radio driver
+    Radio.SetMaxPayloadLength( MODEM_LORA, txConfig->PktLen );
+    // Get the time-on-air of the next tx frame
+    *txTimeOnAir = Radio.TimeOnAir( MODEM_LORA, txConfig->PktLen );
+    *txPower = txPowerLimited;
+
+    return true;
+}
+
+uint8_t RegionUS915HybridLinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed )
+{
+    uint8_t status = 0x07;
+    RegionCommonLinkAdrParams_t linkAdrParams;
+    uint8_t nextIndex = 0;
+    uint8_t bytesProcessed = 0;
+    uint16_t channelsMask[6] = { 0, 0, 0, 0, 0, 0 };
+    GetPhyParams_t getPhy;
+    PhyParam_t phyParam;
+    RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams;
+
+    // Initialize local copy of channels mask
+    RegionCommonChanMaskCopy( channelsMask, ChannelsMask, 6 );
+
+    while( bytesProcessed < linkAdrReq->PayloadSize )
+    {
+        nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams );
+
+        if( nextIndex == 0 )
+            break; // break loop, since no more request has been found
+
+        // Update bytes processed
+        bytesProcessed += nextIndex;
+
+        // Revert status, as we only check the last ADR request for the channel mask KO
+        status = 0x07;
+
+        if( linkAdrParams.ChMaskCtrl == 6 )
+        {
+            // Enable all 125 kHz channels
+            channelsMask[0] = 0xFFFF;
+            channelsMask[1] = 0xFFFF;
+            channelsMask[2] = 0xFFFF;
+            channelsMask[3] = 0xFFFF;
+            // Apply chMask to channels 64 to 71
+            channelsMask[4] = linkAdrParams.ChMask;
+        }
+        else if( linkAdrParams.ChMaskCtrl == 7 )
+        {
+            // Disable all 125 kHz channels
+            channelsMask[0] = 0x0000;
+            channelsMask[1] = 0x0000;
+            channelsMask[2] = 0x0000;
+            channelsMask[3] = 0x0000;
+            // Apply chMask to channels 64 to 71
+            channelsMask[4] = linkAdrParams.ChMask;
+        }
+        else if( linkAdrParams.ChMaskCtrl == 5 )
+        {
+            // RFU
+            status &= 0xFE; // Channel mask KO
+        }
+        else
+        {
+            channelsMask[linkAdrParams.ChMaskCtrl] = linkAdrParams.ChMask;
+        }
+    }
+
+    // FCC 15.247 paragraph F mandates to hop on at least 2 125 kHz channels
+    if( ( linkAdrParams.Datarate < DR_4 ) && ( RegionCommonCountChannels( channelsMask, 0, 4 ) < 2 ) )
+    {
+        status &= 0xFE; // Channel mask KO
+    }
+
+    if( ValidateChannelsMask( channelsMask ) == false )
+    {
+        status &= 0xFE; // Channel mask KO
+    }
+
+    // Get the minimum possible datarate
+    getPhy.Attribute = PHY_MIN_TX_DR;
+    getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime;
+    phyParam = RegionUS915HybridGetPhyParam( &getPhy );
+
+    linkAdrVerifyParams.Status = status;
+    linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled;
+    linkAdrVerifyParams.Datarate = linkAdrParams.Datarate;
+    linkAdrVerifyParams.TxPower = linkAdrParams.TxPower;
+    linkAdrVerifyParams.NbRep = linkAdrParams.NbRep;
+    linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate;
+    linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower;
+    linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep;
+    linkAdrVerifyParams.NbChannels = US915_HYBRID_MAX_NB_CHANNELS;
+    linkAdrVerifyParams.ChannelsMask = channelsMask;
+    linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value;
+    linkAdrVerifyParams.MaxDatarate = US915_HYBRID_TX_MAX_DATARATE;
+    linkAdrVerifyParams.Channels = Channels;
+    linkAdrVerifyParams.MinTxPower = US915_HYBRID_MIN_TX_POWER;
+    linkAdrVerifyParams.MaxTxPower = US915_HYBRID_MAX_TX_POWER;
+
+    // Verify the parameters and update, if necessary
+    status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep );
+
+    // Update channelsMask if everything is correct
+    if( status == 0x07 )
+    {
+        // Copy Mask
+        RegionCommonChanMaskCopy( ChannelsMask, channelsMask, 6 );
+
+        ChannelsMaskRemaining[0] &= ChannelsMask[0];
+        ChannelsMaskRemaining[1] &= ChannelsMask[1];
+        ChannelsMaskRemaining[2] &= ChannelsMask[2];
+        ChannelsMaskRemaining[3] &= ChannelsMask[3];
+        ChannelsMaskRemaining[4] = ChannelsMask[4];
+        ChannelsMaskRemaining[5] = ChannelsMask[5];
+    }
+
+    // Update status variables
+    *drOut = linkAdrParams.Datarate;
+    *txPowOut = linkAdrParams.TxPower;
+    *nbRepOut = linkAdrParams.NbRep;
+    *nbBytesParsed = bytesProcessed;
+
+    return status;
+}
+
+uint8_t RegionUS915HybridRxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq )
+{
+    uint8_t status = 0x07;
+    uint32_t freq = rxParamSetupReq->Frequency;
+
+    // Verify radio frequency
+    if( ( Radio.CheckRfFrequency( freq ) == false ) ||
+        ( freq < US915_HYBRID_FIRST_RX1_CHANNEL ) ||
+        ( freq > US915_HYBRID_LAST_RX1_CHANNEL ) ||
+        ( ( ( freq - ( uint32_t ) US915_HYBRID_FIRST_RX1_CHANNEL ) % ( uint32_t ) US915_HYBRID_STEPWIDTH_RX1_CHANNEL ) != 0 ) )
+    {
+        status &= 0xFE; // Channel frequency KO
+    }
+
+    // Verify datarate
+    if( RegionCommonValueInRange( rxParamSetupReq->Datarate, US915_HYBRID_RX_MIN_DATARATE, US915_HYBRID_RX_MAX_DATARATE ) == false )
+    {
+        status &= 0xFD; // Datarate KO
+    }
+    if( ( RegionCommonValueInRange( rxParamSetupReq->Datarate, DR_5, DR_7 ) == true ) ||
+        ( rxParamSetupReq->Datarate > DR_13 ) )
+    {
+        status &= 0xFD; // Datarate KO
+    }
+
+    // Verify datarate offset
+    if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, US915_HYBRID_MIN_RX1_DR_OFFSET, US915_HYBRID_MAX_RX1_DR_OFFSET ) == false )
+    {
+        status &= 0xFB; // Rx1DrOffset range KO
+    }
+
+    return status;
+}
+
+uint8_t RegionUS915HybridNewChannelReq( NewChannelReqParams_t* newChannelReq )
+{
+    // Datarate and frequency KO
+    return 0;
+}
+
+int8_t RegionUS915HybridTxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq )
+{
+    return -1;
+}
+
+uint8_t RegionUS915HybridDlChannelReq( DlChannelReqParams_t* dlChannelReq )
+{
+    return 0;
+}
+
+int8_t RegionUS915HybridAlternateDr( AlternateDrParams_t* alternateDr )
+{
+    int8_t datarate = 0;
+
+    // Re-enable 500 kHz default channels
+    ReenableChannels( ChannelsMask[4], ChannelsMask );
+
+    if( ( alternateDr->NbTrials & 0x01 ) == 0x01 )
+    {
+        datarate = DR_4;
+    }
+    else
+    {
+        datarate = DR_0;
+    }
+    return datarate;
+}
+
+void RegionUS915HybridCalcBackOff( CalcBackOffParams_t* calcBackOff )
+{
+    RegionCommonCalcBackOffParams_t calcBackOffParams;
+
+    calcBackOffParams.Channels = Channels;
+    calcBackOffParams.Bands = Bands;
+    calcBackOffParams.LastTxIsJoinRequest = calcBackOff->LastTxIsJoinRequest;
+    calcBackOffParams.Joined = calcBackOff->Joined;
+    calcBackOffParams.DutyCycleEnabled = calcBackOff->DutyCycleEnabled;
+    calcBackOffParams.Channel = calcBackOff->Channel;
+    calcBackOffParams.ElapsedTime = calcBackOff->ElapsedTime;
+    calcBackOffParams.TxTimeOnAir = calcBackOff->TxTimeOnAir;
+
+    RegionCommonCalcBackOff( &calcBackOffParams );
+}
+
+bool RegionUS915HybridNextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff )
+{
+    uint8_t nbEnabledChannels = 0;
+    uint8_t delayTx = 0;
+    uint8_t enabledChannels[US915_HYBRID_MAX_NB_CHANNELS] = { 0 };
+    TimerTime_t nextTxDelay = 0;
+
+    // Count 125kHz channels
+    if( RegionCommonCountChannels( ChannelsMaskRemaining, 0, 4 ) == 0 )
+    { // Reactivate default channels
+        RegionCommonChanMaskCopy( ChannelsMaskRemaining, ChannelsMask, 4  );
+    }
+    // Check other channels
+    if( nextChanParams->Datarate >= DR_4 )
+    {
+        if( ( ChannelsMaskRemaining[4] & 0x00FF ) == 0 )
+        {
+            ChannelsMaskRemaining[4] = ChannelsMask[4];
+        }
+    }
+
+    if( nextChanParams->AggrTimeOff <= TimerGetElapsedTime( nextChanParams->LastAggrTx ) )
+    {
+        // Reset Aggregated time off
+        *aggregatedTimeOff = 0;
+
+        // Update bands Time OFF
+        nextTxDelay = RegionCommonUpdateBandTimeOff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, Bands, US915_HYBRID_MAX_NB_BANDS );
+
+        // Search how many channels are enabled
+        nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Datarate,
+                                                      ChannelsMaskRemaining, Channels,
+                                                      Bands, enabledChannels, &delayTx );
+    }
+    else
+    {
+        delayTx++;
+        nextTxDelay = nextChanParams->AggrTimeOff - TimerGetElapsedTime( nextChanParams->LastAggrTx );
+    }
+
+    if( nbEnabledChannels > 0 )
+    {
+        // We found a valid channel
+        *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )];
+        // Disable the channel in the mask
+        RegionCommonChanDisable( ChannelsMaskRemaining, *channel, US915_HYBRID_MAX_NB_CHANNELS - 8 );
+
+        *time = 0;
+        return true;
+    }
+    else
+    {
+        if( delayTx > 0 )
+        {
+            // Delay transmission due to AggregatedTimeOff or to a band time off
+            *time = nextTxDelay;
+            return true;
+        }
+        // Datarate not supported by any channel
+        *time = 0;
+        return false;
+    }
+}
+
+LoRaMacStatus_t RegionUS915HybridChannelAdd( ChannelAddParams_t* channelAdd )
+{
+    return LORAMAC_STATUS_PARAMETER_INVALID;
+}
+
+bool RegionUS915HybridChannelsRemove( ChannelRemoveParams_t* channelRemove  )
+{
+    return LORAMAC_STATUS_PARAMETER_INVALID;
+}
+
+void RegionUS915HybridSetContinuousWave( ContinuousWaveParams_t* continuousWave )
+{
+    int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, Bands[Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, ChannelsMask );
+    int8_t phyTxPower = 0;
+    uint32_t frequency = Channels[continuousWave->Channel].Frequency;
+
+    // Calculate physical TX power
+    phyTxPower = RegionCommonComputeTxPower( txPowerLimited, US915_HYBRID_DEFAULT_MAX_ERP, 0 );
+
+    Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout );
+}
+
+uint8_t RegionUS915HybridApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset )
+{
+    int8_t datarate = DatarateOffsetsUS915_HYBRID[dr][drOffset];
+
+    if( datarate < 0 )
+    {
+        datarate = DR_0;
+    }
+    return datarate;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/region/RegionUS915-Hybrid.h	Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,448 @@
+/*!
+ * \file      RegionUS915Hybrid-Hybrid.h
+ *
+ * \brief     Region definition for US915
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ *                ______                              _
+ *               / _____)             _              | |
+ *              ( (____  _____ ____ _| |_ _____  ____| |__
+ *               \____ \| ___ |    (_   _) ___ |/ ___)  _ \
+ *               _____) ) ____| | | || |_| ____( (___| | | |
+ *              (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ *              (C)2013 Semtech
+ *
+ *               ___ _____ _   ___ _  _____ ___  ___  ___ ___
+ *              / __|_   _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ *              \__ \ | |/ _ \ (__| ' <| _| (_) |   / (__| _|
+ *              |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ *              embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author    Miguel Luis ( Semtech )
+ *
+ * \author    Gregory Cristian ( Semtech )
+ *
+ * \author    Daniel Jaeckle ( STACKFORCE )
+ *
+ * \defgroup  REGIONUS915HYB Region US915 in hybrid mode
+ *            This is a hybrid implementation for US915, supporting 16 uplink channels only.
+ * \{
+ */
+#ifndef __REGION_US915_HYBRID_H__
+#define __REGION_US915_HYBRID_H__
+
+/*!
+ * LoRaMac maximum number of channels
+ */
+#define US915_HYBRID_MAX_NB_CHANNELS                72
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define US915_HYBRID_TX_MIN_DATARATE                DR_0
+
+/*!
+ * Maximal datarate that can be used by the node
+ */
+#define US915_HYBRID_TX_MAX_DATARATE                DR_4
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define US915_HYBRID_RX_MIN_DATARATE                DR_8
+
+/*!
+ * Maximal datarate that can be used by the node
+ */
+#define US915_HYBRID_RX_MAX_DATARATE                DR_13
+
+/*!
+ * Default datarate used by the node
+ */
+#define US915_HYBRID_DEFAULT_DATARATE               DR_0
+
+/*!
+ * Minimal Rx1 receive datarate offset
+ */
+#define US915_HYBRID_MIN_RX1_DR_OFFSET              0
+
+/*!
+ * Maximal Rx1 receive datarate offset
+ */
+#define US915_HYBRID_MAX_RX1_DR_OFFSET              3
+
+/*!
+ * Default Rx1 receive datarate offset
+ */
+#define US915_HYBRID_DEFAULT_RX1_DR_OFFSET          0
+
+/*!
+ * Minimal Tx output power that can be used by the node
+ */
+#define US915_HYBRID_MIN_TX_POWER                   TX_POWER_10
+
+/*!
+ * Maximal Tx output power that can be used by the node
+ */
+#define US915_HYBRID_MAX_TX_POWER                   TX_POWER_0
+
+/*!
+ * Default Tx output power used by the node
+ */
+#define US915_HYBRID_DEFAULT_TX_POWER               TX_POWER_0
+
+/*!
+ * Default Max ERP
+ */
+#define US915_HYBRID_DEFAULT_MAX_ERP                30.0f
+
+/*!
+ * ADR Ack limit
+ */
+#define US915_HYBRID_ADR_ACK_LIMIT                  64
+
+/*!
+ * ADR Ack delay
+ */
+#define US915_HYBRID_ADR_ACK_DELAY                  32
+
+/*!
+ * Enabled or disabled the duty cycle
+ */
+#define US915_HYBRID_DUTY_CYCLE_ENABLED             0
+
+/*!
+ * Maximum RX window duration
+ */
+#define US915_HYBRID_MAX_RX_WINDOW                  3000
+
+/*!
+ * Receive delay 1
+ */
+#define US915_HYBRID_RECEIVE_DELAY1                 1000
+
+/*!
+ * Receive delay 2
+ */
+#define US915_HYBRID_RECEIVE_DELAY2                 2000
+
+/*!
+ * Join accept delay 1
+ */
+#define US915_HYBRID_JOIN_ACCEPT_DELAY1             5000
+
+/*!
+ * Join accept delay 2
+ */
+#define US915_HYBRID_JOIN_ACCEPT_DELAY2             6000
+
+/*!
+ * Maximum frame counter gap
+ */
+#define US915_HYBRID_MAX_FCNT_GAP                   16384
+
+/*!
+ * Ack timeout
+ */
+#define US915_HYBRID_ACKTIMEOUT                     2000
+
+/*!
+ * Random ack timeout limits
+ */
+#define US915_HYBRID_ACK_TIMEOUT_RND                1000
+
+/*!
+ * Second reception window channel frequency definition.
+ */
+#define US915_HYBRID_RX_WND_2_FREQ                  923300000
+
+/*!
+ * Second reception window channel datarate definition.
+ */
+#define US915_HYBRID_RX_WND_2_DR                    DR_8
+
+/*!
+ * LoRaMac maximum number of bands
+ */
+#define US915_HYBRID_MAX_NB_BANDS                   1
+
+/*!
+ * Band 0 definition
+ * { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff }
+ */
+#define US915_HYBRID_BAND0                          { 1, US915_HYBRID_MAX_TX_POWER, 0,  0 } //  100.0 %
+
+/*!
+ * Defines the first channel for RX window 1 for US band
+ */
+#define US915_HYBRID_FIRST_RX1_CHANNEL              ( (uint32_t) 923300000 )
+
+/*!
+ * Defines the last channel for RX window 1 for US band
+ */
+#define US915_HYBRID_LAST_RX1_CHANNEL               ( (uint32_t) 927500000 )
+
+/*!
+ * Defines the step width of the channels for RX window 1
+ */
+#define US915_HYBRID_STEPWIDTH_RX1_CHANNEL          ( (uint32_t) 600000 )
+
+/*!
+ * Data rates table definition
+ */
+static const uint8_t DataratesUS915_HYBRID[]  = { 10, 9, 8,  7,  8,  0,  0, 0, 12, 11, 10, 9, 8, 7, 0, 0 };
+
+/*!
+ * Bandwidths table definition in Hz
+ */
+static const uint32_t BandwidthsUS915_HYBRID[] = { 125000, 125000, 125000, 125000, 500000, 0, 0, 0, 500000, 500000, 500000, 500000, 500000, 500000, 0, 0 };
+
+/*!
+ * Up/Down link data rates offset definition
+ */
+static const int8_t DatarateOffsetsUS915_HYBRID[5][4] =
+{
+    { DR_10, DR_9 , DR_8 , DR_8  }, // DR_0
+    { DR_11, DR_10, DR_9 , DR_8  }, // DR_1
+    { DR_12, DR_11, DR_10, DR_9  }, // DR_2
+    { DR_13, DR_12, DR_11, DR_10 }, // DR_3
+    { DR_13, DR_13, DR_12, DR_11 }, // DR_4
+};
+
+/*!
+ * Maximum payload with respect to the datarate index. Cannot operate with repeater.
+ */
+static const uint8_t MaxPayloadOfDatarateUS915_HYBRID[] = { 11, 53, 125, 242, 242, 0, 0, 0, 53, 129, 242, 242, 242, 242, 0, 0 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Can operate with repeater.
+ */
+static const uint8_t MaxPayloadOfDatarateRepeaterUS915_HYBRID[] = { 11, 53, 125, 242, 242, 0, 0, 0, 33, 109, 222, 222, 222, 222, 0, 0 };
+
+/*!
+ * \brief The function gets a value of a specific phy attribute.
+ *
+ * \param [IN] getPhy Pointer to the function parameters.
+ *
+ * \retval Returns a structure containing the PHY parameter.
+ */
+PhyParam_t RegionUS915HybridGetPhyParam( GetPhyParams_t* getPhy );
+
+/*!
+ * \brief Updates the last TX done parameters of the current channel.
+ *
+ * \param [IN] txDone Pointer to the function parameters.
+ */
+void RegionUS915HybridSetBandTxDone( SetBandTxDoneParams_t* txDone );
+
+/*!
+ * \brief Initializes the channels masks and the channels.
+ *
+ * \param [IN] type Sets the initialization type.
+ */
+void RegionUS915HybridInitDefaults( InitType_t type );
+
+/*!
+ * \brief Verifies a parameter.
+ *
+ * \param [IN] verify Pointer to the function parameters.
+ *
+ * \param [IN] type Sets the initialization type.
+ *
+ * \retval Returns true, if the parameter is valid.
+ */
+bool RegionUS915HybridVerify( VerifyParams_t* verify, PhyAttribute_t phyAttribute );
+
+/*!
+ * \brief The function parses the input buffer and sets up the channels of the
+ *        CF list.
+ *
+ * \param [IN] applyCFList Pointer to the function parameters.
+ */
+void RegionUS915HybridApplyCFList( ApplyCFListParams_t* applyCFList );
+
+/*!
+ * \brief Sets a channels mask.
+ *
+ * \param [IN] chanMaskSet Pointer to the function parameters.
+ *
+ * \retval Returns true, if the channels mask could be set.
+ */
+bool RegionUS915HybridChanMaskSet( ChanMaskSetParams_t* chanMaskSet );
+
+/*!
+ * \brief Calculates the next datarate to set, when ADR is on or off.
+ *
+ * \param [IN] adrNext Pointer to the function parameters.
+ *
+ * \param [OUT] drOut The calculated datarate for the next TX.
+ *
+ * \param [OUT] txPowOut The TX power for the next TX.
+ *
+ * \param [OUT] adrAckCounter The calculated ADR acknowledgement counter.
+ *
+ * \retval Returns true, if an ADR request should be performed.
+ */
+bool RegionUS915HybridAdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter );
+
+/*!
+ * Computes the Rx window timeout and offset.
+ *
+ * \param [IN] datarate     Rx window datarate index to be used
+ *
+ * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame.
+ *
+ * \param [IN] rxError      System maximum timing error of the receiver. In milliseconds
+ *                          The receiver will turn on in a [-rxError : +rxError] ms
+ *                          interval around RxOffset
+ *
+ * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields.
+ */
+void RegionUS915HybridComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams );
+
+/*!
+ * \brief Configuration of the RX windows.
+ *
+ * \param [IN] rxConfig Pointer to the function parameters.
+ *
+ * \param [OUT] datarate The datarate index which was set.
+ *
+ * \retval Returns true, if the configuration was applied successfully.
+ */
+bool RegionUS915HybridRxConfig( RxConfigParams_t* rxConfig, int8_t* datarate );
+
+/*!
+ * \brief TX configuration.
+ *
+ * \param [IN] txConfig Pointer to the function parameters.
+ *
+ * \param [OUT] txPower The tx power index which was set.
+ *
+ * \param [OUT] txTimeOnAir The time-on-air of the frame.
+ *
+ * \retval Returns true, if the configuration was applied successfully.
+ */
+bool RegionUS915HybridTxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir );
+
+/*!
+ * \brief The function processes a Link ADR Request.
+ *
+ * \param [IN] linkAdrReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionUS915HybridLinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed );
+
+/*!
+ * \brief The function processes a RX Parameter Setup Request.
+ *
+ * \param [IN] rxParamSetupReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionUS915HybridRxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq );
+
+/*!
+ * \brief The function processes a Channel Request.
+ *
+ * \param [IN] newChannelReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionUS915HybridNewChannelReq( NewChannelReqParams_t* newChannelReq );
+
+/*!
+ * \brief The function processes a TX ParamSetup Request.
+ *
+ * \param [IN] txParamSetupReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ *         Returns -1, if the functionality is not implemented. In this case, the end node
+ *         shall not process the command.
+ */
+int8_t RegionUS915HybridTxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq );
+
+/*!
+ * \brief The function processes a DlChannel Request.
+ *
+ * \param [IN] dlChannelReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionUS915HybridDlChannelReq( DlChannelReqParams_t* dlChannelReq );
+
+/*!
+ * \brief Alternates the datarate of the channel for the join request.
+ *
+ * \param [IN] alternateDr Pointer to the function parameters.
+ *
+ * \retval Datarate to apply.
+ */
+int8_t RegionUS915HybridAlternateDr( AlternateDrParams_t* alternateDr );
+
+/*!
+ * \brief Calculates the back-off time.
+ *
+ * \param [IN] calcBackOff Pointer to the function parameters.
+ */
+void RegionUS915HybridCalcBackOff( CalcBackOffParams_t* calcBackOff );
+
+/*!
+ * \brief Searches and set the next random available channel
+ *
+ * \param [OUT] channel Next channel to use for TX.
+ *
+ * \param [OUT] time Time to wait for the next transmission according to the duty
+ *              cycle.
+ *
+ * \param [OUT] aggregatedTimeOff Updates the aggregated time off.
+ *
+ * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate]
+ */
+bool RegionUS915HybridNextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff );
+
+/*!
+ * \brief Adds a channel.
+ *
+ * \param [IN] channelAdd Pointer to the function parameters.
+ *
+ * \retval Status of the operation.
+ */
+LoRaMacStatus_t RegionUS915HybridChannelAdd( ChannelAddParams_t* channelAdd );
+
+/*!
+ * \brief Removes a channel.
+ *
+ * \param [IN] channelRemove Pointer to the function parameters.
+ *
+ * \retval Returns true, if the channel was removed successfully.
+ */
+bool RegionUS915HybridChannelsRemove( ChannelRemoveParams_t* channelRemove  );
+
+/*!
+ * \brief Sets the radio into continuous wave mode.
+ *
+ * \param [IN] continuousWave Pointer to the function parameters.
+ */
+void RegionUS915HybridSetContinuousWave( ContinuousWaveParams_t* continuousWave );
+
+/*!
+ * \brief Computes new datarate according to the given offset
+ *
+ * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms
+ *
+ * \param [IN] dr Current datarate
+ *
+ * \param [IN] drOffset Offset to be applied
+ *
+ * \retval newDr Computed datarate.
+ */
+uint8_t RegionUS915HybridApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset );
+
+/*! \} defgroup REGIONUS915HYB */
+
+#endif // __REGION_US915_HYBRID_H__
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/region/RegionUS915.cpp	Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,976 @@
+/*
+ / _____)             _              | |
+( (____  _____ ____ _| |_ _____  ____| |__
+ \____ \| ___ |    (_   _) ___ |/ ___)  _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+    (C)2013 Semtech
+ ___ _____ _   ___ _  _____ ___  ___  ___ ___
+/ __|_   _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+\__ \ | |/ _ \ (__| ' <| _| (_) |   / (__| _|
+|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+embedded.connectivity.solutions===============
+
+Description: LoRa MAC region US915 implementation
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+
+Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE )
+*/
+#include <stdbool.h>
+#include <string.h>
+#include <stdint.h>
+#include <math.h>
+
+#include "board.h"
+#include "LoRaMac.h"
+
+#include "utilities.h"
+
+#include "Region.h"
+#include "RegionCommon.h"
+#include "RegionUS915.h"
+
+// Definitions
+#define CHANNELS_MASK_SIZE              6
+
+// Global attributes
+/*!
+ * LoRaMAC channels
+ */
+static ChannelParams_t Channels[US915_MAX_NB_CHANNELS];
+
+/*!
+ * LoRaMac bands
+ */
+static Band_t Bands[US915_MAX_NB_BANDS] =
+{
+    US915_BAND0
+};
+
+/*!
+ * LoRaMac channels mask
+ */
+static uint16_t ChannelsMask[CHANNELS_MASK_SIZE];
+
+/*!
+ * LoRaMac channels remaining
+ */
+static uint16_t ChannelsMaskRemaining[CHANNELS_MASK_SIZE];
+
+/*!
+ * LoRaMac channels default mask
+ */
+static uint16_t ChannelsDefaultMask[CHANNELS_MASK_SIZE];
+
+// Static functions
+static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr )
+{
+    uint8_t nextLowerDr = 0;
+
+    if( dr == minDr )
+    {
+        nextLowerDr = minDr;
+    }
+    else
+    {
+        nextLowerDr = dr - 1;
+    }
+    return nextLowerDr;
+}
+
+static uint32_t GetBandwidth( uint32_t drIndex )
+{
+    switch( BandwidthsUS915[drIndex] )
+    {
+        default:
+        case 125000:
+            return 0;
+        case 250000:
+            return 1;
+        case 500000:
+            return 2;
+    }
+}
+
+static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask )
+{
+    int8_t txPowerResult = txPower;
+
+    // Limit tx power to the band max
+    txPowerResult =  MAX( txPower, maxBandTxPower );
+
+    if( datarate == DR_4 )
+    {// Limit tx power to max 26dBm
+        txPowerResult = MAX( txPower, TX_POWER_2 );
+    }
+    else
+    {
+        if( RegionCommonCountChannels( channelsMask, 0, 4 ) < 50 )
+        {// Limit tx power to max 21dBm
+            txPowerResult = MAX( txPower, TX_POWER_5 );
+        }
+    }
+    return txPowerResult;
+}
+
+static uint8_t CountNbOfEnabledChannels( uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx )
+{
+    uint8_t nbEnabledChannels = 0;
+    uint8_t delayTransmission = 0;
+
+    for( uint8_t i = 0, k = 0; i < US915_MAX_NB_CHANNELS; i += 16, k++ )
+    {
+        for( uint8_t j = 0; j < 16; j++ )
+        {
+            if( ( channelsMask[k] & ( 1 << j ) ) != 0 )
+            {
+                if( channels[i + j].Frequency == 0 )
+                { // Check if the channel is enabled
+                    continue;
+                }
+                if( RegionCommonValueInRange( datarate, channels[i + j].DrRange.Fields.Min,
+                                              channels[i + j].DrRange.Fields.Max ) == false )
+                { // Check if the current channel selection supports the given datarate
+                    continue;
+                }
+                if( bands[channels[i + j].Band].TimeOff > 0 )
+                { // Check if the band is available for transmission
+                    delayTransmission++;
+                    continue;
+                }
+                enabledChannels[nbEnabledChannels++] = i + j;
+            }
+        }
+    }
+
+    *delayTx = delayTransmission;
+    return nbEnabledChannels;
+}
+
+static uint8_t BeaconChannel( uint32_t devAddr, TimerTime_t beaconTime, TimerTime_t beaconInterval )
+{
+    uint32_t frequency = 0;
+
+    frequency = devAddr + ( beaconTime / ( beaconInterval / 1000 ) );
+
+    return ( ( uint8_t )( frequency % 8 ) );
+}
+
+PhyParam_t RegionUS915GetPhyParam( GetPhyParams_t* getPhy )
+{
+    PhyParam_t phyParam = { 0 };
+
+    switch( getPhy->Attribute )
+    {
+        case PHY_MIN_RX_DR:
+        {
+            phyParam.Value = US915_RX_MIN_DATARATE;
+            break;
+        }
+        case PHY_MIN_TX_DR:
+        {
+            phyParam.Value = US915_TX_MIN_DATARATE;
+            break;
+        }
+        case PHY_DEF_TX_DR:
+        {
+            phyParam.Value = US915_DEFAULT_DATARATE;
+            break;
+        }
+        case PHY_NEXT_LOWER_TX_DR:
+        {
+            phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, US915_TX_MIN_DATARATE );
+            break;
+        }
+        case PHY_DEF_TX_POWER:
+        {
+            phyParam.Value = US915_DEFAULT_TX_POWER;
+            break;
+        }
+        case PHY_MAX_PAYLOAD:
+        {
+            phyParam.Value = MaxPayloadOfDatarateUS915[getPhy->Datarate];
+            break;
+        }
+        case PHY_MAX_PAYLOAD_REPEATER:
+        {
+            phyParam.Value = MaxPayloadOfDatarateRepeaterUS915[getPhy->Datarate];
+            break;
+        }
+        case PHY_DUTY_CYCLE:
+        {
+            phyParam.Value = US915_DUTY_CYCLE_ENABLED;
+            break;
+        }
+        case PHY_MAX_RX_WINDOW:
+        {
+            phyParam.Value = US915_MAX_RX_WINDOW;
+            break;
+        }
+        case PHY_RECEIVE_DELAY1:
+        {
+            phyParam.Value = US915_RECEIVE_DELAY1;
+            break;
+        }
+        case PHY_RECEIVE_DELAY2:
+        {
+            phyParam.Value = US915_RECEIVE_DELAY2;
+            break;
+        }
+        case PHY_JOIN_ACCEPT_DELAY1:
+        {
+            phyParam.Value = US915_JOIN_ACCEPT_DELAY1;
+            break;
+        }
+        case PHY_JOIN_ACCEPT_DELAY2:
+        {
+            phyParam.Value = US915_JOIN_ACCEPT_DELAY2;
+            break;
+        }
+        case PHY_MAX_FCNT_GAP:
+        {
+            phyParam.Value = US915_MAX_FCNT_GAP;
+            break;
+        }
+        case PHY_ACK_TIMEOUT:
+        {
+            phyParam.Value = ( US915_ACKTIMEOUT + randr( -US915_ACK_TIMEOUT_RND, US915_ACK_TIMEOUT_RND ) );
+            break;
+        }
+        case PHY_DEF_DR1_OFFSET:
+        {
+            phyParam.Value = US915_DEFAULT_RX1_DR_OFFSET;
+            break;
+        }
+        case PHY_DEF_RX2_FREQUENCY:
+        {
+            phyParam.Value = US915_RX_WND_2_FREQ;
+            break;
+        }
+        case PHY_DEF_RX2_DR:
+        {
+            phyParam.Value = US915_RX_WND_2_DR;
+            break;
+        }
+        case PHY_CHANNELS_MASK:
+        {
+            phyParam.ChannelsMask = ChannelsMask;
+            break;
+        }
+        case PHY_CHANNELS_DEFAULT_MASK:
+        {
+            phyParam.ChannelsMask = ChannelsDefaultMask;
+            break;
+        }
+        case PHY_MAX_NB_CHANNELS:
+        {
+            phyParam.Value = US915_MAX_NB_CHANNELS;
+            break;
+        }
+        case PHY_CHANNELS:
+        {
+            phyParam.Channels = Channels;
+            break;
+        }
+        case PHY_DEF_UPLINK_DWELL_TIME:
+        case PHY_DEF_DOWNLINK_DWELL_TIME:
+        {
+            phyParam.Value = 0;
+            break;
+        }
+        case PHY_DEF_MAX_EIRP:
+        case PHY_DEF_ANTENNA_GAIN:
+        {
+            phyParam.fValue = 0;
+            break;
+        }
+        case PHY_NB_JOIN_TRIALS:
+        case PHY_DEF_NB_JOIN_TRIALS:
+        {
+            phyParam.Value = 2;
+            break;
+        }
+        case PHY_BEACON_INTERVAL:
+        {
+            phyParam.Value = US915_BEACON_INTERVAL;
+            break;
+        }
+        case PHY_BEACON_RESERVED:
+        {
+            phyParam.Value = US915_BEACON_RESERVED;
+            break;
+        }
+        case PHY_BEACON_GUARD:
+        {
+            phyParam.Value = US915_BEACON_GUARD;
+            break;
+        }
+        case PHY_BEACON_WINDOW:
+        {
+            phyParam.Value = US915_BEACON_WINDOW;
+            break;
+        }
+        case PHY_BEACON_WINDOW_SLOTS:
+        {
+            phyParam.Value = US915_BEACON_WINDOW_SLOTS;
+            break;
+        }
+        case PHY_PING_SLOT_WINDOW:
+        {
+            phyParam.Value = US915_PING_SLOT_WINDOW;
+            break;
+        }
+        case PHY_BEACON_SYMBOL_TO_DEFAULT:
+        {
+            phyParam.Value = US915_BEACON_SYMBOL_TO_DEFAULT;
+            break;
+        }
+        case PHY_BEACON_SYMBOL_TO_EXPANSION_MAX:
+        {
+            phyParam.Value = US915_BEACON_SYMBOL_TO_EXPANSION_MAX;
+            break;
+        }
+        case PHY_PING_SLOT_SYMBOL_TO_EXPANSION_MAX:
+        {
+            phyParam.Value = US915_PING_SLOT_SYMBOL_TO_EXPANSION_MAX;
+            break;
+        }
+        case PHY_BEACON_SYMBOL_TO_EXPANSION_FACTOR:
+        {
+            phyParam.Value = US915_BEACON_SYMBOL_TO_EXPANSION_FACTOR;
+            break;
+        }
+        case PHY_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR:
+        {
+            phyParam.Value = US915_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR;
+            break;
+        }
+        case PHY_MAX_BEACON_LESS_PERIOD:
+        {
+            phyParam.Value = US915_MAX_BEACON_LESS_PERIOD;
+            break;
+        }
+        case PHY_BEACON_DELAY_BEACON_TIMING_ANS:
+        {
+            phyParam.Value = US915_BEACON_DELAY_BEACON_TIMING_ANS;
+            break;
+        }
+        case PHY_PINGSLOT_CHANNEL_FREQ:
+        {
+            phyParam.Value = US915_BEACON_CHANNEL_FREQ;
+            break;
+        }
+        case PHY_BEACON_SIZE:
+        {
+            phyParam.Value = US915_BEACON_SIZE;
+            break;
+        }
+        case PHY_BEACON_CHANNEL_DR:
+        {
+            phyParam.Value = US915_BEACON_CHANNEL_DR;
+            break;
+        }
+        default:
+        {
+            break;
+        }
+    }
+
+    return phyParam;
+}
+
+void RegionUS915SetBandTxDone( SetBandTxDoneParams_t* txDone )
+{
+    RegionCommonSetBandTxDone( txDone->Joined, &Bands[Channels[txDone->Channel].Band], txDone->LastTxDoneTime );
+}
+
+void RegionUS915InitDefaults( InitType_t type )
+{
+    switch( type )
+    {
+        case INIT_TYPE_INIT:
+        {
+            // Channels
+            // 125 kHz channels
+            for( uint8_t i = 0; i < US915_MAX_NB_CHANNELS - 8; i++ )
+            {
+                Channels[i].Frequency = 902300000 + i * 200000;
+                Channels[i].DrRange.Value = ( DR_3 << 4 ) | DR_0;
+                Channels[i].Band = 0;
+            }
+            // 500 kHz channels
+            for( uint8_t i = US915_MAX_NB_CHANNELS - 8; i < US915_MAX_NB_CHANNELS; i++ )
+            {
+                Channels[i].Frequency = 903000000 + ( i - ( US915_MAX_NB_CHANNELS - 8 ) ) * 1600000;
+                Channels[i].DrRange.Value = ( DR_4 << 4 ) | DR_4;
+                Channels[i].Band = 0;
+            }
+
+            // ChannelsMask
+            ChannelsDefaultMask[0] = 0xFFFF;
+            ChannelsDefaultMask[1] = 0xFFFF;
+            ChannelsDefaultMask[2] = 0xFFFF;
+            ChannelsDefaultMask[3] = 0xFFFF;
+            ChannelsDefaultMask[4] = 0x00FF;
+            ChannelsDefaultMask[5] = 0x0000;
+
+            // Copy channels default mask
+            RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 6 );
+
+            // Copy into channels mask remaining
+            RegionCommonChanMaskCopy( ChannelsMaskRemaining, ChannelsMask, 6 );
+            break;
+        }
+        case INIT_TYPE_RESTORE:
+        {
+            // Copy channels default mask
+            RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 6 );
+
+            for( uint8_t i = 0; i < 6; i++ )
+            { // Copy-And the channels mask
+                ChannelsMaskRemaining[i] &= ChannelsMask[i];
+            }
+            break;
+        }
+        default:
+        {
+            break;
+        }
+    }
+}
+
+bool RegionUS915Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute )
+{
+    switch( phyAttribute )
+    {
+        case PHY_TX_DR:
+        {
+            return RegionCommonValueInRange( verify->DatarateParams.Datarate, US915_TX_MIN_DATARATE, US915_TX_MAX_DATARATE );
+        }
+        case PHY_DEF_TX_DR:
+        {
+            return RegionCommonValueInRange( verify->DatarateParams.Datarate, DR_0, DR_5 );
+        }
+        case PHY_RX_DR:
+        {
+            return RegionCommonValueInRange( verify->DatarateParams.Datarate, US915_RX_MIN_DATARATE, US915_RX_MAX_DATARATE );
+        }
+        case PHY_DEF_TX_POWER:
+        case PHY_TX_POWER:
+        {
+            // Remark: switched min and max!
+            return RegionCommonValueInRange( verify->TxPower, US915_MAX_TX_POWER, US915_MIN_TX_POWER );
+        }
+        case PHY_DUTY_CYCLE:
+        {
+            return US915_DUTY_CYCLE_ENABLED;
+        }
+        case PHY_NB_JOIN_TRIALS:
+        {
+            if( verify->NbJoinTrials < 2 )
+            {
+                return false;
+            }
+            break;
+        }
+        default:
+            return false;
+    }
+    return true;
+}
+
+void RegionUS915ApplyCFList( ApplyCFListParams_t* applyCFList )
+{
+    return;
+}
+
+bool RegionUS915ChanMaskSet( ChanMaskSetParams_t* chanMaskSet )
+{
+    uint8_t nbChannels = RegionCommonCountChannels( chanMaskSet->ChannelsMaskIn, 0, 4 );
+
+    // Check the number of active channels
+    if( ( nbChannels < 2 ) &&
+        ( nbChannels > 0 ) )
+    {
+        return false;
+    }
+
+    switch( chanMaskSet->ChannelsMaskType )
+    {
+        case CHANNELS_MASK:
+        {
+            RegionCommonChanMaskCopy( ChannelsMask, chanMaskSet->ChannelsMaskIn, 6 );
+
+            for( uint8_t i = 0; i < 6; i++ )
+            { // Copy-And the channels mask
+                ChannelsMaskRemaining[i] &= ChannelsMask[i];
+            }
+            break;
+        }
+        case CHANNELS_DEFAULT_MASK:
+        {
+            RegionCommonChanMaskCopy( ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 6 );
+            break;
+        }
+        default:
+            return false;
+    }
+    return true;
+}
+
+bool RegionUS915AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter )
+{
+    bool adrAckReq = false;
+    int8_t datarate = adrNext->Datarate;
+    int8_t txPower = adrNext->TxPower;
+    GetPhyParams_t getPhy;
+    PhyParam_t phyParam;
+
+    // Report back the adr ack counter
+    *adrAckCounter = adrNext->AdrAckCounter;
+
+    if( adrNext->AdrEnabled == true )
+    {
+        if( datarate == US915_TX_MIN_DATARATE )
+        {
+            *adrAckCounter = 0;
+            adrAckReq = false;
+        }
+        else
+        {
+            if( adrNext->AdrAckCounter >= US915_ADR_ACK_LIMIT )
+            {
+                adrAckReq = true;
+                txPower = US915_MAX_TX_POWER;
+            }
+            else
+            {
+                adrAckReq = false;
+            }
+            if( adrNext->AdrAckCounter >= ( US915_ADR_ACK_LIMIT + US915_ADR_ACK_DELAY ) )
+            {
+                if( ( adrNext->AdrAckCounter % US915_ADR_ACK_DELAY ) == 1 )
+                {
+                    // Decrease the datarate
+                    getPhy.Attribute = PHY_NEXT_LOWER_TX_DR;
+                    getPhy.Datarate = datarate;
+                    getPhy.UplinkDwellTime = adrNext->UplinkDwellTime;
+                    phyParam = RegionUS915GetPhyParam( &getPhy );
+                    datarate = phyParam.Value;
+
+                    if( datarate == US915_TX_MIN_DATARATE )
+                    {
+                        // We must set adrAckReq to false as soon as we reach the lowest datarate
+                        adrAckReq = false;
+                        if( adrNext->UpdateChanMask == true )
+                        {
+                            // Re-enable default channels
+                            ChannelsMask[0] = 0xFFFF;
+                            ChannelsMask[1] = 0xFFFF;
+                            ChannelsMask[2] = 0xFFFF;
+                            ChannelsMask[3] = 0xFFFF;
+                            ChannelsMask[4] = 0x00FF;
+                            ChannelsMask[5] = 0x0000;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    *drOut = datarate;
+    *txPowOut = txPower;
+    return adrAckReq;
+}
+
+void RegionUS915ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams )
+{
+    double tSymbol = 0.0;
+
+    // Get the datarate, perform a boundary check
+    rxConfigParams->Datarate = MIN( datarate, US915_RX_MAX_DATARATE );
+    rxConfigParams->Bandwidth = GetBandwidth( rxConfigParams->Datarate );
+
+    tSymbol = RegionCommonComputeSymbolTimeLoRa( DataratesUS915[rxConfigParams->Datarate], BandwidthsUS915[rxConfigParams->Datarate] );
+
+    RegionCommonComputeRxWindowParameters( tSymbol, minRxSymbols, rxError, RADIO_WAKEUP_TIME, &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset );
+}
+
+bool RegionUS915RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate )
+{
+    int8_t dr = rxConfig->Datarate;
+    uint8_t maxPayload = 0;
+    int8_t phyDr = 0;
+    uint32_t frequency = rxConfig->Frequency;
+
+    if( Radio.GetStatus( ) != RF_IDLE )
+    {
+        return false;
+    }
+
+    if( rxConfig->Window == 0 )
+    {
+        // Apply window 1 frequency
+        frequency = US915_FIRST_RX1_CHANNEL + ( rxConfig->Channel % 8 ) * US915_STEPWIDTH_RX1_CHANNEL;
+    }
+
+    // Read the physical datarate from the datarates table
+    phyDr = DataratesUS915[dr];
+
+    Radio.SetChannel( frequency );
+
+    // Radio configuration
+    Radio.SetRxConfig( MODEM_LORA, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous );
+
+    if( rxConfig->RepeaterSupport == true )
+    {
+        maxPayload = MaxPayloadOfDatarateRepeaterUS915[dr];
+    }
+    else
+    {
+        maxPayload = MaxPayloadOfDatarateUS915[dr];
+    }
+    Radio.SetMaxPayloadLength( MODEM_LORA, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD );
+
+    *datarate = (uint8_t) dr;
+    return true;
+}
+
+bool RegionUS915TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir )
+{
+    int8_t phyDr = DataratesUS915[txConfig->Datarate];
+    int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, Bands[Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, ChannelsMask );
+    uint32_t bandwidth = GetBandwidth( txConfig->Datarate );
+    int8_t phyTxPower = 0;
+
+    // Calculate physical TX power
+    phyTxPower = RegionCommonComputeTxPower( txPowerLimited, US915_DEFAULT_MAX_ERP, 0 );
+
+    // Setup the radio frequency
+    Radio.SetChannel( Channels[txConfig->Channel].Frequency );
+
+    Radio.SetTxConfig( MODEM_LORA, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 3000 );
+
+    // Setup maximum payload lenght of the radio driver
+    Radio.SetMaxPayloadLength( MODEM_LORA, txConfig->PktLen );
+    // Get the time-on-air of the next tx frame
+    *txTimeOnAir = Radio.TimeOnAir( MODEM_LORA, txConfig->PktLen );
+    *txPower = txPowerLimited;
+
+    return true;
+}
+
+uint8_t RegionUS915LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed )
+{
+    uint8_t status = 0x07;
+    RegionCommonLinkAdrParams_t linkAdrParams;
+    uint8_t nextIndex = 0;
+    uint8_t bytesProcessed = 0;
+    uint16_t channelsMask[6] = { 0, 0, 0, 0, 0, 0 };
+    GetPhyParams_t getPhy;
+    PhyParam_t phyParam;
+    RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams;
+
+    // Initialize local copy of channels mask
+    RegionCommonChanMaskCopy( channelsMask, ChannelsMask, 6 );
+
+    while( bytesProcessed < linkAdrReq->PayloadSize )
+    {
+        nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams );
+
+        if( nextIndex == 0 )
+            break; // break loop, since no more request has been found
+
+        // Update bytes processed
+        bytesProcessed += nextIndex;
+
+        // Revert status, as we only check the last ADR request for the channel mask KO
+        status = 0x07;
+
+        if( linkAdrParams.ChMaskCtrl == 6 )
+        {
+            // Enable all 125 kHz channels
+            channelsMask[0] = 0xFFFF;
+            channelsMask[1] = 0xFFFF;
+            channelsMask[2] = 0xFFFF;
+            channelsMask[3] = 0xFFFF;
+            // Apply chMask to channels 64 to 71
+            channelsMask[4] = linkAdrParams.ChMask;
+        }
+        else if( linkAdrParams.ChMaskCtrl == 7 )
+        {
+            // Disable all 125 kHz channels
+            channelsMask[0] = 0x0000;
+            channelsMask[1] = 0x0000;
+            channelsMask[2] = 0x0000;
+            channelsMask[3] = 0x0000;
+            // Apply chMask to channels 64 to 71
+            channelsMask[4] = linkAdrParams.ChMask;
+        }
+        else if( linkAdrParams.ChMaskCtrl == 5 )
+        {
+            // RFU
+            status &= 0xFE; // Channel mask KO
+        }
+        else
+        {
+            channelsMask[linkAdrParams.ChMaskCtrl] = linkAdrParams.ChMask;
+        }
+    }
+
+    // FCC 15.247 paragraph F mandates to hop on at least 2 125 kHz channels
+    if( ( linkAdrParams.Datarate < DR_4 ) && ( RegionCommonCountChannels( channelsMask, 0, 4 ) < 2 ) )
+    {
+        status &= 0xFE; // Channel mask KO
+    }
+
+    // Get the minimum possible datarate
+    getPhy.Attribute = PHY_MIN_TX_DR;
+    getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime;
+    phyParam = RegionUS915GetPhyParam( &getPhy );
+
+    linkAdrVerifyParams.Status = status;
+    linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled;
+    linkAdrVerifyParams.Datarate = linkAdrParams.Datarate;
+    linkAdrVerifyParams.TxPower = linkAdrParams.TxPower;
+    linkAdrVerifyParams.NbRep = linkAdrParams.NbRep;
+    linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate;
+    linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower;
+    linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep;
+    linkAdrVerifyParams.NbChannels = US915_MAX_NB_CHANNELS;
+    linkAdrVerifyParams.ChannelsMask = channelsMask;
+    linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value;
+    linkAdrVerifyParams.MaxDatarate = US915_TX_MAX_DATARATE;
+    linkAdrVerifyParams.Channels = Channels;
+    linkAdrVerifyParams.MinTxPower = US915_MIN_TX_POWER;
+    linkAdrVerifyParams.MaxTxPower = US915_MAX_TX_POWER;
+
+    // Verify the parameters and update, if necessary
+    status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep );
+
+    // Update channelsMask if everything is correct
+    if( status == 0x07 )
+    {
+        // Copy Mask
+        RegionCommonChanMaskCopy( ChannelsMask, channelsMask, 6 );
+
+        ChannelsMaskRemaining[0] &= ChannelsMask[0];
+        ChannelsMaskRemaining[1] &= ChannelsMask[1];
+        ChannelsMaskRemaining[2] &= ChannelsMask[2];
+        ChannelsMaskRemaining[3] &= ChannelsMask[3];
+        ChannelsMaskRemaining[4] = ChannelsMask[4];
+        ChannelsMaskRemaining[5] = ChannelsMask[5];
+    }
+
+    // Update status variables
+    *drOut = linkAdrParams.Datarate;
+    *txPowOut = linkAdrParams.TxPower;
+    *nbRepOut = linkAdrParams.NbRep;
+    *nbBytesParsed = bytesProcessed;
+
+    return status;
+}
+
+uint8_t RegionUS915RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq )
+{
+    uint8_t status = 0x07;
+    uint32_t freq = rxParamSetupReq->Frequency;
+
+    // Verify radio frequency
+    if( ( Radio.CheckRfFrequency( freq ) == false ) ||
+        ( freq < US915_FIRST_RX1_CHANNEL ) ||
+        ( freq > US915_LAST_RX1_CHANNEL ) ||
+        ( ( ( freq - ( uint32_t ) US915_FIRST_RX1_CHANNEL ) % ( uint32_t ) US915_STEPWIDTH_RX1_CHANNEL ) != 0 ) )
+    {
+        status &= 0xFE; // Channel frequency KO
+    }
+
+    // Verify datarate
+    if( RegionCommonValueInRange( rxParamSetupReq->Datarate, US915_RX_MIN_DATARATE, US915_RX_MAX_DATARATE ) == false )
+    {
+        status &= 0xFD; // Datarate KO
+    }
+    if( ( RegionCommonValueInRange( rxParamSetupReq->Datarate, DR_5, DR_7 ) == true ) ||
+        ( rxParamSetupReq->Datarate > DR_13 ) )
+    {
+        status &= 0xFD; // Datarate KO
+    }
+
+    // Verify datarate offset
+    if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, US915_MIN_RX1_DR_OFFSET, US915_MAX_RX1_DR_OFFSET ) == false )
+    {
+        status &= 0xFB; // Rx1DrOffset range KO
+    }
+
+    return status;
+}
+
+uint8_t RegionUS915NewChannelReq( NewChannelReqParams_t* newChannelReq )
+{
+    // Datarate and frequency KO
+    return 0;
+}
+
+int8_t RegionUS915TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq )
+{
+    return -1;
+}
+
+uint8_t RegionUS915DlChannelReq( DlChannelReqParams_t* dlChannelReq )
+{
+    return 0;
+}
+
+int8_t RegionUS915AlternateDr( AlternateDrParams_t* alternateDr )
+{
+    int8_t datarate = 0;
+
+    // Re-enable 500 kHz default channels
+    ChannelsMask[4] = 0x00FF;
+
+    if( ( alternateDr->NbTrials & 0x01 ) == 0x01 )
+    {
+        datarate = DR_4;
+    }
+    else
+    {
+        datarate = DR_0;
+    }
+    return datarate;
+}
+
+void RegionUS915CalcBackOff( CalcBackOffParams_t* calcBackOff )
+{
+    RegionCommonCalcBackOffParams_t calcBackOffParams;
+
+    calcBackOffParams.Channels = Channels;
+    calcBackOffParams.Bands = Bands;
+    calcBackOffParams.LastTxIsJoinRequest = calcBackOff->LastTxIsJoinRequest;
+    calcBackOffParams.Joined = calcBackOff->Joined;
+    calcBackOffParams.DutyCycleEnabled = calcBackOff->DutyCycleEnabled;
+    calcBackOffParams.Channel = calcBackOff->Channel;
+    calcBackOffParams.ElapsedTime = calcBackOff->ElapsedTime;
+    calcBackOffParams.TxTimeOnAir = calcBackOff->TxTimeOnAir;
+
+    RegionCommonCalcBackOff( &calcBackOffParams );
+}
+
+bool RegionUS915NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff )
+{
+    uint8_t nbEnabledChannels = 0;
+    uint8_t delayTx = 0;
+    uint8_t enabledChannels[US915_MAX_NB_CHANNELS] = { 0 };
+    TimerTime_t nextTxDelay = 0;
+
+    // Count 125kHz channels
+    if( RegionCommonCountChannels( ChannelsMaskRemaining, 0, 4 ) == 0 )
+    { // Reactivate default channels
+        RegionCommonChanMaskCopy( ChannelsMaskRemaining, ChannelsMask, 4  );
+    }
+    // Check other channels
+    if( nextChanParams->Datarate >= DR_4 )
+    {
+        if( ( ChannelsMaskRemaining[4] & 0x00FF ) == 0 )
+        {
+            ChannelsMaskRemaining[4] = ChannelsMask[4];
+        }
+    }
+
+    if( nextChanParams->AggrTimeOff <= TimerGetElapsedTime( nextChanParams->LastAggrTx ) )
+    {
+        // Reset Aggregated time off
+        *aggregatedTimeOff = 0;
+
+        // Update bands Time OFF
+        nextTxDelay = RegionCommonUpdateBandTimeOff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, Bands, US915_MAX_NB_BANDS );
+
+        // Search how many channels are enabled
+        nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Datarate,
+                                                      ChannelsMaskRemaining, Channels,
+                                                      Bands, enabledChannels, &delayTx );
+    }
+    else
+    {
+        delayTx++;
+        nextTxDelay = nextChanParams->AggrTimeOff - TimerGetElapsedTime( nextChanParams->LastAggrTx );
+    }
+
+    if( nbEnabledChannels > 0 )
+    {
+        // We found a valid channel
+        *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )];
+        // Disable the channel in the mask
+        RegionCommonChanDisable( ChannelsMaskRemaining, *channel, US915_MAX_NB_CHANNELS - 8 );
+
+        *time = 0;
+        return true;
+    }
+    else
+    {
+        if( delayTx > 0 )
+        {
+            // Delay transmission due to AggregatedTimeOff or to a band time off
+            *time = nextTxDelay;
+            return true;
+        }
+        // Datarate not supported by any channel
+        *time = 0;
+        return false;
+    }
+}
+
+LoRaMacStatus_t RegionUS915ChannelAdd( ChannelAddParams_t* channelAdd )
+{
+    return LORAMAC_STATUS_PARAMETER_INVALID;
+}
+
+bool RegionUS915ChannelsRemove( ChannelRemoveParams_t* channelRemove  )
+{
+    return LORAMAC_STATUS_PARAMETER_INVALID;
+}
+
+void RegionUS915SetContinuousWave( ContinuousWaveParams_t* continuousWave )
+{
+    int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, Bands[Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, ChannelsMask );
+    int8_t phyTxPower = 0;
+    uint32_t frequency = Channels[continuousWave->Channel].Frequency;
+
+    // Calculate physical TX power
+    phyTxPower = RegionCommonComputeTxPower( txPowerLimited, US915_DEFAULT_MAX_ERP, 0 );
+
+    Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout );
+}
+
+uint8_t RegionUS915ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset )
+{
+    int8_t datarate = DatarateOffsetsUS915[dr][drOffset];
+
+    if( datarate < 0 )
+    {
+        datarate = DR_0;
+    }
+    return datarate;
+}
+
+void RegionUS915RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr, bool *beaconChannelSet )
+{
+    RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup;
+
+    regionCommonRxBeaconSetup.Datarates = DataratesUS915;
+    regionCommonRxBeaconSetup.ChannelPlanFrequency = ( 923.3e6 + ( BeaconChannel( 0, rxBeaconSetup->BeaconTime, rxBeaconSetup->BeaconInterval ) * 600e3 ) );
+    regionCommonRxBeaconSetup.BeaconTimingAnsFrequency = ( 923.3e6 + ( rxBeaconSetup->BeaconTimingChannel * 600e3 ) );
+    regionCommonRxBeaconSetup.BeaconSize = US915_BEACON_SIZE;
+    regionCommonRxBeaconSetup.BeaconDatarate = US915_BEACON_CHANNEL_DR;
+    regionCommonRxBeaconSetup.BeaconChannelBW = US915_BEACON_CHANNEL_BW;
+    regionCommonRxBeaconSetup.CustomFrequency = rxBeaconSetup->CustomFrequency;
+    regionCommonRxBeaconSetup.CustomFrequencyEnabled = rxBeaconSetup->CustomFrequencyEnabled;
+    regionCommonRxBeaconSetup.BeaconChannelSet = rxBeaconSetup->BeaconChannelSet;
+    regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime;
+    regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout;
+
+    RegionCommonRxBeaconSetup( &regionCommonRxBeaconSetup, beaconChannelSet );
+
+    // Store downlink datarate
+    *outDr = US915_BEACON_CHANNEL_DR;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/region/RegionUS915.h	Wed Aug 09 16:20:21 2017 -0400
@@ -0,0 +1,538 @@
+/*!
+ * \file      RegionUS915.h
+ *
+ * \brief     Region definition for US915
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ *                ______                              _
+ *               / _____)             _              | |
+ *              ( (____  _____ ____ _| |_ _____  ____| |__
+ *               \____ \| ___ |    (_   _) ___ |/ ___)  _ \
+ *               _____) ) ____| | | || |_| ____( (___| | | |
+ *              (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ *              (C)2013 Semtech
+ *
+ *               ___ _____ _   ___ _  _____ ___  ___  ___ ___
+ *              / __|_   _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ *              \__ \ | |/ _ \ (__| ' <| _| (_) |   / (__| _|
+ *              |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ *              embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author    Miguel Luis ( Semtech )
+ *
+ * \author    Gregory Cristian ( Semtech )
+ *
+ * \author    Daniel Jaeckle ( STACKFORCE )
+ *
+ * \defgroup  REGIONUS915 Region US915
+ *            Implementation according to LoRaWAN Specification v1.0.2.
+ * \{
+ */
+#ifndef __REGION_US915_H__
+#define __REGION_US915_H__
+
+/*!
+ * LoRaMac maximum number of channels
+ */
+#define US915_MAX_NB_CHANNELS                       72
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define US915_TX_MIN_DATARATE                       DR_0
+
+/*!
+ * Maximal datarate that can be used by the node
+ */
+#define US915_TX_MAX_DATARATE                       DR_4
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define US915_RX_MIN_DATARATE                       DR_8
+
+/*!
+ * Maximal datarate that can be used by the node
+ */
+#define US915_RX_MAX_DATARATE                       DR_13
+
+/*!
+ * Default datarate used by the node
+ */
+#define US915_DEFAULT_DATARATE                      DR_0
+
+/*!
+ * Minimal Rx1 receive datarate offset
+ */
+#define US915_MIN_RX1_DR_OFFSET                     0
+
+/*!
+ * Maximal Rx1 receive datarate offset
+ */
+#define US915_MAX_RX1_DR_OFFSET                     3
+
+/*!
+ * Default Rx1 receive datarate offset
+ */
+#define US915_DEFAULT_RX1_DR_OFFSET                 0
+
+/*!
+ * Minimal Tx output power that can be used by the node
+ */
+#define US915_MIN_TX_POWER                          TX_POWER_10
+
+/*!
+ * Maximal Tx output power that can be used by the node
+ */
+#define US915_MAX_TX_POWER                          TX_POWER_0
+
+/*!
+ * Default Tx output power used by the node
+ */
+#define US915_DEFAULT_TX_POWER                      TX_POWER_0
+
+/*!
+ * Default Max ERP
+ */
+#define US915_DEFAULT_MAX_ERP                      30.0f
+
+/*!
+ * ADR Ack limit
+ */
+#define US915_ADR_ACK_LIMIT                         64
+
+/*!
+ * ADR Ack delay
+ */
+#define US915_ADR_ACK_DELAY                         32
+
+/*!
+ * Enabled or disabled the duty cycle
+ */
+#define US915_DUTY_CYCLE_ENABLED                    0
+
+/*!
+ * Maximum RX window duration
+ */
+#define US915_MAX_RX_WINDOW                         3000
+
+/*!
+ * Receive delay 1
+ */
+#define US915_RECEIVE_DELAY1                        1000
+
+/*!
+ * Receive delay 2
+ */
+#define US915_RECEIVE_DELAY2                        2000
+
+/*!
+ * Join accept delay 1
+ */
+#define US915_JOIN_ACCEPT_DELAY1                    5000
+
+/*!
+ * Join accept delay 2
+ */
+#define US915_JOIN_ACCEPT_DELAY2                    6000
+
+/*!
+ * Maximum frame counter gap
+ */
+#define US915_MAX_FCNT_GAP                          16384
+
+/*!
+ * Ack timeout
+ */
+#define US915_ACKTIMEOUT                            2000
+
+/*!
+ * Random ack timeout limits
+ */
+#define US915_ACK_TIMEOUT_RND                       1000
+
+/*!
+ * Second reception window channel frequency definition.
+ */
+#define US915_RX_WND_2_FREQ                         923300000
+
+/*!
+ * Second reception window channel datarate definition.
+ */
+#define US915_RX_WND_2_DR                           DR_8
+
+/*
+ * CLASS B
+ */
+/*!
+ * Beacon interval in ms
+ */
+#define US915_BEACON_INTERVAL                       128000
+
+/*!
+ * Beacon reserved time in ms
+ */
+#define US915_BEACON_RESERVED                       2120
+
+/*!
+ * Beacon guard time in ms
+ */
+#define US915_BEACON_GUARD                          3000
+
+/*!
+ * Beacon window time in ms
+ */
+#define US915_BEACON_WINDOW                         122880
+
+/*!
+ * Beacon window time in numer of slots
+ */
+#define US915_BEACON_WINDOW_SLOTS                   4096
+
+/*!
+ * Ping slot length time in ms
+ */
+#define US915_PING_SLOT_WINDOW                      30
+
+/*!
+ * Beacon frequency
+ */
+#define US915_BEACON_CHANNEL_FREQ                   923300000
+
+/*!
+ * Default symbol timeout for beacons and ping slot windows
+ */
+#define US915_BEACON_SYMBOL_TO_DEFAULT              8
+
+/*!
+ * Maximum symbol timeout for beacons
+ */
+#define US915_BEACON_SYMBOL_TO_EXPANSION_MAX        400
+
+/*!
+ * Maximum symbol timeout for ping slots
+ */
+#define US915_PING_SLOT_SYMBOL_TO_EXPANSION_MAX     40
+
+/*!
+ * Symbol expansion value for beacon windows in case of beacon
+ * loss in symbols
+ */
+#define US915_BEACON_SYMBOL_TO_EXPANSION_FACTOR     2
+
+/*!
+ * Symbol expansion value for ping slot windows in case of beacon
+ * loss in symbols
+ */
+#define US915_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR  2
+
+/*!
+ * Maximum allowed beacon less time in ms
+ */
+#define US915_MAX_BEACON_LESS_PERIOD                7200000
+
+/*!
+ * Delay time for the BeaconTimingAns in ms
+ */
+#define US915_BEACON_DELAY_BEACON_TIMING_ANS        30
+
+/*!
+ * Payload size of a beacon frame
+ */
+#define US915_BEACON_SIZE                           19
+
+/*!
+ * Datarate of the beacon channel
+ */
+#define US915_BEACON_CHANNEL_DR                     DR_8
+
+/*!
+ * Bandwith of the beacon channel
+ */
+#define US915_BEACON_CHANNEL_BW                     2
+
+/*!
+ * LoRaMac maximum number of bands
+ */
+#define US915_MAX_NB_BANDS                          1
+
+/*!
+ * Band 0 definition
+ * { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff }
+ */
+#define US915_BAND0                                 { 1, US915_MAX_TX_POWER, 0,  0 } //  100.0 %
+
+/*!
+ * Defines the first channel for RX window 1 for US band
+ */
+#define US915_FIRST_RX1_CHANNEL                     ( (uint32_t) 923300000 )
+
+/*!
+ * Defines the last channel for RX window 1 for US band
+ */
+#define US915_LAST_RX1_CHANNEL                      ( (uint32_t) 927500000 )
+
+/*!
+ * Defines the step width of the channels for RX window 1
+ */
+#define US915_STEPWIDTH_RX1_CHANNEL                 ( (uint32_t) 600000 )
+
+/*!
+ * Data rates table definition
+ */
+static const uint8_t DataratesUS915[]  = { 10, 9, 8,  7,  8,  0,  0, 0, 12, 11, 10, 9, 8, 7, 0, 0 };
+
+/*!
+ * Bandwidths table definition in Hz
+ */
+static const uint32_t BandwidthsUS915[] = { 125000, 125000, 125000, 125000, 500000, 0, 0, 0, 500000, 500000, 500000, 500000, 500000, 500000, 0, 0 };
+
+/*!
+ * Up/Down link data rates offset definition
+ */
+static const int8_t DatarateOffsetsUS915[5][4] =
+{
+    { DR_10, DR_9 , DR_8 , DR_8  }, // DR_0
+    { DR_11, DR_10, DR_9 , DR_8  }, // DR_1
+    { DR_12, DR_11, DR_10, DR_9  }, // DR_2
+    { DR_13, DR_12, DR_11, DR_10 }, // DR_3
+    { DR_13, DR_13, DR_12, DR_11 }, // DR_4
+};
+
+/*!
+ * Maximum payload with respect to the datarate index. Cannot operate with repeater.
+ */
+static const uint8_t MaxPayloadOfDatarateUS915[] = { 11, 53, 125, 242, 242, 0, 0, 0, 53, 129, 242, 242, 242, 242, 0, 0 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Can operate with repeater.
+ */
+static const uint8_t MaxPayloadOfDatarateRepeaterUS915[] = { 11, 53, 125, 242, 242, 0, 0, 0, 33, 109, 222, 222, 222, 222, 0, 0 };
+
+/*!
+ * \brief The function gets a value of a specific phy attribute.
+ *
+ * \param [IN] getPhy Pointer to the function parameters.
+ *
+ * \retval Returns a structure containing the PHY parameter.
+ */
+PhyParam_t RegionUS915GetPhyParam( GetPhyParams_t* getPhy );
+
+/*!
+ * \brief Updates the last TX done parameters of the current channel.
+ *
+ * \param [IN] txDone Pointer to the function parameters.
+ */
+void RegionUS915SetBandTxDone( SetBandTxDoneParams_t* txDone );
+
+/*!
+ * \brief Initializes the channels masks and the channels.
+ *
+ * \param [IN] type Sets the initialization type.
+ */
+void RegionUS915InitDefaults( InitType_t type );
+
+/*!
+ * \brief Verifies a parameter.
+ *
+ * \param [IN] verify Pointer to the function parameters.
+ *
+ * \param [IN] type Sets the initialization type.
+ *
+ * \retval Returns true, if the parameter is valid.
+ */
+bool RegionUS915Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute );
+
+/*!
+ * \brief The function parses the input buffer and sets up the channels of the
+ *        CF list.
+ *
+ * \param [IN] applyCFList Pointer to the function parameters.
+ */
+void RegionUS915ApplyCFList( ApplyCFListParams_t* applyCFList );
+
+/*!
+ * \brief Sets a channels mask.
+ *
+ * \param [IN] chanMaskSet Pointer to the function parameters.
+ *
+ * \retval Returns true, if the channels mask could be set.
+ */
+bool RegionUS915ChanMaskSet( ChanMaskSetParams_t* chanMaskSet );
+
+/*!
+ * \brief Calculates the next datarate to set, when ADR is on or off.
+ *
+ * \param [IN] adrNext Pointer to the function parameters.
+ *
+ * \param [OUT] drOut The calculated datarate for the next TX.
+ *
+ * \param [OUT] txPowOut The TX power for the next TX.
+ *
+ * \param [OUT] adrAckCounter The calculated ADR acknowledgement counter.
+ *
+ * \retval Returns true, if an ADR request should be performed.
+ */
+bool RegionUS915AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter );
+
+/*!
+ * Computes the Rx window timeout and offset.
+ *
+ * \param [IN] datarate     Rx window datarate index to be used
+ *
+ * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame.
+ *
+ * \param [IN] rxError      System maximum timing error of the receiver. In milliseconds
+ *                          The receiver will turn on in a [-rxError : +rxError] ms
+ *                          interval around RxOffset
+ *
+ * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields.
+ */
+void RegionUS915ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams );
+
+/*!
+ * \brief Configuration of the RX windows.
+ *
+ * \param [IN] rxConfig Pointer to the function parameters.
+ *
+ * \param [OUT] datarate The datarate index which was set.
+ *
+ * \retval Returns true, if the configuration was applied successfully.
+ */
+bool RegionUS915RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate );
+
+/*!
+ * \brief TX configuration.
+ *
+ * \param [IN] txConfig Pointer to the function parameters.
+ *
+ * \param [OUT] txPower The tx power index which was set.
+ *
+ * \param [OUT] txTimeOnAir The time-on-air of the frame.
+ *
+ * \retval Returns true, if the configuration was applied successfully.
+ */
+bool RegionUS915TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir );
+
+/*!
+ * \brief The function processes a Link ADR Request.
+ *
+ * \param [IN] linkAdrReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionUS915LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed );
+
+/*!
+ * \brief The function processes a RX Parameter Setup Request.
+ *
+ * \param [IN] rxParamSetupReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionUS915RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq );
+
+/*!
+ * \brief The function processes a Channel Request.
+ *
+ * \param [IN] newChannelReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionUS915NewChannelReq( NewChannelReqParams_t* newChannelReq );
+
+/*!
+ * \brief The function processes a TX ParamSetup Request.
+ *
+ * \param [IN] txParamSetupReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ *         Returns -1, if the functionality is not implemented. In this case, the end node
+ *         shall not process the command.
+ */
+int8_t RegionUS915TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq );
+
+/*!
+ * \brief The function processes a DlChannel Request.
+ *
+ * \param [IN] dlChannelReq Pointer to the function parameters.
+ *
+ * \retval Returns the status of the operation, according to the LoRaMAC specification.
+ */
+uint8_t RegionUS915DlChannelReq( DlChannelReqParams_t* dlChannelReq );
+
+/*!
+ * \brief Alternates the datarate of the channel for the join request.
+ *
+ * \param [IN] alternateDr Pointer to the function parameters.
+ *
+ * \retval Datarate to apply.
+ */
+int8_t RegionUS915AlternateDr( AlternateDrParams_t* alternateDr );
+
+/*!
+ * \brief Calculates the back-off time.
+ *
+ * \param [IN] calcBackOff Pointer to the function parameters.
+ */
+void RegionUS915CalcBackOff( CalcBackOffParams_t* calcBackOff );
+
+/*!
+ * \brief Searches and set the next random available channel
+ *
+ * \param [OUT] channel Next channel to use for TX.
+ *
+ * \param [OUT] time Time to wait for the next transmission according to the duty
+ *              cycle.
+ *
+ * \param [OUT] aggregatedTimeOff Updates the aggregated time off.
+ *
+ * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate]
+ */
+bool RegionUS915NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff );
+
+/*!
+ * \brief Adds a channel.
+ *
+ * \param [IN] channelAdd Pointer to the function parameters.
+ *
+ * \retval Status of the operation.
+ */
+LoRaMacStatus_t RegionUS915ChannelAdd( ChannelAddParams_t* channelAdd );
+
+/*!
+ * \brief Removes a channel.
+ *
+ * \param [IN] channelRemove Pointer to the function parameters.
+ *
+ * \retval Returns true, if the channel was removed successfully.
+ */
+bool RegionUS915ChannelsRemove( ChannelRemoveParams_t* channelRemove  );
+
+/*!
+ * \brief Sets the radio into continuous wave mode.
+ *
+ * \param [IN] continuousWave Pointer to the function parameters.
+ */
+void RegionUS915SetContinuousWave( ContinuousWaveParams_t* continuousWave );
+
+/*!
+ * \brief Computes new datarate according to the given offset
+ *
+ * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms
+ *
+ * \param [IN] dr Current datarate
+ *
+ * \param [IN] drOffset Offset to be applied
+ *
+ * \retval newDr Computed datarate.
+ */
+uint8_t RegionUS915ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset );
+
+/*! \} defgroup REGIONUS915 */
+
+#endif // __REGION_US915_H__