diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/adc.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/adc.h
new file mode 100644
index 00000000..e3aa4be1
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/adc.h
@@ -0,0 +1,59 @@
+/**
+ ******************************************************************************
+ * @file adc.h
+ * @author MCD Application Team
+ * @brief configuration of the ADC instances
+ ******************************************************************************
+ * @attention
+ *
+ *
© Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under Ultimate Liberty license
+ * SLA0044, the "License"; You may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at:
+ * www.st.com/SLA0044
+ *
+ ******************************************************************************
+ */
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __ADC_H__
+#define __ADC_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+#include "main.h"
+
+/* Exported types ------------------------------------------------------------*/
+/* Exported constants --------------------------------------------------------*/
+/* External variables --------------------------------------------------------*/
+
+/* USER CODE BEGIN Includes */
+
+/* USER CODE END Includes */
+#ifdef HAL_ADC_MODULE_ENABLED
+extern ADC_HandleTypeDef hadc1;
+#endif
+/* USER CODE BEGIN Private defines */
+
+/* USER CODE END Private defines */
+
+/* Exported macros -----------------------------------------------------------*/
+/* Defines -------------------------------------------------------------------*/
+/* Exported functions ------------------------------------------------------- */
+void MX_ADC1_Init(void);
+
+/* USER CODE BEGIN Prototypes */
+
+/* USER CODE END Prototypes */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ADC_H__ */
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/adc_if.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/adc_if.h
new file mode 100644
index 00000000..1e273376
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/adc_if.h
@@ -0,0 +1,81 @@
+/**
+ ******************************************************************************
+ * @file adc_if.h
+ * @author MCD Application Team
+ * @brief API and configuration of the adc_if.c instances
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under Ultimate Liberty license
+ * SLA0044, the "License"; You may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at:
+ * www.st.com/SLA0044
+ *
+ ******************************************************************************
+ */
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __ADC_IF_H__
+#define __ADC_IF_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/* Includes ------------------------------------------------------------------*/
+#include "main.h"
+#include "platform.h"
+
+/* Includes ------------------------------------------------------------------*/
+/* Exported types ------------------------------------------------------------*/
+/* Exported constants --------------------------------------------------------*/
+
+#define VDDA_VREFINT_CAL ((uint32_t) 3000)
+#define BAT_CR2032 ((uint32_t) 3000)
+#define VDD_BAT BAT_CR2032
+#define VDD_MIN 1800
+
+/* External variables --------------------------------------------------------*/
+/* Exported macros -----------------------------------------------------------*/
+/* Exported functions ------------------------------------------------------- */
+
+/*!
+ * @brief Initializes the ADC input
+ * @param none
+ * @retval none
+ */
+void SYS_InitMeasurement(void);
+
+/*!
+ * @brief DeInitializes the ADC
+ * @param none
+ * @retval none
+ */
+void SYS_DeInitMeasurement(void);
+
+/*!
+ * @brief Get the current temperature
+ * @param none
+ * @retval value temperature in degreeCelcius( q7.8 )
+ */
+int16_t SYS_GetTemperatureLevel(void);
+
+
+/*!
+ * @brief Get the current battery level
+ * @param none
+ * @retval value battery level in linear scale
+ */
+uint16_t SYS_GetBatteryLevel(void);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ADC_IF_H__ */
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
+
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/dma.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/dma.h
new file mode 100644
index 00000000..637c1297
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/dma.h
@@ -0,0 +1,63 @@
+/**
+ ******************************************************************************
+ * @file dma.h
+ * @author MCD Application Team
+ * @brief Header for driver dma.c module
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under Ultimate Liberty license
+ * SLA0044, the "License"; You may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at:
+ * www.st.com/SLA0044
+ *
+ ******************************************************************************
+ */
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __DMA_H__
+#define __DMA_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+#include "main.h"
+
+/* DMA memory to memory transfer handles -------------------------------------*/
+
+/* USER CODE BEGIN Includes */
+
+/* USER CODE END Includes */
+
+/* USER CODE BEGIN Private defines */
+
+/* USER CODE END Private defines */
+
+/**
+ * @brief Enables DMA controller clock
+ * @param None
+ * @retval None
+ *
+ */
+void MX_DMA_Init(void);
+
+/* USER CODE BEGIN Prototypes */
+
+/* USER CODE END Prototypes */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __DMA_H__ */
+
+/**
+ * @}
+ */
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/main.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/main.h
index e0a799e9..a233ce49 100644
--- a/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/main.h
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/main.h
@@ -50,6 +50,10 @@ extern "C" {
/* USER CODE END EM */
+#define RTC_N_PREDIV_S 10 /* subsecond number of bits */
+#define RTC_PREDIV_S ((1<© Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under Ultimate Liberty license
+ * SLA0044, the "License"; You may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at:
+ * www.st.com/SLA0044
+ *
+ ******************************************************************************
+ */
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __PLATFORM_H__
+#define __PLATFORM_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+#include
+#include "stm32wlxx.h"
+#include "stm32wlxx_ll_gpio.h"
+#include "main.h"
+#include "usart.h"
+#include "rtc.h"
+
+
+/* Exported types ------------------------------------------------------------*/
+/* Exported constants --------------------------------------------------------*/
+/* External variables --------------------------------------------------------*/
+/* Exported macros -----------------------------------------------------------*/
+/* Defines -------------------------------------------------------------------*/
+
+/* #warning "DBG LCH: VREFINT to be defined from AN" */
+
+
+/* Nothing for L4 but other platform might need it */
+#define __HAL_RCC_DBGMCU_CLK_ENABLE()
+#define __HAL_RCC_DBGMCU_CLK_DISABLE()
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __PLATFORM_H__ */
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/rtc.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/rtc.h
new file mode 100644
index 00000000..5057c3f9
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/rtc.h
@@ -0,0 +1,63 @@
+/**
+ ******************************************************************************
+ * @file rtc.h
+ * @author MCD Application Team
+ * @brief configuration of the RTC instances
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under Ultimate Liberty license
+ * SLA0044, the "License"; You may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at:
+ * www.st.com/SLA0044
+ *
+ ******************************************************************************
+ */
+
+#ifndef __RTC_H__
+#define __RTC_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+#include "main.h"
+
+/* USER CODE BEGIN Includes */
+
+/* USER CODE END Includes */
+
+extern RTC_HandleTypeDef hrtc;
+/* Defines ------------------------------------------------------------*/
+
+/* USER CODE BEGIN Private defines */
+
+/* USER CODE END Private defines */
+
+/* Exported types ------------------------------------------------------------*/
+/* Exported constants --------------------------------------------------------*/
+/* External variables --------------------------------------------------------*/
+/* Exported macros -----------------------------------------------------------*/
+/* Exported functions ------------------------------------------------------- */
+/**
+ * @brief Initializes the RTC
+ * @param None
+ * @retval None
+ */
+void MX_RTC_Init(void);
+
+/* USER CODE BEGIN Prototypes */
+
+/* USER CODE END Prototypes */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __RTC_H__ */
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/rtc_if.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/rtc_if.h
new file mode 100644
index 00000000..7e5c7fea
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/rtc_if.h
@@ -0,0 +1,156 @@
+/**
+ ******************************************************************************
+ * @file rtc_if.h
+ * @author MCD Application Team
+ * @brief configuration of the rtc_if.c instances
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under Ultimate Liberty license
+ * SLA0044, the "License"; You may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at:
+ * www.st.com/SLA0044
+ *
+ ******************************************************************************
+ */
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __RTC_IF_H__
+#define __RTC_IF_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+//#include "stm32_timer.h"
+/* Exported types ------------------------------------------------------------*/
+/* Exported constants --------------------------------------------------------*/
+/* External variables --------------------------------------------------------*/
+/* Exported macros -----------------------------------------------------------*/
+/* Exported functions ------------------------------------------------------- */
+/*!
+ * @brief Initializes the RTC timer
+ * @note The timer is based on the RTC
+ * @param none
+ * @retval none
+ */
+void RTC_Init( void );
+
+
+
+
+
+/*!
+ * @brief Return the minimum timeout in ticks the RTC is able to handle
+ * @param none
+ * @retval minimum value for a timeout in ticks
+ */
+uint32_t RTC_GetMinimumTimeout( void );
+
+/*!
+ * @brief Get the RTC timer elapsed time since the last Alarm was set
+ * @param none
+ * @retval RTC Elapsed time in ticks
+ */
+uint32_t RTC_GetTimerElapsedTime(void);
+
+/*!
+ * @brief Get the RTC timer value
+ * @param none
+ * @retval RTC Timer value in ticks
+ */
+uint32_t RTC_GetTimerValue( void );
+
+/*!
+ * @brief set Time Reference set also the RTC_DateStruct and RTC_TimeStruct
+ * @param none
+ * @retval Timer Reference Value in Ticks
+ */
+uint32_t RTC_SetTimerContext( void );
+
+/*!
+ * @brief Get the RTC timer Reference
+ * @param none
+ * @retval Timer Value in Ticks
+ */
+uint32_t RTC_GetTimerContext( void );
+
+/*!
+ * @brief RTC IRQ Handler on the RTC Alarm
+ * @param none
+ * @retval none
+ */
+void RTC_IrqHandler ( void );
+
+/*!
+ * @brief a delay of delay ms by polling RTC
+ * @param delay in ms
+ * @retval none
+ */
+void RTC_DelayMs(uint32_t delay);
+
+/*!
+ * @brief calculates the wake up time between wake up and mcu start
+ * @note resolution in RTC_ALARM_TIME_BASE in timer ticks
+ * @param none
+ * @retval none
+ */
+void RTC_setMcuWakeUpTime(void);
+
+/*!
+ * @brief returns the wake up time in us
+ * @param none
+ * @retval wake up time in ticks
+ */
+int16_t RTC_getMcuWakeUpTime( void );
+
+/*!
+ * @brief converts time in ms to time in ticks
+ * @param [IN] time in milliseconds
+ * @retval returns time in timer ticks
+ */
+uint32_t RTC_ms2Tick( uint32_t timeMicroSec );
+
+/*!
+ * @brief converts time in ticks to time in ms
+ * @param [IN] time in timer ticks
+ * @retval returns time in timer milliseconds
+ */
+uint32_t RTC_Tick2ms( uint32_t tick );
+
+/*!
+ * \brief Get system time
+ * \param [IN] subSeconds in ms
+ *
+ * \uint32_t seconds
+ */
+uint32_t HW_RTC_GetCalendarTime(uint16_t *subSeconds);
+
+/*!
+ * \brief Read from backup registers
+ * \param [IN] Data 0
+ * \param [IN] Data 1
+ *
+ */
+void HW_RTC_BKUPRead(uint32_t *Data0, uint32_t *Data1);
+
+/*!
+ * \brief Write in backup registers
+ * \param [IN] Data 0
+ * \param [IN] Data 1
+ *
+ */
+
+void HW_RTC_BKUPWrite(uint32_t Data0, uint32_t Data1);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __RTC_IF_H__ */
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/stm32_lpm_if.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/stm32_lpm_if.h
new file mode 100644
index 00000000..899404eb
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/stm32_lpm_if.h
@@ -0,0 +1,81 @@
+/**
+******************************************************************************
+ * @file stm32_lpm_if.h
+ * @author MCD Application Team
+ * @brief Header for stm32_lpm_f.c module (deveice specfic LP management)
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under Ultimate Liberty license
+ * SLA0044, the "License"; You may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at:
+ * www.st.com/SLA0044
+ *
+ ******************************************************************************
+ */
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __STM32_LPM_IF_H__
+#define __STM32_LPM_IF_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+#include "stm32_lpm.h"
+
+/**
+ * @brief Enters Low Power Off Mode
+ * @param none
+ * @retval none
+ */
+void PWR_EnterOffMode(void);
+/**
+ * @brief Exits Low Power Off Mode
+ * @param none
+ * @retval none
+ */
+void PWR_ExitOffMode(void);
+
+/**
+ * @brief Enters Low Power Stop Mode
+ * @note ARM exists the function when waking up
+ * @param none
+ * @retval none
+ */
+void PWR_EnterStopMode(void);
+/**
+ * @brief Exits Low Power Stop Mode
+ * @note Enable the pll at 32MHz
+ * @param none
+ * @retval none
+ */
+void PWR_ExitStopMode(void);
+
+/**
+ * @brief Enters Low Power Sleep Mode
+ * @note ARM exits the function when waking up
+ * @param none
+ * @retval none
+ */
+void PWR_EnterSleepMode(void);
+
+/**
+ * @brief Enters Low Power Sleep Mode
+ * @note ARM exits the function when waking up
+ * @param none
+ * @retval none
+ */
+void PWR_ExitSleepMode(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*__STM32_LPM_IF_H__ */
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/stm32wlxx_hal_conf.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/stm32wlxx_hal_conf.h
index c2c804f2..96a25727 100644
--- a/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/stm32wlxx_hal_conf.h
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/stm32wlxx_hal_conf.h
@@ -33,30 +33,30 @@
* @brief This is the list of modules to be used in the HAL driver
*/
#define HAL_MODULE_ENABLED
-/* #define HAL_ADC_MODULE_ENABLED */
-/* #define HAL_COMP_MODULE_ENABLED */
-/* #define HAL_CRC_MODULE_ENABLED */
-/* #define HAL_CRYP_MODULE_ENABLED */
-/* #define HAL_DAC_MODULE_ENABLED */
-/* #define HAL_GTZC_MODULE_ENABLED */
-/* #define HAL_HSEM_MODULE_ENABLED */
-/* #define HAL_I2C_MODULE_ENABLED */
-/* #define HAL_I2S_MODULE_ENABLED */
-/* #define HAL_IPCC_MODULE_ENABLED */
-/* #define HAL_IRDA_MODULE_ENABLED */
-/* #define HAL_IWDG_MODULE_ENABLED */
-/* #define HAL_LPTIM_MODULE_ENABLED */
-/* #define HAL_PKA_MODULE_ENABLED */
-/* #define HAL_RNG_MODULE_ENABLED */
-/* #define HAL_RTC_MODULE_ENABLED */
-/* #define HAL_SMARTCARD_MODULE_ENABLED */
-/* #define HAL_SMBUS_MODULE_ENABLED */
-/* #define HAL_SPI_MODULE_ENABLED */
-/* #define HAL_SUBGHZ_MODULE_ENABLED */
-/* #define HAL_TIM_MODULE_ENABLED */
+#define HAL_ADC_MODULE_ENABLED
+//#define HAL_COMP_MODULE_ENABLED
+//#define HAL_CRC_MODULE_ENABLED
+//#define HAL_CRYP_MODULE_ENABLED
+//#define HAL_DAC_MODULE_ENABLED
+//#define HAL_GTZC_MODULE_ENABLED
+//#define HAL_HSEM_MODULE_ENABLED
+//#define HAL_I2C_MODULE_ENABLED
+//#define HAL_I2S_MODULE_ENABLED
+//#define HAL_IPCC_MODULE_ENABLED
+//#define HAL_IRDA_MODULE_ENABLED
+//#define HAL_IWDG_MODULE_ENABLED
+#define HAL_LPTIM_MODULE_ENABLED
+//#define HAL_PKA_MODULE_ENABLED
+//#define HAL_RNG_MODULE_ENABLED
+#define HAL_RTC_MODULE_ENABLED
+//#define HAL_SMARTCARD_MODULE_ENABLED
+//#define HAL_SMBUS_MODULE_ENABLED
+#define HAL_SPI_MODULE_ENABLED
+#define HAL_SUBGHZ_MODULE_ENABLED
+//#define HAL_TIM_MODULE_ENABLED
#define HAL_UART_MODULE_ENABLED
-/* #define HAL_USART_MODULE_ENABLED */
-/* #define HAL_WWDG_MODULE_ENABLED */
+#define HAL_USART_MODULE_ENABLED
+//#define HAL_WWDG_MODULE_ENABLED
#define HAL_EXTI_MODULE_ENABLED
#define HAL_CORTEX_MODULE_ENABLED
#define HAL_DMA_MODULE_ENABLED
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/usart.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/usart.h
index e23715c2..b2380278 100644
--- a/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/usart.h
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/usart.h
@@ -1,26 +1,27 @@
/**
******************************************************************************
- * File Name : USART.h
- * Description : This file provides code for the configuration
- * of the USART instances.
+ * @file usart.h
+ * @author MCD Application Team
+ * @brief configuration of the USART instances
******************************************************************************
* @attention
*
* © Copyright (c) 2019 STMicroelectronics.
* All rights reserved.
*
- * This software component is licensed by ST under BSD 3-Clause license,
- * the "License"; You may not use this file except in compliance with the
- * License. You may obtain a copy of the License at:
- * opensource.org/licenses/BSD-3-Clause
+ * This software component is licensed by ST under Ultimate Liberty license
+ * SLA0044, the "License"; You may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at:
+ * www.st.com/SLA0044
*
******************************************************************************
*/
/* Define to prevent recursive inclusion -------------------------------------*/
-#ifndef __usart_H
-#define __usart_H
+#ifndef __USART_H__
+#define __USART_H__
+
#ifdef __cplusplus
- extern "C" {
+extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
@@ -30,34 +31,32 @@
/* USER CODE END Includes */
-extern UART_HandleTypeDef hlpuart1;
+
+/* Pin mapping */
+
+#define USARTx_TX_Pin GPIO_PIN_6
+#define USARTx_TX_GPIO_Port GPIOB
+
+#define USARTx_RX_Pin GPIO_PIN_7
+#define USARTx_RX_GPIO_Port GPIOB
+
+/* Not generated BY MX (?), was definied in usart_if.c */
+#define USARTx_RX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()
+#define USARTx_TX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()
+#define USARTx_CLK_ENABLE() __HAL_RCC_USART1_CLK_ENABLE()
+
+#define USARTx_TX_AF GPIO_AF7_USART1
+#define USARTx_RX_AF GPIO_AF7_USART1
+
extern UART_HandleTypeDef huart1;
-extern UART_HandleTypeDef huart2;
-/* USER CODE BEGIN Private defines */
-
-/* USER CODE END Private defines */
-
-void MX_LPUART1_UART_Init(void);
void MX_USART1_UART_Init(void);
-void MX_USART2_UART_Init(void);
-/* USER CODE BEGIN Prototypes */
-
-/* USER CODE END Prototypes */
-
#ifdef __cplusplus
}
#endif
-#endif /*__ usart_H */
-/**
- * @}
- */
-
-/**
- * @}
- */
+#endif /* __USART_H__*/
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/usart_if.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/usart_if.h
new file mode 100644
index 00000000..5c9aad60
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/usart_if.h
@@ -0,0 +1,70 @@
+/**
+ ******************************************************************************
+ * @file usart_if.h
+ * @author MCD Application Team
+ * @brief Header file for the UART driver handling for hyperterminal communication
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under Ultimate Liberty license
+ * SLA0044, the "License"; You may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at:
+ * www.st.com/SLA0044
+ *
+ ******************************************************************************
+ */
+
+#include "usart.h"
+#include "dma.h"
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __USART_IF_H__
+#define __USART_IF_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+/* Exported types ------------------------------------------------------------*/
+/* Exported constants --------------------------------------------------------*/
+/* External variables --------------------------------------------------------*/
+/* Exported functions ------------------------------------------------------- */
+
+/**
+* @brief Init the UART and associated DMA.
+* @param TxCpltCallback
+* @return none
+*/
+uint8_t vcom_Init(void (*cb)(void*));
+
+
+/**
+* @brief DeInit the UART and associated DMA.
+* @param none
+* @return none
+*/
+uint8_t vcom_DeInit(void);
+
+
+/**
+* @brief send buffer @p_data of size size to vcom using DMA
+* @param p_data data to be sent
+* @param szie of buffer p_data to be sent
+* @return none
+*/
+uint8_t vcom_Trace(uint8_t *p_data, uint16_t size);
+
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __USART_IF_H__ */
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Crypto/aes.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Crypto/aes.c
new file mode 100644
index 00000000..7f46ea37
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Crypto/aes.c
@@ -0,0 +1,936 @@
+/*
+ ---------------------------------------------------------------------------
+ Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved.
+
+ LICENSE TERMS
+
+ The redistribution and use of this software (with or without changes)
+ is allowed without the payment of fees or royalties provided that:
+
+ 1. source code distributions include the above copyright notice, this
+ list of conditions and the following disclaimer;
+
+ 2. binary distributions include the above copyright notice, this list
+ of conditions and the following disclaimer in their documentation;
+
+ 3. the name of the copyright holder is not used to endorse products
+ built using this software without specific written permission.
+
+ DISCLAIMER
+
+ This software is provided 'as is' with no explicit or implied warranties
+ in respect of its properties, including, but not limited to, correctness
+ and/or fitness for purpose.
+ ---------------------------------------------------------------------------
+ Issue 09/09/2006
+
+ This is an AES implementation that uses only 8-bit byte operations on the
+ cipher state (there are options to use 32-bit types if available).
+
+ The combination of mix columns and byte substitution used here is based on
+ that developed by Karl Malbrain. His contribution is acknowledged.
+ */
+
+/* define if you have a fast memcpy function on your system */
+#if 0
+# define HAVE_MEMCPY
+# include
+# if defined( _MSC_VER )
+# include
+# pragma intrinsic( memcpy )
+# endif
+#endif
+
+
+#include
+#include
+
+/* define if you have fast 32-bit types on your system */
+#if ( __CORTEX_M != 0 ) // if Cortex is different from M0/M0+
+# define HAVE_UINT_32T
+#endif
+
+/* define if you don't want any tables */
+#if 1
+# define USE_TABLES
+#endif
+
+/* On Intel Core 2 duo VERSION_1 is faster */
+
+/* alternative versions (test for performance on your system) */
+#if 1
+# define VERSION_1
+#endif
+
+#include "aes.h"
+
+//#if defined( HAVE_UINT_32T )
+// typedef unsigned long uint32_t;
+//#endif
+
+/* functions for finite field multiplication in the AES Galois field */
+
+#define WPOLY 0x011b
+#define BPOLY 0x1b
+#define DPOLY 0x008d
+
+#define f1(x) (x)
+#define f2(x) ((x << 1) ^ (((x >> 7) & 1) * WPOLY))
+#define f4(x) ((x << 2) ^ (((x >> 6) & 1) * WPOLY) ^ (((x >> 6) & 2) * WPOLY))
+#define f8(x) ((x << 3) ^ (((x >> 5) & 1) * WPOLY) ^ (((x >> 5) & 2) * WPOLY) \
+ ^ (((x >> 5) & 4) * WPOLY))
+#define d2(x) (((x) >> 1) ^ ((x) & 1 ? DPOLY : 0))
+
+#define f3(x) (f2(x) ^ x)
+#define f9(x) (f8(x) ^ x)
+#define fb(x) (f8(x) ^ f2(x) ^ x)
+#define fd(x) (f8(x) ^ f4(x) ^ x)
+#define fe(x) (f8(x) ^ f4(x) ^ f2(x))
+
+#if defined( USE_TABLES )
+
+#define sb_data(w) { /* S Box data values */ \
+ w(0x63), w(0x7c), w(0x77), w(0x7b), w(0xf2), w(0x6b), w(0x6f), w(0xc5),\
+ w(0x30), w(0x01), w(0x67), w(0x2b), w(0xfe), w(0xd7), w(0xab), w(0x76),\
+ w(0xca), w(0x82), w(0xc9), w(0x7d), w(0xfa), w(0x59), w(0x47), w(0xf0),\
+ w(0xad), w(0xd4), w(0xa2), w(0xaf), w(0x9c), w(0xa4), w(0x72), w(0xc0),\
+ w(0xb7), w(0xfd), w(0x93), w(0x26), w(0x36), w(0x3f), w(0xf7), w(0xcc),\
+ w(0x34), w(0xa5), w(0xe5), w(0xf1), w(0x71), w(0xd8), w(0x31), w(0x15),\
+ w(0x04), w(0xc7), w(0x23), w(0xc3), w(0x18), w(0x96), w(0x05), w(0x9a),\
+ w(0x07), w(0x12), w(0x80), w(0xe2), w(0xeb), w(0x27), w(0xb2), w(0x75),\
+ w(0x09), w(0x83), w(0x2c), w(0x1a), w(0x1b), w(0x6e), w(0x5a), w(0xa0),\
+ w(0x52), w(0x3b), w(0xd6), w(0xb3), w(0x29), w(0xe3), w(0x2f), w(0x84),\
+ w(0x53), w(0xd1), w(0x00), w(0xed), w(0x20), w(0xfc), w(0xb1), w(0x5b),\
+ w(0x6a), w(0xcb), w(0xbe), w(0x39), w(0x4a), w(0x4c), w(0x58), w(0xcf),\
+ w(0xd0), w(0xef), w(0xaa), w(0xfb), w(0x43), w(0x4d), w(0x33), w(0x85),\
+ w(0x45), w(0xf9), w(0x02), w(0x7f), w(0x50), w(0x3c), w(0x9f), w(0xa8),\
+ w(0x51), w(0xa3), w(0x40), w(0x8f), w(0x92), w(0x9d), w(0x38), w(0xf5),\
+ w(0xbc), w(0xb6), w(0xda), w(0x21), w(0x10), w(0xff), w(0xf3), w(0xd2),\
+ w(0xcd), w(0x0c), w(0x13), w(0xec), w(0x5f), w(0x97), w(0x44), w(0x17),\
+ w(0xc4), w(0xa7), w(0x7e), w(0x3d), w(0x64), w(0x5d), w(0x19), w(0x73),\
+ w(0x60), w(0x81), w(0x4f), w(0xdc), w(0x22), w(0x2a), w(0x90), w(0x88),\
+ w(0x46), w(0xee), w(0xb8), w(0x14), w(0xde), w(0x5e), w(0x0b), w(0xdb),\
+ w(0xe0), w(0x32), w(0x3a), w(0x0a), w(0x49), w(0x06), w(0x24), w(0x5c),\
+ w(0xc2), w(0xd3), w(0xac), w(0x62), w(0x91), w(0x95), w(0xe4), w(0x79),\
+ w(0xe7), w(0xc8), w(0x37), w(0x6d), w(0x8d), w(0xd5), w(0x4e), w(0xa9),\
+ w(0x6c), w(0x56), w(0xf4), w(0xea), w(0x65), w(0x7a), w(0xae), w(0x08),\
+ w(0xba), w(0x78), w(0x25), w(0x2e), w(0x1c), w(0xa6), w(0xb4), w(0xc6),\
+ w(0xe8), w(0xdd), w(0x74), w(0x1f), w(0x4b), w(0xbd), w(0x8b), w(0x8a),\
+ w(0x70), w(0x3e), w(0xb5), w(0x66), w(0x48), w(0x03), w(0xf6), w(0x0e),\
+ w(0x61), w(0x35), w(0x57), w(0xb9), w(0x86), w(0xc1), w(0x1d), w(0x9e),\
+ w(0xe1), w(0xf8), w(0x98), w(0x11), w(0x69), w(0xd9), w(0x8e), w(0x94),\
+ w(0x9b), w(0x1e), w(0x87), w(0xe9), w(0xce), w(0x55), w(0x28), w(0xdf),\
+ w(0x8c), w(0xa1), w(0x89), w(0x0d), w(0xbf), w(0xe6), w(0x42), w(0x68),\
+ w(0x41), w(0x99), w(0x2d), w(0x0f), w(0xb0), w(0x54), w(0xbb), w(0x16) }
+
+#define isb_data(w) { /* inverse S Box data values */ \
+ w(0x52), w(0x09), w(0x6a), w(0xd5), w(0x30), w(0x36), w(0xa5), w(0x38),\
+ w(0xbf), w(0x40), w(0xa3), w(0x9e), w(0x81), w(0xf3), w(0xd7), w(0xfb),\
+ w(0x7c), w(0xe3), w(0x39), w(0x82), w(0x9b), w(0x2f), w(0xff), w(0x87),\
+ w(0x34), w(0x8e), w(0x43), w(0x44), w(0xc4), w(0xde), w(0xe9), w(0xcb),\
+ w(0x54), w(0x7b), w(0x94), w(0x32), w(0xa6), w(0xc2), w(0x23), w(0x3d),\
+ w(0xee), w(0x4c), w(0x95), w(0x0b), w(0x42), w(0xfa), w(0xc3), w(0x4e),\
+ w(0x08), w(0x2e), w(0xa1), w(0x66), w(0x28), w(0xd9), w(0x24), w(0xb2),\
+ w(0x76), w(0x5b), w(0xa2), w(0x49), w(0x6d), w(0x8b), w(0xd1), w(0x25),\
+ w(0x72), w(0xf8), w(0xf6), w(0x64), w(0x86), w(0x68), w(0x98), w(0x16),\
+ w(0xd4), w(0xa4), w(0x5c), w(0xcc), w(0x5d), w(0x65), w(0xb6), w(0x92),\
+ w(0x6c), w(0x70), w(0x48), w(0x50), w(0xfd), w(0xed), w(0xb9), w(0xda),\
+ w(0x5e), w(0x15), w(0x46), w(0x57), w(0xa7), w(0x8d), w(0x9d), w(0x84),\
+ w(0x90), w(0xd8), w(0xab), w(0x00), w(0x8c), w(0xbc), w(0xd3), w(0x0a),\
+ w(0xf7), w(0xe4), w(0x58), w(0x05), w(0xb8), w(0xb3), w(0x45), w(0x06),\
+ w(0xd0), w(0x2c), w(0x1e), w(0x8f), w(0xca), w(0x3f), w(0x0f), w(0x02),\
+ w(0xc1), w(0xaf), w(0xbd), w(0x03), w(0x01), w(0x13), w(0x8a), w(0x6b),\
+ w(0x3a), w(0x91), w(0x11), w(0x41), w(0x4f), w(0x67), w(0xdc), w(0xea),\
+ w(0x97), w(0xf2), w(0xcf), w(0xce), w(0xf0), w(0xb4), w(0xe6), w(0x73),\
+ w(0x96), w(0xac), w(0x74), w(0x22), w(0xe7), w(0xad), w(0x35), w(0x85),\
+ w(0xe2), w(0xf9), w(0x37), w(0xe8), w(0x1c), w(0x75), w(0xdf), w(0x6e),\
+ w(0x47), w(0xf1), w(0x1a), w(0x71), w(0x1d), w(0x29), w(0xc5), w(0x89),\
+ w(0x6f), w(0xb7), w(0x62), w(0x0e), w(0xaa), w(0x18), w(0xbe), w(0x1b),\
+ w(0xfc), w(0x56), w(0x3e), w(0x4b), w(0xc6), w(0xd2), w(0x79), w(0x20),\
+ w(0x9a), w(0xdb), w(0xc0), w(0xfe), w(0x78), w(0xcd), w(0x5a), w(0xf4),\
+ w(0x1f), w(0xdd), w(0xa8), w(0x33), w(0x88), w(0x07), w(0xc7), w(0x31),\
+ w(0xb1), w(0x12), w(0x10), w(0x59), w(0x27), w(0x80), w(0xec), w(0x5f),\
+ w(0x60), w(0x51), w(0x7f), w(0xa9), w(0x19), w(0xb5), w(0x4a), w(0x0d),\
+ w(0x2d), w(0xe5), w(0x7a), w(0x9f), w(0x93), w(0xc9), w(0x9c), w(0xef),\
+ w(0xa0), w(0xe0), w(0x3b), w(0x4d), w(0xae), w(0x2a), w(0xf5), w(0xb0),\
+ w(0xc8), w(0xeb), w(0xbb), w(0x3c), w(0x83), w(0x53), w(0x99), w(0x61),\
+ w(0x17), w(0x2b), w(0x04), w(0x7e), w(0xba), w(0x77), w(0xd6), w(0x26),\
+ w(0xe1), w(0x69), w(0x14), w(0x63), w(0x55), w(0x21), w(0x0c), w(0x7d) }
+
+#define mm_data(w) { /* basic data for forming finite field tables */ \
+ w(0x00), w(0x01), w(0x02), w(0x03), w(0x04), w(0x05), w(0x06), w(0x07),\
+ w(0x08), w(0x09), w(0x0a), w(0x0b), w(0x0c), w(0x0d), w(0x0e), w(0x0f),\
+ w(0x10), w(0x11), w(0x12), w(0x13), w(0x14), w(0x15), w(0x16), w(0x17),\
+ w(0x18), w(0x19), w(0x1a), w(0x1b), w(0x1c), w(0x1d), w(0x1e), w(0x1f),\
+ w(0x20), w(0x21), w(0x22), w(0x23), w(0x24), w(0x25), w(0x26), w(0x27),\
+ w(0x28), w(0x29), w(0x2a), w(0x2b), w(0x2c), w(0x2d), w(0x2e), w(0x2f),\
+ w(0x30), w(0x31), w(0x32), w(0x33), w(0x34), w(0x35), w(0x36), w(0x37),\
+ w(0x38), w(0x39), w(0x3a), w(0x3b), w(0x3c), w(0x3d), w(0x3e), w(0x3f),\
+ w(0x40), w(0x41), w(0x42), w(0x43), w(0x44), w(0x45), w(0x46), w(0x47),\
+ w(0x48), w(0x49), w(0x4a), w(0x4b), w(0x4c), w(0x4d), w(0x4e), w(0x4f),\
+ w(0x50), w(0x51), w(0x52), w(0x53), w(0x54), w(0x55), w(0x56), w(0x57),\
+ w(0x58), w(0x59), w(0x5a), w(0x5b), w(0x5c), w(0x5d), w(0x5e), w(0x5f),\
+ w(0x60), w(0x61), w(0x62), w(0x63), w(0x64), w(0x65), w(0x66), w(0x67),\
+ w(0x68), w(0x69), w(0x6a), w(0x6b), w(0x6c), w(0x6d), w(0x6e), w(0x6f),\
+ w(0x70), w(0x71), w(0x72), w(0x73), w(0x74), w(0x75), w(0x76), w(0x77),\
+ w(0x78), w(0x79), w(0x7a), w(0x7b), w(0x7c), w(0x7d), w(0x7e), w(0x7f),\
+ w(0x80), w(0x81), w(0x82), w(0x83), w(0x84), w(0x85), w(0x86), w(0x87),\
+ w(0x88), w(0x89), w(0x8a), w(0x8b), w(0x8c), w(0x8d), w(0x8e), w(0x8f),\
+ w(0x90), w(0x91), w(0x92), w(0x93), w(0x94), w(0x95), w(0x96), w(0x97),\
+ w(0x98), w(0x99), w(0x9a), w(0x9b), w(0x9c), w(0x9d), w(0x9e), w(0x9f),\
+ w(0xa0), w(0xa1), w(0xa2), w(0xa3), w(0xa4), w(0xa5), w(0xa6), w(0xa7),\
+ w(0xa8), w(0xa9), w(0xaa), w(0xab), w(0xac), w(0xad), w(0xae), w(0xaf),\
+ w(0xb0), w(0xb1), w(0xb2), w(0xb3), w(0xb4), w(0xb5), w(0xb6), w(0xb7),\
+ w(0xb8), w(0xb9), w(0xba), w(0xbb), w(0xbc), w(0xbd), w(0xbe), w(0xbf),\
+ w(0xc0), w(0xc1), w(0xc2), w(0xc3), w(0xc4), w(0xc5), w(0xc6), w(0xc7),\
+ w(0xc8), w(0xc9), w(0xca), w(0xcb), w(0xcc), w(0xcd), w(0xce), w(0xcf),\
+ w(0xd0), w(0xd1), w(0xd2), w(0xd3), w(0xd4), w(0xd5), w(0xd6), w(0xd7),\
+ w(0xd8), w(0xd9), w(0xda), w(0xdb), w(0xdc), w(0xdd), w(0xde), w(0xdf),\
+ w(0xe0), w(0xe1), w(0xe2), w(0xe3), w(0xe4), w(0xe5), w(0xe6), w(0xe7),\
+ w(0xe8), w(0xe9), w(0xea), w(0xeb), w(0xec), w(0xed), w(0xee), w(0xef),\
+ w(0xf0), w(0xf1), w(0xf2), w(0xf3), w(0xf4), w(0xf5), w(0xf6), w(0xf7),\
+ w(0xf8), w(0xf9), w(0xfa), w(0xfb), w(0xfc), w(0xfd), w(0xfe), w(0xff) }
+
+static const uint8_t sbox[256] = sb_data(f1);
+
+#if defined( AES_DEC_PREKEYED )
+static const uint8_t isbox[256] = isb_data(f1);
+#endif
+
+static const uint8_t gfm2_sbox[256] = sb_data(f2);
+static const uint8_t gfm3_sbox[256] = sb_data(f3);
+
+#if defined( AES_DEC_PREKEYED )
+static const uint8_t gfmul_9[256] = mm_data(f9);
+static const uint8_t gfmul_b[256] = mm_data(fb);
+static const uint8_t gfmul_d[256] = mm_data(fd);
+static const uint8_t gfmul_e[256] = mm_data(fe);
+#endif
+
+#define s_box(x) sbox[(x)]
+#if defined( AES_DEC_PREKEYED )
+#define is_box(x) isbox[(x)]
+#endif
+#define gfm2_sb(x) gfm2_sbox[(x)]
+#define gfm3_sb(x) gfm3_sbox[(x)]
+#if defined( AES_DEC_PREKEYED )
+#define gfm_9(x) gfmul_9[(x)]
+#define gfm_b(x) gfmul_b[(x)]
+#define gfm_d(x) gfmul_d[(x)]
+#define gfm_e(x) gfmul_e[(x)]
+#endif
+#else
+
+/* this is the high bit of x right shifted by 1 */
+/* position. Since the starting polynomial has */
+/* 9 bits (0x11b), this right shift keeps the */
+/* values of all top bits within a byte */
+
+static uint8_t hibit(const uint8_t x)
+{ uint8_t r = (uint8_t)((x >> 1) | (x >> 2));
+
+ r |= (r >> 2);
+ r |= (r >> 4);
+ return (r + 1) >> 1;
+}
+
+/* return the inverse of the finite field element x */
+
+static uint8_t gf_inv(const uint8_t x)
+{ uint8_t p1 = x, p2 = BPOLY, n1 = hibit(x), n2 = 0x80, v1 = 1, v2 = 0;
+
+ if(x < 2)
+ return x;
+
+ for( ; ; )
+ {
+ if(n1)
+ while(n2 >= n1) /* divide polynomial p2 by p1 */
+ {
+ n2 /= n1; /* shift smaller polynomial left */
+ p2 ^= (p1 * n2) & 0xff; /* and remove from larger one */
+ v2 ^= (v1 * n2); /* shift accumulated value and */
+ n2 = hibit(p2); /* add into result */
+ }
+ else
+ return v1;
+
+ if(n2) /* repeat with values swapped */
+ while(n1 >= n2)
+ {
+ n1 /= n2;
+ p1 ^= p2 * n1;
+ v1 ^= v2 * n1;
+ n1 = hibit(p1);
+ }
+ else
+ return v2;
+ }
+}
+
+/* The forward and inverse affine transformations used in the S-box */
+uint8_t fwd_affine(const uint8_t x)
+{
+#if defined( HAVE_UINT_32T )
+ uint32_t w = x;
+ w ^= (w << 1) ^ (w << 2) ^ (w << 3) ^ (w << 4);
+ return 0x63 ^ ((w ^ (w >> 8)) & 0xff);
+#else
+ return 0x63 ^ x ^ (x << 1) ^ (x << 2) ^ (x << 3) ^ (x << 4)
+ ^ (x >> 7) ^ (x >> 6) ^ (x >> 5) ^ (x >> 4);
+#endif
+}
+
+uint8_t inv_affine(const uint8_t x)
+{
+#if defined( HAVE_UINT_32T )
+ uint32_t w = x;
+ w = (w << 1) ^ (w << 3) ^ (w << 6);
+ return 0x05 ^ ((w ^ (w >> 8)) & 0xff);
+#else
+ return 0x05 ^ (x << 1) ^ (x << 3) ^ (x << 6)
+ ^ (x >> 7) ^ (x >> 5) ^ (x >> 2);
+#endif
+}
+
+#define s_box(x) fwd_affine(gf_inv(x))
+#define is_box(x) gf_inv(inv_affine(x))
+#define gfm2_sb(x) f2(s_box(x))
+#define gfm3_sb(x) f3(s_box(x))
+#define gfm_9(x) f9(x)
+#define gfm_b(x) fb(x)
+#define gfm_d(x) fd(x)
+#define gfm_e(x) fe(x)
+
+#endif
+
+#if defined( HAVE_MEMCPY )
+# define block_copy_nn(d, s, l) memcpy(d, s, l)
+# define block_copy(d, s) memcpy(d, s, N_BLOCK)
+#else
+# define block_copy_nn(d, s, l) copy_block_nn(d, s, l)
+# define block_copy(d, s) copy_block(d, s)
+#endif
+
+static void copy_block( void *d, const void *s )
+{
+#if defined( HAVE_UINT_32T )
+ ((uint32_t*)d)[ 0] = ((uint32_t*)s)[ 0];
+ ((uint32_t*)d)[ 1] = ((uint32_t*)s)[ 1];
+ ((uint32_t*)d)[ 2] = ((uint32_t*)s)[ 2];
+ ((uint32_t*)d)[ 3] = ((uint32_t*)s)[ 3];
+#else
+ ((uint8_t*)d)[ 0] = ((uint8_t*)s)[ 0];
+ ((uint8_t*)d)[ 1] = ((uint8_t*)s)[ 1];
+ ((uint8_t*)d)[ 2] = ((uint8_t*)s)[ 2];
+ ((uint8_t*)d)[ 3] = ((uint8_t*)s)[ 3];
+ ((uint8_t*)d)[ 4] = ((uint8_t*)s)[ 4];
+ ((uint8_t*)d)[ 5] = ((uint8_t*)s)[ 5];
+ ((uint8_t*)d)[ 6] = ((uint8_t*)s)[ 6];
+ ((uint8_t*)d)[ 7] = ((uint8_t*)s)[ 7];
+ ((uint8_t*)d)[ 8] = ((uint8_t*)s)[ 8];
+ ((uint8_t*)d)[ 9] = ((uint8_t*)s)[ 9];
+ ((uint8_t*)d)[10] = ((uint8_t*)s)[10];
+ ((uint8_t*)d)[11] = ((uint8_t*)s)[11];
+ ((uint8_t*)d)[12] = ((uint8_t*)s)[12];
+ ((uint8_t*)d)[13] = ((uint8_t*)s)[13];
+ ((uint8_t*)d)[14] = ((uint8_t*)s)[14];
+ ((uint8_t*)d)[15] = ((uint8_t*)s)[15];
+#endif
+}
+
+static void copy_block_nn( uint8_t * d, const uint8_t *s, uint8_t nn )
+{
+ while( nn-- )
+ //*((uint8_t*)d)++ = *((uint8_t*)s)++;
+ *d++ = *s++;
+}
+
+static void xor_block( void *d, const void *s )
+{
+#if defined( HAVE_UINT_32T )
+ ((uint32_t*)d)[ 0] ^= ((uint32_t*)s)[ 0];
+ ((uint32_t*)d)[ 1] ^= ((uint32_t*)s)[ 1];
+ ((uint32_t*)d)[ 2] ^= ((uint32_t*)s)[ 2];
+ ((uint32_t*)d)[ 3] ^= ((uint32_t*)s)[ 3];
+#else
+ ((uint8_t*)d)[ 0] ^= ((uint8_t*)s)[ 0];
+ ((uint8_t*)d)[ 1] ^= ((uint8_t*)s)[ 1];
+ ((uint8_t*)d)[ 2] ^= ((uint8_t*)s)[ 2];
+ ((uint8_t*)d)[ 3] ^= ((uint8_t*)s)[ 3];
+ ((uint8_t*)d)[ 4] ^= ((uint8_t*)s)[ 4];
+ ((uint8_t*)d)[ 5] ^= ((uint8_t*)s)[ 5];
+ ((uint8_t*)d)[ 6] ^= ((uint8_t*)s)[ 6];
+ ((uint8_t*)d)[ 7] ^= ((uint8_t*)s)[ 7];
+ ((uint8_t*)d)[ 8] ^= ((uint8_t*)s)[ 8];
+ ((uint8_t*)d)[ 9] ^= ((uint8_t*)s)[ 9];
+ ((uint8_t*)d)[10] ^= ((uint8_t*)s)[10];
+ ((uint8_t*)d)[11] ^= ((uint8_t*)s)[11];
+ ((uint8_t*)d)[12] ^= ((uint8_t*)s)[12];
+ ((uint8_t*)d)[13] ^= ((uint8_t*)s)[13];
+ ((uint8_t*)d)[14] ^= ((uint8_t*)s)[14];
+ ((uint8_t*)d)[15] ^= ((uint8_t*)s)[15];
+#endif
+}
+
+static void copy_and_key( void *d, const void *s, const void *k )
+{
+#if defined( HAVE_UINT_32T )
+ ((uint32_t*)d)[ 0] = ((uint32_t*)s)[ 0] ^ ((uint32_t*)k)[ 0];
+ ((uint32_t*)d)[ 1] = ((uint32_t*)s)[ 1] ^ ((uint32_t*)k)[ 1];
+ ((uint32_t*)d)[ 2] = ((uint32_t*)s)[ 2] ^ ((uint32_t*)k)[ 2];
+ ((uint32_t*)d)[ 3] = ((uint32_t*)s)[ 3] ^ ((uint32_t*)k)[ 3];
+#elif 1
+ ((uint8_t*)d)[ 0] = ((uint8_t*)s)[ 0] ^ ((uint8_t*)k)[ 0];
+ ((uint8_t*)d)[ 1] = ((uint8_t*)s)[ 1] ^ ((uint8_t*)k)[ 1];
+ ((uint8_t*)d)[ 2] = ((uint8_t*)s)[ 2] ^ ((uint8_t*)k)[ 2];
+ ((uint8_t*)d)[ 3] = ((uint8_t*)s)[ 3] ^ ((uint8_t*)k)[ 3];
+ ((uint8_t*)d)[ 4] = ((uint8_t*)s)[ 4] ^ ((uint8_t*)k)[ 4];
+ ((uint8_t*)d)[ 5] = ((uint8_t*)s)[ 5] ^ ((uint8_t*)k)[ 5];
+ ((uint8_t*)d)[ 6] = ((uint8_t*)s)[ 6] ^ ((uint8_t*)k)[ 6];
+ ((uint8_t*)d)[ 7] = ((uint8_t*)s)[ 7] ^ ((uint8_t*)k)[ 7];
+ ((uint8_t*)d)[ 8] = ((uint8_t*)s)[ 8] ^ ((uint8_t*)k)[ 8];
+ ((uint8_t*)d)[ 9] = ((uint8_t*)s)[ 9] ^ ((uint8_t*)k)[ 9];
+ ((uint8_t*)d)[10] = ((uint8_t*)s)[10] ^ ((uint8_t*)k)[10];
+ ((uint8_t*)d)[11] = ((uint8_t*)s)[11] ^ ((uint8_t*)k)[11];
+ ((uint8_t*)d)[12] = ((uint8_t*)s)[12] ^ ((uint8_t*)k)[12];
+ ((uint8_t*)d)[13] = ((uint8_t*)s)[13] ^ ((uint8_t*)k)[13];
+ ((uint8_t*)d)[14] = ((uint8_t*)s)[14] ^ ((uint8_t*)k)[14];
+ ((uint8_t*)d)[15] = ((uint8_t*)s)[15] ^ ((uint8_t*)k)[15];
+#else
+ block_copy(d, s);
+ xor_block(d, k);
+#endif
+}
+
+static void add_round_key( uint8_t d[N_BLOCK], const uint8_t k[N_BLOCK] )
+{
+ xor_block(d, k);
+}
+
+static void shift_sub_rows( uint8_t st[N_BLOCK] )
+{ uint8_t tt;
+
+ st[ 0] = s_box(st[ 0]); st[ 4] = s_box(st[ 4]);
+ st[ 8] = s_box(st[ 8]); st[12] = s_box(st[12]);
+
+ tt = st[1]; st[ 1] = s_box(st[ 5]); st[ 5] = s_box(st[ 9]);
+ st[ 9] = s_box(st[13]); st[13] = s_box( tt );
+
+ tt = st[2]; st[ 2] = s_box(st[10]); st[10] = s_box( tt );
+ tt = st[6]; st[ 6] = s_box(st[14]); st[14] = s_box( tt );
+
+ tt = st[15]; st[15] = s_box(st[11]); st[11] = s_box(st[ 7]);
+ st[ 7] = s_box(st[ 3]); st[ 3] = s_box( tt );
+}
+
+#if defined( AES_DEC_PREKEYED )
+
+static void inv_shift_sub_rows( uint8_t st[N_BLOCK] )
+{ uint8_t tt;
+
+ st[ 0] = is_box(st[ 0]); st[ 4] = is_box(st[ 4]);
+ st[ 8] = is_box(st[ 8]); st[12] = is_box(st[12]);
+
+ tt = st[13]; st[13] = is_box(st[9]); st[ 9] = is_box(st[5]);
+ st[ 5] = is_box(st[1]); st[ 1] = is_box( tt );
+
+ tt = st[2]; st[ 2] = is_box(st[10]); st[10] = is_box( tt );
+ tt = st[6]; st[ 6] = is_box(st[14]); st[14] = is_box( tt );
+
+ tt = st[3]; st[ 3] = is_box(st[ 7]); st[ 7] = is_box(st[11]);
+ st[11] = is_box(st[15]); st[15] = is_box( tt );
+}
+
+#endif
+
+#if defined( VERSION_1 )
+ static void mix_sub_columns( uint8_t dt[N_BLOCK] )
+ { uint8_t st[N_BLOCK];
+ block_copy(st, dt);
+#else
+ static void mix_sub_columns( uint8_t dt[N_BLOCK], uint8_t st[N_BLOCK] )
+ {
+#endif
+ dt[ 0] = gfm2_sb(st[0]) ^ gfm3_sb(st[5]) ^ s_box(st[10]) ^ s_box(st[15]);
+ dt[ 1] = s_box(st[0]) ^ gfm2_sb(st[5]) ^ gfm3_sb(st[10]) ^ s_box(st[15]);
+ dt[ 2] = s_box(st[0]) ^ s_box(st[5]) ^ gfm2_sb(st[10]) ^ gfm3_sb(st[15]);
+ dt[ 3] = gfm3_sb(st[0]) ^ s_box(st[5]) ^ s_box(st[10]) ^ gfm2_sb(st[15]);
+
+ dt[ 4] = gfm2_sb(st[4]) ^ gfm3_sb(st[9]) ^ s_box(st[14]) ^ s_box(st[3]);
+ dt[ 5] = s_box(st[4]) ^ gfm2_sb(st[9]) ^ gfm3_sb(st[14]) ^ s_box(st[3]);
+ dt[ 6] = s_box(st[4]) ^ s_box(st[9]) ^ gfm2_sb(st[14]) ^ gfm3_sb(st[3]);
+ dt[ 7] = gfm3_sb(st[4]) ^ s_box(st[9]) ^ s_box(st[14]) ^ gfm2_sb(st[3]);
+
+ dt[ 8] = gfm2_sb(st[8]) ^ gfm3_sb(st[13]) ^ s_box(st[2]) ^ s_box(st[7]);
+ dt[ 9] = s_box(st[8]) ^ gfm2_sb(st[13]) ^ gfm3_sb(st[2]) ^ s_box(st[7]);
+ dt[10] = s_box(st[8]) ^ s_box(st[13]) ^ gfm2_sb(st[2]) ^ gfm3_sb(st[7]);
+ dt[11] = gfm3_sb(st[8]) ^ s_box(st[13]) ^ s_box(st[2]) ^ gfm2_sb(st[7]);
+
+ dt[12] = gfm2_sb(st[12]) ^ gfm3_sb(st[1]) ^ s_box(st[6]) ^ s_box(st[11]);
+ dt[13] = s_box(st[12]) ^ gfm2_sb(st[1]) ^ gfm3_sb(st[6]) ^ s_box(st[11]);
+ dt[14] = s_box(st[12]) ^ s_box(st[1]) ^ gfm2_sb(st[6]) ^ gfm3_sb(st[11]);
+ dt[15] = gfm3_sb(st[12]) ^ s_box(st[1]) ^ s_box(st[6]) ^ gfm2_sb(st[11]);
+ }
+
+#if defined( AES_DEC_PREKEYED )
+
+#if defined( VERSION_1 )
+ static void inv_mix_sub_columns( uint8_t dt[N_BLOCK] )
+ { uint8_t st[N_BLOCK];
+ block_copy(st, dt);
+#else
+ static void inv_mix_sub_columns( uint8_t dt[N_BLOCK], uint8_t st[N_BLOCK] )
+ {
+#endif
+ dt[ 0] = is_box(gfm_e(st[ 0]) ^ gfm_b(st[ 1]) ^ gfm_d(st[ 2]) ^ gfm_9(st[ 3]));
+ dt[ 5] = is_box(gfm_9(st[ 0]) ^ gfm_e(st[ 1]) ^ gfm_b(st[ 2]) ^ gfm_d(st[ 3]));
+ dt[10] = is_box(gfm_d(st[ 0]) ^ gfm_9(st[ 1]) ^ gfm_e(st[ 2]) ^ gfm_b(st[ 3]));
+ dt[15] = is_box(gfm_b(st[ 0]) ^ gfm_d(st[ 1]) ^ gfm_9(st[ 2]) ^ gfm_e(st[ 3]));
+
+ dt[ 4] = is_box(gfm_e(st[ 4]) ^ gfm_b(st[ 5]) ^ gfm_d(st[ 6]) ^ gfm_9(st[ 7]));
+ dt[ 9] = is_box(gfm_9(st[ 4]) ^ gfm_e(st[ 5]) ^ gfm_b(st[ 6]) ^ gfm_d(st[ 7]));
+ dt[14] = is_box(gfm_d(st[ 4]) ^ gfm_9(st[ 5]) ^ gfm_e(st[ 6]) ^ gfm_b(st[ 7]));
+ dt[ 3] = is_box(gfm_b(st[ 4]) ^ gfm_d(st[ 5]) ^ gfm_9(st[ 6]) ^ gfm_e(st[ 7]));
+
+ dt[ 8] = is_box(gfm_e(st[ 8]) ^ gfm_b(st[ 9]) ^ gfm_d(st[10]) ^ gfm_9(st[11]));
+ dt[13] = is_box(gfm_9(st[ 8]) ^ gfm_e(st[ 9]) ^ gfm_b(st[10]) ^ gfm_d(st[11]));
+ dt[ 2] = is_box(gfm_d(st[ 8]) ^ gfm_9(st[ 9]) ^ gfm_e(st[10]) ^ gfm_b(st[11]));
+ dt[ 7] = is_box(gfm_b(st[ 8]) ^ gfm_d(st[ 9]) ^ gfm_9(st[10]) ^ gfm_e(st[11]));
+
+ dt[12] = is_box(gfm_e(st[12]) ^ gfm_b(st[13]) ^ gfm_d(st[14]) ^ gfm_9(st[15]));
+ dt[ 1] = is_box(gfm_9(st[12]) ^ gfm_e(st[13]) ^ gfm_b(st[14]) ^ gfm_d(st[15]));
+ dt[ 6] = is_box(gfm_d(st[12]) ^ gfm_9(st[13]) ^ gfm_e(st[14]) ^ gfm_b(st[15]));
+ dt[11] = is_box(gfm_b(st[12]) ^ gfm_d(st[13]) ^ gfm_9(st[14]) ^ gfm_e(st[15]));
+ }
+
+#endif
+
+#if defined( AES_ENC_PREKEYED ) || defined( AES_DEC_PREKEYED )
+
+/* Set the cipher key for the pre-keyed version */
+
+return_type aes_set_key( const uint8_t key[], length_type keylen, aes_context ctx[1] )
+{
+ uint8_t cc, rc, hi;
+
+ switch( keylen )
+ {
+ case 16:
+ case 24:
+ case 32:
+ break;
+ default:
+ ctx->rnd = 0;
+ return ( uint8_t )-1;
+ }
+ block_copy_nn(ctx->ksch, key, keylen);
+ hi = (keylen + 28) << 2;
+ ctx->rnd = (hi >> 4) - 1;
+ for( cc = keylen, rc = 1; cc < hi; cc += 4 )
+ { uint8_t tt, t0, t1, t2, t3;
+
+ t0 = ctx->ksch[cc - 4];
+ t1 = ctx->ksch[cc - 3];
+ t2 = ctx->ksch[cc - 2];
+ t3 = ctx->ksch[cc - 1];
+ if( cc % keylen == 0 )
+ {
+ tt = t0;
+ t0 = s_box(t1) ^ rc;
+ t1 = s_box(t2);
+ t2 = s_box(t3);
+ t3 = s_box(tt);
+ rc = f2(rc);
+ }
+ else if( keylen > 24 && cc % keylen == 16 )
+ {
+ t0 = s_box(t0);
+ t1 = s_box(t1);
+ t2 = s_box(t2);
+ t3 = s_box(t3);
+ }
+ tt = cc - keylen;
+ ctx->ksch[cc + 0] = ctx->ksch[tt + 0] ^ t0;
+ ctx->ksch[cc + 1] = ctx->ksch[tt + 1] ^ t1;
+ ctx->ksch[cc + 2] = ctx->ksch[tt + 2] ^ t2;
+ ctx->ksch[cc + 3] = ctx->ksch[tt + 3] ^ t3;
+ }
+ return 0;
+}
+
+#endif
+
+#if defined( AES_ENC_PREKEYED )
+
+/* Encrypt a single block of 16 bytes */
+
+return_type aes_encrypt( const uint8_t in[N_BLOCK], uint8_t out[N_BLOCK], const aes_context ctx[1] )
+{
+ if( ctx->rnd )
+ {
+ uint8_t s1[N_BLOCK], r;
+ copy_and_key( s1, in, ctx->ksch );
+
+ for( r = 1 ; r < ctx->rnd ; ++r )
+#if defined( VERSION_1 )
+ {
+ mix_sub_columns( s1 );
+ add_round_key( s1, ctx->ksch + r * N_BLOCK);
+ }
+#else
+ { uint8_t s2[N_BLOCK];
+ mix_sub_columns( s2, s1 );
+ copy_and_key( s1, s2, ctx->ksch + r * N_BLOCK);
+ }
+#endif
+ shift_sub_rows( s1 );
+ copy_and_key( out, s1, ctx->ksch + r * N_BLOCK );
+ }
+ else
+ return ( uint8_t )-1;
+ return 0;
+}
+
+/* CBC encrypt a number of blocks (input and return an IV) */
+
+return_type aes_cbc_encrypt( const uint8_t *in, uint8_t *out,
+ int32_t n_block, uint8_t iv[N_BLOCK], const aes_context ctx[1] )
+{
+
+ while(n_block--)
+ {
+ xor_block(iv, in);
+ if(aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS)
+ return EXIT_FAILURE;
+ //memcpy(out, iv, N_BLOCK);
+ block_copy(out, iv);
+ in += N_BLOCK;
+ out += N_BLOCK;
+ }
+ return EXIT_SUCCESS;
+}
+
+#endif
+
+#if defined( AES_DEC_PREKEYED )
+
+/* Decrypt a single block of 16 bytes */
+
+return_type aes_decrypt( const uint8_t in[N_BLOCK], uint8_t out[N_BLOCK], const aes_context ctx[1] )
+{
+ if( ctx->rnd )
+ {
+ uint8_t s1[N_BLOCK], r;
+ copy_and_key( s1, in, ctx->ksch + ctx->rnd * N_BLOCK );
+ inv_shift_sub_rows( s1 );
+
+ for( r = ctx->rnd ; --r ; )
+#if defined( VERSION_1 )
+ {
+ add_round_key( s1, ctx->ksch + r * N_BLOCK );
+ inv_mix_sub_columns( s1 );
+ }
+#else
+ { uint8_t s2[N_BLOCK];
+ copy_and_key( s2, s1, ctx->ksch + r * N_BLOCK );
+ inv_mix_sub_columns( s1, s2 );
+ }
+#endif
+ copy_and_key( out, s1, ctx->ksch );
+ }
+ else
+ return -1;
+ return 0;
+}
+
+/* CBC decrypt a number of blocks (input and return an IV) */
+
+return_type aes_cbc_decrypt( const uint8_t *in, uint8_t *out,
+ int32_t n_block, uint8_t iv[N_BLOCK], const aes_context ctx[1] )
+{
+ while(n_block--)
+ { uint8_t tmp[N_BLOCK];
+
+ //memcpy(tmp, in, N_BLOCK);
+ block_copy(tmp, in);
+ if(aes_decrypt(in, out, ctx) != EXIT_SUCCESS)
+ return EXIT_FAILURE;
+ xor_block(out, iv);
+ //memcpy(iv, tmp, N_BLOCK);
+ block_copy(iv, tmp);
+ in += N_BLOCK;
+ out += N_BLOCK;
+ }
+ return EXIT_SUCCESS;
+}
+
+#endif
+
+#if defined( AES_ENC_128_OTFK )
+
+/* The 'on the fly' encryption key update for for 128 bit keys */
+
+static void update_encrypt_key_128( uint8_t k[N_BLOCK], uint8_t *rc )
+{ uint8_t cc;
+
+ k[0] ^= s_box(k[13]) ^ *rc;
+ k[1] ^= s_box(k[14]);
+ k[2] ^= s_box(k[15]);
+ k[3] ^= s_box(k[12]);
+ *rc = f2( *rc );
+
+ for(cc = 4; cc < 16; cc += 4 )
+ {
+ k[cc + 0] ^= k[cc - 4];
+ k[cc + 1] ^= k[cc - 3];
+ k[cc + 2] ^= k[cc - 2];
+ k[cc + 3] ^= k[cc - 1];
+ }
+}
+
+/* Encrypt a single block of 16 bytes with 'on the fly' 128 bit keying */
+
+void aes_encrypt_128( const uint8_t in[N_BLOCK], uint8_t out[N_BLOCK],
+ const uint8_t key[N_BLOCK], uint8_t o_key[N_BLOCK] )
+{ uint8_t s1[N_BLOCK], r, rc = 1;
+
+ if(o_key != key)
+ block_copy( o_key, key );
+ copy_and_key( s1, in, o_key );
+
+ for( r = 1 ; r < 10 ; ++r )
+#if defined( VERSION_1 )
+ {
+ mix_sub_columns( s1 );
+ update_encrypt_key_128( o_key, &rc );
+ add_round_key( s1, o_key );
+ }
+#else
+ { uint8_t s2[N_BLOCK];
+ mix_sub_columns( s2, s1 );
+ update_encrypt_key_128( o_key, &rc );
+ copy_and_key( s1, s2, o_key );
+ }
+#endif
+
+ shift_sub_rows( s1 );
+ update_encrypt_key_128( o_key, &rc );
+ copy_and_key( out, s1, o_key );
+}
+
+#endif
+
+#if defined( AES_DEC_128_OTFK )
+
+/* The 'on the fly' decryption key update for for 128 bit keys */
+
+static void update_decrypt_key_128( uint8_t k[N_BLOCK], uint8_t *rc )
+{ uint8_t cc;
+
+ for( cc = 12; cc > 0; cc -= 4 )
+ {
+ k[cc + 0] ^= k[cc - 4];
+ k[cc + 1] ^= k[cc - 3];
+ k[cc + 2] ^= k[cc - 2];
+ k[cc + 3] ^= k[cc - 1];
+ }
+ *rc = d2(*rc);
+ k[0] ^= s_box(k[13]) ^ *rc;
+ k[1] ^= s_box(k[14]);
+ k[2] ^= s_box(k[15]);
+ k[3] ^= s_box(k[12]);
+}
+
+/* Decrypt a single block of 16 bytes with 'on the fly' 128 bit keying */
+
+void aes_decrypt_128( const uint8_t in[N_BLOCK], uint8_t out[N_BLOCK],
+ const uint8_t key[N_BLOCK], uint8_t o_key[N_BLOCK] )
+{
+ uint8_t s1[N_BLOCK], r, rc = 0x6c;
+ if(o_key != key)
+ block_copy( o_key, key );
+
+ copy_and_key( s1, in, o_key );
+ inv_shift_sub_rows( s1 );
+
+ for( r = 10 ; --r ; )
+#if defined( VERSION_1 )
+ {
+ update_decrypt_key_128( o_key, &rc );
+ add_round_key( s1, o_key );
+ inv_mix_sub_columns( s1 );
+ }
+#else
+ { uint8_t s2[N_BLOCK];
+ update_decrypt_key_128( o_key, &rc );
+ copy_and_key( s2, s1, o_key );
+ inv_mix_sub_columns( s1, s2 );
+ }
+#endif
+ update_decrypt_key_128( o_key, &rc );
+ copy_and_key( out, s1, o_key );
+}
+
+#endif
+
+#if defined( AES_ENC_256_OTFK )
+
+/* The 'on the fly' encryption key update for for 256 bit keys */
+
+static void update_encrypt_key_256( uint8_t k[2 * N_BLOCK], uint8_t *rc )
+{ uint8_t cc;
+
+ k[0] ^= s_box(k[29]) ^ *rc;
+ k[1] ^= s_box(k[30]);
+ k[2] ^= s_box(k[31]);
+ k[3] ^= s_box(k[28]);
+ *rc = f2( *rc );
+
+ for(cc = 4; cc < 16; cc += 4)
+ {
+ k[cc + 0] ^= k[cc - 4];
+ k[cc + 1] ^= k[cc - 3];
+ k[cc + 2] ^= k[cc - 2];
+ k[cc + 3] ^= k[cc - 1];
+ }
+
+ k[16] ^= s_box(k[12]);
+ k[17] ^= s_box(k[13]);
+ k[18] ^= s_box(k[14]);
+ k[19] ^= s_box(k[15]);
+
+ for( cc = 20; cc < 32; cc += 4 )
+ {
+ k[cc + 0] ^= k[cc - 4];
+ k[cc + 1] ^= k[cc - 3];
+ k[cc + 2] ^= k[cc - 2];
+ k[cc + 3] ^= k[cc - 1];
+ }
+}
+
+/* Encrypt a single block of 16 bytes with 'on the fly' 256 bit keying */
+
+void aes_encrypt_256( const uint8_t in[N_BLOCK], uint8_t out[N_BLOCK],
+ const uint8_t key[2 * N_BLOCK], uint8_t o_key[2 * N_BLOCK] )
+{
+ uint8_t s1[N_BLOCK], r, rc = 1;
+ if(o_key != key)
+ {
+ block_copy( o_key, key );
+ block_copy( o_key + 16, key + 16 );
+ }
+ copy_and_key( s1, in, o_key );
+
+ for( r = 1 ; r < 14 ; ++r )
+#if defined( VERSION_1 )
+ {
+ mix_sub_columns(s1);
+ if( r & 1 )
+ add_round_key( s1, o_key + 16 );
+ else
+ {
+ update_encrypt_key_256( o_key, &rc );
+ add_round_key( s1, o_key );
+ }
+ }
+#else
+ { uint8_t s2[N_BLOCK];
+ mix_sub_columns( s2, s1 );
+ if( r & 1 )
+ copy_and_key( s1, s2, o_key + 16 );
+ else
+ {
+ update_encrypt_key_256( o_key, &rc );
+ copy_and_key( s1, s2, o_key );
+ }
+ }
+#endif
+
+ shift_sub_rows( s1 );
+ update_encrypt_key_256( o_key, &rc );
+ copy_and_key( out, s1, o_key );
+}
+
+#endif
+
+#if defined( AES_DEC_256_OTFK )
+
+/* The 'on the fly' encryption key update for for 256 bit keys */
+
+static void update_decrypt_key_256( uint8_t k[2 * N_BLOCK], uint8_t *rc )
+{ uint8_t cc;
+
+ for(cc = 28; cc > 16; cc -= 4)
+ {
+ k[cc + 0] ^= k[cc - 4];
+ k[cc + 1] ^= k[cc - 3];
+ k[cc + 2] ^= k[cc - 2];
+ k[cc + 3] ^= k[cc - 1];
+ }
+
+ k[16] ^= s_box(k[12]);
+ k[17] ^= s_box(k[13]);
+ k[18] ^= s_box(k[14]);
+ k[19] ^= s_box(k[15]);
+
+ for(cc = 12; cc > 0; cc -= 4)
+ {
+ k[cc + 0] ^= k[cc - 4];
+ k[cc + 1] ^= k[cc - 3];
+ k[cc + 2] ^= k[cc - 2];
+ k[cc + 3] ^= k[cc - 1];
+ }
+
+ *rc = d2(*rc);
+ k[0] ^= s_box(k[29]) ^ *rc;
+ k[1] ^= s_box(k[30]);
+ k[2] ^= s_box(k[31]);
+ k[3] ^= s_box(k[28]);
+}
+
+/* Decrypt a single block of 16 bytes with 'on the fly'
+ 256 bit keying
+*/
+void aes_decrypt_256( const uint8_t in[N_BLOCK], uint8_t out[N_BLOCK],
+ const uint8_t key[2 * N_BLOCK], uint8_t o_key[2 * N_BLOCK] )
+{
+ uint8_t s1[N_BLOCK], r, rc = 0x80;
+
+ if(o_key != key)
+ {
+ block_copy( o_key, key );
+ block_copy( o_key + 16, key + 16 );
+ }
+
+ copy_and_key( s1, in, o_key );
+ inv_shift_sub_rows( s1 );
+
+ for( r = 14 ; --r ; )
+#if defined( VERSION_1 )
+ {
+ if( ( r & 1 ) )
+ {
+ update_decrypt_key_256( o_key, &rc );
+ add_round_key( s1, o_key + 16 );
+ }
+ else
+ add_round_key( s1, o_key );
+ inv_mix_sub_columns( s1 );
+ }
+#else
+ { uint8_t s2[N_BLOCK];
+ if( ( r & 1 ) )
+ {
+ update_decrypt_key_256( o_key, &rc );
+ copy_and_key( s2, s1, o_key + 16 );
+ }
+ else
+ copy_and_key( s2, s1, o_key );
+ inv_mix_sub_columns( s1, s2 );
+ }
+#endif
+ copy_and_key( out, s1, o_key );
+}
+
+#endif
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Crypto/aes.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Crypto/aes.h
new file mode 100644
index 00000000..2411c2fd
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Crypto/aes.h
@@ -0,0 +1,162 @@
+/*
+ ---------------------------------------------------------------------------
+ Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved.
+
+ LICENSE TERMS
+
+ The redistribution and use of this software (with or without changes)
+ is allowed without the payment of fees or royalties provided that:
+
+ 1. source code distributions include the above copyright notice, this
+ list of conditions and the following disclaimer;
+
+ 2. binary distributions include the above copyright notice, this list
+ of conditions and the following disclaimer in their documentation;
+
+ 3. the name of the copyright holder is not used to endorse products
+ built using this software without specific written permission.
+
+ DISCLAIMER
+
+ This software is provided 'as is' with no explicit or implied warranties
+ in respect of its properties, including, but not limited to, correctness
+ and/or fitness for purpose.
+ ---------------------------------------------------------------------------
+ Issue 09/09/2006
+
+ This is an AES implementation that uses only 8-bit byte operations on the
+ cipher state.
+ */
+
+#ifndef AES_H
+#define AES_H
+
+#include
+
+#if 1
+# define AES_ENC_PREKEYED /* AES encryption with a precomputed key schedule */
+#endif
+#if 0
+# define AES_DEC_PREKEYED /* AES decryption with a precomputed key schedule */
+#endif
+#if 0
+# define AES_ENC_128_OTFK /* AES encryption with 'on the fly' 128 bit keying */
+#endif
+#if 0
+# define AES_DEC_128_OTFK /* AES decryption with 'on the fly' 128 bit keying */
+#endif
+#if 0
+# define AES_ENC_256_OTFK /* AES encryption with 'on the fly' 256 bit keying */
+#endif
+#if 0
+# define AES_DEC_256_OTFK /* AES decryption with 'on the fly' 256 bit keying */
+#endif
+
+#define N_ROW 4
+#define N_COL 4
+#define N_BLOCK (N_ROW * N_COL)
+#define N_MAX_ROUNDS 14
+
+typedef uint8_t return_type;
+
+/* Warning: The key length for 256 bit keys overflows a byte
+ (see comment below)
+*/
+
+typedef uint8_t length_type;
+
+typedef struct
+{ uint8_t ksch[(N_MAX_ROUNDS + 1) * N_BLOCK];
+ uint8_t rnd;
+} aes_context;
+
+/* The following calls are for a precomputed key schedule
+
+ NOTE: If the length_type used for the key length is an
+ unsigned 8-bit character, a key length of 256 bits must
+ be entered as a length in bytes (valid inputs are hence
+ 128, 192, 16, 24 and 32).
+*/
+
+#if defined( AES_ENC_PREKEYED ) || defined( AES_DEC_PREKEYED )
+
+return_type aes_set_key( const uint8_t key[],
+ length_type keylen,
+ aes_context ctx[1] );
+#endif
+
+#if defined( AES_ENC_PREKEYED )
+
+return_type aes_encrypt( const uint8_t in[N_BLOCK],
+ uint8_t out[N_BLOCK],
+ const aes_context ctx[1] );
+
+return_type aes_cbc_encrypt( const uint8_t *in,
+ uint8_t *out,
+ int32_t n_block,
+ uint8_t iv[N_BLOCK],
+ const aes_context ctx[1] );
+#endif
+
+#if defined( AES_DEC_PREKEYED )
+
+return_type aes_decrypt( const uint8_t in[N_BLOCK],
+ uint8_t out[N_BLOCK],
+ const aes_context ctx[1] );
+
+return_type aes_cbc_decrypt( const uint8_t *in,
+ uint8_t *out,
+ int32_t n_block,
+ uint8_t iv[N_BLOCK],
+ const aes_context ctx[1] );
+#endif
+
+/* The following calls are for 'on the fly' keying. In this case the
+ encryption and decryption keys are different.
+
+ The encryption subroutines take a key in an array of bytes in
+ key[L] where L is 16, 24 or 32 bytes for key lengths of 128,
+ 192, and 256 bits respectively. They then encrypts the input
+ data, in[] with this key and put the reult in the output array
+ out[]. In addition, the second key array, o_key[L], is used
+ to output the key that is needed by the decryption subroutine
+ to reverse the encryption operation. The two key arrays can
+ be the same array but in this case the original key will be
+ overwritten.
+
+ In the same way, the decryption subroutines output keys that
+ can be used to reverse their effect when used for encryption.
+
+ Only 128 and 256 bit keys are supported in these 'on the fly'
+ modes.
+*/
+
+#if defined( AES_ENC_128_OTFK )
+void aes_encrypt_128( const uint8_t in[N_BLOCK],
+ uint8_t out[N_BLOCK],
+ const uint8_t key[N_BLOCK],
+ uint8_t o_key[N_BLOCK] );
+#endif
+
+#if defined( AES_DEC_128_OTFK )
+void aes_decrypt_128( const uint8_t in[N_BLOCK],
+ uint8_t out[N_BLOCK],
+ const uint8_t key[N_BLOCK],
+ uint8_t o_key[N_BLOCK] );
+#endif
+
+#if defined( AES_ENC_256_OTFK )
+void aes_encrypt_256( const uint8_t in[N_BLOCK],
+ uint8_t out[N_BLOCK],
+ const uint8_t key[2 * N_BLOCK],
+ uint8_t o_key[2 * N_BLOCK] );
+#endif
+
+#if defined( AES_DEC_256_OTFK )
+void aes_decrypt_256( const uint8_t in[N_BLOCK],
+ uint8_t out[N_BLOCK],
+ const uint8_t key[2 * N_BLOCK],
+ uint8_t o_key[2 * N_BLOCK] );
+#endif
+
+#endif
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Crypto/cmac.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Crypto/cmac.c
new file mode 100644
index 00000000..9ea1a42b
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Crypto/cmac.c
@@ -0,0 +1,153 @@
+/**************************************************************************
+Copyright (C) 2009 Lander Casado, Philippas Tsigas
+
+All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files
+(the "Software"), to deal with the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimers. Redistributions in
+binary form must reproduce the above copyright notice, this list of
+conditions and the following disclaimers in the documentation and/or
+other materials provided with the distribution.
+
+In no event shall the authors or copyright holders be liable for any special,
+incidental, indirect or consequential damages of any kind, or any damages
+whatsoever resulting from loss of use, data or profits, whether or not
+advised of the possibility of damage, and on any theory of liability,
+arising out of or in connection with the use or performance of this software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS WITH THE SOFTWARE
+
+*****************************************************************************/
+//#include
+//#include
+#include
+#include "aes.h"
+#include "cmac.h"
+#include "utilities.h"
+
+#define LSHIFT(v, r) do { \
+ int32_t i; \
+ for (i = 0; i < 15; i++) \
+ (r)[i] = (v)[i] << 1 | (v)[i + 1] >> 7; \
+ (r)[15] = (v)[15] << 1; \
+ } while (0)
+
+#define XOR(v, r) do { \
+ int32_t i; \
+ for (i = 0; i < 16; i++) \
+ { \
+ (r)[i] = (r)[i] ^ (v)[i]; \
+ } \
+ } while (0) \
+
+
+void AES_CMAC_Init(AES_CMAC_CTX *ctx)
+{
+ memset1(ctx->X, 0, sizeof ctx->X);
+ ctx->M_n = 0;
+ memset1(ctx->rijndael.ksch, '\0', 240);
+}
+
+void AES_CMAC_SetKey(AES_CMAC_CTX *ctx, const uint8_t key[AES_CMAC_KEY_LENGTH])
+{
+ //rijndael_set_key_enc_only(&ctx->rijndael, key, 128);
+ aes_set_key( key, AES_CMAC_KEY_LENGTH, &ctx->rijndael);
+}
+
+void AES_CMAC_Update(AES_CMAC_CTX *ctx, const uint8_t *data, uint32_t len)
+{
+ uint32_t mlen;
+ uint8_t in[16];
+
+ if (ctx->M_n > 0) {
+ mlen = MIN(16 - ctx->M_n, len);
+ memcpy1(ctx->M_last + ctx->M_n, data, mlen);
+ ctx->M_n += mlen;
+ if (ctx->M_n < 16 || len == mlen)
+ return;
+ XOR(ctx->M_last, ctx->X);
+ //rijndael_encrypt(&ctx->rijndael, ctx->X, ctx->X);
+ aes_encrypt( ctx->X, ctx->X, &ctx->rijndael);
+ data += mlen;
+ len -= mlen;
+ }
+ while (len > 16) { /* not last block */
+
+ XOR(data, ctx->X);
+ //rijndael_encrypt(&ctx->rijndael, ctx->X, ctx->X);
+
+ memcpy1(in, &ctx->X[0], 16); //Bestela ez du ondo iten
+ aes_encrypt( in, in, &ctx->rijndael);
+ memcpy1(&ctx->X[0], in, 16);
+
+ data += 16;
+ len -= 16;
+ }
+ /* potential last block, save it */
+ memcpy1(ctx->M_last, data, len);
+ ctx->M_n = len;
+}
+
+void AES_CMAC_Final(uint8_t digest[AES_CMAC_DIGEST_LENGTH], AES_CMAC_CTX *ctx)
+{
+ uint8_t K[16];
+ uint8_t in[16];
+ /* generate subkey K1 */
+ memset1(K, '\0', 16);
+
+ //rijndael_encrypt(&ctx->rijndael, K, K);
+
+ aes_encrypt( K, K, &ctx->rijndael);
+
+ if (K[0] & 0x80) {
+ LSHIFT(K, K);
+ K[15] ^= 0x87;
+ } else
+ LSHIFT(K, K);
+
+
+ if (ctx->M_n == 16) {
+ /* last block was a complete block */
+ XOR(K, ctx->M_last);
+
+ } else {
+ /* generate subkey K2 */
+ if (K[0] & 0x80) {
+ LSHIFT(K, K);
+ K[15] ^= 0x87;
+ } else
+ LSHIFT(K, K);
+
+ /* padding(M_last) */
+ ctx->M_last[ctx->M_n] = 0x80;
+ while (++ctx->M_n < 16)
+ ctx->M_last[ctx->M_n] = 0;
+
+ XOR(K, ctx->M_last);
+
+
+ }
+ XOR(ctx->M_last, ctx->X);
+
+ //rijndael_encrypt(&ctx->rijndael, ctx->X, digest);
+
+ memcpy1(in, &ctx->X[0], 16); //Bestela ez du ondo iten
+ aes_encrypt(in, digest, &ctx->rijndael);
+ memset1(K, 0, sizeof K);
+
+}
+
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Crypto/cmac.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Crypto/cmac.h
new file mode 100644
index 00000000..af52f5ae
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Crypto/cmac.h
@@ -0,0 +1,63 @@
+/**************************************************************************
+Copyright (C) 2009 Lander Casado, Philippas Tsigas
+
+All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files
+(the "Software"), to deal with the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimers. Redistributions in
+binary form must reproduce the above copyright notice, this list of
+conditions and the following disclaimers in the documentation and/or
+other materials provided with the distribution.
+
+In no event shall the authors or copyright holders be liable for any special,
+incidental, indirect or consequential damages of any kind, or any damages
+whatsoever resulting from loss of use, data or profits, whether or not
+advised of the possibility of damage, and on any theory of liability,
+arising out of or in connection with the use or performance of this software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS WITH THE SOFTWARE
+
+*****************************************************************************/
+
+#ifndef _CMAC_H_
+#define _CMAC_H_
+
+#include "aes.h"
+
+#define AES_CMAC_KEY_LENGTH 16
+#define AES_CMAC_DIGEST_LENGTH 16
+
+typedef struct _AES_CMAC_CTX {
+ aes_context rijndael;
+ uint8_t X[16];
+ uint8_t M_last[16];
+ uint32_t M_n;
+ } AES_CMAC_CTX;
+
+//#include
+
+//__BEGIN_DECLS
+void AES_CMAC_Init(AES_CMAC_CTX * ctx);
+void AES_CMAC_SetKey(AES_CMAC_CTX * ctx, const uint8_t key[AES_CMAC_KEY_LENGTH]);
+void AES_CMAC_Update(AES_CMAC_CTX * ctx, const uint8_t * data, uint32_t len);
+ // __attribute__((__bounded__(__string__,2,3)));
+void AES_CMAC_Final(uint8_t digest[AES_CMAC_DIGEST_LENGTH], AES_CMAC_CTX * ctx);
+ // __attribute__((__bounded__(__minbytes__,1,AES_CMAC_DIGEST_LENGTH)));
+//__END_DECLS
+
+#endif /* _CMAC_H_ */
+
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Crypto/soft-se.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Crypto/soft-se.c
new file mode 100644
index 00000000..068130b2
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Crypto/soft-se.c
@@ -0,0 +1,417 @@
+/*
+ / _____) _ | |
+( (____ _____ ____ _| |_ _____ ____| |__
+ \____ \| ___ | (_ _) ___ |/ ___) _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+ (C)2013 Semtech
+ ___ _____ _ ___ _ _____ ___ ___ ___ ___
+/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+embedded.connectivity.solutions===============
+
+Description: Secure Element software implementation
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+
+Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ),
+ Daniel Jaeckle ( STACKFORCE ), Johannes Bruder ( STACKFORCE )
+*/
+#include "secure-element.h"
+
+#include
+#include
+
+#include "LoRaMacCrypto.h"
+#include "utilities.h"
+#include "aes.h"
+#include "cmac.h"
+#include "radio.h"
+
+#define NUM_OF_KEYS 24
+#define KEY_SIZE 16
+
+/*!
+ * Identifier value pair type for Keys
+ */
+typedef struct sKey
+{
+ /*
+ * Key identifier
+ */
+ KeyIdentifier_t KeyID;
+ /*
+ * Key value
+ */
+ uint8_t KeyValue[KEY_SIZE];
+} Key_t;
+
+/*
+ * Secure Element Non Volatile Context structure
+ */
+typedef struct sSecureElementNvCtx
+{
+ /*
+ * DevEUI storage
+ */
+ uint8_t DevEui[SE_EUI_SIZE];
+ /*
+ * Join EUI storage
+ */
+ uint8_t JoinEui[SE_EUI_SIZE];
+ /*
+ * AES computation context variable
+ */
+ aes_context AesContext;
+ /*
+ * CMAC computation context variable
+ */
+ AES_CMAC_CTX AesCmacCtx[1];
+ /*
+ * Key List
+ */
+ Key_t KeyList[NUM_OF_KEYS];
+}SecureElementNvCtx_t;
+
+/*
+ * Module context
+ */
+static SecureElementNvCtx_t SeNvmCtx;
+
+static SecureElementNvmEvent SeNvmCtxChanged;
+
+/*
+ * Local functions
+ */
+
+/*
+ * Gets key item from key list.
+ *
+ * cmac = aes128_cmac(keyID, B0 | msg)
+ *
+ * \param[IN] keyID - Key identifier
+ * \param[OUT] keyItem - Key item reference
+ * \retval - Status of the operation
+ */
+static SecureElementStatus_t GetKeyByID( KeyIdentifier_t keyID, Key_t** keyItem )
+{
+ for( uint8_t i = 0; i < NUM_OF_KEYS; i++ )
+ {
+ if( SeNvmCtx.KeyList[i].KeyID == keyID )
+ {
+ *keyItem = &( SeNvmCtx.KeyList[i] );
+ return SECURE_ELEMENT_SUCCESS;
+ }
+ }
+ return SECURE_ELEMENT_ERROR_INVALID_KEY_ID;
+}
+
+/*
+ * Dummy callback in case if the user provides NULL function pointer
+ */
+static void DummyCB( void )
+{
+ return;
+}
+
+/*
+ * Computes a CMAC of a message using provided initial Bx block
+ *
+ * cmac = aes128_cmac(keyID, blocks[i].Buffer)
+ *
+ * \param[IN] micBxBuffer - Buffer containing the initial Bx block
+ * \param[IN] buffer - Data buffer
+ * \param[IN] size - Data buffer size
+ * \param[IN] keyID - Key identifier to determine the AES key to be used
+ * \param[OUT] cmac - Computed cmac
+ * \retval - Status of the operation
+ */
+static SecureElementStatus_t ComputeCmac( uint8_t *micBxBuffer, uint8_t *buffer, uint16_t size, KeyIdentifier_t keyID, uint32_t* cmac )
+{
+ if( ( buffer == NULL ) || ( cmac == NULL ) )
+ {
+ return SECURE_ELEMENT_ERROR_NPE;
+ }
+
+ uint8_t Cmac[16];
+
+ AES_CMAC_Init( SeNvmCtx.AesCmacCtx );
+
+ Key_t* keyItem;
+ SecureElementStatus_t retval = GetKeyByID( keyID, &keyItem );
+
+ if( retval == SECURE_ELEMENT_SUCCESS )
+ {
+ AES_CMAC_SetKey( SeNvmCtx.AesCmacCtx, keyItem->KeyValue );
+
+ if( micBxBuffer != NULL )
+ {
+ AES_CMAC_Update( SeNvmCtx.AesCmacCtx, micBxBuffer, 16 );
+ }
+
+ AES_CMAC_Update( SeNvmCtx.AesCmacCtx, buffer, size );
+
+ AES_CMAC_Final( Cmac, SeNvmCtx.AesCmacCtx );
+
+ // Bring into the required format
+ *cmac = ( uint32_t )( ( uint32_t ) Cmac[3] << 24 | ( uint32_t ) Cmac[2] << 16 | ( uint32_t ) Cmac[1] << 8 | ( uint32_t ) Cmac[0] );
+ }
+
+ return retval;
+}
+
+/*
+ * API functions
+ */
+
+SecureElementStatus_t SecureElementInit( SecureElementNvmEvent seNvmCtxChanged )
+{
+ uint8_t itr = 0;
+ uint8_t zeroKey[16] = { 0 };
+
+ // Initialize with defaults
+ SeNvmCtx.KeyList[itr++].KeyID = APP_KEY;
+ SeNvmCtx.KeyList[itr++].KeyID = GEN_APP_KEY;
+ SeNvmCtx.KeyList[itr++].KeyID = NWK_KEY;
+ SeNvmCtx.KeyList[itr++].KeyID = J_S_INT_KEY;
+ SeNvmCtx.KeyList[itr++].KeyID = J_S_ENC_KEY;
+ SeNvmCtx.KeyList[itr++].KeyID = F_NWK_S_INT_KEY;
+ SeNvmCtx.KeyList[itr++].KeyID = S_NWK_S_INT_KEY;
+ SeNvmCtx.KeyList[itr++].KeyID = NWK_S_ENC_KEY;
+ SeNvmCtx.KeyList[itr++].KeyID = APP_S_KEY;
+ SeNvmCtx.KeyList[itr++].KeyID = MC_ROOT_KEY;
+ SeNvmCtx.KeyList[itr++].KeyID = MC_KE_KEY;
+ SeNvmCtx.KeyList[itr++].KeyID = MC_KEY_0;
+ SeNvmCtx.KeyList[itr++].KeyID = MC_APP_S_KEY_0;
+ SeNvmCtx.KeyList[itr++].KeyID = MC_NWK_S_KEY_0;
+ SeNvmCtx.KeyList[itr++].KeyID = MC_KEY_1;
+ SeNvmCtx.KeyList[itr++].KeyID = MC_APP_S_KEY_1;
+ SeNvmCtx.KeyList[itr++].KeyID = MC_NWK_S_KEY_1;
+ SeNvmCtx.KeyList[itr++].KeyID = MC_KEY_2;
+ SeNvmCtx.KeyList[itr++].KeyID = MC_APP_S_KEY_2;
+ SeNvmCtx.KeyList[itr++].KeyID = MC_NWK_S_KEY_2;
+ SeNvmCtx.KeyList[itr++].KeyID = MC_KEY_3;
+ SeNvmCtx.KeyList[itr++].KeyID = MC_APP_S_KEY_3;
+ SeNvmCtx.KeyList[itr++].KeyID = MC_NWK_S_KEY_3;
+ SeNvmCtx.KeyList[itr].KeyID = SLOT_RAND_ZERO_KEY;
+
+ // Set standard keys
+ memcpy1( SeNvmCtx.KeyList[itr].KeyValue, zeroKey, KEY_SIZE );
+
+ memset1( SeNvmCtx.DevEui, 0, SE_EUI_SIZE );
+ memset1( SeNvmCtx.JoinEui, 0, SE_EUI_SIZE );
+
+ // Assign callback
+ if( seNvmCtxChanged != 0 )
+ {
+ SeNvmCtxChanged = seNvmCtxChanged;
+ }
+ else
+ {
+ SeNvmCtxChanged = DummyCB;
+ }
+
+ return SECURE_ELEMENT_SUCCESS;
+}
+
+SecureElementStatus_t SecureElementRestoreNvmCtx( void* seNvmCtx )
+{
+ // Restore nvm context
+ if( seNvmCtx != 0 )
+ {
+ memcpy1( ( uint8_t* ) &SeNvmCtx, ( uint8_t* ) seNvmCtx, sizeof( SeNvmCtx ) );
+ return SECURE_ELEMENT_SUCCESS;
+ }
+ else
+ {
+ return SECURE_ELEMENT_ERROR_NPE;
+ }
+}
+
+void* SecureElementGetNvmCtx( size_t* seNvmCtxSize )
+{
+ *seNvmCtxSize = sizeof( SeNvmCtx );
+ return &SeNvmCtx;
+}
+
+SecureElementStatus_t SecureElementSetKey( KeyIdentifier_t keyID, uint8_t* key )
+{
+ if( key == NULL )
+ {
+ return SECURE_ELEMENT_ERROR_NPE;
+ }
+
+ for( uint8_t i = 0; i < NUM_OF_KEYS; i++ )
+ {
+ if( SeNvmCtx.KeyList[i].KeyID == keyID )
+ {
+ if( ( keyID == MC_KEY_0 ) || ( keyID == MC_KEY_1 ) || ( keyID == MC_KEY_2 ) || ( keyID == MC_KEY_3 ) )
+ { // Decrypt the key if its a Mckey
+ SecureElementStatus_t retval = SECURE_ELEMENT_ERROR;
+ uint8_t decryptedKey[16] = { 0 };
+
+ retval = SecureElementAesEncrypt( key, 16, MC_KE_KEY, decryptedKey );
+
+ memcpy1( SeNvmCtx.KeyList[i].KeyValue, decryptedKey, KEY_SIZE );
+ SeNvmCtxChanged( );
+
+ return retval;
+ }
+ else
+ {
+ memcpy1( SeNvmCtx.KeyList[i].KeyValue, key, KEY_SIZE );
+ SeNvmCtxChanged( );
+ return SECURE_ELEMENT_SUCCESS;
+ }
+ }
+ }
+
+ return SECURE_ELEMENT_ERROR_INVALID_KEY_ID;
+}
+
+SecureElementStatus_t SecureElementComputeAesCmac( uint8_t *micBxBuffer, uint8_t *buffer, uint16_t size, KeyIdentifier_t keyID, uint32_t* cmac )
+{
+ if( keyID >= LORAMAC_CRYPTO_MULTICAST_KEYS )
+ {
+ //Never accept multicast key identifier for cmac computation
+ return SECURE_ELEMENT_ERROR_INVALID_KEY_ID;
+ }
+
+ return ComputeCmac( micBxBuffer, buffer, size, keyID, cmac );
+}
+
+SecureElementStatus_t SecureElementVerifyAesCmac( uint8_t* buffer, uint16_t size, uint32_t expectedCmac, KeyIdentifier_t keyID )
+{
+ if( buffer == NULL )
+ {
+ return SECURE_ELEMENT_ERROR_NPE;
+ }
+
+ SecureElementStatus_t retval = SECURE_ELEMENT_ERROR;
+ uint32_t compCmac = 0;
+ retval = ComputeCmac( NULL, buffer, size, keyID, &compCmac );
+ if( retval != SECURE_ELEMENT_SUCCESS )
+ {
+ return retval;
+ }
+
+ if( expectedCmac != compCmac )
+ {
+ retval = SECURE_ELEMENT_FAIL_CMAC;
+ }
+
+ return retval;
+}
+
+SecureElementStatus_t SecureElementAesEncrypt( uint8_t* buffer, uint16_t size, KeyIdentifier_t keyID, uint8_t* encBuffer )
+{
+ if( buffer == NULL || encBuffer == NULL )
+ {
+ return SECURE_ELEMENT_ERROR_NPE;
+ }
+
+ // Check if the size is divisible by 16,
+ if( ( size % 16 ) != 0 )
+ {
+ return SECURE_ELEMENT_ERROR_BUF_SIZE;
+ }
+
+ memset1( SeNvmCtx.AesContext.ksch, '\0', 240 );
+
+ Key_t* pItem;
+ SecureElementStatus_t retval = GetKeyByID( keyID, &pItem );
+
+ if( retval == SECURE_ELEMENT_SUCCESS )
+ {
+ aes_set_key( pItem->KeyValue, 16, &SeNvmCtx.AesContext );
+
+ uint8_t block = 0;
+
+ while( size != 0 )
+ {
+ aes_encrypt( &buffer[block], &encBuffer[block], &SeNvmCtx.AesContext );
+ block = block + 16;
+ size = size - 16;
+ }
+ }
+ return retval;
+}
+
+SecureElementStatus_t SecureElementDeriveAndStoreKey( Version_t version, uint8_t* input, KeyIdentifier_t rootKeyID, KeyIdentifier_t targetKeyID )
+{
+ if( input == NULL )
+ {
+ return SECURE_ELEMENT_ERROR_NPE;
+ }
+
+ SecureElementStatus_t retval = SECURE_ELEMENT_ERROR;
+ uint8_t key[16] = { 0 };
+
+ // In case of MC_KE_KEY, prevent other keys than NwkKey or AppKey for LoRaWAN 1.1 or later
+ if( targetKeyID == MC_KE_KEY )
+ {
+ if( ( ( rootKeyID == APP_KEY ) && ( version.Fields.Minor == 0 ) ) || ( rootKeyID == NWK_KEY ) )
+ {
+ return SECURE_ELEMENT_ERROR_INVALID_KEY_ID;
+ }
+ }
+
+ // Derive key
+ retval = SecureElementAesEncrypt( input, 16, rootKeyID, key );
+ if( retval != SECURE_ELEMENT_SUCCESS )
+ {
+ return retval;
+ }
+
+ // Store key
+ retval = SecureElementSetKey( targetKeyID, key );
+ if( retval != SECURE_ELEMENT_SUCCESS )
+ {
+ return retval;
+ }
+
+ return SECURE_ELEMENT_SUCCESS;
+}
+
+SecureElementStatus_t SecureElementRandomNumber( uint32_t* randomNum )
+{
+ if( randomNum == NULL )
+ {
+ return SECURE_ELEMENT_ERROR_NPE;
+ }
+ *randomNum = Radio.Random( );
+ return SECURE_ELEMENT_SUCCESS;
+}
+
+SecureElementStatus_t SecureElementSetDevEui( uint8_t* devEui )
+{
+ if( devEui == NULL )
+ {
+ return SECURE_ELEMENT_ERROR_NPE;
+ }
+ memcpy1( SeNvmCtx.DevEui, devEui, SE_EUI_SIZE );
+ SeNvmCtxChanged( );
+ return SECURE_ELEMENT_SUCCESS;
+}
+
+uint8_t* SecureElementGetDevEui( void )
+{
+ return SeNvmCtx.DevEui;
+}
+
+SecureElementStatus_t SecureElementSetJoinEui( uint8_t* joinEui )
+{
+ if( joinEui == NULL )
+ {
+ return SECURE_ELEMENT_ERROR_NPE;
+ }
+ memcpy1( SeNvmCtx.JoinEui, joinEui, SE_EUI_SIZE );
+ SeNvmCtxChanged( );
+ return SECURE_ELEMENT_SUCCESS;
+}
+
+uint8_t* SecureElementGetJoinEui( void )
+{
+ return SeNvmCtx.JoinEui;
+}
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/LICENSE.txt b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/LICENSE.txt
new file mode 100644
index 00000000..d6dfb1b8
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/LICENSE.txt
@@ -0,0 +1,25 @@
+--- Revised BSD License ---
+Copyright (c) 2013, SEMTECH S.A.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the Semtech corporation nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL SEMTECH S.A. BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMac.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMac.c
new file mode 100644
index 00000000..804e897e
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMac.c
@@ -0,0 +1,4754 @@
+/*!
+ * \file LoRaMac.c
+ *
+ * \brief LoRa MAC layer implementation
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013-2017 Semtech
+ *
+ * ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ * embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Gregory Cristian ( Semtech )
+ *
+ * \author Daniel Jaeckle ( STACKFORCE )
+ *
+ * \author Johannes Bruder ( STACKFORCE )
+ */
+#include "utilities.h"
+#include "radio.h"
+#include "region/Region.h"
+#include "LoRaMacClassB.h"
+#include "LoRaMacCrypto.h"
+#include "secure-element.h"
+#include "LoRaMacTest.h"
+#include "LoRaMacTypes.h"
+#include "LoRaMacConfirmQueue.h"
+#include "LoRaMacHeaderTypes.h"
+#include "LoRaMacMessageTypes.h"
+#include "LoRaMacParser.h"
+#include "LoRaMacCommands.h"
+#include "LoRaMacAdr.h"
+
+#include "LoRaMac.h"
+#include "lorawan_conf.h"
+#include "mw_log_conf.h"
+
+/*!
+ * Maximum PHY layer payload size
+ */
+#define LORAMAC_PHY_MAXPAYLOAD 255
+
+/*!
+ * Maximum MAC commands buffer size
+ */
+#define LORA_MAC_COMMAND_MAX_LENGTH 128
+
+/*!
+ * Maximum length of the fOpts field
+ */
+#define LORA_MAC_COMMAND_MAX_FOPTS_LENGTH 15
+
+/*!
+ * LoRaMac duty cycle for the back-off procedure during the first hour.
+ */
+#define BACKOFF_DC_1_HOUR 100
+
+/*!
+ * LoRaMac duty cycle for the back-off procedure during the next 10 hours.
+ */
+#define BACKOFF_DC_10_HOURS 1000
+
+/*!
+ * LoRaMac duty cycle for the back-off procedure during the next 24 hours.
+ */
+#define BACKOFF_DC_24_HOURS 10000
+
+/*!
+ * LoRaMac internal states
+ */
+enum eLoRaMacState
+{
+ LORAMAC_IDLE = 0x00000000,
+ LORAMAC_STOPPED = 0x00000001,
+ LORAMAC_TX_RUNNING = 0x00000002,
+ LORAMAC_RX = 0x00000004,
+ LORAMAC_ACK_RETRY = 0x00000010,
+ LORAMAC_TX_DELAYED = 0x00000020,
+ LORAMAC_TX_CONFIG = 0x00000040,
+ LORAMAC_RX_ABORT = 0x00000080,
+};
+
+/*
+ * Request permission state
+ */
+typedef enum eLoRaMacRequestHandling
+{
+ LORAMAC_REQUEST_HANDLING_OFF = 0,
+ LORAMAC_REQUEST_HANDLING_ON = !LORAMAC_REQUEST_HANDLING_OFF
+}LoRaMacRequestHandling_t;
+
+typedef struct sLoRaMacNvmCtx
+{
+ /*
+ * LoRaMac region.
+ */
+ LoRaMacRegion_t Region;
+ /*
+ * LoRaMac default parameters
+ */
+ LoRaMacParams_t MacParamsDefaults;
+ /*
+ * Network ID ( 3 bytes )
+ */
+ uint32_t NetID;
+ /*
+ * Mote Address
+ */
+ uint32_t DevAddr;
+ /*!
+ * Multicast channel list
+ */
+ MulticastCtx_t MulticastChannelList[LORAMAC_MAX_MC_CTX];
+ /*
+ * Actual device class
+ */
+ DeviceClass_t DeviceClass;
+ /*
+ * Indicates if the node is connected to
+ * a private or public network
+ */
+ bool PublicNetwork;
+ /*
+ * LoRaMac ADR control status
+ */
+ bool AdrCtrlOn;
+ /*
+ * Counts the number of missed ADR acknowledgements
+ */
+ uint32_t AdrAckCounter;
+
+ /*
+ * LoRaMac parameters
+ */
+ LoRaMacParams_t MacParams;
+ /*
+ * Maximum duty cycle
+ * \remark Possibility to shutdown the device.
+ */
+ uint8_t MaxDCycle;
+ /*
+ * Enables/Disables duty cycle management (Test only)
+ */
+ bool DutyCycleOn;
+ /*
+ * Current channel index
+ */
+ uint8_t LastTxChannel;
+ /*
+ * Holds the current rx window slot
+ */
+ bool RepeaterSupport;
+ /*
+ * Buffer containing the MAC layer commands
+ */
+ uint8_t MacCommandsBuffer[LORA_MAC_COMMAND_MAX_LENGTH];
+ /*
+ * If the server has sent a FRAME_TYPE_DATA_CONFIRMED_DOWN this variable indicates
+ * if the ACK bit must be set for the next transmission
+ */
+ bool SrvAckRequested;
+ /*
+ * Aggregated duty cycle management
+ */
+ uint16_t AggregatedDCycle;
+ /*
+ * Aggregated duty cycle management
+ */
+ TimerTime_t LastTxDoneTime;
+ TimerTime_t AggregatedTimeOff;
+ /*
+ * Stores the time at LoRaMac initialization.
+ *
+ * \remark Used for the BACKOFF_DC computation.
+ */
+ TimerTime_t InitializationTime;
+ /*
+ * Current LoRaWAN Version
+ */
+ Version_t Version;
+ /*
+ * End-Device network activation
+ */
+ ActivationType_t NetworkActivation;
+ /*!
+ * Last received Message integrity Code (MIC)
+ */
+ uint32_t LastRxMic;
+}LoRaMacNvmCtx_t;
+
+typedef struct sLoRaMacCtx
+{
+ /*
+ * Length of packet in PktBuffer
+ */
+ uint16_t PktBufferLen;
+ /*
+ * Buffer containing the data to be sent or received.
+ */
+ uint8_t PktBuffer[LORAMAC_PHY_MAXPAYLOAD];
+ /*!
+ * Current processed transmit message
+ */
+ LoRaMacMessage_t TxMsg;
+ /*!
+ * Buffer containing the data received by the application.
+ */
+ uint8_t AppData[LORAMAC_PHY_MAXPAYLOAD];
+ /*
+ * Size of buffer containing the application data.
+ */
+ uint8_t AppDataSize;
+ /*
+ * Buffer containing the upper layer data.
+ */
+ uint8_t RxPayload[LORAMAC_PHY_MAXPAYLOAD];
+ SysTime_t LastTxSysTime;
+ /*
+ * LoRaMac internal state
+ */
+ uint32_t MacState;
+ /*
+ * LoRaMac upper layer event functions
+ */
+ LoRaMacPrimitives_t* MacPrimitives;
+ /*
+ * LoRaMac upper layer callback functions
+ */
+ LoRaMacCallback_t* MacCallbacks;
+ /*
+ * Radio events function pointer
+ */
+ RadioEvents_t RadioEvents;
+ /*
+ * LoRaMac duty cycle delayed Tx timer
+ */
+ TimerEvent_t TxDelayedTimer;
+ /*
+ * LoRaMac reception windows timers
+ */
+ TimerEvent_t RxWindowTimer1;
+ TimerEvent_t RxWindowTimer2;
+ /*
+ * LoRaMac reception windows delay
+ * \remark normal frame: RxWindowXDelay = ReceiveDelayX - RADIO_WAKEUP_TIME
+ * join frame : RxWindowXDelay = JoinAcceptDelayX - RADIO_WAKEUP_TIME
+ */
+ uint32_t RxWindow1Delay;
+ uint32_t RxWindow2Delay;
+ /*
+ * LoRaMac Rx windows configuration
+ */
+ RxConfigParams_t RxWindow1Config;
+ RxConfigParams_t RxWindow2Config;
+ RxConfigParams_t RxWindowCConfig;
+ /*
+ * Limit of uplinks without any donwlink response before the ADRACKReq bit will be set.
+ */
+ uint16_t AdrAckLimit;
+ /*
+ * Limit of uplinks without any donwlink response after a the first frame with set ADRACKReq bit
+ * before the trying to regain the connectivity.
+ */
+ uint16_t AdrAckDelay;
+ /*
+ * Acknowledge timeout timer. Used for packet retransmissions.
+ */
+ TimerEvent_t AckTimeoutTimer;
+ /*
+ * Uplink messages repetitions counter
+ */
+ uint8_t ChannelsNbTransCounter;
+ /*
+ * Number of trials to get a frame acknowledged
+ */
+ uint8_t AckTimeoutRetries;
+ /*
+ * Number of trials to get a frame acknowledged
+ */
+ uint8_t AckTimeoutRetriesCounter;
+ /*
+ * Indicates if the AckTimeout timer has expired or not
+ */
+ bool AckTimeoutRetry;
+ /*
+ * If the node has sent a FRAME_TYPE_DATA_CONFIRMED_UP this variable indicates
+ * if the nodes needs to manage the server acknowledgement.
+ */
+ bool NodeAckRequested;
+ /*
+ * Current channel index
+ */
+ uint8_t Channel;
+ /*
+ * Last transmission time on air
+ */
+ TimerTime_t TxTimeOnAir;
+ /*
+ * Structure to hold an MCPS indication data.
+ */
+ McpsIndication_t McpsIndication;
+ /*
+ * Structure to hold MCPS confirm data.
+ */
+ McpsConfirm_t McpsConfirm;
+ /*
+ * Structure to hold MLME confirm data.
+ */
+ MlmeConfirm_t MlmeConfirm;
+ /*
+ * Structure to hold MLME indication data.
+ */
+ MlmeIndication_t MlmeIndication;
+ /*
+ * Holds the current rx window slot
+ */
+ LoRaMacRxSlot_t RxSlot;
+ /*
+ * LoRaMac tx/rx operation state
+ */
+ LoRaMacFlags_t MacFlags;
+ /*
+ * Data structure indicating if a request is allowed or not.
+ */
+ LoRaMacRequestHandling_t AllowRequests;
+ /*
+ * Non-volatile module context structure
+ */
+ LoRaMacNvmCtx_t* NvmCtx;
+}LoRaMacCtx_t;
+
+/*
+ * Module context.
+ */
+static LoRaMacCtx_t MacCtx;
+
+/*
+ * Non-volatile module context.
+ */
+static LoRaMacNvmCtx_t NvmMacCtx;
+
+
+
+/*
+ * List of module contexts.
+ */
+LoRaMacCtxs_t Contexts;
+
+/*!
+ * Defines the LoRaMac radio events status
+ */
+typedef union uLoRaMacRadioEvents
+{
+ uint32_t Value;
+ struct sEvents
+ {
+ uint32_t RxTimeout : 1;
+ uint32_t RxError : 1;
+ uint32_t TxTimeout : 1;
+ uint32_t RxDone : 1;
+ uint32_t TxDone : 1;
+ }Events;
+}LoRaMacRadioEvents_t;
+
+/*!
+ * LoRaMac radio events status
+ */
+LoRaMacRadioEvents_t LoRaMacRadioEvents = { .Value = 0 };
+
+/*!
+ * \brief Function to be executed on Radio Tx Done event
+ */
+static void OnRadioTxDone( void );
+
+/*!
+ * \brief This function prepares the MAC to abort the execution of function
+ * OnRadioRxDone in case of a reception error.
+ */
+static void PrepareRxDoneAbort( void );
+
+/*!
+ * \brief Function to be executed on Radio Rx Done event
+ */
+static void OnRadioRxDone( uint8_t* payload, uint16_t size, int16_t rssi, int8_t snr );
+
+/*!
+ * \brief Function executed on Radio Tx Timeout event
+ */
+static void OnRadioTxTimeout( void );
+
+/*!
+ * \brief Function executed on Radio Rx error event
+ */
+static void OnRadioRxError( void );
+
+/*!
+ * \brief Function executed on Radio Rx Timeout event
+ */
+static void OnRadioRxTimeout( void );
+
+/*!
+ * \brief Function executed on duty cycle delayed Tx timer event
+ */
+static void OnTxDelayedTimerEvent( void* context );
+
+/*!
+ * \brief Function executed on first Rx window timer event
+ */
+static void OnRxWindow1TimerEvent( void* context );
+
+/*!
+ * \brief Function executed on second Rx window timer event
+ */
+static void OnRxWindow2TimerEvent( void* context );
+
+/*!
+ * \brief Function executed on AckTimeout timer event
+ */
+static void OnAckTimeoutTimerEvent( void* context );
+
+/*!
+ * \brief Configures the events to trigger an MLME-Indication with
+ * a MLME type of MLME_SCHEDULE_UPLINK.
+ */
+static void SetMlmeScheduleUplinkIndication( void );
+
+/*!
+ * Computes next 32 bit downlink counter value and determines the frame counter ID.
+ *
+ * \param[IN] addrID - Address identifier
+ * \param[IN] fType - Frame type
+ * \param[IN] macMsg - Data message object, holding the current 16 bit transmitted frame counter
+ * \param[IN] lrWanVersion - LoRaWAN version
+ * \param[IN] maxFCntGap - Maximum allowed frame counter difference (only for 1.0.X necessary)
+ * \param[OUT] fCntID - Frame counter identifier
+ * \param[OUT] currentDown - Current downlink counter value
+ *
+ * \retval - Status of the operation
+ */
+static LoRaMacCryptoStatus_t GetFCntDown( AddressIdentifier_t addrID, FType_t fType, LoRaMacMessageData_t* macMsg, Version_t lrWanVersion,
+ uint16_t maxFCntGap, FCntIdentifier_t* fCntID, uint32_t* currentDown );
+
+/*!
+ * \brief Switches the device class
+ *
+ * \param [IN] deviceClass Device class to switch to
+ */
+static LoRaMacStatus_t SwitchClass( DeviceClass_t deviceClass );
+
+/*!
+ * \brief Gets the maximum application payload length in the absence of the optional FOpt field.
+ *
+ * \param [IN] datarate Current datarate
+ *
+ * \retval Max length
+ */
+static uint8_t GetMaxAppPayloadWithoutFOptsLength( int8_t datarate );
+
+/*!
+ * \brief Validates if the payload fits into the frame, taking the datarate
+ * into account.
+ *
+ * \details Refer to chapter 4.3.2 of the LoRaWAN specification, v1.0
+ *
+ * \param lenN Length of the application payload. The length depends on the
+ * datarate and is region specific
+ *
+ * \param datarate Current datarate
+ *
+ * \param fOptsLen Length of the fOpts field
+ *
+ * \retval [false: payload does not fit into the frame, true: payload fits into
+ * the frame]
+ */
+static bool ValidatePayloadLength( uint8_t lenN, int8_t datarate, uint8_t fOptsLen );
+
+/*!
+ * \brief Decodes MAC commands in the fOpts field and in the payload
+ *
+ * \param [IN] payload A pointer to the payload
+ * \param [IN] macIndex The index of the payload where the MAC commands start
+ * \param [IN] commandsSize The size of the MAC commands
+ * \param [IN] snr The SNR value of the frame
+ * \param [IN] rxSlot The RX slot where the frame was received
+ */
+static void ProcessMacCommands( uint8_t* payload, uint8_t macIndex, uint8_t commandsSize, int8_t snr, LoRaMacRxSlot_t rxSlot );
+
+/*!
+ * \brief LoRaMAC layer generic send frame
+ *
+ * \param [IN] macHdr MAC header field
+ * \param [IN] fPort MAC payload port
+ * \param [IN] fBuffer MAC data buffer to be sent
+ * \param [IN] fBufferSize MAC data buffer size
+ * \retval status Status of the operation.
+ */
+LoRaMacStatus_t Send( LoRaMacHeader_t* macHdr, uint8_t fPort, void* fBuffer, uint16_t fBufferSize );
+
+/*!
+ * \brief LoRaMAC layer send join/rejoin request
+ *
+ * \param [IN] joinReqType Type of join-request or rejoin
+ *
+ * \retval status Status of the operation.
+ */
+LoRaMacStatus_t SendReJoinReq( JoinReqIdentifier_t joinReqType );
+
+/*!
+ * \brief LoRaMAC layer frame buffer initialization
+ *
+ * \param [IN] macHdr MAC header field
+ * \param [IN] fCtrl MAC frame control field
+ * \param [IN] fOpts MAC commands buffer
+ * \param [IN] fPort MAC payload port
+ * \param [IN] fBuffer MAC data buffer to be sent
+ * \param [IN] fBufferSize MAC data buffer size
+ * \retval status Status of the operation.
+ */
+LoRaMacStatus_t PrepareFrame( LoRaMacHeader_t* macHdr, LoRaMacFrameCtrl_t* fCtrl, uint8_t fPort, void* fBuffer, uint16_t fBufferSize );
+
+/*
+ * \brief Schedules the frame according to the duty cycle
+ *
+ * \param [IN] allowDelayedTx When set to true, the a frame will be delayed,
+ * the duty cycle restriction is active
+ * \retval Status of the operation
+ */
+static LoRaMacStatus_t ScheduleTx( bool allowDelayedTx );
+
+/*
+ * \brief Secures the current processed frame ( TxMsg )
+ * \param[IN] txDr Data rate used for the transmission
+ * \param[IN] txCh Index of the channel used for the transmission
+ * \retval status Status of the operation
+ */
+static LoRaMacStatus_t SecureFrame( uint8_t txDr, uint8_t txCh );
+
+/*
+ * \brief Calculates the back-off time for the band of a channel.
+ *
+ * \param [IN] channel The last Tx channel index
+ */
+static void CalculateBackOff( uint8_t channel );
+
+/*
+ * \brief Function to remove pending MAC commands
+ *
+ * \param [IN] rxSlot The RX slot on which the frame was received
+ * \param [IN] fCtrl The frame control field of the received frame
+ * \param [IN] request The request type
+ */
+static void RemoveMacCommands( LoRaMacRxSlot_t rxSlot, LoRaMacFrameCtrl_t fCtrl, Mcps_t request );
+
+/*!
+ * \brief LoRaMAC layer prepared frame buffer transmission with channel specification
+ *
+ * \remark PrepareFrame must be called at least once before calling this
+ * function.
+ *
+ * \param [IN] channel Channel to transmit on
+ * \retval status Status of the operation.
+ */
+LoRaMacStatus_t SendFrameOnChannel( uint8_t channel );
+
+/*!
+ * \brief Sets the radio in continuous transmission mode
+ *
+ * \remark Uses the radio parameters set on the previous transmission.
+ *
+ * \param [IN] timeout Time in seconds while the radio is kept in continuous wave mode
+ * \retval status Status of the operation.
+ */
+LoRaMacStatus_t SetTxContinuousWave( uint16_t timeout );
+
+/*!
+ * \brief Sets the radio in continuous transmission mode
+ *
+ * \remark Uses the radio parameters set on the previous transmission.
+ *
+ * \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 output power to be set.
+ * \retval status Status of the operation.
+ */
+LoRaMacStatus_t SetTxContinuousWave1( uint16_t timeout, uint32_t frequency, uint8_t power );
+
+/*!
+ * \brief Resets MAC specific parameters to default
+ */
+static void ResetMacParameters( void );
+
+/*!
+ * \brief Initializes and opens the reception window
+ *
+ * \param [IN] rxTimer Window timer to be topped.
+ * \param [IN] rxConfig Window parameters to be setup
+ */
+static void RxWindowSetup( TimerEvent_t* rxTimer, RxConfigParams_t* rxConfig );
+
+/*!
+ * \brief Opens up a continuous RX C window. This is used for
+ * class c devices.
+ */
+static void OpenContinuousRxCWindow( void );
+
+/*!
+ * \brief Returns a pointer to the internal contexts structure.
+ *
+ * \retval void Points to a structure containing all contexts
+ */
+LoRaMacCtxs_t* GetCtxs( void );
+
+/*!
+ * \brief Restoring of internal module contexts
+ *
+ * \details This function allows to restore module contexts by a given pointer.
+ *
+ *
+ * \retval LoRaMacStatus_t Status of the operation. Possible returns are:
+ * returns are:
+ * \ref LORAMAC_STATUS_OK,
+ * \ref LORAMAC_STATUS_PARAMETER_INVALID,
+ */
+LoRaMacStatus_t RestoreCtxs( LoRaMacCtxs_t* contexts );
+
+/*!
+ * \brief Determines the frame type
+ *
+ * \param [IN] macMsg Data message object
+ *
+ * \param [OUT] fType Frame type
+ *
+ * \retval LoRaMacStatus_t Status of the operation. Possible returns are:
+ * returns are:
+ * \ref LORAMAC_STATUS_OK,
+ * \ref LORAMAC_STATUS_PARAMETER_INVALID,
+ */
+LoRaMacStatus_t DetermineFrameType( LoRaMacMessageData_t* macMsg, FType_t* fType );
+
+/*!
+ * \brief Checks if the retransmission should be stopped in case of a unconfirmed uplink
+ *
+ * \retval Returns true if it should be stopped.
+ */
+static bool CheckRetransUnconfirmedUplink( void );
+
+/*!
+ * \brief Checks if the retransmission should be stopped in case of a confirmed uplink
+ *
+ * \retval Returns true it should be stopped.
+ */
+static bool CheckRetransConfirmedUplink( void );
+
+/*!
+ * \brief Stops the uplink retransmission
+ *
+ * \retval Returns true if successful.
+ */
+static bool StopRetransmission( void );
+
+/*!
+ * \brief Handles the ACK retries algorithm.
+ * Increments the re-tries counter up until the specified number of
+ * trials or the allowed maximum. Decrease the uplink datarate every 2
+ * trials.
+ */
+static void AckTimeoutRetriesProcess( void );
+
+/*!
+ * \brief Finalizes the ACK retries algorithm.
+ * If no ACK is received restores the default channels
+ */
+static void AckTimeoutRetriesFinalize( void );
+
+/*!
+ * \brief Calls the callback to indicate that a context changed
+ */
+static void CallNvmCtxCallback( LoRaMacNvmCtxModule_t module );
+
+/*!
+ * \brief MAC NVM Context has been changed
+ */
+static void EventMacNvmCtxChanged( void );
+
+/*!
+ * \brief Region NVM Context has been changed
+ */
+static void EventRegionNvmCtxChanged( void );
+
+/*!
+ * \brief Crypto NVM Context has been changed
+ */
+static void EventCryptoNvmCtxChanged( void );
+
+/*!
+ * \brief Secure Element NVM Context has been changed
+ */
+static void EventSecureElementNvmCtxChanged( void );
+
+/*!
+ * \brief MAC commands module nvm context has been changed
+ */
+static void EventCommandsNvmCtxChanged( void );
+
+/*!
+ * \brief Class B module nvm context has been changed
+ */
+static void EventClassBNvmCtxChanged( void );
+
+/*!
+ * \brief Confirm Queue module nvm context has been changed
+ */
+static void EventConfirmQueueNvmCtxChanged( void );
+
+/*!
+ * \brief Verifies if a request is pending currently
+ *
+ *\retval 1: Request pending, 0: request not pending
+ */
+static uint8_t IsRequestPending( void );
+
+/*!
+ * \brief Enabled the possibility to perform requests
+ *
+ * \param [IN] requestState Request permission state
+ */
+static void LoRaMacEnableRequests( LoRaMacRequestHandling_t requestState );
+
+/*!
+ * \brief This function verifies if a RX abort occurred
+ */
+static void LoRaMacCheckForRxAbort( void );
+
+/*!
+ * \brief This function verifies if a beacon acquisition MLME
+ * request was pending
+ *
+ * \retval 1: Request pending, 0: no request pending
+ */
+static uint8_t LoRaMacCheckForBeaconAcquisition( void );
+
+/*!
+ * \brief This function handles join request
+ */
+static void LoRaMacHandleMlmeRequest( void );
+
+/*!
+ * \brief This function handles mcps request
+ */
+static void LoRaMacHandleMcpsRequest( void );
+
+/*!
+ * \brief This function handles callback events for requests
+ */
+static void LoRaMacHandleRequestEvents( void );
+
+/*!
+ * \brief This function handles callback events for indications
+ */
+static void LoRaMacHandleIndicationEvents( void );
+
+/*!
+ * Structure used to store the radio Tx event data
+ */
+struct
+{
+ TimerTime_t CurTime;
+}TxDoneParams;
+
+/*!
+ * Structure used to store the radio Rx event data
+ */
+struct
+{
+ TimerTime_t LastRxDone;
+ uint8_t *Payload;
+ uint16_t Size;
+ int16_t Rssi;
+ int8_t Snr;
+}RxDoneParams;
+
+static void OnRadioTxDone( void )
+{
+ TxDoneParams.CurTime = TimerGetCurrentTime( );
+ MacCtx.LastTxSysTime = SysTimeGet( );
+
+ LoRaMacRadioEvents.Events.TxDone = 1;
+
+ if( ( MacCtx.MacCallbacks != NULL ) && ( MacCtx.MacCallbacks->MacProcessNotify != NULL ) )
+ {
+ MacCtx.MacCallbacks->MacProcessNotify( );
+ }
+#if !defined(NO_MAC_PRINTF)
+ MW_LOG( "MAC txDone\n\r" );
+#endif
+}
+
+static void OnRadioRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr )
+{
+ RxDoneParams.LastRxDone = TimerGetCurrentTime( );
+ RxDoneParams.Payload = payload;
+ RxDoneParams.Size = size;
+ RxDoneParams.Rssi = rssi;
+ RxDoneParams.Snr = snr;
+
+ LoRaMacRadioEvents.Events.RxDone = 1;
+
+ if( ( MacCtx.MacCallbacks != NULL ) && ( MacCtx.MacCallbacks->MacProcessNotify != NULL ) )
+ {
+ MacCtx.MacCallbacks->MacProcessNotify( );
+ }
+#if !defined(NO_MAC_PRINTF)
+ MW_LOG( "MAC rxDone\n\r" );
+#endif
+}
+
+static void OnRadioTxTimeout( void )
+{
+ LoRaMacRadioEvents.Events.TxTimeout = 1;
+
+ if( ( MacCtx.MacCallbacks != NULL ) && ( MacCtx.MacCallbacks->MacProcessNotify != NULL ) )
+ {
+ MacCtx.MacCallbacks->MacProcessNotify( );
+ }
+#if !defined(NO_MAC_PRINTF)
+ MW_LOG( "MAC txTimeOut\n\r" );
+#endif
+}
+
+static void OnRadioRxError( void )
+{
+ LoRaMacRadioEvents.Events.RxError = 1;
+
+ if( ( MacCtx.MacCallbacks != NULL ) && ( MacCtx.MacCallbacks->MacProcessNotify != NULL ) )
+ {
+ MacCtx.MacCallbacks->MacProcessNotify( );
+ }
+}
+
+static void OnRadioRxTimeout( void )
+{
+ LoRaMacRadioEvents.Events.RxTimeout = 1;
+
+ if( ( MacCtx.MacCallbacks != NULL ) && ( MacCtx.MacCallbacks->MacProcessNotify != NULL ) )
+ {
+ MacCtx.MacCallbacks->MacProcessNotify( );
+ }
+#if !defined(NO_MAC_PRINTF)
+ MW_LOG( "MAC rxTimeOut\n\r" );
+#endif
+}
+
+static void UpdateRxSlotIdleState( void )
+{
+ if( MacCtx.NvmCtx->DeviceClass != CLASS_C )
+ {
+ MacCtx.RxSlot = RX_SLOT_NONE;
+ }
+ else
+ {
+ MacCtx.RxSlot = RX_SLOT_WIN_CLASS_C;
+ }
+}
+
+static void ProcessRadioTxDone( void )
+{
+ GetPhyParams_t getPhy;
+ PhyParam_t phyParam;
+ SetBandTxDoneParams_t txDone;
+
+ if( MacCtx.NvmCtx->DeviceClass != CLASS_C )
+ {
+ Radio.Sleep( );
+ }
+ // Setup timers
+ TimerSetValue( &MacCtx.RxWindowTimer1, MacCtx.RxWindow1Delay );
+ TimerStart( &MacCtx.RxWindowTimer1 );
+ TimerSetValue( &MacCtx.RxWindowTimer2, MacCtx.RxWindow2Delay );
+ TimerStart( &MacCtx.RxWindowTimer2 );
+
+ if( ( MacCtx.NvmCtx->DeviceClass == CLASS_C ) || ( MacCtx.NodeAckRequested == true ) )
+ {
+ getPhy.Attribute = PHY_ACK_TIMEOUT;
+ phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
+ TimerSetValue( &MacCtx.AckTimeoutTimer, MacCtx.RxWindow2Delay + phyParam.Value );
+ TimerStart( &MacCtx.AckTimeoutTimer );
+ }
+
+ // Store last Tx channel
+ MacCtx.NvmCtx->LastTxChannel = MacCtx.Channel;
+ // Update last tx done time for the current channel
+ txDone.Channel = MacCtx.Channel;
+ if( MacCtx.NvmCtx->NetworkActivation == ACTIVATION_TYPE_NONE )
+ {
+ txDone.Joined = false;
+ }
+ else
+ {
+ txDone.Joined = true;
+ }
+ txDone.LastTxDoneTime = TxDoneParams.CurTime;
+ RegionSetBandTxDone( MacCtx.NvmCtx->Region, &txDone );
+ // Update Aggregated last tx done time
+ MacCtx.NvmCtx->LastTxDoneTime = TxDoneParams.CurTime;
+
+ if( MacCtx.NodeAckRequested == false )
+ {
+ MacCtx.McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_OK;
+ }
+}
+
+static void PrepareRxDoneAbort( void )
+{
+ MacCtx.MacState |= LORAMAC_RX_ABORT;
+
+ if( MacCtx.NodeAckRequested == true )
+ {
+ OnAckTimeoutTimerEvent( NULL );
+ }
+
+ MacCtx.MacFlags.Bits.McpsInd = 1;
+ MacCtx.MacFlags.Bits.MacDone = 1;
+
+ UpdateRxSlotIdleState( );
+}
+
+#if (ACTIVE_REGION == LORAMAC_REGION_CN470ALI)
+ static uint8_t adr_on;
+#endif
+static void ProcessRadioRxDone( void )
+{
+ LoRaMacHeader_t macHdr;
+ ApplyCFListParams_t applyCFList;
+ GetPhyParams_t getPhy;
+ PhyParam_t phyParam;
+ LoRaMacCryptoStatus_t macCryptoStatus = LORAMAC_CRYPTO_ERROR;
+
+ LoRaMacMessageData_t macMsgData;
+ LoRaMacMessageJoinAccept_t macMsgJoinAccept;
+ uint8_t *payload = RxDoneParams.Payload;
+ uint16_t size = RxDoneParams.Size;
+ int16_t rssi = RxDoneParams.Rssi;
+ int8_t snr = RxDoneParams.Snr;
+
+ uint8_t pktHeaderLen = 0;
+
+ uint32_t downLinkCounter = 0;
+ uint32_t address = MacCtx.NvmCtx->DevAddr;
+ uint8_t multicast = 0;
+ AddressIdentifier_t addrID = UNICAST_DEV_ADDR;
+ FCntIdentifier_t fCntID;
+
+ MacCtx.McpsConfirm.AckReceived = false;
+ MacCtx.McpsIndication.Rssi = rssi;
+ MacCtx.McpsIndication.Snr = snr;
+ MacCtx.McpsIndication.RxSlot = MacCtx.RxSlot;
+ MacCtx.McpsIndication.Port = 0;
+ MacCtx.McpsIndication.Multicast = 0;
+ MacCtx.McpsIndication.FramePending = 0;
+ MacCtx.McpsIndication.Buffer = NULL;
+ MacCtx.McpsIndication.BufferSize = 0;
+ MacCtx.McpsIndication.RxData = false;
+ MacCtx.McpsIndication.AckReceived = false;
+ MacCtx.McpsIndication.DownLinkCounter = 0;
+ MacCtx.McpsIndication.McpsIndication = MCPS_UNCONFIRMED;
+ MacCtx.McpsIndication.DevAddress = 0;
+ MacCtx.McpsIndication.DeviceTimeAnsReceived = false;
+
+ Radio.Sleep( );
+ TimerStop( &MacCtx.RxWindowTimer2 );
+
+ // This function must be called even if we are not in class b mode yet.
+ if( LoRaMacClassBRxBeacon( payload, size ) == true )
+ {
+ MacCtx.MlmeIndication.BeaconInfo.Rssi = rssi;
+ MacCtx.MlmeIndication.BeaconInfo.Snr = snr;
+ return;
+ }
+ // Check if we expect a ping or a multicast slot.
+ if( MacCtx.NvmCtx->DeviceClass == CLASS_B )
+ {
+ if( LoRaMacClassBIsPingExpected( ) == true )
+ {
+ LoRaMacClassBSetPingSlotState( PINGSLOT_STATE_CALC_PING_OFFSET );
+ LoRaMacClassBPingSlotTimerEvent( NULL );
+ MacCtx.McpsIndication.RxSlot = RX_SLOT_WIN_CLASS_B_PING_SLOT;
+ }
+ else if( LoRaMacClassBIsMulticastExpected( ) == true )
+ {
+ LoRaMacClassBSetMulticastSlotState( PINGSLOT_STATE_CALC_PING_OFFSET );
+ LoRaMacClassBMulticastSlotTimerEvent( NULL );
+ MacCtx.McpsIndication.RxSlot = RX_SLOT_WIN_CLASS_B_MULTICAST_SLOT;
+ }
+ }
+
+ macHdr.Value = payload[pktHeaderLen++];
+
+ switch( macHdr.Bits.MType )
+ {
+ case FRAME_TYPE_JOIN_ACCEPT:
+ macMsgJoinAccept.Buffer = payload;
+ macMsgJoinAccept.BufSize = size;
+
+ // Abort in case if the device isn't joined yet and no rejoin request is ongoing.
+ if( MacCtx.NvmCtx->NetworkActivation != ACTIVATION_TYPE_NONE )
+ {
+ MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
+ PrepareRxDoneAbort( );
+ return;
+ }
+ macCryptoStatus = LoRaMacCryptoHandleJoinAccept( JOIN_REQ, SecureElementGetJoinEui( ), &macMsgJoinAccept );
+
+ if( LORAMAC_CRYPTO_SUCCESS == macCryptoStatus )
+ {
+ // Network ID
+ MacCtx.NvmCtx->NetID = ( uint32_t ) macMsgJoinAccept.NetID[0];
+ MacCtx.NvmCtx->NetID |= ( ( uint32_t ) macMsgJoinAccept.NetID[1] << 8 );
+ MacCtx.NvmCtx->NetID |= ( ( uint32_t ) macMsgJoinAccept.NetID[2] << 16 );
+
+ // Device Address
+ MacCtx.NvmCtx->DevAddr = macMsgJoinAccept.DevAddr;
+
+ // DLSettings
+ MacCtx.NvmCtx->MacParams.Rx1DrOffset = macMsgJoinAccept.DLSettings.Bits.RX1DRoffset;
+ MacCtx.NvmCtx->MacParams.Rx2Channel.Datarate = macMsgJoinAccept.DLSettings.Bits.RX2DataRate;
+ MacCtx.NvmCtx->MacParams.RxCChannel.Datarate = macMsgJoinAccept.DLSettings.Bits.RX2DataRate;
+
+ // RxDelay
+ MacCtx.NvmCtx->MacParams.ReceiveDelay1 = macMsgJoinAccept.RxDelay;
+ if( MacCtx.NvmCtx->MacParams.ReceiveDelay1 == 0 )
+ {
+ MacCtx.NvmCtx->MacParams.ReceiveDelay1 = 1;
+ }
+ MacCtx.NvmCtx->MacParams.ReceiveDelay1 *= 1000;
+ MacCtx.NvmCtx->MacParams.ReceiveDelay2 = MacCtx.NvmCtx->MacParams.ReceiveDelay1 + 1000;
+
+ MacCtx.NvmCtx->Version.Fields.Minor = 0;
+
+ // Apply CF list
+ applyCFList.Payload = macMsgJoinAccept.CFList;
+ // Size of the regular payload is 12. Plus 1 byte MHDR and 4 bytes MIC
+ applyCFList.Size = size - 17;
+
+ RegionApplyCFList( MacCtx.NvmCtx->Region, &applyCFList );
+
+ MacCtx.NvmCtx->NetworkActivation = ACTIVATION_TYPE_OTAA;
+
+ // MLME handling
+ if( LoRaMacConfirmQueueIsCmdActive( MLME_JOIN ) == true )
+ {
+ LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_OK, MLME_JOIN );
+ }
+ }
+ else
+ {
+ // MLME handling
+ if( LoRaMacConfirmQueueIsCmdActive( MLME_JOIN ) == true )
+ {
+ LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL, MLME_JOIN );
+ }
+ }
+ break;
+ case FRAME_TYPE_DATA_CONFIRMED_DOWN:
+ MacCtx.McpsIndication.McpsIndication = MCPS_CONFIRMED;
+ // Intentional fall through
+ case FRAME_TYPE_DATA_UNCONFIRMED_DOWN:
+ // Check if the received payload size is valid
+ getPhy.UplinkDwellTime = MacCtx.NvmCtx->MacParams.DownlinkDwellTime;
+ getPhy.Datarate = MacCtx.McpsIndication.RxDatarate;
+ getPhy.Attribute = PHY_MAX_PAYLOAD;
+
+ // Get the maximum payload length
+ if( MacCtx.NvmCtx->RepeaterSupport == true )
+ {
+ getPhy.Attribute = PHY_MAX_PAYLOAD_REPEATER;
+ }
+ phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
+ if( MAX( 0, ( int16_t )( ( int16_t ) size - ( int16_t ) LORA_MAC_FRMPAYLOAD_OVERHEAD ) ) > ( int16_t )phyParam.Value )
+ {
+ MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
+ PrepareRxDoneAbort( );
+ return;
+ }
+ macMsgData.Buffer = payload;
+ macMsgData.BufSize = size;
+ macMsgData.FRMPayload = MacCtx.RxPayload;
+ macMsgData.FRMPayloadSize = LORAMAC_PHY_MAXPAYLOAD;
+
+ if( LORAMAC_PARSER_SUCCESS != LoRaMacParserData( &macMsgData ) )
+ {
+ MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
+ PrepareRxDoneAbort( );
+ return;
+ }
+
+ // Store device address
+ MacCtx.McpsIndication.DevAddress = macMsgData.FHDR.DevAddr;
+
+ FType_t fType;
+ if( LORAMAC_STATUS_OK != DetermineFrameType( &macMsgData, &fType ) )
+ {
+ MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
+ PrepareRxDoneAbort( );
+ return;
+ }
+
+ //Check if it is a multicast message
+ multicast = 0;
+ downLinkCounter = 0;
+ for( uint8_t i = 0; i < LORAMAC_MAX_MC_CTX; i++ )
+ {
+ if( ( MacCtx.NvmCtx->MulticastChannelList[i].ChannelParams.Address == macMsgData.FHDR.DevAddr ) &&
+ ( MacCtx.NvmCtx->MulticastChannelList[i].ChannelParams.IsEnabled == true ) )
+ {
+ multicast = 1;
+ addrID = MacCtx.NvmCtx->MulticastChannelList[i].ChannelParams.GroupID;
+ downLinkCounter = *( MacCtx.NvmCtx->MulticastChannelList[i].DownLinkCounter );
+ address = MacCtx.NvmCtx->MulticastChannelList[i].ChannelParams.Address;
+ if( MacCtx.NvmCtx->DeviceClass == CLASS_C )
+ {
+ MacCtx.McpsIndication.RxSlot = RX_SLOT_WIN_CLASS_C_MULTICAST;
+ }
+ break;
+ }
+ }
+
+ // Get maximum allowed counter difference
+ getPhy.Attribute = PHY_MAX_FCNT_GAP;
+ phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
+
+ // Get downlink frame counter value
+ macCryptoStatus = GetFCntDown( addrID, fType, &macMsgData, MacCtx.NvmCtx->Version, phyParam.Value, &fCntID, &downLinkCounter );
+ if( macCryptoStatus != LORAMAC_CRYPTO_SUCCESS )
+ {
+ if( macCryptoStatus == LORAMAC_CRYPTO_FAIL_FCNT_DUPLICATED )
+ {
+ // Catch the case of repeated downlink frame counter
+ MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED;
+ if( ( MacCtx.NvmCtx->Version.Fields.Minor == 0 ) && ( macHdr.Bits.MType == FRAME_TYPE_DATA_CONFIRMED_DOWN ) && ( MacCtx.NvmCtx->LastRxMic == macMsgData.MIC ) )
+ {
+ MacCtx.NvmCtx->SrvAckRequested = true;
+ }
+ }
+ else if( macCryptoStatus == LORAMAC_CRYPTO_FAIL_MAX_GAP_FCNT )
+ {
+ // Lost too many frames
+ MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_DOWNLINK_TOO_MANY_FRAMES_LOSS;
+ }
+ else
+ {
+ // Other errors
+ MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
+ }
+ MacCtx.McpsIndication.DownLinkCounter = downLinkCounter;
+ PrepareRxDoneAbort( );
+ return;
+ }
+
+ macCryptoStatus = LoRaMacCryptoUnsecureMessage( addrID, address, fCntID, downLinkCounter, &macMsgData );
+ if( macCryptoStatus != LORAMAC_CRYPTO_SUCCESS )
+ {
+ if( macCryptoStatus == LORAMAC_CRYPTO_FAIL_ADDRESS )
+ {
+ // We are not the destination of this frame.
+ MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ADDRESS_FAIL;
+ }
+ else
+ {
+ // MIC calculation fail
+ MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_MIC_FAIL;
+ }
+ PrepareRxDoneAbort( );
+ return;
+ }
+
+ /*===========================================================*/
+ #if (ACTIVE_REGION == LORAMAC_REGION_CN470ALI)
+ MacCtx.NvmCtx->AdrCtrlOn = macMsgData.FHDR.FCtrl.Bits.Adr;
+ #endif
+
+ // Frame is valid
+ MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_OK;
+ MacCtx.McpsIndication.Multicast = multicast;
+ MacCtx.McpsIndication.FramePending = macMsgData.FHDR.FCtrl.Bits.FPending;
+ MacCtx.McpsIndication.Buffer = NULL;
+ MacCtx.McpsIndication.BufferSize = 0;
+ MacCtx.McpsIndication.DownLinkCounter = downLinkCounter;
+ MacCtx.McpsIndication.AckReceived = macMsgData.FHDR.FCtrl.Bits.Ack;
+
+ MacCtx.McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_OK;
+ MacCtx.McpsConfirm.AckReceived = macMsgData.FHDR.FCtrl.Bits.Ack;
+
+ // Reset ADR ACK Counter only, when RX1 or RX2 slot
+ if( ( MacCtx.McpsIndication.RxSlot == RX_SLOT_WIN_1 ) ||
+ ( MacCtx.McpsIndication.RxSlot == RX_SLOT_WIN_2 ) )
+ {
+ MacCtx.NvmCtx->AdrAckCounter = 0;
+ }
+
+ // MCPS Indication and ack requested handling
+ if( multicast == 1 )
+ {
+ MacCtx.McpsIndication.McpsIndication = MCPS_MULTICAST;
+ }
+ else
+ {
+ if( macHdr.Bits.MType == FRAME_TYPE_DATA_CONFIRMED_DOWN )
+ {
+ MacCtx.NvmCtx->SrvAckRequested = true;
+ if( MacCtx.NvmCtx->Version.Fields.Minor == 0 )
+ {
+ MacCtx.NvmCtx->LastRxMic = macMsgData.MIC;
+ }
+ MacCtx.McpsIndication.McpsIndication = MCPS_CONFIRMED;
+ }
+ else
+ {
+ MacCtx.NvmCtx->SrvAckRequested = false;
+ MacCtx.McpsIndication.McpsIndication = MCPS_UNCONFIRMED;
+ }
+ }
+
+ RemoveMacCommands( MacCtx.McpsIndication.RxSlot, macMsgData.FHDR.FCtrl, MacCtx.McpsConfirm.McpsRequest );
+
+ switch( fType )
+ {
+ case FRAME_TYPE_A:
+ { /* +----------+------+-------+--------------+
+ * | FOptsLen | Fopt | FPort | FRMPayload |
+ * +----------+------+-------+--------------+
+ * | > 0 | X | > 0 | X |
+ * +----------+------+-------+--------------+
+ */
+
+ // Decode MAC commands in FOpts field
+ ProcessMacCommands( macMsgData.FHDR.FOpts, 0, macMsgData.FHDR.FCtrl.Bits.FOptsLen, snr, MacCtx.McpsIndication.RxSlot );
+ MacCtx.McpsIndication.Port = macMsgData.FPort;
+ MacCtx.McpsIndication.Buffer = macMsgData.FRMPayload;
+ MacCtx.McpsIndication.BufferSize = macMsgData.FRMPayloadSize;
+ MacCtx.McpsIndication.RxData = true;
+ break;
+ }
+ case FRAME_TYPE_B:
+ { /* +----------+------+-------+--------------+
+ * | FOptsLen | Fopt | FPort | FRMPayload |
+ * +----------+------+-------+--------------+
+ * | > 0 | X | - | - |
+ * +----------+------+-------+--------------+
+ */
+
+ // Decode MAC commands in FOpts field
+ ProcessMacCommands( macMsgData.FHDR.FOpts, 0, macMsgData.FHDR.FCtrl.Bits.FOptsLen, snr, MacCtx.McpsIndication.RxSlot );
+ MacCtx.McpsIndication.Port = macMsgData.FPort;
+ break;
+ }
+ case FRAME_TYPE_C:
+ { /* +----------+------+-------+--------------+
+ * | FOptsLen | Fopt | FPort | FRMPayload |
+ * +----------+------+-------+--------------+
+ * | = 0 | - | = 0 | MAC commands |
+ * +----------+------+-------+--------------+
+ */
+
+ // Decode MAC commands in FRMPayload
+ ProcessMacCommands( macMsgData.FRMPayload, 0, macMsgData.FRMPayloadSize, snr, MacCtx.McpsIndication.RxSlot );
+ MacCtx.McpsIndication.Port = macMsgData.FPort;
+ break;
+ }
+ case FRAME_TYPE_D:
+ { /* +----------+------+-------+--------------+
+ * | FOptsLen | Fopt | FPort | FRMPayload |
+ * +----------+------+-------+--------------+
+ * | = 0 | - | > 0 | X |
+ * +----------+------+-------+--------------+
+ */
+
+ // No MAC commands just application payload
+ MacCtx.McpsIndication.Port = macMsgData.FPort;
+ MacCtx.McpsIndication.Buffer = macMsgData.FRMPayload;
+ MacCtx.McpsIndication.BufferSize = macMsgData.FRMPayloadSize;
+ MacCtx.McpsIndication.RxData = true;
+ break;
+ }
+ default:
+ MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
+ PrepareRxDoneAbort( );
+ break;
+ }
+
+ // Provide always an indication, skip the callback to the user application,
+ // in case of a confirmed downlink retransmission.
+ MacCtx.MacFlags.Bits.McpsInd = 1;
+
+ break;
+ case FRAME_TYPE_PROPRIETARY:
+ memcpy1( MacCtx.RxPayload, &payload[pktHeaderLen], size - pktHeaderLen );
+
+ MacCtx.McpsIndication.McpsIndication = MCPS_PROPRIETARY;
+ MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_OK;
+ MacCtx.McpsIndication.Buffer = MacCtx.RxPayload;
+ MacCtx.McpsIndication.BufferSize = size - pktHeaderLen;
+
+ MacCtx.MacFlags.Bits.McpsInd = 1;
+ break;
+ default:
+ MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
+ PrepareRxDoneAbort( );
+ break;
+ }
+
+ // Verify if we need to disable the AckTimeoutTimer
+ if( MacCtx.NodeAckRequested == true )
+ {
+ if( MacCtx.McpsConfirm.AckReceived == true )
+ {
+ OnAckTimeoutTimerEvent( NULL );
+ }
+ }
+ else
+ {
+ if( MacCtx.NvmCtx->DeviceClass == CLASS_C )
+ {
+ OnAckTimeoutTimerEvent( NULL );
+ }
+ }
+ MacCtx.MacFlags.Bits.MacDone = 1;
+
+ UpdateRxSlotIdleState( );
+}
+
+static void ProcessRadioTxTimeout( void )
+{
+ if( MacCtx.NvmCtx->DeviceClass != CLASS_C )
+ {
+ Radio.Sleep( );
+ }
+ UpdateRxSlotIdleState( );
+
+ MacCtx.McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT;
+ LoRaMacConfirmQueueSetStatusCmn( LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT );
+ if( MacCtx.NodeAckRequested == true )
+ {
+ MacCtx.AckTimeoutRetry = true;
+ }
+ MacCtx.MacFlags.Bits.MacDone = 1;
+}
+
+static void HandleRadioRxErrorTimeout( LoRaMacEventInfoStatus_t rx1EventInfoStatus, LoRaMacEventInfoStatus_t rx2EventInfoStatus )
+{
+ bool classBRx = false;
+
+ if( MacCtx.NvmCtx->DeviceClass != CLASS_C )
+ {
+ Radio.Sleep( );
+ }
+
+ if( LoRaMacClassBIsBeaconExpected( ) == true )
+ {
+ LoRaMacClassBSetBeaconState( BEACON_STATE_TIMEOUT );
+ LoRaMacClassBBeaconTimerEvent( NULL );
+ classBRx = true;
+ }
+ if( MacCtx.NvmCtx->DeviceClass == CLASS_B )
+ {
+ if( LoRaMacClassBIsPingExpected( ) == true )
+ {
+ LoRaMacClassBSetPingSlotState( PINGSLOT_STATE_CALC_PING_OFFSET );
+ LoRaMacClassBPingSlotTimerEvent( NULL );
+ classBRx = true;
+ }
+ if( LoRaMacClassBIsMulticastExpected( ) == true )
+ {
+ LoRaMacClassBSetMulticastSlotState( PINGSLOT_STATE_CALC_PING_OFFSET );
+ LoRaMacClassBMulticastSlotTimerEvent( NULL );
+ classBRx = true;
+ }
+ }
+
+ if( classBRx == false )
+ {
+ if( MacCtx.RxSlot == RX_SLOT_WIN_1 )
+ {
+ if( MacCtx.NodeAckRequested == true )
+ {
+ MacCtx.McpsConfirm.Status = rx1EventInfoStatus;
+ }
+ LoRaMacConfirmQueueSetStatusCmn( rx1EventInfoStatus );
+
+ if( TimerGetElapsedTime( MacCtx.NvmCtx->LastTxDoneTime ) >= MacCtx.RxWindow2Delay )
+ {
+ TimerStop( &MacCtx.RxWindowTimer2 );
+ MacCtx.MacFlags.Bits.MacDone = 1;
+ }
+ }
+ else
+ {
+ if( MacCtx.NodeAckRequested == true )
+ {
+ MacCtx.McpsConfirm.Status = rx2EventInfoStatus;
+ }
+ LoRaMacConfirmQueueSetStatusCmn( rx2EventInfoStatus );
+
+ if( MacCtx.NvmCtx->DeviceClass != CLASS_C )
+ {
+ MacCtx.MacFlags.Bits.MacDone = 1;
+ }
+ }
+ }
+
+ UpdateRxSlotIdleState( );
+}
+
+static void ProcessRadioRxError( void )
+{
+ HandleRadioRxErrorTimeout( LORAMAC_EVENT_INFO_STATUS_RX1_ERROR, LORAMAC_EVENT_INFO_STATUS_RX2_ERROR );
+}
+
+static void ProcessRadioRxTimeout( void )
+{
+ HandleRadioRxErrorTimeout( LORAMAC_EVENT_INFO_STATUS_RX1_TIMEOUT, LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT );
+}
+
+static void LoRaMacHandleIrqEvents( void )
+{
+ LoRaMacRadioEvents_t events;
+
+ CRITICAL_SECTION_BEGIN( );
+ events = LoRaMacRadioEvents;
+ LoRaMacRadioEvents.Value = 0;
+ CRITICAL_SECTION_END( );
+
+ if( events.Value != 0 )
+ {
+ if( events.Events.TxDone == 1 )
+ {
+ ProcessRadioTxDone( );
+ }
+ if( events.Events.RxDone == 1 )
+ {
+ ProcessRadioRxDone( );
+ }
+ if( events.Events.TxTimeout == 1 )
+ {
+ ProcessRadioTxTimeout( );
+ }
+ if( events.Events.RxError == 1 )
+ {
+ ProcessRadioRxError( );
+ }
+ if( events.Events.RxTimeout == 1 )
+ {
+ ProcessRadioRxTimeout( );
+ }
+ }
+}
+
+bool LoRaMacIsBusy( void )
+{
+ if( ( MacCtx.MacState == LORAMAC_IDLE ) &&
+ ( MacCtx.AllowRequests == LORAMAC_REQUEST_HANDLING_ON ) )
+ {
+ return false;
+ }
+ return true;
+}
+
+
+static void LoRaMacEnableRequests( LoRaMacRequestHandling_t requestState )
+{
+ MacCtx.AllowRequests = requestState;
+}
+
+static void LoRaMacHandleRequestEvents( void )
+{
+ // Handle events
+ LoRaMacFlags_t reqEvents = MacCtx.MacFlags;
+
+ if( MacCtx.MacState == LORAMAC_IDLE )
+ {
+ // Update event bits
+ if( MacCtx.MacFlags.Bits.McpsReq == 1 )
+ {
+ MacCtx.MacFlags.Bits.McpsReq = 0;
+ }
+
+ if( MacCtx.MacFlags.Bits.MlmeReq == 1 )
+ {
+ MacCtx.MacFlags.Bits.MlmeReq = 0;
+ }
+
+ // Allow requests again
+ LoRaMacEnableRequests( LORAMAC_REQUEST_HANDLING_ON );
+
+ // Handle callbacks
+ if( reqEvents.Bits.McpsReq == 1 )
+ {
+ MacCtx.MacPrimitives->MacMcpsConfirm( &MacCtx.McpsConfirm );
+ }
+
+ if( reqEvents.Bits.MlmeReq == 1 )
+ {
+ LoRaMacConfirmQueueHandleCb( &MacCtx.MlmeConfirm );
+ if( LoRaMacConfirmQueueGetCnt( ) > 0 )
+ {
+ MacCtx.MacFlags.Bits.MlmeReq = 1;
+ }
+ }
+
+ // Start beaconing again
+ LoRaMacClassBResumeBeaconing( );
+
+ // Procedure done. Reset variables.
+ MacCtx.MacFlags.Bits.MacDone = 0;
+ }
+}
+
+static void LoRaMacHandleScheduleUplinkEvent( void )
+{
+ // Handle events
+ if( MacCtx.MacState == LORAMAC_IDLE )
+ {
+ // Verify if sticky MAC commands are pending or not
+ bool isStickyMacCommandPending = false;
+ LoRaMacCommandsStickyCmdsPending( &isStickyMacCommandPending );
+ if( isStickyMacCommandPending == true )
+ {// Setup MLME indication
+ SetMlmeScheduleUplinkIndication( );
+ }
+ }
+}
+
+static void LoRaMacHandleIndicationEvents( void )
+{
+ // Handle MLME indication
+ if( MacCtx.MacFlags.Bits.MlmeInd == 1 )
+ {
+ MacCtx.MacFlags.Bits.MlmeInd = 0;
+ MacCtx.MacPrimitives->MacMlmeIndication( &MacCtx.MlmeIndication );
+ }
+
+ if( MacCtx.MacFlags.Bits.MlmeSchedUplinkInd == 1 )
+ {
+ MlmeIndication_t schduleUplinkIndication;
+ schduleUplinkIndication.MlmeIndication = MLME_SCHEDULE_UPLINK;
+ schduleUplinkIndication.Status = LORAMAC_EVENT_INFO_STATUS_OK;
+
+ MacCtx.MacPrimitives->MacMlmeIndication( &schduleUplinkIndication );
+ MacCtx.MacFlags.Bits.MlmeSchedUplinkInd = 0;
+ }
+
+ // Handle MCPS indication
+ if( MacCtx.MacFlags.Bits.McpsInd == 1 )
+ {
+ MacCtx.MacFlags.Bits.McpsInd = 0;
+ MacCtx.MacPrimitives->MacMcpsIndication( &MacCtx.McpsIndication );
+ }
+}
+
+static void LoRaMacHandleMcpsRequest( void )
+{
+ // Handle MCPS uplinks
+ if( MacCtx.MacFlags.Bits.McpsReq == 1 )
+ {
+ bool stopRetransmission = false;
+ bool waitForRetransmission = false;
+
+ if( ( MacCtx.McpsConfirm.McpsRequest == MCPS_UNCONFIRMED ) ||
+ ( MacCtx.McpsConfirm.McpsRequest == MCPS_PROPRIETARY ) )
+ {
+ stopRetransmission = CheckRetransUnconfirmedUplink( );
+ }
+ else if( MacCtx.McpsConfirm.McpsRequest == MCPS_CONFIRMED )
+ {
+ if( MacCtx.AckTimeoutRetry == true )
+ {
+ stopRetransmission = CheckRetransConfirmedUplink( );
+
+ if( MacCtx.NvmCtx->Version.Fields.Minor == 0 )
+ {
+ if( stopRetransmission == false )
+ {
+ AckTimeoutRetriesProcess( );
+ }
+ else
+ {
+ AckTimeoutRetriesFinalize( );
+ }
+ }
+ }
+ else
+ {
+ waitForRetransmission = true;
+ }
+ }
+
+ if( stopRetransmission == true )
+ {// Stop retransmission
+ TimerStop( &MacCtx.TxDelayedTimer );
+ MacCtx.MacState &= ~LORAMAC_TX_DELAYED;
+ StopRetransmission( );
+ }
+ else if( waitForRetransmission == false )
+ {// Arrange further retransmission
+ MacCtx.MacFlags.Bits.MacDone = 0;
+ // Reset the state of the AckTimeout
+ MacCtx.AckTimeoutRetry = false;
+ // Sends the same frame again
+ OnTxDelayedTimerEvent( NULL );
+ }
+ }
+}
+
+static void LoRaMacHandleMlmeRequest( void )
+{
+ // Handle join request
+ if( MacCtx.MacFlags.Bits.MlmeReq == 1 )
+ {
+ if( ( LoRaMacConfirmQueueIsCmdActive( MLME_JOIN ) == true ) )
+ {
+ if( LoRaMacConfirmQueueGetStatus( MLME_JOIN ) == LORAMAC_EVENT_INFO_STATUS_OK )
+ {// Node joined successfully
+ MacCtx.ChannelsNbTransCounter = 0;
+ }
+ MacCtx.MacState &= ~LORAMAC_TX_RUNNING;
+ }
+ else if( ( LoRaMacConfirmQueueIsCmdActive( MLME_TXCW ) == true ) ||
+ ( LoRaMacConfirmQueueIsCmdActive( MLME_TXCW_1 ) == true ) )
+ {
+ MacCtx.MacState &= ~LORAMAC_TX_RUNNING;
+ }
+ }
+}
+
+static uint8_t LoRaMacCheckForBeaconAcquisition( void )
+{
+ if( ( LoRaMacConfirmQueueIsCmdActive( MLME_BEACON_ACQUISITION ) == true ) &&
+ ( MacCtx.MacFlags.Bits.McpsReq == 0 ) )
+ {
+ if( MacCtx.MacFlags.Bits.MlmeReq == 1 )
+ {
+ MacCtx.MacState &= ~LORAMAC_TX_RUNNING;
+ return 0x01;
+ }
+ }
+ return 0x00;
+}
+
+static void LoRaMacCheckForRxAbort( void )
+{
+ // A error occurs during receiving
+ if( ( MacCtx.MacState & LORAMAC_RX_ABORT ) == LORAMAC_RX_ABORT )
+ {
+ MacCtx.MacState &= ~LORAMAC_RX_ABORT;
+ MacCtx.MacState &= ~LORAMAC_TX_RUNNING;
+ }
+}
+
+
+void LoRaMacProcess( void *args)
+{
+ uint8_t noTx = false;
+
+ LoRaMacHandleIrqEvents( );
+ LoRaMacClassBProcess( );
+
+ // MAC proceeded a state and is ready to check
+ if( MacCtx.MacFlags.Bits.MacDone == 1 )
+ {
+ LoRaMacEnableRequests( LORAMAC_REQUEST_HANDLING_OFF );
+ LoRaMacCheckForRxAbort( );
+
+ // An error occurs during transmitting
+ if( IsRequestPending( ) > 0 )
+ {
+ noTx |= LoRaMacCheckForBeaconAcquisition( );
+ }
+
+ if( noTx == 0x00 )
+ {
+ LoRaMacHandleMlmeRequest( );
+ LoRaMacHandleMcpsRequest( );
+ }
+ LoRaMacHandleRequestEvents( );
+ LoRaMacHandleScheduleUplinkEvent( );
+ LoRaMacEnableRequests( LORAMAC_REQUEST_HANDLING_ON );
+ }
+ LoRaMacHandleIndicationEvents( );
+ if( MacCtx.RxSlot == RX_SLOT_WIN_CLASS_C )
+ {
+ OpenContinuousRxCWindow( );
+ }
+}
+
+static void OnTxDelayedTimerEvent( void* context )
+{
+ TimerStop( &MacCtx.TxDelayedTimer );
+ MacCtx.MacState &= ~LORAMAC_TX_DELAYED;
+
+ // Schedule frame, allow delayed frame transmissions
+ switch( ScheduleTx( true ) )
+ {
+ case LORAMAC_STATUS_OK:
+ case LORAMAC_STATUS_DUTYCYCLE_RESTRICTED:
+ {
+ break;
+ }
+ default:
+ {
+ // Stop retransmission attempt
+ MacCtx.McpsConfirm.Datarate = MacCtx.NvmCtx->MacParams.ChannelsDatarate;
+ MacCtx.McpsConfirm.NbRetries = MacCtx.AckTimeoutRetriesCounter;
+ MacCtx.McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_TX_DR_PAYLOAD_SIZE_ERROR;
+ LoRaMacConfirmQueueSetStatusCmn( LORAMAC_EVENT_INFO_STATUS_TX_DR_PAYLOAD_SIZE_ERROR );
+ StopRetransmission( );
+ break;
+ }
+ }
+}
+
+static void OnRxWindow1TimerEvent( void* context )
+{
+ MacCtx.RxWindow1Config.Channel = MacCtx.Channel;
+ MacCtx.RxWindow1Config.DrOffset = MacCtx.NvmCtx->MacParams.Rx1DrOffset;
+ MacCtx.RxWindow1Config.DownlinkDwellTime = MacCtx.NvmCtx->MacParams.DownlinkDwellTime;
+ MacCtx.RxWindow1Config.RepeaterSupport = MacCtx.NvmCtx->RepeaterSupport;
+ MacCtx.RxWindow1Config.RxContinuous = false;
+ MacCtx.RxWindow1Config.RxSlot = RX_SLOT_WIN_1;
+
+ RxWindowSetup( &MacCtx.RxWindowTimer1, &MacCtx.RxWindow1Config );
+}
+
+static void OnRxWindow2TimerEvent( void* context )
+{
+ // Check if we are processing Rx1 window.
+ // If yes, we don't setup the Rx2 window.
+ if( MacCtx.RxSlot == RX_SLOT_WIN_1 )
+ {
+ return;
+ }
+ MacCtx.RxWindow2Config.Channel = MacCtx.Channel;
+ MacCtx.RxWindow2Config.Frequency = MacCtx.NvmCtx->MacParams.Rx2Channel.Frequency;
+ MacCtx.RxWindow2Config.DownlinkDwellTime = MacCtx.NvmCtx->MacParams.DownlinkDwellTime;
+ MacCtx.RxWindow2Config.RepeaterSupport = MacCtx.NvmCtx->RepeaterSupport;
+ MacCtx.RxWindow2Config.RxContinuous = false;
+ MacCtx.RxWindow2Config.RxSlot = RX_SLOT_WIN_2;
+
+ RxWindowSetup( &MacCtx.RxWindowTimer2, &MacCtx.RxWindow2Config );
+}
+
+static void OnAckTimeoutTimerEvent( void* context )
+{
+ TimerStop( &MacCtx.AckTimeoutTimer );
+
+ if( MacCtx.NodeAckRequested == true )
+ {
+ MacCtx.AckTimeoutRetry = true;
+ }
+ if( MacCtx.NvmCtx->DeviceClass == CLASS_C )
+ {
+ MacCtx.MacFlags.Bits.MacDone = 1;
+ }
+ if( ( MacCtx.MacCallbacks != NULL ) && ( MacCtx.MacCallbacks->MacProcessNotify != NULL ) )
+ {
+ MacCtx.MacCallbacks->MacProcessNotify( );
+ }
+}
+
+static LoRaMacCryptoStatus_t GetFCntDown( AddressIdentifier_t addrID, FType_t fType, LoRaMacMessageData_t* macMsg, Version_t lrWanVersion,
+ uint16_t maxFCntGap, FCntIdentifier_t* fCntID, uint32_t* currentDown )
+{
+ if( ( macMsg == NULL ) || ( fCntID == NULL ) ||
+ ( currentDown == NULL ) )
+ {
+ return LORAMAC_CRYPTO_ERROR_NPE;
+ }
+
+ // Determine the frame counter identifier and choose counter from FCntList
+ switch( addrID )
+ {
+ case UNICAST_DEV_ADDR:
+ if( lrWanVersion.Fields.Minor == 1 )
+ {
+ if( ( fType == FRAME_TYPE_A ) || ( fType == FRAME_TYPE_D ) )
+ {
+ *fCntID = A_FCNT_DOWN;
+ }
+ else
+ {
+ *fCntID = N_FCNT_DOWN;
+ }
+ }
+ else
+ { // For LoRaWAN 1.0.X
+ *fCntID = FCNT_DOWN;
+ }
+ break;
+ case MULTICAST_0_ADDR:
+ *fCntID = MC_FCNT_DOWN_0;
+ break;
+ case MULTICAST_1_ADDR:
+ *fCntID = MC_FCNT_DOWN_1;
+ break;
+ case MULTICAST_2_ADDR:
+ *fCntID = MC_FCNT_DOWN_2;
+ break;
+ case MULTICAST_3_ADDR:
+ *fCntID = MC_FCNT_DOWN_3;
+ break;
+ default:
+ return LORAMAC_CRYPTO_FAIL_FCNT_ID;
+ }
+
+ return LoRaMacCryptoGetFCntDown( *fCntID, maxFCntGap, macMsg->FHDR.FCnt, currentDown );
+}
+
+static LoRaMacStatus_t SwitchClass( DeviceClass_t deviceClass )
+{
+ LoRaMacStatus_t status = LORAMAC_STATUS_PARAMETER_INVALID;
+
+ switch( MacCtx.NvmCtx->DeviceClass )
+ {
+ case CLASS_A:
+ {
+ if( deviceClass == CLASS_A )
+ {
+ // Revert back RxC parameters
+ MacCtx.NvmCtx->MacParams.RxCChannel = MacCtx.NvmCtx->MacParams.Rx2Channel;
+ }
+ if( deviceClass == CLASS_B )
+ {
+ status = LoRaMacClassBSwitchClass( deviceClass );
+ if( status == LORAMAC_STATUS_OK )
+ {
+ MacCtx.NvmCtx->DeviceClass = deviceClass;
+ }
+ }
+
+ if( deviceClass == CLASS_C )
+ {
+ MacCtx.NvmCtx->DeviceClass = deviceClass;
+
+ MacCtx.RxWindowCConfig = MacCtx.RxWindow2Config;
+ MacCtx.RxWindowCConfig.RxSlot = RX_SLOT_WIN_CLASS_C;
+
+ for( int8_t i = 0; i < LORAMAC_MAX_MC_CTX; i++ )
+ {
+ if( MacCtx.NvmCtx->MulticastChannelList[i].ChannelParams.IsEnabled == true )
+ // TODO: Check multicast channel device class.
+ {
+ MacCtx.NvmCtx->MacParams.RxCChannel.Frequency = MacCtx.NvmCtx->MulticastChannelList[i].ChannelParams.RxParams.ClassC.Frequency;
+ MacCtx.NvmCtx->MacParams.RxCChannel.Datarate = MacCtx.NvmCtx->MulticastChannelList[i].ChannelParams.RxParams.ClassC.Datarate;
+
+ MacCtx.RxWindowCConfig.Channel = MacCtx.Channel;
+ MacCtx.RxWindowCConfig.Frequency = MacCtx.NvmCtx->MacParams.RxCChannel.Frequency;
+ MacCtx.RxWindowCConfig.DownlinkDwellTime = MacCtx.NvmCtx->MacParams.DownlinkDwellTime;
+ MacCtx.RxWindowCConfig.RepeaterSupport = MacCtx.NvmCtx->RepeaterSupport;
+ MacCtx.RxWindowCConfig.RxSlot = RX_SLOT_WIN_CLASS_C_MULTICAST;
+ MacCtx.RxWindowCConfig.RxContinuous = true;
+ break;
+ }
+ }
+
+ // Set the NodeAckRequested indicator to default
+ MacCtx.NodeAckRequested = false;
+ // Set the radio into sleep mode in case we are still in RX mode
+ Radio.Sleep( );
+ // Compute Rx2 windows parameters in case the RX2 datarate has changed
+ RegionComputeRxWindowParameters( MacCtx.NvmCtx->Region,
+ MacCtx.NvmCtx->MacParams.RxCChannel.Datarate,
+ MacCtx.NvmCtx->MacParams.MinRxSymbols,
+ MacCtx.NvmCtx->MacParams.SystemMaxRxError,
+ &MacCtx.RxWindowCConfig );
+ OpenContinuousRxCWindow( );
+
+ status = LORAMAC_STATUS_OK;
+ }
+ break;
+ }
+ case CLASS_B:
+ {
+ status = LoRaMacClassBSwitchClass( deviceClass );
+ if( status == LORAMAC_STATUS_OK )
+ {
+ MacCtx.NvmCtx->DeviceClass = deviceClass;
+ }
+ break;
+ }
+ case CLASS_C:
+ {
+ if( deviceClass == CLASS_A )
+ {
+ MacCtx.NvmCtx->DeviceClass = deviceClass;
+
+ // Set the radio into sleep to setup a defined state
+ Radio.Sleep( );
+
+ status = LORAMAC_STATUS_OK;
+ }
+ break;
+ }
+ }
+
+ return status;
+}
+
+static uint8_t GetMaxAppPayloadWithoutFOptsLength( int8_t datarate )
+{
+ GetPhyParams_t getPhy;
+ PhyParam_t phyParam;
+
+ // Setup PHY request
+ getPhy.UplinkDwellTime = MacCtx.NvmCtx->MacParams.UplinkDwellTime;
+ getPhy.Datarate = datarate;
+ getPhy.Attribute = PHY_MAX_PAYLOAD;
+
+ // Get the maximum payload length
+ if( MacCtx.NvmCtx->RepeaterSupport == true )
+ {
+ getPhy.Attribute = PHY_MAX_PAYLOAD_REPEATER;
+ }
+ phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
+
+ return phyParam.Value;
+}
+
+static bool ValidatePayloadLength( uint8_t lenN, int8_t datarate, uint8_t fOptsLen )
+{
+ uint16_t maxN = 0;
+ uint16_t payloadSize = 0;
+
+ maxN = GetMaxAppPayloadWithoutFOptsLength( datarate );
+
+ // Calculate the resulting payload size
+ payloadSize = ( lenN + fOptsLen );
+
+ // Validation of the application payload size
+ if( ( payloadSize <= maxN ) && ( payloadSize <= LORAMAC_PHY_MAXPAYLOAD ) )
+ {
+ return true;
+ }
+ return false;
+}
+
+static void SetMlmeScheduleUplinkIndication( void )
+{
+ MacCtx.MacFlags.Bits.MlmeSchedUplinkInd = 1;
+}
+
+static void ProcessMacCommands( uint8_t *payload, uint8_t macIndex, uint8_t commandsSize, int8_t snr, LoRaMacRxSlot_t rxSlot )
+{
+ uint8_t status = 0;
+ bool adrBlockFound = false;
+ uint8_t macCmdPayload[2] = { 0x00, 0x00 };
+
+ while( macIndex < commandsSize )
+ {
+ // Decode Frame MAC commands
+ switch( payload[macIndex++] )
+ {
+ case SRV_MAC_LINK_CHECK_ANS:
+ {
+ if( LoRaMacConfirmQueueIsCmdActive( MLME_LINK_CHECK ) == true )
+ {
+ LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_OK, MLME_LINK_CHECK );
+ MacCtx.MlmeConfirm.DemodMargin = payload[macIndex++];
+ MacCtx.MlmeConfirm.NbGateways = payload[macIndex++];
+ }
+ break;
+ }
+ case SRV_MAC_LINK_ADR_REQ:
+ {
+ LinkAdrReqParams_t linkAdrReq;
+ int8_t linkAdrDatarate = DR_0;
+ int8_t linkAdrTxPower = TX_POWER_0;
+ uint8_t linkAdrNbRep = 0;
+ uint8_t linkAdrNbBytesParsed = 0;
+
+ if( adrBlockFound == false )
+ {
+ adrBlockFound = true;
+
+ // Fill parameter structure
+ linkAdrReq.Payload = &payload[macIndex - 1];
+ linkAdrReq.PayloadSize = commandsSize - ( macIndex - 1 );
+ linkAdrReq.AdrEnabled = MacCtx.NvmCtx->AdrCtrlOn;
+ linkAdrReq.UplinkDwellTime = MacCtx.NvmCtx->MacParams.UplinkDwellTime;
+ linkAdrReq.CurrentDatarate = MacCtx.NvmCtx->MacParams.ChannelsDatarate;
+ linkAdrReq.CurrentTxPower = MacCtx.NvmCtx->MacParams.ChannelsTxPower;
+ linkAdrReq.CurrentNbRep = MacCtx.NvmCtx->MacParams.ChannelsNbTrans;
+ linkAdrReq.Version = MacCtx.NvmCtx->Version;
+
+ // Process the ADR requests
+ status = RegionLinkAdrReq( MacCtx.NvmCtx->Region, &linkAdrReq, &linkAdrDatarate,
+ &linkAdrTxPower, &linkAdrNbRep, &linkAdrNbBytesParsed );
+
+ if( ( status & 0x07 ) == 0x07 )
+ {
+ MacCtx.NvmCtx->MacParams.ChannelsDatarate = linkAdrDatarate;
+ MacCtx.NvmCtx->MacParams.ChannelsTxPower = linkAdrTxPower;
+ MacCtx.NvmCtx->MacParams.ChannelsNbTrans = linkAdrNbRep;
+ }
+
+ // Add the answers to the buffer
+ for( uint8_t i = 0; i < ( linkAdrNbBytesParsed / 5 ); i++ )
+ {
+ LoRaMacCommandsAddCmd( MOTE_MAC_LINK_ADR_ANS, &status, 1 );
+ }
+ // Update MAC index
+ macIndex += linkAdrNbBytesParsed - 1;
+ }
+ break;
+ }
+ case SRV_MAC_DUTY_CYCLE_REQ:
+ {
+ MacCtx.NvmCtx->MaxDCycle = payload[macIndex++] & 0x0F;
+ MacCtx.NvmCtx->AggregatedDCycle = 1 << MacCtx.NvmCtx->MaxDCycle;
+ LoRaMacCommandsAddCmd( MOTE_MAC_DUTY_CYCLE_ANS, macCmdPayload, 0 );
+ break;
+ }
+ case SRV_MAC_RX_PARAM_SETUP_REQ:
+ {
+ RxParamSetupReqParams_t rxParamSetupReq;
+ status = 0x07;
+
+ rxParamSetupReq.DrOffset = ( payload[macIndex] >> 4 ) & 0x07;
+ rxParamSetupReq.Datarate = payload[macIndex] & 0x0F;
+ macIndex++;
+
+ 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( MacCtx.NvmCtx->Region, &rxParamSetupReq );
+
+ if( ( status & 0x07 ) == 0x07 )
+ {
+ MacCtx.NvmCtx->MacParams.Rx2Channel.Datarate = rxParamSetupReq.Datarate;
+ MacCtx.NvmCtx->MacParams.RxCChannel.Datarate = rxParamSetupReq.Datarate;
+ MacCtx.NvmCtx->MacParams.Rx2Channel.Frequency = rxParamSetupReq.Frequency;
+ MacCtx.NvmCtx->MacParams.RxCChannel.Frequency = rxParamSetupReq.Frequency;
+ MacCtx.NvmCtx->MacParams.Rx1DrOffset = rxParamSetupReq.DrOffset;
+ }
+ macCmdPayload[0] = status;
+ LoRaMacCommandsAddCmd( MOTE_MAC_RX_PARAM_SETUP_ANS, macCmdPayload, 1 );
+ // Setup indication to inform the application
+ SetMlmeScheduleUplinkIndication( );
+ break;
+ }
+ case SRV_MAC_DEV_STATUS_REQ:
+ {
+ uint8_t batteryLevel = BAT_LEVEL_NO_MEASURE;
+ if( ( MacCtx.MacCallbacks != NULL ) && ( MacCtx.MacCallbacks->GetBatteryLevel != NULL ) )
+ {
+ batteryLevel = MacCtx.MacCallbacks->GetBatteryLevel( );
+ }
+ macCmdPayload[0] = batteryLevel;
+ macCmdPayload[1] = ( uint8_t )( snr & 0x3F );
+ LoRaMacCommandsAddCmd( MOTE_MAC_DEV_STATUS_ANS, macCmdPayload, 2 );
+ break;
+ }
+ case SRV_MAC_NEW_CHANNEL_REQ:
+ {
+ NewChannelReqParams_t newChannelReq;
+ ChannelParams_t chParam;
+ 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++];
+
+ status = RegionNewChannelReq( MacCtx.NvmCtx->Region, &newChannelReq );
+
+ macCmdPayload[0] = status;
+ LoRaMacCommandsAddCmd( MOTE_MAC_NEW_CHANNEL_ANS, macCmdPayload, 1 );
+ break;
+ }
+ case SRV_MAC_RX_TIMING_SETUP_REQ:
+ {
+ uint8_t delay = payload[macIndex++] & 0x0F;
+
+ if( delay == 0 )
+ {
+ delay++;
+ }
+ MacCtx.NvmCtx->MacParams.ReceiveDelay1 = delay * 1000;
+ MacCtx.NvmCtx->MacParams.ReceiveDelay2 = MacCtx.NvmCtx->MacParams.ReceiveDelay1 + 1000;
+ LoRaMacCommandsAddCmd( MOTE_MAC_RX_TIMING_SETUP_ANS, macCmdPayload, 0 );
+ // Setup indication to inform the application
+ SetMlmeScheduleUplinkIndication( );
+ break;
+ }
+ case SRV_MAC_TX_PARAM_SETUP_REQ:
+ {
+ TxParamSetupReqParams_t txParamSetupReq;
+ GetPhyParams_t getPhy;
+ PhyParam_t phyParam;
+ 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( MacCtx.NvmCtx->Region, &txParamSetupReq ) != -1 )
+ {
+ // Accept command
+ MacCtx.NvmCtx->MacParams.UplinkDwellTime = txParamSetupReq.UplinkDwellTime;
+ MacCtx.NvmCtx->MacParams.DownlinkDwellTime = txParamSetupReq.DownlinkDwellTime;
+ MacCtx.NvmCtx->MacParams.MaxEirp = LoRaMacMaxEirpTable[txParamSetupReq.MaxEirp];
+ // Update the datarate in case of the new configuration limits it
+ getPhy.Attribute = PHY_MIN_TX_DR;
+ getPhy.UplinkDwellTime = MacCtx.NvmCtx->MacParams.UplinkDwellTime;
+ phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
+ MacCtx.NvmCtx->MacParams.ChannelsDatarate = MAX( MacCtx.NvmCtx->MacParams.ChannelsDatarate, ( int8_t )phyParam.Value );
+
+ // Add command response
+ LoRaMacCommandsAddCmd( MOTE_MAC_TX_PARAM_SETUP_ANS, macCmdPayload, 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( MacCtx.NvmCtx->Region, &dlChannelReq );
+ macCmdPayload[0] = status;
+ LoRaMacCommandsAddCmd( MOTE_MAC_DL_CHANNEL_ANS, macCmdPayload, 1 );
+ // Setup indication to inform the application
+ SetMlmeScheduleUplinkIndication( );
+ break;
+ }
+ case SRV_MAC_DEVICE_TIME_ANS:
+ {
+ SysTime_t gpsEpochTime = { 0 };
+ SysTime_t sysTime = { 0 };
+ SysTime_t sysTimeCurrent = { 0 };
+
+ gpsEpochTime.Seconds = ( uint32_t )payload[macIndex++];
+ gpsEpochTime.Seconds |= ( uint32_t )payload[macIndex++] << 8;
+ gpsEpochTime.Seconds |= ( uint32_t )payload[macIndex++] << 16;
+ gpsEpochTime.Seconds |= ( uint32_t )payload[macIndex++] << 24;
+ gpsEpochTime.SubSeconds = payload[macIndex++];
+
+ // Convert the fractional second received in ms
+ // round( pow( 0.5, 8.0 ) * 1000 ) = 3.90625
+ gpsEpochTime.SubSeconds = ( int16_t )( ( ( int32_t )gpsEpochTime.SubSeconds * 1000 ) >> 8 );
+
+ // Copy received GPS Epoch time into system time
+ sysTime = gpsEpochTime;
+ // Add Unix to Gps epcoh offset. The system time is based on Unix time.
+ sysTime.Seconds += UNIX_GPS_EPOCH_OFFSET;
+
+ // Compensate time difference between Tx Done time and now
+ sysTimeCurrent = SysTimeGet( );
+ sysTime = SysTimeAdd( sysTimeCurrent, SysTimeSub( sysTime, MacCtx.LastTxSysTime ) );
+
+ // Apply the new system time.
+ SysTimeSet( sysTime );
+ LoRaMacClassBDeviceTimeAns( );
+ MacCtx.McpsIndication.DeviceTimeAnsReceived = true;
+ break;
+ }
+ case SRV_MAC_PING_SLOT_INFO_ANS:
+ {
+ // According to the specification, it is not allowed to process this answer in
+ // a ping or multicast slot
+ if( ( MacCtx.RxSlot != RX_SLOT_WIN_CLASS_B_PING_SLOT ) && ( MacCtx.RxSlot != RX_SLOT_WIN_CLASS_B_MULTICAST_SLOT ) )
+ {
+ 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 );
+ macCmdPayload[0] = status;
+ LoRaMacCommandsAddCmd( MOTE_MAC_PING_SLOT_FREQ_ANS, macCmdPayload, 1 );
+ 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, RxDoneParams.LastRxDone );
+ 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 )
+ {
+ macCmdPayload[0] = 1;
+ }
+ else
+ {
+ macCmdPayload[0] = 0;
+ }
+ LoRaMacCommandsAddCmd( MOTE_MAC_BEACON_FREQ_ANS, macCmdPayload, 1 );
+ }
+ break;
+ default:
+ // Unknown command. ABORT MAC commands processing
+ return;
+ }
+ }
+}
+
+LoRaMacStatus_t Send( LoRaMacHeader_t* macHdr, uint8_t fPort, void* fBuffer, uint16_t fBufferSize )
+{
+ LoRaMacFrameCtrl_t fCtrl;
+ LoRaMacStatus_t status = LORAMAC_STATUS_PARAMETER_INVALID;
+ int8_t datarate = MacCtx.NvmCtx->MacParams.ChannelsDatarate;
+ int8_t txPower = MacCtx.NvmCtx->MacParams.ChannelsTxPower;
+ uint32_t adrAckCounter = MacCtx.NvmCtx->AdrAckCounter;
+ CalcNextAdrParams_t adrNext;
+
+ // Check if we are joined
+ if( MacCtx.NvmCtx->NetworkActivation == ACTIVATION_TYPE_NONE )
+ {
+ return LORAMAC_STATUS_NO_NETWORK_JOINED;
+ }
+ if( MacCtx.NvmCtx->MaxDCycle == 0 )
+ {
+ MacCtx.NvmCtx->AggregatedTimeOff = 0;
+ }
+
+ fCtrl.Value = 0;
+ fCtrl.Bits.FOptsLen = 0;
+ fCtrl.Bits.Adr = MacCtx.NvmCtx->AdrCtrlOn;
+
+ // Check class b
+ if( MacCtx.NvmCtx->DeviceClass == CLASS_B )
+ {
+ fCtrl.Bits.FPending = 1;
+ }
+ else
+ {
+ fCtrl.Bits.FPending = 0;
+ }
+
+ // Check server ack
+ if( MacCtx.NvmCtx->SrvAckRequested == true )
+ {
+ fCtrl.Bits.Ack = 1;
+ }
+
+ // ADR next request
+ adrNext.Version = MacCtx.NvmCtx->Version;
+ adrNext.UpdateChanMask = true;
+ adrNext.AdrEnabled = fCtrl.Bits.Adr;
+ adrNext.AdrAckCounter = MacCtx.NvmCtx->AdrAckCounter;
+ adrNext.AdrAckLimit = MacCtx.AdrAckLimit;
+ adrNext.AdrAckDelay = MacCtx.AdrAckDelay;
+ adrNext.Datarate = MacCtx.NvmCtx->MacParams.ChannelsDatarate;
+ adrNext.TxPower = MacCtx.NvmCtx->MacParams.ChannelsTxPower;
+ adrNext.UplinkDwellTime = MacCtx.NvmCtx->MacParams.UplinkDwellTime;
+ adrNext.Region = MacCtx.NvmCtx->Region;
+
+ fCtrl.Bits.AdrAckReq = LoRaMacAdrCalcNext( &adrNext, &MacCtx.NvmCtx->MacParams.ChannelsDatarate,
+ &MacCtx.NvmCtx->MacParams.ChannelsTxPower, &adrAckCounter );
+
+ // Prepare the frame
+ status = PrepareFrame( macHdr, &fCtrl, fPort, fBuffer, fBufferSize );
+
+ // Validate status
+ if( ( status == LORAMAC_STATUS_OK ) || ( status == LORAMAC_STATUS_SKIPPED_APP_DATA ) )
+ {
+ // Schedule frame, do not allow delayed transmissions
+ status = ScheduleTx( false );
+ }
+
+ // Post processing
+ if( status != LORAMAC_STATUS_OK )
+ {
+ // Bad case - restore
+ // Store local variables
+ MacCtx.NvmCtx->MacParams.ChannelsDatarate = datarate;
+ MacCtx.NvmCtx->MacParams.ChannelsTxPower = txPower;
+ }
+ else
+ {
+ // Good case
+ MacCtx.NvmCtx->SrvAckRequested = false;
+ MacCtx.NvmCtx->AdrAckCounter = adrAckCounter;
+ // Remove all none sticky MAC commands
+ if( LoRaMacCommandsRemoveNoneStickyCmds( ) != LORAMAC_COMMANDS_SUCCESS )
+ {
+ return LORAMAC_STATUS_MAC_COMMAD_ERROR;
+ }
+ }
+ return status;
+}
+
+LoRaMacStatus_t SendReJoinReq( JoinReqIdentifier_t joinReqType )
+{
+ LoRaMacStatus_t status = LORAMAC_STATUS_OK;
+ LoRaMacHeader_t macHdr;
+ macHdr.Value = 0;
+ bool allowDelayedTx = true;
+
+ // Setup join/rejoin message
+ switch( joinReqType )
+ {
+ case JOIN_REQ:
+ {
+ SwitchClass( CLASS_A );
+
+ MacCtx.TxMsg.Type = LORAMAC_MSG_TYPE_JOIN_REQUEST;
+ MacCtx.TxMsg.Message.JoinReq.Buffer = MacCtx.PktBuffer;
+ MacCtx.TxMsg.Message.JoinReq.BufSize = LORAMAC_PHY_MAXPAYLOAD;
+
+ macHdr.Bits.MType = FRAME_TYPE_JOIN_REQ;
+ MacCtx.TxMsg.Message.JoinReq.MHDR.Value = macHdr.Value;
+
+ memcpy1( MacCtx.TxMsg.Message.JoinReq.JoinEUI, SecureElementGetJoinEui( ), LORAMAC_JOIN_EUI_FIELD_SIZE );
+ memcpy1( MacCtx.TxMsg.Message.JoinReq.DevEUI, SecureElementGetDevEui( ), LORAMAC_DEV_EUI_FIELD_SIZE );
+
+ allowDelayedTx = false;
+
+ break;
+ }
+ default:
+ status = LORAMAC_STATUS_SERVICE_UNKNOWN;
+ break;
+ }
+
+ // Schedule frame
+ status = ScheduleTx( allowDelayedTx );
+ return status;
+}
+
+static LoRaMacStatus_t CheckForClassBCollision( void )
+{
+ if( LoRaMacClassBIsBeaconExpected( ) == true )
+ {
+ return LORAMAC_STATUS_BUSY_BEACON_RESERVED_TIME;
+ }
+
+ if( MacCtx.NvmCtx->DeviceClass == CLASS_B )
+ {
+ if( LoRaMacClassBIsPingExpected( ) == true )
+ {
+ return LORAMAC_STATUS_BUSY_PING_SLOT_WINDOW_TIME;
+ }
+ else if( LoRaMacClassBIsMulticastExpected( ) == true )
+ {
+ return LORAMAC_STATUS_BUSY_PING_SLOT_WINDOW_TIME;
+ }
+ }
+ return LORAMAC_STATUS_OK;
+}
+
+static LoRaMacStatus_t ScheduleTx( bool allowDelayedTx )
+{
+ LoRaMacStatus_t status = LORAMAC_STATUS_PARAMETER_INVALID;
+ TimerTime_t dutyCycleTimeOff = 0;
+ NextChanParams_t nextChan;
+ size_t macCmdsSize = 0;
+
+ // Check class b collisions
+ status = CheckForClassBCollision( );
+
+ if( status != LORAMAC_STATUS_OK )
+ {
+ return status;
+ }
+
+ // Update back-off
+ CalculateBackOff( MacCtx.NvmCtx->LastTxChannel );
+
+ nextChan.AggrTimeOff = MacCtx.NvmCtx->AggregatedTimeOff;
+ nextChan.Datarate = MacCtx.NvmCtx->MacParams.ChannelsDatarate;
+ nextChan.DutyCycleEnabled = MacCtx.NvmCtx->DutyCycleOn;
+ if( MacCtx.NvmCtx->NetworkActivation == ACTIVATION_TYPE_NONE )
+ {
+ nextChan.Joined = false;
+ }
+ else
+ {
+ nextChan.Joined = true;
+ }
+ nextChan.LastAggrTx = MacCtx.NvmCtx->LastTxDoneTime;
+
+ // Select channel
+ status = RegionNextChannel( MacCtx.NvmCtx->Region, &nextChan, &MacCtx.Channel, &dutyCycleTimeOff, &MacCtx.NvmCtx->AggregatedTimeOff );
+
+ if( status != LORAMAC_STATUS_OK )
+ {
+ if( ( status == LORAMAC_STATUS_DUTYCYCLE_RESTRICTED ) &&
+ ( allowDelayedTx == true ) )
+ {
+ // Allow delayed transmissions. We have to allow it in case
+ // the MAC must retransmit a frame with the frame repetitions
+ if( dutyCycleTimeOff != 0 )
+ {// Send later - prepare timer
+ MacCtx.MacState |= LORAMAC_TX_DELAYED;
+ TimerSetValue( &MacCtx.TxDelayedTimer, dutyCycleTimeOff );
+ TimerStart( &MacCtx.TxDelayedTimer );
+ }
+ return LORAMAC_STATUS_OK;
+ }
+ else
+ {// State where the MAC cannot send a frame
+ return status;
+ }
+ }
+
+ // Compute Rx1 windows parameters
+ RegionComputeRxWindowParameters( MacCtx.NvmCtx->Region,
+ RegionApplyDrOffset( MacCtx.NvmCtx->Region, MacCtx.NvmCtx->MacParams.DownlinkDwellTime, MacCtx.NvmCtx->MacParams.ChannelsDatarate, MacCtx.NvmCtx->MacParams.Rx1DrOffset ),
+ MacCtx.NvmCtx->MacParams.MinRxSymbols,
+ MacCtx.NvmCtx->MacParams.SystemMaxRxError,
+ &MacCtx.RxWindow1Config );
+ // Compute Rx2 windows parameters
+ RegionComputeRxWindowParameters( MacCtx.NvmCtx->Region,
+ MacCtx.NvmCtx->MacParams.Rx2Channel.Datarate,
+ MacCtx.NvmCtx->MacParams.MinRxSymbols,
+ MacCtx.NvmCtx->MacParams.SystemMaxRxError,
+ &MacCtx.RxWindow2Config );
+
+ if( MacCtx.NvmCtx->NetworkActivation == ACTIVATION_TYPE_NONE )
+ {
+ MacCtx.RxWindow1Delay = MacCtx.NvmCtx->MacParams.JoinAcceptDelay1 + MacCtx.RxWindow1Config.WindowOffset;
+ MacCtx.RxWindow2Delay = MacCtx.NvmCtx->MacParams.JoinAcceptDelay2 + MacCtx.RxWindow2Config.WindowOffset;
+ }
+ else
+ {
+ if( LoRaMacCommandsGetSizeSerializedCmds( &macCmdsSize ) != LORAMAC_COMMANDS_SUCCESS )
+ {
+ return LORAMAC_STATUS_MAC_COMMAD_ERROR;
+ }
+
+ if( ValidatePayloadLength( MacCtx.AppDataSize, MacCtx.NvmCtx->MacParams.ChannelsDatarate, macCmdsSize ) == false )
+ {
+ return LORAMAC_STATUS_LENGTH_ERROR;
+ }
+ MacCtx.RxWindow1Delay = MacCtx.NvmCtx->MacParams.ReceiveDelay1 + MacCtx.RxWindow1Config.WindowOffset;
+ MacCtx.RxWindow2Delay = MacCtx.NvmCtx->MacParams.ReceiveDelay2 + MacCtx.RxWindow2Config.WindowOffset;
+ }
+
+ // Secure frame
+ LoRaMacStatus_t retval = SecureFrame( MacCtx.NvmCtx->MacParams.ChannelsDatarate, MacCtx.Channel );
+ if( retval != LORAMAC_STATUS_OK )
+ {
+ return retval;
+ }
+
+ // Try to send now
+ return SendFrameOnChannel( MacCtx.Channel );
+}
+
+static LoRaMacStatus_t SecureFrame( uint8_t txDr, uint8_t txCh )
+{
+ LoRaMacCryptoStatus_t macCryptoStatus = LORAMAC_CRYPTO_ERROR;
+ uint32_t fCntUp = 0;
+
+ switch( MacCtx.TxMsg.Type )
+ {
+ case LORAMAC_MSG_TYPE_JOIN_REQUEST:
+ macCryptoStatus = LoRaMacCryptoPrepareJoinRequest( &MacCtx.TxMsg.Message.JoinReq );
+ if( LORAMAC_CRYPTO_SUCCESS != macCryptoStatus )
+ {
+ return LORAMAC_STATUS_CRYPTO_ERROR;
+ }
+ MacCtx.PktBufferLen = MacCtx.TxMsg.Message.JoinReq.BufSize;
+ break;
+ case LORAMAC_MSG_TYPE_DATA:
+
+ if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoGetFCntUp( &fCntUp ) )
+ {
+ return LORAMAC_STATUS_FCNT_HANDLER_ERROR;
+ }
+
+ if( ( MacCtx.ChannelsNbTransCounter >= 1 ) || ( MacCtx.AckTimeoutRetriesCounter > 1 ) )
+ {
+ fCntUp -= 1;
+ }
+
+ macCryptoStatus = LoRaMacCryptoSecureMessage( fCntUp, txDr, txCh, &MacCtx.TxMsg.Message.Data );
+ if( LORAMAC_CRYPTO_SUCCESS != macCryptoStatus )
+ {
+ return LORAMAC_STATUS_CRYPTO_ERROR;
+ }
+ MacCtx.PktBufferLen = MacCtx.TxMsg.Message.Data.BufSize;
+ break;
+ case LORAMAC_MSG_TYPE_JOIN_ACCEPT:
+ case LORAMAC_MSG_TYPE_UNDEF:
+ default:
+ return LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+ return LORAMAC_STATUS_OK;
+}
+
+static void CalculateBackOff( uint8_t channel )
+{
+ CalcBackOffParams_t calcBackOff;
+
+ if( MacCtx.NvmCtx->NetworkActivation == ACTIVATION_TYPE_NONE )
+ {
+ calcBackOff.Joined = false;
+ }
+ else
+ {
+ calcBackOff.Joined = true;
+ }
+ calcBackOff.DutyCycleEnabled = MacCtx.NvmCtx->DutyCycleOn;
+ calcBackOff.Channel = channel;
+ calcBackOff.ElapsedTime = TimerGetElapsedTime( MacCtx.NvmCtx->InitializationTime );
+ calcBackOff.TxTimeOnAir = MacCtx.TxTimeOnAir;
+ calcBackOff.LastTxIsJoinRequest = false;
+ if( ( MacCtx.MacFlags.Bits.MlmeReq == 1 ) && ( LoRaMacConfirmQueueIsCmdActive( MLME_JOIN ) == true ) )
+ {
+ calcBackOff.LastTxIsJoinRequest = true;
+ }
+
+ // Update regional back-off
+ RegionCalcBackOff( MacCtx.NvmCtx->Region, &calcBackOff );
+
+ // Update aggregated time-off. This must be an assignment and no incremental
+ // update as we do only calculate the time-off based on the last transmission
+ MacCtx.NvmCtx->AggregatedTimeOff = ( MacCtx.TxTimeOnAir * MacCtx.NvmCtx->AggregatedDCycle - MacCtx.TxTimeOnAir );
+}
+
+static void RemoveMacCommands( LoRaMacRxSlot_t rxSlot, LoRaMacFrameCtrl_t fCtrl, Mcps_t request )
+{
+ if( rxSlot == RX_SLOT_WIN_1 || rxSlot == RX_SLOT_WIN_2 )
+ {
+ // Remove all sticky MAC commands answers since we can assume
+ // that they have been received by the server.
+ if( request == MCPS_CONFIRMED )
+ {
+ if( fCtrl.Bits.Ack == 1 )
+ { // For confirmed uplinks only if we have received an ACK.
+ LoRaMacCommandsRemoveStickyAnsCmds( );
+ }
+ }
+ else
+ {
+ LoRaMacCommandsRemoveStickyAnsCmds( );
+ }
+ }
+}
+
+
+static void ResetMacParameters( void )
+{
+ MacCtx.NvmCtx->NetworkActivation = ACTIVATION_TYPE_NONE;
+
+ // ADR counter
+ MacCtx.NvmCtx->AdrAckCounter = 0;
+
+ MacCtx.ChannelsNbTransCounter = 0;
+ MacCtx.AckTimeoutRetries = 1;
+ MacCtx.AckTimeoutRetriesCounter = 1;
+ MacCtx.AckTimeoutRetry = false;
+
+ MacCtx.NvmCtx->MaxDCycle = 0;
+ MacCtx.NvmCtx->AggregatedDCycle = 1;
+
+ MacCtx.NvmCtx->MacParams.ChannelsTxPower = MacCtx.NvmCtx->MacParamsDefaults.ChannelsTxPower;
+ MacCtx.NvmCtx->MacParams.ChannelsDatarate = MacCtx.NvmCtx->MacParamsDefaults.ChannelsDatarate;
+ MacCtx.NvmCtx->MacParams.Rx1DrOffset = MacCtx.NvmCtx->MacParamsDefaults.Rx1DrOffset;
+ MacCtx.NvmCtx->MacParams.Rx2Channel = MacCtx.NvmCtx->MacParamsDefaults.Rx2Channel;
+ MacCtx.NvmCtx->MacParams.RxCChannel = MacCtx.NvmCtx->MacParamsDefaults.RxCChannel;
+ MacCtx.NvmCtx->MacParams.UplinkDwellTime = MacCtx.NvmCtx->MacParamsDefaults.UplinkDwellTime;
+ MacCtx.NvmCtx->MacParams.DownlinkDwellTime = MacCtx.NvmCtx->MacParamsDefaults.DownlinkDwellTime;
+ MacCtx.NvmCtx->MacParams.MaxEirp = MacCtx.NvmCtx->MacParamsDefaults.MaxEirp;
+ MacCtx.NvmCtx->MacParams.AntennaGain = MacCtx.NvmCtx->MacParamsDefaults.AntennaGain;
+
+ MacCtx.NodeAckRequested = false;
+ MacCtx.NvmCtx->SrvAckRequested = false;
+
+ // Reset to application defaults
+ InitDefaultsParams_t params;
+ params.Type = INIT_TYPE_RESTORE_DEFAULT_CHANNELS;
+ params.NvmCtx = NULL;
+ RegionInitDefaults( MacCtx.NvmCtx->Region, ¶ms );
+
+ // Initialize channel index.
+ MacCtx.Channel = 0;
+ MacCtx.NvmCtx->LastTxChannel = MacCtx.Channel;
+
+ // Initialize Rx2 config parameters.
+ MacCtx.RxWindow2Config.Channel = MacCtx.Channel;
+ MacCtx.RxWindow2Config.Frequency = MacCtx.NvmCtx->MacParams.Rx2Channel.Frequency;
+ MacCtx.RxWindow2Config.DownlinkDwellTime = MacCtx.NvmCtx->MacParams.DownlinkDwellTime;
+ MacCtx.RxWindow2Config.RepeaterSupport = MacCtx.NvmCtx->RepeaterSupport;
+ MacCtx.RxWindow2Config.RxContinuous = false;
+ MacCtx.RxWindow2Config.RxSlot = RX_SLOT_WIN_2;
+}
+
+/*!
+ * \brief Initializes and opens the reception window
+ *
+ * \param [IN] rxTimer Window timer to be topped.
+ * \param [IN] rxConfig Window parameters to be setup
+ */
+static void RxWindowSetup( TimerEvent_t* rxTimer, RxConfigParams_t* rxConfig )
+{
+ TimerStop( rxTimer );
+
+ // Ensure the radio is Idle
+ Radio.Standby( );
+
+ if( RegionRxConfig( MacCtx.NvmCtx->Region, rxConfig, ( int8_t* )&MacCtx.McpsIndication.RxDatarate ) == true )
+ {
+ Radio.Rx( MacCtx.NvmCtx->MacParams.MaxRxWindow );
+ MacCtx.RxSlot = rxConfig->RxSlot;
+ }
+}
+
+static void OpenContinuousRxCWindow( void )
+{
+ MacCtx.RxWindowCConfig.RxSlot = RX_SLOT_WIN_CLASS_C;
+ // Setup continuous listening
+ MacCtx.RxWindowCConfig.RxContinuous = true;
+
+ // At this point the Radio should be idle.
+ // Thus, there is no need to set the radio in standby mode.
+ if( RegionRxConfig( MacCtx.NvmCtx->Region, &MacCtx.RxWindowCConfig, ( int8_t* )&MacCtx.McpsIndication.RxDatarate ) == true )
+ {
+ Radio.Rx( 0 ); // Continuous mode
+ MacCtx.RxSlot = MacCtx.RxWindowCConfig.RxSlot;
+ }
+}
+
+LoRaMacStatus_t PrepareFrame( LoRaMacHeader_t* macHdr, LoRaMacFrameCtrl_t* fCtrl, uint8_t fPort, void* fBuffer, uint16_t fBufferSize )
+{
+ MacCtx.PktBufferLen = 0;
+ MacCtx.NodeAckRequested = false;
+ uint32_t fCntUp = 0;
+ size_t macCmdsSize = 0;
+ uint8_t availableSize = 0;
+
+ if( fBuffer == NULL )
+ {
+ fBufferSize = 0;
+ }
+
+ memcpy1( MacCtx.AppData, ( uint8_t* ) fBuffer, fBufferSize );
+ MacCtx.AppDataSize = fBufferSize;
+ MacCtx.PktBuffer[0] = macHdr->Value;
+
+ switch( macHdr->Bits.MType )
+ {
+ case FRAME_TYPE_DATA_CONFIRMED_UP:
+ MacCtx.NodeAckRequested = true;
+ // Intentional fall through
+ case FRAME_TYPE_DATA_UNCONFIRMED_UP:
+ MacCtx.TxMsg.Type = LORAMAC_MSG_TYPE_DATA;
+ MacCtx.TxMsg.Message.Data.Buffer = MacCtx.PktBuffer;
+ MacCtx.TxMsg.Message.Data.BufSize = LORAMAC_PHY_MAXPAYLOAD;
+ MacCtx.TxMsg.Message.Data.MHDR.Value = macHdr->Value;
+ MacCtx.TxMsg.Message.Data.FPort = fPort;
+ MacCtx.TxMsg.Message.Data.FHDR.DevAddr = MacCtx.NvmCtx->DevAddr;
+ MacCtx.TxMsg.Message.Data.FHDR.FCtrl.Value = fCtrl->Value;
+ MacCtx.TxMsg.Message.Data.FRMPayloadSize = MacCtx.AppDataSize;
+ MacCtx.TxMsg.Message.Data.FRMPayload = MacCtx.AppData;
+
+ if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoGetFCntUp( &fCntUp ) )
+ {
+ return LORAMAC_STATUS_FCNT_HANDLER_ERROR;
+ }
+ MacCtx.TxMsg.Message.Data.FHDR.FCnt = ( uint16_t )fCntUp;
+
+ // Reset confirm parameters
+ MacCtx.McpsConfirm.NbRetries = 0;
+ MacCtx.McpsConfirm.AckReceived = false;
+ MacCtx.McpsConfirm.UpLinkCounter = fCntUp;
+
+ // Handle the MAC commands if there are any available
+ if( LoRaMacCommandsGetSizeSerializedCmds( &macCmdsSize ) != LORAMAC_COMMANDS_SUCCESS )
+ {
+ return LORAMAC_STATUS_MAC_COMMAD_ERROR;
+ }
+
+ if( macCmdsSize > 0 )
+ {
+ availableSize = GetMaxAppPayloadWithoutFOptsLength( MacCtx.NvmCtx->MacParams.ChannelsDatarate );
+
+ // There is application payload available and the MAC commands fit into FOpts field.
+ if( ( MacCtx.AppDataSize > 0 ) && ( macCmdsSize <= LORA_MAC_COMMAND_MAX_FOPTS_LENGTH ) )
+ {
+ if( LoRaMacCommandsSerializeCmds( LORA_MAC_COMMAND_MAX_FOPTS_LENGTH, &macCmdsSize, MacCtx.TxMsg.Message.Data.FHDR.FOpts ) != LORAMAC_COMMANDS_SUCCESS )
+ {
+ return LORAMAC_STATUS_MAC_COMMAD_ERROR;
+ }
+ fCtrl->Bits.FOptsLen = macCmdsSize;
+ // Update FCtrl field with new value of FOptionsLength
+ MacCtx.TxMsg.Message.Data.FHDR.FCtrl.Value = fCtrl->Value;
+ }
+ // There is application payload available but the MAC commands does NOT fit into FOpts field.
+ else if( ( MacCtx.AppDataSize > 0 ) && ( macCmdsSize > LORA_MAC_COMMAND_MAX_FOPTS_LENGTH ) )
+ {
+
+ if( LoRaMacCommandsSerializeCmds( availableSize, &macCmdsSize, MacCtx.NvmCtx->MacCommandsBuffer ) != LORAMAC_COMMANDS_SUCCESS )
+ {
+ return LORAMAC_STATUS_MAC_COMMAD_ERROR;
+ }
+ return LORAMAC_STATUS_SKIPPED_APP_DATA;
+ }
+ // No application payload available therefore add all mac commands to the FRMPayload.
+ else
+ {
+ if( LoRaMacCommandsSerializeCmds( availableSize, &macCmdsSize, MacCtx.NvmCtx->MacCommandsBuffer ) != LORAMAC_COMMANDS_SUCCESS )
+ {
+ return LORAMAC_STATUS_MAC_COMMAD_ERROR;
+ }
+ // Force FPort to be zero
+ MacCtx.TxMsg.Message.Data.FPort = 0;
+
+ MacCtx.TxMsg.Message.Data.FRMPayload = MacCtx.NvmCtx->MacCommandsBuffer;
+ MacCtx.TxMsg.Message.Data.FRMPayloadSize = macCmdsSize;
+ }
+ }
+
+ break;
+ case FRAME_TYPE_PROPRIETARY:
+ if( ( fBuffer != NULL ) && ( MacCtx.AppDataSize > 0 ) )
+ {
+ memcpy1( MacCtx.PktBuffer + LORAMAC_MHDR_FIELD_SIZE, ( uint8_t* ) fBuffer, MacCtx.AppDataSize );
+ MacCtx.PktBufferLen = LORAMAC_MHDR_FIELD_SIZE + MacCtx.AppDataSize;
+ }
+ break;
+ default:
+ return LORAMAC_STATUS_SERVICE_UNKNOWN;
+ }
+
+ return LORAMAC_STATUS_OK;
+}
+
+LoRaMacStatus_t SendFrameOnChannel( uint8_t channel )
+{
+ TxConfigParams_t txConfig;
+ int8_t txPower = 0;
+
+ txConfig.Channel = channel;
+ txConfig.Datarate = MacCtx.NvmCtx->MacParams.ChannelsDatarate;
+ txConfig.TxPower = MacCtx.NvmCtx->MacParams.ChannelsTxPower;
+ txConfig.MaxEirp = MacCtx.NvmCtx->MacParams.MaxEirp;
+ txConfig.AntennaGain = MacCtx.NvmCtx->MacParams.AntennaGain;
+ txConfig.PktLen = MacCtx.PktBufferLen;
+
+ RegionTxConfig( MacCtx.NvmCtx->Region, &txConfig, &txPower, &MacCtx.TxTimeOnAir );
+
+ MacCtx.McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
+ MacCtx.McpsConfirm.Datarate = MacCtx.NvmCtx->MacParams.ChannelsDatarate;
+ MacCtx.McpsConfirm.TxPower = txPower;
+ MacCtx.McpsConfirm.Channel = channel;
+
+ // Store the time on air
+ MacCtx.McpsConfirm.TxTimeOnAir = MacCtx.TxTimeOnAir;
+ MacCtx.MlmeConfirm.TxTimeOnAir = MacCtx.TxTimeOnAir;
+
+ if( LoRaMacClassBIsBeaconModeActive( ) == true )
+ {
+ // Currently, the Time-On-Air can only be computed when the radio is configured with
+ // the TX configuration
+ TimerTime_t collisionTime = LoRaMacClassBIsUplinkCollision( MacCtx.TxTimeOnAir );
+
+ if( collisionTime > 0 )
+ {
+ return LORAMAC_STATUS_BUSY_UPLINK_COLLISION;
+ }
+ }
+
+ if( MacCtx.NvmCtx->DeviceClass == CLASS_B )
+ {
+ // Stop slots for class b
+ LoRaMacClassBStopRxSlots( );
+ }
+
+ LoRaMacClassBHaltBeaconing( );
+
+ MacCtx.MacState |= LORAMAC_TX_RUNNING;
+ if( MacCtx.NodeAckRequested == false )
+ {
+ MacCtx.ChannelsNbTransCounter++;
+ }
+
+ // Send now
+ Radio.Send( MacCtx.PktBuffer, MacCtx.PktBufferLen );
+
+ return LORAMAC_STATUS_OK;
+}
+
+LoRaMacStatus_t SetTxContinuousWave( uint16_t timeout )
+{
+ ContinuousWaveParams_t continuousWave;
+
+ continuousWave.Channel = MacCtx.Channel;
+ continuousWave.Datarate = MacCtx.NvmCtx->MacParams.ChannelsDatarate;
+ continuousWave.TxPower = MacCtx.NvmCtx->MacParams.ChannelsTxPower;
+ continuousWave.MaxEirp = MacCtx.NvmCtx->MacParams.MaxEirp;
+ continuousWave.AntennaGain = MacCtx.NvmCtx->MacParams.AntennaGain;
+ continuousWave.Timeout = timeout;
+
+ RegionSetContinuousWave( MacCtx.NvmCtx->Region, &continuousWave );
+
+ MacCtx.MacState |= LORAMAC_TX_RUNNING;
+
+ return LORAMAC_STATUS_OK;
+}
+
+LoRaMacStatus_t SetTxContinuousWave1( uint16_t timeout, uint32_t frequency, uint8_t power )
+{
+ Radio.SetTxContinuousWave( frequency, power, timeout );
+
+ MacCtx.MacState |= LORAMAC_TX_RUNNING;
+
+ return LORAMAC_STATUS_OK;
+}
+
+LoRaMacCtxs_t* GetCtxs( void )
+{
+ Contexts.MacNvmCtx = &NvmMacCtx;
+ Contexts.MacNvmCtxSize = sizeof( NvmMacCtx );
+ Contexts.CryptoNvmCtx = LoRaMacCryptoGetNvmCtx( &Contexts.CryptoNvmCtxSize );
+ GetNvmCtxParams_t params ={ 0 };
+ Contexts.RegionNvmCtx = RegionGetNvmCtx( MacCtx.NvmCtx->Region, ¶ms );
+ Contexts.RegionNvmCtxSize = params.nvmCtxSize;
+ Contexts.SecureElementNvmCtx = SecureElementGetNvmCtx( &Contexts.SecureElementNvmCtxSize );
+ Contexts.CommandsNvmCtx = LoRaMacCommandsGetNvmCtx( &Contexts.CommandsNvmCtxSize );
+ Contexts.ClassBNvmCtx = LoRaMacClassBGetNvmCtx( &Contexts.ClassBNvmCtxSize );
+ Contexts.ConfirmQueueNvmCtx = LoRaMacConfirmQueueGetNvmCtx( &Contexts.ConfirmQueueNvmCtxSize );
+ return &Contexts;
+}
+
+LoRaMacStatus_t RestoreCtxs( LoRaMacCtxs_t* contexts )
+{
+ if( contexts == NULL )
+ {
+ return LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+ if( MacCtx.MacState != LORAMAC_STOPPED )
+ {
+ return LORAMAC_STATUS_BUSY;
+ }
+
+ if( contexts->MacNvmCtx != NULL )
+ {
+ memcpy1( ( uint8_t* ) &NvmMacCtx, ( uint8_t* ) contexts->MacNvmCtx, contexts->MacNvmCtxSize );
+ }
+
+ InitDefaultsParams_t params;
+ params.Type = INIT_TYPE_RESTORE_CTX;
+ params.NvmCtx = contexts->RegionNvmCtx;
+ RegionInitDefaults( MacCtx.NvmCtx->Region, ¶ms );
+
+ if( SecureElementRestoreNvmCtx( contexts->SecureElementNvmCtx ) != SECURE_ELEMENT_SUCCESS )
+ {
+ return LORAMAC_STATUS_CRYPTO_ERROR;
+ }
+
+ if( LoRaMacCryptoRestoreNvmCtx( contexts->CryptoNvmCtx ) != LORAMAC_CRYPTO_SUCCESS )
+ {
+ return LORAMAC_STATUS_CRYPTO_ERROR;
+ }
+
+ if( LoRaMacCommandsRestoreNvmCtx( contexts->CommandsNvmCtx ) != LORAMAC_COMMANDS_SUCCESS )
+ {
+ return LORAMAC_STATUS_MAC_COMMAD_ERROR;
+ }
+
+ if( LoRaMacClassBRestoreNvmCtx( contexts->ClassBNvmCtx ) != true )
+ {
+ return LORAMAC_STATUS_CLASS_B_ERROR;
+ }
+
+ if( LoRaMacConfirmQueueRestoreNvmCtx( contexts->ConfirmQueueNvmCtx ) != true )
+ {
+ return LORAMAC_STATUS_CONFIRM_QUEUE_ERROR;
+ }
+
+ return LORAMAC_STATUS_OK;
+}
+
+LoRaMacStatus_t DetermineFrameType( LoRaMacMessageData_t* macMsg, FType_t* fType )
+{
+ if( ( macMsg == NULL ) || ( fType == NULL ) )
+ {
+ return LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+
+ /* The LoRaWAN specification allows several possible configurations how data up/down frames are built up.
+ * In sake of clearness the following naming is applied. Please keep in mind that this is
+ * implementation specific since there is no definition in the LoRaWAN specification included.
+ *
+ * X -> Field is available
+ * - -> Field is not available
+ *
+ * +-------+ +----------+------+-------+--------------+
+ * | FType | | FOptsLen | Fopt | FPort | FRMPayload |
+ * +-------+ +----------+------+-------+--------------+
+ * | A | | > 0 | X | > 0 | X |
+ * +-------+ +----------+------+-------+--------------+
+ * | B | | >= 0 | X/- | - | - |
+ * +-------+ +----------+------+-------+--------------+
+ * | C | | = 0 | - | = 0 | MAC commands |
+ * +-------+ +----------+------+-------+--------------+
+ * | D | | = 0 | - | > 0 | X |
+ * +-------+ +----------+------+-------+--------------+
+ */
+
+ if( ( macMsg->FHDR.FCtrl.Bits.FOptsLen > 0 ) && ( macMsg->FPort > 0 ) )
+ {
+ *fType = FRAME_TYPE_A;
+ }
+ else if( macMsg->FRMPayloadSize == 0 )
+ {
+ *fType = FRAME_TYPE_B;
+ }
+ else if( ( macMsg->FHDR.FCtrl.Bits.FOptsLen == 0 ) && ( macMsg->FPort == 0 ) )
+ {
+ *fType = FRAME_TYPE_C;
+ }
+ else if( ( macMsg->FHDR.FCtrl.Bits.FOptsLen == 0 ) && ( macMsg->FPort > 0 ) )
+ {
+ *fType = FRAME_TYPE_D;
+ }
+ else
+ {
+ // Should never happen.
+ return LORAMAC_STATUS_ERROR;
+ }
+
+ return LORAMAC_STATUS_OK;
+}
+
+static bool CheckRetransUnconfirmedUplink( void )
+{
+ // Unconfirmed uplink, when all retransmissions are done.
+ if( MacCtx.ChannelsNbTransCounter >=
+ MacCtx.NvmCtx->MacParams.ChannelsNbTrans )
+ {
+ return true;
+ }
+ else if( MacCtx.MacFlags.Bits.McpsInd == 1 )
+ {
+ // For Class A stop in each case
+ if( MacCtx.NvmCtx->DeviceClass == CLASS_A )
+ {
+ return true;
+ }
+ else
+ {// For Class B & C stop only if the frame was received in RX1 window
+ if( MacCtx.McpsIndication.RxSlot == RX_SLOT_WIN_1 )
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+static bool CheckRetransConfirmedUplink( void )
+{
+ // Confirmed uplink, when all retransmissions ( tries to get a ack ) are done.
+ if( MacCtx.AckTimeoutRetriesCounter >=
+ MacCtx.AckTimeoutRetries )
+ {
+ return true;
+ }
+ else if( MacCtx.MacFlags.Bits.McpsInd == 1 )
+ {
+ if( MacCtx.McpsConfirm.AckReceived == true )
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool StopRetransmission( void )
+{
+ if( ( MacCtx.MacFlags.Bits.McpsInd == 0 ) ||
+ ( ( MacCtx.McpsIndication.RxSlot != RX_SLOT_WIN_1 ) &&
+ ( MacCtx.McpsIndication.RxSlot != RX_SLOT_WIN_2 ) ) )
+ { // Maximum repetitions without downlink. Increase ADR Ack counter.
+ // Only process the case when the MAC did not receive a downlink.
+ if( MacCtx.NvmCtx->AdrCtrlOn == true )
+ {
+ MacCtx.NvmCtx->AdrAckCounter++;
+ }
+ }
+
+ MacCtx.ChannelsNbTransCounter = 0;
+ MacCtx.NodeAckRequested = false;
+ MacCtx.AckTimeoutRetry = false;
+ MacCtx.MacState &= ~LORAMAC_TX_RUNNING;
+
+ return true;
+}
+
+static void AckTimeoutRetriesProcess( void )
+{
+ if( MacCtx.AckTimeoutRetriesCounter < MacCtx.AckTimeoutRetries )
+ {
+ MacCtx.AckTimeoutRetriesCounter++;
+ if( ( MacCtx.AckTimeoutRetriesCounter % 2 ) == 1 )
+ {
+ GetPhyParams_t getPhy;
+ PhyParam_t phyParam;
+
+ getPhy.Attribute = PHY_NEXT_LOWER_TX_DR;
+ getPhy.UplinkDwellTime = MacCtx.NvmCtx->MacParams.UplinkDwellTime;
+ getPhy.Datarate = MacCtx.NvmCtx->MacParams.ChannelsDatarate;
+ phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
+
+ MacCtx.NvmCtx->MacParams.ChannelsDatarate = phyParam.Value;
+ #if (LORAWAN_DEFAULT_ACTIVE_REGION == LORAMAC_REGION_CN470ALI)
+ if(adr_on && MacCtx.NvmCtx->MacParams.ChannelsDatarate <2)
+ {
+ MacCtx.NvmCtx->MacParams.ChannelsDatarate =2;
+ }
+ #endif
+ }
+ }
+}
+
+static void AckTimeoutRetriesFinalize( void )
+{
+ if( MacCtx.McpsConfirm.AckReceived == false )
+ {
+ InitDefaultsParams_t params;
+ params.Type = INIT_TYPE_RESTORE_DEFAULT_CHANNELS;
+ params.NvmCtx = Contexts.RegionNvmCtx;
+ RegionInitDefaults( MacCtx.NvmCtx->Region, ¶ms );
+
+ MacCtx.NodeAckRequested = false;
+ MacCtx.McpsConfirm.AckReceived = false;
+ }
+ MacCtx.McpsConfirm.NbRetries = MacCtx.AckTimeoutRetriesCounter;
+}
+
+static void CallNvmCtxCallback( LoRaMacNvmCtxModule_t module )
+{
+ if( ( MacCtx.MacCallbacks != NULL ) && ( MacCtx.MacCallbacks->NvmContextChange != NULL ) )
+ {
+ MacCtx.MacCallbacks->NvmContextChange( module );
+ }
+}
+
+static void EventMacNvmCtxChanged( void )
+{
+ CallNvmCtxCallback( LORAMAC_NVMCTXMODULE_MAC );
+}
+
+static void EventRegionNvmCtxChanged( void )
+{
+ CallNvmCtxCallback( LORAMAC_NVMCTXMODULE_REGION );
+}
+
+static void EventCryptoNvmCtxChanged( void )
+{
+ CallNvmCtxCallback( LORAMAC_NVMCTXMODULE_CRYPTO );
+}
+
+static void EventSecureElementNvmCtxChanged( void )
+{
+ CallNvmCtxCallback( LORAMAC_NVMCTXMODULE_SECURE_ELEMENT );
+}
+
+static void EventCommandsNvmCtxChanged( void )
+{
+ CallNvmCtxCallback( LORAMAC_NVMCTXMODULE_COMMANDS );
+}
+
+static void EventClassBNvmCtxChanged( void )
+{
+ CallNvmCtxCallback( LORAMAC_NVMCTXMODULE_CLASS_B );
+}
+
+static void EventConfirmQueueNvmCtxChanged( void )
+{
+ CallNvmCtxCallback( LORAMAC_NVMCTXMODULE_CONFIRM_QUEUE );
+}
+
+static uint8_t IsRequestPending( void )
+{
+ if( ( MacCtx.MacFlags.Bits.MlmeReq == 1 ) ||
+ ( MacCtx.MacFlags.Bits.McpsReq == 1 ) )
+ {
+ return 1;
+ }
+ return 0;
+}
+
+
+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 ) ||
+ ( callbacks == NULL ) )
+ {
+ return LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+
+ if( ( primitives->MacMcpsConfirm == NULL ) ||
+ ( primitives->MacMcpsIndication == 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;
+ }
+
+ // Confirm queue reset
+ LoRaMacConfirmQueueInit( primitives, EventConfirmQueueNvmCtxChanged );
+
+ // Initialize the module context with zeros
+ memset1( ( uint8_t* ) &NvmMacCtx, 0x00, sizeof( LoRaMacNvmCtx_t ) );
+ memset1( ( uint8_t* ) &MacCtx, 0x00, sizeof( LoRaMacCtx_t ) );
+ MacCtx.NvmCtx = &NvmMacCtx;
+
+ // Set non zero variables to its default value
+ MacCtx.AckTimeoutRetriesCounter = 1;
+ MacCtx.AckTimeoutRetries = 1;
+ MacCtx.NvmCtx->Region = region;
+ MacCtx.NvmCtx->DeviceClass = CLASS_A;
+ MacCtx.NvmCtx->RepeaterSupport = false;
+
+ Version_t lrWanVersion;
+ lrWanVersion.Fields.Major = 1;
+ lrWanVersion.Fields.Minor = 0;
+ lrWanVersion.Fields.Revision = 3;
+ lrWanVersion.Fields.Rfu = 0;
+ MacCtx.NvmCtx->Version = lrWanVersion;
+
+ // Reset to defaults
+ getPhy.Attribute = PHY_DUTY_CYCLE;
+ phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
+ MacCtx.NvmCtx->DutyCycleOn = ( bool ) phyParam.Value;
+
+ getPhy.Attribute = PHY_DEF_TX_POWER;
+ phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
+ MacCtx.NvmCtx->MacParamsDefaults.ChannelsTxPower = phyParam.Value;
+
+ getPhy.Attribute = PHY_DEF_TX_DR;
+ phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
+ MacCtx.NvmCtx->MacParamsDefaults.ChannelsDatarate = phyParam.Value;
+
+ getPhy.Attribute = PHY_MAX_RX_WINDOW;
+ phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
+ MacCtx.NvmCtx->MacParamsDefaults.MaxRxWindow = phyParam.Value;
+
+ getPhy.Attribute = PHY_RECEIVE_DELAY1;
+ phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
+ MacCtx.NvmCtx->MacParamsDefaults.ReceiveDelay1 = phyParam.Value;
+
+ getPhy.Attribute = PHY_RECEIVE_DELAY2;
+ phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
+ MacCtx.NvmCtx->MacParamsDefaults.ReceiveDelay2 = phyParam.Value;
+
+ getPhy.Attribute = PHY_JOIN_ACCEPT_DELAY1;
+ phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
+ MacCtx.NvmCtx->MacParamsDefaults.JoinAcceptDelay1 = phyParam.Value;
+
+ getPhy.Attribute = PHY_JOIN_ACCEPT_DELAY2;
+ phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
+ MacCtx.NvmCtx->MacParamsDefaults.JoinAcceptDelay2 = phyParam.Value;
+
+ getPhy.Attribute = PHY_DEF_DR1_OFFSET;
+ phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
+ MacCtx.NvmCtx->MacParamsDefaults.Rx1DrOffset = phyParam.Value;
+
+ getPhy.Attribute = PHY_DEF_RX2_FREQUENCY;
+ phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
+ MacCtx.NvmCtx->MacParamsDefaults.Rx2Channel.Frequency = phyParam.Value;
+ MacCtx.NvmCtx->MacParamsDefaults.RxCChannel.Frequency = phyParam.Value;
+
+ getPhy.Attribute = PHY_DEF_RX2_DR;
+ phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
+ MacCtx.NvmCtx->MacParamsDefaults.Rx2Channel.Datarate = phyParam.Value;
+ MacCtx.NvmCtx->MacParamsDefaults.RxCChannel.Datarate = phyParam.Value;
+
+ getPhy.Attribute = PHY_DEF_UPLINK_DWELL_TIME;
+ phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
+ MacCtx.NvmCtx->MacParamsDefaults.UplinkDwellTime = phyParam.Value;
+
+ getPhy.Attribute = PHY_DEF_DOWNLINK_DWELL_TIME;
+ phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
+ MacCtx.NvmCtx->MacParamsDefaults.DownlinkDwellTime = phyParam.Value;
+
+ getPhy.Attribute = PHY_DEF_MAX_EIRP;
+ phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
+ MacCtx.NvmCtx->MacParamsDefaults.MaxEirp = phyParam.fValue;
+
+ getPhy.Attribute = PHY_DEF_ANTENNA_GAIN;
+ phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
+ MacCtx.NvmCtx->MacParamsDefaults.AntennaGain = phyParam.fValue;
+
+ getPhy.Attribute = PHY_DEF_ADR_ACK_LIMIT;
+ phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
+ MacCtx.AdrAckLimit = phyParam.Value;
+
+ getPhy.Attribute = PHY_DEF_ADR_ACK_DELAY;
+ phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
+ MacCtx.AdrAckDelay = phyParam.Value;
+
+ // Init parameters which are not set in function ResetMacParameters
+ MacCtx.NvmCtx->MacParamsDefaults.ChannelsNbTrans = 1;
+ MacCtx.NvmCtx->MacParamsDefaults.SystemMaxRxError = 20;
+ MacCtx.NvmCtx->MacParamsDefaults.MinRxSymbols = 6;
+
+ MacCtx.NvmCtx->MacParams.SystemMaxRxError = MacCtx.NvmCtx->MacParamsDefaults.SystemMaxRxError;
+ MacCtx.NvmCtx->MacParams.MinRxSymbols = MacCtx.NvmCtx->MacParamsDefaults.MinRxSymbols;
+ MacCtx.NvmCtx->MacParams.MaxRxWindow = MacCtx.NvmCtx->MacParamsDefaults.MaxRxWindow;
+ MacCtx.NvmCtx->MacParams.ReceiveDelay1 = MacCtx.NvmCtx->MacParamsDefaults.ReceiveDelay1;
+ MacCtx.NvmCtx->MacParams.ReceiveDelay2 = MacCtx.NvmCtx->MacParamsDefaults.ReceiveDelay2;
+ MacCtx.NvmCtx->MacParams.JoinAcceptDelay1 = MacCtx.NvmCtx->MacParamsDefaults.JoinAcceptDelay1;
+ MacCtx.NvmCtx->MacParams.JoinAcceptDelay2 = MacCtx.NvmCtx->MacParamsDefaults.JoinAcceptDelay2;
+ MacCtx.NvmCtx->MacParams.ChannelsNbTrans = MacCtx.NvmCtx->MacParamsDefaults.ChannelsNbTrans;
+
+ ResetMacParameters( );
+
+ MacCtx.NvmCtx->PublicNetwork = true;
+
+ MacCtx.MacPrimitives = primitives;
+ MacCtx.MacCallbacks = callbacks;
+ MacCtx.MacFlags.Value = 0;
+ MacCtx.MacState = LORAMAC_STOPPED;
+
+ // Reset duty cycle times
+ MacCtx.NvmCtx->LastTxDoneTime = 0;
+ MacCtx.NvmCtx->AggregatedTimeOff = 0;
+
+ // Initialize timers
+ TimerInit( &MacCtx.TxDelayedTimer, OnTxDelayedTimerEvent );
+ TimerInit( &MacCtx.RxWindowTimer1, OnRxWindow1TimerEvent );
+ TimerInit( &MacCtx.RxWindowTimer2, OnRxWindow2TimerEvent );
+ TimerInit( &MacCtx.AckTimeoutTimer, OnAckTimeoutTimerEvent );
+
+ // Store the current initialization time
+ MacCtx.NvmCtx->InitializationTime = TimerGetCurrentTime( );
+
+ // Initialize Radio driver
+ MacCtx.RadioEvents.TxDone = OnRadioTxDone;
+ MacCtx.RadioEvents.RxDone = OnRadioRxDone;
+ MacCtx.RadioEvents.RxError = OnRadioRxError;
+ MacCtx.RadioEvents.TxTimeout = OnRadioTxTimeout;
+ MacCtx.RadioEvents.RxTimeout = OnRadioRxTimeout;
+ Radio.Init( &MacCtx.RadioEvents );
+
+ InitDefaultsParams_t params;
+ params.Type = INIT_TYPE_INIT;
+ params.NvmCtx = NULL;
+ RegionInitDefaults( MacCtx.NvmCtx->Region, ¶ms );
+
+ // Initialize the Secure Element driver
+ if( SecureElementInit( EventSecureElementNvmCtxChanged ) != SECURE_ELEMENT_SUCCESS )
+ {
+ return LORAMAC_STATUS_CRYPTO_ERROR;
+ }
+
+ // Initialize Crypto module
+ if( LoRaMacCryptoInit( EventCryptoNvmCtxChanged ) != LORAMAC_CRYPTO_SUCCESS )
+ {
+ return LORAMAC_STATUS_CRYPTO_ERROR;
+ }
+
+ // Initialize MAC commands module
+ if( LoRaMacCommandsInit( EventCommandsNvmCtxChanged ) != LORAMAC_COMMANDS_SUCCESS )
+ {
+ return LORAMAC_STATUS_MAC_COMMAD_ERROR;
+ }
+
+ // Set multicast downlink counter reference
+ if( LoRaMacCryptoSetMulticastReference( MacCtx.NvmCtx->MulticastChannelList ) != LORAMAC_CRYPTO_SUCCESS )
+ {
+ return LORAMAC_STATUS_CRYPTO_ERROR;
+ }
+
+ // Random seed initialization
+ srand1( Radio.Random( ) );
+
+ Radio.SetPublicNetwork( MacCtx.NvmCtx->PublicNetwork );
+ Radio.Sleep( );
+
+ // Initialize class b
+ // Apply callback
+ classBCallbacks.GetTemperatureLevel = NULL;
+ classBCallbacks.MacProcessNotify = NULL;
+ if( callbacks != NULL )
+ {
+ classBCallbacks.GetTemperatureLevel = callbacks->GetTemperatureLevel;
+ classBCallbacks.MacProcessNotify = callbacks->MacProcessNotify;
+ }
+
+ // Must all be static. Don't use local references.
+ classBParams.MlmeIndication = &MacCtx.MlmeIndication;
+ classBParams.McpsIndication = &MacCtx.McpsIndication;
+ classBParams.MlmeConfirm = &MacCtx.MlmeConfirm;
+ classBParams.LoRaMacFlags = &MacCtx.MacFlags;
+ classBParams.LoRaMacDevAddr = &MacCtx.NvmCtx->DevAddr;
+ classBParams.LoRaMacRegion = &MacCtx.NvmCtx->Region;
+ classBParams.LoRaMacParams = &MacCtx.NvmCtx->MacParams;
+ classBParams.MulticastChannels = &MacCtx.NvmCtx->MulticastChannelList[0];
+
+ LoRaMacClassBInit( &classBParams, &classBCallbacks, &EventClassBNvmCtxChanged );
+
+ LoRaMacEnableRequests( LORAMAC_REQUEST_HANDLING_ON );
+
+ return LORAMAC_STATUS_OK;
+}
+
+LoRaMacStatus_t LoRaMacStart( void )
+{
+ MacCtx.MacState = LORAMAC_IDLE;
+ return LORAMAC_STATUS_OK;
+}
+
+LoRaMacStatus_t LoRaMacStop( void )
+{
+ if( LoRaMacIsBusy( ) == false )
+ {
+ MacCtx.MacState = LORAMAC_STOPPED;
+ return LORAMAC_STATUS_OK;
+ }
+ else if( MacCtx.MacState == LORAMAC_STOPPED )
+ {
+ return LORAMAC_STATUS_OK;
+ }
+ return LORAMAC_STATUS_BUSY;
+}
+
+LoRaMacStatus_t LoRaMacQueryTxPossible( uint8_t size, LoRaMacTxInfo_t* txInfo )
+{
+ CalcNextAdrParams_t adrNext;
+ uint32_t adrAckCounter = MacCtx.NvmCtx->AdrAckCounter;
+ int8_t datarate = MacCtx.NvmCtx->MacParamsDefaults.ChannelsDatarate;
+ int8_t txPower = MacCtx.NvmCtx->MacParamsDefaults.ChannelsTxPower;
+ size_t macCmdsSize = 0;
+
+ if( txInfo == NULL )
+ {
+ return LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+
+ // Setup ADR request
+ adrNext.Version = MacCtx.NvmCtx->Version;
+ adrNext.UpdateChanMask = false;
+ adrNext.AdrEnabled = MacCtx.NvmCtx->AdrCtrlOn;
+ adrNext.AdrAckCounter = MacCtx.NvmCtx->AdrAckCounter;
+ adrNext.AdrAckLimit = MacCtx.AdrAckLimit;
+ adrNext.AdrAckDelay = MacCtx.AdrAckDelay;
+ adrNext.Datarate = MacCtx.NvmCtx->MacParams.ChannelsDatarate;
+ adrNext.TxPower = MacCtx.NvmCtx->MacParams.ChannelsTxPower;
+ adrNext.UplinkDwellTime = MacCtx.NvmCtx->MacParams.UplinkDwellTime;
+ adrNext.Region = MacCtx.NvmCtx->Region;
+
+ // We call the function for information purposes only. We don't want to
+ // apply the datarate, the tx power and the ADR ack counter.
+ LoRaMacAdrCalcNext( &adrNext, &datarate, &txPower, &adrAckCounter );
+
+ txInfo->CurrentPossiblePayloadSize = GetMaxAppPayloadWithoutFOptsLength( datarate );
+
+ if( LoRaMacCommandsGetSizeSerializedCmds( &macCmdsSize ) != LORAMAC_COMMANDS_SUCCESS )
+ {
+ return LORAMAC_STATUS_MAC_COMMAD_ERROR;
+ }
+
+ // Verify if the MAC commands fit into the FOpts and into the maximum payload.
+ if( ( LORA_MAC_COMMAND_MAX_FOPTS_LENGTH >= macCmdsSize ) && ( txInfo->CurrentPossiblePayloadSize >= macCmdsSize ) )
+ {
+ txInfo->MaxPossibleApplicationDataSize = txInfo->CurrentPossiblePayloadSize - macCmdsSize;
+
+ // Verify if the application data together with MAC command fit into the maximum payload.
+ if( txInfo->CurrentPossiblePayloadSize >= ( macCmdsSize + size ) )
+ {
+ return LORAMAC_STATUS_OK;
+ }
+ else
+ {
+ return LORAMAC_STATUS_LENGTH_ERROR;
+ }
+ }
+ else
+ {
+ txInfo->MaxPossibleApplicationDataSize = 0;
+ return LORAMAC_STATUS_LENGTH_ERROR;
+ }
+}
+
+LoRaMacStatus_t LoRaMacMibGetRequestConfirm( MibRequestConfirm_t* mibGet )
+{
+ LoRaMacStatus_t status = LORAMAC_STATUS_OK;
+ GetPhyParams_t getPhy;
+ PhyParam_t phyParam;
+
+ if( mibGet == NULL )
+ {
+ return LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+
+ switch( mibGet->Type )
+ {
+ case MIB_DEVICE_CLASS:
+ {
+ mibGet->Param.Class = MacCtx.NvmCtx->DeviceClass;
+ break;
+ }
+ case MIB_NETWORK_ACTIVATION:
+ {
+ mibGet->Param.NetworkActivation = MacCtx.NvmCtx->NetworkActivation;
+ break;
+ }
+ case MIB_DEV_EUI:
+ {
+ mibGet->Param.DevEui = SecureElementGetDevEui( );
+ break;
+ }
+ case MIB_JOIN_EUI:
+ {
+ mibGet->Param.JoinEui = SecureElementGetJoinEui( );
+ break;
+ }
+ case MIB_ADR:
+ {
+ mibGet->Param.AdrEnable = MacCtx.NvmCtx->AdrCtrlOn;
+ break;
+ }
+ case MIB_NET_ID:
+ {
+ mibGet->Param.NetID = MacCtx.NvmCtx->NetID;
+ break;
+ }
+ case MIB_DEV_ADDR:
+ {
+ mibGet->Param.DevAddr = MacCtx.NvmCtx->DevAddr;
+ break;
+ }
+ case MIB_PUBLIC_NETWORK:
+ {
+ mibGet->Param.EnablePublicNetwork = MacCtx.NvmCtx->PublicNetwork;
+ break;
+ }
+ case MIB_REPEATER_SUPPORT:
+ {
+ mibGet->Param.EnableRepeaterSupport = MacCtx.NvmCtx->RepeaterSupport;
+ break;
+ }
+ case MIB_CHANNELS:
+ {
+ getPhy.Attribute = PHY_CHANNELS;
+ phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
+
+ mibGet->Param.ChannelList = phyParam.Channels;
+ break;
+ }
+ case MIB_RX2_CHANNEL:
+ {
+ mibGet->Param.Rx2Channel = MacCtx.NvmCtx->MacParams.Rx2Channel;
+ break;
+ }
+ case MIB_RX2_DEFAULT_CHANNEL:
+ {
+ mibGet->Param.Rx2Channel = MacCtx.NvmCtx->MacParamsDefaults.Rx2Channel;
+ break;
+ }
+ case MIB_RXC_CHANNEL:
+ {
+ mibGet->Param.RxCChannel = MacCtx.NvmCtx->MacParams.RxCChannel;
+ break;
+ }
+ case MIB_RXC_DEFAULT_CHANNEL:
+ {
+ mibGet->Param.RxCChannel = MacCtx.NvmCtx->MacParamsDefaults.RxCChannel;
+ break;
+ }
+ case MIB_CHANNELS_DEFAULT_MASK:
+ {
+ getPhy.Attribute = PHY_CHANNELS_DEFAULT_MASK;
+ phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
+
+ mibGet->Param.ChannelsDefaultMask = phyParam.ChannelsMask;
+ break;
+ }
+ case MIB_CHANNELS_MASK:
+ {
+ getPhy.Attribute = PHY_CHANNELS_MASK;
+ phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
+
+ mibGet->Param.ChannelsMask = phyParam.ChannelsMask;
+ break;
+ }
+ case MIB_CHANNELS_NB_TRANS:
+ {
+ mibGet->Param.ChannelsNbTrans = MacCtx.NvmCtx->MacParams.ChannelsNbTrans;
+ break;
+ }
+ case MIB_MAX_RX_WINDOW_DURATION:
+ {
+ mibGet->Param.MaxRxWindow = MacCtx.NvmCtx->MacParams.MaxRxWindow;
+ break;
+ }
+ case MIB_RECEIVE_DELAY_1:
+ {
+ mibGet->Param.ReceiveDelay1 = MacCtx.NvmCtx->MacParams.ReceiveDelay1;
+ break;
+ }
+ case MIB_RECEIVE_DELAY_2:
+ {
+ mibGet->Param.ReceiveDelay2 = MacCtx.NvmCtx->MacParams.ReceiveDelay2;
+ break;
+ }
+ case MIB_JOIN_ACCEPT_DELAY_1:
+ {
+ mibGet->Param.JoinAcceptDelay1 = MacCtx.NvmCtx->MacParams.JoinAcceptDelay1;
+ break;
+ }
+ case MIB_JOIN_ACCEPT_DELAY_2:
+ {
+ mibGet->Param.JoinAcceptDelay2 = MacCtx.NvmCtx->MacParams.JoinAcceptDelay2;
+ break;
+ }
+ case MIB_CHANNELS_DEFAULT_DATARATE:
+ {
+ mibGet->Param.ChannelsDefaultDatarate = MacCtx.NvmCtx->MacParamsDefaults.ChannelsDatarate;
+ break;
+ }
+ case MIB_CHANNELS_DATARATE:
+ {
+ mibGet->Param.ChannelsDatarate = MacCtx.NvmCtx->MacParams.ChannelsDatarate;
+ break;
+ }
+ case MIB_CHANNELS_DEFAULT_TX_POWER:
+ {
+ mibGet->Param.ChannelsDefaultTxPower = MacCtx.NvmCtx->MacParamsDefaults.ChannelsTxPower;
+ break;
+ }
+ case MIB_CHANNELS_TX_POWER:
+ {
+ mibGet->Param.ChannelsTxPower = MacCtx.NvmCtx->MacParams.ChannelsTxPower;
+ break;
+ }
+ case MIB_SYSTEM_MAX_RX_ERROR:
+ {
+ mibGet->Param.SystemMaxRxError = MacCtx.NvmCtx->MacParams.SystemMaxRxError;
+ break;
+ }
+ case MIB_MIN_RX_SYMBOLS:
+ {
+ mibGet->Param.MinRxSymbols = MacCtx.NvmCtx->MacParams.MinRxSymbols;
+ break;
+ }
+ case MIB_ANTENNA_GAIN:
+ {
+ mibGet->Param.AntennaGain = MacCtx.NvmCtx->MacParams.AntennaGain;
+ break;
+ }
+ case MIB_NVM_CTXS:
+ {
+ mibGet->Param.Contexts = GetCtxs( );
+ break;
+ }
+ case MIB_DEFAULT_ANTENNA_GAIN:
+ {
+ mibGet->Param.DefaultAntennaGain = MacCtx.NvmCtx->MacParamsDefaults.AntennaGain;
+ break;
+ }
+ default:
+ {
+ status = LoRaMacClassBMibGetRequestConfirm( mibGet );
+ break;
+ }
+ }
+ return status;
+}
+
+LoRaMacStatus_t LoRaMacMibSetRequestConfirm( MibRequestConfirm_t* mibSet )
+{
+ LoRaMacStatus_t status = LORAMAC_STATUS_OK;
+ ChanMaskSetParams_t chanMaskSet;
+ VerifyParams_t verify;
+
+ if( mibSet == NULL )
+ {
+ return LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+ if( ( MacCtx.MacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING )
+ {
+ return LORAMAC_STATUS_BUSY;
+ }
+
+ switch( mibSet->Type )
+ {
+ case MIB_DEVICE_CLASS:
+ {
+ status = SwitchClass( mibSet->Param.Class );
+ break;
+ }
+ case MIB_NETWORK_ACTIVATION:
+ {
+ if( mibSet->Param.NetworkActivation != ACTIVATION_TYPE_OTAA )
+ {
+ MacCtx.NvmCtx->NetworkActivation = mibSet->Param.NetworkActivation;
+ }
+ else
+ { // Do not allow to set ACTIVATION_TYPE_OTAA since the MAC will set it automatically after a successful join process.
+ status = LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+ break;
+ }
+ case MIB_DEV_EUI:
+ {
+ if( SecureElementSetDevEui( mibSet->Param.DevEui ) != SECURE_ELEMENT_SUCCESS )
+ {
+ status = LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+ break;
+ }
+ case MIB_JOIN_EUI:
+ {
+ if( SecureElementSetJoinEui( mibSet->Param.JoinEui ) != SECURE_ELEMENT_SUCCESS )
+ {
+ status = LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+ break;
+ }
+ case MIB_ADR:
+ {
+ MacCtx.NvmCtx->AdrCtrlOn = mibSet->Param.AdrEnable;
+ break;
+ }
+ case MIB_NET_ID:
+ {
+ MacCtx.NvmCtx->NetID = mibSet->Param.NetID;
+ break;
+ }
+ case MIB_DEV_ADDR:
+ {
+ MacCtx.NvmCtx->DevAddr = mibSet->Param.DevAddr;
+ break;
+ }
+ case MIB_GEN_APP_KEY:
+ {
+ if( mibSet->Param.GenAppKey != NULL )
+ {
+ if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( GEN_APP_KEY, mibSet->Param.GenAppKey ) )
+ {
+ return LORAMAC_STATUS_CRYPTO_ERROR;
+ }
+ }
+ else
+ {
+ status = LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+ break;
+ }
+ case MIB_APP_KEY:
+ {
+ if( mibSet->Param.AppKey != NULL )
+ {
+ if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( APP_KEY, mibSet->Param.AppKey ) )
+ {
+ return LORAMAC_STATUS_CRYPTO_ERROR;
+ }
+ }
+ else
+ {
+ status = LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+ break;
+ }
+ case MIB_NWK_KEY:
+ {
+ if( mibSet->Param.NwkKey != NULL )
+ {
+ if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( NWK_KEY, mibSet->Param.NwkKey ) )
+ {
+ return LORAMAC_STATUS_CRYPTO_ERROR;
+ }
+ }
+ else
+ {
+ status = LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+ break;
+ }
+ case MIB_J_S_INT_KEY:
+ {
+ if( mibSet->Param.JSIntKey != NULL )
+ {
+ if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( J_S_INT_KEY, mibSet->Param.JSIntKey ) )
+ {
+ return LORAMAC_STATUS_CRYPTO_ERROR;
+ }
+ }
+ else
+ {
+ status = LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+ break;
+ }
+ case MIB_J_S_ENC_KEY:
+ {
+ if( mibSet->Param.JSEncKey != NULL )
+ {
+ if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( J_S_ENC_KEY, mibSet->Param.JSEncKey ) )
+ {
+ return LORAMAC_STATUS_CRYPTO_ERROR;
+ }
+ }
+ else
+ {
+ status = LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+ break;
+ }
+ case MIB_F_NWK_S_INT_KEY:
+ {
+ if( mibSet->Param.FNwkSIntKey != NULL )
+ {
+ if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( F_NWK_S_INT_KEY, mibSet->Param.FNwkSIntKey ) )
+ {
+ return LORAMAC_STATUS_CRYPTO_ERROR;
+ }
+ }
+ else
+ {
+ status = LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+ break;
+ }
+ case MIB_S_NWK_S_INT_KEY:
+ {
+ if( mibSet->Param.SNwkSIntKey != NULL )
+ {
+ if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( S_NWK_S_INT_KEY, mibSet->Param.SNwkSIntKey ) )
+ {
+ return LORAMAC_STATUS_CRYPTO_ERROR;
+ }
+ }
+ else
+ {
+ status = LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+ break;
+ }
+ case MIB_NWK_S_ENC_KEY:
+ {
+ if( mibSet->Param.NwkSEncKey != NULL )
+ {
+ if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( NWK_S_ENC_KEY, mibSet->Param.NwkSEncKey ) )
+ {
+ return LORAMAC_STATUS_CRYPTO_ERROR;
+ }
+ }
+ else
+ {
+ status = LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+ break;
+ }
+ case MIB_APP_S_KEY:
+ {
+ if( mibSet->Param.AppSKey != NULL )
+ {
+ if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( APP_S_KEY, mibSet->Param.AppSKey ) )
+ {
+ return LORAMAC_STATUS_CRYPTO_ERROR;
+ }
+ }
+ else
+ {
+ status = LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+ break;
+ }
+ case MIB_MC_KE_KEY:
+ {
+ if( mibSet->Param.McKEKey != NULL )
+ {
+ if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_KE_KEY, mibSet->Param.McKEKey ) )
+ {
+ return LORAMAC_STATUS_CRYPTO_ERROR;
+ }
+ }
+ else
+ {
+ status = LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+ break;
+ }
+ case MIB_MC_KEY_0:
+ {
+ if( mibSet->Param.McKey0 != NULL )
+ {
+ if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_KEY_0, mibSet->Param.McKey0 ) )
+ {
+ return LORAMAC_STATUS_CRYPTO_ERROR;
+ }
+ }
+ else
+ {
+ status = LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+ break;
+ }
+ case MIB_MC_APP_S_KEY_0:
+ {
+ if( mibSet->Param.McAppSKey0 != NULL )
+ {
+ if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_APP_S_KEY_0, mibSet->Param.McAppSKey0 ) )
+ {
+ return LORAMAC_STATUS_CRYPTO_ERROR;
+ }
+ }
+ else
+ {
+ status = LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+ break;
+ }
+ case MIB_MC_NWK_S_KEY_0:
+ {
+ if( mibSet->Param.McNwkSKey0 != NULL )
+ {
+ if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_NWK_S_KEY_0, mibSet->Param.McNwkSKey0 ) )
+ {
+ return LORAMAC_STATUS_CRYPTO_ERROR;
+ }
+ }
+ else
+ {
+ status = LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+ break;
+ }
+ case MIB_MC_KEY_1:
+ {
+ if( mibSet->Param.McKey1 != NULL )
+ {
+ if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_KEY_1, mibSet->Param.McKey1 ) )
+ {
+ return LORAMAC_STATUS_CRYPTO_ERROR;
+ }
+ }
+ else
+ {
+ status = LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+ break;
+ }
+ case MIB_MC_APP_S_KEY_1:
+ {
+ if( mibSet->Param.McAppSKey1 != NULL )
+ {
+ if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_APP_S_KEY_1, mibSet->Param.McAppSKey1 ) )
+ {
+ return LORAMAC_STATUS_CRYPTO_ERROR;
+ }
+ }
+ else
+ {
+ status = LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+ break;
+ }
+ case MIB_MC_NWK_S_KEY_1:
+ {
+ if( mibSet->Param.McNwkSKey1 != NULL )
+ {
+ if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_NWK_S_KEY_1, mibSet->Param.McNwkSKey1 ) )
+ {
+ return LORAMAC_STATUS_CRYPTO_ERROR;
+ }
+ }
+ else
+ {
+ status = LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+ break;
+ }
+ case MIB_MC_KEY_2:
+ {
+ if( mibSet->Param.McKey2 != NULL )
+ {
+ if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_KEY_2, mibSet->Param.McKey2 ) )
+ {
+ return LORAMAC_STATUS_CRYPTO_ERROR;
+ }
+ }
+ else
+ {
+ status = LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+ break;
+ }
+ case MIB_MC_APP_S_KEY_2:
+ {
+ if( mibSet->Param.McAppSKey2 != NULL )
+ {
+ if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_APP_S_KEY_2, mibSet->Param.McAppSKey2 ) )
+ {
+ return LORAMAC_STATUS_CRYPTO_ERROR;
+ }
+ }
+ else
+ {
+ status = LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+ break;
+ }
+ case MIB_MC_NWK_S_KEY_2:
+ {
+ if( mibSet->Param.McNwkSKey2 != NULL )
+ {
+ if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_NWK_S_KEY_2, mibSet->Param.McNwkSKey2 ) )
+ {
+ return LORAMAC_STATUS_CRYPTO_ERROR;
+ }
+ }
+ else
+ {
+ status = LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+ break;
+ }
+ case MIB_MC_KEY_3:
+ {
+ if( mibSet->Param.McKey3 != NULL )
+ {
+ if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_KEY_3, mibSet->Param.McKey3 ) )
+ {
+ return LORAMAC_STATUS_CRYPTO_ERROR;
+ }
+ }
+ else
+ {
+ status = LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+ break;
+ }
+ case MIB_MC_APP_S_KEY_3:
+ {
+ if( mibSet->Param.McAppSKey3 != NULL )
+ {
+ if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_APP_S_KEY_3, mibSet->Param.McAppSKey3 ) )
+ {
+ return LORAMAC_STATUS_CRYPTO_ERROR;
+ }
+ }
+ else
+ {
+ status = LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+ break;
+ }
+ case MIB_MC_NWK_S_KEY_3:
+ {
+ if( mibSet->Param.McNwkSKey3 != NULL )
+ {
+ if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_NWK_S_KEY_3, mibSet->Param.McNwkSKey3 ) )
+ {
+ return LORAMAC_STATUS_CRYPTO_ERROR;
+ }
+ }
+ else
+ {
+ status = LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+ break;
+ }
+ case MIB_PUBLIC_NETWORK:
+ {
+ MacCtx.NvmCtx->PublicNetwork = mibSet->Param.EnablePublicNetwork;
+ Radio.SetPublicNetwork( MacCtx.NvmCtx->PublicNetwork );
+ break;
+ }
+ case MIB_REPEATER_SUPPORT:
+ {
+ MacCtx.NvmCtx->RepeaterSupport = mibSet->Param.EnableRepeaterSupport;
+ break;
+ }
+ case MIB_RX2_CHANNEL:
+ {
+ verify.DatarateParams.Datarate = mibSet->Param.Rx2Channel.Datarate;
+ verify.DatarateParams.DownlinkDwellTime = MacCtx.NvmCtx->MacParams.DownlinkDwellTime;
+
+ if( RegionVerify( MacCtx.NvmCtx->Region, &verify, PHY_RX_DR ) == true )
+ {
+ MacCtx.NvmCtx->MacParams.Rx2Channel = mibSet->Param.Rx2Channel;
+ }
+ else
+ {
+ status = LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+ break;
+ }
+ case MIB_RX2_DEFAULT_CHANNEL:
+ {
+ verify.DatarateParams.Datarate = mibSet->Param.Rx2Channel.Datarate;
+ verify.DatarateParams.DownlinkDwellTime = MacCtx.NvmCtx->MacParams.DownlinkDwellTime;
+
+ if( RegionVerify( MacCtx.NvmCtx->Region, &verify, PHY_RX_DR ) == true )
+ {
+ MacCtx.NvmCtx->MacParamsDefaults.Rx2Channel = mibSet->Param.Rx2DefaultChannel;
+ }
+ else
+ {
+ status = LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+ break;
+ }
+ case MIB_RXC_CHANNEL:
+ {
+ verify.DatarateParams.Datarate = mibSet->Param.RxCChannel.Datarate;
+ verify.DatarateParams.DownlinkDwellTime = MacCtx.NvmCtx->MacParams.DownlinkDwellTime;
+
+ if( RegionVerify( MacCtx.NvmCtx->Region, &verify, PHY_RX_DR ) == true )
+ {
+ MacCtx.NvmCtx->MacParams.RxCChannel = mibSet->Param.RxCChannel;
+
+ if( ( MacCtx.NvmCtx->DeviceClass == CLASS_C ) && ( MacCtx.NvmCtx->NetworkActivation != ACTIVATION_TYPE_NONE ) )
+ {
+ // We can only compute the RX window parameters directly, if we are already
+ // in class c mode and joined. We cannot setup an RX window in case of any other
+ // class type.
+ // Set the radio into sleep mode in case we are still in RX mode
+ Radio.Sleep( );
+ // Compute RxC windows parameters
+ RegionComputeRxWindowParameters( MacCtx.NvmCtx->Region,
+ MacCtx.NvmCtx->MacParams.RxCChannel.Datarate,
+ MacCtx.NvmCtx->MacParams.MinRxSymbols,
+ MacCtx.NvmCtx->MacParams.SystemMaxRxError,
+ &MacCtx.RxWindowCConfig );
+ OpenContinuousRxCWindow( );
+ }
+ }
+ else
+ {
+ status = LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+ break;
+ }
+ case MIB_RXC_DEFAULT_CHANNEL:
+ {
+ verify.DatarateParams.Datarate = mibSet->Param.RxCChannel.Datarate;
+ verify.DatarateParams.DownlinkDwellTime = MacCtx.NvmCtx->MacParams.DownlinkDwellTime;
+
+ if( RegionVerify( MacCtx.NvmCtx->Region, &verify, PHY_RX_DR ) == true )
+ {
+ MacCtx.NvmCtx->MacParamsDefaults.RxCChannel = mibSet->Param.RxCDefaultChannel;
+ }
+ else
+ {
+ status = LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+ break;
+ }
+ case MIB_CHANNELS_DEFAULT_MASK:
+ {
+ chanMaskSet.ChannelsMaskIn = mibSet->Param.ChannelsDefaultMask;
+ chanMaskSet.ChannelsMaskType = CHANNELS_DEFAULT_MASK;
+
+ if( RegionChanMaskSet( MacCtx.NvmCtx->Region, &chanMaskSet ) == false )
+ {
+ status = LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+ break;
+ }
+ case MIB_CHANNELS_MASK:
+ {
+ chanMaskSet.ChannelsMaskIn = mibSet->Param.ChannelsMask;
+ chanMaskSet.ChannelsMaskType = CHANNELS_MASK;
+
+ if( RegionChanMaskSet( MacCtx.NvmCtx->Region, &chanMaskSet ) == false )
+ {
+ status = LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+ break;
+ }
+ case MIB_CHANNELS_NB_TRANS:
+ {
+ if( ( mibSet->Param.ChannelsNbTrans >= 1 ) &&
+ ( mibSet->Param.ChannelsNbTrans <= 15 ) )
+ {
+ MacCtx.NvmCtx->MacParams.ChannelsNbTrans = mibSet->Param.ChannelsNbTrans;
+ }
+ else
+ {
+ status = LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+ break;
+ }
+ case MIB_MAX_RX_WINDOW_DURATION:
+ {
+ MacCtx.NvmCtx->MacParams.MaxRxWindow = mibSet->Param.MaxRxWindow;
+ break;
+ }
+ case MIB_RECEIVE_DELAY_1:
+ {
+ MacCtx.NvmCtx->MacParams.ReceiveDelay1 = mibSet->Param.ReceiveDelay1;
+ break;
+ }
+ case MIB_RECEIVE_DELAY_2:
+ {
+ MacCtx.NvmCtx->MacParams.ReceiveDelay2 = mibSet->Param.ReceiveDelay2;
+ break;
+ }
+ case MIB_JOIN_ACCEPT_DELAY_1:
+ {
+ MacCtx.NvmCtx->MacParams.JoinAcceptDelay1 = mibSet->Param.JoinAcceptDelay1;
+ break;
+ }
+ case MIB_JOIN_ACCEPT_DELAY_2:
+ {
+ MacCtx.NvmCtx->MacParams.JoinAcceptDelay2 = mibSet->Param.JoinAcceptDelay2;
+ break;
+ }
+ case MIB_CHANNELS_DEFAULT_DATARATE:
+ {
+ verify.DatarateParams.Datarate = mibSet->Param.ChannelsDefaultDatarate;
+
+ if( RegionVerify( MacCtx.NvmCtx->Region, &verify, PHY_DEF_TX_DR ) == true )
+ {
+ MacCtx.NvmCtx->MacParamsDefaults.ChannelsDatarate = verify.DatarateParams.Datarate;
+ }
+ else
+ {
+ status = LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+ break;
+ }
+ case MIB_CHANNELS_DATARATE:
+ {
+ verify.DatarateParams.Datarate = mibSet->Param.ChannelsDatarate;
+ verify.DatarateParams.UplinkDwellTime = MacCtx.NvmCtx->MacParams.UplinkDwellTime;
+
+ if( RegionVerify( MacCtx.NvmCtx->Region, &verify, PHY_TX_DR ) == true )
+ {
+ MacCtx.NvmCtx->MacParams.ChannelsDatarate = verify.DatarateParams.Datarate;
+ }
+ else
+ {
+ status = LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+ break;
+ }
+ case MIB_CHANNELS_DEFAULT_TX_POWER:
+ {
+ verify.TxPower = mibSet->Param.ChannelsDefaultTxPower;
+
+ if( RegionVerify( MacCtx.NvmCtx->Region, &verify, PHY_DEF_TX_POWER ) == true )
+ {
+ MacCtx.NvmCtx->MacParamsDefaults.ChannelsTxPower = verify.TxPower;
+ }
+ else
+ {
+ status = LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+ break;
+ }
+ case MIB_CHANNELS_TX_POWER:
+ {
+ verify.TxPower = mibSet->Param.ChannelsTxPower;
+
+ if( RegionVerify( MacCtx.NvmCtx->Region, &verify, PHY_TX_POWER ) == true )
+ {
+ MacCtx.NvmCtx->MacParams.ChannelsTxPower = verify.TxPower;
+ }
+ else
+ {
+ status = LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+ break;
+ }
+ case MIB_SYSTEM_MAX_RX_ERROR:
+ {
+ MacCtx.NvmCtx->MacParams.SystemMaxRxError = MacCtx.NvmCtx->MacParamsDefaults.SystemMaxRxError = mibSet->Param.SystemMaxRxError;
+ break;
+ }
+ case MIB_MIN_RX_SYMBOLS:
+ {
+ MacCtx.NvmCtx->MacParams.MinRxSymbols = MacCtx.NvmCtx->MacParamsDefaults.MinRxSymbols = mibSet->Param.MinRxSymbols;
+ break;
+ }
+ case MIB_ANTENNA_GAIN:
+ {
+ MacCtx.NvmCtx->MacParams.AntennaGain = mibSet->Param.AntennaGain;
+ break;
+ }
+ case MIB_DEFAULT_ANTENNA_GAIN:
+ {
+ MacCtx.NvmCtx->MacParamsDefaults.AntennaGain = mibSet->Param.DefaultAntennaGain;
+ break;
+ }
+ case MIB_NVM_CTXS:
+ {
+ if( mibSet->Param.Contexts != 0 )
+ {
+ status = RestoreCtxs( mibSet->Param.Contexts );
+ }
+ else
+ {
+ status = LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+ break;
+ }
+ case MIB_ABP_LORAWAN_VERSION:
+ {
+ if( mibSet->Param.AbpLrWanVersion.Fields.Minor <= 1 )
+ {
+ MacCtx.NvmCtx->Version = mibSet->Param.AbpLrWanVersion;
+
+ if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetLrWanVersion( mibSet->Param.AbpLrWanVersion ) )
+ {
+ return LORAMAC_STATUS_CRYPTO_ERROR;
+ }
+ }
+ else
+ {
+ status = LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+ break;
+ }
+ default:
+ {
+ status = LoRaMacMibClassBSetRequestConfirm( mibSet );
+ break;
+ }
+ }
+ EventRegionNvmCtxChanged( );
+ EventMacNvmCtxChanged( );
+ return status;
+}
+
+LoRaMacStatus_t LoRaMacChannelAdd( uint8_t id, ChannelParams_t params )
+{
+ ChannelAddParams_t channelAdd;
+
+ // Validate if the MAC is in a correct state
+ if( ( MacCtx.MacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING )
+ {
+ if( ( MacCtx.MacState & LORAMAC_TX_CONFIG ) != LORAMAC_TX_CONFIG )
+ {
+ return LORAMAC_STATUS_BUSY;
+ }
+ }
+
+ channelAdd.NewChannel = ¶ms;
+ channelAdd.ChannelId = id;
+
+ EventRegionNvmCtxChanged( );
+ return RegionChannelAdd( MacCtx.NvmCtx->Region, &channelAdd );
+}
+
+LoRaMacStatus_t LoRaMacChannelRemove( uint8_t id )
+{
+ ChannelRemoveParams_t channelRemove;
+
+ if( ( MacCtx.MacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING )
+ {
+ if( ( MacCtx.MacState & LORAMAC_TX_CONFIG ) != LORAMAC_TX_CONFIG )
+ {
+ return LORAMAC_STATUS_BUSY;
+ }
+ }
+
+ channelRemove.ChannelId = id;
+
+ if( RegionChannelsRemove( MacCtx.NvmCtx->Region, &channelRemove ) == false )
+ {
+ return LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+
+ EventRegionNvmCtxChanged( );
+ return LORAMAC_STATUS_OK;
+}
+
+LoRaMacStatus_t LoRaMacMcChannelSetup( McChannelParams_t *channel )
+{
+ if( ( MacCtx.MacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING )
+ {
+ return LORAMAC_STATUS_BUSY;
+ }
+
+ if( channel->GroupID >= LORAMAC_MAX_MC_CTX )
+ {
+ return LORAMAC_STATUS_MC_GROUP_UNDEFINED;
+ }
+
+ MacCtx.NvmCtx->MulticastChannelList[channel->GroupID].ChannelParams = *channel;
+
+ const KeyIdentifier_t mcKeys[LORAMAC_MAX_MC_CTX] = { MC_KEY_0, MC_KEY_1, MC_KEY_2, MC_KEY_3 };
+ if( LoRaMacCryptoSetKey( mcKeys[channel->GroupID], channel->McKeyE ) != LORAMAC_CRYPTO_SUCCESS )
+ {
+ return LORAMAC_STATUS_CRYPTO_ERROR;
+ }
+
+ if( LoRaMacCryptoDeriveMcSessionKeyPair( channel->GroupID, channel->Address ) != LORAMAC_CRYPTO_SUCCESS )
+ {
+ return LORAMAC_STATUS_CRYPTO_ERROR;
+ }
+
+ if( channel->Class == CLASS_B )
+ {
+ // Calculate class b parameters
+ LoRaMacClassBSetMulticastPeriodicity( &MacCtx.NvmCtx->MulticastChannelList[channel->GroupID] );
+ }
+
+ // Reset multicast channel downlink counter to initial value.
+ *MacCtx.NvmCtx->MulticastChannelList[channel->GroupID].DownLinkCounter = FCNT_DOWN_INITAL_VALUE;
+
+ EventMacNvmCtxChanged( );
+ EventRegionNvmCtxChanged( );
+ return LORAMAC_STATUS_OK;
+}
+
+LoRaMacStatus_t LoRaMacMcChannelDelete( AddressIdentifier_t groupID )
+{
+ if( ( MacCtx.MacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING )
+ {
+ return LORAMAC_STATUS_BUSY;
+ }
+
+ if( ( groupID >= LORAMAC_MAX_MC_CTX ) ||
+ ( MacCtx.NvmCtx->MulticastChannelList[groupID].ChannelParams.IsEnabled == false ) )
+ {
+ return LORAMAC_STATUS_MC_GROUP_UNDEFINED;
+ }
+
+ McChannelParams_t channel;
+
+ // Set all channel fields with 0
+ memset1( ( uint8_t* )&channel, 0, sizeof( McChannelParams_t ) );
+
+ MacCtx.NvmCtx->MulticastChannelList[groupID].ChannelParams = channel;
+
+ EventMacNvmCtxChanged( );
+ EventRegionNvmCtxChanged( );
+ return LORAMAC_STATUS_OK;
+}
+
+uint8_t LoRaMacMcChannelGetGroupId( uint32_t mcAddress )
+{
+ for( uint8_t i = 0; i < LORAMAC_MAX_MC_CTX; i++ )
+ {
+ if( mcAddress == MacCtx.NvmCtx->MulticastChannelList[i].ChannelParams.Address )
+ {
+ return i;
+ }
+ }
+ return 0xFF;
+}
+
+LoRaMacStatus_t LoRaMacMcChannelSetupRxParams( AddressIdentifier_t groupID, McRxParams_t *rxParams, uint8_t *status )
+{
+ *status = 0x1C + ( groupID & 0x03 );
+
+ if( ( MacCtx.MacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING )
+ {
+ return LORAMAC_STATUS_BUSY;
+ }
+
+ DeviceClass_t devClass = MacCtx.NvmCtx->MulticastChannelList[groupID].ChannelParams.Class;
+ if( ( devClass == CLASS_A ) || ( devClass > CLASS_C ) )
+ {
+ return LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+
+ if( ( groupID >= LORAMAC_MAX_MC_CTX ) ||
+ ( MacCtx.NvmCtx->MulticastChannelList[groupID].ChannelParams.IsEnabled == false ) )
+ {
+ return LORAMAC_STATUS_MC_GROUP_UNDEFINED;
+ }
+ *status &= 0x0F; // groupID OK
+
+ VerifyParams_t verify;
+ // Check datarate
+ if( devClass == CLASS_B )
+ {
+ verify.DatarateParams.Datarate = rxParams->ClassB.Datarate;
+ }
+ else
+ {
+ verify.DatarateParams.Datarate = rxParams->ClassC.Datarate;
+ }
+ verify.DatarateParams.DownlinkDwellTime = MacCtx.NvmCtx->MacParams.DownlinkDwellTime;
+
+ if( RegionVerify( MacCtx.NvmCtx->Region, &verify, PHY_RX_DR ) == true )
+ {
+ *status &= 0xFB; // datarate OK
+ }
+
+ // Check frequency
+ if( devClass == CLASS_B )
+ {
+ verify.Frequency = rxParams->ClassB.Frequency;
+ }
+ else
+ {
+ verify.Frequency = rxParams->ClassC.Frequency;
+ }
+ if( RegionVerify( MacCtx.NvmCtx->Region, &verify, PHY_FREQUENCY ) == true )
+ {
+ *status &= 0xF7; // frequency OK
+ }
+
+ if( *status == ( groupID & 0x03 ) )
+ {
+ // Apply parameters
+ MacCtx.NvmCtx->MulticastChannelList[groupID].ChannelParams.RxParams = *rxParams;
+ }
+
+ EventMacNvmCtxChanged( );
+ EventRegionNvmCtxChanged( );
+ return LORAMAC_STATUS_OK;
+}
+
+LoRaMacStatus_t LoRaMacMlmeRequest( MlmeReq_t* mlmeRequest )
+{
+ LoRaMacStatus_t status = LORAMAC_STATUS_SERVICE_UNKNOWN;
+ MlmeConfirmQueue_t queueElement;
+ uint8_t macCmdPayload[2] = { 0x00, 0x00 };
+
+ if( mlmeRequest == NULL )
+ {
+ return LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+ if( LoRaMacIsBusy( ) == true )
+ {
+ return LORAMAC_STATUS_BUSY;
+ }
+ if( LoRaMacConfirmQueueIsFull( ) == true )
+ {
+ return LORAMAC_STATUS_BUSY;
+ }
+
+ if( LoRaMacConfirmQueueGetCnt( ) == 0 )
+ {
+ memset1( ( uint8_t* ) &MacCtx.MlmeConfirm, 0, sizeof( MacCtx.MlmeConfirm ) );
+ }
+ MacCtx.MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
+
+ MacCtx.MacFlags.Bits.MlmeReq = 1;
+ queueElement.Request = mlmeRequest->Type;
+ queueElement.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
+ queueElement.RestrictCommonReadyToHandle = false;
+
+ switch( mlmeRequest->Type )
+ {
+ case MLME_JOIN:
+ {
+ if( ( MacCtx.MacState & LORAMAC_TX_DELAYED ) == LORAMAC_TX_DELAYED )
+ {
+ return LORAMAC_STATUS_BUSY;
+ }
+
+ ResetMacParameters( );
+
+ MacCtx.NvmCtx->MacParams.ChannelsDatarate = RegionAlternateDr( MacCtx.NvmCtx->Region, mlmeRequest->Req.Join.Datarate, ALTERNATE_DR );
+
+ queueElement.Status = LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL;
+ MW_LOG("Join DR = DR_%d\r\n" ,MacCtx.NvmCtx->MacParams.ChannelsDatarate);
+ status = SendReJoinReq( JOIN_REQ );
+
+ if( status != LORAMAC_STATUS_OK )
+ {
+ // Revert back the previous datarate ( mainly used for US915 like regions )
+ MacCtx.NvmCtx->MacParams.ChannelsDatarate = RegionAlternateDr( MacCtx.NvmCtx->Region, mlmeRequest->Req.Join.Datarate, ALTERNATE_DR_RESTORE );
+ }
+
+
+ break;
+ }
+ case MLME_LINK_CHECK:
+ {
+ // LoRaMac will send this command piggy-pack
+ status = LORAMAC_STATUS_OK;
+ if( LoRaMacCommandsAddCmd( MOTE_MAC_LINK_CHECK_REQ, macCmdPayload, 0 ) != LORAMAC_COMMANDS_SUCCESS )
+ {
+ status = LORAMAC_STATUS_MAC_COMMAD_ERROR;
+ }
+ break;
+ }
+ case MLME_TXCW:
+ {
+ status = SetTxContinuousWave( mlmeRequest->Req.TxCw.Timeout );
+ break;
+ }
+ case MLME_TXCW_1:
+ {
+
+ status = SetTxContinuousWave1( mlmeRequest->Req.TxCw.Timeout, mlmeRequest->Req.TxCw.Frequency, mlmeRequest->Req.TxCw.Power );
+ break;
+ }
+ case MLME_DEVICE_TIME:
+ {
+ // LoRaMac will send this command piggy-pack
+ status = LORAMAC_STATUS_OK;
+ if( LoRaMacCommandsAddCmd( MOTE_MAC_DEVICE_TIME_REQ, macCmdPayload, 0 ) != LORAMAC_COMMANDS_SUCCESS )
+ {
+ status = LORAMAC_STATUS_MAC_COMMAD_ERROR;
+ }
+ break;
+ }
+ case MLME_PING_SLOT_INFO:
+ {
+ if( MacCtx.NvmCtx->DeviceClass == CLASS_A )
+ {
+ uint8_t value = mlmeRequest->Req.PingSlotInfo.PingSlot.Value;
+
+ // LoRaMac will send this command piggy-pack
+ LoRaMacClassBSetPingSlotInfo( mlmeRequest->Req.PingSlotInfo.PingSlot.Fields.Periodicity );
+ macCmdPayload[0] = value;
+ status = LORAMAC_STATUS_OK;
+ if( LoRaMacCommandsAddCmd( MOTE_MAC_PING_SLOT_INFO_REQ, macCmdPayload, 1 ) != LORAMAC_COMMANDS_SUCCESS )
+ {
+ status = LORAMAC_STATUS_MAC_COMMAD_ERROR;
+ }
+ }
+ break;
+ }
+ case MLME_BEACON_TIMING:
+ {
+ // LoRaMac will send this command piggy-pack
+ status = LORAMAC_STATUS_OK;
+ if( LoRaMacCommandsAddCmd( MOTE_MAC_BEACON_TIMING_REQ, macCmdPayload, 0 ) != LORAMAC_COMMANDS_SUCCESS )
+ {
+ status = LORAMAC_STATUS_MAC_COMMAD_ERROR;
+ }
+ break;
+ }
+ case MLME_BEACON_ACQUISITION:
+ {
+ // Apply the request
+ queueElement.RestrictCommonReadyToHandle = true;
+
+ if( LoRaMacClassBIsAcquisitionInProgress( ) == false )
+ {
+ // Start class B algorithm
+ LoRaMacClassBSetBeaconState( BEACON_STATE_ACQUISITION );
+ LoRaMacClassBBeaconTimerEvent( NULL );
+
+ status = LORAMAC_STATUS_OK;
+ }
+ else
+ {
+ status = LORAMAC_STATUS_BUSY;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ if( status != LORAMAC_STATUS_OK )
+ {
+ if( LoRaMacConfirmQueueGetCnt( ) == 0 )
+ {
+ MacCtx.NodeAckRequested = false;
+ MacCtx.MacFlags.Bits.MlmeReq = 0;
+ }
+ }
+ else
+ {
+ LoRaMacConfirmQueueAdd( &queueElement );
+ EventMacNvmCtxChanged( );
+ }
+ return status;
+}
+
+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;
+ int8_t datarate = DR_0;
+ bool readyToSend = false;
+
+ if( mcpsRequest == NULL )
+ {
+ return LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+ if( LoRaMacIsBusy( ) == true )
+ {
+ return LORAMAC_STATUS_BUSY;
+ }
+
+ macHdr.Value = 0;
+ memset1( ( uint8_t* ) &MacCtx.McpsConfirm, 0, sizeof( MacCtx.McpsConfirm ) );
+ MacCtx.McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
+
+ // AckTimeoutRetriesCounter must be reset every time a new request (unconfirmed or confirmed) is performed.
+ MacCtx.AckTimeoutRetriesCounter = 1;
+
+ switch( mcpsRequest->Type )
+ {
+ case MCPS_UNCONFIRMED:
+ {
+ readyToSend = true;
+ MacCtx.AckTimeoutRetries = 1;
+
+ macHdr.Bits.MType = FRAME_TYPE_DATA_UNCONFIRMED_UP;
+ fPort = mcpsRequest->Req.Unconfirmed.fPort;
+ fBuffer = mcpsRequest->Req.Unconfirmed.fBuffer;
+ fBufferSize = mcpsRequest->Req.Unconfirmed.fBufferSize;
+ datarate = mcpsRequest->Req.Unconfirmed.Datarate;
+ break;
+ }
+ case MCPS_CONFIRMED:
+ {
+ readyToSend = true;
+ MacCtx.AckTimeoutRetries = MIN( mcpsRequest->Req.Confirmed.NbTrials, MAX_ACK_RETRIES );
+
+ macHdr.Bits.MType = FRAME_TYPE_DATA_CONFIRMED_UP;
+ fPort = mcpsRequest->Req.Confirmed.fPort;
+ fBuffer = mcpsRequest->Req.Confirmed.fBuffer;
+ fBufferSize = mcpsRequest->Req.Confirmed.fBufferSize;
+ datarate = mcpsRequest->Req.Confirmed.Datarate;
+ break;
+ }
+ case MCPS_PROPRIETARY:
+ {
+ readyToSend = true;
+ MacCtx.AckTimeoutRetries = 1;
+
+ macHdr.Bits.MType = FRAME_TYPE_PROPRIETARY;
+ fBuffer = mcpsRequest->Req.Proprietary.fBuffer;
+ fBufferSize = mcpsRequest->Req.Proprietary.fBufferSize;
+ datarate = mcpsRequest->Req.Proprietary.Datarate;
+ break;
+ }
+ default:
+ break;
+ }
+
+ // Get the minimum possible datarate
+ getPhy.Attribute = PHY_MIN_TX_DR;
+ getPhy.UplinkDwellTime = MacCtx.NvmCtx->MacParams.UplinkDwellTime;
+ phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
+ // Apply the minimum possible datarate.
+ // Some regions have limitations for the minimum datarate.
+ datarate = MAX( datarate, ( int8_t )phyParam.Value );
+
+ if( readyToSend == true )
+ {
+ if( MacCtx.NvmCtx->AdrCtrlOn == false )
+ {
+ verify.DatarateParams.Datarate = datarate;
+ verify.DatarateParams.UplinkDwellTime = MacCtx.NvmCtx->MacParams.UplinkDwellTime;
+
+ if( RegionVerify( MacCtx.NvmCtx->Region, &verify, PHY_TX_DR ) == true )
+ {
+ MacCtx.NvmCtx->MacParams.ChannelsDatarate = verify.DatarateParams.Datarate;
+ }
+ else
+ {
+ return LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+ }
+
+ status = Send( &macHdr, fPort, fBuffer, fBufferSize );
+ if( status == LORAMAC_STATUS_OK )
+ {
+ MacCtx.McpsConfirm.McpsRequest = mcpsRequest->Type;
+ MacCtx.MacFlags.Bits.McpsReq = 1;
+ }
+ else
+ {
+ MacCtx.NodeAckRequested = false;
+ }
+ }
+
+ EventMacNvmCtxChanged( );
+ return status;
+}
+
+void LoRaMacTestSetDutyCycleOn( bool enable )
+{
+ VerifyParams_t verify;
+
+ verify.DutyCycle = enable;
+
+ if( RegionVerify( MacCtx.NvmCtx->Region, &verify, PHY_DUTY_CYCLE ) == true )
+ {
+ MacCtx.NvmCtx->DutyCycleOn = enable;
+ }
+}
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMac.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMac.h
new file mode 100644
index 00000000..65d4291e
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMac.h
@@ -0,0 +1,2707 @@
+/*!
+ * \file LoRaMac.h
+ *
+ * \brief LoRa MAC layer implementation
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013-2017 Semtech
+ *
+ * ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ * embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Gregory Cristian ( Semtech )
+ *
+ * \author Daniel Jaeckle ( STACKFORCE )
+ *
+ * \author Johannes Bruder ( STACKFORCE )
+ *
+ * \defgroup LORAMAC LoRa MAC layer implementation
+ * This module specifies the API implementation of the LoRaMAC layer.
+ * This is a placeholder for a detailed description of the LoRaMac
+ * layer and the supported features.
+ * \{
+ *
+ * \example classA/B-L072Z-LRWAN1/main.c
+ * LoRaWAN class A application example for the B-L072Z-LRWAN1.
+ *
+ * \example classB/B-L072Z-LRWAN1/main.c
+ * LoRaWAN class B application example for the B-L072Z-LRWAN1.
+ *
+ * \example classC/B-L072Z-LRWAN1/main.c
+ * LoRaWAN class C application example for the B-L072Z-LRWAN1.
+ *
+ * \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/NucleoL073/main.c
+ * LoRaWAN class A application example for the NucleoL073.
+ *
+ * \example classB/NucleoL073/main.c
+ * LoRaWAN class B application example for the NucleoL073.
+ *
+ * \example classC/NucleoL073/main.c
+ * LoRaWAN class C application example for the NucleoL073.
+ *
+ * \example classA/NucleoL152/main.c
+ * LoRaWAN class A application example for the NucleoL152.
+ *
+ * \example classB/NucleoL152/main.c
+ * LoRaWAN class B application example for the NucleoL152.
+ *
+ * \example classC/NucleoL152/main.c
+ * LoRaWAN class C application example for the NucleoL152.
+ *
+ * \example classA/NucleoL476/main.c
+ * LoRaWAN class A application example for the NucleoL476.
+ *
+ * \example classB/NucleoL476/main.c
+ * LoRaWAN class B application example for the NucleoL476.
+ *
+ * \example classC/NucleoL476/main.c
+ * LoRaWAN class C application example for the NucleoL476.
+ *
+ * \example classA/SAML21/main.c
+ * LoRaWAN class A application example for the SAML21.
+ *
+ * \example classB/SAML21/main.c
+ * LoRaWAN class B application example for the SAML21.
+ *
+ * \example classC/SAML21/main.c
+ * LoRaWAN class C application example for the SAML21.
+ *
+ * \example classA/SKiM880B/main.c
+ * LoRaWAN class A application example for the SKiM880B.
+ *
+ * \example classB/SKiM880B/main.c
+ * LoRaWAN class B application example for the SKiM880B.
+ *
+ * \example classC/SKiM880B/main.c
+ * LoRaWAN class C application example for the SKiM880B.
+ *
+ * \example classA/SKiM881AXL/main.c
+ * LoRaWAN class A application example for the SKiM881AXL.
+ *
+ * \example classB/SKiM881AXL/main.c
+ * LoRaWAN class B application example for the SKiM881AXL.
+ *
+ * \example classC/SKiM881AXL/main.c
+ * LoRaWAN class C application example for the SKiM881AXL.
+ *
+ * \example classA/SKiM980A/main.c
+ * LoRaWAN class A application example for the SKiM980A.
+ *
+ * \example classB/SKiM980A/main.c
+ * LoRaWAN class B application example for the SKiM980A.
+ *
+ * \example classC/SKiM980A/main.c
+ * LoRaWAN class C application example for the SKiM980A.
+ *
+ */
+#ifndef __LORAMAC_H__
+#define __LORAMAC_H__
+
+#include
+#include
+#include "LoRaMacTypes.h"
+#include "lorawan_conf.h"
+
+/*!
+ * Maximum number of times the MAC layer tries to get an acknowledge.
+ */
+#define MAX_ACK_RETRIES 8
+
+/*!
+ * Frame direction definition for up-link communications
+ */
+#define UP_LINK 0
+
+/*!
+ * Frame direction definition for down-link communications
+ */
+#define DOWN_LINK 1
+
+/*!
+ * Sets the length of the LoRaMAC footer field.
+ * Mainly indicates the MIC field length
+ */
+#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)
+
+/*!
+ * Maximum number of multicast context
+ */
+#define LORAMAC_MAX_MC_CTX 4
+
+/*!
+ * Start value for multicast keys enumeration
+ */
+#define LORAMAC_CRYPTO_MULTICAST_KEYS 127
+
+/*!
+ * End-Device activation type
+ */
+typedef enum eActivationType
+{
+ /*!
+ * None
+ */
+ ACTIVATION_TYPE_NONE = 0,
+ /*!
+ * Activation By Personalization (ACTIVATION_TYPE_ABP)
+ */
+ ACTIVATION_TYPE_ABP = 1,
+ /*!
+ * Over-The-Air Activation (ACTIVATION_TYPE_OTAA)
+ */
+ ACTIVATION_TYPE_OTAA = 2,
+}ActivationType_t;
+
+/*!
+ * LoRaMAC channels parameters definition
+ */
+typedef union uDrRange
+{
+ /*!
+ * Byte-access to the bits
+ */
+ int8_t Value;
+ /*!
+ * Structure to store the minimum and the maximum datarate
+ */
+ struct sFields
+ {
+ /*!
+ * Minimum data rate
+ *
+ * LoRaWAN Regional Parameters V1.0.2rB
+ *
+ * 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
+ *
+ * LoRaWAN Regional Parameters V1.0.2rB
+ *
+ * The allowed ranges are region specific. Please refer to \ref DR_0 to \ref DR_15 for details.
+ */
+ int8_t Max : 4;
+ }Fields;
+}DrRange_t;
+
+/*!
+ * LoRaMAC channel definition
+ */
+typedef struct sChannelParams
+{
+ /*!
+ * Frequency in Hz
+ */
+ uint32_t Frequency;
+ /*!
+ * Alternative frequency for RX window 1
+ */
+ uint32_t Rx1Frequency;
+ /*!
+ * Data rate definition
+ */
+ DrRange_t DrRange;
+ /*!
+ * Band index
+ */
+ uint8_t Band;
+}ChannelParams_t;
+
+/*!
+ * LoRaMAC receive window channel parameters
+ */
+typedef struct sRxChannelParams
+{
+ /*!
+ * Frequency in Hz
+ */
+ uint32_t Frequency;
+ /*!
+ * Data rate
+ *
+ * LoRaWAN Regional Parameters V1.0.2rB
+ *
+ * The allowed ranges are region specific. Please refer to \ref DR_0 to \ref DR_15 for details.
+ */
+ uint8_t Datarate;
+}RxChannelParams_t;
+
+/*!
+ * LoRaMAC receive window enumeration
+ */
+typedef enum eLoRaMacRxSlot
+{
+ /*!
+ * LoRaMAC receive window 1
+ */
+ RX_SLOT_WIN_1,
+ /*!
+ * LoRaMAC receive window 2
+ */
+ RX_SLOT_WIN_2,
+ /*!
+ * LoRaMAC receive window 2 for class c - continuous listening
+ */
+ RX_SLOT_WIN_CLASS_C,
+ /*!
+ * LoRaMAC class c multicast downlink
+ */
+ RX_SLOT_WIN_CLASS_C_MULTICAST,
+ /*!
+ * LoRaMAC class b ping slot window
+ */
+ RX_SLOT_WIN_CLASS_B_PING_SLOT,
+ /*!
+ * LoRaMAC class b multicast slot window
+ */
+ RX_SLOT_WIN_CLASS_B_MULTICAST_SLOT,
+ /*!
+ * LoRaMAC no active receive window
+ */
+ RX_SLOT_NONE,
+}LoRaMacRxSlot_t;
+
+/*!
+ * LoRaMAC structure to hold internal context pointers and its lengths
+ */
+typedef struct sLoRaMacCtxs
+{
+ /*!
+ * \brief Pointer to Mac context
+ */
+ void* MacNvmCtx;
+ /*!
+ * \brief Size of Mac context
+ */
+ size_t MacNvmCtxSize;
+ /*!
+ * \brief Pointer to region context
+ */
+ void* RegionNvmCtx;
+ /*!
+ * \brief Size of region context
+ */
+ size_t RegionNvmCtxSize;
+ /*!
+ * \brief Pointer to crypto module context
+ */
+ void* CryptoNvmCtx;
+ /*!
+ * \brief Size of crypto module context
+ */
+ size_t CryptoNvmCtxSize;
+ /*!
+ * \brief Pointer to secure element driver context
+ */
+ void* SecureElementNvmCtx;
+ /*!
+ * \brief Size of secure element driver context
+ */
+ size_t SecureElementNvmCtxSize;
+ /*!
+ * \brief Pointer to MAC commands module context
+ */
+ void* CommandsNvmCtx;
+ /*!
+ * \brief Size of MAC commands module context
+ */
+ size_t CommandsNvmCtxSize;
+ /*!
+ * \brief Pointer to Class B module context
+ */
+ void* ClassBNvmCtx;
+ /*!
+ * \brief Size of MAC Class B module context
+ */
+ size_t ClassBNvmCtxSize;
+ /*!
+ * \brief Pointer to MLME Confirm queue module context
+ */
+ void* ConfirmQueueNvmCtx;
+ /*!
+ * \brief Size of MLME Confirm queue module context
+ */
+ size_t ConfirmQueueNvmCtxSize;
+}LoRaMacCtxs_t;
+
+/*!
+ * Global MAC layer parameters
+ */
+typedef struct sLoRaMacParams
+{
+ /*!
+ * Channels TX power
+ */
+ int8_t ChannelsTxPower;
+ /*!
+ * Channels data rate
+ */
+ int8_t ChannelsDatarate;
+ /*!
+ * System overall timing error in milliseconds.
+ * [-SystemMaxRxError : +SystemMaxRxError]
+ * Default: +/-10 ms
+ */
+ uint32_t SystemMaxRxError;
+ /*!
+ * Minimum required number of symbols to detect an Rx frame
+ * Default: 6 symbols
+ */
+ uint8_t MinRxSymbols;
+ /*!
+ * LoRaMac maximum time a reception window stays open
+ */
+ uint32_t MaxRxWindow;
+ /*!
+ * Receive delay 1
+ */
+ uint32_t ReceiveDelay1;
+ /*!
+ * Receive delay 2
+ */
+ uint32_t ReceiveDelay2;
+ /*!
+ * Join accept delay 1
+ */
+ uint32_t JoinAcceptDelay1;
+ /*!
+ * Join accept delay 1
+ */
+ uint32_t JoinAcceptDelay2;
+ /*!
+ * Number of uplink messages repetitions [1:15] (unconfirmed messages only)
+ */
+ uint8_t ChannelsNbTrans;
+ /*!
+ * Datarate offset between uplink and downlink on first window
+ */
+ uint8_t Rx1DrOffset;
+ /*!
+ * LoRaMAC 2nd reception window settings
+ */
+ RxChannelParams_t Rx2Channel;
+ /*!
+ * LoRaMAC continuous reception window settings
+ */
+ RxChannelParams_t RxCChannel;
+ /*!
+ * 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
+ */
+ float MaxEirp;
+ /*!
+ * Antenna gain of the node
+ */
+ float AntennaGain;
+ /*!
+ * Indicates if the node supports repeaters
+ */
+ bool RepeaterSupport;
+}LoRaMacParams_t;
+
+/*!
+ * LoRaMAC data structure for a PingSlotInfoReq \ref MLME_PING_SLOT_INFO
+ *
+ * LoRaWAN Specification
+ */
+typedef union uPingSlotInfo
+{
+ /*!
+ * Parameter for byte access
+ */
+ uint8_t Value;
+ /*!
+ * Structure containing the parameters for the PingSlotInfoReq
+ */
+ struct sInfoFields
+ {
+ /*!
+ * Periodicity = 0: ping slot every second
+ * Periodicity = 7: ping slot every 128 seconds
+ */
+ uint8_t Periodicity : 3;
+ /*!
+ * RFU
+ */
+ uint8_t RFU : 5;
+ }Fields;
+}PingSlotInfo_t;
+
+/*!
+ * LoRaMAC data structure for the \ref MLME_BEACON MLME-Indication
+ *
+ * LoRaWAN Specification
+ */
+typedef struct sBeaconInfo
+{
+ /*!
+ * Timestamp in seconds since 00:00:00, Sunday 6th of January 1980
+ * (start of the GPS epoch) modulo 2^32
+ */
+ SysTime_t Time;
+ /*!
+ * Frequency
+ */
+ uint32_t Frequency;
+ /*!
+ * Datarate
+ */
+ uint8_t Datarate;
+ /*!
+ * RSSI
+ */
+ int16_t Rssi;
+ /*!
+ * SNR
+ */
+ int8_t Snr;
+ /*!
+ * Data structure for the gateway specific part. The
+ * content of the values may differ for each gateway
+ */
+ struct sGwSpecific
+ {
+ /*!
+ * Info descriptor - can differ for each gateway
+ */
+ uint8_t InfoDesc;
+ /*!
+ * Info - can differ for each gateway
+ */
+ uint8_t Info[6];
+ }GwSpecific;
+}BeaconInfo_t;
+
+/*!
+ * Enumeration containing the status of the operation of a MAC service
+ */
+typedef enum eLoRaMacEventInfoStatus
+{
+ /*!
+ * Service performed successfully
+ */
+ LORAMAC_EVENT_INFO_STATUS_OK = 0,
+ /*!
+ * An error occurred during the execution of the service
+ */
+ LORAMAC_EVENT_INFO_STATUS_ERROR,
+ /*!
+ * A Tx timeout occurred
+ */
+ 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,
+ /*!
+ * An Rx error occurred on receive window 1
+ */
+ LORAMAC_EVENT_INFO_STATUS_RX1_ERROR,
+ /*!
+ * An Rx error occurred on receive window 2
+ */
+ LORAMAC_EVENT_INFO_STATUS_RX2_ERROR,
+ /*!
+ * An error occurred in the join procedure
+ */
+ 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.
+ */
+ LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED,
+ /*!
+ * The MAC could not retransmit a frame since the MAC decreased the datarate. The
+ * payload size is not applicable for the datarate.
+ */
+ LORAMAC_EVENT_INFO_STATUS_TX_DR_PAYLOAD_SIZE_ERROR,
+ /*!
+ * The node has lost MAX_FCNT_GAP or more frames.
+ */
+ LORAMAC_EVENT_INFO_STATUS_DOWNLINK_TOO_MANY_FRAMES_LOSS,
+ /*!
+ * An address error occurred
+ */
+ LORAMAC_EVENT_INFO_STATUS_ADDRESS_FAIL,
+ /*!
+ * 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;
+
+/*!
+ * LoRaMac tx/rx operation state
+ */
+typedef union eLoRaMacFlags_t
+{
+ /*!
+ * Byte-access to the bits
+ */
+ uint8_t Value;
+ /*!
+ * Structure containing single access to bits
+ */
+ struct sMacFlagBits
+ {
+ /*!
+ * MCPS-Req pending
+ */
+ uint8_t McpsReq : 1;
+ /*!
+ * MCPS-Ind pending
+ */
+ uint8_t McpsInd : 1;
+ /*!
+ * MLME-Req pending
+ */
+ uint8_t MlmeReq : 1;
+ /*!
+ * MLME-Ind pending
+ */
+ uint8_t MlmeInd : 1;
+ /*!
+ * MLME-Ind to schedule an uplink pending
+ */
+ uint8_t MlmeSchedUplinkInd : 1;
+ /*!
+ * MAC cycle done
+ */
+ uint8_t MacDone : 1;
+ }Bits;
+}LoRaMacFlags_t;
+
+/*!
+ *
+ * \brief LoRaMAC data services
+ *
+ * \details The following table list the primitives which are supported by the
+ * specific MAC data service:
+ *
+ * Name | Request | Indication | Response | Confirm
+ * --------------------- | :-----: | :--------: | :------: | :-----:
+ * \ref MCPS_UNCONFIRMED | YES | YES | NO | YES
+ * \ref MCPS_CONFIRMED | YES | YES | NO | YES
+ * \ref MCPS_MULTICAST | NO | YES | NO | NO
+ * \ref MCPS_PROPRIETARY | YES | YES | NO | YES
+ *
+ * The following table provides links to the function implementations of the
+ * related MCPS primitives:
+ *
+ * Primitive | Function
+ * ---------------- | :---------------------:
+ * MCPS-Request | \ref LoRaMacMlmeRequest
+ * MCPS-Confirm | MacMcpsConfirm in \ref LoRaMacPrimitives_t
+ * MCPS-Indication | MacMcpsIndication in \ref LoRaMacPrimitives_t
+ */
+typedef enum eMcps
+{
+ /*!
+ * Unconfirmed LoRaMAC frame
+ */
+ MCPS_UNCONFIRMED,
+ /*!
+ * Confirmed LoRaMAC frame
+ */
+ MCPS_CONFIRMED,
+ /*!
+ * Multicast LoRaMAC frame
+ */
+ MCPS_MULTICAST,
+ /*!
+ * Proprietary frame
+ */
+ MCPS_PROPRIETARY,
+}Mcps_t;
+
+/*!
+ * LoRaMAC MCPS-Request for an unconfirmed frame
+ */
+typedef struct sMcpsReqUnconfirmed
+{
+ /*!
+ * 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.2, chapter 4.3.2
+ */
+ uint8_t fPort;
+ /*!
+ * Pointer to the buffer of the frame payload
+ */
+ void* fBuffer;
+ /*!
+ * Size of the frame payload
+ */
+ uint16_t fBufferSize;
+ /*!
+ * Uplink datarate, if ADR is off
+ */
+ int8_t Datarate;
+}McpsReqUnconfirmed_t;
+
+/*!
+ * LoRaMAC MCPS-Request for a confirmed frame
+ */
+typedef struct sMcpsReqConfirmed
+{
+ /*!
+ * 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.2, chapter 4.3.2
+ */
+ uint8_t fPort;
+ /*!
+ * Pointer to the buffer of the frame payload
+ */
+ void* fBuffer;
+ /*!
+ * Size of the frame payload
+ */
+ uint16_t fBufferSize;
+ /*!
+ * Uplink datarate, if ADR is off
+ */
+ int8_t Datarate;
+ /*!
+ * 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.2, chapter 18.4, according
+ * to the following table:
+ *
+ * Transmission nb | Data Rate
+ * ----------------|-----------
+ * 1 (first) | DR
+ * 2 | DR
+ * 3 | max(DR-1,0)
+ * 4 | max(DR-1,0)
+ * 5 | max(DR-2,0)
+ * 6 | max(DR-2,0)
+ * 7 | max(DR-3,0)
+ * 8 | max(DR-3,0)
+ *
+ * Note, that if NbTrials is set to 1 or 2, the MAC will not decrease
+ * the datarate, in case the LoRaMAC layer did not receive an acknowledgment
+ */
+ uint8_t NbTrials;
+}McpsReqConfirmed_t;
+
+/*!
+ * LoRaMAC MCPS-Request for a proprietary frame
+ */
+typedef struct sMcpsReqProprietary
+{
+ /*!
+ * Pointer to the buffer of the frame payload
+ */
+ void* fBuffer;
+ /*!
+ * Size of the frame payload
+ */
+ uint16_t fBufferSize;
+ /*!
+ * Uplink datarate, if ADR is off
+ */
+ int8_t Datarate;
+}McpsReqProprietary_t;
+
+/*!
+ * LoRaMAC MCPS-Request structure
+ */
+typedef struct sMcpsReq
+{
+ /*!
+ * MCPS-Request type
+ */
+ Mcps_t Type;
+
+ /*!
+ * MCPS-Request parameters
+ */
+ union uMcpsParam
+ {
+ /*!
+ * MCPS-Request parameters for an unconfirmed frame
+ */
+ McpsReqUnconfirmed_t Unconfirmed;
+ /*!
+ * MCPS-Request parameters for a confirmed frame
+ */
+ McpsReqConfirmed_t Confirmed;
+ /*!
+ * MCPS-Request parameters for a proprietary frame
+ */
+ McpsReqProprietary_t Proprietary;
+ }Req;
+}McpsReq_t;
+
+/*!
+ * LoRaMAC MCPS-Confirm
+ */
+typedef struct sMcpsConfirm
+{
+ /*!
+ * Holds the previously performed MCPS-Request
+ */
+ Mcps_t McpsRequest;
+ /*!
+ * Status of the operation
+ */
+ LoRaMacEventInfoStatus_t Status;
+ /*!
+ * Uplink datarate
+ */
+ uint8_t Datarate;
+ /*!
+ * Transmission power
+ */
+ int8_t TxPower;
+ /*!
+ * Set if an acknowledgement was received
+ */
+ bool AckReceived;
+ /*!
+ * Provides the number of retransmissions
+ */
+ uint8_t NbRetries;
+ /*!
+ * The transmission time on air of the frame
+ */
+ TimerTime_t TxTimeOnAir;
+ /*!
+ * The uplink counter value related to the frame
+ */
+ uint32_t UpLinkCounter;
+ /*!
+ * The uplink channel related to the frame
+ */
+ uint32_t Channel;
+}McpsConfirm_t;
+
+/*!
+ * LoRaMAC MCPS-Indication primitive
+ */
+typedef struct sMcpsIndication
+{
+ /*!
+ * MCPS-Indication type
+ */
+ Mcps_t McpsIndication;
+ /*!
+ * Status of the operation
+ */
+ LoRaMacEventInfoStatus_t Status;
+ /*!
+ * Multicast
+ */
+ uint8_t Multicast;
+ /*!
+ * Application port
+ */
+ uint8_t Port;
+ /*!
+ * Downlink datarate
+ */
+ uint8_t RxDatarate;
+ /*!
+ * Frame pending status
+ */
+ uint8_t FramePending;
+ /*!
+ * Pointer to the received data stream
+ */
+ uint8_t* Buffer;
+ /*!
+ * Size of the received data stream
+ */
+ uint8_t BufferSize;
+ /*!
+ * Indicates, if data is available
+ */
+ bool RxData;
+ /*!
+ * Rssi of the received packet
+ */
+ int16_t Rssi;
+ /*!
+ * Snr of the received packet
+ */
+ int8_t Snr;
+ /*!
+ * Receive window
+ */
+ LoRaMacRxSlot_t RxSlot;
+ /*!
+ * Set if an acknowledgement was received
+ */
+ bool AckReceived;
+ /*!
+ * The downlink counter value for the received frame
+ */
+ uint32_t DownLinkCounter;
+ /*!
+ * The device address of the frame
+ */
+ uint32_t DevAddress;
+ /*!
+ * Set if a DeviceTimeAns MAC command was received.
+ */
+ bool DeviceTimeAnsReceived;
+}McpsIndication_t;
+
+/*!
+ * \brief LoRaMAC management services
+ *
+ * \details The following table list the primitives which are supported by the
+ * specific MAC management service:
+ *
+ * Name | Request | Indication | Response | Confirm
+ * ---------------------------- | :-----: | :--------: | :------: | :-----:
+ * \ref MLME_JOIN | YES | NO | NO | YES
+ * \ref MLME_LINK_CHECK | YES | NO | NO | YES
+ * \ref MLME_TXCW | YES | NO | NO | YES
+ * \ref MLME_SCHEDULE_UPLINK | NO | YES | NO | NO
+ * \ref MLME_DERIVE_MC_KE_KEY | YES | NO | NO | YES
+ * \ref MLME_DERIVE_MC_KEY_PAIR | YES | NO | NO | YES
+ *
+ * The following table provides links to the function implementations of the
+ * related MLME primitives.
+ *
+ * Primitive | Function
+ * ---------------- | :---------------------:
+ * MLME-Request | \ref LoRaMacMlmeRequest
+ * MLME-Confirm | MacMlmeConfirm in \ref LoRaMacPrimitives_t
+ * MLME-Indication | MacMlmeIndication in \ref LoRaMacPrimitives_t
+ */
+typedef enum eMlme
+{
+ /*!
+ * Initiates the Over-the-Air activation
+ *
+ * LoRaWAN Specification V1.0.2, chapter 6.2
+ */
+ MLME_JOIN,
+ /*!
+ * Initiates sending a ReJoin-request type 0
+ *
+ * LoRaWAN Specification V1.1.0, chapter 6.2.4.1
+ */
+ MLME_REJOIN_0,
+ /*!
+ * Initiates sending a ReJoin-request type 1
+ *
+ * LoRaWAN Specification V1.1.0, chapter 6.2.4.2
+ */
+ MLME_REJOIN_1,
+ /*!
+ * LinkCheckReq - Connectivity validation
+ *
+ * LoRaWAN Specification V1.0.2, chapter 5, table 4
+ */
+ MLME_LINK_CHECK,
+ /*!
+ * Sets Tx continuous wave mode
+ *
+ * LoRaWAN end-device certification
+ */
+ MLME_TXCW,
+ /*!
+ * Sets Tx continuous wave mode (new LoRa-Alliance CC definition)
+ *
+ * LoRaWAN end-device certification
+ */
+ MLME_TXCW_1,
+ /*!
+ * Indicates that the application shall perform an uplink as
+ * soon as possible.
+ */
+ MLME_SCHEDULE_UPLINK,
+ /*!
+ * Derives the McKEKey from the AppKey or NwkKey.
+ */
+ MLME_DERIVE_MC_KE_KEY,
+ /*!
+ * Derives a Multicast group key pair ( McAppSKey, McNwkSKey ) from McKey
+ */
+ MLME_DERIVE_MC_KEY_PAIR,
+ /*!
+ * Initiates a DeviceTimeReq
+ *
+ * LoRaWAN end-device certification
+ */
+ MLME_DEVICE_TIME,
+ /*!
+ * The MAC uses this MLME primitive to indicate a beacon reception
+ * status.
+ *
+ * LoRaWAN end-device certification
+ */
+ MLME_BEACON,
+ /*!
+ * Initiate a beacon acquisition. The MAC will search for a beacon.
+ * It will search for XX_BEACON_INTERVAL milliseconds.
+ *
+ * LoRaWAN end-device certification
+ */
+ MLME_BEACON_ACQUISITION,
+ /*!
+ * Initiates a PingSlotInfoReq
+ *
+ * LoRaWAN end-device certification
+ */
+ MLME_PING_SLOT_INFO,
+ /*!
+ * Initiates a BeaconTimingReq
+ *
+ * LoRaWAN end-device certification
+ */
+ MLME_BEACON_TIMING,
+ /*!
+ * Primitive which indicates that the beacon has been lost
+ *
+ * \remark The upper layer is required to switch the device class to ClassA
+ *
+ * LoRaWAN end-device certification
+ */
+ MLME_BEACON_LOST,
+}Mlme_t;
+
+/*!
+ * LoRaMAC MLME-Request for the join service
+ */
+typedef struct sMlmeReqJoin
+{
+ /*!
+ * Datarate used for join request.
+ */
+ uint8_t Datarate;
+}MlmeReqJoin_t;
+
+/*!
+ * LoRaMAC MLME-Request for Tx continuous wave mode
+ */
+typedef struct sMlmeReqTxCw
+{
+ /*!
+ * Time in seconds while the radio is kept in continuous wave mode
+ */
+ uint16_t Timeout;
+ /*!
+ * RF frequency to set (Only used with new way)
+ */
+ uint32_t Frequency;
+ /*!
+ * RF output power to set (Only used with new way)
+ */
+ uint8_t Power;
+}MlmeReqTxCw_t;
+
+/*!
+ * LoRaMAC MLME-Request for the ping slot info service
+ */
+typedef struct sMlmeReqPingSlotInfo
+{
+ PingSlotInfo_t PingSlot;
+}MlmeReqPingSlotInfo_t;
+
+/*!
+ * LoRaMAC MLME-Request to derive the McKEKey from the AppKey or NwkKey
+ */
+typedef struct sMlmeReqDeriveMcKEKey
+{
+ /*!
+ * Key identifier of the root key to use to perform the derivation ( NwkKey or AppKey )
+ */
+ KeyIdentifier_t KeyID;
+ /*!
+ * Nonce value ( nonce <= 15)
+ */
+ uint16_t Nonce;
+ /*!
+ * DevEUI Value
+ */
+ uint8_t* DevEUI;
+}MlmeReqDeriveMcKEKey_t;
+
+/*!
+ * LoRaMAC MLME-Request to derive a Multicast group key pair ( McAppSKey, McNwkSKey ) from McKey
+ */
+typedef struct sMlmeReqDeriveMcSessionKeyPair
+{
+ /*!
+ * Address identifier to select the multicast group
+ */
+ AddressIdentifier_t GroupID;
+}MlmeReqDeriveMcSessionKeyPair_t;
+
+/*!
+ * LoRaMAC MLME-Request structure
+ */
+typedef struct sMlmeReq
+{
+ /*!
+ * MLME-Request type
+ */
+ Mlme_t Type;
+
+ /*!
+ * MLME-Request parameters
+ */
+ union uMlmeParam
+ {
+ /*!
+ * MLME-Request parameters for a join request
+ */
+ MlmeReqJoin_t Join;
+ /*!
+ * MLME-Request parameters for Tx continuous mode request
+ */
+ MlmeReqTxCw_t TxCw;
+ /*!
+ * MLME-Request parameters for a ping slot info request
+ */
+ MlmeReqPingSlotInfo_t PingSlotInfo;
+ /*!
+ * MLME-Request to derive the McKEKey from the AppKey or NwkKey
+ */
+ MlmeReqDeriveMcKEKey_t DeriveMcKEKey;
+ /*!
+ * MLME-Request to derive a Multicast group key pair ( McAppSKey, McNwkSKey ) from McKey
+ */
+ MlmeReqDeriveMcSessionKeyPair_t DeriveMcSessionKeyPair;
+ }Req;
+}MlmeReq_t;
+
+/*!
+ * LoRaMAC MLME-Confirm primitive
+ */
+typedef struct sMlmeConfirm
+{
+ /*!
+ * Holds the previously performed MLME-Request
+ */
+ Mlme_t MlmeRequest;
+ /*!
+ * Status of the operation
+ */
+ LoRaMacEventInfoStatus_t Status;
+ /*!
+ * The transmission time on air of the frame
+ */
+ TimerTime_t TxTimeOnAir;
+ /*!
+ * Demodulation margin. Contains the link margin [dB] of the last
+ * successfully received LinkCheckReq
+ */
+ uint8_t DemodMargin;
+ /*!
+ * Number of gateways which received the last LinkCheckReq
+ */
+ uint8_t NbGateways;
+ /*!
+ * Provides the number of retransmissions
+ */
+ uint8_t NbRetries;
+ /*!
+ * The delay which we have received through the
+ * BeaconTimingAns
+ */
+ TimerTime_t BeaconTimingDelay;
+ /*!
+ * The channel of the next beacon
+ */
+ uint8_t BeaconTimingChannel;
+}MlmeConfirm_t;
+
+/*!
+ * LoRaMAC MLME-Indication primitive
+ */
+typedef struct sMlmeIndication
+{
+ /*!
+ * MLME-Indication type
+ */
+ Mlme_t MlmeIndication;
+ /*!
+ * Status of the operation
+ */
+ LoRaMacEventInfoStatus_t Status;
+ /*!
+ * Beacon information. Only valid for \ref MLME_BEACON,
+ * status \ref LORAMAC_EVENT_INFO_STATUS_BEACON_LOCKED
+ */
+ 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_ACTIVATION | YES | YES
+ * \ref MIB_DEV_EUI | YES | YES
+ * \ref MIB_JOIN_EUI | YES | YES
+ * \ref MIB_ADR | YES | YES
+ * \ref MIB_NET_ID | YES | YES
+ * \ref MIB_DEV_ADDR | YES | YES
+ * \ref MIB_GEN_APP_KEY | NO | YES
+ * \ref MIB_APP_KEY | NO | YES
+ * \ref MIB_NWK_KEY | NO | YES
+ * \ref MIB_J_S_INT_KEY | NO | YES
+ * \ref MIB_J_S_ENC_KEY | NO | YES
+ * \ref MIB_F_NWK_S_INT_KEY | NO | YES
+ * \ref MIB_S_NWK_S_INT_KEY | NO | YES
+ * \ref MIB_NWK_S_ENC_KEY | NO | YES
+ * \ref MIB_APP_S_KEY | NO | YES
+ * \ref MIB_MC_KE_KEY | NO | YES
+ * \ref MIB_MC_KEY_0 | NO | YES
+ * \ref MIB_MC_APP_S_KEY_0 | NO | YES
+ * \ref MIB_MC_NWK_S_KEY_0 | NO | YES
+ * \ref MIB_MC_KEY_1 | NO | YES
+ * \ref MIB_MC_APP_S_KEY_1 | NO | YES
+ * \ref MIB_MC_NWK_S_KEY_1 | NO | YES
+ * \ref MIB_MC_KEY_2 | NO | YES
+ * \ref MIB_MC_APP_S_KEY_2 | NO | YES
+ * \ref MIB_MC_NWK_S_KEY_2 | NO | YES
+ * \ref MIB_MC_KEY_3 | NO | YES
+ * \ref MIB_MC_APP_S_KEY_3 | NO | YES
+ * \ref MIB_MC_NWK_S_KEY_3 | NO | 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_RX2_DFAULT_CHANNEL | YES | YES
+ * \ref MIB_RXC_CHANNEL | YES | YES
+ * \ref MIB_RXC_DFAULT_CHANNEL | YES | YES
+ * \ref MIB_CHANNELS_MASK | YES | YES
+ * \ref MIB_CHANNELS_DEFAULT_MASK | YES | YES
+ * \ref MIB_CHANNELS_NB_TRANS | 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_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
+ * \ref MIB_DEFAULT_ANTENNA_GAIN | YES | YES
+ * \ref MIB_NVM_CTXS | YES | YES
+ * \ref MIB_ABP_LORAWAN_VERSION | YES | YES
+ *
+ * The following table provides links to the function implementations of the
+ * related MIB primitives:
+ *
+ * Primitive | Function
+ * ---------------- | :---------------------:
+ * MIB-Set | \ref LoRaMacMibSetRequestConfirm
+ * MIB-Get | \ref LoRaMacMibGetRequestConfirm
+ */
+typedef enum eMib
+{
+ /*!
+ * LoRaWAN device class
+ *
+ * LoRaWAN Specification V1.0.2
+ */
+ MIB_DEVICE_CLASS,
+ /*!
+ * LoRaWAN Network End-Device Activation
+ *
+ * LoRaWAN Specification V1.0.2
+ */
+ MIB_NETWORK_ACTIVATION,
+ /*!
+ * LoRaWAN device EUI
+ *
+ * LoRaWAN Specification V1.0.2
+ */
+ MIB_DEV_EUI,
+ /*!
+ * LoRaWAN join EUI
+ *
+ * LoRaWAN Specification V1.0.2
+ */
+ MIB_JOIN_EUI,
+ /*!
+ * Adaptive data rate
+ *
+ * LoRaWAN Specification V1.0.2, chapter 4.3.1.1
+ *
+ * [true: ADR enabled, false: ADR disabled]
+ */
+ MIB_ADR,
+ /*!
+ * Network identifier
+ *
+ * LoRaWAN Specification V1.0.2, chapter 6.1.1
+ */
+ MIB_NET_ID,
+ /*!
+ * End-device address
+ *
+ * LoRaWAN Specification V1.0.2, chapter 6.1.1
+ */
+ MIB_DEV_ADDR,
+ /*!
+ * Application root key - 1.0.x devices only.
+ *
+ * LoRaWAN Remote Multicast Setup v1.0.0 Specification, chapter 4.3
+ */
+ MIB_GEN_APP_KEY,
+ /*!
+ * Application root key
+ *
+ * LoRaWAN Specification V1.1.0, chapter 6.1.1.3
+ */
+ MIB_APP_KEY,
+ /*!
+ * Network root key
+ *
+ * LoRaWAN Specification V1.1.0, chapter 6.1.1.3
+ */
+ MIB_NWK_KEY,
+ /*!
+ * Join session integrity key
+ *
+ * LoRaWAN Specification V1.1.0, chapter 6.1.1.4
+ */
+ MIB_J_S_INT_KEY,
+ /*!
+ * Join session encryption key
+ *
+ * LoRaWAN Specification V1.1.0, chapter 6.1.1.4
+ */
+ MIB_J_S_ENC_KEY,
+ /*!
+ * Forwarding Network session integrity key
+ *
+ * LoRaWAN Specification V1.1.0, chapter 6.1.2.2
+ */
+ MIB_F_NWK_S_INT_KEY,
+ /*!
+ * Serving Network session integrity key
+ *
+ * LoRaWAN Specification V1.1.0, chapter 6.1.2.3
+ */
+ MIB_S_NWK_S_INT_KEY,
+ /*!
+ * Network session encryption key
+ *
+ * LoRaWAN Specification V1.1.0, chapter 6.1.2.4
+ */
+ MIB_NWK_S_ENC_KEY,
+ /*!
+ * Application session key
+ *
+ * LoRaWAN Specification V1.1.0, chapter 6.1.1.3
+ */
+ MIB_APP_S_KEY,
+ /*!
+ * Multicast key encryption key
+ *
+ * LoRaWAN - Secure element specification v1
+ */
+ MIB_MC_KE_KEY,
+ /*!
+ * Multicast root key index 0
+ *
+ * LoRaWAN - Secure element specification v1
+ */
+ MIB_MC_KEY_0,
+ /*!
+ * Multicast Application session key index 0
+ *
+ * LoRaWAN - Secure element specification v1
+ */
+ MIB_MC_APP_S_KEY_0,
+ /*!
+ * Multicast Network session key index 0
+ *
+ * LoRaWAN - Secure element specification v1
+ */
+ MIB_MC_NWK_S_KEY_0,
+ /*!
+ * Multicast root key index 1
+ *
+ * LoRaWAN - Secure element specification v1
+ */
+ MIB_MC_KEY_1,
+ /*!
+ * Multicast Application session key index 1
+ *
+ * LoRaWAN - Secure element specification v1
+ */
+ MIB_MC_APP_S_KEY_1,
+ /*!
+ * Multicast Network session key index 1
+ *
+ * LoRaWAN - Secure element specification v1
+ */
+ MIB_MC_NWK_S_KEY_1,
+ /*!
+ * Multicast root key index 2
+ *
+ * LoRaWAN - Secure element specification v1
+ */
+ MIB_MC_KEY_2,
+ /*!
+ * Multicast Application session key index 2
+ *
+ * LoRaWAN - Secure element specification v1
+ */
+ MIB_MC_APP_S_KEY_2,
+ /*!
+ * Multicast Network session key index 2
+ *
+ * LoRaWAN - Secure element specification v1
+ */
+ MIB_MC_NWK_S_KEY_2,
+ /*!
+ * Multicast root key index 3
+ *
+ * LoRaWAN - Secure element specification v1
+ */
+ MIB_MC_KEY_3,
+ /*!
+ * Multicast Application session key index 3
+ *
+ * LoRaWAN - Secure element specification v1
+ */
+ MIB_MC_APP_S_KEY_3,
+ /*!
+ * Multicast Network session key index 3
+ *
+ * LoRaWAN - Secure element specification v1
+ */
+ MIB_MC_NWK_S_KEY_3,
+ /*!
+ * Set the network type to public or private
+ *
+ * LoRaWAN Regional Parameters V1.0.2rB
+ *
+ * [true: public network, false: private network]
+ */
+ MIB_PUBLIC_NETWORK,
+ /*!
+ * Support the operation with repeaters
+ *
+ * LoRaWAN Regional Parameters V1.0.2rB
+ *
+ * [true: repeater support enabled, false: repeater support disabled]
+ */
+ MIB_REPEATER_SUPPORT,
+ /*!
+ * Communication channels. A get request will return a
+ * pointer which references the first entry of the channel list. The
+ * list is of size LORA_MAX_NB_CHANNELS
+ *
+ * LoRaWAN Regional Parameters V1.0.2rB
+ */
+ MIB_CHANNELS,
+ /*!
+ * Set receive window 2 channel
+ *
+ * LoRaWAN Specification V1.0.2, chapter 3.3.1
+ */
+ MIB_RX2_CHANNEL,
+ /*!
+ * Set receive window 2 channel
+ *
+ * LoRaWAN Specification V1.0.2, chapter 3.3.2
+ */
+ MIB_RX2_DEFAULT_CHANNEL,
+ /*!
+ * Set receive window C channel
+ *
+ * LoRaWAN Specification V1.0.2, chapter 3.3.1
+ */
+ MIB_RXC_CHANNEL,
+ /*!
+ * Set receive window C channel
+ *
+ * LoRaWAN Specification V1.0.2, chapter 3.3.2
+ */
+ MIB_RXC_DEFAULT_CHANNEL,
+ /*!
+ * LoRaWAN channels mask
+ *
+ * LoRaWAN Regional Parameters V1.0.2rB
+ */
+ MIB_CHANNELS_MASK,
+ /*!
+ * LoRaWAN default channels mask
+ *
+ * LoRaWAN Regional Parameters V1.0.2rB
+ */
+ MIB_CHANNELS_DEFAULT_MASK,
+ /*!
+ * Set the number of repetitions on a channel
+ *
+ * LoRaWAN Specification V1.0.2, chapter 5.2
+ */
+ MIB_CHANNELS_NB_TRANS,
+ /*!
+ * Maximum receive window duration in [ms]
+ *
+ * LoRaWAN Specification V1.0.2, chapter 3.3.3
+ */
+ MIB_MAX_RX_WINDOW_DURATION,
+ /*!
+ * Receive delay 1 in [ms]
+ *
+ * LoRaWAN Regional Parameters V1.0.2rB
+ */
+ MIB_RECEIVE_DELAY_1,
+ /*!
+ * Receive delay 2 in [ms]
+ *
+ * LoRaWAN Regional Parameters V1.0.2rB
+ */
+ MIB_RECEIVE_DELAY_2,
+ /*!
+ * Join accept delay 1 in [ms]
+ *
+ * LoRaWAN Regional Parameters V1.0.2rB
+ */
+ MIB_JOIN_ACCEPT_DELAY_1,
+ /*!
+ * Join accept delay 2 in [ms]
+ *
+ * LoRaWAN Regional Parameters V1.0.2rB
+ */
+ MIB_JOIN_ACCEPT_DELAY_2,
+ /*!
+ * Default Data rate of a channel
+ *
+ * LoRaWAN Regional Parameters V1.0.2rB
+ *
+ * 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 Regional Parameters V1.0.2rB
+ *
+ * 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 Regional Parameters V1.0.2rB
+ *
+ * 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 Regional Parameters V1.0.2rB
+ *
+ * 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,
+ /*!
+ * System overall timing error in milliseconds.
+ * [-SystemMaxRxError : +SystemMaxRxError]
+ * Default: +/-10 ms
+ */
+ MIB_SYSTEM_MAX_RX_ERROR,
+ /*!
+ * Minimum required number of symbols to detect an Rx frame
+ * 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 )
+ *
+ * \remark The antenna gain value is referenced to the isotropic antenna.
+ * The value is in dBi.
+ * MIB_ANTENNA_GAIN[dBi] = measuredAntennaGain[dBd] + 2.15
+ */
+ MIB_ANTENNA_GAIN,
+ /*!
+ * Default 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 )
+ *
+ * \remark The antenna gain value is referenced to the isotropic antenna.
+ * The value is in dBi.
+ * MIB_DEFAULT_ANTENNA_GAIN[dBi] = measuredAntennaGain[dBd] + 2.15
+ */
+ MIB_DEFAULT_ANTENNA_GAIN,
+ /*!
+ * Structure holding pointers to internal contexts and its size
+ */
+ MIB_NVM_CTXS,
+ /*!
+ * LoRaWAN MAC layer operating version when activated by ABP.
+ */
+ MIB_ABP_LORAWAN_VERSION,
+ /*!
+ * 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,
+ /*!
+ * Ping slot data rate
+ *
+ * LoRaWAN Regional Parameters V1.0.2rB
+ *
+ * The allowed ranges are region specific. Please refer to \ref DR_0 to \ref DR_15 for details.
+ */
+ MIB_PING_SLOT_DATARATE,
+}Mib_t;
+
+/*!
+ * LoRaMAC MIB parameters
+ */
+typedef union uMibParam
+{
+ /*!
+ * LoRaWAN device class
+ *
+ * Related MIB type: \ref MIB_DEVICE_CLASS
+ */
+ DeviceClass_t Class;
+ /*!
+ * LoRaWAN Network End-Device Activation ( ACTIVATION_TYPE_NONE, ACTIVATION_TYPE_ABP or OTTA )
+ *
+ * Related MIB type: \ref MIB_NETWORK_ACTIVATION
+ */
+ ActivationType_t NetworkActivation;
+ /*!
+ * LoRaWAN device class
+ *
+ * Related MIB type: \ref MIB_DEV_EUI
+ */
+ uint8_t* DevEui;
+ /*!
+ * LoRaWAN device class
+ *
+ * Related MIB type: \ref MIB_JOIN_EUI
+ */
+ uint8_t* JoinEui;
+ /*!
+ * Activation state of ADR
+ *
+ * Related MIB type: \ref MIB_ADR
+ */
+ bool AdrEnable;
+ /*!
+ * Network identifier
+ *
+ * Related MIB type: \ref MIB_NET_ID
+ */
+ uint32_t NetID;
+ /*!
+ * End-device address
+ *
+ * Related MIB type: \ref MIB_DEV_ADDR
+ */
+ uint32_t DevAddr;
+ /*!
+ * Application root key - 1.0.x device only
+ *
+ * Related MIB type: \ref MIB_GEN_APP_KEY
+ */
+ uint8_t* GenAppKey;
+ /*!
+ * Application root key
+ *
+ * Related MIB type: \ref MIB_APP_KEY
+ */
+ uint8_t* AppKey;
+ /*!
+ * Network root key
+ *
+ * Related MIB type: \ref MIB_NWK_KEY
+ */
+ uint8_t* NwkKey;
+ /*!
+ * Join session integrity key
+ *
+ * Related MIB type: \ref MIB_J_S_INT_KEY
+ */
+ uint8_t* JSIntKey;
+ /*!
+ * Join session encryption key
+ *
+ * Related MIB type: \ref MIB_J_S_ENC_KEY
+ */
+ uint8_t* JSEncKey;
+ /*!
+ * Forwarding Network session integrity key
+ *
+ * Related MIB type: \ref MIB_F_NWK_S_INT_KEY
+ */
+ uint8_t* FNwkSIntKey;
+ /*!
+ * Serving Network session integrity key
+ *
+ * Related MIB type: \ref MIB_S_NWK_S_INT_KEY
+ */
+ uint8_t* SNwkSIntKey;
+ /*!
+ * Network session encryption key
+ *
+ * Related MIB type: \ref MIB_NWK_S_ENC_KEY
+ */
+ uint8_t* NwkSEncKey;
+ /*!
+ * Application session key
+ *
+ * Related MIB type: \ref MIB_APP_S_KEY
+ */
+ uint8_t* AppSKey;
+ /*!
+ * Multicast key encryption key
+ *
+ * Related MIB type: \ref MIB_MC_KE_KEY
+ */
+ uint8_t* McKEKey;
+ /*!
+ * Multicast root key index 0
+ *
+ * Related MIB type: \ref MIB_MC_KEY_0
+ */
+ uint8_t* McKey0;
+ /*!
+ * Multicast Application session key index 0
+ *
+ * Related MIB type: \ref MIB_MC_APP_S_KEY_0
+ */
+ uint8_t* McAppSKey0;
+ /*!
+ * Multicast Network session key index 0
+ *
+ * Related MIB type: \ref MIB_MC_NWK_S_KEY_0
+ */
+ uint8_t* McNwkSKey0;
+ /*!
+ * Multicast root key index 0
+ *
+ * Related MIB type: \ref MIB_MC_KEY_0
+ */
+ uint8_t* McKey1;
+ /*!
+ * Multicast Application session key index 1
+ *
+ * Related MIB type: \ref MIB_MC_APP_S_KEY_1
+ */
+ uint8_t* McAppSKey1;
+ /*!
+ * Multicast Network session key index 1
+ *
+ * Related MIB type: \ref MIB_MC_NWK_S_KEY_1
+ */
+ uint8_t* McNwkSKey1;
+ /*!
+ * Multicast root key index 2
+ *
+ * Related MIB type: \ref MIB_MC_KEY_2
+ */
+ uint8_t* McKey2;
+ /*!
+ * Multicast Application session key index 2
+ *
+ * Related MIB type: \ref MIB_MC_APP_S_KEY_2
+ */
+ uint8_t* McAppSKey2;
+ /*!
+ * Multicast Network session key index 2
+ *
+ * Related MIB type: \ref MIB_MC_NWK_S_KEY_2
+ */
+ uint8_t* McNwkSKey2;
+ /*!
+ * Multicast root key index 2
+ *
+ * Related MIB type: \ref MIB_MC_KEY_2
+ */
+ uint8_t* McKey3;
+ /*!
+ * Multicast Application session key index 2
+ *
+ * Related MIB type: \ref MIB_MC_APP_S_KEY_2
+ */
+ uint8_t* McAppSKey3;
+ /*!
+ * Multicast Network session key index 2
+ *
+ * Related MIB type: \ref MIB_MC_NWK_S_KEY_2
+ */
+ uint8_t* McNwkSKey3;
+ /*!
+ * Enable or disable a public network
+ *
+ * Related MIB type: \ref MIB_PUBLIC_NETWORK
+ */
+ bool EnablePublicNetwork;
+ /*!
+ * Enable or disable repeater support
+ *
+ * Related MIB type: \ref MIB_REPEATER_SUPPORT
+ */
+ bool EnableRepeaterSupport;
+ /*!
+ * LoRaWAN Channel
+ *
+ * Related MIB type: \ref MIB_CHANNELS
+ */
+ ChannelParams_t* ChannelList;
+ /*!
+ * Channel for the receive window 2
+ *
+ * Related MIB type: \ref MIB_RX2_CHANNEL
+ */
+ RxChannelParams_t Rx2Channel;
+ /*!
+ * Channel for the receive window 2
+ *
+ * Related MIB type: \ref MIB_RX2_DEFAULT_CHANNEL
+ */
+ RxChannelParams_t Rx2DefaultChannel;
+ /*!
+ * Channel for the receive window C
+ *
+ * Related MIB type: \ref MIB_RXC_CHANNEL
+ */
+ RxChannelParams_t RxCChannel;
+ /*!
+ * Channel for the receive window C
+ *
+ * Related MIB type: \ref MIB_RXC_DEFAULT_CHANNEL
+ */
+ RxChannelParams_t RxCDefaultChannel;
+ /*!
+ * Channel mask
+ *
+ * Related MIB type: \ref MIB_CHANNELS_MASK
+ */
+ uint16_t* ChannelsMask;
+ /*!
+ * Default channel mask
+ *
+ * Related MIB type: \ref MIB_CHANNELS_DEFAULT_MASK
+ */
+ uint16_t* ChannelsDefaultMask;
+ /*!
+ * Number of frame repetitions
+ *
+ * Related MIB type: \ref MIB_CHANNELS_NB_TRANS
+ */
+ uint8_t ChannelsNbTrans;
+ /*!
+ * Maximum receive window duration
+ *
+ * Related MIB type: \ref MIB_MAX_RX_WINDOW_DURATION
+ */
+ uint32_t MaxRxWindow;
+ /*!
+ * Receive delay 1
+ *
+ * Related MIB type: \ref MIB_RECEIVE_DELAY_1
+ */
+ uint32_t ReceiveDelay1;
+ /*!
+ * Receive delay 2
+ *
+ * Related MIB type: \ref MIB_RECEIVE_DELAY_2
+ */
+ uint32_t ReceiveDelay2;
+ /*!
+ * Join accept delay 1
+ *
+ * Related MIB type: \ref MIB_JOIN_ACCEPT_DELAY_1
+ */
+ uint32_t JoinAcceptDelay1;
+ /*!
+ * Join accept delay 2
+ *
+ * Related MIB type: \ref MIB_JOIN_ACCEPT_DELAY_2
+ */
+ uint32_t JoinAcceptDelay2;
+ /*!
+ * Channels data rate
+ *
+ * Related MIB type: \ref MIB_CHANNELS_DEFAULT_DATARATE
+ */
+ int8_t ChannelsDefaultDatarate;
+ /*!
+ * Channels data rate
+ *
+ * Related MIB type: \ref MIB_CHANNELS_DATARATE
+ */
+ int8_t ChannelsDatarate;
+ /*!
+ * Channels TX power
+ *
+ * Related MIB type: \ref MIB_CHANNELS_DEFAULT_TX_POWER
+ */
+ int8_t ChannelsDefaultTxPower;
+ /*!
+ * Channels TX power
+ *
+ * Related MIB type: \ref MIB_CHANNELS_TX_POWER
+ */
+ int8_t ChannelsTxPower;
+ /*!
+ * Multicast channels
+ *
+ * Related MIB type: \ref MIB_MULTICAST_CHANNEL
+ */
+ McChannelParams_t MulticastChannel;
+ /*!
+ * System overall timing error in milliseconds.
+ *
+ * Related MIB type: \ref MIB_SYSTEM_MAX_RX_ERROR
+ */
+ uint32_t SystemMaxRxError;
+ /*!
+ * Minimum required number of symbols to detect an Rx frame
+ *
+ * Related MIB type: \ref MIB_MIN_RX_SYMBOLS
+ */
+ uint8_t MinRxSymbols;
+ /*!
+ * Antenna gain
+ *
+ * Related MIB type: \ref MIB_ANTENNA_GAIN
+ */
+ float AntennaGain;
+ /*!
+ * Default antenna gain
+ *
+ * Related MIB type: \ref MIB_DEFAULT_ANTENNA_GAIN
+ */
+ float DefaultAntennaGain;
+ /*!
+ * Structure holding pointers to internal non-volatile contexts and its lengths.
+ *
+ * Related MIB type: \ref MIB_NVM_CTXS
+ */
+ LoRaMacCtxs_t* Contexts;
+ /*
+ * LoRaWAN MAC layer operating version when activated by ABP.
+ *
+ * Related MIB type: \ref MIB_ABP_LORAWAN_VERSION
+ */
+ Version_t AbpLrWanVersion;
+ /*!
+ * 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;
+ /*!
+ * Ping slots data rate
+ *
+ * Related MIB type: \ref MIB_PING_SLOT_DATARATE
+ */
+ int8_t PingSlotDatarate;
+}MibParam_t;
+
+/*!
+ * LoRaMAC MIB-RequestConfirm structure
+ */
+typedef struct eMibRequestConfirm
+{
+ /*!
+ * MIB-Request type
+ */
+ Mib_t Type;
+
+ /*!
+ * MLME-RequestConfirm parameters
+ */
+ MibParam_t Param;
+}MibRequestConfirm_t;
+
+/*!
+ * LoRaMAC tx information
+ */
+typedef struct sLoRaMacTxInfo
+{
+ /*!
+ * Size of the application data payload which can be transmitted.
+ */
+ uint8_t MaxPossibleApplicationDataSize;
+ /*!
+ * The current maximum possible payload size without MAC commands
+ * which is dependent on the current datarate.
+ */
+ uint8_t CurrentPossiblePayloadSize;
+}LoRaMacTxInfo_t;
+
+/*!
+ * LoRaMAC Status
+ */
+typedef enum eLoRaMacStatus
+{
+ /*!
+ * Service started successfully
+ */
+ LORAMAC_STATUS_OK,
+ /*!
+ * Service not started - LoRaMAC is busy
+ */
+ LORAMAC_STATUS_BUSY,
+ /*!
+ * Service unknown
+ */
+ LORAMAC_STATUS_SERVICE_UNKNOWN,
+ /*!
+ * Service not started - invalid parameter
+ */
+ LORAMAC_STATUS_PARAMETER_INVALID,
+ /*!
+ * Service not started - invalid frequency
+ */
+ LORAMAC_STATUS_FREQUENCY_INVALID,
+ /*!
+ * Service not started - invalid datarate
+ */
+ LORAMAC_STATUS_DATARATE_INVALID,
+ /*!
+ * Service not started - invalid frequency and datarate
+ */
+ LORAMAC_STATUS_FREQ_AND_DR_INVALID,
+ /*!
+ * Service not started - the device is not in a LoRaWAN
+ */
+ LORAMAC_STATUS_NO_NETWORK_JOINED,
+ /*!
+ * Service not started - payload length error
+ */
+ LORAMAC_STATUS_LENGTH_ERROR,
+ /*!
+ * Service not started - the specified region is not supported
+ * or not activated with preprocessor definitions.
+ */
+ LORAMAC_STATUS_REGION_NOT_SUPPORTED,
+ /*!
+ * The application data was not transmitted
+ * because prioritized pending MAC commands had to be sent.
+ */
+ LORAMAC_STATUS_SKIPPED_APP_DATA,
+ /*!
+ * ToDo
+ */
+ LORAMAC_STATUS_DUTYCYCLE_RESTRICTED,
+ /*!
+ *
+ */
+ LORAMAC_STATUS_NO_CHANNEL_FOUND,
+ /*!
+ *
+ */
+ LORAMAC_STATUS_NO_FREE_CHANNEL_FOUND,
+ /*!
+ * ToDo
+ */
+ LORAMAC_STATUS_BUSY_BEACON_RESERVED_TIME,
+ /*!
+ * ToDo
+ */
+ LORAMAC_STATUS_BUSY_PING_SLOT_WINDOW_TIME,
+ /*!
+ * ToDo
+ */
+ LORAMAC_STATUS_BUSY_UPLINK_COLLISION,
+ /*!
+ * An error in the cryptographic module is occurred
+ */
+ LORAMAC_STATUS_CRYPTO_ERROR,
+ /*!
+ * An error in the frame counter handler module is occurred
+ */
+ LORAMAC_STATUS_FCNT_HANDLER_ERROR,
+ /*!
+ * An error in the MAC command module is occurred
+ */
+ LORAMAC_STATUS_MAC_COMMAD_ERROR,
+ /*!
+ * An error in the Class B module is occurred
+ */
+ LORAMAC_STATUS_CLASS_B_ERROR,
+ /*!
+ * An error in the Confirm Queue module is occurred
+ */
+ LORAMAC_STATUS_CONFIRM_QUEUE_ERROR,
+ /*!
+ * The multicast group doesn't exist
+ */
+ LORAMAC_STATUS_MC_GROUP_UNDEFINED,
+ /*!
+ * Undefined error occurred
+ */
+ LORAMAC_STATUS_ERROR
+}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,
+
+ LORAMAC_REGION_CN470ALI,
+ /*!
+ * 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,
+ /*!
+ * Russia band on 864MHz
+ */
+ LORAMAC_REGION_RU864,
+}LoRaMacRegion_t;
+
+/*!
+ * Enumeration of modules which have a context
+ */
+typedef enum LoRaMacNvmCtxModule_e
+{
+ /*!
+ * Context for the MAC
+ */
+ LORAMAC_NVMCTXMODULE_MAC,
+ /*!
+ * Context for the regions
+ */
+ LORAMAC_NVMCTXMODULE_REGION,
+ /*!
+ * Context for the crypto module
+ */
+ LORAMAC_NVMCTXMODULE_CRYPTO,
+ /*!
+ * Context for the secure element
+ */
+ LORAMAC_NVMCTXMODULE_SECURE_ELEMENT,
+ /*!
+ * Context for the command queue
+ */
+ LORAMAC_NVMCTXMODULE_COMMANDS,
+ /*!
+ * Context for class b
+ */
+ LORAMAC_NVMCTXMODULE_CLASS_B,
+ /*!
+ * Context for the confirm queue
+ */
+ LORAMAC_NVMCTXMODULE_CONFIRM_QUEUE,
+}LoRaMacNvmCtxModule_t;
+
+
+/*!
+ * LoRaMAC events structure
+ * Used to notify upper layers of MAC events
+ */
+typedef struct sLoRaMacPrimitives
+{
+ /*!
+ * \brief MCPS-Confirm primitive
+ *
+ * \param [OUT] MCPS-Confirm parameters
+ */
+ void ( *MacMcpsConfirm )( McpsConfirm_t* McpsConfirm );
+ /*!
+ * \brief MCPS-Indication primitive
+ *
+ * \param [OUT] MCPS-Indication parameters
+ */
+ void ( *MacMcpsIndication )( McpsIndication_t* McpsIndication );
+ /*!
+ * \brief MLME-Confirm primitive
+ *
+ * \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
+{
+ /*!
+ * \brief Measures the battery level
+ *
+ * \retval Battery level [0: node is connected to an external
+ * power source, 1..254: battery level, where 1 is the minimum
+ * and 254 is the maximum value, 255: the node was not able
+ * to measure the battery level]
+ */
+ uint8_t ( *GetBatteryLevel )( void );
+ /*!
+ * \brief Measures the temperature level
+ *
+ * \retval Temperature level
+ */
+ uint16_t ( *GetTemperatureLevel )( void );
+ /*!
+ * \brief Will be called when an attribute has changed in one of the context.
+ *
+ * \param Context that changed
+ */
+ void ( *NvmContextChange )( LoRaMacNvmCtxModule_t module );
+ /*!
+ *\brief Will be called each time a Radio IRQ is handled by the MAC
+ * layer.
+ *
+ *\warning Runs in a IRQ context. Should only change variables state.
+ */
+ void ( *MacProcessNotify )( 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
+ * function initializes the callback primitives of the MCPS and
+ * MLME services. Every data field of \ref LoRaMacPrimitives_t must be
+ * set to a valid callback function.
+ *
+ * \param [IN] primitives - Pointer to a structure defining the LoRaMAC
+ * event functions. Refer to \ref LoRaMacPrimitives_t.
+ *
+ * \param [IN] callbacks - 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_REGION_NOT_SUPPORTED.
+ */
+LoRaMacStatus_t LoRaMacInitialization( LoRaMacPrimitives_t* primitives, LoRaMacCallback_t* callbacks, LoRaMacRegion_t region );
+
+/*!
+ * \brief Starts LoRaMAC layer
+ *
+ * \retval LoRaMacStatus_t Status of the operation. Possible returns are:
+ * returns are:
+ * \ref LORAMAC_STATUS_OK,
+ */
+LoRaMacStatus_t LoRaMacStart( void );
+
+/*!
+ * \brief Stops LoRaMAC layer
+ *
+ * \retval LoRaMacStatus_t Status of the operation. Possible returns are:
+ * returns are:
+ * \ref LORAMAC_STATUS_OK,
+ */
+LoRaMacStatus_t LoRaMacStop( void );
+
+/*!
+ * \brief Returns a value indicating if the MAC layer is busy or not.
+ *
+ * \retval isBusy Mac layer is busy.
+ */
+bool LoRaMacIsBusy( void );
+
+/*!
+ * Processes the LoRaMac events.
+ *
+ * \remark This function must be called in the main loop.
+ */
+void LoRaMacProcess( void *args);
+
+/*!
+ * \brief Queries the LoRaMAC if it is possible to send the next frame with
+ * a given application data payload size. The LoRaMAC takes scheduled
+ * MAC commands into account and reports, when the frame can be send or not.
+ *
+ * \param [IN] size - Size of application data payload to be send next
+ *
+ * \param [OUT] txInfo - The structure \ref LoRaMacTxInfo_t contains
+ * information about the actual maximum payload possible
+ * ( according to the configured datarate or the next
+ * datarate according to ADR ), and the maximum frame
+ * size, taking the scheduled MAC commands into account.
+ *
+ * \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 application data payload in combination
+ * with the MAC commands, the function returns \ref LORAMAC_STATUS_LENGTH_ERROR.
+ * In this case its recommended to send a frame without application data to flush
+ * the MAC commands. Otherwise the LoRaMAC will prioritize the MAC commands and
+ * if needed it will skip the application data. Please note that if MAC commands do
+ * not fit at all into the payload size on the related datarate, the LoRaMAC will
+ * automatically clip 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 );
+
+/*!
+ * \brief LoRaMAC channel add service
+ *
+ * \details Adds a new channel to the channel list and activates the id in
+ * 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.
+ *
+ * \param [IN] params - Channel parameters to set.
+ *
+ * \retval LoRaMacStatus_t Status of the operation. Possible returns are:
+ * \ref LORAMAC_STATUS_OK,
+ * \ref LORAMAC_STATUS_BUSY,
+ * \ref LORAMAC_STATUS_PARAMETER_INVALID.
+ */
+LoRaMacStatus_t LoRaMacChannelAdd( uint8_t id, ChannelParams_t params );
+
+/*!
+ * \brief LoRaMAC channel remove service
+ *
+ * \details Deactivates the id in the channel mask.
+ *
+ * \param [IN] id - Id of the channel.
+ *
+ * \retval LoRaMacStatus_t Status of the operation. Possible returns are:
+ * \ref LORAMAC_STATUS_OK,
+ * \ref LORAMAC_STATUS_BUSY,
+ * \ref LORAMAC_STATUS_PARAMETER_INVALID.
+ */
+LoRaMacStatus_t LoRaMacChannelRemove( uint8_t id );
+
+/*!
+ * \brief LoRaMAC multicast channel setup service
+ *
+ * \details Sets up a multicast channel.
+ *
+ * \param [IN] channel - Multicast channel to set.
+ *
+ * \retval LoRaMacStatus_t Status of the operation. Possible returns are:
+ * \ref LORAMAC_STATUS_OK,
+ * \ref LORAMAC_STATUS_BUSY,
+ * \ref LORAMAC_STATUS_PARAMETER_INVALID,
+ * \ref LORAMAC_STATUS_MC_GROUP_UNDEFINED.
+ */
+LoRaMacStatus_t LoRaMacMcChannelSetup( McChannelParams_t *channel );
+
+/*!
+ * \brief LoRaMAC multicast channel removal service
+ *
+ * \details Removes/Disables a multicast channel.
+ *
+ * \param [IN] groupID - Multicast channel ID to be removed/disabled
+ *
+ * \retval LoRaMacStatus_t Status of the operation. Possible returns are:
+ * \ref LORAMAC_STATUS_OK,
+ * \ref LORAMAC_STATUS_BUSY,
+ * \ref LORAMAC_STATUS_MC_GROUP_UNDEFINED.
+ */
+LoRaMacStatus_t LoRaMacMcChannelDelete( AddressIdentifier_t groupID );
+
+/*!
+ * \brief LoRaMAC multicast channel get groupId from MC address.
+ *
+ * \param [IN] mcAddress - Multicast address to be checked
+ *
+ * \retval groupID Multicast channel ID associated to the address.
+ * Returns 0xFF if the address isn't found.
+ */
+uint8_t LoRaMacMcChannelGetGroupId( uint32_t mcAddress );
+
+/*!
+ * \brief LoRaMAC multicast channel Rx parameters setup service
+ *
+ * \details Sets up a multicast channel reception parameters.
+ *
+ * \param [IN] groupID - Multicast channel ID
+ * \param [IN] rxParams - Reception parameters
+ * \param [OUT] status - Status mask [UNDEF_ID | FREQ_ERR | DR_ERR | GROUP_ID]
+ *
+ * \retval LoRaMacStatus_t Status of the operation. Possible returns are:
+ * \ref LORAMAC_STATUS_OK,
+ * \ref LORAMAC_STATUS_BUSY,
+ * \ref LORAMAC_STATUS_PARAMETER_INVALID,
+ * \ref LORAMAC_STATUS_MC_GROUP_UNDEFINED.
+ */
+LoRaMacStatus_t LoRaMacMcChannelSetupRxParams( AddressIdentifier_t groupID, McRxParams_t *rxParams, uint8_t *status );
+
+/*!
+ * \brief LoRaMAC MIB-Get
+ *
+ * \details The mac information base service to get attributes of the LoRaMac
+ * layer.
+ *
+ * The following code-snippet shows how to use the API to get the
+ * parameter AdrEnable, defined by the enumeration type
+ * \ref MIB_ADR.
+ * \code
+ * MibRequestConfirm_t mibReq;
+ * mibReq.Type = MIB_ADR;
+ *
+ * if( LoRaMacMibGetRequestConfirm( &mibReq ) == LORAMAC_STATUS_OK )
+ * {
+ * // LoRaMAC updated the parameter mibParam.AdrEnable
+ * }
+ * \endcode
+ *
+ * \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 LoRaMacMibGetRequestConfirm( MibRequestConfirm_t* mibGet );
+
+/*!
+ * \brief LoRaMAC MIB-Set
+ *
+ * \details The mac information base service to set attributes of the LoRaMac
+ * layer.
+ *
+ * The following code-snippet shows how to use the API to set the
+ * parameter AdrEnable, defined by the enumeration type
+ * \ref MIB_ADR.
+ *
+ * \code
+ * MibRequestConfirm_t mibReq;
+ * mibReq.Type = MIB_ADR;
+ * mibReq.Param.AdrEnable = true;
+ *
+ * if( LoRaMacMibGetRequestConfirm( &mibReq ) == LORAMAC_STATUS_OK )
+ * {
+ * // LoRaMAC updated the parameter
+ * }
+ * \endcode
+ *
+ * \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 LoRaMacMibSetRequestConfirm( MibRequestConfirm_t* mibSet );
+
+/*!
+ * \brief LoRaMAC MLME-Request
+ *
+ * \details The Mac layer management entity handles management services. The
+ * following code-snippet shows how to use the API to perform a
+ * network join request. Please note that for a join request, the
+ * DevEUI and the JoinEUI must be set previously via the MIB. Please
+ * also refer to the sample implementations.
+ *
+ * \code
+ *
+ * MlmeReq_t mlmeReq;
+ * mlmeReq.Type = MLME_JOIN;
+ * mlmeReq.Req.Join.Datarate = LORAWAN_DEFAULT_DATARATE;
+ *
+ * if( LoRaMacMlmeRequest( &mlmeReq ) == LORAMAC_STATUS_OK )
+ * {
+ * // Service started successfully. Waiting for the Mlme-Confirm event
+ * }
+ * \endcode
+ *
+ * \param [IN] mlmeRequest - MLME-Request to perform. Refer to \ref MlmeReq_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,
+ * \ref LORAMAC_STATUS_NO_NETWORK_JOINED,
+ * \ref LORAMAC_STATUS_LENGTH_ERROR,
+ */
+LoRaMacStatus_t LoRaMacMlmeRequest( MlmeReq_t* mlmeRequest );
+
+/*!
+ * \brief LoRaMAC MCPS-Request
+ *
+ * \details The Mac Common Part Sublayer handles data services. The following
+ * code-snippet shows how to use the API to send an unconfirmed
+ * LoRaMAC frame.
+ *
+ * \code
+ * uint8_t myBuffer[] = { 1, 2, 3 };
+ *
+ * McpsReq_t mcpsReq;
+ * mcpsReq.Type = MCPS_UNCONFIRMED;
+ * mcpsReq.Req.Unconfirmed.fPort = 1;
+ * mcpsReq.Req.Unconfirmed.fBuffer = myBuffer;
+ * mcpsReq.Req.Unconfirmed.fBufferSize = sizeof( myBuffer );
+ *
+ * if( LoRaMacMcpsRequest( &mcpsReq ) == LORAMAC_STATUS_OK )
+ * {
+ * // Service started successfully. Waiting for the MCPS-Confirm event
+ * }
+ * \endcode
+ *
+ * \param [IN] mcpsRequest - MCPS-Request to perform. Refer to \ref McpsReq_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,
+ * \ref LORAMAC_STATUS_NO_NETWORK_JOINED,
+ * \ref LORAMAC_STATUS_LENGTH_ERROR,
+ */
+LoRaMacStatus_t LoRaMacMcpsRequest( McpsReq_t* mcpsRequest );
+
+/*!
+ * Automatically add the Region.h file at the end of LoRaMac.h file.
+ * This is required because Region.h uses definitions from LoRaMac.h
+ */
+#include "region/Region.h"
+
+/*! \} defgroup LORAMAC */
+
+#endif // __LORAMAC_H__
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacAdr.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacAdr.c
new file mode 100644
index 00000000..d80c10ed
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacAdr.c
@@ -0,0 +1,131 @@
+/*!
+ * \file LoRaMacAdr.c
+ *
+ * \brief LoRa MAC ADR implementation
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013-2017 Semtech
+ *
+ * ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ * embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Gregory Cristian ( Semtech )
+ *
+ * \author Daniel Jaeckle ( STACKFORCE )
+ *
+ * \author Johannes Bruder ( STACKFORCE )
+ */
+
+#include "utilities.h"
+#include "region/Region.h"
+#include "LoRaMacAdr.h"
+
+static bool CalcNextV10X( CalcNextAdrParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter )
+{
+ bool adrAckReq = false;
+ int8_t datarate = adrNext->Datarate;
+ int8_t txPower = adrNext->TxPower;
+ int8_t minTxDatarate;
+ GetPhyParams_t getPhy;
+ PhyParam_t phyParam;
+
+ // Report back the adr ack counter
+ *adrAckCounter = adrNext->AdrAckCounter;
+
+ if( adrNext->AdrEnabled == true )
+ {
+ // Query minimum TX Datarate
+ getPhy.Attribute = PHY_MIN_TX_DR;
+ getPhy.UplinkDwellTime = adrNext->UplinkDwellTime;
+ phyParam = RegionGetPhyParam( adrNext->Region, &getPhy );
+ minTxDatarate = phyParam.Value;
+ datarate = MAX( datarate, minTxDatarate );
+
+ if( datarate == minTxDatarate )
+ {
+ *adrAckCounter = 0;
+ adrAckReq = false;
+ }
+ else
+ {
+ if( adrNext->AdrAckCounter >= adrNext->AdrAckLimit )
+ {
+ adrAckReq = true;
+ }
+ else
+ {
+ adrAckReq = false;
+ }
+ if( adrNext->AdrAckCounter >= ( adrNext->AdrAckLimit + adrNext->AdrAckDelay ) )
+ {
+ // Set TX Power to maximum
+ getPhy.Attribute = PHY_MAX_TX_POWER;
+ phyParam = RegionGetPhyParam( adrNext->Region, &getPhy );
+ txPower = phyParam.Value;
+
+ if( ( adrNext->AdrAckCounter % adrNext->AdrAckDelay ) == 1 )
+ {
+ // Decrease the datarate
+ getPhy.Attribute = PHY_NEXT_LOWER_TX_DR;
+ getPhy.Datarate = datarate;
+ getPhy.UplinkDwellTime = adrNext->UplinkDwellTime;
+ phyParam = RegionGetPhyParam( adrNext->Region, &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 )
+ {
+ InitDefaultsParams_t params;
+ params.Type = INIT_TYPE_RESTORE_DEFAULT_CHANNELS;
+ RegionInitDefaults( adrNext->Region, ¶ms );
+ }
+ }
+ }
+ }
+ }
+ }
+
+ *drOut = datarate;
+ *txPowOut = txPower;
+ return adrAckReq;
+}
+
+/*!
+ * \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 LoRaMacAdrCalcNext( CalcNextAdrParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter )
+{
+ if( adrNext->Version.Fields.Minor == 0 )
+ {
+ return CalcNextV10X( adrNext, drOut, txPowOut, adrAckCounter );
+ }
+ return false;
+}
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacAdr.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacAdr.h
new file mode 100644
index 00000000..228df763
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacAdr.h
@@ -0,0 +1,104 @@
+/*!
+ * \file LoRaMacAdr.h
+ *
+ * \brief LoRa MAC ADR implementation
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013-2017 Semtech
+ *
+ * ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ * embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Gregory Cristian ( Semtech )
+ *
+ * \author Daniel Jaeckle ( STACKFORCE )
+ *
+ * \author Johannes Bruder ( STACKFORCE )
+ *
+ * \defgroup LORAMACADR LoRa MAC ADR implementation
+ * Implementation of the ADR algorithm for LoRa.
+ * \{
+ */
+#ifndef __LORAMACADR_H__
+#define __LORAMACADR_H__
+
+/*! \} defgroup LORAMACADR */
+
+/*
+ * Parameter structure for the function CalcNextAdr.
+ */
+typedef struct sCalcNextAdrParams
+{
+ /*!
+ * LoRaWAN Minor Version 1.X
+ */
+ Version_t Version;
+ /*!
+ * 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;
+ /*!
+ * ADR Ack limit
+ */
+ uint16_t AdrAckLimit;
+ /*!
+ * ADR Ack delay
+ */
+ uint16_t AdrAckDelay;
+ /*!
+ * Datarate used currently.
+ */
+ int8_t Datarate;
+ /*!
+ * TX power used currently.
+ */
+ int8_t TxPower;
+ /*!
+ * UplinkDwellTime
+ */
+ uint8_t UplinkDwellTime;
+ /*!
+ * Region
+ */
+ LoRaMacRegion_t Region;
+}CalcNextAdrParams_t;
+
+/*!
+ * \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 LoRaMacAdrCalcNext( CalcNextAdrParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter );
+
+#endif // __LORAMACADR_H__
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacClassB.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacClassB.c
new file mode 100644
index 00000000..5a6108b2
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacClassB.c
@@ -0,0 +1,1707 @@
+/*
+ / _____) _ | |
+( (____ _____ ____ _| |_ _____ ____| |__
+ \____ \| ___ | (_ _) ___ |/ ___) _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+ (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
+#include "utilities.h"
+#include "lorawan_conf.h" /* Don't remove: needed for LORAMAC_CLASSB_ENABLED */
+#include "radio.h"
+#include "secure-element.h"
+#include "LoRaMac.h"
+#include "LoRaMacClassB.h"
+#include "LoRaMacClassBConfig.h"
+#include "LoRaMacCrypto.h"
+#include "LoRaMacConfirmQueue.h"
+
+
+#ifdef LORAMAC_CLASSB_ENABLED
+
+
+/*!
+ * Defines the LoRaMac radio events status
+ */
+typedef union uLoRaMacClassBEvents
+{
+ uint32_t Value;
+ struct sEvents
+ {
+ uint32_t Beacon : 1;
+ uint32_t PingSlot : 1;
+ uint32_t MulticastSlot : 1;
+ }Events;
+}LoRaMacClassBEvents_t;
+
+LoRaMacClassBEvents_t LoRaMacClassBEvents = { .Value = 0 };
+
+/*
+ * Non-volatile module context.
+ */
+static LoRaMacClassBNvmCtx_t NvmCtx;
+
+/*
+ * Module context.
+ */
+static LoRaMacClassBCtx_t Ctx;
+
+
+/*!
+ * \brief Computes the temperature compensation for a period of time on a
+ * specific temperature.
+ *
+ * \param [IN] period Time period to compensate
+ * \param [IN] temperature Current temperature
+ *
+ * \retval Compensated time period
+ */
+static TimerTime_t TimerTempCompensation( TimerTime_t period, float temperature )
+{
+ float k = RTC_TEMP_COEFFICIENT;
+ float kDev = RTC_TEMP_DEV_COEFFICIENT;
+ float t = RTC_TEMP_TURNOVER;
+ float tDev = RTC_TEMP_DEV_TURNOVER;
+ float interim = 0.0;
+ float ppm = 0.0;
+
+ if (k < 0.0f)
+ {
+ ppm = (k - kDev);
+ }
+ else
+ {
+ ppm = (k + kDev);
+ }
+ interim = (temperature - (t - tDev));
+ ppm *= interim * interim;
+
+ // Calculate the drift in time
+ interim = ((float) period * ppm) / 1000000;
+ // Calculate the resulting time period
+ interim += period;
+ interim = floor(interim);
+
+ if (interim < 0.0f)
+ {
+ interim = (float)period;
+ }
+
+ // Calculate the resulting period
+ return ( UTIL_TIMER_Time_t ) interim;
+}
+
+/*!
+ * Computes the Ping Offset
+ *
+ * \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
+ */
+static void ComputePingOffset( uint64_t beaconTime, uint32_t address, uint16_t pingPeriod, uint16_t *pingOffset )
+{
+ 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( buffer, 0, 16 );
+ memset1( cipher, 0, 16 );
+
+ 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;
+
+ SecureElementAesEncrypt( buffer, 16, SLOT_RAND_ZERO_KEY, cipher );
+
+ result = ( ( ( uint32_t ) cipher[0] ) + ( ( ( uint32_t ) cipher[1] ) * 256 ) );
+
+ *pingOffset = ( uint16_t )( result % pingPeriod );
+}
+
+/*!
+ * \brief Calculates the downlink frequency for a given channel.
+ *
+ * \param [IN] channel The channel according to the channel plan.
+ *
+ * \retval The downlink frequency
+ */
+static uint32_t CalcDownlinkFrequency( uint8_t channel )
+{
+ GetPhyParams_t getPhy;
+ PhyParam_t phyParam;
+ uint32_t frequency = 0;
+ uint32_t stepwidth = 0;
+
+ getPhy.Attribute = PHY_BEACON_CHANNEL_FREQ;
+ phyParam = RegionGetPhyParam( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &getPhy );
+ frequency = phyParam.Value;
+
+ getPhy.Attribute = PHY_BEACON_CHANNEL_STEPWIDTH;
+ phyParam = RegionGetPhyParam( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &getPhy );
+ stepwidth = phyParam.Value;
+
+ // Calculate the frequency
+ return frequency + ( channel * stepwidth );
+}
+
+/*!
+ * \brief Calculates the downlink channel for the beacon and for
+ * ping slot downlinks.
+ *
+ * \param [IN] devAddr The address of the device
+ *
+ * \param [IN] beaconTime The beacon time of the beacon.
+ *
+ * \param [IN] beaconInterval The beacon interval
+ *
+ * \retval The downlink channel
+ */
+static uint32_t CalcDownlinkChannelAndFrequency( uint32_t devAddr, TimerTime_t beaconTime, TimerTime_t beaconInterval )
+{
+ GetPhyParams_t getPhy;
+ PhyParam_t phyParam;
+ uint32_t channel = 0;
+ uint8_t nbChannels = 0;
+ uint32_t frequency = 0;
+
+ getPhy.Attribute = PHY_BEACON_NB_CHANNELS;
+ phyParam = RegionGetPhyParam( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &getPhy );
+ nbChannels = (uint8_t) phyParam.Value;
+
+ if( nbChannels > 1 )
+ {
+ // Calculate the channel for the next downlink
+ channel = devAddr + ( beaconTime / ( beaconInterval / 1000 ) );
+ channel = channel % nbChannels;
+ }
+
+ // Calculate the frequency for the next downlink
+ frequency = CalcDownlinkFrequency( channel );
+
+ // Calculate the frequency for the next downlink
+ return frequency;
+}
+
+/*!
+ * \brief Calculates the correct frequency and opens up the beacon reception window.
+ *
+ * \param [IN] rxTime The reception time which should be setup
+ *
+ * \param [IN] activateDefaultChannel Set to true, if the function shall setup the default channel
+ */
+static void RxBeaconSetup( TimerTime_t rxTime, bool activateDefaultChannel )
+{
+ RxBeaconSetup_t rxBeaconSetup;
+ uint32_t frequency = 0;
+ RxConfigParams_t beaconRxConfig;
+ GetPhyParams_t getPhy;
+ PhyParam_t phyParam;
+ uint16_t windowTimeout = Ctx.BeaconCtx.SymbolTimeout;
+
+ if( activateDefaultChannel == true )
+ {
+ // This is the default frequency in case we don't know when the next
+ // beacon will be transmitted. We select channel 0 as default.
+ frequency = CalcDownlinkFrequency( 0 );
+ }
+ else
+ {
+ // This is the frequency according to the channel plan
+ frequency = CalcDownlinkChannelAndFrequency( 0, Ctx.BeaconCtx.BeaconTime.Seconds + ( CLASSB_BEACON_INTERVAL / 1000 ),
+ CLASSB_BEACON_INTERVAL );
+ }
+
+ if( Ctx.NvmCtx->BeaconCtx.Ctrl.CustomFreq == 1 )
+ {
+ // Set the frequency from the BeaconFreqReq
+ frequency = Ctx.NvmCtx->BeaconCtx.Frequency;
+ }
+
+ if( Ctx.BeaconCtx.Ctrl.BeaconChannelSet == 1 )
+ {
+ // Set the frequency which was provided by BeaconTimingAns MAC command
+ Ctx.BeaconCtx.Ctrl.BeaconChannelSet = 0;
+ frequency = CalcDownlinkFrequency( Ctx.BeaconCtx.BeaconTimingChannel );
+ }
+
+ if( ( Ctx.BeaconCtx.Ctrl.BeaconAcquired == 1 ) || ( Ctx.BeaconCtx.Ctrl.AcquisitionPending == 1 ) )
+ {
+ // Apply the symbol timeout only if we have acquired the beacon
+ // Otherwise, take the window enlargement into account
+ // Read beacon datarate
+ getPhy.Attribute = PHY_BEACON_CHANNEL_DR;
+ phyParam = RegionGetPhyParam( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &getPhy );
+
+ // Calculate downlink symbols
+ RegionComputeRxWindowParameters( *Ctx.LoRaMacClassBParams.LoRaMacRegion,
+ ( int8_t )phyParam.Value, // datarate
+ Ctx.LoRaMacClassBParams.LoRaMacParams->MinRxSymbols,
+ Ctx.LoRaMacClassBParams.LoRaMacParams->SystemMaxRxError,
+ &beaconRxConfig );
+ windowTimeout = beaconRxConfig.WindowTimeout;
+ }
+
+ rxBeaconSetup.SymbolTimeout = windowTimeout;
+ rxBeaconSetup.RxTime = rxTime;
+ rxBeaconSetup.Frequency = frequency;
+
+ RegionRxBeaconSetup( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &rxBeaconSetup, &Ctx.LoRaMacClassBParams.McpsIndication->RxDatarate );
+
+ Ctx.LoRaMacClassBParams.MlmeIndication->BeaconInfo.Frequency = frequency;
+ Ctx.LoRaMacClassBParams.MlmeIndication->BeaconInfo.Datarate = Ctx.LoRaMacClassBParams.McpsIndication->RxDatarate;
+}
+
+/*!
+ * \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, uint16_t pingNb, 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 - SysTimeToMs( Ctx.BeaconCtx.LastBeaconRx ) ) % CLASSB_BEACON_INTERVAL );
+ slotTime = currentTime - slotTime;
+
+ // Add the reserved time and the ping offset
+ slotTime += CLASSB_BEACON_RESERVED;
+ slotTime += slotOffset * CLASSB_PING_SLOT_WINDOW;
+
+ if( slotTime < currentTime )
+ {
+ currentPingSlot = ( ( currentTime - slotTime ) /
+ ( pingPeriod * CLASSB_PING_SLOT_WINDOW ) ) + 1;
+ slotTime += ( ( TimerTime_t )( currentPingSlot * pingPeriod ) *
+ CLASSB_PING_SLOT_WINDOW );
+ }
+
+ if( currentPingSlot < pingNb )
+ {
+ if( slotTime <= ( SysTimeToMs( Ctx.BeaconCtx.NextBeaconRx ) - CLASSB_BEACON_GUARD - CLASSB_PING_SLOT_WINDOW ) )
+ {
+ // Calculate the relative ping slot time
+ slotTime -= currentTime;
+ slotTime -= Radio.GetWakeupTime( );
+ slotTime = TimerTempCompensation( slotTime, Ctx.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 = (float) callbacks->GetTemperatureLevel( );
+ }
+}
+
+static void InitClassB( void )
+{
+ GetPhyParams_t getPhy;
+ PhyParam_t phyParam;
+
+ // Init events
+ LoRaMacClassBEvents.Value = 0;
+
+ // Init variables to default
+ memset1( ( uint8_t* ) &NvmCtx, 0, sizeof( LoRaMacClassBNvmCtx_t ) );
+ memset1( ( uint8_t* ) &Ctx.PingSlotCtx, 0, sizeof( PingSlotContext_t ) );
+ memset1( ( uint8_t* ) &Ctx.BeaconCtx, 0, sizeof( BeaconContext_t ) );
+
+ // Setup default temperature
+ Ctx.BeaconCtx.Temperature = 25.0;
+
+ //YBO removed this useless line from Semthec stack which gives a problem in DualCore
+ //GetTemperatureLevel( &Ctx.LoRaMacClassBCallbacks, &Ctx.BeaconCtx );
+
+ // Setup default ping slot datarate
+ getPhy.Attribute = PHY_PING_SLOT_CHANNEL_DR;
+ phyParam = RegionGetPhyParam( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &getPhy );
+ Ctx.NvmCtx->PingSlotCtx.Datarate = (int8_t)( phyParam.Value );
+
+ // Setup default beacon freq
+ getPhy.Attribute = PHY_BEACON_CHANNEL_FREQ;
+ phyParam = RegionGetPhyParam( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &getPhy );
+ Ctx.NvmCtx->BeaconCtx.Frequency = phyParam.Value;
+
+ // Setup default states
+ Ctx.BeaconState = BEACON_STATE_ACQUISITION;
+ Ctx.PingSlotState = PINGSLOT_STATE_CALC_PING_OFFSET;
+ Ctx.MulticastSlotState = PINGSLOT_STATE_CALC_PING_OFFSET;
+ Ctx.NvmCtx->BeaconCtx.BeaconState = BEACON_STATE_ACQUISITION;
+}
+
+static void InitClassBDefaults( void )
+{
+ // This function shall reset the Class B settings to default,
+ // but should keep important configurations
+ LoRaMacClassBBeaconNvmCtx_t beaconCtx = Ctx.NvmCtx->BeaconCtx;
+ LoRaMacClassBPingSlotNvmCtx_t pingSlotCtx = Ctx.NvmCtx->PingSlotCtx;
+
+ InitClassB( );
+
+ // Parameters from BeaconFreqReq
+ Ctx.NvmCtx->BeaconCtx.Frequency = beaconCtx.Frequency;
+ Ctx.NvmCtx->BeaconCtx.Ctrl.CustomFreq = beaconCtx.Ctrl.CustomFreq;
+
+ // Parameters from PingSlotChannelReq
+ Ctx.NvmCtx->PingSlotCtx.Ctrl.CustomFreq = pingSlotCtx.Ctrl.CustomFreq;
+ Ctx.NvmCtx->PingSlotCtx.Frequency = pingSlotCtx.Frequency;
+ Ctx.NvmCtx->PingSlotCtx.Datarate = pingSlotCtx.Datarate;
+}
+
+static void EnlargeWindowTimeout( void )
+{
+ // Update beacon movement
+ Ctx.BeaconCtx.BeaconWindowMovement *= CLASSB_WINDOW_MOVE_EXPANSION_FACTOR;
+ if( Ctx.BeaconCtx.BeaconWindowMovement > CLASSB_WINDOW_MOVE_EXPANSION_MAX )
+ {
+ Ctx.BeaconCtx.BeaconWindowMovement = CLASSB_WINDOW_MOVE_EXPANSION_MAX;
+ }
+ // Update symbol timeout
+ Ctx.BeaconCtx.SymbolTimeout *= CLASSB_BEACON_SYMBOL_TO_EXPANSION_FACTOR;
+ if( Ctx.BeaconCtx.SymbolTimeout > CLASSB_BEACON_SYMBOL_TO_EXPANSION_MAX )
+ {
+ Ctx.BeaconCtx.SymbolTimeout = CLASSB_BEACON_SYMBOL_TO_EXPANSION_MAX;
+ }
+ Ctx.PingSlotCtx.SymbolTimeout *= CLASSB_BEACON_SYMBOL_TO_EXPANSION_FACTOR;
+ if( Ctx.PingSlotCtx.SymbolTimeout > CLASSB_PING_SLOT_SYMBOL_TO_EXPANSION_MAX )
+ {
+ Ctx.PingSlotCtx.SymbolTimeout = CLASSB_PING_SLOT_SYMBOL_TO_EXPANSION_MAX;
+ }
+}
+
+static void ResetWindowTimeout( void )
+{
+ Ctx.BeaconCtx.SymbolTimeout = CLASSB_BEACON_SYMBOL_TO_DEFAULT;
+ Ctx.PingSlotCtx.SymbolTimeout = CLASSB_BEACON_SYMBOL_TO_DEFAULT;
+ Ctx.BeaconCtx.BeaconWindowMovement = CLASSB_WINDOW_MOVE_DEFAULT;
+}
+
+static TimerTime_t CalcDelayForNextBeacon( TimerTime_t currentTime, TimerTime_t lastBeaconRx )
+{
+ TimerTime_t nextBeaconRxTime = 0;
+
+ // Calculate the point in time of the next beacon
+ nextBeaconRxTime = ( ( currentTime - lastBeaconRx ) % CLASSB_BEACON_INTERVAL );
+ return ( CLASSB_BEACON_INTERVAL - nextBeaconRxTime );
+}
+
+static void IndicateBeaconStatus( LoRaMacEventInfoStatus_t status )
+{
+ if( Ctx.BeaconCtx.Ctrl.ResumeBeaconing == 0 )
+ {
+ Ctx.LoRaMacClassBParams.MlmeIndication->MlmeIndication = MLME_BEACON;
+ Ctx.LoRaMacClassBParams.MlmeIndication->Status = status;
+ Ctx.LoRaMacClassBParams.LoRaMacFlags->Bits.MlmeInd = 1;
+
+ Ctx.LoRaMacClassBParams.LoRaMacFlags->Bits.MacDone = 1;
+ }
+ Ctx.BeaconCtx.Ctrl.ResumeBeaconing = 0;
+}
+
+static TimerTime_t ApplyGuardTime( TimerTime_t beaconEventTime )
+{
+ TimerTime_t timeGuard = beaconEventTime;
+
+ if( timeGuard > CLASSB_BEACON_GUARD )
+ {
+ timeGuard -= CLASSB_BEACON_GUARD;
+ }
+ return timeGuard;
+}
+
+static TimerTime_t UpdateBeaconState( LoRaMacEventInfoStatus_t status,
+ TimerTime_t windowMovement, TimerTime_t currentTime )
+
+{
+ TimerTime_t beaconEventTime = 0;
+
+ // Calculate the next beacon RX time
+ beaconEventTime = CalcDelayForNextBeacon( currentTime, SysTimeToMs( Ctx.BeaconCtx.LastBeaconRx ) );
+ Ctx.BeaconCtx.NextBeaconRx = SysTimeFromMs( currentTime + beaconEventTime );
+
+ // Take temperature compensation into account
+ beaconEventTime = TimerTempCompensation( beaconEventTime, Ctx.BeaconCtx.Temperature );
+
+ // Move the window
+ if( beaconEventTime > windowMovement )
+ {
+ beaconEventTime -= windowMovement;
+ }
+ Ctx.BeaconCtx.NextBeaconRxAdjusted = currentTime + beaconEventTime;
+
+ // Start the RX slot state machine for ping and multicast slots
+ LoRaMacClassBStartRxSlots( );
+
+ // Setup an MLME_BEACON indication to inform the upper layer
+ IndicateBeaconStatus( status );
+
+ // Apply guard time
+ return ApplyGuardTime( beaconEventTime );
+}
+
+static uint8_t CalcPingNb( uint16_t periodicity )
+{
+ return 128 / ( 1 << periodicity );
+}
+
+static uint16_t CalcPingPeriod( uint8_t pingNb )
+{
+ return CLASSB_BEACON_WINDOW_SLOTS / pingNb;
+}
+
+/*
+ * Dummy callback in case if the user provides NULL function pointer
+ */
+static void NvmContextChange( void )
+{
+ if( Ctx.LoRaMacClassBNvmEvent != NULL )
+ {
+ Ctx.LoRaMacClassBNvmEvent( );
+ }
+}
+
+#endif // LORAMAC_CLASSB_ENABLED
+
+void LoRaMacClassBInit( LoRaMacClassBParams_t *classBParams, LoRaMacClassBCallback_t *callbacks, LoRaMacClassBNvmEvent classBNvmCtxChanged )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ // Store callbacks
+ Ctx.LoRaMacClassBCallbacks = *callbacks;
+
+ // Store parameter pointers
+ Ctx.LoRaMacClassBParams = *classBParams;
+
+ // Assign non-volatile context
+ Ctx.NvmCtx = &NvmCtx;
+
+ // Assign callback
+ Ctx.LoRaMacClassBNvmEvent = classBNvmCtxChanged;
+
+ // Initialize timers
+ TimerInit( &Ctx.BeaconTimer, LoRaMacClassBBeaconTimerEvent );
+ TimerInit( &Ctx.PingSlotTimer, LoRaMacClassBPingSlotTimerEvent );
+ TimerInit( &Ctx.MulticastSlotTimer, LoRaMacClassBMulticastSlotTimerEvent );
+
+ InitClassB( );
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+bool LoRaMacClassBRestoreNvmCtx( void* classBNvmCtx )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ // Restore module context
+ if( classBNvmCtx != NULL )
+ {
+ memcpy1( ( uint8_t* ) &NvmCtx, ( uint8_t* ) classBNvmCtx, sizeof( NvmCtx ) );
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+#else
+ return true;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+void* LoRaMacClassBGetNvmCtx( size_t* classBNvmCtxSize )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ *classBNvmCtxSize = sizeof( NvmCtx );
+ return &NvmCtx;
+#else
+ *classBNvmCtxSize = 0;
+ return NULL;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+void LoRaMacClassBSetBeaconState( BeaconState_t beaconState )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ if( beaconState == BEACON_STATE_ACQUISITION )
+ {
+ // If the MAC has received a time reference for the beacon,
+ // apply the state BEACON_STATE_ACQUISITION_BY_TIME.
+ if( ( Ctx.BeaconCtx.Ctrl.BeaconDelaySet == 1 ) &&
+ ( LoRaMacClassBIsAcquisitionPending( ) == false ) )
+ {
+ Ctx.BeaconState = BEACON_STATE_ACQUISITION_BY_TIME;
+ }
+ else
+ {
+ Ctx.BeaconState = beaconState;
+ }
+ }
+ else
+ {
+ if( ( Ctx.BeaconState != BEACON_STATE_ACQUISITION ) &&
+ ( Ctx.BeaconState != BEACON_STATE_ACQUISITION_BY_TIME ) )
+ {
+ Ctx.BeaconState = beaconState;
+ }
+ }
+ Ctx.NvmCtx->BeaconCtx.BeaconState = Ctx.BeaconState;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+void LoRaMacClassBSetPingSlotState( PingSlotState_t pingSlotState )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ Ctx.PingSlotState = pingSlotState;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+void LoRaMacClassBSetMulticastSlotState( PingSlotState_t multicastSlotState )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ Ctx.MulticastSlotState = multicastSlotState;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+bool LoRaMacClassBIsAcquisitionInProgress( void )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ if( Ctx.BeaconState == BEACON_STATE_ACQUISITION_BY_TIME )
+ {
+ // In this case the acquisition is in progress, as the MAC has
+ // a time reference for the next beacon RX.
+ return true;
+ }
+ if( LoRaMacClassBIsAcquisitionPending( ) == true )
+ {
+ // In this case the acquisition is in progress, as the MAC
+ // searches for a beacon.
+ return true;
+ }
+ return false;
+#else
+ return false;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+void LoRaMacClassBBeaconTimerEvent( void* context )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ Ctx.BeaconCtx.TimeStamp = TimerGetCurrentTime( );
+ TimerStop( &Ctx.BeaconTimer );
+ LoRaMacClassBEvents.Events.Beacon = 1;
+
+ if( Ctx.LoRaMacClassBCallbacks.MacProcessNotify != NULL )
+ {
+ Ctx.LoRaMacClassBCallbacks.MacProcessNotify( );
+ }
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+#ifdef LORAMAC_CLASSB_ENABLED
+static void LoRaMacClassBProcessBeacon( void )
+{
+ bool activateTimer = false;
+ TimerTime_t beaconEventTime = 1;
+ TimerTime_t currentTime = Ctx.BeaconCtx.TimeStamp;
+
+ // Beacon state machine
+ switch( Ctx.BeaconState )
+ {
+ case BEACON_STATE_ACQUISITION_BY_TIME:
+ {
+ activateTimer = true;
+
+ if( Ctx.BeaconCtx.Ctrl.AcquisitionPending == 1 )
+ {
+ Radio.Sleep();
+ Ctx.BeaconState = BEACON_STATE_LOST;
+ }
+ else
+ {
+ // Default symbol timeouts
+ ResetWindowTimeout( );
+
+ if( Ctx.BeaconCtx.Ctrl.BeaconDelaySet == 1 )
+ {
+ if( Ctx.BeaconCtx.BeaconTimingDelay > 0 )
+ {
+ if( SysTimeToMs( Ctx.BeaconCtx.NextBeaconRx ) > currentTime )
+ {
+ beaconEventTime = TimerTempCompensation( SysTimeToMs( Ctx.BeaconCtx.NextBeaconRx ) - currentTime, Ctx.BeaconCtx.Temperature );
+ }
+ else
+ {
+ // Reset status provides by BeaconTimingAns
+ Ctx.BeaconCtx.Ctrl.BeaconDelaySet = 0;
+ Ctx.BeaconCtx.Ctrl.BeaconChannelSet = 0;
+ Ctx.BeaconState = BEACON_STATE_ACQUISITION;
+ }
+ Ctx.BeaconCtx.BeaconTimingDelay = 0;
+ }
+ else
+ {
+ activateTimer = false;
+
+ // Reset status provides by BeaconTimingAns
+ Ctx.BeaconCtx.Ctrl.BeaconDelaySet = 0;
+ // Set the node into acquisition mode
+ Ctx.BeaconCtx.Ctrl.AcquisitionPending = 1;
+
+ // Don't use the default channel. We know on which
+ // channel the next beacon will be transmitted
+ RxBeaconSetup( CLASSB_BEACON_RESERVED, false );
+ }
+ }
+ else
+ {
+ Ctx.BeaconCtx.NextBeaconRx.Seconds = 0;
+ Ctx.BeaconCtx.NextBeaconRx.SubSeconds = 0;
+ Ctx.BeaconCtx.BeaconTimingDelay = 0;
+
+ Ctx.BeaconState = BEACON_STATE_ACQUISITION;
+ }
+ }
+ break;
+ }
+ case BEACON_STATE_ACQUISITION:
+ {
+ activateTimer = true;
+
+ if( Ctx.BeaconCtx.Ctrl.AcquisitionPending == 1 )
+ {
+ Radio.Sleep();
+ Ctx.BeaconState = BEACON_STATE_LOST;
+ }
+ else
+ {
+ // Default symbol timeouts
+ ResetWindowTimeout( );
+
+ Ctx.BeaconCtx.Ctrl.AcquisitionPending = 1;
+ beaconEventTime = CLASSB_BEACON_INTERVAL;
+
+ // Start the beacon acquisition. When the MAC has received a beacon in function
+ // RxBeacon successfully, the next state is BEACON_STATE_LOCKED. If the MAC does not
+ // find a beacon, the state machine will stay in state BEACON_STATE_ACQUISITION.
+ // This state detects that a acquisition was pending previously and will change the next
+ // state to BEACON_STATE_LOST.
+ RxBeaconSetup( 0, true );
+ }
+ break;
+ }
+ case BEACON_STATE_TIMEOUT:
+ {
+ // We have to update the beacon time, since we missed a beacon
+ Ctx.BeaconCtx.BeaconTime.Seconds += ( CLASSB_BEACON_INTERVAL / 1000 );
+ Ctx.BeaconCtx.BeaconTime.SubSeconds = 0;
+
+ // Enlarge window timeouts to increase the chance to receive the next beacon
+ EnlargeWindowTimeout( );
+
+ // Setup next state
+ Ctx.BeaconState = BEACON_STATE_REACQUISITION;
+ }
+ // Intentional fall through
+ case BEACON_STATE_REACQUISITION:
+ {
+ activateTimer = true;
+
+ // The beacon is no longer acquired
+ Ctx.BeaconCtx.Ctrl.BeaconAcquired = 0;
+
+ // Verify if the maximum beacon less period has been elapsed
+ if( ( currentTime - SysTimeToMs( Ctx.BeaconCtx.LastBeaconRx ) ) > CLASSB_MAX_BEACON_LESS_PERIOD )
+ {
+ Ctx.BeaconState = BEACON_STATE_LOST;
+ }
+ else
+ {
+ // Handle beacon miss
+ beaconEventTime = UpdateBeaconState( LORAMAC_EVENT_INFO_STATUS_BEACON_LOST,
+ Ctx.BeaconCtx.BeaconWindowMovement, currentTime );
+
+ // Setup next state
+ Ctx.BeaconState = BEACON_STATE_IDLE;
+ }
+ break;
+ }
+ case BEACON_STATE_LOCKED:
+ {
+ activateTimer = true;
+
+ // We have received a beacon. Acquisition is no longer pending.
+ Ctx.BeaconCtx.Ctrl.AcquisitionPending = 0;
+
+ // Handle beacon reception
+ beaconEventTime = UpdateBeaconState( LORAMAC_EVENT_INFO_STATUS_BEACON_LOCKED,
+ 0, currentTime );
+
+ // Setup the MLME confirm for the MLME_BEACON_ACQUISITION
+ if( Ctx.LoRaMacClassBParams.LoRaMacFlags->Bits.MlmeReq == 1 )
+ {
+ if( LoRaMacConfirmQueueIsCmdActive( MLME_BEACON_ACQUISITION ) == true )
+ {
+ LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_OK, MLME_BEACON_ACQUISITION );
+ Ctx.LoRaMacClassBParams.MlmeConfirm->TxTimeOnAir = 0;
+ }
+ }
+
+ // Setup next state
+ Ctx.BeaconState = BEACON_STATE_IDLE;
+ break;
+ }
+ case BEACON_STATE_IDLE:
+ {
+ activateTimer = true;
+ GetTemperatureLevel( &Ctx.LoRaMacClassBCallbacks, &Ctx.BeaconCtx );
+ beaconEventTime = Ctx.BeaconCtx.NextBeaconRxAdjusted - Radio.GetWakeupTime( );
+ currentTime = TimerGetCurrentTime( );
+
+ if( beaconEventTime > currentTime )
+ {
+ Ctx.BeaconState = BEACON_STATE_GUARD;
+ beaconEventTime -= currentTime;
+ beaconEventTime = TimerTempCompensation( beaconEventTime, Ctx.BeaconCtx.Temperature );
+ }
+ else
+ {
+ Ctx.BeaconState = BEACON_STATE_REACQUISITION;
+ beaconEventTime = 1;
+ }
+ break;
+ }
+ case BEACON_STATE_GUARD:
+ {
+ Ctx.BeaconState = BEACON_STATE_RX;
+
+ // Stop slot timers
+ LoRaMacClassBStopRxSlots( );
+
+ // Don't use the default channel. We know on which
+ // channel the next beacon will be transmitted
+ RxBeaconSetup( CLASSB_BEACON_RESERVED, false );
+ break;
+ }
+ case BEACON_STATE_LOST:
+ {
+ // Handle events
+ if( Ctx.LoRaMacClassBParams.LoRaMacFlags->Bits.MlmeReq == 1 )
+ {
+ if( LoRaMacConfirmQueueIsCmdActive( MLME_BEACON_ACQUISITION ) == true )
+ {
+ LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_BEACON_NOT_FOUND, MLME_BEACON_ACQUISITION );
+ }
+ }
+ else
+ {
+ Ctx.LoRaMacClassBParams.MlmeIndication->MlmeIndication = MLME_BEACON_LOST;
+ Ctx.LoRaMacClassBParams.MlmeIndication->Status = LORAMAC_EVENT_INFO_STATUS_OK;
+ Ctx.LoRaMacClassBParams.LoRaMacFlags->Bits.MlmeInd = 1;
+ }
+
+ // Stop slot timers
+ LoRaMacClassBStopRxSlots( );
+
+ // Initialize default state for class b
+ InitClassBDefaults( );
+
+ Ctx.LoRaMacClassBParams.LoRaMacFlags->Bits.MacDone = 1;
+
+ break;
+ }
+ default:
+ {
+ Ctx.BeaconState = BEACON_STATE_ACQUISITION;
+ break;
+ }
+ }
+
+ if( activateTimer == true )
+ {
+ TimerSetValue( &Ctx.BeaconTimer, beaconEventTime );
+ TimerStart( &Ctx.BeaconTimer );
+ }
+}
+#endif // LORAMAC_CLASSB_ENABLED
+
+void LoRaMacClassBPingSlotTimerEvent( void* context )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ LoRaMacClassBEvents.Events.PingSlot = 1;
+
+ if( Ctx.LoRaMacClassBCallbacks.MacProcessNotify != NULL )
+ {
+ Ctx.LoRaMacClassBCallbacks.MacProcessNotify( );
+ }
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+#ifdef LORAMAC_CLASSB_ENABLED
+static void LoRaMacClassBProcessPingSlot( void )
+{
+ static RxConfigParams_t pingSlotRxConfig;
+ TimerTime_t pingSlotTime = 0;
+
+ switch( Ctx.PingSlotState )
+ {
+ case PINGSLOT_STATE_CALC_PING_OFFSET:
+ {
+ ComputePingOffset( Ctx.BeaconCtx.BeaconTime.Seconds,
+ *Ctx.LoRaMacClassBParams.LoRaMacDevAddr,
+ Ctx.NvmCtx->PingSlotCtx.PingPeriod,
+ &( Ctx.PingSlotCtx.PingOffset ) );
+ Ctx.PingSlotState = PINGSLOT_STATE_SET_TIMER;
+ }
+ // Intentional fall through
+ case PINGSLOT_STATE_SET_TIMER:
+ {
+ if( CalcNextSlotTime( Ctx.PingSlotCtx.PingOffset, Ctx.NvmCtx->PingSlotCtx.PingPeriod, Ctx.NvmCtx->PingSlotCtx.PingNb, &pingSlotTime ) == true )
+ {
+ if( Ctx.BeaconCtx.Ctrl.BeaconAcquired == 1 )
+ {
+ // Compute the symbol timeout. Apply it only, if the beacon is acquired
+ // Otherwise, take the enlargement of the symbols into account.
+ RegionComputeRxWindowParameters( *Ctx.LoRaMacClassBParams.LoRaMacRegion,
+ Ctx.NvmCtx->PingSlotCtx.Datarate,
+ Ctx.LoRaMacClassBParams.LoRaMacParams->MinRxSymbols,
+ Ctx.LoRaMacClassBParams.LoRaMacParams->SystemMaxRxError,
+ &pingSlotRxConfig );
+ Ctx.PingSlotCtx.SymbolTimeout = pingSlotRxConfig.WindowTimeout;
+
+ if( ( int32_t )pingSlotTime > pingSlotRxConfig.WindowOffset )
+ {// Apply the window offset
+ pingSlotTime += pingSlotRxConfig.WindowOffset;
+ }
+ }
+
+ // Start the timer if the ping slot time is in range
+ Ctx.PingSlotState = PINGSLOT_STATE_IDLE;
+ TimerSetValue( &Ctx.PingSlotTimer, pingSlotTime );
+ TimerStart( &Ctx.PingSlotTimer );
+ }
+ break;
+ }
+ case PINGSLOT_STATE_IDLE:
+ {
+ uint32_t frequency = Ctx.NvmCtx->PingSlotCtx.Frequency;
+
+ // Apply a custom frequency if the following bit is set
+ if( Ctx.NvmCtx->PingSlotCtx.Ctrl.CustomFreq == 0 )
+ {
+ // Restore floor plan
+ frequency = CalcDownlinkChannelAndFrequency( *Ctx.LoRaMacClassBParams.LoRaMacDevAddr, Ctx.BeaconCtx.BeaconTime.Seconds, CLASSB_BEACON_INTERVAL );
+ }
+
+ // Open the ping slot window only, if there is no multicast ping slot
+ // open. Multicast ping slots have always priority
+ if( Ctx.MulticastSlotState != PINGSLOT_STATE_RX )
+ {
+ Ctx.PingSlotState = PINGSLOT_STATE_RX;
+
+ pingSlotRxConfig.Datarate = Ctx.NvmCtx->PingSlotCtx.Datarate;
+ pingSlotRxConfig.DownlinkDwellTime = Ctx.LoRaMacClassBParams.LoRaMacParams->DownlinkDwellTime;
+ pingSlotRxConfig.RepeaterSupport = Ctx.LoRaMacClassBParams.LoRaMacParams->RepeaterSupport;
+ pingSlotRxConfig.Frequency = frequency;
+ pingSlotRxConfig.RxContinuous = false;
+ pingSlotRxConfig.RxSlot = RX_SLOT_WIN_CLASS_B_PING_SLOT;
+
+ RegionRxConfig( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &pingSlotRxConfig, ( int8_t* )&Ctx.LoRaMacClassBParams.McpsIndication->RxDatarate );
+
+ if( pingSlotRxConfig.RxContinuous == false )
+ {
+ Radio.Rx( Ctx.LoRaMacClassBParams.LoRaMacParams->MaxRxWindow );
+ }
+ else
+ {
+ Radio.Rx( 0 ); // Continuous mode
+ }
+ }
+ else
+ {
+ // Multicast slots have priority. Skip Rx
+ Ctx.PingSlotState = PINGSLOT_STATE_CALC_PING_OFFSET;
+ TimerSetValue( &Ctx.PingSlotTimer, CLASSB_PING_SLOT_WINDOW );
+ TimerStart( &Ctx.PingSlotTimer );
+ }
+ break;
+ }
+ default:
+ {
+ Ctx.PingSlotState = PINGSLOT_STATE_CALC_PING_OFFSET;
+ break;
+ }
+ }
+}
+#endif // LORAMAC_CLASSB_ENABLED
+
+void LoRaMacClassBMulticastSlotTimerEvent( void* context )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ LoRaMacClassBEvents.Events.MulticastSlot = 1;
+
+ if( Ctx.LoRaMacClassBCallbacks.MacProcessNotify != NULL )
+ {
+ Ctx.LoRaMacClassBCallbacks.MacProcessNotify( );
+ }
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+#ifdef LORAMAC_CLASSB_ENABLED
+static void LoRaMacClassBProcessMulticastSlot( void )
+{
+ static RxConfigParams_t multicastSlotRxConfig;
+ TimerTime_t multicastSlotTime = 0;
+ TimerTime_t slotTime = 0;
+ MulticastCtx_t *cur = Ctx.LoRaMacClassBParams.MulticastChannels;
+
+
+ if( cur == NULL )
+ {
+ return;
+ }
+
+ if( Ctx.MulticastSlotState == PINGSLOT_STATE_RX )
+ {
+ // A multicast slot is already open
+ return;
+ }
+
+ switch( Ctx.MulticastSlotState )
+ {
+ case PINGSLOT_STATE_CALC_PING_OFFSET:
+ {
+ // Compute all offsets for every multicast slots
+ for( uint8_t i = 0; i < 4; i++ )
+ {
+ ComputePingOffset( Ctx.BeaconCtx.BeaconTime.Seconds,
+ cur->ChannelParams.Address,
+ cur->PingPeriod,
+ &( cur->PingOffset ) );
+ cur++;
+ }
+ Ctx.MulticastSlotState = PINGSLOT_STATE_SET_TIMER;
+ }
+ // Intentional fall through
+ case PINGSLOT_STATE_SET_TIMER:
+ {
+ cur = Ctx.LoRaMacClassBParams.MulticastChannels;
+ Ctx.PingSlotCtx.NextMulticastChannel = NULL;
+
+ for( uint8_t i = 0; i < 4; i++ )
+ {
+ // Calculate the next slot time for every multicast slot
+ if( CalcNextSlotTime( cur->PingOffset, cur->PingPeriod, cur->PingNb, &slotTime ) == true )
+ {
+ if( ( multicastSlotTime == 0 ) || ( multicastSlotTime > slotTime ) )
+ {
+ // Update the slot time and the next multicast channel
+ multicastSlotTime = slotTime;
+ Ctx.PingSlotCtx.NextMulticastChannel = cur;
+ }
+ }
+ cur++;
+ }
+
+ // Schedule the next multicast slot
+ if( Ctx.PingSlotCtx.NextMulticastChannel != NULL )
+ {
+ if( Ctx.BeaconCtx.Ctrl.BeaconAcquired == 1 )
+ {
+ RegionComputeRxWindowParameters( *Ctx.LoRaMacClassBParams.LoRaMacRegion,
+ Ctx.NvmCtx->PingSlotCtx.Datarate,
+ Ctx.LoRaMacClassBParams.LoRaMacParams->MinRxSymbols,
+ Ctx.LoRaMacClassBParams.LoRaMacParams->SystemMaxRxError,
+ &multicastSlotRxConfig );
+ Ctx.PingSlotCtx.SymbolTimeout = multicastSlotRxConfig.WindowTimeout;
+ }
+
+ if( ( int32_t )multicastSlotTime > multicastSlotRxConfig.WindowOffset )
+ {// Apply the window offset
+ multicastSlotTime += multicastSlotRxConfig.WindowOffset;
+ }
+
+ // Start the timer if the ping slot time is in range
+ Ctx.MulticastSlotState = PINGSLOT_STATE_IDLE;
+ TimerSetValue( &Ctx.MulticastSlotTimer, multicastSlotTime );
+ TimerStart( &Ctx.MulticastSlotTimer );
+ }
+ break;
+ }
+ case PINGSLOT_STATE_IDLE:
+ {
+ uint32_t frequency = 0;
+
+ // Verify if the multicast channel is valid
+ if( Ctx.PingSlotCtx.NextMulticastChannel == NULL )
+ {
+ Ctx.MulticastSlotState = PINGSLOT_STATE_CALC_PING_OFFSET;
+ TimerSetValue( &Ctx.MulticastSlotTimer, 1 );
+ TimerStart( &Ctx.MulticastSlotTimer );
+ break;
+ }
+
+ // Apply frequency
+ frequency = Ctx.PingSlotCtx.NextMulticastChannel->ChannelParams.RxParams.ClassB.Frequency;
+
+ // Restore the floor plan frequency if there is no individual frequency assigned
+ if( frequency == 0 )
+ {
+ // Restore floor plan
+ frequency = CalcDownlinkChannelAndFrequency( Ctx.PingSlotCtx.NextMulticastChannel->ChannelParams.Address, Ctx.BeaconCtx.BeaconTime.Seconds, CLASSB_BEACON_INTERVAL );
+ }
+
+ Ctx.MulticastSlotState = PINGSLOT_STATE_RX;
+
+ multicastSlotRxConfig.Datarate = Ctx.PingSlotCtx.NextMulticastChannel->ChannelParams.RxParams.ClassB.Datarate;
+ multicastSlotRxConfig.DownlinkDwellTime = Ctx.LoRaMacClassBParams.LoRaMacParams->DownlinkDwellTime;
+ multicastSlotRxConfig.RepeaterSupport = Ctx.LoRaMacClassBParams.LoRaMacParams->RepeaterSupport;
+ multicastSlotRxConfig.Frequency = frequency;
+ multicastSlotRxConfig.RxContinuous = false;
+ multicastSlotRxConfig.RxSlot = RX_SLOT_WIN_CLASS_B_MULTICAST_SLOT;
+
+ RegionRxConfig( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &multicastSlotRxConfig, ( int8_t* )&Ctx.LoRaMacClassBParams.McpsIndication->RxDatarate );
+
+ if( Ctx.PingSlotState == PINGSLOT_STATE_RX )
+ {
+ // Close ping slot window, if necessary. Multicast slots have priority
+ Radio.Standby( );
+ Ctx.PingSlotState = PINGSLOT_STATE_CALC_PING_OFFSET;
+ TimerSetValue( &Ctx.PingSlotTimer, CLASSB_PING_SLOT_WINDOW );
+ TimerStart( &Ctx.PingSlotTimer );
+ }
+
+ if( multicastSlotRxConfig.RxContinuous == false )
+ {
+ Radio.Rx( Ctx.LoRaMacClassBParams.LoRaMacParams->MaxRxWindow );
+ }
+ else
+ {
+ Radio.Rx( 0 ); // Continuous mode
+ }
+ break;
+ }
+ default:
+ {
+ Ctx.MulticastSlotState = PINGSLOT_STATE_CALC_PING_OFFSET;
+ break;
+ }
+ }
+}
+#endif // LORAMAC_CLASSB_ENABLED
+
+bool LoRaMacClassBRxBeacon( uint8_t *payload, uint16_t size )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ GetPhyParams_t getPhy;
+ PhyParam_t phyParam;
+ bool beaconProcessed = false;
+ uint16_t crc0 = 0;
+ uint16_t crc1 = 0;
+ uint16_t beaconCrc0 = 0;
+ uint16_t beaconCrc1 = 0;
+
+ getPhy.Attribute = PHY_BEACON_FORMAT;
+ phyParam = RegionGetPhyParam( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &getPhy );
+
+ // Verify if we are in the state where we expect a beacon
+ if( ( Ctx.BeaconState == BEACON_STATE_RX ) || ( Ctx.BeaconCtx.Ctrl.AcquisitionPending == 1 ) )
+ {
+ if( size == phyParam.BeaconFormat.BeaconSize )
+ {
+ // A beacon frame is defined as:
+ // Bytes: | x | 4 | 2 | 7 | y | 2 |
+ // |------|------|------|------------|------|------|
+ // Field: | RFU1 | Time | CRC1 | GwSpecific | RFU2 | CRC2 |
+ //
+ // Field RFU1 and RFU2 have variable sizes. It depends on the region specific implementation
+
+ // Read CRC1 field from the frame
+ beaconCrc0 = ( ( uint16_t )payload[phyParam.BeaconFormat.Rfu1Size + 4] ) & 0x00FF;
+ beaconCrc0 |= ( ( uint16_t )payload[phyParam.BeaconFormat.Rfu1Size + 4 + 1] << 8 ) & 0xFF00;
+ crc0 = BeaconCrc( payload, phyParam.BeaconFormat.Rfu1Size + 4 );
+
+ // Validate the first crc of the beacon frame
+ if( crc0 == beaconCrc0 )
+ {
+ // Read Time field from the frame
+ Ctx.BeaconCtx.BeaconTime.Seconds = ( ( uint32_t )payload[phyParam.BeaconFormat.Rfu1Size] ) & 0x000000FF;
+ Ctx.BeaconCtx.BeaconTime.Seconds |= ( ( uint32_t )( payload[phyParam.BeaconFormat.Rfu1Size + 1] << 8 ) ) & 0x0000FF00;
+ Ctx.BeaconCtx.BeaconTime.Seconds |= ( ( uint32_t )( payload[phyParam.BeaconFormat.Rfu1Size + 2] << 16 ) ) & 0x00FF0000;
+ Ctx.BeaconCtx.BeaconTime.Seconds |= ( ( uint32_t )( payload[phyParam.BeaconFormat.Rfu1Size + 3] << 24 ) ) & 0xFF000000;
+ Ctx.BeaconCtx.BeaconTime.SubSeconds = 0;
+ Ctx.LoRaMacClassBParams.MlmeIndication->BeaconInfo.Time = Ctx.BeaconCtx.BeaconTime;
+ beaconProcessed = true;
+ }
+
+ // Read CRC2 field from the frame
+ beaconCrc1 = ( ( uint16_t )payload[phyParam.BeaconFormat.Rfu1Size + 4 + 2 + 7 + phyParam.BeaconFormat.Rfu2Size] ) & 0x00FF;
+ beaconCrc1 |= ( ( uint16_t )payload[phyParam.BeaconFormat.Rfu1Size + 4 + 2 + 7 + phyParam.BeaconFormat.Rfu2Size + 1] << 8 ) & 0xFF00;
+ crc1 = BeaconCrc( &payload[phyParam.BeaconFormat.Rfu1Size + 4 + 2], 7 + phyParam.BeaconFormat.Rfu2Size );
+
+ // Validate the second crc of the beacon frame
+ if( crc1 == beaconCrc1 )
+ {
+ // Read GwSpecific field from the frame
+ // The GwSpecific field contains 1 byte InfoDesc and 6 bytes Info
+ Ctx.LoRaMacClassBParams.MlmeIndication->BeaconInfo.GwSpecific.InfoDesc = payload[phyParam.BeaconFormat.Rfu1Size + 4 + 2];
+ memcpy1( Ctx.LoRaMacClassBParams.MlmeIndication->BeaconInfo.GwSpecific.Info, &payload[phyParam.BeaconFormat.Rfu1Size + 4 + 2 + 1], 6 );
+
+ Ctx.NvmCtx->BeaconCtx.GwSpecific.InfoDesc = Ctx.LoRaMacClassBParams.MlmeIndication->BeaconInfo.GwSpecific.InfoDesc;
+ memcpy1( Ctx.NvmCtx->BeaconCtx.GwSpecific.Info, Ctx.LoRaMacClassBParams.MlmeIndication->BeaconInfo.GwSpecific.Info, 6 );
+ }
+
+ // Reset beacon variables, if one of the crc is valid
+ if( beaconProcessed == true )
+ {
+ TimerTime_t time = Radio.TimeOnAir( MODEM_LORA, size );
+ SysTime_t timeOnAir;
+ timeOnAir.Seconds = time / 1000;
+ timeOnAir.SubSeconds = time - timeOnAir.Seconds * 1000;
+
+ Ctx.BeaconCtx.LastBeaconRx = Ctx.BeaconCtx.BeaconTime;
+ Ctx.BeaconCtx.LastBeaconRx.Seconds += UNIX_GPS_EPOCH_OFFSET;
+
+ // Update system time.
+ SysTimeSet( SysTimeAdd( Ctx.BeaconCtx.LastBeaconRx, timeOnAir ) );
+
+ Ctx.NvmCtx->BeaconCtx.LastBeaconRx = Ctx.BeaconCtx.LastBeaconRx;
+
+ Ctx.BeaconCtx.Ctrl.BeaconAcquired = 1;
+ Ctx.BeaconCtx.Ctrl.BeaconMode = 1;
+ ResetWindowTimeout( );
+ Ctx.BeaconState = BEACON_STATE_LOCKED;
+ Ctx.NvmCtx->BeaconCtx.BeaconState = Ctx.BeaconState;
+ LoRaMacClassBBeaconTimerEvent( NULL );
+ }
+ }
+
+ if( Ctx.BeaconState == BEACON_STATE_RX )
+ {
+ Ctx.BeaconState = BEACON_STATE_TIMEOUT;
+ Ctx.NvmCtx->BeaconCtx.BeaconState = Ctx.BeaconState;
+ LoRaMacClassBBeaconTimerEvent( NULL );
+ }
+ // When the MAC listens for a beacon, it is not allowed to process any other
+ // downlink except the beacon frame itself. The reason for this is that no valid downlink window is open.
+ // If it receives a frame which is
+ // 1. not a beacon or
+ // 2. a beacon with a crc fail
+ // the MAC shall ignore the frame completely. Thus, the function must always return true, even if no
+ // valid beacon has been received.
+ beaconProcessed = true;
+ }
+ return beaconProcessed;
+#else
+ return false;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+bool LoRaMacClassBIsBeaconExpected( void )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ if( ( Ctx.BeaconCtx.Ctrl.AcquisitionPending == 1 ) ||
+ ( Ctx.BeaconState == BEACON_STATE_RX ) )
+ {
+ return true;
+ }
+ return false;
+#else
+ return false;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+bool LoRaMacClassBIsPingExpected( void )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ if( Ctx.PingSlotState == PINGSLOT_STATE_RX )
+ {
+ return true;
+ }
+ return false;
+#else
+ return false;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+bool LoRaMacClassBIsMulticastExpected( void )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ if( Ctx.MulticastSlotState == PINGSLOT_STATE_RX )
+ {
+ return true;
+ }
+ return false;
+#else
+ return false;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+bool LoRaMacClassBIsAcquisitionPending( void )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ if( Ctx.BeaconCtx.Ctrl.AcquisitionPending == 1 )
+ {
+ return true;
+ }
+ return false;
+#else
+ return false;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+bool LoRaMacClassBIsBeaconModeActive( void )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ if( ( Ctx.BeaconCtx.Ctrl.BeaconMode == 1 ) ||
+ ( Ctx.BeaconState == BEACON_STATE_ACQUISITION_BY_TIME ) )
+ {
+ return true;
+ }
+ return false;
+#else
+ return false;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+void LoRaMacClassBSetPingSlotInfo( uint8_t periodicity )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ Ctx.NvmCtx->PingSlotCtx.PingNb = CalcPingNb( periodicity );
+ Ctx.NvmCtx->PingSlotCtx.PingPeriod = CalcPingPeriod( Ctx.NvmCtx->PingSlotCtx.PingNb );
+ NvmContextChange( );
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+void LoRaMacClassBHaltBeaconing( void )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ if( Ctx.BeaconCtx.Ctrl.BeaconMode == 1 )
+ {
+ if( ( Ctx.BeaconState == BEACON_STATE_TIMEOUT ) ||
+ ( Ctx.BeaconState == BEACON_STATE_LOST ) )
+ {
+ // Update the state machine before halt
+ LoRaMacClassBBeaconTimerEvent( NULL );
+ }
+
+ CRITICAL_SECTION_BEGIN( );
+ LoRaMacClassBEvents.Events.Beacon = 0;
+ CRITICAL_SECTION_END( );
+
+ // Halt ping slot state machine
+ TimerStop( &Ctx.BeaconTimer );
+
+ // Halt beacon state machine
+ Ctx.BeaconState = BEACON_STATE_HALT;
+ Ctx.NvmCtx->BeaconCtx.BeaconState = Ctx.BeaconState;
+
+ // Halt ping and multicast slot state machines
+ LoRaMacClassBStopRxSlots( );
+ }
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+void LoRaMacClassBResumeBeaconing( void )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ if( Ctx.BeaconState == BEACON_STATE_HALT )
+ {
+ Ctx.BeaconCtx.Ctrl.ResumeBeaconing = 1;
+
+ // Set default state
+ Ctx.BeaconState = BEACON_STATE_LOCKED;
+
+ if( Ctx.BeaconCtx.Ctrl.BeaconAcquired == 0 )
+ {
+ // Set the default state for beacon less operation
+ Ctx.BeaconState = BEACON_STATE_REACQUISITION;
+ }
+
+ Ctx.NvmCtx->BeaconCtx.BeaconState = Ctx.BeaconState;
+ LoRaMacClassBBeaconTimerEvent( NULL );
+ }
+#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( ( Ctx.BeaconCtx.Ctrl.BeaconMode == 1 ) && ( Ctx.NvmCtx->PingSlotCtx.Ctrl.Assigned == 1 ) )
+ {
+ return LORAMAC_STATUS_OK;
+ }
+ }
+ if( nextClass == CLASS_A )
+ {// Switch from class b to class a
+ LoRaMacClassBHaltBeaconing( );
+
+ // Initialize default state for class b
+ InitClassBDefaults( );
+
+ 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_PING_SLOT_DATARATE:
+ {
+ mibGet->Param.PingSlotDatarate = Ctx.NvmCtx->PingSlotCtx.Datarate;
+ 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_PING_SLOT_DATARATE:
+ {
+ Ctx.NvmCtx->PingSlotCtx.Datarate = mibSet->Param.PingSlotDatarate;
+ NvmContextChange( );
+ 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
+ if( LoRaMacConfirmQueueIsCmdActive( MLME_PING_SLOT_INFO ) == true )
+ {
+ LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_OK, MLME_PING_SLOT_INFO );
+ Ctx.NvmCtx->PingSlotCtx.Ctrl.Assigned = 1;
+ NvmContextChange( );
+ }
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+uint8_t LoRaMacClassBPingSlotChannelReq( uint8_t datarate, uint32_t frequency )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ uint8_t status = 0x03;
+ VerifyParams_t verify;
+ bool isCustomFreq = false;
+
+ if( frequency != 0 )
+ {
+ isCustomFreq = true;
+ verify.Frequency = frequency;
+ if( RegionVerify( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &verify, PHY_FREQUENCY ) == false )
+ {
+ status &= 0xFE; // Channel frequency KO
+ }
+ }
+
+ verify.DatarateParams.Datarate = datarate;
+ verify.DatarateParams.DownlinkDwellTime = Ctx.LoRaMacClassBParams.LoRaMacParams->DownlinkDwellTime;
+
+ if( RegionVerify( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &verify, PHY_RX_DR ) == false )
+ {
+ status &= 0xFD; // Datarate range KO
+ }
+
+ if( status == 0x03 )
+ {
+ if( isCustomFreq == true )
+ {
+ Ctx.NvmCtx->PingSlotCtx.Ctrl.CustomFreq = 1;
+ Ctx.NvmCtx->PingSlotCtx.Frequency = frequency;
+ }
+ else
+ {
+ Ctx.NvmCtx->PingSlotCtx.Ctrl.CustomFreq = 0;
+ Ctx.NvmCtx->PingSlotCtx.Frequency = 0;
+ }
+ Ctx.NvmCtx->PingSlotCtx.Datarate = datarate;
+ NvmContextChange( );
+ }
+
+ return status;
+#else
+ return 0;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+void LoRaMacClassBBeaconTimingAns( uint16_t beaconTimingDelay, uint8_t beaconTimingChannel, TimerTime_t lastRxDone )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ Ctx.BeaconCtx.BeaconTimingDelay = ( CLASSB_BEACON_DELAY_BEACON_TIMING_ANS * beaconTimingDelay );
+ Ctx.BeaconCtx.BeaconTimingChannel = beaconTimingChannel;
+
+ if( LoRaMacConfirmQueueIsCmdActive( MLME_BEACON_TIMING ) == true )
+ {
+ if( Ctx.BeaconCtx.BeaconTimingDelay > CLASSB_BEACON_INTERVAL )
+ {
+ // We missed the beacon already
+ Ctx.BeaconCtx.BeaconTimingDelay = 0;
+ Ctx.BeaconCtx.BeaconTimingChannel = 0;
+ LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_BEACON_NOT_FOUND, MLME_BEACON_TIMING );
+ }
+ else
+ {
+ Ctx.BeaconCtx.Ctrl.BeaconDelaySet = 1;
+ Ctx.BeaconCtx.Ctrl.BeaconChannelSet = 1;
+ Ctx.BeaconCtx.NextBeaconRx = SysTimeFromMs( lastRxDone + Ctx.BeaconCtx.BeaconTimingDelay );
+ LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_OK, MLME_BEACON_TIMING );
+ }
+
+ Ctx.LoRaMacClassBParams.MlmeConfirm->BeaconTimingDelay = Ctx.BeaconCtx.BeaconTimingDelay;
+ Ctx.LoRaMacClassBParams.MlmeConfirm->BeaconTimingChannel = Ctx.BeaconCtx.BeaconTimingChannel;
+ }
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+void LoRaMacClassBDeviceTimeAns( void )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+
+ SysTime_t nextBeacon = SysTimeGet( );
+ uint32_t currentTimeMs = SysTimeToMs( nextBeacon );
+
+ nextBeacon.Seconds = nextBeacon.Seconds + ( 128 - ( nextBeacon.Seconds % 128 ) );
+ nextBeacon.SubSeconds = 0;
+
+ Ctx.BeaconCtx.NextBeaconRx = nextBeacon;
+ Ctx.BeaconCtx.LastBeaconRx = SysTimeSub( Ctx.BeaconCtx.NextBeaconRx, ( SysTime_t ){ .Seconds = CLASSB_BEACON_INTERVAL / 1000, .SubSeconds = 0 } );
+
+ if( LoRaMacConfirmQueueIsCmdActive( MLME_DEVICE_TIME ) == true )
+ {
+ if( currentTimeMs > SysTimeToMs( Ctx.BeaconCtx.NextBeaconRx ) )
+ {
+ // We missed the beacon already
+ Ctx.BeaconCtx.LastBeaconRx.Seconds = 0;
+ Ctx.BeaconCtx.LastBeaconRx.SubSeconds = 0;
+ Ctx.BeaconCtx.NextBeaconRx.Seconds = 0;
+ Ctx.BeaconCtx.NextBeaconRx.SubSeconds = 0;
+ LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_BEACON_NOT_FOUND, MLME_DEVICE_TIME );
+ }
+ else
+ {
+ Ctx.BeaconCtx.Ctrl.BeaconDelaySet = 1;
+ Ctx.BeaconCtx.BeaconTimingDelay = SysTimeToMs( Ctx.BeaconCtx.NextBeaconRx ) - currentTimeMs;
+ Ctx.BeaconCtx.BeaconTime.Seconds = nextBeacon.Seconds - UNIX_GPS_EPOCH_OFFSET - 128;
+ Ctx.BeaconCtx.BeaconTime.SubSeconds = 0;
+ LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_OK, MLME_DEVICE_TIME );
+ }
+ }
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+bool LoRaMacClassBBeaconFreqReq( uint32_t frequency )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ VerifyParams_t verify;
+
+ if( frequency != 0 )
+ {
+ verify.Frequency = frequency;
+
+ if( RegionVerify( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &verify, PHY_FREQUENCY ) == true )
+ {
+ Ctx.NvmCtx->BeaconCtx.Ctrl.CustomFreq = 1;
+ Ctx.NvmCtx->BeaconCtx.Frequency = frequency;
+ NvmContextChange( );
+ return true;
+ }
+ }
+ else
+ {
+ Ctx.NvmCtx->BeaconCtx.Ctrl.CustomFreq = 0;
+ NvmContextChange( );
+ return true;
+ }
+ return false;
+#else
+ return false;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+TimerTime_t LoRaMacClassBIsUplinkCollision( TimerTime_t txTimeOnAir )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ TimerTime_t currentTime = TimerGetCurrentTime( );
+ TimerTime_t beaconReserved = 0;
+ TimerTime_t nextBeacon = SysTimeToMs( Ctx.BeaconCtx.NextBeaconRx );
+
+ beaconReserved = nextBeacon -
+ CLASSB_BEACON_GUARD -
+ Ctx.LoRaMacClassBParams.LoRaMacParams->ReceiveDelay1 -
+ Ctx.LoRaMacClassBParams.LoRaMacParams->ReceiveDelay2 -
+ txTimeOnAir;
+
+ // Check if the next beacon will be received during the next uplink.
+ if( ( currentTime >= beaconReserved ) && ( currentTime < ( nextBeacon + CLASSB_BEACON_RESERVED ) ) )
+ {// Next beacon will be sent during the next uplink.
+ return CLASSB_BEACON_RESERVED;
+ }
+ return 0;
+#else
+ return 0;
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+void LoRaMacClassBStopRxSlots( void )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ TimerStop( &Ctx.PingSlotTimer );
+ TimerStop( &Ctx.MulticastSlotTimer );
+
+ CRITICAL_SECTION_BEGIN( );
+ LoRaMacClassBEvents.Events.PingSlot = 0;
+ LoRaMacClassBEvents.Events.MulticastSlot = 0;
+ CRITICAL_SECTION_END( );
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+void LoRaMacClassBStartRxSlots( void )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ if( Ctx.NvmCtx->PingSlotCtx.Ctrl.Assigned == 1 )
+ {
+ Ctx.PingSlotState = PINGSLOT_STATE_CALC_PING_OFFSET;
+ TimerSetValue( &Ctx.PingSlotTimer, 1 );
+ TimerStart( &Ctx.PingSlotTimer );
+
+ Ctx.MulticastSlotState = PINGSLOT_STATE_CALC_PING_OFFSET;
+ TimerSetValue( &Ctx.MulticastSlotTimer, 1 );
+ TimerStart( &Ctx.MulticastSlotTimer );
+ }
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+void LoRaMacClassBSetMulticastPeriodicity( MulticastCtx_t* multicastChannel )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ if( multicastChannel != NULL )
+ {
+ multicastChannel->PingNb = CalcPingNb( multicastChannel->ChannelParams.RxParams.ClassB.Periodicity );
+ multicastChannel->PingPeriod = CalcPingPeriod( multicastChannel->PingNb );
+ }
+#endif // LORAMAC_CLASSB_ENABLED
+}
+
+void LoRaMacClassBProcess( void )
+{
+#ifdef LORAMAC_CLASSB_ENABLED
+ LoRaMacClassBEvents_t events;
+
+ CRITICAL_SECTION_BEGIN( );
+ events = LoRaMacClassBEvents;
+ LoRaMacClassBEvents.Value = 0;
+ CRITICAL_SECTION_END( );
+
+ if( events.Value != 0 )
+ {
+ if( events.Events.Beacon == 1 )
+ {
+ LoRaMacClassBProcessBeacon( );
+ }
+ if( events.Events.PingSlot == 1 )
+ {
+ LoRaMacClassBProcessPingSlot( );
+ }
+ if( events.Events.MulticastSlot == 1 )
+ {
+ LoRaMacClassBProcessMulticastSlot( );
+ }
+ }
+#endif // LORAMAC_CLASSB_ENABLED
+}
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacClassB.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacClassB.h
new file mode 100644
index 00000000..81b92b12
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacClassB.h
@@ -0,0 +1,670 @@
+/*!
+ * \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__
+
+#include "systime.h"
+#include "LoRaMacTypes.h"
+
+/*!
+ * States of the class B beacon acquisition and tracking
+ */
+typedef enum eBeaconState
+{
+ /*!
+ * Initial state to acquire the beacon
+ */
+ BEACON_STATE_ACQUISITION,
+ /*!
+ * Beacon acquisition state when a time reference is available
+ */
+ BEACON_STATE_ACQUISITION_BY_TIME,
+ /*!
+ * 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_LOST,
+}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
+{
+
+ /*!
+ * Ping slot length time in ms
+ */
+ uint32_t PingSlotWindow;
+ /*!
+ * Ping offset
+ */
+ uint16_t PingOffset;
+ /*!
+ * Current symbol timeout. The node enlarges this variable in case of beacon
+ * loss.
+ */
+ uint16_t SymbolTimeout;
+ /*!
+ * The multicast channel which will be enabled next.
+ */
+ MulticastCtx_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 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 the beacon state machine will be resumed
+ */
+ uint8_t ResumeBeaconing : 1;
+ }Ctrl;
+
+ /*!
+ * Current temperature
+ */
+ float Temperature;
+ /*!
+ * Beacon time received with the beacon frame
+ */
+ SysTime_t BeaconTime;
+ /*!
+ * Time when the last beacon was received
+ */
+ SysTime_t LastBeaconRx;
+ /*!
+ * Time when the next beacon will be received
+ */
+ SysTime_t NextBeaconRx;
+ /*!
+ * This is the time where the RX window will be opened.
+ * Its base is NextBeaconRx with temperature compensations
+ * and RX window movement.
+ */
+ TimerTime_t NextBeaconRxAdjusted;
+ /*!
+ * Current symbol timeout. The node enlarges this variable in case of beacon
+ * loss.
+ */
+ uint16_t SymbolTimeout;
+ /*!
+ * Specifies how much time the beacon window will be moved.
+ */
+ TimerTime_t BeaconWindowMovement;
+ /*!
+ * Beacon timing channel for next beacon
+ */
+ uint8_t BeaconTimingChannel;
+ /*!
+ * Delay for next beacon in ms
+ */
+ TimerTime_t BeaconTimingDelay;
+ TimerTime_t TimeStamp;
+}BeaconContext_t;
+
+/*!
+ * Data structure which contains the callbacks
+ */
+typedef struct sLoRaMacClassBCallback
+{
+ /*!
+ * \brief Measures the temperature level
+ *
+ * \retval Temperature level
+ */
+ uint16_t ( *GetTemperatureLevel )( void );
+ /*!
+ *\brief Will be called each time a Radio IRQ is handled by the MAC
+ * layer.
+ *
+ *\warning Runs in a IRQ context. Should only change variables state.
+ */
+ void ( *MacProcessNotify )( void );
+}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 LoRaMac region definition
+ */
+ LoRaMacRegion_t *LoRaMacRegion;
+ /*!
+ * Pointer to the LoRaMacParams structure
+ */
+ LoRaMacParams_t *LoRaMacParams;
+ /*!
+ * Pointer to the multicast channel list
+ */
+ MulticastCtx_t *MulticastChannels;
+}LoRaMacClassBParams_t;
+
+
+/*!
+ * Signature of callback function to be called by this module when the
+ * non-volatile needs to be saved.
+ */
+typedef void ( *LoRaMacClassBNvmEvent )( void );
+
+/*!
+ * \brief Initialize LoRaWAN Class B
+ *
+ * \param [IN] classBParams Information and feedback parameter
+ * \param [IN] callbacks Contains the callback which the Class B implementation needs
+ * \param [IN] callback function which will be called when the non-volatile context needs to be saved.
+ */
+void LoRaMacClassBInit( LoRaMacClassBParams_t *classBParams, LoRaMacClassBCallback_t *callbacks, LoRaMacClassBNvmEvent classBNvmCtxChanged );
+
+/*!
+ * Restores the internal non-volatile context from passed pointer.
+ *
+ * \param [IN] classBNvmCtx - Pointer to non-volatile class B module context to be restored.
+ *
+ * \retval - Status of the operation
+ */
+bool LoRaMacClassBRestoreNvmCtx( void* classBNvmCtx );
+
+/*!
+ * Returns a pointer to the internal non-volatile context.
+ *
+ * \param [IN] classBNvmCtxSize - Size of the module non-volatile context
+ *
+ * \retval - Points to a structure where the module store its non-volatile context
+ */
+void* LoRaMacClassBGetNvmCtx( size_t* classBNvmCtxSize );
+
+/*!
+ * \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 Set the state of the multicast slot state machine
+ *
+ * \param [IN] pingSlotState multicast slot state.
+ */
+void LoRaMacClassBSetMulticastSlotState( PingSlotState_t multicastSlotState );
+
+/*!
+ * \brief Verifies if an acquisition procedure is in progress
+ *
+ * \retval [true, if the acquisition is in progress; false, if not]
+ */
+bool LoRaMacClassBIsAcquisitionInProgress( void );
+
+/*!
+ * \brief State machine of the Class B for beaconing
+ */
+void LoRaMacClassBBeaconTimerEvent( void* context );
+
+/*!
+ * \brief State machine of the Class B for ping slots
+ */
+void LoRaMacClassBPingSlotTimerEvent( void* context );
+
+/*!
+ * \brief State machine of the Class B for multicast slots
+ */
+void LoRaMacClassBMulticastSlotTimerEvent( void* context );
+
+/*!
+ * \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 The function validates, if the node expects a multicast slot
+ * at the current time.
+ *
+ * \retval [true, if the node expects a multicast slot; false, if not]
+ */
+bool LoRaMacClassBIsMulticastExpected( 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 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
+ * \param [IN] lastRxDone The time of the last frame reception
+ */
+void LoRaMacClassBBeaconTimingAns( uint16_t beaconTimingDelay, uint8_t beaconTimingChannel, TimerTime_t lastRxDone );
+
+/*!
+ * \brief This function handles the ClassB DEVICE_TIME_ANS
+ */
+void LoRaMacClassBDeviceTimeAns( void );
+
+/*!
+ * \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 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 );
+
+/*!
+ * \brief Stops the timers for the RX slots. This includes the
+ * timers for ping and multicast slots.
+ */
+void LoRaMacClassBStopRxSlots( void );
+
+/*!
+ * \brief Starts the timers for the RX slots. This includes the
+ * timers for ping and multicast slots.
+ */
+void LoRaMacClassBStartRxSlots( void );
+
+/*!
+ * \brief Starts the timers for the RX slots. This includes the
+ * timers for ping and multicast slots.
+ *
+ * \param [IN] periodicity Downlink periodicity
+ *
+ * \param [IN] multicastChannel Related multicast channel
+ */
+void LoRaMacClassBSetMulticastPeriodicity( MulticastCtx_t* multicastChannel );
+
+void LoRaMacClassBProcess( void );
+
+
+/*
+ * LoRaMac Class B Context structure for NVM parameters
+ * related to ping slots
+ */
+typedef struct sLoRaMacClassBPingSlotNvmCtx
+{
+ struct sPingSlotCtrlNvm
+ {
+ /*!
+ * 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;
+ /*!
+ * Number of ping slots
+ */
+ uint8_t PingNb;
+ /*!
+ * Period of the ping slots
+ */
+ uint16_t PingPeriod;
+ /*!
+ * Reception frequency of the ping slot windows
+ */
+ uint32_t Frequency;
+ /*!
+ * Datarate of the ping slot
+ */
+ int8_t Datarate;
+} LoRaMacClassBPingSlotNvmCtx_t;
+
+/*
+ * LoRaMac Class B Context structure for NVM parameters
+ * related to beaconing
+ */
+typedef struct sLoRaMacClassBBeaconNvmCtx
+{
+ struct sBeaconCtrlNvm
+ {
+ /*!
+ * Set if the node has a custom frequency for beaconing and ping slots
+ */
+ uint8_t CustomFreq : 1;
+ }Ctrl;
+ /*!
+ * Beacon reception frequency
+ */
+ uint32_t Frequency;
+ /*!
+ * State of the beaconing mechanism
+ */
+ BeaconState_t BeaconState;
+ /*!
+ * Time when the last beacon was received
+ */
+ SysTime_t LastBeaconRx;
+ /*!
+ * Data structure for the gateway specific part. The
+ * content of the values may differ for each gateway
+ */
+ struct sGwSpecific GwSpecific;
+} LoRaMacClassBBeaconNvmCtx_t;
+
+/*
+ * LoRaMac Class B Context structure
+ */
+typedef struct sLoRaMacClassBNvmCtx
+{
+ /*!
+ * Class B ping slot context
+ */
+ LoRaMacClassBPingSlotNvmCtx_t PingSlotCtx;
+ /*!
+ * Class B beacon context
+ */
+ LoRaMacClassBBeaconNvmCtx_t BeaconCtx;
+} LoRaMacClassBNvmCtx_t;
+
+/*
+ * LoRaMac Class B Context structure
+ */
+typedef struct sLoRaMacClassBCtx
+{
+ /*!
+ * Class B ping slot context
+ */
+ PingSlotContext_t PingSlotCtx;
+ /*!
+ * Class B beacon context
+ */
+ BeaconContext_t BeaconCtx;
+ /*!
+ * State of the beaconing mechanism
+ */
+ BeaconState_t BeaconState;
+ /*!
+ * State of the ping slot mechanism
+ */
+ PingSlotState_t PingSlotState;
+ /*!
+ * State of the multicast slot mechanism
+ */
+ PingSlotState_t MulticastSlotState;
+ /*!
+ * Timer for CLASS B beacon acquisition and tracking.
+ */
+ TimerEvent_t BeaconTimer;
+ /*!
+ * Timer for CLASS B ping slot timer.
+ */
+ TimerEvent_t PingSlotTimer;
+ /*!
+ * Timer for CLASS B multicast ping slot timer.
+ */
+ TimerEvent_t MulticastSlotTimer;
+ /*!
+ * Container for the callbacks related to class b.
+ */
+ LoRaMacClassBCallback_t LoRaMacClassBCallbacks;
+ /*!
+ * Data structure which holds the parameters which needs to be set
+ * in class b operation.
+ */
+ LoRaMacClassBParams_t LoRaMacClassBParams;
+ /*
+ * Callback function to notify the upper layer about context change
+ */
+ LoRaMacClassBNvmEvent LoRaMacClassBNvmEvent;
+ /*!
+ * Non-volatile module context.
+ */
+ LoRaMacClassBNvmCtx_t* NvmCtx;
+} LoRaMacClassBCtx_t;
+
+#endif // __LORAMACCLASSB_H__
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacClassBConfig.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacClassBConfig.h
new file mode 100644
index 00000000..b1b089c6
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacClassBConfig.h
@@ -0,0 +1,115 @@
+/*!
+ * \file LoRaMacClassBConfig.h
+ *
+ * \brief LoRa MAC Class B configuration
+ *
+ * \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 configuration
+ * This header file contains parameters to configure the class b operation.
+ * By default, all parameters are set according to the specification.
+ * \{
+ */
+#ifndef __LORAMACCLASSBCONFIG_H__
+#define __LORAMACCLASSBCONFIG_H__
+
+/*!
+ * Defines the beacon interval in ms
+ */
+#define CLASSB_BEACON_INTERVAL 128000
+
+/*!
+ * Beacon reserved time in ms
+ */
+#define CLASSB_BEACON_RESERVED 2120
+
+/*!
+ * Beacon guard time in ms
+ */
+#define CLASSB_BEACON_GUARD 3000
+
+/*!
+ * Beacon window time in ms
+ */
+#define CLASSB_BEACON_WINDOW 122880
+
+/*!
+ * Beacon window time in numer of slots
+ */
+#define CLASSB_BEACON_WINDOW_SLOTS 4096
+
+/*!
+ * Ping slot length time in ms
+ */
+#define CLASSB_PING_SLOT_WINDOW 30
+
+/*!
+ * Maximum allowed beacon less time in ms
+ */
+#define CLASSB_MAX_BEACON_LESS_PERIOD 7200000
+
+/*!
+ * Delay time for the BeaconTimingAns in ms
+ */
+#define CLASSB_BEACON_DELAY_BEACON_TIMING_ANS 30
+
+/*!
+ * Default symbol timeout for beacons and ping slot windows
+ */
+#define CLASSB_BEACON_SYMBOL_TO_DEFAULT 8
+
+/*!
+ * Maximum symbol timeout for beacons
+ */
+#define CLASSB_BEACON_SYMBOL_TO_EXPANSION_MAX 255
+
+/*!
+ * Maximum symbol timeout for ping slots
+ */
+#define CLASSB_PING_SLOT_SYMBOL_TO_EXPANSION_MAX 30
+
+/*!
+ * Symbol expansion value for beacon windows in case of beacon
+ * loss in symbols
+ */
+#define CLASSB_BEACON_SYMBOL_TO_EXPANSION_FACTOR 2
+
+/*!
+ * Defines the default window movement time
+ */
+#define CLASSB_WINDOW_MOVE_DEFAULT 2
+
+/*!
+ * Defines the maximum time for the beacon movement
+ */
+#define CLASSB_WINDOW_MOVE_EXPANSION_MAX 256
+
+/*!
+ * Defines the expansion factor for the beacon movement
+ */
+#define CLASSB_WINDOW_MOVE_EXPANSION_FACTOR 2
+
+#endif // __LORAMACCLASSBCONFIG_H__
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacCommands.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacCommands.c
new file mode 100644
index 00000000..b330e714
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacCommands.c
@@ -0,0 +1,547 @@
+/*
+ / _____) _ | |
+( (____ _____ ____ _| |_ _____ ____| |__
+ \____ \| ___ | (_ _) ___ |/ ___) _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+ (C)2013 Semtech
+ ___ _____ _ ___ _ _____ ___ ___ ___ ___
+/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+embedded.connectivity.solutions===============
+
+Description: LoRa MAC commands
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+
+Maintainer: Miguel Luis ( Semtech ), Daniel Jaeckle ( STACKFORCE ), Johannes Bruder ( STACKFORCE )
+*/
+
+#include "utilities.h"
+#include "LoRaMacCommands.h"
+#include "LoRaMacConfirmQueue.h"
+
+/*!
+ * Number of MAC Command slots
+ */
+#define NUM_OF_MAC_COMMANDS 15
+
+/*!
+ * Size of the CID field of MAC commands
+ */
+#define CID_FIELD_SIZE 1
+
+/*!
+ * Mac Commands list structure
+ */
+typedef struct sMacCommandsList
+{
+ /*
+ * First element of MAC command list.
+ */
+ MacCommand_t* First;
+ /*
+ * Last element of MAC command list.
+ */
+ MacCommand_t* Last;
+} MacCommandsList_t;
+
+/*!
+ * LoRaMac Commands Context structure
+ */
+typedef struct sLoRaMacCommandsCtx
+{
+ /*
+ * List of MAC command elements
+ */
+ MacCommandsList_t MacCommandList;
+ /*
+ * Buffer to store MAC command elements
+ */
+ MacCommand_t MacCommandSlots[NUM_OF_MAC_COMMANDS];
+ /*
+ * Size of all MAC commands serialized as buffer
+ */
+ size_t SerializedCmdsSize;
+} LoRaMacCommandsCtx_t;
+
+/*!
+ * Callback function to notify the upper layer about context change
+ */
+static LoRaMacCommandsNvmEvent CommandsNvmCtxChanged;
+
+/*!
+ * Non-volatile module context.
+ */
+static LoRaMacCommandsCtx_t NvmCtx;
+
+/* Memory management functions */
+
+/*!
+ * \brief Determines if a MAC command slot is free
+ *
+ * \param[IN] slot - Slot to check
+ * \retval - Status of the operation
+ */
+static bool IsSlotFree( const MacCommand_t* slot )
+{
+ uint8_t* mem = ( uint8_t* )slot;
+
+ for( uint16_t size = 0; size < sizeof( MacCommand_t ); size++ )
+ {
+ if( mem[size] != 0x00 )
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+/*!
+ * \brief Allocates a new MAC command memory slot
+ *
+ * \retval - Pointer to slot
+ */
+static MacCommand_t* MallocNewMacCommandSlot( void )
+{
+ uint8_t itr = 0;
+
+ while( IsSlotFree( ( const MacCommand_t* )&NvmCtx.MacCommandSlots[itr] ) == false )
+ {
+ itr++;
+ if( itr == NUM_OF_MAC_COMMANDS )
+ {
+ return 0;
+ }
+ }
+
+ return &NvmCtx.MacCommandSlots[itr];
+}
+
+/*!
+ * \brief Free memory slot
+ *
+ * \param[IN] slot - Slot to free
+ *
+ * \retval - Status of the operation
+ */
+static bool FreeMacCommandSlot( MacCommand_t* slot )
+{
+ if( slot == 0 )
+ {
+ return false;
+ }
+
+ memset1( ( uint8_t* )slot, 0x00, sizeof( MacCommand_t ) );
+
+ return true;
+}
+
+/* Linked list functions */
+
+/*!
+ * \brief Initialize list
+ *
+ * \param[IN] list - List that shall be initialized
+ * \retval - Status of the operation
+ */
+static bool LinkedListInit( MacCommandsList_t* list )
+{
+ if( list == 0 )
+ {
+ return false;
+ }
+
+ list->First = 0;
+ list->Last = 0;
+
+ return true;
+}
+
+/*!
+ * \brief Add an element to the list
+ *
+ * \param[IN] list - List where the element shall be added.
+ * \param[IN] element - Element to add
+ * \retval - Status of the operation
+ */
+static bool LinkedListAdd( MacCommandsList_t* list, MacCommand_t* element )
+{
+ if( ( list == 0 ) && ( element == 0 ) )
+ {
+ return false;
+ }
+
+ // Check if this is the first entry to enter the list.
+ if( list->First == 0 )
+ {
+ list->First = element;
+ }
+
+ // Check if the last entry exists and update its next point.
+ if( list->Last )
+ {
+ list->Last->Next = element;
+ }
+
+ // Update the next point of this entry.
+ element->Next = 0;
+
+ // Update the last entry of the list.
+ list->Last = element;
+
+ return true;
+}
+
+/*!
+ * \brief Return the previous element in the list.
+ *
+ * \param[IN] list - List
+ * \param[IN] element - Element where the previous element shall be searched
+ * \retval - Status of the operation
+ */
+static MacCommand_t* LinkedListGetPrevious( MacCommandsList_t* list, MacCommand_t* element )
+{
+ if( ( list == 0 ) && ( element == 0 ) )
+ {
+ return NULL;
+ }
+
+ MacCommand_t* curElement;
+
+ // Start at the head of the list
+ curElement = list->First;
+
+ // When current element is the first of the list, there's no previous element so we can return NULL immediately.
+ if( element != curElement )
+ {
+ // Loop through all elements until the end is reached or the next of current is the current element.
+ while( ( curElement != NULL ) && ( curElement->Next != element ) )
+ {
+ curElement = curElement->Next;
+ }
+ }
+ else
+ {
+ curElement = NULL;
+ }
+
+ return curElement;
+}
+
+/*!
+ * \brief Remove an element from the list
+ *
+ * \param[IN] list - List where the element shall be removed from.
+ * \param[IN] element - Element to remove
+ * \retval - Status of the operation
+ */
+static bool LinkedListRemove( MacCommandsList_t* list, MacCommand_t* element )
+{
+ if( ( list == 0 ) && ( element == 0 ) )
+ {
+ return false;
+ }
+
+ MacCommand_t* PrevElement = LinkedListGetPrevious( list, element );
+
+ if( list->First == element )
+ {
+ list->First = element->Next;
+ }
+
+ if( list->Last == element )
+ {
+ list->Last = PrevElement;
+ }
+
+ if( PrevElement != NULL )
+ {
+ PrevElement->Next = element->Next;
+ }
+
+ element->Next = NULL;
+
+ return true;
+}
+
+/*
+ * \brief Determines if a MAC command is sticky or not
+ *
+ * \param[IN] cid - MAC command identifier
+ *
+ * \retval - Status of the operation
+ */
+static bool IsSticky( uint8_t cid )
+{
+ switch( cid )
+ {
+ case MOTE_MAC_DL_CHANNEL_ANS:
+ case MOTE_MAC_RX_PARAM_SETUP_ANS:
+ case MOTE_MAC_RX_TIMING_SETUP_ANS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/*
+ * \brief Wrapper function for the NvmCtx
+ */
+static void NvmCtxCallback( void )
+{
+ if( CommandsNvmCtxChanged != NULL )
+ {
+ CommandsNvmCtxChanged( );
+ }
+}
+
+LoRaMacCommandStatus_t LoRaMacCommandsInit( LoRaMacCommandsNvmEvent commandsNvmCtxChanged )
+{
+ // Initialize with default
+ memset1( ( uint8_t* )&NvmCtx, 0, sizeof( NvmCtx ) );
+
+ LinkedListInit( &NvmCtx.MacCommandList );
+
+ // Assign callback
+ CommandsNvmCtxChanged = commandsNvmCtxChanged;
+
+ return LORAMAC_COMMANDS_SUCCESS;
+}
+
+LoRaMacCommandStatus_t LoRaMacCommandsRestoreNvmCtx( void* commandsNvmCtx )
+{
+ // Restore module context
+ if( commandsNvmCtx != NULL )
+ {
+ memcpy1( ( uint8_t* )&NvmCtx, ( uint8_t* )commandsNvmCtx, sizeof( NvmCtx ) );
+ return LORAMAC_COMMANDS_SUCCESS;
+ }
+ else
+ {
+ return LORAMAC_COMMANDS_ERROR_NPE;
+ }
+}
+
+void* LoRaMacCommandsGetNvmCtx( size_t* commandsNvmCtxSize )
+{
+ *commandsNvmCtxSize = sizeof( NvmCtx );
+ return &NvmCtx;
+}
+
+LoRaMacCommandStatus_t LoRaMacCommandsAddCmd( uint8_t cid, uint8_t* payload, size_t payloadSize )
+{
+ if( payload == 0 )
+ {
+ return LORAMAC_COMMANDS_ERROR_NPE;
+ }
+ MacCommand_t* newCmd;
+
+ // Allocate a memory slot
+ newCmd = MallocNewMacCommandSlot( );
+
+ if( newCmd == 0 )
+ {
+ return LORAMAC_COMMANDS_ERROR_MEMORY;
+ }
+
+ // Add it to the list of Mac commands
+ if( LinkedListAdd( &NvmCtx.MacCommandList, newCmd ) == false )
+ {
+ return LORAMAC_COMMANDS_ERROR;
+ }
+
+ // Set Values
+ newCmd->CID = cid;
+ newCmd->PayloadSize = payloadSize;
+ memcpy1( ( uint8_t* )newCmd->Payload, payload, payloadSize );
+ newCmd->IsSticky = IsSticky( cid );
+
+ NvmCtx.SerializedCmdsSize += ( CID_FIELD_SIZE + payloadSize );
+
+ NvmCtxCallback( );
+
+ return LORAMAC_COMMANDS_SUCCESS;
+}
+
+LoRaMacCommandStatus_t LoRaMacCommandsRemoveCmd( MacCommand_t* macCmd )
+{
+ if( macCmd == NULL )
+ {
+ return LORAMAC_COMMANDS_ERROR_NPE;
+ }
+
+ // Remove the Mac command element from MacCommandList
+ if( LinkedListRemove( &NvmCtx.MacCommandList, macCmd ) == false )
+ {
+ return LORAMAC_COMMANDS_ERROR_CMD_NOT_FOUND;
+ }
+
+ NvmCtx.SerializedCmdsSize -= ( CID_FIELD_SIZE + macCmd->PayloadSize );
+
+ // Free the MacCommand Slot
+ if( FreeMacCommandSlot( macCmd ) == false )
+ {
+ return LORAMAC_COMMANDS_ERROR;
+ }
+
+ NvmCtxCallback( );
+
+ return LORAMAC_COMMANDS_SUCCESS;
+}
+
+LoRaMacCommandStatus_t LoRaMacCommandsGetCmd( uint8_t cid, MacCommand_t** macCmd )
+{
+ MacCommand_t* curElement;
+
+ // Start at the head of the list
+ curElement = NvmCtx.MacCommandList.First;
+
+ // Loop through all elements until we find the element with the given CID
+ while( ( curElement != NULL ) && ( curElement->CID != cid ) )
+ {
+ curElement = curElement->Next;
+ }
+
+ // Update the pointer anyway
+ *macCmd = curElement;
+
+ // Handle error in case if we reached the end without finding it.
+ if( curElement == NULL )
+ {
+ return LORAMAC_COMMANDS_ERROR_CMD_NOT_FOUND;
+ }
+ return LORAMAC_COMMANDS_SUCCESS;
+}
+
+LoRaMacCommandStatus_t LoRaMacCommandsRemoveNoneStickyCmds( void )
+{
+ MacCommand_t* curElement;
+ MacCommand_t* nexElement;
+
+ // Start at the head of the list
+ curElement = NvmCtx.MacCommandList.First;
+
+ // Loop through all elements
+ while( curElement != NULL )
+ {
+ if( curElement->IsSticky == false )
+ {
+ nexElement = curElement->Next;
+ LoRaMacCommandsRemoveCmd( curElement );
+ curElement = nexElement;
+ }
+ else
+ {
+ curElement = curElement->Next;
+ }
+ }
+
+ NvmCtxCallback( );
+
+ return LORAMAC_COMMANDS_SUCCESS;
+}
+
+LoRaMacCommandStatus_t LoRaMacCommandsRemoveStickyAnsCmds( void )
+{
+ MacCommand_t* curElement;
+ MacCommand_t* nexElement;
+
+ // Start at the head of the list
+ curElement = NvmCtx.MacCommandList.First;
+
+ // Loop through all elements
+ while( curElement != NULL )
+ {
+ nexElement = curElement->Next;
+ if( IsSticky( curElement->CID ) == true )
+ {
+ LoRaMacCommandsRemoveCmd( curElement );
+ }
+ curElement = nexElement;
+ }
+
+ NvmCtxCallback( );
+
+ return LORAMAC_COMMANDS_SUCCESS;
+}
+
+LoRaMacCommandStatus_t LoRaMacCommandsGetSizeSerializedCmds( size_t* size )
+{
+ if( size == NULL )
+ {
+ return LORAMAC_COMMANDS_ERROR_NPE;
+ }
+ *size = NvmCtx.SerializedCmdsSize;
+ return LORAMAC_COMMANDS_SUCCESS;
+}
+
+LoRaMacCommandStatus_t LoRaMacCommandsSerializeCmds( size_t availableSize, size_t* effectiveSize, uint8_t* buffer )
+{
+ MacCommand_t* curElement = NvmCtx.MacCommandList.First;
+ MacCommand_t* nextElement;
+ uint8_t itr = 0;
+
+ if( ( buffer == NULL ) || ( effectiveSize == NULL ) )
+ {
+ return LORAMAC_COMMANDS_ERROR_NPE;
+ }
+
+ // Loop through all elements which fits into the buffer
+ while( curElement != NULL )
+ {
+ // If the next MAC command still fits into the buffer, add it.
+ if( ( availableSize - itr ) >= ( CID_FIELD_SIZE + curElement->PayloadSize ) )
+ {
+ buffer[itr++] = curElement->CID;
+ memcpy1( &buffer[itr], curElement->Payload, curElement->PayloadSize );
+ itr += curElement->PayloadSize;
+ }
+ else
+ {
+ break;
+ }
+ curElement = curElement->Next;
+ }
+
+ // Remove all commands which do not fit into the buffer
+ while( curElement != NULL )
+ {
+ // Store the next element before removing the current one
+ nextElement = curElement->Next;
+ LoRaMacCommandsRemoveCmd( curElement );
+ curElement = nextElement;
+ }
+
+ // Fetch the effective size of the mac commands
+ LoRaMacCommandsGetSizeSerializedCmds( effectiveSize );
+
+ return LORAMAC_COMMANDS_SUCCESS;
+}
+
+LoRaMacCommandStatus_t LoRaMacCommandsStickyCmdsPending( bool* cmdsPending )
+{
+ if( cmdsPending == NULL )
+ {
+ return LORAMAC_COMMANDS_ERROR_NPE;
+ }
+ MacCommand_t* curElement;
+ curElement = NvmCtx.MacCommandList.First;
+
+ *cmdsPending = false;
+
+ // Loop through all elements
+ while( curElement != NULL )
+ {
+ if( curElement->IsSticky == true )
+ {
+ // Found one sticky MAC command
+ *cmdsPending = true;
+ return LORAMAC_COMMANDS_SUCCESS;
+ }
+ curElement = curElement->Next;
+ }
+
+ return LORAMAC_COMMANDS_SUCCESS;
+}
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacCommands.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacCommands.h
new file mode 100644
index 00000000..6e411ba7
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacCommands.h
@@ -0,0 +1,218 @@
+/*!
+ * \file LoRaMacCommands.h
+ *
+ * \brief LoRa MAC commands
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013 Semtech
+ *
+ * ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ * embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Daniel Jaeckle ( STACKFORCE )
+ *
+ * \author Johannes Bruder ( STACKFORCE )
+ *
+ * addtogroup LORAMAC
+ * \{
+ *
+ */
+#ifndef __LORAMAC_COMMANDS_H__
+#define __LORAMAC_COMMANDS_H__
+
+#include
+#include
+#include "LoRaMacTypes.h"
+
+
+/*
+ * Number of MAC Command slots
+ */
+#define LORAMAC_COMMADS_MAX_NUM_OF_PARAMS 2
+
+/*!
+ * LoRaWAN MAC Command element
+ */
+typedef struct sMacCommand MacCommand_t;
+
+struct sMacCommand
+{
+ /*!
+ * The pointer to the next MAC Command element in the list
+ */
+ MacCommand_t* Next;
+ /*!
+ * MAC command identifier
+ */
+ uint8_t CID;
+ /*!
+ * MAC command payload
+ */
+ uint8_t Payload[LORAMAC_COMMADS_MAX_NUM_OF_PARAMS];
+ /*!
+ * Size of MAC command payload
+ */
+ size_t PayloadSize;
+ /*!
+ * Indicates if it's a sticky MAC command
+ */
+ bool IsSticky;
+};
+
+/*!
+ * LoRaMac Commands Status
+ */
+typedef enum eLoRaMacCommandsStatus
+{
+ /*!
+ * No error occurred
+ */
+ LORAMAC_COMMANDS_SUCCESS = 0,
+ /*!
+ * Null pointer exception
+ */
+ LORAMAC_COMMANDS_ERROR_NPE,
+ /*!
+ * There is no memory left to add a further MAC command
+ */
+ LORAMAC_COMMANDS_ERROR_MEMORY,
+ /*!
+ * MAC command not found.
+ */
+ LORAMAC_COMMANDS_ERROR_CMD_NOT_FOUND,
+ /*!
+ * Unknown or corrupted command error occurred.
+ */
+ LORAMAC_COMMANDS_ERROR_UNKNOWN_CMD,
+ /*!
+ * Undefined Error occurred
+ */
+ LORAMAC_COMMANDS_ERROR,
+}LoRaMacCommandStatus_t;
+
+/*!
+ * Signature of callback function to be called by this module when the
+ * non-volatile needs to be saved.
+ */
+typedef void ( *LoRaMacCommandsNvmEvent )( void );
+
+/*!
+ * \brief Initialization of LoRaMac MAC commands module
+ *
+ * \param[IN] commandsNvmCtxChanged - Callback function which will be called when the
+ * non-volatile context needs to be saved.
+ *
+ * \retval - Status of the operation
+ */
+LoRaMacCommandStatus_t LoRaMacCommandsInit( LoRaMacCommandsNvmEvent commandsNvmCtxChanged );
+
+/*!
+ * Restores the internal non-volatile context from passed pointer.
+ *
+ * \param[IN] commandsNvmCtx - Pointer to non-volatile MAC commands module context to be restored.
+ *
+ * \retval - Status of the operation
+ */
+LoRaMacCommandStatus_t LoRaMacCommandsRestoreNvmCtx( void* commandsNvmCtx );
+
+/*!
+ * Returns a pointer to the internal non-volatile context.
+ *
+ * \param[IN] commandsNvmCtxSize - Size of the module non-volatile context
+ *
+ * \retval - Points to a structure where the module store its non-volatile context
+ */
+void* LoRaMacCommandsGetNvmCtx( size_t* commandsNvmCtxSize );
+
+/*!
+ * \brief Adds a new MAC command to be sent.
+ *
+ * \param[IN] cid - MAC command identifier
+ * \param[IN] payload - MAC command payload containing parameters
+ * \param[IN] payloadSize - Size of MAC command payload
+ *
+ * \retval - Status of the operation
+ */
+LoRaMacCommandStatus_t LoRaMacCommandsAddCmd( uint8_t cid, uint8_t* payload, size_t payloadSize );
+
+/*!
+ * \brief Remove a MAC command.
+ *
+ * \param[OUT] cmd - MAC command
+ *
+ * \retval - Status of the operation
+ */
+LoRaMacCommandStatus_t LoRaMacCommandsRemoveCmd( MacCommand_t* macCmd );
+
+/*!
+ * \brief Get the MAC command with corresponding CID.
+ *
+ * \param[IN] cid - MAC command identifier
+ * \param[OUT] cmd - MAC command
+ *
+ * \retval - Status of the operation
+ */
+LoRaMacCommandStatus_t LoRaMacCommandsGetCmd( uint8_t cid, MacCommand_t** macCmd );
+
+/*!
+ * \brief Remove all none sticky MAC commands.
+ *
+ * \retval - Status of the operation
+ */
+LoRaMacCommandStatus_t LoRaMacCommandsRemoveNoneStickyCmds( void );
+
+/*!
+ * \brief Remove all sticky answer MAC commands.
+ *
+ * \retval - Status of the operation
+ */
+LoRaMacCommandStatus_t LoRaMacCommandsRemoveStickyAnsCmds( void );
+
+/*!
+ * \brief Get size of all MAC commands serialized as buffer
+ *
+ * \param[out] size - Available size of memory for MAC commands
+ *
+ * \retval - Status of the operation
+ */
+LoRaMacCommandStatus_t LoRaMacCommandsGetSizeSerializedCmds( size_t* size );
+
+/*!
+ * \brief Get as many as possible MAC commands serialized
+ *
+ * \param[IN] availableSize - Available size of memory for MAC commands
+ * \param[out] effectiveSize - Size of memory which was effectively used for serializing.
+ * \param[out] buffer - Destination data buffer
+ *
+ * \retval - Status of the operation
+ */
+LoRaMacCommandStatus_t LoRaMacCommandsSerializeCmds( size_t availableSize, size_t* effectiveSize, uint8_t* buffer );
+
+/*!
+ * \brief Determines if there are sticky MAC commands pending.
+ *
+ * \param[IN] cmdsPending - Indicates if there are sticky MAC commands in the queue.
+ *
+ * \retval - Status of the operation
+ */
+LoRaMacCommandStatus_t LoRaMacCommandsStickyCmdsPending( bool* cmdsPending );
+
+/*! \} addtogroup LORAMAC */
+
+#endif // __LORAMAC_COMMANDS_H__
+
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacConfirmQueue.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacConfirmQueue.c
new file mode 100644
index 00000000..11698448
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacConfirmQueue.c
@@ -0,0 +1,344 @@
+/*
+ / _____) _ | |
+( (____ _____ ____ _| |_ _____ ____| |__
+ \____ \| ___ | (_ _) ___ |/ ___) _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+ (C)2013 Semtech
+ ___ _____ _ ___ _ _____ ___ ___ ___ ___
+/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+embedded.connectivity.solutions===============
+
+Description: LoRa MAC confirm queue 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
+#include
+#include
+
+#include "timer.h"
+#include "utilities.h"
+#include "LoRaMac.h"
+#include "LoRaMacConfirmQueue.h"
+
+
+/*
+ * LoRaMac Confirm Queue Context NVM structure
+ */
+typedef struct sLoRaMacConfirmQueueNvmCtx
+{
+ /*!
+ * MlmeConfirm queue data structure
+ */
+ MlmeConfirmQueue_t MlmeConfirmQueue[LORA_MAC_MLME_CONFIRM_QUEUE_LEN];
+ /*!
+ * Counts the number of MlmeConfirms to process
+ */
+ uint8_t MlmeConfirmQueueCnt;
+ /*!
+ * Variable which holds a common status
+ */
+ LoRaMacEventInfoStatus_t CommonStatus;
+} LoRaMacConfirmQueueNvmCtx_t;
+
+/*
+ * LoRaMac Confirm Queue Context structure
+ */
+typedef struct sLoRaMacConfirmQueueCtx
+{
+ /*!
+ * LoRaMac callback function primitives
+ */
+ LoRaMacPrimitives_t* Primitives;
+ /*!
+ * Pointer to the first element of the ring buffer
+ */
+ MlmeConfirmQueue_t* BufferStart;
+ /*!
+ * Pointer to the last element of the ring buffer
+ */
+ MlmeConfirmQueue_t* BufferEnd;
+ /*
+ * Callback function to notify the upper layer about context change
+ */
+ LoRaMacConfirmQueueNvmEvent LoRaMacConfirmQueueNvmEvent;
+ /*!
+ * Non-volatile module context.
+ */
+ LoRaMacConfirmQueueNvmCtx_t* ConfirmQueueNvmCtx;
+} LoRaMacConfirmQueueCtx_t;
+
+/*
+ * Non-volatile module context.
+ */
+static LoRaMacConfirmQueueNvmCtx_t ConfirmQueueNvmCtx;
+
+/*
+ * Module context.
+ */
+static LoRaMacConfirmQueueCtx_t ConfirmQueueCtx;
+
+static MlmeConfirmQueue_t* IncreaseBufferPointer( MlmeConfirmQueue_t* bufferPointer )
+{
+ if( bufferPointer == &ConfirmQueueCtx.ConfirmQueueNvmCtx->MlmeConfirmQueue[LORA_MAC_MLME_CONFIRM_QUEUE_LEN - 1] )
+ {
+ // Reset to the first element
+ bufferPointer = ConfirmQueueCtx.ConfirmQueueNvmCtx->MlmeConfirmQueue;
+ }
+ else
+ {
+ // Increase
+ bufferPointer++;
+ }
+ return bufferPointer;
+}
+
+static MlmeConfirmQueue_t* DecreaseBufferPointer( MlmeConfirmQueue_t* bufferPointer )
+{
+ if( bufferPointer == ConfirmQueueCtx.ConfirmQueueNvmCtx->MlmeConfirmQueue )
+ {
+ // Reset to the last element
+ bufferPointer = &ConfirmQueueCtx.ConfirmQueueNvmCtx->MlmeConfirmQueue[LORA_MAC_MLME_CONFIRM_QUEUE_LEN - 1];
+ }
+ else
+ {
+ bufferPointer--;
+ }
+ return bufferPointer;
+}
+
+static MlmeConfirmQueue_t* GetElement( Mlme_t request, MlmeConfirmQueue_t* bufferStart, MlmeConfirmQueue_t* bufferEnd )
+{
+ MlmeConfirmQueue_t* element = bufferStart;
+
+ while( element != bufferEnd )
+ {
+ if( element->Request == request )
+ {
+ // We have found the element
+ return element;
+ }
+ else
+ {
+ element = IncreaseBufferPointer( element );
+ }
+ }
+ return NULL;
+}
+
+void LoRaMacConfirmQueueInit( LoRaMacPrimitives_t* primitives, LoRaMacConfirmQueueNvmEvent confirmQueueNvmCtxChanged )
+{
+ ConfirmQueueCtx.Primitives = primitives;
+
+ // Assign nvm context
+ ConfirmQueueCtx.ConfirmQueueNvmCtx = &ConfirmQueueNvmCtx;
+
+ // Init counter
+ ConfirmQueueCtx.ConfirmQueueNvmCtx->MlmeConfirmQueueCnt = 0;
+
+ // Init buffer
+ ConfirmQueueCtx.BufferStart = ConfirmQueueCtx.ConfirmQueueNvmCtx->MlmeConfirmQueue;
+ ConfirmQueueCtx.BufferEnd = ConfirmQueueCtx.ConfirmQueueNvmCtx->MlmeConfirmQueue;
+
+ memset1( ( uint8_t* )ConfirmQueueCtx.ConfirmQueueNvmCtx->MlmeConfirmQueue, 0xFF, sizeof( ConfirmQueueCtx.ConfirmQueueNvmCtx->MlmeConfirmQueue ) );
+
+ // Common status
+ ConfirmQueueCtx.ConfirmQueueNvmCtx->CommonStatus = LORAMAC_EVENT_INFO_STATUS_ERROR;
+
+ // Assign callback
+ ConfirmQueueCtx.LoRaMacConfirmQueueNvmEvent = confirmQueueNvmCtxChanged;
+}
+
+bool LoRaMacConfirmQueueRestoreNvmCtx( void* confirmQueueNvmCtx )
+{
+ // Restore module context
+ if( confirmQueueNvmCtx != NULL )
+ {
+ memcpy1( ( uint8_t* )&ConfirmQueueNvmCtx, ( uint8_t* ) confirmQueueNvmCtx, sizeof( ConfirmQueueNvmCtx ) );
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+void* LoRaMacConfirmQueueGetNvmCtx( size_t* confirmQueueNvmCtxSize )
+{
+ *confirmQueueNvmCtxSize = sizeof( ConfirmQueueNvmCtx );
+ return &ConfirmQueueNvmCtx;
+}
+
+bool LoRaMacConfirmQueueAdd( MlmeConfirmQueue_t* mlmeConfirm )
+{
+ if( ConfirmQueueCtx.ConfirmQueueNvmCtx->MlmeConfirmQueueCnt >= LORA_MAC_MLME_CONFIRM_QUEUE_LEN )
+ {
+ // Protect the buffer against overwrites
+ return false;
+ }
+
+ // Add the element to the ring buffer
+ ConfirmQueueCtx.BufferEnd->Request = mlmeConfirm->Request;
+ ConfirmQueueCtx.BufferEnd->Status = mlmeConfirm->Status;
+ ConfirmQueueCtx.BufferEnd->RestrictCommonReadyToHandle = mlmeConfirm->RestrictCommonReadyToHandle;
+ ConfirmQueueCtx.BufferEnd->ReadyToHandle = false;
+ // Increase counter
+ ConfirmQueueCtx.ConfirmQueueNvmCtx->MlmeConfirmQueueCnt++;
+ // Update end pointer
+ ConfirmQueueCtx.BufferEnd = IncreaseBufferPointer( ConfirmQueueCtx.BufferEnd );
+
+ return true;
+}
+
+bool LoRaMacConfirmQueueRemoveLast( void )
+{
+ if( ConfirmQueueCtx.ConfirmQueueNvmCtx->MlmeConfirmQueueCnt == 0 )
+ {
+ return false;
+ }
+
+ // Increase counter
+ ConfirmQueueCtx.ConfirmQueueNvmCtx->MlmeConfirmQueueCnt--;
+ // Update start pointer
+ ConfirmQueueCtx.BufferEnd = DecreaseBufferPointer( ConfirmQueueCtx.BufferEnd );
+
+ return true;
+}
+
+bool LoRaMacConfirmQueueRemoveFirst( void )
+{
+ if( ConfirmQueueCtx.ConfirmQueueNvmCtx->MlmeConfirmQueueCnt == 0 )
+ {
+ return false;
+ }
+
+ // Increase counter
+ ConfirmQueueCtx.ConfirmQueueNvmCtx->MlmeConfirmQueueCnt--;
+ // Update start pointer
+ ConfirmQueueCtx.BufferStart = IncreaseBufferPointer( ConfirmQueueCtx.BufferStart );
+
+ return true;
+}
+
+void LoRaMacConfirmQueueSetStatus( LoRaMacEventInfoStatus_t status, Mlme_t request )
+{
+ MlmeConfirmQueue_t* element = NULL;
+
+ if( ConfirmQueueCtx.ConfirmQueueNvmCtx->MlmeConfirmQueueCnt > 0 )
+ {
+ element = GetElement( request, ConfirmQueueCtx.BufferStart, ConfirmQueueCtx.BufferEnd );
+ if( element != NULL )
+ {
+ element->Status = status;
+ element->ReadyToHandle = true;
+ }
+ }
+}
+
+LoRaMacEventInfoStatus_t LoRaMacConfirmQueueGetStatus( Mlme_t request )
+{
+ MlmeConfirmQueue_t* element = NULL;
+
+ if( ConfirmQueueCtx.ConfirmQueueNvmCtx->MlmeConfirmQueueCnt > 0 )
+ {
+ element = GetElement( request, ConfirmQueueCtx.BufferStart, ConfirmQueueCtx.BufferEnd );
+ if( element != NULL )
+ {
+ return element->Status;
+ }
+ }
+ return LORAMAC_EVENT_INFO_STATUS_ERROR;
+}
+
+void LoRaMacConfirmQueueSetStatusCmn( LoRaMacEventInfoStatus_t status )
+{
+ MlmeConfirmQueue_t* element = ConfirmQueueCtx.BufferStart;
+
+ ConfirmQueueCtx.ConfirmQueueNvmCtx->CommonStatus = status;
+
+ if( ConfirmQueueCtx.ConfirmQueueNvmCtx->MlmeConfirmQueueCnt > 0 )
+ {
+ do
+ {
+ element->Status = status;
+ // Set the status if it is allowed to set it with a call to
+ // LoRaMacConfirmQueueSetStatusCmn.
+ if( element->RestrictCommonReadyToHandle == false )
+ {
+ element->ReadyToHandle = true;
+ }
+ element = IncreaseBufferPointer( element );
+ }while( element != ConfirmQueueCtx.BufferEnd );
+ }
+}
+
+LoRaMacEventInfoStatus_t LoRaMacConfirmQueueGetStatusCmn( void )
+{
+ return ConfirmQueueCtx.ConfirmQueueNvmCtx->CommonStatus;
+}
+
+bool LoRaMacConfirmQueueIsCmdActive( Mlme_t request )
+{
+ if( GetElement( request, ConfirmQueueCtx.BufferStart, ConfirmQueueCtx.BufferEnd ) != NULL )
+ {
+ return true;
+ }
+ return false;
+}
+
+void LoRaMacConfirmQueueHandleCb( MlmeConfirm_t* mlmeConfirm )
+{
+ uint8_t nbElements = ConfirmQueueCtx.ConfirmQueueNvmCtx->MlmeConfirmQueueCnt;
+ bool readyToHandle = false;
+ MlmeConfirmQueue_t mlmeConfirmToStore;
+
+ for( uint8_t i = 0; i < nbElements; i++ )
+ {
+ mlmeConfirm->MlmeRequest = ConfirmQueueCtx.BufferStart->Request;
+ mlmeConfirm->Status = ConfirmQueueCtx.BufferStart->Status;
+ readyToHandle = ConfirmQueueCtx.BufferStart->ReadyToHandle;
+
+ if( readyToHandle == true )
+ {
+ ConfirmQueueCtx.Primitives->MacMlmeConfirm( mlmeConfirm );
+ }
+ else
+ {
+ // The request is not processed yet. Store the state.
+ mlmeConfirmToStore.Request = ConfirmQueueCtx.BufferStart->Request;
+ mlmeConfirmToStore.Status = ConfirmQueueCtx.BufferStart->Status;
+ mlmeConfirmToStore.RestrictCommonReadyToHandle = ConfirmQueueCtx.BufferStart->RestrictCommonReadyToHandle;
+ }
+
+ // Increase the pointer afterwards to prevent overwrites
+ LoRaMacConfirmQueueRemoveFirst( );
+
+ if( readyToHandle == false )
+ {
+ // Add a request which has not been finished again to the queue
+ LoRaMacConfirmQueueAdd( &mlmeConfirmToStore );
+ }
+ }
+}
+
+uint8_t LoRaMacConfirmQueueGetCnt( void )
+{
+ return ConfirmQueueCtx.ConfirmQueueNvmCtx->MlmeConfirmQueueCnt;
+}
+
+bool LoRaMacConfirmQueueIsFull( void )
+{
+ if( ConfirmQueueCtx.ConfirmQueueNvmCtx->MlmeConfirmQueueCnt >= LORA_MAC_MLME_CONFIRM_QUEUE_LEN )
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacConfirmQueue.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacConfirmQueue.h
new file mode 100644
index 00000000..2dc88ad0
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacConfirmQueue.h
@@ -0,0 +1,195 @@
+/*!
+ * \file LoRaMacConfirmQueue.h
+ *
+ * \brief LoRa MAC confirm queue 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 LORAMACCONFIRMQUEUE LoRa MAC confirm queue implementation
+ * This module specifies the API implementation of the LoRaMAC confirm queue.
+ * The confirm queue is implemented with as a ring buffer. The number of
+ * elements can be defined with \ref LORA_MAC_MLME_CONFIRM_QUEUE_LEN. The
+ * current implementation does not support multiple elements of the same
+ * Mlme_t type.
+ * \{
+ */
+#ifndef __LORAMAC_CONFIRMQUEUE_H__
+#define __LORAMAC_CONFIRMQUEUE_H__
+
+#include
+#include
+
+#include "LoRaMac.h"
+
+/*!
+ * LoRaMac MLME-Confirm queue length
+ */
+#define LORA_MAC_MLME_CONFIRM_QUEUE_LEN 5
+
+/*!
+ * Structure to hold multiple MLME request confirm data
+ */
+typedef struct sMlmeConfirmQueue
+{
+ /*!
+ * Holds the previously performed MLME-Request
+ */
+ Mlme_t Request;
+ /*!
+ * Status of the operation
+ */
+ LoRaMacEventInfoStatus_t Status;
+ /*!
+ * Set to true, if the request is ready to be handled
+ */
+ bool ReadyToHandle;
+ /*!
+ * Set to true, if it is not permitted to set the ReadyToHandle variable
+ * with a function call to LoRaMacConfirmQueueSetStatusCmn.
+ */
+ bool RestrictCommonReadyToHandle;
+}MlmeConfirmQueue_t;
+
+/*!
+ * Signature of callback function to be called by this module when the
+ * non-volatile needs to be saved.
+ */
+typedef void ( *LoRaMacConfirmQueueNvmEvent )( void );
+
+/*!
+ * \brief Initializes the confirm queue
+ *
+ * \param [IN] primitives - Pointer to the LoRaMac primitives.
+ *
+ * \param [IN] confirmQueueNvmCtxChanged - Callback function which will be called when the
+ * non-volatile context needs to be saved.
+ */
+void LoRaMacConfirmQueueInit( LoRaMacPrimitives_t* primitives, LoRaMacConfirmQueueNvmEvent confirmQueueNvmCtxChanged );
+
+/*!
+ * Restores the internal non-volatile context from passed pointer.
+ *
+ * \param [IN] confirmQueueNvmCtx - Pointer to non-volatile class B module context to be restored.
+ *
+ * \retval [true - operation was successful, false - operation failed]
+ */
+bool LoRaMacConfirmQueueRestoreNvmCtx( void* confirmQueueNvmCtx );
+
+/*!
+ * Returns a pointer to the internal non-volatile context.
+ *
+ * \param [IN] confirmQueueNvmCtxSize - Size of the module non-volatile context
+ *
+ * \retval - Points to a structure where the module store its non-volatile context
+ */
+void* LoRaMacConfirmQueueGetNvmCtx( size_t* confirmQueueNvmCtxSize );
+
+/*!
+ * \brief Adds an element to the confirm queue.
+ *
+ * \param [IN] mlmeConfirm - Pointer to the element to add.
+ *
+ * \retval [true - operation was successful, false - operation failed]
+ */
+bool LoRaMacConfirmQueueAdd( MlmeConfirmQueue_t* mlmeConfirm );
+
+/*!
+ * \brief Removes the last element which was added into the queue.
+ *
+ * \retval [true - operation was successful, false - operation failed]
+ */
+bool LoRaMacConfirmQueueRemoveLast( void );
+
+/*!
+ * \brief Removes the first element which was added to the confirm queue.
+ *
+ * \retval [true - operation was successful, false - operation failed]
+ */
+bool LoRaMacConfirmQueueRemoveFirst( void );
+
+/*!
+ * \brief Sets the status of an element.
+ *
+ * \param [IN] status - The status to set.
+ *
+ * \param [IN] request - The related request to set the status.
+ */
+void LoRaMacConfirmQueueSetStatus( LoRaMacEventInfoStatus_t status, Mlme_t request );
+
+/*!
+ * \brief Gets the status of an element.
+ *
+ * \param [IN] request - The request to query the status.
+ *
+ * \retval The status of the related MlmeRequest.
+ */
+LoRaMacEventInfoStatus_t LoRaMacConfirmQueueGetStatus( Mlme_t request );
+
+/*!
+ * \brief Sets a common status for all elements in the queue.
+ *
+ * \param [IN] status - The status to set.
+ */
+void LoRaMacConfirmQueueSetStatusCmn( LoRaMacEventInfoStatus_t status );
+
+/*!
+ * \brief Gets the common status of all elements.
+ *
+ * \retval The common status of all elements.
+ */
+LoRaMacEventInfoStatus_t LoRaMacConfirmQueueGetStatusCmn( void );
+
+/*!
+ * \brief Verifies if a request is in the queue and active.
+ *
+ * \param [IN] request - The request to verify.
+ *
+ * \retval [true - element is in the queue, false - element is not in the queue].
+ */
+bool LoRaMacConfirmQueueIsCmdActive( Mlme_t request );
+
+/*!
+ * \brief Handles all callbacks of active requests
+ *
+ * \param [IN] mlmeConfirm - Pointer to the generic mlmeConfirm structure.
+ */
+void LoRaMacConfirmQueueHandleCb( MlmeConfirm_t* mlmeConfirm );
+
+/*!
+ * \brief Query number of elements in the queue.
+ *
+ * \retval Number of elements.
+ */
+uint8_t LoRaMacConfirmQueueGetCnt( void );
+
+/*!
+ * \brief Verify if the confirm queue is full.
+ *
+ * \retval [true - queue is full, false - queue is not full].
+ */
+bool LoRaMacConfirmQueueIsFull( void );
+
+#endif // __LORAMAC_CONFIRMQUEUE_H__
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacCrypto.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacCrypto.c
new file mode 100644
index 00000000..2e2d22c6
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacCrypto.c
@@ -0,0 +1,1693 @@
+/*!
+ * \file LoRaMacCrypto.c
+ *
+ * \brief LoRa MAC layer cryptography implementation
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013-2017 Semtech
+ *
+ * ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ * embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Gregory Cristian ( Semtech )
+ *
+ * \author Daniel Jaeckle ( STACKFORCE )
+ *
+ * \author Johannes Bruder ( STACKFORCE )
+ */
+#include
+#include
+#include
+
+#include "utilities.h"
+#include "secure-element.h"
+
+#include "LoRaMacParser.h"
+#include "LoRaMacSerializer.h"
+#include "LoRaMacCrypto.h"
+
+/*!
+ * Indicates if LoRaWAN 1.1.x crypto scheme is enabled
+ */
+#define USE_LRWAN_1_1_X_CRYPTO 0
+
+/*!
+ * Indicates if a random devnonce must be used or not
+ */
+#define USE_RANDOM_DEV_NONCE 1
+
+/*
+ * Frame direction definition for uplink communications
+ */
+#define UPLINK 0
+
+/*
+ * Frame direction definition for downlink communications
+ */
+#define DOWNLINK 1
+
+/*
+ * CMAC/AES Message Integrity Code (MIC) Block B0 size
+ */
+#define MIC_BLOCK_BX_SIZE 16
+
+/*
+ * Size of JoinReqType is field for integrity check
+ */
+#define JOIN_REQ_TYPE_SIZE 1
+
+/*
+ * Size of DevNonce is field for integrity check
+ */
+#define DEV_NONCE_SIZE 2
+
+/*
+ * Number of security context entries
+ */
+#define NUM_OF_SEC_CTX 5
+
+/*
+ * Size of the module context
+ */
+#define CRYPTO_CTX_SIZE sizeof( LoRaMacCryptoCtx_t )
+
+/*
+ * Size of the module non volatile context
+ */
+#define CRYPTO_NVM_CTX_SIZE sizeof( LoRaMacCryptoNvmCtx_t )
+
+/*
+ * Maximum size of the message that can be handled by the crypto operations
+ */
+#define CRYPTO_MAXMESSAGE_SIZE 256
+
+/*
+ * Maximum size of the buffer for crypto operations
+ */
+#define CRYPTO_BUFFER_SIZE CRYPTO_MAXMESSAGE_SIZE + MIC_BLOCK_BX_SIZE
+
+/*
+ * MIC computaion offset
+ */
+#define CRYPTO_MIC_COMPUTATION_OFFSET JOIN_REQ_TYPE_SIZE + LORAMAC_JOIN_EUI_FIELD_SIZE + DEV_NONCE_SIZE + LORAMAC_MHDR_FIELD_SIZE
+
+/*!
+ * LoRaWAN Frame counter list.
+ */
+typedef struct sFCntList
+{
+ /*!
+ * Uplink frame counter which is incremented with each uplink.
+ */
+ uint32_t FCntUp;
+ /*!
+ * Network downlink frame counter which is incremented with each downlink on FPort 0
+ * or when the FPort field is missing.
+ */
+ uint32_t NFCntDown;
+ /*!
+ * Application downlink frame counter which is incremented with each downlink
+ * on a port different than 0.
+ */
+ uint32_t AFCntDown;
+ /*!
+ * In case if the device is connected to a LoRaWAN 1.0 Server,
+ * this counter is used for every kind of downlink frame.
+ */
+ uint32_t FCntDown;
+ /*!
+ * Multicast downlink counter for index 0
+ */
+ uint32_t McFCntDown0;
+ /*!
+ * Multicast downlink counter for index 1
+ */
+ uint32_t McFCntDown1;
+ /*!
+ * Multicast downlink counter for index 2
+ */
+ uint32_t McFCntDown2;
+ /*!
+ * Multicast downlink counter for index 3
+ */
+ uint32_t McFCntDown3;
+}FCntList_t;
+
+/*
+ * LoRaMac Crypto Non Volatile Context structure
+ */
+typedef struct sLoRaMacCryptoNvmCtx
+{
+ /*
+ * Stores the information if the device is connected to a LoRaWAN network
+ * server with prior to 1.1.0 implementation.
+ */
+ Version_t LrWanVersion;
+ /*
+ * Device nonce is a counter starting at 0 when the device is initially
+ * powered up and incremented with every JoinRequest.
+ */
+ uint16_t DevNonce;
+ /*
+ * JoinNonce is a device specific counter value (that never repeats itself)
+ * provided by the join server and incremented with every JoinAccept message.
+ */
+ uint32_t JoinNonce;
+ /*
+ * Frame counter list
+ */
+ FCntList_t FCntList;
+ /*
+ * RJcount1 is a counter incremented with every Rejoin request Type 1 frame transmitted.
+ */
+ uint16_t RJcount1;
+ /*
+ * LastDownFCnt stores the information which frame counter was used to unsecure the last frame.
+ * This information is needed to compute ConfFCnt in B1 block for the MIC.
+ */
+ uint32_t* LastDownFCnt;
+}LoRaMacCryptoNvmCtx_t;
+
+/*
+ * LoRaMac Crypto Context structure
+ */
+typedef struct sLoRaMacCryptoCtx
+{
+ /*
+ * RJcount0 is a counter incremented with every Type 0 or 2 Rejoin frame transmitted.
+ */
+ uint16_t RJcount0;
+ /*
+ * Non volatile module context structure
+ */
+ LoRaMacCryptoNvmCtx_t* NvmCtx;
+ /*
+ * Callback function to notify the upper layer about context change
+ */
+ LoRaMacCryptoNvmEvent EventCryptoNvmCtxChanged;
+}LoRaMacCryptoCtx_t;
+
+/*
+ * Key-Address item
+ */
+typedef struct sKeyAddr
+{
+ /*
+ * Address identifier
+ */
+ AddressIdentifier_t AddrID;
+ /*
+ * Application session key
+ */
+ KeyIdentifier_t AppSkey;
+ /*
+ * Network session key
+ */
+ KeyIdentifier_t NwkSkey;
+ /*
+ * Rootkey (Multicast only)
+ */
+ KeyIdentifier_t RootKey;
+}KeyAddr_t;
+
+/*
+ *Crypto module context.
+ */
+static LoRaMacCryptoCtx_t CryptoCtx;
+
+/*
+ * Non volatile module context.
+ */
+static LoRaMacCryptoNvmCtx_t NvmCryptoCtx;
+
+/*
+ * Key-Address list
+ */
+static KeyAddr_t KeyAddrList[NUM_OF_SEC_CTX] =
+ {
+ { MULTICAST_0_ADDR, MC_APP_S_KEY_0, MC_NWK_S_KEY_0, MC_KEY_0 },
+ { MULTICAST_1_ADDR, MC_APP_S_KEY_1, MC_NWK_S_KEY_1, MC_KEY_1 },
+ { MULTICAST_2_ADDR, MC_APP_S_KEY_2, MC_NWK_S_KEY_2, MC_KEY_2 },
+ { MULTICAST_3_ADDR, MC_APP_S_KEY_3, MC_NWK_S_KEY_3, MC_KEY_3 },
+ { UNICAST_DEV_ADDR, APP_S_KEY, S_NWK_S_INT_KEY, NO_KEY }
+ };
+
+/*
+ * Local functions
+ */
+
+/*
+ * Encrypts the payload
+ *
+ * \param[IN] keyID - Key identifier
+ * \param[IN] address - Address
+ * \param[IN] dir - Frame direction ( Uplink or Downlink )
+ * \param[IN] frameCounter - Frame counter
+ * \param[IN] size - Size of data
+ * \param[IN/OUT] buffer - Data buffer
+ * \retval - Status of the operation
+ */
+static LoRaMacCryptoStatus_t PayloadEncrypt( uint8_t* buffer, int16_t size, KeyIdentifier_t keyID, uint32_t address, uint8_t dir, uint32_t frameCounter )
+{
+ if( buffer == 0 )
+ {
+ return LORAMAC_CRYPTO_ERROR_NPE;
+ }
+
+ uint8_t bufferIndex = 0;
+ uint16_t ctr = 1;
+ uint8_t sBlock[16] = { 0 };
+ uint8_t aBlock[16] = { 0 };
+
+ aBlock[0] = 0x01;
+
+ aBlock[5] = dir;
+
+ aBlock[6] = address & 0xFF;
+ aBlock[7] = ( address >> 8 ) & 0xFF;
+ aBlock[8] = ( address >> 16 ) & 0xFF;
+ aBlock[9] = ( address >> 24 ) & 0xFF;
+
+ aBlock[10] = frameCounter & 0xFF;
+ aBlock[11] = ( frameCounter >> 8 ) & 0xFF;
+ aBlock[12] = ( frameCounter >> 16 ) & 0xFF;
+ aBlock[13] = ( frameCounter >> 24 ) & 0xFF;
+
+ while( size > 0 )
+ {
+ aBlock[15] = ctr & 0xFF;
+ ctr++;
+ if( SecureElementAesEncrypt( aBlock, 16, keyID, sBlock ) != SECURE_ELEMENT_SUCCESS )
+ {
+ return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC;
+ }
+
+ for( uint8_t i = 0; i < ( ( size > 16 ) ? 16 : size ); i++ )
+ {
+ buffer[bufferIndex + i] = buffer[bufferIndex + i] ^ sBlock[i];
+ }
+ size -= 16;
+ bufferIndex += 16;
+ }
+
+ return LORAMAC_CRYPTO_SUCCESS;
+}
+
+#if( USE_LRWAN_1_1_X_CRYPTO == 1 )
+/*
+ * Encrypts the FOpts
+ *
+ * \param[IN] address - Address
+ * \param[IN] dir - Frame direction ( Uplink or Downlink )
+ * \param[IN] fCntID - Frame counter identifier
+ * \param[IN] frameCounter - Frame counter
+ * \param[IN] size - Size of data
+ * \param[IN/OUT] buffer - Data buffer
+ * \retval - Status of the operation
+ */
+static LoRaMacCryptoStatus_t FOptsEncrypt( uint16_t size, uint32_t address, uint8_t dir, FCntIdentifier_t fCntID, uint32_t frameCounter, uint8_t* buffer )
+{
+ if( buffer == 0 )
+ {
+ return LORAMAC_CRYPTO_ERROR_NPE;
+ }
+
+ uint8_t bufferIndex = 0;
+ uint8_t sBlock[16] = { 0 };
+ uint8_t aBlock[16] = { 0 };
+
+ aBlock[0] = 0x01;
+
+ if( CryptoCtx.NvmCtx->LrWanVersion.Value > 0x01010000 )
+ {
+ // Introduced in LoRaWAN 1.1.1 specification
+ switch( fCntID )
+ {
+ case FCNT_UP:
+ {
+ aBlock[4] = 0x01;
+ break;
+ }
+ case N_FCNT_DOWN:
+ {
+ aBlock[4] = 0x01;
+ break;
+ }
+ case A_FCNT_DOWN:
+ {
+ aBlock[4] = 0x02;
+ break;
+ }
+ default:
+ return LORAMAC_CRYPTO_FAIL_PARAM;
+ }
+ }
+
+ aBlock[5] = dir;
+
+ aBlock[6] = address & 0xFF;
+ aBlock[7] = ( address >> 8 ) & 0xFF;
+ aBlock[8] = ( address >> 16 ) & 0xFF;
+ aBlock[9] = ( address >> 24 ) & 0xFF;
+
+ aBlock[10] = frameCounter & 0xFF;
+ aBlock[11] = ( frameCounter >> 8 ) & 0xFF;
+ aBlock[12] = ( frameCounter >> 16 ) & 0xFF;
+ aBlock[13] = ( frameCounter >> 24 ) & 0xFF;
+
+ if( CryptoCtx.NvmCtx->LrWanVersion.Value > 0x01010000 )
+ {
+ // Introduced in LoRaWAN 1.1.1 specification
+ aBlock[15] = 0x01;
+ }
+
+ if( size > 0 )
+ {
+ if( SecureElementAesEncrypt( aBlock, 16, NWK_S_ENC_KEY, sBlock ) != SECURE_ELEMENT_SUCCESS )
+ {
+ return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC;
+ }
+ for( uint8_t i = 0; i < size; i++ )
+ {
+ buffer[bufferIndex + i] = buffer[bufferIndex + i] ^ sBlock[i];
+ }
+ }
+
+ return LORAMAC_CRYPTO_SUCCESS;
+}
+#endif
+
+/*
+ * Prepares B0 block for cmac computation.
+ *
+ * \param[IN] msgLen - Length of message
+ * \param[IN] keyID - Key identifier
+ * \param[IN] isAck - True if it is a acknowledge frame ( Sets ConfFCnt in B0 block )
+ * \param[IN] devAddr - Device address
+ * \param[IN] dir - Frame direction ( Uplink:0, Downlink:1 )
+ * \param[IN] fCnt - Frame counter
+ * \param[IN/OUT] b0 - B0 block
+ * \retval - Status of the operation
+ */
+static LoRaMacCryptoStatus_t PrepareB0( uint16_t msgLen, KeyIdentifier_t keyID, bool isAck, uint8_t dir, uint32_t devAddr, uint32_t fCnt, uint8_t* b0 )
+{
+ if( b0 == 0 )
+ {
+ return LORAMAC_CRYPTO_ERROR_NPE;
+ }
+
+ b0[0] = 0x49;
+
+ if( ( isAck == true ) && ( dir == DOWNLINK ) )
+ {
+ // confFCnt contains the frame counter value modulo 2^16 of the "confirmed" uplink or downlink frame that is being acknowledged
+ uint16_t confFCnt = 0;
+
+ confFCnt = ( uint16_t )( CryptoCtx.NvmCtx->FCntList.FCntUp % 65536 );
+
+ b0[1] = confFCnt & 0xFF;
+ b0[2] = ( confFCnt >> 8 ) & 0xFF;
+ }
+ else
+ {
+ b0[1] = 0x00;
+ b0[2] = 0x00;
+ }
+
+ b0[3] = 0x00;
+ b0[4] = 0x00;
+
+ b0[5] = dir;
+
+ b0[6] = devAddr & 0xFF;
+ b0[7] = ( devAddr >> 8 ) & 0xFF;
+ b0[8] = ( devAddr >> 16 ) & 0xFF;
+ b0[9] = ( devAddr >> 24 ) & 0xFF;
+
+ b0[10] = fCnt & 0xFF;
+ b0[11] = ( fCnt >> 8 ) & 0xFF;
+ b0[12] = ( fCnt >> 16 ) & 0xFF;
+ b0[13] = ( fCnt >> 24 ) & 0xFF;
+
+ b0[14] = 0x00;
+
+ b0[15] = msgLen & 0xFF;
+
+ return LORAMAC_CRYPTO_SUCCESS;
+}
+
+/*
+ * Computes cmac with adding B0 block in front.
+ *
+ * cmac = aes128_cmac(keyID, B0 | msg)
+ *
+ * \param[IN] msg - Message to compute the integrity code
+ * \param[IN] len - Length of message
+ * \param[IN] keyID - Key identifier
+ * \param[IN] isAck - True if it is a acknowledge frame ( Sets ConfFCnt in B0 block )
+ * \param[IN] devAddr - Device address
+ * \param[IN] dir - Frame direction ( Uplink:0, Downlink:1 )
+ * \param[IN] fCnt - Frame counter
+ * \param[OUT] cmac - Computed cmac
+ * \retval - Status of the operation
+ */
+static LoRaMacCryptoStatus_t ComputeCmacB0( uint8_t* msg, uint16_t len, KeyIdentifier_t keyID, bool isAck, uint8_t dir, uint32_t devAddr, uint32_t fCnt, uint32_t* cmac )
+{
+ if( ( msg == 0 ) || ( cmac == 0 ) )
+ {
+ return LORAMAC_CRYPTO_ERROR_NPE;
+ }
+ if( len > CRYPTO_MAXMESSAGE_SIZE )
+ {
+ return LORAMAC_CRYPTO_ERROR_BUF_SIZE;
+ }
+
+ uint8_t micBuff[MIC_BLOCK_BX_SIZE];
+
+ // Initialize the first Block
+ PrepareB0( len, keyID, isAck, dir, devAddr, fCnt, micBuff );
+
+ if( SecureElementComputeAesCmac( micBuff, msg, len, keyID, cmac ) != SECURE_ELEMENT_SUCCESS )
+ {
+ return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC;
+ }
+ return LORAMAC_CRYPTO_SUCCESS;
+}
+
+/*!
+ * Verifies cmac with adding B0 block in front.
+ *
+ * \param[IN] msg - Message to compute the integrity code
+ * \param[IN] len - Length of message
+ * \param[IN] keyID - Key identifier
+ * \param[IN] isAck - True if it is a acknowledge frame ( Sets ConfFCnt in B0 block )
+ * \param[IN] devAddr - Device address
+ * \param[IN] dir - Frame direction ( Uplink:0, Downlink:1 )
+ * \param[IN] fCnt - Frame counter
+ * \param[in] expectedCmac - Expected cmac
+ * \retval - Status of the operation
+ */
+static LoRaMacCryptoStatus_t VerifyCmacB0( uint8_t* msg, uint16_t len, KeyIdentifier_t keyID, bool isAck, uint8_t dir, uint32_t devAddr, uint32_t fCnt, uint32_t expectedCmac )
+{
+ if( msg == 0 )
+ {
+ return LORAMAC_CRYPTO_ERROR_NPE;
+ }
+ if( len > CRYPTO_MAXMESSAGE_SIZE )
+ {
+ return LORAMAC_CRYPTO_ERROR_BUF_SIZE;
+ }
+
+ uint8_t micBuff[CRYPTO_BUFFER_SIZE];
+ memset1( micBuff, 0, CRYPTO_BUFFER_SIZE );
+
+ // Initialize the first Block
+ PrepareB0( len, keyID, isAck, dir, devAddr, fCnt, micBuff );
+
+ // Copy the given data to the mic computation buffer
+ memcpy1( ( micBuff + MIC_BLOCK_BX_SIZE ), msg, len );
+
+ SecureElementStatus_t retval = SECURE_ELEMENT_ERROR;
+ retval = SecureElementVerifyAesCmac( micBuff, ( len + MIC_BLOCK_BX_SIZE ), expectedCmac, keyID );
+
+ if( retval == SECURE_ELEMENT_SUCCESS )
+ {
+ return LORAMAC_CRYPTO_SUCCESS;
+ }
+ else if( retval == SECURE_ELEMENT_FAIL_CMAC )
+ {
+ return LORAMAC_CRYPTO_FAIL_MIC;
+ }
+
+ return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC;
+}
+
+#if( USE_LRWAN_1_1_X_CRYPTO == 1 )
+/*
+ * Prpares B1 block for cmac computation.
+ *
+ * \param[IN] msgLen - Length of message
+ * \param[IN] keyID - Key identifier
+ * \param[IN] isAck - True if it is a acknowledge frame ( Sets ConfFCnt in B0 block )
+ * \param[IN] txDr - Data rate used for the transmission
+ * \param[IN] txCh - Index of the channel used for the transmission
+ * \param[IN] devAddr - Device address
+ * \param[IN] fCntUp - Frame counter
+ * \param[IN/OUT] b0 - B0 block
+ * \retval - Status of the operation
+ */
+static LoRaMacCryptoStatus_t PrepareB1( uint16_t msgLen, KeyIdentifier_t keyID, bool isAck, uint8_t txDr, uint8_t txCh, uint32_t devAddr, uint32_t fCntUp, uint8_t* b1 )
+{
+ if( b1 == 0 )
+ {
+ return LORAMAC_CRYPTO_ERROR_NPE;
+ }
+
+ b1[0] = 0x49;
+
+ if( isAck == true )
+ {
+ // confFCnt contains the frame counter value modulo 2^16 of the "confirmed" uplink frame that is being acknowledged
+ uint16_t confFCnt = ( uint16_t )( *CryptoCtx.NvmCtx->LastDownFCnt % 65536 );
+ b1[1] = confFCnt & 0xFF;
+ b1[2] = ( confFCnt >> 8 ) & 0xFF;
+ }
+ else
+ {
+ b1[1] = 0x00;
+ b1[2] = 0x00;
+ }
+
+ b1[3] = txDr;
+ b1[4] = txCh;
+ b1[5] = UPLINK; // dir = Uplink
+
+ b1[6] = devAddr & 0xFF;
+ b1[7] = ( devAddr >> 8 ) & 0xFF;
+ b1[8] = ( devAddr >> 16 ) & 0xFF;
+ b1[9] = ( devAddr >> 24 ) & 0xFF;
+
+ b1[10] = fCntUp & 0xFF;
+ b1[11] = ( fCntUp >> 8 ) & 0xFF;
+ b1[12] = ( fCntUp >> 16 ) & 0xFF;
+ b1[13] = ( fCntUp >> 24 ) & 0xFF;
+
+ b1[14] = 0x00;
+
+ b1[15] = msgLen & 0xFF;
+
+ return LORAMAC_CRYPTO_SUCCESS;
+}
+
+/*
+ * Computes cmac with adding B1 block in front ( only for Uplink frames LoRaWAN 1.1 )
+ *
+ * cmac = aes128_cmac(keyID, B1 | msg)
+ *
+ * \param[IN] msg - Message to calculate the Integrity code
+ * \param[IN] len - Length of message
+ * \param[IN] keyID - Key identifier
+ * \param[IN] isAck - True if it is a acknowledge frame ( Sets ConfFCnt in B0 block )
+ * \param[IN] txDr - Data rate used for the transmission
+ * \param[IN] txCh - Index of the channel used for the transmission
+ * \param[IN] devAddr - Device address
+ * \param[IN] fCntUp - Uplink Frame counter
+ * \param[OUT] cmac - Computed cmac
+ * \retval - Status of the operation
+ */
+static LoRaMacCryptoStatus_t ComputeCmacB1( uint8_t* msg, uint16_t len, KeyIdentifier_t keyID, bool isAck, uint8_t txDr, uint8_t txCh, uint32_t devAddr, uint32_t fCntUp, uint32_t* cmac )
+{
+ if( ( msg == 0 ) || ( cmac == 0 ) )
+ {
+ return LORAMAC_CRYPTO_ERROR_NPE;
+ }
+ if( len > CRYPTO_MAXMESSAGE_SIZE )
+ {
+ return LORAMAC_CRYPTO_ERROR_BUF_SIZE;
+ }
+
+ uint8_t micBuff[MIC_BLOCK_BX_SIZE];
+
+ // Initialize the first Block
+ PrepareB1( len, keyID, isAck, txDr, txCh, devAddr, fCntUp, micBuff );
+
+ if( SecureElementComputeAesCmac( micBuff, msg, len, keyID, cmac ) != SECURE_ELEMENT_SUCCESS )
+ {
+ return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC;
+ }
+ return LORAMAC_CRYPTO_SUCCESS;
+}
+#endif
+
+/*
+ * Gets security item from list.
+ *
+ * \param[IN] addrID - Address identifier
+ * \param[OUT] keyItem - Key item reference
+ * \retval - Status of the operation
+ */
+static LoRaMacCryptoStatus_t GetKeyAddrItem( AddressIdentifier_t addrID, KeyAddr_t** item )
+{
+ for( uint8_t i = 0; i < NUM_OF_SEC_CTX; i++ )
+ {
+ if( KeyAddrList[i].AddrID == addrID )
+ {
+ *item = &( KeyAddrList[i] );
+ return LORAMAC_CRYPTO_SUCCESS;
+ }
+ }
+ return LORAMAC_CRYPTO_ERROR_INVALID_ADDR_ID;
+}
+
+/*
+ * Derives a session key as of LoRaWAN versions prior to 1.1.0
+ *
+ * \param[IN] keyID - Key Identifier for the key to be calculated
+ * \param[IN] joinNonce - Sever nonce
+ * \param[IN] netID - Network Identifier
+ * \param[IN] deviceNonce - Device nonce
+ * \retval - Status of the operation
+ */
+static LoRaMacCryptoStatus_t DeriveSessionKey10x( KeyIdentifier_t keyID, uint8_t* joinNonce, uint8_t* netID, uint8_t* devNonce )
+{
+ if( ( joinNonce == 0 ) || ( netID == 0 ) || ( devNonce == 0 ) )
+ {
+ return LORAMAC_CRYPTO_ERROR_NPE;
+ }
+
+ uint8_t compBase[16] = { 0 };
+
+ switch( keyID )
+ {
+ case F_NWK_S_INT_KEY:
+ case S_NWK_S_INT_KEY:
+ case NWK_S_ENC_KEY:
+ compBase[0] = 0x01;
+ break;
+ case APP_S_KEY:
+ compBase[0] = 0x02;
+ break;
+ default:
+ return LORAMAC_CRYPTO_ERROR_INVALID_KEY_ID;
+ }
+
+ memcpy1( compBase + 1, joinNonce, 3 );
+ memcpy1( compBase + 4, netID, 3 );
+ memcpy1( compBase + 7, devNonce, 2 );
+
+ if( SecureElementDeriveAndStoreKey( CryptoCtx.NvmCtx->LrWanVersion, compBase, NWK_KEY, keyID ) != SECURE_ELEMENT_SUCCESS )
+ {
+ return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC;
+ }
+
+ return LORAMAC_CRYPTO_SUCCESS;
+}
+
+#if( USE_LRWAN_1_1_X_CRYPTO == 1 )
+/*
+ * Derives a session key as of LoRaWAN 1.1.0
+ *
+ * \param[IN] keyID - Key Identifier for the key to be calculated
+ * \param[IN] joinNonce - Sever nonce
+ * \param[IN] joinEUI - Join Server EUI
+ * \param[IN] deviceNonce - Device nonce
+ * \retval - Status of the operation
+ */
+static LoRaMacCryptoStatus_t DeriveSessionKey11x( KeyIdentifier_t keyID, uint8_t* joinNonce, uint8_t* joinEUI, uint8_t* devNonce )
+{
+ if( ( joinNonce == 0 ) || ( joinEUI == 0 ) || ( devNonce == 0 ) )
+ {
+ return LORAMAC_CRYPTO_ERROR_NPE;
+ }
+
+ uint8_t compBase[16] = { 0 };
+ KeyIdentifier_t rootKeyId = NWK_KEY;
+
+ switch( keyID )
+ {
+ case F_NWK_S_INT_KEY:
+ compBase[0] = 0x01;
+ break;
+ case S_NWK_S_INT_KEY:
+ compBase[0] = 0x03;
+ break;
+ case NWK_S_ENC_KEY:
+ compBase[0] = 0x04;
+ break;
+ case APP_S_KEY:
+ rootKeyId = APP_KEY;
+ compBase[0] = 0x02;
+ break;
+ default:
+ return LORAMAC_CRYPTO_ERROR_INVALID_KEY_ID;
+ }
+
+ memcpy1( compBase + 1, joinNonce, 3 );
+ memcpyr( compBase + 4, joinEUI, 8 );
+ memcpy1( compBase + 12, devNonce, 2 );
+
+ if( SecureElementDeriveAndStoreKey( CryptoCtx.NvmCtx->LrWanVersion, compBase, rootKeyId, keyID ) != SECURE_ELEMENT_SUCCESS )
+ {
+ return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC;
+ }
+
+ return LORAMAC_CRYPTO_SUCCESS;
+}
+
+/*
+ * Derives a life time session key (JSIntKey or JSEncKey) as of LoRaWAN 1.1.0
+ *
+ * \param[IN] keyID - Key Identifier for the key to be calculated
+ * \param[IN] devEUI - Device EUI
+ * \retval - Status of the operation
+ */
+static LoRaMacCryptoStatus_t DeriveLifeTimeSessionKey( KeyIdentifier_t keyID, uint8_t* devEUI )
+{
+ if( devEUI == 0 )
+ {
+ return LORAMAC_CRYPTO_ERROR_NPE;
+ }
+
+ uint8_t compBase[16] = { 0 };
+
+ switch( keyID )
+ {
+ case J_S_INT_KEY:
+ compBase[0] = 0x06;
+ break;
+ case J_S_ENC_KEY:
+ compBase[0] = 0x05;
+ break;
+ default:
+ return LORAMAC_CRYPTO_ERROR_INVALID_KEY_ID;
+ }
+
+ memcpyr( compBase + 1, devEUI, 8 );
+
+ if( SecureElementDeriveAndStoreKey( CryptoCtx.NvmCtx->LrWanVersion, compBase, NWK_KEY, keyID ) != SECURE_ELEMENT_SUCCESS )
+ {
+ return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC;
+ }
+
+ return LORAMAC_CRYPTO_SUCCESS;
+}
+#endif
+
+/*
+ * Gets the last received frame counter
+ *
+ * \param[IN] fCntID - Frame counter identifier
+ * \param[IN] lastDown - Last downlink counter value
+ *
+ * \retval - Status of the operation
+ */
+static LoRaMacCryptoStatus_t GetLastFcntDown( FCntIdentifier_t fCntID, uint32_t* lastDown )
+{
+ if( lastDown == NULL )
+ {
+ return LORAMAC_CRYPTO_ERROR_NPE;
+ }
+ switch( fCntID )
+ {
+ case N_FCNT_DOWN:
+ *lastDown = CryptoCtx.NvmCtx->FCntList.NFCntDown;
+ CryptoCtx.NvmCtx->LastDownFCnt = &CryptoCtx.NvmCtx->FCntList.NFCntDown;
+ break;
+ case A_FCNT_DOWN:
+ *lastDown = CryptoCtx.NvmCtx->FCntList.AFCntDown;
+ CryptoCtx.NvmCtx->LastDownFCnt = &CryptoCtx.NvmCtx->FCntList.AFCntDown;
+ break;
+ case FCNT_DOWN:
+ *lastDown = CryptoCtx.NvmCtx->FCntList.FCntDown;
+ CryptoCtx.NvmCtx->LastDownFCnt = &CryptoCtx.NvmCtx->FCntList.FCntDown;
+ break;
+ case MC_FCNT_DOWN_0:
+ *lastDown = CryptoCtx.NvmCtx->FCntList.McFCntDown0;
+ break;
+ case MC_FCNT_DOWN_1:
+ *lastDown = CryptoCtx.NvmCtx->FCntList.McFCntDown1;
+ break;
+ case MC_FCNT_DOWN_2:
+ *lastDown = CryptoCtx.NvmCtx->FCntList.McFCntDown2;
+ break;
+ case MC_FCNT_DOWN_3:
+ *lastDown = CryptoCtx.NvmCtx->FCntList.McFCntDown3;
+ break;
+ default:
+ return LORAMAC_CRYPTO_FAIL_FCNT_ID;
+ }
+ return LORAMAC_CRYPTO_SUCCESS;
+}
+
+/*
+ * Checks the downlink counter value
+ *
+ * \param[IN] fCntID - Frame counter identifier
+ * \param[IN] currentDown - Current downlink counter value
+ *
+ * \retval - Status of the operation
+ */
+static bool CheckFCntDown( FCntIdentifier_t fCntID, uint32_t currentDown )
+{
+ uint32_t lastDown = 0;
+ if( GetLastFcntDown( fCntID, &lastDown ) != LORAMAC_CRYPTO_SUCCESS )
+ {
+ return false;
+ }
+ if( ( currentDown > lastDown ) ||
+ // For LoRaWAN 1.0.X only. Allow downlink frames of 0
+ ( lastDown == FCNT_DOWN_INITAL_VALUE ) )
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+/*!
+ * Updates the reference downlink counter
+ *
+ * \param[IN] fCntID - Frame counter identifier
+ * \param[IN] currentDown - Current downlink counter value
+ *
+ * \retval - Status of the operation
+ */
+static void UpdateFCntDown( FCntIdentifier_t fCntID, uint32_t currentDown )
+{
+ switch( fCntID )
+ {
+ case N_FCNT_DOWN:
+ CryptoCtx.NvmCtx->FCntList.NFCntDown = currentDown;
+ break;
+ case A_FCNT_DOWN:
+ CryptoCtx.NvmCtx->FCntList.AFCntDown = currentDown;
+ break;
+ case FCNT_DOWN:
+ CryptoCtx.NvmCtx->FCntList.FCntDown = currentDown;
+ break;
+ case MC_FCNT_DOWN_0:
+ CryptoCtx.NvmCtx->FCntList.McFCntDown0 = currentDown;
+ break;
+ case MC_FCNT_DOWN_1:
+ CryptoCtx.NvmCtx->FCntList.McFCntDown1 = currentDown;
+ break;
+ case MC_FCNT_DOWN_2:
+ CryptoCtx.NvmCtx->FCntList.McFCntDown2 = currentDown;
+ break;
+ case MC_FCNT_DOWN_3:
+ CryptoCtx.NvmCtx->FCntList.McFCntDown3 = currentDown;
+ break;
+ default:
+ break;
+ }
+ CryptoCtx.EventCryptoNvmCtxChanged( );
+}
+
+/*!
+ * Resets the frame counters
+ */
+static void ResetFCnts( void )
+{
+
+ CryptoCtx.NvmCtx->FCntList.FCntUp = 0;
+ CryptoCtx.NvmCtx->FCntList.NFCntDown = FCNT_DOWN_INITAL_VALUE;
+ CryptoCtx.NvmCtx->FCntList.AFCntDown = FCNT_DOWN_INITAL_VALUE;
+ CryptoCtx.NvmCtx->FCntList.FCntDown = FCNT_DOWN_INITAL_VALUE;
+ CryptoCtx.NvmCtx->LastDownFCnt = &CryptoCtx.NvmCtx->FCntList.FCntDown;
+
+ CryptoCtx.NvmCtx->FCntList.McFCntDown0 = FCNT_DOWN_INITAL_VALUE;
+ CryptoCtx.NvmCtx->FCntList.McFCntDown1 = FCNT_DOWN_INITAL_VALUE;
+ CryptoCtx.NvmCtx->FCntList.McFCntDown2 = FCNT_DOWN_INITAL_VALUE;
+ CryptoCtx.NvmCtx->FCntList.McFCntDown3 = FCNT_DOWN_INITAL_VALUE;
+
+ CryptoCtx.EventCryptoNvmCtxChanged( );
+}
+
+/*
+ * Dummy callback in case if the user provides NULL function pointer
+ */
+static void DummyCB( void )
+{
+ return;
+}
+
+/*
+ * API functions
+ */
+
+LoRaMacCryptoStatus_t LoRaMacCryptoInit( LoRaMacCryptoNvmEvent cryptoNvmCtxChanged )
+{
+ // Assign non volatile context
+ CryptoCtx.NvmCtx = &NvmCryptoCtx;
+
+ // Assign callback
+ if( cryptoNvmCtxChanged != 0 )
+ {
+ CryptoCtx.EventCryptoNvmCtxChanged = cryptoNvmCtxChanged;
+ }
+ else
+ {
+ CryptoCtx.EventCryptoNvmCtxChanged = DummyCB;
+ }
+
+ // Initialize with default
+ memset1( (uint8_t*) CryptoCtx.NvmCtx, 0, sizeof( LoRaMacCryptoNvmCtx_t ) );
+
+ // Set default LoRaWAN version
+ CryptoCtx.NvmCtx->LrWanVersion.Fields.Major = 1;
+ CryptoCtx.NvmCtx->LrWanVersion.Fields.Minor = 1;
+ CryptoCtx.NvmCtx->LrWanVersion.Fields.Revision = 1;
+ CryptoCtx.NvmCtx->LrWanVersion.Fields.Rfu = 0;
+
+ // Reset frame counters
+ ResetFCnts( );
+
+ return LORAMAC_CRYPTO_SUCCESS;
+}
+
+LoRaMacCryptoStatus_t LoRaMacCryptoSetLrWanVersion( Version_t version )
+{
+ CryptoCtx.NvmCtx->LrWanVersion = version;
+ return LORAMAC_CRYPTO_SUCCESS;
+}
+
+LoRaMacCryptoStatus_t LoRaMacCryptoRestoreNvmCtx( void* cryptoNvmCtx )
+{
+ // Restore module context
+ if( cryptoNvmCtx != 0 )
+ {
+ memcpy1( ( uint8_t* ) &NvmCryptoCtx, ( uint8_t* ) cryptoNvmCtx, CRYPTO_NVM_CTX_SIZE );
+ return LORAMAC_CRYPTO_SUCCESS;
+ }
+ else
+ {
+ return LORAMAC_CRYPTO_ERROR_NPE;
+ }
+}
+
+void* LoRaMacCryptoGetNvmCtx( size_t* cryptoNvmCtxSize )
+{
+ *cryptoNvmCtxSize = CRYPTO_NVM_CTX_SIZE;
+ return &NvmCryptoCtx;
+}
+
+LoRaMacCryptoStatus_t LoRaMacCryptoGetFCntUp( uint32_t* currentUp )
+{
+ if( currentUp == NULL )
+ {
+ return LORAMAC_CRYPTO_ERROR_NPE;
+ }
+
+ *currentUp = CryptoCtx.NvmCtx->FCntList.FCntUp + 1;
+
+ return LORAMAC_CRYPTO_SUCCESS;
+}
+
+LoRaMacCryptoStatus_t LoRaMacCryptoGetFCntDown( FCntIdentifier_t fCntID, uint16_t maxFCntGap, uint32_t frameFcnt, uint32_t* currentDown )
+{
+ uint32_t lastDown = 0;
+ int32_t fCntDiff = 0;
+ LoRaMacCryptoStatus_t cryptoStatus = LORAMAC_CRYPTO_ERROR;
+
+ if( currentDown == NULL )
+ {
+ return LORAMAC_CRYPTO_ERROR_NPE;
+ }
+
+ cryptoStatus = GetLastFcntDown( fCntID, &lastDown );
+ if( cryptoStatus != LORAMAC_CRYPTO_SUCCESS )
+ {
+ return cryptoStatus;
+ }
+
+ // For LoRaWAN 1.0.X only, allow downlink frames of 0
+ if( lastDown == FCNT_DOWN_INITAL_VALUE )
+ {
+ *currentDown = frameFcnt;
+ }
+ else
+ {
+ // Add difference, consider roll-over
+ fCntDiff = ( int32_t )( ( int64_t )frameFcnt - ( int64_t )( lastDown & 0x0000FFFF ) );
+
+ if( fCntDiff > 0 )
+ { // Positive difference
+ *currentDown = lastDown + fCntDiff;
+ }
+ else if( fCntDiff == 0 )
+ { // Duplicate FCnt value, keep the current value.
+ *currentDown = lastDown;
+ return LORAMAC_CRYPTO_FAIL_FCNT_DUPLICATED;
+ }
+ else
+ { // Negative difference, assume a roll-over of one uint16_t
+ *currentDown = ( lastDown & 0xFFFF0000 ) + 0x10000 + frameFcnt;
+ }
+ }
+
+
+ // For LoRaWAN 1.0.X only, check maxFCntGap
+ if( CryptoCtx.NvmCtx->LrWanVersion.Fields.Minor == 0 )
+ {
+ if( ( ( int64_t )*currentDown - ( int64_t )lastDown ) >= maxFCntGap )
+ {
+ return LORAMAC_CRYPTO_FAIL_MAX_GAP_FCNT;
+ }
+ }
+
+ return LORAMAC_CRYPTO_SUCCESS;
+}
+
+LoRaMacCryptoStatus_t LoRaMacCryptoSetMulticastReference( MulticastCtx_t* multicastList )
+{
+ if( multicastList == NULL )
+ {
+ return LORAMAC_CRYPTO_ERROR_NPE;
+ }
+
+ multicastList[0].DownLinkCounter = &CryptoCtx.NvmCtx->FCntList.McFCntDown0;
+ multicastList[1].DownLinkCounter = &CryptoCtx.NvmCtx->FCntList.McFCntDown1;
+ multicastList[2].DownLinkCounter = &CryptoCtx.NvmCtx->FCntList.McFCntDown2;
+ multicastList[3].DownLinkCounter = &CryptoCtx.NvmCtx->FCntList.McFCntDown3;
+
+ return LORAMAC_CRYPTO_SUCCESS;
+}
+
+LoRaMacCryptoStatus_t LoRaMacCryptoSetKey( KeyIdentifier_t keyID, uint8_t* key )
+{
+ if( SecureElementSetKey( keyID, key ) != SECURE_ELEMENT_SUCCESS )
+ {
+ return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC;
+ }
+ if( ( keyID == GEN_APP_KEY ) || ( keyID == APP_KEY ) )
+ {
+ // Derive lifetime keys
+ if( LoRaMacCryptoDeriveMcRootKey( keyID ) != LORAMAC_CRYPTO_SUCCESS )
+ {
+ return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC;
+ }
+ if( LoRaMacCryptoDeriveMcKEKey( MC_ROOT_KEY ) != LORAMAC_CRYPTO_SUCCESS )
+ {
+ return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC;
+ }
+ }
+ return LORAMAC_CRYPTO_SUCCESS;
+}
+
+LoRaMacCryptoStatus_t LoRaMacCryptoPrepareJoinRequest( LoRaMacMessageJoinRequest_t* macMsg )
+{
+ if( macMsg == 0 )
+ {
+ return LORAMAC_CRYPTO_ERROR_NPE;
+ }
+ KeyIdentifier_t micComputationKeyID = NWK_KEY;
+
+ // Add device nonce
+#if ( USE_RANDOM_DEV_NONCE == 1 )
+ uint32_t devNonce = 0;
+ SecureElementRandomNumber( &devNonce );
+ CryptoCtx.NvmCtx->DevNonce = devNonce;
+#else
+ CryptoCtx.NvmCtx->DevNonce++;
+#endif
+ CryptoCtx.EventCryptoNvmCtxChanged( );
+ macMsg->DevNonce = CryptoCtx.NvmCtx->DevNonce;
+
+#if( USE_LRWAN_1_1_X_CRYPTO == 1 )
+ // Derive lifetime session keys
+ if( DeriveLifeTimeSessionKey( J_S_INT_KEY, macMsg->DevEUI ) != LORAMAC_CRYPTO_SUCCESS )
+ {
+ return LORAMAC_CRYPTO_ERROR;
+ }
+ if( DeriveLifeTimeSessionKey( J_S_ENC_KEY, macMsg->DevEUI ) != LORAMAC_CRYPTO_SUCCESS )
+ {
+ return LORAMAC_CRYPTO_ERROR;
+ }
+#endif
+
+ // Serialize message
+ if( LoRaMacSerializerJoinRequest( macMsg ) != LORAMAC_SERIALIZER_SUCCESS )
+ {
+ return LORAMAC_CRYPTO_ERROR_SERIALIZER;
+ }
+
+ // Compute mic
+ if( SecureElementComputeAesCmac( NULL, macMsg->Buffer, ( LORAMAC_JOIN_REQ_MSG_SIZE - LORAMAC_MIC_FIELD_SIZE ), micComputationKeyID, &macMsg->MIC ) != SECURE_ELEMENT_SUCCESS )
+ {
+ return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC;
+ }
+
+ // Reserialize message to add the MIC
+ if( LoRaMacSerializerJoinRequest( macMsg ) != LORAMAC_SERIALIZER_SUCCESS )
+ {
+ return LORAMAC_CRYPTO_ERROR_SERIALIZER;
+ }
+
+ return LORAMAC_CRYPTO_SUCCESS;
+}
+
+LoRaMacCryptoStatus_t LoRaMacCryptoPrepareReJoinType1( LoRaMacMessageReJoinType1_t* macMsg )
+{
+ if( macMsg == 0 )
+ {
+ return LORAMAC_CRYPTO_ERROR_NPE;
+ }
+
+ // Check for RJcount1 overflow
+ if( CryptoCtx.NvmCtx->RJcount1 == 65535 )
+ {
+ return LORAMAC_CRYPTO_ERROR_RJCOUNT1_OVERFLOW;
+ }
+
+ // Serialize message
+ if( LoRaMacSerializerReJoinType1( macMsg ) != LORAMAC_SERIALIZER_SUCCESS )
+ {
+ return LORAMAC_CRYPTO_ERROR_SERIALIZER;
+ }
+
+ // Compute mic
+ // cmac = aes128_cmac(JSIntKey, MHDR | RejoinType | JoinEUI| DevEUI | RJcount1)
+ if( SecureElementComputeAesCmac( NULL, macMsg->Buffer, ( LORAMAC_RE_JOIN_1_MSG_SIZE - LORAMAC_MIC_FIELD_SIZE ), J_S_INT_KEY, &macMsg->MIC ) != SECURE_ELEMENT_SUCCESS )
+ {
+ return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC;
+ }
+
+ // Reserialize message to add the MIC
+ if( LoRaMacSerializerReJoinType1( macMsg ) != LORAMAC_SERIALIZER_SUCCESS )
+ {
+ return LORAMAC_CRYPTO_ERROR_SERIALIZER;
+ }
+
+ // Increment RJcount1
+ CryptoCtx.NvmCtx->RJcount1++;
+ CryptoCtx.EventCryptoNvmCtxChanged( );
+
+ return LORAMAC_CRYPTO_SUCCESS;
+}
+
+LoRaMacCryptoStatus_t LoRaMacCryptoPrepareReJoinType0or2( LoRaMacMessageReJoinType0or2_t* macMsg )
+{
+ if( macMsg == 0 )
+ {
+ return LORAMAC_CRYPTO_ERROR_NPE;
+ }
+
+ // Check for RJcount0 overflow
+ if( CryptoCtx.RJcount0 == 65535 )
+ {
+ return LORAMAC_CRYPTO_FAIL_RJCOUNT0_OVERFLOW;
+ }
+
+ // Serialize message
+ if( LoRaMacSerializerReJoinType0or2( macMsg ) != LORAMAC_SERIALIZER_SUCCESS )
+ {
+ return LORAMAC_CRYPTO_ERROR_SERIALIZER;
+ }
+
+ // Compute mic
+ // cmac = aes128_cmac(SNwkSIntKey, MHDR | Rejoin Type | NetID | DevEUI | RJcount0)
+ if( SecureElementComputeAesCmac( NULL, macMsg->Buffer, ( LORAMAC_RE_JOIN_0_2_MSG_SIZE - LORAMAC_MIC_FIELD_SIZE ), S_NWK_S_INT_KEY, &macMsg->MIC ) != SECURE_ELEMENT_SUCCESS )
+ {
+ return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC;
+ }
+
+ // Reserialize message to add the MIC
+ if( LoRaMacSerializerReJoinType0or2( macMsg ) != LORAMAC_SERIALIZER_SUCCESS )
+ {
+ return LORAMAC_CRYPTO_ERROR_SERIALIZER;
+ }
+
+ // Increment RJcount0
+ CryptoCtx.RJcount0++;
+
+ return LORAMAC_CRYPTO_SUCCESS;
+}
+
+LoRaMacCryptoStatus_t LoRaMacCryptoHandleJoinAccept( JoinReqIdentifier_t joinReqType, uint8_t* joinEUI, LoRaMacMessageJoinAccept_t* macMsg )
+{
+ if( ( macMsg == 0 ) || ( joinEUI == 0 ) )
+ {
+ return LORAMAC_CRYPTO_ERROR_NPE;
+ }
+
+ LoRaMacCryptoStatus_t retval = LORAMAC_CRYPTO_ERROR;
+ KeyIdentifier_t micComputationKeyID;
+ KeyIdentifier_t encryptionKeyID;
+ uint8_t micComputationOffset = 0;
+#if( USE_LRWAN_1_1_X_CRYPTO == 1 )
+ uint8_t* devNonceForKeyDerivation = ( uint8_t* ) &CryptoCtx.NvmCtx->DevNonce;
+#endif
+
+ // Determine decryption key and DevNonce for key derivation
+ if( joinReqType == JOIN_REQ )
+ {
+ encryptionKeyID = NWK_KEY;
+ micComputationOffset = CRYPTO_MIC_COMPUTATION_OFFSET;
+ }
+#if( USE_LRWAN_1_1_X_CRYPTO == 1 )
+ else
+ {
+ encryptionKeyID = J_S_ENC_KEY;
+
+ // If Join-accept is a reply to a rejoin, the RJcount(0 or 1) replaces DevNonce in the key derivation process.
+ if( ( joinReqType == REJOIN_REQ_0 ) || ( joinReqType == REJOIN_REQ_2 ) )
+ {
+ devNonceForKeyDerivation = ( uint8_t* ) &CryptoCtx.RJcount0;
+ }
+ else
+ {
+ devNonceForKeyDerivation = ( uint8_t* ) &CryptoCtx.NvmCtx->RJcount1;
+ }
+ }
+#endif
+ // Decrypt header, skip MHDR
+ uint8_t procBuffer[CRYPTO_MAXMESSAGE_SIZE + CRYPTO_MIC_COMPUTATION_OFFSET];
+ memset1( procBuffer, 0, ( macMsg->BufSize + micComputationOffset ) );
+
+ if( SecureElementAesEncrypt( macMsg->Buffer + LORAMAC_MHDR_FIELD_SIZE, ( macMsg->BufSize - LORAMAC_MHDR_FIELD_SIZE ), encryptionKeyID, ( procBuffer + micComputationOffset ) ) != SECURE_ELEMENT_SUCCESS )
+ {
+ return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC;
+ }
+ // Copy the result to an offset location to keep space for additional information which have to be added in case of 1.1 and later
+ memcpy1( macMsg->Buffer + LORAMAC_MHDR_FIELD_SIZE, ( procBuffer + micComputationOffset ), ( macMsg->BufSize - LORAMAC_MHDR_FIELD_SIZE ) );
+
+ // Parse the message
+ if( LoRaMacParserJoinAccept( macMsg ) != LORAMAC_PARSER_SUCCESS )
+ {
+ return LORAMAC_CRYPTO_ERROR_PARSER;
+ }
+
+ // Is it a LoRaWAN 1.1.0 or later ?
+ if( macMsg->DLSettings.Bits.OptNeg == 1 )
+ {
+ CryptoCtx.NvmCtx->LrWanVersion.Fields.Minor = 1;
+ micComputationKeyID = J_S_INT_KEY;
+ }
+ else
+ {
+ CryptoCtx.NvmCtx->LrWanVersion.Fields.Minor = 0;
+ micComputationKeyID = NWK_KEY;
+ }
+
+ // Verify mic
+ if( CryptoCtx.NvmCtx->LrWanVersion.Fields.Minor == 0 )
+ {
+ // For legacy mode :
+ // cmac = aes128_cmac(NwkKey, MHDR | JoinNonce | NetID | DevAddr | DLSettings | RxDelay | CFList | CFListType)
+ if( SecureElementVerifyAesCmac( macMsg->Buffer, ( macMsg->BufSize - LORAMAC_MIC_FIELD_SIZE ), macMsg->MIC, micComputationKeyID ) != SECURE_ELEMENT_SUCCESS )
+ {
+ return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC;
+ }
+ }
+ else
+ {
+ // For 1.1 and later:
+ // cmac = aes128_cmac(JSIntKey, JoinReqType | JoinEUI | DevNonce | MHDR | JoinNonce | NetID | DevAddr | DLSettings | RxDelay | CFList | CFListType)
+
+ // Prepare the msg for integrity check (adding JoinReqType, JoinEUI and DevNonce)
+ uint16_t bufItr = 0;
+ procBuffer[bufItr++] = ( uint8_t ) joinReqType;
+
+ memcpyr( &procBuffer[bufItr], joinEUI, LORAMAC_JOIN_EUI_FIELD_SIZE );
+ bufItr += LORAMAC_JOIN_EUI_FIELD_SIZE;
+
+ procBuffer[bufItr++] = CryptoCtx.NvmCtx->DevNonce & 0xFF;
+ procBuffer[bufItr++] = ( CryptoCtx.NvmCtx->DevNonce >> 8 ) & 0xFF;
+
+ procBuffer[bufItr++] = macMsg->MHDR.Value;
+
+ if( SecureElementVerifyAesCmac( procBuffer, ( macMsg->BufSize + micComputationOffset - LORAMAC_MHDR_FIELD_SIZE - LORAMAC_MIC_FIELD_SIZE ), macMsg->MIC, micComputationKeyID ) != SECURE_ELEMENT_SUCCESS )
+ {
+ return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC;
+ }
+
+ // Check if the JoinNonce is greater as the previous one
+ uint32_t currentJoinNonce = 0;
+ currentJoinNonce = ( uint32_t ) macMsg->JoinNonce[0];
+ currentJoinNonce |= ( ( uint32_t ) macMsg->JoinNonce[1] << 8 );
+ currentJoinNonce |= ( ( uint32_t ) macMsg->JoinNonce[2] << 16 );
+
+ if( currentJoinNonce > CryptoCtx.NvmCtx->JoinNonce )
+ {
+ CryptoCtx.NvmCtx->JoinNonce = currentJoinNonce;
+ CryptoCtx.EventCryptoNvmCtxChanged( );
+ }
+ else
+ {
+ return LORAMAC_CRYPTO_FAIL_JOIN_NONCE;
+ }
+ }
+
+ // Derive session keys
+#if( USE_LRWAN_1_1_X_CRYPTO == 1 )
+ if( CryptoCtx.NvmCtx->LrWanVersion.Fields.Minor == 1 )
+ {
+ // Derive lifetime keys
+ retval = LoRaMacCryptoDeriveMcRootKey( APP_KEY );
+ if( retval != LORAMAC_CRYPTO_SUCCESS )
+ {
+ return retval;
+ }
+
+ retval = LoRaMacCryptoDeriveMcKEKey( MC_ROOT_KEY );
+ if( retval != LORAMAC_CRYPTO_SUCCESS )
+ {
+ return retval;
+ }
+
+ retval = DeriveSessionKey11x( F_NWK_S_INT_KEY, macMsg->JoinNonce, joinEUI, devNonceForKeyDerivation );
+ if( retval != LORAMAC_CRYPTO_SUCCESS )
+ {
+ return retval;
+ }
+
+ retval = DeriveSessionKey11x( S_NWK_S_INT_KEY, macMsg->JoinNonce, joinEUI, devNonceForKeyDerivation );
+ if( retval != LORAMAC_CRYPTO_SUCCESS )
+ {
+ return retval;
+ }
+
+ retval = DeriveSessionKey11x( NWK_S_ENC_KEY, macMsg->JoinNonce, joinEUI, devNonceForKeyDerivation );
+ if( retval != LORAMAC_CRYPTO_SUCCESS )
+ {
+ return retval;
+ }
+
+ retval = DeriveSessionKey11x( APP_S_KEY, macMsg->JoinNonce, joinEUI, devNonceForKeyDerivation );
+ if( retval != LORAMAC_CRYPTO_SUCCESS )
+ {
+ return retval;
+ }
+ }
+ else
+#endif
+ {
+ // prior LoRaWAN 1.1.0
+ retval = LoRaMacCryptoDeriveMcRootKey( GEN_APP_KEY );
+ if( retval != LORAMAC_CRYPTO_SUCCESS )
+ {
+ return retval;
+ }
+
+ retval = LoRaMacCryptoDeriveMcKEKey( MC_ROOT_KEY );
+ if( retval != LORAMAC_CRYPTO_SUCCESS )
+ {
+ return retval;
+ }
+
+ retval = DeriveSessionKey10x( APP_S_KEY, macMsg->JoinNonce, macMsg->NetID, ( uint8_t* ) &CryptoCtx.NvmCtx->DevNonce );
+ if( retval != LORAMAC_CRYPTO_SUCCESS )
+ {
+ return retval;
+ }
+
+ retval = DeriveSessionKey10x( NWK_S_ENC_KEY, macMsg->JoinNonce, macMsg->NetID, ( uint8_t* ) &CryptoCtx.NvmCtx->DevNonce );
+ if( retval != LORAMAC_CRYPTO_SUCCESS )
+ {
+ return retval;
+ }
+
+ retval = DeriveSessionKey10x( F_NWK_S_INT_KEY, macMsg->JoinNonce, macMsg->NetID, ( uint8_t* ) &CryptoCtx.NvmCtx->DevNonce );
+ if( retval != LORAMAC_CRYPTO_SUCCESS )
+ {
+ return retval;
+ }
+
+ retval = DeriveSessionKey10x( S_NWK_S_INT_KEY, macMsg->JoinNonce, macMsg->NetID, ( uint8_t* ) &CryptoCtx.NvmCtx->DevNonce );
+ if( retval != LORAMAC_CRYPTO_SUCCESS )
+ {
+ return retval;
+ }
+ }
+
+ // Join-Accept is successfully processed, reset frame counters
+ CryptoCtx.RJcount0 = 0;
+ CryptoCtx.NvmCtx->FCntList.FCntUp = 0;
+ CryptoCtx.NvmCtx->FCntList.FCntDown = FCNT_DOWN_INITAL_VALUE;
+ CryptoCtx.NvmCtx->FCntList.NFCntDown = FCNT_DOWN_INITAL_VALUE;
+ CryptoCtx.NvmCtx->FCntList.AFCntDown = FCNT_DOWN_INITAL_VALUE;
+ CryptoCtx.EventCryptoNvmCtxChanged( );
+
+ return LORAMAC_CRYPTO_SUCCESS;
+}
+
+LoRaMacCryptoStatus_t LoRaMacCryptoSecureMessage( uint32_t fCntUp, uint8_t txDr, uint8_t txCh, LoRaMacMessageData_t* macMsg )
+{
+ LoRaMacCryptoStatus_t retval = LORAMAC_CRYPTO_ERROR;
+ KeyIdentifier_t payloadDecryptionKeyID = APP_S_KEY;
+
+ if( macMsg == NULL )
+ {
+ return LORAMAC_CRYPTO_ERROR_NPE;
+ }
+
+ if( fCntUp < CryptoCtx.NvmCtx->FCntList.FCntUp )
+ {
+ return LORAMAC_CRYPTO_FAIL_FCNT_SMALLER;
+ }
+
+ // Encrypt payload
+ if( macMsg->FPort == 0 )
+ {
+ // Use network session key
+ payloadDecryptionKeyID = NWK_S_ENC_KEY;
+ }
+
+ if( fCntUp > CryptoCtx.NvmCtx->FCntList.FCntUp )
+ {
+ retval = PayloadEncrypt( macMsg->FRMPayload, macMsg->FRMPayloadSize, payloadDecryptionKeyID, macMsg->FHDR.DevAddr, UPLINK, fCntUp );
+ if( retval != LORAMAC_CRYPTO_SUCCESS )
+ {
+ return retval;
+ }
+
+#if( USE_LRWAN_1_1_X_CRYPTO == 1 )
+ if( CryptoCtx.NvmCtx->LrWanVersion.Fields.Minor == 1 )
+ {
+ // Encrypt FOpts
+ retval = FOptsEncrypt( macMsg->FHDR.FCtrl.Bits.FOptsLen, macMsg->FHDR.DevAddr, UPLINK, FCNT_UP, fCntUp, macMsg->FHDR.FOpts );
+ if( retval != LORAMAC_CRYPTO_SUCCESS )
+ {
+ return retval;
+ }
+ }
+#endif
+ }
+ CryptoCtx.NvmCtx->FCntList.FCntUp = fCntUp;
+ CryptoCtx.EventCryptoNvmCtxChanged( );
+
+ // Serialize message
+ if( LoRaMacSerializerData( macMsg ) != LORAMAC_SERIALIZER_SUCCESS )
+ {
+ return LORAMAC_CRYPTO_ERROR_SERIALIZER;
+ }
+
+ // Compute mic
+#if( USE_LRWAN_1_1_X_CRYPTO == 1 )
+ if( CryptoCtx.NvmCtx->LrWanVersion.Fields.Minor == 1 )
+ {
+ uint32_t cmacS = 0;
+ uint32_t cmacF = 0;
+
+ // cmacS = aes128_cmac(SNwkSIntKey, B1 | msg)
+ retval = ComputeCmacB1( macMsg->Buffer, ( macMsg->BufSize - LORAMAC_MIC_FIELD_SIZE ), S_NWK_S_INT_KEY, macMsg->FHDR.FCtrl.Bits.Ack, txDr, txCh, macMsg->FHDR.DevAddr, fCntUp, &cmacS );
+ if( retval != LORAMAC_CRYPTO_SUCCESS )
+ {
+ return retval;
+ }
+ //cmacF = aes128_cmac(FNwkSIntKey, B0 | msg)
+ retval = ComputeCmacB0( macMsg->Buffer, ( macMsg->BufSize - LORAMAC_MIC_FIELD_SIZE ), F_NWK_S_INT_KEY, macMsg->FHDR.FCtrl.Bits.Ack, UPLINK, macMsg->FHDR.DevAddr, fCntUp, &cmacF );
+ if( retval != LORAMAC_CRYPTO_SUCCESS )
+ {
+ return retval;
+ }
+ // MIC = cmacS[0..1] | cmacF[0..1]
+ macMsg->MIC = ( ( cmacF << 16 ) & 0xFFFF0000 ) | ( cmacS & 0x0000FFFF );
+ }
+ else
+#endif
+ {
+ // MIC = cmacF[0..3]
+ // The IsAck parameter is every time false since the ConfFCnt field is not used in legacy mode.
+ retval = ComputeCmacB0( macMsg->Buffer, ( macMsg->BufSize - LORAMAC_MIC_FIELD_SIZE ), NWK_S_ENC_KEY, false, UPLINK, macMsg->FHDR.DevAddr, fCntUp, &macMsg->MIC );
+ if( retval != LORAMAC_CRYPTO_SUCCESS )
+ {
+ return retval;
+ }
+ }
+
+ // Re-serialize message to add the MIC
+ if( LoRaMacSerializerData( macMsg ) != LORAMAC_SERIALIZER_SUCCESS )
+ {
+ return LORAMAC_CRYPTO_ERROR_SERIALIZER;
+ }
+
+ return LORAMAC_CRYPTO_SUCCESS;
+}
+
+LoRaMacCryptoStatus_t LoRaMacCryptoUnsecureMessage( AddressIdentifier_t addrID, uint32_t address, FCntIdentifier_t fCntID, uint32_t fCntDown, LoRaMacMessageData_t* macMsg )
+{
+ if( macMsg == 0 )
+ {
+ return LORAMAC_CRYPTO_ERROR_NPE;
+ }
+
+ if( CheckFCntDown( fCntID, fCntDown ) == false )
+ {
+ return LORAMAC_CRYPTO_FAIL_FCNT_SMALLER;
+ }
+
+ LoRaMacCryptoStatus_t retval = LORAMAC_CRYPTO_ERROR;
+ KeyIdentifier_t payloadDecryptionKeyID = APP_S_KEY;
+ KeyIdentifier_t micComputationKeyID = S_NWK_S_INT_KEY;
+ KeyAddr_t* curItem;
+
+ // Parse the message
+ if( LoRaMacParserData( macMsg ) != LORAMAC_PARSER_SUCCESS )
+ {
+ return LORAMAC_CRYPTO_ERROR_PARSER;
+ }
+
+ // Determine current security context
+ retval = GetKeyAddrItem( addrID, &curItem );
+ if( retval != LORAMAC_CRYPTO_SUCCESS )
+ {
+ return retval;
+ }
+
+ payloadDecryptionKeyID = curItem->AppSkey;
+ micComputationKeyID = curItem->NwkSkey;
+
+ // Check if it is our address
+ if( address != macMsg->FHDR.DevAddr )
+ {
+ return LORAMAC_CRYPTO_FAIL_ADDRESS;
+ }
+
+ // Compute mic
+ bool isAck = macMsg->FHDR.FCtrl.Bits.Ack;
+ if( CryptoCtx.NvmCtx->LrWanVersion.Fields.Minor == 0 )
+ {
+ // In legacy mode the IsAck parameter is forced to be false since the ConfFCnt field is not used.
+ isAck = false;
+ }
+
+ // Verify mic
+ retval = VerifyCmacB0( macMsg->Buffer, ( macMsg->BufSize - LORAMAC_MIC_FIELD_SIZE ), micComputationKeyID, isAck, DOWNLINK, address, fCntDown, macMsg->MIC );
+ if( retval != LORAMAC_CRYPTO_SUCCESS )
+ {
+ return retval;
+ }
+
+ // Decrypt payload
+ if( macMsg->FPort == 0 )
+ {
+ // Use network session encryption key
+ payloadDecryptionKeyID = NWK_S_ENC_KEY;
+ }
+ retval = PayloadEncrypt( macMsg->FRMPayload, macMsg->FRMPayloadSize, payloadDecryptionKeyID, address, DOWNLINK, fCntDown );
+ if( retval != LORAMAC_CRYPTO_SUCCESS )
+ {
+ return retval;
+ }
+
+#if( USE_LRWAN_1_1_X_CRYPTO == 1 )
+ if( CryptoCtx.NvmCtx->LrWanVersion.Fields.Minor == 1 )
+ {
+ // Decrypt FOpts
+ retval = FOptsEncrypt( macMsg->FHDR.FCtrl.Bits.FOptsLen, address, DOWNLINK, fCntID, fCntDown, macMsg->FHDR.FOpts );
+ if( retval != LORAMAC_CRYPTO_SUCCESS )
+ {
+ return retval;
+ }
+ }
+#endif
+
+ UpdateFCntDown( fCntID, fCntDown );
+
+ return LORAMAC_CRYPTO_SUCCESS;
+}
+
+LoRaMacCryptoStatus_t LoRaMacCryptoDeriveMcRootKey( KeyIdentifier_t keyID )
+{
+ // Prevent other keys than GenAppKey for LoRaWAN 1.0.x or AppKey for LoRaWAN 1.1 or later
+ if( ( ( keyID == APP_KEY ) && ( CryptoCtx.NvmCtx->LrWanVersion.Fields.Minor == 0 ) ) ||
+ ( ( keyID == GEN_APP_KEY ) && ( CryptoCtx.NvmCtx->LrWanVersion.Fields.Minor == 1 ) ) )
+ {
+ return LORAMAC_CRYPTO_ERROR_INVALID_KEY_ID;
+ }
+ uint8_t compBase[16] = { 0 };
+
+ if( CryptoCtx.NvmCtx->LrWanVersion.Fields.Minor == 1 )
+ {
+ compBase[0] = 0x20;
+ }
+ if( SecureElementDeriveAndStoreKey( CryptoCtx.NvmCtx->LrWanVersion, compBase, keyID, MC_ROOT_KEY ) != SECURE_ELEMENT_SUCCESS )
+ {
+ return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC;
+ }
+
+ return LORAMAC_CRYPTO_SUCCESS;
+}
+
+LoRaMacCryptoStatus_t LoRaMacCryptoDeriveMcKEKey( KeyIdentifier_t keyID )
+{
+ // Prevent other keys than McRootKey
+ if( keyID != MC_ROOT_KEY )
+ {
+ return LORAMAC_CRYPTO_ERROR_INVALID_KEY_ID;
+ }
+ uint8_t compBase[16] = { 0 };
+
+ if( SecureElementDeriveAndStoreKey( CryptoCtx.NvmCtx->LrWanVersion, compBase, keyID, MC_KE_KEY ) != SECURE_ELEMENT_SUCCESS )
+ {
+ return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC;
+ }
+
+ return LORAMAC_CRYPTO_SUCCESS;
+}
+
+LoRaMacCryptoStatus_t LoRaMacCryptoDeriveMcSessionKeyPair( AddressIdentifier_t addrID, uint32_t mcAddr )
+{
+ if( mcAddr == 0 )
+ {
+ return LORAMAC_CRYPTO_ERROR_NPE;
+ }
+
+ LoRaMacCryptoStatus_t retval = LORAMAC_CRYPTO_ERROR;
+
+ // Determine current security context
+ KeyAddr_t* curItem;
+ retval = GetKeyAddrItem( addrID, &curItem );
+ if( retval != LORAMAC_CRYPTO_SUCCESS )
+ {
+ return retval;
+ }
+
+ //McAppSKey = aes128_encrypt(McKey, 0x01 | McAddr | pad16)
+ //McNwkSKey = aes128_encrypt(McKey, 0x02 | McAddr | pad16)
+
+ uint8_t compBaseAppS[16] = { 0 };
+ uint8_t compBaseNwkS[16] = { 0 };
+
+ compBaseAppS[0] = 0x01;
+ compBaseAppS[1] = mcAddr & 0xFF;
+ compBaseAppS[2] = ( mcAddr >> 8 ) & 0xFF;
+ compBaseAppS[3] = ( mcAddr >> 16 ) & 0xFF;
+ compBaseAppS[4] = ( mcAddr >> 24 ) & 0xFF;
+
+ compBaseNwkS[0] = 0x02;
+ compBaseNwkS[1] = mcAddr & 0xFF;
+ compBaseNwkS[2] = ( mcAddr >> 8 ) & 0xFF;
+ compBaseNwkS[3] = ( mcAddr >> 16 ) & 0xFF;
+ compBaseNwkS[4] = ( mcAddr >> 24 ) & 0xFF;
+
+ if( SecureElementDeriveAndStoreKey( CryptoCtx.NvmCtx->LrWanVersion, compBaseAppS, curItem->RootKey, curItem->AppSkey ) != SECURE_ELEMENT_SUCCESS )
+ {
+ return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC;
+ }
+
+ if( SecureElementDeriveAndStoreKey( CryptoCtx.NvmCtx->LrWanVersion, compBaseNwkS, curItem->RootKey, curItem->NwkSkey ) != SECURE_ELEMENT_SUCCESS )
+ {
+ return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC;
+ }
+
+ return LORAMAC_CRYPTO_SUCCESS;
+}
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacCrypto.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacCrypto.h
new file mode 100644
index 00000000..613740b7
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacCrypto.h
@@ -0,0 +1,320 @@
+/*!
+ * \file LoRaMacCrypto.h
+ *
+ * \brief LoRa MAC layer cryptographic functionality implementation
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013-2017 Semtech
+ *
+ * ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ * embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Gregory Cristian ( Semtech )
+ *
+ * \author Daniel Jaeckle ( STACKFORCE )
+ *
+ * \author Johannes Bruder ( STACKFORCE )
+ *
+ * addtogroup LORAMAC
+ * \{
+ *
+ */
+#ifndef __LORAMAC_CRYPTO_H__
+#define __LORAMAC_CRYPTO_H__
+
+#include
+#include
+#include
+#include "utilities.h"
+#include "LoRaMacTypes.h"
+#include "LoRaMacMessageTypes.h"
+
+/*
+ * Initial value of the frame counters
+ */
+#define FCNT_DOWN_INITAL_VALUE 0xFFFFFFFF
+
+/*!
+ * LoRaMac Cryto Status
+ */
+typedef enum eLoRaMacCryptoStatus
+{
+ /*!
+ * No error occurred
+ */
+ LORAMAC_CRYPTO_SUCCESS = 0,
+ /*!
+ * MIC does not match
+ */
+ LORAMAC_CRYPTO_FAIL_MIC,
+ /*!
+ * Address does not match
+ */
+ LORAMAC_CRYPTO_FAIL_ADDRESS,
+ /*!
+ * JoinNonce was not greater than previous one.
+ */
+ LORAMAC_CRYPTO_FAIL_JOIN_NONCE,
+ /*!
+ * RJcount0 reached 2^16-1
+ */
+ LORAMAC_CRYPTO_FAIL_RJCOUNT0_OVERFLOW,
+ /*!
+ * FCNT_ID is not supported
+ */
+ LORAMAC_CRYPTO_FAIL_FCNT_ID,
+ /*!
+ * FCntUp/Down check failed (new FCnt is smaller than previous one)
+ */
+ LORAMAC_CRYPTO_FAIL_FCNT_SMALLER,
+ /*!
+ * FCntUp/Down check failed (duplicated)
+ */
+ LORAMAC_CRYPTO_FAIL_FCNT_DUPLICATED,
+ /*!
+ * MAX_GAP_FCNT check failed
+ */
+ LORAMAC_CRYPTO_FAIL_MAX_GAP_FCNT,
+ /*!
+ * Not allowed parameter value
+ */
+ LORAMAC_CRYPTO_FAIL_PARAM,
+ /*!
+ * Null pointer exception
+ */
+ LORAMAC_CRYPTO_ERROR_NPE,
+ /*!
+ * Invalid key identifier exception
+ */
+ LORAMAC_CRYPTO_ERROR_INVALID_KEY_ID,
+ /*!
+ * Invalid address identifier exception
+ */
+ LORAMAC_CRYPTO_ERROR_INVALID_ADDR_ID,
+ /*!
+ * Invalid LoRaWAN specification version
+ */
+ LORAMAC_CRYPTO_ERROR_INVALID_VERSION,
+ /*!
+ * Incompatible buffer size
+ */
+ LORAMAC_CRYPTO_ERROR_BUF_SIZE,
+ /*!
+ * The secure element reports an error
+ */
+ LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC,
+ /*!
+ * Error from parser reported
+ */
+ LORAMAC_CRYPTO_ERROR_PARSER,
+ /*!
+ * Error from serializer reported
+ */
+ LORAMAC_CRYPTO_ERROR_SERIALIZER,
+ /*!
+ * RJcount1 reached 2^16-1 which should never happen
+ */
+ LORAMAC_CRYPTO_ERROR_RJCOUNT1_OVERFLOW,
+ /*!
+ * Undefined Error occurred
+ */
+ LORAMAC_CRYPTO_ERROR,
+}LoRaMacCryptoStatus_t;
+
+/*!
+ * Signature of callback function to be called by the LoRaMac Crypto module when the
+ * non-volatile context have to be stored. It is also possible to save the entire
+ * crypto module context.
+ *
+ */
+typedef void ( *LoRaMacCryptoNvmEvent )( void );
+
+/*!
+ * Initialization of LoRaMac Crypto module
+ * It sets initial values of volatile variables and assigns the non-volatile context.
+ *
+ * \param[IN] cryptoNvmCtxChanged - Callback function which will be called when the
+ * non-volatile context have to be stored.
+ * \retval - Status of the operation
+ */
+LoRaMacCryptoStatus_t LoRaMacCryptoInit( LoRaMacCryptoNvmEvent cryptoNvmCtxChanged );
+
+/*!
+ * Sets the LoRaWAN specification version to be used.
+ *
+ * \warning This function should be used for ABP only. In case of OTA the version will be set automatically.
+ *
+ * \param[IN] version - LoRaWAN specification version to be used.
+ *
+ * \retval - Status of the operation
+ */
+LoRaMacCryptoStatus_t LoRaMacCryptoSetLrWanVersion( Version_t version );
+
+/*!
+ * Restores the internal nvm context from passed pointer.
+ *
+ * \param[IN] cryptoNmvCtx - Pointer to non-volatile crypto module context to be restored.
+ * \retval - Status of the operation
+ */
+LoRaMacCryptoStatus_t LoRaMacCryptoRestoreNvmCtx( void* cryptoNvmCtx );
+
+/*!
+ * Returns a pointer to the internal non-volatile context.
+ *
+ * \param[IN] cryptoNvmCtxSize - Size of the module non-volatile context
+ * \retval - Points to a structure where the module store its non-volatile context
+ */
+void* LoRaMacCryptoGetNvmCtx( size_t* cryptoNvmCtxSize );
+
+/*!
+ * Returns updated fCntID downlink counter value.
+ *
+ * \param[IN] fCntID - Frame counter identifier
+ * \param[IN] maxFcntGap - Maximum allowed frame counter difference (only necessary for L2 LW1.0.x)
+ * \param[IN] frameFcnt - Received frame counter (used to update current counter value)
+ * \param[OUT] currentDown - Current downlink counter value
+ * \retval - Status of the operation
+ */
+LoRaMacCryptoStatus_t LoRaMacCryptoGetFCntDown( FCntIdentifier_t fCntID, uint16_t maxFCntGap, uint32_t frameFcnt, uint32_t* currentDown );
+
+/*!
+ * Returns updated fCntUp uplink counter value.
+ *
+ * \param[IN] currentUp - Uplink counter value
+ * \retval - Status of the operation
+ */
+LoRaMacCryptoStatus_t LoRaMacCryptoGetFCntUp( uint32_t* currentUp );
+
+/*!
+ * Provides multicast context.
+ *
+ * \param[IN] multicastList - Pointer to the multicast context list
+ *
+ * \retval - Status of the operation
+ */
+LoRaMacCryptoStatus_t LoRaMacCryptoSetMulticastReference( MulticastCtx_t* multicastList );
+
+/*!
+ * Sets a key
+ *
+ * \param[IN] keyID - Key identifier
+ * \param[IN] key - Key value (16 byte), if its a multicast key it must be encrypted with McKEKey
+ * \retval - Status of the operation
+ */
+LoRaMacCryptoStatus_t LoRaMacCryptoSetKey( KeyIdentifier_t keyID, uint8_t* key );
+
+/*!
+ * Prepares the join-request message.
+ * It computes the mic and add it to the message.
+ *
+ * \param[IN/OUT] macMsg - Join-request message object
+ * \retval - Status of the operation
+ */
+LoRaMacCryptoStatus_t LoRaMacCryptoPrepareJoinRequest( LoRaMacMessageJoinRequest_t* macMsg );
+
+/*!
+ * Prepares a rejoin-request type 1 message.
+ * It computes the mic and add it to the message.
+ *
+ * \param[IN/OUT] macMsg - Rejoin message object
+ * \retval - Status of the operation
+ */
+LoRaMacCryptoStatus_t LoRaMacCryptoPrepareReJoinType1( LoRaMacMessageReJoinType1_t* macMsg );
+
+/*!
+ * Prepares a rejoin-request type 0 or 2 message.
+ * It computes the mic and add it to the message.
+ *
+ * \param[IN/OUT] macMsg - Rejoin message object
+ * \retval - Status of the operation
+ */
+LoRaMacCryptoStatus_t LoRaMacCryptoPrepareReJoinType0or2( LoRaMacMessageReJoinType0or2_t* macMsg );
+
+/*!
+ * Handles the join-accept message.
+ * It decrypts the message, verifies the MIC and if successful derives the session keys.
+ *
+ * \param[IN] joinReqType - Type of last join-request or rejoin which triggered the join-accept response
+ * \param[IN] joinEUI - Join server EUI (8 byte)
+ * \param[IN/OUT] macMsg - Join-accept message object
+ * \retval - Status of the operation
+ */
+LoRaMacCryptoStatus_t LoRaMacCryptoHandleJoinAccept( JoinReqIdentifier_t joinReqType, uint8_t* joinEUI, LoRaMacMessageJoinAccept_t* macMsg );
+
+/*!
+ * Secures a message (encryption + integrity).
+ *
+ * \param[IN] fCntUp - Uplink sequence counter
+ * \param[IN] txDr - Data rate used for the transmission
+ * \param[IN] txCh - Index of the channel used for the transmission
+ * \param[IN/OUT] macMsg - Data message object
+ * \retval - Status of the operation
+ */
+LoRaMacCryptoStatus_t LoRaMacCryptoSecureMessage( uint32_t fCntUp, uint8_t txDr, uint8_t txCh, LoRaMacMessageData_t* macMsg );
+
+/*!
+ * Unsecures a message (decryption + integrity verification).
+ *
+ * \param[IN] addrID - Address identifier
+ * \param[IN] address - Address
+ * \param[IN] fCntID - Frame counter identifier
+ * \param[IN] fCntDown - Downlink sequence counter
+ * \param[IN/OUT] macMsg - Data message object
+ * \retval - Status of the operation
+ */
+LoRaMacCryptoStatus_t LoRaMacCryptoUnsecureMessage( AddressIdentifier_t addrID, uint32_t address, FCntIdentifier_t fCntID, uint32_t fCntDown, LoRaMacMessageData_t* macMsg );
+
+/*!
+ * Derives the McRootKey from the GenAppKey or AppKey.
+ *
+ * 1.0.x
+ * McRootKey = aes128_encrypt(GenAppKey , 0x00 | pad16)
+ *
+ * 1.1.x
+ * McRootKey = aes128_encrypt(AppKey , 0x20 | pad16)
+ *
+ * \param[IN] keyID - Key identifier of the root key to use to perform the derivation ( GenAppKey or AppKey )
+ * \retval - Status of the operation
+ */
+LoRaMacCryptoStatus_t LoRaMacCryptoDeriveMcRootKey( KeyIdentifier_t keyID );
+
+/*!
+ * Derives the McKEKey from the McRootKey.
+ *
+ * McKEKey = aes128_encrypt(McRootKey , 0x00 | pad16)
+ *
+ * \param[IN] keyID - Key identifier of the root key to use to perform the derivation ( McRootKey )
+ * \retval - Status of the operation
+ */
+LoRaMacCryptoStatus_t LoRaMacCryptoDeriveMcKEKey( KeyIdentifier_t keyID );
+
+/*!
+ * Derives a Multicast group key pair ( McAppSKey, McNwkSKey ) from McKey
+ *
+ * McAppSKey = aes128_encrypt(McKey, 0x01 | McAddr | pad16)
+ * McNwkSKey = aes128_encrypt(McKey, 0x02 | McAddr | pad16)
+ *
+ * \param[IN] addrID - Address identifier to select the multicast group
+ * \param[IN] mcAddr - Multicast group address (4 bytes)
+ * \retval - Status of the operation
+ */
+LoRaMacCryptoStatus_t LoRaMacCryptoDeriveMcSessionKeyPair( AddressIdentifier_t addrID, uint32_t mcAddr );
+
+/*! \} addtogroup LORAMAC */
+
+#endif // __LORAMAC_CRYPTO_H__
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacFCntHandler.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacFCntHandler.h
new file mode 100644
index 00000000..df766251
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacFCntHandler.h
@@ -0,0 +1,170 @@
+/*!
+ * \file LoRaMacFCntHandler.h
+ *
+ * \brief LoRa MAC layer frame counter handling
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013 Semtech
+ *
+ * ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ * embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Daniel Jaeckle ( STACKFORCE )
+ *
+ * \author Johannes Bruder ( STACKFORCE )
+ *
+ * addtogroup LORAMAC
+ * \{
+ *
+ */
+#ifndef __LORAMAC_FCNT_HANDLER_H__
+#define __LORAMAC_FCNT_HANDLER_H__
+
+#include
+#include "LoRaMacTypes.h"
+#include "LoRaMacMessageTypes.h"
+
+/*!
+ * LoRaMac FCnt Handler Status
+ */
+typedef enum eLoRaMacFCntHandlerStatus
+{
+ /*!
+ * No error occurred
+ */
+ LORAMAC_FCNT_HANDLER_SUCCESS = 0,
+ /*!
+ * FCntUp/Down check failed
+ */
+ LORAMAC_FCNT_HANDLER_CHECK_FAIL,
+ /*!
+ * The status idicates that the node has lost MAX_FCNT_GAP or more frames
+ */
+ LORAMAC_FCNT_HANDLER_MAX_GAP_FAIL,
+ /*!
+ * Invalid frame counter identifier exception
+ */
+ LORAMAC_FCNT_HANDLER_ERROR_INVALID_FCNT_ID,
+ /*!
+ * Null pointer exception
+ */
+ LORAMAC_FCNT_HANDLER_ERROR_NPE,
+ /*!
+ * Undefined Error occurred
+ */
+ LORAMAC_FCNT_HANDLER_ERROR,
+}LoRaMacFCntHandlerStatus_t;
+
+/*!
+ * Signature of callback function to be called by this module when the
+ * non-volatile needs to be saved.
+ */
+typedef void ( *EventNvmCtxChanged )( void );
+
+/*!
+ * \brief Initialization of LoRaMac FCnt Handler module
+ *
+ * \param[IN] fCntHandlerNvmCtxChanged - Callback function which will be called when the
+ * non-volatile context needs to be saved.
+ *
+ * \retval - Status of the operation
+ */
+LoRaMacFCntHandlerStatus_t LoRaMacFCntHandlerInit( EventNvmCtxChanged fCntHandlerNvmCtxChanged );
+
+/*!
+ * Restores the internal non-volatile context from passed pointer.
+ *
+ * \param[IN] fCntHandlerNvmCtx - Pointer to non-volatile FCnt handler module context to be restored.
+ *
+ * \retval - Status of the operation
+ */
+LoRaMacFCntHandlerStatus_t LoRaMacFCntHandlerRestoreNvmCtx( void* fCntHandlerNvmCtx );
+
+/*!
+ * Returns a pointer to the internal non-volatile context.
+ *
+ * \param[IN] fCntHandlerNvmCtxSize - Size of the module non-volatile context
+ *
+ * \retval - Points to a structure where the module store its non-volatile context
+ */
+void* LoRaMacFCntHandlerGetNvmCtx( size_t* fCntHandlerNvmCtxSize );
+
+/*!
+ * Computes next 32 bit downlink counter value and determines the frame counter ID.
+ *
+ * \param[IN] addrID - Address identifier
+ * \param[IN] fType - Frame type
+ * \param[IN] macMsg - Data message object, holding the current 16 bit transmitted frame counter
+ * \param[IN] lrWanVersion - LoRaWAN version
+ * \param[IN] maxFCntGap - Maximum allowed frame counter difference (only for 1.0.X necessary)
+ * \param[OUT] fCntID - Frame counter identifier
+ * \param[OUT] currentDown - Current downlink counter value
+ *
+ * \retval - Status of the operation
+ */
+LoRaMacFCntHandlerStatus_t LoRaMacGetFCntDown( AddressIdentifier_t addrID, FType_t fType, LoRaMacMessageData_t* macMsg, Version_t lrWanVersion,
+ uint16_t maxFCntGap, FCntIdentifier_t* fCntID, uint32_t* currentDown );
+
+/*!
+ * Sets the downlink counter value according to the counter ID.
+ *
+ * \param[IN] fCntID - Frame counter identifier
+ * \param[IN] currentDown - Current downlink counter value
+ *
+ * \retval - Status of the operation
+ */
+LoRaMacFCntHandlerStatus_t LoRaMacSetFCntDown( FCntIdentifier_t fCntID, uint32_t currentDown );
+
+/*!
+ * Computes next uplink counter value.
+ *
+ * \param[OUT] currentUp - Current uplink counter value
+ *
+ * \retval - Status of the operation
+ */
+LoRaMacFCntHandlerStatus_t LoRaMacGetFCntUp( uint32_t* currentUp );
+
+/*!
+ * Sets the uplink counter value according to the counter ID.
+ *
+ * \param[IN] currentUp - Current uplink counter value
+ *
+ * \retval - Status of the operation
+ */
+LoRaMacFCntHandlerStatus_t LoRaMacSetFCntUp( uint32_t currentUp );
+
+/*!
+ * Resets the value of FCntUp, NFCntDown, AFCntDown and FCntDown to zero
+ *
+ * \retval - Status of the operation
+ */
+LoRaMacFCntHandlerStatus_t LoRaMacResetFCnts( void );
+
+/*!
+ * Sets the reference to the multicast downlink counter
+ *
+ * \param[IN] multicastList Pointer to the multicast list
+ *
+ * \retval - Status of the operation
+ */
+LoRaMacFCntHandlerStatus_t LoRaMacFCntHandlerSetMulticastReference( MulticastCtx_t* multicastList );
+
+/*! \} addtogroup LORAMAC */
+
+#endif // __LORAMAC_FCNT_HANDLER_H__
+
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacHeaderTypes.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacHeaderTypes.h
new file mode 100644
index 00000000..61c43c06
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacHeaderTypes.h
@@ -0,0 +1,186 @@
+/*!
+ * \file LoRaMacHeaderTypes.h
+ *
+ * \brief LoRa MAC layer header type definitions
+ *
+ * \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 )
+ *
+ * \author Johannes Bruder ( STACKFORCE )
+ *
+ * addtogroup LORAMAC
+ * \{
+ *
+ */
+#ifndef __LORAMAC_HEADER_TYPES_H__
+#define __LORAMAC_HEADER_TYPES_H__
+
+#include
+
+/*! Frame header (FHDR) maximum field size */
+#define LORAMAC_FHDR_MAX_FIELD_SIZE 22
+
+/*! FHDR Device address field size */
+#define LORAMAC_FHDR_DEV_ADD_FIELD_SIZE 4
+
+/*! FHDR Frame control field size */
+#define LORAMAC_FHDR_F_CTRL_FIELD_SIZE 1
+
+/*! FHDR Frame control field size */
+#define LORAMAC_FHDR_F_CNT_FIELD_SIZE 2
+
+/*! FOpts maximum field size */
+#define LORAMAC_FHDR_F_OPTS_MAX_FIELD_SIZE 15
+
+
+/*!
+ * LoRaMAC field definition of DLSettings
+ *
+ * LoRaWAN Specification V1.0.2, chapter 5.4
+ */
+typedef union uLoRaMacDLSettings
+{
+ /*!
+ * Byte-access to the bits
+ */
+ uint8_t Value;
+ /*!
+ * Structure containing single access to header bits
+ */
+ struct sDLSettingsBits
+ {
+ /*!
+ * Data rate of a downlink using the second receive window
+ */
+ uint8_t RX2DataRate : 4;
+ /*!
+ * Offset between up and downlink datarate of first reception slot
+ */
+ uint8_t RX1DRoffset : 3;
+ /*!
+ * Indicates network server LoRaWAN implementation version 1.1 or later.
+ */
+ uint8_t OptNeg : 1;
+ }Bits;
+}LoRaMacDLSettings_t;
+
+/*!
+ * LoRaMAC header field definition (MHDR field)
+ *
+ * LoRaWAN Specification V1.0.2, chapter 4.2
+ */
+typedef union uLoRaMacHeader
+{
+ /*!
+ * Byte-access to the bits
+ */
+ uint8_t Value;
+ /*!
+ * Structure containing single access to header bits
+ */
+ struct sMacHeaderBits
+ {
+ /*!
+ * Major version
+ */
+ uint8_t Major : 2;
+ /*!
+ * RFU
+ */
+ uint8_t RFU : 3;
+ /*!
+ * Message type
+ */
+ uint8_t MType : 3;
+ }Bits;
+}LoRaMacHeader_t;
+
+/*!
+ * LoRaMAC frame control field definition (FCtrl)
+ *
+ * LoRaWAN Specification V1.0.2, chapter 4.3.1
+ */
+typedef union uLoRaMacFrameCtrl
+{
+ /*!
+ * Byte-access to the bits
+ */
+ uint8_t Value;
+ /*!
+ * Structure containing single access to bits
+ */
+ struct sCtrlBits
+ {
+ /*!
+ * Frame options length
+ */
+ uint8_t FOptsLen : 4;
+ /*!
+ * Frame pending bit
+ */
+ uint8_t FPending : 1;
+ /*!
+ * Message acknowledge bit
+ */
+ uint8_t Ack : 1;
+ /*!
+ * ADR acknowledgment request bit
+ */
+ uint8_t AdrAckReq : 1;
+ /*!
+ * ADR control in frame header
+ */
+ uint8_t Adr : 1;
+ }Bits;
+}LoRaMacFrameCtrl_t;
+
+/*!
+ * LoRaMac Frame header (FHDR)
+ *
+ * LoRaWAN Specification V1.0.2, chapter 4.3.1
+ */
+typedef struct sLoRaMacFrameHeader
+{
+ /*!
+ * Device address
+ */
+ uint32_t DevAddr;
+ /*!
+ * Frame control field
+ */
+ LoRaMacFrameCtrl_t FCtrl;
+ /*!
+ * Frame counter
+ */
+ uint16_t FCnt;
+ /*!
+ * FOpts field may transport MAC commands (opt. 0-15 Bytes)
+ */
+ uint8_t FOpts[LORAMAC_FHDR_F_OPTS_MAX_FIELD_SIZE];
+}LoRaMacFrameHeader_t;
+
+/*! \} addtogroup LORAMAC */
+
+#endif // __LORAMAC_HEADER_TYPES_H__
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacMessageTypes.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacMessageTypes.h
new file mode 100644
index 00000000..de53f52a
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacMessageTypes.h
@@ -0,0 +1,327 @@
+/*!
+ * \file LoRaMacMessageTypes.h
+ *
+ * \brief LoRa MAC layer message type definitions
+ *
+ * \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 )
+ *
+ * \author Johannes Bruder ( STACKFORCE )
+ *
+ * addtogroup LORAMAC
+ * \{
+ *
+ */
+#ifndef __LORAMAC_MESSAGE_TYPES_H__
+#define __LORAMAC_MESSAGE_TYPES_H__
+
+#include
+#include "LoRaMacHeaderTypes.h"
+
+
+/*! MAC header field size */
+#define LORAMAC_MHDR_FIELD_SIZE 1
+
+/*! Join EUI field size */
+#define LORAMAC_JOIN_EUI_FIELD_SIZE 8
+
+/*! Device EUI field size */
+#define LORAMAC_DEV_EUI_FIELD_SIZE 8
+
+/*! Join-server nonce field size */
+#define LORAMAC_JOIN_NONCE_FIELD_SIZE 3
+
+/*! Network ID field size */
+#define LORAMAC_NET_ID_FIELD_SIZE 3
+
+/*! Port field size */
+#define LORAMAC_F_PORT_FIELD_SIZE 1
+
+/*! CFList field size */
+#define LORAMAC_C_FLIST_FIELD_SIZE 16
+
+/*! MIC field size */
+#define LORAMAC_MIC_FIELD_SIZE 4
+
+/*! Join-request message size */
+#define LORAMAC_JOIN_REQ_MSG_SIZE 23
+
+/*! ReJoin-request type 1 message size */
+#define LORAMAC_RE_JOIN_1_MSG_SIZE 24
+
+/*! ReJoin-request type 0 or 2 message size */
+#define LORAMAC_RE_JOIN_0_2_MSG_SIZE 19
+
+/*!
+ * LoRaMac type for Join-request message
+ */
+typedef struct sLoRaMacMessageJoinRequest
+{
+ /*!
+ * Serialized message buffer
+ */
+ uint8_t* Buffer;
+ /*!
+ * Size of serialized message buffer
+ */
+ uint8_t BufSize;
+ /*!
+ * MAC header
+ */
+ LoRaMacHeader_t MHDR;
+ /*!
+ * Join EUI
+ */
+ uint8_t JoinEUI[LORAMAC_JOIN_EUI_FIELD_SIZE];
+ /*!
+ * Device EUI
+ */
+ uint8_t DevEUI[LORAMAC_DEV_EUI_FIELD_SIZE];
+ /*!
+ * Device Nonce
+ */
+ uint16_t DevNonce;
+ /*!
+ * Message integrity code (MIC)
+ */
+ uint32_t MIC;
+}LoRaMacMessageJoinRequest_t;
+
+/*!
+ * LoRaMac type for rejoin-request type 1 message
+ */
+typedef struct sLoRaMacMessageReJoinType1
+{
+ /*!
+ * Serialized message buffer
+ */
+ uint8_t* Buffer;
+ /*!
+ * Size of serialized message buffer
+ */
+ uint8_t BufSize;
+ /*!
+ * MAC header
+ */
+ LoRaMacHeader_t MHDR;
+ /*!
+ * Rejoin-request type ( 1 )
+ */
+ uint8_t ReJoinType;
+ /*!
+ * Join EUI
+ */
+ uint8_t JoinEUI[LORAMAC_JOIN_EUI_FIELD_SIZE];
+ /*!
+ * Device EUI
+ */
+ uint8_t DevEUI[LORAMAC_DEV_EUI_FIELD_SIZE];
+ /*!
+ * ReJoin Type 1 counter
+ */
+ uint16_t RJcount1;
+ /*!
+ * Message integrity code (MIC)
+ */
+ uint32_t MIC;
+}LoRaMacMessageReJoinType1_t;
+
+/*!
+ * LoRaMac type for rejoin-request type 0 or 2 message
+ */
+typedef struct sLoRaMacMessageReJoinType0or2
+{
+ /*!
+ * Serialized message buffer
+ */
+ uint8_t* Buffer;
+ /*!
+ * Size of serialized message buffer
+ */
+ uint8_t BufSize;
+ /*!
+ * MAC header
+ */
+ LoRaMacHeader_t MHDR;
+ /*!
+ * Rejoin-request type ( 0 or 2 )
+ */
+ uint8_t ReJoinType;
+ /*!
+ * Network ID ( 3 bytes )
+ */
+ uint8_t NetID[LORAMAC_NET_ID_FIELD_SIZE];
+ /*!
+ * Device EUI
+ */
+ uint8_t DevEUI[LORAMAC_DEV_EUI_FIELD_SIZE];
+ /*!
+ * ReJoin Type 0 and 2 frame counter
+ */
+ uint16_t RJcount0;
+ /*!
+ * Message integrity code (MIC)
+ */
+ uint32_t MIC;
+}LoRaMacMessageReJoinType0or2_t;
+
+/*!
+ * LoRaMac type for Join-accept message
+ */
+typedef struct sLoRaMacMessageJoinAccept
+{
+ /*!
+ * Serialized message buffer
+ */
+ uint8_t* Buffer;
+ /*!
+ * Size of serialized message buffer
+ */
+ uint8_t BufSize;
+ /*!
+ * MAC header
+ */
+ LoRaMacHeader_t MHDR;
+ /*!
+ * Server Nonce ( 3 bytes )
+ */
+ uint8_t JoinNonce[LORAMAC_JOIN_NONCE_FIELD_SIZE];
+ /*!
+ * Network ID ( 3 bytes )
+ */
+ uint8_t NetID[LORAMAC_NET_ID_FIELD_SIZE];
+ /*!
+ * Device address
+ */
+ uint32_t DevAddr;
+ /*!
+ * Device address
+ */
+ LoRaMacDLSettings_t DLSettings;
+ /*!
+ * Delay between TX and RX
+ */
+ uint8_t RxDelay;
+ /*!
+ * List of channel frequencies (opt.)
+ */
+ uint8_t CFList[16];
+ /*!
+ * Message integrity code (MIC)
+ */
+ uint32_t MIC;
+}LoRaMacMessageJoinAccept_t;
+
+
+/*!
+ * LoRaMac type for Data MAC messages
+ * (Unconfirmed Data Up, Confirmed Data Up, Unconfirmed Data Down, Confirmed Data Down)
+ */
+typedef struct sLoRaMacMessageData
+{
+ /*!
+ * Serialized message buffer
+ */
+ uint8_t* Buffer;
+ /*!
+ * Size of serialized message buffer
+ */
+ uint8_t BufSize;
+ /*!
+ * MAC header
+ */
+ LoRaMacHeader_t MHDR;
+ /*!
+ * Frame header (FHDR)
+ */
+ LoRaMacFrameHeader_t FHDR;
+ /*!
+ * Port field (opt.)
+ */
+ uint8_t FPort;
+ /*!
+ * Frame payload may contain MAC commands or data (opt.)
+ */
+ uint8_t* FRMPayload;
+ /*!
+ * Size of frame payload (not included in LoRaMac messages)
+ */
+ uint8_t FRMPayloadSize;
+ /*!
+ * Message integrity code (MIC)
+ */
+ uint32_t MIC;
+}LoRaMacMessageData_t;
+
+/*!
+ * LoRaMac message type enumerator
+ */
+typedef enum eLoRaMacMessageType
+{
+ /*!
+ * Join-request message
+ */
+ LORAMAC_MSG_TYPE_JOIN_REQUEST,
+ /*!
+ * Rejoin-request type 1 message
+ */
+ LORAMAC_MSG_TYPE_RE_JOIN_1,
+ /*!
+ * Rejoin-request type 1 message
+ */
+ LORAMAC_MSG_TYPE_RE_JOIN_0_2,
+ /*!
+ * Join-accept message
+ */
+ LORAMAC_MSG_TYPE_JOIN_ACCEPT,
+ /*!
+ * Data MAC messages
+ */
+ LORAMAC_MSG_TYPE_DATA,
+ /*!
+ * Undefined message type
+ */
+ LORAMAC_MSG_TYPE_UNDEF,
+}LoRaMacMessageType_t;
+
+/*!
+ * LoRaMac general message type
+ */
+typedef struct sLoRaMacMessage
+{
+ LoRaMacMessageType_t Type;
+ union uMessage
+ {
+ LoRaMacMessageJoinRequest_t JoinReq;
+ LoRaMacMessageReJoinType1_t ReJoin1;
+ LoRaMacMessageReJoinType0or2_t ReJoin0or2;
+ LoRaMacMessageJoinAccept_t JoinAccept;
+ LoRaMacMessageData_t Data;
+ }Message;
+}LoRaMacMessage_t;
+
+/*! \} addtogroup LORAMAC */
+
+#endif // __LORAMAC_MESSAGE_TYPES_H__
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacParser.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacParser.c
new file mode 100644
index 00000000..6f29d06d
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacParser.c
@@ -0,0 +1,118 @@
+/*
+ / _____) _ | |
+( (____ _____ ____ _| |_ _____ ____| |__
+ \____ \| ___ | (_ _) ___ |/ ___) _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+ (C)2013 Semtech
+ ___ _____ _ ___ _ _____ ___ ___ ___ ___
+/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+embedded.connectivity.solutions===============
+
+Description: LoRa MAC layer message parser functionality implementation
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+
+Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ),
+ Daniel Jaeckle ( STACKFORCE ), Johannes Bruder ( STACKFORCE )
+*/
+#include "LoRaMacParser.h"
+#include "utilities.h"
+
+LoRaMacParserStatus_t LoRaMacParserJoinAccept( LoRaMacMessageJoinAccept_t* macMsg )
+{
+ if( ( macMsg == 0 ) || ( macMsg->Buffer == 0 ) )
+ {
+ return LORAMAC_PARSER_ERROR_NPE;
+ }
+
+ uint16_t bufItr = 0;
+
+ macMsg->MHDR.Value = macMsg->Buffer[bufItr++];
+
+ memcpy1( macMsg->JoinNonce, &macMsg->Buffer[bufItr], 3 );
+ bufItr = bufItr + 3;
+
+ memcpy1( macMsg->NetID, &macMsg->Buffer[bufItr], 3 );
+ bufItr = bufItr + 3;
+
+ macMsg->DevAddr = ( uint32_t ) macMsg->Buffer[bufItr++];
+ macMsg->DevAddr |= ( ( uint32_t ) macMsg->Buffer[bufItr++] << 8 );
+ macMsg->DevAddr |= ( ( uint32_t ) macMsg->Buffer[bufItr++] << 16 );
+ macMsg->DevAddr |= ( ( uint32_t ) macMsg->Buffer[bufItr++] << 24 );
+
+ macMsg->DLSettings.Value = macMsg->Buffer[bufItr++];
+
+ macMsg->RxDelay = macMsg->Buffer[bufItr++];
+
+ if( ( macMsg->BufSize - LORAMAC_MIC_FIELD_SIZE - bufItr ) == LORAMAC_C_FLIST_FIELD_SIZE )
+ {
+ memcpy1( macMsg->CFList, &macMsg->Buffer[bufItr], LORAMAC_C_FLIST_FIELD_SIZE );
+ bufItr = bufItr + LORAMAC_C_FLIST_FIELD_SIZE;
+ }
+ else if( ( macMsg->BufSize - LORAMAC_MIC_FIELD_SIZE - bufItr ) > 0 )
+ {
+ return LORAMAC_PARSER_FAIL;
+ }
+
+ macMsg->MIC = ( uint32_t ) macMsg->Buffer[bufItr++];
+ macMsg->MIC |= ( ( uint32_t ) macMsg->Buffer[bufItr++] << 8 );
+ macMsg->MIC |= ( ( uint32_t ) macMsg->Buffer[bufItr++] << 16 );
+ macMsg->MIC |= ( ( uint32_t ) macMsg->Buffer[bufItr++] << 24 );
+
+ return LORAMAC_PARSER_SUCCESS;
+}
+
+LoRaMacParserStatus_t LoRaMacParserData( LoRaMacMessageData_t* macMsg )
+{
+ if( ( macMsg == 0 ) || ( macMsg->Buffer == 0 ) )
+ {
+ return LORAMAC_PARSER_ERROR_NPE;
+ }
+
+ uint16_t bufItr = 0;
+
+ macMsg->MHDR.Value = macMsg->Buffer[bufItr++];
+
+ macMsg->FHDR.DevAddr = macMsg->Buffer[bufItr++];
+ macMsg->FHDR.DevAddr |= ( ( uint32_t ) macMsg->Buffer[bufItr++] << 8 );
+ macMsg->FHDR.DevAddr |= ( ( uint32_t ) macMsg->Buffer[bufItr++] << 16 );
+ macMsg->FHDR.DevAddr |= ( ( uint32_t ) macMsg->Buffer[bufItr++] << 24 );
+
+ macMsg->FHDR.FCtrl.Value = macMsg->Buffer[bufItr++];
+
+ macMsg->FHDR.FCnt = macMsg->Buffer[bufItr++];
+ macMsg->FHDR.FCnt |= macMsg->Buffer[bufItr++] << 8;
+
+ if( macMsg->FHDR.FCtrl.Bits.FOptsLen <= 15 )
+ {
+ memcpy1( macMsg->FHDR.FOpts, &macMsg->Buffer[bufItr], macMsg->FHDR.FCtrl.Bits.FOptsLen );
+ bufItr = bufItr + macMsg->FHDR.FCtrl.Bits.FOptsLen;
+ }
+ else
+ {
+ return LORAMAC_PARSER_FAIL;
+ }
+
+ // Initialize anyway with zero.
+ macMsg->FPort = 0;
+ macMsg->FRMPayloadSize = 0;
+
+ if( ( macMsg->BufSize - bufItr - LORAMAC_MIC_FIELD_SIZE ) > 0 )
+ {
+ macMsg->FPort = macMsg->Buffer[bufItr++];
+
+ macMsg->FRMPayloadSize = ( macMsg->BufSize - bufItr - LORAMAC_MIC_FIELD_SIZE );
+ memcpy1( macMsg->FRMPayload, &macMsg->Buffer[bufItr], macMsg->FRMPayloadSize );
+ bufItr = bufItr + macMsg->FRMPayloadSize;
+ }
+
+ macMsg->MIC = ( uint32_t ) macMsg->Buffer[( macMsg->BufSize - LORAMAC_MIC_FIELD_SIZE )];
+ macMsg->MIC |= ( ( uint32_t ) macMsg->Buffer[( macMsg->BufSize - LORAMAC_MIC_FIELD_SIZE ) + 1] << 8 );
+ macMsg->MIC |= ( ( uint32_t ) macMsg->Buffer[( macMsg->BufSize - LORAMAC_MIC_FIELD_SIZE ) + 2] << 16 );
+ macMsg->MIC |= ( ( uint32_t ) macMsg->Buffer[( macMsg->BufSize - LORAMAC_MIC_FIELD_SIZE ) + 3] << 24 );
+
+ return LORAMAC_PARSER_SUCCESS;
+}
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacParser.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacParser.h
new file mode 100644
index 00000000..3446ae19
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacParser.h
@@ -0,0 +1,86 @@
+/*!
+ * \file LoRaMacParser.h
+ *
+ * \brief LoRa MAC layer message parser functionality 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 )
+ *
+ * \author Johannes Bruder ( STACKFORCE )
+ *
+ * addtogroup LORAMAC
+ * \{
+ *
+ */
+#ifndef __LORAMAC_PARSER_H__
+#define __LORAMAC_PARSER_H__
+
+#include
+#include "LoRaMacMessageTypes.h"
+
+/*!
+ * LoRaMac Parser Status
+ */
+typedef enum eLoRaMacParserStatus
+{
+ /*!
+ * No error occurred
+ */
+ LORAMAC_PARSER_SUCCESS = 0,
+ /*!
+ * Failure during parsing occurred
+ */
+ LORAMAC_PARSER_FAIL,
+ /*!
+ * Null pointer exception
+ */
+ LORAMAC_PARSER_ERROR_NPE,
+ /*!
+ * Undefined Error occurred
+ */
+ LORAMAC_PARSER_ERROR,
+}LoRaMacParserStatus_t;
+
+
+/*!
+ * Parse a serialized join-accept message and fills the structured object.
+ *
+ * \param[IN/OUT] macMsg - Join-accept message object
+ * \retval - Status of the operation
+ */
+LoRaMacParserStatus_t LoRaMacParserJoinAccept( LoRaMacMessageJoinAccept_t *macMsg );
+
+/*!
+ * Parse a serialized data message and fills the structured object.
+ *
+ * \param[IN/OUT] macMsg - Data message object
+ * \retval - Status of the operation
+ */
+LoRaMacParserStatus_t LoRaMacParserData( LoRaMacMessageData_t *macMsg );
+
+/*! \} addtogroup LORAMAC */
+
+#endif // __LORAMAC_PARSER_H__
+
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacSerializer.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacSerializer.c
new file mode 100644
index 00000000..261cfb2f
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacSerializer.c
@@ -0,0 +1,183 @@
+/*
+ / _____) _ | |
+( (____ _____ ____ _| |_ _____ ____| |__
+ \____ \| ___ | (_ _) ___ |/ ___) _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+ (C)2013 Semtech
+ ___ _____ _ ___ _ _____ ___ ___ ___ ___
+/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+embedded.connectivity.solutions===============
+
+Description: LoRa MAC layer message serializer functionality implementation
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+
+Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ),
+ Daniel Jaeckle ( STACKFORCE ), Johannes Bruder ( STACKFORCE )
+*/
+#include "LoRaMacSerializer.h"
+#include "utilities.h"
+
+LoRaMacSerializerStatus_t LoRaMacSerializerJoinRequest( LoRaMacMessageJoinRequest_t* macMsg )
+{
+ if( ( macMsg == 0 ) || ( macMsg->Buffer == 0 ) )
+ {
+ return LORAMAC_SERIALIZER_ERROR_NPE;
+ }
+
+ uint16_t bufItr = 0;
+
+ // Check macMsg->BufSize
+ if( macMsg->BufSize < LORAMAC_JOIN_REQ_MSG_SIZE )
+ {
+ return LORAMAC_SERIALIZER_ERROR_BUF_SIZE;
+ }
+
+ macMsg->Buffer[bufItr++] = macMsg->MHDR.Value;
+
+ memcpyr( &macMsg->Buffer[bufItr], macMsg->JoinEUI, LORAMAC_JOIN_EUI_FIELD_SIZE );
+ bufItr += LORAMAC_JOIN_EUI_FIELD_SIZE;
+
+ memcpyr( &macMsg->Buffer[bufItr], macMsg->DevEUI, LORAMAC_DEV_EUI_FIELD_SIZE );
+ bufItr += LORAMAC_DEV_EUI_FIELD_SIZE;
+
+ macMsg->Buffer[bufItr++] = macMsg->DevNonce & 0xFF;
+ macMsg->Buffer[bufItr++] = ( macMsg->DevNonce >> 8 ) & 0xFF;
+
+ macMsg->Buffer[bufItr++] = macMsg->MIC & 0xFF;
+ macMsg->Buffer[bufItr++] = ( macMsg->MIC >> 8 ) & 0xFF;
+ macMsg->Buffer[bufItr++] = ( macMsg->MIC >> 16 ) & 0xFF;
+ macMsg->Buffer[bufItr++] = ( macMsg->MIC >> 24 ) & 0xFF;
+
+ macMsg->BufSize = bufItr;
+
+ return LORAMAC_SERIALIZER_SUCCESS;
+}
+
+LoRaMacSerializerStatus_t LoRaMacSerializerReJoinType1( LoRaMacMessageReJoinType1_t* macMsg )
+{
+ if( ( macMsg == 0 ) || ( macMsg->Buffer == 0 ) )
+ {
+ return LORAMAC_SERIALIZER_ERROR_NPE;
+ }
+
+ uint16_t bufItr = 0;
+
+ // Check macMsg->BufSize
+ if( macMsg->BufSize < LORAMAC_RE_JOIN_1_MSG_SIZE )
+ {
+ return LORAMAC_SERIALIZER_ERROR_BUF_SIZE;
+ }
+
+ macMsg->Buffer[bufItr++] = macMsg->MHDR.Value;
+
+ macMsg->Buffer[bufItr++] = macMsg->ReJoinType;
+
+ memcpyr( &macMsg->Buffer[bufItr], macMsg->JoinEUI, LORAMAC_JOIN_EUI_FIELD_SIZE );
+ bufItr += LORAMAC_JOIN_EUI_FIELD_SIZE;
+
+ memcpyr( &macMsg->Buffer[bufItr], macMsg->DevEUI, LORAMAC_DEV_EUI_FIELD_SIZE );
+ bufItr += LORAMAC_DEV_EUI_FIELD_SIZE;
+
+ macMsg->Buffer[bufItr++] = macMsg->RJcount1 & 0xFF;
+ macMsg->Buffer[bufItr++] = ( macMsg->RJcount1 >> 8 ) & 0xFF;
+
+ return LORAMAC_SERIALIZER_SUCCESS;
+}
+
+LoRaMacSerializerStatus_t LoRaMacSerializerReJoinType0or2( LoRaMacMessageReJoinType0or2_t* macMsg )
+{
+ if( ( macMsg == 0 ) || ( macMsg->Buffer == 0 ) )
+ {
+ return LORAMAC_SERIALIZER_ERROR_NPE;
+ }
+
+ uint16_t bufItr = 0;
+
+ // Check macMsg->BufSize
+ if( macMsg->BufSize < LORAMAC_RE_JOIN_0_2_MSG_SIZE )
+ {
+ return LORAMAC_SERIALIZER_ERROR_BUF_SIZE;
+ }
+
+ macMsg->Buffer[bufItr++] = macMsg->MHDR.Value;
+
+ macMsg->Buffer[bufItr++] = macMsg->ReJoinType;
+
+ memcpy1( &macMsg->Buffer[bufItr], macMsg->NetID, LORAMAC_NET_ID_FIELD_SIZE );
+ bufItr += LORAMAC_NET_ID_FIELD_SIZE;
+
+ memcpyr( &macMsg->Buffer[bufItr], macMsg->DevEUI, LORAMAC_DEV_EUI_FIELD_SIZE );
+ bufItr += LORAMAC_DEV_EUI_FIELD_SIZE;
+
+ macMsg->Buffer[bufItr++] = macMsg->RJcount0 & 0xFF;
+ macMsg->Buffer[bufItr++] = ( macMsg->RJcount0 >> 8 ) & 0xFF;
+
+ return LORAMAC_SERIALIZER_SUCCESS;
+}
+
+LoRaMacSerializerStatus_t LoRaMacSerializerData( LoRaMacMessageData_t* macMsg )
+{
+ if( ( macMsg == 0 ) || ( macMsg->Buffer == 0 ) )
+ {
+ return LORAMAC_SERIALIZER_ERROR_NPE;
+ }
+
+ uint16_t bufItr = 0;
+
+ // Check macMsg->BufSize
+ uint16_t computedBufSize = LORAMAC_MHDR_FIELD_SIZE
+ + LORAMAC_FHDR_DEV_ADD_FIELD_SIZE
+ + LORAMAC_FHDR_F_CTRL_FIELD_SIZE
+ + LORAMAC_FHDR_F_CNT_FIELD_SIZE;
+
+ computedBufSize += macMsg->FHDR.FCtrl.Bits.FOptsLen;
+
+ if( macMsg->FRMPayloadSize > 0 )
+ {
+ computedBufSize += LORAMAC_F_PORT_FIELD_SIZE;
+ }
+
+ computedBufSize += macMsg->FRMPayloadSize;
+ computedBufSize += LORAMAC_MIC_FIELD_SIZE;
+
+ if( macMsg->BufSize < computedBufSize )
+ {
+ return LORAMAC_SERIALIZER_ERROR_BUF_SIZE;
+ }
+
+ macMsg->Buffer[bufItr++] = macMsg->MHDR.Value;
+
+ macMsg->Buffer[bufItr++] = ( macMsg->FHDR.DevAddr ) & 0xFF;
+ macMsg->Buffer[bufItr++] = ( macMsg->FHDR.DevAddr >> 8 ) & 0xFF;
+ macMsg->Buffer[bufItr++] = ( macMsg->FHDR.DevAddr >> 16 ) & 0xFF;
+ macMsg->Buffer[bufItr++] = ( macMsg->FHDR.DevAddr >> 24 ) & 0xFF;
+
+ macMsg->Buffer[bufItr++] = macMsg->FHDR.FCtrl.Value;
+
+ macMsg->Buffer[bufItr++] = macMsg->FHDR.FCnt & 0xFF;
+ macMsg->Buffer[bufItr++] = ( macMsg->FHDR.FCnt >> 8 ) & 0xFF;
+
+ memcpy1( &macMsg->Buffer[bufItr], macMsg->FHDR.FOpts, macMsg->FHDR.FCtrl.Bits.FOptsLen );
+ bufItr = bufItr + macMsg->FHDR.FCtrl.Bits.FOptsLen;
+
+ if( macMsg->FRMPayloadSize > 0 )
+ {
+ macMsg->Buffer[bufItr++] = macMsg->FPort;
+ }
+
+ memcpy1( &macMsg->Buffer[bufItr], macMsg->FRMPayload, macMsg->FRMPayloadSize );
+ bufItr = bufItr + macMsg->FRMPayloadSize;
+
+ macMsg->Buffer[bufItr++] = macMsg->MIC & 0xFF;
+ macMsg->Buffer[bufItr++] = ( macMsg->MIC >> 8 ) & 0xFF;
+ macMsg->Buffer[bufItr++] = ( macMsg->MIC >> 16 ) & 0xFF;
+ macMsg->Buffer[bufItr++] = ( macMsg->MIC >> 24 ) & 0xFF;
+
+ macMsg->BufSize = bufItr;
+
+ return LORAMAC_SERIALIZER_SUCCESS;
+}
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacSerializer.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacSerializer.h
new file mode 100644
index 00000000..0236abee
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacSerializer.h
@@ -0,0 +1,101 @@
+/*!
+ * \file LoRaMacSerializer.h
+ *
+ * \brief LoRa MAC layer message serializer functionality 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 )
+ *
+ * \author Johannes Bruder ( STACKFORCE )
+ *
+ * addtogroup LORAMAC
+ * \{
+ *
+ */
+#ifndef __LORAMAC_SERIALIZER_H__
+#define __LORAMAC_SERIALIZER_H__
+#include
+#include "LoRaMacMessageTypes.h"
+
+
+/*!
+ * LoRaMac Serializer Status
+ */
+typedef enum eLoRaMacSerializerStatus
+{
+ /*!
+ * No error occurred
+ */
+ LORAMAC_SERIALIZER_SUCCESS = 0,
+ /*!
+ * Null pointer exception
+ */
+ LORAMAC_SERIALIZER_ERROR_NPE,
+ /*!
+ * Incompatible buffer size
+ */
+ LORAMAC_SERIALIZER_ERROR_BUF_SIZE,
+ /*!
+ * Undefined Error occurred
+ */
+ LORAMAC_SERIALIZER_ERROR,
+}LoRaMacSerializerStatus_t;
+
+/*!
+ * Creates serialized MAC message of structured object.
+ *
+ * \param[IN/OUT] macMsg - Join-request message object
+ * \retval - Status of the operation
+ */
+LoRaMacSerializerStatus_t LoRaMacSerializerJoinRequest( LoRaMacMessageJoinRequest_t* macMsg );
+
+/*!
+ * Creates serialized MAC message of structured object.
+ *
+ * \param[IN/OUT] macMsg - Join-request message object
+ * \retval - Status of the operation
+ */
+LoRaMacSerializerStatus_t LoRaMacSerializerReJoinType1( LoRaMacMessageReJoinType1_t* macMsg );
+
+/*!
+ * Creates serialized MAC message of structured object.
+ *
+ * \param[IN/OUT] macMsg - Join-request message object
+ * \retval - Status of the operation
+ */
+LoRaMacSerializerStatus_t LoRaMacSerializerReJoinType0or2( LoRaMacMessageReJoinType0or2_t* macMsg );
+
+/*!
+ * Creates serialized MAC message of structured object.
+ *
+ * \param[IN/OUT] macMsg - Data message object
+ * \retval - Status of the operation
+ */
+LoRaMacSerializerStatus_t LoRaMacSerializerData( LoRaMacMessageData_t* macMsg );
+
+/*! \} addtogroup LORAMAC */
+
+#endif // __LORAMAC_SERIALIZER_H__
+
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacTest.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacTest.h
new file mode 100644
index 00000000..0c348602
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacTest.h
@@ -0,0 +1,51 @@
+/*!
+ * \file LoRaMacTest.h
+ *
+ * \brief LoRa MAC layer test function implementation
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013-2017 Semtech
+ *
+ * ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ * embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Gregory Cristian ( Semtech )
+ *
+ * \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.
+ * The functions in this file are only for testing purposes only.
+ * \{
+ */
+#ifndef __LORAMACTEST_H__
+#define __LORAMACTEST_H__
+
+/*!
+ * \brief Enabled or disables the duty cycle
+ *
+ * \details This is a test function. It shall be used for testing purposes only.
+ * Changing this attribute may lead to a non-conformance LoRaMac operation.
+ *
+ * \param [IN] enable - Enabled or disables the duty cycle
+ */
+void LoRaMacTestSetDutyCycleOn( bool enable );
+
+/*! \} defgroup LORAMACTEST */
+
+#endif // __LORAMACTEST_H__
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacTypes.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacTypes.h
new file mode 100644
index 00000000..291b5cd0
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacTypes.h
@@ -0,0 +1,655 @@
+/*!
+ * \file LoRaMacTypes.h
+ *
+ * \brief LoRa MAC layer internal types definition. Please do not include in application sources.
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013 Semtech
+ *
+ * ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ * embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Daniel Jaeckle ( STACKFORCE )
+ *
+ * \author Johannes Bruder ( STACKFORCE )
+ *
+ * addtogroup LORAMAC
+ * \{
+ *
+ */
+#ifndef __LORAMAC_TYPES_H__
+#define __LORAMAC_TYPES_H__
+
+#include
+#include
+#include "timer.h"
+
+/*!
+ * Start value for unicast keys enumeration
+ */
+#define LORAMAC_CRYPTO_UNICAST_KEYS 0
+
+/*!
+ * Start value for multicast keys enumeration
+ */
+#define LORAMAC_CRYPTO_MULTICAST_KEYS 127
+
+typedef union Version_u
+{
+ struct Version_s
+ {
+ uint8_t Rfu;
+ uint8_t Revision;
+ uint8_t Minor;
+ uint8_t Major;
+ }Fields;
+ uint32_t Value;
+}Version_t;
+
+/*!
+ * LoRaWAN devices classes definition
+ *
+ * LoRaWAN Specification V1.0.2, chapter 2.1
+ */
+typedef enum DeviceClass_e
+{
+ /*!
+ * LoRaWAN device class A
+ *
+ * LoRaWAN Specification V1.0.2, chapter 3
+ */
+ CLASS_A = 0x00,
+ /*!
+ * LoRaWAN device class B
+ *
+ * LoRaWAN Specification V1.0.2, chapter 8
+ */
+ CLASS_B = 0x01,
+ /*!
+ * LoRaWAN device class C
+ *
+ * LoRaWAN Specification V1.0.2, chapter 17
+ */
+ CLASS_C = 0x02,
+}DeviceClass_t;
+
+/*!
+ * LoRaWAN Frame type enumeration to differ between the possible data up/down frame configurations.
+ *
+ * Note: The naming is implementation specific since there is no definition
+ * in the LoRaWAN specification included.
+ */
+typedef enum eFType
+{
+ /*!
+ * Frame type A
+ *
+ * FOptsLen > 0, Fopt present, FPort > 0, FRMPayload present
+ */
+ FRAME_TYPE_A,
+ /*!
+ * Frame type B
+ *
+ * FOptsLen > 0, Fopt present, FPort not present, FRMPayload not present
+ */
+ FRAME_TYPE_B,
+ /*!
+ * Frame type C
+ *
+ * FOptsLen = 0, Fopt not present, FPort = 0 , FRMPayload containing MAC commands
+ */
+ FRAME_TYPE_C,
+ /*!
+ * Frame type D
+ *
+ * FOptsLen = 0, Fopt not present, FPort > 0 , FRMPayload present
+ */
+ FRAME_TYPE_D,
+}FType_t;
+
+/*!
+ * LoRaWAN Frame counter identifier.
+ */
+typedef enum eFCntIdentifier
+{
+ /*!
+ * Uplink frame counter which is incremented with each uplink.
+ */
+ FCNT_UP = 0,
+ /*!
+ * Network downlink frame counter which is incremented with each downlink on FPort 0
+ * or when the FPort field is missing.
+ */
+ N_FCNT_DOWN,
+ /*!
+ * Application downlink frame counter which is incremented with each downlink
+ * on a port different than 0.
+ */
+ A_FCNT_DOWN,
+ /*!
+ * In case if the device is connected to a LoRaWAN 1.0 Server,
+ * this counter is used for every kind of downlink frame.
+ */
+ FCNT_DOWN,
+ /*!
+ * Multicast downlink counter for index 0
+ */
+ MC_FCNT_DOWN_0,
+ /*!
+ * Multicast downlink counter for index 1
+ */
+ MC_FCNT_DOWN_1,
+ /*!
+ * Multicast downlink counter for index 2
+ */
+ MC_FCNT_DOWN_2,
+ /*!
+ * Multicast downlink counter for index 3
+ */
+ MC_FCNT_DOWN_3,
+}FCntIdentifier_t;
+
+/*!
+ * LoRaMac Key identifier
+ */
+typedef enum eKeyIdentifier
+{
+ /*!
+ * Application root key
+ */
+ APP_KEY = 0,
+ /*!
+ * Application root key
+ * Used to derive McRootKey for 1.0.x devices
+ */
+ GEN_APP_KEY,
+ /*!
+ * Network root key
+ */
+ NWK_KEY,
+ /*!
+ * Join session integrity key
+ */
+ J_S_INT_KEY,
+ /*!
+ * Join session encryption key
+ */
+ J_S_ENC_KEY,
+ /*!
+ * Forwarding Network session integrity key
+ */
+ F_NWK_S_INT_KEY,
+ /*!
+ * Serving Network session integrity key
+ */
+ S_NWK_S_INT_KEY,
+ /*!
+ * Network session encryption key
+ */
+ NWK_S_ENC_KEY,
+ /*!
+ * Application session key
+ */
+ APP_S_KEY,
+ /*!
+ * Multicast root key
+ */
+ MC_ROOT_KEY,
+ /*!
+ * Multicast key encryption key
+ */
+ MC_KE_KEY = LORAMAC_CRYPTO_MULTICAST_KEYS,
+ /*!
+ * Multicast root key index 0
+ */
+ MC_KEY_0,
+ /*!
+ * Multicast Application session key index 0
+ */
+ MC_APP_S_KEY_0,
+ /*!
+ * Multicast Network session key index 0
+ */
+ MC_NWK_S_KEY_0,
+ /*!
+ * Multicast root key index 1
+ */
+ MC_KEY_1,
+ /*!
+ * Multicast Application session key index 1
+ */
+ MC_APP_S_KEY_1,
+ /*!
+ * Multicast Network session key index 1
+ */
+ MC_NWK_S_KEY_1,
+ /*!
+ * Multicast root key index 2
+ */
+ MC_KEY_2,
+ /*!
+ * Multicast Application session key index 2
+ */
+ MC_APP_S_KEY_2,
+ /*!
+ * Multicast Network session key index 2
+ */
+ MC_NWK_S_KEY_2,
+ /*!
+ * Multicast root key index 3
+ */
+ MC_KEY_3,
+ /*!
+ * Multicast Application session key index 3
+ */
+ MC_APP_S_KEY_3,
+ /*!
+ * Multicast Network session key index 3
+ */
+ MC_NWK_S_KEY_3,
+ /*!
+ * Zero key for slot randomization in class B
+ */
+ SLOT_RAND_ZERO_KEY,
+ /*!
+ * No Key
+ */
+ NO_KEY,
+}KeyIdentifier_t;
+
+/*!
+ * LoRaMac Crypto address identifier
+ */
+typedef enum eAddressIdentifier
+{
+ /*!
+ * Multicast Address for index 0
+ */
+ MULTICAST_0_ADDR = 0,
+ /*!
+ * Multicast Address for index 1
+ */
+ MULTICAST_1_ADDR = 1,
+ /*!
+ * Multicast Address for index 2
+ */
+ MULTICAST_2_ADDR = 2,
+ /*!
+ * Multicast Address for index 3
+ */
+ MULTICAST_3_ADDR = 3,
+ /*!
+ * Unicast End-device address
+ */
+ UNICAST_DEV_ADDR = 4,
+}AddressIdentifier_t;
+
+/*
+ * Multicast Rx window parameters
+ */
+typedef union uMcRxParams
+{
+ struct
+ {
+ /*!
+ * Reception frequency of the ping slot windows
+ */
+ uint32_t Frequency;
+ /*!
+ * Datarate of the ping slot
+ */
+ int8_t Datarate;
+ /*!
+ * This parameter is necessary for class B operation. It defines the
+ * periodicity of the multicast downlink slots
+ */
+ uint16_t Periodicity;
+ }ClassB;
+ struct
+ {
+ /*!
+ * Reception frequency of the ping slot windows
+ */
+ uint32_t Frequency;
+ /*!
+ * Datarate of the ping slot
+ */
+ int8_t Datarate;
+ }ClassC;
+}McRxParams_t;
+
+/*!
+ * Multicast channel
+ */
+typedef struct sMcChannelParams
+{
+ /*!
+ * Multicats channel LoRaWAN class B or C
+ */
+ DeviceClass_t Class;
+ /*!
+ * True if the entry is active
+ */
+ bool IsEnabled;
+ /*
+ * Address identifier
+ */
+ AddressIdentifier_t GroupID;
+ /*!
+ * Address
+ */
+ uint32_t Address;
+ /*!
+ * Encrypted multicast key
+ */
+ uint8_t *McKeyE;
+ /*!
+ * Minimum multicast frame counter value
+ */
+ uint32_t FCountMin;
+ /*!
+ * Maximum multicast frame counter value
+ */
+ uint32_t FCountMax;
+ /*!
+ * Multicast reception parameters
+ */
+ McRxParams_t RxParams;
+}McChannelParams_t;
+
+/*!
+ * Multicast context
+ */
+typedef struct sMulticastCtx
+{
+ /*!
+ * Multicast channel parameters
+ */
+ McChannelParams_t ChannelParams;
+ /*!
+ * Downlink counter
+ */
+ uint32_t* DownLinkCounter;
+ /*
+ * Following parameters are only used for ClassB multicast channels
+ */
+ /*!
+ * Number of multicast slots. The variable can be
+ * calculated as follows:
+ * PingNb = 128 / ( 1 << periodicity ), where
+ * 0 <= periodicity <= 7
+ */
+ uint8_t PingNb;
+ /*!
+ * Period of the multicast slots. The variable can be
+ * calculated as follows:
+ * PingPeriod = 4096 / PingNb
+ */
+ uint16_t PingPeriod;
+ /*!
+ * Ping offset of the multicast channel for Class B
+ */
+ uint16_t PingOffset;
+}MulticastCtx_t;
+
+/*!
+ * LoRaMac join-request / rejoin type identifier
+ */
+typedef enum eJoinReqIdentifier
+{
+ /*!
+ * Rejoin type 0
+ */
+ REJOIN_REQ_0 = 0x00,
+ /*!
+ * Rejoin type 1
+ */
+ REJOIN_REQ_1 = 0x01,
+ /*!
+ * Rejoin type 2
+ */
+ REJOIN_REQ_2 = 0x02,
+ /*!
+ * Join-request
+ */
+ JOIN_REQ = 0xFF,
+}JoinReqIdentifier_t;
+
+/*!
+ * LoRaMAC mote MAC commands
+ *
+ * LoRaWAN Specification V1.1.0, chapter 5, table 4
+ */
+typedef enum eLoRaMacMoteCmd
+{
+ /*!
+ * LinkCheckReq
+ */
+ MOTE_MAC_LINK_CHECK_REQ = 0x02,
+ /*!
+ * LinkADRAns
+ */
+ MOTE_MAC_LINK_ADR_ANS = 0x03,
+ /*!
+ * DutyCycleAns
+ */
+ MOTE_MAC_DUTY_CYCLE_ANS = 0x04,
+ /*!
+ * RXParamSetupAns
+ */
+ MOTE_MAC_RX_PARAM_SETUP_ANS = 0x05,
+ /*!
+ * DevStatusAns
+ */
+ MOTE_MAC_DEV_STATUS_ANS = 0x06,
+ /*!
+ * NewChannelAns
+ */
+ MOTE_MAC_NEW_CHANNEL_ANS = 0x07,
+ /*!
+ * RXTimingSetupAns
+ */
+ MOTE_MAC_RX_TIMING_SETUP_ANS = 0x08,
+ /*!
+ * TXParamSetupAns
+ */
+ MOTE_MAC_TX_PARAM_SETUP_ANS = 0x09,
+ /*!
+ * DlChannelAns
+ */
+ MOTE_MAC_DL_CHANNEL_ANS = 0x0A,
+ /*!
+ * DeviceTimeReq
+ */
+ MOTE_MAC_DEVICE_TIME_REQ = 0x0D,
+ /*!
+ * 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.1.0 chapter 5, table 4
+ */
+typedef enum eLoRaMacSrvCmd
+{
+ /*!
+ * ResetInd
+ */
+ SRV_MAC_RESET_CONF = 0x01,
+ /*!
+ * LinkCheckAns
+ */
+ SRV_MAC_LINK_CHECK_ANS = 0x02,
+ /*!
+ * LinkADRReq
+ */
+ SRV_MAC_LINK_ADR_REQ = 0x03,
+ /*!
+ * DutyCycleReq
+ */
+ SRV_MAC_DUTY_CYCLE_REQ = 0x04,
+ /*!
+ * RXParamSetupReq
+ */
+ SRV_MAC_RX_PARAM_SETUP_REQ = 0x05,
+ /*!
+ * DevStatusReq
+ */
+ SRV_MAC_DEV_STATUS_REQ = 0x06,
+ /*!
+ * NewChannelReq
+ */
+ SRV_MAC_NEW_CHANNEL_REQ = 0x07,
+ /*!
+ * RXTimingSetupReq
+ */
+ SRV_MAC_RX_TIMING_SETUP_REQ = 0x08,
+ /*!
+ * NewChannelReq
+ */
+ SRV_MAC_TX_PARAM_SETUP_REQ = 0x09,
+ /*!
+ * DlChannelReq
+ */
+ SRV_MAC_DL_CHANNEL_REQ = 0x0A,
+ /*!
+ * DeviceTimeAns
+ */
+ SRV_MAC_DEVICE_TIME_ANS = 0x0D,
+ /*!
+ * 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;
+
+/*!
+ * LoRaMAC band parameters definition
+ */
+typedef struct sBand
+{
+ /*!
+ * Duty cycle
+ */
+ uint16_t DCycle;
+ /*!
+ * Maximum Tx power
+ */
+ int8_t TxMaxPower;
+ /*!
+ * Time stamp of the last JoinReq Tx frame.
+ */
+ TimerTime_t LastJoinTxDoneTime;
+ /*!
+ * Time stamp of the last Tx frame
+ */
+ TimerTime_t LastTxDoneTime;
+ /*!
+ * Holds the time where the device is off
+ */
+ TimerTime_t TimeOff;
+}Band_t;
+
+/*!
+ * LoRaMAC frame types
+ *
+ * LoRaWAN Specification V1.0.2, chapter 4.2.1, table 1
+ */
+typedef enum eLoRaMacFrameType
+{
+ /*!
+ * LoRaMAC join request frame
+ */
+ FRAME_TYPE_JOIN_REQ = 0x00,
+ /*!
+ * LoRaMAC join accept frame
+ */
+ FRAME_TYPE_JOIN_ACCEPT = 0x01,
+ /*!
+ * LoRaMAC unconfirmed up-link frame
+ */
+ FRAME_TYPE_DATA_UNCONFIRMED_UP = 0x02,
+ /*!
+ * LoRaMAC unconfirmed down-link frame
+ */
+ FRAME_TYPE_DATA_UNCONFIRMED_DOWN = 0x03,
+ /*!
+ * LoRaMAC confirmed up-link frame
+ */
+ FRAME_TYPE_DATA_CONFIRMED_UP = 0x04,
+ /*!
+ * LoRaMAC confirmed down-link frame
+ */
+ FRAME_TYPE_DATA_CONFIRMED_DOWN = 0x05,
+ /*!
+ * LoRaMAC proprietary frame
+ */
+ FRAME_TYPE_PROPRIETARY = 0x07,
+}LoRaMacFrameType_t;
+
+/*!
+ * LoRaMAC Battery level indicator
+ */
+typedef enum eLoRaMacBatteryLevel
+{
+ /*!
+ * External power source
+ */
+ BAT_LEVEL_EXT_SRC = 0x00,
+ /*!
+ * Battery level empty
+ */
+ BAT_LEVEL_EMPTY = 0x01,
+ /*!
+ * Battery level full
+ */
+ BAT_LEVEL_FULL = 0xFE,
+ /*!
+ * Battery level - no measurement available
+ */
+ BAT_LEVEL_NO_MEASURE = 0xFF,
+}LoRaMacBatteryLevel_t;
+
+#endif // __LORAMAC_TYPES_H__
+
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/lora_mac_version.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/lora_mac_version.h
new file mode 100644
index 00000000..2c67edb1
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/lora_mac_version.h
@@ -0,0 +1,56 @@
+/**
+ ******************************************************************************
+ * @file lora_mac_version.h
+ * @author MCD Application Team
+ * @brief Identifies the version of loramac on github
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under BSD 3-Clause license,
+ * the "License"; You may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ * opensource.org/licenses/BSD-3-Clause
+ *
+ ******************************************************************************
+ */
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+
+#ifndef __LORA_MAC_VERSION_H__
+#define __LORA_MAC_VERSION_H__
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+/* Exported constants --------------------------------------------------------*/
+
+/*the 4 MSBs define the version based on Github version */
+/*https://github.com/Lora-net/LoRaMac-node/wiki/LoRaMAC-node-Wiki*/
+/* version 4.4.2-rc.5 from develop branch */
+/*#define LORA_MAC_VERSION (uint32_t) 0x44250000*/
+#define __LORA_MAC_VERSION_MAIN (0x04U) /*!< [31:24] main version */
+#define __LORA_MAC_VERSION_SUB1 (0x04U) /*!< [23:16] sub1 version */
+#define __LORA_MAC_VERSION_SUB2 (0x02U) /*!< [15:8] sub2 version */
+#define __LORA_MAC_VERSION_RC (0x00U) /*!< [7:0] release candidate */
+#define __LORA_MAC_VERSION ((__LORA_MAC_VERSION_MAIN <<24)\
+ |(__LORA_MAC_VERSION_SUB1 << 16)\
+ |(__LORA_MAC_VERSION_SUB2 << 8 )\
+ |(__LORA_MAC_VERSION_RC))
+
+/* Exported types ------------------------------------------------------------*/
+/* External variables --------------------------------------------------------*/
+/* Exported macros -----------------------------------------------------------*/
+/* Exported functions ------------------------------------------------------- */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*__LORA_MAC_VERSION_H__*/
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/Region.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/Region.c
new file mode 100644
index 00000000..c83f0088
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/Region.c
@@ -0,0 +1,1155 @@
+/*!
+ * \file Region.c
+ *
+ * \brief Region implementation.
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013-2017 Semtech
+ *
+ * ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ * embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Gregory Cristian ( Semtech )
+ *
+ * \author Daniel Jaeckle ( STACKFORCE )
+ */
+#include "LoRaMac.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( params ); break; }
+#define AS923_GET_NVM_CTX( ) AS923_CASE { return RegionAS923GetNvmCtx( params ); }
+#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_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( currentDr, type ); }
+#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( ) AS923_CASE { RegionAS923RxBeaconSetup( rxBeaconSetup, outDr ); break; }
+#else
+#define AS923_IS_ACTIVE( )
+#define AS923_GET_PHY_PARAM( )
+#define AS923_SET_BAND_TX_DONE( )
+#define AS923_INIT_DEFAULTS( )
+#define AS923_GET_NVM_CTX( )
+#define AS923_VERIFY( )
+#define AS923_APPLY_CF_LIST( )
+#define AS923_CHAN_MASK_SET( )
+#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( params ); break; }
+#define AU915_GET_NVM_CTX( ) AU915_CASE { return RegionAU915GetNvmCtx( params ); }
+#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_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( currentDr, type ); }
+#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( ) AU915_CASE { RegionAU915RxBeaconSetup( rxBeaconSetup, outDr ); break; }
+#else
+#define AU915_IS_ACTIVE( )
+#define AU915_GET_PHY_PARAM( )
+#define AU915_SET_BAND_TX_DONE( )
+#define AU915_INIT_DEFAULTS( )
+#define AU915_GET_NVM_CTX( )
+#define AU915_VERIFY( )
+#define AU915_APPLY_CF_LIST( )
+#define AU915_CHAN_MASK_SET( )
+#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( params ); break; }
+#define CN470_GET_NVM_CTX( ) CN470_CASE { return RegionCN470GetNvmCtx( params ); }
+#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_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( currentDr, type ); }
+#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( ) CN470_CASE { RegionCN470RxBeaconSetup( rxBeaconSetup, outDr ); break; }
+#else
+#define CN470_IS_ACTIVE( )
+#define CN470_GET_PHY_PARAM( )
+#define CN470_SET_BAND_TX_DONE( )
+#define CN470_INIT_DEFAULTS( )
+#define CN470_GET_NVM_CTX( )
+#define CN470_VERIFY( )
+#define CN470_APPLY_CF_LIST( )
+#define CN470_CHAN_MASK_SET( )
+#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_CN470ALI
+#include "RegionCN470ALI.h"
+#define CN470ALI_CASE case LORAMAC_REGION_CN470ALI:
+#define CN470ALI_IS_ACTIVE( ) CN470ALI_CASE { return true; }
+#define CN470ALI_GET_PHY_PARAM( ) CN470ALI_CASE { return RegionCN470ALIGetPhyParam( getPhy ); }
+#define CN470ALI_SET_BAND_TX_DONE( ) CN470ALI_CASE { RegionCN470ALISetBandTxDone( txDone ); break; }
+#define CN470ALI_INIT_DEFAULTS( ) CN470ALI_CASE { RegionCN470ALIInitDefaults( params ); break; }
+#define CN470ALI_GET_NVM_CTX( ) CN470ALI_CASE { return RegionCN470ALIGetNvmCtx( params ); }
+#define CN470ALI_VERIFY( ) CN470ALI_CASE { return RegionCN470ALIVerify( verify, phyAttribute ); }
+#define CN470ALI_APPLY_CF_LIST( ) CN470ALI_CASE { RegionCN470ALIApplyCFList( applyCFList ); break; }
+#define CN470ALI_CHAN_MASK_SET( ) CN470ALI_CASE { return RegionCN470ALIChanMaskSet( chanMaskSet ); }
+#define CN470ALI_COMPUTE_RX_WINDOW_PARAMETERS( ) CN470ALI_CASE { RegionCN470ALIComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; }
+#define CN470ALI_RX_CONFIG( ) CN470ALI_CASE { return RegionCN470ALIRxConfig( rxConfig, datarate ); }
+#define CN470ALI_TX_CONFIG( ) CN470ALI_CASE { return RegionCN470ALITxConfig( txConfig, txPower, txTimeOnAir ); }
+#define CN470ALI_LINK_ADR_REQ( ) CN470ALI_CASE { return RegionCN470ALILinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); }
+#define CN470ALI_RX_PARAM_SETUP_REQ( ) CN470ALI_CASE { return RegionCN470ALIRxParamSetupReq( rxParamSetupReq ); }
+#define CN470ALI_NEW_CHANNEL_REQ( ) CN470ALI_CASE { return RegionCN470ALINewChannelReq( newChannelReq ); }
+#define CN470ALI_TX_PARAM_SETUP_REQ( ) CN470ALI_CASE { return RegionCN470ALITxParamSetupReq( txParamSetupReq ); }
+#define CN470ALI_DL_CHANNEL_REQ( ) CN470ALI_CASE { return RegionCN470ALIDlChannelReq( dlChannelReq ); }
+#define CN470ALI_ALTERNATE_DR( ) CN470ALI_CASE { return RegionCN470ALIAlternateDr( currentDr, type ); }
+#define CN470ALI_CALC_BACKOFF( ) CN470ALI_CASE { RegionCN470ALICalcBackOff( calcBackOff ); break; }
+#define CN470ALI_NEXT_CHANNEL( ) CN470ALI_CASE { return RegionCN470ALINextChannel( nextChanParams, channel, time, aggregatedTimeOff ); }
+#define CN470ALI_CHANNEL_ADD( ) CN470ALI_CASE { return RegionCN470ALIChannelAdd( channelAdd ); }
+#define CN470ALI_CHANNEL_REMOVE( ) CN470ALI_CASE { return RegionCN470ALIChannelsRemove( channelRemove ); }
+#define CN470ALI_SET_CONTINUOUS_WAVE( ) CN470ALI_CASE { RegionCN470ALISetContinuousWave( continuousWave ); break; }
+#define CN470ALI_APPLY_DR_OFFSET( ) CN470ALI_CASE { return RegionCN470ALIApplyDrOffset( downlinkDwellTime, dr, drOffset ); }
+#define CN470ALI_RX_BEACON_SETUP( ) CN470ALI_CASE { RegionCN470ALIRxBeaconSetup( rxBeaconSetup, outDr ); break; }
+#else
+#define CN470ALI_IS_ACTIVE( )
+#define CN470ALI_GET_PHY_PARAM( )
+#define CN470ALI_SET_BAND_TX_DONE( )
+#define CN470ALI_INIT_DEFAULTS( )
+#define CN470ALI_GET_NVM_CTX( )
+#define CN470ALI_VERIFY( )
+#define CN470ALI_APPLY_CF_LIST( )
+#define CN470ALI_CHAN_MASK_SET( )
+#define CN470ALI_COMPUTE_RX_WINDOW_PARAMETERS( )
+#define CN470ALI_RX_CONFIG( )
+#define CN470ALI_TX_CONFIG( )
+#define CN470ALI_LINK_ADR_REQ( )
+#define CN470ALI_RX_PARAM_SETUP_REQ( )
+#define CN470ALI_NEW_CHANNEL_REQ( )
+#define CN470ALI_TX_PARAM_SETUP_REQ( )
+#define CN470ALI_DL_CHANNEL_REQ( )
+#define CN470ALI_ALTERNATE_DR( )
+#define CN470ALI_CALC_BACKOFF( )
+#define CN470ALI_NEXT_CHANNEL( )
+#define CN470ALI_CHANNEL_ADD( )
+#define CN470ALI_CHANNEL_REMOVE( )
+#define CN470ALI_SET_CONTINUOUS_WAVE( )
+#define CN470ALI_APPLY_DR_OFFSET( )
+#define CN470ALI_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( params ); break; }
+#define CN779_GET_NVM_CTX( ) CN779_CASE { return RegionCN779GetNvmCtx( params ); }
+#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_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( currentDr, type ); }
+#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( ) CN779_CASE { RegionCN779RxBeaconSetup( rxBeaconSetup, outDr ); break; }
+#else
+#define CN779_IS_ACTIVE( )
+#define CN779_GET_PHY_PARAM( )
+#define CN779_SET_BAND_TX_DONE( )
+#define CN779_INIT_DEFAULTS( )
+#define CN779_GET_NVM_CTX( )
+#define CN779_VERIFY( )
+#define CN779_APPLY_CF_LIST( )
+#define CN779_CHAN_MASK_SET( )
+#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( params ); break; }
+#define EU433_GET_NVM_CTX( ) EU433_CASE { return RegionEU433GetNvmCtx( params ); }
+#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_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( currentDr, type ); }
+#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( ) EU433_CASE { RegionEU433RxBeaconSetup( rxBeaconSetup, outDr ); break; }
+#else
+#define EU433_IS_ACTIVE( )
+#define EU433_GET_PHY_PARAM( )
+#define EU433_SET_BAND_TX_DONE( )
+#define EU433_INIT_DEFAULTS( )
+#define EU433_GET_NVM_CTX( )
+#define EU433_VERIFY( )
+#define EU433_APPLY_CF_LIST( )
+#define EU433_CHAN_MASK_SET( )
+#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( params ); break; }
+#define EU868_GET_NVM_CTX( ) EU868_CASE { return RegionEU868GetNvmCtx( params ); }
+#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_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( currentDr, type ); }
+#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 ); break; }
+#else
+#define EU868_IS_ACTIVE( )
+#define EU868_GET_PHY_PARAM( )
+#define EU868_SET_BAND_TX_DONE( )
+#define EU868_INIT_DEFAULTS( )
+#define EU868_GET_NVM_CTX( )
+#define EU868_VERIFY( )
+#define EU868_APPLY_CF_LIST( )
+#define EU868_CHAN_MASK_SET( )
+#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( params ); break; }
+#define KR920_GET_NVM_CTX( ) KR920_CASE { return RegionKR920GetNvmCtx( params ); }
+#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_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( currentDr, type ); }
+#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( ) KR920_CASE { RegionKR920RxBeaconSetup( rxBeaconSetup, outDr ); break; }
+#else
+#define KR920_IS_ACTIVE( )
+#define KR920_GET_PHY_PARAM( )
+#define KR920_SET_BAND_TX_DONE( )
+#define KR920_INIT_DEFAULTS( )
+#define KR920_GET_NVM_CTX( )
+#define KR920_VERIFY( )
+#define KR920_APPLY_CF_LIST( )
+#define KR920_CHAN_MASK_SET( )
+#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( params ); break; }
+#define IN865_GET_NVM_CTX( ) IN865_CASE { return RegionIN865GetNvmCtx( params ); }
+#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_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( currentDr, type ); }
+#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( ) IN865_CASE { RegionIN865RxBeaconSetup( rxBeaconSetup, outDr ); break; }
+#else
+#define IN865_IS_ACTIVE( )
+#define IN865_GET_PHY_PARAM( )
+#define IN865_SET_BAND_TX_DONE( )
+#define IN865_INIT_DEFAULTS( )
+#define IN865_GET_NVM_CTX( )
+#define IN865_VERIFY( )
+#define IN865_APPLY_CF_LIST( )
+#define IN865_CHAN_MASK_SET( )
+#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( params ); break; }
+#define US915_GET_NVM_CTX( ) US915_CASE { return RegionUS915GetNvmCtx( params ); }
+#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_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( currentDr, type ); }
+#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( ) US915_CASE { RegionUS915RxBeaconSetup( rxBeaconSetup, outDr ); break; }
+#else
+#define US915_IS_ACTIVE( )
+#define US915_GET_PHY_PARAM( )
+#define US915_SET_BAND_TX_DONE( )
+#define US915_INIT_DEFAULTS( )
+#define US915_GET_NVM_CTX( )
+#define US915_VERIFY( )
+#define US915_APPLY_CF_LIST( )
+#define US915_CHAN_MASK_SET( )
+#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_RU864
+#include "RegionRU864.h"
+#define RU864_CASE case LORAMAC_REGION_RU864:
+#define RU864_IS_ACTIVE( ) RU864_CASE { return true; }
+#define RU864_GET_PHY_PARAM( ) RU864_CASE { return RegionRU864GetPhyParam( getPhy ); }
+#define RU864_SET_BAND_TX_DONE( ) RU864_CASE { RegionRU864SetBandTxDone( txDone ); break; }
+#define RU864_INIT_DEFAULTS( ) RU864_CASE { RegionRU864InitDefaults( params ); break; }
+#define RU864_GET_NVM_CTX( ) RU864_CASE { return RegionRU864GetNvmCtx( params ); }
+#define RU864_VERIFY( ) RU864_CASE { return RegionRU864Verify( verify, phyAttribute ); }
+#define RU864_APPLY_CF_LIST( ) RU864_CASE { RegionRU864ApplyCFList( applyCFList ); break; }
+#define RU864_CHAN_MASK_SET( ) RU864_CASE { return RegionRU864ChanMaskSet( chanMaskSet ); }
+#define RU864_COMPUTE_RX_WINDOW_PARAMETERS( ) RU864_CASE { RegionRU864ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; }
+#define RU864_RX_CONFIG( ) RU864_CASE { return RegionRU864RxConfig( rxConfig, datarate ); }
+#define RU864_TX_CONFIG( ) RU864_CASE { return RegionRU864TxConfig( txConfig, txPower, txTimeOnAir ); }
+#define RU864_LINK_ADR_REQ( ) RU864_CASE { return RegionRU864LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); }
+#define RU864_RX_PARAM_SETUP_REQ( ) RU864_CASE { return RegionRU864RxParamSetupReq( rxParamSetupReq ); }
+#define RU864_NEW_CHANNEL_REQ( ) RU864_CASE { return RegionRU864NewChannelReq( newChannelReq ); }
+#define RU864_TX_PARAM_SETUP_REQ( ) RU864_CASE { return RegionRU864TxParamSetupReq( txParamSetupReq ); }
+#define RU864_DL_CHANNEL_REQ( ) RU864_CASE { return RegionRU864DlChannelReq( dlChannelReq ); }
+#define RU864_ALTERNATE_DR( ) RU864_CASE { return RegionRU864AlternateDr( currentDr, type ); }
+#define RU864_CALC_BACKOFF( ) RU864_CASE { RegionRU864CalcBackOff( calcBackOff ); break; }
+#define RU864_NEXT_CHANNEL( ) RU864_CASE { return RegionRU864NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); }
+#define RU864_CHANNEL_ADD( ) RU864_CASE { return RegionRU864ChannelAdd( channelAdd ); }
+#define RU864_CHANNEL_REMOVE( ) RU864_CASE { return RegionRU864ChannelsRemove( channelRemove ); }
+#define RU864_SET_CONTINUOUS_WAVE( ) RU864_CASE { RegionRU864SetContinuousWave( continuousWave ); break; }
+#define RU864_APPLY_DR_OFFSET( ) RU864_CASE { return RegionRU864ApplyDrOffset( downlinkDwellTime, dr, drOffset ); }
+#define RU864_RX_BEACON_SETUP( ) RU864_CASE { RegionRU864RxBeaconSetup( rxBeaconSetup, outDr ); break; }
+#else
+#define RU864_IS_ACTIVE( )
+#define RU864_GET_PHY_PARAM( )
+#define RU864_SET_BAND_TX_DONE( )
+#define RU864_INIT_DEFAULTS( )
+#define RU864_GET_NVM_CTX( )
+#define RU864_VERIFY( )
+#define RU864_APPLY_CF_LIST( )
+#define RU864_CHAN_MASK_SET( )
+#define RU864_COMPUTE_RX_WINDOW_PARAMETERS( )
+#define RU864_RX_CONFIG( )
+#define RU864_TX_CONFIG( )
+#define RU864_LINK_ADR_REQ( )
+#define RU864_RX_PARAM_SETUP_REQ( )
+#define RU864_NEW_CHANNEL_REQ( )
+#define RU864_TX_PARAM_SETUP_REQ( )
+#define RU864_DL_CHANNEL_REQ( )
+#define RU864_ALTERNATE_DR( )
+#define RU864_CALC_BACKOFF( )
+#define RU864_NEXT_CHANNEL( )
+#define RU864_CHANNEL_ADD( )
+#define RU864_CHANNEL_REMOVE( )
+#define RU864_SET_CONTINUOUS_WAVE( )
+#define RU864_APPLY_DR_OFFSET( )
+#define RU864_RX_BEACON_SETUP( )
+#endif
+
+bool RegionIsActive( LoRaMacRegion_t region )
+{
+ switch( region )
+ {
+ AS923_IS_ACTIVE( );
+ AU915_IS_ACTIVE( );
+ CN470_IS_ACTIVE( );
+ CN470ALI_IS_ACTIVE( );
+ CN779_IS_ACTIVE( );
+ EU433_IS_ACTIVE( );
+ EU868_IS_ACTIVE( );
+ KR920_IS_ACTIVE( );
+ IN865_IS_ACTIVE( );
+ US915_IS_ACTIVE( );
+ RU864_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( );
+ CN470ALI_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( );
+ RU864_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( );
+ CN470ALI_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( );
+ RU864_SET_BAND_TX_DONE( );
+ default:
+ {
+ return;
+ }
+ }
+}
+
+void RegionInitDefaults( LoRaMacRegion_t region, InitDefaultsParams_t* params )
+{
+ switch( region )
+ {
+ AS923_INIT_DEFAULTS( );
+ AU915_INIT_DEFAULTS( );
+ CN470_INIT_DEFAULTS( );
+ CN470ALI_INIT_DEFAULTS( );
+ CN779_INIT_DEFAULTS( );
+ EU433_INIT_DEFAULTS( );
+ EU868_INIT_DEFAULTS( );
+ KR920_INIT_DEFAULTS( );
+ IN865_INIT_DEFAULTS( );
+ US915_INIT_DEFAULTS( );
+ RU864_INIT_DEFAULTS( );
+ default:
+ {
+ break;
+ }
+ }
+}
+
+void* RegionGetNvmCtx( LoRaMacRegion_t region, GetNvmCtxParams_t* params )
+{
+ switch( region )
+ {
+ AS923_GET_NVM_CTX( );
+ AU915_GET_NVM_CTX( );
+ CN470_GET_NVM_CTX( );
+ CN470ALI_GET_NVM_CTX( );
+ CN779_GET_NVM_CTX( );
+ EU433_GET_NVM_CTX( );
+ EU868_GET_NVM_CTX( );
+ KR920_GET_NVM_CTX( );
+ IN865_GET_NVM_CTX( );
+ US915_GET_NVM_CTX( );
+ RU864_GET_NVM_CTX( );
+ default:
+ {
+ return 0;
+ }
+ }
+}
+
+bool RegionVerify( LoRaMacRegion_t region, VerifyParams_t* verify, PhyAttribute_t phyAttribute )
+{
+ switch( region )
+ {
+ AS923_VERIFY( );
+ AU915_VERIFY( );
+ CN470_VERIFY( );
+ CN470ALI_VERIFY( );
+ CN779_VERIFY( );
+ EU433_VERIFY( );
+ EU868_VERIFY( );
+ KR920_VERIFY( );
+ IN865_VERIFY( );
+ US915_VERIFY( );
+ RU864_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( );
+ CN470ALI_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( );
+ RU864_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( );
+ CN470ALI_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( );
+ RU864_CHAN_MASK_SET( );
+ 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( );
+ CN470ALI_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( );
+ RU864_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( );
+ CN470ALI_RX_CONFIG( );
+ CN779_RX_CONFIG( );
+ EU433_RX_CONFIG( );
+ EU868_RX_CONFIG( );
+ KR920_RX_CONFIG( );
+ IN865_RX_CONFIG( );
+ US915_RX_CONFIG( );
+ RU864_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( );
+ CN470ALI_TX_CONFIG( );
+ CN779_TX_CONFIG( );
+ EU433_TX_CONFIG( );
+ EU868_TX_CONFIG( );
+ KR920_TX_CONFIG( );
+ IN865_TX_CONFIG( );
+ US915_TX_CONFIG( );
+ RU864_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( );
+ CN470ALI_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( );
+ RU864_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( );
+ CN470ALI_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( );
+ RU864_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( );
+ CN470ALI_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( );
+ RU864_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( );
+ CN470ALI_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( );
+ RU864_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( );
+ CN470ALI_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( );
+ RU864_DL_CHANNEL_REQ( );
+ default:
+ {
+ return 0;
+ }
+ }
+}
+
+int8_t RegionAlternateDr( LoRaMacRegion_t region, int8_t currentDr, AlternateDrType_t type )
+{
+ switch( region )
+ {
+ AS923_ALTERNATE_DR( );
+ AU915_ALTERNATE_DR( );
+ CN470_ALTERNATE_DR( );
+ CN470ALI_ALTERNATE_DR( );
+ CN779_ALTERNATE_DR( );
+ EU433_ALTERNATE_DR( );
+ EU868_ALTERNATE_DR( );
+ KR920_ALTERNATE_DR( );
+ IN865_ALTERNATE_DR( );
+ US915_ALTERNATE_DR( );
+ RU864_ALTERNATE_DR( );
+ default:
+ {
+ return 0;
+ }
+ }
+}
+
+void RegionCalcBackOff( LoRaMacRegion_t region, CalcBackOffParams_t* calcBackOff )
+{
+ switch( region )
+ {
+ AS923_CALC_BACKOFF( );
+ AU915_CALC_BACKOFF( );
+ CN470_CALC_BACKOFF( );
+ CN470ALI_CALC_BACKOFF( );
+ CN779_CALC_BACKOFF( );
+ EU433_CALC_BACKOFF( );
+ EU868_CALC_BACKOFF( );
+ KR920_CALC_BACKOFF( );
+ IN865_CALC_BACKOFF( );
+ US915_CALC_BACKOFF( );
+ RU864_CALC_BACKOFF( );
+ default:
+ {
+ break;
+ }
+ }
+}
+
+LoRaMacStatus_t 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( );
+ CN470ALI_NEXT_CHANNEL( );
+ CN779_NEXT_CHANNEL( );
+ EU433_NEXT_CHANNEL( );
+ EU868_NEXT_CHANNEL( );
+ KR920_NEXT_CHANNEL( );
+ IN865_NEXT_CHANNEL( );
+ US915_NEXT_CHANNEL( );
+ RU864_NEXT_CHANNEL( );
+ default:
+ {
+ return LORAMAC_STATUS_REGION_NOT_SUPPORTED;
+ }
+ }
+}
+
+LoRaMacStatus_t RegionChannelAdd( LoRaMacRegion_t region, ChannelAddParams_t* channelAdd )
+{
+ switch( region )
+ {
+ AS923_CHANNEL_ADD( );
+ AU915_CHANNEL_ADD( );
+ CN470_CHANNEL_ADD( );
+ CN470ALI_CHANNEL_ADD( );
+ CN779_CHANNEL_ADD( );
+ EU433_CHANNEL_ADD( );
+ EU868_CHANNEL_ADD( );
+ KR920_CHANNEL_ADD( );
+ IN865_CHANNEL_ADD( );
+ US915_CHANNEL_ADD( );
+ RU864_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( );
+ CN470ALI_CHANNEL_REMOVE( );
+ CN779_CHANNEL_REMOVE( );
+ EU433_CHANNEL_REMOVE( );
+ EU868_CHANNEL_REMOVE( );
+ KR920_CHANNEL_REMOVE( );
+ IN865_CHANNEL_REMOVE( );
+ US915_CHANNEL_REMOVE( );
+ RU864_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( );
+ CN470ALI_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( );
+ RU864_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( );
+ CN470ALI_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( );
+ RU864_APPLY_DR_OFFSET( );
+ default:
+ {
+ return dr;
+ }
+ }
+}
+
+void RegionRxBeaconSetup( LoRaMacRegion_t region, RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr )
+{
+ switch( region )
+ {
+ AS923_RX_BEACON_SETUP( );
+ AU915_RX_BEACON_SETUP( );
+ CN470_RX_BEACON_SETUP( );
+ CN470ALI_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( );
+ RU864_RX_BEACON_SETUP( );
+ default:
+ {
+ break;
+ }
+ }
+}
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/Region.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/Region.h
new file mode 100644
index 00000000..6b5fd45e
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/Region.h
@@ -0,0 +1,1687 @@
+/*!
+ * \file Region.h
+ *
+ * \brief Region implementation.
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013-2017 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_RU864
+ *
+ * \{
+ */
+#ifndef __REGION_H__
+#define __REGION_H__
+
+#include
+#include
+#include "LoRaMac.h"
+#include "timer.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
+ * RU864 | SF12 - 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
+ * RU864 | SF11 - 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
+ * RU864 | SF10 - 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
+ * RU864 | SF9 - 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
+ * RU864 | SF8 - BW125
+ */
+#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
+ * RU864 | SF7 - BW125
+ */
+#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
+ * RU864 | SF7 - BW250
+ */
+#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
+ * RU864 | FSK
+ */
+#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
+ * RU864 | RFU
+ */
+#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
+ * RU864 | RFU
+ */
+#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
+ * RU864 | RFU
+ */
+#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
+ * RU864 | RFU
+ */
+#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
+ * RU864 | RFU
+ */
+#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
+ * RU864 | RFU
+ */
+#define DR_13 13
+
+/*!
+ * Region | SF
+ * ------------ | :-----:
+ * AS923 | RFU
+ * AU915 | RFU
+ * CN470 | RFU
+ * CN779 | RFU
+ * EU433 | RFU
+ * EU868 | RFU
+ * IN865 | RFU
+ * KR920 | RFU
+ * US915 | RFU
+ * RU864 | 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
+ * RU864 | 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
+ * RU864 | Max EIRP
+ */
+#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
+ * RU864 | Max EIRP - 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
+ * RU864 | Max EIRP - 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
+ * RU864 | Max EIRP - 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
+ * RU864 | Max EIRP - 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
+ * RU864 | Max EIRP - 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
+ * RU864 | Max EIRP - 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
+ * RU864 | Max EIRP - 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
+ * RU864 | -
+ */
+#define TX_POWER_8 8
+
+/*!
+ * Region | dBM
+ * ------------ | :-----:
+ * AS923 | -
+ * AU915 | Max EIRP - 18
+ * CN470 | -
+ * CN779 | -
+ * EU433 | -
+ * EU868 | -
+ * IN865 | Max EIRP - 18
+ * KR920 | -
+ * US915 | Max ERP - 18
+ * RU864 | -
+ */
+#define TX_POWER_9 9
+
+/*!
+ * Region | dBM
+ * ------------ | :-----:
+ * AS923 | -
+ * AU915 | Max EIRP - 20
+ * CN470 | -
+ * CN779 | -
+ * EU433 | -
+ * EU868 | -
+ * IN865 | Max EIRP - 20
+ * KR920 | -
+ * US915 | Max ERP - 20
+ * RU864 | -
+ */
+#define TX_POWER_10 10
+
+/*!
+ * Region | dBM
+ * ------------ | :-----:
+ * AS923 | -
+ * AU915 | Max EIRP - 22
+ * CN470 | -
+ * CN779 | -
+ * EU433 | -
+ * EU868 | -
+ * IN865 | -
+ * KR920 | -
+ * US915 | Max ERP - 22
+ * RU864 | -
+ */
+#define TX_POWER_11 11
+
+/*!
+ * Region | dBM
+ * ------------ | :-----:
+ * AS923 | -
+ * AU915 | Max EIRP - 24
+ * CN470 | -
+ * CN779 | -
+ * EU433 | -
+ * EU868 | -
+ * IN865 | -
+ * KR920 | -
+ * US915 | Max ERP - 24
+ * RU864 | -
+ */
+#define TX_POWER_12 12
+
+/*!
+ * Region | dBM
+ * ------------ | :-----:
+ * AS923 | -
+ * AU915 | Max EIRP - 26
+ * CN470 | -
+ * CN779 | -
+ * EU433 | -
+ * EU868 | -
+ * IN865 | -
+ * KR920 | -
+ * US915 | Max ERP - 26
+ * RU864 | -
+ */
+#define TX_POWER_13 13
+
+/*!
+ * Region | dBM
+ * ------------ | :-----:
+ * AS923 | -
+ * AU915 | Max EIRP - 28
+ * CN470 | -
+ * CN779 | -
+ * EU433 | -
+ * EU868 | -
+ * IN865 | -
+ * KR920 | -
+ * US915 | Max ERP - 28
+ * RU864 | -
+ */
+#define TX_POWER_14 14
+
+/*!
+ * RFU
+ */
+#define TX_POWER_15 15
+
+
+
+/*!
+ * Enumeration of phy attributes.
+ */
+typedef enum ePhyAttribute
+{
+ /*!
+ * Frequency. It is available
+ * to perform a verification with RegionVerify().
+ */
+ PHY_FREQUENCY,
+ /*!
+ * 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.
+ * This is a parameter which can't be queried. It is available
+ * to perform a verification with RegionVerify().
+ */
+ PHY_TX_DR,
+ /*!
+ * Default TX datarate.
+ */
+ PHY_DEF_TX_DR,
+ /*!
+ * RX datarate. It is available
+ * to perform a verification with RegionVerify().
+ */
+ PHY_RX_DR,
+ /*!
+ * Maximum TX power.
+ */
+ PHY_MAX_TX_POWER,
+ /*!
+ * TX power. It is available
+ * to perform a verification with RegionVerify().
+ */
+ PHY_TX_POWER,
+ /*!
+ * Default TX power.
+ */
+ PHY_DEF_TX_POWER,
+ /*!
+ * Default ADR_ACK_LIMIT value.
+ */
+ PHY_DEF_ADR_ACK_LIMIT,
+ /*!
+ * Default ADR_ACK_DELAY value.
+ */
+ PHY_DEF_ADR_ACK_DELAY,
+ /*!
+ * 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,
+ /*!
+ * Next lower datarate.
+ */
+ PHY_NEXT_LOWER_TX_DR,
+ /*!
+ * 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,
+ /*!
+ * Beacon channel frequency.
+ */
+ PHY_BEACON_CHANNEL_FREQ,
+ /*!
+ * The format of the beacon.
+ */
+ PHY_BEACON_FORMAT,
+ /*!
+ * The beacon channel datarate.
+ */
+ PHY_BEACON_CHANNEL_DR,
+ /*!
+ * The frequency stepwidth between the beacon channels.
+ */
+ PHY_BEACON_CHANNEL_STEPWIDTH,
+ /*!
+ * The number of channels for the beacon reception.
+ */
+ PHY_BEACON_NB_CHANNELS,
+ /*!
+ * The datarate of a ping slot channel.
+ */
+ PHY_PING_SLOT_CHANNEL_DR
+}PhyAttribute_t;
+
+/*!
+ * Enumeration of initialization types.
+ */
+typedef enum eInitType
+{
+ /*!
+ * Initializes the region specific data to defaults, according to the
+ * LoRaWAN specification.
+ */
+ INIT_TYPE_INIT,
+ /*!
+ * Restores default channels defined by the LoRaWAN specification only.
+ */
+ INIT_TYPE_RESTORE_DEFAULT_CHANNELS,
+ /*!
+ * Restores internal context from passed pointer.
+ */
+ INIT_TYPE_RESTORE_CTX
+}InitType_t;
+
+typedef enum eChannelsMask
+{
+ /*!
+ * The channels mask.
+ */
+ CHANNELS_MASK,
+ /*!
+ * The channels default mask.
+ */
+ CHANNELS_DEFAULT_MASK
+}ChannelsMask_t;
+
+/*!
+ * Structure containing the beacon format
+ */
+typedef struct sBeaconFormat
+{
+ /*!
+ * Size of the beacon
+ */
+ uint8_t BeaconSize;
+ /*!
+ * Size of the RFU 1 data field
+ */
+ uint8_t Rfu1Size;
+ /*!
+ * Size of the RFU 2 data field
+ */
+ uint8_t Rfu2Size;
+}BeaconFormat_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;
+ /*!
+ * Beacon format
+ */
+ BeaconFormat_t BeaconFormat;
+}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;
+}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 RegionInitDefaults.
+ */
+typedef struct sInitDefaultsParams
+{
+ /*!
+ * Pointer to region module context to be restored.
+ */
+ void* NvmCtx;
+ /*!
+ * Sets the initialization type.
+ */
+ InitType_t Type;
+}InitDefaultsParams_t;
+
+/*!
+ * Parameter structure for the function RegionGetNvmCtx.
+ */
+typedef struct sGetNvmCtxParams
+{
+ /*!
+ * Size of module context.
+ */
+ size_t nvmCtxSize;
+}GetNvmCtxParams_t;
+
+
+/*!
+ * Parameter structure for the function RegionVerify.
+ */
+typedef union uVerifyParams
+{
+ /*!
+ * Channel frequency to verify
+ */
+ uint32_t Frequency;
+ /*!
+ * TX power to verify.
+ */
+ int8_t TxPower;
+ /*!
+ * Set to true, if the duty cycle is enabled, otherwise false.
+ */
+ bool DutyCycle;
+ /*!
+ * 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 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.
+ */
+ LoRaMacRxSlot_t RxSlot;
+}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
+{
+ /*!
+ * Current LoRaWAN Version
+ */
+ Version_t Version;
+ /*!
+ * 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;
+
+/*!
+ * Enumeration of alternation type
+ */
+typedef enum eAlternateDrType
+{
+ /*!
+ * Type to use for an alternation
+ */
+ ALTERNATE_DR,
+ /*!
+ * Type to use to restore one alternation
+ */
+ ALTERNATE_DR_RESTORE
+}AlternateDrType_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 frequency to setup.
+ */
+ uint32_t Frequency;
+}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] params Pointer to the function parameters.
+ */
+void RegionInitDefaults( LoRaMacRegion_t region, InitDefaultsParams_t* params );
+
+/*!
+ * \brief Returns a pointer to the internal context and its size.
+ *
+ * \param [IN] region LoRaWAN region.
+ *
+ * \param [IN] params Pointer to the function parameters.
+ *
+ * \retval Points to a structure where the module store its non-volatile context.
+ */
+void* RegionGetNvmCtx( LoRaMacRegion_t region, GetNvmCtxParams_t* params );
+
+/*!
+ * \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 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.
+ * https://www.semtech.com/uploads/documents/SX1272_settings_for_LoRaWAN_v2.0.pdf
+ * or
+ * https://www.semtech.com/uploads/documents/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] currentDr Current datarate.
+ *
+ * \param [IN] type Alternation type.
+ *
+ * \retval Datarate to apply.
+ */
+int8_t RegionAlternateDr( LoRaMacRegion_t region, int8_t currentDr, AlternateDrType_t type );
+
+/*!
+ * \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].
+ */
+LoRaMacStatus_t 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 );
+
+/*!
+ * \brief Sets the radio into beacon reception mode
+ *
+ * \param [IN] rxBeaconSetup Pointer to the function parameters
+ *
+ * \param [out] outDr Datarate used to receive the beacon
+ */
+void RegionRxBeaconSetup( LoRaMacRegion_t region, RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr );
+
+/*! \} defgroup REGION */
+
+#endif // __REGION_H__
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionAS923.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionAS923.c
new file mode 100644
index 00000000..9ee6122b
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionAS923.c
@@ -0,0 +1,1110 @@
+/*!
+ * \file RegionAS923.c
+ *
+ * \brief Region implementation for AS923
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013-2017 Semtech
+ *
+ * ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ * embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Gregory Cristian ( Semtech )
+ *
+ * \author Daniel Jaeckle ( STACKFORCE )
+*/
+#include "utilities.h"
+
+#include "RegionCommon.h"
+#include "RegionAS923.h"
+#include "lorawan_conf.h"
+#include "mw_log_conf.h"
+
+// Definitions
+#define CHANNELS_MASK_SIZE 1
+
+/*!
+ * Region specific context
+ */
+typedef struct sRegionAS923NvmCtx
+{
+ /*!
+ * LoRaMAC channels
+ */
+ ChannelParams_t Channels[ AS923_MAX_NB_CHANNELS ];
+ /*!
+ * LoRaMac bands
+ */
+ Band_t Bands[ AS923_MAX_NB_BANDS ];
+ /*!
+ * LoRaMac channels mask
+ */
+ uint16_t ChannelsMask[ CHANNELS_MASK_SIZE ];
+ /*!
+ * LoRaMac channels default mask
+ */
+ uint16_t ChannelsDefaultMask[ CHANNELS_MASK_SIZE ];
+}RegionAS923NvmCtx_t;
+
+/*
+ * Non-volatile module context.
+ */
+static RegionAS923NvmCtx_t NvmCtx;
+
+// 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 VerifyRfFreq( 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_MAX_TX_POWER:
+ {
+ phyParam.Value = AS923_MAX_TX_POWER;
+ break;
+ }
+ case PHY_DEF_TX_POWER:
+ {
+ phyParam.Value = AS923_DEFAULT_TX_POWER;
+ break;
+ }
+ case PHY_DEF_ADR_ACK_LIMIT:
+ {
+ phyParam.Value = AS923_ADR_ACK_LIMIT;
+ break;
+ }
+ case PHY_DEF_ADR_ACK_DELAY:
+ {
+ phyParam.Value = AS923_ADR_ACK_DELAY;
+ 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 = NvmCtx.ChannelsMask;
+ break;
+ }
+ case PHY_CHANNELS_DEFAULT_MASK:
+ {
+ phyParam.ChannelsMask = NvmCtx.ChannelsDefaultMask;
+ break;
+ }
+ case PHY_MAX_NB_CHANNELS:
+ {
+ phyParam.Value = AS923_MAX_NB_CHANNELS;
+ break;
+ }
+ case PHY_CHANNELS:
+ {
+ phyParam.Channels = NvmCtx.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_BEACON_CHANNEL_FREQ:
+ {
+ phyParam.Value = AS923_BEACON_CHANNEL_FREQ;
+ break;
+ }
+ case PHY_BEACON_FORMAT:
+ {
+ phyParam.BeaconFormat.BeaconSize = AS923_BEACON_SIZE;
+ phyParam.BeaconFormat.Rfu1Size = AS923_RFU1_SIZE;
+ phyParam.BeaconFormat.Rfu2Size = AS923_RFU2_SIZE;
+ break;
+ }
+ case PHY_BEACON_CHANNEL_DR:
+ {
+ phyParam.Value = AS923_BEACON_CHANNEL_DR;
+ break;
+ }
+ case PHY_PING_SLOT_CHANNEL_DR:
+ {
+ phyParam.Value = AS923_PING_SLOT_CHANNEL_DR;
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ return phyParam;
+}
+
+void RegionAS923SetBandTxDone( SetBandTxDoneParams_t* txDone )
+{
+ RegionCommonSetBandTxDone( txDone->Joined, &NvmCtx.Bands[NvmCtx.Channels[txDone->Channel].Band], txDone->LastTxDoneTime );
+}
+
+void RegionAS923InitDefaults( InitDefaultsParams_t* params )
+{
+ Band_t bands[AS923_MAX_NB_BANDS] =
+ {
+ AS923_BAND0
+ };
+
+ switch( params->Type )
+ {
+ case INIT_TYPE_INIT:
+ {
+ // Initialize bands
+ memcpy1( ( uint8_t* )NvmCtx.Bands, ( uint8_t* )bands, sizeof( Band_t ) * AS923_MAX_NB_BANDS );
+
+ // Channels
+ NvmCtx.Channels[0] = ( ChannelParams_t ) AS923_LC1;
+ NvmCtx.Channels[1] = ( ChannelParams_t ) AS923_LC2;
+
+ // Initialize the channels default mask
+ NvmCtx.ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 );
+ // Update the channels mask
+ RegionCommonChanMaskCopy( NvmCtx.ChannelsMask, NvmCtx.ChannelsDefaultMask, 1 );
+ break;
+ }
+ case INIT_TYPE_RESTORE_CTX:
+ {
+ if( params->NvmCtx != 0 )
+ {
+ memcpy1( (uint8_t*) &NvmCtx, (uint8_t*) params->NvmCtx, sizeof( NvmCtx ) );
+ }
+ break;
+ }
+ case INIT_TYPE_RESTORE_DEFAULT_CHANNELS:
+ {
+ // Restore channels default mask
+ NvmCtx.ChannelsMask[0] |= NvmCtx.ChannelsDefaultMask[0];
+
+ // Channels
+ NvmCtx.Channels[0] = ( ChannelParams_t ) AS923_LC1;
+ NvmCtx.Channels[1] = ( ChannelParams_t ) AS923_LC2;
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+void* RegionAS923GetNvmCtx( GetNvmCtxParams_t* params )
+{
+ params->nvmCtxSize = sizeof( RegionAS923NvmCtx_t );
+ return &NvmCtx;
+}
+
+bool RegionAS923Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute )
+{
+ switch( phyAttribute )
+ {
+ case PHY_FREQUENCY:
+ {
+ return VerifyRfFreq( verify->Frequency );
+ }
+ 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;
+ }
+ 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 CFListType must be 0 to indicate the CFList contains a list of frequencies
+ if( applyCFList->Payload[15] != 0 )
+ {
+ 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( NvmCtx.ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 );
+ break;
+ }
+ case CHANNELS_DEFAULT_MASK:
+ {
+ RegionCommonChanMaskCopy( NvmCtx.ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 );
+ break;
+ }
+ default:
+ return false;
+ }
+ return true;
+}
+
+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.GetWakeupTime( ), &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;
+ const char *slotStrings[] = { "1", "2", "C", "Multi_C", "P", "Multi_P" };
+
+ if( Radio.GetStatus( ) != RF_IDLE )
+ {
+ return false;
+ }
+
+ if( rxConfig->RxSlot == RX_SLOT_WIN_1 )
+ {
+ // Apply window 1 frequency
+ frequency = NvmCtx.Channels[rxConfig->Channel].Frequency;
+ // Apply the alternative RX 1 window frequency, if it is available
+ if( NvmCtx.Channels[rxConfig->Channel].Rx1Frequency != 0 )
+ {
+ frequency = NvmCtx.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 );
+ if ( rxConfig->RxSlot < RX_SLOT_NONE )
+ {
+ MW_LOG( "RX_%s on freq %d Hz at DR %d\n\r", slotStrings[rxConfig->RxSlot], frequency, dr );
+ }
+ else
+ {
+ MW_LOG( "RX on freq %d Hz at DR %d\n\r", frequency, dr );
+ }
+
+ *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, NvmCtx.Bands[NvmCtx.Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, NvmCtx.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( NvmCtx.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, 4000 );
+ }
+ else
+ {
+ modem = MODEM_LORA;
+ Radio.SetTxConfig( modem, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 4000 );
+ }
+ MW_LOG( "TX on freq %d Hz at DR %d\n\r", NvmCtx.Channels[txConfig->Channel].Frequency, txConfig->Datarate );
+ // 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( NvmCtx.Channels[i].Frequency != 0 )
+ {
+ chMask |= 1 << i;
+ }
+ }
+ else
+ {
+ if( ( ( chMask & ( 1 << i ) ) != 0 ) &&
+ ( NvmCtx.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 = NvmCtx.Channels;
+ linkAdrVerifyParams.MinTxPower = AS923_MIN_TX_POWER;
+ linkAdrVerifyParams.MaxTxPower = AS923_MAX_TX_POWER;
+ linkAdrVerifyParams.Version = linkAdrReq->Version;
+
+ // 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
+ memset1( ( uint8_t* ) NvmCtx.ChannelsMask, 0, sizeof( NvmCtx.ChannelsMask ) );
+ // Update the channels mask
+ NvmCtx.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( VerifyRfFreq( 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( VerifyRfFreq( dlChannelReq->Rx1Frequency ) == false )
+ {
+ status &= 0xFE;
+ }
+
+ // Verify if an uplink frequency exists
+ if( NvmCtx.Channels[dlChannelReq->ChannelId].Frequency == 0 )
+ {
+ status &= 0xFD;
+ }
+
+ // Apply Rx1 frequency, if the status is OK
+ if( status == 0x03 )
+ {
+ NvmCtx.Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency;
+ }
+
+ return status;
+}
+
+int8_t RegionAS923AlternateDr( int8_t currentDr, AlternateDrType_t type )
+{
+ // Only AS923_DWELL_LIMIT_DATARATE is supported
+ return AS923_DWELL_LIMIT_DATARATE;
+}
+
+void RegionAS923CalcBackOff( CalcBackOffParams_t* calcBackOff )
+{
+ RegionCommonCalcBackOffParams_t calcBackOffParams;
+
+ calcBackOffParams.Channels = NvmCtx.Channels;
+ calcBackOffParams.Bands = NvmCtx.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 );
+}
+
+LoRaMacStatus_t 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( NvmCtx.ChannelsMask, 0, 1 ) == 0 )
+ { // Reactivate default channels
+ NvmCtx.ChannelsMask[0] |= LC( 1 ) + LC( 2 );
+ }
+
+ TimerTime_t elapsed = TimerGetElapsedTime( nextChanParams->LastAggrTx );
+ if( ( nextChanParams->LastAggrTx == 0 ) || ( nextChanParams->AggrTimeOff <= elapsed ) )
+ {
+ // Reset Aggregated time off
+ *aggregatedTimeOff = 0;
+
+ // Update bands Time OFF
+ nextTxDelay = RegionCommonUpdateBandTimeOff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, NvmCtx.Bands, AS923_MAX_NB_BANDS );
+
+ // Search how many channels are enabled
+ nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Joined, nextChanParams->Datarate,
+ NvmCtx.ChannelsMask, NvmCtx.Channels,
+ NvmCtx.Bands, enabledChannels, &delayTx );
+ }
+ else
+ {
+ delayTx++;
+ nextTxDelay = nextChanParams->AggrTimeOff - elapsed;
+ }
+
+ 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, NvmCtx.Channels[channelNext].Frequency, AS923_RSSI_FREE_TH, AS923_CARRIER_SENSE_TIME ) == true )
+ {
+ // Free channel found
+ *channel = channelNext;
+ *time = 0;
+ return LORAMAC_STATUS_OK;
+ }
+ }
+ return LORAMAC_STATUS_NO_FREE_CHANNEL_FOUND;
+ }
+ else
+ {
+ if( delayTx > 0 )
+ {
+ // Delay transmission due to AggregatedTimeOff or to a band time off
+ *time = nextTxDelay;
+ return LORAMAC_STATUS_DUTYCYCLE_RESTRICTED;
+ }
+ // Datarate not supported by any channel, restore defaults
+ NvmCtx.ChannelsMask[0] |= LC( 1 ) + LC( 2 );
+ *time = 0;
+ return LORAMAC_STATUS_NO_CHANNEL_FOUND;
+ }
+}
+
+LoRaMacStatus_t RegionAS923ChannelAdd( ChannelAddParams_t* channelAdd )
+{
+ bool drInvalid = false;
+ bool freqInvalid = false;
+ uint8_t id = channelAdd->ChannelId;
+
+ if( id < AS923_NUMB_DEFAULT_CHANNELS )
+ {
+ return LORAMAC_STATUS_FREQ_AND_DR_INVALID;
+ }
+
+ 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;
+ }
+
+ // Check frequency
+ if( freqInvalid == false )
+ {
+ if( VerifyRfFreq( 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;
+ }
+
+ memcpy1( ( uint8_t* ) &(NvmCtx.Channels[id]), ( uint8_t* ) channelAdd->NewChannel, sizeof( NvmCtx.Channels[id] ) );
+ NvmCtx.Channels[id].Band = 0;
+ NvmCtx.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
+ NvmCtx.Channels[id] = ( ChannelParams_t ){ 0, 0, { 0 }, 0 };
+
+ return RegionCommonChanDisable( NvmCtx.ChannelsMask, id, AS923_MAX_NB_CHANNELS );
+}
+
+void RegionAS923SetContinuousWave( ContinuousWaveParams_t* continuousWave )
+{
+ int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, NvmCtx.Bands[NvmCtx.Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, NvmCtx.ChannelsMask );
+ int8_t phyTxPower = 0;
+ uint32_t frequency = NvmCtx.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 )
+{
+ RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup;
+
+ regionCommonRxBeaconSetup.Datarates = DataratesAS923;
+ regionCommonRxBeaconSetup.Frequency = rxBeaconSetup->Frequency;
+ regionCommonRxBeaconSetup.BeaconSize = AS923_BEACON_SIZE;
+ regionCommonRxBeaconSetup.BeaconDatarate = AS923_BEACON_CHANNEL_DR;
+ regionCommonRxBeaconSetup.BeaconChannelBW = AS923_BEACON_CHANNEL_BW;
+ regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime;
+ regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout;
+
+ RegionCommonRxBeaconSetup( ®ionCommonRxBeaconSetup );
+
+ // Store downlink datarate
+ *outDr = AS923_BEACON_CHANNEL_DR;
+}
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionAS923.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionAS923.h
new file mode 100644
index 00000000..ab0a3d33
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionAS923.h
@@ -0,0 +1,546 @@
+/*!
+ * \file RegionAS923.h
+ *
+ * \brief Region definition for AS923
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013-2017 Semtech
+ *
+ * ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ * embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Gregory Cristian ( Semtech )
+ *
+ * \author Daniel Jaeckle ( STACKFORCE )
+ *
+ * \author Johannes Bruder ( STACKFORCE )
+ *
+ * \defgroup REGIONAS923 Region AS923
+ * Implementation according to LoRaWAN Specification v1.0.2.
+ * \{
+ */
+#ifndef __REGION_AS923_H__
+#define __REGION_AS923_H__
+
+#include "region/Region.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 frequency
+ */
+#define AS923_BEACON_CHANNEL_FREQ 923400000
+
+/*!
+ * Payload size of a beacon frame
+ */
+#define AS923_BEACON_SIZE 17
+
+/*!
+ * Size of RFU 1 field
+ */
+#define AS923_RFU1_SIZE 2
+
+/*!
+ * Size of RFU 2 field
+ */
+#define AS923_RFU2_SIZE 0
+
+/*!
+ * Datarate of the beacon channel
+ */
+#define AS923_BEACON_CHANNEL_DR DR_3
+
+/*!
+ * Bandwith of the beacon channel
+ */
+#define AS923_BEACON_CHANNEL_BW 0
+
+/*!
+ * Ping slot channel datarate
+ */
+#define AS923_PING_SLOT_CHANNEL_DR DR_3
+
+/*!
+ * Maximum number of bands
+ */
+#define AS923_MAX_NB_BANDS 1
+
+/*!
+ * Band 0 definition
+ * { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff }
+ */
+#define AS923_BAND0 { 100, AS923_MAX_TX_POWER, 0, 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( InitDefaultsParams_t* params );
+
+/*!
+ * \brief Returns a pointer to the internal context and its size.
+ *
+ * \param [OUT] params Pointer to the function parameters.
+ *
+ * \retval Points to a structure where the module store its non-volatile context.
+ */
+void* RegionAS923GetNvmCtx( GetNvmCtxParams_t* params );
+
+/*!
+ * \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 );
+
+/*!
+ * 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] currentDr Current datarate.
+ *
+ * \retval Datarate to apply.
+ */
+int8_t RegionAS923AlternateDr( int8_t currentDr, AlternateDrType_t type );
+
+/*!
+ * \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]
+ */
+LoRaMacStatus_t 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 );
+
+/*!
+ * \brief Sets the radio into beacon reception mode
+ *
+ * \param [IN] rxBeaconSetup Pointer to the function parameters
+ */
+ void RegionAS923RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr );
+
+/*! \} defgroup REGIONAS923 */
+
+#endif // __REGION_AS923_H__
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionAU915.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionAU915.c
new file mode 100644
index 00000000..30b89860
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionAU915.c
@@ -0,0 +1,1037 @@
+/*!
+ * \file RegionAU915.c
+ *
+ * \brief Region implementation for AU915
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013-2017 Semtech
+ *
+ * ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ * embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Gregory Cristian ( Semtech )
+ *
+ * \author Daniel Jaeckle ( STACKFORCE )
+*/
+#include "utilities.h"
+
+#include "RegionCommon.h"
+#include "RegionAU915.h"
+#include "lorawan_conf.h"
+#include "mw_log_conf.h"
+
+// Definitions
+#define CHANNELS_MASK_SIZE 6
+
+// A mask to select only valid 500KHz channels
+#define CHANNELS_MASK_500KHZ_MASK 0x00FF
+
+/*!
+ * Region specific context
+ */
+typedef struct sRegionAU915NvmCtx
+{
+ /*!
+ * LoRaMAC channels
+ */
+ ChannelParams_t Channels[ AU915_MAX_NB_CHANNELS ];
+ /*!
+ * LoRaMac bands
+ */
+ Band_t Bands[ AU915_MAX_NB_BANDS ];
+ /*!
+ * LoRaMac channels mask
+ */
+ uint16_t ChannelsMask[ CHANNELS_MASK_SIZE ];
+ /*!
+ * LoRaMac channels remaining
+ */
+ uint16_t ChannelsMaskRemaining[CHANNELS_MASK_SIZE];
+ /*!
+ * LoRaMac channels default mask
+ */
+ uint16_t ChannelsDefaultMask[ CHANNELS_MASK_SIZE ];
+}RegionAU915NvmCtx_t;
+
+/*
+ * Non-volatile module context.
+ */
+static RegionAU915NvmCtx_t NvmCtx;
+
+// 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_8 )
+ { // DR_7 is not allowed
+ nextLowerDr = DR_6;
+ }
+ 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 bool VerifyRfFreq( uint32_t freq )
+{
+ // Check radio driver support
+ if( Radio.CheckRfFrequency( freq ) == false )
+ {
+ return false;
+ }
+
+ // Rx frequencies
+ if( ( freq < AU915_FIRST_RX1_CHANNEL ) ||
+ ( freq > AU915_LAST_RX1_CHANNEL ) ||
+ ( ( ( freq - ( uint32_t ) AU915_FIRST_RX1_CHANNEL ) % ( uint32_t ) AU915_STEPWIDTH_RX1_CHANNEL ) != 0 ) )
+ {
+ return false;
+ }
+
+ // Tx frequencies for 125kHz
+ // Also includes the range for 500kHz channels
+ if( ( freq < 915200000 ) || ( freq > 927800000 ) )
+ {
+ return false;
+ }
+ return true;
+}
+
+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;
+}
+
+PhyParam_t RegionAU915GetPhyParam( GetPhyParams_t* getPhy )
+{
+ PhyParam_t phyParam = { 0 };
+
+ switch( getPhy->Attribute )
+ {
+ case PHY_MIN_RX_DR:
+ {
+ if( getPhy->DownlinkDwellTime == 0)
+ {
+ phyParam.Value = AU915_RX_MIN_DATARATE;
+ }
+ else
+ {
+ phyParam.Value = AU915_DWELL_LIMIT_DATARATE;
+ }
+ break;
+ }
+ case PHY_MIN_TX_DR:
+ {
+ if( getPhy->UplinkDwellTime == 0)
+ {
+ phyParam.Value = AU915_TX_MIN_DATARATE;
+ }
+ else
+ {
+ phyParam.Value = AU915_DWELL_LIMIT_DATARATE;
+ }
+ break;
+ }
+ case PHY_DEF_TX_DR:
+ {
+ phyParam.Value = AU915_DEFAULT_DATARATE;
+ break;
+ }
+ case PHY_NEXT_LOWER_TX_DR:
+ {
+ if( getPhy->UplinkDwellTime == 0)
+ {
+ phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, AU915_TX_MIN_DATARATE );
+ }
+ else
+ {
+ phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, AU915_DWELL_LIMIT_DATARATE );
+ }
+ break;
+ }
+ case PHY_MAX_TX_POWER:
+ {
+ phyParam.Value = AU915_MAX_TX_POWER;
+ break;
+ }
+ case PHY_DEF_TX_POWER:
+ {
+ phyParam.Value = AU915_DEFAULT_TX_POWER;
+ break;
+ }
+ case PHY_DEF_ADR_ACK_LIMIT:
+ {
+ phyParam.Value = AU915_ADR_ACK_LIMIT;
+ break;
+ }
+ case PHY_DEF_ADR_ACK_DELAY:
+ {
+ phyParam.Value = AU915_ADR_ACK_DELAY;
+ break;
+ }
+ case PHY_MAX_PAYLOAD:
+ {
+ if( getPhy->UplinkDwellTime == 0 )
+ {
+ phyParam.Value = MaxPayloadOfDatarateDwell0AU915[getPhy->Datarate];
+ }
+ else
+ {
+ phyParam.Value = MaxPayloadOfDatarateDwell1AU915[getPhy->Datarate];
+ }
+ break;
+ }
+ case PHY_MAX_PAYLOAD_REPEATER:
+ {
+ if( getPhy->UplinkDwellTime == 0)
+ {
+ phyParam.Value = MaxPayloadOfDatarateRepeaterDwell0AU915[getPhy->Datarate];
+ }
+ else
+ {
+ phyParam.Value = MaxPayloadOfDatarateRepeaterDwell1AU915[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 = NvmCtx.ChannelsMask;
+ break;
+ }
+ case PHY_CHANNELS_DEFAULT_MASK:
+ {
+ phyParam.ChannelsMask = NvmCtx.ChannelsDefaultMask;
+ break;
+ }
+ case PHY_MAX_NB_CHANNELS:
+ {
+ phyParam.Value = AU915_MAX_NB_CHANNELS;
+ break;
+ }
+ case PHY_CHANNELS:
+ {
+ phyParam.Channels = NvmCtx.Channels;
+ break;
+ }
+ case PHY_DEF_UPLINK_DWELL_TIME:
+ {
+ phyParam.Value = AU915_DEFAULT_UPLINK_DWELL_TIME;
+ break;
+ }
+ case PHY_DEF_DOWNLINK_DWELL_TIME:
+ {
+ phyParam.Value = AU915_DEFAULT_DOWNLINK_DWELL_TIME;
+ 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_BEACON_FORMAT:
+ {
+ phyParam.BeaconFormat.BeaconSize = AU915_BEACON_SIZE;
+ phyParam.BeaconFormat.Rfu1Size = AU915_RFU1_SIZE;
+ phyParam.BeaconFormat.Rfu2Size = AU915_RFU2_SIZE;
+ break;
+ }
+ case PHY_BEACON_CHANNEL_DR:
+ {
+ phyParam.Value = AU915_BEACON_CHANNEL_DR;
+ break;
+ }
+ case PHY_BEACON_CHANNEL_STEPWIDTH:
+ {
+ phyParam.Value = AU915_BEACON_CHANNEL_STEPWIDTH;
+ break;
+ }
+ case PHY_BEACON_NB_CHANNELS:
+ {
+ phyParam.Value = AU915_BEACON_NB_CHANNELS;
+ break;
+ }
+ case PHY_PING_SLOT_CHANNEL_DR:
+ {
+ phyParam.Value = AU915_PING_SLOT_CHANNEL_DR;
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ return phyParam;
+}
+
+void RegionAU915SetBandTxDone( SetBandTxDoneParams_t* txDone )
+{
+ RegionCommonSetBandTxDone( txDone->Joined, &NvmCtx.Bands[NvmCtx.Channels[txDone->Channel].Band], txDone->LastTxDoneTime );
+}
+
+void RegionAU915InitDefaults( InitDefaultsParams_t* params )
+{
+ Band_t bands[AU915_MAX_NB_BANDS] =
+ {
+ AU915_BAND0
+ };
+
+ switch( params->Type )
+ {
+ case INIT_TYPE_INIT:
+ {
+ // Initialize bands
+ memcpy1( ( uint8_t* )NvmCtx.Bands, ( uint8_t* )bands, sizeof( Band_t ) * AU915_MAX_NB_BANDS );
+
+ // Channels
+ // 125 kHz channels
+ for( uint8_t i = 0; i < AU915_MAX_NB_CHANNELS - 8; i++ )
+ {
+ NvmCtx.Channels[i].Frequency = 915200000 + i * 200000;
+ NvmCtx.Channels[i].DrRange.Value = ( DR_5 << 4 ) | DR_0;
+ NvmCtx.Channels[i].Band = 0;
+ }
+ // 500 kHz channels
+ for( uint8_t i = AU915_MAX_NB_CHANNELS - 8; i < AU915_MAX_NB_CHANNELS; i++ )
+ {
+ NvmCtx.Channels[i].Frequency = 915900000 + ( i - ( AU915_MAX_NB_CHANNELS - 8 ) ) * 1600000;
+ NvmCtx.Channels[i].DrRange.Value = ( DR_6 << 4 ) | DR_6;
+ NvmCtx.Channels[i].Band = 0;
+ }
+
+ // Initialize channels default mask
+ NvmCtx.ChannelsDefaultMask[0] = 0xFFFF;
+ NvmCtx.ChannelsDefaultMask[1] = 0xFFFF;
+ NvmCtx.ChannelsDefaultMask[2] = 0xFFFF;
+ NvmCtx.ChannelsDefaultMask[3] = 0xFFFF;
+ NvmCtx.ChannelsDefaultMask[4] = 0x00FF;
+ NvmCtx.ChannelsDefaultMask[5] = 0x0000;
+
+ // Copy channels default mask
+ RegionCommonChanMaskCopy( NvmCtx.ChannelsMask, NvmCtx.ChannelsDefaultMask, 6 );
+
+ // Copy into channels mask remaining
+ RegionCommonChanMaskCopy( NvmCtx.ChannelsMaskRemaining, NvmCtx.ChannelsMask, 6 );
+ break;
+ }
+ case INIT_TYPE_RESTORE_CTX:
+ {
+ if( params->NvmCtx != 0 )
+ {
+ memcpy1( (uint8_t*) &NvmCtx, (uint8_t*) params->NvmCtx, sizeof( NvmCtx ) );
+ }
+ break;
+ }
+ case INIT_TYPE_RESTORE_DEFAULT_CHANNELS:
+ {
+ // Copy channels default mask
+ RegionCommonChanMaskCopy( NvmCtx.ChannelsMask, NvmCtx.ChannelsDefaultMask, 6 );
+
+ for( uint8_t i = 0; i < 6; i++ )
+ { // Copy-And the channels mask
+ NvmCtx.ChannelsMaskRemaining[i] &= NvmCtx.ChannelsMask[i];
+ }
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+void* RegionAU915GetNvmCtx( GetNvmCtxParams_t* params )
+{
+ params->nvmCtxSize = sizeof( RegionAU915NvmCtx_t );
+ return &NvmCtx;
+}
+
+bool RegionAU915Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute )
+{
+ switch( phyAttribute )
+ {
+ case PHY_FREQUENCY:
+ {
+ return VerifyRfFreq( verify->Frequency );
+ }
+ case PHY_TX_DR:
+ case PHY_DEF_TX_DR:
+ {
+ if( verify->DatarateParams.UplinkDwellTime == 0 )
+ {
+ return RegionCommonValueInRange( verify->DatarateParams.Datarate, AU915_TX_MIN_DATARATE, AU915_TX_MAX_DATARATE );
+ }
+ else
+ {
+ return RegionCommonValueInRange( verify->DatarateParams.Datarate, AU915_DWELL_LIMIT_DATARATE, AU915_TX_MAX_DATARATE );
+ }
+ }
+ case PHY_RX_DR:
+ {
+ if( verify->DatarateParams.UplinkDwellTime == 0 )
+ {
+ return RegionCommonValueInRange( verify->DatarateParams.Datarate, AU915_RX_MIN_DATARATE, AU915_RX_MAX_DATARATE );
+ }
+ else
+ {
+ return RegionCommonValueInRange( verify->DatarateParams.Datarate, AU915_DWELL_LIMIT_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;
+ }
+ default:
+ return false;
+ }
+}
+
+void RegionAU915ApplyCFList( ApplyCFListParams_t* applyCFList )
+{
+ // Size of the optional CF list must be 16 byte
+ if( applyCFList->Size != 16 )
+ {
+ return;
+ }
+
+ // Last byte CFListType must be 0x01 to indicate the CFList contains a series of ChMask fields
+ if( applyCFList->Payload[15] != 0x01 )
+ {
+ return;
+ }
+
+ // ChMask0 - ChMask4 must be set (every ChMask has 16 bit)
+ for( uint8_t chMaskItr = 0, cntPayload = 0; chMaskItr <= 4; chMaskItr++, cntPayload+=2 )
+ {
+ NvmCtx.ChannelsMask[chMaskItr] = (uint16_t) (0x00FF & applyCFList->Payload[cntPayload]);
+ NvmCtx.ChannelsMask[chMaskItr] |= (uint16_t) (applyCFList->Payload[cntPayload+1] << 8);
+ if( chMaskItr == 4 )
+ {
+ NvmCtx.ChannelsMask[chMaskItr] = NvmCtx.ChannelsMask[chMaskItr] & CHANNELS_MASK_500KHZ_MASK;
+ }
+ // Set the channel mask to the remaining
+ NvmCtx.ChannelsMaskRemaining[chMaskItr] &= NvmCtx.ChannelsMask[chMaskItr];
+ }
+}
+
+bool RegionAU915ChanMaskSet( ChanMaskSetParams_t* chanMaskSet )
+{
+ switch( chanMaskSet->ChannelsMaskType )
+ {
+ case CHANNELS_MASK:
+ {
+ RegionCommonChanMaskCopy( NvmCtx.ChannelsMask, chanMaskSet->ChannelsMaskIn, 6 );
+
+ NvmCtx.ChannelsDefaultMask[4] = NvmCtx.ChannelsDefaultMask[4] & CHANNELS_MASK_500KHZ_MASK;
+ NvmCtx.ChannelsDefaultMask[5] = 0x0000;
+
+ for( uint8_t i = 0; i < 6; i++ )
+ { // Copy-And the channels mask
+ NvmCtx.ChannelsMaskRemaining[i] &= NvmCtx.ChannelsMask[i];
+ }
+ break;
+ }
+ case CHANNELS_DEFAULT_MASK:
+ {
+ RegionCommonChanMaskCopy( NvmCtx.ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 6 );
+ break;
+ }
+ default:
+ return false;
+ }
+ return true;
+}
+
+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.GetWakeupTime( ), &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;
+ const char *slotStrings[] = { "1", "2", "C", "Multi_C", "P", "Multi_P" };
+
+ if( Radio.GetStatus( ) != RF_IDLE )
+ {
+ return false;
+ }
+
+ if( rxConfig->RxSlot == RX_SLOT_WIN_1 )
+ {
+ // 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 = MaxPayloadOfDatarateRepeaterDwell0AU915[dr];
+ }
+ else
+ {
+ maxPayload = MaxPayloadOfDatarateDwell0AU915[dr];
+ }
+ Radio.SetMaxPayloadLength( MODEM_LORA, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD );
+ MW_LOG( "RX on freq %d Hz at DR %d\n\r", frequency, dr );
+ if ( rxConfig->RxSlot < RX_SLOT_NONE )
+ {
+ MW_LOG( "RX_%s on freq %d Hz at DR %d\n\r", slotStrings[rxConfig->RxSlot], frequency, dr );
+ }
+ else
+ {
+ MW_LOG( "RX on freq %d Hz at DR %d\n\r", frequency, dr );
+ }
+
+ *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, NvmCtx.Bands[NvmCtx.Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, NvmCtx.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( NvmCtx.Channels[txConfig->Channel].Frequency );
+
+ Radio.SetTxConfig( MODEM_LORA, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 3000 );
+ MW_LOG( "TX on freq %d Hz at DR %d\n\r", NvmCtx.Channels[txConfig->Channel].Frequency, txConfig->Datarate );
+
+ // 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, NvmCtx.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 & CHANNELS_MASK_500KHZ_MASK;
+ }
+ 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 & CHANNELS_MASK_500KHZ_MASK;
+ }
+ else if( linkAdrParams.ChMaskCtrl == 5 )
+ {
+ // Start value for comparision
+ uint8_t bitMask = 1;
+
+ // cntChannelMask for channelsMask[0] until channelsMask[3]
+ uint8_t cntChannelMask = 0;
+
+ // i will be 1, 2, 3, ..., 7
+ for( uint8_t i = 0; i <= 7; i++ )
+ {
+ // 8 MSBs of ChMask are RFU
+ // Checking if the ChMask is set, then true
+ if( ( ( linkAdrParams.ChMask & 0x00FF ) & ( bitMask << i ) ) != 0 )
+ {
+ if( ( i % 2 ) == 0 )
+ {
+ // Enable a bank of 8 125kHz channels, 8 LSBs
+ channelsMask[cntChannelMask] |= 0x00FF;
+ // Enable the corresponding 500kHz channel
+ channelsMask[4] |= ( bitMask << i );
+ }
+ else
+ {
+ // Enable a bank of 8 125kHz channels, 8 MSBs
+ channelsMask[cntChannelMask] |= 0xFF00;
+ // Enable the corresponding 500kHz channel
+ channelsMask[4] |= ( bitMask << i );
+ // cntChannelMask increment for uneven i
+ cntChannelMask++;
+ }
+ }
+ // ChMask is not set
+ else
+ {
+ if( ( i % 2 ) == 0 )
+ {
+ // Disable a bank of 8 125kHz channels, 8 LSBs
+ channelsMask[cntChannelMask] &= 0xFF00;
+ // Disable the corresponding 500kHz channel
+ channelsMask[4] &= ~( bitMask << i );
+ }
+ else
+ {
+ // Enable a bank of 8 125kHz channels, 8 MSBs
+ channelsMask[cntChannelMask] &= 0x00FF;
+ // Disable the corresponding 500kHz channel
+ channelsMask[4] &= ~( bitMask << i );
+ // cntChannelMask increment for uneven i
+ cntChannelMask++;
+ }
+ }
+ }
+ }
+ 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 = NvmCtx.Channels;
+ linkAdrVerifyParams.MinTxPower = AU915_MIN_TX_POWER;
+ linkAdrVerifyParams.MaxTxPower = AU915_MAX_TX_POWER;
+ linkAdrVerifyParams.Version = linkAdrReq->Version;
+
+ // 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( NvmCtx.ChannelsMask, channelsMask, 6 );
+
+ NvmCtx.ChannelsMaskRemaining[0] &= NvmCtx.ChannelsMask[0];
+ NvmCtx.ChannelsMaskRemaining[1] &= NvmCtx.ChannelsMask[1];
+ NvmCtx.ChannelsMaskRemaining[2] &= NvmCtx.ChannelsMask[2];
+ NvmCtx.ChannelsMaskRemaining[3] &= NvmCtx.ChannelsMask[3];
+ NvmCtx.ChannelsMaskRemaining[4] = NvmCtx.ChannelsMask[4];
+ NvmCtx.ChannelsMaskRemaining[5] = NvmCtx.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;
+
+ // Verify radio frequency
+ if( VerifyRfFreq( rxParamSetupReq->Frequency ) == false )
+ {
+ 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 )
+{
+ // Accept the request
+ return 0;
+}
+
+uint8_t RegionAU915DlChannelReq( DlChannelReqParams_t* dlChannelReq )
+{
+ return 0;
+}
+
+int8_t RegionAU915AlternateDr( int8_t currentDr, AlternateDrType_t type )
+{
+ static int8_t trialsCount = 0;
+
+ // Re-enable 500 kHz default channels
+ NvmCtx.ChannelsMask[4] = CHANNELS_MASK_500KHZ_MASK;
+
+ if( ( trialsCount & 0x01 ) == 0x01 )
+ {
+ currentDr = DR_6;
+ }
+ else
+ {
+ currentDr = DR_2;
+ }
+ trialsCount++;
+ return currentDr;
+}
+
+void RegionAU915CalcBackOff( CalcBackOffParams_t* calcBackOff )
+{
+ RegionCommonCalcBackOffParams_t calcBackOffParams;
+
+ calcBackOffParams.Channels = NvmCtx.Channels;
+ calcBackOffParams.Bands = NvmCtx.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 );
+}
+
+LoRaMacStatus_t 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( NvmCtx.ChannelsMaskRemaining, 0, 4 ) == 0 )
+ { // Reactivate default channels
+ RegionCommonChanMaskCopy( NvmCtx.ChannelsMaskRemaining, NvmCtx.ChannelsMask, 4 );
+ }
+ // Check other channels
+ if( nextChanParams->Datarate >= DR_6 )
+ {
+ if( ( NvmCtx.ChannelsMaskRemaining[4] & CHANNELS_MASK_500KHZ_MASK ) == 0 )
+ {
+ NvmCtx.ChannelsMaskRemaining[4] = NvmCtx.ChannelsMask[4];
+ }
+ }
+
+ TimerTime_t elapsed = TimerGetElapsedTime( nextChanParams->LastAggrTx );
+ if( ( nextChanParams->LastAggrTx == 0 ) || ( nextChanParams->AggrTimeOff <= elapsed ) )
+ {
+ // Reset Aggregated time off
+ *aggregatedTimeOff = 0;
+
+ // Update bands Time OFF
+ nextTxDelay = RegionCommonUpdateBandTimeOff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, NvmCtx.Bands, AU915_MAX_NB_BANDS );
+
+ // Search how many channels are enabled
+ nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Datarate,
+ NvmCtx.ChannelsMaskRemaining, NvmCtx.Channels,
+ NvmCtx.Bands, enabledChannels, &delayTx );
+ }
+ else
+ {
+ delayTx++;
+ nextTxDelay = nextChanParams->AggrTimeOff - elapsed;
+ }
+
+ if( nbEnabledChannels > 0 )
+ {
+ // We found a valid channel
+ *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )];
+ // Disable the channel in the mask
+ RegionCommonChanDisable( NvmCtx.ChannelsMaskRemaining, *channel, AU915_MAX_NB_CHANNELS - 8 );
+
+ *time = 0;
+ return LORAMAC_STATUS_OK;
+ }
+ else
+ {
+ if( delayTx > 0 )
+ {
+ // Delay transmission due to AggregatedTimeOff or to a band time off
+ *time = nextTxDelay;
+ return LORAMAC_STATUS_DUTYCYCLE_RESTRICTED;
+ }
+ // Datarate not supported by any channel
+ *time = 0;
+ return LORAMAC_STATUS_NO_CHANNEL_FOUND;
+ }
+}
+
+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, NvmCtx.Bands[NvmCtx.Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, NvmCtx.ChannelsMask );
+ int8_t phyTxPower = 0;
+ uint32_t frequency = NvmCtx.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 )
+ {
+ if( downlinkDwellTime == 0 )
+ {
+ datarate = AU915_TX_MIN_DATARATE;
+ }
+ else
+ {
+ datarate = AU915_DWELL_LIMIT_DATARATE;
+ }
+ }
+ return datarate;
+}
+
+void RegionAU915RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr )
+{
+ RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup;
+
+ regionCommonRxBeaconSetup.Datarates = DataratesAU915;
+ regionCommonRxBeaconSetup.Frequency = rxBeaconSetup->Frequency;
+ regionCommonRxBeaconSetup.BeaconSize = AU915_BEACON_SIZE;
+ regionCommonRxBeaconSetup.BeaconDatarate = AU915_BEACON_CHANNEL_DR;
+ regionCommonRxBeaconSetup.BeaconChannelBW = AU915_BEACON_CHANNEL_BW;
+ regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime;
+ regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout;
+
+ RegionCommonRxBeaconSetup( ®ionCommonRxBeaconSetup );
+
+ // Store downlink datarate
+ *outDr = AU915_BEACON_CHANNEL_DR;
+}
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionAU915.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionAU915.h
new file mode 100644
index 00000000..1af8c789
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionAU915.h
@@ -0,0 +1,540 @@
+/*!
+ * \file RegionAU915.h
+ *
+ * \brief Region definition for AU915
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013-2017 Semtech
+ *
+ * ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ * embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Gregory Cristian ( Semtech )
+ *
+ * \author Daniel Jaeckle ( STACKFORCE )
+ *
+ * \author Johannes Bruder ( STACKFORCE )
+ *
+ * \defgroup REGIONAU915 Region AU915
+ * Implementation according to LoRaWAN Specification v1.0.2.
+ * \{
+ */
+#ifndef __REGION_AU915_H__
+#define __REGION_AU915_H__
+
+#include "region/Region.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_13
+
+/*!
+ * 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_2
+
+/*!
+ * The minimum datarate which is used when the
+ * dwell time is limited.
+ */
+#define AU915_DWELL_LIMIT_DATARATE DR_2
+
+/*!
+ * 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_14
+
+/*!
+ * 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 uplink dwell time configuration
+ */
+#define AU915_DEFAULT_UPLINK_DWELL_TIME 1
+
+/*!
+ * Default downlink dwell time configuration
+ */
+#define AU915_DEFAULT_DOWNLINK_DWELL_TIME 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 frequency
+ */
+#define AU915_BEACON_CHANNEL_FREQ 923300000
+
+/*!
+ * Beacon frequency channel stepwidth
+ */
+#define AU915_BEACON_CHANNEL_STEPWIDTH 600000
+
+/*!
+ * Number of possible beacon channels
+ */
+#define AU915_BEACON_NB_CHANNELS 8
+
+/*!
+ * Payload size of a beacon frame
+ */
+#define AU915_BEACON_SIZE 19
+
+/*!
+ * Size of RFU 1 field
+ */
+#define AU915_RFU1_SIZE 3
+
+/*!
+ * Size of RFU 2 field
+ */
+#define AU915_RFU2_SIZE 1
+
+/*!
+ * Datarate of the beacon channel
+ */
+#define AU915_BEACON_CHANNEL_DR DR_10
+
+/*!
+ * Bandwith of the beacon channel
+ */
+#define AU915_BEACON_CHANNEL_BW 2
+
+/*!
+ * Ping slot channel datarate
+ */
+#define AU915_PING_SLOT_CHANNEL_DR DR_10
+
+/*!
+ * LoRaMac maximum number of bands
+ */
+#define AU915_MAX_NB_BANDS 1
+
+/*!
+ * Band 0 definition
+ * { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff }
+ */
+#define AU915_BAND0 { 1, AU915_MAX_TX_POWER, 0, 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.
+ * The table is valid for the dwell time configuration of 0 for uplinks.
+ */
+static const uint8_t MaxPayloadOfDatarateDwell0AU915[] = { 51, 51, 51, 115, 242, 242, 242, 0, 53, 129, 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. The table provides
+ * repeater support.
+ */
+static const uint8_t MaxPayloadOfDatarateRepeaterDwell0AU915[] = { 51, 51, 51, 115, 222, 222, 222, 0, 33, 109, 222, 222, 222, 222 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Cannot operate with repeater.
+ * The table is valid for the dwell time configuration of 1 for uplinks.
+ */
+static const uint8_t MaxPayloadOfDatarateDwell1AU915[] = { 0, 0, 11, 53, 125, 242, 242, 0, 53, 129, 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 1 for uplinks. The table provides
+ * repeater support.
+ */
+static const uint8_t MaxPayloadOfDatarateRepeaterDwell1AU915[] = { 0, 0, 11, 53, 125, 242, 242, 0, 33, 109, 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 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( InitDefaultsParams_t* params );
+
+/*!
+ * \brief Returns a pointer to the internal context and its size.
+ *
+ * \param [OUT] params Pointer to the function parameters.
+ *
+ * \retval Points to a structure where the module store its non-volatile context.
+ */
+void* RegionAU915GetNvmCtx( GetNvmCtxParams_t* params );
+
+/*!
+ * \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 );
+
+/*!
+ * 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] currentDr Current datarate.
+ *
+ * \retval Datarate to apply.
+ */
+int8_t RegionAU915AlternateDr( int8_t currentDr, AlternateDrType_t type );
+
+/*!
+ * \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]
+ */
+LoRaMacStatus_t 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 );
+
+/*!
+ * \brief Sets the radio into beacon reception mode
+ *
+ * \param [IN] rxBeaconSetup Pointer to the function parameters
+ */
+ void RegionAU915RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr );
+
+/*! \} defgroup REGIONAU915 */
+
+#endif // __REGION_AU915_H__
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCN470.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCN470.c
new file mode 100644
index 00000000..975508eb
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCN470.c
@@ -0,0 +1,845 @@
+/*!
+ * \file RegionCN470.c
+ *
+ * \brief Region implementation for CN470
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013-2017 Semtech
+ *
+ * ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ * embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Gregory Cristian ( Semtech )
+ *
+ * \author Daniel Jaeckle ( STACKFORCE )
+*/
+#include "utilities.h"
+
+#include "RegionCommon.h"
+#include "RegionCN470.h"
+#include "lorawan_conf.h"
+#include "mw_log_conf.h"
+
+// Definitions
+#define CHANNELS_MASK_SIZE 6
+
+/*!
+ * Region specific context
+ */
+typedef struct sRegionCN470NvmCtx
+{
+ /*!
+ * LoRaMAC channels
+ */
+ ChannelParams_t Channels[ CN470_MAX_NB_CHANNELS ];
+ /*!
+ * LoRaMac bands
+ */
+ Band_t Bands[ CN470_MAX_NB_BANDS ];
+ /*!
+ * LoRaMac channels mask
+ */
+ uint16_t ChannelsMask[ CHANNELS_MASK_SIZE ];
+ /*!
+ * LoRaMac channels default mask
+ */
+ uint16_t ChannelsDefaultMask[ CHANNELS_MASK_SIZE ];
+}RegionCN470NvmCtx_t;
+
+/*
+ * Non-volatile module context.
+ */
+static RegionCN470NvmCtx_t NvmCtx;
+
+// 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 bool VerifyRfFreq( uint32_t freq )
+{
+ // Check radio driver support
+ if( Radio.CheckRfFrequency( freq ) == false )
+ {
+ return false;
+ }
+
+ // Rx frequencies
+ if( ( freq < CN470_FIRST_RX1_CHANNEL ) ||
+ ( freq > CN470_LAST_RX1_CHANNEL ) ||
+ ( ( ( freq - ( uint32_t ) CN470_FIRST_RX1_CHANNEL ) % ( uint32_t ) CN470_STEPWIDTH_RX1_CHANNEL ) != 0 ) )
+ {
+ return false;
+ }
+
+ // Test for frequency range - take RX and TX freqencies into account
+ if( ( freq < 470300000 ) || ( freq > 509700000 ) )
+ {
+ return false;
+ }
+ return true;
+}
+
+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;
+}
+
+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_MAX_TX_POWER:
+ {
+ phyParam.Value = CN470_MAX_TX_POWER;
+ break;
+ }
+ case PHY_DEF_TX_POWER:
+ {
+ phyParam.Value = CN470_DEFAULT_TX_POWER;
+ break;
+ }
+ case PHY_DEF_ADR_ACK_LIMIT:
+ {
+ phyParam.Value = CN470_ADR_ACK_LIMIT;
+ break;
+ }
+ case PHY_DEF_ADR_ACK_DELAY:
+ {
+ phyParam.Value = CN470_ADR_ACK_DELAY;
+ 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 = NvmCtx.ChannelsMask;
+ break;
+ }
+ case PHY_CHANNELS_DEFAULT_MASK:
+ {
+ phyParam.ChannelsMask = NvmCtx.ChannelsDefaultMask;
+ break;
+ }
+ case PHY_MAX_NB_CHANNELS:
+ {
+ phyParam.Value = CN470_MAX_NB_CHANNELS;
+ break;
+ }
+ case PHY_CHANNELS:
+ {
+ phyParam.Channels = NvmCtx.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_BEACON_FORMAT:
+ {
+ phyParam.BeaconFormat.BeaconSize = CN470_BEACON_SIZE;
+ phyParam.BeaconFormat.Rfu1Size = CN470_RFU1_SIZE;
+ phyParam.BeaconFormat.Rfu2Size = CN470_RFU2_SIZE;
+ break;
+ }
+ case PHY_BEACON_CHANNEL_DR:
+ {
+ phyParam.Value = CN470_BEACON_CHANNEL_DR;
+ break;
+ }
+ case PHY_BEACON_CHANNEL_STEPWIDTH:
+ {
+ phyParam.Value = CN470_BEACON_CHANNEL_STEPWIDTH;
+ break;
+ }
+ case PHY_BEACON_NB_CHANNELS:
+ {
+ phyParam.Value = CN470_BEACON_NB_CHANNELS;
+ break;
+ }
+ case PHY_PING_SLOT_CHANNEL_DR:
+ {
+ phyParam.Value = CN470_PING_SLOT_CHANNEL_DR;
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ return phyParam;
+}
+
+void RegionCN470SetBandTxDone( SetBandTxDoneParams_t* txDone )
+{
+ RegionCommonSetBandTxDone( txDone->Joined, &NvmCtx.Bands[NvmCtx.Channels[txDone->Channel].Band], txDone->LastTxDoneTime );
+}
+
+void RegionCN470InitDefaults( InitDefaultsParams_t* params )
+{
+ Band_t bands[CN470_MAX_NB_BANDS] =
+ {
+ CN470_BAND0
+ };
+
+ switch( params->Type )
+ {
+ case INIT_TYPE_INIT:
+ {
+ // Initialize bands
+ memcpy1( ( uint8_t* )NvmCtx.Bands, ( uint8_t* )bands, sizeof( Band_t ) * CN470_MAX_NB_BANDS );
+
+ // Channels
+ // 125 kHz channels
+ for( uint8_t i = 0; i < CN470_MAX_NB_CHANNELS; i++ )
+ {
+ NvmCtx.Channels[i].Frequency = 470300000 + i * 200000;
+ NvmCtx.Channels[i].DrRange.Value = ( DR_5 << 4 ) | DR_0;
+ NvmCtx.Channels[i].Band = 0;
+ }
+
+ // Initialize the channels default mask
+ NvmCtx.ChannelsDefaultMask[0] = 0xFFFF;
+ NvmCtx.ChannelsDefaultMask[1] = 0xFFFF;
+ NvmCtx.ChannelsDefaultMask[2] = 0xFFFF;
+ NvmCtx.ChannelsDefaultMask[3] = 0xFFFF;
+ NvmCtx.ChannelsDefaultMask[4] = 0xFFFF;
+ NvmCtx.ChannelsDefaultMask[5] = 0xFFFF;
+
+ // Update the channels mask
+ RegionCommonChanMaskCopy( NvmCtx.ChannelsMask, NvmCtx.ChannelsDefaultMask, 6 );
+ break;
+ }
+ case INIT_TYPE_RESTORE_CTX:
+ {
+ if( params->NvmCtx != 0 )
+ {
+ memcpy1( (uint8_t*) &NvmCtx, (uint8_t*) params->NvmCtx, sizeof( NvmCtx ) );
+ }
+ break;
+ }
+ case INIT_TYPE_RESTORE_DEFAULT_CHANNELS:
+ {
+ // Restore channels default mask
+ RegionCommonChanMaskCopy( NvmCtx.ChannelsMask, NvmCtx.ChannelsDefaultMask, 6 );
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+void* RegionCN470GetNvmCtx( GetNvmCtxParams_t* params )
+{
+ params->nvmCtxSize = sizeof( RegionCN470NvmCtx_t );
+ return &NvmCtx;
+}
+
+bool RegionCN470Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute )
+{
+ switch( phyAttribute )
+ {
+ case PHY_FREQUENCY:
+ {
+ return VerifyRfFreq( verify->Frequency );
+ }
+ 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;
+ }
+ default:
+ return false;
+ }
+}
+
+void RegionCN470ApplyCFList( ApplyCFListParams_t* applyCFList )
+{
+ // Size of the optional CF list must be 16 byte
+ if( applyCFList->Size != 16 )
+ {
+ return;
+ }
+
+ // Last byte CFListType must be 0x01 to indicate the CFList contains a series of ChMask fields
+ if( applyCFList->Payload[15] != 0x01 )
+ {
+ return;
+ }
+
+ // ChMask0 - ChMask5 must be set (every ChMask has 16 bit)
+ for( uint8_t chMaskItr = 0, cntPayload = 0; chMaskItr <= 5; chMaskItr++, cntPayload+=2 )
+ {
+ NvmCtx.ChannelsMask[chMaskItr] = (uint16_t) (0x00FF & applyCFList->Payload[cntPayload]);
+ NvmCtx.ChannelsMask[chMaskItr] |= (uint16_t) (applyCFList->Payload[cntPayload+1] << 8);
+ }
+}
+
+bool RegionCN470ChanMaskSet( ChanMaskSetParams_t* chanMaskSet )
+{
+ switch( chanMaskSet->ChannelsMaskType )
+ {
+ case CHANNELS_MASK:
+ {
+ RegionCommonChanMaskCopy( NvmCtx.ChannelsMask, chanMaskSet->ChannelsMaskIn, 6 );
+ break;
+ }
+ case CHANNELS_DEFAULT_MASK:
+ {
+ RegionCommonChanMaskCopy( NvmCtx.ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 6 );
+ break;
+ }
+ default:
+ return false;
+ }
+ return true;
+}
+
+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.GetWakeupTime( ), &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;
+ const char *slotStrings[] = { "1", "2", "C", "Multi_C", "P", "Multi_P" };
+
+ if( Radio.GetStatus( ) != RF_IDLE )
+ {
+ return false;
+ }
+
+ if( rxConfig->RxSlot == RX_SLOT_WIN_1 )
+ {
+ // 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 );
+ if ( rxConfig->RxSlot < RX_SLOT_NONE )
+ {
+ MW_LOG( "RX_%s on freq %d Hz at DR %d\n\r", slotStrings[rxConfig->RxSlot], frequency, dr );
+ }
+ else
+ {
+ MW_LOG( "RX on freq %d Hz at DR %d\n\r", frequency, dr );
+ }
+
+ *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, NvmCtx.Bands[NvmCtx.Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, NvmCtx.ChannelsMask );
+ int8_t phyTxPower = 0;
+
+ // Calculate physical TX power
+ phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain );
+
+ // Setup the radio frequency
+ Radio.SetChannel( NvmCtx.Channels[txConfig->Channel].Frequency );
+
+ Radio.SetTxConfig( MODEM_LORA, phyTxPower, 0, 0, phyDr, 1, 8, false, true, 0, 0, false, 4000 );
+ MW_LOG( "TX on freq %d Hz at DR %d\n\r", NvmCtx.Channels[txConfig->Channel].Frequency, txConfig->Datarate );
+
+ // 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, NvmCtx.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 ) &&
+ ( NvmCtx.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 = NvmCtx.Channels;
+ linkAdrVerifyParams.MinTxPower = CN470_MIN_TX_POWER;
+ linkAdrVerifyParams.MaxTxPower = CN470_MAX_TX_POWER;
+ linkAdrVerifyParams.Version = linkAdrReq->Version;
+
+ // 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( NvmCtx.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;
+
+ // Verify radio frequency
+ if( VerifyRfFreq( rxParamSetupReq->Frequency ) == false )
+ {
+ 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( int8_t currentDr, AlternateDrType_t type )
+{
+ return currentDr;
+}
+
+void RegionCN470CalcBackOff( CalcBackOffParams_t* calcBackOff )
+{
+ RegionCommonCalcBackOffParams_t calcBackOffParams;
+
+ calcBackOffParams.Channels = NvmCtx.Channels;
+ calcBackOffParams.Bands = NvmCtx.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 );
+}
+
+LoRaMacStatus_t 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( NvmCtx.ChannelsMask, 0, 6 ) == 0 )
+ { // Reactivate default channels
+ NvmCtx.ChannelsMask[0] = 0xFFFF;
+ NvmCtx.ChannelsMask[1] = 0xFFFF;
+ NvmCtx.ChannelsMask[2] = 0xFFFF;
+ NvmCtx.ChannelsMask[3] = 0xFFFF;
+ NvmCtx.ChannelsMask[4] = 0xFFFF;
+ NvmCtx.ChannelsMask[5] = 0xFFFF;
+ }
+
+ TimerTime_t elapsed = TimerGetElapsedTime( nextChanParams->LastAggrTx );
+ if( ( nextChanParams->LastAggrTx == 0 ) || ( nextChanParams->AggrTimeOff <= elapsed ) )
+ {
+ // Reset Aggregated time off
+ *aggregatedTimeOff = 0;
+
+ // Update bands Time OFF
+ nextTxDelay = RegionCommonUpdateBandTimeOff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, NvmCtx.Bands, CN470_MAX_NB_BANDS );
+
+ // Search how many channels are enabled
+ nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Datarate,
+ NvmCtx.ChannelsMask, NvmCtx.Channels,
+ NvmCtx.Bands, enabledChannels, &delayTx );
+ }
+ else
+ {
+ delayTx++;
+ nextTxDelay = nextChanParams->AggrTimeOff - elapsed;
+ }
+
+ if( nbEnabledChannels > 0 )
+ {
+ // We found a valid channel
+ *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )];
+
+ *time = 0;
+ return LORAMAC_STATUS_OK;
+ }
+ else
+ {
+ if( delayTx > 0 )
+ {
+ // Delay transmission due to AggregatedTimeOff or to a band time off
+ *time = nextTxDelay;
+ return LORAMAC_STATUS_DUTYCYCLE_RESTRICTED;
+ }
+ // Datarate not supported by any channel
+ *time = 0;
+ return LORAMAC_STATUS_NO_CHANNEL_FOUND;
+ }
+}
+
+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, NvmCtx.Bands[NvmCtx.Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, NvmCtx.ChannelsMask );
+ int8_t phyTxPower = 0;
+ uint32_t frequency = NvmCtx.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 )
+{
+ RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup;
+
+ regionCommonRxBeaconSetup.Datarates = DataratesCN470;
+ regionCommonRxBeaconSetup.Frequency = rxBeaconSetup->Frequency;
+ regionCommonRxBeaconSetup.BeaconSize = CN470_BEACON_SIZE;
+ regionCommonRxBeaconSetup.BeaconDatarate = CN470_BEACON_CHANNEL_DR;
+ regionCommonRxBeaconSetup.BeaconChannelBW = CN470_BEACON_CHANNEL_BW;
+ regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime;
+ regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout;
+
+ RegionCommonRxBeaconSetup( ®ionCommonRxBeaconSetup );
+
+ // Store downlink datarate
+ *outDr = CN470_BEACON_CHANNEL_DR;
+}
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCN470.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCN470.h
new file mode 100644
index 00000000..0be4e736
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCN470.h
@@ -0,0 +1,494 @@
+/*!
+ * \file RegionCN470.h
+ *
+ * \brief Region definition for CN470
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013-2017 Semtech
+ *
+ * ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ * embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Gregory Cristian ( Semtech )
+ *
+ * \author Daniel Jaeckle ( STACKFORCE )
+ *
+ * \author Johannes Bruder ( STACKFORCE )
+ *
+ * \defgroup REGIONCN470 Region CN470
+ * Implementation according to LoRaWAN Specification v1.0.2.
+ * \{
+ */
+#ifndef __REGION_CN470_H__
+#define __REGION_CN470_H__
+
+#include "region/Region.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 frequency
+ */
+#define CN470_BEACON_CHANNEL_FREQ 508300000
+
+/*!
+ * Beacon frequency channel stepwidth
+ */
+#define CN470_BEACON_CHANNEL_STEPWIDTH 200000
+
+/*!
+ * Number of possible beacon channels
+ */
+#define CN470_BEACON_NB_CHANNELS 8
+
+/*!
+ * Payload size of a beacon frame
+ */
+#define CN470_BEACON_SIZE 19
+
+/*!
+ * Size of RFU 1 field
+ */
+#define CN470_RFU1_SIZE 3
+
+/*!
+ * Size of RFU 2 field
+ */
+#define CN470_RFU2_SIZE 1
+
+/*!
+ * Datarate of the beacon channel
+ */
+#define CN470_BEACON_CHANNEL_DR DR_2
+
+/*!
+ * Bandwith of the beacon channel
+ */
+#define CN470_BEACON_CHANNEL_BW 0
+
+/*!
+ * Ping slot channel datarate
+ */
+#define CN470_PING_SLOT_CHANNEL_DR DR_2
+
+/*!
+ * LoRaMac maximum number of bands
+ */
+#define CN470_MAX_NB_BANDS 1
+
+/*!
+ * Band 0 definition
+ * { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff }
+ */
+#define CN470_BAND0 { 1, CN470_MAX_TX_POWER, 0, 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, 242, 242 };
+
+/*!
+ * 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( InitDefaultsParams_t* params );
+
+/*!
+ * \brief Returns a pointer to the internal context and its size.
+ *
+ * \param [OUT] params Pointer to the function parameters.
+ *
+ * \retval Points to a structure where the module store its non-volatile context.
+ */
+void* RegionCN470GetNvmCtx( GetNvmCtxParams_t* params );
+
+/*!
+ * \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 );
+
+/*!
+ * 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] currentDr Current datarate.
+ *
+ * \retval Datarate to apply.
+ */
+int8_t RegionCN470AlternateDr( int8_t currentDr, AlternateDrType_t type );
+
+/*!
+ * \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]
+ */
+LoRaMacStatus_t 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 );
+
+/*!
+ * \brief Sets the radio into beacon reception mode
+ *
+ * \param [IN] rxBeaconSetup Pointer to the function parameters
+ */
+ void RegionCN470RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr );
+
+/*! \} defgroup REGIONCN470 */
+
+#endif // __REGION_CN470_H__
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCN470ALI.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCN470ALI.c
new file mode 100644
index 00000000..51b84776
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCN470ALI.c
@@ -0,0 +1,895 @@
+/*!
+ * \file RegionCN470ALIALI.c
+ *
+ * \brief Region implementation for CN470ALIALI
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013-2017 Semtech
+ *
+ * ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ * embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Gregory Cristian ( Semtech )
+ *
+ * \author Daniel Jaeckle ( STACKFORCE )
+*/
+#include "utilities.h"
+
+#include "RegionCommon.h"
+#include "RegionCN470ALI.h"
+#include "lorawan_conf.h"
+#include "mw_log_conf.h"
+
+// Definitions
+#define CHANNELS_MASK_SIZE 6
+
+/*!
+ * Region specific context
+ */
+typedef struct sRegionCN470ALINvmCtx
+{
+ /*!
+ * LoRaMAC channels
+ */
+ ChannelParams_t Channels[ CN470ALI_MAX_NB_CHANNELS ];
+ /*!
+ * LoRaMac bands
+ */
+ Band_t Bands[ CN470ALI_MAX_NB_BANDS ];
+ /*!
+ * LoRaMac channels mask
+ */
+ uint16_t ChannelsMask[ CHANNELS_MASK_SIZE ];
+ /*!
+ * LoRaMac channels default mask
+ */
+ uint16_t ChannelsDefaultMask[ CHANNELS_MASK_SIZE ];
+}RegionCN470ALINvmCtx_t;
+
+/*
+ * Non-volatile module context.
+ */
+static RegionCN470ALINvmCtx_t NvmCtx;
+
+// 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( BandwidthsCN470ALI[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 VerifyRfFreq( uint32_t freq )
+{
+ // Check radio driver support
+ if( Radio.CheckRfFrequency( freq ) == false )
+ {
+ return false;
+ }
+
+#if ((ALI_DOWNLINK_FREQ == ALI_FREQ470_S) || (ALI_DOWNLINK_FREQ == ALI_FREQ480_D))
+ // Rx frequencies
+ if((freq < 503500000) && (freq >476500000))
+ {
+ return false;
+ }
+#endif
+
+ // Rx frequencies
+ if( ( freq < CN470ALI_FIRST_RX1_CHANNEL ) ||
+ ( freq > CN470ALI_LAST_RX1_CHANNEL ) ||
+ ( ( ( freq - ( uint32_t ) CN470ALI_FIRST_RX1_CHANNEL ) % ( uint32_t ) CN470ALI_STEPWIDTH_RX1_CHANNEL ) != 0 ) )
+ {
+ return false;
+ }
+
+ // Test for frequency range - take RX and TX freqencies into account
+ if( ( freq < 470300000 ) || ( freq > 509700000 ) )
+ {
+ return false;
+ }
+ return true;
+}
+
+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 < CN470ALI_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 RegionCN470ALIGetPhyParam( GetPhyParams_t* getPhy )
+{
+ PhyParam_t phyParam = { 0 };
+
+ switch( getPhy->Attribute )
+ {
+ case PHY_MIN_RX_DR:
+ {
+ phyParam.Value = CN470ALI_RX_MIN_DATARATE;
+ break;
+ }
+ case PHY_MIN_TX_DR:
+ {
+ phyParam.Value = CN470ALI_TX_MIN_DATARATE;
+ break;
+ }
+ case PHY_DEF_TX_DR:
+ {
+ phyParam.Value = CN470ALI_DEFAULT_DATARATE;
+ break;
+ }
+ case PHY_NEXT_LOWER_TX_DR:
+ {
+ /*next low speed*/
+
+ phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, DR_0);
+ break;
+ }
+ case PHY_MAX_TX_POWER:
+ {
+ phyParam.Value = CN470ALI_MAX_TX_POWER;
+ break;
+ }
+ case PHY_DEF_TX_POWER:
+ {
+ phyParam.Value = CN470ALI_DEFAULT_TX_POWER;
+ break;
+ }
+ case PHY_DEF_ADR_ACK_LIMIT:
+ {
+ phyParam.Value = CN470ALI_ADR_ACK_LIMIT;
+ break;
+ }
+ case PHY_DEF_ADR_ACK_DELAY:
+ {
+ phyParam.Value = CN470ALI_ADR_ACK_DELAY;
+ break;
+ }
+ case PHY_MAX_PAYLOAD:
+ {
+ phyParam.Value = MaxPayloadOfDatarateCN470ALI[getPhy->Datarate];
+ break;
+ }
+ case PHY_MAX_PAYLOAD_REPEATER:
+ {
+ phyParam.Value = MaxPayloadOfDatarateRepeaterCN470ALI[getPhy->Datarate];
+ break;
+ }
+ case PHY_DUTY_CYCLE:
+ {
+ phyParam.Value = CN470ALI_DUTY_CYCLE_ENABLED;
+ break;
+ }
+ case PHY_MAX_RX_WINDOW:
+ {
+ phyParam.Value = CN470ALI_MAX_RX_WINDOW;
+ break;
+ }
+ case PHY_RECEIVE_DELAY1:
+ {
+ phyParam.Value = CN470ALI_RECEIVE_DELAY1;
+ break;
+ }
+ case PHY_RECEIVE_DELAY2:
+ {
+ phyParam.Value = CN470ALI_RECEIVE_DELAY2;
+ break;
+ }
+ case PHY_JOIN_ACCEPT_DELAY1:
+ {
+ phyParam.Value = CN470ALI_JOIN_ACCEPT_DELAY1;
+ break;
+ }
+ case PHY_JOIN_ACCEPT_DELAY2:
+ {
+ phyParam.Value = CN470ALI_JOIN_ACCEPT_DELAY2;
+ break;
+ }
+ case PHY_MAX_FCNT_GAP:
+ {
+ phyParam.Value = CN470ALI_MAX_FCNT_GAP;
+ break;
+ }
+ case PHY_ACK_TIMEOUT:
+ {
+/*===============================ALI_Region==============================================*/
+ phyParam.Value = ( CN470ALI_ACKTIMEOUT)+ randr( -CN470ALI_ACK_TIMEOUT_RND, CN470ALI_ACK_TIMEOUT_RND );
+/*=======================================================================================*/
+ break;
+ }
+ case PHY_DEF_DR1_OFFSET:
+ {
+ phyParam.Value = CN470ALI_DEFAULT_RX1_DR_OFFSET;
+ break;
+ }
+ case PHY_DEF_RX2_FREQUENCY:
+ {
+ phyParam.Value = CN470ALI_RX_WND_2_FREQ;
+ break;
+ }
+ case PHY_DEF_RX2_DR:
+ {
+ phyParam.Value = CN470ALI_RX_WND_2_DR;
+ break;
+ }
+ case PHY_CHANNELS_MASK:
+ {
+ phyParam.ChannelsMask = NvmCtx.ChannelsMask;
+ break;
+ }
+ case PHY_CHANNELS_DEFAULT_MASK:
+ {
+ phyParam.ChannelsMask = NvmCtx.ChannelsDefaultMask;
+ break;
+ }
+ case PHY_MAX_NB_CHANNELS:
+ {
+ phyParam.Value = CN470ALI_MAX_NB_CHANNELS;
+ break;
+ }
+ case PHY_CHANNELS:
+ {
+ phyParam.Channels = NvmCtx.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 = CN470ALI_DEFAULT_MAX_EIRP;
+ break;
+ }
+ case PHY_DEF_ANTENNA_GAIN:
+ {
+ phyParam.fValue = CN470ALI_DEFAULT_ANTENNA_GAIN;
+ break;
+ }
+ case PHY_BEACON_FORMAT:
+ {
+ phyParam.BeaconFormat.BeaconSize = CN470ALI_BEACON_SIZE;
+ phyParam.BeaconFormat.Rfu1Size = CN470ALI_RFU1_SIZE;
+ phyParam.BeaconFormat.Rfu2Size = CN470ALI_RFU2_SIZE;
+ break;
+ }
+ case PHY_BEACON_CHANNEL_DR:
+ {
+ phyParam.Value = CN470ALI_BEACON_CHANNEL_DR;
+ break;
+ }
+ case PHY_BEACON_CHANNEL_STEPWIDTH:
+ {
+ phyParam.Value = CN470ALI_BEACON_CHANNEL_STEPWIDTH;
+ break;
+ }
+ case PHY_BEACON_NB_CHANNELS:
+ {
+ phyParam.Value = CN470ALI_BEACON_NB_CHANNELS;
+ break;
+ }
+ case PHY_PING_SLOT_CHANNEL_DR:
+ {
+ phyParam.Value = CN470ALI_PING_SLOT_CHANNEL_DR;
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ return phyParam;
+}
+
+void RegionCN470ALISetBandTxDone( SetBandTxDoneParams_t* txDone )
+{
+ RegionCommonSetBandTxDone( txDone->Joined, &NvmCtx.Bands[NvmCtx.Channels[txDone->Channel].Band], txDone->LastTxDoneTime );
+}
+
+void RegionCN470ALIInitDefaults( InitDefaultsParams_t* params )
+{
+ Band_t bands[CN470ALI_MAX_NB_BANDS] =
+ {
+ CN470ALI_BAND0
+ };
+
+ switch( params->Type )
+ {
+ case INIT_TYPE_INIT:
+ {
+ // Initialize bands
+ memcpy1( ( uint8_t* )NvmCtx.Bands, ( uint8_t* )bands, sizeof( Band_t ) * CN470ALI_MAX_NB_BANDS );
+/*===============================ALI_Region==============================================*/
+
+ // Channels
+ // 125 kHz channels
+
+ #if (ALI_DOWNLINK_FREQ == ALI_FREQ470_S)||(ALI_DOWNLINK_FREQ == ALI_FREQ470_D)
+
+ for( uint8_t i = 0; i < 32; i++ )
+ {
+ NvmCtx.Channels[i].Frequency = 470300000 + i * 200000;
+ NvmCtx.Channels[i].DrRange.Value = ( DR_5 << 4 ) | DR_0;
+ NvmCtx.Channels[i].Band = 0;
+ }
+ for( uint8_t i = 32; i < CN470ALI_MAX_NB_CHANNELS; i++ )
+ {
+ NvmCtx.Channels[i].Frequency = 470300000 + (i + (166-32)) * 200000;
+ NvmCtx.Channels[i].DrRange.Value = ( DR_5 << 4 ) | DR_0;
+ NvmCtx.Channels[i].Band = 0;
+ }
+ #else
+ for(i=0; iNvmCtx != 0 )
+ {
+ memcpy1( (uint8_t*) &NvmCtx, (uint8_t*) params->NvmCtx, sizeof( NvmCtx ) );
+ }
+ break;
+ }
+ case INIT_TYPE_RESTORE_DEFAULT_CHANNELS:
+ {
+ // Restore channels default mask
+ RegionCommonChanMaskCopy( NvmCtx.ChannelsMask, NvmCtx.ChannelsDefaultMask, 6 );
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+void* RegionCN470ALIGetNvmCtx( GetNvmCtxParams_t* params )
+{
+ params->nvmCtxSize = sizeof( RegionCN470ALINvmCtx_t );
+ return &NvmCtx;
+}
+
+bool RegionCN470ALIVerify( VerifyParams_t* verify, PhyAttribute_t phyAttribute )
+{
+ switch( phyAttribute )
+ {
+ case PHY_FREQUENCY:
+ {
+ return VerifyRfFreq( verify->Frequency );
+ }
+ case PHY_TX_DR:
+ case PHY_DEF_TX_DR:
+ {
+ return RegionCommonValueInRange( verify->DatarateParams.Datarate, CN470ALI_TX_MIN_DATARATE, CN470ALI_TX_MAX_DATARATE );
+ }
+ case PHY_RX_DR:
+ {
+ return RegionCommonValueInRange( verify->DatarateParams.Datarate, CN470ALI_RX_MIN_DATARATE, CN470ALI_RX_MAX_DATARATE );
+ }
+ case PHY_DEF_TX_POWER:
+ case PHY_TX_POWER:
+ {
+ // Remark: switched min and max!
+ return RegionCommonValueInRange( verify->TxPower, CN470ALI_MAX_TX_POWER, CN470ALI_MIN_TX_POWER );
+ }
+ case PHY_DUTY_CYCLE:
+ {
+ return CN470ALI_DUTY_CYCLE_ENABLED;
+ }
+ default:
+ return false;
+ }
+}
+
+void RegionCN470ALIApplyCFList( ApplyCFListParams_t* applyCFList )
+{
+ // Size of the optional CF list must be 16 byte
+ if( applyCFList->Size != 16 )
+ {
+ return;
+ }
+
+ // Last byte CFListType must be 0x01 to indicate the CFList contains a series of ChMask fields
+ if( applyCFList->Payload[15] != 0x01 )
+ {
+ return;
+ }
+
+ // ChMask0 - ChMask5 must be set (every ChMask has 16 bit)
+ for( uint8_t chMaskItr = 0, cntPayload = 0; chMaskItr <= 5; chMaskItr++, cntPayload+=2 )
+ {
+ NvmCtx.ChannelsMask[chMaskItr] = (uint16_t) (0x00FF & applyCFList->Payload[cntPayload]);
+ NvmCtx.ChannelsMask[chMaskItr] |= (uint16_t) (applyCFList->Payload[cntPayload+1] << 8);
+ }
+}
+
+bool RegionCN470ALIChanMaskSet( ChanMaskSetParams_t* chanMaskSet )
+{
+ switch( chanMaskSet->ChannelsMaskType )
+ {
+ case CHANNELS_MASK:
+ {
+ RegionCommonChanMaskCopy( NvmCtx.ChannelsMask, chanMaskSet->ChannelsMaskIn, 6 );
+ break;
+ }
+ case CHANNELS_DEFAULT_MASK:
+ {
+ RegionCommonChanMaskCopy( NvmCtx.ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 6 );
+ break;
+ }
+ default:
+ return false;
+ }
+ return true;
+}
+
+void RegionCN470ALIComputeRxWindowParameters( 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, CN470ALI_RX_MAX_DATARATE );
+ rxConfigParams->Bandwidth = GetBandwidth( rxConfigParams->Datarate );
+
+ tSymbol = RegionCommonComputeSymbolTimeLoRa( DataratesCN470ALI[rxConfigParams->Datarate], BandwidthsCN470ALI[rxConfigParams->Datarate] );
+
+ RegionCommonComputeRxWindowParameters( tSymbol, minRxSymbols, rxError, Radio.GetWakeupTime( ), &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset );
+}
+
+bool RegionCN470ALIRxConfig( RxConfigParams_t* rxConfig, int8_t* datarate )
+{
+ int8_t dr = rxConfig->Datarate;
+ uint8_t maxPayload = 0;
+ int8_t phyDr = 0;
+ uint32_t frequency = rxConfig->Frequency;
+ const char *slotStrings[] = { "1", "2", "C", "Multi_C", "P", "Multi_P" };
+
+ if( Radio.GetStatus( ) != RF_IDLE )
+ {
+ return false;
+ }
+
+ if( rxConfig->RxSlot == RX_SLOT_WIN_1 )
+ {
+ #if (ALI_DOWNLINK_FREQ == ALI_FREQ470_S || ALI_DOWNLINK_FREQ == ALI_FREQ480_D)
+ if(rxConfig->Channel <32)
+ frequency = CN470ALI_FIRST_RX1_CHANNEL + ( rxConfig->Channel % 64 ) * CN470ALI_STEPWIDTH_RX1_CHANNEL;
+ else
+ frequency = (uint32_t)503500000 + ((rxConfig->Channel-32) % 64 ) * CN470ALI_STEPWIDTH_RX1_CHANNEL;
+
+ #else
+ // Apply window 1 frequency
+ frequency = CN470ALI_FIRST_RX1_CHANNEL + ( rxConfig->Channel % 64 ) * CN470ALI_STEPWIDTH_RX1_CHANNEL;
+ #endif
+ }
+
+ // Read the physical datarate from the datarates table
+ phyDr = DataratesCN470ALI[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 = MaxPayloadOfDatarateRepeaterCN470ALI[dr];
+ }
+ else
+ {
+ maxPayload = MaxPayloadOfDatarateCN470ALI[dr];
+ }
+ Radio.SetMaxPayloadLength( MODEM_LORA, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD );
+ if ( rxConfig->RxSlot < RX_SLOT_NONE )
+ {
+ MW_LOG( "RX_%s on freq %d Hz at DR %d\n\r", slotStrings[rxConfig->RxSlot], frequency, dr );
+ }
+ else
+ {
+ MW_LOG( "RX on freq %d Hz at DR %d\n\r", frequency, dr );
+ }
+
+ *datarate = (uint8_t) dr;
+ return true;
+}
+
+bool RegionCN470ALITxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir )
+{
+ int8_t phyDr = DataratesCN470ALI[txConfig->Datarate];
+ int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, NvmCtx.Bands[NvmCtx.Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, NvmCtx.ChannelsMask );
+ int8_t phyTxPower = 0;
+
+ // Calculate physical TX power
+ phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain );
+
+ // Setup the radio frequency
+ Radio.SetChannel( NvmCtx.Channels[txConfig->Channel].Frequency );
+
+ Radio.SetTxConfig( MODEM_LORA, phyTxPower, 0, 0, phyDr, 1, 8, false, true, 0, 0, false, 4000 );
+ MW_LOG( "TX on freq %d Hz at DR %d,TxPower %d\n\r", NvmCtx.Channels[txConfig->Channel].Frequency, txConfig->Datarate ,phyTxPower);
+
+ // 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 RegionCN470ALILinkAdrReq( 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, NvmCtx.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;
+
+/*===============================ALI_Region==============================================*/
+ if( linkAdrParams.ChMaskCtrl >4 )
+ {
+ status &= 0xFE; // Channel mask KO
+ }
+ else if( linkAdrParams.ChMaskCtrl == 4)
+ {
+ // Enable all 125 kHz channels
+ channelsMask[0] = 0xFFFF;
+ channelsMask[1] = 0xFFFF;
+ channelsMask[2] = 0xFFFF;
+ channelsMask[3] = 0xFFFF;
+ channelsMask[4] = 0;
+ channelsMask[5] = 0;
+ }
+ else
+ {
+ for( uint8_t i = 0; i < 16; i++ )
+ {
+ if( ( ( linkAdrParams.ChMask & ( 1 << i ) ) != 0 ) &&
+ ( NvmCtx.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 = RegionCN470ALIGetPhyParam( &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 = CN470ALI_MAX_NB_CHANNELS;
+ linkAdrVerifyParams.ChannelsMask = channelsMask;
+ linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value;
+ linkAdrVerifyParams.MaxDatarate = CN470ALI_TX_MAX_DATARATE;
+ linkAdrVerifyParams.Channels = NvmCtx.Channels;
+ linkAdrVerifyParams.MinTxPower = CN470ALI_MIN_TX_POWER;
+ linkAdrVerifyParams.MaxTxPower = CN470ALI_MAX_TX_POWER;
+ linkAdrVerifyParams.Version = linkAdrReq->Version;
+
+ // 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( NvmCtx.ChannelsMask, channelsMask, 6 );
+ }
+
+ // Update status variables
+ *drOut = linkAdrParams.Datarate;
+ *txPowOut = linkAdrParams.TxPower;
+ *nbRepOut = linkAdrParams.NbRep;
+ *nbBytesParsed = bytesProcessed;
+
+ return status;
+}
+
+uint8_t RegionCN470ALIRxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq )
+{
+ uint8_t status = 0x07;
+
+ // Verify radio frequency
+ if( VerifyRfFreq( rxParamSetupReq->Frequency ) == false )
+ {
+ status &= 0xFE; // Channel frequency KO
+ }
+
+ // Verify datarate
+ if( RegionCommonValueInRange( rxParamSetupReq->Datarate, CN470ALI_RX_MIN_DATARATE, CN470ALI_RX_MAX_DATARATE ) == false )
+ {
+ status &= 0xFD; // Datarate KO
+ }
+
+ // Verify datarate offset
+ if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, CN470ALI_MIN_RX1_DR_OFFSET, CN470ALI_MAX_RX1_DR_OFFSET ) == false )
+ {
+ status &= 0xFB; // Rx1DrOffset range KO
+ }
+
+ return status;
+}
+
+uint8_t RegionCN470ALINewChannelReq( NewChannelReqParams_t* newChannelReq )
+{
+ // Datarate and frequency KO
+ return 0;
+}
+
+int8_t RegionCN470ALITxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq )
+{
+ return -1;
+}
+
+uint8_t RegionCN470ALIDlChannelReq( DlChannelReqParams_t* dlChannelReq )
+{
+ return 0;
+}
+
+/*===============================ALI_Region==============================================*/
+int8_t RegionCN470ALIAlternateDr( int8_t currentDr, AlternateDrType_t type )
+{
+ static uint16_t JoinRequestTrials;
+ if(ALTERNATE_DR == type)
+ {
+ ++JoinRequestTrials;
+ return ((JoinRequestTrials%2) ? DR_2 : DR_3);
+ }
+ return currentDr;
+}
+/*=======================================================================================*/
+
+void RegionCN470ALICalcBackOff( CalcBackOffParams_t* calcBackOff )
+{
+ RegionCommonCalcBackOffParams_t calcBackOffParams;
+
+ calcBackOffParams.Channels = NvmCtx.Channels;
+ calcBackOffParams.Bands = NvmCtx.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 );
+}
+
+LoRaMacStatus_t RegionCN470ALINextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff )
+{
+ uint8_t nbEnabledChannels = 0;
+ uint8_t delayTx = 0;
+ uint8_t enabledChannels[CN470ALI_MAX_NB_CHANNELS] = { 0 };
+ TimerTime_t nextTxDelay = 0;
+
+ // Count 125kHz channels
+ if( RegionCommonCountChannels( NvmCtx.ChannelsMask, 0, 6 ) == 0 )
+ { // Reactivate default channels
+ NvmCtx.ChannelsMask[0] = 0xFFFF;
+ NvmCtx.ChannelsMask[1] = 0xFFFF;
+ NvmCtx.ChannelsMask[2] = 0xFFFF;
+ NvmCtx.ChannelsMask[3] = 0xFFFF;
+ NvmCtx.ChannelsMask[4] = 0;
+ NvmCtx.ChannelsMask[5] = 0;
+ }
+
+ TimerTime_t elapsed = TimerGetElapsedTime( nextChanParams->LastAggrTx );
+ if( ( nextChanParams->LastAggrTx == 0 ) || ( nextChanParams->AggrTimeOff <= elapsed ) )
+ {
+ // Reset Aggregated time off
+ *aggregatedTimeOff = 0;
+
+ // Update bands Time OFF
+ nextTxDelay = RegionCommonUpdateBandTimeOff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, NvmCtx.Bands, CN470ALI_MAX_NB_BANDS );
+
+ // Search how many channels are enabled
+ nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Datarate,
+ NvmCtx.ChannelsMask, NvmCtx.Channels,
+ NvmCtx.Bands, enabledChannels, &delayTx );
+ }
+ else
+ {
+ delayTx++;
+ nextTxDelay = nextChanParams->AggrTimeOff - elapsed;
+ }
+
+ if( nbEnabledChannels > 0 )
+ {
+ // We found a valid channel
+ *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )];
+
+ *time = 0;
+ return LORAMAC_STATUS_OK;
+ }
+ else
+ {
+ if( delayTx > 0 )
+ {
+ // Delay transmission due to AggregatedTimeOff or to a band time off
+ *time = nextTxDelay;
+ return LORAMAC_STATUS_DUTYCYCLE_RESTRICTED;
+ }
+ // Datarate not supported by any channel
+ *time = 0;
+ return LORAMAC_STATUS_NO_CHANNEL_FOUND;
+ }
+}
+
+LoRaMacStatus_t RegionCN470ALIChannelAdd( ChannelAddParams_t* channelAdd )
+{
+ return LORAMAC_STATUS_PARAMETER_INVALID;
+}
+
+bool RegionCN470ALIChannelsRemove( ChannelRemoveParams_t* channelRemove )
+{
+ return LORAMAC_STATUS_PARAMETER_INVALID;
+}
+
+void RegionCN470ALISetContinuousWave( ContinuousWaveParams_t* continuousWave )
+{
+ int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, NvmCtx.Bands[NvmCtx.Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, NvmCtx.ChannelsMask );
+ int8_t phyTxPower = 0;
+ uint32_t frequency = NvmCtx.Channels[continuousWave->Channel].Frequency;
+
+ // Calculate physical TX power
+ phyTxPower = RegionCommonComputeTxPower( txPowerLimited, continuousWave->MaxEirp, continuousWave->AntennaGain );
+
+ Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout );
+}
+
+uint8_t RegionCN470ALIApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset )
+{
+ int8_t datarate = dr - drOffset;
+
+ if( datarate < 0 )
+ {
+ datarate = DR_0;
+ }
+ return datarate;
+}
+
+void RegionCN470ALIRxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr )
+{
+ RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup;
+
+ regionCommonRxBeaconSetup.Datarates = DataratesCN470ALI;
+ regionCommonRxBeaconSetup.Frequency = rxBeaconSetup->Frequency;
+ regionCommonRxBeaconSetup.BeaconSize = CN470ALI_BEACON_SIZE;
+ regionCommonRxBeaconSetup.BeaconDatarate = CN470ALI_BEACON_CHANNEL_DR;
+ regionCommonRxBeaconSetup.BeaconChannelBW = CN470ALI_BEACON_CHANNEL_BW;
+ regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime;
+ regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout;
+
+ RegionCommonRxBeaconSetup( ®ionCommonRxBeaconSetup );
+
+ // Store downlink datarate
+ *outDr = CN470ALI_BEACON_CHANNEL_DR;
+}
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCN470ALI.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCN470ALI.h
new file mode 100644
index 00000000..ec4110c2
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCN470ALI.h
@@ -0,0 +1,594 @@
+/*!
+ * \file RegionCN470ALI.h
+ *
+ * \brief Region definition for CN470ALI
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013-2017 Semtech
+ *
+ * ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ * embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Gregory Cristian ( Semtech )
+ *
+ * \author Daniel Jaeckle ( STACKFORCE )
+ *
+ * \author Johannes Bruder ( STACKFORCE )
+ *
+ * \defgroup REGIONCN470ALI Region CN470ALI
+ * Implementation according to LoRaWAN Specification v1.0.2.
+ * \{
+ */
+#ifndef __REGION_CN470ALI_H__
+#define __REGION_CN470ALI_H__
+
+#include "region/Region.h"
+
+#define ALI_FREQ470_S (0)
+#define ALI_FREQ470_D (1)
+
+#define ALI_FREQ480_S (3)
+#define ALI_FREQ480_D (4)
+
+/*table[1] for CN470ALID and CN480ALIS*/
+#define ALI_RX_WND_2_FREQ_1A1D_1B1S 485300000
+#define ALI_RX_WND_2_FREQ_1A2D_1B2S 486900000
+#define ALI_RX_WND_2_FREQ_2A1D_2B1S 488500000
+#define ALI_RX_WND_2_FREQ_2A2D_2B2S 490100000
+#define ALI_RX_WND_2_FREQ_3B1D_3A1S 491700000
+#define ALI_RX_WND_2_FREQ_3B2D_3A2S 493300000
+#define ALI_RX_WND_2_FREQ_4B1D_4A1S 494900000
+#define ALI_RX_WND_2_FREQ_4B2D_4A2S 496500000
+
+/*table[2] for CN470ALIS and CN480ALID*/
+#define ALI_RX_WND_2_FREQ_1A1S_1B1D 471700000
+#define ALI_RX_WND_2_FREQ_1A2S_1B2D 473300000
+#define ALI_RX_WND_2_FREQ_2A1S_2B1D 474900000
+#define ALI_RX_WND_2_FREQ_2A2S_2B2D 476500000
+#define ALI_RX_WND_2_FREQ_3B1S_3A1D 504900000
+#define ALI_RX_WND_2_FREQ_3B2S_3A2D 506500000
+#define ALI_RX_WND_2_FREQ_4B1S_4A1D 508100000
+#define ALI_RX_WND_2_FREQ_4B2S_4A2D 509700000
+
+
+
+/*TODO: add beacon freq*/
+
+
+
+#define ALI_DOWNLINK_FREQ ALI_FREQ470_S
+#define ALI_RX2WIN_FREQ ALI_RX_WND_2_FREQ_1A2S_1B2D
+#define ALI_BEACON_FREQ
+
+
+/*error msg*/
+#if ((ALI_DOWNLINK_FREQ == ALI_FREQ470_D)||(ALI_DOWNLINK_FREQ == ALI_FREQ480_S))
+ #if (ALI_RX2WIN_FREQ < ALI_RX_WND_2_FREQ_1A1D_1B1S) ||((ALI_RX2WIN_FREQ > ALI_RX_WND_2_FREQ_4B2D_4A2S))
+ #error ======>Incorrect ALI_RX2WIN_FREQ , you should use the value in table[1]
+ #endif
+#else
+ #if ((ALI_RX2WIN_FREQ < ALI_RX_WND_2_FREQ_1A1S_1B1D)||(ALI_RX2WIN_FREQ > ALI_RX_WND_2_FREQ_4B2S_4A2D))
+ #error ======>Incorrect ALI_RX2WIN_FREQ , you should use the value in table[2]
+ #endif
+ #if ((ALI_RX2WIN_FREQ > ALI_RX_WND_2_FREQ_2A2S_2B2D) && (ALI_RX2WIN_FREQ < ALI_RX_WND_2_FREQ_3B1S_3A1D))
+ ##error ======>Incorrect ALI_RX2WIN_FREQ , you should use the value in table[2]
+ #endif
+#endif
+
+#ifndef ALI_DOWNLINK_FREQ
+ #error ALI_DOWNLINK_FREQ not defined
+#endif
+#ifndef ALI_RX2WIN_FREQ
+ #erorr ALI_RX2WIN_FREQ not defined
+#endif
+
+/*!
+ * LoRaMac maximum number of channels
+ */
+#define CN470ALI_MAX_NB_CHANNELS 64
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define CN470ALI_TX_MIN_DATARATE DR_0
+
+/*!
+ * Maximal datarate that can be used by the node
+ */
+#define CN470ALI_TX_MAX_DATARATE DR_5
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define CN470ALI_RX_MIN_DATARATE DR_0
+
+/*!
+ * Maximal datarate that can be used by the node
+ */
+#define CN470ALI_RX_MAX_DATARATE DR_5
+
+/*!
+ * Default datarate used by the node
+ */
+
+/*===============================ALI_Region==============================================*/
+#define CN470ALI_DEFAULT_DATARATE DR_2//primitive_cn470 DR_0
+/*=======================================================================================*/
+
+/*!
+ * Minimal Rx1 receive datarate offset
+ */
+#define CN470ALI_MIN_RX1_DR_OFFSET 0
+
+/*!
+ * Maximal Rx1 receive datarate offset
+ */
+#define CN470ALI_MAX_RX1_DR_OFFSET 3
+
+/*!
+ * Default Rx1 receive datarate offset
+ */
+#define CN470ALI_DEFAULT_RX1_DR_OFFSET 0
+
+/*!
+ * Minimal Tx output power that can be used by the node
+ */
+#define CN470ALI_MIN_TX_POWER TX_POWER_7
+
+/*!
+ * Maximal Tx output power that can be used by the node
+ */
+#define CN470ALI_MAX_TX_POWER TX_POWER_0
+
+/*!
+ * Default Tx output power used by the node
+ */
+#define CN470ALI_DEFAULT_TX_POWER TX_POWER_0
+
+/*!
+ * Default Max EIRP
+ */
+#define CN470ALI_DEFAULT_MAX_EIRP 19.15f
+
+/*!
+ * Default antenna gain
+ */
+#define CN470ALI_DEFAULT_ANTENNA_GAIN 2.15f
+
+/*!
+ * ADR Ack limit
+ */
+#define CN470ALI_ADR_ACK_LIMIT 64
+
+/*!
+ * ADR Ack delay
+ */
+#define CN470ALI_ADR_ACK_DELAY 32
+
+/*!
+ * Enabled or disabled the duty cycle
+ */
+#define CN470ALI_DUTY_CYCLE_ENABLED 0
+
+/*!
+ * Maximum RX window duration
+ */
+#define CN470ALI_MAX_RX_WINDOW 3000
+
+/*!
+ * Receive delay 1
+ */
+#define CN470ALI_RECEIVE_DELAY1 1000
+
+/*!
+ * Receive delay 2
+ */
+#define CN470ALI_RECEIVE_DELAY2 2000
+
+/*!
+ * Join accept delay 1
+ */
+#define CN470ALI_JOIN_ACCEPT_DELAY1 5000
+
+/*!
+ * Join accept delay 2
+ */
+#define CN470ALI_JOIN_ACCEPT_DELAY2 6000
+
+/*!
+ * Maximum frame counter gap
+ */
+#define CN470ALI_MAX_FCNT_GAP 16384
+
+/*!
+ * Ack timeout
+ */
+#define CN470ALI_ACKTIMEOUT 5500
+
+/*!
+ * Random ack timeout limits
+ */
+#define CN470ALI_ACK_TIMEOUT_RND 500
+
+
+/*!
+ * Second reception window channel datarate definition.
+ */
+#define CN470ALI_RX_WND_2_DR DR_0
+
+/*
+ * CLASS B
+ */
+/*!
+ * Beacon frequency
+ */
+#define CN470ALI_BEACON_CHANNEL_FREQ 485500000
+
+/*!
+ * Beacon frequency channel stepwidth
+ */
+#define CN470ALI_BEACON_CHANNEL_STEPWIDTH 200000
+
+/*!
+ * Number of possible beacon channels
+ */
+#define CN470ALI_BEACON_NB_CHANNELS 8
+
+/*!
+ * Payload size of a beacon frame
+ */
+#define CN470ALI_BEACON_SIZE 19
+
+/*!
+ * Size of RFU 1 field
+ */
+#define CN470ALI_RFU1_SIZE 3
+
+/*!
+ * Size of RFU 2 field
+ */
+#define CN470ALI_RFU2_SIZE 1
+
+/*!
+ * Datarate of the beacon channel
+ */
+#define CN470ALI_BEACON_CHANNEL_DR DR_2
+
+/*!
+ * Bandwith of the beacon channel
+ */
+#define CN470ALI_BEACON_CHANNEL_BW 0
+
+/*!
+ * Ping slot channel datarate
+ */
+#define CN470ALI_PING_SLOT_CHANNEL_DR DR_2
+
+/*!
+ * LoRaMac maximum number of bands
+ */
+#define CN470ALI_MAX_NB_BANDS 1
+
+/*!
+ * Band 0 definition
+ * { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff }
+ */
+#define CN470ALI_BAND0 { 1, CN470ALI_MAX_TX_POWER, 0, 0, 0 } // 100.0 %
+
+#ifdef ALI_DOWNLINK_FREQ
+
+ #if (ALI_DOWNLINK_FREQ == ALI_FREQ470_S || ALI_DOWNLINK_FREQ == ALI_FREQ480_D)
+ /*!
+ * Defines the first channel for RX window 1 for CN470ALI band
+ */
+ #define CN470ALI_FIRST_RX1_CHANNEL ( (uint32_t) 470300000 )
+
+ /*!
+ * Defines the last channel for RX window 1 for CN470ALI band
+ */
+ #define CN470ALI_LAST_RX1_CHANNEL ( (uint32_t) 509700000 )
+
+ /*!
+ * Defines the step width of the channels for RX window 1
+ */
+ #define CN470ALI_STEPWIDTH_RX1_CHANNEL ( (uint32_t) 200000 )
+
+
+ /*!
+ * Second reception window channel frequency definition.
+ */
+ #define CN470ALI_RX_WND_2_FREQ ((uint32_t)ALI_RX2WIN_FREQ)
+
+
+ #elif (ALI_DOWNLINK_FREQ == ALI_FREQ470_D || ALI_DOWNLINK_FREQ == ALI_FREQ480_S)
+
+ /*!
+ * Defines the first channel for RX window 1 for CN470ALI band
+ */
+ #define CN470ALI_FIRST_RX1_CHANNEL ( (uint32_t) 483900000 )
+
+ /*!
+ * Defines the last channel for RX window 1 for CN470ALI band
+ */
+ #define CN470ALI_LAST_RX1_CHANNEL ( (uint32_t) 496500000 )
+
+ /*!
+ * Defines the step width of the channels for RX window 1
+ */
+ #define CN470ALI_STEPWIDTH_RX1_CHANNEL ( (uint32_t) 200000 )
+
+ /*!
+ * Second reception window channel frequency definition.
+ */
+ #define CN470ALI_RX_WND_2_FREQ ((uint32_t)ALI_RX2WIN_FREQ)
+
+
+ #else
+ #error ALI_DOWNLINK_FREQ not existed
+
+
+ #endif //===============================================================================>
+#else
+ #error ALI_DOWNLINK_FREQ not define
+#endif
+
+/*!
+ * Data rates table definition
+ */
+static const uint8_t DataratesCN470ALI[] = { 12, 11, 10, 9, 8, 7 };
+
+/*!
+ * Bandwidths table definition in Hz
+ */
+static const uint32_t BandwidthsCN470ALI[] = { 125000, 125000, 125000, 125000, 125000, 125000 };
+/*!
+ * Maximum payload with respect to the datarate index. Cannot operate with repeater.
+
+
+ */
+static const uint8_t MaxPayloadOfDatarateCN470ALI[] = { 51+1, 51+1, 51+1, 115+1, 242+1, 242+1 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Can operate with repeater.
+ */
+static const uint8_t MaxPayloadOfDatarateRepeaterCN470ALI[] = { 51+1, 51+1, 51+1, 115+1, 222+1, 222+1 };
+
+/*!
+ * \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 RegionCN470ALIGetPhyParam( GetPhyParams_t* getPhy );
+
+/*!
+ * \brief Updates the last TX done parameters of the current channel.
+ *
+ * \param [IN] txDone Pointer to the function parameters.
+ */
+void RegionCN470ALISetBandTxDone( SetBandTxDoneParams_t* txDone );
+
+/*!
+ * \brief Initializes the channels masks and the channels.
+ *
+ * \param [IN] type Sets the initialization type.
+ */
+void RegionCN470ALIInitDefaults( InitDefaultsParams_t* params );
+
+/*!
+ * \brief Returns a pointer to the internal context and its size.
+ *
+ * \param [OUT] params Pointer to the function parameters.
+ *
+ * \retval Points to a structure where the module store its non-volatile context.
+ */
+void* RegionCN470ALIGetNvmCtx( GetNvmCtxParams_t* params );
+
+/*!
+ * \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 RegionCN470ALIVerify( 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 RegionCN470ALIApplyCFList( 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 RegionCN470ALIChanMaskSet( ChanMaskSetParams_t* chanMaskSet );
+
+/*!
+ * 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 RegionCN470ALIComputeRxWindowParameters( 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 RegionCN470ALIRxConfig( 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 RegionCN470ALITxConfig( 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 RegionCN470ALILinkAdrReq( 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 RegionCN470ALIRxParamSetupReq( 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 RegionCN470ALINewChannelReq( 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 RegionCN470ALITxParamSetupReq( 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 RegionCN470ALIDlChannelReq( DlChannelReqParams_t* dlChannelReq );
+
+/*!
+ * \brief Alternates the datarate of the channel for the join request.
+ *
+ * \param [IN] currentDr Current datarate.
+ *
+ * \retval Datarate to apply.
+ */
+int8_t RegionCN470ALIAlternateDr( int8_t currentDr, AlternateDrType_t type );
+
+/*!
+ * \brief Calculates the back-off time.
+ *
+ * \param [IN] calcBackOff Pointer to the function parameters.
+ */
+void RegionCN470ALICalcBackOff( 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]
+ */
+LoRaMacStatus_t RegionCN470ALINextChannel( 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 RegionCN470ALIChannelAdd( 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 RegionCN470ALIChannelsRemove( ChannelRemoveParams_t* channelRemove );
+
+/*!
+ * \brief Sets the radio into continuous wave mode.
+ *
+ * \param [IN] continuousWave Pointer to the function parameters.
+ */
+void RegionCN470ALISetContinuousWave( 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 RegionCN470ALIApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset );
+
+/*!
+ * \brief Sets the radio into beacon reception mode
+ *
+ * \param [IN] rxBeaconSetup Pointer to the function parameters
+ */
+ void RegionCN470ALIRxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr );
+
+/*! \} defgroup REGIONCN470ALI */
+
+#endif // __REGION_CN470ALI_H__
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCN779.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCN779.c
new file mode 100644
index 00000000..27cb86d3
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCN779.c
@@ -0,0 +1,1040 @@
+/*!
+ * \file RegionCN779.c
+ *
+ * \brief Region implementation for CN779
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013-2017 Semtech
+ *
+ * ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ * embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Gregory Cristian ( Semtech )
+ *
+ * \author Daniel Jaeckle ( STACKFORCE )
+*/
+#include "utilities.h"
+
+#include "RegionCommon.h"
+#include "RegionCN779.h"
+#include "lorawan_conf.h"
+#include "mw_log_conf.h"
+
+// Definitions
+#define CHANNELS_MASK_SIZE 1
+
+/*!
+ * Region specific context
+ */
+typedef struct sRegionCN779NvmCtx
+{
+ /*!
+ * LoRaMAC channels
+ */
+ ChannelParams_t Channels[ CN779_MAX_NB_CHANNELS ];
+ /*!
+ * LoRaMac bands
+ */
+ Band_t Bands[ CN779_MAX_NB_BANDS ];
+ /*!
+ * LoRaMac channels mask
+ */
+ uint16_t ChannelsMask[ CHANNELS_MASK_SIZE ];
+ /*!
+ * LoRaMac channels default mask
+ */
+ uint16_t ChannelsDefaultMask[ CHANNELS_MASK_SIZE ];
+}RegionCN779NvmCtx_t;
+
+/*
+ * Non-volatile module context.
+ */
+static RegionCN779NvmCtx_t NvmCtx;
+
+// 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 VerifyRfFreq( 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_MAX_TX_POWER:
+ {
+ phyParam.Value = CN779_MAX_TX_POWER;
+ break;
+ }
+ case PHY_DEF_TX_POWER:
+ {
+ phyParam.Value = CN779_DEFAULT_TX_POWER;
+ break;
+ }
+ case PHY_DEF_ADR_ACK_LIMIT:
+ {
+ phyParam.Value = CN779_ADR_ACK_LIMIT;
+ break;
+ }
+ case PHY_DEF_ADR_ACK_DELAY:
+ {
+ phyParam.Value = CN779_ADR_ACK_DELAY;
+ 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 = NvmCtx.ChannelsMask;
+ break;
+ }
+ case PHY_CHANNELS_DEFAULT_MASK:
+ {
+ phyParam.ChannelsMask = NvmCtx.ChannelsDefaultMask;
+ break;
+ }
+ case PHY_MAX_NB_CHANNELS:
+ {
+ phyParam.Value = CN779_MAX_NB_CHANNELS;
+ break;
+ }
+ case PHY_CHANNELS:
+ {
+ phyParam.Channels = NvmCtx.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_BEACON_CHANNEL_FREQ:
+ {
+ phyParam.Value = CN779_BEACON_CHANNEL_FREQ;
+ break;
+ }
+ case PHY_BEACON_FORMAT:
+ {
+ phyParam.BeaconFormat.BeaconSize = CN779_BEACON_SIZE;
+ phyParam.BeaconFormat.Rfu1Size = CN779_RFU1_SIZE;
+ phyParam.BeaconFormat.Rfu2Size = CN779_RFU2_SIZE;
+ break;
+ }
+ case PHY_BEACON_CHANNEL_DR:
+ {
+ phyParam.Value = CN779_BEACON_CHANNEL_DR;
+ break;
+ }
+ case PHY_PING_SLOT_CHANNEL_DR:
+ {
+ phyParam.Value = CN779_PING_SLOT_CHANNEL_DR;
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ return phyParam;
+}
+
+void RegionCN779SetBandTxDone( SetBandTxDoneParams_t* txDone )
+{
+ RegionCommonSetBandTxDone( txDone->Joined, &NvmCtx.Bands[NvmCtx.Channels[txDone->Channel].Band], txDone->LastTxDoneTime );
+}
+
+void RegionCN779InitDefaults( InitDefaultsParams_t* params )
+{
+ Band_t bands[CN779_MAX_NB_BANDS] =
+ {
+ CN779_BAND0
+ };
+
+ switch( params->Type )
+ {
+ case INIT_TYPE_INIT:
+ {
+ // Initialize bands
+ memcpy1( ( uint8_t* )NvmCtx.Bands, ( uint8_t* )bands, sizeof( Band_t ) * CN779_MAX_NB_BANDS );
+
+ // Channels
+ NvmCtx.Channels[0] = ( ChannelParams_t ) CN779_LC1;
+ NvmCtx.Channels[1] = ( ChannelParams_t ) CN779_LC2;
+ NvmCtx.Channels[2] = ( ChannelParams_t ) CN779_LC3;
+
+ // Initialize the channels default mask
+ NvmCtx.ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 );
+ // Update the channels mask
+ RegionCommonChanMaskCopy( NvmCtx.ChannelsMask, NvmCtx.ChannelsDefaultMask, 1 );
+ break;
+ }
+ case INIT_TYPE_RESTORE_CTX:
+ {
+ if( params->NvmCtx != 0 )
+ {
+ memcpy1( (uint8_t*) &NvmCtx, (uint8_t*) params->NvmCtx, sizeof( NvmCtx ) );
+ }
+ break;
+ }
+ case INIT_TYPE_RESTORE_DEFAULT_CHANNELS:
+ {
+ // Restore channels default mask
+ NvmCtx.ChannelsMask[0] |= NvmCtx.ChannelsDefaultMask[0];
+
+ // Channels
+ NvmCtx.Channels[0] = ( ChannelParams_t ) CN779_LC1;
+ NvmCtx.Channels[1] = ( ChannelParams_t ) CN779_LC2;
+ NvmCtx.Channels[2] = ( ChannelParams_t ) CN779_LC3;
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+void* RegionCN779GetNvmCtx( GetNvmCtxParams_t* params )
+{
+ params->nvmCtxSize = sizeof( RegionCN779NvmCtx_t );
+ return &NvmCtx;
+}
+
+bool RegionCN779Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute )
+{
+ switch( phyAttribute )
+ {
+ case PHY_FREQUENCY:
+ {
+ return VerifyRfFreq( verify->Frequency );
+ }
+ 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;
+ }
+ default:
+ return false;
+ }
+}
+
+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 CFListType must be 0 to indicate the CFList contains a list of frequencies
+ if( applyCFList->Payload[15] != 0 )
+ {
+ 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( NvmCtx.ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 );
+ break;
+ }
+ case CHANNELS_DEFAULT_MASK:
+ {
+ RegionCommonChanMaskCopy( NvmCtx.ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 );
+ break;
+ }
+ default:
+ return false;
+ }
+ return true;
+}
+
+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.GetWakeupTime( ), &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;
+ const char *slotStrings[] = { "1", "2", "C", "Multi_C", "P", "Multi_P" };
+
+ if( Radio.GetStatus( ) != RF_IDLE )
+ {
+ return false;
+ }
+
+ if( rxConfig->RxSlot == RX_SLOT_WIN_1 )
+ {
+ // Apply window 1 frequency
+ frequency = NvmCtx.Channels[rxConfig->Channel].Frequency;
+ // Apply the alternative RX 1 window frequency, if it is available
+ if( NvmCtx.Channels[rxConfig->Channel].Rx1Frequency != 0 )
+ {
+ frequency = NvmCtx.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 );
+ if ( rxConfig->RxSlot < RX_SLOT_NONE )
+ {
+ MW_LOG( "RX_%s on freq %d Hz at DR %d\n\r", slotStrings[rxConfig->RxSlot], frequency, dr );
+ }
+ else
+ {
+ MW_LOG( "RX on freq %d Hz at DR %d\n\r", frequency, dr );
+ }
+
+ *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, NvmCtx.Bands[NvmCtx.Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, NvmCtx.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( NvmCtx.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, 4000 );
+ }
+ else
+ {
+ modem = MODEM_LORA;
+ Radio.SetTxConfig( modem, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 4000 );
+ }
+ MW_LOG( "TX on freq %d Hz at DR %d\n\r", NvmCtx.Channels[txConfig->Channel].Frequency, txConfig->Datarate );
+
+ // 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( NvmCtx.Channels[i].Frequency != 0 )
+ {
+ chMask |= 1 << i;
+ }
+ }
+ else
+ {
+ if( ( ( chMask & ( 1 << i ) ) != 0 ) &&
+ ( NvmCtx.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 = NvmCtx.Channels;
+ linkAdrVerifyParams.MinTxPower = CN779_MIN_TX_POWER;
+ linkAdrVerifyParams.MaxTxPower = CN779_MAX_TX_POWER;
+ linkAdrVerifyParams.Version = linkAdrReq->Version;
+
+ // 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
+ memset1( ( uint8_t* ) NvmCtx.ChannelsMask, 0, sizeof( NvmCtx.ChannelsMask ) );
+ // Update the channels mask
+ NvmCtx.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( VerifyRfFreq( 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( VerifyRfFreq( dlChannelReq->Rx1Frequency ) == false )
+ {
+ status &= 0xFE;
+ }
+
+ // Verify if an uplink frequency exists
+ if( NvmCtx.Channels[dlChannelReq->ChannelId].Frequency == 0 )
+ {
+ status &= 0xFD;
+ }
+
+ // Apply Rx1 frequency, if the status is OK
+ if( status == 0x03 )
+ {
+ NvmCtx.Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency;
+ }
+
+ return status;
+}
+
+int8_t RegionCN779AlternateDr( int8_t currentDr, AlternateDrType_t type )
+{
+ return currentDr;
+}
+
+void RegionCN779CalcBackOff( CalcBackOffParams_t* calcBackOff )
+{
+ RegionCommonCalcBackOffParams_t calcBackOffParams;
+
+ calcBackOffParams.Channels = NvmCtx.Channels;
+ calcBackOffParams.Bands = NvmCtx.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 );
+}
+
+LoRaMacStatus_t 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( NvmCtx.ChannelsMask, 0, 1 ) == 0 )
+ { // Reactivate default channels
+ NvmCtx.ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 );
+ }
+
+ TimerTime_t elapsed = TimerGetElapsedTime( nextChanParams->LastAggrTx );
+ if( ( nextChanParams->LastAggrTx == 0 ) || ( nextChanParams->AggrTimeOff <= elapsed ) )
+ {
+ // Reset Aggregated time off
+ *aggregatedTimeOff = 0;
+
+ // Update bands Time OFF
+ nextTxDelay = RegionCommonUpdateBandTimeOff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, NvmCtx.Bands, CN779_MAX_NB_BANDS );
+
+ // Search how many channels are enabled
+ nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Joined, nextChanParams->Datarate,
+ NvmCtx.ChannelsMask, NvmCtx.Channels,
+ NvmCtx.Bands, enabledChannels, &delayTx );
+ }
+ else
+ {
+ delayTx++;
+ nextTxDelay = nextChanParams->AggrTimeOff - elapsed;
+ }
+
+ if( nbEnabledChannels > 0 )
+ {
+ // We found a valid channel
+ *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )];
+
+ *time = 0;
+ return LORAMAC_STATUS_OK;
+ }
+ else
+ {
+ if( delayTx > 0 )
+ {
+ // Delay transmission due to AggregatedTimeOff or to a band time off
+ *time = nextTxDelay;
+ return LORAMAC_STATUS_DUTYCYCLE_RESTRICTED;
+ }
+ // Datarate not supported by any channel, restore defaults
+ NvmCtx.ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 );
+ *time = 0;
+ return LORAMAC_STATUS_NO_CHANNEL_FOUND;
+ }
+}
+
+LoRaMacStatus_t RegionCN779ChannelAdd( ChannelAddParams_t* channelAdd )
+{
+ bool drInvalid = false;
+ bool freqInvalid = false;
+ uint8_t id = channelAdd->ChannelId;
+
+ if( id < CN779_NUMB_DEFAULT_CHANNELS )
+ {
+ return LORAMAC_STATUS_FREQ_AND_DR_INVALID;
+ }
+
+ 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;
+ }
+
+ // Check frequency
+ if( freqInvalid == false )
+ {
+ if( VerifyRfFreq( 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;
+ }
+
+ memcpy1( ( uint8_t* ) &(NvmCtx.Channels[id]), ( uint8_t* ) channelAdd->NewChannel, sizeof( NvmCtx.Channels[id] ) );
+ NvmCtx.Channels[id].Band = 0;
+ NvmCtx.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
+ NvmCtx.Channels[id] = ( ChannelParams_t ){ 0, 0, { 0 }, 0 };
+
+ return RegionCommonChanDisable( NvmCtx.ChannelsMask, id, CN779_MAX_NB_CHANNELS );
+}
+
+void RegionCN779SetContinuousWave( ContinuousWaveParams_t* continuousWave )
+{
+ int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, NvmCtx.Bands[NvmCtx.Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, NvmCtx.ChannelsMask );
+ int8_t phyTxPower = 0;
+ uint32_t frequency = NvmCtx.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 )
+{
+ RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup;
+
+ regionCommonRxBeaconSetup.Datarates = DataratesCN779;
+ regionCommonRxBeaconSetup.Frequency = rxBeaconSetup->Frequency;
+ regionCommonRxBeaconSetup.BeaconSize = CN779_BEACON_SIZE;
+ regionCommonRxBeaconSetup.BeaconDatarate = CN779_BEACON_CHANNEL_DR;
+ regionCommonRxBeaconSetup.BeaconChannelBW = CN779_BEACON_CHANNEL_BW;
+ regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime;
+ regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout;
+
+ RegionCommonRxBeaconSetup( ®ionCommonRxBeaconSetup );
+
+ // Store downlink datarate
+ *outDr = CN779_BEACON_CHANNEL_DR;
+}
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCN779.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCN779.h
new file mode 100644
index 00000000..3c404dd8
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCN779.h
@@ -0,0 +1,508 @@
+/*!
+ * \file RegionCN779.h
+ *
+ * \brief Region definition for CN779
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013-2017 Semtech
+ *
+ * ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ * embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Gregory Cristian ( Semtech )
+ *
+ * \author Daniel Jaeckle ( STACKFORCE )
+ *
+ * \author Johannes Bruder ( STACKFORCE )
+ *
+ * \defgroup REGIONCN779 Region CN779
+ * Implementation according to LoRaWAN Specification v1.0.2.
+ * \{
+ */
+#ifndef __REGION_CN779_H__
+#define __REGION_CN779_H__
+
+#include "region/Region.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 frequency
+ */
+#define CN779_BEACON_CHANNEL_FREQ 785000000
+
+/*!
+ * Payload size of a beacon frame
+ */
+#define CN779_BEACON_SIZE 17
+
+/*!
+ * Size of RFU 1 field
+ */
+#define CN779_RFU1_SIZE 2
+
+/*!
+ * Size of RFU 2 field
+ */
+#define CN779_RFU2_SIZE 0
+
+/*!
+ * Datarate of the beacon channel
+ */
+#define CN779_BEACON_CHANNEL_DR DR_3
+
+/*!
+ * Bandwith of the beacon channel
+ */
+#define CN779_BEACON_CHANNEL_BW 0
+
+/*!
+ * Ping slot channel datarate
+ */
+#define CN779_PING_SLOT_CHANNEL_DR DR_3
+
+/*!
+ * LoRaMac maximum number of bands
+ */
+#define CN779_MAX_NB_BANDS 1
+
+/*!
+ * Band 0 definition
+ * { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff }
+ */
+#define CN779_BAND0 { 100, CN779_MAX_TX_POWER, 0, 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( InitDefaultsParams_t* params );
+
+/*!
+ * \brief Returns a pointer to the internal context and its size.
+ *
+ * \param [OUT] params Pointer to the function parameters.
+ *
+ * \retval Points to a structure where the module store its non-volatile context.
+ */
+void* RegionCN779GetNvmCtx( GetNvmCtxParams_t* params );
+
+/*!
+ * \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 );
+
+/*!
+ * 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] currentDr Current datarate.
+ *
+ * \retval Datarate to apply.
+ */
+int8_t RegionCN779AlternateDr( int8_t currentDr, AlternateDrType_t type );
+
+/*!
+ * \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]
+ */
+LoRaMacStatus_t 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 );
+
+/*!
+ * \brief Sets the radio into beacon reception mode
+ *
+ * \param [IN] rxBeaconSetup Pointer to the function parameters
+ */
+ void RegionCN779RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr );
+
+/*! \} defgroup REGIONCN779 */
+
+#endif // __REGION_CN779_H__
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCommon.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCommon.c
new file mode 100644
index 00000000..33a90c2a
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCommon.c
@@ -0,0 +1,387 @@
+/*!
+ * \file RegionCommon.c
+ *
+ * \brief LoRa MAC common region implementation
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013-2017 Semtech
+ *
+ * ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ * embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Gregory Cristian ( Semtech )
+ *
+ * \author Daniel Jaeckle ( STACKFORCE )
+ */
+#include
+#include "radio.h"
+#include "utilities.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_MAX;
+
+ // Update bands Time OFF
+ for( uint8_t i = 0; i < nbBands; i++ )
+ {
+ if( joined == false )
+ {
+ TimerTime_t elapsedJoin = TimerGetElapsedTime( bands[i].LastJoinTxDoneTime );
+ TimerTime_t elapsedTx = TimerGetElapsedTime( bands[i].LastTxDoneTime );
+ TimerTime_t txDoneTime = MAX( elapsedJoin,
+ ( dutyCycle == true ) ? elapsedTx : 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 )
+ {
+ TimerTime_t elapsed = TimerGetElapsedTime( bands[i].LastTxDoneTime );
+ if( bands[i].TimeOff <= elapsed )
+ {
+ bands[i].TimeOff = 0;
+ }
+ if( bands[i].TimeOff != 0 )
+ {
+ nextTxDelay = MIN( bands[i].TimeOff - elapsed, nextTxDelay );
+ }
+ }
+ else
+ {
+ nextTxDelay = 0;
+ bands[i].TimeOff = 0;
+ }
+ }
+ }
+
+ return ( nextTxDelay == TIMERTIME_T_MAX ) ? 0 : 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
+ nbRepetitions = verifyParams->CurrentNbRep;
+ 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 )
+ { // Restore the default value according to the LoRaWAN specification
+ nbRepetitions = 1;
+ }
+ }
+
+ // 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 rxContinuous = true;
+ uint8_t datarate;
+
+ // Set the radio into sleep mode
+ Radio.Sleep( );
+
+ // Setup frequency and payload length
+ Radio.SetChannel( rxBeaconSetupParams->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 );
+}
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCommon.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCommon.h
new file mode 100644
index 00000000..79fafeae
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCommon.h
@@ -0,0 +1,421 @@
+/*!
+ * \file RegionCommon.h
+ *
+ * \brief Region independent implementations which are common to all regions.
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013-2017 Semtech
+ *
+ * ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ * embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Gregory Cristian ( Semtech )
+ *
+ * \author Daniel Jaeckle ( STACKFORCE )
+ *
+ * \author Johannes Bruder ( STACKFORCE )
+ *
+ * \defgroup REGIONCOMMON Common region implementation
+ * Region independent implementations which are common to all regions.
+ * \{
+ */
+#ifndef __REGIONCOMMON_H__
+#define __REGIONCOMMON_H__
+
+#include "utilities.h"
+#include "radio.h"
+#include "LoRaMacTypes.h"
+#include "region/Region.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
+{
+ /*!
+ * LoRaWAN specification Version
+ */
+ Version_t Version;
+ /*!
+ * 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;
+ /*!
+ * Frequency
+ */
+ uint32_t Frequency;
+ /*!
+ * 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;
+ /*!
+ * 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.
+ *
+ * \remark US915 region uses a conducted power as input value for maxEirp.
+ * Thus, the antennaGain parameter must be set to 0.
+ *
+ * \param [IN] txPower TX power index.
+ *
+ * \param [IN] maxEirp Maximum EIRP.
+ *
+ * \param [IN] antennaGain Antenna gain. Referenced to the isotropic antenna.
+ * Value is in dBi. ( antennaGain[dBi] = measuredAntennaGain[dBd] + 2.15 )
+ *
+ * \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.
+ */
+void RegionCommonRxBeaconSetup( RegionCommonRxBeaconSetupParams_t* rxBeaconSetupParams );
+
+/*! \} defgroup REGIONCOMMON */
+
+#endif // __REGIONCOMMON_H__
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionEU433.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionEU433.c
new file mode 100644
index 00000000..f87de7aa
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionEU433.c
@@ -0,0 +1,1040 @@
+/*!
+ * \file RegionEU433.c
+ *
+ * \brief Region implementation for EU433
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013-2017 Semtech
+ *
+ * ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ * embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Gregory Cristian ( Semtech )
+ *
+ * \author Daniel Jaeckle ( STACKFORCE )
+*/
+#include "utilities.h"
+
+#include "RegionCommon.h"
+#include "RegionEU433.h"
+#include "lorawan_conf.h"
+#include "mw_log_conf.h"
+
+// Definitions
+#define CHANNELS_MASK_SIZE 1
+
+/*!
+ * Region specific context
+ */
+typedef struct sRegionEU433NvmCtx
+{
+ /*!
+ * LoRaMAC channels
+ */
+ ChannelParams_t Channels[ EU433_MAX_NB_CHANNELS ];
+ /*!
+ * LoRaMac bands
+ */
+ Band_t Bands[ EU433_MAX_NB_BANDS ];
+ /*!
+ * LoRaMac channels mask
+ */
+ uint16_t ChannelsMask[ CHANNELS_MASK_SIZE ];
+ /*!
+ * LoRaMac channels default mask
+ */
+ uint16_t ChannelsDefaultMask[ CHANNELS_MASK_SIZE ];
+}RegionEU433NvmCtx_t;
+
+/*
+ * Non-volatile module context.
+ */
+static RegionEU433NvmCtx_t NvmCtx;
+
+// 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 VerifyRfFreq( 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_MAX_TX_POWER:
+ {
+ phyParam.Value = EU433_MAX_TX_POWER;
+ break;
+ }
+ case PHY_DEF_TX_POWER:
+ {
+ phyParam.Value = EU433_DEFAULT_TX_POWER;
+ break;
+ }
+ case PHY_DEF_ADR_ACK_LIMIT:
+ {
+ phyParam.Value = EU433_ADR_ACK_LIMIT;
+ break;
+ }
+ case PHY_DEF_ADR_ACK_DELAY:
+ {
+ phyParam.Value = EU433_ADR_ACK_DELAY;
+ 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 = NvmCtx.ChannelsMask;
+ break;
+ }
+ case PHY_CHANNELS_DEFAULT_MASK:
+ {
+ phyParam.ChannelsMask = NvmCtx.ChannelsDefaultMask;
+ break;
+ }
+ case PHY_MAX_NB_CHANNELS:
+ {
+ phyParam.Value = EU433_MAX_NB_CHANNELS;
+ break;
+ }
+ case PHY_CHANNELS:
+ {
+ phyParam.Channels = NvmCtx.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_BEACON_CHANNEL_FREQ:
+ {
+ phyParam.Value = EU433_BEACON_CHANNEL_FREQ;
+ break;
+ }
+ case PHY_BEACON_FORMAT:
+ {
+ phyParam.BeaconFormat.BeaconSize = EU433_BEACON_SIZE;
+ phyParam.BeaconFormat.Rfu1Size = EU433_RFU1_SIZE;
+ phyParam.BeaconFormat.Rfu2Size = EU433_RFU2_SIZE;
+ break;
+ }
+ case PHY_BEACON_CHANNEL_DR:
+ {
+ phyParam.Value = EU433_BEACON_CHANNEL_DR;
+ break;
+ }
+ case PHY_PING_SLOT_CHANNEL_DR:
+ {
+ phyParam.Value = EU433_PING_SLOT_CHANNEL_DR;
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ return phyParam;
+}
+
+void RegionEU433SetBandTxDone( SetBandTxDoneParams_t* txDone )
+{
+ RegionCommonSetBandTxDone( txDone->Joined, &NvmCtx.Bands[NvmCtx.Channels[txDone->Channel].Band], txDone->LastTxDoneTime );
+}
+
+void RegionEU433InitDefaults( InitDefaultsParams_t* params )
+{
+ Band_t bands[EU433_MAX_NB_BANDS] =
+ {
+ EU433_BAND0
+ };
+
+ switch( params->Type )
+ {
+ case INIT_TYPE_INIT:
+ {
+ // Initialize bands
+ memcpy1( ( uint8_t* )NvmCtx.Bands, ( uint8_t* )bands, sizeof( Band_t ) * EU433_MAX_NB_BANDS );
+
+ // Channels
+ NvmCtx.Channels[0] = ( ChannelParams_t ) EU433_LC1;
+ NvmCtx.Channels[1] = ( ChannelParams_t ) EU433_LC2;
+ NvmCtx.Channels[2] = ( ChannelParams_t ) EU433_LC3;
+
+ // Initialize the channels default mask
+ NvmCtx.ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 );
+ // Update the channels mask
+ RegionCommonChanMaskCopy( NvmCtx.ChannelsMask, NvmCtx.ChannelsDefaultMask, 1 );
+ break;
+ }
+ case INIT_TYPE_RESTORE_CTX:
+ {
+ if( params->NvmCtx != 0 )
+ {
+ memcpy1( (uint8_t*) &NvmCtx, (uint8_t*) params->NvmCtx, sizeof( NvmCtx ) );
+ }
+ break;
+ }
+ case INIT_TYPE_RESTORE_DEFAULT_CHANNELS:
+ {
+ // Restore channels default mask
+ NvmCtx.ChannelsMask[0] |= NvmCtx.ChannelsDefaultMask[0];
+
+ // Channels
+ NvmCtx.Channels[0] = ( ChannelParams_t ) EU433_LC1;
+ NvmCtx.Channels[1] = ( ChannelParams_t ) EU433_LC2;
+ NvmCtx.Channels[2] = ( ChannelParams_t ) EU433_LC3;
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+void* RegionEU433GetNvmCtx( GetNvmCtxParams_t* params )
+{
+ params->nvmCtxSize = sizeof( RegionEU433NvmCtx_t );
+ return &NvmCtx;
+}
+
+bool RegionEU433Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute )
+{
+ switch( phyAttribute )
+ {
+ case PHY_FREQUENCY:
+ {
+ return VerifyRfFreq( verify->Frequency );
+ }
+ 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;
+ }
+ default:
+ return false;
+ }
+}
+
+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 CFListType must be 0 to indicate the CFList contains a list of frequencies
+ if( applyCFList->Payload[15] != 0 )
+ {
+ 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( NvmCtx.ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 );
+ break;
+ }
+ case CHANNELS_DEFAULT_MASK:
+ {
+ RegionCommonChanMaskCopy( NvmCtx.ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 );
+ break;
+ }
+ default:
+ return false;
+ }
+ return true;
+}
+
+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.GetWakeupTime( ), &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;
+ const char *slotStrings[] = { "1", "2", "C", "Multi_C", "P", "Multi_P" };
+
+ if( Radio.GetStatus( ) != RF_IDLE )
+ {
+ return false;
+ }
+
+ if( rxConfig->RxSlot == RX_SLOT_WIN_1 )
+ {
+ // Apply window 1 frequency
+ frequency = NvmCtx.Channels[rxConfig->Channel].Frequency;
+ // Apply the alternative RX 1 window frequency, if it is available
+ if( NvmCtx.Channels[rxConfig->Channel].Rx1Frequency != 0 )
+ {
+ frequency = NvmCtx.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 );
+ if ( rxConfig->RxSlot < RX_SLOT_NONE )
+ {
+ MW_LOG( "RX_%s on freq %d Hz at DR %d\n\r", slotStrings[rxConfig->RxSlot], frequency, dr );
+ }
+ else
+ {
+ MW_LOG( "RX on freq %d Hz at DR %d\n\r", frequency, dr );
+ }
+
+ *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, NvmCtx.Bands[NvmCtx.Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, NvmCtx.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( NvmCtx.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, 4000 );
+ }
+ else
+ {
+ modem = MODEM_LORA;
+ Radio.SetTxConfig( modem, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 4000 );
+ }
+ MW_LOG( "TX on freq %d Hz at DR %d\n\r", NvmCtx.Channels[txConfig->Channel].Frequency, txConfig->Datarate );
+
+ // 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( NvmCtx.Channels[i].Frequency != 0 )
+ {
+ chMask |= 1 << i;
+ }
+ }
+ else
+ {
+ if( ( ( chMask & ( 1 << i ) ) != 0 ) &&
+ ( NvmCtx.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 = NvmCtx.Channels;
+ linkAdrVerifyParams.MinTxPower = EU433_MIN_TX_POWER;
+ linkAdrVerifyParams.MaxTxPower = EU433_MAX_TX_POWER;
+ linkAdrVerifyParams.Version = linkAdrReq->Version;
+
+ // 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
+ memset1( ( uint8_t* ) NvmCtx.ChannelsMask, 0, sizeof( NvmCtx.ChannelsMask ) );
+ // Update the channels mask
+ NvmCtx.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( VerifyRfFreq( 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( VerifyRfFreq( dlChannelReq->Rx1Frequency ) == false )
+ {
+ status &= 0xFE;
+ }
+
+ // Verify if an uplink frequency exists
+ if( NvmCtx.Channels[dlChannelReq->ChannelId].Frequency == 0 )
+ {
+ status &= 0xFD;
+ }
+
+ // Apply Rx1 frequency, if the status is OK
+ if( status == 0x03 )
+ {
+ NvmCtx.Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency;
+ }
+
+ return status;
+}
+
+int8_t RegionEU433AlternateDr( int8_t currentDr, AlternateDrType_t type )
+{
+ return currentDr;
+}
+
+void RegionEU433CalcBackOff( CalcBackOffParams_t* calcBackOff )
+{
+ RegionCommonCalcBackOffParams_t calcBackOffParams;
+
+ calcBackOffParams.Channels = NvmCtx.Channels;
+ calcBackOffParams.Bands = NvmCtx.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 );
+}
+
+LoRaMacStatus_t 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( NvmCtx.ChannelsMask, 0, 1 ) == 0 )
+ { // Reactivate default channels
+ NvmCtx.ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 );
+ }
+
+ TimerTime_t elapsed = TimerGetElapsedTime( nextChanParams->LastAggrTx );
+ if( ( nextChanParams->LastAggrTx == 0 ) || ( nextChanParams->AggrTimeOff <= elapsed ) )
+ {
+ // Reset Aggregated time off
+ *aggregatedTimeOff = 0;
+
+ // Update bands Time OFF
+ nextTxDelay = RegionCommonUpdateBandTimeOff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, NvmCtx.Bands, EU433_MAX_NB_BANDS );
+
+ // Search how many channels are enabled
+ nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Joined, nextChanParams->Datarate,
+ NvmCtx.ChannelsMask, NvmCtx.Channels,
+ NvmCtx.Bands, enabledChannels, &delayTx );
+ }
+ else
+ {
+ delayTx++;
+ nextTxDelay = nextChanParams->AggrTimeOff - elapsed;
+ }
+
+ if( nbEnabledChannels > 0 )
+ {
+ // We found a valid channel
+ *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )];
+
+ *time = 0;
+ return LORAMAC_STATUS_OK;
+ }
+ else
+ {
+ if( delayTx > 0 )
+ {
+ // Delay transmission due to AggregatedTimeOff or to a band time off
+ *time = nextTxDelay;
+ return LORAMAC_STATUS_DUTYCYCLE_RESTRICTED;
+ }
+ // Datarate not supported by any channel, restore defaults
+ NvmCtx.ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 );
+ *time = 0;
+ return LORAMAC_STATUS_NO_CHANNEL_FOUND;
+ }
+}
+
+LoRaMacStatus_t RegionEU433ChannelAdd( ChannelAddParams_t* channelAdd )
+{
+ bool drInvalid = false;
+ bool freqInvalid = false;
+ uint8_t id = channelAdd->ChannelId;
+
+ if( id < EU433_NUMB_DEFAULT_CHANNELS )
+ {
+ return LORAMAC_STATUS_FREQ_AND_DR_INVALID;
+ }
+
+ 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;
+ }
+
+ // Check frequency
+ if( freqInvalid == false )
+ {
+ if( VerifyRfFreq( 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;
+ }
+
+ memcpy1( ( uint8_t* ) &(NvmCtx.Channels[id]), ( uint8_t* ) channelAdd->NewChannel, sizeof( NvmCtx.Channels[id] ) );
+ NvmCtx.Channels[id].Band = 0;
+ NvmCtx.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
+ NvmCtx.Channels[id] = ( ChannelParams_t ){ 0, 0, { 0 }, 0 };
+
+ return RegionCommonChanDisable( NvmCtx.ChannelsMask, id, EU433_MAX_NB_CHANNELS );
+}
+
+void RegionEU433SetContinuousWave( ContinuousWaveParams_t* continuousWave )
+{
+ int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, NvmCtx.Bands[NvmCtx.Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, NvmCtx.ChannelsMask );
+ int8_t phyTxPower = 0;
+ uint32_t frequency = NvmCtx.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 )
+{
+ RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup;
+
+ regionCommonRxBeaconSetup.Datarates = DataratesEU433;
+ regionCommonRxBeaconSetup.Frequency = rxBeaconSetup->Frequency;
+ regionCommonRxBeaconSetup.BeaconSize = EU433_BEACON_SIZE;
+ regionCommonRxBeaconSetup.BeaconDatarate = EU433_BEACON_CHANNEL_DR;
+ regionCommonRxBeaconSetup.BeaconChannelBW = EU433_BEACON_CHANNEL_BW;
+ regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime;
+ regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout;
+
+ RegionCommonRxBeaconSetup( ®ionCommonRxBeaconSetup );
+
+ // Store downlink datarate
+ *outDr = EU433_BEACON_CHANNEL_DR;
+}
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionEU433.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionEU433.h
new file mode 100644
index 00000000..6f8b38d3
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionEU433.h
@@ -0,0 +1,509 @@
+/*!
+ * \file RegionEU433.h
+ *
+ * \brief Region definition for EU433
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013-2017 Semtech
+ *
+ * ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ * embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Gregory Cristian ( Semtech )
+ *
+ * \author Daniel Jaeckle ( STACKFORCE )
+ *
+ * \author Johannes Bruder ( STACKFORCE )
+ *
+ * \defgroup REGIONEU433 Region EU433
+ * Implementation according to LoRaWAN Specification v1.0.2.
+ * \{
+ */
+#ifndef __REGION_EU433_H__
+#define __REGION_EU433_H__
+
+#include "region/Region.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 frequency
+ */
+#define EU433_BEACON_CHANNEL_FREQ 434665000
+
+/*!
+ * Payload size of a beacon frame
+ */
+#define EU433_BEACON_SIZE 17
+
+/*!
+ * Size of RFU 1 field
+ */
+#define EU433_RFU1_SIZE 2
+
+/*!
+ * Size of RFU 2 field
+ */
+#define EU433_RFU2_SIZE 0
+
+/*!
+ * Datarate of the beacon channel
+ */
+#define EU433_BEACON_CHANNEL_DR DR_3
+
+/*!
+ * Bandwith of the beacon channel
+ */
+#define EU433_BEACON_CHANNEL_BW 0
+
+/*!
+ * Ping slot channel datarate
+ */
+#define EU433_PING_SLOT_CHANNEL_DR DR_3
+
+/*!
+ * Band 0 definition
+ * { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff }
+ */
+#define EU433_BAND0 { 100, EU433_MAX_TX_POWER, 0, 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( InitDefaultsParams_t* params );
+
+/*!
+ * \brief Returns a pointer to the internal context and its size.
+ *
+ * \param [OUT] params Pointer to the function parameters.
+ *
+ * \retval Points to a structure where the module store its non-volatile context.
+ */
+void* RegionEU433GetNvmCtx( GetNvmCtxParams_t* params );
+
+/*!
+ * \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 );
+
+/*!
+ * 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] currentDr Current datarate.
+ *
+ * \retval Datarate to apply.
+ */
+int8_t RegionEU433AlternateDr( int8_t currentDr, AlternateDrType_t type );
+
+/*!
+ * \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]
+ */
+LoRaMacStatus_t 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 );
+
+/*!
+ * \brief Sets the radio into beacon reception mode
+ *
+ * \param [IN] rxBeaconSetup Pointer to the function parameters
+ */
+ void RegionEU433RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr );
+
+/*! \} defgroup REGIONEU433 */
+
+#endif // __REGION_EU433_H__
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionEU868.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionEU868.c
new file mode 100644
index 00000000..a7e78c4c
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionEU868.c
@@ -0,0 +1,1075 @@
+/*!
+ * \file RegionEU868.c
+ *
+ * \brief Region implementation for EU868
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013-2017 Semtech
+ *
+ * ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ * embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Gregory Cristian ( Semtech )
+ *
+ * \author Daniel Jaeckle ( STACKFORCE )
+*/
+#include "utilities.h"
+
+#include "RegionCommon.h"
+#include "RegionEU868.h"
+#include "lorawan_conf.h"
+#include "mw_log_conf.h"
+
+// Definitions
+#define CHANNELS_MASK_SIZE 1
+
+/*!
+ * Region specific context
+ */
+typedef struct sRegionEU868NvmCtx
+{
+ /*!
+ * LoRaMAC channels
+ */
+ ChannelParams_t Channels[ EU868_MAX_NB_CHANNELS ];
+ /*!
+ * LoRaMac bands
+ */
+ Band_t Bands[ EU868_MAX_NB_BANDS ];
+ /*!
+ * LoRaMac channels mask
+ */
+ uint16_t ChannelsMask[ CHANNELS_MASK_SIZE ];
+ /*!
+ * LoRaMac channels default mask
+ */
+ uint16_t ChannelsDefaultMask[ CHANNELS_MASK_SIZE ];
+}RegionEU868NvmCtx_t;
+
+/*
+ * Non-volatile module context.
+ */
+static RegionEU868NvmCtx_t NvmCtx;
+
+// 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 VerifyRfFreq( 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 = 5;
+ }
+ 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_MAX_TX_POWER:
+ {
+ phyParam.Value = EU868_MAX_TX_POWER;
+ break;
+ }
+ case PHY_DEF_TX_POWER:
+ {
+ phyParam.Value = EU868_DEFAULT_TX_POWER;
+ break;
+ }
+ case PHY_DEF_ADR_ACK_LIMIT:
+ {
+ phyParam.Value = EU868_ADR_ACK_LIMIT;
+ break;
+ }
+ case PHY_DEF_ADR_ACK_DELAY:
+ {
+ phyParam.Value = EU868_ADR_ACK_DELAY;
+ 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 = NvmCtx.ChannelsMask;
+ break;
+ }
+ case PHY_CHANNELS_DEFAULT_MASK:
+ {
+ phyParam.ChannelsMask = NvmCtx.ChannelsDefaultMask;
+ break;
+ }
+ case PHY_MAX_NB_CHANNELS:
+ {
+ phyParam.Value = EU868_MAX_NB_CHANNELS;
+ break;
+ }
+ case PHY_CHANNELS:
+ {
+ phyParam.Channels = NvmCtx.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_BEACON_CHANNEL_FREQ:
+ {
+ phyParam.Value = EU868_BEACON_CHANNEL_FREQ;
+ break;
+ }
+ case PHY_BEACON_FORMAT:
+ {
+ phyParam.BeaconFormat.BeaconSize = EU868_BEACON_SIZE;
+ phyParam.BeaconFormat.Rfu1Size = EU868_RFU1_SIZE;
+ phyParam.BeaconFormat.Rfu2Size = EU868_RFU2_SIZE;
+ break;
+ }
+ case PHY_BEACON_CHANNEL_DR:
+ {
+ phyParam.Value = EU868_BEACON_CHANNEL_DR;
+ break;
+ }
+ case PHY_PING_SLOT_CHANNEL_DR:
+ {
+ phyParam.Value = EU868_PING_SLOT_CHANNEL_DR;
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ return phyParam;
+}
+
+void RegionEU868SetBandTxDone( SetBandTxDoneParams_t* txDone )
+{
+ RegionCommonSetBandTxDone( txDone->Joined, &NvmCtx.Bands[NvmCtx.Channels[txDone->Channel].Band], txDone->LastTxDoneTime );
+}
+
+void RegionEU868InitDefaults( InitDefaultsParams_t* params )
+{
+ Band_t bands[EU868_MAX_NB_BANDS] =
+ {
+ EU868_BAND0,
+ EU868_BAND1,
+ EU868_BAND2,
+ EU868_BAND3,
+ EU868_BAND4,
+ EU868_BAND5,
+ };
+
+ switch( params->Type )
+ {
+ case INIT_TYPE_INIT:
+ {
+ // Initialize bands
+ memcpy1( ( uint8_t* )NvmCtx.Bands, ( uint8_t* )bands, sizeof( Band_t ) * EU868_MAX_NB_BANDS );
+
+ // Channels
+ NvmCtx.Channels[0] = ( ChannelParams_t ) EU868_LC1;
+ NvmCtx.Channels[1] = ( ChannelParams_t ) EU868_LC2;
+ NvmCtx.Channels[2] = ( ChannelParams_t ) EU868_LC3;
+
+ // Initialize the channels default mask
+ NvmCtx.ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 );
+ // Update the channels mask
+ RegionCommonChanMaskCopy( NvmCtx.ChannelsMask, NvmCtx.ChannelsDefaultMask, 1 );
+ break;
+ }
+ case INIT_TYPE_RESTORE_CTX:
+ {
+ if( params->NvmCtx != 0 )
+ {
+ memcpy1( (uint8_t*) &NvmCtx, (uint8_t*) params->NvmCtx, sizeof( NvmCtx ) );
+ }
+ break;
+ }
+ case INIT_TYPE_RESTORE_DEFAULT_CHANNELS:
+ {
+ // Restore channels default mask
+ NvmCtx.ChannelsMask[0] |= NvmCtx.ChannelsDefaultMask[0];
+
+ // Channels
+ NvmCtx.Channels[0] = ( ChannelParams_t ) EU868_LC1;
+ NvmCtx.Channels[1] = ( ChannelParams_t ) EU868_LC2;
+ NvmCtx.Channels[2] = ( ChannelParams_t ) EU868_LC3;
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+void* RegionEU868GetNvmCtx( GetNvmCtxParams_t* params )
+{
+ params->nvmCtxSize = sizeof( RegionEU868NvmCtx_t );
+ return &NvmCtx;
+}
+
+bool RegionEU868Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute )
+{
+ switch( phyAttribute )
+ {
+ case PHY_FREQUENCY:
+ {
+ uint8_t band = 0;
+ return VerifyRfFreq( verify->Frequency, &band );
+ }
+ 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;
+ }
+ default:
+ return false;
+ }
+}
+
+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 CFListType must be 0 to indicate the CFList contains a list of frequencies
+ if( applyCFList->Payload[15] != 0 )
+ {
+ 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( NvmCtx.ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 );
+ break;
+ }
+ case CHANNELS_DEFAULT_MASK:
+ {
+ RegionCommonChanMaskCopy( NvmCtx.ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 );
+ break;
+ }
+ default:
+ return false;
+ }
+ return true;
+}
+
+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.GetWakeupTime( ), &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;
+ const char *slotStrings[] = { "1", "2", "C", "Multi_C", "P", "Multi_P" };
+
+ if( Radio.GetStatus( ) != RF_IDLE )
+ {
+ return false;
+ }
+
+ if( rxConfig->RxSlot == RX_SLOT_WIN_1 )
+ {
+ // Apply window 1 frequency
+ frequency = NvmCtx.Channels[rxConfig->Channel].Frequency;
+ // Apply the alternative RX 1 window frequency, if it is available
+ if( NvmCtx.Channels[rxConfig->Channel].Rx1Frequency != 0 )
+ {
+ frequency = NvmCtx.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 );
+ if ( rxConfig->RxSlot < RX_SLOT_NONE )
+ {
+ MW_LOG( "RX_%s on freq %d Hz at DR %d\n\r", slotStrings[rxConfig->RxSlot], frequency, dr );
+ }
+ else
+ {
+ MW_LOG( "RX on freq %d Hz at DR %d\n\r", frequency, dr );
+ }
+
+ *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, NvmCtx.Bands[NvmCtx.Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, NvmCtx.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( NvmCtx.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, 4000 );
+ }
+ else
+ {
+ modem = MODEM_LORA;
+ Radio.SetTxConfig( modem, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 4000 );
+ }
+ MW_LOG( "TX on freq %d Hz at DR %d\n\r", NvmCtx.Channels[txConfig->Channel].Frequency, txConfig->Datarate );
+
+ // 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( NvmCtx.Channels[i].Frequency != 0 )
+ {
+ chMask |= 1 << i;
+ }
+ }
+ else
+ {
+ if( ( ( chMask & ( 1 << i ) ) != 0 ) &&
+ ( NvmCtx.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 = NvmCtx.Channels;
+ linkAdrVerifyParams.MinTxPower = EU868_MIN_TX_POWER;
+ linkAdrVerifyParams.MaxTxPower = EU868_MAX_TX_POWER;
+ linkAdrVerifyParams.Version = linkAdrReq->Version;
+
+ // 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
+ memset1( ( uint8_t* ) NvmCtx.ChannelsMask, 0, sizeof( NvmCtx.ChannelsMask ) );
+ // Update the channels mask
+ NvmCtx.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;
+ uint8_t band = 0;
+
+ // Verify radio frequency
+ if( VerifyRfFreq( rxParamSetupReq->Frequency, &band ) == 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( VerifyRfFreq( dlChannelReq->Rx1Frequency, &band ) == false )
+ {
+ status &= 0xFE;
+ }
+
+ // Verify if an uplink frequency exists
+ if( NvmCtx.Channels[dlChannelReq->ChannelId].Frequency == 0 )
+ {
+ status &= 0xFD;
+ }
+
+ // Apply Rx1 frequency, if the status is OK
+ if( status == 0x03 )
+ {
+ NvmCtx.Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency;
+ }
+
+ return status;
+}
+
+int8_t RegionEU868AlternateDr( int8_t currentDr, AlternateDrType_t type )
+{
+ return currentDr;
+}
+
+void RegionEU868CalcBackOff( CalcBackOffParams_t* calcBackOff )
+{
+ RegionCommonCalcBackOffParams_t calcBackOffParams;
+
+ calcBackOffParams.Channels = NvmCtx.Channels;
+ calcBackOffParams.Bands = NvmCtx.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 );
+}
+
+LoRaMacStatus_t 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( NvmCtx.ChannelsMask, 0, 1 ) == 0 )
+ { // Reactivate default channels
+ NvmCtx.ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 );
+ }
+
+ TimerTime_t elapsed = TimerGetElapsedTime( nextChanParams->LastAggrTx );
+ if( ( nextChanParams->LastAggrTx == 0 ) || ( nextChanParams->AggrTimeOff <= elapsed ) )
+ {
+ // Reset Aggregated time off
+ *aggregatedTimeOff = 0;
+
+ // Update bands Time OFF
+ nextTxDelay = RegionCommonUpdateBandTimeOff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, NvmCtx.Bands, EU868_MAX_NB_BANDS );
+
+ // Search how many channels are enabled
+ nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Joined, nextChanParams->Datarate,
+ NvmCtx.ChannelsMask, NvmCtx.Channels,
+ NvmCtx.Bands, enabledChannels, &delayTx );
+ }
+ else
+ {
+ delayTx++;
+ nextTxDelay = nextChanParams->AggrTimeOff - elapsed;
+ }
+
+ if( nbEnabledChannels > 0 )
+ {
+ // We found a valid channel
+ *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )];
+
+ *time = 0;
+ return LORAMAC_STATUS_OK;
+ }
+ else
+ {
+ if( delayTx > 0 )
+ {
+ // Delay transmission due to AggregatedTimeOff or to a band time off
+ *time = nextTxDelay;
+ return LORAMAC_STATUS_DUTYCYCLE_RESTRICTED;
+ }
+ // Datarate not supported by any channel, restore defaults
+ NvmCtx.ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 );
+ *time = 0;
+ return LORAMAC_STATUS_NO_CHANNEL_FOUND;
+ }
+}
+
+LoRaMacStatus_t RegionEU868ChannelAdd( ChannelAddParams_t* channelAdd )
+{
+ uint8_t band = 0;
+ bool drInvalid = false;
+ bool freqInvalid = false;
+ uint8_t id = channelAdd->ChannelId;
+
+ if( id < EU868_NUMB_DEFAULT_CHANNELS )
+ {
+ return LORAMAC_STATUS_FREQ_AND_DR_INVALID;
+ }
+
+ 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;
+ }
+
+ // Check frequency
+ if( freqInvalid == false )
+ {
+ if( VerifyRfFreq( 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;
+ }
+
+ memcpy1( ( uint8_t* ) &(NvmCtx.Channels[id]), ( uint8_t* ) channelAdd->NewChannel, sizeof( NvmCtx.Channels[id] ) );
+ NvmCtx.Channels[id].Band = band;
+ NvmCtx.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
+ NvmCtx.Channels[id] = ( ChannelParams_t ){ 0, 0, { 0 }, 0 };
+
+ return RegionCommonChanDisable( NvmCtx.ChannelsMask, id, EU868_MAX_NB_CHANNELS );
+}
+
+void RegionEU868SetContinuousWave( ContinuousWaveParams_t* continuousWave )
+{
+ int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, NvmCtx.Bands[NvmCtx.Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, NvmCtx.ChannelsMask );
+ int8_t phyTxPower = 0;
+ uint32_t frequency = NvmCtx.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 )
+{
+ RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup;
+
+ regionCommonRxBeaconSetup.Datarates = DataratesEU868;
+ regionCommonRxBeaconSetup.Frequency = rxBeaconSetup->Frequency;
+ regionCommonRxBeaconSetup.BeaconSize = EU868_BEACON_SIZE;
+ regionCommonRxBeaconSetup.BeaconDatarate = EU868_BEACON_CHANNEL_DR;
+ regionCommonRxBeaconSetup.BeaconChannelBW = EU868_BEACON_CHANNEL_BW;
+ regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime;
+ regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout;
+
+ RegionCommonRxBeaconSetup( ®ionCommonRxBeaconSetup );
+
+ // Store downlink datarate
+ *outDr = EU868_BEACON_CHANNEL_DR;
+}
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionEU868.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionEU868.h
new file mode 100644
index 00000000..3c1d4ed5
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionEU868.h
@@ -0,0 +1,536 @@
+/*!
+ * \file RegionEU868.h
+ *
+ * \brief Region definition for EU868
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013-2017 Semtech
+ *
+ * ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ * embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Gregory Cristian ( Semtech )
+ *
+ * \author Daniel Jaeckle ( STACKFORCE )
+ *
+ * \author Johannes Bruder ( STACKFORCE )
+ *
+ * \defgroup REGIONEU868 Region EU868
+ * Implementation according to LoRaWAN Specification v1.0.2.
+ * \{
+ */
+#ifndef __REGION_EU868_H__
+#define __REGION_EU868_H__
+
+#include "region/Region.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 frequency
+ */
+#define EU868_BEACON_CHANNEL_FREQ 869525000
+
+/*!
+ * Payload size of a beacon frame
+ */
+#define EU868_BEACON_SIZE 17
+
+/*!
+ * Size of RFU 1 field
+ */
+#define EU868_RFU1_SIZE 2
+
+/*!
+ * Size of RFU 2 field
+ */
+#define EU868_RFU2_SIZE 0
+
+/*!
+ * Datarate of the beacon channel
+ */
+#define EU868_BEACON_CHANNEL_DR DR_3
+
+/*!
+ * Bandwith of the beacon channel
+ */
+#define EU868_BEACON_CHANNEL_BW 0
+
+/*!
+ * Ping slot channel datarate
+ */
+#define EU868_PING_SLOT_CHANNEL_DR DR_3
+
+/*!
+ * Maximum number of bands
+ */
+#define EU868_MAX_NB_BANDS 6
+
+/*!
+ * Band 0 definition
+ * { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff }
+ */
+#define EU868_BAND0 { 100 , EU868_MAX_TX_POWER, 0, 0, 0 } // 1.0 %
+
+/*!
+ * Band 1 definition
+ * { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff }
+ */
+#define EU868_BAND1 { 100 , EU868_MAX_TX_POWER, 0, 0, 0 } // 1.0 %
+
+/*!
+ * Band 2 definition
+ * Band = { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff }
+ */
+#define EU868_BAND2 { 1000, EU868_MAX_TX_POWER, 0, 0, 0 } // 0.1 %
+
+/*!
+ * Band 3 definition
+ * Band = { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff }
+ */
+#define EU868_BAND3 { 10 , EU868_MAX_TX_POWER, 0, 0, 0 } // 10.0 %
+
+/*!
+ * Band 4 definition
+ * Band = { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff }
+ */
+#define EU868_BAND4 { 100 , EU868_MAX_TX_POWER, 0, 0, 0 } // 1.0 %
+
+/*!
+ * Band 5 definition
+ * Band = { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff }
+ */
+#define EU868_BAND5 { 1000, EU868_MAX_TX_POWER, 0, 0, 0 } // 0.1 %
+
+/*!
+ * 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( InitDefaultsParams_t* params );
+
+/*!
+ * \brief Returns a pointer to the internal context and its size.
+ *
+ * \param [OUT] params Pointer to the function parameters.
+ *
+ * \retval Points to a structure where the module store its non-volatile context.
+ */
+void* RegionEU868GetNvmCtx( GetNvmCtxParams_t* params );
+
+/*!
+ * \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 );
+
+/*!
+ * 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] currentDr Current datarate.
+ *
+ * \retval Datarate to apply.
+ */
+int8_t RegionEU868AlternateDr( int8_t currentDr, AlternateDrType_t type );
+
+/*!
+ * \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]
+ */
+LoRaMacStatus_t 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 );
+
+/*!
+ * \brief Sets the radio into beacon reception mode
+ *
+ * \param [IN] rxBeaconSetup Pointer to the function parameters
+ */
+void RegionEU868RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr );
+
+/*! \} defgroup REGIONEU868 */
+
+#endif // __REGION_EU868_H__
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionIN865.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionIN865.c
new file mode 100644
index 00000000..4a4cbfbe
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionIN865.c
@@ -0,0 +1,1060 @@
+/*!
+ * \file RegionIN865.c
+ *
+ * \brief Region implementation for IN865
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013-2017 Semtech
+ *
+ * ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ * embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Gregory Cristian ( Semtech )
+ *
+ * \author Daniel Jaeckle ( STACKFORCE )
+*/
+#include "utilities.h"
+
+#include "RegionCommon.h"
+#include "RegionIN865.h"
+#include "lorawan_conf.h"
+#include "mw_log_conf.h"
+
+// Definitions
+#define CHANNELS_MASK_SIZE 1
+
+/*!
+ * Region specific context
+ */
+typedef struct sRegionIN865NvmCtx
+{
+ /*!
+ * LoRaMAC channels
+ */
+ ChannelParams_t Channels[ IN865_MAX_NB_CHANNELS ];
+ /*!
+ * LoRaMac bands
+ */
+ Band_t Bands[ IN865_MAX_NB_BANDS ];
+ /*!
+ * LoRaMac channels mask
+ */
+ uint16_t ChannelsMask[ CHANNELS_MASK_SIZE ];
+ /*!
+ * LoRaMac channels default mask
+ */
+ uint16_t ChannelsDefaultMask[ CHANNELS_MASK_SIZE ];
+}RegionIN865NvmCtx_t;
+
+/*
+ * Non-volatile module context.
+ */
+static RegionIN865NvmCtx_t NvmCtx;
+
+// 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 VerifyRfFreq( uint32_t freq )
+{
+ // 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_MAX_TX_POWER:
+ {
+ phyParam.Value = IN865_MAX_TX_POWER;
+ break;
+ }
+ case PHY_DEF_TX_POWER:
+ {
+ phyParam.Value = IN865_DEFAULT_TX_POWER;
+ break;
+ }
+ case PHY_DEF_ADR_ACK_LIMIT:
+ {
+ phyParam.Value = IN865_ADR_ACK_LIMIT;
+ break;
+ }
+ case PHY_DEF_ADR_ACK_DELAY:
+ {
+ phyParam.Value = IN865_ADR_ACK_DELAY;
+ 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 = NvmCtx.ChannelsMask;
+ break;
+ }
+ case PHY_CHANNELS_DEFAULT_MASK:
+ {
+ phyParam.ChannelsMask = NvmCtx.ChannelsDefaultMask;
+ break;
+ }
+ case PHY_MAX_NB_CHANNELS:
+ {
+ phyParam.Value = IN865_MAX_NB_CHANNELS;
+ break;
+ }
+ case PHY_CHANNELS:
+ {
+ phyParam.Channels = NvmCtx.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_BEACON_CHANNEL_FREQ:
+ {
+ phyParam.Value = IN865_BEACON_CHANNEL_FREQ;
+ break;
+ }
+ case PHY_BEACON_FORMAT:
+ {
+ phyParam.BeaconFormat.BeaconSize = IN865_BEACON_SIZE;
+ phyParam.BeaconFormat.Rfu1Size = IN865_RFU1_SIZE;
+ phyParam.BeaconFormat.Rfu2Size = IN865_RFU2_SIZE;
+ break;
+ }
+ case PHY_BEACON_CHANNEL_DR:
+ {
+ phyParam.Value = IN865_BEACON_CHANNEL_DR;
+ break;
+ }
+ case PHY_PING_SLOT_CHANNEL_DR:
+ {
+ phyParam.Value = IN865_PING_SLOT_CHANNEL_DR;
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ return phyParam;
+}
+
+void RegionIN865SetBandTxDone( SetBandTxDoneParams_t* txDone )
+{
+ RegionCommonSetBandTxDone( txDone->Joined, &NvmCtx.Bands[NvmCtx.Channels[txDone->Channel].Band], txDone->LastTxDoneTime );
+}
+
+void RegionIN865InitDefaults( InitDefaultsParams_t* params )
+{
+ Band_t bands[IN865_MAX_NB_BANDS] =
+ {
+ IN865_BAND0
+ };
+
+ switch( params->Type )
+ {
+ case INIT_TYPE_INIT:
+ {
+ // Initialize bands
+ memcpy1( ( uint8_t* )NvmCtx.Bands, ( uint8_t* )bands, sizeof( Band_t ) * IN865_MAX_NB_BANDS );
+
+ // Channels
+ NvmCtx.Channels[0] = ( ChannelParams_t ) IN865_LC1;
+ NvmCtx.Channels[1] = ( ChannelParams_t ) IN865_LC2;
+ NvmCtx.Channels[2] = ( ChannelParams_t ) IN865_LC3;
+
+ // Initialize the channels default mask
+ NvmCtx.ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 );
+ // Update the channels mask
+ RegionCommonChanMaskCopy( NvmCtx.ChannelsMask, NvmCtx.ChannelsDefaultMask, 1 );
+ break;
+ }
+ case INIT_TYPE_RESTORE_CTX:
+ {
+ if( params->NvmCtx != 0 )
+ {
+ memcpy1( (uint8_t*) &NvmCtx, (uint8_t*) params->NvmCtx, sizeof( NvmCtx ) );
+ }
+ break;
+ }
+ case INIT_TYPE_RESTORE_DEFAULT_CHANNELS:
+ {
+ // Restore channels default mask
+ NvmCtx.ChannelsMask[0] |= NvmCtx.ChannelsDefaultMask[0];
+
+ // Channels
+ NvmCtx.Channels[0] = ( ChannelParams_t ) IN865_LC1;
+ NvmCtx.Channels[1] = ( ChannelParams_t ) IN865_LC2;
+ NvmCtx.Channels[2] = ( ChannelParams_t ) IN865_LC3;
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+void* RegionIN865GetNvmCtx( GetNvmCtxParams_t* params )
+{
+ params->nvmCtxSize = sizeof( RegionIN865NvmCtx_t );
+ return &NvmCtx;
+}
+
+bool RegionIN865Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute )
+{
+ switch( phyAttribute )
+ {
+ case PHY_FREQUENCY:
+ {
+ return VerifyRfFreq( verify->Frequency );
+ }
+ case PHY_TX_DR:
+ {
+ if( verify->DatarateParams.Datarate == DR_6 )
+ {// DR_6 is not supported by this region
+ return false;
+ }
+ else
+ {
+ 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:
+ {
+ if( verify->DatarateParams.Datarate == DR_6 )
+ {// DR_6 is not supported by this region
+ return false;
+ }
+ else
+ {
+ 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;
+ }
+ default:
+ return false;
+ }
+}
+
+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 CFListType must be 0 to indicate the CFList contains a list of frequencies
+ if( applyCFList->Payload[15] != 0 )
+ {
+ 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( NvmCtx.ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 );
+ break;
+ }
+ case CHANNELS_DEFAULT_MASK:
+ {
+ RegionCommonChanMaskCopy( NvmCtx.ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 );
+ break;
+ }
+ default:
+ return false;
+ }
+ return true;
+}
+
+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.GetWakeupTime( ), &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;
+ const char *slotStrings[] = { "1", "2", "C", "Multi_C", "P", "Multi_P" };
+
+ if( Radio.GetStatus( ) != RF_IDLE )
+ {
+ return false;
+ }
+
+ if( rxConfig->RxSlot == RX_SLOT_WIN_1 )
+ {
+ // Apply window 1 frequency
+ frequency = NvmCtx.Channels[rxConfig->Channel].Frequency;
+ // Apply the alternative RX 1 window frequency, if it is available
+ if( NvmCtx.Channels[rxConfig->Channel].Rx1Frequency != 0 )
+ {
+ frequency = NvmCtx.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 );
+ if ( rxConfig->RxSlot < RX_SLOT_NONE )
+ {
+ MW_LOG( "RX_%s on freq %d Hz at DR %d\n\r", slotStrings[rxConfig->RxSlot], frequency, dr );
+ }
+ else
+ {
+ MW_LOG( "RX on freq %d Hz at DR %d\n\r", frequency, dr );
+ }
+
+ *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, NvmCtx.Bands[NvmCtx.Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, NvmCtx.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( NvmCtx.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, 4000 );
+ }
+ else
+ {
+ modem = MODEM_LORA;
+ Radio.SetTxConfig( modem, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 4000 );
+ }
+ MW_LOG( "TX on freq %d Hz at DR %d\n\r", NvmCtx.Channels[txConfig->Channel].Frequency, txConfig->Datarate );
+
+ // 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( NvmCtx.Channels[i].Frequency != 0 )
+ {
+ chMask |= 1 << i;
+ }
+ }
+ else
+ {
+ if( ( ( chMask & ( 1 << i ) ) != 0 ) &&
+ ( NvmCtx.Channels[i].Frequency == 0 ) )
+ {// Trying to enable an undefined channel
+ status &= 0xFE; // Channel mask KO
+ }
+ }
+ }
+ }
+ }
+
+ if( linkAdrParams.Datarate != DR_6 )
+ {
+ // 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 = NvmCtx.Channels;
+ linkAdrVerifyParams.MinTxPower = IN865_MIN_TX_POWER;
+ linkAdrVerifyParams.MaxTxPower = IN865_MAX_TX_POWER;
+ linkAdrVerifyParams.Version = linkAdrReq->Version;
+
+ // Verify the parameters and update, if necessary
+ status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep );
+ }
+ else
+ {// DR_6 is not supported by this region
+ status &= 0xFD; // Datarate KO
+ }
+
+ // Update channelsMask if everything is correct
+ if( status == 0x07 )
+ {
+ // Set the channels mask to a default value
+ memset1( ( uint8_t* ) NvmCtx.ChannelsMask, 0, sizeof( NvmCtx.ChannelsMask ) );
+ // Update the channels mask
+ NvmCtx.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( VerifyRfFreq( 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;
+
+ // Verify if the frequency is supported
+ if( VerifyRfFreq( dlChannelReq->Rx1Frequency ) == false )
+ {
+ status &= 0xFE;
+ }
+
+ // Verify if an uplink frequency exists
+ if( NvmCtx.Channels[dlChannelReq->ChannelId].Frequency == 0 )
+ {
+ status &= 0xFD;
+ }
+
+ // Apply Rx1 frequency, if the status is OK
+ if( status == 0x03 )
+ {
+ NvmCtx.Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency;
+ }
+
+ return status;
+}
+
+int8_t RegionIN865AlternateDr( int8_t currentDr, AlternateDrType_t type )
+{
+ return currentDr;
+}
+
+void RegionIN865CalcBackOff( CalcBackOffParams_t* calcBackOff )
+{
+ RegionCommonCalcBackOffParams_t calcBackOffParams;
+
+ calcBackOffParams.Channels = NvmCtx.Channels;
+ calcBackOffParams.Bands = NvmCtx.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 );
+}
+
+LoRaMacStatus_t 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( NvmCtx.ChannelsMask, 0, 1 ) == 0 )
+ { // Reactivate default channels
+ NvmCtx.ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 );
+ }
+
+ TimerTime_t elapsed = TimerGetElapsedTime( nextChanParams->LastAggrTx );
+ if( ( nextChanParams->LastAggrTx == 0 ) || ( nextChanParams->AggrTimeOff <= elapsed ) )
+ {
+ // Reset Aggregated time off
+ *aggregatedTimeOff = 0;
+
+ // Update bands Time OFF
+ nextTxDelay = RegionCommonUpdateBandTimeOff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, NvmCtx.Bands, IN865_MAX_NB_BANDS );
+
+ // Search how many channels are enabled
+ nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Joined, nextChanParams->Datarate,
+ NvmCtx.ChannelsMask, NvmCtx.Channels,
+ NvmCtx.Bands, enabledChannels, &delayTx );
+ }
+ else
+ {
+ delayTx++;
+ nextTxDelay = nextChanParams->AggrTimeOff - elapsed;
+ }
+
+ if( nbEnabledChannels > 0 )
+ {
+ // We found a valid channel
+ *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )];
+
+ *time = 0;
+ return LORAMAC_STATUS_OK;
+ }
+ else
+ {
+ if( delayTx > 0 )
+ {
+ // Delay transmission due to AggregatedTimeOff or to a band time off
+ *time = nextTxDelay;
+ return LORAMAC_STATUS_DUTYCYCLE_RESTRICTED;
+ }
+ // Datarate not supported by any channel, restore defaults
+ NvmCtx.ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 );
+ *time = 0;
+ return LORAMAC_STATUS_NO_CHANNEL_FOUND;
+ }
+}
+
+LoRaMacStatus_t RegionIN865ChannelAdd( ChannelAddParams_t* channelAdd )
+{
+ bool drInvalid = false;
+ bool freqInvalid = false;
+ uint8_t id = channelAdd->ChannelId;
+
+ if( id < IN865_NUMB_DEFAULT_CHANNELS )
+ {
+ return LORAMAC_STATUS_FREQ_AND_DR_INVALID;
+ }
+
+ 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;
+ }
+
+ // Check frequency
+ if( freqInvalid == false )
+ {
+ if( VerifyRfFreq( 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;
+ }
+
+ memcpy1( ( uint8_t* ) &(NvmCtx.Channels[id]), ( uint8_t* ) channelAdd->NewChannel, sizeof( NvmCtx.Channels[id] ) );
+ NvmCtx.Channels[id].Band = 0;
+ NvmCtx.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
+ NvmCtx.Channels[id] = ( ChannelParams_t ){ 0, 0, { 0 }, 0 };
+
+ return RegionCommonChanDisable( NvmCtx.ChannelsMask, id, IN865_MAX_NB_CHANNELS );
+}
+
+void RegionIN865SetContinuousWave( ContinuousWaveParams_t* continuousWave )
+{
+ int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, NvmCtx.Bands[NvmCtx.Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, NvmCtx.ChannelsMask );
+ int8_t phyTxPower = 0;
+ uint32_t frequency = NvmCtx.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 )
+{
+ RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup;
+
+ regionCommonRxBeaconSetup.Datarates = DataratesIN865;
+ regionCommonRxBeaconSetup.Frequency = rxBeaconSetup->Frequency;
+ regionCommonRxBeaconSetup.BeaconSize = IN865_BEACON_SIZE;
+ regionCommonRxBeaconSetup.BeaconDatarate = IN865_BEACON_CHANNEL_DR;
+ regionCommonRxBeaconSetup.BeaconChannelBW = IN865_BEACON_CHANNEL_BW;
+ regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime;
+ regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout;
+
+ RegionCommonRxBeaconSetup( ®ionCommonRxBeaconSetup );
+
+ // Store downlink datarate
+ *outDr = IN865_BEACON_CHANNEL_DR;
+}
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionIN865.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionIN865.h
new file mode 100644
index 00000000..60c871e6
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionIN865.h
@@ -0,0 +1,511 @@
+/*!
+ * \file RegionIN865.h
+ *
+ * \brief Region definition for IN865
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013-2017 Semtech
+ *
+ * ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ * embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Gregory Cristian ( Semtech )
+ *
+ * \author Daniel Jaeckle ( STACKFORCE )
+ *
+ * \author Johannes Bruder ( STACKFORCE )
+ *
+ * \defgroup REGIONIN865 Region IN865
+ * Implementation according to LoRaWAN Specification v1.0.2.
+ * \{
+ */
+#ifndef __REGION_IN865_H__
+#define __REGION_IN865_H__
+
+#include "region/Region.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 frequency
+ */
+#define IN865_BEACON_CHANNEL_FREQ 866550000
+
+/*!
+ * Payload size of a beacon frame
+ */
+#define IN865_BEACON_SIZE 19
+
+/*!
+ * Size of RFU 1 field
+ */
+#define IN865_RFU1_SIZE 1
+
+/*!
+ * Size of RFU 2 field
+ */
+#define IN865_RFU2_SIZE 3
+
+/*!
+ * Datarate of the beacon channel
+ */
+#define IN865_BEACON_CHANNEL_DR DR_4
+
+/*!
+ * Bandwith of the beacon channel
+ */
+#define IN865_BEACON_CHANNEL_BW 0
+
+/*!
+ * Ping slot channel datarate
+ */
+#define IN865_PING_SLOT_CHANNEL_DR DR_4
+
+/*!
+ * Maximum number of bands
+ */
+#define IN865_MAX_NB_BANDS 1
+
+/*!
+ * Band 0 definition
+ * { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff }
+ */
+#define IN865_BAND0 { 1 , IN865_MAX_TX_POWER, 0, 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( InitDefaultsParams_t* params );
+
+/*!
+ * \brief Returns a pointer to the internal context and its size.
+ *
+ * \param [OUT] params Pointer to the function parameters.
+ *
+ * \retval Points to a structure where the module store its non-volatile context.
+ */
+void* RegionIN865GetNvmCtx( GetNvmCtxParams_t* params );
+
+/*!
+ * \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 );
+
+/*!
+ * 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] currentDr Current datarate.
+ *
+ * \retval Datarate to apply.
+ */
+int8_t RegionIN865AlternateDr( int8_t currentDr, AlternateDrType_t type );
+
+/*!
+ * \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]
+ */
+LoRaMacStatus_t 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 );
+
+/*!
+ * \brief Sets the radio into beacon reception mode
+ *
+ * \param [IN] rxBeaconSetup Pointer to the function parameters
+ */
+ void RegionIN865RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr );
+
+/*! \} defgroup REGIONIN865 */
+
+#endif // __REGION_IN865_H__
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionKR920.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionKR920.c
new file mode 100644
index 00000000..4a2647de
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionKR920.c
@@ -0,0 +1,1050 @@
+/*!
+ * \file RegionKR920.c
+ *
+ * \brief Region implementation for KR920
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013-2017 Semtech
+ *
+ * ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ * embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Gregory Cristian ( Semtech )
+ *
+ * \author Daniel Jaeckle ( STACKFORCE )
+*/
+#include "utilities.h"
+
+#include "RegionCommon.h"
+#include "RegionKR920.h"
+#include "lorawan_conf.h"
+#include "mw_log_conf.h"
+
+// Definitions
+#define CHANNELS_MASK_SIZE 1
+
+/*!
+ * Region specific context
+ */
+typedef struct sRegionKR920NvmCtx
+{
+ /*!
+ * LoRaMAC channels
+ */
+ ChannelParams_t Channels[ KR920_MAX_NB_CHANNELS ];
+ /*!
+ * LoRaMac bands
+ */
+ Band_t Bands[ KR920_MAX_NB_BANDS ];
+ /*!
+ * LoRaMac channels mask
+ */
+ uint16_t ChannelsMask[ CHANNELS_MASK_SIZE ];
+ /*!
+ * LoRaMac channels default mask
+ */
+ uint16_t ChannelsDefaultMask[ CHANNELS_MASK_SIZE ];
+}RegionKR920NvmCtx_t;
+
+/*
+ * Non-volatile module context.
+ */
+static RegionKR920NvmCtx_t NvmCtx;
+
+// 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 VerifyRfFreq( 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_MAX_TX_POWER:
+ {
+ phyParam.Value = KR920_MAX_TX_POWER;
+ break;
+ }
+ case PHY_DEF_TX_POWER:
+ {
+ phyParam.Value = KR920_DEFAULT_TX_POWER;
+ break;
+ }
+ case PHY_DEF_ADR_ACK_LIMIT:
+ {
+ phyParam.Value = KR920_ADR_ACK_LIMIT;
+ break;
+ }
+ case PHY_DEF_ADR_ACK_DELAY:
+ {
+ phyParam.Value = KR920_ADR_ACK_DELAY;
+ 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 = NvmCtx.ChannelsMask;
+ break;
+ }
+ case PHY_CHANNELS_DEFAULT_MASK:
+ {
+ phyParam.ChannelsMask = NvmCtx.ChannelsDefaultMask;
+ break;
+ }
+ case PHY_MAX_NB_CHANNELS:
+ {
+ phyParam.Value = KR920_MAX_NB_CHANNELS;
+ break;
+ }
+ case PHY_CHANNELS:
+ {
+ phyParam.Channels = NvmCtx.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_BEACON_CHANNEL_FREQ:
+ {
+ phyParam.Value = KR920_BEACON_CHANNEL_FREQ;
+ break;
+ }
+ case PHY_BEACON_FORMAT:
+ {
+ phyParam.BeaconFormat.BeaconSize = KR920_BEACON_SIZE;
+ phyParam.BeaconFormat.Rfu1Size = KR920_RFU1_SIZE;
+ phyParam.BeaconFormat.Rfu2Size = KR920_RFU2_SIZE;
+ break;
+ }
+ case PHY_BEACON_CHANNEL_DR:
+ {
+ phyParam.Value = KR920_BEACON_CHANNEL_DR;
+ break;
+ }
+ case PHY_PING_SLOT_CHANNEL_DR:
+ {
+ phyParam.Value = KR920_PING_SLOT_CHANNEL_DR;
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ return phyParam;
+}
+
+void RegionKR920SetBandTxDone( SetBandTxDoneParams_t* txDone )
+{
+ RegionCommonSetBandTxDone( txDone->Joined, &NvmCtx.Bands[NvmCtx.Channels[txDone->Channel].Band], txDone->LastTxDoneTime );
+}
+
+void RegionKR920InitDefaults( InitDefaultsParams_t* params )
+{
+ Band_t bands[KR920_MAX_NB_BANDS] =
+ {
+ KR920_BAND0
+ };
+
+ switch( params->Type )
+ {
+ case INIT_TYPE_INIT:
+ {
+ // Initialize bands
+ memcpy1( ( uint8_t* )NvmCtx.Bands, ( uint8_t* )bands, sizeof( Band_t ) * KR920_MAX_NB_BANDS );
+
+ // Channels
+ NvmCtx.Channels[0] = ( ChannelParams_t ) KR920_LC1;
+ NvmCtx.Channels[1] = ( ChannelParams_t ) KR920_LC2;
+ NvmCtx.Channels[2] = ( ChannelParams_t ) KR920_LC3;
+
+ // Initialize the channels default mask
+ NvmCtx.ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 );
+ // Update the channels mask
+ RegionCommonChanMaskCopy( NvmCtx.ChannelsMask, NvmCtx.ChannelsDefaultMask, 1 );
+ break;
+ }
+ case INIT_TYPE_RESTORE_CTX:
+ {
+ if( params->NvmCtx != 0 )
+ {
+ memcpy1( (uint8_t*) &NvmCtx, (uint8_t*) params->NvmCtx, sizeof( NvmCtx ) );
+ }
+ break;
+ }
+ case INIT_TYPE_RESTORE_DEFAULT_CHANNELS:
+ {
+ // Restore channels default mask
+ NvmCtx.ChannelsMask[0] |= NvmCtx.ChannelsDefaultMask[0];
+
+ // Channels
+ NvmCtx.Channels[0] = ( ChannelParams_t ) KR920_LC1;
+ NvmCtx.Channels[1] = ( ChannelParams_t ) KR920_LC2;
+ NvmCtx.Channels[2] = ( ChannelParams_t ) KR920_LC3;
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+void* RegionKR920GetNvmCtx( GetNvmCtxParams_t* params )
+{
+ params->nvmCtxSize = sizeof( RegionKR920NvmCtx_t );
+ return &NvmCtx;
+}
+
+bool RegionKR920Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute )
+{
+ switch( phyAttribute )
+ {
+ case PHY_FREQUENCY:
+ {
+ return VerifyRfFreq( verify->Frequency );
+ }
+ 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;
+ }
+ default:
+ return false;
+ }
+}
+
+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 CFListType must be 0 to indicate the CFList contains a list of frequencies
+ if( applyCFList->Payload[15] != 0 )
+ {
+ 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( NvmCtx.ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 );
+ break;
+ }
+ case CHANNELS_DEFAULT_MASK:
+ {
+ RegionCommonChanMaskCopy( NvmCtx.ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 );
+ break;
+ }
+ default:
+ return false;
+ }
+ return true;
+}
+
+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.GetWakeupTime( ), &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;
+ const char *slotStrings[] = { "1", "2", "C", "Multi_C", "P", "Multi_P" };
+
+ if( Radio.GetStatus( ) != RF_IDLE )
+ {
+ return false;
+ }
+
+ if( rxConfig->RxSlot == RX_SLOT_WIN_1 )
+ {
+ // Apply window 1 frequency
+ frequency = NvmCtx.Channels[rxConfig->Channel].Frequency;
+ // Apply the alternative RX 1 window frequency, if it is available
+ if( NvmCtx.Channels[rxConfig->Channel].Rx1Frequency != 0 )
+ {
+ frequency = NvmCtx.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 );
+ if ( rxConfig->RxSlot < RX_SLOT_NONE )
+ {
+ MW_LOG( "RX_%s on freq %d Hz at DR %d\n\r", slotStrings[rxConfig->RxSlot], frequency, dr );
+ }
+ else
+ {
+ MW_LOG( "RX on freq %d Hz at DR %d\n\r", frequency, dr );
+ }
+
+ *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, NvmCtx.Bands[NvmCtx.Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, NvmCtx.ChannelsMask );
+ uint32_t bandwidth = GetBandwidth( txConfig->Datarate );
+ float maxEIRP = GetMaxEIRP( NvmCtx.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( NvmCtx.Channels[txConfig->Channel].Frequency );
+
+ Radio.SetTxConfig( MODEM_LORA, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 4000 );
+ MW_LOG( "TX on freq %d Hz at DR %d\n\r", NvmCtx.Channels[txConfig->Channel].Frequency, txConfig->Datarate );
+
+ // 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( NvmCtx.Channels[i].Frequency != 0 )
+ {
+ chMask |= 1 << i;
+ }
+ }
+ else
+ {
+ if( ( ( chMask & ( 1 << i ) ) != 0 ) &&
+ ( NvmCtx.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 = NvmCtx.Channels;
+ linkAdrVerifyParams.MinTxPower = KR920_MIN_TX_POWER;
+ linkAdrVerifyParams.MaxTxPower = KR920_MAX_TX_POWER;
+ linkAdrVerifyParams.Version = linkAdrReq->Version;
+
+ // 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
+ memset1( ( uint8_t* ) NvmCtx.ChannelsMask, 0, sizeof( NvmCtx.ChannelsMask ) );
+ // Update the channels mask
+ NvmCtx.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( VerifyRfFreq( 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( VerifyRfFreq( dlChannelReq->Rx1Frequency ) == false )
+ {
+ status &= 0xFE;
+ }
+
+ // Verify if an uplink frequency exists
+ if( NvmCtx.Channels[dlChannelReq->ChannelId].Frequency == 0 )
+ {
+ status &= 0xFD;
+ }
+
+ // Apply Rx1 frequency, if the status is OK
+ if( status == 0x03 )
+ {
+ NvmCtx.Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency;
+ }
+
+ return status;
+}
+
+int8_t RegionKR920AlternateDr( int8_t currentDr, AlternateDrType_t type )
+{
+ return currentDr;
+}
+
+void RegionKR920CalcBackOff( CalcBackOffParams_t* calcBackOff )
+{
+ RegionCommonCalcBackOffParams_t calcBackOffParams;
+
+ calcBackOffParams.Channels = NvmCtx.Channels;
+ calcBackOffParams.Bands = NvmCtx.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 );
+}
+
+LoRaMacStatus_t 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( NvmCtx.ChannelsMask, 0, 1 ) == 0 )
+ { // Reactivate default channels
+ NvmCtx.ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 );
+ }
+
+ TimerTime_t elapsed = TimerGetElapsedTime( nextChanParams->LastAggrTx );
+ if( ( nextChanParams->LastAggrTx == 0 ) || ( nextChanParams->AggrTimeOff <= elapsed ) )
+ {
+ // Reset Aggregated time off
+ *aggregatedTimeOff = 0;
+
+ // Update bands Time OFF
+ nextTxDelay = RegionCommonUpdateBandTimeOff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, NvmCtx.Bands, KR920_MAX_NB_BANDS );
+
+ // Search how many channels are enabled
+ nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Joined, nextChanParams->Datarate,
+ NvmCtx.ChannelsMask, NvmCtx.Channels,
+ NvmCtx.Bands, enabledChannels, &delayTx );
+ }
+ else
+ {
+ delayTx++;
+ nextTxDelay = nextChanParams->AggrTimeOff - elapsed;
+ }
+
+ 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, NvmCtx.Channels[channelNext].Frequency, KR920_RSSI_FREE_TH, KR920_CARRIER_SENSE_TIME ) == true )
+ {
+ // Free channel found
+ *channel = channelNext;
+ *time = 0;
+ return LORAMAC_STATUS_OK;
+ }
+ }
+ return LORAMAC_STATUS_NO_FREE_CHANNEL_FOUND;
+ }
+ else
+ {
+ if( delayTx > 0 )
+ {
+ // Delay transmission due to AggregatedTimeOff or to a band time off
+ *time = nextTxDelay;
+ return LORAMAC_STATUS_DUTYCYCLE_RESTRICTED;
+ }
+ // Datarate not supported by any channel, restore defaults
+ NvmCtx.ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 );
+ *time = 0;
+ return LORAMAC_STATUS_NO_CHANNEL_FOUND;
+ }
+}
+
+LoRaMacStatus_t RegionKR920ChannelAdd( ChannelAddParams_t* channelAdd )
+{
+ bool drInvalid = false;
+ bool freqInvalid = false;
+ uint8_t id = channelAdd->ChannelId;
+
+ if( id < KR920_NUMB_DEFAULT_CHANNELS )
+ {
+ return LORAMAC_STATUS_FREQ_AND_DR_INVALID;
+ }
+
+ 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;
+ }
+
+ // Check frequency
+ if( freqInvalid == false )
+ {
+ if( VerifyRfFreq( 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;
+ }
+
+ memcpy1( ( uint8_t* ) &(NvmCtx.Channels[id]), ( uint8_t* ) channelAdd->NewChannel, sizeof( NvmCtx.Channels[id] ) );
+ NvmCtx.Channels[id].Band = 0;
+ NvmCtx.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
+ NvmCtx.Channels[id] = ( ChannelParams_t ){ 0, 0, { 0 }, 0 };
+
+ return RegionCommonChanDisable( NvmCtx.ChannelsMask, id, KR920_MAX_NB_CHANNELS );
+}
+
+void RegionKR920SetContinuousWave( ContinuousWaveParams_t* continuousWave )
+{
+ int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, NvmCtx.Bands[NvmCtx.Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, NvmCtx.ChannelsMask );
+ float maxEIRP = GetMaxEIRP( NvmCtx.Channels[continuousWave->Channel].Frequency );
+ int8_t phyTxPower = 0;
+ uint32_t frequency = NvmCtx.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 )
+{
+ RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup;
+
+ regionCommonRxBeaconSetup.Datarates = DataratesKR920;
+ regionCommonRxBeaconSetup.Frequency = rxBeaconSetup->Frequency;
+ regionCommonRxBeaconSetup.BeaconSize = KR920_BEACON_SIZE;
+ regionCommonRxBeaconSetup.BeaconDatarate = KR920_BEACON_CHANNEL_DR;
+ regionCommonRxBeaconSetup.BeaconChannelBW = KR920_BEACON_CHANNEL_BW;
+ regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime;
+ regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout;
+
+ RegionCommonRxBeaconSetup( ®ionCommonRxBeaconSetup );
+
+ // Store downlink datarate
+ *outDr = KR920_BEACON_CHANNEL_DR;
+}
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionKR920.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionKR920.h
new file mode 100644
index 00000000..7f5052c2
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionKR920.h
@@ -0,0 +1,521 @@
+/*!
+ * \file RegionKR920.h
+ *
+ * \brief Region definition for KR920
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013-2017 Semtech
+ *
+ * ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ * embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Gregory Cristian ( Semtech )
+ *
+ * \author Daniel Jaeckle ( STACKFORCE )
+ *
+ * \author Johannes Bruder ( STACKFORCE )
+ *
+ * \defgroup REGIONKR920 Region KR920
+ * Implementation according to LoRaWAN Specification v1.0.2.
+ * \{
+ */
+#ifndef __REGION_KR920_H__
+#define __REGION_KR920_H__
+
+#include "region/Region.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 frequency
+ */
+#define KR920_BEACON_CHANNEL_FREQ 923100000
+
+/*!
+ * Payload size of a beacon frame
+ */
+#define KR920_BEACON_SIZE 17
+
+/*!
+ * Size of RFU 1 field
+ */
+#define KR920_RFU1_SIZE 2
+
+/*!
+ * Size of RFU 2 field
+ */
+#define KR920_RFU2_SIZE 0
+
+/*!
+ * Datarate of the beacon channel
+ */
+#define KR920_BEACON_CHANNEL_DR DR_3
+
+/*!
+ * Bandwith of the beacon channel
+ */
+#define KR920_BEACON_CHANNEL_BW 0
+
+/*!
+ * Ping slot channel datarate
+ */
+#define KR920_PING_SLOT_CHANNEL_DR DR_3
+
+/*!
+ * Maximum number of bands
+ */
+#define KR920_MAX_NB_BANDS 1
+
+/*!
+ * Band 0 definition
+ * { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff }
+ */
+#define KR920_BAND0 { 1 , KR920_MAX_TX_POWER, 0, 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( InitDefaultsParams_t* params );
+
+/*!
+ * \brief Returns a pointer to the internal context and its size.
+ *
+ * \param [OUT] params Pointer to the function parameters.
+ *
+ * \retval Points to a structure where the module store its non-volatile context.
+ */
+void* RegionKR920GetNvmCtx( GetNvmCtxParams_t* params );
+
+/*!
+ * \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 );
+
+/*!
+ * 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] currentDr current datarate.
+ *
+ * \retval Datarate to apply.
+ */
+int8_t RegionKR920AlternateDr( int8_t currentDr, AlternateDrType_t type );
+
+/*!
+ * \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]
+ */
+LoRaMacStatus_t 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 );
+
+/*!
+ * \brief Sets the radio into beacon reception mode
+ *
+ * \param [IN] rxBeaconSetup Pointer to the function parameters
+ */
+ void RegionKR920RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr );
+
+/*! \} defgroup REGIONKR920 */
+
+#endif // __REGION_KR920_H__
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionRU864.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionRU864.c
new file mode 100644
index 00000000..4ae5d494
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionRU864.c
@@ -0,0 +1,1039 @@
+/*!
+ * \file RegionRU864.c
+ *
+ * \brief Region implementation for RU864
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013-2017 Semtech
+ *
+ * ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ * embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Gregory Cristian ( Semtech )
+ *
+ * \author Daniel Jaeckle ( STACKFORCE )
+*/
+#include "utilities.h"
+
+#include "RegionCommon.h"
+#include "RegionRU864.h"
+#include "lorawan_conf.h"
+#include "mw_log_conf.h"
+
+// Definitions
+#define CHANNELS_MASK_SIZE 1
+
+/*!
+ * Region specific context
+ */
+typedef struct sRegionRU864NvmCtx
+{
+ /*!
+ * LoRaMAC channels
+ */
+ ChannelParams_t Channels[ RU864_MAX_NB_CHANNELS ];
+ /*!
+ * LoRaMac bands
+ */
+ Band_t Bands[ RU864_MAX_NB_BANDS ];
+ /*!
+ * LoRaMac channels mask
+ */
+ uint16_t ChannelsMask[ CHANNELS_MASK_SIZE ];
+ /*!
+ * LoRaMac channels default mask
+ */
+ uint16_t ChannelsDefaultMask[ CHANNELS_MASK_SIZE ];
+}RegionRU864NvmCtx_t;
+
+/*
+ * Non-volatile module context.
+ */
+static RegionRU864NvmCtx_t NvmCtx;
+
+// 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( BandwidthsRU864[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 VerifyRfFreq( uint32_t freq )
+{
+ // Check radio driver support
+ if( Radio.CheckRfFrequency( freq ) == false )
+ {
+ return false;
+ }
+
+ // Check frequency bands
+ if( ( freq < 864000000 ) || ( freq > 870000000 ) )
+ {
+ 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 < RU864_MAX_NB_CHANNELS; i += 16, k++ )
+ {
+ for( uint8_t j = 0; j < MIN( RU864_MAX_NB_CHANNELS, 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( ( RU864_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 RegionRU864GetPhyParam( GetPhyParams_t* getPhy )
+{
+ PhyParam_t phyParam = { 0 };
+
+ switch( getPhy->Attribute )
+ {
+ case PHY_MIN_RX_DR:
+ {
+ phyParam.Value = RU864_RX_MIN_DATARATE;
+ break;
+ }
+ case PHY_MIN_TX_DR:
+ {
+ phyParam.Value = RU864_TX_MIN_DATARATE;
+ break;
+ }
+ case PHY_DEF_TX_DR:
+ {
+ phyParam.Value = RU864_DEFAULT_DATARATE;
+ break;
+ }
+ case PHY_NEXT_LOWER_TX_DR:
+ {
+ phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, RU864_TX_MIN_DATARATE );
+ break;
+ }
+ case PHY_MAX_TX_POWER:
+ {
+ phyParam.Value = RU864_MAX_TX_POWER;
+ break;
+ }
+ case PHY_DEF_TX_POWER:
+ {
+ phyParam.Value = RU864_DEFAULT_TX_POWER;
+ break;
+ }
+ case PHY_DEF_ADR_ACK_LIMIT:
+ {
+ phyParam.Value = RU864_ADR_ACK_LIMIT;
+ break;
+ }
+ case PHY_DEF_ADR_ACK_DELAY:
+ {
+ phyParam.Value = RU864_ADR_ACK_DELAY;
+ break;
+ }
+ case PHY_MAX_PAYLOAD:
+ {
+ phyParam.Value = MaxPayloadOfDatarateRU864[getPhy->Datarate];
+ break;
+ }
+ case PHY_MAX_PAYLOAD_REPEATER:
+ {
+ phyParam.Value = MaxPayloadOfDatarateRepeaterRU864[getPhy->Datarate];
+ break;
+ }
+ case PHY_DUTY_CYCLE:
+ {
+ phyParam.Value = RU864_DUTY_CYCLE_ENABLED;
+ break;
+ }
+ case PHY_MAX_RX_WINDOW:
+ {
+ phyParam.Value = RU864_MAX_RX_WINDOW;
+ break;
+ }
+ case PHY_RECEIVE_DELAY1:
+ {
+ phyParam.Value = RU864_RECEIVE_DELAY1;
+ break;
+ }
+ case PHY_RECEIVE_DELAY2:
+ {
+ phyParam.Value = RU864_RECEIVE_DELAY2;
+ break;
+ }
+ case PHY_JOIN_ACCEPT_DELAY1:
+ {
+ phyParam.Value = RU864_JOIN_ACCEPT_DELAY1;
+ break;
+ }
+ case PHY_JOIN_ACCEPT_DELAY2:
+ {
+ phyParam.Value = RU864_JOIN_ACCEPT_DELAY2;
+ break;
+ }
+ case PHY_MAX_FCNT_GAP:
+ {
+ phyParam.Value = RU864_MAX_FCNT_GAP;
+ break;
+ }
+ case PHY_ACK_TIMEOUT:
+ {
+ phyParam.Value = ( RU864_ACKTIMEOUT + randr( -RU864_ACK_TIMEOUT_RND, RU864_ACK_TIMEOUT_RND ) );
+ break;
+ }
+ case PHY_DEF_DR1_OFFSET:
+ {
+ phyParam.Value = RU864_DEFAULT_RX1_DR_OFFSET;
+ break;
+ }
+ case PHY_DEF_RX2_FREQUENCY:
+ {
+ phyParam.Value = RU864_RX_WND_2_FREQ;
+ break;
+ }
+ case PHY_DEF_RX2_DR:
+ {
+ phyParam.Value = RU864_RX_WND_2_DR;
+ break;
+ }
+ case PHY_CHANNELS_MASK:
+ {
+ phyParam.ChannelsMask = NvmCtx.ChannelsMask;
+ break;
+ }
+ case PHY_CHANNELS_DEFAULT_MASK:
+ {
+ phyParam.ChannelsMask = NvmCtx.ChannelsDefaultMask;
+ break;
+ }
+ case PHY_MAX_NB_CHANNELS:
+ {
+ phyParam.Value = RU864_MAX_NB_CHANNELS;
+ break;
+ }
+ case PHY_CHANNELS:
+ {
+ phyParam.Channels = NvmCtx.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 = RU864_DEFAULT_MAX_EIRP;
+ break;
+ }
+ case PHY_DEF_ANTENNA_GAIN:
+ {
+ phyParam.fValue = RU864_DEFAULT_ANTENNA_GAIN;
+ break;
+ }
+ case PHY_BEACON_CHANNEL_FREQ:
+ {
+ phyParam.Value = RU864_BEACON_CHANNEL_FREQ;
+ break;
+ }
+ case PHY_BEACON_FORMAT:
+ {
+ phyParam.BeaconFormat.BeaconSize = RU864_BEACON_SIZE;
+ phyParam.BeaconFormat.Rfu1Size = RU864_RFU1_SIZE;
+ phyParam.BeaconFormat.Rfu2Size = RU864_RFU2_SIZE;
+ break;
+ }
+ case PHY_BEACON_CHANNEL_DR:
+ {
+ phyParam.Value = RU864_BEACON_CHANNEL_DR;
+ break;
+ }
+ case PHY_PING_SLOT_CHANNEL_DR:
+ {
+ phyParam.Value = RU864_PING_SLOT_CHANNEL_DR;
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ return phyParam;
+}
+
+void RegionRU864SetBandTxDone( SetBandTxDoneParams_t* txDone )
+{
+ RegionCommonSetBandTxDone( txDone->Joined, &NvmCtx.Bands[NvmCtx.Channels[txDone->Channel].Band], txDone->LastTxDoneTime );
+}
+
+void RegionRU864InitDefaults( InitDefaultsParams_t* params )
+{
+ Band_t bands[RU864_MAX_NB_BANDS] =
+ {
+ RU864_BAND0
+ };
+
+ switch( params->Type )
+ {
+ case INIT_TYPE_INIT:
+ {
+ // Initialize bands
+ memcpy1( ( uint8_t* )NvmCtx.Bands, ( uint8_t* )bands, sizeof( Band_t ) * RU864_MAX_NB_BANDS );
+
+ // Channels
+ NvmCtx.Channels[0] = ( ChannelParams_t ) RU864_LC1;
+ NvmCtx.Channels[1] = ( ChannelParams_t ) RU864_LC2;
+
+ // Initialize the channels default mask
+ NvmCtx.ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 );
+ // Update the channels mask
+ RegionCommonChanMaskCopy( NvmCtx.ChannelsMask, NvmCtx.ChannelsDefaultMask, 1 );
+ break;
+ }
+ case INIT_TYPE_RESTORE_CTX:
+ {
+ if( params->NvmCtx != 0 )
+ {
+ memcpy1( (uint8_t*) &NvmCtx, (uint8_t*) params->NvmCtx, sizeof( NvmCtx ) );
+ }
+ break;
+ }
+ case INIT_TYPE_RESTORE_DEFAULT_CHANNELS:
+ {
+ // Restore channels default mask
+ NvmCtx.ChannelsMask[0] |= NvmCtx.ChannelsDefaultMask[0];
+
+ // Channels
+ NvmCtx.Channels[0] = ( ChannelParams_t ) RU864_LC1;
+ NvmCtx.Channels[1] = ( ChannelParams_t ) RU864_LC2;
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+void* RegionRU864GetNvmCtx( GetNvmCtxParams_t* params )
+{
+ params->nvmCtxSize = sizeof( RegionRU864NvmCtx_t );
+ return &NvmCtx;
+}
+
+bool RegionRU864Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute )
+{
+ switch( phyAttribute )
+ {
+ case PHY_FREQUENCY:
+ {
+ return VerifyRfFreq( verify->Frequency );
+ }
+ case PHY_TX_DR:
+ {
+ return RegionCommonValueInRange( verify->DatarateParams.Datarate, RU864_TX_MIN_DATARATE, RU864_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, RU864_RX_MIN_DATARATE, RU864_RX_MAX_DATARATE );
+ }
+ case PHY_DEF_TX_POWER:
+ case PHY_TX_POWER:
+ {
+ // Remark: switched min and max!
+ return RegionCommonValueInRange( verify->TxPower, RU864_MAX_TX_POWER, RU864_MIN_TX_POWER );
+ }
+ case PHY_DUTY_CYCLE:
+ {
+ return RU864_DUTY_CYCLE_ENABLED;
+ }
+ default:
+ return false;
+ }
+}
+
+void RegionRU864ApplyCFList( 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 CFListType must be 0 to indicate the CFList contains a list of frequencies
+ if( applyCFList->Payload[15] != 0 )
+ {
+ return;
+ }
+
+ // Last byte is RFU, don't take it into account
+ for( uint8_t i = 0, chanIdx = RU864_NUMB_DEFAULT_CHANNELS; chanIdx < RU864_MAX_NB_CHANNELS; i+=3, chanIdx++ )
+ {
+ if( chanIdx < ( RU864_NUMB_CHANNELS_CF_LIST + RU864_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
+ RegionRU864ChannelAdd( &channelAdd );
+ }
+ else
+ {
+ channelRemove.ChannelId = chanIdx;
+
+ RegionRU864ChannelsRemove( &channelRemove );
+ }
+ }
+}
+
+bool RegionRU864ChanMaskSet( ChanMaskSetParams_t* chanMaskSet )
+{
+ switch( chanMaskSet->ChannelsMaskType )
+ {
+ case CHANNELS_MASK:
+ {
+ RegionCommonChanMaskCopy( NvmCtx.ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 );
+ break;
+ }
+ case CHANNELS_DEFAULT_MASK:
+ {
+ RegionCommonChanMaskCopy( NvmCtx.ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 );
+ break;
+ }
+ default:
+ return false;
+ }
+ return true;
+}
+
+void RegionRU864ComputeRxWindowParameters( 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, RU864_RX_MAX_DATARATE );
+ rxConfigParams->Bandwidth = GetBandwidth( rxConfigParams->Datarate );
+
+ if( rxConfigParams->Datarate == DR_7 )
+ { // FSK
+ tSymbol = RegionCommonComputeSymbolTimeFsk( DataratesRU864[rxConfigParams->Datarate] );
+ }
+ else
+ { // LoRa
+ tSymbol = RegionCommonComputeSymbolTimeLoRa( DataratesRU864[rxConfigParams->Datarate], BandwidthsRU864[rxConfigParams->Datarate] );
+ }
+
+ RegionCommonComputeRxWindowParameters( tSymbol, minRxSymbols, rxError, Radio.GetWakeupTime( ), &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset );
+}
+
+bool RegionRU864RxConfig( 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;
+ const char *slotStrings[] = { "1", "2", "C", "Multi_C", "P", "Multi_P" };
+
+ if( Radio.GetStatus( ) != RF_IDLE )
+ {
+ return false;
+ }
+
+ if( rxConfig->RxSlot == RX_SLOT_WIN_1 )
+ {
+ // Apply window 1 frequency
+ frequency = NvmCtx.Channels[rxConfig->Channel].Frequency;
+ // Apply the alternative RX 1 window frequency, if it is available
+ if( NvmCtx.Channels[rxConfig->Channel].Rx1Frequency != 0 )
+ {
+ frequency = NvmCtx.Channels[rxConfig->Channel].Rx1Frequency;
+ }
+ }
+
+ // Read the physical datarate from the datarates table
+ phyDr = DataratesRU864[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 = MaxPayloadOfDatarateRepeaterRU864[dr];
+ }
+ else
+ {
+ maxPayload = MaxPayloadOfDatarateRU864[dr];
+ }
+
+ Radio.SetMaxPayloadLength( modem, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD );
+ if ( rxConfig->RxSlot < RX_SLOT_NONE )
+ {
+ MW_LOG( "RX_%s on freq %d Hz at DR %d\n\r", slotStrings[rxConfig->RxSlot], frequency, dr );
+ }
+ else
+ {
+ MW_LOG( "RX on freq %d Hz at DR %d\n\r", frequency, dr );
+ }
+
+ *datarate = (uint8_t) dr;
+ return true;
+}
+
+bool RegionRU864TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir )
+{
+ RadioModems_t modem;
+ int8_t phyDr = DataratesRU864[txConfig->Datarate];
+ int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, NvmCtx.Bands[NvmCtx.Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, NvmCtx.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( NvmCtx.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, 4000 );
+ }
+ else
+ {
+ modem = MODEM_LORA;
+ Radio.SetTxConfig( modem, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 4000 );
+ }
+ MW_LOG( "TX on freq %d Hz at DR %d\n\r", NvmCtx.Channels[txConfig->Channel].Frequency, txConfig->Datarate );
+ // 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 RegionRU864LinkAdrReq( 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 < RU864_MAX_NB_CHANNELS; i++ )
+ {
+ if( linkAdrParams.ChMaskCtrl == 6 )
+ {
+ if( NvmCtx.Channels[i].Frequency != 0 )
+ {
+ chMask |= 1 << i;
+ }
+ }
+ else
+ {
+ if( ( ( chMask & ( 1 << i ) ) != 0 ) &&
+ ( NvmCtx.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 = RegionRU864GetPhyParam( &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 = RU864_MAX_NB_CHANNELS;
+ linkAdrVerifyParams.ChannelsMask = &chMask;
+ linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value;
+ linkAdrVerifyParams.MaxDatarate = RU864_TX_MAX_DATARATE;
+ linkAdrVerifyParams.Channels = NvmCtx.Channels;
+ linkAdrVerifyParams.MinTxPower = RU864_MIN_TX_POWER;
+ linkAdrVerifyParams.MaxTxPower = RU864_MAX_TX_POWER;
+ linkAdrVerifyParams.Version = linkAdrReq->Version;
+
+ // 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
+ memset1( ( uint8_t* ) NvmCtx.ChannelsMask, 0, sizeof( NvmCtx.ChannelsMask ) );
+ // Update the channels mask
+ NvmCtx.ChannelsMask[0] = chMask;
+ }
+
+ // Update status variables
+ *drOut = linkAdrParams.Datarate;
+ *txPowOut = linkAdrParams.TxPower;
+ *nbRepOut = linkAdrParams.NbRep;
+ *nbBytesParsed = bytesProcessed;
+
+ return status;
+}
+
+uint8_t RegionRU864RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq )
+{
+ uint8_t status = 0x07;
+
+ // Verify radio frequency
+ if( VerifyRfFreq( rxParamSetupReq->Frequency ) == false )
+ {
+ status &= 0xFE; // Channel frequency KO
+ }
+
+ // Verify datarate
+ if( RegionCommonValueInRange( rxParamSetupReq->Datarate, RU864_RX_MIN_DATARATE, RU864_RX_MAX_DATARATE ) == false )
+ {
+ status &= 0xFD; // Datarate KO
+ }
+
+ // Verify datarate offset
+ if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, RU864_MIN_RX1_DR_OFFSET, RU864_MAX_RX1_DR_OFFSET ) == false )
+ {
+ status &= 0xFB; // Rx1DrOffset range KO
+ }
+
+ return status;
+}
+
+uint8_t RegionRU864NewChannelReq( NewChannelReqParams_t* newChannelReq )
+{
+ uint8_t status = 0x03;
+ ChannelAddParams_t channelAdd;
+ ChannelRemoveParams_t channelRemove;
+
+ if( newChannelReq->NewChannel->Frequency == 0 )
+ {
+ channelRemove.ChannelId = newChannelReq->ChannelId;
+
+ // Remove
+ if( RegionRU864ChannelsRemove( &channelRemove ) == false )
+ {
+ status &= 0xFC;
+ }
+ }
+ else
+ {
+ channelAdd.NewChannel = newChannelReq->NewChannel;
+ channelAdd.ChannelId = newChannelReq->ChannelId;
+
+ switch( RegionRU864ChannelAdd( &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 RegionRU864TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq )
+{
+ return -1;
+}
+
+uint8_t RegionRU864DlChannelReq( DlChannelReqParams_t* dlChannelReq )
+{
+ uint8_t status = 0x03;
+
+ // Verify if the frequency is supported
+ if( VerifyRfFreq( dlChannelReq->Rx1Frequency ) == false )
+ {
+ status &= 0xFE;
+ }
+
+ // Verify if an uplink frequency exists
+ if( NvmCtx.Channels[dlChannelReq->ChannelId].Frequency == 0 )
+ {
+ status &= 0xFD;
+ }
+
+ // Apply Rx1 frequency, if the status is OK
+ if( status == 0x03 )
+ {
+ NvmCtx.Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency;
+ }
+
+ return status;
+}
+
+int8_t RegionRU864AlternateDr( int8_t currentDr, AlternateDrType_t type )
+{
+ return currentDr;
+}
+
+void RegionRU864CalcBackOff( CalcBackOffParams_t* calcBackOff )
+{
+ RegionCommonCalcBackOffParams_t calcBackOffParams;
+
+ calcBackOffParams.Channels = NvmCtx.Channels;
+ calcBackOffParams.Bands = NvmCtx.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 );
+}
+
+LoRaMacStatus_t RegionRU864NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff )
+{
+ uint8_t nbEnabledChannels = 0;
+ uint8_t delayTx = 0;
+ uint8_t enabledChannels[RU864_MAX_NB_CHANNELS] = { 0 };
+ TimerTime_t nextTxDelay = 0;
+
+ if( RegionCommonCountChannels( NvmCtx.ChannelsMask, 0, 1 ) == 0 )
+ { // Reactivate default channels
+ NvmCtx.ChannelsMask[0] |= LC( 1 ) + LC( 2 );
+ }
+
+ TimerTime_t elapsed = TimerGetElapsedTime( nextChanParams->LastAggrTx );
+ if( ( nextChanParams->LastAggrTx == 0 ) || ( nextChanParams->AggrTimeOff <= elapsed ) )
+ {
+ // Reset Aggregated time off
+ *aggregatedTimeOff = 0;
+
+ // Update bands Time OFF
+ nextTxDelay = RegionCommonUpdateBandTimeOff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, NvmCtx.Bands, RU864_MAX_NB_BANDS );
+
+ // Search how many channels are enabled
+ nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Joined, nextChanParams->Datarate,
+ NvmCtx.ChannelsMask, NvmCtx.Channels,
+ NvmCtx.Bands, enabledChannels, &delayTx );
+ }
+ else
+ {
+ delayTx++;
+ nextTxDelay = nextChanParams->AggrTimeOff - elapsed;
+ }
+
+ if( nbEnabledChannels > 0 )
+ {
+ // We found a valid channel
+ *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )];
+
+ *time = 0;
+ return LORAMAC_STATUS_OK;
+ }
+ else
+ {
+ if( delayTx > 0 )
+ {
+ // Delay transmission due to AggregatedTimeOff or to a band time off
+ *time = nextTxDelay;
+ return LORAMAC_STATUS_DUTYCYCLE_RESTRICTED;
+ }
+ // Datarate not supported by any channel, restore defaults
+ NvmCtx.ChannelsMask[0] |= LC( 1 ) + LC( 2 );
+ *time = 0;
+ return LORAMAC_STATUS_NO_CHANNEL_FOUND;
+ }
+}
+
+LoRaMacStatus_t RegionRU864ChannelAdd( ChannelAddParams_t* channelAdd )
+{
+ bool drInvalid = false;
+ bool freqInvalid = false;
+ uint8_t id = channelAdd->ChannelId;
+
+ if( id < RU864_NUMB_DEFAULT_CHANNELS )
+ {
+ return LORAMAC_STATUS_FREQ_AND_DR_INVALID;
+ }
+
+ if( id >= RU864_MAX_NB_CHANNELS )
+ {
+ return LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+
+ // Validate the datarate range
+ if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Min, RU864_TX_MIN_DATARATE, RU864_TX_MAX_DATARATE ) == false )
+ {
+ drInvalid = true;
+ }
+ if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, RU864_TX_MIN_DATARATE, RU864_TX_MAX_DATARATE ) == false )
+ {
+ drInvalid = true;
+ }
+ if( channelAdd->NewChannel->DrRange.Fields.Min > channelAdd->NewChannel->DrRange.Fields.Max )
+ {
+ drInvalid = true;
+ }
+
+ // Check frequency
+ if( freqInvalid == false )
+ {
+ if( VerifyRfFreq( 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;
+ }
+
+ memcpy1( ( uint8_t* ) &(NvmCtx.Channels[id]), ( uint8_t* ) channelAdd->NewChannel, sizeof( NvmCtx.Channels[id] ) );
+ NvmCtx.Channels[id].Band = 0;
+ NvmCtx.ChannelsMask[0] |= ( 1 << id );
+ return LORAMAC_STATUS_OK;
+}
+
+bool RegionRU864ChannelsRemove( ChannelRemoveParams_t* channelRemove )
+{
+ uint8_t id = channelRemove->ChannelId;
+
+ if( id < RU864_NUMB_DEFAULT_CHANNELS )
+ {
+ return false;
+ }
+
+ // Remove the channel from the list of channels
+ NvmCtx.Channels[id] = ( ChannelParams_t ){ 0, 0, { 0 }, 0 };
+
+ return RegionCommonChanDisable( NvmCtx.ChannelsMask, id, RU864_MAX_NB_CHANNELS );
+}
+
+void RegionRU864SetContinuousWave( ContinuousWaveParams_t* continuousWave )
+{
+ int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, NvmCtx.Bands[NvmCtx.Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, NvmCtx.ChannelsMask );
+ int8_t phyTxPower = 0;
+ uint32_t frequency = NvmCtx.Channels[continuousWave->Channel].Frequency;
+
+ // Calculate physical TX power
+ phyTxPower = RegionCommonComputeTxPower( txPowerLimited, continuousWave->MaxEirp, continuousWave->AntennaGain );
+
+ Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout );
+}
+
+uint8_t RegionRU864ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset )
+{
+ int8_t datarate = dr - drOffset;
+
+ if( datarate < 0 )
+ {
+ datarate = DR_0;
+ }
+ return datarate;
+}
+
+void RegionRU864RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr )
+{
+ RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup;
+
+ regionCommonRxBeaconSetup.Datarates = DataratesRU864;
+ regionCommonRxBeaconSetup.Frequency = rxBeaconSetup->Frequency;
+ regionCommonRxBeaconSetup.BeaconSize = RU864_BEACON_SIZE;
+ regionCommonRxBeaconSetup.BeaconDatarate = RU864_BEACON_CHANNEL_DR;
+ regionCommonRxBeaconSetup.BeaconChannelBW = RU864_BEACON_CHANNEL_BW;
+ regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime;
+ regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout;
+
+ RegionCommonRxBeaconSetup( ®ionCommonRxBeaconSetup );
+
+ // Store downlink datarate
+ *outDr = RU864_BEACON_CHANNEL_DR;
+}
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionRU864.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionRU864.h
new file mode 100644
index 00000000..bfd6fe3f
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionRU864.h
@@ -0,0 +1,500 @@
+/*!
+ * \file RegionRU864.h
+ *
+ * \brief Region definition for RU864
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013-2017 Semtech
+ *
+ * ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ * embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Gregory Cristian ( Semtech )
+ *
+ * \author Daniel Jaeckle ( STACKFORCE )
+ *
+ * \defgroup REGIONRU864 Region RU864
+ * Implementation according to LoRaWAN Specification v1.0.2.
+ * \{
+ */
+#ifndef __REGION_RU864_H__
+#define __REGION_RU864_H__
+
+#include "LoRaMac.h"
+
+/*!
+ * LoRaMac maximum number of channels
+ */
+#define RU864_MAX_NB_CHANNELS 8
+
+/*!
+ * Number of default channels
+ */
+#define RU864_NUMB_DEFAULT_CHANNELS 2
+
+/*!
+ * Number of channels to apply for the CF list
+ */
+#define RU864_NUMB_CHANNELS_CF_LIST 5
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define RU864_TX_MIN_DATARATE DR_0
+
+/*!
+ * Maximal datarate that can be used by the node
+ */
+#define RU864_TX_MAX_DATARATE DR_7
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define RU864_RX_MIN_DATARATE DR_0
+
+/*!
+ * Maximal datarate that can be used by the node
+ */
+#define RU864_RX_MAX_DATARATE DR_7
+
+/*!
+ * Default datarate used by the node
+ */
+#define RU864_DEFAULT_DATARATE DR_0
+
+/*!
+ * Minimal Rx1 receive datarate offset
+ */
+#define RU864_MIN_RX1_DR_OFFSET 0
+
+/*!
+ * Maximal Rx1 receive datarate offset
+ */
+#define RU864_MAX_RX1_DR_OFFSET 5
+
+/*!
+ * Default Rx1 receive datarate offset
+ */
+#define RU864_DEFAULT_RX1_DR_OFFSET 0
+
+/*!
+ * Minimal Tx output power that can be used by the node
+ */
+#define RU864_MIN_TX_POWER TX_POWER_7
+
+/*!
+ * Maximal Tx output power that can be used by the node
+ */
+#define RU864_MAX_TX_POWER TX_POWER_0
+
+/*!
+ * Default Tx output power used by the node
+ */
+#define RU864_DEFAULT_TX_POWER TX_POWER_0
+
+/*!
+ * Default Max EIRP
+ */
+#define RU864_DEFAULT_MAX_EIRP 16.0f
+
+/*!
+ * Default antenna gain
+ */
+#define RU864_DEFAULT_ANTENNA_GAIN 2.15f
+
+/*!
+ * ADR Ack limit
+ */
+#define RU864_ADR_ACK_LIMIT 64
+
+/*!
+ * ADR Ack delay
+ */
+#define RU864_ADR_ACK_DELAY 32
+
+/*!
+ * Enabled or disabled the duty cycle
+ */
+#define RU864_DUTY_CYCLE_ENABLED 1
+
+/*!
+ * Maximum RX window duration
+ */
+#define RU864_MAX_RX_WINDOW 3000 //TODO
+
+/*!
+ * Receive delay 1
+ */
+#define RU864_RECEIVE_DELAY1 1000
+
+/*!
+ * Receive delay 2
+ */
+#define RU864_RECEIVE_DELAY2 2000
+
+/*!
+ * Join accept delay 1
+ */
+#define RU864_JOIN_ACCEPT_DELAY1 5000
+
+/*!
+ * Join accept delay 2
+ */
+#define RU864_JOIN_ACCEPT_DELAY2 6000
+
+/*!
+ * Maximum frame counter gap
+ */
+#define RU864_MAX_FCNT_GAP 16384
+
+/*!
+ * Ack timeout
+ */
+#define RU864_ACKTIMEOUT 2000
+
+/*!
+ * Random ack timeout limits
+ */
+#define RU864_ACK_TIMEOUT_RND 1000
+
+#if ( RU864_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 RU864_RX_WND_2_FREQ 869100000
+
+/*!
+ * Second reception window channel datarate definition.
+ */
+#define RU864_RX_WND_2_DR DR_0
+
+/*
+ * CLASS B
+ */
+/*!
+ * Beacon frequency
+ */
+#define RU864_BEACON_CHANNEL_FREQ 869100000
+
+/*!
+ * Payload size of a beacon frame
+ */
+#define RU864_BEACON_SIZE 17
+
+/*!
+ * Size of RFU 1 field
+ */
+#define RU864_RFU1_SIZE 2
+
+/*!
+ * Size of RFU 2 field
+ */
+#define RU864_RFU2_SIZE 0
+
+/*!
+ * Datarate of the beacon channel
+ */
+#define RU864_BEACON_CHANNEL_DR DR_3
+
+/*!
+ * Bandwith of the beacon channel (Index of BandwidthsRU864[])
+ */
+#define RU864_BEACON_CHANNEL_BW 0
+
+/*!
+ * Ping slot channel datarate
+ */
+#define RU864_PING_SLOT_CHANNEL_DR DR_3
+
+
+/*!
+ * Maximum number of bands
+ */
+#define RU864_MAX_NB_BANDS 1
+
+/*!
+ * Band 0 definition
+ * { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff }
+ */
+#define RU864_BAND0 { 100 , RU864_MAX_TX_POWER, 0, 0, 0 } // 1.0 %
+
+/*!
+ * LoRaMac default channel 1
+ * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
+ */
+#define RU864_LC1 { 868900000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
+
+/*!
+ * LoRaMac default channel 2
+ * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
+ */
+#define RU864_LC2 { 869100000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
+
+
+/*!
+ * LoRaMac channels which are allowed for the join procedure
+ */
+#define RU864_JOIN_CHANNELS ( uint16_t )( LC( 1 ) | LC( 2 ) )
+
+/*!
+ * Data rates table definition
+ */
+static const uint8_t DataratesRU864[] = { 12, 11, 10, 9, 8, 7, 7, 50 };
+
+/*!
+ * Bandwidths table definition in Hz
+ */
+static const uint32_t BandwidthsRU864[] = { 125000, 125000, 125000, 125000, 125000, 125000, 250000, 0 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Cannot operate with repeater.
+ */
+static const uint8_t MaxPayloadOfDatarateRU864[] = { 51, 51, 51, 115, 242, 242, 242, 242 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Can operate with repeater.
+ */
+static const uint8_t MaxPayloadOfDatarateRepeaterRU864[] = { 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 RegionRU864GetPhyParam( GetPhyParams_t* getPhy );
+
+/*!
+ * \brief Updates the last TX done parameters of the current channel.
+ *
+ * \param [IN] txDone Pointer to the function parameters.
+ */
+void RegionRU864SetBandTxDone( SetBandTxDoneParams_t* txDone );
+
+/*!
+ * \brief Initializes the channels masks and the channels.
+ *
+ * \param [IN] type Sets the initialization type.
+ */
+void RegionRU864InitDefaults( InitDefaultsParams_t* params );
+
+/*!
+ * \brief Returns a pointer to the internal context and its size.
+ *
+ * \param [OUT] params Pointer to the function parameters.
+ *
+ * \retval Points to a structure where the module store its non-volatile context.
+ */
+void* RegionRU864GetNvmCtx( GetNvmCtxParams_t* params );
+
+/*!
+ * \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 RegionRU864Verify( 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 RegionRU864ApplyCFList( 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 RegionRU864ChanMaskSet( ChanMaskSetParams_t* chanMaskSet );
+
+/*!
+ * 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 RegionRU864ComputeRxWindowParameters( 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 RegionRU864RxConfig( 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 RegionRU864TxConfig( 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 RegionRU864LinkAdrReq( 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 RegionRU864RxParamSetupReq( 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 RegionRU864NewChannelReq( 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 RegionRU864TxParamSetupReq( 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 RegionRU864DlChannelReq( DlChannelReqParams_t* dlChannelReq );
+
+/*!
+ * \brief Alternates the datarate of the channel for the join request.
+ *
+ * \param [IN] currentDr Current datarate.
+ *
+ * \retval Datarate to apply.
+ */
+int8_t RegionRU864AlternateDr( int8_t currentDr, AlternateDrType_t type );
+
+/*!
+ * \brief Calculates the back-off time.
+ *
+ * \param [IN] calcBackOff Pointer to the function parameters.
+ */
+void RegionRU864CalcBackOff( 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]
+ */
+LoRaMacStatus_t RegionRU864NextChannel( 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 RegionRU864ChannelAdd( 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 RegionRU864ChannelsRemove( ChannelRemoveParams_t* channelRemove );
+
+/*!
+ * \brief Sets the radio into continuous wave mode.
+ *
+ * \param [IN] continuousWave Pointer to the function parameters.
+ */
+void RegionRU864SetContinuousWave( 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 RegionRU864ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset );
+
+/*!
+ * \brief Sets the radio into beacon reception mode
+ *
+ * \param [IN] rxBeaconSetup Pointer to the function parameters
+ */
+void RegionRU864RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr );
+
+/*! \} defgroup REGIONRU864 */
+
+#endif // __REGION_RU864_H__
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionUS915.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionUS915.c
new file mode 100644
index 00000000..688d5b53
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionUS915.c
@@ -0,0 +1,1153 @@
+/*!
+ * \file RegionUS915.c
+ *
+ * \brief Region implementation for US915
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013-2017 Semtech
+ *
+ * ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ * embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Gregory Cristian ( Semtech )
+ *
+ * \author Daniel Jaeckle ( STACKFORCE )
+*/
+#include "utilities.h"
+
+#include "RegionCommon.h"
+#include "RegionUS915.h"
+#include "lorawan_conf.h"
+#include "mw_log_conf.h"
+
+// Definitions
+#define CHANNELS_MASK_SIZE 6
+
+// A mask to select only valid 500KHz channels
+#define CHANNELS_MASK_500KHZ_MASK 0x00FF
+
+/*!
+ * Region specific context
+ */
+typedef struct sRegionUS915NvmCtx
+{
+ /*!
+ * LoRaMAC channels
+ */
+ ChannelParams_t Channels[ US915_MAX_NB_CHANNELS ];
+ /*!
+ * LoRaMac bands
+ */
+ Band_t Bands[ US915_MAX_NB_BANDS ];
+ /*!
+ * LoRaMac channels mask
+ */
+ uint16_t ChannelsMask[ CHANNELS_MASK_SIZE ];
+ /*!
+ * LoRaMac channels remaining
+ */
+ uint16_t ChannelsMaskRemaining[CHANNELS_MASK_SIZE];
+ /*!
+ * LoRaMac channels default mask
+ */
+ uint16_t ChannelsDefaultMask[ CHANNELS_MASK_SIZE ];
+ /*!
+ * Index of current in use 8 bit group (0: bit 0 - 7, 1: bit 8 - 15, ..., 7: bit 56 - 63)
+ */
+ uint8_t JoinChannelGroupsCurrentIndex;
+ /*!
+ * Counter of join trials needed to alternate between DR0 and DR4, see \ref RegionUS915AlternateDr
+ */
+ uint8_t JoinTrialsCounter;
+}RegionUS915NvmCtx_t;
+
+/*
+ * Non-volatile module context.
+ */
+static RegionUS915NvmCtx_t NvmCtx;
+
+// 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;
+}
+
+/*!
+ * \brief Searches for available 125 kHz channels in the given channel mask.
+ *
+ * \param [IN] channelMaskRemaining The remaining channel mask.
+ *
+ * \param [OUT] findAvailableChannelsIndex List containing the indexes of all available 125 kHz channels.
+ *
+ * \param [OUT] availableChannels Number of available 125 kHz channels.
+ *
+ * \retval Status
+ */
+static LoRaMacStatus_t FindAvailable125kHzChannels( uint8_t* findAvailableChannelsIndex, uint16_t channelMaskRemaining, uint8_t* availableChannels )
+{
+ // Nullpointer check
+ if( findAvailableChannelsIndex == NULL || availableChannels == NULL )
+ {
+ return LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+
+ // Initialize counter
+ *availableChannels = 0;
+ for( uint8_t i = 0; i < 8; i++ )
+ {
+ // Find available channels
+ if( ( channelMaskRemaining & ( 1 << i ) ) != 0 )
+ {
+ // Save available channel index
+ findAvailableChannelsIndex[*availableChannels] = i;
+ // Increment counter of available channels if the current channel is available
+ ( *availableChannels )++;
+ }
+ }
+
+ return LORAMAC_STATUS_OK;
+}
+
+/*!
+ * \brief Computes the next 125kHz channel used for join requests.
+ *
+ * \param [OUT] newChannelIndex Index of available channel.
+ *
+ * \retval Status
+ */
+static LoRaMacStatus_t ComputeNext125kHzJoinChannel( uint8_t* newChannelIndex )
+{
+ uint8_t currentChannelsMaskRemainingIndex;
+ uint16_t channelMaskRemaining;
+ uint8_t findAvailableChannelsIndex[8] = { 0 };
+ uint8_t availableChannels = 0;
+ uint8_t startIndex = NvmCtx.JoinChannelGroupsCurrentIndex;
+
+ // Null pointer check
+ if( newChannelIndex == NULL )
+ {
+ return LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+
+ do {
+ // Current ChannelMaskRemaining, two groups per channel mask. For example Group 0 and 1 (8 bit) are ChannelMaskRemaining 0 (16 bit), etc.
+ currentChannelsMaskRemainingIndex = (uint8_t) startIndex / 2;
+
+ // For even numbers we need the 8 LSBs and for uneven the 8 MSBs
+ if( ( startIndex % 2 ) == 0 )
+ {
+ channelMaskRemaining = ( NvmCtx.ChannelsMaskRemaining[currentChannelsMaskRemainingIndex] & 0x00FF );
+ }
+ else
+ {
+ channelMaskRemaining = ( ( NvmCtx.ChannelsMaskRemaining[currentChannelsMaskRemainingIndex] >> 8 ) & 0x00FF );
+ }
+
+
+ if( FindAvailable125kHzChannels( findAvailableChannelsIndex, channelMaskRemaining, &availableChannels ) == LORAMAC_STATUS_PARAMETER_INVALID )
+ {
+ return LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+
+ if ( availableChannels > 0 )
+ {
+ // Choose randomly a free channel 125kHz
+ *newChannelIndex = ( startIndex * 8 ) + findAvailableChannelsIndex[randr( 0, ( availableChannels - 1 ) )];
+ }
+
+ // Increment start index
+ startIndex++;
+ if ( startIndex > 7 )
+ {
+ startIndex = 0;
+ }
+ } while( ( availableChannels == 0 ) && ( startIndex != NvmCtx.JoinChannelGroupsCurrentIndex ) );
+
+ if ( availableChannels > 0 )
+ {
+ NvmCtx.JoinChannelGroupsCurrentIndex = startIndex;
+ return LORAMAC_STATUS_OK;
+ }
+
+ return LORAMAC_STATUS_PARAMETER_INVALID;
+}
+
+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 bool VerifyRfFreq( uint32_t freq )
+{
+ // Check radio driver support
+ if( Radio.CheckRfFrequency( freq ) == false )
+ {
+ return false;
+ }
+
+ // Rx frequencies
+ if( ( freq < US915_FIRST_RX1_CHANNEL ) ||
+ ( freq > US915_LAST_RX1_CHANNEL ) ||
+ ( ( ( freq - ( uint32_t ) US915_FIRST_RX1_CHANNEL ) % ( uint32_t ) US915_STEPWIDTH_RX1_CHANNEL ) != 0 ) )
+ {
+ return false;
+ }
+
+ // Test for frequency range - take RX and TX freqencies into account
+ if( ( freq < 902300000 ) || ( freq > 927500000 ) )
+ {
+ return false;
+ }
+ return true;
+}
+
+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;
+}
+
+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_MAX_TX_POWER:
+ {
+ phyParam.Value = US915_MAX_TX_POWER;
+ break;
+ }
+ case PHY_DEF_TX_POWER:
+ {
+ phyParam.Value = US915_DEFAULT_TX_POWER;
+ break;
+ }
+ case PHY_DEF_ADR_ACK_LIMIT:
+ {
+ phyParam.Value = US915_ADR_ACK_LIMIT;
+ break;
+ }
+ case PHY_DEF_ADR_ACK_DELAY:
+ {
+ phyParam.Value = US915_ADR_ACK_DELAY;
+ 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 = NvmCtx.ChannelsMask;
+ break;
+ }
+ case PHY_CHANNELS_DEFAULT_MASK:
+ {
+ phyParam.ChannelsMask = NvmCtx.ChannelsDefaultMask;
+ break;
+ }
+ case PHY_MAX_NB_CHANNELS:
+ {
+ phyParam.Value = US915_MAX_NB_CHANNELS;
+ break;
+ }
+ case PHY_CHANNELS:
+ {
+ phyParam.Channels = NvmCtx.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 = US915_DEFAULT_MAX_ERP + (float) 2.15;
+ break;
+ }
+ case PHY_DEF_ANTENNA_GAIN:
+ {
+ phyParam.fValue = 0;
+ break;
+ }
+ case PHY_BEACON_CHANNEL_FREQ:
+ {
+ phyParam.Value = US915_BEACON_CHANNEL_FREQ;
+ break;
+ }
+ case PHY_BEACON_FORMAT:
+ {
+ phyParam.BeaconFormat.BeaconSize = US915_BEACON_SIZE;
+ phyParam.BeaconFormat.Rfu1Size = US915_RFU1_SIZE;
+ phyParam.BeaconFormat.Rfu2Size = US915_RFU2_SIZE;
+ break;
+ }
+ case PHY_BEACON_CHANNEL_DR:
+ {
+ phyParam.Value = US915_BEACON_CHANNEL_DR;
+ break;
+ }
+ case PHY_BEACON_CHANNEL_STEPWIDTH:
+ {
+ phyParam.Value = US915_BEACON_CHANNEL_STEPWIDTH;
+ break;
+ }
+ case PHY_BEACON_NB_CHANNELS:
+ {
+ phyParam.Value = US915_BEACON_NB_CHANNELS;
+ break;
+ }
+ case PHY_PING_SLOT_CHANNEL_DR:
+ {
+ phyParam.Value = US915_PING_SLOT_CHANNEL_DR;
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ return phyParam;
+}
+
+void RegionUS915SetBandTxDone( SetBandTxDoneParams_t* txDone )
+{
+ RegionCommonSetBandTxDone( txDone->Joined, &NvmCtx.Bands[NvmCtx.Channels[txDone->Channel].Band], txDone->LastTxDoneTime );
+}
+
+void RegionUS915InitDefaults( InitDefaultsParams_t* params )
+{
+ Band_t bands[US915_MAX_NB_BANDS] =
+ {
+ US915_BAND0
+ };
+
+ switch( params->Type )
+ {
+ case INIT_TYPE_INIT:
+ {
+ // Initialize bands
+ memcpy1( ( uint8_t* )NvmCtx.Bands, ( uint8_t* )bands, sizeof( Band_t ) * US915_MAX_NB_BANDS );
+
+ // Initialize 8 bit channel groups index
+ NvmCtx.JoinChannelGroupsCurrentIndex = 0;
+
+ // Initialize the join trials counter
+ NvmCtx.JoinTrialsCounter = 0;
+
+ // Channels
+ // 125 kHz channels
+ for( uint8_t i = 0; i < US915_MAX_NB_CHANNELS - 8; i++ )
+ {
+ NvmCtx.Channels[i].Frequency = 902300000 + i * 200000;
+ NvmCtx.Channels[i].DrRange.Value = ( DR_3 << 4 ) | DR_0;
+ NvmCtx.Channels[i].Band = 0;
+ }
+ // 500 kHz channels
+ for( uint8_t i = US915_MAX_NB_CHANNELS - 8; i < US915_MAX_NB_CHANNELS; i++ )
+ {
+ NvmCtx.Channels[i].Frequency = 903000000 + ( i - ( US915_MAX_NB_CHANNELS - 8 ) ) * 1600000;
+ NvmCtx.Channels[i].DrRange.Value = ( DR_4 << 4 ) | DR_4;
+ NvmCtx.Channels[i].Band = 0;
+ }
+
+ // ChannelsMask
+ NvmCtx.ChannelsDefaultMask[0] = 0xFFFF;
+ NvmCtx.ChannelsDefaultMask[1] = 0xFFFF;
+ NvmCtx.ChannelsDefaultMask[2] = 0xFFFF;
+ NvmCtx.ChannelsDefaultMask[3] = 0xFFFF;
+ NvmCtx.ChannelsDefaultMask[4] = 0x00FF;
+ NvmCtx.ChannelsDefaultMask[5] = 0x0000;
+
+ // Copy channels default mask
+ RegionCommonChanMaskCopy( NvmCtx.ChannelsMask, NvmCtx.ChannelsDefaultMask, 6 );
+
+ // Copy into channels mask remaining
+ RegionCommonChanMaskCopy( NvmCtx.ChannelsMaskRemaining, NvmCtx.ChannelsMask, 6 );
+ break;
+ }
+ case INIT_TYPE_RESTORE_CTX:
+ {
+ if( params->NvmCtx != 0 )
+ {
+ memcpy1( (uint8_t*) &NvmCtx, (uint8_t*) params->NvmCtx, sizeof( NvmCtx ) );
+ }
+ break;
+ }
+ case INIT_TYPE_RESTORE_DEFAULT_CHANNELS:
+ {
+ // Copy channels default mask
+ RegionCommonChanMaskCopy( NvmCtx.ChannelsMask, NvmCtx.ChannelsDefaultMask, 6 );
+
+ for( uint8_t i = 0; i < 6; i++ )
+ { // Copy-And the channels mask
+ NvmCtx.ChannelsMaskRemaining[i] &= NvmCtx.ChannelsMask[i];
+ }
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+void* RegionUS915GetNvmCtx( GetNvmCtxParams_t* params )
+{
+ params->nvmCtxSize = sizeof( RegionUS915NvmCtx_t );
+ return &NvmCtx;
+}
+
+bool RegionUS915Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute )
+{
+ switch( phyAttribute )
+ {
+ case PHY_FREQUENCY:
+ {
+ return VerifyRfFreq( verify->Frequency );
+ }
+ 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;
+ }
+ default:
+ return false;
+ }
+}
+
+void RegionUS915ApplyCFList( ApplyCFListParams_t* applyCFList )
+{
+ // Size of the optional CF list must be 16 byte
+ if( applyCFList->Size != 16 )
+ {
+ return;
+ }
+
+ // Last byte CFListType must be 0x01 to indicate the CFList contains a series of ChMask fields
+ if( applyCFList->Payload[15] != 0x01 )
+ {
+ return;
+ }
+
+ // ChMask0 - ChMask4 must be set (every ChMask has 16 bit)
+ for( uint8_t chMaskItr = 0, cntPayload = 0; chMaskItr <= 4; chMaskItr++, cntPayload+=2 )
+ {
+ NvmCtx.ChannelsMask[chMaskItr] = (uint16_t) (0x00FF & applyCFList->Payload[cntPayload]);
+ NvmCtx.ChannelsMask[chMaskItr] |= (uint16_t) (applyCFList->Payload[cntPayload+1] << 8);
+ if( chMaskItr == 4 )
+ {
+ NvmCtx.ChannelsMask[chMaskItr] = NvmCtx.ChannelsMask[chMaskItr] & CHANNELS_MASK_500KHZ_MASK;
+ }
+ // Set the channel mask to the remaining
+ NvmCtx.ChannelsMaskRemaining[chMaskItr] &= NvmCtx.ChannelsMask[chMaskItr];
+ }
+}
+
+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( NvmCtx.ChannelsMask, chanMaskSet->ChannelsMaskIn, CHANNELS_MASK_SIZE );
+
+ NvmCtx.ChannelsDefaultMask[4] = NvmCtx.ChannelsDefaultMask[4] & CHANNELS_MASK_500KHZ_MASK;
+ NvmCtx.ChannelsDefaultMask[5] = 0x0000;
+
+ for( uint8_t i = 0; i < CHANNELS_MASK_SIZE; i++ )
+ { // Copy-And the channels mask
+ NvmCtx.ChannelsMaskRemaining[i] &= NvmCtx.ChannelsMask[i];
+ }
+ break;
+ }
+ case CHANNELS_DEFAULT_MASK:
+ {
+ RegionCommonChanMaskCopy( NvmCtx.ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, CHANNELS_MASK_SIZE );
+ break;
+ }
+ default:
+ return false;
+ }
+ return true;
+}
+
+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.GetWakeupTime( ), &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;
+ const char *slotStrings[] = { "1", "2", "C", "Multi_C", "P", "Multi_P" };
+
+ if( Radio.GetStatus( ) != RF_IDLE )
+ {
+ return false;
+ }
+
+ if( rxConfig->RxSlot == RX_SLOT_WIN_1 )
+ {
+ // 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 );
+ if ( rxConfig->RxSlot < RX_SLOT_NONE )
+ {
+ MW_LOG( "RX_%s on freq %d Hz at DR %d\n\r", slotStrings[rxConfig->RxSlot], frequency, dr );
+ }
+ else
+ {
+ MW_LOG( "RX on freq %d Hz at DR %d\n\r", frequency, dr );
+ }
+
+ *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, NvmCtx.Bands[NvmCtx.Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, NvmCtx.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( NvmCtx.Channels[txConfig->Channel].Frequency );
+
+ Radio.SetTxConfig( MODEM_LORA, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 4000 );
+ MW_LOG( "TX on freq %d Hz at DR %d\n\r", NvmCtx.Channels[txConfig->Channel].Frequency, txConfig->Datarate );
+
+ // 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, NvmCtx.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 & CHANNELS_MASK_500KHZ_MASK;
+ }
+ 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 & CHANNELS_MASK_500KHZ_MASK;
+ }
+ else if( linkAdrParams.ChMaskCtrl == 5 )
+ {
+ // Start value for comparision
+ uint8_t bitMask = 1;
+
+ // cntChannelMask for channelsMask[0] until channelsMask[3]
+ uint8_t cntChannelMask = 0;
+
+ // i will be 1, 2, 3, ..., 7
+ for( uint8_t i = 0; i <= 7; i++ )
+ {
+ // 8 MSBs of ChMask are RFU
+ // Checking if the ChMask is set, then true
+ if( ( ( linkAdrParams.ChMask & 0x00FF ) & ( bitMask << i ) ) != 0 )
+ {
+ if( ( i % 2 ) == 0 )
+ {
+ // Enable a bank of 8 125kHz channels, 8 LSBs
+ channelsMask[cntChannelMask] |= 0x00FF;
+ // Enable the corresponding 500kHz channel
+ channelsMask[4] |= ( bitMask << i );
+ }
+ else
+ {
+ // Enable a bank of 8 125kHz channels, 8 MSBs
+ channelsMask[cntChannelMask] |= 0xFF00;
+ // Enable the corresponding 500kHz channel
+ channelsMask[4] |= ( bitMask << i );
+ // cntChannelMask increment for uneven i
+ cntChannelMask++;
+ }
+ }
+ // ChMask is not set
+ else
+ {
+ if( ( i % 2 ) == 0 )
+ {
+ // Disable a bank of 8 125kHz channels, 8 LSBs
+ channelsMask[cntChannelMask] &= 0xFF00;
+ // Disable the corresponding 500kHz channel
+ channelsMask[4] &= ~( bitMask << i );
+ }
+ else
+ {
+ // Enable a bank of 8 125kHz channels, 8 MSBs
+ channelsMask[cntChannelMask] &= 0x00FF;
+ // Disable the corresponding 500kHz channel
+ channelsMask[4] &= ~( bitMask << i );
+ // cntChannelMask increment for uneven i
+ cntChannelMask++;
+ }
+ }
+ }
+ }
+ 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 = NvmCtx.Channels;
+ linkAdrVerifyParams.MinTxPower = US915_MIN_TX_POWER;
+ linkAdrVerifyParams.MaxTxPower = US915_MAX_TX_POWER;
+ linkAdrVerifyParams.Version = linkAdrReq->Version;
+
+ // 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( NvmCtx.ChannelsMask, channelsMask, 6 );
+
+ NvmCtx.ChannelsMaskRemaining[0] &= NvmCtx.ChannelsMask[0];
+ NvmCtx.ChannelsMaskRemaining[1] &= NvmCtx.ChannelsMask[1];
+ NvmCtx.ChannelsMaskRemaining[2] &= NvmCtx.ChannelsMask[2];
+ NvmCtx.ChannelsMaskRemaining[3] &= NvmCtx.ChannelsMask[3];
+ NvmCtx.ChannelsMaskRemaining[4] = NvmCtx.ChannelsMask[4];
+ NvmCtx.ChannelsMaskRemaining[5] = NvmCtx.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;
+
+ // Verify radio frequency
+ if( VerifyRfFreq( rxParamSetupReq->Frequency ) == false )
+ {
+ 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( int8_t currentDr, AlternateDrType_t type )
+{
+ // Alternates the data rate according to the channel sequence:
+ // Eight times a 125kHz DR_0 and then one 500kHz DR_4 channel
+ if( type == ALTERNATE_DR )
+ {
+ NvmCtx.JoinTrialsCounter++;
+ }
+ else
+ {
+ NvmCtx.JoinTrialsCounter--;
+ }
+
+ if( NvmCtx.JoinTrialsCounter % 9 == 0 )
+ {
+ // Use DR_4 every 9th times.
+ currentDr = DR_4;
+ }
+ else
+ {
+ currentDr = DR_0;
+ }
+ return currentDr;
+}
+
+void RegionUS915CalcBackOff( CalcBackOffParams_t* calcBackOff )
+{
+ RegionCommonCalcBackOffParams_t calcBackOffParams;
+
+ calcBackOffParams.Channels = NvmCtx.Channels;
+ calcBackOffParams.Bands = NvmCtx.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 );
+}
+
+LoRaMacStatus_t 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;
+ uint8_t newChannelIndex;
+
+ // Count 125kHz channels
+ if( RegionCommonCountChannels( NvmCtx.ChannelsMaskRemaining, 0, 4 ) == 0 )
+ { // Reactivate default channels
+ RegionCommonChanMaskCopy( NvmCtx.ChannelsMaskRemaining, NvmCtx.ChannelsMask, 4 );
+
+ NvmCtx.JoinChannelGroupsCurrentIndex = 0;
+ }
+ // Check other channels
+ if( nextChanParams->Datarate >= DR_4 )
+ {
+ if( ( NvmCtx.ChannelsMaskRemaining[4] & CHANNELS_MASK_500KHZ_MASK ) == 0 )
+ {
+ NvmCtx.ChannelsMaskRemaining[4] = NvmCtx.ChannelsMask[4];
+ }
+ }
+
+ TimerTime_t elapsed = TimerGetElapsedTime( nextChanParams->LastAggrTx );
+ if( ( nextChanParams->LastAggrTx == 0 ) || ( nextChanParams->AggrTimeOff <= elapsed ) )
+ {
+ // Reset Aggregated time off
+ *aggregatedTimeOff = 0;
+
+ // Update bands Time OFF
+ nextTxDelay = RegionCommonUpdateBandTimeOff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, NvmCtx.Bands, US915_MAX_NB_BANDS );
+
+ // Search how many channels are enabled
+ nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Datarate,
+ NvmCtx.ChannelsMaskRemaining, NvmCtx.Channels,
+ NvmCtx.Bands, enabledChannels, &delayTx );
+ }
+ else
+ {
+ delayTx++;
+ nextTxDelay = nextChanParams->AggrTimeOff - elapsed;
+ }
+
+ if( nbEnabledChannels > 0 )
+ {
+ if( nextChanParams->Joined == true )
+ {
+ // Choose randomly on of the remaining channels
+ *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )];
+ }
+ else
+ {
+ // For rapid network acquisition in mixed gateway channel plan environments, the device
+ // follow a random channel selection sequence. It probes alternating one out of a
+ // group of eight 125 kHz channels followed by probing one 500 kHz channel each pass.
+ // Each time a 125 kHz channel will be selected from another group.
+
+ // 125kHz Channels (0 - 63) DR0
+ if( nextChanParams->Datarate == DR_0 )
+ {
+ if( ComputeNext125kHzJoinChannel( &newChannelIndex ) == LORAMAC_STATUS_PARAMETER_INVALID )
+ {
+ return LORAMAC_STATUS_PARAMETER_INVALID;
+ }
+ *channel = newChannelIndex;
+ }
+ // 500kHz Channels (64 - 71) DR4
+ else
+ {
+ // Choose the next available channel
+ uint8_t i = 0;
+ while( ( ( NvmCtx.ChannelsMaskRemaining[4] & CHANNELS_MASK_500KHZ_MASK ) & ( 1 << i ) ) == 0 )
+ {
+ i++;
+ }
+ *channel = 64 + i;
+ }
+ }
+
+ // Disable the channel in the mask
+ RegionCommonChanDisable( NvmCtx.ChannelsMaskRemaining, *channel, US915_MAX_NB_CHANNELS );
+
+ *time = 0;
+ return LORAMAC_STATUS_OK;
+ }
+ else
+ {
+ if( delayTx > 0 )
+ {
+ // Delay transmission due to AggregatedTimeOff or to a band time off
+ *time = nextTxDelay;
+ return LORAMAC_STATUS_DUTYCYCLE_RESTRICTED;
+ }
+ // Datarate not supported by any channel
+ *time = 0;
+ return LORAMAC_STATUS_NO_CHANNEL_FOUND;
+ }
+}
+
+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, NvmCtx.Bands[NvmCtx.Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, NvmCtx.ChannelsMask );
+ int8_t phyTxPower = 0;
+ uint32_t frequency = NvmCtx.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 )
+{
+ RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup;
+
+ regionCommonRxBeaconSetup.Datarates = DataratesUS915;
+ regionCommonRxBeaconSetup.Frequency = rxBeaconSetup->Frequency;
+ regionCommonRxBeaconSetup.BeaconSize = US915_BEACON_SIZE;
+ regionCommonRxBeaconSetup.BeaconDatarate = US915_BEACON_CHANNEL_DR;
+ regionCommonRxBeaconSetup.BeaconChannelBW = US915_BEACON_CHANNEL_BW;
+ regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime;
+ regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout;
+
+ RegionCommonRxBeaconSetup( ®ionCommonRxBeaconSetup );
+
+ // Store downlink datarate
+ *outDr = US915_BEACON_CHANNEL_DR;
+}
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionUS915.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionUS915.h
new file mode 100644
index 00000000..37c08b17
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionUS915.h
@@ -0,0 +1,506 @@
+/*!
+ * \file RegionUS915.h
+ *
+ * \brief Region definition for US915
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013-2017 Semtech
+ *
+ * ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ * embedded.connectivity.solutions===============
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Gregory Cristian ( Semtech )
+ *
+ * \author Daniel Jaeckle ( STACKFORCE )
+ *
+ * \author Johannes Bruder ( STACKFORCE )
+ *
+ * \defgroup REGIONUS915 Region US915
+ * Implementation according to LoRaWAN Specification v1.0.2.
+ * \{
+ */
+#ifndef __REGION_US915_H__
+#define __REGION_US915_H__
+
+#include "region/Region.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
+ */
+#if (defined(CERTIF_LORAWAN_VERSION) &&(CERTIF_LORAWAN_VERSION == 103))
+#define US915_MIN_TX_POWER TX_POWER_14
+#else
+#define US915_MIN_TX_POWER TX_POWER_10
+#endif
+/*!
+ * 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 frequency
+ */
+#define US915_BEACON_CHANNEL_FREQ 923300000
+
+/*!
+ * Beacon frequency channel stepwidth
+ */
+#define US915_BEACON_CHANNEL_STEPWIDTH 600000
+
+/*!
+ * Number of possible beacon channels
+ */
+#define US915_BEACON_NB_CHANNELS 8
+
+/*!
+ * Payload size of a beacon frame
+ */
+#define US915_BEACON_SIZE 23
+
+/*!
+ * Size of RFU 1 field
+ */
+#define US915_RFU1_SIZE 5
+
+/*!
+ * Size of RFU 2 field
+ */
+#define US915_RFU2_SIZE 3
+
+/*!
+ * Datarate of the beacon channel
+ */
+#define US915_BEACON_CHANNEL_DR DR_8
+
+/*!
+ * Bandwith of the beacon channel
+ */
+#define US915_BEACON_CHANNEL_BW 2
+
+/*!
+ * Ping slot channel datarate
+ */
+#define US915_PING_SLOT_CHANNEL_DR DR_8
+
+/*!
+ * LoRaMac maximum number of bands
+ */
+#define US915_MAX_NB_BANDS 1
+
+/*!
+ * Band 0 definition
+ * { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff }
+ */
+#define US915_BAND0 { 1, US915_MAX_TX_POWER, 0, 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( InitDefaultsParams_t* params );
+
+/*!
+ * \brief Returns a pointer to the internal context and its size.
+ *
+ * \param [OUT] params Pointer to the function parameters.
+ *
+ * \retval Points to a structure where the module store its non-volatile context.
+ */
+void* RegionUS915GetNvmCtx( GetNvmCtxParams_t* params );
+
+/*!
+ * \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 );
+
+/*!
+ * 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] currentDr Current datarate.
+ *
+ * \param [IN] type Alternation type.
+ *
+ * \retval Datarate to apply.
+ */
+int8_t RegionUS915AlternateDr( int8_t currentDr, AlternateDrType_t type );
+
+/*!
+ * \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]
+ */
+LoRaMacStatus_t 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 );
+
+/*!
+ * \brief Sets the radio into beacon reception mode
+ *
+ * \param [IN] rxBeaconSetup Pointer to the function parameters
+ */
+ void RegionUS915RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr );
+
+/*! \} defgroup REGIONUS915 */
+
+#endif // __REGION_US915_H__
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/secure-element.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/secure-element.h
new file mode 100644
index 00000000..aee98ed9
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/secure-element.h
@@ -0,0 +1,207 @@
+/*!
+ * \file secure-element.h
+ *
+ * \brief Secure Element driver API
+ *
+ * \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 )
+ *
+ * \author Johannes Bruder ( STACKFORCE )
+ *
+ * \defgroup SECUREELEMENT Secure Element API Definition
+ *
+ * \{
+ *
+ */
+#ifndef __SECURE_ELEMENT_H__
+#define __SECURE_ELEMENT_H__
+
+#include
+#include "LoRaMacCrypto.h"
+
+#define SE_EUI_SIZE 16
+
+/*!
+ * Return values.
+ */
+typedef enum eSecureElementStatus
+{
+ /*!
+ * No error occurred
+ */
+ SECURE_ELEMENT_SUCCESS = 0,
+ /*!
+ * CMAC does not match
+ */
+ SECURE_ELEMENT_FAIL_CMAC,
+ /*!
+ * Null pointer exception
+ */
+ SECURE_ELEMENT_ERROR_NPE,
+ /*!
+ * Invalid key identifier exception
+ */
+ SECURE_ELEMENT_ERROR_INVALID_KEY_ID,
+ /*!
+ * Invalid LoRaWAN specification version
+ */
+ SECURE_ELEMENT_ERROR_INVALID_LORAWAM_SPEC_VERSION,
+ /*!
+ * Incompatible buffer size
+ */
+ SECURE_ELEMENT_ERROR_BUF_SIZE,
+ /*!
+ * Undefined Error occurred
+ */
+ SECURE_ELEMENT_ERROR,
+}SecureElementStatus_t;
+
+/*!
+ * Signature of callback function to be called by the Secure Element driver when the
+ * non volatile context have to be stored.
+ *
+ */
+typedef void ( *SecureElementNvmEvent )( void );
+
+/*!
+ * Initialization of Secure Element driver
+ *
+ * \param[IN] seNvmCtxChanged - Callback function which will be called when the
+ * non-volatile context have to be stored.
+ * \retval - Status of the operation
+ */
+SecureElementStatus_t SecureElementInit( SecureElementNvmEvent seNvmCtxChanged );
+
+/*!
+ * Restores the internal nvm context from passed pointer.
+ *
+ * \param[IN] seNvmCtx - Pointer to non-volatile module context to be restored.
+ * \retval - Status of the operation
+ */
+SecureElementStatus_t SecureElementRestoreNvmCtx( void* seNvmCtx );
+
+/*!
+ * Returns a pointer to the internal non-volatile context.
+ *
+ * \param[IN] seNvmCtxSize - Size of the module non volatile context
+ * \retval - Points to a structure where the module store its non volatile context
+ */
+void* SecureElementGetNvmCtx( size_t* seNvmCtxSize );
+
+/*!
+ * Sets a key
+ *
+ * \param[IN] keyID - Key identifier
+ * \param[IN] key - Key value
+ * \retval - Status of the operation
+ */
+SecureElementStatus_t SecureElementSetKey( KeyIdentifier_t keyID, uint8_t* key );
+
+/*!
+ * Computes a CMAC of a message using provided initial Bx block
+ *
+ * \param[IN] micBxBuffer - Buffer containing the initial Bx block
+ * \param[IN] buffer - Data buffer
+ * \param[IN] size - Data buffer size
+ * \param[IN] keyID - Key identifier to determine the AES key to be used
+ * \param[OUT] cmac - Computed cmac
+ * \retval - Status of the operation
+ */
+SecureElementStatus_t SecureElementComputeAesCmac( uint8_t* micBxBuffer, uint8_t* buffer, uint16_t size, KeyIdentifier_t keyID, uint32_t* cmac );
+
+/*!
+ * Verifies a CMAC (computes and compare with expected cmac)
+ *
+ * \param[IN] buffer - Data buffer
+ * \param[IN] size - Data buffer size
+ * \param[in] expectedCmac - Expected cmac
+ * \param[IN] keyID - Key identifier to determine the AES key to be used
+ * \retval - Status of the operation
+ */
+SecureElementStatus_t SecureElementVerifyAesCmac( uint8_t* buffer, uint16_t size, uint32_t expectedCmac, KeyIdentifier_t keyID );
+
+/*!
+ * Encrypt a buffer
+ *
+ * \param[IN] buffer - Data buffer
+ * \param[IN] size - Data buffer size
+ * \param[IN] keyID - Key identifier to determine the AES key to be used
+ * \param[OUT] encBuffer - Encrypted buffer
+ * \retval - Status of the operation
+ */
+SecureElementStatus_t SecureElementAesEncrypt( uint8_t* buffer, uint16_t size, KeyIdentifier_t keyID, uint8_t* encBuffer );
+
+/*!
+ * Derives and store a key
+ *
+ * \param[IN] version - LoRaWAN specification version currently in use.
+ * \param[IN] input - Input data from which the key is derived ( 16 byte )
+ * \param[IN] rootKeyID - Key identifier of the root key to use to perform the derivation
+ * \param[IN] targetKeyID - Key identifier of the key which will be derived
+ * \retval - Status of the operation
+ */
+SecureElementStatus_t SecureElementDeriveAndStoreKey( Version_t version, uint8_t* input, KeyIdentifier_t rootKeyID, KeyIdentifier_t targetKeyID );
+
+/*!
+ * Generates a random number
+ *
+ * \param[OUT] randomNum - 32 bit random number
+ * \retval - Status of the operation
+ */
+SecureElementStatus_t SecureElementRandomNumber( uint32_t* randomNum );
+
+/*!
+ * Sets the DevEUI
+ *
+ * \param[IN] devEui - Pointer to the 16-byte devEUI
+ * \retval - Status of the operation
+ */
+SecureElementStatus_t SecureElementSetDevEui( uint8_t* devEui );
+
+/*!
+ * Gets the DevEUI
+ *
+ * \retval - Pointer to the 16-byte devEUI
+ */
+uint8_t* SecureElementGetDevEui( void );
+
+/*!
+ * Sets the JoinEUI
+ *
+ * \param[IN] joinEui - Pointer to the 16-byte joinEui
+ * \retval - Status of the operation
+ */
+SecureElementStatus_t SecureElementSetJoinEui( uint8_t* joinEui );
+
+/*!
+ * Gets the DevEUI
+ *
+ * \retval - Pointer to the 16-byte joinEui
+ */
+uint8_t* SecureElementGetJoinEui( void );
+
+/*! \} defgroup SECUREELEMENT */
+
+#endif // __SECURE_ELEMENT_H__
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/app_conf_template.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/app_conf_template.h
new file mode 100644
index 00000000..66077eef
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/app_conf_template.h
@@ -0,0 +1,56 @@
+/**
+ ******************************************************************************
+ * @file app_conf_template.h
+ * @author MCD Application Team
+ * @brief applic configuration, e.g. : debug, trace, low power, sensors
+ * @note this is a template: the app_conf.h shall be defined in the Project/...
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under BSD 3-Clause license,
+ * the "License"; You may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ * opensource.org/licenses/BSD-3-Clause
+ *
+ ******************************************************************************
+ */
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __APP_CONF_H__
+#define __APP_CONF_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+/* Exported types ------------------------------------------------------------*/
+/* Exported constants --------------------------------------------------------*/
+
+/* LoraWAN application configuration (Mw is configured by lorawan_conf.h) */
+#define ACTIVE_REGION LORAMAC_REGION_EU868
+#define HYBRID_ENABLED 0
+
+#define LORAMAC_CLASSB_ACTIVE 0 /* 0 if not active, 1 if active */
+
+/* Enable trace logs. Note verbose level can be selected in app_system.h */
+#define APP_LOG_ENABLED 1
+
+#define DEBUGGER_ON 0 /* if ON (=1) it enables the debbugger plus 4 dgb pins */
+ /* if OFF (=0) the debugger is OFF (lower consumption) */
+
+/* External variables --------------------------------------------------------*/
+/* Exported macros -----------------------------------------------------------*/
+/* Defines -------------------------------------------------------------------*/
+/* Exported functions ------------------------------------------------------- */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __APP_CONF_H__ */
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/app_system_template.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/app_system_template.h
new file mode 100644
index 00000000..378f1515
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/app_system_template.h
@@ -0,0 +1,62 @@
+/**
+ ******************************************************************************
+ * @file app_system_template.h
+ * @author MCD Application Team
+ * @brief function prototypes for app_system.c file
+ * @note this is a template: the app_conf.h shall be defined in the Project/...
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under BSD 3-Clause license,
+ * the "License"; You may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ * opensource.org/licenses/BSD-3-Clause
+ *
+ ******************************************************************************
+ */
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __APP_SYSTEM_H__
+#define __APP_SYSTEM_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+#include
+#include "app_conf.h"
+#include "trace.h"
+
+/* Exported types ------------------------------------------------------------*/
+/* Exported constants --------------------------------------------------------*/
+#define VERBOSE_LEVEL VLEVEL_L
+
+/* External variables --------------------------------------------------------*/
+/* Exported macros -----------------------------------------------------------*/
+#define APP_PPRINTF(...) do{ } while( 0!= TraceSend(0, 0, __VA_ARGS__) ) //Polling Mode
+#define APP_TPRINTF(...) do{ {TraceSend(1, 0, __VA_ARGS__);} }while(0); //with timestamp
+#define APP_PRINTF(...) do{ {TraceSend(0, 0, __VA_ARGS__);} }while(0);
+
+
+#if defined (APP_LOG_ENABLED) && (APP_LOG_ENABLED == 1)
+#define APP_LOG(TS,VL,...) do{ {TraceSend(TS, VL, __VA_ARGS__);} }while(0);
+#elif defined (APP_LOG_ENABLED) && (APP_LOG_ENABLED == 0) /* APP_LOG disabled */
+#define APP_LOG(TS,VL,...)
+#else
+#error "APP_LOG_ENABLED not defined or out of range <0,1>"
+#endif
+
+/* Defines -------------------------------------------------------------------*/
+/* Exported functions ------------------------------------------------------- */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __APP_SYSTEM_H__ */
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/lora-test.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/lora-test.c
new file mode 100644
index 00000000..d0b33d56
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/lora-test.c
@@ -0,0 +1,433 @@
+/******************************************************************************
+ * @file lora-test.c
+ * @author MCD Application Team
+ * @brief lora API to drive the lora state Machine
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under BSD 3-Clause license,
+ * the "License"; You may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ * opensource.org/licenses/BSD-3-Clause
+ *
+ ******************************************************************************
+ */
+
+
+
+/* Includes ------------------------------------------------------------------*/
+#include "stddef.h"
+//#include "lorawan_conf.h" /* Don't remove: needed for LORAMAC_CLASSB_ENABLED */
+//#include "mw_log_conf.h"
+#include "LoRaMac.h"
+#include "LoRaMacTest.h"
+#include "lora.h"
+
+
+/* Private typedef -----------------------------------------------------------*/
+
+typedef struct ComplianceTest_s
+{
+ bool Running;
+ uint8_t State;
+ LoraConfirm_t IsTxConfirmed;
+ uint8_t DataBufferSize;
+ uint8_t DataBuffer[242]; /* max possible payload */
+ uint16_t DownLinkCounter;
+ bool LinkCheck;
+ uint8_t DemodMargin;
+ uint8_t NbGateways;
+} ComplianceTest_t;
+
+typedef enum
+{
+ CERTIF_TX_PROCESS = 0,
+ CERTIF_RX_PROCESS
+} requestProcess_t;
+
+/* Private define ------------------------------------------------------------*/
+#define TEST_TX_DUTYCYCLE 5000
+/* Private variables ---------------------------------------------------------*/
+
+
+static requestProcess_t requested_process_id;
+
+/*!
+ * Timer to handle the application data transmission duty cycle
+ */
+static TimerEvent_t CertifTxNextPacketTimer;
+
+static ComplianceTest_t certifParam;
+static LoraConfirm_t IsTxConfirmed;
+static bool AdrEnableInit;
+static McpsIndication_t *CertifMcpsIndication;
+static MlmeReqJoin_t *CertifJoinParameters;
+
+/* Private functions ---------------------------------------------------------*/
+static bool CERTIF_running(void);
+
+static void CERTIF_DownLinkIncrement(void);
+
+static void CERTIF_linkCheck(MlmeConfirm_t *mlmeConfirm);
+
+static void CERTIF_rx_request(McpsIndication_t *mcpsIndication, MlmeReqJoin_t *JoinParameters);
+
+static void CERTIF_rx(McpsIndication_t *mcpsIndication, MlmeReqJoin_t *JoinParameters);
+
+static bool CERTIF_tx(void);
+
+static void OnCertifTxNextPacketTimerEvent(void *context);
+
+static void (*CertifProcessNotify)(void);
+
+static LoRaCertifCallback_t certifCb =
+{
+ CERTIF_running,
+ CERTIF_DownLinkIncrement,
+ CERTIF_linkCheck,
+ CERTIF_rx_request,
+};
+
+/* Exported functions definition---------------------------------------------------------*/
+
+void CERTIF_Init(void (*CERTIF_ProcessNotify)(void))
+{
+ /*initialises Callback into the Lora.c layer*/
+ LORA_TestInit(&certifCb);
+ /*Register TestProcessNotify callback*/
+ CertifProcessNotify = CERTIF_ProcessNotify;
+}
+
+void CERTIF_Process(void)
+{
+ switch (requested_process_id)
+ {
+ case CERTIF_TX_PROCESS:
+ CERTIF_tx();
+ break;
+ case CERTIF_RX_PROCESS:
+ CERTIF_rx(CertifMcpsIndication, CertifJoinParameters);
+ break;
+ default:
+ break;
+ }
+}
+static bool CERTIF_running(void)
+{
+ return certifParam.Running;
+}
+
+static void CERTIF_DownLinkIncrement(void)
+{
+ certifParam.DownLinkCounter++;
+}
+
+static void CERTIF_linkCheck(MlmeConfirm_t *mlmeConfirm)
+{
+ certifParam.LinkCheck = true;
+ certifParam.DemodMargin = mlmeConfirm->DemodMargin;
+ certifParam.NbGateways = mlmeConfirm->NbGateways;
+}
+
+static bool CERTIF_tx(void)
+{
+ McpsReq_t mcpsReq;
+ LoRaMacTxInfo_t txInfo;
+#if (ACTIVE_REGION == LORAMAC_REGION_US915)
+ bool abortTx = false;
+#endif /* ACTIVE_REGION == LORAMAC_REGION_US915 */
+
+ if (certifParam.LinkCheck == true)
+ {
+ certifParam.LinkCheck = false;
+ certifParam.DataBufferSize = 3;
+ certifParam.DataBuffer[0] = 5;
+ certifParam.DataBuffer[1] = certifParam.DemodMargin;
+ certifParam.DataBuffer[2] = certifParam.NbGateways;
+ certifParam.State = 1;
+ }
+ else
+ {
+ switch (certifParam.State)
+ {
+ case 4:
+ certifParam.State = 1;
+ break;
+ case 1:
+ certifParam.DataBufferSize = 2;
+ certifParam.DataBuffer[0] = certifParam.DownLinkCounter >> 8;
+ certifParam.DataBuffer[1] = certifParam.DownLinkCounter;
+ break;
+ }
+ }
+
+ if (LoRaMacQueryTxPossible(certifParam.DataBufferSize, &txInfo) != LORAMAC_STATUS_OK)
+ {
+#if (ACTIVE_REGION == LORAMAC_REGION_US915)
+ abortTx = true; /* Do not send empty frame as some testers do not support it */
+#else
+ // Send empty frame in order to flush MAC commands
+ mcpsReq.Type = MCPS_UNCONFIRMED;
+ mcpsReq.Req.Unconfirmed.fBuffer = NULL;
+ mcpsReq.Req.Unconfirmed.fBufferSize = 0;
+ mcpsReq.Req.Unconfirmed.Datarate = DR_0;
+#endif /* ACTIVE_REGION == LORAMAC_REGION_US915 */
+ }
+ else
+ {
+ if (IsTxConfirmed == LORAWAN_UNCONFIRMED_MSG)
+ {
+ mcpsReq.Type = MCPS_UNCONFIRMED;
+ mcpsReq.Req.Unconfirmed.fPort = CERTIF_PORT;
+ mcpsReq.Req.Unconfirmed.fBufferSize = certifParam.DataBufferSize;
+ mcpsReq.Req.Unconfirmed.fBuffer = &(certifParam.DataBuffer);
+ mcpsReq.Req.Unconfirmed.Datarate = DR_0;
+ }
+ else
+ {
+ mcpsReq.Type = MCPS_CONFIRMED;
+ mcpsReq.Req.Confirmed.fPort = CERTIF_PORT;
+ mcpsReq.Req.Confirmed.fBufferSize = certifParam.DataBufferSize;
+ mcpsReq.Req.Confirmed.fBuffer = &(certifParam.DataBuffer);
+ mcpsReq.Req.Confirmed.NbTrials = 8;
+ mcpsReq.Req.Confirmed.Datarate = DR_0;
+ }
+ }
+
+ /*cerification test on-going*/
+ TimerStart(&CertifTxNextPacketTimer);
+
+#if (ACTIVE_REGION == LORAMAC_REGION_US915)
+ if (abortTx == true)
+ {
+ return true;
+ }
+#endif /* ACTIVE_REGION == LORAMAC_REGION_US915 */
+
+ if (LoRaMacMcpsRequest(&mcpsReq) == LORAMAC_STATUS_OK)
+ {
+ return false;
+ }
+ return true;
+}
+
+static void CERTIF_rx_request(McpsIndication_t *mcpsIndication, MlmeReqJoin_t *JoinParameters)
+{
+ CertifMcpsIndication = mcpsIndication;
+ CertifJoinParameters = JoinParameters;
+
+ requested_process_id = CERTIF_RX_PROCESS;
+
+ CertifProcessNotify();
+}
+
+static void CERTIF_rx(McpsIndication_t *mcpsIndication, MlmeReqJoin_t *JoinParameters)
+{
+ if (certifParam.Running == false)
+ {
+ // Check compliance test enable command (i)
+ if ((mcpsIndication->BufferSize == 4) &&
+ (mcpsIndication->Buffer[0] == 0x01) &&
+ (mcpsIndication->Buffer[1] == 0x01) &&
+ (mcpsIndication->Buffer[2] == 0x01) &&
+ (mcpsIndication->Buffer[3] == 0x01))
+ {
+ MibRequestConfirm_t mibReq;
+ IsTxConfirmed = LORAWAN_UNCONFIRMED_MSG;
+ certifParam.DataBufferSize = 2;
+ certifParam.DownLinkCounter = 0;
+ certifParam.LinkCheck = false;
+ certifParam.DemodMargin = 0;
+ certifParam.NbGateways = 0;
+ certifParam.Running = true;
+ certifParam.State = 1;
+
+ mibReq.Type = MIB_ADR;
+
+ LoRaMacMibGetRequestConfirm(&mibReq);
+ AdrEnableInit = mibReq.Param.AdrEnable;
+
+ mibReq.Type = MIB_ADR;
+ mibReq.Param.AdrEnable = true;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+
+ if ( (ACTIVE_REGION == LORAMAC_REGION_EU868)
+ || (ACTIVE_REGION == LORAMAC_REGION_RU864)
+ || (ACTIVE_REGION == LORAMAC_REGION_CN779)
+ || (ACTIVE_REGION == LORAMAC_REGION_EU433))
+ {
+ LoRaMacTestSetDutyCycleOn(false);
+ }
+
+ TimerInit(&CertifTxNextPacketTimer, OnCertifTxNextPacketTimerEvent);
+ TimerSetValue(&CertifTxNextPacketTimer, TEST_TX_DUTYCYCLE);
+
+ /*confirm test mode activation */
+ CERTIF_tx();
+ }
+ }
+
+ else
+ {
+ certifParam.State = mcpsIndication->Buffer[0];
+ switch (certifParam.State)
+ {
+ case 0: // Check compliance test disable command (ii)
+ {
+
+ certifParam.DownLinkCounter = 0;
+ certifParam.Running = false;
+
+ MibRequestConfirm_t mibReq;
+ mibReq.Type = MIB_ADR;
+ mibReq.Param.AdrEnable = AdrEnableInit;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+
+ if ( (ACTIVE_REGION == LORAMAC_REGION_EU868)
+ || (ACTIVE_REGION == LORAMAC_REGION_RU864)
+ || (ACTIVE_REGION == LORAMAC_REGION_CN779)
+ || (ACTIVE_REGION == LORAMAC_REGION_EU433))
+ {
+ LoRaMacTestSetDutyCycleOn(true);
+ }
+
+ break;
+ }
+ case 1: // (iii, iv)
+ certifParam.DataBufferSize = 2;
+ break;
+ case 2: // Enable confirmed messages (v)
+ IsTxConfirmed = LORAWAN_CONFIRMED_MSG;
+ certifParam.State = 1;
+ break;
+ case 3: // Disable confirmed messages (vi)
+ IsTxConfirmed = LORAWAN_UNCONFIRMED_MSG;
+ certifParam.State = 1;
+ break;
+ case 4: // (vii)
+ certifParam.DataBufferSize = mcpsIndication->BufferSize;
+
+ certifParam.DataBuffer[0] = 4;
+ for (uint8_t i = 1; i < certifParam.DataBufferSize; i++)
+ {
+ certifParam.DataBuffer[i] = mcpsIndication->Buffer[i] + 1;
+ }
+ break;
+ case 5: // (viii)
+ {
+ MlmeReq_t mlmeReq;
+ mlmeReq.Type = MLME_LINK_CHECK;
+ LoRaMacMlmeRequest(&mlmeReq);
+ break;
+ }
+ case 6: // (ix)
+ {
+ MlmeReq_t mlmeReq;
+
+ // Disable TestMode and revert back to normal operation
+
+ certifParam.DownLinkCounter = 0;
+ certifParam.Running = false;
+
+ MibRequestConfirm_t mibReq;
+ mibReq.Type = MIB_ADR;
+ mibReq.Param.AdrEnable = AdrEnableInit;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+
+ mlmeReq.Type = MLME_JOIN;
+ mlmeReq.Req.Join = *JoinParameters;
+
+ LoRaMacMlmeRequest(&mlmeReq);
+ break;
+ }
+
+ case 7: // (x)
+ {
+ if (mcpsIndication->BufferSize == 3)
+ {
+ MlmeReq_t mlmeReq;
+ mlmeReq.Type = MLME_TXCW;
+ mlmeReq.Req.TxCw.Timeout = (uint16_t)((mcpsIndication->Buffer[1] << 8) | mcpsIndication->Buffer[2]);
+ LoRaMacMlmeRequest(&mlmeReq);
+ }
+ else if (mcpsIndication->BufferSize == 7)
+ {
+ MlmeReq_t mlmeReq;
+ mlmeReq.Type = MLME_TXCW_1;
+ mlmeReq.Req.TxCw.Timeout = (uint16_t)((mcpsIndication->Buffer[1] << 8) | mcpsIndication->Buffer[2]);
+ mlmeReq.Req.TxCw.Frequency = (uint32_t)((mcpsIndication->Buffer[3] << 16) | (mcpsIndication->Buffer[4] << 8) | mcpsIndication->Buffer[5]) * 100;
+ mlmeReq.Req.TxCw.Power = mcpsIndication->Buffer[6];
+ LoRaMacMlmeRequest(&mlmeReq);
+ }
+ certifParam.State = 1;
+ break;
+ }
+
+ case 8: // Send DeviceTimeReq
+ {
+ MlmeReq_t mlmeReq;
+
+ mlmeReq.Type = MLME_DEVICE_TIME;
+
+ LoRaMacMlmeRequest(&mlmeReq);
+ break;
+ }
+#if defined (LORAMAC_CLASSB_ACTIVE) && ( LORAMAC_CLASSB_ACTIVE == 1 )
+ case 9: // Switch end device Class
+ {
+ MibRequestConfirm_t mibReq;
+
+ mibReq.Type = MIB_DEVICE_CLASS;
+ // CLASS_A = 0, CLASS_B = 1, CLASS_C = 2
+ mibReq.Param.Class = (DeviceClass_t)mcpsIndication->Buffer[1];
+ LoRaMacMibSetRequestConfirm(&mibReq);
+ break;
+ }
+ case 10: // Send PingSlotInfoReq
+ {
+ MlmeReq_t mlmeReq;
+
+ mlmeReq.Type = MLME_PING_SLOT_INFO;
+
+ mlmeReq.Req.PingSlotInfo.PingSlot.Value = mcpsIndication->Buffer[1];
+
+ LoRaMacMlmeRequest(&mlmeReq);
+ break;
+ }
+ case 11: // Send BeaconTimingReq
+ {
+ MlmeReq_t mlmeReq;
+
+ mlmeReq.Type = MLME_BEACON_TIMING;
+
+ LoRaMacMlmeRequest(&mlmeReq);
+ break;
+ }
+#elif !defined (LORAMAC_CLASSB_ACTIVE)
+ #error LORAMAC_CLASSB_ACTIVE not defined
+#endif
+ default:
+ break;
+ }
+ }
+
+ if (certifParam.Running == false)
+ {
+ /*certification test stops*/
+ TimerStop(&CertifTxNextPacketTimer);
+ }
+}
+
+/*!
+ * \brief Function executed on TxNextPacket Timeout event
+ */
+static void OnCertifTxNextPacketTimerEvent(void *context)
+{
+ requested_process_id = CERTIF_TX_PROCESS;
+ CertifProcessNotify();
+}
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
+
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/lora-test.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/lora-test.h
new file mode 100644
index 00000000..40bc4fff
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/lora-test.h
@@ -0,0 +1,72 @@
+/******************************************************************************
+ * @file lora_test.h
+ * @author MCD Application Team
+ * @brief lora API to drive the lora state Machine
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under BSD 3-Clause license,
+ * the "License"; You may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ * opensource.org/licenses/BSD-3-Clause
+ *
+ ******************************************************************************
+ */
+
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+
+#ifndef __LORA_TEST_H__
+#define __LORA_TEST_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+/* Exported constants --------------------------------------------------------*/
+
+#define CERTIF_PORT 224
+/* Exported types ------------------------------------------------------------*/
+
+
+/* External variables --------------------------------------------------------*/
+/* Exported macros -----------------------------------------------------------*/
+/* Exported functions ------------------------------------------------------- */
+
+/**
+ * @brief Certification Initialisation
+ * @param [IN] Notification call back to the appl that a CERTIF_Process must be called
+ * @retval none
+ */
+void CERTIF_Init(void (*CERTIF_ProcessNotify)(void));
+/**
+ * @brief CERTIF_Process to be called when a CERTIF_ProcessNotify callback is generated
+ * @param [IN] none
+ * @retval none
+ */
+void CERTIF_Process(void);
+
+/* Lora Test callbacks*/
+typedef struct sLoRaCertifCallback
+{
+ bool (*running)(void);
+
+ void (*DownLinkIncrement)(void);
+
+ void (*linkCheck)(MlmeConfirm_t *mlmeConfirm);
+
+ void (*rx)(McpsIndication_t *mcpsIndication, MlmeReqJoin_t *JoinParameters);
+
+} LoRaCertifCallback_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*__LORA_TEST_H__*/
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/lora.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/lora.c
new file mode 100644
index 00000000..d313d87f
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/lora.c
@@ -0,0 +1,985 @@
+/*
+ _/_/_/ _/_/_/ _/_/_/ _/_/_/ _/ _/ _/_/_/ _/ _/ _/_/_/_/
+ _/ _/ _/ _/ _/ _/_/ _/ _/ _/ _/ _/
+ _/_/_/ _/ _/_/ _/ _/ _/ _/ _/ _/_/ _/_/_/_/ _/_/_/
+ _/ _/ _/ _/ _/ _/ _/_/ _/ _/ _/ _/ _/
+_/ _/ _/_/_/ _/_/_/ _/_/_/ _/ _/ _/_/_/ _/ _/ _/
+ (C)2020/5/20 RisingHF, all rights reserved.
+ Lorawan API
+*/
+
+#include "timer.h"
+#include "LoRaMac.h"
+#include "LoRaMacTest.h"
+#include "lora.h"
+#include "app_system.h"
+#include "lorawan_info.h"
+#include "stm32_mem.h"
+
+
+typedef struct
+{
+ lw_state_t otaa; /*< ENABLE if over the air activation, DISABLE otherwise */
+ lw_state_t duty_cycle; /*< ENABLE if dutycyle is on, DISABLE otherwise */
+ uint8_t DevEui[8]; /*< Device EUI */
+ uint8_t JoinEui[8]; /*< Join Eui */
+ uint8_t AppKey[16]; /*< Application Key */
+ uint8_t NwkKey[16]; /*< Application Key */
+ uint8_t NwkSEncKey[16]; /*< Network Session Key */
+ uint8_t AppSKey[16]; /*< Application Session Key */
+ uint8_t FNwkSIntKey[16]; /*< Application Session Key */
+ uint8_t SNwkSIntKey[16]; /*< Application Session Key */
+ int16_t Rssi; /*< Rssi of the received packet */
+ int8_t Snr; /*< Snr of the received packet */
+ uint8_t application_port; /*< Application port we will receive to */
+ LoraConfirm_t ReqAck; /*< ENABLE if acknowledge is requested */
+ int8_t TxDatarate;
+}lorawan_configuration_t;
+
+/*!
+ * LoRa State Machine states
+ */
+typedef struct slorawan_param
+{
+ bool adr_enable; /*Activation state of adaptativeDatarate*/
+ bool enable_public_network; /*Enable or disable a public network*/
+ int8_t tx_datarate; /*Uplink datarate, if AdrEnable is off*/
+ uint8_t confirm_retry; /*the uplink confirm frame retry times*/
+ LoRaMacRegion_t active_region; /*region to open*/
+ uint32_t dev_addr;
+} lw_param_t;
+
+static lorawan_configuration_t lw_config =
+{
+ .otaa = LORA_ENABLE,
+ .duty_cycle = LORA_ENABLE,
+ .DevEui = LORAWAN_DEVICE_EUI,
+ .JoinEui = LORAWAN_JOIN_EUI,
+ .AppKey = LORAWAN_APP_KEY,
+ .NwkKey = LORAWAN_NWK_KEY,
+ .NwkSEncKey = LORAWAN_NWK_S_ENC_KEY,
+ .AppSKey = LORAWAN_APP_S_KEY,
+ .FNwkSIntKey = LORAWAN_F_NWK_S_INT_KEY,
+ .SNwkSIntKey = LORAWAN_S_NWK_S_INT_KEY,
+ .Rssi = 0,
+ .Snr = 0,
+ .ReqAck = LORAWAN_UNCONFIRMED_MSG,
+ .TxDatarate = 0
+};
+
+
+static lw_param_t lw_param_init =
+{
+ .adr_enable = 0,
+ .tx_datarate =DR_0,
+ .confirm_retry = 2,
+ .enable_public_network = LORAWAN_PUBLIC_NETWORK,
+ .active_region = LORAWAN_DEFAULT_ACTIVE_REGION,
+ .dev_addr = LORAWAN_DEVICE_ADDRESS
+};
+
+/*!
+ * Select either Device_Time_req or Beacon_Time_Req following LoRaWAN version
+ * - Device_Time_req Available for V1.0.3 or later
+ * - Beacon_time_Req Available for V1.0.2 and before
+ */
+#define USE_DEVICE_TIMING
+
+
+#include "LoRaMacTest.h"
+
+/*for classB=============================================================*/
+#ifndef LORAMAC_CLASSB_ACTIVE
+ #error LORAMAC_CLASSB_ACTIVE not defined ( shall be <0 or 1> )
+#endif /* LORAMAC_CLASSB_ACTIVE */
+
+#if ( LORAMAC_CLASSB_ACTIVE == 1 )
+/*!
+ * Default ping slots periodicity
+ *
+ * \remark periodicity is equal to 2^LORAWAN_DEFAULT_PING_SLOT_PERIODICITY seconds
+ * example: 2^3 = 8 seconds. The end-device will open an Rx slot every 8 seconds.
+ */
+#define LORAWAN_DEFAULT_PING_SLOT_PERIODICITY 0
+static uint8_t DefaultPingSlotPeriodicity;
+
+#endif /* LORAMAC_CLASSB_ACTIVE */
+/*=============================================================*/
+
+#define HEX16(X) X[0],X[1], X[2],X[3], X[4],X[5], X[6],X[7],X[8],X[9], X[10],X[11], X[12],X[13], X[14],X[15]
+#define HEX8(X) X[0],X[1], X[2],X[3], X[4],X[5], X[6],X[7]
+
+static MlmeReqJoin_t JoinParameters;
+
+
+
+
+/* Dummy data sent periodically to let the tester respond with start test command*/
+static TimerEvent_t TxcertifTimer;
+
+/* Private macro -------------------------------------------------------------*/
+/* Private variables ---------------------------------------------------------*/
+/* Private function prototypes -----------------------------------------------*/
+/*for classB=============================================================*/
+#if ( LORAMAC_CLASSB_ACTIVE == 1 )
+ static LoraErrorStatus lw_beacon_req(void);
+ static LoraErrorStatus lw_pingslot_req(void);
+
+ #if defined( USE_DEVICE_TIMING )
+ static LoraErrorStatus lw_device_time_req(void);
+ #else
+ static LoraErrorStatus lw_beacon_time_req(void);
+ #endif /* USE_DEVICE_TIMING */
+#endif /* LORAMAC_CLASSB_ACTIVE */
+/*======================================================================*/
+
+
+
+static LoRaMacPrimitives_t LoRaMacPrimitives;
+static LoRaMacCallback_t LoRaMacCallbacks;
+static MibRequestConfirm_t mibReq;
+
+
+static LoRaMainCallback_t *LoRaMainCallbacks;
+
+
+/* Private user code ---------------------------------------------------------*/
+/*!
+ * \brief MCPS-Confirm event function
+ *
+ * \param [IN] McpsConfirm - Pointer to the confirm structure,
+ * containing confirm attributes.
+ */
+static void McpsConfirm(McpsConfirm_t *mcpsConfirm)
+{
+ lw_app_data_t app_data = {0};
+
+ if (mcpsConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK)
+ {
+ switch (mcpsConfirm->McpsRequest)
+ {
+ case MCPS_UNCONFIRMED:
+ case MCPS_CONFIRMED:
+ {
+ // Check Datarate
+ // Check TxPower
+ // Check AckReceived
+
+ // Check NbTrials
+ break;
+ }
+ case MCPS_PROPRIETARY:
+ {
+ break;
+ }
+ default:
+ break;
+
+
+ }
+ }
+ app_data.ack = mcpsConfirm->AckReceived;
+ LoRaMainCallbacks->app_events(APP_TX_DONE_EVENT,&app_data);
+
+}
+
+/*!
+ * \brief MCPS-Indication event function
+ *
+ * \param [IN] mcpsIndication - Pointer to the indication structure,
+ * containing indication attributes.
+ */
+static void McpsIndication(McpsIndication_t *mcpsIndication)
+{
+ lw_app_data_t app_data ={0};
+
+ if(mcpsIndication->Status != LORAMAC_EVENT_INFO_STATUS_OK)
+ {
+ return;
+ }
+
+ switch (mcpsIndication->McpsIndication)
+ {
+ case MCPS_UNCONFIRMED:
+ {
+ break;
+ }
+ case MCPS_CONFIRMED:
+ {
+ break;
+ }
+ case MCPS_PROPRIETARY:
+ {
+ break;
+ }
+ case MCPS_MULTICAST:
+ {
+ break;
+ }
+ default:
+ break;
+ }
+
+ // Check Buffer
+ // Check BufferSize
+ // Check Rssi
+ // Check Snr
+ // Check RxSlot
+ if(mcpsIndication->RxData == true)
+ {
+ app_data.port = mcpsIndication->Port;
+ app_data.size = mcpsIndication->BufferSize;
+ app_data.buff = mcpsIndication->Buffer;
+ app_data.fpending = mcpsIndication->FramePending;
+
+ lw_config.Rssi = mcpsIndication->Rssi;
+ lw_config.Snr = mcpsIndication->Snr;
+ LoRaMainCallbacks->app_events(APP_RX_EVENT,&app_data);
+ }
+}
+
+/*!
+ * \brief MLME-Confirm event function
+ *
+ * \param [IN] MlmeConfirm - Pointer to the confirm structure,
+ * containing confirm attributes.
+ */
+static void MlmeConfirm(MlmeConfirm_t *mlmeConfirm)
+{
+#if (LORAMAC_CLASSB_ACTIVE == 1)
+ MibRequestConfirm_t mibReq;
+#endif /* LORAMAC_CLASSB_ACTIVE */
+
+ switch (mlmeConfirm->MlmeRequest)
+ {
+ case MLME_JOIN:
+ {
+ if(mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK)
+ {
+ // Status is OK, node has joined the network
+ LoRaMainCallbacks->app_events(APP_JOINED_EVENT,NULL);
+ #if ( LORAMAC_CLASSB_ACTIVE == 1 )
+ #if defined( USE_DEVICE_TIMING )
+ lw_device_time_req();
+ #else
+ lw_beacon_time_req();
+ #endif /* USE_DEVICE_TIMING */
+ #endif /* LORAMAC_CLASSB_ACTIVE */
+ }
+ else
+ {
+ // Join was not successful. Try to join again
+ lw_join();
+ }
+ break;
+ }
+ case MLME_LINK_CHECK:
+ {
+ if (mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK)
+ {
+
+ }
+ break;
+ }
+#if ( LORAMAC_CLASSB_ACTIVE == 1 )
+ case MLME_BEACON_ACQUISITION:
+ {
+ if(mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK)
+ {
+ /* Beacon has been acquired */
+ /* REquest Server for Ping Slot */
+ lw_pingslot_req();
+ }
+ else
+ {
+ /* Beacon not acquired */
+ /* Search again */
+ /* we can check if the MAC has received a time reference for the beacon*/
+ /* in this case do either a Device_Time_Req or a Beacon_Timing_req*/
+ lw_beacon_req();
+ }
+ break;
+ }
+ case MLME_PING_SLOT_INFO:
+ {
+ if(mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK)
+ {
+ /* class B is now ativated*/
+ mibReq.Type = MIB_DEVICE_CLASS;
+ mibReq.Param.Class = CLASS_B;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+
+ if((lw_param_init.active_region == LORAMAC_REGION_AU915) || (lw_param_init.active_region == LORAMAC_REGION_US915))
+ {
+ mibReq.Type = MIB_PING_SLOT_DATARATE;
+ mibReq.Param.PingSlotDatarate = DR_8;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+ }
+ /*notify upper layer*/
+ /*ClassB*/
+ DeviceClass_t cur_class =CLASS_B;
+ LoRaMainCallbacks->app_events(APP_CONFIRM_ClASS_EVENT,&cur_class);
+ }
+ else
+ {
+ lw_pingslot_req();
+ }
+ break;
+ }
+#if defined( USE_DEVICE_TIMING )
+ case MLME_DEVICE_TIME:
+ {
+ if (mlmeConfirm->Status != LORAMAC_EVENT_INFO_STATUS_OK)
+ {
+ lw_device_time_req();
+ }
+ }
+#endif /* USE_DEVICE_TIMING */
+#endif /* LORAMAC_CLASSB_ACTIVE */
+ default:
+ break;
+ }
+}
+
+/*!
+ * \brief MLME-Indication event function
+ *
+ * \param [IN] MlmeIndication - Pointer to the indication structure.
+ */
+static void MlmeIndication(MlmeIndication_t *MlmeIndication)
+{
+ lw_app_data_t app_data ={0};
+
+#if ( LORAMAC_CLASSB_ACTIVE == 1 )
+ MibRequestConfirm_t mibReq;
+#endif /* LORAMAC_CLASSB_ACTIVE */
+
+ switch (MlmeIndication->MlmeIndication)
+ {
+ case MLME_SCHEDULE_UPLINK:
+ {
+ app_data.fpending = 1;
+ // The MAC signals that we shall provide an uplink as soon as possible
+ LoRaMainCallbacks->app_events(APP_RX_EVENT,&app_data);
+ break;
+ }
+ #if ( LORAMAC_CLASSB_ACTIVE == 1 )
+ case MLME_BEACON_LOST:
+ {
+ // Switch to class A again
+ mibReq.Type = MIB_DEVICE_CLASS;
+ mibReq.Param.Class = CLASS_A;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+ lw_beacon_req();
+ break;
+ }
+ case MLME_BEACON:
+ {
+ if(MlmeIndication->Status == LORAMAC_EVENT_INFO_STATUS_BEACON_LOCKED)
+ {
+ APP_LOG("BEACON RECEIVED\n\r");
+ }
+ else
+ {
+ APP_LOG("BEACON NOT RECEIVED\n\r");
+ }
+ break;
+ }
+ #endif /* LORAMAC_CLASSB_ACTIVE */
+ default:
+ break;
+ }
+}
+/**
+ * lorawan init
+ */
+void lw_init(LoRaMainCallback_t *callbacks)
+{
+ LoRaMacInfo_t *LoRaMacRegionInfo;
+
+ LoraMacInfo_Init();
+ LoRaMacRegionInfo = LoraMacInfo_GetPtr();
+ if(LoRaMacRegionInfo->Region == 0)
+ {
+ APP_PRINTF("error: At leats one region shall be defined in the MW: check lorawan_conf.h \n\r");
+ while(1) {} /* At leats one region shall be defined in MW */
+ }
+
+ #if ( LORAMAC_CLASSB_ACTIVE == 1 )
+ if(LoRaMacRegionInfo->ClassB != 1)
+ {
+ APP_PRINTF("error: ClassB is not defined in the MW: set lorawan_conf.h accordingly\n\r");
+ while(1) {} /* error: ClassB is not defined in the MW*/
+ }
+ #elif ( LORAMAC_CLASSB_ACTIVE == 0 )
+ if(LoRaMacRegionInfo->ClassB != 0)
+ {
+ APP_PRINTF("error: missmatch between applic and MW CLASSB defines: set lorawan_conf.h according to app_conf.h\n\r");
+ while(1) {} /* error: ClassB should not be defined in the MW*/
+ }
+ #endif /* LORAMAC_CLASSB_ACTIVE */
+
+ /* init the main call backs*/
+ LoRaMainCallbacks = callbacks;
+
+ #if (STATIC_DEVICE_EUI != 1)
+ LoRaMainCallbacks->BoardGetUniqueId(lw_config.DevEui);
+ #endif
+
+ #if (STATIC_DEVICE_ADDRESS != 1)
+ // Choose a "pseudo-random" device address
+ lw_param_init.dev_addr = LoRaMainCallbacks->BoardGetRandomSeed();
+ #endif
+
+ LoRaMacPrimitives.MacMcpsConfirm = McpsConfirm;
+ LoRaMacPrimitives.MacMcpsIndication = McpsIndication;
+ LoRaMacPrimitives.MacMlmeConfirm = MlmeConfirm;
+ LoRaMacPrimitives.MacMlmeIndication = MlmeIndication;
+ LoRaMacCallbacks.GetBatteryLevel = LoRaMainCallbacks->BoardGetBatteryLevel;
+ LoRaMacCallbacks.GetTemperatureLevel = LoRaMainCallbacks->BoardGetTemperatureLevel;
+ LoRaMacCallbacks.MacProcessNotify = LoRaMainCallbacks->MacProcessNotify;
+
+ if(0U != ((1<<(lw_param_init.active_region))& (LoRaMacRegionInfo->Region)))
+ {
+ LoRaMacInitialization(&LoRaMacPrimitives, &LoRaMacCallbacks, lw_param_init.active_region);
+ }
+ else
+ {
+ APP_PRINTF("error: Region is not defined in the MW: set lorawan_conf.h accordingly\n\r");
+ while(1) {} /* error: Region is not defined in the MW */
+ }
+ if((lw_param_init.active_region == LORAMAC_REGION_CN470)||(lw_param_init.active_region == LORAMAC_REGION_CN470ALI))
+ {
+ uint16_t channelMask[] = {0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000};
+ if(lw_param_init.active_region == LORAMAC_REGION_CN470){
+ channelMask[0] = 0x00FF;
+ }
+ else{
+ channelMask[0] = 0xFF00;
+ }
+ mibReq.Type = MIB_CHANNELS_MASK;
+ mibReq.Param.ChannelsMask = channelMask;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+ mibReq.Type = MIB_CHANNELS_DEFAULT_MASK;
+ mibReq.Param.ChannelsDefaultMask = channelMask;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+ }
+
+ lw_config_otaa_set(lw_config.otaa);
+
+ mibReq.Type = MIB_DEV_EUI;
+ mibReq.Param.DevEui = lw_config.DevEui;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+
+ mibReq.Type = MIB_JOIN_EUI;
+ mibReq.Param.JoinEui = lw_config.JoinEui;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+
+ mibReq.Type = MIB_ADR;
+ mibReq.Param.AdrEnable = lw_param_init.adr_enable;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+
+ mibReq.Type = MIB_PUBLIC_NETWORK;
+ mibReq.Param.EnablePublicNetwork = lw_param_init.enable_public_network;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+
+ mibReq.Type = MIB_APP_KEY;
+ mibReq.Param.AppKey = lw_config.AppKey;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+
+ mibReq.Type = MIB_NWK_KEY;
+ mibReq.Param.NwkKey = lw_config.NwkKey;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+
+ mibReq.Type = MIB_DEVICE_CLASS;
+ mibReq.Param.Class = CLASS_A;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+
+ if((lw_param_init.active_region == LORAMAC_REGION_EU868)
+ || (lw_param_init.active_region == LORAMAC_REGION_RU864)
+ || (lw_param_init.active_region == LORAMAC_REGION_CN779)
+ || (lw_param_init.active_region == LORAMAC_REGION_EU433))
+ {
+ LoRaMacTestSetDutyCycleOn(true);
+ lw_config.duty_cycle = LORA_ENABLE;
+ }
+ else
+ {
+ lw_config.duty_cycle = LORA_DISABLE;
+ }
+
+ mibReq.Type = MIB_SYSTEM_MAX_RX_ERROR;
+ mibReq.Param.SystemMaxRxError = 20;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+
+ #if ( LORAMAC_CLASSB_ACTIVE == 1 )
+ if (LoRaMacRegionInfo->ClassB == 1)
+ {
+ DefaultPingSlotPeriodicity = LORAWAN_DEFAULT_PING_SLOT_PERIODICITY;
+ }
+ #endif /* LORAMAC_CLASSB_ACTIVE */
+
+ /*set Mac statein Idle*/
+ LoRaMacStart();
+}
+
+LoRaMacStatus_t lw_join(void)
+{
+ LoRaMacStatus_t status;
+
+ if (lw_config.otaa == LORA_ENABLE)
+ {
+ MlmeReq_t mlmeReq;
+
+ mlmeReq.Type = MLME_JOIN;
+ mlmeReq.Req.Join.Datarate = lw_param_init.tx_datarate;
+
+ JoinParameters = mlmeReq.Req.Join;
+ status = LoRaMacMlmeRequest(&mlmeReq);
+ }
+ else
+ {
+ /*no Join in abp*/
+ status = LORAMAC_STATUS_BUSY;
+ }
+
+ return status;
+}
+
+LoraFlagStatus lw_join_status(void)
+{
+ MibRequestConfirm_t mibReq;
+
+ mibReq.Type = MIB_NETWORK_ACTIVATION;
+
+ LoRaMacMibGetRequestConfirm(&mibReq);
+
+ if(mibReq.Param.NetworkActivation == ACTIVATION_TYPE_NONE)
+ {
+ return LORA_RESET;
+ }
+ else
+ {
+ return LORA_SET;
+ }
+}
+
+LoRaMacStatus_t lw_current_chmask_get(LWChannel_mask_t* ch)
+{
+ mibReq.Type = MIB_CHANNELS_MASK;
+ mibReq.Param.ChannelsMask = (uint16_t*)ch;
+ LoRaMacStatus_t sta = LoRaMacMibGetRequestConfirm(&mibReq);
+
+ if(LORAMAC_STATUS_OK == sta)
+ {
+ for(uint8_t i=0;i<6;i++)
+ {
+ ch->mask[i] = mibReq.Param.ChannelsMask[i];
+ }
+ }
+ return sta;
+}
+LoRaMacStatus_t lw_current_chmask_set(const LWChannel_mask_t* ch)
+{
+ static bool only;
+
+ if(only == false)
+ {
+ only = true;
+ mibReq.Type = MIB_CHANNELS_DEFAULT_MASK;
+ mibReq.Param.ChannelsDefaultMask = (uint16_t*)ch;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+ }
+
+ mibReq.Type = MIB_CHANNELS_MASK;
+ mibReq.Param.ChannelsMask = (uint16_t*)ch;
+ return LoRaMacMibSetRequestConfirm(&mibReq);
+}
+LoRaMacStatus_t lw_rxwin2_ch_set(const RxChannelParams_t* rx2_ch)
+{
+ LoRaMacStatus_t sta;
+
+ mibReq.Type = MIB_RX2_CHANNEL;
+ mibReq.Param.Rx2Channel.Frequency = rx2_ch->Frequency;
+ mibReq.Param.Rx2Channel.Datarate = rx2_ch->Datarate;
+
+ sta = LoRaMacMibSetRequestConfirm(&mibReq);
+ return sta;
+}
+
+LoRaMacStatus_t lw_rxwin2_ch_get(RxChannelParams_t* rx2_ch)
+{
+ LoRaMacStatus_t sta;
+ mibReq.Type = MIB_RX2_CHANNEL;
+
+ sta = LoRaMacMibGetRequestConfirm(&mibReq);
+
+ if(LORAMAC_STATUS_OK == sta)
+ {
+ *rx2_ch = mibReq.Param.Rx2Channel;
+ }
+ return sta;
+ }
+
+
+LoRaMacStatus_t lw_adr_set(lw_state_t sta)
+{
+ LoRaMacStatus_t n_sta=0;
+
+ mibReq.Type = MIB_ADR;
+ mibReq.Param.AdrEnable = sta ;
+ n_sta = LoRaMacMibSetRequestConfirm(&mibReq);
+ if(LORAMAC_STATUS_OK == n_sta){
+ lw_param_init.adr_enable = sta;
+ }
+ return n_sta;
+}
+
+
+LoRaMacStatus_t lw_send(lw_app_data_t *app_data, LoraConfirm_t IsTxConfirmed)
+{
+ McpsReq_t mcpsReq;
+ LoRaMacTxInfo_t txInfo;
+
+ if(LoRaMacQueryTxPossible(app_data->size, &txInfo) != LORAMAC_STATUS_OK)
+ {
+ // Send empty frame in order to flush MAC commands
+ mcpsReq.Type = MCPS_UNCONFIRMED;
+ mcpsReq.Req.Unconfirmed.fBuffer = NULL;
+ mcpsReq.Req.Unconfirmed.fBufferSize = 0;
+ mcpsReq.Req.Unconfirmed.Datarate = lw_config_tx_datarate_get() ;
+ }
+ else
+ {
+ if (IsTxConfirmed == LORAWAN_UNCONFIRMED_MSG)
+ {
+ mcpsReq.Type = MCPS_UNCONFIRMED;
+ mcpsReq.Req.Unconfirmed.fPort = app_data->port;
+ mcpsReq.Req.Unconfirmed.fBufferSize = app_data->size;
+ mcpsReq.Req.Unconfirmed.fBuffer = app_data->buff;
+ mcpsReq.Req.Unconfirmed.Datarate = lw_config_tx_datarate_get() ;
+ }
+ else
+ {
+ mcpsReq.Type = MCPS_CONFIRMED;
+ mcpsReq.Req.Confirmed.fPort = app_data->port;
+ mcpsReq.Req.Confirmed.fBufferSize = app_data->size;
+ mcpsReq.Req.Confirmed.fBuffer = app_data->buff;
+ mcpsReq.Req.Confirmed.NbTrials = lw_confirm_retry_get();
+ mcpsReq.Req.Confirmed.Datarate = lw_config_tx_datarate_get() ;
+ }
+ }
+ return LoRaMacMcpsRequest(&mcpsReq);
+
+}
+
+#if ( LORAMAC_CLASSB_ACTIVE == 1 )
+ #if defined( USE_DEVICE_TIMING )
+ static LoRaMacStatus_t lw_device_time_req(void)
+ {
+ MlmeReq_t mlmeReq;
+
+ mlmeReq.Type = MLME_DEVICE_TIME;
+
+ return LoRaMacMlmeRequest(&mlmeReq);
+ }
+ #else
+ static LoRaMacStatus_t lw_beacon_time_req(void)
+ {
+ MlmeReq_t mlmeReq;
+
+ mlmeReq.Type = MLME_BEACON_TIMING;
+
+ return LoRaMacMlmeRequest(&mlmeReq);
+ }
+ #endif /* USE_DEVICE_TIMING */
+
+ static LoRaMacStatus_t lw_beacon_req(void)
+ {
+ MlmeReq_t mlmeReq;
+
+ mlmeReq.Type = MLME_BEACON_ACQUISITION;
+
+ return LoRaMacMlmeRequest(&mlmeReq);
+ }
+
+ static LoRaMacStatus_t lw_pingslot_req(void)
+ {
+ MlmeReq_t mlmeReq;
+
+ mlmeReq.Type = MLME_LINK_CHECK;
+ LoRaMacMlmeRequest(&mlmeReq);
+
+ mlmeReq.Type = MLME_PING_SLOT_INFO;
+ mlmeReq.Req.PingSlotInfo.PingSlot.Fields.Periodicity = DefaultPingSlotPeriodicity;
+ mlmeReq.Req.PingSlotInfo.PingSlot.Fields.RFU = 0;
+
+ return LoRaMacMlmeRequest(&mlmeReq);
+ }
+#endif /* LORAMAC_CLASSB_ACTIVE */
+
+LoRaMacStatus_t lw_request_class(DeviceClass_t newClass)
+{
+ LoRaMacStatus_t Errorstatus = LORAMAC_STATUS_OK;
+
+ MibRequestConfirm_t mibReq;
+ DeviceClass_t currentClass;
+
+ mibReq.Type = MIB_DEVICE_CLASS;
+ LoRaMacMibGetRequestConfirm(&mibReq);
+
+ currentClass = mibReq.Param.Class;
+ /*attempt to swicth only if class update*/
+ if(currentClass != newClass)
+ {
+ switch(newClass)
+ {
+ case CLASS_A:
+ {
+ mibReq.Param.Class = CLASS_A;
+
+ Errorstatus = LoRaMacMibSetRequestConfirm(&mibReq);
+ if(Errorstatus == LORAMAC_STATUS_OK)
+ {
+ DeviceClass_t cur_class =CLASS_A;
+ LoRaMainCallbacks->app_events(APP_CONFIRM_ClASS_EVENT,&cur_class);
+ }
+ break;
+ }
+ case CLASS_B:
+ {
+ #if (LORAMAC_CLASSB_ACTIVE == 1)
+ if(currentClass != CLASS_A)
+ {
+ Errorstatus = LORAMAC_STATUS_ERROR;
+ }
+ /*switch is not instantanuous*/
+ Errorstatus = lw_beacon_req();
+ #else
+ APP_PRINTF("warning: LORAMAC_CLASSB_ACTIVE has not been defined at compilation\n\r");
+ #endif /* LORAMAC_CLASSB_ACTIVE */
+ break;
+ }
+ case CLASS_C:
+ {
+ if (currentClass != CLASS_A)
+ {
+ Errorstatus = LORAMAC_STATUS_ERROR;
+ }
+ /*switch is instantanuous*/
+ mibReq.Param.Class = CLASS_C;
+
+ Errorstatus = LoRaMacMibSetRequestConfirm(&mibReq);
+
+ if(Errorstatus == LORAMAC_STATUS_OK)
+ {
+ DeviceClass_t cur_class =CLASS_C;
+ LoRaMainCallbacks->app_events(APP_CONFIRM_ClASS_EVENT,&cur_class);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ return Errorstatus;
+}
+
+DeviceClass_t lw_get_current_class(void)
+{
+ MibRequestConfirm_t mibReq;
+
+ mibReq.Type = MIB_DEVICE_CLASS;
+ LoRaMacMibGetRequestConfirm(&mibReq);
+
+ return mibReq.Param.Class;
+}
+
+void lw_config_otaa_set(lw_state_t otaa)
+{
+ lw_config.otaa = otaa;
+
+ if (lw_config.otaa == LORA_ENABLE)
+ {
+ APP_PPRINTF("OTAA Mode enabled\n\r");
+ APP_PPRINTF("DevEui= %02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X\n\r", HEX8(lw_config.DevEui));
+ APP_PPRINTF("JoinEui= %02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X\n\r", HEX8(lw_config.JoinEui));
+ APP_PPRINTF("AppKey= %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n\r", HEX16(lw_config.AppKey));
+
+ mibReq.Type = MIB_NETWORK_ACTIVATION;
+ mibReq.Param.NetworkActivation = ACTIVATION_TYPE_NONE;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+ }
+ else
+ {
+ APP_PPRINTF("ABP Mode enabled\n\r");
+ APP_PPRINTF("DevEui= %02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X\n\r", HEX8(lw_config.DevEui));
+ APP_PPRINTF("DevAdd= %08X\n\r", lw_param_init.dev_addr) ;
+ APP_PPRINTF("NwkSKey= %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n\r", HEX16(lw_config.NwkSEncKey));
+ APP_PPRINTF("AppSKey= %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n\r", HEX16(lw_config.AppSKey));
+
+ mibReq.Type = MIB_NET_ID;
+ mibReq.Param.NetID = LORAWAN_NETWORK_ID;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+
+ mibReq.Type = MIB_DEV_ADDR;
+ mibReq.Param.DevAddr = lw_param_init.dev_addr;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+
+ mibReq.Type = MIB_F_NWK_S_INT_KEY;
+ mibReq.Param.FNwkSIntKey = lw_config.FNwkSIntKey;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+
+ mibReq.Type = MIB_S_NWK_S_INT_KEY;
+ mibReq.Param.SNwkSIntKey = lw_config.SNwkSIntKey;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+
+ mibReq.Type = MIB_NWK_S_ENC_KEY;
+ mibReq.Param.NwkSEncKey = lw_config.NwkSEncKey;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+
+ mibReq.Type = MIB_APP_S_KEY;
+ mibReq.Param.AppSKey = lw_config.AppSKey;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+
+ mibReq.Type = MIB_NETWORK_ACTIVATION;
+ mibReq.Param.NetworkActivation = ACTIVATION_TYPE_ABP;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+
+ // Enable legacy mode to operate according to LoRaWAN Spec. 1.0.3
+ Version_t abpLrWanVersion;
+
+ abpLrWanVersion.Fields.Major = 1;
+ abpLrWanVersion.Fields.Minor = 0;
+ abpLrWanVersion.Fields.Revision = 3;
+ abpLrWanVersion.Fields.Rfu = 0;
+
+ mibReq.Type = MIB_ABP_LORAWAN_VERSION;
+ mibReq.Param.AbpLrWanVersion = abpLrWanVersion;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+ }
+}
+
+lw_state_t lw_config_otaa_get(void)
+{
+ return lw_config.otaa;
+}
+
+
+void lw_confirm_retry_set(uint8_t retry)
+{
+ lw_param_init.confirm_retry = retry;
+}
+
+
+uint8_t lw_confirm_retry_get(void)
+{
+ return lw_param_init.confirm_retry;
+}
+
+
+void lw_config_duty_cycle_set(lw_state_t duty_cycle)
+{
+ lw_config.duty_cycle = duty_cycle;
+ LoRaMacTestSetDutyCycleOn((duty_cycle == LORA_ENABLE) ? 1 : 0);
+}
+
+lw_state_t lw_config_duty_cycle_get(void)
+{
+ return lw_config.duty_cycle;
+}
+
+uint8_t *lw_config_deveui_get(void)
+{
+ return lw_config.DevEui;
+}
+
+uint8_t *lw_config_joineui_get(void)
+{
+ return lw_config.JoinEui;
+}
+
+void lw_config_joineui_set(uint8_t joineui[8])
+{
+ UTIL_MEM_cpy_8(lw_config.JoinEui, joineui, sizeof(lw_config.JoinEui));
+
+ mibReq.Type = MIB_JOIN_EUI;
+ mibReq.Param.JoinEui = lw_config.JoinEui;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+}
+
+uint8_t *lw_config_appkey_get(void)
+{
+ return lw_config.AppKey;
+}
+
+void lw_config_appkey_set(uint8_t appkey[16])
+{
+ UTIL_MEM_cpy_8(lw_config.AppKey, appkey, sizeof(lw_config.AppKey));
+ UTIL_MEM_cpy_8(lw_config.NwkKey, appkey, sizeof(lw_config.NwkKey));
+
+ mibReq.Type = MIB_APP_KEY;
+ mibReq.Param.AppKey = lw_config.AppKey;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+
+ mibReq.Type = MIB_NWK_KEY;
+ mibReq.Param.NwkKey = lw_config.NwkKey;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+}
+
+void lw_config_reqack_set(LoraConfirm_t reqack)
+{
+ lw_config.ReqAck = reqack;
+}
+
+LoraConfirm_t lw_config_reqack_get(void)
+{
+ return lw_config.ReqAck;
+}
+
+int8_t lw_config_snr_get(void)
+{
+ return lw_config.Snr;
+}
+
+int16_t lw_config_rssi_get(void)
+{
+ return lw_config.Rssi;
+}
+
+void lw_config_tx_datarate_set(int8_t TxDataRate)
+{
+ lw_config.TxDatarate = TxDataRate;
+}
+
+int8_t lw_config_tx_datarate_get(void)
+{
+ return lw_config.TxDatarate;
+}
+
+
+LoRaMacRegion_t lora_region_get(void)
+{
+ return lw_param_init.active_region;
+}
+#if ( LORAMAC_CLASSB_ACTIVE == 1 )
+ LoRaMacClassBNvmCtx_t* lw_get_classBCtx(void)
+ {
+ mibReq.Type = MIB_NVM_CTXS;
+ LoRaMacMibGetRequestConfirm( &mibReq );
+ return ( LoRaMacClassBNvmCtx_t*) mibReq.Param.Contexts->ClassBNvmCtx;
+ }
+
+ uint8_t lw_get_pingslot_periodicity(void)
+ {
+ return DefaultPingSlotPeriodicity;
+ }
+
+ void lw_set_pingslot_periodicity(uint8_t PingSlotPeriodicity)
+ {
+ DefaultPingSlotPeriodicity= PingSlotPeriodicity;
+ }
+#endif /* LORAMAC_CLASSB_ACTIVE */
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
+
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/lora.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/lora.h
new file mode 100644
index 00000000..41fc7fee
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/lora.h
@@ -0,0 +1,407 @@
+/**
+ ******************************************************************************
+ * @file lora.h
+ * @author MCD Application Team
+ * @brief lora API to drive the lora state Machine
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under BSD 3-Clause license,
+ * the "License"; You may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ * opensource.org/licenses/BSD-3-Clause
+ *
+ ******************************************************************************
+ */
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+
+#ifndef __LORA_H__
+#define __LORA_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+#include "Commissioning.h"
+#include "LoRaMac.h"
+#include "app_conf.h"
+#if defined (LORAMAC_CLASSB_ACTIVE) && ( LORAMAC_CLASSB_ACTIVE == 1 )
+#include "LoRaMacClassB.h"
+#elif !defined (LORAMAC_CLASSB_ACTIVE)
+ #error LORAMAC_CLASSB_ACTIVE not defined
+#endif /* LORAMAC_CLASSB_ACTIVE */
+
+#include "region/Region.h"
+
+
+/* Exported constants --------------------------------------------------------*/
+/*!
+* LoRaWAN confirmed messages
+*/
+
+#define LORAWAN_ADR_ON 1
+#define LORAWAN_ADR_OFF 0
+/* Exported types ------------------------------------------------------------*/
+
+/*!
+ * Application Data structure
+ */
+typedef struct
+{
+ /*point to the LoRa App data buffer*/
+ uint8_t *buff;
+ /*LoRa App data buffer size*/
+ uint8_t size;
+ /*Port on which the LoRa App is data is sent/ received*/
+ uint8_t port;
+
+ uint8_t ack;
+
+ uint8_t fpending;
+
+} lw_app_data_t;
+
+
+typedef enum
+{
+ LORA_RESET = 0,
+ LORA_SET = !LORA_RESET
+} LoraFlagStatus;
+
+typedef enum
+{
+ LORA_DISABLE = 0,
+ LORA_ENABLE = !LORA_DISABLE
+} lw_state_t;
+
+typedef enum
+{
+ LORA_ERROR = -1,
+ LORA_SUCCESS = 0
+} LoraErrorStatus;
+
+typedef enum
+{
+ LORAWAN_UNCONFIRMED_MSG = 0,
+ LORAWAN_CONFIRMED_MSG = !LORAWAN_UNCONFIRMED_MSG
+} LoraConfirm_t;
+
+typedef enum
+{
+ LORA_TRUE = 0,
+ LORA_FALSE = !LORA_TRUE
+} LoraBool_t;
+
+
+typedef enum
+{
+ APP_RX_EVENT,
+ APP_JOINED_EVENT,
+ APP_CONFIRM_ClASS_EVENT,
+ APP_TX_DONE_EVENT,
+}APP_EVENT_t;
+
+
+/* Lora Main callbacks*/
+typedef struct sLoRaMainCallback
+{
+ /*!
+ * @brief mac event callback
+ *
+ * @retval null
+ */
+ void (*app_events)(APP_EVENT_t evt,const void* param);
+ /*!
+ *\brief Will be called each time a Radio IRQ is handled by the MAC
+ * layer.
+ *
+ *\warning Runs in a IRQ context. Should only change variables state.
+ */
+ void (*MacProcessNotify)(void);
+ /*!
+ * @brief Get the current battery level
+ *
+ * @retval value battery level ( 0: very low, 254: fully charged )
+ */
+ uint8_t (*BoardGetBatteryLevel)(void);
+ /*!
+ * \brief Get the current temperature
+ *
+ * \retval value temperature in degreeCelcius( q7.8 )
+ */
+ uint16_t (*BoardGetTemperatureLevel)(void);
+ /*!
+ * @brief Gets the board 64 bits unique ID
+ *
+ * @param [IN] id Pointer to an array that will contain the Unique ID
+ */
+ void (*BoardGetUniqueId)(uint8_t *id);
+ /*!
+ * Returns a pseudo random seed generated using the MCU Unique ID
+ *
+ * @retval seed Generated pseudo random seed
+ */
+ uint32_t (*BoardGetRandomSeed)(void);
+
+} LoRaMainCallback_t;
+
+
+
+typedef struct
+{
+ uint16_t mask[6];
+}LWChannel_mask_t;
+
+
+/* External variables --------------------------------------------------------*/
+/* Exported macros -----------------------------------------------------------*/
+/* Exported functions ------------------------------------------------------- */
+/**
+ * @brief Lora Initialisation
+ * @param [IN] LoRaMainCallback_t
+ * @param [IN] application parmaters
+ * @retval none
+ */
+void lw_init(LoRaMainCallback_t *callbacks);
+
+/**
+ * @brief run Lora classA state Machine
+ * @param [IN] none
+ * @retval none
+ */
+LoRaMacStatus_t lw_send(lw_app_data_t *AppData, LoraConfirm_t IsTxConfirmed);
+
+
+
+/**
+ * @brief Join a Lora Network in classA
+ * @Note if the device is ABP, this is a pass through functon
+ * @param [IN] none
+ * @retval none
+ */
+LoRaMacStatus_t lw_join(void);
+
+/**
+ * @brief Check whether the Device is joined to the network
+ * @param [IN] none
+ * @retval returns LORA_SET if joined
+ */
+LoraFlagStatus lw_join_status(void);
+
+/*=====================================================================
+if you want to use the Api below , you must call them after LORA_Init
+
+because it has been made Initialization configuration
+
+======================================================================*/
+
+/**
+ * @brief Enable or Disable ADR
+ * @param [IN] lw_state_t
+ * @retval returns req state
+ */
+LoRaMacStatus_t lw_adr_set(lw_state_t sta);
+
+
+/**
+ * @brief uplink confirm retry times setting
+ * @param [IN] times
+ * @retval returns req state
+ */
+void lw_confirm_retry_set(uint8_t retry);
+
+/**
+ * @brief return uplink confirm retry times
+ * @param [IN] null
+ * @retval times
+ */
+uint8_t lw_confirm_retry_get(void);
+
+
+/**
+ * @brief set the currnt chmask
+ * @param [IN] LWChannel_mask_t
+ * @retval status
+ */
+LoRaMacStatus_t lw_current_chmask_set(const LWChannel_mask_t* ch);
+
+/**
+ * @brief return the currnt chmask
+ * @param [IN] LWChannel_mask_t
+ * @retval status
+ */
+LoRaMacStatus_t lw_current_chmask_get(LWChannel_mask_t* ch);
+
+/**
+ * @brief set Rx win2 datarate and freq , there are default configuration in region parameter
+ * @param [IN] RxChannelParams_t
+ * @retval status
+ */
+LoRaMacStatus_t lw_rxwin2_ch_set(const RxChannelParams_t* rx2_ch);
+
+/**
+ * @brief get Rx win2 datarate and freq
+ * @param [IN] RxChannelParams_t
+ * @retval status
+ */
+
+LoRaMacStatus_t lw_rxwin2_ch_get(RxChannelParams_t* rx2_ch);
+
+/**
+ * @brief change Lora Class
+ * @Note callback LORA_ConfirmClass informs upper layer that the change has occured
+ * @Note Only switch from class A to class B/C OR from class B/C to class A is allowed
+ * @Attention can be calld only in LORA_ClassSwitchSlot or LORA_RxData callbacks
+ * @param [IN] DeviceClass_t NewClass
+ * @retval LoraErrorStatus
+ */
+LoRaMacStatus_t lw_request_class(DeviceClass_t newClass);
+
+/**
+ * @brief get the current Lora Class
+ * @param [IN] DeviceClass_t NewClass
+ * @retval None
+ */
+DeviceClass_t lw_get_current_class(void);
+
+/**
+ * @brief Set join activation process: OTAA vs ABP
+ * @param Over The Air Activation status to set: enable or disable
+ * @retval None
+ */
+void lw_config_otaa_set(lw_state_t otaa);
+
+/**
+ * @brief Get join activation process: OTAA vs ABP
+ * @param None
+ * @retval ENABLE if OTAA is used, DISABLE if ABP is used
+ */
+lw_state_t lw_config_otaa_get(void);
+
+/**
+ * @brief Set duty cycle: ENABLE or DISABLE
+ * @param Duty cycle to set: enable or disable
+ * @retval None
+ */
+void lw_config_duty_cycle_set(lw_state_t duty_cycle);
+
+/**
+ * @brief Get Duty cycle: OTAA vs ABP
+ * @param None
+ * @retval ENABLE / DISABLE
+ */
+lw_state_t lw_config_duty_cycle_get(void);
+
+/**
+ * @brief Get Device EUI
+ * @param None
+ * @retval DevEUI
+ */
+uint8_t *lw_config_deveui_get(void);
+
+/**
+ * @brief Get Join Eui
+ * @param None
+ * @retval JoinEUI
+ */
+uint8_t *lw_config_joineui_get(void);
+
+/**
+ * @brief Set Join Eui
+ * @param JoinEUI
+ * @retval Nonoe
+ */
+void lw_config_joineui_set(uint8_t joineui[8]);
+
+/**
+ * @brief Get Application Key
+ * @param None
+ * @retval AppKey
+ */
+uint8_t *lw_config_appkey_get(void);
+
+/**
+ * @brief Set Application Key
+ * @param AppKey
+ * @retval None
+ */
+void lw_config_appkey_set(uint8_t appkey[16]);
+
+/**
+ * @brief Set whether or not acknowledgement is required
+ * @param ENABLE or DISABLE
+ * @retval None
+ */
+void lw_config_reqack_set(LoraConfirm_t reqack);
+
+/**
+ * @brief Get whether or not acknowledgement is required
+ * @param None
+ * @retval ENABLE or DISABLE
+ */
+LoraConfirm_t lw_config_reqack_get(void);
+
+/**
+ * @brief Get the SNR of the last received data
+ * @param None
+ * @retval SNR
+ */
+int8_t lw_config_snr_get(void);
+
+/**
+ * @brief Get the RSSI of the last received data
+ * @param None
+ * @retval RSSI
+ */
+int16_t lw_config_rssi_get(void);
+
+/**
+ * @brief set tx datarate
+ * @param None
+ * @retval The application port
+ */
+void lw_config_tx_datarate_set(int8_t TxDataRate);
+
+/**
+ * @brief get tx datarate
+ * @param None
+ * @retval tx datarate
+ */
+int8_t lw_config_tx_datarate_get(void);
+
+/**
+ * @brief get LoRaMac region
+ * @param None
+ * @retval LoRaMac region
+ */
+LoRaMacRegion_t lw_region_get(void);
+
+#if defined (LORAMAC_CLASSB_ACTIVE) && ( LORAMAC_CLASSB_ACTIVE == 1 )
+
+ LoRaMacClassBNvmCtx_t* LORA_Get_ClassBCtx(void);
+ uint8_t LORA_Get_PingSlotPeriodicity(void);
+ void LORA_Set_PingSlotPeriodicity(uint8_t PingSlotPeriodicity);
+
+#elif !defined (LORAMAC_CLASSB_ACTIVE)
+ #error LORAMAC_CLASSB_ACTIVE not defined
+#endif
+
+#ifdef __cplusplus
+ }
+#endif
+
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*__LORA_H__*/
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/lorawan_info.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/lorawan_info.c
new file mode 100644
index 00000000..d9a25552
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/lorawan_info.c
@@ -0,0 +1,96 @@
+/**
+ ******************************************************************************
+ * @file lorawan_info.c
+ * @author MCD Application Team
+ * @brief To give info to the application about LoraWAN configuration
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under BSD 3-Clause license,
+ * the "License"; You may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ * opensource.org/licenses/BSD-3-Clause
+ *
+ ******************************************************************************
+ */
+
+/* Includes ------------------------------------------------------------------*/
+#include "lorawan_info.h"
+#include "LoRaMac.h"
+
+/* Private typedef -----------------------------------------------------------*/
+/* Private define ------------------------------------------------------------*/
+/* Private macro -------------------------------------------------------------*/
+/* Private variables ---------------------------------------------------------*/
+/* Private function prototypes -----------------------------------------------*/
+/* Exported variables --------------------------------------------------------*/
+LoRaMacInfo_t LoRaMacInfoTable;
+
+/* Exported functions --------------------------------------------------------*/
+/**
+ * @brief initialises the LoraMacInfo capabilities table
+ * @param none
+ * @retval none
+ */
+void LoraMacInfo_Init(void)
+{
+ LoRaMacInfoTable.Region = 0;
+#ifdef REGION_AS923
+ LoRaMacInfoTable.Region |= (1<© Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under BSD 3-Clause license,
+ * the "License"; You may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ * opensource.org/licenses/BSD-3-Clause
+ *
+ ******************************************************************************
+ */
+#ifndef __LORAWAN_INFO_H__
+#define __LORAWAN_INFO_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+#include
+
+/* Exported constants --------------------------------------------------------*/
+/* Exported types ------------------------------------------------------------*/
+
+/*!
+ * To give info to the application about LoraWAN capability
+ * it can depend how it has been compiled (e.g. compiled regions ...)
+ * Params should be better uint32_t foe easier alignment with info_table concept
+ */
+typedef struct
+{
+ uint32_t Region; /*!< Combination of regions compiled on MW */
+ uint32_t ClassB; /*!< 0: not compiled in Mw, 1 : compiled in MW */
+}LoRaMacInfo_t;
+
+
+/* External variables --------------------------------------------------------*/
+/* Exported macros -----------------------------------------------------------*/
+/* Exported functions ------------------------------------------------------- */
+
+/**
+ * @brief initialises the LoraMacInfo table
+ * @param none
+ * @retval none
+ */
+void LoraMacInfo_Init(void);
+
+/**
+ * @brief returns the pointer to the LoraMacInfo table
+ * @param none
+ * @retval LoRaMacInfoTable pointer
+ */
+LoRaMacInfo_t* LoraMacInfo_GetPtr(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __LORAWAN_INFO_H__
+
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/app_conf_template.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/app_conf_template.h
new file mode 100644
index 00000000..66077eef
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/app_conf_template.h
@@ -0,0 +1,56 @@
+/**
+ ******************************************************************************
+ * @file app_conf_template.h
+ * @author MCD Application Team
+ * @brief applic configuration, e.g. : debug, trace, low power, sensors
+ * @note this is a template: the app_conf.h shall be defined in the Project/...
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under BSD 3-Clause license,
+ * the "License"; You may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ * opensource.org/licenses/BSD-3-Clause
+ *
+ ******************************************************************************
+ */
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __APP_CONF_H__
+#define __APP_CONF_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+/* Exported types ------------------------------------------------------------*/
+/* Exported constants --------------------------------------------------------*/
+
+/* LoraWAN application configuration (Mw is configured by lorawan_conf.h) */
+#define ACTIVE_REGION LORAMAC_REGION_EU868
+#define HYBRID_ENABLED 0
+
+#define LORAMAC_CLASSB_ACTIVE 0 /* 0 if not active, 1 if active */
+
+/* Enable trace logs. Note verbose level can be selected in app_system.h */
+#define APP_LOG_ENABLED 1
+
+#define DEBUGGER_ON 0 /* if ON (=1) it enables the debbugger plus 4 dgb pins */
+ /* if OFF (=0) the debugger is OFF (lower consumption) */
+
+/* External variables --------------------------------------------------------*/
+/* Exported macros -----------------------------------------------------------*/
+/* Defines -------------------------------------------------------------------*/
+/* Exported functions ------------------------------------------------------- */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __APP_CONF_H__ */
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/app_system_template.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/app_system_template.h
new file mode 100644
index 00000000..378f1515
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/app_system_template.h
@@ -0,0 +1,62 @@
+/**
+ ******************************************************************************
+ * @file app_system_template.h
+ * @author MCD Application Team
+ * @brief function prototypes for app_system.c file
+ * @note this is a template: the app_conf.h shall be defined in the Project/...
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under BSD 3-Clause license,
+ * the "License"; You may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ * opensource.org/licenses/BSD-3-Clause
+ *
+ ******************************************************************************
+ */
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __APP_SYSTEM_H__
+#define __APP_SYSTEM_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+#include
+#include "app_conf.h"
+#include "trace.h"
+
+/* Exported types ------------------------------------------------------------*/
+/* Exported constants --------------------------------------------------------*/
+#define VERBOSE_LEVEL VLEVEL_L
+
+/* External variables --------------------------------------------------------*/
+/* Exported macros -----------------------------------------------------------*/
+#define APP_PPRINTF(...) do{ } while( 0!= TraceSend(0, 0, __VA_ARGS__) ) //Polling Mode
+#define APP_TPRINTF(...) do{ {TraceSend(1, 0, __VA_ARGS__);} }while(0); //with timestamp
+#define APP_PRINTF(...) do{ {TraceSend(0, 0, __VA_ARGS__);} }while(0);
+
+
+#if defined (APP_LOG_ENABLED) && (APP_LOG_ENABLED == 1)
+#define APP_LOG(TS,VL,...) do{ {TraceSend(TS, VL, __VA_ARGS__);} }while(0);
+#elif defined (APP_LOG_ENABLED) && (APP_LOG_ENABLED == 0) /* APP_LOG disabled */
+#define APP_LOG(TS,VL,...)
+#else
+#error "APP_LOG_ENABLED not defined or out of range <0,1>"
+#endif
+
+/* Defines -------------------------------------------------------------------*/
+/* Exported functions ------------------------------------------------------- */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __APP_SYSTEM_H__ */
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/lora-test.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/lora-test.c
new file mode 100644
index 00000000..7d05d67f
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/lora-test.c
@@ -0,0 +1,417 @@
+/******************************************************************************
+ * @file lora-test.c
+ * @author MCD Application Team
+ * @brief lora API to drive the lora state Machine
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under BSD 3-Clause license,
+ * the "License"; You may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ * opensource.org/licenses/BSD-3-Clause
+ *
+ ******************************************************************************
+ */
+
+
+/* Includes ------------------------------------------------------------------*/
+#include "stddef.h"
+//#include "lorawan_conf.h" /* Don't remove: needed for LORAMAC_CLASSB_ENABLED */
+//#include "mw_log_conf.h"
+#include "LoRaMac.h"
+#include "LoRaMacTest.h"
+#include "lora.h"
+
+/* Private typedef -----------------------------------------------------------*/
+
+typedef struct ComplianceTest_s
+{
+ bool Running;
+ uint8_t State;
+ LoraConfirm_t IsTxConfirmed;
+ uint8_t DataBufferSize;
+ uint8_t DataBuffer[242]; /* max possible payload */
+ uint16_t DownLinkCounter;
+ bool LinkCheck;
+ uint8_t DemodMargin;
+ uint8_t NbGateways;
+} ComplianceTest_t;
+
+typedef enum
+{
+ CERTIF_TX_PROCESS = 0,
+ CERTIF_RX_PROCESS
+} requestProcess_t;
+
+/* Private define ------------------------------------------------------------*/
+#define TEST_TX_DUTYCYCLE 5000
+/* Private variables ---------------------------------------------------------*/
+
+
+static requestProcess_t requested_process_id;
+
+/*!
+ * Timer to handle the application data transmission duty cycle
+ */
+static TimerEvent_t CertifTxNextPacketTimer;
+
+static ComplianceTest_t certifParam;
+static LoraConfirm_t IsTxConfirmed;
+static bool AdrEnableInit;
+static McpsIndication_t *CertifMcpsIndication;
+static MlmeReqJoin_t *CertifJoinParameters;
+
+/* Private functions ---------------------------------------------------------*/
+static bool CERTIF_running(void);
+
+static void CERTIF_DownLinkIncrement(void);
+
+static void CERTIF_linkCheck(MlmeConfirm_t *mlmeConfirm);
+
+static void CERTIF_rx_request(McpsIndication_t *mcpsIndication, MlmeReqJoin_t *JoinParameters);
+
+static void CERTIF_rx(McpsIndication_t *mcpsIndication, MlmeReqJoin_t *JoinParameters);
+
+static bool CERTIF_tx(void);
+
+static void OnCertifTxNextPacketTimerEvent(void *context);
+
+static void (*CertifProcessNotify)(void);
+
+static LoRaCertifCallback_t certifCb =
+{
+ CERTIF_running,
+ CERTIF_DownLinkIncrement,
+ CERTIF_linkCheck,
+ CERTIF_rx_request,
+};
+
+/* Exported functions definition---------------------------------------------------------*/
+
+void CERTIF_Init(void (*CERTIF_ProcessNotify)(void))
+{
+ /*initialises Callback into the Lora.c layer*/
+ LORA_TestInit(&certifCb);
+ /*Register TestProcessNotify callback*/
+ CertifProcessNotify = CERTIF_ProcessNotify;
+}
+
+void CERTIF_Process(void)
+{
+ switch (requested_process_id)
+ {
+ case CERTIF_TX_PROCESS:
+ CERTIF_tx();
+ break;
+ case CERTIF_RX_PROCESS:
+ CERTIF_rx(CertifMcpsIndication, CertifJoinParameters);
+ break;
+ default:
+ break;
+ }
+}
+static bool CERTIF_running(void)
+{
+ return certifParam.Running;
+}
+
+static void CERTIF_DownLinkIncrement(void)
+{
+ certifParam.DownLinkCounter++;
+}
+
+static void CERTIF_linkCheck(MlmeConfirm_t *mlmeConfirm)
+{
+ certifParam.LinkCheck = true;
+ certifParam.DemodMargin = mlmeConfirm->DemodMargin;
+ certifParam.NbGateways = mlmeConfirm->NbGateways;
+}
+
+static bool CERTIF_tx(void)
+{
+ McpsReq_t mcpsReq;
+ LoRaMacTxInfo_t txInfo;
+
+ if (certifParam.LinkCheck == true)
+ {
+ certifParam.LinkCheck = false;
+ certifParam.DataBufferSize = 3;
+ certifParam.DataBuffer[0] = 5;
+ certifParam.DataBuffer[1] = certifParam.DemodMargin;
+ certifParam.DataBuffer[2] = certifParam.NbGateways;
+ certifParam.State = 1;
+ }
+ else
+ {
+ switch (certifParam.State)
+ {
+ case 4:
+ certifParam.State = 1;
+ break;
+ case 1:
+ certifParam.DataBufferSize = 2;
+ certifParam.DataBuffer[0] = certifParam.DownLinkCounter >> 8;
+ certifParam.DataBuffer[1] = certifParam.DownLinkCounter;
+ break;
+ }
+ }
+
+ if (LoRaMacQueryTxPossible(certifParam.DataBufferSize, &txInfo) != LORAMAC_STATUS_OK)
+ {
+ // Send empty frame in order to flush MAC commands
+ mcpsReq.Type = MCPS_UNCONFIRMED;
+ mcpsReq.Req.Unconfirmed.fBuffer = NULL;
+ mcpsReq.Req.Unconfirmed.fBufferSize = 0;
+ mcpsReq.Req.Unconfirmed.Datarate = DR_0;
+ }
+ else
+ {
+ if (IsTxConfirmed == LORAWAN_UNCONFIRMED_MSG)
+ {
+ mcpsReq.Type = MCPS_UNCONFIRMED;
+ mcpsReq.Req.Unconfirmed.fPort = CERTIF_PORT;
+ mcpsReq.Req.Unconfirmed.fBufferSize = certifParam.DataBufferSize;
+ mcpsReq.Req.Unconfirmed.fBuffer = &(certifParam.DataBuffer);
+ mcpsReq.Req.Unconfirmed.Datarate = DR_0;
+ }
+ else
+ {
+ mcpsReq.Type = MCPS_CONFIRMED;
+ mcpsReq.Req.Confirmed.fPort = CERTIF_PORT;
+ mcpsReq.Req.Confirmed.fBufferSize = certifParam.DataBufferSize;
+ mcpsReq.Req.Confirmed.fBuffer = &(certifParam.DataBuffer);
+ mcpsReq.Req.Confirmed.NbTrials = 8;
+ mcpsReq.Req.Confirmed.Datarate = DR_0;
+ }
+ }
+
+ /*cerification test on-going*/
+ TimerStart(&CertifTxNextPacketTimer);
+
+ if (LoRaMacMcpsRequest(&mcpsReq) == LORAMAC_STATUS_OK)
+ {
+ return false;
+ }
+ return true;
+}
+
+static void CERTIF_rx_request(McpsIndication_t *mcpsIndication, MlmeReqJoin_t *JoinParameters)
+{
+ CertifMcpsIndication = mcpsIndication;
+ CertifJoinParameters = JoinParameters;
+
+ requested_process_id = CERTIF_RX_PROCESS;
+
+ CertifProcessNotify();
+}
+
+static void CERTIF_rx(McpsIndication_t *mcpsIndication, MlmeReqJoin_t *JoinParameters)
+{
+ if (certifParam.Running == false)
+ {
+ // Check compliance test enable command (i)
+ if ((mcpsIndication->BufferSize == 4) &&
+ (mcpsIndication->Buffer[0] == 0x01) &&
+ (mcpsIndication->Buffer[1] == 0x01) &&
+ (mcpsIndication->Buffer[2] == 0x01) &&
+ (mcpsIndication->Buffer[3] == 0x01))
+ {
+ MibRequestConfirm_t mibReq;
+ IsTxConfirmed = LORAWAN_UNCONFIRMED_MSG;
+ certifParam.DataBufferSize = 2;
+ certifParam.DownLinkCounter = 0;
+ certifParam.LinkCheck = false;
+ certifParam.DemodMargin = 0;
+ certifParam.NbGateways = 0;
+ certifParam.Running = true;
+ certifParam.State = 1;
+
+ mibReq.Type = MIB_ADR;
+
+ LoRaMacMibGetRequestConfirm(&mibReq);
+ AdrEnableInit = mibReq.Param.AdrEnable;
+
+ mibReq.Type = MIB_ADR;
+ mibReq.Param.AdrEnable = true;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+
+ if ( (ACTIVE_REGION == LORAMAC_REGION_EU868)
+ || (ACTIVE_REGION == LORAMAC_REGION_RU864)
+ || (ACTIVE_REGION == LORAMAC_REGION_CN779)
+ || (ACTIVE_REGION == LORAMAC_REGION_EU433))
+ {
+ LoRaMacTestSetDutyCycleOn(false);
+ }
+
+ TimerInit(&CertifTxNextPacketTimer, OnCertifTxNextPacketTimerEvent);
+ TimerSetValue(&CertifTxNextPacketTimer, TEST_TX_DUTYCYCLE);
+
+ /*confirm test mode activation */
+ CERTIF_tx();
+ }
+ }
+
+ else
+ {
+ certifParam.State = mcpsIndication->Buffer[0];
+ switch (certifParam.State)
+ {
+ case 0: // Check compliance test disable command (ii)
+ {
+
+ certifParam.DownLinkCounter = 0;
+ certifParam.Running = false;
+
+ MibRequestConfirm_t mibReq;
+ mibReq.Type = MIB_ADR;
+ mibReq.Param.AdrEnable = AdrEnableInit;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+
+ if ( (ACTIVE_REGION == LORAMAC_REGION_EU868)
+ || (ACTIVE_REGION == LORAMAC_REGION_RU864)
+ || (ACTIVE_REGION == LORAMAC_REGION_CN779)
+ || (ACTIVE_REGION == LORAMAC_REGION_EU433))
+ {
+ LoRaMacTestSetDutyCycleOn(true);
+ }
+
+ break;
+ }
+ case 1: // (iii, iv)
+ certifParam.DataBufferSize = 2;
+ break;
+ case 2: // Enable confirmed messages (v)
+ IsTxConfirmed = LORAWAN_CONFIRMED_MSG;
+ certifParam.State = 1;
+ break;
+ case 3: // Disable confirmed messages (vi)
+ IsTxConfirmed = LORAWAN_UNCONFIRMED_MSG;
+ certifParam.State = 1;
+ break;
+ case 4: // (vii)
+ certifParam.DataBufferSize = mcpsIndication->BufferSize;
+
+ certifParam.DataBuffer[0] = 4;
+ for (uint8_t i = 1; i < certifParam.DataBufferSize; i++)
+ {
+ certifParam.DataBuffer[i] = mcpsIndication->Buffer[i] + 1;
+ }
+ break;
+ case 5: // (viii)
+ {
+ MlmeReq_t mlmeReq;
+ mlmeReq.Type = MLME_LINK_CHECK;
+ LoRaMacMlmeRequest(&mlmeReq);
+ break;
+ }
+ case 6: // (ix)
+ {
+ MlmeReq_t mlmeReq;
+
+ // Disable TestMode and revert back to normal operation
+
+ certifParam.DownLinkCounter = 0;
+ certifParam.Running = false;
+
+ MibRequestConfirm_t mibReq;
+ mibReq.Type = MIB_ADR;
+ mibReq.Param.AdrEnable = AdrEnableInit;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+
+ mlmeReq.Type = MLME_JOIN;
+ mlmeReq.Req.Join = *JoinParameters;
+
+ LoRaMacMlmeRequest(&mlmeReq);
+ break;
+ }
+
+ case 7: // (x)
+ {
+ if (mcpsIndication->BufferSize == 3)
+ {
+ MlmeReq_t mlmeReq;
+ mlmeReq.Type = MLME_TXCW;
+ mlmeReq.Req.TxCw.Timeout = (uint16_t)((mcpsIndication->Buffer[1] << 8) | mcpsIndication->Buffer[2]);
+ LoRaMacMlmeRequest(&mlmeReq);
+ }
+ else if (mcpsIndication->BufferSize == 7)
+ {
+ MlmeReq_t mlmeReq;
+ mlmeReq.Type = MLME_TXCW_1;
+ mlmeReq.Req.TxCw.Timeout = (uint16_t)((mcpsIndication->Buffer[1] << 8) | mcpsIndication->Buffer[2]);
+ mlmeReq.Req.TxCw.Frequency = (uint32_t)((mcpsIndication->Buffer[3] << 16) | (mcpsIndication->Buffer[4] << 8) | mcpsIndication->Buffer[5]) * 100;
+ mlmeReq.Req.TxCw.Power = mcpsIndication->Buffer[6];
+ LoRaMacMlmeRequest(&mlmeReq);
+ }
+ certifParam.State = 1;
+ break;
+ }
+
+ case 8: // Send DeviceTimeReq
+ {
+ MlmeReq_t mlmeReq;
+
+ mlmeReq.Type = MLME_DEVICE_TIME;
+
+ LoRaMacMlmeRequest(&mlmeReq);
+ break;
+ }
+#if defined (LORAMAC_CLASSB_ACTIVE) && ( LORAMAC_CLASSB_ACTIVE == 1 )
+ case 9: // Switch end device Class
+ {
+ MibRequestConfirm_t mibReq;
+
+ mibReq.Type = MIB_DEVICE_CLASS;
+ // CLASS_A = 0, CLASS_B = 1, CLASS_C = 2
+ mibReq.Param.Class = (DeviceClass_t)mcpsIndication->Buffer[1];
+ LoRaMacMibSetRequestConfirm(&mibReq);
+ break;
+ }
+ case 10: // Send PingSlotInfoReq
+ {
+ MlmeReq_t mlmeReq;
+
+ mlmeReq.Type = MLME_PING_SLOT_INFO;
+
+ mlmeReq.Req.PingSlotInfo.PingSlot.Value = mcpsIndication->Buffer[1];
+
+ LoRaMacMlmeRequest(&mlmeReq);
+ break;
+ }
+ case 11: // Send BeaconTimingReq
+ {
+ MlmeReq_t mlmeReq;
+
+ mlmeReq.Type = MLME_BEACON_TIMING;
+
+ LoRaMacMlmeRequest(&mlmeReq);
+ break;
+ }
+#elif !defined (LORAMAC_CLASSB_ACTIVE)
+ #error LORAMAC_CLASSB_ACTIVE not defined
+#endif
+ default:
+ break;
+ }
+ }
+
+ if (certifParam.Running == false)
+ {
+ /*certification test stops*/
+ TimerStop(&CertifTxNextPacketTimer);
+ }
+}
+
+/*!
+ * \brief Function executed on TxNextPacket Timeout event
+ */
+static void OnCertifTxNextPacketTimerEvent(void *context)
+{
+ requested_process_id = CERTIF_TX_PROCESS;
+ CertifProcessNotify();
+}
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
+
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/lora-test.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/lora-test.h
new file mode 100644
index 00000000..1e8056e0
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/lora-test.h
@@ -0,0 +1,71 @@
+/******************************************************************************
+ * @file lora_test.h
+ * @author MCD Application Team
+ * @brief lora API to drive the lora state Machine
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under BSD 3-Clause license,
+ * the "License"; You may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ * opensource.org/licenses/BSD-3-Clause
+ *
+ ******************************************************************************
+ */
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+
+#ifndef __LORA_TEST_H__
+#define __LORA_TEST_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+/* Exported constants --------------------------------------------------------*/
+
+#define CERTIF_PORT 224
+/* Exported types ------------------------------------------------------------*/
+
+
+/* External variables --------------------------------------------------------*/
+/* Exported macros -----------------------------------------------------------*/
+/* Exported functions ------------------------------------------------------- */
+
+/**
+ * @brief Certification Initialisation
+ * @param [IN] Notification call back to the appl that a CERTIF_Process must be called
+ * @retval none
+ */
+void CERTIF_Init(void (*CERTIF_ProcessNotify)(void));
+/**
+ * @brief CERTIF_Process to be called when a CERTIF_ProcessNotify callback is generated
+ * @param [IN] none
+ * @retval none
+ */
+void CERTIF_Process(void);
+
+/* Lora Test callbacks*/
+typedef struct sLoRaCertifCallback
+{
+ bool (*running)(void);
+
+ void (*DownLinkIncrement)(void);
+
+ void (*linkCheck)(MlmeConfirm_t *mlmeConfirm);
+
+ void (*rx)(McpsIndication_t *mcpsIndication, MlmeReqJoin_t *JoinParameters);
+
+} LoRaCertifCallback_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*__LORA_TEST_H__*/
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/lora.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/lora.c
new file mode 100644
index 00000000..6459d781
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/lora.c
@@ -0,0 +1,1016 @@
+/**
+ ******************************************************************************
+ * @file lora.c
+ * @author MCD Application Team
+ * @brief lora API to drive the lora state Machine
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under BSD 3-Clause license,
+ * the "License"; You may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ * opensource.org/licenses/BSD-3-Clause
+ *
+ ******************************************************************************
+ */
+
+/* Includes ------------------------------------------------------------------*/
+#include "timer.h"
+#include "LoRaMac.h"
+#include "lora.h"
+#include "stm32_tiny_sscanf.h"
+#include "app_system.h"
+#include "stm32_seq.h"
+#include "lorawan_info.h"
+
+/* External variables --------------------------------------------------------*/
+/* Private typedef -----------------------------------------------------------*/
+
+/**
+ * Lora Configuration
+ */
+typedef struct
+{
+ LoraState_t otaa; /*< ENABLE if over the air activation, DISABLE otherwise */
+ LoraState_t duty_cycle; /*< ENABLE if dutycyle is on, DISABLE otherwise */
+ uint8_t DevEui[8]; /*< Device EUI */
+ uint8_t JoinEui[8]; /*< Join Eui */
+ uint8_t AppKey[16]; /*< Application Key */
+ uint8_t NwkKey[16]; /*< Application Key */
+ uint8_t NwkSEncKey[16]; /*< Network Session Key */
+ uint8_t AppSKey[16]; /*< Application Session Key */
+ uint8_t FNwkSIntKey[16]; /*< Application Session Key */
+ uint8_t SNwkSIntKey[16]; /*< Application Session Key */
+ int16_t Rssi; /*< Rssi of the received packet */
+ int8_t Snr; /*< Snr of the received packet */
+ uint8_t application_port; /*< Application port we will receive to */
+ LoraConfirm_t ReqAck; /*< ENABLE if acknowledge is requested */
+ int8_t TxDatarate;
+} lora_configuration_t;
+
+
+static lora_configuration_t lora_config =
+{
+ .otaa = LORA_ENABLE,
+ .duty_cycle = LORA_ENABLE,
+ .DevEui = LORAWAN_DEVICE_EUI,
+ .JoinEui = LORAWAN_JOIN_EUI,
+ .AppKey = LORAWAN_APP_KEY,
+ .NwkKey = LORAWAN_NWK_KEY,
+ .NwkSEncKey = LORAWAN_NWK_S_ENC_KEY,
+ .AppSKey = LORAWAN_APP_S_KEY,
+ .FNwkSIntKey = LORAWAN_F_NWK_S_INT_KEY,
+ .SNwkSIntKey = LORAWAN_S_NWK_S_INT_KEY,
+ .Rssi = 0,
+ .Snr = 0,
+ .ReqAck = LORAWAN_UNCONFIRMED_MSG,
+ .TxDatarate = 0
+};
+
+/*!
+ * Select either Device_Time_req or Beacon_Time_Req following LoRaWAN version
+ * - Device_Time_req Available for V1.0.3 or later
+ * - Beacon_time_Req Available for V1.0.2 and before
+ */
+#define USE_DEVICE_TIMING
+
+/*!
+ * Join requests trials duty cycle.
+ */
+#define OVER_THE_AIR_ACTIVATION_DUTYCYCLE 10000 // 10 [s] value in ms
+
+#include "LoRaMacTest.h"
+
+#ifndef LORAMAC_CLASSB_ACTIVE
+ #error LORAMAC_CLASSB_ACTIVE not defined ( shall be <0 or 1> )
+#endif /* LORAMAC_CLASSB_ACTIVE */
+
+#if ( LORAMAC_CLASSB_ACTIVE == 1 )
+/*!
+ * Default ping slots periodicity
+ *
+ * \remark periodicity is equal to 2^LORAWAN_DEFAULT_PING_SLOT_PERIODICITY seconds
+ * example: 2^3 = 8 seconds. The end-device will open an Rx slot every 8 seconds.
+ */
+#define LORAWAN_DEFAULT_PING_SLOT_PERIODICITY 0
+static uint8_t DefaultPingSlotPeriodicity;
+
+#endif /* LORAMAC_CLASSB_ACTIVE */
+
+
+#define HEX16(X) X[0],X[1], X[2],X[3], X[4],X[5], X[6],X[7],X[8],X[9], X[10],X[11], X[12],X[13], X[14],X[15]
+#define HEX8(X) X[0],X[1], X[2],X[3], X[4],X[5], X[6],X[7]
+
+static MlmeReqJoin_t JoinParameters;
+
+
+static uint32_t DevAddr = LORAWAN_DEVICE_ADDRESS;
+
+/* Dummy data sent periodically to let the tester respond with start test command*/
+static TimerEvent_t TxcertifTimer;
+
+/* Private macro -------------------------------------------------------------*/
+/* Private variables ---------------------------------------------------------*/
+/* Private function prototypes -----------------------------------------------*/
+
+#if ( LORAMAC_CLASSB_ACTIVE == 1 )
+ static LoraErrorStatus LORA_BeaconReq(void);
+ static LoraErrorStatus LORA_PingSlotReq(void);
+
+ #if defined( USE_DEVICE_TIMING )
+ static LoraErrorStatus LORA_DeviceTimeReq(void);
+ #else
+ static LoraErrorStatus LORA_BeaconTimeReq(void);
+ #endif /* USE_DEVICE_TIMING */
+#endif /* LORAMAC_CLASSB_ACTIVE */
+
+static void OnCertifTimer(void *);
+
+static void CertifSend(void);
+/*!
+ * Defines the LoRa parameters at Init
+ */
+static LoRaParam_t *LoRaParamInit;
+static LoRaMacPrimitives_t LoRaMacPrimitives;
+static LoRaMacCallback_t LoRaMacCallbacks;
+static MibRequestConfirm_t mibReq;
+static LoRaMacRegion_t LoRaRegion;
+
+static LoRaMainCallback_t *LoRaMainCallbacks;
+
+static LoRaCertifCallback_t *certif;
+
+/* Private user code ---------------------------------------------------------*/
+/*!
+ * \brief MCPS-Confirm event function
+ *
+ * \param [IN] McpsConfirm - Pointer to the confirm structure,
+ * containing confirm attributes.
+ */
+static void McpsConfirm(McpsConfirm_t *mcpsConfirm)
+{
+ if (mcpsConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK)
+ {
+ switch (mcpsConfirm->McpsRequest)
+ {
+ case MCPS_UNCONFIRMED:
+ {
+ // Check Datarate
+ // Check TxPower
+ break;
+ }
+ case MCPS_CONFIRMED:
+ {
+ // Check Datarate
+ // Check TxPower
+ // Check AckReceived
+ if (mcpsConfirm->AckReceived)
+ {
+ LoRaMainCallbacks->LORA_McpsDataConfirm();
+ }
+ // Check NbTrials
+ break;
+ }
+ case MCPS_PROPRIETARY:
+ {
+ break;
+ }
+ default:
+ break;
+ }
+ }
+}
+
+/*!
+ * \brief MCPS-Indication event function
+ *
+ * \param [IN] mcpsIndication - Pointer to the indication structure,
+ * containing indication attributes.
+ */
+static void McpsIndication(McpsIndication_t *mcpsIndication)
+{
+ lora_AppData_t AppData;
+
+ if (mcpsIndication->Status != LORAMAC_EVENT_INFO_STATUS_OK)
+ {
+ return;
+ }
+
+ switch (mcpsIndication->McpsIndication)
+ {
+ case MCPS_UNCONFIRMED:
+ {
+ break;
+ }
+ case MCPS_CONFIRMED:
+ {
+ break;
+ }
+ case MCPS_PROPRIETARY:
+ {
+ break;
+ }
+ case MCPS_MULTICAST:
+ {
+ break;
+ }
+ default:
+ break;
+ }
+
+ // Check Multicast
+ // Check Port
+ // Check Datarate
+ // Check FramePending
+ if (mcpsIndication->FramePending == true)
+ {
+ // The server signals that it has pending data to be sent.
+ // We schedule an uplink as soon as possible to flush the server.
+ LoRaMainCallbacks->LORA_TxNeeded();
+ }
+ // Check Buffer
+ // Check BufferSize
+ // Check Rssi
+ // Check Snr
+ // Check RxSlot
+ /*if (certif->running() == true)
+ {
+ certif->DownLinkIncrement();
+ }*/
+
+ if (mcpsIndication->RxData == true)
+ {
+ switch (mcpsIndication->Port)
+ {
+ /*case CERTIF_PORT:
+ certif->rx(mcpsIndication, &JoinParameters);
+ break;
+ */
+ default:
+
+ AppData.Port = mcpsIndication->Port;
+ AppData.BuffSize = mcpsIndication->BufferSize;
+ AppData.Buff = mcpsIndication->Buffer;
+ lora_config.Rssi = mcpsIndication->Rssi;
+ lora_config.Snr = mcpsIndication->Snr;
+ LoRaMainCallbacks->LORA_RxData(&AppData);
+ break;
+ }
+ }
+}
+
+/*!
+ * \brief MLME-Confirm event function
+ *
+ * \param [IN] MlmeConfirm - Pointer to the confirm structure,
+ * containing confirm attributes.
+ */
+static void MlmeConfirm(MlmeConfirm_t *mlmeConfirm)
+{
+#if ( LORAMAC_CLASSB_ACTIVE == 1 )
+ MibRequestConfirm_t mibReq;
+#endif /* LORAMAC_CLASSB_ACTIVE */
+
+ switch (mlmeConfirm->MlmeRequest)
+ {
+ case MLME_JOIN:
+ {
+ if (mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK)
+ {
+ // Status is OK, node has joined the network
+ LoRaMainCallbacks->LORA_HasJoined();
+#if ( LORAMAC_CLASSB_ACTIVE == 1 )
+#if defined( USE_DEVICE_TIMING )
+ LORA_DeviceTimeReq();
+#else
+ LORA_BeaconTimeReq();
+#endif /* USE_DEVICE_TIMING */
+
+#endif /* LORAMAC_CLASSB_ACTIVE */
+ }
+ else
+ {
+ // Join was not successful. Try to join again
+ LORA_Join();
+ }
+ break;
+ }
+ case MLME_LINK_CHECK:
+ {
+ if (mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK)
+ {
+ // Check DemodMargin
+ // Check NbGateways
+ /*if (certif->running() == true)
+ {
+ certif->linkCheck(mlmeConfirm);
+ }*/
+ }
+ break;
+ }
+#if ( LORAMAC_CLASSB_ACTIVE == 1 )
+ case MLME_BEACON_ACQUISITION:
+ {
+ if (mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK)
+ {
+ /* Beacon has been acquired */
+ /* REquest Server for Ping Slot */
+ LORA_PingSlotReq();
+ }
+ else
+ {
+ /* Beacon not acquired */
+ /* Search again */
+ /* we can check if the MAC has received a time reference for the beacon*/
+ /* in this case do either a Device_Time_Req or a Beacon_Timing_req*/
+ LORA_BeaconReq();
+ }
+ break;
+ }
+ case MLME_PING_SLOT_INFO:
+ {
+ if (mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK)
+ {
+ /* class B is now ativated*/
+ mibReq.Type = MIB_DEVICE_CLASS;
+ mibReq.Param.Class = CLASS_B;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+
+ if ((LoRaParamInit->ActiveRegion == LORAMAC_REGION_AU915) || (LoRaParamInit->ActiveRegion == LORAMAC_REGION_US915))
+ {
+ mibReq.Type = MIB_PING_SLOT_DATARATE;
+ mibReq.Param.PingSlotDatarate = DR_8;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+ }
+
+ /*notify upper layer*/
+ LoRaMainCallbacks->LORA_ConfirmClass(CLASS_B);
+ }
+ else
+ {
+ LORA_PingSlotReq();
+ }
+ break;
+ }
+#if defined( USE_DEVICE_TIMING )
+ case MLME_DEVICE_TIME:
+ {
+ if (mlmeConfirm->Status != LORAMAC_EVENT_INFO_STATUS_OK)
+ {
+ LORA_DeviceTimeReq();
+ }
+ }
+#endif /* USE_DEVICE_TIMING */
+#endif /* LORAMAC_CLASSB_ACTIVE */
+ default:
+ break;
+ }
+}
+
+/*!
+ * \brief MLME-Indication event function
+ *
+ * \param [IN] MlmeIndication - Pointer to the indication structure.
+ */
+static void MlmeIndication(MlmeIndication_t *MlmeIndication)
+{
+#if ( LORAMAC_CLASSB_ACTIVE == 1 )
+ MibRequestConfirm_t mibReq;
+#endif /* LORAMAC_CLASSB_ACTIVE */
+
+
+ switch (MlmeIndication->MlmeIndication)
+ {
+ case MLME_SCHEDULE_UPLINK:
+ {
+ // The MAC signals that we shall provide an uplink as soon as possible
+ LoRaMainCallbacks->LORA_TxNeeded();
+ break;
+ }
+#if ( LORAMAC_CLASSB_ACTIVE == 1 )
+ case MLME_BEACON_LOST:
+ {
+ // Switch to class A again
+ mibReq.Type = MIB_DEVICE_CLASS;
+ mibReq.Param.Class = CLASS_A;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+
+ LORA_BeaconReq();
+ break;
+ }
+ case MLME_BEACON:
+ {
+ if (MlmeIndication->Status == LORAMAC_EVENT_INFO_STATUS_BEACON_LOCKED)
+ {
+ APP_LOG(TS_ON, VLEVEL_H, "BEACON RECEIVED\n\r");
+ }
+ else
+ {
+ APP_LOG(TS_ON, VLEVEL_H, "BEACON NOT RECEIVED\n\r");
+ }
+ break;
+
+ }
+#endif /* LORAMAC_CLASSB_ACTIVE */
+ default:
+ break;
+ }
+}
+/**
+ * lora Init
+ */
+void LORA_Init(LoRaMainCallback_t *callbacks, LoRaParam_t *LoRaParam)
+{
+ LoRaMacInfo_t *LoRaMacRegionInfo;
+
+ LoraMacInfo_Init();
+ LoRaMacRegionInfo = LoraMacInfo_GetPtr();
+ if (LoRaMacRegionInfo->Region == 0)
+ {
+ APP_PRINTF("error: At leats one region shall be defined in the MW: check lorawan_conf.h \n\r");
+ while(1) {} /* At leats one region shall be defined in MW */
+ }
+
+#if ( LORAMAC_CLASSB_ACTIVE == 1 )
+ if (LoRaMacRegionInfo->ClassB != 1)
+ {
+ APP_PRINTF("error: ClassB is not defined in the MW: set lorawan_conf.h accordingly\n\r");
+ while(1) {} /* error: ClassB is not defined in the MW*/
+ }
+#elif ( LORAMAC_CLASSB_ACTIVE == 0 )
+ if (LoRaMacRegionInfo->ClassB != 0)
+ {
+ APP_PRINTF("error: missmatch between applic and MW CLASSB defines: set lorawan_conf.h according to app_conf.h\n\r");
+ while(1) {} /* error: ClassB should not be defined in the MW*/
+ }
+#endif /* LORAMAC_CLASSB_ACTIVE */
+
+ /* init the Tx Duty Cycle*/
+ LoRaParamInit = LoRaParam;
+
+ /* init the main call backs*/
+ LoRaMainCallbacks = callbacks;
+
+#if (STATIC_DEVICE_EUI != 1)
+ LoRaMainCallbacks->BoardGetUniqueId(lora_config.DevEui);
+#endif
+
+#if (STATIC_DEVICE_ADDRESS != 1)
+ // Choose a "pseudo-random" device address
+ DevAddr = LoRaMainCallbacks->BoardGetRandomSeed();
+#endif
+
+ LoRaMacPrimitives.MacMcpsConfirm = McpsConfirm;
+ LoRaMacPrimitives.MacMcpsIndication = McpsIndication;
+ LoRaMacPrimitives.MacMlmeConfirm = MlmeConfirm;
+ LoRaMacPrimitives.MacMlmeIndication = MlmeIndication;
+ LoRaMacCallbacks.GetBatteryLevel = LoRaMainCallbacks->BoardGetBatteryLevel;
+ LoRaMacCallbacks.GetTemperatureLevel = LoRaMainCallbacks->BoardGetTemperatureLevel;
+ LoRaMacCallbacks.MacProcessNotify = LoRaMainCallbacks->MacProcessNotify;
+
+ if( 0U != ((1<<(LoRaParam->ActiveRegion))& (LoRaMacRegionInfo->Region)) )
+ {
+ LoRaMacInitialization(&LoRaMacPrimitives, &LoRaMacCallbacks, LoRaParam->ActiveRegion);
+ }
+ else
+ {
+ APP_PRINTF("error: Region is not defined in the MW: set lorawan_conf.h accordingly\n\r");
+ while(1) {} /* error: Region is not defined in the MW */
+ }
+
+#if ( HYBRID_ENABLED == 1 )
+ if ((LoRaParamInit->ActiveRegion == LORAMAC_REGION_AU915) || (LoRaParamInit->ActiveRegion == LORAMAC_REGION_US915))
+ {
+ uint16_t channelMask[] = { 0x00FF, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000};
+ mibReq.Type = MIB_CHANNELS_MASK;
+ mibReq.Param.ChannelsMask = channelMask;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+ mibReq.Type = MIB_CHANNELS_DEFAULT_MASK;
+ mibReq.Param.ChannelsDefaultMask = channelMask;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+ }
+ if (LoRaParamInit->ActiveRegion == LORAMAC_REGION_CN470)
+ {
+ uint16_t channelMask[] = { 0x00FF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000};
+ mibReq.Type = MIB_CHANNELS_MASK;
+ mibReq.Param.ChannelsMask = channelMask;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+ mibReq.Type = MIB_CHANNELS_DEFAULT_MASK;
+ mibReq.Param.ChannelsDefaultMask = channelMask;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+ }
+#elif !defined (HYBRID_ENABLED)
+ APP_PRINTF("error: HYBRID_ENABLED has not been defined at compilation\n\r");
+ #error HYBRID_ENABLED not defined ( shall be <0 or 1> )
+#endif /* HYBRID_ENABLED */
+
+ lora_config_otaa_set(LORA_ENABLE);
+
+ mibReq.Type = MIB_DEV_EUI;
+ mibReq.Param.DevEui = lora_config.DevEui;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+
+ mibReq.Type = MIB_JOIN_EUI;
+ mibReq.Param.JoinEui = lora_config.JoinEui;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+
+ mibReq.Type = MIB_ADR;
+ mibReq.Param.AdrEnable = LoRaParamInit->AdrEnable;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+
+ mibReq.Type = MIB_PUBLIC_NETWORK;
+ mibReq.Param.EnablePublicNetwork = LoRaParamInit->EnablePublicNetwork;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+
+ mibReq.Type = MIB_APP_KEY;
+ mibReq.Param.AppKey = lora_config.AppKey;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+
+ mibReq.Type = MIB_NWK_KEY;
+ mibReq.Param.NwkKey = lora_config.NwkKey;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+
+ mibReq.Type = MIB_DEVICE_CLASS;
+ mibReq.Param.Class = CLASS_A;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+
+ if ( (LoRaParamInit->ActiveRegion == LORAMAC_REGION_EU868)
+ || (LoRaParamInit->ActiveRegion == LORAMAC_REGION_RU864)
+ || (LoRaParamInit->ActiveRegion == LORAMAC_REGION_CN779)
+ || (LoRaParamInit->ActiveRegion == LORAMAC_REGION_EU433))
+ {
+ LoRaMacTestSetDutyCycleOn(true);
+ lora_config.duty_cycle = LORA_ENABLE;
+ }
+ else
+ {
+ lora_config.duty_cycle = LORA_DISABLE;
+ }
+
+ mibReq.Type = MIB_SYSTEM_MAX_RX_ERROR;
+ mibReq.Param.SystemMaxRxError = 20;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+
+#if ( LORAMAC_CLASSB_ACTIVE == 1 )
+ if (LoRaMacRegionInfo->ClassB == 1)
+ {
+ DefaultPingSlotPeriodicity = LORAWAN_DEFAULT_PING_SLOT_PERIODICITY;
+ }
+#endif /* LORAMAC_CLASSB_ACTIVE */
+
+ /*set Mac statein Idle*/
+ LoRaMacStart();
+}
+
+LoraErrorStatus LORA_Join(void)
+{
+ LoraErrorStatus status;
+ if (lora_config.otaa == LORA_ENABLE)
+ {
+ MlmeReq_t mlmeReq;
+
+ mlmeReq.Type = MLME_JOIN;
+ mlmeReq.Req.Join.Datarate = LoRaParamInit->TxDatarate;
+
+ JoinParameters = mlmeReq.Req.Join;
+
+ LoRaMacMlmeRequest(&mlmeReq);
+
+ status = LORA_SUCCESS;
+ }
+ else
+ {
+ /*no Join in abp*/
+ status = LORA_ERROR;
+ }
+
+ return status;
+}
+
+LoraFlagStatus LORA_JoinStatus(void)
+{
+ MibRequestConfirm_t mibReq;
+
+ mibReq.Type = MIB_NETWORK_ACTIVATION;
+
+ LoRaMacMibGetRequestConfirm(&mibReq);
+
+ if (mibReq.Param.NetworkActivation == ACTIVATION_TYPE_NONE)
+ {
+ return LORA_RESET;
+ }
+ else
+ {
+ return LORA_SET;
+ }
+}
+
+
+
+LoraErrorStatus LORA_send(lora_AppData_t *AppData, LoraConfirm_t IsTxConfirmed)
+{
+ McpsReq_t mcpsReq;
+ LoRaMacTxInfo_t txInfo;
+
+ /*if certification test are on going, application data is not sent*/
+ /*
+ if (certif->running() == true)
+ {
+ return LORA_ERROR;
+ }*/
+
+ if (LoRaMacQueryTxPossible(AppData->BuffSize, &txInfo) != LORAMAC_STATUS_OK)
+ {
+ // Send empty frame in order to flush MAC commands
+ mcpsReq.Type = MCPS_UNCONFIRMED;
+ mcpsReq.Req.Unconfirmed.fBuffer = NULL;
+ mcpsReq.Req.Unconfirmed.fBufferSize = 0;
+ mcpsReq.Req.Unconfirmed.Datarate = lora_config_tx_datarate_get() ;
+ }
+ else
+ {
+ if (IsTxConfirmed == LORAWAN_UNCONFIRMED_MSG)
+ {
+ mcpsReq.Type = MCPS_UNCONFIRMED;
+ mcpsReq.Req.Unconfirmed.fPort = AppData->Port;
+ mcpsReq.Req.Unconfirmed.fBufferSize = AppData->BuffSize;
+ mcpsReq.Req.Unconfirmed.fBuffer = AppData->Buff;
+ mcpsReq.Req.Unconfirmed.Datarate = lora_config_tx_datarate_get() ;
+ }
+ else
+ {
+ mcpsReq.Type = MCPS_CONFIRMED;
+ mcpsReq.Req.Confirmed.fPort = AppData->Port;
+ mcpsReq.Req.Confirmed.fBufferSize = AppData->BuffSize;
+ mcpsReq.Req.Confirmed.fBuffer = AppData->Buff;
+ mcpsReq.Req.Confirmed.NbTrials = 8;
+ mcpsReq.Req.Confirmed.Datarate = lora_config_tx_datarate_get() ;
+ }
+ }
+ if (LoRaMacMcpsRequest(&mcpsReq) == LORAMAC_STATUS_OK)
+ {
+ return LORA_SUCCESS;
+ }
+ return LORA_ERROR;
+}
+
+#if ( LORAMAC_CLASSB_ACTIVE == 1 )
+#if defined( USE_DEVICE_TIMING )
+static LoraErrorStatus LORA_DeviceTimeReq(void)
+{
+ MlmeReq_t mlmeReq;
+
+ mlmeReq.Type = MLME_DEVICE_TIME;
+
+ if (LoRaMacMlmeRequest(&mlmeReq) == LORAMAC_STATUS_OK)
+ {
+ return LORA_SUCCESS;
+ }
+ else
+ {
+ return LORA_ERROR;
+ }
+}
+#else
+static LoraErrorStatus LORA_BeaconTimeReq(void)
+{
+ MlmeReq_t mlmeReq;
+
+ mlmeReq.Type = MLME_BEACON_TIMING;
+
+ if (LoRaMacMlmeRequest(&mlmeReq) == LORAMAC_STATUS_OK)
+ {
+ return LORA_SUCCESS;
+ }
+ else
+ {
+ return LORA_ERROR;
+ }
+}
+#endif /* USE_DEVICE_TIMING */
+
+static LoraErrorStatus LORA_BeaconReq(void)
+{
+ MlmeReq_t mlmeReq;
+
+ mlmeReq.Type = MLME_BEACON_ACQUISITION;
+
+ if (LoRaMacMlmeRequest(&mlmeReq) == LORAMAC_STATUS_OK)
+ {
+ return LORA_SUCCESS;
+ }
+ else
+ {
+ return LORA_ERROR;
+ }
+}
+
+static LoraErrorStatus LORA_PingSlotReq(void)
+{
+ MlmeReq_t mlmeReq;
+
+ mlmeReq.Type = MLME_LINK_CHECK;
+ LoRaMacMlmeRequest(&mlmeReq);
+
+ mlmeReq.Type = MLME_PING_SLOT_INFO;
+ mlmeReq.Req.PingSlotInfo.PingSlot.Fields.Periodicity = DefaultPingSlotPeriodicity;
+ mlmeReq.Req.PingSlotInfo.PingSlot.Fields.RFU = 0;
+
+ if (LoRaMacMlmeRequest(&mlmeReq) == LORAMAC_STATUS_OK)
+ {
+ return LORA_SUCCESS;
+ }
+ else
+ {
+ return LORA_ERROR;
+ }
+}
+#endif /* LORAMAC_CLASSB_ACTIVE */
+
+LoraErrorStatus LORA_RequestClass(DeviceClass_t newClass)
+{
+ LoraErrorStatus Errorstatus = LORA_SUCCESS;
+ MibRequestConfirm_t mibReq;
+ DeviceClass_t currentClass;
+
+ mibReq.Type = MIB_DEVICE_CLASS;
+ LoRaMacMibGetRequestConfirm(&mibReq);
+
+ currentClass = mibReq.Param.Class;
+ /*attempt to swicth only if class update*/
+ if (currentClass != newClass)
+ {
+ switch (newClass)
+ {
+ case CLASS_A:
+ {
+ mibReq.Param.Class = CLASS_A;
+ if (LoRaMacMibSetRequestConfirm(&mibReq) == LORAMAC_STATUS_OK)
+ {
+ /*switch is instantanuous*/
+ LoRaMainCallbacks->LORA_ConfirmClass(CLASS_A);
+ }
+ else
+ {
+ Errorstatus = LORA_ERROR;
+ }
+ break;
+ }
+ case CLASS_B:
+ {
+#if ( LORAMAC_CLASSB_ACTIVE == 1 )
+ if (currentClass != CLASS_A)
+ {
+ Errorstatus = LORA_ERROR;
+ }
+ /*switch is not instantanuous*/
+ Errorstatus = LORA_BeaconReq();
+#else
+ APP_PRINTF("warning: LORAMAC_CLASSB_ACTIVE has not been defined at compilation\n\r");
+#endif /* LORAMAC_CLASSB_ACTIVE */
+ break;
+ }
+ case CLASS_C:
+ {
+ if (currentClass != CLASS_A)
+ {
+ Errorstatus = LORA_ERROR;
+ }
+ /*switch is instantanuous*/
+ mibReq.Param.Class = CLASS_C;
+ if (LoRaMacMibSetRequestConfirm(&mibReq) == LORAMAC_STATUS_OK)
+ {
+ LoRaMainCallbacks->LORA_ConfirmClass(CLASS_C);
+ }
+ else
+ {
+ Errorstatus = LORA_ERROR;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ return Errorstatus;
+}
+
+void LORA_GetCurrentClass(DeviceClass_t *currentClass)
+{
+ MibRequestConfirm_t mibReq;
+
+ mibReq.Type = MIB_DEVICE_CLASS;
+ LoRaMacMibGetRequestConfirm(&mibReq);
+
+ *currentClass = mibReq.Param.Class;
+}
+
+
+void lora_config_otaa_set(LoraState_t otaa)
+{
+ lora_config.otaa = otaa;
+
+ if (lora_config.otaa == LORA_ENABLE)
+ {
+ APP_PPRINTF("OTAA Mode enabled\n\r");
+ APP_PPRINTF("DevEui= %02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X\n\r", HEX8(lora_config.DevEui));
+ APP_PPRINTF("JoinEui= %02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X\n\r", HEX8(lora_config.JoinEui));
+ APP_PPRINTF("AppKey= %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n\r", HEX16(lora_config.AppKey));
+
+ mibReq.Type = MIB_NETWORK_ACTIVATION;
+ mibReq.Param.NetworkActivation = ACTIVATION_TYPE_NONE;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+ }
+ else
+ {
+ APP_PPRINTF("ABP Mode enabled\n\r");
+ APP_PPRINTF("DevEui= %02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X\n\r", HEX8(lora_config.DevEui));
+ APP_PPRINTF("DevAdd= %08X\n\r", DevAddr) ;
+ APP_PPRINTF("NwkSKey= %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n\r", HEX16(lora_config.NwkSEncKey));
+ APP_PPRINTF("AppSKey= %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n\r", HEX16(lora_config.AppSKey));
+
+ mibReq.Type = MIB_NET_ID;
+ mibReq.Param.NetID = LORAWAN_NETWORK_ID;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+
+ mibReq.Type = MIB_DEV_ADDR;
+ mibReq.Param.DevAddr = DevAddr;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+
+ mibReq.Type = MIB_F_NWK_S_INT_KEY;
+ mibReq.Param.FNwkSIntKey = lora_config.FNwkSIntKey;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+
+ mibReq.Type = MIB_S_NWK_S_INT_KEY;
+ mibReq.Param.SNwkSIntKey = lora_config.SNwkSIntKey;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+
+ mibReq.Type = MIB_NWK_S_ENC_KEY;
+ mibReq.Param.NwkSEncKey = lora_config.NwkSEncKey;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+
+ mibReq.Type = MIB_APP_S_KEY;
+ mibReq.Param.AppSKey = lora_config.AppSKey;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+
+ mibReq.Type = MIB_NETWORK_ACTIVATION;
+ mibReq.Param.NetworkActivation = ACTIVATION_TYPE_ABP;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+
+ // Enable legacy mode to operate according to LoRaWAN Spec. 1.0.3
+ Version_t abpLrWanVersion;
+
+ abpLrWanVersion.Fields.Major = 1;
+ abpLrWanVersion.Fields.Minor = 0;
+ abpLrWanVersion.Fields.Revision = 3;
+ abpLrWanVersion.Fields.Rfu = 0;
+
+ mibReq.Type = MIB_ABP_LORAWAN_VERSION;
+ mibReq.Param.AbpLrWanVersion = abpLrWanVersion;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+ }
+}
+
+
+LoraState_t lora_config_otaa_get(void)
+{
+ return lora_config.otaa;
+}
+
+void lora_config_duty_cycle_set(LoraState_t duty_cycle)
+{
+ lora_config.duty_cycle = duty_cycle;
+ LoRaMacTestSetDutyCycleOn((duty_cycle == LORA_ENABLE) ? 1 : 0);
+}
+
+LoraState_t lora_config_duty_cycle_get(void)
+{
+ return lora_config.duty_cycle;
+}
+
+uint8_t *lora_config_deveui_get(void)
+{
+ return lora_config.DevEui;
+}
+
+uint8_t *lora_config_joineui_get(void)
+{
+ return lora_config.JoinEui;
+}
+
+void lora_config_joineui_set(uint8_t joineui[8])
+{
+ UTIL_MEM_cpy_8(lora_config.JoinEui, joineui, sizeof(lora_config.JoinEui));
+}
+
+uint8_t *lora_config_appkey_get(void)
+{
+ return lora_config.AppKey;
+}
+
+void lora_config_appkey_set(uint8_t appkey[16])
+{
+ UTIL_MEM_cpy_8(lora_config.AppKey, appkey, sizeof(lora_config.AppKey));
+ UTIL_MEM_cpy_8(lora_config.NwkKey, appkey, sizeof(lora_config.NwkKey));
+
+ mibReq.Type = MIB_APP_KEY;
+ mibReq.Param.AppKey = lora_config.AppKey;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+
+ mibReq.Type = MIB_NWK_KEY;
+ mibReq.Param.NwkKey = lora_config.NwkKey;
+ LoRaMacMibSetRequestConfirm(&mibReq);
+}
+
+void lora_config_reqack_set(LoraConfirm_t reqack)
+{
+ lora_config.ReqAck = reqack;
+}
+
+LoraConfirm_t lora_config_reqack_get(void)
+{
+ return lora_config.ReqAck;
+}
+
+int8_t lora_config_snr_get(void)
+{
+ return lora_config.Snr;
+}
+
+int16_t lora_config_rssi_get(void)
+{
+ return lora_config.Rssi;
+}
+
+void lora_config_tx_datarate_set(int8_t TxDataRate)
+{
+ lora_config.TxDatarate = TxDataRate;
+}
+
+int8_t lora_config_tx_datarate_get(void)
+{
+ return lora_config.TxDatarate;
+}
+
+/*
+static void OnCertifTimer(void *context)
+{
+ UTIL_SEQ_SetTask(CFG_SEQ_TASK_LORA_CERTIF_TX, CFG_SEQ_Prio_0);
+}
+
+static void CertifSend(void)
+{
+ uint8_t Dummy[1] = {1};
+ lora_AppData_t AppData;
+ AppData.Buff = Dummy;
+ AppData.BuffSize = sizeof(Dummy);
+ AppData.Port = 99;
+
+ LORA_send(&AppData, LORAWAN_UNCONFIRMED_MSG);
+}
+
+void lora_wan_certif(void)
+{
+ LoRaMacTestSetDutyCycleOn(false);
+ LORA_Join();
+ TimerInit(&TxcertifTimer, OnCertifTimer); // 8s
+ TimerSetValue(&TxcertifTimer, 8000); // 8s
+ TimerStart(&TxcertifTimer);
+ UTIL_SEQ_RegTask(CFG_SEQ_TASK_LORA_CERTIF_TX, UTIL_SEQ_RFU, CertifSend);
+
+}
+*/
+
+LoRaMacRegion_t lora_region_get(void)
+{
+ return LoRaRegion;
+}
+/*
+void LORA_TestInit(LoRaCertifCallback_t *callbacks)
+{
+ certif = callbacks;
+}
+*/
+#if ( LORAMAC_CLASSB_ACTIVE == 1 )
+LoRaMacClassBNvmCtx_t* LORA_Get_ClassBCtx(void)
+{
+ mibReq.Type = MIB_NVM_CTXS;
+ LoRaMacMibGetRequestConfirm( &mibReq );
+ return ( LoRaMacClassBNvmCtx_t*) mibReq.Param.Contexts->ClassBNvmCtx;
+}
+
+uint8_t LORA_Get_PingSlotPeriodicity(void)
+{
+ return DefaultPingSlotPeriodicity;
+}
+
+void LORA_Set_PingSlotPeriodicity(uint8_t PingSlotPeriodicity)
+{
+ DefaultPingSlotPeriodicity= PingSlotPeriodicity;
+}
+#endif /* LORAMAC_CLASSB_ACTIVE */
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/lora.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/lora.h
new file mode 100644
index 00000000..402b88ca
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/lora.h
@@ -0,0 +1,403 @@
+/**
+ ******************************************************************************
+ * @file lora.h
+ * @author MCD Application Team
+ * @brief lora API to drive the lora state Machine
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under BSD 3-Clause license,
+ * the "License"; You may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ * opensource.org/licenses/BSD-3-Clause
+ *
+ ******************************************************************************
+ */
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+
+#ifndef __LORA_H__
+#define __LORA_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+#include "Commissioning.h"
+#include "LoRaMac.h"
+#include "app_conf.h"
+#if defined (LORAMAC_CLASSB_ACTIVE) && ( LORAMAC_CLASSB_ACTIVE == 1 )
+#include "LoRaMacClassB.h"
+#elif !defined (LORAMAC_CLASSB_ACTIVE)
+ #error LORAMAC_CLASSB_ACTIVE not defined
+#endif /* LORAMAC_CLASSB_ACTIVE */
+
+#include "region/Region.h"
+#include "lora-test.h"
+
+/* Exported constants --------------------------------------------------------*/
+/*!
+* LoRaWAN confirmed messages
+*/
+
+#define LORAWAN_ADR_ON 1
+#define LORAWAN_ADR_OFF 0
+/* Exported types ------------------------------------------------------------*/
+
+/*!
+ * Application Data structure
+ */
+typedef struct
+{
+ /*point to the LoRa App data buffer*/
+ uint8_t *Buff;
+ /*LoRa App data buffer size*/
+ uint8_t BuffSize;
+ /*Port on which the LoRa App is data is sent/ received*/
+ uint8_t Port;
+
+} lora_AppData_t;
+
+
+
+typedef enum
+{
+ LORA_RESET = 0,
+ LORA_SET = !LORA_RESET
+} LoraFlagStatus;
+
+typedef enum
+{
+ LORA_DISABLE = 0,
+ LORA_ENABLE = !LORA_DISABLE
+} LoraState_t;
+
+typedef enum
+{
+ LORA_ERROR = -1,
+ LORA_SUCCESS = 0
+} LoraErrorStatus;
+
+typedef enum
+{
+ LORAWAN_UNCONFIRMED_MSG = 0,
+ LORAWAN_CONFIRMED_MSG = !LORAWAN_UNCONFIRMED_MSG
+} LoraConfirm_t;
+
+typedef enum
+{
+ LORA_TRUE = 0,
+ LORA_FALSE = !LORA_TRUE
+} LoraBool_t;
+
+/*!
+ * LoRa State Machine states
+ */
+typedef enum eTxEventType
+{
+ /*!
+ * @brief AppdataTransmition issue based on timer every TxDutyCycleTime
+ */
+ TX_ON_TIMER,
+ /*!
+ * @brief AppdataTransmition external event plugged on OnSendEvent( )
+ */
+ TX_ON_EVENT
+} TxEventType_t;
+
+
+/*!
+ * LoRa State Machine states
+ */
+typedef struct sLoRaParam
+{
+ /*!
+ * @brief Activation state of adaptativeDatarate
+ */
+ bool AdrEnable;
+ /*!
+ * @brief Uplink datarate, if AdrEnable is off
+ */
+ int8_t TxDatarate;
+ /*!
+ * @brief Enable or disable a public network
+ */
+ bool EnablePublicNetwork;
+ /*!
+ * @brief region to open
+ */
+ LoRaMacRegion_t ActiveRegion;
+} LoRaParam_t;
+
+/* Lora Main callbacks*/
+typedef struct sLoRaMainCallback
+{
+ /*!
+ * @brief Get the current battery level
+ *
+ * @retval value battery level ( 0: very low, 254: fully charged )
+ */
+ uint8_t (*BoardGetBatteryLevel)(void);
+ /*!
+ * \brief Get the current temperature
+ *
+ * \retval value temperature in degreeCelcius( q7.8 )
+ */
+ uint16_t (*BoardGetTemperatureLevel)(void);
+ /*!
+ * @brief Gets the board 64 bits unique ID
+ *
+ * @param [IN] id Pointer to an array that will contain the Unique ID
+ */
+ void (*BoardGetUniqueId)(uint8_t *id);
+ /*!
+ * Returns a pseudo random seed generated using the MCU Unique ID
+ *
+ * @retval seed Generated pseudo random seed
+ */
+ uint32_t (*BoardGetRandomSeed)(void);
+ /*!
+ * @brief Process Rx Data received from Lora network
+ *
+ * @param [IN] AppData structure
+ *
+ */
+ void (*LORA_RxData)(lora_AppData_t *AppData);
+
+ /*!
+ * @brief callback indicating EndNode has jsu joiny
+ *
+ * @param [IN] None
+ */
+ void (*LORA_HasJoined)(void);
+ /*!
+ * @brief Confirms the class change
+ *
+ * @param [IN] AppData is a buffer to process
+ *
+ * @param [IN] port is a Application port on wicth Appdata will be sent
+ *
+ * @param [IN] length is the number of recieved bytes
+ */
+ void (*LORA_ConfirmClass)(DeviceClass_t Class);
+ /*!
+ * @brief callback indicating an uplink transmission is needed to allow
+ * a pending downlink transmission
+ *
+ * @param [IN] None
+ */
+ void (*LORA_TxNeeded)(void);
+ /*!
+ *\brief Will be called each time a Radio IRQ is handled by the MAC
+ * layer.
+ *
+ *\warning Runs in a IRQ context. Should only change variables state.
+ */
+ void (*MacProcessNotify)(void);
+ /*!
+ * @brief callback indicating a downlink transmission providing
+ * an acknowledgment for an uplink confirmed data message transmission
+ *
+ * @param [IN] None
+ */
+ void (*LORA_McpsDataConfirm)(void);
+} LoRaMainCallback_t;
+
+
+
+/* External variables --------------------------------------------------------*/
+/* Exported macros -----------------------------------------------------------*/
+/* Exported functions ------------------------------------------------------- */
+/**
+ * @brief Lora Initialisation
+ * @param [IN] LoRaMainCallback_t
+ * @param [IN] application parmaters
+ * @retval none
+ */
+void LORA_Init(LoRaMainCallback_t *callbacks, LoRaParam_t *LoRaParam);
+
+/**
+ * @brief Lora Certif Initialisation (Registers CB)
+ * @param [IN] LoRaCertifCallback_t
+ * @retval none
+ */
+void LORA_TestInit(LoRaCertifCallback_t *callbacks);
+/**
+ * @brief run Lora classA state Machine
+ * @param [IN] none
+ * @retval none
+ */
+LoraErrorStatus LORA_send(lora_AppData_t *AppData, LoraConfirm_t IsTxConfirmed);
+
+/**
+ * @brief Join a Lora Network in classA
+ * @Note if the device is ABP, this is a pass through functon
+ * @param [IN] none
+ * @retval none
+ */
+LoraErrorStatus LORA_Join(void);
+
+/**
+ * @brief Check whether the Device is joined to the network
+ * @param [IN] none
+ * @retval returns LORA_SET if joined
+ */
+LoraFlagStatus LORA_JoinStatus(void);
+
+/**
+ * @brief change Lora Class
+ * @Note callback LORA_ConfirmClass informs upper layer that the change has occured
+ * @Note Only switch from class A to class B/C OR from class B/C to class A is allowed
+ * @Attention can be calld only in LORA_ClassSwitchSlot or LORA_RxData callbacks
+ * @param [IN] DeviceClass_t NewClass
+ * @retval LoraErrorStatus
+ */
+LoraErrorStatus LORA_RequestClass(DeviceClass_t newClass);
+
+/**
+ * @brief get the current Lora Class
+ * @param [IN] DeviceClass_t NewClass
+ * @retval None
+ */
+void LORA_GetCurrentClass(DeviceClass_t *currentClass);
+
+/**
+ * @brief Set join activation process: OTAA vs ABP
+ * @param Over The Air Activation status to set: enable or disable
+ * @retval None
+ */
+void lora_config_otaa_set(LoraState_t otaa);
+
+/**
+ * @brief Get join activation process: OTAA vs ABP
+ * @param None
+ * @retval ENABLE if OTAA is used, DISABLE if ABP is used
+ */
+LoraState_t lora_config_otaa_get(void);
+
+/**
+ * @brief Set duty cycle: ENABLE or DISABLE
+ * @param Duty cycle to set: enable or disable
+ * @retval None
+ */
+void lora_config_duty_cycle_set(LoraState_t duty_cycle);
+
+/**
+ * @brief Get Duty cycle: OTAA vs ABP
+ * @param None
+ * @retval ENABLE / DISABLE
+ */
+LoraState_t lora_config_duty_cycle_get(void);
+
+/**
+ * @brief Get Device EUI
+ * @param None
+ * @retval DevEUI
+ */
+uint8_t *lora_config_deveui_get(void);
+
+/**
+ * @brief Get Join Eui
+ * @param None
+ * @retval JoinEUI
+ */
+uint8_t *lora_config_joineui_get(void);
+
+/**
+ * @brief Set Join Eui
+ * @param JoinEUI
+ * @retval Nonoe
+ */
+void lora_config_joineui_set(uint8_t joineui[8]);
+
+/**
+ * @brief Get Application Key
+ * @param None
+ * @retval AppKey
+ */
+uint8_t *lora_config_appkey_get(void);
+
+/**
+ * @brief Set Application Key
+ * @param AppKey
+ * @retval None
+ */
+void lora_config_appkey_set(uint8_t appkey[16]);
+
+/**
+ * @brief Set whether or not acknowledgement is required
+ * @param ENABLE or DISABLE
+ * @retval None
+ */
+void lora_config_reqack_set(LoraConfirm_t reqack);
+
+/**
+ * @brief Get whether or not acknowledgement is required
+ * @param None
+ * @retval ENABLE or DISABLE
+ */
+LoraConfirm_t lora_config_reqack_get(void);
+
+/**
+ * @brief Get the SNR of the last received data
+ * @param None
+ * @retval SNR
+ */
+int8_t lora_config_snr_get(void);
+
+/**
+ * @brief Get the RSSI of the last received data
+ * @param None
+ * @retval RSSI
+ */
+int16_t lora_config_rssi_get(void);
+
+/**
+ * @brief Launch LoraWan certification tests
+ * @param None
+ * @retval The application port
+ */
+void lora_wan_certif(void);
+
+/**
+ * @brief set tx datarate
+ * @param None
+ * @retval The application port
+ */
+void lora_config_tx_datarate_set(int8_t TxDataRate);
+
+/**
+ * @brief get tx datarate
+ * @param None
+ * @retval tx datarate
+ */
+int8_t lora_config_tx_datarate_get(void);
+
+/**
+ * @brief get LoRaMac region
+ * @param None
+ * @retval LoRaMac region
+ */
+LoRaMacRegion_t lora_region_get(void);
+
+#if defined (LORAMAC_CLASSB_ACTIVE) && ( LORAMAC_CLASSB_ACTIVE == 1 )
+
+LoRaMacClassBNvmCtx_t* LORA_Get_ClassBCtx(void);
+uint8_t LORA_Get_PingSlotPeriodicity(void);
+void LORA_Set_PingSlotPeriodicity(uint8_t PingSlotPeriodicity);
+
+#elif !defined (LORAMAC_CLASSB_ACTIVE)
+ #error LORAMAC_CLASSB_ACTIVE not defined
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*__LORA_H__*/
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/lorawan_info.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/lorawan_info.c
new file mode 100644
index 00000000..4dadc38a
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/lorawan_info.c
@@ -0,0 +1,94 @@
+/**
+ ******************************************************************************
+ * @file lorawan_info.c
+ * @author MCD Application Team
+ * @brief To give info to the application about LoraWAN configuration
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under BSD 3-Clause license,
+ * the "License"; You may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ * opensource.org/licenses/BSD-3-Clause
+ *
+ ******************************************************************************
+ */
+
+/* Includes ------------------------------------------------------------------*/
+#include "lorawan_info.h"
+#include "LoRaMac.h"
+
+/* Private typedef -----------------------------------------------------------*/
+/* Private define ------------------------------------------------------------*/
+/* Private macro -------------------------------------------------------------*/
+/* Private variables ---------------------------------------------------------*/
+/* Private function prototypes -----------------------------------------------*/
+/* Exported variables --------------------------------------------------------*/
+
+LoRaMacInfo_t LoRaMacInfoTable;
+
+/* Exported functions --------------------------------------------------------*/
+/**
+ * @brief initialises the LoraMacInfo capabilities table
+ * @param none
+ * @retval none
+ */
+void LoraMacInfo_Init(void)
+{
+ LoRaMacInfoTable.Region = 0;
+#ifdef REGION_AS923
+ LoRaMacInfoTable.Region |= (1<© Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under BSD 3-Clause license,
+ * the "License"; You may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ * opensource.org/licenses/BSD-3-Clause
+ *
+ ******************************************************************************
+ */
+#ifndef __LORAWAN_INFO_H__
+#define __LORAWAN_INFO_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+#include
+
+/* Exported constants --------------------------------------------------------*/
+/* Exported types ------------------------------------------------------------*/
+
+/*!
+ * To give info to the application about LoraWAN capability
+ * it can depend how it has been compiled (e.g. compiled regions ...)
+ * Params should be better uint32_t foe easier alignment with info_table concept
+ */
+typedef struct
+{
+ uint32_t Region; /*!< Combination of regions compiled on MW */
+ uint32_t ClassB; /*!< 0: not compiled in Mw, 1 : compiled in MW */
+}LoRaMacInfo_t;
+
+
+/* External variables --------------------------------------------------------*/
+/* Exported macros -----------------------------------------------------------*/
+/* Exported functions ------------------------------------------------------- */
+
+/**
+ * @brief initialises the LoraMacInfo table
+ * @param none
+ * @retval none
+ */
+void LoraMacInfo_Init(void);
+
+/**
+ * @brief returns the pointer to the LoraMacInfo table
+ * @param none
+ * @retval LoRaMacInfoTable pointer
+ */
+LoRaMacInfo_t* LoraMacInfo_GetPtr(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __LORAWAN_INFO_H__
+
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Utilities/utilities.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Utilities/utilities.c
new file mode 100644
index 00000000..120e08a2
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Utilities/utilities.c
@@ -0,0 +1,94 @@
+/*
+ / _____) _ | |
+( (____ _____ ____ _| |_ _____ ____| |__
+ \____ \| ___ | (_ _) ___ |/ ___) _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+ (C)2013 Semtech
+
+Description: Helper functions implementation
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+
+Maintainer: Miguel Luis and Gregory Cristian
+*/
+/**
+ ******************************************************************************
+ *
+ * Portions COPYRIGHT 2019 STMicroelectronics
+ *
+ * @file utilities.c
+ * @author MCD Application Team
+ * @brief Helper functions implementation
+ ******************************************************************************
+ */
+
+#include "utilities.h"
+
+/*!
+ * Redefinition of rand() and srand() standard C functions.
+ * These functions are redefined in order to get the same behavior across
+ * different compiler toolchains implementations.
+ */
+// Standard random functions redefinition start
+#define RAND_LOCAL_MAX 2147483647L
+
+static uint32_t next = 1;
+
+int32_t rand1( void )
+{
+ return ( ( next = next * 1103515245L + 12345L ) % RAND_LOCAL_MAX );
+}
+
+void srand1( uint32_t seed )
+{
+ next = seed;
+}
+// Standard random functions redefinition end
+
+int32_t randr( int32_t min, int32_t max )
+{
+ return ( int32_t )rand1( ) % ( max - min + 1 ) + min;
+}
+
+void memcpy1( uint8_t *dst, const uint8_t *src, uint16_t size )
+{
+ while( size-- )
+ {
+ *dst++ = *src++;
+ }
+}
+
+void memcpyr( uint8_t *dst, const uint8_t *src, uint16_t size )
+{
+ dst = dst + ( size - 1 );
+ while( size-- )
+ {
+ *dst-- = *src++;
+ }
+}
+
+void memset1( uint8_t *dst, uint8_t value, uint16_t size )
+{
+ while( size-- )
+ {
+ *dst++ = value;
+ }
+}
+
+int8_t Nibble2HexChar( uint8_t a )
+{
+ if( a < 10 )
+ {
+ return '0' + a;
+ }
+ else if( a < 16 )
+ {
+ return 'A' + ( a - 10 );
+ }
+ else
+ {
+ return '?';
+ }
+}
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Utilities/utilities.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Utilities/utilities.h
new file mode 100644
index 00000000..5bbad2c3
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Utilities/utilities.h
@@ -0,0 +1,136 @@
+/*
+ / _____) _ | |
+( (____ _____ ____ _| |_ _____ ____| |__
+ \____ \| ___ | (_ _) ___ |/ ___) _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+ (C)2013 Semtech
+
+Description: Helper functions implementation
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+
+Maintainer: Miguel Luis and Gregory Cristian
+*/
+
+ /**
+ ******************************************************************************
+ *
+ * Portions COPYRIGHT 2019 STMicroelectronics
+ *
+ * @file utilities.h
+ * @author MCD Application Team
+ * @brief Header for driver utilities.c module
+ ******************************************************************************
+ */
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __UTILITIES_H__
+#define __UTILITIES_H__
+
+/* Includes ------------------------------------------------------------------*/
+#include
+#include "utilities_conf.h"
+
+
+/* Exported types ------------------------------------------------------------*/
+/* Exported constants --------------------------------------------------------*/
+/* External variables --------------------------------------------------------*/
+/* Exported macros -----------------------------------------------------------*/
+/* Defines -------------------------------------------------------------------*/
+
+
+
+#define TIMERTIME_T_MAX ( ( uint32_t )~0 )
+
+/*!
+ * @brief Returns the minimum value between a and b
+ *
+ * @param [IN] a 1st value
+ * @param [IN] b 2nd value
+ * @retval minValue Minimum value
+ */
+#ifndef MIN
+ #define MIN( a, b ) ( ( ( a ) < ( b ) ) ? ( a ) : ( b ) )
+#endif
+
+/*!
+ * @brief Returns the maximum value between a and b
+ *
+ * @param [IN] a 1st value
+ * @param [IN] b 2nd value
+ * @retval maxValue Maximum value
+ */
+#ifndef MAX
+ #define MAX( a, b ) ( ( ( a ) > ( b ) ) ? ( a ) : ( b ) )
+#endif
+
+/*!
+ * @brief Returns 2 raised to the power of n
+ *
+ * @param [IN] n power value
+ * @retval result of raising 2 to the power n
+ */
+#define POW2( n ) ( 1 << n )
+
+
+
+/*!
+ * @brief Initializes the pseudo random generator initial value
+ * @param [IN] seed Pseudo random generator initial value
+ * @retval none
+ */
+void srand1( uint32_t seed );
+
+/*!
+ * @brief Computes a random number between min and max
+ * @param [IN] min range minimum value
+ * @param [IN] max range maximum value
+ * @retval random random value in range min..max
+ */
+int32_t randr( int32_t min, int32_t max );
+
+/*!
+ * @brief Computes a random number
+ * @param none
+ * @retval random value in range min..max
+ */
+int32_t rand1( void );
+
+/*!
+ * @brief Copies size elements of src array to dst array
+ * @remark STM32 Standard memcpy function only works on pointers that are aligned
+ * @param [OUT] dst Destination array
+ * @param [IN] src Source array
+ * @param [IN] size Number of bytes to be copied
+ * @retval none
+ */
+void memcpy1( uint8_t *dst, const uint8_t *src, uint16_t size );
+
+/*!
+ * @brief Copies size elements of src array to dst array reversing the byte order
+ * @param [OUT] dst Destination array
+ * @param [IN] src Source array
+ * @param [IN] size Number of bytes to be copied
+ * @retval none
+ */
+void memcpyr( uint8_t *dst, const uint8_t *src, uint16_t size );
+
+/*!
+ * @brief Set size elements of dst array with value
+ * @remark STM32 Standard memset function only works on pointers that are aligned
+ * @param [OUT] dst Destination array
+ * @param [IN] value Default value
+ * @param [IN] size Number of bytes to be copied
+ * @retval none
+ */
+void memset1( uint8_t *dst, uint8_t value, uint16_t size );
+
+/*!
+ * @brief Converts a nibble to an hexadecimal character
+ * @param [IN] a Nibble to be converted
+ * @retval hexChar Converted hexadecimal character
+ */
+int8_t Nibble2HexChar( uint8_t a );
+
+#endif // __UTILITIES_H__
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/readme.md b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/readme.md
new file mode 100644
index 00000000..5e042dc1
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/readme.md
@@ -0,0 +1,771 @@
+# LoRaWAN endpoint stack implementation and example projects
+
+ ______ _
+ / _____) _ | |
+ ( (____ _____ ____ _| |_ _____ ____| |__
+ \____ \| ___ | (_ _) ___ |/ ___) _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+ (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ (C)2013-2019 Semtech
+
+ ___ _____ _ ___ _ _____ ___ ___ ___ ___
+ / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
+ \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
+ |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
+ embedded.connectivity.solutions===============
+
+## Introduction
+
+The aim of this project is to show an example of the endpoint LoRaWAN stack implementation.
+
+This project has 3 active branches in place. The **[master](https://github.com/Lora-net/LoRaMac-node/tree/master)** branch which provides the latest released source code ([v4.4.2](https://github.com/Lora-net/LoRaMac-node/releases/tag/v4.4.2)), the **[develop](https://github.com/Lora-net/LoRaMac-node/tree/develop)** branch which provides the current source code development status to be released next ([Milestone 4.4.3](https://github.com/Lora-net/LoRaMac-node/milestone/4)) and the **[feature/5.0.0](https://github.com/Lora-net/LoRaMac-node/tree/feature/5.0.0)** branch which provides a preview of the current source code development status for [LoRaWAN Specification v1.1](https://lora-alliance.org/resource-hub/lorawantm-specification-v11) specification.([Milestone 5.0.0](https://github.com/Lora-net/LoRaMac-node/milestone/3))
+
+* The [master](https://github.com/Lora-net/LoRaMac-node/tree/master) branch implementation is based on [LoRaWAN Specification v1.0.3](https://lora-alliance.org/resource-hub/lorawantm-specification-v103) and [LoRaWAN Regional Parameters v1.0.3revA](https://www.lora-alliance.org/resource-hub/lorawantm-regional-parameters-v103reva) specifications.
+ClassA, ClassB and ClassC end-device classes are fully implemented.
+
+* The [develop](https://github.com/Lora-net/LoRaMac-node/tree/develop) branch implementation is based on [LoRaWAN Specification v1.0.3](https://lora-alliance.org/resource-hub/lorawantm-specification-v103) and [LoRaWAN Regional Parameters v1.0.3revA](https://lora-alliance.org/resource-hub/lorawan-regional-parameters-v103reva) specifications.
+ClassA, ClassB and ClassC end-device classes are fully implemented.
+
+* The [feature/5.0.0](https://github.com/Lora-net/LoRaMac-node/tree/feature/5.0.0) branch implementation is based on [LoRaWAN Specification v1.1](https://lora-alliance.org/resource-hub/lorawantm-specification-v11) and [LoRaWAN Regional Parameters v1.1rB](https://lora-alliance.org/resource-hub/lorawantm-regional-parameters-v11rb) specifications.
+ClassA, ClassB and ClassC end-device classes are fully implemented.
+
+This project also provides SX1272/73, SX1276/77/78/79 and SX1261/2 radio drivers.
+
+For each currently supported platform example applications are provided.
+
+* **LoRaMac/classA**: ClassA end-device example application.
+
+* **LoRaMac/classB**: ClassB end-device example application.
+
+* **LoRaMac/classC**: ClassC end-device example application.
+
+* **LoRaMac/fuota-test-01**: FUOTA test scenario 01 end-device example application. (Based on provided application common packages)
+
+* **LoRaMac/periodic-uplink-lpp**: ClassA/B/C end-device example application. Periodically uplinks a frame using the Cayenne LPP protocol. (Based on provided application common packages)
+
+* **ping-pong**: Point to point RF link example application.
+
+* **rx-sensi**: Example application useful to measure the radio sensitivity level using an RF generator.
+
+* **tx-cw**: Example application to show how to generate an RF Continuous Wave transmission.
+
+**Note**: *Each LoRaWAN application example (LoRaMac/classX) includes an implementation of the LoRa-Alliance; LoRaWAN certification protocol.*
+
+**Note**: *The LoRaWAN stack API documentation can be found at: http://stackforce.github.io/LoRaMac-doc/*
+
+## Supported platforms
+
+This project currently provides support for the below platforms.
+This project can be ported to other platforms using different MCU than the ones currently supported.
+The [Porting Guide](http://stackforce.github.io/LoRaMac-doc/_p_o_r_t_i_n_g__g_u_i_d_e.html) document provides guide lines on how to port the project to other platforms.
+
+* NAMote72
+ * [NAMote72 platform documentation](Doc/NAMote72-platform.md)
+
+* NucleoLxxx - Discovery kit
+ * [NucleoLxxx and Discovery kit platforms documentation](Doc/NucleoLxxx-platform.md)
+
+* SKiM880B, SKiM980A, SKiM881AXL
+ * [SKiM88xx platforms documentation](Doc/SKiM88xx-platform.md)
+
+* SAML21
+ * [SAML21 platform documentation](Doc/SAML21-platform.md)
+
+## Usage
+
+A CMAKE building system is used in order to generate the right set of files to compile and debug the different projects.
+
+Further information can be found in [Development environment](Doc/development-environment.md) document.
+
+## Acknowledgments
+
+The mbed (https://mbed.org/) project was used at the beginning as source of
+inspiration.
+
+This program uses the AES algorithm implementation (http://www.gladman.me.uk/) by Brian Gladman.
+
+This program uses the CMAC algorithm implementation
+(http://www.cse.chalmers.se/research/group/dcs/masters/contikisec/) by Lander Casado, Philippas Tsigas.
+
+## Changelog
+
+### 2019-07-19, V4.4.2
+
+* General
+ 1. Release based on "LoRaWAN specification 1.0.3" and "LoRaWAN Regional Parameters v1.0.3revA"
+ 2. Examples application refactoring plus the addition of the application status display on the serial port. (921600-8-N-1)
+ 3. Added new refactored application examples
+ * Added LoRa-Alliance defined application layer protocols support. Only FUOTA test scenario 01 required features are currently implemented.
+ - "Clock Synchronization" package
+ - "Fragmented data block transport" package
+ - "Remote multicast setup" package
+ 4. Added a NVM context management module. (Disabled by default)
+ 5. Added secure-element support
+ 6. Refactored timer.c/h, rtc-driver.c/h and added a systime.c/h module.
+ 7. Functions in ISR context have been moved to main context
+ 8. Replaced BoardDisableIrq and BoardEnableIrq functions by CRITICAL_SECTION_BEGIN and CRITICAL_SECTION_END respectively.
+ 9. Added B-L072Z-LRWAN1 platform support.
+ 10. Added NucleoL476 platform support.
+ 11. Added IMST new platforms.
+ 12. Removed SensorNode platform support
+ 13. Removed MoteII platform support
+ 14. Removed LoRaMote platform support
+ 15. Applied SX1272 and SX1276 radios errata note 3.1 to the radio drivers implementation.
+ 16. Fix printf/scanf functions when GCC is used as compiler.
+
+* LoRaWAN
+ 1. GitHub reported issues corrections. Please refer to [Release Version 4.4.2](https://github.com/Lora-net/LoRaMac-node/milestone/2)
+ 2. Heavily refactored the LoRaMac.c/h implementation.
+ 3. Added RU864 region support.
+ 4. Removed US915-Hybrid region support
+ 5. Added ClassB support.
+ 6. Added a callback to notify the upper layer to call LoRaMacProcess function
+ 7. Implemented the support for RxC windows (ClassC) required by the application layer protocols.
+ 8. Changed multicast channels handling according to the application layer protocols.
+
+**LoRaWAN pre-certification results**
+
+Please refer to [Releases pre-certification-results](https://github.com/Lora-net/LoRaMac-node/wiki/releases-pre-certification-results) document for further information.
+
+### 2018-03-07, V4.4.1
+
+* General
+ 1. Release based on "LoRaWAN specification 1.0.2" and "LoRaWAN Regional Parameters v1.0.2rB"
+ 2. Added SX126x radio support.
+ 3. Added NucleoL073 and NucleoL152 board platforms support.
+ 4. Added Microchip/Atmel SAML21 Xplained Pro hardware platform support.
+ 5. Added CMAKE build system support
+ 6. Removed CoIDE projects support
+ 7. Removed Keil projects support
+
+* LoRaWAN
+ 1. GitHub reported issues corrections. Please refer to [Release Version 4.4.1](https://github.com/Lora-net/LoRaMac-node/milestone/1)
+
+**LoRaWAN pre-certification results**
+
+Please refer to [Releases pre-certification-results](https://github.com/Lora-net/LoRaMac-node/wiki/releases-pre-certification-results) document for further information.
+
+### 2017-09-08, V4.4.0
+
+* General
+ 1. First release based on "LoRaWAN specification 1.0.2" and "LoRaWAN Regional Parameters v1.0.2rB"
+ 2. This version has passed the LoRa-Alliance compliance tests for the regions shown in the below table.
+ 3. GitHub reported issues corrections.
+ 4. Updated radio driver to perform the LBT carrier sense continuously for a given period of time.
+
+* LoRaWAN
+ 1. GitHub reported issues corrections.
+ 2. Updated implementation to support 1.0.2 specification additions.
+ 3. Added the support for all "LoRaWAN Regional Parameters v1.0.2rB" document defined regions.
+
+**LoRaWAN pre-certification results**
+
+Please refer to [Releases pre-certification-results](https://github.com/Lora-net/LoRaMac-node/wiki/releases-pre-certification-results) document for further information.
+
+### 2017-04-19, V4.3.2
+
+* General (Last release based on LoRaWAN specification 1.0.1)
+ 1. This version has passed EU868 and US915 LoRa-Alliance compliance tests.
+ 2. GitHub reported issues corrections.
+ 3. Added an algorithm to automatically compute the Rx windows parameters. (Window symbolTimeout and Offset from downlink expected time)
+ 4. Added a workaround to reset the radio in case a TxTimeout occurs.
+ 5. Modified FSK modem handling to use the provided symbolTimeout (1 symbol equals 1 byte) when in RxSingle mode.
+ 6. Added newly defined TxCw(Tx Continuous Wave) certification protocol command.
+ 7. Added a fix for an overflow issue that could happen with NmeaStringSize variable.
+ 8. Improved GpioMcuInit function to first configure the output pin state before activating the pin.
+
+* LoRaWAN
+ 1. GitHub reported issues corrections.
+ 2. Changed the AdrAckCounter handling as expected by the test houses.
+ 3. Fix an issue where the node stopped transmitting.
+ 4. Removed useless LoRaMacPayload buffer.
+ 5. MAC layer indications handling simplification.
+ 6. Relocate parameter settings from ResetMacParameters to the initialization.
+
+**LoRaWAN pre-certification results**
+
+Please refer to [Releases pre-certification-results](https://github.com/Lora-net/LoRaMac-node/wiki/releases-pre-certification-results) document for further information.
+
+### 2017-02-27, V4.3.1
+
+* General
+ 1. This version has passed EU868 and US915 LoRa-Alliance compliance tests.
+ 2. Update the MAC layer in order to be LoRaWAN version 1.0.1 compliant (Mainly US915 bug fixes)
+ 3. Removed api-v3 support from the project.
+ 4. GitHub reported issues corrections.
+ 5. Updated SensorNode projects according to the new MCU reference STM32L151CBU6. Bigger memories.
+ 6. Addition of MoteII platform based on the IMST module iM881A (STM32L051C8)
+ 7. Addition of NAMote72 platform
+ 8. Correct compliance test protocol command 0x06 behavior
+ 9. Added TxCw (Tx continuous wave) LoRaWAN compliance protocol command.
+ 10. Added TxContinuousWave support to the radio drivers.
+ 11. Updated ST HAL drivers.
+ * STM32L1xx_HAL-Driver : V1.2.0
+ * STM32L0xx_HAL_Driver : V1.7.0
+
+* LoRaWAN
+ 1. US band corrections in order to pass the LoRaWAN certification.
+ 2. GitHub reported issues corrections.
+ 3. Add region CN470 support.
+
+**LoRaWAN pre-certification results**
+
+Please refer to [Releases pre-certification-results](https://github.com/Lora-net/LoRaMac-node/wiki/releases-pre-certification-results) document for further information.
+
+### 2016-06-22, V4.3.0
+
+* General
+ 1. This version has passed all LoRa-Alliance compliance tests.
+ 2. Update the MAC layer in order to be LoRaWAN version 1.0.1 compliant
+ 3. Applied to all application files the certification protocol change for LoRaWAN 1.0.1 compliance tests.
+
+ **REMARK**: api-v3 application files aren't updated.
+
+ 4. Add radio RX_TIMEOUT irq clear into the irq handler.
+ 5. Removed the end less loop from HAL_UART_ErrorCallback.
+ 6. Update of the STM32L0 HAL to version 1.6.0
+ 7. Consolidated the line endings across all project files.
+ Windows line endings has been choose for almost every file.
+
+* LoRaWAN
+ 1. Updated maximum payload size for US band.
+ 2. Update datarate offset table for US band.
+ 3. Make MAC commands sticky
+ 4. Add retransmission back-off
+ 5. Remove the TxPower limitation for US band on LoRaMacMibSetRequestConfirm function. The power will be limited anyway when the SendFrameOnChannel functions is called.
+ 6. Issue(#81): Bug fix in function LoRaMacMlmeRequest case MLME_JOIN. Function will return LORAMAC_STATUS_BUSY in case the MAC is in status MAC_TX_DELAYED.
+ 7. Add debug pin support to LoRaMote platform.
+ 8. Updated and improved MPL3115 device driver.
+ 9. Issue(#83): Bug fix in parameter validation
+ 10. Issue(#84): Fix issue of CalibrateTimer function.
+ 11. RTC driver major update
+ 12. Applied pull request #87.
+ 13. Add a function to verify the RX frequency of window 2 for US band.
+ 14. Issue(#88): Bug fix in function PrepareFrame where repeated MAC commands were not handled correctly.
+ 15. Bug fix in OnRadioRxDone. Node now drops frames on port 0 with fOpts > 0.
+ 16. Bug fix in OnRadioRxDone. Node now receives frames with fOpts > 0 when no payload is present.
+
+**LoRaWAN pre-certification results**
+
+Please refer to [Releases pre-certification-results](https://github.com/Lora-net/LoRaMac-node/wiki/releases-pre-certification-results) document for further information.
+
+### 2016-05-13, V4.2.0
+
+* General
+ 1. This version has passed all LoRa-Alliance compliance tests.
+ 2. Update STM32L1xx_HAL_Driver version to 1.5. Update related drivers and implementations accordingly.
+
+ **REMARK**: This change implies that the time base had to be changed from microseconds to milliseconds.
+
+ 3. Corrected the frequency check condition for // ERRATA 2.1 - Sensitivity Optimization with a 500 kHz Bandwidth
+ 4. Optimize radio drivers regarding FSK PER
+ 5. Resolve issue when calling SX127xInit function more than once
+ 6. Add a definition for the LoRaWAN device address. Add an IEEE_OUI for the LoRaWAN device EUI.
+ 7. Add a definition for the default datarate.
+ 8. Issue(#66) correction of functions SX1276SetOpMode and SX1272SetOpMode.
+ 9. Issue(#68): Fix for low level RF switch control.
+ 10. Increase RTC tick frequency for higher resolution.
+ 11. Update the radio wake up time.
+
+* LoRaWAN
+ 1. Issue(#56) correction
+ 2. Update channel mask handling for US915 hybrid mode to support any block in the channel mask.
+ 3. Issue(#63) correct the maximum payload length in RX mode.
+ 4. Fix Tx power setting loss for repeated join requests on US band.
+ 5. Introduce individual MIN and MAX datarates for RX and TX.
+ 6. Add the possibility to set and get the ChannelsDefaultDatarate.
+ 7. Optimization of the RX symbol timeout.
+ 8. Issue(#59): Add the possibility to set the uplink and downlink counter.
+ 9. Replace definition LORAMAC_DEFAULT_DATARATE by ChannelsDefaultDatarate in LoRaMacChannelAdd.
+ 10. Issue(#72): Fix of possible array overrun in LoRaMacChannelRemove.
+ 11. Introduce a new status MAC_RX_ABORT. Reset MAC_TX_RUNNING only in OnMacStateCheckTimerEvent.
+ 12. Accept MAC information of duplicated, confirmed downlinks.
+ 13. Issue(#74): Drop frames with a downlink counter difference greater or equal to MAX_FCNT_GAP.
+
+**LoRaWAN pre-certification results**
+
+Please refer to [Releases pre-certification-results](https://github.com/Lora-net/LoRaMac-node/wiki/releases-pre-certification-results) document for further information.
+
+### 2016-03-10, V4.1.0
+
+* General
+ 1. This version has passed all mandatory LoRa-Alliance compliance tests.
+
+ *One of the optional tests is unsuccessful (FSK downlinks PER on Rx1 and Rx2 windows) and is currently under investigation.*
+ 2. Removed support for Raisonance Ride7 IDE (Reduces the amount of work to be done at each new release)
+ 3. Removed the Bleeper-72 and Bleeper-76 platforms support as these are now deprecated.
+ 4. Application state machine. Relocate setting sleep state and update the duty cycle in compliance test mode.
+ 5. Bug fix in TimerIrqHandler. Now, it is possible to insert timers in callback.
+ 6. Changed TimerHwDelayMs function to be re-entrant.
+ 7. Corrected FSK modem packets bigger than 64 bytes handling (Issue #36)
+
+* LoRaWAN
+ 1. Rename attribute nbRetries to NbTrials in structure McpsReqConfirmed_t. (Issue #37)
+ 2. Updated implementation of SetNextChannel. Added enabling default channels in case of join request. (Issue #39)
+ 3. Add missing documentation about MIB_REPEATER_SUPPORT. (Issue #42).
+ 4. Add a new LoRaMacState to allow adding channels during TX procedure. (Issue #43)
+ 5. Relocate the activation of LoRaMacFlags.Bits.McpsInd in OnRadioRxDone.
+ 6. Add a new function PrepareRxDoneAbort to prepare a break-out of OnRadioRxDone in case of an error
+ 7. Activate default channels in case all others are disabled. (Issue #39)
+ 8. Bug fix in setting the default channel in case none is enabled.
+ 9. SRV_MAC_NEW_CHANNEL_REQ MAC command added a fix to the macIndex variable on US915 band.
+ 10. Start the MacStateCheckTimer in OnRxDone and related error cases with a short interval to handle events promptly. (Issue #44)
+ 11. Reset status of NodeAckRequested if we received an ACK or in case of timeout.
+ 12. Removed additional EU868 channels from the LoRaWAN implementation files. GitHub (Issue #49)
+ The creation of these additional channels has been moved to the application example.
+ 13. Improved and corrected AdrNextDr function.
+
+### 2015-12-18, V4.0.0
+
+* General
+ 1. STACKFORCE new API integration
+ 2. Reverse the EUIs arrays in the MAC layer.
+ 3. LoRaWAN certification protocol implementation
+ 4. All reported issues and Pull requests have been addressed.
+
+### 2015-10-06, V3.4.1
+
+* General
+ 1. Bug fixes
+
+* LoRaWAN
+ 1. Corrected downlink counter roll over management when several downlinks were missed.
+ 2. Corrected the Radio maximum payload length management. Radio was filtering received frames with a length bigger than the transmitted one.
+ 3. Applied Pull request #22 solution proposition.
+
+### 2015-10-30, V3.4.0
+
+* General
+ 1. Changed all applications in order to have preprocessing definitions on top of the files and added relevant comments
+ 2. Applications LED control is no more done into the timer callback functions but instead on the main while loop.
+ 3. Added TimerStop function calls to each timer event callback.
+ 4. Corrected timings comments. Timing values are most of the time us based.
+ 5. Changed types names for stdint.h names. Helps on code portability
+ 6. Renamed rand and srand to rand1 and srand1. Helps on code portability
+ 7. Added some missing variables casts. Helps on code portability
+ 8. Removed NULL definition from board.h
+ 9. Added const variable attribute when necessary to functions prototypes
+ 10. Moved ID1, ID2 and ID3 definition from board.h to board.c, usb-cdc-board.c and usb-dfu-board.c
+ 11. Removed the definition of RAND_SEED. It has been replaced by a function named BoardGetRandomSeed
+ 12. Renamed BoardMeasureBatterieLevel to BoardGetBatteryLevel
+ 13. Added SetMaxPayloadLength API function to SX1272 and SX1276 radio drivers
+ 14. Changed the name of Radio API Status function to GetStatus
+ 15. AES/CMAC Changed types names for stdint.h names. Helps on code portability (Issue #20)
+ 16. Moved __ffs function from utilities.h to spi-board.c. This function is only used there.
+ 17. Utilities.c removed fputc function redefinition.
+ 18. Replaced the usage of __IO attribute by volatile.
+
+* LoRaWAN
+ 1. Added support for the US915 band (Normal mode and hybrid mode. Hybrid mode is a temporary configuration up until servers support it automatically) (Issue #16)
+ 2. Corrected and simplified the downlink sequence counter management.
+ 3. Removed the usage of PACKED attribute for data structures.
+ 4. Renamed LoRaMacEvent_t into LoRaMacCallbacks_t and added a function pointer for getting battery level status
+ 5. Renamed LoRaMacSetDutyCycleOn into LoRaMacSetTestDutyCycleOn
+ 6. Renamed LoRaMacSetMicTest into LoRaMacTestSetMic
+ 7. Increased the PHY buffer size to 250
+ 8. Removed IsChannelFree check on LoRaMacSetNextChannel function. LoRaWAN is an ALHOA protocol. (Pull request #8)
+ 9. LoRaMacEventInfo.TxDatarate now returns LoRaWAN datarate (DR0 -> DR7) instead of (SF12 -> DF7)
+ 10. Corrected channel mask management for EU868 band.
+ 11. Corrected LoRaMacPrepareFrame behavior function when no applicative payload is present.
+ 12. LoRaMac-board.h now implements the settings for the PHY layers specified by LoRaWAN 1.0 specification. ( EU433, CN780, EU868, US915 ) (Issue #19)
+ 13. Added LORAMAC_MIN_RX1_DR_OFFSET and LORAMAC_MAX_RX1_DR_OFFSET definitions to LoRaMac-board.h. Can be different upon used PHY layer
+ 14. Added the limitation of output power according to the number of enabled channels for US915 band.
+ 15. Added the limitation of the applicative payload length according to the datarate. Does not yet take in account the MAC commands buffer. (Issue #15)
+ 16. Corrected MacCommandBufferIndex management. (Issue #18)
+
+### 2015-08-07, v3.3.0
+
+* General
+ 1. Added the support for LoRaWAN Class C devices.
+ 2. Implemented the radios errata note workarounds. SX1276 errata 2.3 "Receiver Spurious Reception of a LoRa Signal" is not yet implemented.
+ 3. Increased FSK SyncWord timeout value in order to listen for longer time if a down link is available or not. Makes FSK downlink more reliable.
+ 4. Increased the UART USB FIFO buffer size in order to handle bigger chunks of data.
+
+* LoRaWAN
+ 1. Renamed data rates as per LoRaWAN specification.
+ 2. Added the support for LoRaWAN Class C devices.
+ 3. Handling of the MAC commands was done incorrectly the condition to verify the length of the buffer has changed from < to <=.
+ 4. Added the possibility to change the channel mask and number of repetitions trough SRV_MAC_LINK_ADR_REQ command when ADR is disabled.
+ 5. Corrected Rx1DrOffset management. In previous version DR1 was missing for all offsets.
+ 6. Changed confirmed messages function to use default datarate when ADR control is off.
+ 7. After a Join accept the node falls back to the default datarate. Enables the user to Join a network using a different datarate from its own default one.
+ 8. Corrected default FSK channel frequency.
+ 9. Solved a firmware freezing when one of the following situations arrived in OnRxDone callback: bad address, bad MIC, bad frame. (Pull request #10)
+ 10. Moved the MAC commands processing to the right places. FOpts field before the Payload and Port 0 just after the decryption. (Pull request #9)
+ 11. Weird conditions to check datarate on cmd mac SRV_MAC_NEW_CHANNEL_REQ (Pull request #7)
+ 12. Ignore join accept message if already joined (Pull request #6)
+ 13. Channel index verification should use OR on SRV_MAC_NEW_CHANNEL_REQ command (Pull request #5)
+ 14. Corrected the CFList management on JoinAccept. The for loop indexes were wrong. (Pull request #4)
+ 15. Correction of AES key size (Pull request #3)
+
+### 2015-04-30, v3.2.0
+
+* General
+ 1. Updated LoRaMac implementation according to LoRaWAN R1.0 specification
+ 2. General cosmetics corrections
+ 3. Added the support of packed structures when using IAR tool chain
+ 4. Timers: Added a function to get the time in us.
+ 5. Timers: Added a typedef for time variables (TimerTime_t)
+ 6. Radios: Changed the TimeOnAir radio function to return a uint32_t value instead of a double. The value is in us.
+ 7. Radios: Corrected the 250 kHz bandwidth choice for the FSK modem
+ 8. GPS: Added a function that returns if the GPS has a fix or not.
+ 9. GPS: Changed the GetPosition functions to return a latitude and longitude of 0 and altitude of 65535 when no GPS fix.
+
+* LoRaWAN
+ 1. Removed support for previous LoRaMac/LoRaWAN specifications
+ 2. Added missing MAC commands and updated others when necessary
+ * Corrected the Port 0 MAC commands decryption
+ * Changed the way the upper layer is notified. Now it is only notified
+ when all the operations are finished.
+
+ When a ClassA Tx cycle starts a timer is launched to check every second if everything is finished.
+
+ * Added a new parameter to LoRaMacEventFlags structure that indicates on which Rx window the data has been received.
+ * Added a new parameter to LoRaMacEventFlags structure that indicates if there is applicative data on the received payload.
+ * Corrected ADR MAC command behavior
+ * DutyCycle enforcement implementation (EU868 PHY only)
+
+ **REMARK 1** *The regulatory duty cycle enforcement is enabled by default
+ which means that for lower data rates the node may not transmit a new
+ frame as quickly as requested.
+ The formula used to compute the node idle time is*
+
+ *Toff = TimeOnAir / DutyCycle - TxTimeOnAir*
+
+ *Example:*
+ *A device just transmitted a 0.5 s long frame on one default channel.
+ This channel is in a sub-band allowing 1% duty-cycle. Therefore this
+ whole sub-band (868 MHz - 868.6 MHz) will be unavailable for 49.5 s.*
+
+ **REMARK 2** *The duty cycle enforcement can be disabled for test
+ purposes by calling the LoRaMacSetDutyCycleOn function with false
+ parameter.*
+ * Implemented aggregated duty cycle management
+ * Added a function to create new channels
+ * Implemented the missing features on the JoinAccept MAC command
+ * Updated LoRaMacJoinDecrypt function to handle the CFList field.
+ 3. Due to duty cycle management the applicative API has changed.
+ All applications must be updated accordingly.
+ 4. Added the possibility to chose to use either public or private networks
+
+### 2015-01-30, v3.1.0
+
+* General
+ 1. Started to add support for CooCox CoIDE Integrated Development Environment.
+ Currently only LoRaMote and SensorNode platform projects are available.
+ 2. Updated GCC compiler linker scripts.
+ 3. Added the support of different tool chains for the HardFault_Handler function.
+
+ 4. Corrected Radio drivers I&Q signals inversion to be possible in Rx and in Tx.
+ Added some missing radio state machine initialization.
+ 5. Changed the RSSI values type from int8_t to int16_t. We can have RSSI values below -128 dBm.
+ 6. Corrected SNR computation on RxDone interrupt.
+ 7. Updated radio API to support FHSS and CAD handling.
+ 8. Corrected in SetRxConfig function the FSK modem preamble register name.
+ 9. Added an invalid bandwidth to the Bandwidths table in order to avoid an error
+ when selecting 250 kHz bandwidth when using FSK modem.
+
+ 10. Corrected RTC alarm setup which could be set to an invalid date.
+ 11. Added another timer in order increment the tick counter without blocking the normal timer count.
+ 12. Added the possibility to switch between low power timers and normal timers on the fly.
+ 13. I2C driver corrected the 2 bytes internal address management.
+ Corrected buffer read function when more that 1 byte was to be read.
+ Added a function to wait for the I2C bus to become IDLE.
+ 14. Added an I2C EEPROM driver.
+ 15. Corrected and improved USB Virtual COM Port management files.
+ Corrected the USB CDC and USB UART drivers.
+ 16. Added the possibility to analyze a hard fault interrupt.
+
+* LoRaMac
+ 1. Corrected RxWindow2 Datarate management.
+ 2. SrvAckRequested variable was never reset.
+ 3. Corrected tstIndoor applications for LoRaMac R3.0 support.
+ 4. LoRaMac added the possibility to configure almost all the MAC parameters.
+ 5. Corrected the LoRaMacSetNextChannel function.
+ 6. Corrected the port 0 MAC command decoding.
+ 7. Changed all structures declarations to be packed.
+ 8. Corrected the Acknowledgment retries management when only 1 trial is needed.
+ Before the device was issuing at least 2 trials.
+ 9. Corrected server mac new channel req answer.
+ 10. Added the functions to read the Up and Down Link sequence counters.
+ 11. Corrected SRV_MAC_RX2_SETUP_REQ frequency handling. Added a 100 multiplication.
+ 12. Corrected SRV_MAC_NEW_CHANNEL_REQ. Removed the DutyCycle parameter decoding.
+ 13. Automatically activate the channel once it is created.
+ 14. Corrected NbRepTimeoutTimer initial value. RxWindow2Delay already contains RxWindow1Delay in it.
+
+### 2014-07-18, v3.0.0
+
+* General
+ 1. Added to Radio API the possibility to select the modem.
+ 2. Corrected RSSI reading formulas as well as changed the RSSI and SNR values from double to int8_t type.
+ 3. Changed radio callbacks events to timeout when it is a timeout event and error when it is a CRC error.
+ 4. Radio API updated.
+ 5. Updated ping-pong applications.
+ 6. Updated tx-cw applications.
+ 7. Updated LoRaMac applications in order to handle LoRaMac returned functions calls status.
+ 8. Updated LoRaMac applications to toggle LED2 each time there is an application payload down link.
+ 9. Updated tstIndoor application to handle correctly more than 6 channels.
+ 10. Changed the MPL3115 altitude variable from unsigned to signed value.
+ 11. Replaced the usage of pow(2, n) by defining POW2 functions. Saves ~2 KBytes of code.
+ 12. Corrected an issue potentially arriving when LOW_POWER_MODE_ENABLE wasn't defined.
+ A timer interrupt could be generated while the TimerList could already be emptied.
+
+* LoRaMac
+ 1. Implemented LoRaMac specification R3.0 changes.
+
+ 2. MAC commands implemented
+ * LinkCheckReq **YES**
+ * LinkCheckAns **YES**
+ * LinkADRReq **YES**
+ * LinkADRAns **YES**
+ * DutyCycleReq **YES**
+ * DutyCycleAns **YES**
+ * Rx2SetupReq **YES**
+ * Rx2SetupAns **YES**
+ * DevStatusReq **YES**
+ * DevStatusAns **YES**
+ * JoinReq **YES**
+ * JoinAccept **YES**
+ * NewChannelReq **YES**
+ * NewChannelAns **YES**
+
+ 3. Features implemented
+ * Possibility to shut-down the device **YES**
+
+ Possible by issuing DutyCycleReq MAC command.
+ * Duty cycle management enforcement **NO**
+ * Acknowledgments retries **YES**
+ * Unconfirmed messages retries **YES**
+
+### 2014-07-10, v2.3.RC2
+
+* General
+ 1. Corrected all radios antenna switch low power mode handling.
+ 2. SX1276: Corrected antenna switch control.
+
+### 2014-06-06, v2.3.RC1
+
+* General
+ 1. Added the support for SX1276 radio.
+ 2. Radio continuous reception mode correction.
+ 3. Radio driver RxDone callback function API has changed ( size parameter is no more a pointer).
+ Previous function prototype:
+
+ void ( *RxDone )( uint8_t *payload, uint16_t *size, double rssi, double snr, uint8_t rawSnr );
+
+ New function prototype:
+
+ void ( *RxDone )( uint8_t *payload, uint16_t size, double rssi, double snr, uint8_t rawSnr );
+
+ 4. Added Bleeper-76 and SensorNode platforms support.
+ 5. Added to the radio drivers a function that generates a random value from
+ RSSI readings.
+ 6. Added a project to transmit a continuous wave and a project to measure the
+ the radio sensitivity.
+ 7. Added a bootloader project for the LoRaMote and SensorNode platforms.
+ 8. The LoRaMac application for Bleeper platforms now sends the Selector and LED status plus the sensors values.
+ * The application payload for the Bleeper platforms is as follows:
+
+ LoRaMac port 1:
+
+ { 0xX0/0xX1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
+ ---------- ---------- ---------- ---------- ----
+ | | | | |
+ SELECTOR/LED PRESSURE TEMPERATURE ALTITUDE BATTERY
+ MSB nibble = SELECTOR (barometric)
+ LSB bit = LED
+ 9. Redefined rand() and srand() standard C functions. These functions are
+ redefined in order to get the same behavior across different compiler
+ tool chains implementations.
+ 10. GPS driver improvements. Made independent of the board platform.
+ 11. Simplified the RTC management.
+ 12. Added a function to the timer driver that checks if a timer is already in
+ the list or not.
+ 13. Added the UART Overrun bit exception handling to the UART driver.
+ 14. Removed dependency of spi-board files to the "__builtin_ffs" function.
+ This function is only available on GNU compiler tool suite. Removed --gnu
+ compiler option from Keil projects. Added own __ffs function
+ implementation to utilities.h file.
+ 15. Removed obsolete class1 devices support.
+ 16. Known bugs correction.
+
+* LoRaMac
+ 1. MAC commands implemented
+ * LinkCheckReq **YES**
+ * LinkCheckAns **YES**
+ * LinkADRReq **YES**
+ * LinkADRAns **YES**
+ * DutyCycleReq **YES** (LoRaMac specification R2.2.1)
+ * DutyCycleAns **YES** (LoRaMac specification R2.2.1)
+ * Rx2SetupReq **YES** (LoRaMac specification R2.2.1)
+ * Rx2SetupAns **YES** (LoRaMac specification R2.2.1)
+ * DevStatusReq **YES**
+ * DevStatusAns **YES**
+ * JoinReq **YES**
+ * JoinAccept **YES** (LoRaMac specification R2.2.1)
+ * NewChannelReq **YES** (LoRaMac specification R2.2.1)
+ * NewChannelAns **YES** (LoRaMac specification R2.2.1)
+ 2. Features implemented
+ * Possibility to shut-down the device **YES**
+
+ Possible by issuing DutyCycleReq MAC command.
+ * Duty cycle management enforcement **NO**
+ * Acknowledgments retries **WORK IN PROGRESS**
+
+ Not fully debugged. Disabled by default.
+ * Unconfirmed messages retries **WORK IN PROGRESS** (LoRaMac specification R2.2.1)
+ 3. Implemented LoRaMac specification R2.2.1 changes.
+ 4. Due to new specification the LoRaMacInitNwkIds LoRaMac API function had
+ to be modified.
+
+ Previous function prototype:
+
+ void LoRaMacInitNwkIds( uint32_t devAddr, uint8_t *nwkSKey, uint8_t *appSKey );
+
+ New function prototype:
+
+ void LoRaMacInitNwkIds( uint32_t netID, uint32_t devAddr, uint8_t *nwkSKey, uint8_t *appSKey );
+ 5. Changed the LoRaMac channels management.
+ 6. LoRaMac channels definition has been moved to LoRaMac-board.h file
+ located in each specific board directory.
+
+### 2014-04-07, v2.2.0
+
+* General
+ 1. Added IMST SK-iM880A starter kit board support to the project.
+ * The application payload for the SK-iM880A platform is as follows:
+
+ LoRaMac port 3:
+
+ { 0x00/0x01, 0x00, 0x00, 0x00 }
+ ---------- ----- ----------
+ | | |
+ LED POTI VDD
+ 2. Ping-Pong applications have been split per supported board.
+ 3. Corrected the SX1272 output power management.
+ Added a variable to store the current Radio channel.
+ Added missing FSK bit definition.
+ 4. Made fifo functions coding style coherent with the project.
+ 5. UART driver is now independent of the used MCU
+
+### 2014-03-28, v2.1.0
+
+* General
+ 1. The timers and RTC management has been rewritten.
+ 2. Improved the UART and UP501 GPS drivers.
+ 3. Corrected GPIO pin names management.
+ 4. Corrected the antenna switch management in the SX1272 driver.
+ 5. Added to the radio driver the possibility to choose the preamble length
+ and rxSingle symbol timeout in reception.
+ 6. Added Hex coder selector driver for the Bleeper board.
+ 7. Changed copyright Unicode character to (C) in all source files.
+
+* LoRaMac
+ 1. MAC commands implemented
+ * LinkCheckReq **YES**
+ * LinkCheckAns **YES**
+ * LinkADRReq **YES**
+ * LinkADRAns **YES**
+ * DevStatusReq **YES**
+ * DevStatusAns **YES**
+ * JoinReq **YES**
+ * JoinAccept **YES**
+ 2. Added acknowledgments retries management.
+ Split the LoRaMacSendOnChannel function in LoRaMacPrepareFrame and
+ LoRaMacSendFrameOnChannel. LoRaMacSendOnChannel now calls the 2 newly
+ defined functions.
+ **WARNING**: By default the acknowledgment retries specific code isn't
+ enabled. The current http://iot.semtech.com server version doesn't support
+ it.
+ 3. Corrected issues on JoinRequest and JoinAccept MAC commands.
+ Added LORAMAC_EVENT_INFO_STATUS_MAC_ERROR event info status.
+
+### 2014-02-21, v2.0.0
+
+* General
+ 1. The LoRaMac applications now sends the LED status plus the sensors values.
+ For the LoRaMote platform the application also sends the GPS coordinates.
+ * The application payload for the Bleeper platform is as follows:
+
+ LoRaMac port 1:
+
+ { 0x00/0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
+ ---------- ---------- ---------- ---------- ----
+ | | | | |
+ LED PRESSURE TEMPERATURE ALTITUDE BATTERY
+ (barometric)
+ * The application payload for the LoRaMote platform is as follows:
+
+ LoRaMac port 2:
+
+ { 0x00/0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
+ ---------- ---------- ---------- ---------- ---- ---------------- ---------------- ----------
+ | | | | | | | |
+ LED PRESSURE TEMPERATURE ALTITUDE BATTERY LATITUDE LONGITUDE ALTITUDE
+ (barometric) (gps)
+ 2. Adapted applications to the new MAC layer API.
+ 3. Added sensors drivers implementation.
+ 4. Corrected new or known issues.
+* LoRaMac
+ 1. MAC commands implemented
+ * LinkCheckReq **YES**
+ * LinkCheckAns **YES**
+ * LinkADRReq **YES**
+ * LinkADRAns **YES**
+ * DevStatusReq **YES**
+ * DevStatusAns **YES**
+ * JoinReq **YES (Not tested)**
+ * JoinAccept **YES (Not tested)**
+ 2. New MAC layer application API implementation.
+* Timers and RTC.
+ 1. Still some issues. They will be corrected on next revisions of the firmware.
+
+### 2014-01-24, v1.1.0
+
+* LoRaMac
+ 1. MAC commands implemented
+ * LinkCheckReq **NO**
+ * LinkCheckAns **NO**
+ * LinkADRReq **YES**
+ * LinkADRAns **YES**
+ * DevStatusReq **YES**
+ * DevStatusAns **YES**
+ 2. Implemented an application LED control
+ If the server sends on port 1 an application payload of one byte with
+ the following contents:
+
+ 0: LED off
+ 1: LED on
+ The node transmits periodically on port 1 the LED status on 1st byte and
+ the message "Hello World!!!!" the array looks like:
+
+ { 0, 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!', '!', '!', '!' }
+* Timers and RTC.
+ 1. Corrected issues existing in the previous version
+ 2. Known bugs are:
+ * There is an issue when launching an asynchronous Timer. Will be solved
+ in a future version
+
+### 2014-01-20, v1.1.RC1
+
+* Added Doc directory. The directory contains:
+ 1. LoRa MAC specification
+ 2. Bleeper board schematic
+* LoRaMac has been updated according to Release1 of the specification. Main changes are:
+ 1. MAC API changed.
+ 2. Frame format.
+ 3. ClassA first ADR implementation.
+ 4. MAC commands implemented
+ * LinkCheckReq **NO**
+ * LinkCheckAns **NO**
+ * LinkADRReq **YES**
+ * LinkADRAns **NO**
+ * DevStatusReq **NO**
+ * DevStatusAns **NO**
+* Timers and RTC rewriting. Known bugs are:
+ 1. The Radio wakeup time is taken in account for all timings.
+ 2. When opening the second reception window the microcontroller sometimes doesn't enter in low power mode.
+
+### 2013-11-28, v1.0.0
+
+* Initial version of the LoRa MAC node firmware implementation.
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/st_readme.txt b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/st_readme.txt
new file mode 100644
index 00000000..8cc703e5
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/st_readme.txt
@@ -0,0 +1,212 @@
+ @verbatim
+ ******************************************************************************
+ * @file st_readme.txt
+ * @author MCD Application Team
+ * @brief This file lists the main modification done by STMicroelectronics on
+ * LoRa for integration with STM32Cube solution.
+ * For more details on LoRa implementation on STM32Cube, please refer
+ * to UM2073 "STM32 LORA Expansion Package for STM32Cube "
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under BSD 3-Clause license,
+ * the "License"; You may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ * opensource.org/licenses/BSD-3-Clause
+ *
+ ******************************************************************************
+ @endverbatim
+
+### V1.2.4/28-Nov-2019 ###
+===============================
+ + Implements LoRa Mac from Semtech/StackForce develop branch (31-Oct-2019 commits, version 4.4.2)
+
+ + Patterns/basic and Patterns/modem directory structure change
+
+ + update files license
+
+ + certification obtained with LoRAWAN version v1.0.2 in the following regions:
+ - EU868
+ - IN865
+ - KR920
+ - AS923
+ - US915
+
+
+### V1.2.3/20-Sept-2019 ###
+===============================
+ + Implements LoRa Mac from Semtech/StackForce develop branch (17-Jun-2019 commits, 4.4.2 release candidate)
+ https://github.com/Lora-net/LoRaMac-node/tree/6231a5d65806142a6f887e6183227b8da9e49238
+
+ + Various bug fixes and enhancements
+
+ + Removed inclusions of radio.h, utilities.h, etc. from loraMac.h API
+ + Lora.c/h and lora-test.c/h moved from Certifications/ to Patterns/Basic
+
+### V1.2.2/17-June-2019 ###
+===============================
+ + Trace logging & LoRa Mac version display updates
+ + Some function prototypes added
+
+### V1.2.1/13-December-2018 ###
+===============================
+ + Implements LoRa Mac 4.4.2-rc.5 from Semtech/StackForce develop branch
+
+### V1.2.0/10-July-2018 ###
+===============================
+ + Implements LoRa Mac 4.4.2-rc.1 from Semtech/StackForce develop branch
+
+ + Supports Class B
+ + Features secure element API
+ + Added RU864 region
+ + Removed US915-Hybrid
+
+### V1.1.5/30-March-2018 ###
+===============================
+ + Implements LoRa Mac 4.4.1 from Semtech/StackForce
+
+### V1.1.4/08-January-2018 ###
+===============================
+ + Bug fix in McpsIndication related to downlink Application data buffer
+
+### V1.1.3/20-December-2017 ###
+===============================
+ + Implements LoRa Mac from Semtech/StackForce develop branch (10-Oct-2017 commits, 4.4.1 release candidate)
+ https://github.com/Lora-net/LoRaMac-node/tree/ae0c9cc5d53ebbc11437dfe9711a1c3a1114dc5f
+
+ + Merge pull request #302 from nestorayuso/patch-3
+
+### V1.1.2/08-September-2017 ###
+===============================
+ + Implements LoRa Mac 4.4.0 release from Semtech/StackForce
+ https://github.com/Lora-net/LoRaMac-node/tree/f42be67be402a40b3586724800771bfe13fb18e6
+
+ + AckTimeoutRetriesCounter must be reset every time a new request (unconfirmed or confirmed) is performed.
+ + Added verification for the power parameter in order to check that it is greater or equal to 0.
+
+ + Enhancement for regions without FSK modulation support.
+ + Bug fix for RX window 2 in class c mode.
+ + AdrAckReq bit must be set to false as soon as we reach the lowest datarate.
+ + MacCommandsBufferIndex must be reset when the mac commands are being sent on port 0.
+ + Fixed RegionCommonSetBandTxDone in order to also update band->LastTxDoneTime when performing the Join procedure.
+ + Added verification of payload size for Unconfirmed and Confirmed messages depending on Dwell time.
+
+ + Added missing Rx1 timeout handling.
+ + Updated all regions to use MAX output power by default.
+ + Merge remote-tracking branch 'origin/develop' into develop
+ + Bug fix in KR920 - update the maxEIRP calculation for continuous wave
+ + Bug fix in IN865 - Update the band of the default channels
+ + Bug fix in AS923 - for RX use always the payload limitation of dwell 0
+ + Update function RegionCommonChanVerifyDr. Perform an 'AND' operation for security
+ + Move the verification of ADR parameters into the common section. Update all regions with the related changes
+ + Update comment for function RegionLinkAdrReq
+ + Apply variables to data structure LinkAdrReqParams_t
+ + Bug fix for KR920
+ + Rename data structure LinkAdrParams_t
+ + Issue(#238): Apply missing variable in sSetBandTxDoneParams
+ + Bug fix in processing MAC commands for case SRV_MAC_TX_PARAM_SETUP_REQ.
+ + Isse(#238): Apply missing variable in struct sBand
+ + Update the MAC to enable the server to control the Channels Mask and the number of transmissions even ADR is off
+ + Issue(#238): Update the backoff procedure for all regions. Move code parts into the common section
+ + Update implementation to allow automatic MAC answers on port 0
+ + Issue(#253): Delete all preconfigured channels when performing a join request
+ + Initialize variable phyParam in functions RegionXXGetPhyParam
+ + Remove assert_param from the radio drivers
+ + Synchronize function RegionXXTxConfig
+ + Update carrier sense functionality for LBT
+ + Issue(#259): Update comment for variable HasLoopedThroughMain
+ + Issue(#257): Fix typo in OnRxWindow2TimerEvent
+ + Change scientific notation to numeric notation
+ + Merge pull request #260 from clmklk/AU915
+
+ + AU915: update Datarate limits according to LoRaWan 1.0.2rB
+ + AU915: update Downstream datarate table according to LoRaWan 1.0.2rB
+
+ + Merge pull request #225 from OpenChirp/patch_1
+
+### V1.1.1/01-June-2017 ###
+===============================
+ + Implements LoRa Mac from Semtech/StackForce develop branch (30-May-2017 commits, 4.4.0 release candidate)
+ https://github.com/Lora-net/LoRaMac-node/tree/e2f35db75c1b449379d3b520c2d4e5922a9f5c81
+
+ + Issue(235): Update functions RegionXXNextChannel.
+ + Issue(238): Update initialization value of nextTxDelay.
+ + Issue(234): Report back the aggregated time off.
+ + Issue(232): Relocate function call to CalculateBackOff.
+ + Issue(239): Update band TX power to the maximum.
+ + Update LimitTxPower for US915.
+ + Bug fix in function RegionCN470Verify.
+ + Bug fix in function RegionAU915Verify.
+ + Issue(229): Fix issue when receiving frames in second RX2 in Class C.
+ + Add a ommand to get the next lower datarate.
+ + Group initializations
+ + Update regional definitions of KR920.
+ + Update regional definitions of EU868.
+ + Issue(239): Update regional definitions of AU915.
+ + Update regional definitions of AU915.
+ + Update regional definitions of AS923.
+ + Update TX power computations.
+
+ + Remove duplicated call to ApplyDrOffset in function RegionRxConfig.
+ + Relocate the datarate, up- and downlink dwell time into a structure.
+ + Change API of RegionGetPhyParam and the related functions.
+ + Bug fix in function LoRaMacQueryTxPossible.
+ + Apply patch for dwell time and minimum datarate.
+ + Change the default datarate to DR_2 for AS923.
+ + Take dwell time for ADR calculations and datarate settings into account.
+ + Update LoRaMacQueryTxPossible to reset the MAC commands buffer.
+
+ + Issue(#221): Add the dwell time in function ValidatePayloadLength.
+ + Increase the transmission and reception timeout for KR920.
+ + Bug fix in functions OnRxWindowXTimerEvent.
+ + Remove datarate assignment.
+ + Setup the downlink and uplink dwell time default value to 1.
+ + Add frequency range check for AS923
+ + Issue(#221): Bug fix in max payload size calculation.
+
+ + GitHub reported issues corrections.
+ + Changed the AdrAckCounter handling as expected by the test houses.
+ + Fix an issue where the node stopped transmitting.
+ + Removed useless LoRaMacPayload buffer.
+ + MAC layer indications handling simplification.
+ + Relocate parameter settings from ResetMacParameters to the initialization.
+
+### V1.1.0/27-February-2017 ###
+===============================
+ + Implements LoRa Mac 4.4.0 from Semtech/StackForce from the develop branch
+
+### V1.0.3/01-January-2017 ###
+===============================
+ + Read date between 2 successive read time to make sure date is ok
+
+### V1.0.2/15-November-2016 ###
+===============================
+ + Corrected 1 bug in LoRaMac-board.h: RX_WND_2_CHANNEL for EU is now back at DR_0
+ + Corrected 1 bug in LoRaMac.c for dataRate adaptation
+
+### V1.0.1/15-September-2016 ###
+===============================
+ + Implements LoRa Mac 4.3.0 from Semtech/StackForce
+
+### V1.0.0/01-July-2016 ###
+===============================
+ + First R1.0.0 customized version for STM32Cube solution.
+ + Comissioning_template.h in /Conf contains all Lora Ids to connect on LoRa network
+ It is provided as a template. It must be moved to /Projects/inc/ as Comissioning.h
+ + All files in Conf/src are provided as template and must be copied in /Projects/src.
+ + All files in Conf/inc are provided as template and must be copied in /Projects/inc.
+ #if 0 and #endif must be removed to enable the template in the user directory
+ + Implements LoRa Mac 4.2.0 from Semtech/StackForce
+ + \Lora\Mac\LoRaMac.c : replace floating exponent e3 and e6 by int number
+ + \Lora\Utilities\delay.c : cast uint32_t
+ + Modified intensively timeServer.c
+ + new low layer interfacing Cube HAL (hw_rtc.c, hw_gpio.c and hw_spi.c)
+ + added lora.c as an interface layer to ease product integration
+
+
+ * © COPYRIGHT STMicroelectronics
+ */
+
\ No newline at end of file
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/LICENSE.txt b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/LICENSE.txt
new file mode 100644
index 00000000..d6dfb1b8
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/LICENSE.txt
@@ -0,0 +1,25 @@
+--- Revised BSD License ---
+Copyright (c) 2013, SEMTECH S.A.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the Semtech corporation nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL SEMTECH S.A. BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/radio.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/radio.h
new file mode 100644
index 00000000..2885966d
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/radio.h
@@ -0,0 +1,418 @@
+/*
+ / _____) _ | |
+( (____ _____ ____ _| |_ _____ ____| |__
+ \____ \| ___ | (_ _) ___ |/ ___) _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+ (C)2013 Semtech
+
+Description: Generic radio driver definition
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+
+Maintainer: Miguel Luis and Gregory Cristian
+*/
+/**
+ ******************************************************************************
+ *
+ * Portions COPYRIGHT 2019 STMicroelectronics
+ *
+ * @file radio.h
+ * @author MCD Application Team
+ * @brief generic radio driver definition
+ ******************************************************************************
+ */
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __RADIO_H__
+#define __RADIO_H__
+
+/* Includes ------------------------------------------------------------------*/
+
+#include
+#include
+
+/* Private typedef -----------------------------------------------------------*/
+
+/*!
+ * Radio driver supported modems
+ */
+typedef enum
+{
+ MODEM_FSK = 0,
+ MODEM_LORA,
+ MODEM_BPSK,
+ MODEM_SIGFOX_TX,
+ MODEM_SIGFOX_RX,
+}RadioModems_t;
+
+/*!
+ * Radio driver internal state machine states definition
+ */
+typedef enum
+{
+ RF_IDLE = 0, //!< The radio is idle
+ RF_RX_RUNNING, //!< The radio is in reception state
+ RF_TX_RUNNING, //!< The radio is in transmission state
+ RF_CAD, //!< The radio is doing channel activity detection
+}RadioState_t;
+
+/*!
+ * \brief Radio driver callback functions
+ */
+typedef struct
+{
+ /*!
+ * \brief Tx Done callback prototype.
+ */
+ void ( *TxDone )( void );
+ /*!
+ * \brief Tx Timeout callback prototype.
+ */
+ void ( *TxTimeout )( void );
+ /*!
+ * \brief Rx Done callback prototype.
+ *
+ * \param [IN] payload Received buffer pointer
+ * \param [IN] size Received buffer size
+ * \param [IN] rssi RSSI value computed while receiving the frame [dBm]
+ * \param [IN] snr SNR value computed while receiving the frame [dB]
+ * FSK : N/A ( set to 0 )
+ * LoRa: SNR value in dB
+ */
+ void ( *RxDone )( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr );
+ /*!
+ * \brief Rx Timeout callback prototype.
+ */
+ void ( *RxTimeout )( void );
+ /*!
+ * \brief Rx Error callback prototype.
+ */
+ void ( *RxError )( void );
+ /*!
+ * \brief FHSS Change Channel callback prototype.
+ *
+ * \param [IN] currentChannel Index number of the current channel
+ */
+ void ( *FhssChangeChannel )( uint8_t currentChannel );
+
+ /*!
+ * \brief CAD Done callback prototype.
+ *
+ * \param [IN] channelDetected Channel Activity detected during the CAD
+ */
+ void ( *CadDone ) ( bool channelActivityDetected );
+}RadioEvents_t;
+
+/*!
+ * \brief Radio driver definition
+ */
+struct Radio_s
+{
+ /*!
+ * \brief Initializes the io
+ */
+// void ( *IoInit )( void );
+ /*!
+ * \brief Deinitializes the io
+ */
+// void ( *IoDeInit )( void );
+ /*!
+ * \brief Initializes the radio
+ *
+ * \param [IN] events Structure containing the driver callback functions
+ * \param [OUT] returns radioWakeUpTime
+ */
+ uint32_t ( *Init )( RadioEvents_t *events );
+ /*!
+ * Return current radio status
+ *
+ * \param status Radio status.[RF_IDLE, RF_RX_RUNNING, RF_TX_RUNNING]
+ */
+ RadioState_t ( *GetStatus )( void );
+ /*!
+ * \brief Configures the radio with the given modem
+ *
+ * \param [IN] modem Modem to be used [0: FSK, 1: LoRa]
+ */
+ void ( *SetModem )( RadioModems_t modem );
+ /*!
+ * \brief Sets the channel frequency
+ *
+ * \param [IN] freq Channel RF frequency
+ */
+ void ( *SetChannel )( uint32_t freq );
+ /*!
+ * \brief Checks if the channel is free for the given time
+ *
+ * \param [IN] modem Radio modem to be used [0: FSK, 1: LoRa]
+ * \param [IN] freq Channel RF frequency
+ * \param [IN] rssiThresh RSSI threshold
+ * \param [IN] maxCarrierSenseTime Max time while the RSSI is measured
+ *
+ * \retval isFree [true: Channel is free, false: Channel is not free]
+ */
+ bool ( *IsChannelFree )( RadioModems_t modem, uint32_t freq, int16_t rssiThresh, uint32_t maxCarrierSenseTime );
+ /*!
+ * \brief Generates a 32 bits random value based on the RSSI readings
+ *
+ * \remark This function sets the radio in LoRa modem mode and disables
+ * all interrupts.
+ * After calling this function either Radio.SetRxConfig or
+ * Radio.SetTxConfig functions must be called.
+ *
+ * \retval randomValue 32 bits random value
+ */
+ uint32_t ( *Random )( void );
+ /*!
+ * \brief Sets the reception parameters
+ *
+ * \param [IN] modem Radio modem to be used [0: FSK, 1: LoRa]
+ * \param [IN] bandwidth Sets the bandwidth
+ * FSK : >= 2600 and <= 250000 Hz
+ * LoRa: [0: 125 kHz, 1: 250 kHz,
+ * 2: 500 kHz, 3: Reserved]
+ * \param [IN] datarate Sets the Datarate
+ * FSK : 600..300000 bits/s
+ * LoRa: [6: 64, 7: 128, 8: 256, 9: 512,
+ * 10: 1024, 11: 2048, 12: 4096 chips]
+ * \param [IN] coderate Sets the coding rate (LoRa only)
+ * FSK : N/A ( set to 0 )
+ * LoRa: [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8]
+ * \param [IN] bandwidthAfc Sets the AFC Bandwidth (FSK only)
+ * FSK : >= 2600 and <= 250000 Hz
+ * LoRa: N/A ( set to 0 )
+ * \param [IN] preambleLen Sets the Preamble length
+ * FSK : Number of bytes
+ * LoRa: Length in symbols (the hardware adds 4 more symbols)
+ * \param [IN] symbTimeout Sets the RxSingle timeout value
+ * FSK : timeout in number of bytes
+ * LoRa: timeout in symbols
+ * \param [IN] fixLen Fixed length packets [0: variable, 1: fixed]
+ * \param [IN] payloadLen Sets payload length when fixed length is used
+ * \param [IN] crcOn Enables/Disables the CRC [0: OFF, 1: ON]
+ * \param [IN] freqHopOn Enables disables the intra-packet frequency hopping
+ * FSK : N/A ( set to 0 )
+ * LoRa: [0: OFF, 1: ON]
+ * \param [IN] hopPeriod Number of symbols between each hop
+ * FSK : N/A ( set to 0 )
+ * LoRa: Number of symbols
+ * \param [IN] iqInverted Inverts IQ signals (LoRa only)
+ * FSK : N/A ( set to 0 )
+ * LoRa: [0: not inverted, 1: inverted]
+ * \param [IN] rxContinuous Sets the reception in continuous mode
+ * [false: single mode, true: continuous mode]
+ */
+ void ( *SetRxConfig )( RadioModems_t modem, uint32_t bandwidth,
+ uint32_t datarate, uint8_t coderate,
+ uint32_t bandwidthAfc, uint16_t preambleLen,
+ uint16_t symbTimeout, bool fixLen,
+ uint8_t payloadLen,
+ bool crcOn, bool freqHopOn, uint8_t hopPeriod,
+ bool iqInverted, bool rxContinuous );
+ /*!
+ * \brief Sets the transmission parameters
+ *
+ * \param [IN] modem Radio modem to be used [0: FSK, 1: LoRa]
+ * \param [IN] power Sets the output power [dBm]
+ * \param [IN] fdev Sets the frequency deviation (FSK only)
+ * FSK : [Hz]
+ * LoRa: 0
+ * \param [IN] bandwidth Sets the bandwidth (LoRa only)
+ * FSK : 0
+ * LoRa: [0: 125 kHz, 1: 250 kHz,
+ * 2: 500 kHz, 3: Reserved]
+ * \param [IN] datarate Sets the Datarate
+ * FSK : 600..300000 bits/s
+ * LoRa: [6: 64, 7: 128, 8: 256, 9: 512,
+ * 10: 1024, 11: 2048, 12: 4096 chips]
+ * \param [IN] coderate Sets the coding rate (LoRa only)
+ * FSK : N/A ( set to 0 )
+ * LoRa: [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8]
+ * \param [IN] preambleLen Sets the preamble length
+ * FSK : Number of bytes
+ * LoRa: Length in symbols (the hardware adds 4 more symbols)
+ * \param [IN] fixLen Fixed length packets [0: variable, 1: fixed]
+ * \param [IN] crcOn Enables disables the CRC [0: OFF, 1: ON]
+ * \param [IN] freqHopOn Enables disables the intra-packet frequency hopping
+ * FSK : N/A ( set to 0 )
+ * LoRa: [0: OFF, 1: ON]
+ * \param [IN] hopPeriod Number of symbols between each hop
+ * FSK : N/A ( set to 0 )
+ * LoRa: Number of symbols
+ * \param [IN] iqInverted Inverts IQ signals (LoRa only)
+ * FSK : N/A ( set to 0 )
+ * LoRa: [0: not inverted, 1: inverted]
+ * \param [IN] timeout Transmission timeout [ms]
+ */
+ void ( *SetTxConfig )( RadioModems_t modem, int8_t power, uint32_t fdev,
+ uint32_t bandwidth, uint32_t datarate,
+ uint8_t coderate, uint16_t preambleLen,
+ bool fixLen, bool crcOn, bool freqHopOn,
+ uint8_t hopPeriod, bool iqInverted, uint32_t timeout );
+ /*!
+ * \brief Checks if the given RF frequency is supported by the hardware
+ *
+ * \param [IN] frequency RF frequency to be checked
+ * \retval isSupported [true: supported, false: unsupported]
+ */
+ bool ( *CheckRfFrequency )( uint32_t frequency );
+ /*!
+ * \brief Computes the packet time on air in ms for the given payload
+ *
+ * \Remark Can only be called once SetRxConfig or SetTxConfig have been called
+ *
+ * \param [IN] modem Radio modem to be used [0: FSK, 1: LoRa]
+ * \param [IN] pktLen Packet payload length
+ *
+ * \retval airTime Computed airTime (ms) for the given packet payload length
+ */
+ uint32_t ( *TimeOnAir )( RadioModems_t modem, uint8_t pktLen );
+ /*!
+ * \brief Sends the buffer of size. Prepares the packet to be sent and sets
+ * the radio in transmission
+ *
+ * \param [IN]: buffer Buffer pointer
+ * \param [IN]: size Buffer size
+ */
+ void ( *Send )( uint8_t *buffer, uint8_t size );
+ /*!
+ * \brief Sets the radio in sleep mode
+ */
+ void ( *Sleep )( void );
+ /*!
+ * \brief Sets the radio in standby mode
+ */
+ void ( *Standby )( void );
+ /*!
+ * \brief Sets the radio in reception mode for the given time
+ * \param [IN] timeout Reception timeout [ms]
+ * [0: continuous, others timeout]
+ */
+ void ( *Rx )( uint32_t timeout );
+ /*!
+ * \brief Start a Channel Activity Detection
+ */
+ void ( *StartCad )( void );
+ /*!
+ * \brief Sets the radio in continuous wave transmission mode
+ *
+ * \param [IN]: freq Channel RF frequency
+ * \param [IN]: power Sets the output power [dBm]
+ * \param [IN]: time Transmission mode timeout [s]
+ */
+ void ( *SetTxContinuousWave )( uint32_t freq, int8_t power, uint16_t time );
+ /*!
+ * \brief Reads the current RSSI value
+ *
+ * \retval rssiValue Current RSSI value in [dBm]
+ */
+ int16_t ( *Rssi )( RadioModems_t modem );
+ /*!
+ * \brief Writes the radio register at the specified address
+ *
+ * \param [IN]: addr Register address
+ * \param [IN]: data New register value
+ */
+ void ( *Write )( uint16_t addr, uint8_t data );
+ /*!
+ * \brief Reads the radio register at the specified address
+ *
+ * \param [IN]: addr Register address
+ * \retval data Register value
+ */
+ uint8_t ( *Read )( uint16_t addr );
+ /*!
+ * \brief Writes multiple radio registers starting at address
+ *
+ * \param [IN] addr First Radio register address
+ * \param [IN] buffer Buffer containing the new register's values
+ * \param [IN] size Number of registers to be written
+ */
+// void ( *WriteBuffer )( uint16_t addr, uint8_t *buffer, uint8_t size );
+ /*!
+ * \brief Reads multiple radio registers starting at address
+ *
+ * \param [IN] addr First Radio register address
+ * \param [OUT] buffer Buffer where to copy the registers data
+ * \param [IN] size Number of registers to be read
+ */
+// void ( *ReadBuffer )( uint16_t addr, uint8_t *buffer, uint8_t size );
+ /*!
+ * \brief Sets the maximum payload length.
+ *
+ * \param [IN] modem Radio modem to be used [0: FSK, 1: LoRa]
+ * \param [IN] max Maximum payload length in bytes
+ */
+ void ( *SetMaxPayloadLength )( RadioModems_t modem, uint8_t max );
+ /*!
+ * \brief Sets the network to public or private. Updates the sync byte.
+ *
+ * \remark Applies to LoRa modem only
+ *
+ * \param [IN] enable if true, it enables a public network
+ */
+ void ( *SetPublicNetwork )( bool enable );
+ /*!
+ * \brief Gets the time required for the board plus radio to get out of sleep.[ms]
+ *
+ * \retval time Radio plus board wakeup time in ms.
+ */
+ uint32_t ( *GetWakeupTime )( void );
+ /*!
+ * \brief Process radio irq
+ */
+ void ( *IrqProcess )( void );
+ /*
+ * The next functions are available only on SX126x radios.
+ */
+ /*!
+ * \brief Sets the radio in reception mode with Max LNA gain for the given time
+ *
+ * \remark Available on SX126x radios only.
+ *
+ * \param [IN] timeout Reception timeout [ms]
+ * [0: continuous, others timeout]
+ */
+ void ( *RxBoosted )( uint32_t timeout );
+ /*!
+ * \brief Sets the Rx duty cycle management parameters
+ *
+ * \remark Available on SX126x radios only.
+ *
+ * \param [in] rxTime Structure describing reception timeout value
+ * \param [in] sleepTime Structure describing sleep timeout value
+ */
+ void ( *SetRxDutyCycle ) ( uint32_t rxTime, uint32_t sleepTime );
+
+
+
+ /*!
+ * @brief Sets the Transmitter in continuous PRBS mode
+ *
+ * \remark power and datarate shall be configured prior calling TxPrbs
+ */
+ void (*TxPrbs) ( void );
+ /*!
+ * @brief Sets the Transmitter in continuous unmodulated Carrier mode
+ */
+ void (*TxCw) ( void );
+};
+
+/*!
+ * \brief Radio driver
+ *
+ * \remark This variable is defined and initialized in the specific radio
+ * board implementation
+ */
+extern const struct Radio_s Radio;
+/* Private defines -----------------------------------------------------------*/
+/* Private macros ------------------------------------------------------------*/
+/* Private variables ---------------------------------------------------------*/
+/* Global variables ----------------------------------------------------------*/
+/* Private function prototypes -----------------------------------------------*/
+/* Functions Definition ------------------------------------------------------*/
+
+#endif // __RADIO_H__
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/stm32_radio_driver/mw_log_conf_template.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/stm32_radio_driver/mw_log_conf_template.h
new file mode 100644
index 00000000..89809fc8
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/stm32_radio_driver/mw_log_conf_template.h
@@ -0,0 +1,61 @@
+/**
+ ******************************************************************************
+ * @file mw_log_conf.h
+ * @author MCD Application Team
+ * @brief Interface layer CM4 System to MBMUX (Mailbox Multiplexer)
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under BSD 3-Clause license,
+ * the "License"; You may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ * opensource.org/licenses/BSD-3-Clause
+ *
+ ******************************************************************************
+ */
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __MW_LOG_CONF_H__
+#define __MW_LOG_CONF_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+#include
+#include "trace.h"
+
+/* Exported types ------------------------------------------------------------*/
+/* Exported constants --------------------------------------------------------*/
+#define MW_LOG_ENABLED
+/* Exported macro ------------------------------------------------------------*/
+
+#ifdef MW_LOG_ENABLED
+#define PPRINTF(...) do{ } while( 0!= TraceSend(TIME_STAMP_OFF, 0, __VA_ARGS__) ) //Polling Mode
+#define TPRINTF(...) do{ {TraceSend(TIME_STAMP_ON, 0, __VA_ARGS__);} }while(0)//with timestamp
+#define PRINTF(...) do{ {TraceSend(TIME_STAMP_OFF, 0, __VA_ARGS__);} }while(0)
+#define MW_LOG(VL, TS, ...) do{ {TraceSend(TS, VL, __VA_ARGS__);} }while(0)
+
+#else
+#define PPRINTF(...)
+#define TPRINTF(...)
+#define PRINTF(...)
+#define MW_LOG(VL, TS, ...)
+#endif
+
+
+/* Exported functions ------------------------------------------------------- */
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*__MW_LOG_CONF_H__ */
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/stm32_radio_driver/radio.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/stm32_radio_driver/radio.c
new file mode 100644
index 00000000..930bb063
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/stm32_radio_driver/radio.c
@@ -0,0 +1,1531 @@
+/*!
+ * \file radio.c
+ *
+ * \brief Radio driver API definition
+ *
+ * \copyright Revised BSD License, see section \ref LICENSE.
+ *
+ * \code
+ * ______ _
+ * / _____) _ | |
+ * ( (____ _____ ____ _| |_ _____ ____| |__
+ * \____ \| ___ | (_ _) ___ |/ ___) _ \
+ * _____) ) ____| | | || |_| ____( (___| | | |
+ * (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ * (C)2013-2017 Semtech
+ *
+ * \endcode
+ *
+ * \author Miguel Luis ( Semtech )
+ *
+ * \author Gregory Cristian ( Semtech )
+ */
+/**
+ ******************************************************************************
+ *
+ * Portions COPYRIGHT 2019 STMicroelectronics
+ *
+ * @file radio.c
+ * @author MCD Application Team
+ * @brief radio API definition
+ ******************************************************************************
+ */
+/* Includes ------------------------------------------------------------------*/
+#include
+
+#include "radio.h"
+#include "timer.h"
+#include "radio_driver.h"
+#include "radio_conf.h"
+#include "mw_log_conf.h"
+
+/*
+ * Local types definition
+ */
+/*
+ * Private types
+ */
+/*!
+ * Radio hardware and global parameters
+ */
+typedef struct SubgRf_s
+{
+ RadioModems_t modem;
+ bool RxContinuous;
+ uint32_t TxTimeout;
+ uint32_t RxTimeout;
+ bool publicNetwork;
+ PacketParams_t PacketParams;
+ PacketStatus_t PacketStatus;
+ ModulationParams_t ModulationParams;
+ RadioIrqMasks_t radioIrq;
+}SubgRf_t;
+
+#define RADIO_BIT_MASK(__n) (~(1<<__n))
+
+/*
+ * Private functions prototypes
+ */
+
+
+/* **********************************************************************
+ * Interrupts functions prototypes
+ ************************************************************************/
+/*!
+ * @brief Process radio irq
+ */
+void RadioIrqProcess( void );
+
+/*!
+ * @brief Tx timeout timer callback
+ */
+void RadioOnTxTimeoutIrq( void * context );
+
+/*!
+ * @brief Rx timeout timer callback
+ */
+void RadioOnRxTimeoutIrq( void * context );
+
+
+/* **********************************************************************
+ * Middleware Interface functions prototypes
+ ************************************************************************/
+
+/*!
+ * \brief Initializes the radio
+ *
+ * \param [IN] events Structure containing the driver callback functions
+ */
+uint32_t RadioInit( RadioEvents_t *events );
+
+/*!
+ * Return current radio status
+ *
+ * \param status Radio status.[RF_IDLE, RF_RX_RUNNING, RF_TX_RUNNING]
+ */
+RadioState_t RadioGetStatus( void );
+
+/*!
+ * \brief Configures the radio with the given modem
+ *
+ * \param [IN] modem Modem to be used [0: FSK, 1: LoRa]
+ */
+void RadioSetModem( RadioModems_t modem );
+
+/*!
+ * \brief Sets the channel frequency
+ *
+ * \param [IN] freq Channel RF frequency
+ */
+void RadioSetChannel( uint32_t freq );
+
+/*!
+ * \brief Checks if the channel is free for the given time
+ *
+ * \param [IN] modem Radio modem to be used [0: FSK, 1: LoRa]
+ * \param [IN] freq Channel RF frequency
+ * \param [IN] rssiThresh RSSI threshold
+ * \param [IN] maxCarrierSenseTime Max time while the RSSI is measured
+ *
+ * \retval isFree [true: Channel is free, false: Channel is not free]
+ */
+bool RadioIsChannelFree( RadioModems_t modem, uint32_t freq, int16_t rssiThresh, uint32_t maxCarrierSenseTime );
+
+/*!
+ * \brief Generates a 32 bits random value based on the RSSI readings
+ *
+ * \remark This function sets the radio in LoRa modem mode and disables
+ * all interrupts.
+ * After calling this function either Radio.SetRxConfig or
+ * Radio.SetTxConfig functions must be called.
+ *
+ * \retval randomValue 32 bits random value
+ */
+uint32_t RadioRandom( void );
+
+/*!
+ * \brief Sets the reception parameters
+ *
+ * \param [IN] modem Radio modem to be used [0: FSK, 1: LoRa]
+ * \param [IN] bandwidth Sets the bandwidth
+ * FSK : >= 2600 and <= 250000 Hz
+ * LoRa: [0: 125 kHz, 1: 250 kHz,
+ * 2: 500 kHz, 3: Reserved]
+ * \param [IN] datarate Sets the Datarate
+ * FSK : 600..300000 bits/s
+ * LoRa: [6: 64, 7: 128, 8: 256, 9: 512,
+ * 10: 1024, 11: 2048, 12: 4096 chips]
+ * \param [IN] coderate Sets the coding rate (LoRa only)
+ * FSK : N/A ( set to 0 )
+ * LoRa: [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8]
+ * \param [IN] bandwidthAfc Sets the AFC Bandwidth (FSK only)
+ * FSK : >= 2600 and <= 250000 Hz
+ * LoRa: N/A ( set to 0 )
+ * \param [IN] preambleLen Sets the Preamble length
+ * FSK : Number of bytes
+ * LoRa: Length in symbols (the hardware adds 4 more symbols)
+ * \param [IN] symbTimeout Sets the RxSingle timeout value
+ * FSK : timeout in number of bytes
+ * LoRa: timeout in symbols
+ * \param [IN] fixLen Fixed length packets [0: variable, 1: fixed]
+ * \param [IN] payloadLen Sets payload length when fixed length is used
+ * \param [IN] crcOn Enables/Disables the CRC [0: OFF, 1: ON]
+ * \param [IN] FreqHopOn Enables disables the intra-packet frequency hopping
+ * FSK : N/A ( set to 0 )
+ * LoRa: [0: OFF, 1: ON]
+ * \param [IN] HopPeriod Number of symbols between each hop
+ * FSK : N/A ( set to 0 )
+ * LoRa: Number of symbols
+ * \param [IN] iqInverted Inverts IQ signals (LoRa only)
+ * FSK : N/A ( set to 0 )
+ * LoRa: [0: not inverted, 1: inverted]
+ * \param [IN] rxContinuous Sets the reception in continuous mode
+ * [false: single mode, true: continuous mode]
+ */
+void RadioSetRxConfig( RadioModems_t modem, uint32_t bandwidth,
+ uint32_t datarate, uint8_t coderate,
+ uint32_t bandwidthAfc, uint16_t preambleLen,
+ uint16_t symbTimeout, bool fixLen,
+ uint8_t payloadLen,
+ bool crcOn, bool FreqHopOn, uint8_t HopPeriod,
+ bool iqInverted, bool rxContinuous );
+
+/*!
+ * \brief Sets the transmission parameters
+ *
+ * \param [IN] modem Radio modem to be used [0: FSK, 1: LoRa]
+ * \param [IN] power Sets the output power [dBm]
+ * \param [IN] fdev Sets the frequency deviation (FSK only)
+ * FSK : [Hz]
+ * LoRa: 0
+ * \param [IN] bandwidth Sets the bandwidth (LoRa only)
+ * FSK : 0
+ * LoRa: [0: 125 kHz, 1: 250 kHz,
+ * 2: 500 kHz, 3: Reserved]
+ * \param [IN] datarate Sets the Datarate
+ * FSK : 600..300000 bits/s
+ * LoRa: [6: 64, 7: 128, 8: 256, 9: 512,
+ * 10: 1024, 11: 2048, 12: 4096 chips]
+ * \param [IN] coderate Sets the coding rate (LoRa only)
+ * FSK : N/A ( set to 0 )
+ * LoRa: [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8]
+ * \param [IN] preambleLen Sets the preamble length
+ * FSK : Number of bytes
+ * LoRa: Length in symbols (the hardware adds 4 more symbols)
+ * \param [IN] fixLen Fixed length packets [0: variable, 1: fixed]
+ * \param [IN] crcOn Enables disables the CRC [0: OFF, 1: ON]
+ * \param [IN] FreqHopOn Enables disables the intra-packet frequency hopping
+ * FSK : N/A ( set to 0 )
+ * LoRa: [0: OFF, 1: ON]
+ * \param [IN] HopPeriod Number of symbols between each hop
+ * FSK : N/A ( set to 0 )
+ * LoRa: Number of symbols
+ * \param [IN] iqInverted Inverts IQ signals (LoRa only)
+ * FSK : N/A ( set to 0 )
+ * LoRa: [0: not inverted, 1: inverted]
+ * \param [IN] timeout Transmission timeout [ms]
+ */
+void RadioSetTxConfig( RadioModems_t modem, int8_t power, uint32_t fdev,
+ uint32_t bandwidth, uint32_t datarate,
+ uint8_t coderate, uint16_t preambleLen,
+ bool fixLen, bool crcOn, bool FreqHopOn,
+ uint8_t HopPeriod, bool iqInverted, uint32_t timeout );
+
+/*!
+ * \brief Checks if the given RF frequency is supported by the hardware
+ *
+ * \param [IN] frequency RF frequency to be checked
+ * \retval isSupported [true: supported, false: unsupported]
+ */
+bool RadioCheckRfFrequency( uint32_t frequency );
+
+/*!
+ * \brief Computes the packet time on air in ms for the given payload
+ *
+ * \Remark Can only be called once SetRxConfig or SetTxConfig have been called
+ *
+ * \param [IN] modem Radio modem to be used [0: FSK, 1: LoRa]
+ * \param [IN] pktLen Packet payload length
+ *
+ * \retval airTime Computed airTime (ms) for the given packet payload length
+ */
+uint32_t RadioTimeOnAir( RadioModems_t modem, uint8_t pktLen );
+
+/*!
+ * \brief Sends the buffer of size. Prepares the packet to be sent and sets
+ * the radio in transmission
+ *
+ * \param [IN]: buffer Buffer pointer
+ * \param [IN]: size Buffer size
+ */
+void RadioSend( uint8_t *buffer, uint8_t size );
+
+/*!
+ * \brief Sets the radio in sleep mode
+ */
+void RadioSleep( void );
+
+/*!
+ * \brief Sets the radio in standby mode
+ */
+void RadioStandby( void );
+
+/*!
+ * \brief Sets the radio in reception mode for the given time
+ * \param [IN] timeout Reception timeout [ms]
+ * [0: continuous, others timeout]
+ */
+void RadioRx( uint32_t timeout );
+
+/*!
+ * \brief Start a Channel Activity Detection
+ */
+void RadioStartCad( void );
+
+/*!
+ * \brief Sets the radio in continuous wave transmission mode
+ *
+ * \param [IN]: freq Channel RF frequency
+ * \param [IN]: power Sets the output power [dBm]
+ * \param [IN]: time Transmission mode timeout [s]
+ */
+void RadioSetTxContinuousWave( uint32_t freq, int8_t power, uint16_t time );
+
+/*!
+ * \brief Reads the current RSSI value
+ *
+ * \retval rssiValue Current RSSI value in [dBm]
+ */
+int16_t RadioRssi( RadioModems_t modem );
+
+/*!
+ * \brief Writes the radio register at the specified address
+ *
+ * \param [IN]: addr Register address
+ * \param [IN]: data New register value
+ */
+void RadioWrite( uint16_t addr, uint8_t data );
+
+/*!
+ * \brief Reads the radio register at the specified address
+ *
+ * \param [IN]: addr Register address
+ * \retval data Register value
+ */
+uint8_t RadioRead( uint16_t addr );
+
+/*!
+ * \brief Writes multiple radio registers starting at address
+ *
+ * \param [IN] addr First Radio register address
+ * \param [IN] buffer Buffer containing the new register's values
+ * \param [IN] size Number of registers to be written
+ */
+void RadioWriteBuffer( uint16_t addr, uint8_t *buffer, uint8_t size );
+
+/*!
+ * \brief Reads multiple radio registers starting at address
+ *
+ * \param [IN] addr First Radio register address
+ * \param [OUT] buffer Buffer where to copy the registers data
+ * \param [IN] size Number of registers to be read
+ */
+void RadioReadBuffer( uint16_t addr, uint8_t *buffer, uint8_t size );
+
+/*!
+ * \brief Sets the maximum payload length.
+ *
+ * \param [IN] modem Radio modem to be used [0: FSK, 1: LoRa]
+ * \param [IN] max Maximum payload length in bytes
+ */
+void RadioSetMaxPayloadLength( RadioModems_t modem, uint8_t max );
+
+/*!
+ * \brief Sets the network to public or private. Updates the sync byte.
+ *
+ * \remark Applies to LoRa modem only
+ *
+ * \param [IN] enable if true, it enables a public network
+ */
+void RadioSetPublicNetwork( bool enable );
+
+/*!
+ * \brief Gets the time required for the board plus radio to get out of sleep.[ms]
+ *
+ * \retval time Radio plus board wakeup time in ms.
+ */
+uint32_t RadioGetWakeUpTime( void );
+
+
+/*!
+ * \brief Sets the radio in reception mode with Max LNA gain for the given time
+ * \param [IN] timeout Reception timeout [ms]
+ * [0: continuous, others timeout]
+ */
+void RadioRxBoosted( uint32_t timeout );
+
+/*!
+ * \brief Sets the Rx duty cycle management parameters
+ *
+ * \param [in] rxTime Structure describing reception timeout value
+ * \param [in] sleepTime Structure describing sleep timeout value
+ */
+void RadioSetRxDutyCycle( uint32_t rxTime, uint32_t sleepTime );
+
+/*!
+ * \brief Set Tx
+ * \param timeout
+ * \retval none
+ */
+void RadioTx( uint32_t timeout );
+
+
+
+/*!
+* @brief D-BPSK to BPSK
+*
+* @param [in] inBuffer buffer with frame to encode
+* @param [out] OutBuffer buffer with frame encoded
+* @param [in] size size of the payload to encode
+*/
+static void payload_integration( uint8_t *outBuffer, uint8_t *inBuffer, uint8_t size);
+
+/*!
+ * \brief Set Tx PRBS modulated wave
+ * \retval none
+ */
+void RadioTxPrbs( void );
+
+/*!
+ * \brief Set Tx continuous wave
+ * \retval none
+ */
+void RadioTxCw( void );
+
+ /*
+ * Private global constants
+ */
+
+/*!
+ * Radio driver structure initialization
+ */
+const struct Radio_s Radio =
+{
+ RadioInit,
+ RadioGetStatus,
+ RadioSetModem,
+ RadioSetChannel,
+ RadioIsChannelFree,
+ RadioRandom,
+ RadioSetRxConfig,
+ RadioSetTxConfig,
+ RadioCheckRfFrequency,
+ RadioTimeOnAir,
+ RadioSend,
+ RadioSleep,
+ RadioStandby,
+ RadioRx,
+ RadioStartCad,
+ RadioSetTxContinuousWave,
+ RadioRssi,
+ RadioWrite,
+ RadioRead,
+ RadioSetMaxPayloadLength,
+ RadioSetPublicNetwork,
+ RadioGetWakeUpTime,
+ RadioIrqProcess,
+ RadioRxBoosted,
+ RadioSetRxDutyCycle,
+ RadioTxPrbs,
+ RadioTxCw,
+};
+
+
+
+ /*!
+ * FSK bandwidth definition
+ */
+typedef struct
+{
+ uint32_t bandwidth;
+ uint8_t RegValue;
+}FskBandwidth_t;
+
+/*!
+ * Precomputed FSK bandwidth registers values
+ */
+const FskBandwidth_t FskBandwidths[] =
+{
+ { 4800 , 0x1F },
+ { 5800 , 0x17 },
+ { 7300 , 0x0F },
+ { 9700 , 0x1E },
+ { 11700 , 0x16 },
+ { 14600 , 0x0E },
+ { 19500 , 0x1D },
+ { 23400 , 0x15 },
+ { 29300 , 0x0D },
+ { 39000 , 0x1C },
+ { 46900 , 0x14 },
+ { 58600 , 0x0C },
+ { 78200 , 0x1B },
+ { 93800 , 0x13 },
+ { 117300, 0x0B },
+ { 156200, 0x1A },
+ { 187200, 0x12 },
+ { 234300, 0x0A },
+ { 312000, 0x19 },
+ { 373600, 0x11 },
+ { 467000, 0x09 },
+ { 500000, 0x00 }, // Invalid Bandwidth
+};
+
+const RadioLoRaBandwidths_t Bandwidths[] = { LORA_BW_125, LORA_BW_250, LORA_BW_500 };
+
+// SF12 SF11 SF10 SF9 SF8 SF7
+static const double RadioLoRaSymbTime[3][6] = {{ 32.768, 16.384, 8.192, 4.096, 2.048, 1.024 }, // 125 KHz
+ { 16.384, 8.192, 4.096, 2.048, 1.024, 0.512 }, // 250 KHz
+ { 8.192, 4.096, 2.048, 1.024, 0.512, 0.256 }}; // 500 KHz
+
+static uint8_t MaxPayloadLength = RADIO_RX_BUF_SIZE;
+
+ /* RF switch Power variable */
+static uint8_t AntSwitchPower;
+
+static uint8_t RadioRxPayload[RADIO_RX_BUF_SIZE];
+
+bool IrqFired = false;
+
+/*
+ * Private global variables
+ */
+
+
+
+
+/*!
+ * Radio callbacks variable
+ */
+static RadioEvents_t* RadioEvents;
+
+/*
+ * Public global variables
+ */
+
+/*!
+ * Radio hardware and global parameters
+ */
+SubgRf_t SubgRf;
+
+
+/*!
+ * Tx and Rx timers
+ */
+TimerEvent_t TxTimeoutTimer;
+TimerEvent_t RxTimeoutTimer;
+
+/*!
+ * Returns the known FSK bandwidth registers value
+ *
+ * \param [IN] bandwidth Bandwidth value in Hz
+ * \retval regValue Bandwidth register value.
+ */
+static uint8_t RadioGetFskBandwidthRegValue( uint32_t bandwidth )
+{
+ uint8_t i;
+
+ if( bandwidth == 0 )
+ {
+ return( 0x1F );
+ }
+
+ for( i = 0; i < ( sizeof( FskBandwidths ) / sizeof( FskBandwidth_t ) ); i++ )
+ {
+ if ( bandwidth < FskBandwidths[i].bandwidth )
+ {
+ return FskBandwidths[i].RegValue;
+ }
+ }
+ // ERROR: Value not found
+ while(1){}
+}
+
+uint32_t RadioInit( RadioEvents_t *events )
+{
+ RadioEvents = events;
+
+ SubgRf.RxContinuous = false;
+ SubgRf.TxTimeout = 0;
+ SubgRf.RxTimeout = 0;
+
+ if( SUBGRF_Init( RadioOnDioIrq ) != SUBGRF_OK)
+ {
+ /* Initialization Error */
+ while(1){}
+ }
+ SUBGRF_SetStandby( STDBY_RC );
+ /*SubgRf.publicNetwork set to false*/
+ RadioSetPublicNetwork( false );
+
+ SUBGRF_SetRegulatorMode( );
+
+ SUBGRF_SetBufferBaseAddress( 0x00, 0x00 );
+
+ SUBGRF_SetTxParams(RFO_LP, 0, RADIO_RAMP_200_US);
+
+ /* Radio IRQ is set to DIO1 by default */
+ SUBGRF_SetDioIrqParams( IRQ_RADIO_ALL, IRQ_RADIO_ALL, IRQ_RADIO_NONE, IRQ_RADIO_NONE );
+
+
+ // Initialize driver timeout timers
+ TimerInit( &TxTimeoutTimer, RadioOnTxTimeoutIrq );
+ TimerInit( &RxTimeoutTimer, RadioOnRxTimeoutIrq );
+ TimerStop( &TxTimeoutTimer );
+ TimerStop( &RxTimeoutTimer );
+
+ IrqFired = false;
+ return ( uint32_t )RadioGetWakeUpTime( );
+
+}
+
+RadioState_t RadioGetStatus( void )
+{
+ switch( SUBGRF_GetOperatingMode( ) )
+ {
+ case MODE_TX:
+ return RF_TX_RUNNING;
+ case MODE_RX:
+ return RF_RX_RUNNING;
+ case RF_CAD:
+ return RF_CAD;
+ default:
+ return RF_IDLE;
+ }
+}
+
+void RadioSetModem( RadioModems_t modem )
+{
+ SubgRf.modem=modem ;
+
+ switch( modem )
+ {
+ default:
+ case MODEM_FSK:
+ SUBGRF_SetPacketType( PACKET_TYPE_GFSK );
+ // When switching to GFSK mode the LoRa SyncWord register value is reset
+ // Thus, we also reset the RadioPublicNetwork variable
+ break;
+ case MODEM_LORA:
+ SUBGRF_SetPacketType( PACKET_TYPE_LORA );
+ // Public/Private network register is reset when switching modems
+ RadioSetPublicNetwork( SubgRf.publicNetwork );
+ break;
+ case MODEM_SIGFOX_TX:
+ SUBGRF_SetPacketType( PACKET_TYPE_BPSK );
+ break;
+ case MODEM_SIGFOX_RX:
+ SUBGRF_SetPacketType( PACKET_TYPE_GFSK );
+ break;
+ }
+}
+
+void RadioSetChannel( uint32_t freq )
+{
+ SUBGRF_SetRfFrequency( freq );
+}
+
+bool RadioIsChannelFree( RadioModems_t modem, uint32_t freq, int16_t rssiThresh, uint32_t maxCarrierSenseTime )
+{
+ bool status = true;
+ int16_t rssi = 0;
+ uint32_t carrierSenseTime = 0;
+
+ RadioStandby( );
+
+ RadioSetModem( modem );
+
+ RadioSetChannel( freq );
+
+ RadioRx( 0 );
+
+ RADIO_DELAY_MS( RadioGetWakeUpTime( ) );
+
+ carrierSenseTime = TimerGetCurrentTime( );
+
+ // Perform carrier sense for maxCarrierSenseTime
+ while( TimerGetElapsedTime( carrierSenseTime ) < maxCarrierSenseTime )
+ {
+ rssi = RadioRssi( modem );
+
+ if( rssi > rssiThresh )
+ {
+ status = false;
+ break;
+ }
+ }
+ RadioStandby( );
+ return status;
+}
+
+uint32_t RadioRandom( void )
+{
+ uint32_t rnd = SUBGRF_GetRandom();
+
+ return rnd;
+}
+
+void RadioSetRxConfig( RadioModems_t modem, uint32_t bandwidth,
+ uint32_t datarate, uint8_t coderate,
+ uint32_t bandwidthAfc, uint16_t preambleLen,
+ uint16_t symbTimeout, bool fixLen,
+ uint8_t payloadLen,
+ bool crcOn, bool freqHopOn, uint8_t hopPeriod,
+ bool iqInverted, bool rxContinuous )
+{
+
+ uint8_t modReg;
+ SubgRf.RxContinuous = rxContinuous;
+ if( rxContinuous == true )
+ {
+ symbTimeout = 0;
+ }
+ if( fixLen == true )
+ {
+ MaxPayloadLength = payloadLen;
+ }
+ else
+ {
+ MaxPayloadLength = 0xFF;
+ }
+
+ switch( modem )
+ {
+
+ case MODEM_SIGFOX_RX:
+ SUBGRF_SetStopRxTimerOnPreambleDetect( true );
+ SubgRf.ModulationParams.PacketType = PACKET_TYPE_GFSK;
+
+ SubgRf.ModulationParams.Params.Gfsk.BitRate = datarate;
+ SubgRf.ModulationParams.Params.Gfsk.ModulationShaping = MOD_SHAPING_G_BT_05;
+ SubgRf.ModulationParams.Params.Gfsk.Fdev = 800;
+ SubgRf.ModulationParams.Params.Gfsk.Bandwidth = RadioGetFskBandwidthRegValue( bandwidth );
+
+ SubgRf.PacketParams.PacketType = PACKET_TYPE_GFSK;
+ SubgRf.PacketParams.Params.Gfsk.PreambleLength = ( preambleLen << 3 ); // convert byte into bit
+ SubgRf.PacketParams.Params.Gfsk.PreambleMinDetect = RADIO_PREAMBLE_DETECTOR_OFF;
+ SubgRf.PacketParams.Params.Gfsk.SyncWordLength = 2 << 3; // convert byte into bit
+ SubgRf.PacketParams.Params.Gfsk.AddrComp = RADIO_ADDRESSCOMP_FILT_OFF;
+ SubgRf.PacketParams.Params.Gfsk.HeaderType = RADIO_PACKET_FIXED_LENGTH;
+ SubgRf.PacketParams.Params.Gfsk.PayloadLength = MaxPayloadLength;
+ SubgRf.PacketParams.Params.Gfsk.CrcLength = RADIO_CRC_OFF;
+
+ SubgRf.PacketParams.Params.Gfsk.DcFree = RADIO_DC_FREE_OFF;
+
+ // RadioStandby( );
+ RadioSetModem( MODEM_SIGFOX_RX );
+ SUBGRF_SetModulationParams( &SubgRf.ModulationParams );
+ SUBGRF_SetPacketParams( &SubgRf.PacketParams );
+ SUBGRF_SetSyncWord( ( uint8_t[] ){0xB2, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } );
+ SUBGRF_SetWhiteningSeed( 0x01FF );
+
+ /*• NO gfo reset (better sensitivity). Reg 0x8b8, bit4 = 0*/
+ modReg= RadioRead(0x8b8);
+ modReg&=RADIO_BIT_MASK(4);
+// modReg =modReg & (1<<2);
+ RadioWrite(0x8b8, modReg);
+ /*3. Lower the threshold of cfo_reset */
+ RadioWrite(0x8b9, 0x4 );
+
+ /*•Bigger rssi_len (stability AGC). Reg 0x89b, bits[2 :4] = 0x1 */
+ modReg= RadioRead(0x89b);
+ modReg&=( RADIO_BIT_MASK(2) & RADIO_BIT_MASK(3) & RADIO_BIT_MASK(4) );
+ RadioWrite(0x89b, (modReg| (0x1<<3) ) );
+
+ /*•Biger afc_pbl_len (better frequency correction). Reg 0x6d1, bits[3 :4] = 0x3 */
+ modReg= RadioRead(0x6d1);
+ modReg&=( RADIO_BIT_MASK(3) & RADIO_BIT_MASK(4) );
+ RadioWrite(0x6d1, (modReg| (0x3<<3) ));
+
+ /*••Use of new bit synchronizer (to avoid CRC errors during PER for payloads with a small amount of transitions). Reg 0x6ac, bits[4 :6] = 0x5 */
+ modReg= RadioRead(0x6ac);
+ modReg&=( RADIO_BIT_MASK(4) & RADIO_BIT_MASK(5) & RADIO_BIT_MASK(6) );
+ RadioWrite(0x6ac, (modReg| (0x5<<4) ));
+
+ SubgRf.RxTimeout = ( uint32_t )( symbTimeout * ( ( 1.0 / ( double )datarate ) * 8.0 ) * 1000 );
+ break;
+ case MODEM_FSK:
+ SUBGRF_SetStopRxTimerOnPreambleDetect( false );
+ SubgRf.ModulationParams.PacketType = PACKET_TYPE_GFSK;
+
+ SubgRf.ModulationParams.Params.Gfsk.BitRate = datarate;
+ SubgRf.ModulationParams.Params.Gfsk.ModulationShaping = MOD_SHAPING_G_BT_1;
+ SubgRf.ModulationParams.Params.Gfsk.Bandwidth = RadioGetFskBandwidthRegValue( bandwidth );
+
+ SubgRf.PacketParams.PacketType = PACKET_TYPE_GFSK;
+ SubgRf.PacketParams.Params.Gfsk.PreambleLength = ( preambleLen << 3 ); // convert byte into bit
+ SubgRf.PacketParams.Params.Gfsk.PreambleMinDetect = RADIO_PREAMBLE_DETECTOR_08_BITS;
+ SubgRf.PacketParams.Params.Gfsk.SyncWordLength = 3 << 3; // convert byte into bit
+ SubgRf.PacketParams.Params.Gfsk.AddrComp = RADIO_ADDRESSCOMP_FILT_OFF;
+ SubgRf.PacketParams.Params.Gfsk.HeaderType = ( fixLen == true ) ? RADIO_PACKET_FIXED_LENGTH : RADIO_PACKET_VARIABLE_LENGTH;
+ SubgRf.PacketParams.Params.Gfsk.PayloadLength = MaxPayloadLength;
+ if( crcOn == true )
+ {
+ SubgRf.PacketParams.Params.Gfsk.CrcLength = RADIO_CRC_2_BYTES_CCIT;
+ }
+ else
+ {
+ SubgRf.PacketParams.Params.Gfsk.CrcLength = RADIO_CRC_OFF;
+ }
+ SubgRf.PacketParams.Params.Gfsk.DcFree = RADIO_DC_FREEWHITENING;
+
+ RadioStandby( );
+ RadioSetModem( ( SubgRf.ModulationParams.PacketType == PACKET_TYPE_GFSK ) ? MODEM_FSK : MODEM_LORA );
+ SUBGRF_SetModulationParams( &SubgRf.ModulationParams );
+ SUBGRF_SetPacketParams( &SubgRf.PacketParams );
+ SUBGRF_SetSyncWord( ( uint8_t[] ){ 0xC1, 0x94, 0xC1, 0x00, 0x00, 0x00, 0x00, 0x00 } );
+ SUBGRF_SetWhiteningSeed( 0x01FF );
+
+ SubgRf.RxTimeout = ( uint32_t )( symbTimeout * ( ( 1.0 / ( double )datarate ) * 8.0 ) * 1000 );
+ break;
+
+ case MODEM_LORA:
+ SUBGRF_SetStopRxTimerOnPreambleDetect( false );
+ SUBGRF_SetLoRaSymbNumTimeout( symbTimeout );
+ SubgRf.ModulationParams.PacketType = PACKET_TYPE_LORA;
+ SubgRf.ModulationParams.Params.LoRa.SpreadingFactor = ( RadioLoRaSpreadingFactors_t )datarate;
+ SubgRf.ModulationParams.Params.LoRa.Bandwidth = Bandwidths[bandwidth];
+ SubgRf.ModulationParams.Params.LoRa.CodingRate = ( RadioLoRaCodingRates_t )coderate;
+
+ if( ( ( bandwidth == 0 ) && ( ( datarate == 11 ) || ( datarate == 12 ) ) ) ||
+ ( ( bandwidth == 1 ) && ( datarate == 12 ) ) )
+ {
+ SubgRf.ModulationParams.Params.LoRa.LowDatarateOptimize = 0x01;
+ }
+ else
+ {
+ SubgRf.ModulationParams.Params.LoRa.LowDatarateOptimize = 0x00;
+ }
+
+ SubgRf.PacketParams.PacketType = PACKET_TYPE_LORA;
+
+ if( ( SubgRf.ModulationParams.Params.LoRa.SpreadingFactor == LORA_SF5 ) ||
+ ( SubgRf.ModulationParams.Params.LoRa.SpreadingFactor == LORA_SF6 ) )
+ {
+ if( preambleLen < 12 )
+ {
+ SubgRf.PacketParams.Params.LoRa.PreambleLength = 12;
+ }
+ else
+ {
+ SubgRf.PacketParams.Params.LoRa.PreambleLength = preambleLen;
+ }
+ }
+ else
+ {
+ SubgRf.PacketParams.Params.LoRa.PreambleLength = preambleLen;
+ }
+
+ SubgRf.PacketParams.Params.LoRa.HeaderType = ( RadioLoRaPacketLengthsMode_t )fixLen;
+
+ SubgRf.PacketParams.Params.LoRa.PayloadLength = MaxPayloadLength;
+ SubgRf.PacketParams.Params.LoRa.CrcMode = ( RadioLoRaCrcModes_t )crcOn;
+ SubgRf.PacketParams.Params.LoRa.InvertIQ = ( RadioLoRaIQModes_t )iqInverted;
+
+ RadioSetModem( ( SubgRf.ModulationParams.PacketType == PACKET_TYPE_GFSK ) ? MODEM_FSK : MODEM_LORA );
+ SUBGRF_SetModulationParams( &SubgRf.ModulationParams );
+ SUBGRF_SetPacketParams( &SubgRf.PacketParams );
+
+ // WORKAROUND - Optimizing the Inverted IQ Operation, see DS_SX1261-2_V1.2 datasheet chapter 15.4
+ if( SubgRf.PacketParams.Params.LoRa.InvertIQ == LORA_IQ_INVERTED )
+ {
+ // RegIqPolaritySetup = @address 0x0736
+ SUBGRF_WriteRegister( 0x0736, SUBGRF_ReadRegister( 0x0736 ) & ~( 1 << 2 ) );
+ }
+ else
+ {
+ // RegIqPolaritySetup @address 0x0736
+ SUBGRF_WriteRegister( 0x0736, SUBGRF_ReadRegister( 0x0736 ) | ( 1 << 2 ) );
+ }
+ // WORKAROUND END
+
+ // Timeout Max, Timeout handled directly in SetRx function
+ SubgRf.RxTimeout = 0xFFFF;
+
+ break;
+ default:
+ break;
+ }
+
+}
+
+void RadioSetTxConfig( RadioModems_t modem, int8_t power, uint32_t fdev,
+ uint32_t bandwidth, uint32_t datarate,
+ uint8_t coderate, uint16_t preambleLen,
+ bool fixLen, bool crcOn, bool freqHopOn,
+ uint8_t hopPeriod, bool iqInverted, uint32_t timeout )
+{
+ switch( modem )
+ {
+ case MODEM_SIGFOX_TX:
+
+ RadioSetModem(MODEM_SIGFOX_TX);
+ SubgRf.ModulationParams.PacketType = PACKET_TYPE_BPSK;
+ SubgRf.ModulationParams.Params.Bpsk.BitRate = datarate;
+ SubgRf.ModulationParams.Params.Bpsk.ModulationShaping = MOD_SHAPING_DBPSK;
+ SUBGRF_SetModulationParams( &SubgRf.ModulationParams );
+
+ //SubgRf.PacketParams.Params.Bpsk.PayloadLength = frameSize + 1; // for added start and stop bit
+ // SubgRf.PacketParams.Params.Bpsk.
+ //SUBGRF_SetPacketParams( &SubgRf.PacketParams );
+ break;
+ case MODEM_FSK:
+ SubgRf.ModulationParams.PacketType = PACKET_TYPE_GFSK;
+ SubgRf.ModulationParams.Params.Gfsk.BitRate = datarate;
+
+ SubgRf.ModulationParams.Params.Gfsk.ModulationShaping = MOD_SHAPING_G_BT_1;
+ SubgRf.ModulationParams.Params.Gfsk.Bandwidth = RadioGetFskBandwidthRegValue( bandwidth );
+ SubgRf.ModulationParams.Params.Gfsk.Fdev = fdev;
+
+ SubgRf.PacketParams.PacketType = PACKET_TYPE_GFSK;
+ SubgRf.PacketParams.Params.Gfsk.PreambleLength = ( preambleLen << 3 ); // convert byte into bit
+ SubgRf.PacketParams.Params.Gfsk.PreambleMinDetect = RADIO_PREAMBLE_DETECTOR_08_BITS;
+ SubgRf.PacketParams.Params.Gfsk.SyncWordLength = 3 << 3 ; // convert byte into bit
+ SubgRf.PacketParams.Params.Gfsk.AddrComp = RADIO_ADDRESSCOMP_FILT_OFF;
+ SubgRf.PacketParams.Params.Gfsk.HeaderType = ( fixLen == true ) ? RADIO_PACKET_FIXED_LENGTH : RADIO_PACKET_VARIABLE_LENGTH;
+
+ if( crcOn == true )
+ {
+ SubgRf.PacketParams.Params.Gfsk.CrcLength = RADIO_CRC_2_BYTES_CCIT;
+ }
+ else
+ {
+ SubgRf.PacketParams.Params.Gfsk.CrcLength = RADIO_CRC_OFF;
+ }
+ SubgRf.PacketParams.Params.Gfsk.DcFree = RADIO_DC_FREEWHITENING;
+
+ RadioStandby( );
+ RadioSetModem( ( SubgRf.ModulationParams.PacketType == PACKET_TYPE_GFSK ) ? MODEM_FSK : MODEM_LORA );
+ SUBGRF_SetModulationParams( &SubgRf.ModulationParams );
+ SUBGRF_SetPacketParams( &SubgRf.PacketParams );
+ SUBGRF_SetSyncWord( ( uint8_t[] ){ 0xC1, 0x94, 0xC1, 0x00, 0x00, 0x00, 0x00, 0x00 } );
+ SUBGRF_SetWhiteningSeed( 0x01FF );
+ break;
+
+ case MODEM_LORA:
+ SubgRf.ModulationParams.PacketType = PACKET_TYPE_LORA;
+ SubgRf.ModulationParams.Params.LoRa.SpreadingFactor = ( RadioLoRaSpreadingFactors_t ) datarate;
+ SubgRf.ModulationParams.Params.LoRa.Bandwidth = Bandwidths[bandwidth];
+ SubgRf.ModulationParams.Params.LoRa.CodingRate= ( RadioLoRaCodingRates_t )coderate;
+
+ if( ( ( bandwidth == 0 ) && ( ( datarate == 11 ) || ( datarate == 12 ) ) ) ||
+ ( ( bandwidth == 1 ) && ( datarate == 12 ) ) )
+ {
+ SubgRf.ModulationParams.Params.LoRa.LowDatarateOptimize = 0x01;
+ }
+ else
+ {
+ SubgRf.ModulationParams.Params.LoRa.LowDatarateOptimize = 0x00;
+ }
+
+ SubgRf.PacketParams.PacketType = PACKET_TYPE_LORA;
+
+ if( ( SubgRf.ModulationParams.Params.LoRa.SpreadingFactor == LORA_SF5 ) ||
+ ( SubgRf.ModulationParams.Params.LoRa.SpreadingFactor == LORA_SF6 ) )
+ {
+ if( preambleLen < 12 )
+ {
+ SubgRf.PacketParams.Params.LoRa.PreambleLength = 12;
+ }
+ else
+ {
+ SubgRf.PacketParams.Params.LoRa.PreambleLength = preambleLen;
+ }
+ }
+ else
+ {
+ SubgRf.PacketParams.Params.LoRa.PreambleLength = preambleLen;
+ }
+
+ SubgRf.PacketParams.Params.LoRa.HeaderType = ( RadioLoRaPacketLengthsMode_t )fixLen;
+ SubgRf.PacketParams.Params.LoRa.PayloadLength = MaxPayloadLength;
+ SubgRf.PacketParams.Params.LoRa.CrcMode = ( RadioLoRaCrcModes_t )crcOn;
+ SubgRf.PacketParams.Params.LoRa.InvertIQ = ( RadioLoRaIQModes_t )iqInverted;
+
+ RadioStandby( );
+ RadioSetModem( ( SubgRf.ModulationParams.PacketType == PACKET_TYPE_GFSK ) ? MODEM_FSK : MODEM_LORA );
+ SUBGRF_SetModulationParams( &SubgRf.ModulationParams );
+ SUBGRF_SetPacketParams( &SubgRf.PacketParams );
+ break;
+ default: break;
+ }
+
+ // WORKAROUND - Modulation Quality with 500 kHz LoRa® Bandwidth, see DS_SX1261-2_V1.2 datasheet chapter 15.1
+ if( ( modem == MODEM_LORA ) && ( SubgRf.ModulationParams.Params.LoRa.Bandwidth == LORA_BW_500 ) )
+ {
+ // RegTxModulation = @address 0x0889
+ SUBGRF_WriteRegister( 0x0889, SUBGRF_ReadRegister( 0x0889 ) & ~( 1 << 2 ) );
+ }
+ else
+ {
+ // RegTxModulation = @address 0x0889
+ SUBGRF_WriteRegister( 0x0889, SUBGRF_ReadRegister( 0x0889 ) | ( 1 << 2 ) );
+ }
+ // WORKAROUND END
+
+ AntSwitchPower = SUBGRF_SetTxPower( power, RADIO_RAMP_40_US );
+ SubgRf.TxTimeout = timeout;
+}
+
+bool RadioCheckRfFrequency( uint32_t frequency )
+{
+ return true;
+}
+
+uint32_t RadioTimeOnAir( RadioModems_t modem, uint8_t pktLen )
+{
+ uint32_t airTime = 0;
+
+ switch( modem )
+ {
+ case MODEM_FSK:
+ {
+ airTime = (uint32_t)rint( ( 8 * ( SubgRf.PacketParams.Params.Gfsk.PreambleLength +
+ ( SubgRf.PacketParams.Params.Gfsk.SyncWordLength >> 3 ) +
+ ( ( SubgRf.PacketParams.Params.Gfsk.HeaderType == RADIO_PACKET_FIXED_LENGTH ) ? 0.0 : 1.0 ) +
+ pktLen +
+ ( ( SubgRf.PacketParams.Params.Gfsk.CrcLength == RADIO_CRC_2_BYTES ) ? 2.0 : 0 ) ) /
+ SubgRf.ModulationParams.Params.Gfsk.BitRate ) * 1e3 );
+ }
+ break;
+ case MODEM_LORA:
+ {
+ double ts = RadioLoRaSymbTime[SubgRf.ModulationParams.Params.LoRa.Bandwidth - 4][12 - SubgRf.ModulationParams.Params.LoRa.SpreadingFactor];
+ // time of preamble
+ double tPreamble = ( SubgRf.PacketParams.Params.LoRa.PreambleLength + 4.25 ) * ts;
+ // Symbol length of payload and time
+ double tmp = ceil( ( 8 * pktLen - 4 * SubgRf.ModulationParams.Params.LoRa.SpreadingFactor +
+ 28 + 16 * SubgRf.PacketParams.Params.LoRa.CrcMode -
+ ( ( SubgRf.PacketParams.Params.LoRa.HeaderType == LORA_PACKET_FIXED_LENGTH ) ? 20 : 0 ) ) /
+ ( double )( 4 * ( SubgRf.ModulationParams.Params.LoRa.SpreadingFactor -
+ ( ( SubgRf.ModulationParams.Params.LoRa.LowDatarateOptimize > 0 ) ? 2 : 0 ) ) ) ) *
+ ( ( SubgRf.ModulationParams.Params.LoRa.CodingRate % 4 ) + 4 );
+ double nPayload = 8 + ( ( tmp > 0 ) ? tmp : 0 );
+ double tPayload = nPayload * ts;
+ // Time on air
+ double tOnAir = tPreamble + tPayload;
+ // return milli seconds
+ airTime = (uint32_t) floor( tOnAir + 0.999 );
+ }
+ break;
+ default :break;
+ }
+
+ return airTime;
+}
+
+
+void RadioSend( uint8_t *buffer, uint8_t size )
+{
+ /* Radio IRQ is set to DIO1 by default */
+ SUBGRF_SetDioIrqParams( IRQ_TX_DONE | IRQ_RX_TX_TIMEOUT,
+ IRQ_TX_DONE | IRQ_RX_TX_TIMEOUT,
+ IRQ_RADIO_NONE,
+ IRQ_RADIO_NONE );
+
+//Set DBG pin PB13
+#ifdef LET_SUBGHZ_MW_USING_DGB_LINE2_PIN
+ DBG_GPIO_SET(DGB_LINE2_PORT, DGB_LINE2_PIN);
+#endif
+
+/* Set RF switch */
+ SUBGRF_SetSwitch(AntSwitchPower, RFSWITCH_TX);
+
+ switch(SubgRf.modem)
+ {
+ case MODEM_LORA:
+ {
+ SubgRf.PacketParams.Params.LoRa.PayloadLength = size;
+ SUBGRF_SetPacketParams( &SubgRf.PacketParams );
+ SUBGRF_SendPayload( buffer, size, 0 );
+ break;
+ }
+ case MODEM_FSK:
+ {
+ SubgRf.PacketParams.Params.Gfsk.PayloadLength = size;
+ SUBGRF_SetPacketParams( &SubgRf.PacketParams );
+ SUBGRF_SendPayload( buffer, size, 0 );
+ break;
+ }
+
+ case MODEM_BPSK:
+ SubgRf.PacketParams.PacketType=PACKET_TYPE_BPSK;
+ SubgRf.PacketParams.Params.Bpsk.PayloadLength = size;
+ SUBGRF_SetPacketParams( &SubgRf.PacketParams );
+
+ SUBGRF_SendPayload( buffer, size, 0 );
+ break;
+ case MODEM_SIGFOX_TX:
+ {
+ uint8_t outBuffer[35] = {0};
+ /*from bpsk to dbpsk*/
+ /*first 1 bit duplicated*/
+ payload_integration( outBuffer, buffer, size );
+
+ SubgRf.PacketParams.PacketType=PACKET_TYPE_BPSK;
+ SubgRf.PacketParams.Params.Bpsk.PayloadLength = size+1;
+ SUBGRF_SetPacketParams( &SubgRf.PacketParams );
+
+
+ if( SubgRf.ModulationParams.Params.Bpsk.BitRate == 100 )
+ {
+ RadioWrite( 0x00F1, 0x0F ); // clean start-up LSB
+ RadioWrite( 0x00F0, 0x37 ); // clean start-up MSB
+ RadioWrite( 0x00F3, 0x70 ); // clean end of frame LSB
+ RadioWrite( 0x00F2, 0x1D ); // clean end of frame MSB
+ }
+ else // 600 bps
+ {
+ RadioWrite( 0x00F1, 0x2F ); // clean start-up LSB
+ RadioWrite( 0x00F0, 0x09 ); // clean start-up MSB
+ RadioWrite( 0x00F3, 0xE1 ); // clean end of frame LSB
+ RadioWrite( 0x00F2, 0x04 ); // clean end of frame MSB
+ }
+
+ uint16_t bitNum = (size*8)+2;
+ RadioWrite( 0x00F4, ( bitNum >> 8 ) & 0x00FF ); // limit frame
+ RadioWrite( 0x00F5, bitNum & 0x00FF ); // limit frame
+ //
+ SUBGRF_SendPayload( outBuffer, size+1 , 0xFFFFFF );
+ break;
+ }
+ default:
+ break;
+ }
+
+
+ TimerSetValue( &TxTimeoutTimer, SubgRf.TxTimeout );
+ TimerStart( &TxTimeoutTimer );
+}
+
+void RadioSleep( void )
+{
+ SleepParams_t params = { 0 };
+
+ params.Fields.WarmStart = 1;
+ SUBGRF_SetSleep( params );
+
+ RADIO_DELAY_MS( 2 );
+}
+
+void RadioStandby( void )
+{
+ SUBGRF_SetStandby( STDBY_RC );
+}
+
+void RadioRx( uint32_t timeout )
+{
+
+ /* Radio IRQ is set to DIO1 by default */
+ SUBGRF_SetDioIrqParams( IRQ_RADIO_ALL, //IRQ_RX_DONE | IRQ_RX_TX_TIMEOUT,
+ IRQ_RADIO_ALL, //IRQ_RX_DONE | IRQ_RX_TX_TIMEOUT,
+ IRQ_RADIO_NONE,
+ IRQ_RADIO_NONE );
+
+ if( timeout != 0 )
+ {
+ TimerSetValue( &RxTimeoutTimer, timeout );
+ TimerStart( &RxTimeoutTimer );
+ }
+
+ // Set DBG pin PB12
+#ifdef LET_SUBGHZ_MW_USING_DGB_LINE1_PIN
+ DBG_GPIO_SET(DGB_LINE1_PORT, DGB_LINE1_PIN);
+#endif
+ /* RF switch configuration */
+ SUBGRF_SetSwitch(AntSwitchPower, RFSWITCH_RX );
+ if( SubgRf.RxContinuous == true )
+ {
+ SUBGRF_SetRx( 0xFFFFFF ); // Rx Continuous
+ }
+ else
+ {
+ SUBGRF_SetRx( SubgRf.RxTimeout << 6 );
+ }
+}
+
+void RadioRxBoosted( uint32_t timeout )
+{
+ SUBGRF_SetDioIrqParams( IRQ_RADIO_ALL, //IRQ_RX_DONE | IRQ_RX_TX_TIMEOUT,
+ IRQ_RADIO_ALL, //IRQ_RX_DONE | IRQ_RX_TX_TIMEOUT,
+ IRQ_RADIO_NONE,
+ IRQ_RADIO_NONE );
+
+ if( timeout != 0 )
+ {
+ TimerSetValue( &RxTimeoutTimer, timeout );
+ TimerStart( &RxTimeoutTimer );
+ }
+
+ /* RF switch configuration */
+ SUBGRF_SetSwitch(AntSwitchPower, RFSWITCH_RX);
+ if( SubgRf.RxContinuous == true )
+ {
+ SUBGRF_SetRxBoosted( 0xFFFFFF ); // Rx Continuous
+ }
+ else
+ {
+ SUBGRF_SetRxBoosted( SubgRf.RxTimeout << 6 );
+ }
+}
+
+void RadioSetRxDutyCycle( uint32_t rxTime, uint32_t sleepTime )
+{
+ /* RF switch configuration */
+ SUBGRF_SetSwitch(AntSwitchPower, RFSWITCH_RX);
+ SUBGRF_SetRxDutyCycle( rxTime, sleepTime );
+}
+
+void RadioStartCad( void )
+{
+ SUBGRF_SetCad( );
+}
+
+void RadioTx( uint32_t timeout )
+{
+ SUBGRF_SetTx( timeout << 6 );
+}
+
+void RadioSetTxContinuousWave( uint32_t freq, int8_t power, uint16_t time )
+{
+ uint8_t antswitchpow;
+
+ SUBGRF_SetRfFrequency( freq );
+
+ antswitchpow = SUBGRF_SetTxPower( power, RADIO_RAMP_40_US );
+
+ /* Set RF switch */
+ SUBGRF_SetSwitch(antswitchpow, RFSWITCH_TX);
+
+ SUBGRF_SetTxContinuousWave( );
+
+ TimerSetValue( &TxTimeoutTimer, (uint32_t)(time * 1e3 ));
+ TimerStart( &TxTimeoutTimer );
+}
+
+int16_t RadioRssi( RadioModems_t modem )
+{
+ return SUBGRF_GetRssiInst( );
+}
+
+void RadioWrite( uint16_t addr, uint8_t data )
+{
+ SUBGRF_WriteRegister(addr, data );
+}
+
+uint8_t RadioRead( uint16_t addr )
+{
+ return SUBGRF_ReadRegister(addr);
+}
+
+void RadioWriteBuffer( uint16_t addr, uint8_t *buffer, uint8_t size )
+{
+ SUBGRF_WriteRegisters( addr, buffer, size );
+}
+
+void RadioReadBuffer( uint16_t addr, uint8_t *buffer, uint8_t size )
+{
+ SUBGRF_ReadRegisters( addr, buffer, size );
+}
+
+//void RadioWriteFifo( uint8_t *buffer, uint8_t size )
+//{
+// SUBGRF_WriteBuffer( 0, buffer, size );
+//}
+//
+//void RadioReadFifo( uint8_t *buffer, uint8_t size )
+//{
+// SUBGRF_ReadBuffer( 0, buffer, size );
+//}
+
+void RadioSetMaxPayloadLength( RadioModems_t modem, uint8_t max )
+{
+ if( modem == MODEM_LORA )
+ {
+ SubgRf.PacketParams.Params.LoRa.PayloadLength = MaxPayloadLength = max;
+ SUBGRF_SetPacketParams( &SubgRf.PacketParams );
+ }
+ else
+ {
+ if( SubgRf.PacketParams.Params.Gfsk.HeaderType == RADIO_PACKET_VARIABLE_LENGTH )
+ {
+ SubgRf.PacketParams.Params.Gfsk.PayloadLength = MaxPayloadLength = max;
+ SUBGRF_SetPacketParams( &SubgRf.PacketParams );
+ }
+ }
+}
+
+void RadioSetPublicNetwork( bool enable )
+{
+ uint8_t reg;
+
+ SubgRf.publicNetwork= enable;
+
+ if( enable == true )
+ {
+ // Change LoRa modem SyncWord
+ reg = ( LORA_MAC_PUBLIC_SYNCWORD >> 8 ) & 0xFF;
+ SUBGRF_WriteRegisters( REG_LR_SYNCWORD, (uint8_t*)®, 1 );
+ reg = LORA_MAC_PUBLIC_SYNCWORD & 0xFF;
+ SUBGRF_WriteRegisters( REG_LR_SYNCWORD + 1, (uint8_t*)®, 1 );
+ }
+ else
+ {
+ // Change LoRa modem SyncWord
+ reg = ( LORA_MAC_PRIVATE_SYNCWORD >> 8 ) & 0xFF;
+ SUBGRF_WriteRegisters( REG_LR_SYNCWORD, (uint8_t*)®, 1 );
+ reg = LORA_MAC_PRIVATE_SYNCWORD & 0xFF;
+ SUBGRF_WriteRegisters( REG_LR_SYNCWORD + 1, (uint8_t*)®, 1 );
+ }
+}
+
+uint32_t RadioGetWakeUpTime( void )
+{
+ return (SUBGRF_GetRadioWakeUpTime() + RADIO_WAKEUP_TIME);
+}
+
+
+void RadioOnTxTimeoutIrq( void* context )
+{
+ // Reset DBG pin PB13
+#ifdef LET_SUBGHZ_MW_USING_DGB_LINE2_PIN
+ DBG_GPIO_RST(DGB_LINE2_PORT, DGB_LINE2_PIN);
+#endif
+ if( ( RadioEvents != NULL ) && ( RadioEvents->TxTimeout != NULL ) )
+ {
+ RadioEvents->TxTimeout( );
+ }
+}
+
+void RadioOnRxTimeoutIrq( void* context )
+{
+ // Reset DBG pin PB12
+#ifdef LET_SUBGHZ_MW_USING_DGB_LINE1_PIN
+ DBG_GPIO_RST(DGB_LINE1_PORT, DGB_LINE1_PIN);
+#endif
+ if( ( RadioEvents != NULL ) && ( RadioEvents->RxTimeout != NULL ) )
+ {
+ RadioEvents->RxTimeout( );
+ }
+}
+
+void RadioOnDioIrq( RadioIrqMasks_t radioIrq )
+{
+ SubgRf.radioIrq=radioIrq;
+
+ RadioIrqProcess();
+
+}
+
+
+void RadioIrqProcess(void )
+{
+ PacketStatus_t RadioPktStatus;
+ uint8_t size;
+
+ switch (SubgRf.radioIrq)
+ {
+ case IRQ_TX_DONE:
+ // Reset DBG pin PB13
+#ifdef LET_SUBGHZ_MW_USING_DGB_LINE2_PIN
+ DBG_GPIO_RST(DGB_LINE2_PORT, DGB_LINE2_PIN);
+#endif
+ TimerStop( &TxTimeoutTimer );
+ SUBGRF_SetStandby( STDBY_RC );
+ if( ( RadioEvents != NULL ) && ( RadioEvents->TxDone != NULL ) )
+ {
+ RadioEvents->TxDone( );
+ }
+ break;
+
+ case IRQ_RX_DONE:
+ // Reset DBG pin PB12
+#ifdef LET_SUBGHZ_MW_USING_DGB_LINE1_PIN
+ DBG_GPIO_RST(DGB_LINE1_PORT, DGB_LINE1_PIN);
+#endif
+ TimerStop( &RxTimeoutTimer );
+ if( SubgRf.RxContinuous == false )
+ {
+ //!< Update operating mode state to a value lower than \ref MODE_STDBY_XOSC
+ SUBGRF_SetStandby( STDBY_RC );
+
+ // WORKAROUND - Implicit Header Mode Timeout Behavior, see DS_SX1261-2_V1.2 datasheet chapter 15.3
+ // RegRtcControl = @address 0x0902
+ SUBGRF_WriteRegister( 0x0902, 0x00 );
+ // RegEventMask = @address 0x0944
+ SUBGRF_WriteRegister( 0x0944, SUBGRF_ReadRegister( 0x0944 ) | ( 1 << 1 ) );
+ // WORKAROUND END
+ }
+ SUBGRF_GetPayload( RadioRxPayload, &size , 255 );
+ SUBGRF_GetPacketStatus( &RadioPktStatus );
+ if( ( RadioEvents != NULL ) && ( RadioEvents->RxDone != NULL ) )
+ {
+ switch (RadioPktStatus.packetType)
+ {
+ case PACKET_TYPE_LORA:
+ RadioEvents->RxDone( RadioRxPayload, size, RadioPktStatus.Params.LoRa.RssiPkt, RadioPktStatus.Params.LoRa.SnrPkt );
+ break;
+ default:
+ RadioEvents->RxDone( RadioRxPayload, size, RadioPktStatus.Params.Gfsk.RssiAvg, (int8_t)(RadioPktStatus.Params.Gfsk.FreqError) );
+ break;
+ }
+ }
+ break;
+
+ case IRQ_CRC_ERROR:
+
+ if( SubgRf.RxContinuous == false )
+ {
+ //!< Update operating mode state to a value lower than \ref MODE_STDBY_XOSC
+ SUBGRF_SetStandby( STDBY_RC );
+ }
+ if( ( RadioEvents != NULL ) && ( RadioEvents->RxError ) )
+ {
+ RadioEvents->RxError( );
+ }
+ break;
+
+
+ case IRQ_CAD_DONE:
+ //!< Update operating mode state to a value lower than \ref MODE_STDBY_XOSC
+ SUBGRF_SetStandby( STDBY_RC );
+ if( ( RadioEvents != NULL ) && ( RadioEvents->CadDone != NULL ) )
+ {
+ RadioEvents->CadDone( ( ( SubgRf.radioIrq & IRQ_CAD_ACTIVITY_DETECTED ) == IRQ_CAD_ACTIVITY_DETECTED ) );
+ }
+ break;
+
+ case IRQ_RX_TX_TIMEOUT:
+ if( SUBGRF_GetOperatingMode( ) == MODE_TX )
+ {
+ // Reset DBG pin PB13
+#ifdef LET_SUBGHZ_MW_USING_DGB_LINE2_PIN
+ DBG_GPIO_RST(DGB_LINE2_PORT, DGB_LINE2_PIN);
+#endif
+ TimerStop( &TxTimeoutTimer );
+ //!< Update operating mode state to a value lower than \ref MODE_STDBY_XOSC
+ SUBGRF_SetStandby( STDBY_RC );
+ if( ( RadioEvents != NULL ) && ( RadioEvents->TxTimeout != NULL ) )
+ {
+ RadioEvents->TxTimeout( );
+ }
+ }
+ else if( SUBGRF_GetOperatingMode( ) == MODE_RX )
+ {
+ // Reset DBG pin PB12
+#ifdef LET_SUBGHZ_MW_USING_DGB_LINE1_PIN
+ DBG_GPIO_RST(DGB_LINE1_PORT, DGB_LINE1_PIN);
+#endif
+ TimerStop( &RxTimeoutTimer );
+ //!< Update operating mode state to a value lower than \ref MODE_STDBY_XOSC
+ SUBGRF_SetStandby( STDBY_RC );
+ if( ( RadioEvents != NULL ) && ( RadioEvents->RxTimeout != NULL ) )
+ {
+ RadioEvents->RxTimeout( );
+ }
+ }
+ break;
+
+ case IRQ_PREAMBLE_DETECTED:
+ MW_LOG( "PRE OK\n\r" );
+ break;
+
+ case IRQ_SYNCWORD_VALID:
+
+ MW_LOG( "SYNC OK\n\r" );
+ break;
+
+ case IRQ_HEADER_VALID:
+
+ MW_LOG( "HDR OK\n\r" );
+ break;
+
+ case IRQ_HEADER_ERROR:
+ TimerStop( &RxTimeoutTimer );
+ if( SubgRf.RxContinuous == false )
+ {
+ //!< Update operating mode state to a value lower than \ref MODE_STDBY_XOSC
+ SUBGRF_SetStandby( STDBY_RC );
+ }
+ if( ( RadioEvents != NULL ) && ( RadioEvents->RxTimeout != NULL ) )
+ {
+ RadioEvents->RxTimeout( );
+ MW_LOG( "HDR KO\n\r" );
+ }
+ break;
+
+ default:
+ break;
+
+ }
+}
+
+void RadioTxPrbs( void )
+{
+ SUBGRF_SetSwitch(AntSwitchPower, RFSWITCH_TX);
+ Radio.Write(0x6B8, 0x2d); // sel mode prbs9 instead of preamble
+ SUBGRF_SetTxInfinitePreamble();
+ SUBGRF_SetTx(0x0fffff);
+}
+
+void RadioTxCw( void )
+{
+ SUBGRF_SetSwitch(AntSwitchPower, RFSWITCH_TX);
+
+ SUBGRF_SetTxContinuousWave();
+}
+
+/*!
+* @brief D-BPSK to BPSK
+*
+* @param [in] inBuffer buffer with frame to encode
+* @param [out] outBuffer buffer with frame encoded
+* @param [in] size size of the payload to encode
+*/
+static void payload_integration( uint8_t *outBuffer, uint8_t *inBuffer, uint8_t size)
+{
+ uint8_t prevInt=0;
+ uint8_t currBit;
+ uint8_t index_bit;
+ uint8_t index_byte;
+ uint8_t index_bit_out;
+ uint8_t index_byte_out;
+ int i=0;
+
+ for (i=0; i> index_bit) & 0x01;
+ /*integration*/
+ prevInt ^= currBit;
+ /* write result integration in output*/
+ outBuffer[index_byte_out]|= (prevInt << index_bit_out);
+ }
+
+ outBuffer[size] =(prevInt<<7) | (prevInt<<6) | (( (!prevInt) & 0x01)<<5) ;
+}
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/stm32_radio_driver/radio_conf_template.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/stm32_radio_driver/radio_conf_template.h
new file mode 100644
index 00000000..12ca6737
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/stm32_radio_driver/radio_conf_template.h
@@ -0,0 +1,51 @@
+ /*******************************************************************************
+ * @file radio_conf.h
+ * @author MCD Application Team
+ * @brief driver sx1276 board (sx1276mb1mas for EU and sx1276mb1las for US)
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under BSD 3-Clause license,
+ * the "License"; You may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ * opensource.org/licenses/BSD-3-Clause
+ *
+ ******************************************************************************
+ */
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __RADIO_CONF_H__
+#define __RADIO_CONF_H__
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+#include "platform.h"
+
+/* Exported types ------------------------------------------------------------*/
+/* Defines ------------------------*/
+#define RADIO_RX_BUF_SIZE 255
+
+/* Function mapping */
+#define RADIO_DELAY_MS HAL_Delay
+#define RADIO_MEMSET8( dest, value, size ) UTIL_MEM_cpy_8( dest, value, size)
+
+#define LET_SUBGHZ_MW_USING_DGB_LINE1_PIN
+#define LET_SUBGHZ_MW_USING_DGB_LINE2_PIN
+
+/* Exported constants --------------------------------------------------------*/
+/* External variables --------------------------------------------------------*/
+/* Exported macros -----------------------------------------------------------*/
+/* Exported functions ------------------------------------------------------- */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __RADIO_CONF_H__*/
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/stm32_radio_driver/radio_driver.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/stm32_radio_driver/radio_driver.c
new file mode 100644
index 00000000..95e4bc1c
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/stm32_radio_driver/radio_driver.c
@@ -0,0 +1,1168 @@
+/*
+ ______ _
+ / _____) _ | |
+( (____ _____ ____ _| |_ _____ ____| |__
+ \____ \| ___ | (_ _) ___ |/ ___) _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+ (C)2017 Semtech
+
+Description: Generic SX126x driver implementation
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+
+Authors: Miguel Luis, Gregory Cristian
+*/
+/**
+ ******************************************************************************
+ *
+ * Portions COPYRIGHT 2019 STMicroelectronics
+ *
+ * @file radio_driver.c
+ * @author MCD Application Team
+ * @brief radio driver implementation
+ ******************************************************************************
+ */
+
+/* Includes ------------------------------------------------------------------*/
+#include "radio_conf.h"
+#include "mw_log_conf.h"
+#include "stm32_lpm.h"
+
+
+
+/*
+ radio io function
+*/
+/*============================================================================*/
+/** @addtogroup STM32WLXX_NUCLEO_RADIO_LOW_LEVEL_Exported_Functions
+ * @{
+ */
+
+/**
+ * @brief Init Radio Switch
+ * @retval BSP status
+ */
+int32_t BSP_RADIO_Init(void)
+{
+ GPIO_InitTypeDef gpio_init_structure = {0};
+
+ /* Enable the Radio Switch Clock */
+ RF_SW_CTRL3_GPIO_CLK_ENABLE();
+
+ /* Configure the Radio Switch pin */
+ gpio_init_structure.Pin = RF_SW_CTRL1_PIN;
+ gpio_init_structure.Mode = GPIO_MODE_OUTPUT_PP;
+ gpio_init_structure.Pull = GPIO_NOPULL;
+ gpio_init_structure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
+
+ HAL_GPIO_Init(RF_SW_CTRL1_GPIO_PORT, &gpio_init_structure);
+
+ gpio_init_structure.Pin = RF_SW_CTRL2_PIN;
+ HAL_GPIO_Init(RF_SW_CTRL2_GPIO_PORT, &gpio_init_structure);
+
+ gpio_init_structure.Pin = RF_SW_CTRL3_PIN;
+ HAL_GPIO_Init(RF_SW_CTRL3_GPIO_PORT, &gpio_init_structure);
+
+ HAL_GPIO_WritePin(RF_SW_CTRL2_GPIO_PORT, RF_SW_CTRL2_PIN, GPIO_PIN_RESET);
+ HAL_GPIO_WritePin(RF_SW_CTRL1_GPIO_PORT, RF_SW_CTRL1_PIN, GPIO_PIN_RESET);
+ HAL_GPIO_WritePin(RF_SW_CTRL3_GPIO_PORT, RF_SW_CTRL3_PIN, GPIO_PIN_RESET);
+
+ return BSP_ERROR_NONE;
+}
+
+/**
+ * @brief DeInit Radio Swicth
+ * @retval BSP status
+ */
+int32_t BSP_RADIO_DeInit(void)
+{
+ RF_SW_CTRL3_GPIO_CLK_ENABLE();
+
+ /* Turn off switch */
+ HAL_GPIO_WritePin(RF_SW_CTRL1_GPIO_PORT, RF_SW_CTRL1_PIN, GPIO_PIN_RESET);
+ HAL_GPIO_WritePin(RF_SW_CTRL2_GPIO_PORT, RF_SW_CTRL2_PIN, GPIO_PIN_RESET);
+ HAL_GPIO_WritePin(RF_SW_CTRL3_GPIO_PORT, RF_SW_CTRL3_PIN, GPIO_PIN_RESET);
+
+ /* DeInit the Radio Switch pin */
+ HAL_GPIO_DeInit(RF_SW_CTRL1_GPIO_PORT, RF_SW_CTRL1_PIN);
+ HAL_GPIO_DeInit(RF_SW_CTRL2_GPIO_PORT, RF_SW_CTRL2_PIN);
+ HAL_GPIO_DeInit(RF_SW_CTRL3_GPIO_PORT, RF_SW_CTRL3_PIN);
+
+ return BSP_ERROR_NONE;
+}
+
+/**
+ * @brief Configure Radio Switch.
+ * @param Config: Specifies the Radio RF switch path to be set.
+ * This parameter can be one of following parameters:
+ * @arg RADIO_SWITCH_OFF
+ * @arg RADIO_SWITCH_RX
+ * @arg RADIO_SWITCH_RFO_LP
+ * @arg RADIO_SWITCH_RFO_HP
+ * @retval BSP status
+ */
+int32_t BSP_RADIO_ConfigRFSwitch(BSP_RADIO_Switch_TypeDef Config)
+{
+ switch (Config)
+ {
+ case RADIO_SWITCH_OFF:
+ {
+ /* Turn off switch */
+ HAL_GPIO_WritePin(RF_SW_CTRL3_GPIO_PORT, RF_SW_CTRL3_PIN, GPIO_PIN_RESET);
+ HAL_GPIO_WritePin(RF_SW_CTRL1_GPIO_PORT, RF_SW_CTRL1_PIN, GPIO_PIN_RESET);
+ HAL_GPIO_WritePin(RF_SW_CTRL2_GPIO_PORT, RF_SW_CTRL2_PIN, GPIO_PIN_RESET);
+ break;
+ }
+ case RADIO_SWITCH_RX:
+ {
+ /*Turns On in Rx Mode the RF Swicth */
+ HAL_GPIO_WritePin(RF_SW_CTRL3_GPIO_PORT, RF_SW_CTRL3_PIN, GPIO_PIN_SET);
+ HAL_GPIO_WritePin(RF_SW_CTRL1_GPIO_PORT, RF_SW_CTRL1_PIN, GPIO_PIN_SET);
+ HAL_GPIO_WritePin(RF_SW_CTRL2_GPIO_PORT, RF_SW_CTRL2_PIN, GPIO_PIN_RESET);
+ break;
+ }
+ case RADIO_SWITCH_RFO_LP:
+ {
+ /*Turns On in Tx Low Power the RF Swicth */
+ HAL_GPIO_WritePin(RF_SW_CTRL3_GPIO_PORT, RF_SW_CTRL3_PIN, GPIO_PIN_SET);
+ HAL_GPIO_WritePin(RF_SW_CTRL1_GPIO_PORT, RF_SW_CTRL1_PIN, GPIO_PIN_SET);
+ HAL_GPIO_WritePin(RF_SW_CTRL2_GPIO_PORT, RF_SW_CTRL2_PIN, GPIO_PIN_SET);
+ break;
+ }
+ case RADIO_SWITCH_RFO_HP:
+ {
+ /*Turns On in Tx High Power the RF Swicth */
+ HAL_GPIO_WritePin(RF_SW_CTRL3_GPIO_PORT, RF_SW_CTRL3_PIN, GPIO_PIN_SET);
+ HAL_GPIO_WritePin(RF_SW_CTRL1_GPIO_PORT, RF_SW_CTRL1_PIN, GPIO_PIN_RESET);
+ HAL_GPIO_WritePin(RF_SW_CTRL2_GPIO_PORT, RF_SW_CTRL2_PIN, GPIO_PIN_SET);
+ break;
+ }
+ default:
+ break;
+ }
+
+ return BSP_ERROR_NONE;
+}
+
+/**
+ * @brief Return Board Configuration
+ * @retval
+ * RADIO_CONF_RFO_LP_HP
+ * RADIO_CONF_RFO_LP
+ * RADIO_CONF_RFO_HP
+ */
+int32_t BSP_RADIO_GetTxConfig(void)
+{
+ return RADIO_CONF_RFO_LP_HP;
+}
+
+/**
+ * @brief Get Radio Wake Time
+ * @retval the wake upt time in ms
+ */
+int32_t BSP_RADIO_GetWakeUpTime(void)
+{
+ return RF_WAKEUP_TIME;
+}
+
+/**
+ * @brief Get If TCXO is to be present on board
+ * @note never remove called by MW,
+ * @retval return 1 if present, 0 if not present
+ */
+int32_t BSP_RADIO_IsTCXO(void)
+{
+ return IS_TCXO_SUPPORTED;
+}
+
+/**
+ * @brief Get If DCDC is to be present on board
+ * @note never remove called by MW,
+ * @retval return 1 if present, 0 if not present
+ */
+int32_t BSP_RADIO_IsDCDC(void)
+{
+ return IS_DCDC_SUPPORTED;
+}
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+/*=============================================================================*/
+/*
+ * Local types definition
+ */
+/*!
+ * @brief Radio registers definition
+ */
+typedef struct
+{
+ uint16_t Addr; //!< The address of the register
+ uint8_t Value; //!< The value of the register
+}RadioRegisters_t;
+
+typedef enum
+{
+ RF_API_STATE_TCXO_OFF=0,
+ RF_API_STATE_TCXO_ON
+} RF_API_Tcxo_State_t;
+/*!
+* \This is the workaround for tcxo bug in cut 1.0
+*/
+static void TCXO_Workaround( RF_API_Tcxo_State_t state );
+
+/*!
+* \This is the workaround for Reset bug in cut 1.0
+*/
+static void RadioReset_Workaround( void );
+
+/*!
+ * @brief Stores the current packet type set in the radio
+ */
+static RadioPacketTypes_t PacketType;
+
+/*!
+ * @brief Stores the last frequency error measured on LoRa received packet
+ */
+volatile uint32_t FrequencyError = 0;
+
+/*!
+ * @brief Hold the status of the Image calibration
+ */
+static bool ImageCalibrated = false;
+
+/*
+ * @brief SUBGHZ_HandleTypeDef structure that contains
+ * the configuration information for SPI module
+ */
+SUBGHZ_HandleTypeDef hsubghz;
+/*
+ * Private functions prototypes
+ */
+
+/*!
+ * @brief Holds the internal operating mode of the radio
+ */
+RadioOperatingModes_t OperatingMode;
+
+void (*RadioOnDioIrqCb) ( RadioIrqMasks_t radioIrq );
+
+/*
+ * Public functions
+ */
+
+uint32_t SUBGRF_GetRadioWakeUpTime( void )
+{
+ return ( uint32_t ) BSP_RADIO_GetWakeUpTime();
+}
+
+RadioOperatingModes_t SUBGRF_GetOperatingMode( void )
+{
+ return OperatingMode;
+}
+
+int32_t SUBGRF_Init( void (*RadioOnDioIrq) ( RadioIrqMasks_t radioIrq ) )
+{
+ if ( RadioOnDioIrq!=NULL)
+ {
+ RadioOnDioIrqCb = RadioOnDioIrq;
+ }
+
+ TCXO_Workaround(RF_API_STATE_TCXO_ON);
+
+ hsubghz.Init.BaudratePrescaler=SUBGHZSPI_BAUDRATEPRESCALER_4;
+
+ HAL_SUBGHZ_Init(&hsubghz);
+
+ ImageCalibrated = false;
+
+ RadioReset_Workaround();
+
+ SUBGRF_SetStandby( STDBY_RC );
+
+ if (1U ==BSP_RADIO_IsTCXO() )
+ {
+ uint8_t reg = 0x2F;
+
+ SUBGRF_SetTcxoMode( TCXO_CTRL_1_8V, 64 * BSP_RADIO_GetWakeUpTime() );// 100 ms
+
+ HAL_SUBGHZ_WriteRegisters( &hsubghz, REG_XTA_TRIM, (uint8_t*)® , 1);
+
+ /*enable calibration for cut1.1 and later*/
+ if (LL_DBGMCU_GetRevisionID() !=0x1000)
+ {
+ CalibrationParams_t calibParam;
+ calibParam.Value = 0x7F;
+ SUBGRF_Calibrate( calibParam );
+ }
+ }
+ /* Init RF Switch */
+ BSP_RADIO_Init();
+
+ OperatingMode = MODE_STDBY_RC;
+
+ TCXO_Workaround(RF_API_STATE_TCXO_OFF);
+
+ return SUBGRF_OK;
+}
+
+void SUBGRF_SetPayload( uint8_t *payload, uint8_t size )
+{
+ HAL_SUBGHZ_WriteBuffer( &hsubghz, 0x00, payload, size );
+}
+
+uint8_t SUBGRF_GetPayload( uint8_t *buffer, uint8_t *size, uint8_t maxSize )
+{
+ uint8_t offset = 0;
+
+ SUBGRF_GetRxBufferStatus( size, &offset );
+ if( *size > maxSize )
+ {
+ return 1;
+ }
+ HAL_SUBGHZ_ReadBuffer( &hsubghz, offset, buffer, *size );
+ return 0;
+}
+
+void SUBGRF_SendPayload( uint8_t *payload, uint8_t size, uint32_t timeout)
+{
+ SUBGRF_SetPayload( payload, size );
+ SUBGRF_SetTx( timeout );
+}
+
+void SUBGRF_SetSwitch (uint8_t power, RFState_t rxtx)
+{
+ BSP_RADIO_Switch_TypeDef state;
+
+ if (rxtx == RFSWITCH_TX)
+ {
+ if (power == RFO_LP)
+ {
+ state = RADIO_SWITCH_RFO_LP;
+ }
+ if (power == RFO_HP)
+ {
+ state = RADIO_SWITCH_RFO_HP;
+ }
+ }
+ else
+ {
+ if (rxtx == RFSWITCH_RX)
+ {
+ state = RADIO_SWITCH_RX;
+ }
+ }
+ BSP_RADIO_ConfigRFSwitch(state);
+}
+
+uint8_t SUBGRF_SetSyncWord( uint8_t *syncWord )
+{
+ HAL_SUBGHZ_WriteRegisters( &hsubghz,REG_LR_SYNCWORDBASEADDRESS, syncWord, 8 );
+ return 0;
+}
+
+void SUBGRF_SetCrcSeed( uint16_t seed )
+{
+ uint8_t buf[2];
+
+ buf[0] = ( uint8_t )( ( seed >> 8 ) & 0xFF );
+ buf[1] = ( uint8_t )( seed & 0xFF );
+
+ switch( SUBGRF_GetPacketType( ) )
+ {
+ case PACKET_TYPE_GFSK:
+ HAL_SUBGHZ_WriteRegisters( &hsubghz, REG_LR_CRCSEEDBASEADDR, buf, 2 );
+ break;
+ default:
+ break;
+ }
+}
+
+void SUBGRF_SetCrcPolynomial( uint16_t polynomial )
+{
+ uint8_t buf[2];
+
+ buf[0] = ( uint8_t )( ( polynomial >> 8 ) & 0xFF );
+ buf[1] = ( uint8_t )( polynomial & 0xFF );
+
+ switch( SUBGRF_GetPacketType( ) )
+ {
+ case PACKET_TYPE_GFSK:
+ HAL_SUBGHZ_WriteRegisters( &hsubghz, REG_LR_CRCPOLYBASEADDR, buf, 2 );
+ break;
+
+ default:
+ break;
+ }
+}
+
+void SUBGRF_SetWhiteningSeed( uint16_t seed )
+{
+ uint8_t regValue = 0;
+
+ switch( SUBGRF_GetPacketType( ) )
+ {
+ case PACKET_TYPE_GFSK:
+ HAL_SUBGHZ_ReadRegisters( &hsubghz, REG_LR_WHITSEEDBASEADDR_MSB, ®Value, 1 );
+ regValue = regValue & 0xFE;
+ regValue = ( ( seed >> 8 ) & 0x01 ) | regValue;
+ HAL_SUBGHZ_WriteRegisters( &hsubghz, REG_LR_WHITSEEDBASEADDR_MSB, (uint8_t*)®Value, 1 ); // only 1 bit.
+ HAL_SUBGHZ_WriteRegisters( &hsubghz, REG_LR_WHITSEEDBASEADDR_LSB, (uint8_t*)&seed, 1);
+ break;
+
+ default:
+ break;
+ }
+}
+
+uint32_t SUBGRF_GetRandom( void )
+{
+ uint8_t buf[] = { 0, 0, 0, 0 };
+
+ // Set radio in continuous reception
+ SUBGRF_SetRfFrequency( 400e6 );
+
+ SUBGRF_SetRx( 0 );
+
+ HAL_Delay( 1 );
+
+ HAL_SUBGHZ_ReadRegisters( &hsubghz, RANDOM_NUMBER_GENERATORBASEADDR, buf, 4 );
+
+ SUBGRF_SetStandby( STDBY_RC );
+
+ return ( buf[0] << 24 ) | ( buf[1] << 16 ) | ( buf[2] << 8 ) | buf[3];
+}
+
+void SUBGRF_SetSleep( SleepParams_t sleepConfig )
+{
+ BSP_RADIO_ConfigRFSwitch(RADIO_SWITCH_OFF); /* switch the antenna OFF by SW */
+
+ HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_SLEEP, &sleepConfig.Value, 1 );
+ OperatingMode = MODE_SLEEP;
+
+ TCXO_Workaround(RF_API_STATE_TCXO_OFF);
+}
+
+void SUBGRF_SetStandby( RadioStandbyModes_t standbyConfig )
+{
+ HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_STANDBY, ( uint8_t* )&standbyConfig, 1 );
+ if( standbyConfig == STDBY_RC )
+ {
+ OperatingMode = MODE_STDBY_RC;
+ }
+ else
+ {
+ OperatingMode = MODE_STDBY_XOSC;
+ }
+}
+
+void SUBGRF_SetFs( void )
+{
+ HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_FS, 0, 0 );
+ OperatingMode = MODE_FS;
+}
+
+void SUBGRF_SetTx( uint32_t timeout )
+{
+ uint8_t buf[3];
+
+ OperatingMode = MODE_TX;
+
+ buf[0] = ( uint8_t )( ( timeout >> 16 ) & 0xFF );
+ buf[1] = ( uint8_t )( ( timeout >> 8 ) & 0xFF );
+ buf[2] = ( uint8_t )( timeout & 0xFF );
+ HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_TX, buf, 3 );
+}
+
+void SUBGRF_SetRx( uint32_t timeout )
+{
+ uint8_t buf[3];
+
+ OperatingMode = MODE_RX;
+
+ buf[0] = ( uint8_t )( ( timeout >> 16 ) & 0xFF );
+ buf[1] = ( uint8_t )( ( timeout >> 8 ) & 0xFF );
+ buf[2] = ( uint8_t )( timeout & 0xFF );
+ HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_RX, buf, 3 );
+
+}
+
+void SUBGRF_SetRxBoosted( uint32_t timeout )
+{
+ uint8_t buf[3];
+ uint8_t reg = 0x97;
+ OperatingMode = MODE_RX;
+
+ HAL_SUBGHZ_WriteRegisters( &hsubghz, REG_RX_GAIN, (uint8_t*)®, 1 ); // max LNA gain, increase current by ~2mA for around ~3dB in sensivity
+
+ buf[0] = ( uint8_t )( ( timeout >> 16 ) & 0xFF );
+ buf[1] = ( uint8_t )( ( timeout >> 8 ) & 0xFF );
+ buf[2] = ( uint8_t )( timeout & 0xFF );
+ HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_RX, buf, 3 );
+}
+
+void SUBGRF_SetRxDutyCycle( uint32_t rxTime, uint32_t sleepTime )
+{
+ uint8_t buf[6];
+
+ buf[0] = ( uint8_t )( ( rxTime >> 16 ) & 0xFF );
+ buf[1] = ( uint8_t )( ( rxTime >> 8 ) & 0xFF );
+ buf[2] = ( uint8_t )( rxTime & 0xFF );
+ buf[3] = ( uint8_t )( ( sleepTime >> 16 ) & 0xFF );
+ buf[4] = ( uint8_t )( ( sleepTime >> 8 ) & 0xFF );
+ buf[5] = ( uint8_t )( sleepTime & 0xFF );
+ HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_RXDUTYCYCLE, buf, 6 );
+ OperatingMode = MODE_RX_DC;
+}
+
+void SUBGRF_SetCad( void )
+{
+ HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_CAD, 0, 0 );
+
+ OperatingMode = MODE_CAD;
+}
+
+void SUBGRF_SetTxContinuousWave( void )
+{
+ HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_TXCONTINUOUSWAVE, 0, 0 );
+}
+
+void SUBGRF_SetTxInfinitePreamble( void )
+{
+ HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_TXCONTINUOUSPREAMBLE, 0, 0 );
+}
+
+void SUBGRF_SetStopRxTimerOnPreambleDetect( bool enable )
+{
+ HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_STOPRXTIMERONPREAMBLE, ( uint8_t* )&enable, 1 );
+}
+
+void SUBGRF_SetLoRaSymbNumTimeout( uint8_t SymbNum )
+{
+ HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_LORASYMBTIMEOUT, &SymbNum, 1 );
+}
+
+void SUBGRF_SetRegulatorMode( void )
+{
+ RadioRegulatorMode_t mode;
+
+ if (1U == BSP_RADIO_IsDCDC() )
+ {
+ mode=USE_DCDC ;
+ }
+ else
+ {
+ mode=USE_LDO ;
+ }
+
+ HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_REGULATORMODE, ( uint8_t* )&mode, 1 );
+}
+
+void SUBGRF_Calibrate( CalibrationParams_t calibParam )
+{
+ HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_CALIBRATE, ( uint8_t* )&calibParam, 1 );
+}
+
+void SUBGRF_CalibrateImage( uint32_t freq )
+{
+ uint8_t calFreq[2];
+
+ if( freq > 900000000 )
+ {
+ calFreq[0] = 0xE1;
+ calFreq[1] = 0xE9;
+ }
+ else if( freq > 850000000 )
+ {
+ calFreq[0] = 0xD7;
+ calFreq[1] = 0xD8;
+ }
+ else if( freq > 770000000 )
+ {
+ calFreq[0] = 0xC1;
+ calFreq[1] = 0xC5;
+ }
+ else if( freq > 460000000 )
+ {
+ calFreq[0] = 0x75;
+ calFreq[1] = 0x81;
+ }
+ else if( freq > 425000000 )
+ {
+ calFreq[0] = 0x6B;
+ calFreq[1] = 0x6F;
+ }
+ HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_CALIBRATEIMAGE, calFreq, 2 );
+}
+
+void SUBGRF_SetPaConfig( uint8_t paDutyCycle, uint8_t hpMax, uint8_t deviceSel, uint8_t paLut )
+{
+ uint8_t buf[4];
+
+ buf[0] = paDutyCycle;
+ buf[1] = hpMax;
+ buf[2] = deviceSel;
+ buf[3] = paLut;
+ HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_PACONFIG, buf, 4 );
+}
+
+void SUBGRF_SetRxTxFallbackMode( uint8_t fallbackMode )
+{
+ HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_TXFALLBACKMODE, &fallbackMode, 1 );
+}
+
+void SUBGRF_SetDioIrqParams( uint16_t irqMask, uint16_t dio1Mask, uint16_t dio2Mask, uint16_t dio3Mask )
+{
+ uint8_t buf[8];
+
+ buf[0] = ( uint8_t )( ( irqMask >> 8 ) & 0x00FF );
+ buf[1] = ( uint8_t )( irqMask & 0x00FF );
+ buf[2] = ( uint8_t )( ( dio1Mask >> 8 ) & 0x00FF );
+ buf[3] = ( uint8_t )( dio1Mask & 0x00FF );
+ buf[4] = ( uint8_t )( ( dio2Mask >> 8 ) & 0x00FF );
+ buf[5] = ( uint8_t )( dio2Mask & 0x00FF );
+ buf[6] = ( uint8_t )( ( dio3Mask >> 8 ) & 0x00FF );
+ buf[7] = ( uint8_t )( dio3Mask & 0x00FF );
+ HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_CFG_DIOIRQ, buf, 8 );
+}
+
+uint16_t SUBGRF_GetIrqStatus( void )
+{
+ uint8_t irqStatus[2];
+
+ HAL_SUBGHZ_ExecGetCmd( &hsubghz, RADIO_GET_IRQSTATUS, irqStatus, 2 );
+ return ( irqStatus[0] << 8 ) | irqStatus[1];
+}
+
+void SUBGRF_SetTcxoMode (RadioTcxoCtrlVoltage_t tcxoVoltage, uint32_t timeout )
+{
+ uint8_t buf[4];
+
+ buf[0] = tcxoVoltage & 0x07;
+ buf[1] = ( uint8_t )( ( timeout >> 16 ) & 0xFF );
+ buf[2] = ( uint8_t )( ( timeout >> 8 ) & 0xFF );
+ buf[3] = ( uint8_t )( timeout & 0xFF );
+
+ HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_TCXOMODE, buf, 4 );
+}
+
+void SUBGRF_SetRfFrequency( uint32_t frequency )
+{
+ uint8_t buf[4];
+ uint32_t chan;
+
+ TCXO_Workaround(RF_API_STATE_TCXO_ON);
+
+ if( ImageCalibrated == false )
+ {
+ SUBGRF_CalibrateImage( frequency );
+ ImageCalibrated = true;
+ }
+
+ //chan = ( uint32_t )( ( double )frequency / ( double )FREQ_STEP );
+ SX_FREQ_TO_CHANNEL(chan, frequency);
+
+ buf[0] = ( uint8_t )( ( chan >> 24 ) & 0xFF );
+ buf[1] = ( uint8_t )( ( chan >> 16 ) & 0xFF );
+ buf[2] = ( uint8_t )( ( chan >> 8 ) & 0xFF );
+ buf[3] = ( uint8_t )( chan & 0xFF );
+ HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_RFFREQUENCY, buf, 4 );
+
+
+}
+
+void SUBGRF_SetPacketType( RadioPacketTypes_t packetType )
+{
+ uint8_t reg = 0x00;
+ // Save packet type internally to avoid questioning the radio
+ PacketType = packetType;
+
+ if( packetType == PACKET_TYPE_GFSK )
+ {
+ HAL_SUBGHZ_WriteRegisters( &hsubghz, REG_BIT_SYNC, (uint8_t*)®, 1 );
+ }
+ HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_PACKETTYPE, ( uint8_t* )&packetType, 1 );
+}
+
+RadioPacketTypes_t SUBGRF_GetPacketType( void )
+{
+ return PacketType;
+}
+uint8_t SUBGRF_SetTxPower( int8_t power, RadioRampTimes_t rampTime )
+{
+ uint8_t paSelect;
+
+ int32_t TxConfig = BSP_RADIO_GetTxConfig();
+
+ switch (TxConfig)
+ {
+ case RADIO_CONF_RFO_LP_HP:
+ {
+ if (power>15)
+ {
+ paSelect= RFO_HP;
+ }
+ else
+ {
+ paSelect= RFO_LP;
+ }
+ break;
+ }
+ case RADIO_CONF_RFO_LP:
+ {
+ paSelect= RFO_LP;
+ break;
+ }
+ case RADIO_CONF_RFO_HP:
+ {
+ paSelect= RFO_HP;
+ break;
+ }
+ default:
+ break;
+ }
+
+ SUBGRF_SetTxParams( paSelect, power, rampTime );
+
+ return paSelect;
+}
+void SUBGRF_SetTxParams( uint8_t paSelect, int8_t power, RadioRampTimes_t rampTime )
+{
+ uint8_t buf[2];
+ uint8_t reg;
+
+ if( paSelect == RFO_LP )
+ {
+ if( power == 15 )
+ {
+ SUBGRF_SetPaConfig( 0x06, 0x00, 0x01, 0x01 );
+ }
+ else
+ {
+ SUBGRF_SetPaConfig( 0x04, 0x00, 0x01, 0x01 );
+ }
+ if( power >= 14 )
+ {
+ power = 14;
+ }
+ else if( power < -3 )
+ {
+ power = -3;
+ }
+ reg = 0x18;
+ HAL_SUBGHZ_WriteRegisters( &hsubghz, REG_OCP, (uint8_t*)®, 1 ); // current max is 80 mA for the whole device
+ }
+ else // rfo_hp
+ {
+ SUBGRF_SetPaConfig( 0x04, 0x07, 0x00, 0x01 );
+ if( power > 22 )
+ {
+ power = 22;
+ }
+ else if( power < -3 )
+ {
+ power = -3;
+ }
+ reg = 0x38;
+ HAL_SUBGHZ_WriteRegisters( &hsubghz, REG_OCP, (uint8_t*)®, 1 ); // current max 160mA for the whole device
+ }
+ buf[0] = power;
+ buf[1] = ( uint8_t )rampTime;
+ HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_TXPARAMS, buf, 2 );
+}
+
+
+
+void SUBGRF_SetModulationParams( ModulationParams_t *modulationParams )
+{
+ uint8_t n;
+ uint32_t tempVal = 0;
+ uint8_t buf[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+ // Check if required configuration corresponds to the stored packet type
+ // If not, silently update radio packet type
+ if( PacketType != modulationParams->PacketType )
+ {
+ SUBGRF_SetPacketType( modulationParams->PacketType );
+ }
+
+ switch( modulationParams->PacketType )
+ {
+ case PACKET_TYPE_GFSK:
+ n = 8;
+ tempVal = ( uint32_t )( 32 * ( ( double )XTAL_FREQ / ( double )modulationParams->Params.Gfsk.BitRate ) );
+ buf[0] = ( tempVal >> 16 ) & 0xFF;
+ buf[1] = ( tempVal >> 8 ) & 0xFF;
+ buf[2] = tempVal & 0xFF;
+ buf[3] = modulationParams->Params.Gfsk.ModulationShaping;
+ buf[4] = modulationParams->Params.Gfsk.Bandwidth;
+ //tempVal = ( uint32_t )( ( double )modulationParams->Params.Gfsk.Fdev / ( double )FREQ_STEP );
+ SX_FREQ_TO_CHANNEL(tempVal, modulationParams->Params.Gfsk.Fdev);
+ buf[5] = ( tempVal >> 16 ) & 0xFF;
+ buf[6] = ( tempVal >> 8 ) & 0xFF;
+ buf[7] = ( tempVal& 0xFF );
+ HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_MODULATIONPARAMS, buf, n );
+ break;
+ case PACKET_TYPE_BPSK:
+ n = 4;
+ tempVal = ( uint32_t )( 32 * ( ( double )XTAL_FREQ / ( double )modulationParams->Params.Bpsk.BitRate ) );
+
+ buf[0] = ( tempVal >> 16 ) & 0xFF;
+ buf[1] = ( tempVal >> 8 ) & 0xFF;
+ buf[2] = tempVal & 0xFF;
+ buf[3] = modulationParams->Params.Bpsk.ModulationShaping;
+ HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_MODULATIONPARAMS, buf, n );
+ break;
+ case PACKET_TYPE_LORA:
+ n = 4;
+ buf[0] = modulationParams->Params.LoRa.SpreadingFactor;
+ buf[1] = modulationParams->Params.LoRa.Bandwidth;
+ buf[2] = modulationParams->Params.LoRa.CodingRate;
+ buf[3] = modulationParams->Params.LoRa.LowDatarateOptimize;
+
+ HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_MODULATIONPARAMS, buf, n );
+
+ break;
+ case PACKET_TYPE_GMSK:
+ n = 5;
+ tempVal = ( uint32_t )( 32 * ( ( double )XTAL_FREQ / ( double )modulationParams->Params.Gfsk.BitRate ) );
+ buf[0] = ( tempVal >> 16 ) & 0xFF;
+ buf[1] = ( tempVal >> 8 ) & 0xFF;
+ buf[2] = tempVal & 0xFF;
+ buf[3] = modulationParams->Params.Gfsk.ModulationShaping;
+ buf[4] = modulationParams->Params.Gfsk.Bandwidth;
+ HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_MODULATIONPARAMS, buf, n );
+ break;
+ default:
+ case PACKET_TYPE_NONE:
+ break;
+ }
+}
+
+void SUBGRF_SetPacketParams( PacketParams_t *packetParams )
+{
+ uint8_t n;
+ uint8_t crcVal = 0;
+ uint8_t buf[9] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+ // Check if required configuration corresponds to the stored packet type
+ // If not, silently update radio packet type
+ if( PacketType != packetParams->PacketType )
+ {
+ SUBGRF_SetPacketType( packetParams->PacketType );
+ }
+
+ switch( packetParams->PacketType )
+ {
+ case PACKET_TYPE_GMSK:
+ case PACKET_TYPE_GFSK:
+ if( packetParams->Params.Gfsk.CrcLength == RADIO_CRC_2_BYTES_IBM )
+ {
+ SUBGRF_SetCrcSeed( CRC_IBM_SEED );
+ SUBGRF_SetCrcPolynomial( CRC_POLYNOMIAL_IBM );
+ crcVal = RADIO_CRC_2_BYTES;
+ }
+ else if( packetParams->Params.Gfsk.CrcLength == RADIO_CRC_2_BYTES_CCIT )
+ {
+ SUBGRF_SetCrcSeed( CRC_CCITT_SEED );
+ SUBGRF_SetCrcPolynomial( CRC_POLYNOMIAL_CCITT );
+ crcVal = RADIO_CRC_2_BYTES_INV;
+ }
+ else
+ {
+ crcVal = packetParams->Params.Gfsk.CrcLength;
+ }
+ n = 9;
+ buf[0] = ( packetParams->Params.Gfsk.PreambleLength >> 8 ) & 0xFF;
+ buf[1] = packetParams->Params.Gfsk.PreambleLength;
+ buf[2] = packetParams->Params.Gfsk.PreambleMinDetect;
+ buf[3] = ( packetParams->Params.Gfsk.SyncWordLength /*<< 3*/ ); // convert from byte to bit
+ buf[4] = packetParams->Params.Gfsk.AddrComp;
+ buf[5] = packetParams->Params.Gfsk.HeaderType;
+ buf[6] = packetParams->Params.Gfsk.PayloadLength;
+ buf[7] = crcVal;
+ buf[8] = packetParams->Params.Gfsk.DcFree;
+ break;
+ case PACKET_TYPE_BPSK:
+ n = 1;
+ buf[0] = packetParams->Params.Bpsk.PayloadLength;
+ break;
+ case PACKET_TYPE_LORA:
+ n = 6;
+ buf[0] = ( packetParams->Params.LoRa.PreambleLength >> 8 ) & 0xFF;
+ buf[1] = packetParams->Params.LoRa.PreambleLength;
+ buf[2] = packetParams->Params.LoRa.HeaderType;
+ buf[3] = packetParams->Params.LoRa.PayloadLength;
+ buf[4] = packetParams->Params.LoRa.CrcMode;
+ buf[5] = packetParams->Params.LoRa.InvertIQ;
+ break;
+ default:
+ case PACKET_TYPE_NONE:
+ return;
+ }
+ HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_PACKETPARAMS, buf, n );
+}
+
+void SUBGRF_SetCadParams( RadioLoRaCadSymbols_t cadSymbolNum, uint8_t cadDetPeak, uint8_t cadDetMin, RadioCadExitModes_t cadExitMode, uint32_t cadTimeout )
+{
+ uint8_t buf[7];
+
+ buf[0] = ( uint8_t )cadSymbolNum;
+ buf[1] = cadDetPeak;
+ buf[2] = cadDetMin;
+ buf[3] = ( uint8_t )cadExitMode;
+ buf[4] = ( uint8_t )( ( cadTimeout >> 16 ) & 0xFF );
+ buf[5] = ( uint8_t )( ( cadTimeout >> 8 ) & 0xFF );
+ buf[6] = ( uint8_t )( cadTimeout & 0xFF );
+ HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_CADPARAMS, buf, 5 );
+ OperatingMode = MODE_CAD;
+}
+
+void SUBGRF_SetBufferBaseAddress( uint8_t txBaseAddress, uint8_t rxBaseAddress )
+{
+ uint8_t buf[2];
+
+ buf[0] = txBaseAddress;
+ buf[1] = rxBaseAddress;
+ HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_BUFFERBASEADDRESS, buf, 2 );
+}
+
+RadioStatus_t SUBGRF_GetStatus( void )
+{
+ uint8_t stat = 0;
+ RadioStatus_t status;
+
+ HAL_SUBGHZ_ExecGetCmd( &hsubghz, RADIO_GET_STATUS, ( uint8_t * )&stat, 1 );
+ status.Value = stat;
+ return status;
+}
+
+int8_t SUBGRF_GetRssiInst( void )
+{
+ uint8_t buf[1];
+ int8_t rssi = 0;
+
+ HAL_SUBGHZ_ExecGetCmd( &hsubghz, RADIO_GET_RSSIINST, buf, 1 );
+ rssi = -buf[0] >> 1;
+ return rssi;
+}
+
+void SUBGRF_GetRxBufferStatus( uint8_t *payloadLength, uint8_t *rxStartBufferPointer )
+{
+ uint8_t status[2];
+ uint8_t data;
+
+ HAL_SUBGHZ_ExecGetCmd( &hsubghz, RADIO_GET_RXBUFFERSTATUS, status, 2 );
+
+ // In case of LORA fixed header, the payloadLength is obtained by reading
+ // the register REG_LR_PAYLOADLENGTH
+ HAL_SUBGHZ_ReadRegisters( &hsubghz, REG_LR_PACKETPARAMS, &data, 1 );
+ if( ( SUBGRF_GetPacketType( ) == PACKET_TYPE_LORA ) && (data >> 7 == 1 ) )
+ {
+ HAL_SUBGHZ_ReadRegisters( &hsubghz, REG_LR_PAYLOADLENGTH, &data, 1 );
+ *payloadLength = data;
+ }
+ else
+ {
+ *payloadLength = status[0];
+ }
+ *rxStartBufferPointer = status[1];
+}
+
+void SUBGRF_GetPacketStatus( PacketStatus_t *pktStatus )
+{
+ uint8_t status[3];
+
+ HAL_SUBGHZ_ExecGetCmd( &hsubghz, RADIO_GET_PACKETSTATUS, status, 3 );
+
+ pktStatus->packetType = SUBGRF_GetPacketType( );
+ switch( pktStatus->packetType )
+ {
+ case PACKET_TYPE_GFSK:
+ pktStatus->Params.Gfsk.RxStatus = status[0];
+ pktStatus->Params.Gfsk.RssiSync = -status[1] >> 1;
+ pktStatus->Params.Gfsk.RssiAvg = -status[2] >> 1;
+ pktStatus->Params.Gfsk.FreqError = 0;
+ break;
+
+ case PACKET_TYPE_LORA:
+ pktStatus->Params.LoRa.RssiPkt = -status[0] >> 1;
+ ( status[1] < 128 ) ? ( pktStatus->Params.LoRa.SnrPkt = status[1] >> 2 ) : ( pktStatus->Params.LoRa.SnrPkt = ( ( status[1] - 256 ) >> 2 ) );
+ pktStatus->Params.LoRa.SignalRssiPkt = -status[2] >> 1;
+ pktStatus->Params.LoRa.FreqError = FrequencyError;
+ break;
+
+ default:
+ case PACKET_TYPE_NONE:
+ // In that specific case, we set everything in the pktStatus to zeros
+ // and reset the packet type accordingly
+ RADIO_MEMSET8( (uint8_t*)pktStatus, 0, sizeof( PacketStatus_t ) );
+ pktStatus->packetType = PACKET_TYPE_NONE;
+ break;
+ }
+}
+
+RadioError_t SUBGRF_GetErrors( void )
+{
+ RadioError_t error;
+
+ HAL_SUBGHZ_ExecGetCmd( &hsubghz, RADIO_GET_ERROR, ( uint8_t * )&error, 2 );
+ return error;
+}
+
+void SUBGRF_ClearIrqStatus( uint16_t irq )
+{
+ uint8_t buf[2];
+
+ buf[0] = ( uint8_t )( ( ( uint16_t )irq >> 8 ) & 0x00FF );
+ buf[1] = ( uint8_t )( ( uint16_t )irq & 0x00FF );
+ HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_CLR_IRQSTATUS, buf, 2 );
+}
+
+
+void SUBGRF_WriteRegister( uint16_t addr, uint8_t data )
+{
+ HAL_SUBGHZ_WriteRegisters( &hsubghz, addr, (uint8_t*)&data, 1 );
+}
+
+uint8_t SUBGRF_ReadRegister( uint16_t addr )
+{
+ uint8_t data;
+ HAL_SUBGHZ_ReadRegisters( &hsubghz, addr, &data, 1 );
+ return data;
+}
+
+void SUBGRF_WriteRegisters( uint16_t addr, uint8_t *buffer, uint8_t size )
+{
+ HAL_SUBGHZ_WriteRegisters( &hsubghz, addr, buffer, size );
+}
+
+void SUBGRF_ReadRegisters( uint16_t addr, uint8_t *buffer, uint8_t size )
+{
+ HAL_SUBGHZ_ReadRegisters( &hsubghz, addr, buffer, size );
+}
+
+
+/* HAL_SUBGHz Callbacks definitions */
+void HAL_SUBGHZ_TxCpltCallback(SUBGHZ_HandleTypeDef *hsubghz)
+{
+ RadioOnDioIrqCb( IRQ_TX_DONE );
+}
+
+void HAL_SUBGHZ_RxCpltCallback(SUBGHZ_HandleTypeDef *hsubghz)
+{
+ RadioOnDioIrqCb( IRQ_RX_DONE );
+}
+
+void HAL_SUBGHZ_CRCErrorCallback (SUBGHZ_HandleTypeDef *hsubghz)
+{
+ RadioOnDioIrqCb( IRQ_CRC_ERROR);
+}
+
+void HAL_SUBGHZ_CADDoneCallback(SUBGHZ_HandleTypeDef *hsubghz)
+{
+ RadioOnDioIrqCb( IRQ_CAD_DONE);
+}
+
+void HAL_SUBGHZ_CADActivityDetectedCallback(SUBGHZ_HandleTypeDef *hsubghz)
+{
+ RadioOnDioIrqCb( IRQ_CAD_ACTIVITY_DETECTED);
+}
+
+void HAL_SUBGHZ_RxTxTimeoutCallback(SUBGHZ_HandleTypeDef *hsubghz)
+{
+ RadioOnDioIrqCb( IRQ_RX_TX_TIMEOUT);
+}
+
+void HAL_SUBGHZ_HeaderErrorCallback(SUBGHZ_HandleTypeDef *hsubghz)
+{
+ RadioOnDioIrqCb( IRQ_HEADER_ERROR);
+}
+
+void HAL_SUBGHZ_PreambleDetectedCallback(SUBGHZ_HandleTypeDef *hsubghz)
+{
+ RadioOnDioIrqCb( IRQ_PREAMBLE_DETECTED);
+}
+
+void HAL_SUBGHZ_SyncWordValidCallback(SUBGHZ_HandleTypeDef *hsubghz)
+{
+ RadioOnDioIrqCb( IRQ_SYNCWORD_VALID);
+}
+
+void HAL_SUBGHZ_HeaderValidCallback(SUBGHZ_HandleTypeDef *hsubghz)
+{
+ RadioOnDioIrqCb( IRQ_HEADER_VALID);
+}
+
+
+static void TCXO_Workaround( RF_API_Tcxo_State_t state )
+{
+ /* Workaround needed only for cut1.0*/
+ uint16_t RevisionID = LL_DBGMCU_GetRevisionID();
+
+ if (1U ==BSP_RADIO_IsTCXO() && (RevisionID==0x1000) )
+ {
+ switch (state)
+ {
+ case RF_API_STATE_TCXO_OFF:
+ LL_RCC_HSE_Disable();
+
+ LL_RCC_HSE_DisableTcxo();
+
+ LL_RCC_HSE_DisableBypass();
+
+ UTIL_LPM_SetStopMode( LPM_TCXO_WA_Id , UTIL_LPM_ENABLE);
+
+ MW_LOG("TCXOFF\n\r");
+ break;
+
+ case RF_API_STATE_TCXO_ON:
+ LL_RCC_HSE_EnableTcxo();
+
+ LL_RCC_HSE_EnableBypass();
+
+ LL_RCC_HSE_Enable();
+
+ UTIL_LPM_SetStopMode( LPM_TCXO_WA_Id , UTIL_LPM_DISABLE);
+
+ MW_LOG("TCXON\n\r");
+
+ while(LL_RCC_HSE_IsReady() == 0)
+ {
+ }
+
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+static void RadioReset_Workaround( void )
+{
+ if ( LL_DBGMCU_GetRevisionID() ==0x1000 )
+ {
+ uint8_t reg=0;
+
+ HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_SLEEP, ®, 1 );
+
+ RADIO_DELAY_MS( 2 );
+ }
+}
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
+
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/stm32_radio_driver/radio_driver.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/stm32_radio_driver/radio_driver.h
new file mode 100644
index 00000000..729a32b1
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/stm32_radio_driver/radio_driver.h
@@ -0,0 +1,1223 @@
+/*
+ ______ _
+ / _____) _ | |
+( (____ _____ ____ _| |_ _____ ____| |__
+ \____ \| ___ | (_ _) ___ |/ ___) _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+ (C)2017 Semtech
+
+Description: Generic SX126x driver implementation
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+
+Authors: Miguel Luis, Gregory Cristian
+*/
+/**
+ ******************************************************************************
+ *
+ * Portions COPYRIGHT 2019 STMicroelectronics
+ *
+ * @file radio_driver.h
+ * @author MCD Application Team
+ * @brief Header for driver radio interface
+ ******************************************************************************
+ */
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __RADIO_DRIVER_H__
+#define __RADIO_DRIVER_H__
+
+/* Includes ------------------------------------------------------------------*/
+
+#include
+#include
+
+/*==========================================================================*/
+/** @addtogroup BSP
+ * @{
+ */
+
+/** @addtogroup STM32WLXX_NUCLEO STM32WLXX-NUCLEO
+ * @{
+ */
+
+/** @defgroup STM32WLXX_NUCLEO_RADIO_LOW_LEVEL RADIO LOW LEVEL
+ * @{
+ */
+
+/** @defgroup STM32WLXX_NUCLEO_RADIO_LOW_LEVEL_Exported_Types RADIO LOW LEVEL Exported Types
+ * @{
+ */
+
+typedef enum
+{
+ RADIO_SWITCH_OFF = 0,
+ RADIO_SWITCH_RX = 1,
+ RADIO_SWITCH_RFO_LP = 2,
+ RADIO_SWITCH_RFO_HP = 3,
+}BSP_RADIO_Switch_TypeDef;
+
+/**
+ * @}
+ */
+
+/** @defgroup STM32WLXX_NUCLEO_RADIO_LOW_LEVEL_Exported_Constants RADIO LOW LEVEL Exported Constants
+ * @{
+ */
+
+/** @defgroup STM32WLXX_NUCLEO_RADIO_LOW_LEVEL_RADIOCONFIG RADIO LOW LEVEL RADIO CONFIG Constants
+ * @{
+ */
+
+#define RADIO_CONF_RFO_LP_HP 0
+#define RADIO_CONF_RFO_LP 1
+#define RADIO_CONF_RFO_HP 2
+
+
+/* Common Error codes */
+#define BSP_ERROR_NONE 0
+#define BSP_ERROR_NO_INIT -1
+#define BSP_ERROR_WRONG_PARAM -2
+#define BSP_ERROR_BUSY -3
+#define BSP_ERROR_PERIPH_FAILURE -4
+#define BSP_ERROR_COMPONENT_FAILURE -5
+#define BSP_ERROR_UNKNOWN_FAILURE -6
+#define BSP_ERROR_UNKNOWN_COMPONENT -7
+#define BSP_ERROR_BUS_FAILURE -8
+#define BSP_ERROR_CLOCK_FAILURE -9
+#define BSP_ERROR_MSP_FAILURE -10
+#define BSP_ERROR_FEATURE_NOT_SUPPORTED -11
+/**
+ * @}
+ */
+
+/** @defgroup STM32WLXX_NUCLEO_RADIO_LOW_LEVEL_RFSWITCH RADIO LOW LEVEL RF SWITCH Constants
+ * @{
+ */
+
+#define RF_SW_CTRL3_PIN GPIO_PIN_3
+#define RF_SW_CTRL3_GPIO_PORT GPIOC
+#define RF_SW_CTRL3_GPIO_CLK_ENABLE() __HAL_RCC_GPIOC_CLK_ENABLE()
+#define RF_SW_CTRL3_GPIO_CLK_DISABLE() __HAL_RCC_GPIOC_CLK_DISABLE()
+
+#define RF_SW_CTRL1_PIN GPIO_PIN_4
+#define RF_SW_CTRL1_GPIO_PORT GPIOC
+#define RF_SW_CTRL1_GPIO_CLK_ENABLE() __HAL_RCC_GPIOC_CLK_ENABLE()
+#define RF_SW_RX_GPIO_CLK_DISABLE() __HAL_RCC_GPIOC_CLK_DISABLE()
+
+#define RF_SW_CTRL2_PIN GPIO_PIN_5
+#define RF_SW_CTRL2_GPIO_PORT GPIOC
+#define RF_SW_CTRL2_GPIO_CLK_ENABLE() __HAL_RCC_GPIOC_CLK_ENABLE()
+#define RF_SW_CTRL2_GPIO_CLK_DISABLE() __HAL_RCC_GPIOC_CLK_DISABLE()
+
+#define RF_TCXO_VCC_PIN GPIO_PIN_0
+#define RF_TCXO_VCC_GPIO_PORT GPIOB
+#define RF_TCXO_VCC_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()
+#define RF_TCXO_VCC_CLK_DISABLE() __HAL_RCC_GPIOB_CLK_DISABLE()
+
+/** @defgroup STM32WLXX_NUCLEO_CONFIG_Exported_Constants Exported Constants
+ * @{
+ */
+/* COM usage define */
+#define USE_BSP_COM_FEATURE 0U
+
+/* COM log define */
+#define USE_COM_LOG 0U
+
+/* IRQ priorities */
+#define BSP_BUTTON_SWx_IT_PRIORITY 15U
+
+/* Radio maximum wakeup time (in ms) */
+#define RF_WAKEUP_TIME 10U
+
+/* Indicates whether or not TCXO is supported by the board
+ * 0: TCXO not supported
+ * 1: TCXO supported
+ */
+#define IS_TCXO_SUPPORTED 1U
+
+/* Indicates whether or not DCDC is supported by the board
+ * 0: DCDC not supported
+ * 1: DCDC supported
+ */
+#define IS_DCDC_SUPPORTED 1U
+
+/*================================================================================*/
+
+/* Exported constants --------------------------------------------------------*/
+#define RFO_LP 1
+#define RFO_HP 2
+
+/*!
+ * Generic SUBGRF_ error code
+ */
+#define SUBGRF_OK 0
+#define SUBGRF_ERROR -1
+
+/*!
+ * Radio complete Wake-up Time with margin for temperature compensation
+ */
+#define RADIO_WAKEUP_TIME 3 // [ms]
+
+/*!
+ * @brief Compensation delay for SetAutoTx/Rx functions in 15.625 microseconds
+ */
+#define AUTO_RX_TX_OFFSET 2
+
+/*!
+ * @brief LFSR initial value to compute IBM type CRC
+ */
+#define CRC_IBM_SEED 0xFFFF
+
+/*!
+ * @brief LFSR initial value to compute CCIT type CRC
+ */
+#define CRC_CCITT_SEED 0x1D0F
+
+/*!
+ * @brief Polynomial used to compute IBM CRC
+ */
+#define CRC_POLYNOMIAL_IBM 0x8005
+
+/*!
+ * @brief Polynomial used to compute CCIT CRC
+ */
+#define CRC_POLYNOMIAL_CCITT 0x1021
+
+/*!
+ * @brief The address of the register holding the first byte defining the CRC seed
+ *
+ */
+#define REG_LR_CRCSEEDBASEADDR 0x06BC
+
+/*!
+ * @brief The address of the register holding the first byte defining the CRC polynomial
+ */
+#define REG_LR_CRCPOLYBASEADDR 0x06BE
+
+/*!
+ * @brief The address of the register holding the first byte defining the whitening seed
+ */
+#define REG_LR_WHITSEEDBASEADDR_MSB 0x06B8
+#define REG_LR_WHITSEEDBASEADDR_LSB 0x06B9
+
+/*!
+ * @brief The address of the register holding the packet configuration
+ */
+#define REG_LR_PACKETPARAMS 0x0704
+
+/*!
+ * @brief The address of the register holding the payload size
+ */
+#define REG_LR_PAYLOADLENGTH 0x0702
+
+/*!
+ * @brief The addresses of the registers holding SyncWords values
+ */
+#define REG_LR_SYNCWORDBASEADDRESS 0x06C0
+
+/*!
+ * @brief The addresses of the register holding LoRa Modem SyncWord value
+ */
+#define REG_LR_SYNCWORD 0x0740
+
+/*!
+ * Syncword for Private LoRa networks
+ */
+#define LORA_MAC_PRIVATE_SYNCWORD 0x1424
+
+/*!
+ * Syncword for Public LoRa networks
+ */
+#define LORA_MAC_PUBLIC_SYNCWORD 0x3444
+
+/*!
+ * The address of the register giving a 4 bytes random number
+ */
+#define RANDOM_NUMBER_GENERATORBASEADDR 0x0819
+
+/*!
+ * The address of the register holding RX Gain value (0x94: power saving, 0x96: rx boosted)
+ */
+#define REG_RX_GAIN 0x08AC
+
+/*!
+ * The address of the register holding Bit Sync configuration
+ */
+#define REG_BIT_SYNC 0x06AC
+
+/*!
+ * Change the value on the device internal trimming capacitor
+ */
+#define REG_XTA_TRIM 0x0911
+
+/*!
+ * Set the current max value in the over current protection
+ */
+#define REG_OCP 0x08E7
+
+/* Exported types ------------------------------------------------------------*/
+
+/*!
+ * @brief Structure describing the radio status
+ */
+typedef union RadioStatus_u
+{
+ uint8_t Value;
+ struct
+ { //bit order is lsb -> msb
+ uint8_t Reserved : 1; //!< Reserved
+ uint8_t CmdStatus : 3; //!< Command status
+ uint8_t ChipMode : 3; //!< Chip mode
+ uint8_t CpuBusy : 1; //!< Flag for CPU radio busy
+ }Fields;
+}RadioStatus_t;
+
+/*!
+ * @brief Structure describing the error codes for callback functions
+ */
+typedef enum
+{
+ IRQ_HEADER_ERROR_CODE = 0x01,
+ IRQ_SYNCWORD_ERROR_CODE = 0x02,
+ IRQ_CRC_ERROR_CODE = 0x04,
+}IrqErrorCode_t;
+
+enum IrqPblSyncHeaderCode_t
+{
+ IRQ_PBL_DETECT_CODE = 0x01,
+ IRQ_SYNCWORD_VALID_CODE = 0x02,
+ IRQ_HEADER_VALID_CODE = 0x04,
+};
+
+
+
+/*!
+ * @brief Declares the oscillator in use while in standby mode
+ *
+ * Using the STDBY_RC standby mode allow to reduce the energy consumption
+ * STDBY_XOSC should be used for time critical applications
+ */
+typedef enum
+{
+ STDBY_RC = 0x00,
+ STDBY_XOSC = 0x01,
+}RadioStandbyModes_t;
+
+/*!
+ * @brief Declares the power regulation used to power the device
+ *
+ * This command allows the user to specify if DC-DC or LDO is used for power regulation.
+ * Using only LDO implies that the Rx or Tx current is doubled
+ */
+typedef enum
+{
+ USE_LDO = 0x00, // default
+ USE_DCDC = 0x01,
+}RadioRegulatorMode_t;
+
+/*!
+ * @brief Represents the possible packet type (i.e. modem) used
+ */
+typedef enum
+{
+ PACKET_TYPE_GFSK = 0x00,
+ PACKET_TYPE_LORA = 0x01,
+ PACKET_TYPE_BPSK = 0x02,
+ PACKET_TYPE_GMSK = 0x03,
+ PACKET_TYPE_NONE = 0x0F,
+}RadioPacketTypes_t;
+
+/*!
+ * @brief Represents the ramping time for power amplifier
+ */
+typedef enum
+{
+ RADIO_RAMP_10_US = 0x00,
+ RADIO_RAMP_20_US = 0x01,
+ RADIO_RAMP_40_US = 0x02,
+ RADIO_RAMP_80_US = 0x03,
+ RADIO_RAMP_200_US = 0x04,
+ RADIO_RAMP_800_US = 0x05,
+ RADIO_RAMP_1700_US = 0x06,
+ RADIO_RAMP_3400_US = 0x07,
+}RadioRampTimes_t;
+
+/*!
+ * @brief Represents the number of symbols to be used for channel activity detection operation
+ */
+typedef enum
+{
+ LORA_CAD_01_SYMBOL = 0x00,
+ LORA_CAD_02_SYMBOL = 0x01,
+ LORA_CAD_04_SYMBOL = 0x02,
+ LORA_CAD_08_SYMBOL = 0x03,
+ LORA_CAD_16_SYMBOL = 0x04,
+}RadioLoRaCadSymbols_t;
+
+/*!
+ * @brief Represents the Channel Activity Detection actions after the CAD operation is finished
+ */
+typedef enum
+{
+ LORA_CAD_ONLY = 0x00,
+ LORA_CAD_RX = 0x01,
+ LORA_CAD_LBT = 0x10,
+}RadioCadExitModes_t;
+
+/*!
+ * @brief Represents the modulation shaping parameter
+ */
+typedef enum
+{
+ MOD_SHAPING_OFF = 0x00,
+ MOD_SHAPING_G_BT_03 = 0x08,
+ MOD_SHAPING_G_BT_05 = 0x09,
+ MOD_SHAPING_G_BT_07 = 0x0A,
+ MOD_SHAPING_G_BT_1 = 0x0B,
+ MOD_SHAPING_DBPSK = 0x16,
+}RadioModShapings_t;
+
+/*!
+ * @brief Represents the modulation shaping parameter
+ */
+typedef enum
+{
+ RX_BW_4800 = 0x1F,
+ RX_BW_5800 = 0x17,
+ RX_BW_7300 = 0x0F,
+ RX_BW_9700 = 0x1E,
+ RX_BW_11700 = 0x16,
+ RX_BW_14600 = 0x0E,
+ RX_BW_19500 = 0x1D,
+ RX_BW_23400 = 0x15,
+ RX_BW_29300 = 0x0D,
+ RX_BW_39000 = 0x1C,
+ RX_BW_46900 = 0x14,
+ RX_BW_58600 = 0x0C,
+ RX_BW_78200 = 0x1B,
+ RX_BW_93800 = 0x13,
+ RX_BW_117300 = 0x0B,
+ RX_BW_156200 = 0x1A,
+ RX_BW_187200 = 0x12,
+ RX_BW_234300 = 0x0A,
+ RX_BW_312000 = 0x19,
+ RX_BW_373600 = 0x11,
+ RX_BW_467000 = 0x09,
+}RadioRxBandwidth_t;
+
+/*!
+ * @brief Represents the possible spreading factor values in LoRa packet types
+ */
+typedef enum
+{
+ LORA_SF5 = 0x05,
+ LORA_SF6 = 0x06,
+ LORA_SF7 = 0x07,
+ LORA_SF8 = 0x08,
+ LORA_SF9 = 0x09,
+ LORA_SF10 = 0x0A,
+ LORA_SF11 = 0x0B,
+ LORA_SF12 = 0x0C,
+}RadioLoRaSpreadingFactors_t;
+
+/*!
+ * @brief Represents the bandwidth values for LoRa packet type
+ */
+typedef enum
+{
+ LORA_BW_500 = 6,
+ LORA_BW_250 = 5,
+ LORA_BW_125 = 4,
+ LORA_BW_062 = 3,
+ LORA_BW_041 = 10,
+ LORA_BW_031 = 2,
+ LORA_BW_020 = 9,
+ LORA_BW_015 = 1,
+ LORA_BW_010 = 8,
+ LORA_BW_007 = 0,
+}RadioLoRaBandwidths_t;
+
+/*!
+ * @brief Represents the coding rate values for LoRa packet type
+ */
+typedef enum
+{
+ LORA_CR_4_5 = 0x01,
+ LORA_CR_4_6 = 0x02,
+ LORA_CR_4_7 = 0x03,
+ LORA_CR_4_8 = 0x04,
+}RadioLoRaCodingRates_t;
+
+/*!
+ * @brief Represents the preamble length used to detect the packet on Rx side
+ */
+typedef enum
+{
+ RADIO_PREAMBLE_DETECTOR_OFF = 0x00, //!< Preamble detection length off
+ RADIO_PREAMBLE_DETECTOR_08_BITS = 0x04, //!< Preamble detection length 8 bits
+ RADIO_PREAMBLE_DETECTOR_16_BITS = 0x05, //!< Preamble detection length 16 bits
+ RADIO_PREAMBLE_DETECTOR_24_BITS = 0x06, //!< Preamble detection length 24 bits
+ RADIO_PREAMBLE_DETECTOR_32_BITS = 0x07, //!< Preamble detection length 32 bit
+}RadioPreambleDetection_t;
+
+/*!
+ * @brief Represents the possible combinations of SyncWord correlators activated
+ */
+typedef enum
+{
+ RADIO_ADDRESSCOMP_FILT_OFF = 0x00, //!< No correlator turned on, i.e. do not search for SyncWord
+ RADIO_ADDRESSCOMP_FILT_NODE = 0x01,
+ RADIO_ADDRESSCOMP_FILT_NODE_BROAD = 0x02,
+}RadioAddressComp_t;
+
+/*!
+ * @brief Radio packet length mode
+ */
+typedef enum
+{
+ RADIO_PACKET_FIXED_LENGTH = 0x00, //!< The packet is known on both sides, no header included in the packet
+ RADIO_PACKET_VARIABLE_LENGTH = 0x01, //!< The packet is on variable size, header included
+}RadioPacketLengthModes_t;
+
+/*!
+ * @brief Represents the CRC length
+ */
+typedef enum
+{
+ RADIO_CRC_OFF = 0x01, //!< No CRC in use
+ RADIO_CRC_1_BYTES = 0x00,
+ RADIO_CRC_2_BYTES = 0x02,
+ RADIO_CRC_1_BYTES_INV = 0x04,
+ RADIO_CRC_2_BYTES_INV = 0x06,
+ RADIO_CRC_2_BYTES_IBM = 0xF1,
+ RADIO_CRC_2_BYTES_CCIT = 0xF2,
+}RadioCrcTypes_t;
+
+/*!
+ * @brief Radio whitening mode activated or deactivated
+ */
+typedef enum
+{
+ RADIO_DC_FREE_OFF = 0x00,
+ RADIO_DC_FREEWHITENING = 0x01,
+}RadioDcFree_t;
+
+/*!
+ * @brief Holds the lengths mode of a LoRa packet type
+ */
+typedef enum
+{
+ LORA_PACKET_VARIABLE_LENGTH = 0x00, //!< The packet is on variable size, header included
+ LORA_PACKET_FIXED_LENGTH = 0x01, //!< The packet is known on both sides, no header included in the packet
+ LORA_PACKET_EXPLICIT = LORA_PACKET_VARIABLE_LENGTH,
+ LORA_PACKET_IMPLICIT = LORA_PACKET_FIXED_LENGTH,
+}RadioLoRaPacketLengthsMode_t;
+
+/*!
+ * @brief Represents the CRC mode for LoRa packet type
+ */
+typedef enum
+{
+ LORA_CRC_ON = 0x01, //!< CRC activated
+ LORA_CRC_OFF = 0x00, //!< CRC not used
+}RadioLoRaCrcModes_t;
+
+/*!
+ * @brief Represents the IQ mode for LoRa packet type
+ */
+typedef enum
+{
+ LORA_IQ_NORMAL = 0x00,
+ LORA_IQ_INVERTED = 0x01,
+}RadioLoRaIQModes_t;
+
+/*!
+ * @brief Represents the volatge used to control the TCXO on/off from DIO3
+ */
+typedef enum
+{
+ TCXO_CTRL_1_6V = 0x00,
+ TCXO_CTRL_1_7V = 0x01,
+ TCXO_CTRL_1_8V = 0x02,
+ TCXO_CTRL_2_2V = 0x03,
+ TCXO_CTRL_2_4V = 0x04,
+ TCXO_CTRL_2_7V = 0x05,
+ TCXO_CTRL_3_0V = 0x06,
+ TCXO_CTRL_3_3V = 0x07,
+}RadioTcxoCtrlVoltage_t;
+
+/*!
+ * @brief Represents the interruption masks available for the radio
+ *
+ * @remark Note that not all these interruptions are available for all packet types
+ */
+typedef enum
+{
+ IRQ_RADIO_NONE = 0x0000,
+ IRQ_TX_DONE = 0x0001,
+ IRQ_RX_DONE = 0x0002,
+ IRQ_PREAMBLE_DETECTED = 0x0004,
+ IRQ_SYNCWORD_VALID = 0x0008,
+ IRQ_HEADER_VALID = 0x0010,
+ IRQ_HEADER_ERROR = 0x0020,
+ IRQ_CRC_ERROR = 0x0040,
+ IRQ_CAD_DONE = 0x0080,
+ IRQ_CAD_ACTIVITY_DETECTED = 0x0100,
+ IRQ_RX_TX_TIMEOUT = 0x0200,
+ IRQ_RADIO_ALL = 0xFFFF,
+}RadioIrqMasks_t;
+
+
+
+/*!
+ * @brief The type describing the modulation parameters for every packet types
+ */
+typedef struct
+{
+ RadioPacketTypes_t PacketType; //!< Packet to which the modulation parameters are referring to.
+ struct
+ {
+ struct
+ {
+ uint32_t BitRate;
+ uint32_t Fdev;
+ RadioModShapings_t ModulationShaping;
+ uint8_t Bandwidth;
+ }Gfsk;
+ struct
+ {
+ uint32_t BitRate;
+ RadioModShapings_t ModulationShaping;
+ }Bpsk;
+ struct
+ {
+ RadioLoRaSpreadingFactors_t SpreadingFactor; //!< Spreading Factor for the LoRa modulation
+ RadioLoRaBandwidths_t Bandwidth; //!< Bandwidth for the LoRa modulation
+ RadioLoRaCodingRates_t CodingRate; //!< Coding rate for the LoRa modulation
+ uint8_t LowDatarateOptimize; //!< Indicates if the modem uses the low datarate optimization
+ }LoRa;
+ }Params; //!< Holds the modulation parameters structure
+}ModulationParams_t;
+
+/*!
+ * @brief The type describing the packet parameters for every packet types
+ */
+typedef struct
+{
+ RadioPacketTypes_t PacketType; //!< Packet to which the packet parameters are referring to.
+ struct
+ {
+ /*!
+ * @brief Holds the GFSK packet parameters
+ */
+ struct
+ {
+ uint16_t PreambleLength; //!< The preamble Tx length for GFSK packet type in bit
+ RadioPreambleDetection_t PreambleMinDetect; //!< The preamble Rx length minimal for GFSK packet type
+ uint8_t SyncWordLength; //!< The synchronization word length for GFSK packet type
+ RadioAddressComp_t AddrComp; //!< Activated SyncWord correlators
+ RadioPacketLengthModes_t HeaderType; //!< If the header is explicit, it will be transmitted in the GFSK packet. If the header is implicit, it will not be transmitted
+ uint8_t PayloadLength; //!< Size of the payload in the GFSK packet
+ RadioCrcTypes_t CrcLength; //!< Size of the CRC block in the GFSK packet
+ RadioDcFree_t DcFree;
+ }Gfsk;
+ /*!
+ * @brief Holds the BPSK packet parameters
+ */
+ struct
+ {
+ uint8_t PayloadLength; //!< Size of the payload in the BPSK packet
+ }Bpsk;
+ /*!
+ * @brief Holds the LoRa packet parameters
+ */
+ struct
+ {
+ uint16_t PreambleLength; //!< The preamble length is the number of LoRa symbols in the preamble
+ RadioLoRaPacketLengthsMode_t HeaderType; //!< If the header is explicit, it will be transmitted in the LoRa packet. If the header is implicit, it will not be transmitted
+ uint8_t PayloadLength; //!< Size of the payload in the LoRa packet
+ RadioLoRaCrcModes_t CrcMode; //!< Size of CRC block in LoRa packet
+ RadioLoRaIQModes_t InvertIQ; //!< Allows to swap IQ for LoRa packet
+ }LoRa;
+ }Params; //!< Holds the packet parameters structure
+}PacketParams_t;
+
+/*!
+ * @brief Represents the packet status for every packet type
+ */
+typedef struct
+{
+ RadioPacketTypes_t packetType; //!< Packet to which the packet status are referring to.
+ struct
+ {
+ struct
+ {
+ uint8_t RxStatus;
+ int8_t RssiAvg; //!< The averaged RSSI
+ int8_t RssiSync; //!< The RSSI measured on last packet
+ uint32_t FreqError;
+ }Gfsk;
+ struct
+ {
+ int8_t RssiPkt; //!< The RSSI of the last packet
+ int8_t SnrPkt; //!< The SNR of the last packet
+ int8_t SignalRssiPkt;
+ uint32_t FreqError;
+ }LoRa;
+ }Params;
+}PacketStatus_t;
+
+/*!
+ * @brief Represents the Rx internal counters values when GFSK or LoRa packet type is used
+ */
+typedef struct
+{
+ RadioPacketTypes_t packetType; //!< Packet to which the packet status are referring to.
+ uint16_t PacketReceived;
+ uint16_t CrcOk;
+ uint16_t LengthError;
+}RxCounter_t;
+
+/*!
+ * @brief Represents a calibration configuration
+ */
+typedef union
+{
+ struct
+ {
+ uint8_t RC64KEnable : 1; //!< Calibrate RC64K clock
+ uint8_t RC13MEnable : 1; //!< Calibrate RC13M clock
+ uint8_t PLLEnable : 1; //!< Calibrate PLL
+ uint8_t ADCPulseEnable : 1; //!< Calibrate ADC Pulse
+ uint8_t ADCBulkNEnable : 1; //!< Calibrate ADC bulkN
+ uint8_t ADCBulkPEnable : 1; //!< Calibrate ADC bulkP
+ uint8_t ImgEnable : 1;
+ uint8_t : 1;
+ }Fields;
+ uint8_t Value;
+}CalibrationParams_t;
+
+/*!
+ * @brief Represents a sleep mode configuration
+ */
+typedef union
+{
+ struct
+ {
+ uint8_t WakeUpRTC : 1; //!< Get out of sleep mode if wakeup signal received from RTC
+ uint8_t Reset : 1;
+ uint8_t WarmStart : 1;
+ uint8_t Reserved : 5;
+ }Fields;
+ uint8_t Value;
+}SleepParams_t;
+
+/*!
+ * @brief Represents the possible radio system error states
+ */
+typedef union
+{
+ struct
+ {
+ uint8_t Rc64kCalib : 1; //!< RC 64kHz oscillator calibration failed
+ uint8_t Rc13mCalib : 1; //!< RC 13MHz oscillator calibration failed
+ uint8_t PllCalib : 1; //!< PLL calibration failed
+ uint8_t AdcCalib : 1; //!< ADC calibration failed
+ uint8_t ImgCalib : 1; //!< Image calibration failed
+ uint8_t XoscStart : 1; //!< XOSC oscillator failed to start
+ uint8_t PllLock : 1; //!< PLL lock failed
+ uint8_t BuckStart : 1; //!< Buck converter failed to start
+ uint8_t PaRamp : 1; //!< PA ramp failed
+ uint8_t : 7; //!< Reserved
+ }Fields;
+ uint16_t Value;
+}RadioError_t;
+
+/*!
+ * @brief Represents the operating mode the radio is actually running
+ */
+typedef enum
+{
+ MODE_SLEEP = 0x00, //! The radio is in sleep mode
+ MODE_STDBY_RC, //! The radio is in standby mode with RC oscillator
+ MODE_STDBY_XOSC, //! The radio is in standby mode with XOSC oscillator
+ MODE_FS, //! The radio is in frequency synthesis mode
+ MODE_TX, //! The radio is in transmit mode
+ MODE_RX, //! The radio is in receive mode
+ MODE_RX_DC, //! The radio is in receive duty cycle mode
+ MODE_CAD //! The radio is in channel activity detection mode
+}RadioOperatingModes_t;
+
+/*!
+ * Radio driver internal state machine states definition
+ */
+typedef enum
+{
+ RFSWITCH_RX = 0, //!< The radio is in RX
+ RFSWITCH_TX = 1 //!< The radio is in TX
+}RFState_t;
+
+/*!
+ * SUBG_RF definitions
+ */
+
+/*!
+ * @brief Provides the frequency of the chip running on the radio and the frequency step
+ *
+ * @remark These defines are used for computing the frequency divider to set the RF frequency
+ */
+#define XTAL_FREQ ( double )32000000
+//#define FREQ_DIV ( double )pow( 2.0, 25.0 )
+//#define FREQ_STEP ( double )( XTAL_FREQ / FREQ_DIV )
+#define FREQ_STEP_14 15625 /* (XTAL_FREQ / FREQ_DIV) >> 14 */
+
+/* channel = Freq / FREQ_STEP */
+#define SX_FREQ_TO_CHANNEL( channel, freq ) \
+ do \
+ { \
+ uint32_t initialFreqInt, initialFreqFrac; \
+ initialFreqInt = freq / FREQ_STEP_14; \
+ initialFreqFrac = freq - ( initialFreqInt * FREQ_STEP_14 ); \
+ channel = ( initialFreqInt << 14 ) + ( ( ( initialFreqFrac << 14 ) + ( FREQ_STEP_14 / 2 ) ) / FREQ_STEP_14 ); \
+ }while( 0 )
+
+
+
+#define RX_BUFFER_SIZE 256
+
+/* External variables --------------------------------------------------------*/
+/* Exported macros -----------------------------------------------------------*/
+/* Exported functions ------------------------------------------------------- */
+
+/*!
+ * ============================================================================
+ * Public functions prototypes
+ * ============================================================================
+ */
+
+/*
+ * SUBG_RF DIO IRQ callback functions prototype
+ */
+
+/*!
+ * @brief DIO IRQ callback : must be provided by SUBG_RF clients
+ * @param none
+ * @retval none
+ */
+void RadioOnDioIrq( RadioIrqMasks_t radioIrq );
+
+/*!
+ * @brief Initialises the bus for SUBG_RF driver communication
+ * @param none
+ * @retval error code 0: OK, negatif: fail
+ */
+int32_t SUBGRF_Init( void (*RadioOnDioIrqCb) ( RadioIrqMasks_t radioIrq ) );
+
+ /*!
+ * @brief Gets the current Operation Mode of the Radio
+ * @param none
+ * @retval RadioOperatingModes_t last operating mode
+ */
+RadioOperatingModes_t SUBGRF_GetOperatingMode( void );
+
+
+
+/*!
+ * @brief Saves the payload to be send in the radio buffer
+ * @param [in] payload A pointer to the payload
+ * @param [in] size The size of the payload
+ * @retval none
+ */
+void SUBGRF_SetPayload( uint8_t *payload, uint8_t size );
+
+/*!
+ * @brief Reads the payload received. If the received payload is longer
+ * than maxSize, then the method returns 1 and do not set size and payload.
+ * @param [out] payload A pointer to a buffer into which the payload will be copied
+ * @param [out] size A pointer to the size of the payload received
+ * @param [in] maxSize The maximal size allowed to copy into the buffer
+ * @retval none
+ */
+uint8_t SUBGRF_GetPayload( uint8_t *payload, uint8_t *size, uint8_t maxSize );
+
+/*!
+ * @brief Sends a payload
+ * @param [in] payload A pointer to the payload to send
+ * @param [in] size The size of the payload to send
+ * @param [in] timeout The timeout for Tx operation
+ * @retval none
+ */
+void SUBGRF_SendPayload( uint8_t *payload, uint8_t size, uint32_t timeout );
+/*!
+ * @brief Sets RF switch for TX & RX
+ * @param [in] power Low Power or High Power board
+ * @param [in] rxtx RX/TX mode
+ * @retval none
+ */
+void SUBGRF_SetSwitch (uint8_t power, RFState_t rxtx);
+
+/*!
+ * @brief Sets the Sync Word given by index used in GFSK
+ * @param [in] syncWord SyncWord bytes ( 8 bytes )
+ * @retval status [0: OK, 1: NOK]
+ */
+uint8_t SUBGRF_SetSyncWord( uint8_t *syncWord );
+
+/*!
+ * @brief Sets the Initial value for the LFSR used for the CRC calculation
+ * @param [in] seed Initial LFSR value ( 2 bytes )
+ * @retval none
+ */
+void SUBGRF_SetCrcSeed( uint16_t seed );
+
+/*!
+ * @brief Sets the seed used for the CRC calculation
+ * @param [in] seed The seed value
+ * @retval none
+ */
+void SUBGRF_SetCrcPolynomial( uint16_t polynomial );
+
+/*!
+ * @brief Sets the Initial value of the LFSR used for the whitening in GFSK protocols
+ * @param [in] seed Initial LFSR value
+ * @retval none
+ */
+void SUBGRF_SetWhiteningSeed( uint16_t seed );
+
+/*!
+ * @brief Gets a 32 bits random value generated by the radio
+ * @param none
+ * @remark The radio must be in reception mode before executing this function
+ * @retval randomValue 32 bits random value
+ */
+uint32_t SUBGRF_GetRandom( void );
+
+/*!
+ * @brief Sets the radio in sleep mode
+ * @param [in] sleepConfig The sleep configuration describing data
+ * retention and RTC wake-up
+ * @retval none
+ */
+void SUBGRF_SetSleep( SleepParams_t sleepConfig );
+
+/*!
+ * @brief Sets the radio in configuration mode
+ * @param [in] mode The standby mode to put the radio into
+ * @retval none
+ */
+void SUBGRF_SetStandby( RadioStandbyModes_t mode );
+
+/*!
+ * @brief Sets the radio in FS mode
+ * @param none
+ * @retval none
+ */
+void SUBGRF_SetFs( void );
+
+/*!
+ * @brief Sets the radio in transmission mode
+ * @param [in] timeout : Structure describing the transmission timeout value
+ * @retval none
+ */
+void SUBGRF_SetTx( uint32_t timeout );
+
+/*!
+ * @brief Sets the radio in reception mode
+ * @param [in] timeout : Structure describing the reception timeout value
+ * @retval none
+ */
+void SUBGRF_SetRx( uint32_t timeout );
+
+/*!
+ * @brief Sets the radio in reception mode with Boosted LNA gain
+ * @param [in] timeout Structure describing the reception timeout value
+ * @retval none
+ */
+void SUBGRF_SetRxBoosted( uint32_t timeout );
+
+/*!
+ * @brief Sets the Rx duty cycle management parameters
+ * @param [in] rxTime Structure describing reception timeout value
+ * @param [in] sleepTime Structure describing sleep timeout value
+ * @retval none
+ */
+void SUBGRF_SetRxDutyCycle( uint32_t rxTime, uint32_t sleepTime );
+
+/*!
+ * @brief Sets the radio in CAD mode
+ * @param none
+ * @retval none
+ */
+void SUBGRF_SetCad( void );
+
+/*!
+ * @brief Sets the radio in continuous wave transmission mode
+ * @param none
+ * @retval none
+ */
+void SUBGRF_SetTxContinuousWave( void );
+
+/*!
+ * @brief Sets the radio in continuous preamble transmission mode
+ * @param none
+ * @retval none
+ */
+void SUBGRF_SetTxInfinitePreamble( void );
+
+/*!
+ * @brief Decide which interrupt will stop the internal radio rx timer.
+ * @param [in] enable [0: Timer stop after header/syncword detection
+ * 1: Timer stop after preamble detection]
+ * @retval none
+ */
+void SUBGRF_SetStopRxTimerOnPreambleDetect( bool enable );
+
+/*!
+ * @brief Set the number of symbol the radio will wait to validate a reception
+ * @param [in] SymbNum number of LoRa symbols
+ * @retval none
+ */
+void SUBGRF_SetLoRaSymbNumTimeout( uint8_t SymbNum );
+
+/*!
+ * @brief Sets the power regulators operating mode
+ * @param [in] void
+ * @retval none
+ */
+void SUBGRF_SetRegulatorMode( void );
+
+/*!
+ * @brief Calibrates the given radio block
+ * @param [in] calibParam The description of blocks to be calibrated
+ * @retval none
+ */
+void SUBGRF_Calibrate( CalibrationParams_t calibParam );
+
+/*!
+ * @brief Calibrates the Image rejection depending of the frequency
+ * @param [in] freq The operating frequency
+ * @retval none
+ */
+void SUBGRF_CalibrateImage( uint32_t freq );
+
+/*!
+ * @brief Activate the extention of the timeout when long preamble is used
+ * @param [in] enable The radio will extend the timeout to cope with long preamble
+ * @retval none
+ */
+void SUBGRF_SetLongPreamble( uint8_t enable );
+
+/*!
+ * @brief Sets the transmission parameters
+ * @param [in] paDutyCycle Duty Cycle for the PA
+ * @param [in] hpMax 0 for RFO_LP, 7 for RFO_HP
+ * @param [in] deviceSel 1 for RFO_LP, 0 for RFO_HP
+ * @param [in] paLut 0 for 14dBm LUT, 1 for 22dBm LUT
+ * @retval none
+ */
+void SUBGRF_SetPaConfig( uint8_t paDutyCycle, uint8_t hpMax, uint8_t deviceSel, uint8_t paLut );
+
+/*!
+ * @brief Defines into which mode the chip goes after a TX / RX done
+ * @param [in] fallbackMode The mode in which the radio goes
+ * @retval none
+ */
+void SUBGRF_SetRxTxFallbackMode( uint8_t fallbackMode );
+
+
+
+/*!
+ * @brief Sets the IRQ mask and DIO masks
+ * @param [in] irqMask General IRQ mask
+ * @param [in] dio1Mask DIO1 mask
+ * @param [in] dio2Mask DIO2 mask
+ * @param [in] dio3Mask DIO3 mask
+ * @retval none
+ */
+void SUBGRF_SetDioIrqParams( uint16_t irqMask, uint16_t dio1Mask, uint16_t dio2Mask, uint16_t dio3Mask );
+
+/*!
+ * @brief Returns the current IRQ status
+ * @retval irqStatus IRQ status
+ * @retval none
+ */
+uint16_t SUBGRF_GetIrqStatus( void );
+
+
+/*!
+ * @brief Indicates if the Radio main clock is supplied from a tcxo
+ * @param [in] tcxoVoltage voltage used to control the TCXO
+ * @param [in] timeout time given to the TCXO to go to 32MHz
+ * @retval none
+ */
+void SUBGRF_SetTcxoMode( RadioTcxoCtrlVoltage_t tcxoVoltage, uint32_t timeout );
+
+/*!
+ * @brief Sets the RF frequency
+ * @param [in] frequency RF frequency [Hz]
+ * @retval none
+ */
+void SUBGRF_SetRfFrequency( uint32_t frequency );
+
+/*!
+ * @brief Sets the radio for the given protocol
+ * @param [in] packetType [PACKET_TYPE_GFSK, PACKET_TYPE_LORA]
+ * @remark This method has to be called before SetRfFrequency,
+ * SetModulationParams and SetPacketParams
+ * @retval none
+ */
+void SUBGRF_SetPacketType( RadioPacketTypes_t packetType );
+
+/*!
+ * @brief Gets the current radio protocol
+ * @param none
+ * @retval packetType [PACKET_TYPE_GFSK, PACKET_TYPE_LORA]
+ */
+RadioPacketTypes_t SUBGRF_GetPacketType( void );
+
+/*!
+ * @brief Sets the transmission parameters
+ *
+ * @param [in] paSelect RegPaConfig PaSelect value (RFO_LP, RFO_HP, etc)
+ * @param [in] power RF output power [-18..13] dBm
+ * @param [in] rampTime Transmission ramp up time
+ * @retval none
+ */
+void SUBGRF_SetTxParams( uint8_t paSelect, int8_t power, RadioRampTimes_t rampTime );
+
+
+
+uint8_t SUBGRF_SetTxPower( int8_t power, RadioRampTimes_t rampTime );
+/*!
+ * @brief Set the modulation parameters
+ * @param [in] modParams : structure describing the modulation parameters
+ * @retval none
+ */
+void SUBGRF_SetModulationParams( ModulationParams_t *modParams );
+
+/*!
+ * @brief Sets the packet parameters
+ * @param [in] packetParams: structure describing the packet parameters
+ * @retval none
+ */
+void SUBGRF_SetPacketParams( PacketParams_t *packetParams );
+
+/*!
+ * @brief Sets the Channel Activity Detection (CAD) parameters
+ *
+ * @param [in] cadSymbolNum The number of symbol to use for CAD operations
+ * [LORA_CAD_01_SYMBOL, LORA_CAD_02_SYMBOL,
+ * LORA_CAD_04_SYMBOL, LORA_CAD_08_SYMBOL,
+ * LORA_CAD_16_SYMBOL]
+ * @param [in] cadDetPeak Limite for detection of SNR peak used in the CAD
+ * @param [in] cadDetMin Set the minimum symbol recognition for CAD
+ * @param [in] cadExitMode Operation to be done at the end of CAD action
+ * [LORA_CAD_ONLY, LORA_CAD_RX, LORA_CAD_LBT]
+ * @param [in] cadTimeout Defines the timeout value to abort the CAD activity
+ * @retval none
+ */
+void SUBGRF_SetCadParams( RadioLoRaCadSymbols_t cadSymbolNum, uint8_t cadDetPeak, uint8_t cadDetMin, RadioCadExitModes_t cadExitMode, uint32_t cadTimeout );
+
+/*!
+ * @brief Sets the data buffer base address for transmission and reception
+ * @param [in] txBaseAddress Transmission base address
+ * @param [in] rxBaseAddress Reception base address
+ * @retval none
+ */
+void SUBGRF_SetBufferBaseAddress( uint8_t txBaseAddress, uint8_t rxBaseAddress );
+
+/*!
+ * @brief Gets the current radio status
+ * @param none
+ * @retval Radio status
+ */
+RadioStatus_t SUBGRF_GetStatus( void );
+
+/*!
+ * @brief Returns the instantaneous RSSI value for the last packet received
+ * @param none
+ * @retval rssiInst Instantaneous RSSI
+ */
+int8_t SUBGRF_GetRssiInst( void );
+
+/*!
+ * @brief Gets the last received packet buffer status
+ * @param [out] payloadLength Last received packet payload length
+ * @param [out] rxStartBuffer Last received packet buffer address pointer
+ * @retval none
+ */
+void SUBGRF_GetRxBufferStatus( uint8_t *payloadLength, uint8_t *rxStartBuffer );
+
+/*!
+ * @brief Gets the last received packet payload length
+ * @param [out] pktStatus A structure of packet status
+ * @retval none
+ */
+void SUBGRF_GetPacketStatus( PacketStatus_t *pktStatus );
+
+/*!
+ * @brief Returns the possible system erros
+ * @param none
+ * @retval sysErrors Value representing the possible sys failures
+ */
+RadioError_t SUBGRF_GetDeviceErrors( void );
+
+/*!
+ * @brief Clears the IRQs
+ * @param [in] irq IRQ(s) to be cleared
+ * @retval none
+ */
+void SUBGRF_ClearIrqStatus( uint16_t irq );
+
+/*!
+ * @brief Service to get the radio wake-up time.
+ * @param none
+ * @retval Value of the radio wake-up time.
+ */
+uint32_t SUBGRF_GetRadioWakeUpTime( void );
+
+/*!
+ * @brief Get Error
+ * @param none
+ * @retval sysErrors Value representing the possible sys failures
+ */
+RadioError_t SUBGRF_GetErrors( void );
+
+/*!
+ * @brief Write radio register
+ * @param adress of the register
+ * @param data to write
+ * @retval none
+ */
+void SUBGRF_WriteRegister( uint16_t addr, uint8_t data );
+
+/*!
+ * @brief Read radio register
+ * @param adress of the register
+ * @retval Data read
+ */
+uint8_t SUBGRF_ReadRegister( uint16_t addr );
+
+/*!
+ * @brief Write several consecutive radio register
+ * @param adress of the 1st register to write
+ * @param array of datas to write
+ * @param amount of datas to write
+ * @retval none
+ */
+void SUBGRF_WriteRegisters( uint16_t addr, uint8_t *buffer, uint8_t size );
+
+/*!
+ * @brief Read several consecutive radio register
+ * @param adress of the 1st register to read
+ * @param array of datas to read
+ * @param amount of datas to read
+ * @retval none
+ */
+void SUBGRF_ReadRegisters( uint16_t addr, uint8_t *buffer, uint8_t size );
+#endif // __RADIO_DRIVER_H__
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
+
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/stm32_radio_driver/radio_mw_version.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/stm32_radio_driver/radio_mw_version.h
new file mode 100644
index 00000000..3946e834
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/stm32_radio_driver/radio_mw_version.h
@@ -0,0 +1,56 @@
+/******************************************************************************
+ * @file radio_mw_version.h
+ * @author MCD Application Team
+ * @brief defines the radio driver version
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under BSD 3-Clause license,
+ * the "License"; You may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ * opensource.org/licenses/BSD-3-Clause
+ *
+ ******************************************************************************
+ */
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+
+#ifndef __RADIO_MW_VERSION_H__
+#define __RADIO_MW_VERSION_H__
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+/* Exported constants --------------------------------------------------------*/
+
+/* __SUBGH_RADIO_TYPE: 0x01 WTM32WL
+ 0x61 sx126x
+ 0x72 sx1272
+ 0x76 sx1276 */
+
+#define __SUBGH_RADIO_TYPE (0x01U) /*!< [31:24] main version */
+#define __SUBGH_MW_VERSION_MAIN (0x01U) /*!< [23:16] main version */
+#define __SUBGH_MW_VERSION_SUB1 (0x00U) /*!< [15:8] sub1 version */
+#define __SUBGH_MW_VERSION_SUB2 (0x00U) /*!< [7:0] sub2 version */
+#define __SUBGH_MW_VERSION ((__SUBGH_RADIO_TYPE <<24)\
+ |(__SUBGH_MW_VERSION_MAIN << 16)\
+ |(__SUBGH_MW_VERSION_SUB1 << 8 )\
+ |(__SUBGH_MW_VERSION_SUB2))
+
+/* Exported types ------------------------------------------------------------*/
+/* External variables --------------------------------------------------------*/
+/* Exported macros -----------------------------------------------------------*/
+/* Exported functions ------------------------------------------------------- */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*__RADIO_MW_VERSION_H__*/
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Src/adc.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Src/adc.c
new file mode 100644
index 00000000..eed304a7
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Src/adc.c
@@ -0,0 +1,142 @@
+/**
+ ******************************************************************************
+ * @file adc.c
+ * @author MCD Application Team
+ * @brief configuration of the ADC instances
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under Ultimate Liberty license
+ * SLA0044, the "License"; You may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at:
+ * www.st.com/SLA0044
+ *
+ ******************************************************************************
+ */
+
+/* Includes ------------------------------------------------------------------*/
+#include "platform.h"
+#include "adc.h"
+#include "dma.h"
+
+#ifdef HAL_ADC_MODULE_ENABLED
+/* USER CODE BEGIN 0 */
+
+/* USER CODE END 0 */
+ADC_HandleTypeDef hadc1;
+DMA_HandleTypeDef hdma_adc1;
+
+/* Private function prototypes -----------------------------------------------*/
+/* Functions Definition ------------------------------------------------------*/
+
+
+/**
+ * @brief ADC1 Initialization Function
+ * @param None
+ * @retval None
+ */
+
+void MX_ADC1_Init(void)
+{
+ /* USER CODE BEGIN ADC1_Init 0 */
+
+ /* USER CODE END ADC1_Init 0 */
+
+
+
+ /* USER CODE BEGIN ADC1_Init 1 */
+
+ /* USER CODE END ADC1_Init 1 */
+ /** Common config
+ */
+ hadc1.Instance = ADC1;
+ hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2;
+ hadc1.Init.Resolution = ADC_RESOLUTION_12B;
+ hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
+ hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE; /* Sequencer set to fully configurable: only the rank 1 is enabled (no scan sequence on several ranks) */
+ hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
+ hadc1.Init.ContinuousConvMode = DISABLE; /* Continuous mode disabled to have only 1 conversion at each conversion trig */
+ hadc1.Init.NbrOfConversion = 1; /* Parameter discarded because sequencer is disabled. Parameter relevancy depending on setting of parameter "ScanConvMode" */
+ hadc1.Init.DiscontinuousConvMode = DISABLE; /* Parameter discarded because sequencer is disabled */
+ hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START; /* Software start to trig the 1st conversion manually, without external event */
+ hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; /* Parameter discarded because trig of conversion by software start (no external event) */
+ hadc1.Init.DMAContinuousRequests = DISABLE; /* ADC with DMA transfer: continuous requests to DMA disabled (default state) since DMA is not used in this example. */
+ hadc1.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
+ hadc1.Init.SamplingTimeCommon1 = LL_ADC_SAMPLINGTIME_160CYCLES_5;
+ hadc1.Init.OversamplingMode = DISABLE;
+ hadc1.Init.TriggerFrequencyMode = ADC_TRIGGER_FREQ_HIGH;
+ if (HAL_ADC_Init(&hadc1) != HAL_OK)
+ {
+ Error_Handler();
+ }
+
+ if (HAL_ADCEx_Calibration_Start(&hadc1) != HAL_OK)
+ {
+ Error_Handler();
+ }
+ /* USER CODE BEGIN ADC1_Init 2 */
+
+ /* USER CODE END ADC1_Init 2 */
+}
+
+
+/**
+* @brief ADC MSP Initialization
+* This function configures the hardware resources used in this example
+* @param hadc: ADC handle pointer
+* @retval None
+*/
+void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)
+{
+
+ /*##-1- Enable peripherals and GPIO Clocks #################################*/
+ /* Enable clock of GPIO associated to the peripheral channels */
+ /* Enable clock of ADCx peripheral (core clock) */
+ __HAL_RCC_ADC_CLK_ENABLE();
+
+ /* Note: In case of usage of asynchronous clock for ADC, with ADC setting */
+ /* "AdcHandle.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIVx", */
+ /* the clock source has to be enabled at RCC top level using function */
+ /* "HAL_RCCEx_PeriphCLKConfig()" or macro "__HAL_RCC_ADC_CONFIG()" */
+ /* (refer to comments in driver file header). */
+
+ /*##-4- Configure the NVIC #################################################*/
+ /* NVIC configuration for ADC interrupt */
+ /* Priority: high-priority */
+
+}
+
+/**
+* @brief ADC MSP De-Initialization
+* This function freeze the hardware resources used in this example
+* @param hadc: ADC handle pointer
+* @retval None
+*/
+void HAL_ADC_MspDeInit(ADC_HandleTypeDef *hadc)
+{
+ if (hadc->Instance == ADC1)
+ {
+ /* USER CODE BEGIN ADC1_MspDeInit 0 */
+
+ /* USER CODE END ADC1_MspDeInit 0 */
+ /* Peripheral clock disable */
+ __HAL_RCC_ADC_CLK_DISABLE();
+
+ /**ADC1 GPIO Configuration
+ PC3 ------> ADC1_IN4
+ */
+ /* USER CODE BEGIN ADC1_MspDeInit 1 */
+
+ /* USER CODE END ADC1_MspDeInit 1 */
+ }
+
+}
+#endif
+/* USER CODE BEGIN 1 */
+
+/* USER CODE END 1 */
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Src/adc_if.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Src/adc_if.c
new file mode 100644
index 00000000..9e0e469b
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Src/adc_if.c
@@ -0,0 +1,179 @@
+/**
+ ******************************************************************************
+ * @file adc_if.c
+ * @author MCD Application Team
+ * @brief Read status related to the chip (battery level, VREF, chip temperature)
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under Ultimate Liberty license
+ * SLA0044, the "License"; You may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at:
+ * www.st.com/SLA0044
+ *
+ ******************************************************************************
+*/
+
+/* Includes ------------------------------------------------------------------*/
+#include "adc_if.h"
+#include "app_system.h"
+#ifdef HAL_ADC_MODULE_ENABLED
+#include "adc.h"
+#endif
+
+/* External variables ---------------------------------------------------------*/
+#ifdef HAL_ADC_MODULE_ENABLED
+extern ADC_HandleTypeDef hadc1;
+#endif
+/* Private typedef -----------------------------------------------------------*/
+/* Private define ------------------------------------------------------------*/
+
+/*from dtatsheet*/
+#define TEMPSENSOR_TYP_CAL1_V (( int32_t) 760) /* Internal temperature sensor, parameter V30 (unit: mV). Refer to device datasheet for min/typ/max values. */
+#define TEMPSENSOR_TYP_AVGSLOPE (( int32_t) 2500) /* Internal temperature sensor, parameter Avg_Slope (unit: uV/DegCelsius). Refer to device datasheet for min/typ/max values. */
+#define TEMPSENSOR_CAL_VREF ((uint32_t) 3000) /* Vdda value with which temperature sensor has been calibrated in production (+-10 mV). */
+
+/* Private macro -------------------------------------------------------------*/
+#define __LL_ADC_CALC_VREFANALOG_VOLTAGE_TEMPORARY(__VREFINT_ADC_DATA__,\
+ __ADC_RESOLUTION__) \
+ (((uint32_t)(*VREFINT_CAL_ADDR) * VREFINT_CAL_VREF) \
+ / __LL_ADC_CONVERT_DATA_RESOLUTION((__VREFINT_ADC_DATA__), \
+ (__ADC_RESOLUTION__), \
+ LL_ADC_RESOLUTION_12B) \
+ )
+
+/* Private variables ---------------------------------------------------------*/
+
+
+/* Private function prototypes -----------------------------------------------*/
+
+static uint32_t ADC_ReadChannels(uint32_t channel);
+
+/* Functions Definition ------------------------------------------------------*/
+
+
+/**
+ * @brief This function initializes the ADC
+ * @param none
+ * @retval none
+ */
+void SYS_InitMeasurement(void)
+{
+ hadc1.Instance = ADC1;
+}
+
+void SYS_DeInitMeasurement(void)
+{
+}
+
+int16_t SYS_GetTemperatureLevel(void)
+{
+ int16_t temperatureDegreeC = 6400; /*25degreeC in q7.8*/
+
+#ifdef HAL_ADC_MODULE_ENABLED
+ uint32_t measuredLevel = 0;
+ uint16_t batteryLevelmV = SYS_GetBatteryLevel();
+
+ measuredLevel = ADC_ReadChannels(ADC_CHANNEL_TEMPSENSOR);
+
+ /* #warning "to be replaced by __LL_ADC_CALC_TEMPERATURE when calibration data will be set in prod"*/
+ temperatureDegreeC = __LL_ADC_CALC_TEMPERATURE_TYP_PARAMS(TEMPSENSOR_TYP_AVGSLOPE,
+ TEMPSENSOR_TYP_CAL1_V,
+ TEMPSENSOR_CAL1_TEMP,
+ batteryLevelmV,
+ measuredLevel,
+ LL_ADC_RESOLUTION_12B);
+ /*to be replaced */
+ APP_LOG("temp= %d\n\r", temperatureDegreeC);
+
+ /* from int16 to q8.7*/
+ temperatureDegreeC <<= 8;
+
+#endif /* HAL_ADC_MODULE_ENABLED */
+
+ return (int16_t) temperatureDegreeC;
+}
+/**
+ * @brief This function return the battery level
+ * @param none
+ * @retval the battery level in mV
+ */
+uint16_t SYS_GetBatteryLevel(void)
+{
+ uint16_t batteryLevelmV = 3300;
+#ifdef HAL_ADC_MODULE_ENABLED
+ uint32_t measuredLevel = 0;
+
+ measuredLevel = ADC_ReadChannels(ADC_CHANNEL_VREFINT);
+
+ if (measuredLevel == 0)
+ {
+ batteryLevelmV = 0;
+ }
+ else
+ {
+ batteryLevelmV = (3300 * 1510) / measuredLevel;
+ /* #warning "to be replaced by __LL_ADC_CALC_VREFANALOG_VOLTAGE when calibration data will be set in prod" */
+ }
+#endif /* HAL_ADC_MODULE_ENABLED */
+
+ return batteryLevelmV;
+}
+
+/* Private function definition -----------------------------------------------*/
+/**
+ * @brief This function reads the ADC channel
+ * @param Channel
+ * @retval Value
+ */
+static uint32_t ADC_ReadChannels(uint32_t channel)
+{
+ uint32_t ADCxConvertedValues = 0;
+#ifdef HAL_ADC_MODULE_ENABLED
+ ADC_ChannelConfTypeDef sConfig = {0};
+
+ MX_ADC1_Init();
+ /** Configure Regular Channel */
+ sConfig.Channel = channel;
+ sConfig.Rank = ADC_REGULAR_RANK_1;
+ sConfig.SamplingTime = ADC_SAMPLINGTIME_COMMON_1;
+ if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
+ {
+ Error_Handler();
+ }
+
+ if (HAL_ADC_Start(&hadc1) != HAL_OK)
+ {
+ /* Start Error */
+ Error_Handler();
+ }
+ /** Wait for end of conversion */
+ HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY);
+
+ /* Workaround for tempsensor value*/
+ if (channel == ADC_CHANNEL_TEMPSENSOR)
+ {
+ if (HAL_ADC_Start(&hadc1) != HAL_OK)
+ {
+ /* Start Error */
+ Error_Handler();
+ }
+ /** Wait for end of conversion */
+ HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY);
+ }
+
+ /** Wait for end of conversion */
+ HAL_ADC_Stop(&hadc1) ; /* it calls also ADC_Disable() */
+
+ ADCxConvertedValues = HAL_ADC_GetValue(&hadc1);
+
+ HAL_ADC_DeInit(&hadc1);
+
+#endif /* HAL_ADC_MODULE_ENABLED */
+ return ADCxConvertedValues;
+}
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Src/dma.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Src/dma.c
new file mode 100644
index 00000000..4a190677
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Src/dma.c
@@ -0,0 +1,56 @@
+/**
+ ******************************************************************************
+ * @file dma.c
+ * @author MCD Application Team
+ * @brief configuration of all the requested memory to memory DMA transfers
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under Ultimate Liberty license
+ * SLA0044, the "License"; You may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at:
+ * www.st.com/SLA0044
+ *
+ ******************************************************************************
+ */
+/* Includes ------------------------------------------------------------------*/
+#include "dma.h"
+
+/* USER CODE BEGIN 0 */
+
+/* USER CODE END 0 */
+
+/*----------------------------------------------------------------------------*/
+/* Configure DMA */
+/*----------------------------------------------------------------------------*/
+
+/* USER CODE BEGIN 1 */
+
+/* USER CODE END 1 */
+
+/**
+ * Enable DMA controller clock
+ */
+void MX_DMA_Init(void)
+{
+ /* DMA controller clock enable */
+ __HAL_RCC_DMA1_CLK_ENABLE();
+#ifdef HAL_ADC_MODULE_ENABLED
+ /* DMA interrupt init */
+ /* DMA1_Channel1_IRQn interrupt configuration */
+ HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 1, 0);
+ HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
+#endif
+ /* DMA1_Channel7_IRQn interrupt configuration */
+ HAL_NVIC_SetPriority(DMA1_Channel7_IRQn, 1, 0);
+ HAL_NVIC_EnableIRQ(DMA1_Channel7_IRQn);
+}
+
+/* USER CODE BEGIN 2 */
+
+/* USER CODE END 2 */
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Src/mcu_init.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Src/mcu_init.c
index 1c31ba63..71587d52 100644
--- a/board/RHF0M0E5_STM32WLE5xx/BSP/Src/mcu_init.c
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Src/mcu_init.c
@@ -29,9 +29,9 @@ void board_init(void)
{
HAL_Init();
SystemClock_Config();
- MX_LPUART1_UART_Init();
- MX_USART1_UART_Init();
- MX_USART2_UART_Init();
+
+ SystemApp_Init();
+ MX_LoRaWAN_Init();
}
/**
@@ -75,6 +75,7 @@ void SystemClock_Config(void)
}
}
+
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Src/rtc.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Src/rtc.c
new file mode 100644
index 00000000..ef084bc2
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Src/rtc.c
@@ -0,0 +1,183 @@
+/**
+ ******************************************************************************
+ * @file rtc.c
+ * @author MCD Application Team
+ * @brief Configures the RTC instances
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under Ultimate Liberty license
+ * SLA0044, the "License"; You may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at:
+ * www.st.com/SLA0044
+ *
+ ******************************************************************************
+ */
+
+/* Includes ------------------------------------------------------------------*/
+#include "rtc.h"
+
+
+/* External variables --------------------------------------------------------*/
+/* Private typedef -----------------------------------------------------------*/
+RCC_PeriphCLKInitTypeDef PeriphClkInitStruct;
+RTC_TimeTypeDef RTC_TimeStructureGet;
+RTC_DateTypeDef RTC_DateStructureGet;
+
+RTC_HandleTypeDef hrtc;
+/* Private define ------------------------------------------------------------*/
+/* Private macro -------------------------------------------------------------*/
+/* Private variables ---------------------------------------------------------*/
+/* Private function prototypes -----------------------------------------------*/
+/* Exported variables --------------------------------------------------------*/
+
+/* Exported functions --------------------------------------------------------*/
+void MX_RTC_Init(void)
+{
+ RTC_TimeTypeDef sTime = {0};
+ RTC_DateTypeDef sDate = {0};
+ RTC_AlarmTypeDef sAlarm = {0};
+
+ /** Initialize RTC Only
+ */
+ hrtc.Instance = RTC;
+
+ hrtc.Init.HourFormat = RTC_HOURFORMAT_24;
+ hrtc.Init.AsynchPrediv = RTC_PREDIV_A;
+ hrtc.Init.SynchPrediv = RTC_PREDIV_S;
+ hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;
+ hrtc.Init.OutPutRemap = RTC_OUTPUT_REMAP_NONE;
+ hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
+ hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
+
+ if (HAL_RTC_Init(&hrtc) != HAL_OK)
+ {
+ Error_Handler();
+ }
+
+ /* USER CODE BEGIN Check_RTC_BKUP */
+
+ /* USER CODE END Check_RTC_BKUP */
+
+ /**Initialize RTC and set the Time and Date
+ */
+ sTime.Hours = 0;
+ sTime.Minutes = 0;
+ sTime.Seconds = 0;
+ sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
+ sTime.StoreOperation = RTC_STOREOPERATION_RESET;
+ if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN) != HAL_OK)
+ {
+ Error_Handler();
+ }
+
+
+ sDate.WeekDay = RTC_WEEKDAY_MONDAY;
+ sDate.Month = RTC_MONTH_JANUARY;
+ sDate.Date = 1;
+ sDate.Year = 0;
+
+ if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN) != HAL_OK)
+ {
+ Error_Handler();
+ }
+
+ /**Enable the Alarm A
+ */
+ sAlarm.AlarmTime.Hours = 0;
+ sAlarm.AlarmTime.Minutes = 0;
+ sAlarm.AlarmTime.Seconds = 0;
+ sAlarm.AlarmTime.SubSeconds = 0;
+ sAlarm.AlarmTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
+ sAlarm.AlarmTime.StoreOperation = RTC_STOREOPERATION_RESET;
+ sAlarm.AlarmMask = RTC_ALARMMASK_NONE;
+ sAlarm.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_SS14_10;
+ sAlarm.AlarmDateWeekDaySel = RTC_ALARMDATEWEEKDAYSEL_DATE;
+ sAlarm.AlarmDateWeekDay = 30;
+ sAlarm.Alarm = RTC_ALARM_A;
+ if (HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, RTC_FORMAT_BIN) != HAL_OK)
+ {
+ Error_Handler();
+ }
+}
+
+/**
+ * @brief RTC MSP Initialization
+ * This function configures the hardware resources used in this example:
+ * - Peripheral's clock enable
+ * @param hrtc: RTC handle pointer
+ * @note Care must be taken when HAL_RCCEx_PeriphCLKConfig() is used to select
+ * the RTC clock source; in this case the Backup domain will be reset in
+ * order to modify the RTC Clock source, as consequence RTC registers (including
+ * the backup registers) and RCC_CSR register are set to their reset values.
+ * @retval none
+ */
+void HAL_RTC_MspInit(RTC_HandleTypeDef *rtcHandle)
+{
+ RCC_OscInitTypeDef RCC_OscInitStruct = {0};
+
+ if (rtcHandle->Instance == RTC)
+ {
+ /* USER CODE BEGIN RTC_MspInit 0 */
+
+ /* RCC configuration */
+ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSE;
+ RCC_OscInitStruct.LSEState = RCC_LSE_ON;
+ RCC_OscInitStruct.LSIState = RCC_LSI_OFF;
+ RCC_OscInitStruct.HSEState = RCC_HSE_OFF;
+ RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
+ if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
+ {
+ Error_Handler();
+ }
+
+ __HAL_RCC_RTCAPB_CLK_ENABLE() ;
+ /* USER CODE END RTC_MspInit 0 */
+
+ /* RTC clock enable */
+ __HAL_RCC_RTC_ENABLE();
+
+ /* RTC interrupt Init */
+ HAL_NVIC_SetPriority(RTC_Alarm_IRQn, 0, 0);
+ HAL_NVIC_EnableIRQ(RTC_Alarm_IRQn);
+
+ /* USER CODE BEGIN RTC_MspInit 1 */
+
+ /* USER CODE END RTC_MspInit 1 */
+
+ }
+}
+
+/**
+ * @brief RTC MSP DeInitialization
+ * @param hrtc: RTC handle pointer
+ * @retval none
+ */
+void HAL_RTC_MspDeInit(RTC_HandleTypeDef *rtcHandle)
+{
+ if (rtcHandle->Instance == RTC)
+ {
+ /* USER CODE BEGIN RTC_MspDeInit 0 */
+
+ /* USER CODE END RTC_MspDeInit 0 */
+ /* Peripheral clock disable */
+ __HAL_RCC_RTC_DISABLE();
+ /* RTC interrupt Deinit */
+ HAL_NVIC_DisableIRQ(RTC_Alarm_IRQn);
+ /* USER CODE BEGIN RTC_MspDeInit 1 */
+
+
+ /* USER CODE END RTC_MspDeInit 1 */
+
+ }
+}
+
+/* USER CODE BEGIN 1 */
+
+/* USER CODE END 1 */
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
+
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Src/rtc_if.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Src/rtc_if.c
new file mode 100644
index 00000000..f2974443
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Src/rtc_if.c
@@ -0,0 +1,541 @@
+/*******************************************************************************
+ * @file rtc_if.c
+ * @author MCD Application Team
+ * @brief Handles the RTC
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under Ultimate Liberty license
+ * SLA0044, the "License"; You may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at:
+ * www.st.com/SLA0044
+ *
+ ******************************************************************************
+ */
+
+
+/* Includes ------------------------------------------------------------------*/
+#include
+#include "platform.h"
+#include "rtc_if.h"
+#include "stm32_lpm.h"
+#include "app_system.h"
+#include "stm32wlxx_ll_rtc.h"
+/* External variables ---------------------------------------------------------*/
+extern RTC_HandleTypeDef hrtc;
+#define RtcHandle hrtc
+
+/* Private typedef -----------------------------------------------------------*/
+typedef struct
+{
+ uint32_t Rtc_Time; /* Reference time */
+
+ RTC_TimeTypeDef RTC_Calndr_Time; /* Reference time in calendar format */
+
+ RTC_DateTypeDef RTC_Calndr_Date; /* Reference date in calendar format */
+
+} RtcTimerContext_t;
+
+/* Private define ------------------------------------------------------------*/
+
+/* MCU Wake Up Time */
+#define MIN_ALARM_DELAY 3 /* in ticks */
+
+/* subsecond number of bits */ /* Now defined in main.h via MX GUI */
+//#define RTC_N_PREDIV_S 10
+//#define RTC_PREDIV_S ((1<>RTC_N_PREDIV_S)
+
+#define COMMON_FACTOR 3
+#define CONV_NUMER (MSEC_NUMBER>>COMMON_FACTOR)
+#define CONV_DENOM (1<<(RTC_N_PREDIV_S-COMMON_FACTOR))
+
+#define DAYS_IN_LEAP_YEAR (uint32_t) 366
+
+#define DAYS_IN_YEAR (uint32_t) 365
+
+#define SECONDS_IN_1DAY (uint32_t) 86400
+
+#define SECONDS_IN_1HOUR (uint32_t) 3600
+
+#define SECONDS_IN_1MINUTE (uint32_t) 60
+
+#define MINUTES_IN_1HOUR (uint32_t) 60
+
+#define HOURS_IN_1DAY (uint32_t) 24
+
+#define DAYS_IN_MONTH_CORRECTION_NORM ((uint32_t) 0x99AAA0 )
+#define DAYS_IN_MONTH_CORRECTION_LEAP ((uint32_t) 0x445550 )
+
+
+/* Calculates ceiling(X/N) */
+#define DIVC(X,N) ( ( (X) + (N) -1 ) / (N) )
+
+
+/* Private macro -------------------------------------------------------------*/
+/* Private variables ---------------------------------------------------------*/
+/*!
+ * \brief Indicates if the RTC is already Initalized or not
+ */
+static bool RTC_Initalized = false;
+
+/*!
+ * \brief compensates MCU wakeup time
+ */
+
+static bool McuWakeUpTimeInitialized = false;
+
+/*!
+ * \brief compensates MCU wakeup time
+ */
+
+static int16_t McuWakeUpTimeCal = 0;
+
+/*!
+ * Number of days in each month on a normal year
+ */
+static const uint8_t DaysInMonth[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+
+/*!
+ * Number of days in each month on a leap year
+ */
+static const uint8_t DaysInMonthLeapYear[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+
+static RTC_AlarmTypeDef RTC_AlarmStructure;
+
+
+/*!
+ * Keep the value of the RTC timer when the RTC alarm is set
+ * Set with the RTC_SetTimerContext function
+ * Value is kept as a Reference to calculate alarm
+ */
+static RtcTimerContext_t RtcTimerContext;
+
+
+/* Private function prototypes -----------------------------------------------*/
+/*!
+ * @brief start wake up alarm
+ * @note alarm in RtcTimerContext.Rtc_Time + timeoutValue
+ * @param timeoutValue in ticks
+ * @retval none
+ */
+static void RTC_StartWakeUpAlarm(uint32_t timeoutValue);
+
+/*!
+ * @brief get current time from calendar in ticks
+ * @param pointer to RTC_DateStruct
+ * @param pointer to RTC_TimeStruct
+ * @retval time in ticks
+ */
+static uint32_t RTC_GetCalendarValue(RTC_DateTypeDef *RTC_DateStruct, RTC_TimeTypeDef *RTC_TimeStruct);
+
+/* Exported variables ---------------------------------------------------------*/
+/* Exported functions ---------------------------------------------------------*/
+/**
+ * @brief This function configures the source of the time base.
+ * @brief don't enable systick
+ * @param TickPriority: Tick interrupt priority.
+ * @retval HAL status
+ */
+HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
+{
+ /*Initialize the RTC services */
+ RTC_Init();
+ return HAL_OK;
+}
+
+/**
+ * @brief Provide a tick value in millisecond measured using RTC
+ * @note This function overwrites the __weak one from HAL
+ * @retval tick value
+ */
+uint32_t HAL_GetTick(void)
+{
+ if (RTC_Initalized == false)
+ {
+ return 0; /* HAL_GetTick() based on RTC cannot be used until RTC is initialised */
+ }
+ else
+ {
+ return RTC_GetTimerValue();
+ }
+}
+
+/**
+ * @brief This function provides delay (in ms)
+ * @param Delay: specifies the delay time length, in milliseconds.
+ * @retval None
+ */
+void HAL_Delay(__IO uint32_t Delay)
+{
+ RTC_DelayMs(Delay); /* based on RTC */
+}
+
+
+
+/**
+ * @brief Alarm A callback.
+ * @param RtcHandle: RTC handle
+ * @retval None
+ */
+void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *RtcHandle)
+{
+ //UTIL_TIMER_IRQ_Handler( );
+}
+
+
+
+void RTC_Init(void)
+{
+ RCC_PeriphCLKInitTypeDef PeriphClkInitStruct;
+
+ if (RTC_Initalized == false)
+ {
+ /*##-1- Configue the RTC clock soucre ######################################*/
+
+ /* Select LSE as RTC clock source */
+ PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC;
+ PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
+ if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
+ {
+ /* Initialization Error */
+ while (1);
+ }
+
+ MX_RTC_Init();
+
+#if defined (LPM_STOP_MODE_DISABLE) && (LPM_STOP_MODE_DISABLE == 0) /* LOW_POWER enabled */
+ /* enable RTC ALARM EXTI (AIEC) line */
+ LL_EXTI_EnableIT_0_31(LL_EXTI_LINE_17);
+ LL_EXTI_EnableRisingTrig_0_31(LL_EXTI_LINE_17);
+#elif !defined (LPM_STOP_MODE_DISABLE)
+ #error LPM_STOP_MODE_DISABLE not defined
+#endif
+
+ /*Enable Direct Read of the calendar registers (not through Shadow) */
+ HAL_RTCEx_EnableBypassShadow(&RtcHandle);
+
+ /** Configure the Alarm A */
+ HAL_RTC_DeactivateAlarm(&RtcHandle, RTC_ALARM_A);
+
+ RTC_SetTimerContext();
+ RTC_Initalized = true;
+ }
+}
+
+
+void RTC_setMcuWakeUpTime(void)
+{
+ RTC_TimeTypeDef RTC_TimeStruct;
+ RTC_DateTypeDef RTC_DateStruct;
+
+ uint32_t now, hit;
+ int16_t McuWakeUpTime;
+
+ if ((McuWakeUpTimeInitialized == false) &&
+ (HAL_NVIC_GetPendingIRQ(RTC_Alarm_IRQn) == 1))
+ {
+ /* warning: works ok if now is below 30 days
+ it is ok since it's done once at first alarm wake-up*/
+ McuWakeUpTimeInitialized = true;
+ now = RTC_GetCalendarValue(&RTC_DateStruct, &RTC_TimeStruct);
+
+
+ HAL_RTC_GetAlarm(&RtcHandle, &RTC_AlarmStructure, RTC_ALARM_A, RTC_FORMAT_BIN);
+ hit = RTC_AlarmStructure.AlarmTime.Seconds +
+ 60 * (RTC_AlarmStructure.AlarmTime.Minutes +
+ 60 * (RTC_AlarmStructure.AlarmTime.Hours +
+ 24 * (RTC_AlarmStructure.AlarmDateWeekDay)));
+ hit = (hit << RTC_N_PREDIV_S) + (RTC_PREDIV_S - RTC_AlarmStructure.AlarmTime.SubSeconds);
+
+ McuWakeUpTime = (int16_t)((now - hit));
+ McuWakeUpTimeCal += McuWakeUpTime;
+ APP_LOG("Cal=%d, %d\n\r", McuWakeUpTimeCal, McuWakeUpTime);
+ }
+}
+
+int16_t RTC_getMcuWakeUpTime(void)
+{
+ return McuWakeUpTimeCal;
+}
+
+
+uint32_t RTC_GetMinimumTimeout(void)
+{
+ return (MIN_ALARM_DELAY);
+}
+
+
+uint32_t RTC_ms2Tick(uint32_t timeMicroSec)
+{
+ /*return( ( timeMicroSec / RTC_ALARM_TIME_BASE ) ); */
+ return (uint32_t)((((uint64_t)timeMicroSec) * CONV_DENOM) / CONV_NUMER);
+}
+
+
+uint32_t RTC_Tick2ms(uint32_t tick)
+{
+ /*return( ( timeMicroSec * RTC_ALARM_TIME_BASE ) ); */
+ return (((uint64_t)(tick) * CONV_NUMER) / CONV_DENOM);
+}
+
+uint32_t RTC_GetTimerElapsedTime(void)
+{
+ RTC_TimeTypeDef RTC_TimeStruct;
+ RTC_DateTypeDef RTC_DateStruct;
+
+ uint32_t CalendarValue = RTC_GetCalendarValue(&RTC_DateStruct, &RTC_TimeStruct);
+
+ return ((uint32_t)(CalendarValue - RtcTimerContext.Rtc_Time));
+}
+
+uint32_t RTC_GetTimerValue(void)
+{
+ RTC_TimeTypeDef RTC_TimeStruct;
+ RTC_DateTypeDef RTC_DateStruct;
+
+ uint32_t CalendarValue = (uint32_t) RTC_GetCalendarValue(&RTC_DateStruct, &RTC_TimeStruct);
+
+ return (CalendarValue);
+}
+
+
+
+
+void RTC_DelayMs(uint32_t delay)
+{
+ uint32_t delayValue = 0;
+ uint32_t timeout = 0;
+
+ delayValue = RTC_ms2Tick(delay);
+
+ /* Wait delay ms */
+ timeout = RTC_GetTimerValue();
+ while (((RTC_GetTimerValue() - timeout)) < delayValue)
+ {
+ __NOP();
+ }
+}
+
+
+uint32_t RTC_SetTimerContext(void)
+{
+ RtcTimerContext.Rtc_Time = RTC_GetCalendarValue(&RtcTimerContext.RTC_Calndr_Date, &RtcTimerContext.RTC_Calndr_Time);
+ return (uint32_t) RtcTimerContext.Rtc_Time;
+}
+
+
+uint32_t RTC_GetTimerContext(void)
+{
+ return (uint32_t) RtcTimerContext.Rtc_Time;
+}
+/* Private functions ---------------------------------------------------------*/
+
+/*!
+ * @brief start wake up alarm
+ * @note alarm in RtcTimerContext.Rtc_Time + timeoutValue
+ * @param timeoutValue in ticks
+ * @retval none
+ */
+static void RTC_StartWakeUpAlarm(uint32_t timeoutValue)
+{
+ uint16_t rtcAlarmSubSeconds = 0;
+ uint16_t rtcAlarmSeconds = 0;
+ uint16_t rtcAlarmMinutes = 0;
+ uint16_t rtcAlarmHours = 0;
+ uint16_t rtcAlarmDays = 0;
+ RTC_TimeTypeDef RTC_TimeStruct = RtcTimerContext.RTC_Calndr_Time;
+ RTC_DateTypeDef RTC_DateStruct = RtcTimerContext.RTC_Calndr_Date;
+
+ //RTC_StopAlarm();
+
+
+ /*reverse counter */
+ rtcAlarmSubSeconds = RTC_PREDIV_S - RTC_TimeStruct.SubSeconds;
+ rtcAlarmSubSeconds += (timeoutValue & RTC_PREDIV_S);
+ /* convert timeout to seconds */
+ timeoutValue >>= RTC_N_PREDIV_S; /* convert timeout in seconds */
+
+ /*convert microsecs to RTC format and add to 'Now' */
+ rtcAlarmDays = RTC_DateStruct.Date;
+ while (timeoutValue >= SECONDS_IN_1DAY)
+ {
+ timeoutValue -= SECONDS_IN_1DAY;
+ rtcAlarmDays++;
+ }
+
+ /* calc hours */
+ rtcAlarmHours = RTC_TimeStruct.Hours;
+ while (timeoutValue >= SECONDS_IN_1HOUR)
+ {
+ timeoutValue -= SECONDS_IN_1HOUR;
+ rtcAlarmHours++;
+ }
+
+ /* calc minutes */
+ rtcAlarmMinutes = RTC_TimeStruct.Minutes;
+ while (timeoutValue >= SECONDS_IN_1MINUTE)
+ {
+ timeoutValue -= SECONDS_IN_1MINUTE;
+ rtcAlarmMinutes++;
+ }
+
+ /* calc seconds */
+ rtcAlarmSeconds = RTC_TimeStruct.Seconds + timeoutValue;
+
+ /***** correct for modulo********/
+ while (rtcAlarmSubSeconds >= (RTC_PREDIV_S + 1))
+ {
+ rtcAlarmSubSeconds -= (RTC_PREDIV_S + 1);
+ rtcAlarmSeconds++;
+ }
+
+ while (rtcAlarmSeconds >= SECONDS_IN_1MINUTE)
+ {
+ rtcAlarmSeconds -= SECONDS_IN_1MINUTE;
+ rtcAlarmMinutes++;
+ }
+
+ while (rtcAlarmMinutes >= MINUTES_IN_1HOUR)
+ {
+ rtcAlarmMinutes -= MINUTES_IN_1HOUR;
+ rtcAlarmHours++;
+ }
+
+ while (rtcAlarmHours >= HOURS_IN_1DAY)
+ {
+ rtcAlarmHours -= HOURS_IN_1DAY;
+ rtcAlarmDays++;
+ }
+
+ if (RTC_DateStruct.Year % 4 == 0)
+ {
+ if (rtcAlarmDays > DaysInMonthLeapYear[ RTC_DateStruct.Month - 1 ])
+ {
+ rtcAlarmDays = rtcAlarmDays % DaysInMonthLeapYear[ RTC_DateStruct.Month - 1 ];
+ }
+ }
+ else
+ {
+ if (rtcAlarmDays > DaysInMonth[ RTC_DateStruct.Month - 1 ])
+ {
+ rtcAlarmDays = rtcAlarmDays % DaysInMonth[ RTC_DateStruct.Month - 1 ];
+ }
+ }
+
+ /* Set RTC_AlarmStructure with calculated values*/
+ RTC_AlarmStructure.AlarmTime.SubSeconds = RTC_PREDIV_S - rtcAlarmSubSeconds;
+ RTC_AlarmStructure.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK;
+ RTC_AlarmStructure.AlarmTime.Seconds = rtcAlarmSeconds;
+ RTC_AlarmStructure.AlarmTime.Minutes = rtcAlarmMinutes;
+ RTC_AlarmStructure.AlarmTime.Hours = rtcAlarmHours;
+ RTC_AlarmStructure.AlarmDateWeekDay = (uint8_t)rtcAlarmDays;
+ RTC_AlarmStructure.AlarmTime.TimeFormat = RTC_TimeStruct.TimeFormat;
+ RTC_AlarmStructure.AlarmDateWeekDaySel = RTC_ALARMDATEWEEKDAYSEL_DATE;
+ RTC_AlarmStructure.AlarmMask = RTC_ALARMMASK_NONE;
+ RTC_AlarmStructure.Alarm = RTC_ALARM_A;
+ RTC_AlarmStructure.AlarmTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
+ RTC_AlarmStructure.AlarmTime.StoreOperation = RTC_STOREOPERATION_RESET;
+
+ /* Set RTC_Alarm */
+ HAL_RTC_SetAlarm_IT(&RtcHandle, &RTC_AlarmStructure, RTC_FORMAT_BIN);
+}
+
+
+/*!
+ * @brief get current time from calendar in ticks
+ * @param pointer to RTC_DateStruct
+ * @param pointer to RTC_TimeStruct
+ * @retval time in ticks
+ */
+static uint32_t RTC_GetCalendarValue(RTC_DateTypeDef *RTC_DateStruct, RTC_TimeTypeDef *RTC_TimeStruct)
+{
+ uint32_t calendarValue = 0;
+ uint32_t first_read;
+ uint32_t correction;
+
+ /* Get Time and Date*/
+ HAL_RTC_GetTime(&RtcHandle, RTC_TimeStruct, RTC_FORMAT_BIN);
+
+
+ /* make sure it is correct due to asynchronus nature of RTC*/
+ do
+ {
+ first_read = LL_RTC_TIME_GetSubSecond(RTC);
+ HAL_RTC_GetDate(&RtcHandle, RTC_DateStruct, RTC_FORMAT_BIN);
+ HAL_RTC_GetTime(&RtcHandle, RTC_TimeStruct, RTC_FORMAT_BIN);
+
+ }
+ while (first_read != LL_RTC_TIME_GetSubSecond(RTC));
+
+ /* calculte amount of elapsed days since 01/01/2000 */
+ calendarValue = DIVC((DAYS_IN_YEAR * 3 + DAYS_IN_LEAP_YEAR) * RTC_DateStruct->Year, 4);
+
+ correction = ((RTC_DateStruct->Year % 4) == 0) ? DAYS_IN_MONTH_CORRECTION_LEAP : DAYS_IN_MONTH_CORRECTION_NORM ;
+
+ calendarValue += (DIVC((RTC_DateStruct->Month - 1) * (30 + 31), 2) - (((correction >> ((RTC_DateStruct->Month - 1) * 2)) & 0x3)));
+
+ calendarValue += (RTC_DateStruct->Date - 1);
+
+ /* convert from days to seconds */
+ calendarValue *= SECONDS_IN_1DAY;
+
+ calendarValue += ((uint32_t)RTC_TimeStruct->Seconds +
+ ((uint32_t)RTC_TimeStruct->Minutes * SECONDS_IN_1MINUTE) +
+ ((uint32_t)RTC_TimeStruct->Hours * SECONDS_IN_1HOUR)) ;
+
+ calendarValue = (calendarValue << RTC_N_PREDIV_S) + (RTC_PREDIV_S - RTC_TimeStruct->SubSeconds);
+
+ return (calendarValue);
+}
+
+/*!
+ * \brief Get system time
+ * \param [IN] pointer to ms
+ *
+ * \return uint32_t seconds
+ */
+uint32_t HW_RTC_GetCalendarTime(uint16_t *mSeconds)
+{
+ RTC_TimeTypeDef RTC_TimeStruct ;
+ RTC_DateTypeDef RTC_DateStruct;
+ uint32_t ticks;
+
+ uint64_t calendarValue = RTC_GetCalendarValue(&RTC_DateStruct, &RTC_TimeStruct);
+
+ uint32_t seconds = (uint32_t)(calendarValue >> RTC_N_PREDIV_S);
+
+ ticks = (uint32_t) calendarValue & RTC_PREDIV_S;
+
+ *mSeconds = RTC_Tick2ms(ticks);
+
+ return seconds;
+}
+
+void HW_RTC_BKUPWrite(uint32_t Data0, uint32_t Data1)
+{
+ HAL_RTCEx_BKUPWrite(&RtcHandle, RTC_BKP_DR0, Data0);
+ HAL_RTCEx_BKUPWrite(&RtcHandle, RTC_BKP_DR1, Data1);
+}
+
+void HW_RTC_BKUPRead(uint32_t *Data0, uint32_t *Data1)
+{
+ *Data0 = HAL_RTCEx_BKUPRead(&RtcHandle, RTC_BKP_DR0);
+ *Data1 = HAL_RTCEx_BKUPRead(&RtcHandle, RTC_BKP_DR1);
+}
+
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
+
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Src/stm32_lpm_if.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Src/stm32_lpm_if.c
new file mode 100644
index 00000000..2db1beb5
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Src/stm32_lpm_if.c
@@ -0,0 +1,133 @@
+/*******************************************************************************
+ * @file stm32_lpm_if.c
+ * @author MCD Application Team
+ * @brief Low layer function to enter/exit low power modes (stop, sleep)
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under Ultimate Liberty license
+ * SLA0044, the "License"; You may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at:
+ * www.st.com/SLA0044
+ *
+ ******************************************************************************
+ */
+
+/* Includes ------------------------------------------------------------------*/
+#include "stm32_lpm_if.h"
+#include "usart_if.h"
+
+/* External variables --------------------------------------------------------*/
+extern UART_HandleTypeDef huart1;
+
+/* Private typedef -----------------------------------------------------------*/
+
+const struct UTIL_LPM_Driver_s UTIL_PowerDriver =
+{
+ PWR_EnterSleepMode,
+ PWR_ExitSleepMode,
+
+ PWR_EnterStopMode,
+ PWR_ExitStopMode,
+
+ PWR_EnterOffMode,
+ PWR_ExitOffMode,
+};
+
+/* Private define ------------------------------------------------------------*/
+/* Private macro -------------------------------------------------------------*/
+/* Private variables ---------------------------------------------------------*/
+/* Private function prototypes -----------------------------------------------*/
+/* Exported variables --------------------------------------------------------*/
+/* Exported functions --------------------------------------------------------*/
+
+/**
+ * @brief Enters Low Power Off Mode
+ * @param none
+ * @retval none
+ */
+void PWR_EnterOffMode(void)
+{
+}
+/**
+ * @brief Exits Low Power Off Mode
+ * @param none
+ * @retval none
+ */
+void PWR_ExitOffMode(void)
+{
+}
+
+/**
+ * @brief Enters Low Power Stop Mode
+ * @note ARM exists the function when waking up
+ * @param none
+ * @retval none
+ */
+void PWR_EnterStopMode(void)
+{
+
+ /* USER CODE BEGIN 1 */
+ /* Clear Status Flag before entering STOP/STANDBY Mode */
+ LL_PWR_ClearFlag_C1STOP_C1STB();
+ /* USER CODE END 1 */
+
+ /* USER CODE BEGIN 2 */
+ HAL_PWREx_EnterSTOP2Mode(PWR_STOPENTRY_WFI);
+ /* USER CODE END 2 */
+}
+/**
+ * @brief Exits Low Power Stop Mode
+ * @note Enable the pll at 32MHz
+ * @param none
+ * @retval none
+ */
+void PWR_ExitStopMode(void)
+{
+ /* USER CODE BEGIN 4 */
+ /*Not retained periph:
+ ADC interface
+ DAC interface USARTx, TIMx, i2Cx, SPIx
+ SRAM ctrls, DMAx, DMAMux, AES, RNG, HSEM */
+ /*to reenable lost DMA settings*/
+ if (HAL_UART_Init(&huart1) != HAL_OK)
+ {
+ Error_Handler();
+ }
+
+ /* USER CODE END 4 */
+}
+
+/**
+ * @brief Enters Low Power Sleep Mode
+ * @note ARM exits the function when waking up
+ * @param none
+ * @retval none
+ */
+void PWR_EnterSleepMode(void)
+{
+ /* Suspend sysTick */
+ HAL_SuspendTick();
+
+ /* USER CODE BEGIN 5 */
+ HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI);
+ /* USER CODE END 5 */
+}
+
+/**
+ * @brief Enters Low Power Sleep Mode
+ * @note ARM exits the function when waking up
+ * @param none
+ * @retval none
+ */
+void PWR_ExitSleepMode(void)
+{
+ /* Suspend sysTick */
+ HAL_ResumeTick();
+
+}
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
+
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Src/stm32wlxx_it.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Src/stm32wlxx_it.c
index 18226557..a57a0b1f 100644
--- a/board/RHF0M0E5_STM32WLE5xx/BSP/Src/stm32wlxx_it.c
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Src/stm32wlxx_it.c
@@ -61,9 +61,8 @@
/* USER CODE END 0 */
/* External variables --------------------------------------------------------*/
-extern UART_HandleTypeDef hlpuart1;
extern UART_HandleTypeDef huart1;
-extern UART_HandleTypeDef huart2;
+extern SUBGHZ_HandleTypeDef hsubghz;
/* USER CODE BEGIN EV */
/* USER CODE END EV */
@@ -221,32 +220,9 @@ void USART1_IRQHandler(void)
/* USER CODE END USART2_IRQn 1 */
}
-/**
- * @brief This function handles USART3 global interrupt.
- */
-void USART2_IRQHandler(void)
+void Radio_IRQHandler(void)
{
- /* USER CODE BEGIN USART2_IRQn 0 */
-
- /* USER CODE END USART2_IRQn 0 */
- HAL_UART_IRQHandler(&huart2);
- /* USER CODE BEGIN USART2_IRQn 1 */
-
- /* USER CODE END USART2_IRQn 1 */
-}
-
-/**
- * @brief This function handles LPUART1 global interrupt.
- */
-void LPUART1_IRQHandler(void)
-{
- /* USER CODE BEGIN LPUART1_IRQn 0 */
-
- /* USER CODE END LPUART1_IRQn 0 */
- HAL_UART_IRQHandler(&hlpuart1);
- /* USER CODE BEGIN LPUART1_IRQn 1 */
-
- /* USER CODE END LPUART1_IRQn 1 */
+ HAL_SUBGHZ_IRQHandler(&hsubghz);
}
/* USER CODE BEGIN 1 */
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Src/usart.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Src/usart.c
index e44cc523..e38935f5 100644
--- a/board/RHF0M0E5_STM32WLE5xx/BSP/Src/usart.c
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Src/usart.c
@@ -1,250 +1,80 @@
/**
******************************************************************************
- * File Name : USART.c
- * Description : This file provides code for the configuration
- * of the USART instances.
+ * @file usart.c
+ * @author MCD Application Team
+ * @brief configuration of the USART instances
******************************************************************************
* @attention
*
* © Copyright (c) 2019 STMicroelectronics.
* All rights reserved.
*
- * This software component is licensed by ST under BSD 3-Clause license,
- * the "License"; You may not use this file except in compliance with the
- * License. You may obtain a copy of the License at:
- * opensource.org/licenses/BSD-3-Clause
+ * This software component is licensed by ST under Ultimate Liberty license
+ * SLA0044, the "License"; You may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at:
+ * www.st.com/SLA0044
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
+#include "main.h"
#include "usart.h"
-/* USER CODE BEGIN 0 */
+#include "stdio.h"
+/* External variables --------------------------------------------------------*/
+/* Private typedef -----------------------------------------------------------*/
-/* USER CODE END 0 */
-
-UART_HandleTypeDef hlpuart1;
UART_HandleTypeDef huart1;
-UART_HandleTypeDef huart2;
-/* LPUART1 init function */
-
-void MX_LPUART1_UART_Init(void)
-{
-
- hlpuart1.Instance = LPUART1;
- hlpuart1.Init.BaudRate = 115200;
- hlpuart1.Init.WordLength = UART_WORDLENGTH_8B;
- hlpuart1.Init.StopBits = UART_STOPBITS_1;
- hlpuart1.Init.Parity = UART_PARITY_NONE;
- hlpuart1.Init.Mode = UART_MODE_TX_RX;
- hlpuart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
- hlpuart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
- hlpuart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
- if (HAL_UART_Init(&hlpuart1) != HAL_OK)
- {
- Error_Handler();
- }
-}
-/* USART1 init function */
+/* Private define ------------------------------------------------------------*/
+/* Private macro -------------------------------------------------------------*/
+/* Private variables ---------------------------------------------------------*/
+/* Private function prototypes -----------------------------------------------*/
+/* Functions Definition ------------------------------------------------------*/
void MX_USART1_UART_Init(void)
{
- huart1.Instance = USART1;
- huart1.Init.BaudRate = 115200;
+ huart1.Instance = USART1;
+ huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
- huart1.Init.StopBits = UART_STOPBITS_1;
- huart1.Init.Parity = UART_PARITY_NONE;
- huart1.Init.Mode = UART_MODE_TX_RX;
- huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
+ huart1.Init.StopBits = UART_STOPBITS_1;
+ huart1.Init.Parity = UART_PARITY_NONE;
+ huart1.Init.Mode = UART_MODE_TX_RX;
+ huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
- huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
- huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
- if (HAL_UART_Init(&huart1) != HAL_OK)
+
+ if(HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
}
-/* USART2 init function */
-void MX_USART2_UART_Init(void)
+void HAL_UART_MspInit(UART_HandleTypeDef *uartHandle)
{
+ GPIO_InitTypeDef GPIO_InitStruct = {0};
+ RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
- huart2.Instance = USART2;
- huart2.Init.BaudRate = 115200;
- huart2.Init.WordLength = UART_WORDLENGTH_8B;
- huart2.Init.StopBits = UART_STOPBITS_1;
- huart2.Init.Parity = UART_PARITY_NONE;
- huart2.Init.Mode = UART_MODE_TX_RX;
- huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
- huart2.Init.OverSampling = UART_OVERSAMPLING_16;
- huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
- huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
- if (HAL_UART_Init(&huart2) != HAL_OK)
- {
- Error_Handler();
- }
+ /* Enable GPIO TX/RX clock */
+ USARTx_TX_GPIO_CLK_ENABLE();
+ USARTx_RX_GPIO_CLK_ENABLE();
+ USARTx_CLK_ENABLE();
+
+ GPIO_InitStruct.Pin = USARTx_TX_Pin;
+ GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
+ GPIO_InitStruct.Pull = GPIO_PULLUP;
+ GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
+ GPIO_InitStruct.Alternate = USARTx_TX_AF;
+ HAL_GPIO_Init(USARTx_TX_GPIO_Port, &GPIO_InitStruct);
+
+ GPIO_InitStruct.Pin = USARTx_RX_Pin;
+ GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
+ GPIO_InitStruct.Pull = GPIO_NOPULL;
+ GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
+ GPIO_InitStruct.Alternate = USARTx_RX_AF;
+ HAL_GPIO_Init(USARTx_RX_GPIO_Port, &GPIO_InitStruct);
}
-
-void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
-{
-
- GPIO_InitTypeDef GPIO_InitStruct = {0};
- if(uartHandle->Instance==LPUART1)
- {
- /* USER CODE BEGIN LPUART1_MspInit 0 */
-
- /* USER CODE END LPUART1_MspInit 0 */
- /* LPUART1 clock enable */
- __HAL_RCC_LPUART1_CLK_ENABLE();
-
- __HAL_RCC_GPIOC_CLK_ENABLE();
- /**LPUART1 GPIO Configuration
- PC0 ------> LPUART1_RX
- PC1 ------> LPUART1_TX
- */
- GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1;
- GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
- GPIO_InitStruct.Pull = GPIO_NOPULL;
- GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
- GPIO_InitStruct.Alternate = GPIO_AF8_LPUART1;
- HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
-
- /* LPUART1 interrupt Init */
- HAL_NVIC_SetPriority(LPUART1_IRQn, 0, 0);
- HAL_NVIC_EnableIRQ(LPUART1_IRQn);
- /* USER CODE BEGIN LPUART1_MspInit 1 */
-
- /* USER CODE END LPUART1_MspInit 1 */
- }
- else if(uartHandle->Instance==USART1)
- {
- /* USER CODE BEGIN USART1_MspInit 0 */
-
- /* USER CODE END USART1_MspInit 0 */
- /* USART1 clock enable */
- __HAL_RCC_USART1_CLK_ENABLE();
-
- __HAL_RCC_GPIOB_CLK_ENABLE();
- /**USART1 GPIO Configuration
- PB6 ------> USART1_TX
- PB7 ------> USART1_RX
- */
- GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;
- GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
- GPIO_InitStruct.Pull = GPIO_NOPULL;
- GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
- GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
- HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
-
- /* USART1 interrupt Init */
- HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);
- HAL_NVIC_EnableIRQ(USART1_IRQn);
- /* USER CODE BEGIN USART1_MspInit 1 */
-
- /* USER CODE END USART1_MspInit 1 */
- }
- else if(uartHandle->Instance==USART2)
- {
- /* USER CODE BEGIN USART2_MspInit 0 */
-
- /* USER CODE END USART2_MspInit 0 */
- /* USART2 clock enable */
- __HAL_RCC_USART2_CLK_ENABLE();
-
- __HAL_RCC_GPIOA_CLK_ENABLE();
- /**USART2 GPIO Configuration
- PA2 ------> USART2_TX
- PA3 ------> USART2_RX
- */
- GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_3;
- GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
- GPIO_InitStruct.Pull = GPIO_NOPULL;
- GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
- GPIO_InitStruct.Alternate = GPIO_AF7_USART2;
- HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
-
- /* USART2 interrupt Init */
- HAL_NVIC_SetPriority(USART2_IRQn, 0, 0);
- HAL_NVIC_EnableIRQ(USART2_IRQn);
- /* USER CODE BEGIN USART2_MspInit 1 */
-
- /* USER CODE END USART2_MspInit 1 */
- }
-
-}
-
-void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle)
-{
-
- if(uartHandle->Instance==LPUART1)
- {
- /* USER CODE BEGIN LPUART1_MspDeInit 0 */
-
- /* USER CODE END LPUART1_MspDeInit 0 */
- /* Peripheral clock disable */
- __HAL_RCC_LPUART1_CLK_DISABLE();
-
- /**LPUART1 GPIO Configuration
- PC0 ------> LPUART1_RX
- PC1 ------> LPUART1_TX
- */
- HAL_GPIO_DeInit(GPIOC, GPIO_PIN_0|GPIO_PIN_1);
-
- /* LPUART1 interrupt Deinit */
- HAL_NVIC_DisableIRQ(LPUART1_IRQn);
- /* USER CODE BEGIN LPUART1_MspDeInit 1 */
-
- /* USER CODE END LPUART1_MspDeInit 1 */
- }
- else if(uartHandle->Instance==USART1)
- {
- /* USER CODE BEGIN USART1_MspDeInit 0 */
-
- /* USER CODE END USART1_MspDeInit 0 */
- /* Peripheral clock disable */
- __HAL_RCC_USART1_CLK_DISABLE();
-
- /**USART3 GPIO Configuration
- PB6 ------> USART1_TX
- PB7 ------> USART1_RX
- */
- HAL_GPIO_DeInit(GPIOB, GPIO_PIN_6|GPIO_PIN_7);
-
- /* USART3 interrupt Deinit */
- HAL_NVIC_DisableIRQ(USART1_IRQn);
- /* USER CODE BEGIN USART1_MspDeInit 1 */
-
- /* USER CODE END USART1_MspDeInit 1 */
- }
- else if(uartHandle->Instance==USART2)
- {
- /* USER CODE BEGIN USART2_MspDeInit 0 */
-
- /* USER CODE END USART2_MspDeInit 0 */
- /* Peripheral clock disable */
- __HAL_RCC_USART2_CLK_DISABLE();
-
- /**USART2 GPIO Configuration
- PA2 ------> USART2_TX
- PA3 ------> USART2_RX
- */
- HAL_GPIO_DeInit(GPIOA, GPIO_PIN_2|GPIO_PIN_3);
-
- /* USART2 interrupt Deinit */
- HAL_NVIC_DisableIRQ(USART2_IRQn);
- /* USER CODE BEGIN USART2_MspDeInit 1 */
-
- /* USER CODE END USART2_MspDeInit 1 */
- }
-}
-
-/* USER CODE BEGIN 1 */
-
-/* USER CODE END 1 */
-
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Src/usart_if.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Src/usart_if.c
new file mode 100644
index 00000000..81381713
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Src/usart_if.c
@@ -0,0 +1,83 @@
+/**
+ ******************************************************************************
+ * @file : usart_if.c
+ * @author MCD Application Team
+ * @brief interfaces UART MX driver for hyperterminal communication
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under Ultimate Liberty license
+ * SLA0044, the "License"; You may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at:
+ * www.st.com/SLA0044
+ *
+ ******************************************************************************
+ */
+
+/* Includes ------------------------------------------------------------------*/
+#include "usart_if.h"
+
+/* Private typedef -----------------------------------------------------------*/
+
+/* Private define ------------------------------------------------------------*/
+/* Private macro -------------------------------------------------------------*/
+/* Private variables ---------------------------------------------------------*/
+/* Uart Handle */
+extern UART_HandleTypeDef huart1;
+#define UartHandle huart1
+uint8_t charRx;
+
+/* Private function prototypes -----------------------------------------------*/
+/**
+* @brief TX complete callback
+* @param None
+* @return None
+*/
+static void (*TxCpltCallback)(void*);
+/**
+* @brief RX complete callback
+* @param char sent by user
+* @return None
+*/
+static void (*RxCpltCallback)(uint8_t *rxChar, uint16_t size, uint8_t error);
+
+/* Functions Definition ------------------------------------------------------*/
+
+uint8_t vcom_Init(void (*cb)(void *))
+{
+ TxCpltCallback = cb;
+
+ MX_USART1_UART_Init();
+ return 0;
+}
+
+uint8_t vcom_DeInit(void)
+{
+ /*##-1- Reset peripherals ##################################################*/
+// __HAL_RCC_USART2_FORCE_RESET();
+// __HAL_RCC_USART2_RELEASE_RESET();
+
+ /*##-2- MspDeInit ##################################################*/
+// HAL_UART_MspDeInit(&UartHandle);
+
+ /*##-3- Disable the NVIC for DMA ###########################################*/
+ /* temorary while waiting CR 50840: MX implementation of MX_DMA_DeInit() */
+ /* For the time being user should change mannualy the channel according to the MX settings */
+ /* USER CODE BEGIN 1 */
+// HAL_NVIC_DisableIRQ(DMA1_Channel5_IRQn);
+//
+ return 0;
+ /* USER CODE END 1 */
+}
+
+uint8_t vcom_Trace(uint8_t *p_data, uint16_t size)
+{
+// HAL_UART_Transmit_DMA(&UartHandle, p_data, size);
+ HAL_UART_Transmit(&UartHandle, p_data, size, 1000);
+ return 0;
+}
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/lpm/tiny_lpm/stm32_lpm.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/lpm/tiny_lpm/stm32_lpm.c
new file mode 100644
index 00000000..1cfd17aa
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/lpm/tiny_lpm/stm32_lpm.c
@@ -0,0 +1,180 @@
+/**
+ ******************************************************************************
+ * @file stm32_lpm.c
+ * @author MCD Application Team
+ * @brief Low Power Manager
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under BSD 3-Clause license,
+ * the "License"; You may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ * opensource.org/licenses/BSD-3-Clause
+ *
+ ******************************************************************************
+ */
+
+/* Includes ------------------------------------------------------------------*/
+#include "stm32_lpm.h"
+#include "utilities_conf.h"
+
+/* Private typedef -----------------------------------------------------------*/
+/* Private macros ------------------------------------------------------------*/
+#define UTIL_LPM_NO_BIT_SET (0)
+
+#ifndef UTIL_LPM_INIT_CRITICAL_SECTION
+ #define UTIL_LPM_INIT_CRITICAL_SECTION( )
+#endif
+
+#ifndef UTIL_LPM_ENTER_CRITICAL_SECTION
+ #define UTIL_LPM_ENTER_CRITICAL_SECTION( ) UTILS_ENTER_CRITICAL_SECTION( )
+#endif
+
+#ifndef UTIL_LPM_EXIT_CRITICAL_SECTION
+ #define UTIL_LPM_EXIT_CRITICAL_SECTION( ) UTILS_EXIT_CRITICAL_SECTION( )
+#endif
+
+/* Private function prototypes -----------------------------------------------*/
+/* Private variables ---------------------------------------------------------*/
+/* Private typedef -----------------------------------------------------------*/
+/* Private defines -----------------------------------------------------------*/
+/* Private macros ------------------------------------------------------------*/
+/* Private variables ---------------------------------------------------------*/
+static UTIL_LPM_bm_t StopModeDisable = UTIL_LPM_NO_BIT_SET;
+static UTIL_LPM_bm_t OffModeDisable = UTIL_LPM_NO_BIT_SET;
+
+/* Global variables ----------------------------------------------------------*/
+/* Private function prototypes -----------------------------------------------*/
+/* Functions Definition ------------------------------------------------------*/
+
+void UTIL_LPM_Init( void )
+{
+ StopModeDisable = UTIL_LPM_NO_BIT_SET;
+ OffModeDisable = UTIL_LPM_NO_BIT_SET;
+ UTIL_LPM_INIT_CRITICAL_SECTION( );
+}
+
+void UTIL_LPM_DeInit( void )
+{
+}
+
+void UTIL_LPM_SetStopMode( UTIL_LPM_bm_t lpm_id_bm, UTIL_LPM_State_t state )
+{
+ UTIL_LPM_ENTER_CRITICAL_SECTION( );
+
+ switch( state )
+ {
+ case UTIL_LPM_DISABLE:
+ {
+ StopModeDisable |= lpm_id_bm;
+ break;
+ }
+ case UTIL_LPM_ENABLE:
+ {
+ StopModeDisable &= ( ~lpm_id_bm );
+ break;
+ }
+ default:
+ break;
+ }
+
+ UTIL_LPM_EXIT_CRITICAL_SECTION( );
+}
+
+void UTIL_LPM_SetOffMode( UTIL_LPM_bm_t lpm_id_bm, UTIL_LPM_State_t state )
+{
+ UTIL_LPM_ENTER_CRITICAL_SECTION( );
+
+ switch(state)
+ {
+ case UTIL_LPM_DISABLE:
+ {
+ OffModeDisable |= lpm_id_bm;
+ break;
+ }
+ case UTIL_LPM_ENABLE:
+ {
+ OffModeDisable &= ( ~lpm_id_bm );
+ break;
+ }
+ default:
+ break;
+ }
+
+ UTIL_LPM_EXIT_CRITICAL_SECTION( );
+}
+
+UTIL_LPM_Mode_t UTIL_LPM_GetMode( void )
+{
+ UTIL_LPM_Mode_t mode_selected;
+
+ UTIL_LPM_ENTER_CRITICAL_SECTION( );
+
+ if( StopModeDisable != UTIL_LPM_NO_BIT_SET )
+ {
+ /**
+ * At least one user disallows Stop Mode
+ */
+ mode_selected = UTIL_LPM_SLEEPMODE;
+ }
+ else
+ {
+ if( OffModeDisable != UTIL_LPM_NO_BIT_SET )
+ {
+ /**
+ * At least one user disallows Off Mode
+ */
+ mode_selected = UTIL_LPM_STOPMODE;
+ }
+ else
+ {
+ mode_selected = UTIL_LPM_OFFMODE;
+ }
+ }
+
+ UTIL_LPM_EXIT_CRITICAL_SECTION( );
+
+ return mode_selected;
+}
+
+void UTIL_LPM_EnterLowPower( void )
+{
+ UTIL_LPM_ENTER_CRITICAL_SECTION( );
+
+ if( StopModeDisable != UTIL_LPM_NO_BIT_SET )
+ {
+ /**
+ * At least one user disallows Stop Mode
+ * SLEEP mode is required
+ */
+ UTIL_PowerDriver.EnterSleepMode( );
+ UTIL_PowerDriver.ExitSleepMode( );
+ }
+ else
+ {
+ if( OffModeDisable != UTIL_LPM_NO_BIT_SET )
+ {
+ /**
+ * At least one user disallows Off Mode
+ * STOP mode is required
+ */
+ UTIL_PowerDriver.EnterStopMode( );
+ UTIL_PowerDriver.ExitStopMode( );
+ }
+ else
+ {
+ /**
+ * OFF mode is required
+ */
+ UTIL_PowerDriver.EnterOffMode( );
+ UTIL_PowerDriver.ExitOffMode( );
+ }
+ }
+
+ UTIL_LPM_EXIT_CRITICAL_SECTION( );
+}
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/lpm/tiny_lpm/stm32_lpm.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/lpm/tiny_lpm/stm32_lpm.h
new file mode 100644
index 00000000..05553f5b
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/lpm/tiny_lpm/stm32_lpm.h
@@ -0,0 +1,126 @@
+/**
+ ******************************************************************************
+ * @file stm32_lpm.h
+ * @author MCD Application Team
+ * @brief Header for stm32_lpm.c module
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under BSD 3-Clause license,
+ * the "License"; You may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ * opensource.org/licenses/BSD-3-Clause
+ *
+ ******************************************************************************
+*/
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __STM32_LPM_H
+#define __STM32_LPM_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+#include "stdint.h"
+
+typedef uint32_t UTIL_LPM_bm_t;
+
+typedef enum
+{
+ UTIL_LPM_ENABLE=0,
+ UTIL_LPM_DISABLE,
+} UTIL_LPM_State_t;
+
+typedef enum
+{
+ UTIL_LPM_SLEEPMODE,
+ UTIL_LPM_STOPMODE,
+ UTIL_LPM_OFFMODE,
+} UTIL_LPM_Mode_t;
+
+/*!
+ * \brief LPM driver definition
+ */
+struct UTIL_LPM_Driver_s
+{
+ void (* EnterSleepMode) ( void );
+ void (* ExitSleepMode) ( void );
+ void (* EnterStopMode) ( void );
+ void (* ExitStopMode) ( void );
+ void (* EnterOffMode) ( void );
+ void (* ExitOffMode) ( void );
+};
+
+/*!
+ *
+ * \Remark : This structure is defined and initialized in the specific platform
+ * power implementation
+ */
+extern const struct UTIL_LPM_Driver_s UTIL_PowerDriver;
+
+/* Exported constants --------------------------------------------------------*/
+/* External variables --------------------------------------------------------*/
+/* Exported macros -----------------------------------------------------------*/
+/* Exported functions ------------------------------------------------------- */
+
+/**
+ * @brief This API Initializes the LPM resources.
+ * @param None
+ * @retval None
+ */
+void UTIL_LPM_Init( void );
+
+/**
+ * @brief This API Un-Initializes the LPM resources.
+ * @param None
+ * @retval None
+ */
+void UTIL_LPM_DeInit( void );
+
+/**
+ * @brief This API returns the Low Power Mode selected that will be applied when the system will enter low power mode
+ * if there is no update between the time the mode is read with this API and the time the system enters
+ * low power mode.
+ * @param None
+ * @retval UTIL_LPM_Mode_t
+ */
+UTIL_LPM_Mode_t UTIL_LPM_GetMode( void );
+
+/**
+ * @brief This API notifies the low power manager if the specified user allows the Stop mode or not.
+ * The default mode selection for all users is Stop Mode enabled
+ * @param lpm_id_bm: identifier of the user ( 1 bit per user )
+ * @param state: Specify whether StopMode is allowed or not by this user
+ * @retval None
+ */
+void UTIL_LPM_SetStopMode( UTIL_LPM_bm_t lpm_id_bm, UTIL_LPM_State_t state );
+
+/**
+ * @brief This API notifies the low power manager if the specified user allows the Off mode or not.
+ * The default mode selection for all users is Off mode enabled
+ * @param lpm_id_bm: identifier of the user ( 1 bit per user )
+ * @param state: Specify whether OffMode is allowed or not by this user
+ * @retval None
+ */
+void UTIL_LPM_SetOffMode( UTIL_LPM_bm_t lpm_id_bm, UTIL_LPM_State_t state );
+
+/**
+ * @brief This API is called by the low power manager in a critical section (PRIMASK bit set) to allow the
+ * application to implement dedicated code before entering Low Power Mode
+ * @param None
+ * @retval None
+ */
+void UTIL_LPM_EnterLowPower( void );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*__STM32_LPM_H */
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_mem.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_mem.c
new file mode 100644
index 00000000..302fb223
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_mem.c
@@ -0,0 +1,65 @@
+/**
+ ******************************************************************************
+ * @file stm32_mem.c
+ * @author MCD Application Team
+ * @brief standard memory operation
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under BSD 3-Clause license,
+ * the "License"; You may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ * opensource.org/licenses/BSD-3-Clause
+ *
+ ******************************************************************************
+ */
+
+/* Includes ------------------------------------------------------------------*/
+#include "stdint.h"
+#include "stm32_mem.h"
+
+/* Private typedef -----------------------------------------------------------*/
+/* Private defines -----------------------------------------------------------*/
+/* Private macros ------------------------------------------------------------*/
+/* Private variables ---------------------------------------------------------*/
+/* Global variables ----------------------------------------------------------*/
+/* Private function prototypes -----------------------------------------------*/
+/* Functions Definition ------------------------------------------------------*/
+
+void UTIL_MEM_cpy_8( void *dst, const void *src, uint16_t size )
+{
+ uint8_t* dst8= (uint8_t *) dst;
+ uint8_t* src8= (uint8_t *) src;
+
+ while( size-- )
+ {
+ *dst8++ = *src8++;
+ }
+}
+
+void UTIL_MEM_cpyr_8( void *dst, const void *src, uint16_t size )
+{
+ uint8_t* dst8= (uint8_t *) dst;
+ uint8_t* src8= (uint8_t *) src;
+
+ dst8 = dst8 + ( size - 1 );
+ while( size-- )
+ {
+ *dst8-- = *src8++;
+ }
+}
+
+void UTIL_MEM_set_8( void *dst, uint8_t value, uint16_t size )
+{
+ uint8_t* dst8= (uint8_t *) dst;
+ while( size-- )
+ {
+ *dst8++ = value;
+ }
+}
+
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_mem.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_mem.h
new file mode 100644
index 00000000..5cfb8659
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_mem.h
@@ -0,0 +1,76 @@
+/**
+ ******************************************************************************
+ * @file stm32_mem.h
+ * @author MCD Application Team
+ * @brief standard memory operation
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under BSD 3-Clause license,
+ * the "License"; You may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ * opensource.org/licenses/BSD-3-Clause
+ *
+ ******************************************************************************
+*/
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __STM32_MEM_H__
+#define __STM32_MEM_H__
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+ /* Includes ------------------------------------------------------------------*/
+#include
+#include "utilities_conf.h"
+
+/* Exported types ------------------------------------------------------------*/
+ /* Exported constants --------------------------------------------------------*/
+/* Exported macro ------------------------------------------------------------*/
+/* ---- Memory mapping macros --------------------------------------- */
+#define UTIL_MEM_PLACE_IN_SECTION( __x__ ) UTIL_PLACE_IN_SECTION( __x__ )
+#define UTIL_MEM_ALIGN ALIGN
+
+
+ /* Exported functions ------------------------------------------------------- */
+ /**
+ * @brief This API copies one buffer to another
+ * @param dst: output buffer to be filled
+ * @param src: input buffer
+ * @param size: size of 8b data
+ * @retval None
+ */
+ void UTIL_MEM_cpy_8( void *dst, const void *src, uint16_t size );
+
+ /**
+ * @brief This API copies one buffer to another in reverse
+ * @param dst: output buffer to be filled
+ * @param src: input buffer
+ * @param size: size of 8b data
+ * @retval None
+ */
+ void UTIL_MEM_cpyr_8( void *dst, const void *src, uint16_t size );
+
+ /**
+ * @brief This API copies one value into buffer
+ * @param dst: output buffer to be filled
+ * @param value: value
+ * @param size: size of 8b data
+ * @retval None
+ */
+ void UTIL_MEM_set_8( void *dst, uint8_t value, uint16_t size );
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __STM32_MEM_H__
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
+
+
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_systime.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_systime.c
new file mode 100644
index 00000000..c155f42e
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_systime.c
@@ -0,0 +1,351 @@
+/**
+******************************************************************************
+ * @file stm32_systime.c
+ * @author MCD Application Team
+ * @brief System time functions implementation
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under BSD 3-Clause license,
+ * the "License"; You may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ * opensource.org/licenses/BSD-3-Clause
+ *
+ ******************************************************************************
+ */
+
+/* Includes ------------------------------------------------------------------*/
+#include
+#include "stm32_systime.h"
+#include "rtc_if.h"
+
+#define END_OF_FEBRUARY_LEAP 60 //31+29
+#define END_OF_JULY_LEAP 213 //31+29+...
+
+#define END_OF_FEBRUARY_NORM 59 //31+28
+#define END_OF_JULY_NORM 212 //31+28+...
+
+#define UNIX_YEAR 68 //1968 is leap year
+
+//UNIX time 0 = start at 01:00:00, 01/01/1970
+#define UNIX_HOUR_OFFSET ( ( TM_DAYS_IN_LEAP_YEAR + TM_DAYS_IN_YEAR ) * TM_SECONDS_IN_1DAY )
+
+/*!
+ * \brief Correction factors
+ */
+#define DAYS_IN_MONTH_CORRECTION_NORM ( (uint32_t )0x99AAA0 )
+#define DAYS_IN_MONTH_CORRECTION_LEAP ( (uint32_t )0x445550 )
+
+
+/* 365.25 = (366 + 365 + 365 + 365)/4 */
+#define DIV_365_25( X ) ( ( ( X ) * 91867 + 22750 ) >> 25 )
+
+#define DIV_APPROX_86400( X ) ( ( ( X ) >> 18 ) + ( ( X ) >> 17 ) )
+
+#define DIV_APPROX_1000( X ) ( ( ( X ) >> 10 ) +( ( X ) >> 16 ) + ( ( X ) >> 17 ) )
+
+#define DIV_APPROX_60( X ) ( ( ( X ) * 17476 ) >> 20 )
+
+#define DIV_APPROX_61( X ) ( ( ( X ) * 68759 ) >> 22 )
+
+#define MODULO_7( X ) ( ( X ) -( ( ( ( ( X ) + 1 ) * 299593 ) >> 21 ) * 7 ) )
+
+/*!
+ * \brief Calculates ceiling( X / N )
+ */
+#define DIVC( X, N ) ( ( ( X ) + ( N ) -1 ) / ( N ) )
+
+#define DIVC_BY_4( X ) ( ( ( X ) + 3 ) >>2 )
+
+#define DIVC_BY_2( X ) ( ( ( X ) + 1 ) >> 1 )
+
+static uint32_t CalendarGetMonth( uint32_t days, uint32_t year );
+static void CalendarDiv86400( uint32_t in, uint32_t* out, uint32_t* remainder );
+static uint32_t CalendarDiv61( uint32_t in );
+static void CalendarDiv60( uint32_t in, uint32_t* out, uint32_t* remainder );
+
+const char *WeekDayString[]={ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
+
+SysTime_t SysTimeAdd( SysTime_t a, SysTime_t b )
+{
+ SysTime_t c = { .Seconds = 0, .SubSeconds = 0 };
+
+ c.Seconds = a.Seconds + b.Seconds;
+ c.SubSeconds = a.SubSeconds + b.SubSeconds;
+ if( c.SubSeconds >= 1000 )
+ {
+ c.Seconds++;
+ c.SubSeconds -= 1000;
+ }
+ return c;
+}
+
+SysTime_t SysTimeSub( SysTime_t a, SysTime_t b )
+{
+ SysTime_t c = { .Seconds = 0, .SubSeconds = 0 };
+
+ c.Seconds = a.Seconds - b.Seconds;
+ c.SubSeconds = a.SubSeconds - b.SubSeconds;
+ if( c.SubSeconds < 0 )
+ {
+ c.Seconds--;
+ c.SubSeconds += 1000;
+ }
+ return c;
+}
+
+void SysTimeSet( SysTime_t sysTime )
+{
+ SysTime_t DeltaTime;
+
+ SysTime_t calendarTime = { .Seconds = 0, .SubSeconds = 0 };
+
+ calendarTime.Seconds = HW_RTC_GetCalendarTime( ( uint16_t* )&calendarTime.SubSeconds );
+
+ // sysTime is epoch
+ DeltaTime = SysTimeSub( sysTime, calendarTime );
+
+ HW_RTC_BKUPWrite( DeltaTime.Seconds, ( uint32_t )DeltaTime.SubSeconds );
+}
+
+SysTime_t SysTimeGet( void )
+{
+ SysTime_t calendarTime = { .Seconds = 0, .SubSeconds = 0 };
+ SysTime_t sysTime = { .Seconds = 0, .SubSeconds = 0 };
+ SysTime_t DeltaTime;
+
+ calendarTime.Seconds = HW_RTC_GetCalendarTime( ( uint16_t* )&calendarTime.SubSeconds );
+
+ HW_RTC_BKUPRead( &DeltaTime.Seconds, ( uint32_t* )&DeltaTime.SubSeconds );
+
+ sysTime = SysTimeAdd( DeltaTime, calendarTime );
+
+ return sysTime;
+}
+
+
+SysTime_t SysTimeGetMcuTime( void )
+{
+ SysTime_t calendarTime = { .Seconds = 0, .SubSeconds = 0 };
+
+ calendarTime.Seconds = HW_RTC_GetCalendarTime( ( uint16_t* )&calendarTime.SubSeconds );
+
+ return calendarTime;
+}
+
+uint32_t SysTimeToMs( SysTime_t sysTime )
+{
+ SysTime_t DeltaTime;
+ HW_RTC_BKUPRead( &DeltaTime.Seconds, ( uint32_t* )&DeltaTime.SubSeconds );
+ SysTime_t calendarTime = SysTimeSub( sysTime, DeltaTime );
+ return calendarTime.Seconds * 1000 + calendarTime.SubSeconds;
+}
+
+SysTime_t SysTimeFromMs( uint32_t timeMs )
+{
+ uint32_t seconds = timeMs / 1000;
+ SysTime_t sysTime = { .Seconds = seconds, .SubSeconds = timeMs - seconds * 1000 };
+ SysTime_t DeltaTime = { 0 };
+ HW_RTC_BKUPRead( &DeltaTime.Seconds, ( uint32_t* )&DeltaTime.SubSeconds );
+
+ return SysTimeAdd( sysTime, DeltaTime );
+}
+
+uint32_t SysTimeMkTime( const struct tm* localtime )
+{
+ uint32_t nbdays;
+ uint32_t nbsecs;
+ uint32_t year = localtime->tm_year - UNIX_YEAR;
+ uint32_t correctionMonth[4] =
+ {
+ DAYS_IN_MONTH_CORRECTION_LEAP,
+ DAYS_IN_MONTH_CORRECTION_NORM,
+ DAYS_IN_MONTH_CORRECTION_NORM,
+ DAYS_IN_MONTH_CORRECTION_NORM
+ };
+
+ nbdays = DIVC( ( TM_DAYS_IN_YEAR * 3 + TM_DAYS_IN_LEAP_YEAR ) * year, 4 );
+
+ nbdays += ( DIVC_BY_2( ( localtime->tm_mon ) * ( 30 + 31 ) ) -
+ ( ( ( correctionMonth[year % 4] >> ( ( localtime->tm_mon ) * 2 ) ) & 0x03 ) ) );
+
+ nbdays += ( localtime->tm_mday - 1 );
+
+ // Convert from days to seconds
+ nbsecs = nbdays * TM_SECONDS_IN_1DAY;
+
+ nbsecs += ( ( uint32_t )localtime->tm_sec +
+ ( ( uint32_t )localtime->tm_min * TM_SECONDS_IN_1MINUTE ) +
+ ( ( uint32_t )localtime->tm_hour * TM_SECONDS_IN_1HOUR ) );
+ return nbsecs - UNIX_HOUR_OFFSET;
+}
+
+
+
+void SysTimeLocalTime( const uint32_t timestamp, struct tm *localtime )
+{
+ uint32_t correctionMonth[4] =
+ {
+ DAYS_IN_MONTH_CORRECTION_LEAP,
+ DAYS_IN_MONTH_CORRECTION_NORM,
+ DAYS_IN_MONTH_CORRECTION_NORM,
+ DAYS_IN_MONTH_CORRECTION_NORM
+ };
+ uint32_t weekDays = 1; // Monday 1st January 1968
+ uint32_t seconds;
+ uint32_t minutes;
+ uint32_t days;
+ uint32_t divOut;
+ uint32_t divReminder;
+
+ CalendarDiv86400( timestamp + UNIX_HOUR_OFFSET, &days, &seconds );
+
+ // Calculates seconds
+ CalendarDiv60( seconds, &minutes, &divReminder );
+ localtime->tm_sec = ( uint8_t )divReminder;
+
+ // Calculates minutes and hours
+ CalendarDiv60( minutes, &divOut, &divReminder);
+ localtime->tm_min = ( uint8_t )divReminder;
+ localtime->tm_hour = ( uint8_t )divOut;
+
+ // Calculates year
+ localtime->tm_year = DIV_365_25( days );
+ days-= DIVC_BY_4( ( TM_DAYS_IN_YEAR * 3 + TM_DAYS_IN_LEAP_YEAR ) * localtime->tm_year );
+
+ localtime->tm_yday = days;
+
+ // Calculates month
+ localtime->tm_mon = CalendarGetMonth( days, localtime->tm_year );
+
+ // calculates weekdays
+ weekDays += DIVC_BY_4( ( localtime->tm_year * 5 ) );
+ weekDays += days;
+ localtime->tm_wday = MODULO_7( weekDays );
+
+ days -= ( DIVC_BY_2( ( localtime->tm_mon ) * ( 30 + 31 ) ) -
+ ( ( ( correctionMonth[localtime->tm_year % 4] >> ( ( localtime->tm_mon ) * 2 ) ) & 0x03 ) ) );
+
+ // Convert 0 to 1 indexed.
+ localtime->tm_mday = days + 1;
+
+ localtime->tm_year += UNIX_YEAR;
+
+ localtime->tm_isdst = -1;
+}
+
+static uint32_t CalendarGetMonth( uint32_t days, uint32_t year )
+{
+ uint32_t month;
+ if( ( year % 4 ) == 0 )
+ { /*leap year*/
+ if( days < END_OF_FEBRUARY_LEAP )
+ { // January or February
+ // month = days * 2 / ( 30 + 31 );
+ month = CalendarDiv61( days * 2 );
+ }
+ else if( days < END_OF_JULY_LEAP )
+ {
+ month = CalendarDiv61( ( days - END_OF_FEBRUARY_LEAP ) * 2 ) + 2;
+ }
+ else
+ {
+ month = CalendarDiv61( ( days - END_OF_JULY_LEAP ) * 2 ) + 7;
+ }
+ }
+ else
+ {
+ if( days < END_OF_FEBRUARY_NORM )
+ { // January or February
+ month = CalendarDiv61( days * 2 );
+ }
+ else if( days < END_OF_JULY_NORM )
+ {
+ month = CalendarDiv61( ( days - END_OF_FEBRUARY_NORM ) * 2 ) + 2;
+ }
+ else
+ {
+ month = CalendarDiv61( ( days - END_OF_JULY_NORM ) * 2 ) + 7;
+ }
+ }
+ return month;
+}
+
+static void CalendarDiv86400( uint32_t in, uint32_t* out, uint32_t* remainder )
+{
+#if 0
+ *remainder = in % SECONDS_IN_1DAY;
+ *out = in / SECONDS_IN_1DAY;
+#else
+ uint32_t outTemp = 0;
+ uint32_t divResult = DIV_APPROX_86400( in );
+
+ while( divResult >=1 )
+ {
+ outTemp += divResult;
+ in -= divResult * 86400;
+ divResult= DIV_APPROX_86400( in );
+ }
+ if( in >= 86400 )
+ {
+ outTemp += 1;
+ in -= 86400;
+ }
+
+ *remainder = in;
+ *out = outTemp;
+#endif
+}
+
+static uint32_t CalendarDiv61( uint32_t in )
+{
+#if 0
+ return( in / 61 );
+#else
+ uint32_t outTemp = 0;
+ uint32_t divResult = DIV_APPROX_61( in );
+ while( divResult >=1 )
+ {
+ outTemp += divResult;
+ in -= divResult * 61;
+ divResult = DIV_APPROX_61( in );
+ }
+ if( in >= 61 )
+ {
+ outTemp += 1;
+ in -= 61;
+ }
+ return outTemp;
+#endif
+}
+
+static void CalendarDiv60( uint32_t in, uint32_t* out, uint32_t* remainder )
+{
+#if 0
+ *remainder = in % 60;
+ *out = in / 60;
+#else
+ uint32_t outTemp = 0;
+ uint32_t divResult = DIV_APPROX_60( in );
+
+ while( divResult >=1 )
+ {
+ outTemp += divResult;
+ in -= divResult * 60;
+ divResult = DIV_APPROX_60( in );
+ }
+ if( in >= 60 )
+ {
+ outTemp += 1;
+ in -= 60;
+ }
+ *remainder = in;
+ *out = outTemp;
+#endif
+}
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
+
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_systime.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_systime.h
new file mode 100644
index 00000000..46f85703
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_systime.h
@@ -0,0 +1,175 @@
+/**
+******************************************************************************
+* @file stm32_systime.h
+* @author MCD Application Team
+* @brief System time functions implementation
+******************************************************************************
+* @attention
+*
+* © Copyright (c) 2019 STMicroelectronics.
+* All rights reserved.
+*
+* This software component is licensed by ST under BSD 3-Clause license,
+* the "License"; You may not use this file except in compliance with the
+* License. You may obtain a copy of the License at:
+* opensource.org/licenses/BSD-3-Clause
+*
+******************************************************************************
+*/
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __STM32_SYS_TIME_H__
+#define __STM32_SYS_TIME_H__
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+ /* Includes ------------------------------------------------------------------*/
+#include
+#include "time.h"
+
+
+ /* Exported constants --------------------------------------------------------*/
+
+
+ /*!
+ * @brief Days, Hours, Minutes and seconds of systime.h
+ */
+#define TM_DAYS_IN_LEAP_YEAR ( ( uint32_t ) 366U )
+#define TM_DAYS_IN_YEAR ( ( uint32_t ) 365U )
+#define TM_SECONDS_IN_1DAY ( ( uint32_t )86400U )
+#define TM_SECONDS_IN_1HOUR ( ( uint32_t ) 3600U )
+#define TM_SECONDS_IN_1MINUTE ( ( uint32_t ) 60U )
+#define TM_MINUTES_IN_1HOUR ( ( uint32_t ) 60U )
+#define TM_HOURS_IN_1DAY ( ( uint32_t ) 24U )
+
+
+ /*!
+ * @brief Months of systime.h
+ */
+#define TM_MONTH_JANUARY ( ( uint8_t ) 0U )
+#define TM_MONTH_FEBRUARY ( ( uint8_t ) 1U )
+#define TM_MONTH_MARCH ( ( uint8_t ) 2U )
+#define TM_MONTH_APRIL ( ( uint8_t ) 3U )
+#define TM_MONTH_MAY ( ( uint8_t ) 4U )
+#define TM_MONTH_JUNE ( ( uint8_t ) 5U )
+#define TM_MONTH_JULY ( ( uint8_t ) 6U )
+#define TM_MONTH_AUGUST ( ( uint8_t ) 7U )
+#define TM_MONTH_SEPTEMBER ( ( uint8_t ) 8U )
+#define TM_MONTH_OCTOBER ( ( uint8_t ) 9U )
+#define TM_MONTH_NOVEMBER ( ( uint8_t )10U )
+#define TM_MONTH_DECEMBER ( ( uint8_t )11U )
+
+ /*!
+ * @brief Week days of systime.h
+ */
+#define TM_WEEKDAY_SUNDAY ( ( uint8_t )0U )
+#define TM_WEEKDAY_MONDAY ( ( uint8_t )1U )
+#define TM_WEEKDAY_TUESDAY ( ( uint8_t )2U )
+#define TM_WEEKDAY_WEDNESDAY ( ( uint8_t )3U )
+#define TM_WEEKDAY_THURSDAY ( ( uint8_t )4U )
+#define TM_WEEKDAY_FRIDAY ( ( uint8_t )5U )
+#define TM_WEEKDAY_SATURDAY ( ( uint8_t )6U )
+
+ /*!
+ * @brief Number of seconds elapsed between Unix and GPS epoch
+ */
+#define UNIX_GPS_EPOCH_OFFSET 315964800
+ /* External variables --------------------------------------------------------*/
+
+ /*!
+ * @brief Structure holding the system time in seconds and milliseconds.
+ */
+ typedef struct SysTime_s
+ {
+ uint32_t Seconds;
+ int16_t SubSeconds;
+ }SysTime_t;
+
+ /* Exported macros -----------------------------------------------------------*/
+ /* Exported functions ------------------------------------------------------- */
+ /*!
+ * @brief Adds 2 SysTime_t values
+ *
+ * @param a Value
+ * @param b Value to added
+ *
+ * @retval result Addition result (SysTime_t value)
+ */
+ SysTime_t SysTimeAdd( SysTime_t a, SysTime_t b );
+
+ /*!
+ * @brief Subtracts 2 SysTime_t values
+ *
+ * @param a Value
+ * @param b Value to be subtracted
+ *
+ * @retval result Subtraction result (SysTime_t value)
+ */
+ SysTime_t SysTimeSub( SysTime_t a, SysTime_t b );
+
+ /*!
+ * @brief Sets new system time
+ *
+ * @param sysTime New seconds/sub-seconds since UNIX epoch origin
+ */
+ void SysTimeSet( SysTime_t sysTime );
+
+ /*!
+ * @brief Gets current system time
+ *
+ * @retval sysTime Current seconds/sub-seconds since UNIX epoch origin
+ */
+ SysTime_t SysTimeGet( void );
+
+ /*!
+ * @brief Gets current MCU system time
+ *
+ * @retval sysTime Current seconds/sub-seconds since Mcu started
+ */
+ SysTime_t SysTimeGetMcuTime( void );
+
+ /*!
+ * Converts the given SysTime to the equivalent RTC value in milliseconds
+ *
+ * @param [IN] sysTime System time to be converted
+ *
+ * @retval timeMs The RTC converted time value in ms
+ */
+ uint32_t SysTimeToMs( SysTime_t sysTime );
+
+ /*!
+ * Converts the given RTC value in milliseconds to the equivalent SysTime
+ *
+ * \param [IN] timeMs The RTC time value in ms to be converted
+ *
+ * \retval sysTime Converted system time
+ */
+SysTime_t SysTimeFromMs( uint32_t timeMs );
+
+ /*!
+ * @brief Convert a calendar time into time since UNIX epoch as a uint32_t.
+ *
+ * @param [IN] localtime Pointer to the object containing the calendar time
+ * @retval timestamp The calendar time as seconds since UNIX epoch.
+ */
+ uint32_t SysTimeMkTime( const struct tm* localtime );
+
+ /*!
+ * @brief Converts a given time in seconds since UNIX epoch into calendar time.
+ *
+ * @param [IN] timestamp The time since UNIX epoch to convert into calendar time.
+ * @param [OUT] localtime Pointer to the calendar time object which will contain
+ the result of the conversion.
+ */
+ void SysTimeLocalTime( const uint32_t timestamp, struct tm *localtime );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __STM32_SYS_TIME_H__
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
+
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_tiny_sscanf.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_tiny_sscanf.c
new file mode 100644
index 00000000..c411eeb0
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_tiny_sscanf.c
@@ -0,0 +1,976 @@
+/******************************************************************************
+ * @file stm32_tiny_sscanf.c
+ * @author MCD Application Team
+ * @brief Tiny implementation of sscanf
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under BSD 3-Clause license,
+ * the "License"; You may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ * opensource.org/licenses/BSD-3-Clause
+ *
+ ******************************************************************************
+ */
+
+
+/*
+ * This code is derived from
+ * https://github.com/rapid7/metasploit-payloads/, in c/meterpreter/source/bionic/libc/stdio/sscanf.c
+ * It has been derived in order to optimize code size. In this context
+ * all the formats are not supported. Current supported formats are
+ * %hx, %hhx, %ul, %d,...
+ * when TINY_SSCANF is defined
+ *
+ * When TINY_NO_OX is defined, this is not possible to sscanf("%x") of "0xab",
+ * only "ab" is possible
+ *
+ * When TINY_SPACE_NOT_SPECIALCASE is defined, "space" is not a special character.
+ * That means that we expect a single space, and not any of ispace() character
+ * (space, tabs,...)
+ */
+
+/* Includes ------------------------------------------------------------------*/
+#include
+#include
+#include
+#include
+#include
+#include
+#include "stm32_tiny_sscanf.h"
+
+/* Private typedef -----------------------------------------------------------*/
+/* Private define ------------------------------------------------------------*/
+#define TINY_SSCANF
+#define TINY_NO_OX
+#define TINY_SPACE_NOT_SPECIALCASE
+
+/* Private macro -------------------------------------------------------------*/
+/* Private variables ---------------------------------------------------------*/
+/* Functions Definition ------------------------------------------------------*/
+
+#ifdef FLOATING_POINT
+#include "floatio.h"
+#endif
+
+#define BUF 513 /* Maximum length of numeric string. */
+
+/*
+ * Flags used during conversion.
+ */
+#define LONG 0x00001 /* l: long or double */
+#define SHORT 0x00004 /* h: short */
+#define SHORTSHORT 0x00008 /* hh: 8 bit integer */
+#define UNSIGNED 0x00800 /* %[oupxX] conversions */
+#ifdef TINY_SSCANF
+#else
+#define LONGDBL 0x00002 /* L: long double; unimplemented */
+#define LLONG 0x00010 /* ll: long long (+ deprecated q: quad) */
+#define POINTER 0x00020 /* p: void * (as hex) */
+#define SIZEINT 0x00040 /* z: (signed) size_t */
+#define MAXINT 0x00080 /* j: intmax_t */
+#define PTRINT 0x00100 /* t: ptrdiff_t */
+#define NOSKIP 0x00200 /* [ or c: do not skip blanks */
+#define SUPPRESS 0x00400 /* *: suppress assignment */
+#endif
+
+/*
+ * The following are used in numeric conversions only:
+ * SIGNOK, HAVESIGN, NDIGITS, DPTOK, and EXPOK are for floating point;
+ * SIGNOK, HAVESIGN, NDIGITS, PFXOK, and NZDIGITS are for integral.
+ */
+#define SIGNOK 0x01000 /* +/- is (still) legal */
+#define HAVESIGN 0x02000 /* sign detected */
+#define NDIGITS 0x04000 /* no digits detected */
+
+#define DPTOK 0x08000 /* (float) decimal point is still legal */
+#define EXPOK 0x10000 /* (float) exponent (e+3, etc) still legal */
+
+#ifdef TINY_NO_OX
+#else
+#define PFXOK 0x08000 /* 0x prefix is (still) legal */
+#define NZDIGITS 0x10000 /* no zero digits detected */
+#endif
+
+/*
+ * Conversion types.
+ */
+#define CT_INT 3 /* integer, i.e., strtoimax or strtoumax */
+#define CT_FLOAT 4 /* floating, i.e., strtod */
+
+#ifdef TINY_SSCANF
+#else
+#define CT_CHAR 0 /* %c conversion */
+#define CT_CCL 1 /* %[...] conversion */
+#define CT_STRING 2 /* %s conversion */
+#endif
+
+#define u_char unsigned char
+#define u_long unsigned long
+
+#ifdef TINY_SSCANF
+#else
+static u_char *__sccl(char *, u_char *);
+#endif
+
+#define VFSCANF tiny_vfscanf
+
+#if !defined(VFSCANF)
+#define VFSCANF vfscanf
+#endif
+
+
+#define __srefill(_x) 1
+#define ungetc(_c, _fp) do { (_c), fp_p--; fp_r++; } while (0)
+
+/*
+ * vfscanf
+ */
+static inline int
+VFSCANF(const char *str, const char *fmt0, __Va_list ap)
+{
+ u_char *fmt = (u_char *)fmt0;
+ int c; /* character from format, or conversion */
+ size_t width; /* field width, or 0 */
+ char *p; /* points into all kinds of strings */
+ int flags; /* flags as defined above */
+ int nassigned; /* number of fields assigned */
+ int nread; /* number of characters consumed from fp */
+ int base; /* base argument to strtoimax/strtouimax */
+ char buf[BUF]; /* buffer for numeric conversions */
+ const char *fp_p;
+ int fp_r;
+ uintmax_t value;
+ int sign_minus;
+
+
+#ifdef TINY_SSCANF
+#else
+ int n; /* handy integer */
+ char *p0; /* saves original value of p when necessary */
+ char ccltab[256]; /* character class table for %[...] */
+#endif
+
+ /* `basefix' is used to avoid `if' tests in the integer scanner */
+#ifdef TINY_SSCANF
+ /* basefix[] can be removed as we do not support %i */
+#else
+ static short basefix[17] =
+ { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
+#endif
+
+ fp_p = str;
+ fp_r = strlen(str);
+
+ nassigned = 0;
+ nread = 0;
+ base = 0; /* XXX just to keep gcc happy */
+ for (;;) {
+ c = *fmt++;
+ if (c == 0)
+ return (nassigned);
+#ifdef TINY_SPACE_NOT_SPECIALCASE
+#else
+ if (isspace(c)) {
+ while ((fp_r > 0 || __srefill(fp) == 0) &&
+ isspace(*fp_p))
+ nread++, fp_r--, fp_p++;
+ continue;
+ }
+#endif
+ if (c != '%')
+ goto literal;
+ width = 0;
+ flags = 0;
+ /*
+ * switch on the format. continue if done;
+ * break once format type is derived.
+ */
+again: c = *fmt++;
+ switch (c) {
+ case '%':
+literal:
+ if (fp_r <= 0 && __srefill(fp))
+ goto input_failure;
+ if (*fp_p != c)
+ goto match_failure;
+ fp_r--, fp_p++;
+ nread++;
+ continue;
+
+#ifdef TINY_SSCANF
+#else
+ case '*':
+ flags |= SUPPRESS;
+ goto again;
+ case 'j':
+ flags |= MAXINT;
+ goto again;
+ case 'L':
+ flags |= LONGDBL;
+ goto again;
+#endif
+ case 'h':
+ if (*fmt == 'h') {
+ fmt++;
+ flags |= SHORTSHORT;
+ } else {
+ flags |= SHORT;
+ }
+ goto again;
+ case 'l':
+#ifdef TINY_SSCANF
+ /* %ll not supported */
+ flags |= LONG;
+ goto again;
+#else
+ if (*fmt == 'l') {
+ fmt++;
+ flags |= LLONG;
+ } else {
+ flags |= LONG;
+ }
+ goto again;
+#endif
+
+#ifdef TINY_SSCANF
+#else
+ case 'q':
+ flags |= LLONG; /* deprecated */
+ goto again;
+ case 't':
+ flags |= PTRINT;
+ goto again;
+ case 'z':
+ flags |= SIZEINT;
+ goto again;
+#endif
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ width = width * 10 + c - '0';
+ goto again;
+
+ /*
+ * Conversions.
+ * Those marked `compat' are for 4.[123]BSD compatibility.
+ *
+ * (According to ANSI, E and X formats are supposed
+ * to the same as e and x. Sorry about that.)
+ */
+ case 'D': /* compat */
+ flags |= LONG;
+ /* FALLTHROUGH */
+ case 'd':
+ c = CT_INT;
+ base = 10;
+ break;
+
+#ifdef TINY_SSCANF
+ /*
+ * We do not support %i to remove potential base=8 in the following
+ * Hence basefix can be removed
+ */
+#else
+ case 'i':
+ c = CT_INT;
+ base = 0;
+ break;
+#endif
+
+#ifdef TINY_SSCANF
+#else
+ case 'O': /* compat */
+ flags |= LONG;
+ /* FALLTHROUGH */
+ case 'o':
+ c = CT_INT;
+ flags |= UNSIGNED;
+ base = 8;
+ break;
+#endif
+
+ case 'u':
+ c = CT_INT;
+ flags |= UNSIGNED;
+ base = 10;
+ break;
+
+ case 'X':
+ case 'x':
+#ifdef TINY_NO_OX
+#else
+ flags |= PFXOK; /* enable 0x prefixing */
+#endif
+ c = CT_INT;
+ flags |= UNSIGNED;
+ base = 16;
+ break;
+
+#ifdef FLOATING_POINT
+ case 'E':
+ case 'G':
+ case 'e':
+ case 'f':
+ case 'g':
+ c = CT_FLOAT;
+ break;
+#endif
+
+#ifdef TINY_SSCANF
+#else
+ case 's':
+ c = CT_STRING;
+ break;
+
+ case '[':
+ fmt = __sccl(ccltab, fmt);
+ flags |= NOSKIP;
+ c = CT_CCL;
+ break;
+
+ case 'c':
+ flags |= NOSKIP;
+ c = CT_CHAR;
+ break;
+
+ case 'p': /* pointer format is like hex */
+ flags |= POINTER | PFXOK;
+ c = CT_INT;
+ flags |= UNSIGNED;
+ base = 16;
+ break;
+
+ case 'n':
+ if (flags & SUPPRESS)
+ continue;
+ if (flags & SHORTSHORT)
+ *va_arg(ap, char *) = nread;
+ else if (flags & SHORT)
+ *va_arg(ap, short *) = nread;
+ else if (flags & LONG)
+ *va_arg(ap, long *) = nread;
+ else if (flags & SIZEINT)
+ *va_arg(ap, size_t *) = nread;
+ else if (flags & PTRINT)
+ *va_arg(ap, ptrdiff_t *) = nread;
+ else if (flags & LLONG)
+ *va_arg(ap, long long *) = nread;
+ else if (flags & MAXINT)
+ *va_arg(ap, intmax_t *) = nread;
+ else
+ *va_arg(ap, int *) = nread;
+ continue;
+#endif
+
+ /*
+ * Disgusting backwards compatibility hacks. XXX
+ */
+ case '\0': /* compat */
+ return (EOF);
+
+ default: /* compat */
+#ifdef TINY_SSCANF
+#else
+ if (isupper(c))
+ flags |= LONG;
+ c = CT_INT;
+ base = 10;
+#endif
+ break;
+ }
+
+ /*
+ * We have a conversion that requires input.
+ */
+ if (fp_r <= 0 && __srefill(fp))
+ goto input_failure;
+
+
+ /*
+ * Consume leading white space, except for formats
+ * that suppress this.
+ */
+#ifdef TINY_SSCANF
+#else
+ if ((flags & NOSKIP) == 0) {
+ while (isspace(*fp_p)) {
+ nread++;
+ if (--fp_r > 0)
+ fp_p++;
+ else if (__srefill(fp))
+ goto input_failure;
+ }
+ /*
+ * Note that there is at least one character in
+ * the buffer, so conversions that do not set NOSKIP
+ * ca no longer result in an input failure.
+ */
+ }
+#endif
+
+ /*
+ * Do the conversion.
+ */
+ switch (c) {
+#ifdef TINY_SSCANF
+#else
+ case CT_CHAR:
+ /* scan arbitrary characters (sets NOSKIP) */
+ if (width == 0)
+ width = 1;
+ if (flags & SUPPRESS) {
+ size_t sum = 0;
+ for (;;) {
+ if ((n = fp_r) < (int)width) {
+ sum += n;
+ width -= n;
+ fp_p += n;
+ if (__srefill(fp)) {
+ if (sum == 0)
+ goto input_failure;
+ break;
+ }
+ } else {
+ sum += width;
+ fp_r -= width;
+ fp_p += width;
+ break;
+ }
+ }
+ nread += sum;
+ } else {
+ size_t r = fread((void *)va_arg(ap, char *), 1,
+ width, fp);
+
+ if (r == 0)
+ goto input_failure;
+ nread += r;
+ nassigned++;
+ }
+ break;
+#endif
+
+#ifdef TINY_SSCANF
+#else
+ case CT_CCL:
+ /* scan a (nonempty) character class (sets NOSKIP) */
+ if (width == 0)
+ width = (size_t)~0; /* `infinity' */
+ /* take only those things in the class */
+ if (flags & SUPPRESS) {
+ n = 0;
+ while (ccltab[*fp_p]) {
+ n++, fp_r--, fp_p++;
+ if (--width == 0)
+ break;
+ if (fp_r <= 0 && __srefill(fp)) {
+ if (n == 0)
+ goto input_failure;
+ break;
+ }
+ }
+ if (n == 0)
+ goto match_failure;
+ } else {
+ p0 = p = va_arg(ap, char *);
+ while (ccltab[*fp_p]) {
+ fp_r--;
+ *p++ = *fp_p++;
+ if (--width == 0)
+ break;
+ if (fp_r <= 0 && __srefill(fp)) {
+ if (p == p0)
+ goto input_failure;
+ break;
+ }
+ }
+ n = p - p0;
+ if (n == 0)
+ goto match_failure;
+ *p = '\0';
+ nassigned++;
+ }
+ nread += n;
+ break;
+#endif
+
+#ifdef TINY_SSCANF
+#else
+ case CT_STRING:
+ /* like CCL, but zero-length string OK, & no NOSKIP */
+ if (width == 0)
+ width = (size_t)~0;
+ if (flags & SUPPRESS) {
+ n = 0;
+ while (!isspace(*fp_p)) {
+ n++, fp_r--, fp_p++;
+ if (--width == 0)
+ break;
+ if (fp_r <= 0 && __srefill(fp))
+ break;
+ }
+ nread += n;
+ } else {
+ p0 = p = va_arg(ap, char *);
+ while (!isspace(*fp_p)) {
+ fp_r--;
+ *p++ = *fp_p++;
+ if (--width == 0)
+ break;
+ if (fp_r <= 0 && __srefill(fp))
+ break;
+ }
+ *p = '\0';
+ nread += p - p0;
+ nassigned++;
+ }
+ continue;
+#endif
+
+ case CT_INT:
+ /* scan an integer as if by strtoimax/strtoumax */
+#ifdef hardway
+ if (width == 0 || width > sizeof(buf) - 1)
+ width = sizeof(buf) - 1;
+#else
+ /* size_t is unsigned, hence this optimisation */
+ if (--width > sizeof(buf) - 2)
+ width = sizeof(buf) - 2;
+ width++;
+#endif
+
+#ifdef TINY_NO_OX
+ flags |= SIGNOK | NDIGITS;
+#else
+ flags |= SIGNOK | NDIGITS | NZDIGITS;
+#endif
+
+ sign_minus = 0;
+ value = 0;
+ for (p = buf; width; width--) {
+ c = *fp_p;
+ /*
+ * Switch on the character; `goto ok'
+ * if we accept it as a part of number.
+ */
+ switch (c) {
+
+ /*
+ * The digit 0 is always legal, but is
+ * special. For %i conversions, if no
+ * digits (zero or nonzero) have been
+ * scanned (only signs), we will have
+ * base==0. In that case, we should set
+ * it to 8 and enable 0x prefixing.
+ * Also, if we have not scanned zero digits
+ * before this, do not turn off prefixing
+ * (someone else will turn it off if we
+ * have scanned any nonzero digits).
+ */
+ case '0':
+#ifdef TINY_NO_OX
+ /* FALLTHROUGH */
+#else
+#ifdef TINY_SSCANF
+#else
+ if (base == 0) {
+ base = 8;
+ flags |= PFXOK;
+ }
+#endif
+ if (!(flags & NDIGITS)) {
+ value = value * base;
+ }
+
+ if (flags & NZDIGITS)
+ flags &= ~(SIGNOK|NZDIGITS|NDIGITS);
+ else
+ flags &= ~(SIGNOK|PFXOK|NDIGITS);
+ goto ok;
+#endif
+
+#ifdef TINY_SSCANF
+ /* we only support base 10 and 16 */
+ case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ case '8': case '9':
+#ifdef TINY_NO_OX
+ flags &= ~(SIGNOK | NDIGITS);
+#else
+ flags &= ~(SIGNOK | PFXOK | NDIGITS);
+#endif
+ value = value * base + c - '0';
+ goto ok;
+#else
+ /* 1 through 7 always legal */
+ case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ base = basefix[base];
+ flags &= ~(SIGNOK | PFXOK | NDIGITS);
+ value = value * base + c - '0';
+ goto ok;
+
+ /* digits 8 and 9 ok iff decimal or hex */
+ case '8': case '9':
+ base = basefix[base];
+ if (base <= 8)
+ break; /* not legal here */
+ flags &= ~(SIGNOK | PFXOK | NDIGITS);
+ value = value * base + c - '0';
+ goto ok;
+#endif
+
+ /* letters ok iff hex */
+ case 'A': case 'B': case 'C':
+ case 'D': case 'E': case 'F':
+ /* no need to fix base here */
+ if (base <= 10)
+ break; /* not legal here */
+#ifdef TINY_NO_OX
+ flags &= ~(SIGNOK | NDIGITS);
+#else
+ flags &= ~(SIGNOK | PFXOK | NDIGITS);
+#endif
+ value = value * base + c - 'A' + 10;
+ goto ok;
+
+ case 'a': case 'b': case 'c':
+ case 'd': case 'e': case 'f':
+ /* no need to fix base here */
+ if (base <= 10)
+ break; /* not legal here */
+#ifdef TINY_NO_OX
+ flags &= ~(SIGNOK | NDIGITS);
+#else
+ flags &= ~(SIGNOK | PFXOK | NDIGITS);
+#endif
+ value = value * base + c - 'a' + 10;
+ goto ok;
+
+ /* sign ok only as first character */
+ case '-':
+ if (!(flags & HAVESIGN)) {
+ sign_minus = 1;
+ }
+ /* FALLTHROUGH */
+ case '+':
+ if (flags & SIGNOK) {
+ flags &= ~SIGNOK;
+ flags |= HAVESIGN;
+ goto ok;
+ }
+ break;
+
+ /*
+ * x ok iff flag still set and 2nd char (or
+ * 3rd char if we have a sign).
+ */
+#ifdef TINY_NO_OX
+#else
+ case 'x': case 'X':
+ if ((flags & PFXOK) && p ==
+ buf + 1 + !!(flags & HAVESIGN)) {
+ base = 16; /* if %i */
+ flags &= ~PFXOK;
+ goto ok;
+ }
+ break;
+#endif
+ }
+
+ /*
+ * If we got here, c is not a legal character
+ * for a number. Stop accumulating digits.
+ */
+ break;
+ ok:
+ /*
+ * c is legal: store it and look at the next.
+ */
+ *p++ = c;
+ if (--fp_r > 0)
+ fp_p++;
+ else if (__srefill(fp))
+ break; /* EOF */
+ }
+ /*
+ * If we had only a sign, it is no good; push
+ * back the sign. If the number ends in `x',
+ * it was [sign] '0' 'x', so push back the x
+ * and treat it as [sign] '0'.
+ */
+ if (flags & NDIGITS) {
+ if (p > buf)
+ {
+ --c;
+ --p;
+ ungetc(c++, fp);
+ /* There is a dummy post-increment to
+ avoid an unused value warning */
+ }
+ goto match_failure;
+ }
+#ifdef TINY_NO_OX
+#else
+ c = ((u_char *)p)[-1];
+ if (c == 'x' || c == 'X') {
+ --p;
+ ungetc(c, fp);
+ }
+#endif
+
+#ifdef TINY_SSCANF
+ {
+#else
+ if ((flags & SUPPRESS) == 0) {
+#endif
+
+ *p = '\0';
+ if (sign_minus)
+ value = -value;
+
+#ifdef TINY_SSCANF
+#else
+ if (flags & POINTER)
+ *va_arg(ap, void **) =
+ (void *)(uintptr_t)value;
+ else if (flags & MAXINT)
+ *va_arg(ap, intmax_t *) = value;
+ else if (flags & LLONG)
+ *va_arg(ap, long long *) = value;
+ else if (flags & SIZEINT)
+ *va_arg(ap, size_t *) = value;
+ else if (flags & PTRINT)
+ *va_arg(ap, ptrdiff_t *) = value;
+ else
+#endif
+ if (flags & LONG)
+ *va_arg(ap, long *) = value;
+ else if (flags & SHORT)
+ *va_arg(ap, short *) = value;
+ else if (flags & SHORTSHORT)
+ *va_arg(ap, char *) = value;
+ else
+ *va_arg(ap, int *) = value;
+ nassigned++;
+ }
+ nread += p - buf;
+ break;
+
+#ifdef FLOATING_POINT
+ case CT_FLOAT:
+ /* scan a floating point number as if by strtod */
+#ifdef hardway
+ if (width == 0 || width > sizeof(buf) - 1)
+ width = sizeof(buf) - 1;
+#else
+ /* size_t is unsigned, hence this optimisation */
+ if (--width > sizeof(buf) - 2)
+ width = sizeof(buf) - 2;
+ width++;
+#endif
+ flags |= SIGNOK | NDIGITS | DPTOK | EXPOK;
+ for (p = buf; width; width--) {
+ c = *fp->_p;
+ /*
+ * This code mimicks the integer conversion
+ * code, but is much simpler.
+ */
+ switch (c) {
+
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ case '8': case '9':
+ flags &= ~(SIGNOK | NDIGITS);
+ goto fok;
+
+ case '+': case '-':
+ if (flags & SIGNOK) {
+ flags &= ~SIGNOK;
+ goto fok;
+ }
+ break;
+ case '.':
+ if (flags & DPTOK) {
+ flags &= ~(SIGNOK | DPTOK);
+ goto fok;
+ }
+ break;
+ case 'e': case 'E':
+ /* no exponent without some digits */
+ if ((flags&(NDIGITS|EXPOK)) == EXPOK) {
+ flags =
+ (flags & ~(EXPOK|DPTOK)) |
+ SIGNOK | NDIGITS;
+ goto fok;
+ }
+ break;
+ }
+ break;
+ fok:
+ *p++ = c;
+ if (--fp->_r > 0)
+ fp->_p++;
+ else if (__srefill(fp))
+ break; /* EOF */
+ }
+ /*
+ * If no digits, might be missing exponent digits
+ * (just give back the exponent) or might be missing
+ * regular digits, but had sign and/or decimal point.
+ */
+ if (flags & NDIGITS) {
+ if (flags & EXPOK) {
+ /* no digits at all */
+ while (p > buf)
+ ungetc(*(u_char *)--p, fp);
+ goto match_failure;
+ }
+ /* just a bad exponent (e and maybe sign) */
+ c = *(u_char *)--p;
+ if (c != 'e' && c != 'E') {
+ (void) ungetc(c, fp);/* sign */
+ c = *(u_char *)--p;
+ }
+ (void) ungetc(c, fp);
+ }
+ if ((flags & SUPPRESS) == 0) {
+ double res;
+
+ *p = '\0';
+ res = strtod(buf, (char **) NULL);
+ if (flags & LONGDBL)
+ *va_arg(ap, long double *) = res;
+ else if (flags & LONG)
+ *va_arg(ap, double *) = res;
+ else
+ *va_arg(ap, float *) = res;
+ nassigned++;
+ }
+ nread += p - buf;
+ break;
+#endif /* FLOATING_POINT */
+ }
+ }
+input_failure:
+ return (nassigned ? nassigned : -1);
+match_failure:
+ return (nassigned);
+}
+
+#ifdef TINY_SSCANF
+#else
+/*
+ * Fill in the given table from the scanset at the given format
+ * (just after `['). Return a pointer to the character past the
+ * closing `]'. The table has a 1 wherever characters should be
+ * considered part of the scanset.
+ */
+static u_char *
+__sccl(char *tab, u_char *fmt)
+{
+ int c, n, v;
+
+ /* first `clear' the whole table */
+ c = *fmt++; /* first char hat => negated scanset */
+ if (c == '^') {
+ v = 1; /* default => accept */
+ c = *fmt++; /* get new first char */
+ } else
+ v = 0; /* default => reject */
+ /* should probably use memset here */
+ for (n = 0; n < 256; n++)
+ tab[n] = v;
+ if (c == 0)
+ return (fmt - 1);/* format ended before closing ] */
+
+ /*
+ * Now set the entries corresponding to the actual scanset
+ * to the opposite of the above.
+ *
+ * The first character may be ']' (or '-') without being special;
+ * the last character may be '-'.
+ */
+ v = 1 - v;
+ for (;;) {
+ tab[c] = v; /* take character c */
+doswitch:
+ n = *fmt++; /* and examine the next */
+ switch (n) {
+
+ case 0: /* format ended too soon */
+ return (fmt - 1);
+
+ case '-':
+ /*
+ * A scanset of the form
+ * [01+-]
+ * is defined as `the digit 0, the digit 1,
+ * the character +, the character -', but
+ * the effect of a scanset such as
+ * [a-zA-Z0-9]
+ * is implementation defined. The V7 Unix
+ * scanf treats `a-z' as `the letters a through
+ * z', but treats `a-a' as `the letter a, the
+ * character -, and the letter a'.
+ *
+ * For compatibility, the `-' is not considerd
+ * to define a range if the character following
+ * it is either a close bracket (required by ANSI)
+ * or is not numerically greater than the character
+ * we just stored in the table (c).
+ */
+ n = *fmt;
+ if (n == ']' || n < c) {
+ c = '-';
+ break; /* resume the for(;;) */
+ }
+ fmt++;
+ do { /* fill in the range */
+ tab[++c] = v;
+ } while (c < n);
+#if 1 /* XXX another disgusting compatibility hack */
+ /*
+ * Alas, the V7 Unix scanf also treats formats
+ * such as [a-c-e] as `the letters a through e'.
+ * This too is permitted by the standard....
+ */
+ goto doswitch;
+#else
+ c = *fmt++;
+ if (c == 0)
+ return (fmt - 1);
+ if (c == ']')
+ return (fmt);
+#endif
+ break;
+
+ case ']': /* end of scanset */
+ return (fmt);
+
+ default: /* just another character */
+ c = n;
+ break;
+ }
+ }
+ /* NOTREACHED */
+}
+#endif
+
+int
+tiny_sscanf(const char *str, const char *fmt, ...)
+{
+ int ret;
+ va_list ap;
+
+ va_start(ap, fmt);
+ ret = tiny_vfscanf(str, fmt, ap);
+ va_end(ap);
+ return (ret);
+}
+
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_tiny_sscanf.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_tiny_sscanf.h
new file mode 100644
index 00000000..f19f8631
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_tiny_sscanf.h
@@ -0,0 +1,67 @@
+/*******************************************************************************
+* @file stm32_tiny_sscanf.h
+* @author MCD Application Team
+* @brief Header for driver tiny_sscanf.c module
+******************************************************************************
+* @attention
+*
+* © Copyright (c) 2019 STMicroelectronics.
+* All rights reserved.
+*
+* This software component is licensed by ST under BSD 3-Clause license,
+* the "License"; You may not use this file except in compliance with the
+* License. You may obtain a copy of the License at:
+* opensource.org/licenses/BSD-3-Clause
+*
+******************************************************************************
+*/
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __STM32_TINY_SSCANF_H__
+#define __STM32_TINY_SSCANF_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /* Includes ------------------------------------------------------------------*/
+ /* Exported types ------------------------------------------------------------*/
+ /* Exported constants --------------------------------------------------------*/
+ /* External variables --------------------------------------------------------*/
+ /* Exported macros -----------------------------------------------------------*/
+ /* Exported functions ------------------------------------------------------- */
+
+ /**
+ * @brief Read formatted data from string
+ *
+ * Reads data from s and stores them according to parameter format into the
+ * locations given by the additional arguments, as if scanf was used, but
+ * reading from s instead of the standard input (stdin).
+ *
+ * The additional arguments should point to already allocated objects of the
+ * type specified by their corresponding format specifier within the format string.
+ *
+ * @param C string that the function processes as its source to retrieve the data.
+ * @param C string that contains a format string that follows the same specifications
+ * as format in scanf (see scanf for details).
+ * @param Depending on the format string, the function may expect a sequence of
+ * additional arguments, each containing a pointer to allocated storage
+ * where the interpretation of the extracted characters is stored with
+ * the appropriate type.
+ * There should be at least as many of these arguments as the number of
+ * values stored by the format specifiers. Additional arguments are
+ * ignored by the function.
+ * @retval The number of items in the argument list successfully filled. This
+ * count can match the expected number of items or be less (even zero)
+ * in the case of a matching failure
+ * @note Current supported formats are %hx, %hhx, %ul, %d,...
+ */
+ int tiny_sscanf(const char *str, const char *fmt, ...);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __STM32_TINY_SSCANF_H__ */
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_tiny_vsnprintf.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_tiny_vsnprintf.c
new file mode 100644
index 00000000..a71e84cc
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_tiny_vsnprintf.c
@@ -0,0 +1,669 @@
+/******************************************************************************
+ * @file stm32_tiny_vsnprintf.c
+ * @author MCD Application Team
+ * @brief Tiny implementation of vsnprintf like function
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under BSD 3-Clause license,
+ * the "License"; You may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ * opensource.org/licenses/BSD-3-Clause
+ *
+ ******************************************************************************
+ */
+/*
+ * Following implementation is adapted from original one
+ * https://github.com/jpbonn/coremark_lm32/blob/master/ee_printf.c
+ */
+
+/* Includes ------------------------------------------------------------------*/
+#include "stm32_tiny_vsnprintf.h"
+
+/* Private typedef -----------------------------------------------------------*/
+/* Private defines -----------------------------------------------------------*/
+#define TINY_PRINTF
+
+#define ZEROPAD (1<<0) /* Pad with zero */
+#define SIGN (1<<1) /* Unsigned/signed long */
+#define UPPERCASE (1<<6) /* 'ABCDEF' */
+#ifdef TINY_PRINTF
+#else
+#define PLUS (1<<2) /* Show plus */
+#define HEX_PREP (1<<5) /* 0x */
+#define SPACE (1<<3) /* Spacer */
+#define LEFT (1<<4) /* Left justified */
+#endif
+
+#define is_digit(c) ((c) >= '0' && (c) <= '9')
+
+/* Private macros ------------------------------------------------------------*/
+/* Private variables ---------------------------------------------------------*/
+/* Global variables ----------------------------------------------------------*/
+/* Private function prototypes -----------------------------------------------*/
+
+static char *lower_digits = "0123456789abcdefghijklmnopqrstuvwxyz";
+static char *upper_digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+/* Functions Definition ------------------------------------------------------*/
+#ifdef TINY_PRINTF
+#else
+static size_t strnlen(const char *s, size_t count);
+
+static size_t strnlen(const char *s, size_t count)
+{
+ const char *sc;
+ for (sc = s; *sc != '\0' && count--; ++sc);
+ return sc - s;
+}
+#endif
+
+static int ee_skip_atoi(const char **s)
+{
+ int i = 0;
+ while (is_digit(**s)) i = i*10 + *((*s)++) - '0';
+ return i;
+}
+
+#define ASSIGN_STR(_c) do { *str++ = (_c); max_size--; if (max_size == 0) return str; } while (0)
+
+static char *ee_number(char *str, int max_size, long num, int base, int size, int precision, int type)
+{
+ char c;
+ char sign, tmp[66];
+ char *dig = lower_digits;
+ int i;
+
+ if (type & UPPERCASE) dig = upper_digits;
+#ifdef TINY_PRINTF
+#else
+ if (type & LEFT) type &= ~ZEROPAD;
+#endif
+ if (base < 2 || base > 36) return 0;
+
+ c = (type & ZEROPAD) ? '0' : ' ';
+ sign = 0;
+ if (type & SIGN)
+ {
+ if (num < 0)
+ {
+ sign = '-';
+ num = -num;
+ size--;
+ }
+#ifdef TINY_PRINTF
+#else
+ else if (type & PLUS)
+ {
+ sign = '+';
+ size--;
+ }
+ else if (type & SPACE)
+ {
+ sign = ' ';
+ size--;
+ }
+#endif
+ }
+
+#ifdef TINY_PRINTF
+#else
+ if (type & HEX_PREP)
+ {
+ if (base == 16)
+ size -= 2;
+ else if (base == 8)
+ size--;
+ }
+#endif
+
+ i = 0;
+
+ if (num == 0)
+ tmp[i++] = '0';
+ else
+ {
+ while (num != 0)
+ {
+ tmp[i++] = dig[((unsigned long) num) % (unsigned) base];
+ num = ((unsigned long) num) / (unsigned) base;
+ }
+ }
+
+ if (i > precision) precision = i;
+ size -= precision;
+ if (!(type & (ZEROPAD /* TINY option | LEFT */))) while (size-- > 0) ASSIGN_STR(' ');
+ if (sign) ASSIGN_STR(sign);
+
+#ifdef TINY_PRINTF
+#else
+ if (type & HEX_PREP)
+ {
+ if (base == 8)
+ ASSIGN_STR('0');
+ else if (base == 16)
+ {
+ ASSIGN_STR('0');
+ ASSIGN_STR(lower_digits[33]);
+ }
+ }
+#endif
+
+#ifdef TINY_PRINTF
+ while (size-- > 0) ASSIGN_STR(c);
+#else
+ if (!(type & LEFT)) while (size-- > 0) ASSIGN_STR(c);
+#endif
+ while (i < precision--) ASSIGN_STR('0');
+ while (i-- > 0) ASSIGN_STR(tmp[i]);
+ while (size-- > 0) ASSIGN_STR(' ');
+
+ return str;
+}
+
+#ifdef TINY_PRINTF
+#else
+static char *eaddr(char *str, unsigned char *addr, int size, int precision, int type)
+{
+ char tmp[24];
+ char *dig = lower_digits;
+ int i, len;
+
+ if (type & UPPERCASE) dig = upper_digits;
+ len = 0;
+ for (i = 0; i < 6; i++)
+ {
+ if (i != 0) tmp[len++] = ':';
+ tmp[len++] = dig[addr[i] >> 4];
+ tmp[len++] = dig[addr[i] & 0x0F];
+ }
+
+ if (!(type & LEFT)) while (len < size--) *str++ = ' ';
+ for (i = 0; i < len; ++i) *str++ = tmp[i];
+ while (len < size--) *str++ = ' ';
+
+ return str;
+}
+
+static char *iaddr(char *str, unsigned char *addr, int size, int precision, int type)
+{
+ char tmp[24];
+ int i, n, len;
+
+ len = 0;
+ for (i = 0; i < 4; i++)
+ {
+ if (i != 0) tmp[len++] = '.';
+ n = addr[i];
+
+ if (n == 0)
+ tmp[len++] = lower_digits[0];
+ else
+ {
+ if (n >= 100)
+ {
+ tmp[len++] = lower_digits[n / 100];
+ n = n % 100;
+ tmp[len++] = lower_digits[n / 10];
+ n = n % 10;
+ }
+ else if (n >= 10)
+ {
+ tmp[len++] = lower_digits[n / 10];
+ n = n % 10;
+ }
+
+ tmp[len++] = lower_digits[n];
+ }
+ }
+
+ if (!(type & LEFT)) while (len < size--) *str++ = ' ';
+ for (i = 0; i < len; ++i) *str++ = tmp[i];
+ while (len < size--) *str++ = ' ';
+
+ return str;
+}
+#endif
+
+#ifdef HAS_FLOAT
+
+char *ecvtbuf(double arg, int ndigits, int *decpt, int *sign, char *buf);
+char *fcvtbuf(double arg, int ndigits, int *decpt, int *sign, char *buf);
+static void ee_bufcpy(char *d, char *s, int count);
+
+void ee_bufcpy(char *pd, char *ps, int count) {
+ char *pe=ps+count;
+ while (ps!=pe)
+ *pd++=*ps++;
+}
+
+static void parse_float(double value, char *buffer, char fmt, int precision)
+{
+ int decpt, sign, exp, pos;
+ char *fdigits = NULL;
+ char cvtbuf[80];
+ int capexp = 0;
+ int magnitude;
+
+ if (fmt == 'G' || fmt == 'E')
+ {
+ capexp = 1;
+ fmt += 'a' - 'A';
+ }
+
+ if (fmt == 'g')
+ {
+ fdigits = ecvtbuf(value, precision, &decpt, &sign, cvtbuf);
+ magnitude = decpt - 1;
+ if (magnitude < -4 || magnitude > precision - 1)
+ {
+ fmt = 'e';
+ precision -= 1;
+ }
+ else
+ {
+ fmt = 'f';
+ precision -= decpt;
+ }
+ }
+
+ if (fmt == 'e')
+ {
+ fdigits = ecvtbuf(value, precision + 1, &decpt, &sign, cvtbuf);
+
+ if (sign) *buffer++ = '-';
+ *buffer++ = *fdigits;
+ if (precision > 0) *buffer++ = '.';
+ ee_bufcpy(buffer, fdigits + 1, precision);
+ buffer += precision;
+ *buffer++ = capexp ? 'E' : 'e';
+
+ if (decpt == 0)
+ {
+ if (value == 0.0)
+ exp = 0;
+ else
+ exp = -1;
+ }
+ else
+ exp = decpt - 1;
+
+ if (exp < 0)
+ {
+ *buffer++ = '-';
+ exp = -exp;
+ }
+ else
+ *buffer++ = '+';
+
+ buffer[2] = (exp % 10) + '0';
+ exp = exp / 10;
+ buffer[1] = (exp % 10) + '0';
+ exp = exp / 10;
+ buffer[0] = (exp % 10) + '0';
+ buffer += 3;
+ }
+ else if (fmt == 'f')
+ {
+ fdigits = fcvtbuf(value, precision, &decpt, &sign, cvtbuf);
+ if (sign) *buffer++ = '-';
+ if (*fdigits)
+ {
+ if (decpt <= 0)
+ {
+ *buffer++ = '0';
+ *buffer++ = '.';
+ for (pos = 0; pos < -decpt; pos++) *buffer++ = '0';
+ while (*fdigits) *buffer++ = *fdigits++;
+ }
+ else
+ {
+ pos = 0;
+ while (*fdigits)
+ {
+ if (pos++ == decpt) *buffer++ = '.';
+ *buffer++ = *fdigits++;
+ }
+ }
+ }
+ else
+ {
+ *buffer++ = '0';
+ if (precision > 0)
+ {
+ *buffer++ = '.';
+ for (pos = 0; pos < precision; pos++) *buffer++ = '0';
+ }
+ }
+ }
+
+ *buffer = '\0';
+}
+
+static void decimal_point(char *buffer)
+{
+ while (*buffer)
+ {
+ if (*buffer == '.') return;
+ if (*buffer == 'e' || *buffer == 'E') break;
+ buffer++;
+ }
+
+ if (*buffer)
+ {
+ int n = strnlen(buffer,256);
+ while (n > 0)
+ {
+ buffer[n + 1] = buffer[n];
+ n--;
+ }
+
+ *buffer = '.';
+ }
+ else
+ {
+ *buffer++ = '.';
+ *buffer = '\0';
+ }
+}
+
+static void cropzeros(char *buffer)
+{
+ char *stop;
+
+ while (*buffer && *buffer != '.') buffer++;
+ if (*buffer++)
+ {
+ while (*buffer && *buffer != 'e' && *buffer != 'E') buffer++;
+ stop = buffer--;
+ while (*buffer == '0') buffer--;
+ if (*buffer == '.') buffer--;
+ while (buffer!=stop)
+ *++buffer=0;
+ }
+}
+
+static char *flt(char *str, double num, int size, int precision, char fmt, int flags)
+{
+ char tmp[80];
+ char c, sign;
+ int n, i;
+
+ // Left align means no zero padding
+#ifdef TINY_PRINTF
+#else
+ if (flags & LEFT) flags &= ~ZEROPAD;
+#endif
+
+ // Determine padding and sign char
+ c = (flags & ZEROPAD) ? '0' : ' ';
+ sign = 0;
+ if (flags & SIGN)
+ {
+ if (num < 0.0)
+ {
+ sign = '-';
+ num = -num;
+ size--;
+ }
+#ifdef TINY_PRINTF
+#else
+ else if (flags & PLUS)
+ {
+ sign = '+';
+ size--;
+ }
+ else if (flags & SPACE)
+ {
+ sign = ' ';
+ size--;
+ }
+#endif
+ }
+
+ // Compute the precision value
+ if (precision < 0)
+ precision = 6; // Default precision: 6
+
+ // Convert floating point number to text
+ parse_float(num, tmp, fmt, precision);
+
+#ifdef TINY_PRINTF
+#else
+ if ((flags & HEX_PREP) && precision == 0) decimal_point(tmp);
+#endif
+ if (fmt == 'g' && !(flags & HEX_PREP)) cropzeros(tmp);
+
+ n = strnlen(tmp,256);
+
+ // Output number with alignment and padding
+ size -= n;
+ if (!(flags & (ZEROPAD | LEFT))) while (size-- > 0) *str++ = ' ';
+ if (sign) *str++ = sign;
+ if (!(flags & LEFT)) while (size-- > 0) *str++ = c;
+ for (i = 0; i < n; i++) *str++ = tmp[i];
+ while (size-- > 0) *str++ = ' ';
+
+ return str;
+}
+
+#endif
+
+#define CHECK_STR_SIZE(_buf, _str, _size) \
+ if ((((_str) - (_buf)) >= ((_size)-1))) { break; }
+
+int tiny_vsnprintf_like(char *buf, const int size, const char *fmt, va_list args)
+{
+ unsigned long num;
+ int base;
+ char *str;
+ int len;
+ int i;
+ char *s;
+
+ int flags; // Flags to number()
+
+ int field_width; // Width of output field
+ int precision; // Min. # of digits for integers; max number of chars for from string
+ int qualifier; // 'h', 'l', or 'L' for integer fields
+
+ if (size <= 0)
+ {
+ return 0;
+ }
+
+ for (str = buf; *fmt || ((str - buf) >= size-1); fmt++)
+ {
+ CHECK_STR_SIZE(buf, str, size);
+
+ if (*fmt != '%')
+ {
+ *str++ = *fmt;
+ continue;
+ }
+
+ // Process flags
+ flags = 0;
+#ifdef TINY_PRINTF
+ /* Support %0, but not %-, %+, %space and %# */
+ fmt++;
+ if (*fmt == '0')
+ {
+ flags |= ZEROPAD;
+ }
+#else
+repeat:
+ fmt++; // This also skips first '%'
+ switch (*fmt)
+ {
+ case '-': flags |= LEFT; goto repeat;
+ case '+': flags |= PLUS; goto repeat;
+ case ' ': flags |= SPACE; goto repeat;
+ case '#': flags |= HEX_PREP; goto repeat;
+ case '0': flags |= ZEROPAD; goto repeat;
+ }
+#endif
+
+ // Get field width
+ field_width = -1;
+ if (is_digit(*fmt))
+ field_width = ee_skip_atoi(&fmt);
+#ifdef TINY_PRINTF
+ /* Does not support %* */
+#else
+ else if (*fmt == '*')
+ {
+ fmt++;
+ field_width = va_arg(args, int);
+ if (field_width < 0)
+ {
+ field_width = -field_width;
+ flags |= LEFT;
+ }
+ }
+#endif
+
+ // Get the precision
+ precision = -1;
+#ifdef TINY_PRINTF
+ /* Does not support %. */
+#else
+ if (*fmt == '.')
+ {
+ ++fmt;
+ if (is_digit(*fmt))
+ precision = ee_skip_atoi(&fmt);
+ else if (*fmt == '*')
+ {
+ ++fmt;
+ precision = va_arg(args, int);
+ }
+ if (precision < 0) precision = 0;
+ }
+#endif
+
+ // Get the conversion qualifier
+ qualifier = -1;
+#ifdef TINY_PRINTF
+ /* Does not support %l and %L */
+#else
+ if (*fmt == 'l' || *fmt == 'L')
+ {
+ qualifier = *fmt;
+ fmt++;
+ }
+#endif
+
+ // Default base
+ base = 10;
+
+ switch (*fmt)
+ {
+ case 'c':
+#ifdef TINY_PRINTF
+#else
+ if (!(flags & LEFT))
+#endif
+ while (--field_width > 0) *str++ = ' ';
+ *str++ = (unsigned char) va_arg(args, int);
+#ifdef TINY_PRINTF
+#else
+ while (--field_width > 0) *str++ = ' ';
+#endif
+ continue;
+
+ case 's':
+ s = va_arg(args, char *);
+ if (!s) s = "";
+#ifdef TINY_PRINTF
+ len = strlen(s);
+#else
+ len = strnlen(s, precision);
+ if (!(flags & LEFT))
+#endif
+ while (len < field_width--) *str++ = ' ';
+ for (i = 0; i < len; ++i) *str++ = *s++;
+#ifdef TINY_PRINTF
+#else
+ while (len < field_width--) *str++ = ' ';
+#endif
+ continue;
+
+#ifdef TINY_PRINTF
+ /* Does not support %p, %A, %a, %o */
+#else
+ case 'p':
+ if (field_width == -1)
+ {
+ field_width = 2 * sizeof(void *);
+ flags |= ZEROPAD;
+ }
+ str = ee_number(str, (size - (str - buf)), (unsigned long) va_arg(args, void *), 16, field_width, precision, flags);
+ continue;
+
+ case 'A':
+ flags |= UPPERCASE;
+
+ case 'a':
+ if (qualifier == 'l')
+ str = eaddr(str, va_arg(args, unsigned char *), field_width, precision, flags);
+ else
+ str = iaddr(str, va_arg(args, unsigned char *), field_width, precision, flags);
+ continue;
+
+ // Integer number formats - set up the flags and "break"
+ case 'o':
+ base = 8;
+ break;
+#endif
+
+ case 'X':
+ flags |= UPPERCASE;
+
+ case 'x':
+ base = 16;
+ break;
+
+ case 'd':
+ case 'i':
+ flags |= SIGN;
+
+ case 'u':
+ break;
+
+#ifdef HAS_FLOAT
+
+ case 'f':
+ str = flt(str, va_arg(args, double), field_width, precision, *fmt, flags | SIGN);
+ continue;
+
+#endif
+
+ default:
+ if (*fmt != '%') *str++ = '%';
+ CHECK_STR_SIZE(buf, str, size);
+ if (*fmt)
+ *str++ = *fmt;
+ else
+ --fmt;
+ CHECK_STR_SIZE(buf, str, size);
+ continue;
+ }
+
+ if (qualifier == 'l')
+ num = va_arg(args, unsigned long);
+ else if (flags & SIGN)
+ num = va_arg(args, int);
+ else
+ num = va_arg(args, unsigned int);
+
+ str = ee_number(str, ((size - 1) - (str - buf)), num, base, field_width, precision, flags);
+ }
+
+ *str = '\0';
+ return str - buf;
+}
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_tiny_vsnprintf.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_tiny_vsnprintf.h
new file mode 100644
index 00000000..a7685997
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_tiny_vsnprintf.h
@@ -0,0 +1,62 @@
+/******************************************************************************
+ * @file tiny_vsnprintf.h
+ * @author MCD Application Team
+ * @brief Header for tiny_vsnprintf.c module
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under BSD 3-Clause license,
+ * the "License"; You may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ * opensource.org/licenses/BSD-3-Clause
+ *
+ ******************************************************************************
+*/
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __STM32_TINY_VSNPRINTF_H__
+#define __STM32_TINY_VSNPRINTF_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+#include
+#include
+/* Exported types ------------------------------------------------------------*/
+/* Exported constants --------------------------------------------------------*/
+/* External variables --------------------------------------------------------*/
+/* Exported functions ------------------------------------------------------- */
+
+/**
+ * @brief Tiny implementation of vsnprintf() like function
+ *
+ * It has been adapted so that:
+ * - Tiny implementation, when defining TINY_PRINTF, is available. In such as case,
+ * not all the format are available. Instead, only %02X, %x, %d, %u, %s and %c are available.
+ * %f,, %+, %#, %- and others are excluded
+ * - Provide a snprintf like implementation. The size of the buffer is provided,
+ * and the length of the filled buffer is returned (not including the final '\0' char).
+ * The string may be truncated
+ * @param Pointer to a buffer where the resulting C-string is stored. The buffer should have a size of
+ * at least n characters.
+ * @param Maximum number of bytes to be used in the buffer. The generated string has a length of at
+ * most n-1, leaving space for the additional terminating null character.
+ * @param C string that contains a format string that follows the same specifications as format
+ * in printf (see printf for details).
+ * @param A value identifying a variable arguments list initialized with va_start.
+ * @retval The number of written char (note that this is different from vsnprintf()
+ */
+int tiny_vsnprintf_like(char *buf, const int size, const char *fmt, va_list args);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __STM32_TINY_VSNPRINTF_H__*/
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/Commissioning.h b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/Commissioning.h
new file mode 100644
index 00000000..07f45097
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/Commissioning.h
@@ -0,0 +1,95 @@
+/**
+ **********************************************************************************************************************************************************
+ * @file commissioning.h
+ * @author MCD Application Team
+ * @brief End device commissioning parameters
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under Ultimate Liberty license
+ * SLA0044, the "License"; You may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at:
+ * www.st.com/SLA0044
+ *
+ ******************************************************************************
+ */
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __LORA_COMMISSIONING_H__
+#define __LORA_COMMISSIONING_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*!
+ ******************************************************************************
+ ********************************** WARNING ***********************************
+ ******************************************************************************
+ The crypto-element implementation supports both 1.0.x and 1.1.x LoRaWAN
+ versions of the specification.
+ Thus it has been decided to use the 1.1.x keys and EUI name definitions.
+ The below table shows the names equivalence between versions:
+ +-------------------+-------------------------+
+ | 1.0.x | 1.1.x |
+ +===================+=========================+
+ | LORAWAN_DEVICE_EUI| LORAWAN_DEVICE_EUI |
+ +-------------------+-------------------------+
+ | LORAWAN_APP_EUI | LORAWAN_JOIN_EUI |
+ +-------------------+-------------------------+
+ | N/A | LORAWAN_APP_KEY |
+ +-------------------+-------------------------+
+ | LORAWAN_APP_KEY | LORAWAN_NWK_KEY |
+ +-------------------+-------------------------+
+ | LORAWAN_NWK_S_KEY | LORAWAN_F_NWK_S_INT_KEY |
+ +-------------------+-------------------------+
+ | LORAWAN_NWK_S_KEY | LORAWAN_S_NWK_S_INT_KEY |
+ +-------------------+-------------------------+
+ | LORAWAN_NWK_S_KEY | LORAWAN_NWK_S_ENC_KEY |
+ +-------------------+-------------------------+
+ | LORAWAN_APP_S_KEY | LORAWAN_APP_S_KEY |
+ +-------------------+-------------------------+
+ ******************************************************************************
+ ******************************************************************************
+ ******************************************************************************
+ */
+/*!
+ * Indicates if the end-device is to be connected to a private or public network
+ */
+#define LORAWAN_PUBLIC_NETWORK true
+
+/*root key*/
+#define LORAWAN_APP_KEY {0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C }
+
+/*for ABP================================================================================*/
+#define STATIC_DEVICE_ADDRESS 1 //if 0 , using a ramdom address
+
+#define LORAWAN_NETWORK_ID ( uint32_t )0
+
+#define LORAWAN_DEVICE_ADDRESS ( uint32_t )0x0100000a
+#define LORAWAN_NWK_KEY { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C }
+
+
+/*for 1.1.x*/
+#define LORAWAN_F_NWK_S_INT_KEY { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C }
+#define LORAWAN_S_NWK_S_INT_KEY { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C }
+#define LORAWAN_NWK_S_ENC_KEY { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C }
+#define LORAWAN_APP_S_KEY { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C }
+
+/*for OTAA================================================================================*/
+#define STATIC_DEVICE_EUI 1 //if 0 , using a ramdom EUI
+
+#define IEEE_OUI 0x01, 0x01, 0x01
+#define LORAWAN_DEVICE_EUI { IEEE_OUI, 0x01, 0x01, 0x01, 0x04, 0x02 }
+#define LORAWAN_JOIN_EUI /*APP_EUI*/ { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }
+
+
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LORA_COMMISSIONING_H__ */
diff --git a/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/app_conf.h b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/app_conf.h
new file mode 100644
index 00000000..ed79f997
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/app_conf.h
@@ -0,0 +1,109 @@
+/**
+ ******************************************************************************
+ * @file app_conf.h
+ * @author MCD Application Team
+ * @brief applic configuration, e.g. : debug, trace, low power, sensors
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under Ultimate Liberty license
+ * SLA0044, the "License"; You may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at:
+ * www.st.com/SLA0044
+ *
+ ******************************************************************************
+ */
+
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __APP_CONF_H__
+#define __APP_CONF_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/******************************************************************************
+ * lw APP configuration
+ ******************************************************************************/
+
+/* lw application configuration (Mw is configured by lorawan_conf.h) */
+#define LORAWAN_DEFAULT_ACTIVE_REGION LORAMAC_REGION_CN470ALI
+
+
+
+/*enable Low power*/
+#define LOW_POWER_ENBLE 0
+
+/* LPM_STOP_MODE_DISABLE = 0 : LowPowerMode enabled : MCU enters stop2 mode*/
+/* LPM_STOP_MODE_DISABLE = 1 : LowPowerMode disabled : MCU enters sleep mode only */
+#define LPM_STOP_MODE_DISABLE 0
+
+
+/******************************************************************************
+ * classB configuration
+ ******************************************************************************/
+/*using class b must define LORAMAC_CLASSB_ENABLED and active ClassB*/
+/* Class B ------------------------------------*/
+//#define LORAMAC_CLASSB_ENABLED
+#define LORAMAC_CLASSB_ACTIVE 0 /* 0 if not active, 1 if active */
+
+
+/******************************************************************************
+ * DEBUGGING configuration
+ ******************************************************************************/
+
+/* Enable trace logs. Note verbose level can be selected in app_system.h */
+#define APP_LOG_ENABLED 1
+#define MW_LOG_ENABLED 1
+/*
+0 only print the informtion
+1.print the function and line
+2 print the file , line ,function
+*/
+#define LOG_LEVEL 0
+
+
+
+/******************************************************************************
+ * Sequencer
+ ******************************************************************************/
+#define CFG_SEQ_Prio_0 0
+
+/**
+* This is the list of task id required by the application
+* Each Id shall be in the range 0..1<31
+*/
+#define CFG_SEQ_TASK_LORAMAC_PROCESS (1 << 1)
+#define CFG_SEQ_TASK_LORA_SEND_ON_TX_TIMER (1 << 2)
+#define CFG_SEQ_TASK_LORA_CERTIF_PROCESS (1 << 3)
+
+/******************************************************************************
+ * LOW POWER MANAGER
+ ******************************************************************************/
+/**
+ * Supported requester to the MCU Low Power Manager - can be increased up to 32
+ * It lits a bit mapping of all user of the Low Power Manager
+ */
+typedef enum
+{
+ LPM_APPLI_Id = (1 << 0),
+ LPM_LIB_Id = (1 << 1),
+ LPM_RTC_Id = (1 << 2),
+ LPM_GPS_Id = (1 << 3),
+ LPM_UART_RX_Id = (1 << 4),
+ LPM_UART_TX_Id = (1 << 5),
+ LPM_TCXO_WA_Id = (1 << 6),
+} LPM_Id_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __APP_CONF_H__ */
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/app_lorawan.c b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/app_lorawan.c
new file mode 100644
index 00000000..4e6fba42
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/app_lorawan.c
@@ -0,0 +1,329 @@
+
+/* Includes ------------------------------------------------------------------*/
+#include "timer.h"
+#include "app_system.h"
+#include "app_lorawan.h"
+//#include "stm32_seq.h"
+#include "stm32_lpm.h"
+
+#include "adc_if.h"
+#include "lorawan_version.h"
+
+#include
+
+
+#define APP_TX_DUTYCYCLE 30000 //ms
+#define LORAWAN_DOWNLINK_PORT 2
+#define LORAWAN_UPLINK_PORT 10
+
+/*!
+ * user main_callback function declare , you need to implement them
+ * and register.
+ */
+static uint8_t LORA_GetBatteryLevel(void);
+static uint16_t LORA_GetTemperatureLevel(void);
+static void GetUniqueId(uint8_t *id);
+static uint32_t GetRandomSeed(void);
+
+static void LoraMacProcessNotify(void);
+
+
+void mac_event_callback(APP_EVENT_t evt,const void* param);
+
+static LoRaMainCallback_t LoRaMainCallbacks = { mac_event_callback,
+ LoraMacProcessNotify,
+ LORA_GetBatteryLevel,
+ LORA_GetTemperatureLevel,
+ GetUniqueId,
+ GetRandomSeed,
+ };
+
+
+
+#define LORAWAN_APP_DATA_BUFF_SIZE 64
+static uint8_t app_data_buff[LORAWAN_APP_DATA_BUFF_SIZE];
+lw_app_data_t app_data = { app_data_buff, 0, 0 };
+
+
+/*send task function declaration*/
+static TimerEvent_t send_timer;
+//static k_timer_t send_timer;
+static void send_process(void *args);
+static void cycle_send_event(void *context);
+static void Lora_start_send(void);
+
+k_task_t lora_mac_process_task;
+k_stack_t lora_mac_process_task_stack[512];
+
+k_sem_t lora_mac_process_sem;
+
+//MAC²ãÈÎÎñ´¦Àí
+void LoraMacProcess_task_entry(void *args)
+{
+ while(1)
+ {
+ tos_sem_pend(&lora_mac_process_sem, TOS_TIME_FOREVER);
+ LoRaMacProcess(NULL);
+ }
+
+}
+/*Exported functions ---------------------------------------------------------*/
+void MX_LoRaWAN_Init(void)
+{
+ printf("Lora node powered by TencentOS-tiny\r\n");
+
+ /* Configure the Lora Stack*/
+ lw_init(&LoRaMainCallbacks);
+
+ APP_LOG("APP_VERSION= %02X.%02X.%02X.%02X\r\n", (uint8_t)(__APP_VERSION >> 24), (uint8_t)(__APP_VERSION >> 16), (uint8_t)(__APP_VERSION >> 8), (uint8_t)__APP_VERSION);
+ APP_LOG("MAC_VERSION= %02X.%02X.%02X.%02X\r\n", (uint8_t)(__LORA_MAC_VERSION >> 24), (uint8_t)(__LORA_MAC_VERSION >> 16), (uint8_t)(__LORA_MAC_VERSION >> 8), (uint8_t)__LORA_MAC_VERSION);
+
+ /*Class*/
+ lw_request_class(CLASS_A);
+
+ /*channel 8-16*/
+ LWChannel_mask_t ch={0xFF00,0,0,0,0,0};
+ lw_current_chmask_set(&ch);
+
+ /*tx dr*/
+ lw_config_tx_datarate_set(DR_3);
+
+ /*OTAA*/
+ lw_config_otaa_set(LORA_ENABLE);
+
+ /*enable adr*/
+ lw_adr_set(LORA_ENABLE);
+
+ /*set retry*/
+ lw_confirm_retry_set(3);
+
+ printf("lorawan init ok.\r\n");
+}
+
+void application_entry(void *arg)
+{
+
+ //´´½¨ÐźÅÁ¿
+ tos_sem_create_max(&lora_mac_process_sem, 0, 1);
+
+ //´´½¨ÈÎÎñ
+ tos_task_create(&lora_mac_process_task,
+ "lora_mac_process_task",
+ LoraMacProcess_task_entry,
+ NULL,
+ 2,
+ lora_mac_process_task_stack,
+ sizeof(lora_mac_process_task_stack),
+ 10);
+
+ Lora_start_send();
+}
+
+
+/*==============================================================
+create a send task ,the task is periodic trigger
+================================================================*/
+void Lora_start_send(void)
+{
+ /* send everytime timer elapses */
+ TimerInit(&send_timer, cycle_send_event);
+ TimerSetValue(&send_timer, APP_TX_DUTYCYCLE);
+ cycle_send_event(NULL);
+}
+
+static void cycle_send_event(void *args)
+{
+ //´¥·¢´ËÈÎÎñ
+ //UTIL_SEQ_SetTask(CFG_SEQ_TASK_LORA_SEND_ON_TX_TIMER, CFG_SEQ_Prio_0);
+ send_process(NULL);
+ /*Wait for next tx slot*/
+ TimerStart(&send_timer);
+ //tos_timer_start(&send_timer);
+}
+
+static void send_process(void *args)
+{
+ int16_t temperature = 0;
+ uint8_t batteryLevel=0;
+
+ /*Check if it is in OTAA mode*/
+ if(LORA_ENABLE == lw_config_otaa_get())
+ {
+ /*check if it is already joined*/
+ if(lw_join_status() != LORA_SET)
+ {
+ lw_join();
+ return;
+ }
+ }
+
+ APP_LOG("SEND REQUEST=======================================>\n\r");
+ /*fill the data*/
+ uint32_t i = 0;
+
+ app_data.port = LORAWAN_UPLINK_PORT;
+
+ batteryLevel = LORA_GetBatteryLevel(); /* 1 (very low) to 254 (fully charged) */
+ temperature = SYS_GetTemperatureLevel(); /* report Temperature */
+
+ app_data.buff[i++] = batteryLevel * 100 / 254;
+ app_data.buff[i++] = temperature>>8;
+ app_data.buff[i++] = temperature&0xff;
+
+ app_data.size = i;
+
+ /*you can set the channel by using these function*/
+// LWChannel_mask_t ch_r={0};
+// LWChannel_mask_t ch_w={0x00FF,0,0,0,0,0};
+
+// lw_current_chmask_set(&ch_w);
+// APP_LOG("ch_write: %04X %04X %04X %04X %04X %04X\r\n",ch_w.mask[0],ch_w.mask[1],ch_w.mask[2],ch_w.mask[3],ch_w.mask[4],ch_w.mask[5]);
+// lw_current_chmask_get(&ch_r);
+// APP_LOG("ch_read: %04X %04X %04X %04X %04X %04X\r\n",ch_r.mask[0],ch_r.mask[1],ch_r.mask[2],ch_r.mask[3],ch_r.mask[4],ch_r.mask[5]);
+
+
+ lw_send(&app_data, LORAWAN_CONFIRMED_MSG);
+
+
+}
+
+
+void mac_event_callback(APP_EVENT_t evt,const void* param)
+{
+ switch(evt)
+ {
+ case APP_RX_EVENT:{
+ lw_app_data_t* rx_data = (lw_app_data_t*)param;
+
+ switch(rx_data->port)
+ {
+ case 2:
+ APP_LOG("%s\r\n",rx_data->buff);
+ APP_LOG("size = %d\r\n",rx_data->size);
+ APP_LOG("<===============================>");break;
+ case 3:
+ lw_request_class(CLASS_A);break;
+ case 4:
+ lw_request_class(CLASS_B);break;
+ case 5:
+ lw_request_class(CLASS_C);break;
+ }
+
+ if(rx_data->fpending)
+ {
+ app_data.size = 0;
+ app_data.port = LORAWAN_UPLINK_PORT;
+ lw_send(&app_data, LORAWAN_UNCONFIRMED_MSG);
+ }
+
+ }break;
+
+ case APP_JOINED_EVENT:{
+ APP_LOG("end node joined \n\r");
+ //lw_request_class(CLASS_A);
+ }break;
+
+ case APP_CONFIRM_ClASS_EVENT:{
+ DeviceClass_t* cur_class = (DeviceClass_t*)param;
+ APP_LOG("==================Switch to Class%c done\n\r","ABC"[*cur_class]);
+ /*Optionnal*/
+ /*informs the server that switch has occurred ASAP*/
+ app_data.size = 0;
+ app_data.port = LORAWAN_UPLINK_PORT;
+ lw_send(&app_data, LORAWAN_UNCONFIRMED_MSG);
+ }break;
+
+ case APP_TX_DONE_EVENT:{
+ lw_app_data_t* tx_done = (lw_app_data_t*)param;
+
+ if(tx_done->ack){
+ APP_LOG("Network Server \"ack\" an uplink data confirmed message transmission\n\r");
+ }
+ else{
+ APP_LOG("tx =======> down\n\r");
+ }
+ }break;
+
+ default :break;
+ }
+}
+static void LoraMacProcessNotify(void)
+{
+ //UTIL_SEQ_SetTask(CFG_SEQ_TASK_LORAMAC_PROCESS, CFG_SEQ_Prio_0);
+ //ÊÍ·ÅÐźÅÁ¿
+ tos_sem_post(&lora_mac_process_sem);
+}
+
+/**
+ * @brief This function return the battery level
+ * @param none
+ * @retval the battery level 1 (very low) to 254 (fully charged)
+ */
+static uint8_t LORA_GetBatteryLevel(void)
+{
+ /* USER CODE BEGIN 1 */
+ uint16_t batteryLevelmV;
+ uint8_t batteryLevel = 0;
+
+ batteryLevelmV = (uint16_t) SYS_GetBatteryLevel();
+
+ /* Convert batterey level from mV to linea scale: 1 (very low) to 254 (fully charged) */
+ if(batteryLevelmV > VDD_BAT)
+ {
+ batteryLevel = 254;
+ }
+ else if(batteryLevelmV < VDD_MIN)
+ {
+ batteryLevel = 0;
+ }
+ else
+ {
+ batteryLevel = (((uint32_t)(batteryLevelmV - VDD_MIN) * 254) / (VDD_BAT - VDD_MIN));
+ }
+ APP_LOG("VDDA= %d\n\r", batteryLevel);
+
+ return batteryLevel; /* 1 (very low) to 254 (fully charged) */
+ /* USER CODE END 1 */
+}
+
+static uint16_t LORA_GetTemperatureLevel(void)
+{
+ return (uint16_t) SYS_GetTemperatureLevel();
+}
+
+
+/**
+ * @brief This function return a random seed
+ * @note based on the device unique ID
+ * @param None
+ * @retval see
+ */
+static uint32_t GetRandomSeed(void)
+{
+ return ((HAL_GetUIDw0()) ^ (HAL_GetUIDw1()) ^ (HAL_GetUIDw2()));
+}
+
+/**
+ * @brief This function return a unique ID
+ * @param unique ID
+ * @retval none
+ */
+static void GetUniqueId(uint8_t *id)
+{
+ uint32_t ID_1_3_val = HAL_GetUIDw0() + HAL_GetUIDw2();
+ uint32_t ID_2_val = HAL_GetUIDw1();
+
+ id[7] = (ID_1_3_val) >> 24;
+ id[6] = (ID_1_3_val) >> 16;
+ id[5] = (ID_1_3_val) >> 8;
+ id[4] = (ID_1_3_val);
+ id[3] = (ID_2_val) >> 24;
+ id[2] = (ID_2_val) >> 16;
+ id[1] = (ID_2_val) >> 8;
+ id[0] = (ID_2_val);
+}
+
+
+
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/app_lorawan.h b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/app_lorawan.h
new file mode 100644
index 00000000..abd99aae
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/app_lorawan.h
@@ -0,0 +1,57 @@
+/**
+ ******************************************************************************
+ * @file app_lorawan.h
+ * @author MCD Application Team
+ * @brief header of application of the LRWAN Middleware
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under Ultimate Liberty license
+ * SLA0044, the "License"; You may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at:
+ * www.st.com/SLA0044
+ *
+ ******************************************************************************
+ */
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __APP_LORAWAN_H__
+#define __APP_LORAWAN_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+#include "lora.h"
+
+/* Exported constants --------------------------------------------------------*/
+/* Exported types ------------------------------------------------------------*/
+/* External variables --------------------------------------------------------*/
+/* Exported macros -----------------------------------------------------------*/
+/* Exported functions ------------------------------------------------------- */
+
+/**
+* @brief Init Lora Application
+* @param None
+* @retval None
+*/
+void MX_LoRaWAN_Init(void);
+/**
+* @brief Send Lora Data
+* @param None
+* @retval None
+*/
+void MX_LoRaWAN_Process(void);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*__APP_LORAWAN_H__*/
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
+
diff --git a/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/app_system.c b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/app_system.c
new file mode 100644
index 00000000..15d573e4
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/app_system.c
@@ -0,0 +1,128 @@
+#include
+#include
+#include "main.h"
+#include "app_system.h"
+#include "adc_if.h"
+//#include "stm32_seq.h"
+
+#include "stm32_systime.h"
+#include "stm32_lpm.h"
+
+#include "usart_if.h"
+/**
+ * @brief Set all pins such to minimized consumption (necessary for some STM32 families)
+ * @param none
+ * @retval None
+ */
+static void Gpio_PreInit(void);
+/**
+ * @brief Returns sec and msec based on the systime in use
+ * @param none
+ * @retval none
+ */
+static void TimestampNow(uint8_t *buff, uint16_t* size);
+
+/* Exported functions ---------------------------------------------------------*/
+
+/**
+ * @brief initialises the system (dbg pins, trace, mbmux, systiemr, LPM, ...)
+ * @param none
+ * @retval none
+ */
+void SystemApp_Init(void)
+{
+ Gpio_PreInit();
+
+ vcom_Init(NULL);
+
+ /*Initialize the temperature and Battery measurement services */
+ SYS_InitMeasurement();
+
+#if LOW_POWER_MODE_ENBLE
+ /*Init low power manager*/
+ UTIL_LPM_Init();
+ /* Disable Stand-by mode */
+ UTIL_LPM_SetOffMode(LPM_APPLI_Id, UTIL_LPM_DISABLE);
+
+ #if defined (LPM_STOP_MODE_DISABLE) && (LPM_STOP_MODE_DISABLE == 1)
+ /* Disable Stop Mode */
+ UTIL_LPM_SetStopMode(LPM_APPLI_Id, UTIL_LPM_DISABLE);
+ #elif !defined (LPM_STOP_MODE_DISABLE)
+ #error LPM_STOP_MODE_DISABLE not defined
+ #endif
+#endif
+}
+
+/**
+ * @brief redefines __weak function in stm32_seq.c such to enter low power
+ * @param none
+ * @retval none
+ */
+void UTIL_SEQ_Idle(void)
+{
+#if LOW_POWER_MODE_ENBLE
+ UTIL_LPM_EnterLowPower();
+#endif
+}
+
+/* Private functions ---------------------------------------------------------*/
+
+static void TimestampNow(uint8_t *buff, uint16_t* size)
+{
+ SysTime_t curtime = SysTimeGet();
+ sprintf((char *)buff, "%ds%d:", curtime.Seconds, curtime.SubSeconds);
+ *size = strlen((char *)buff);
+}
+
+
+
+static void Gpio_PreInit(void)
+{
+ GPIO_InitTypeDef GPIO_InitStruct = {0};
+
+ /* Configure all IOs in analog input */
+ /* Except PA143 and PA14 (SWCLK and SWD) for debug*/
+ /* PA13 and PA14 are configured in debug_init */
+ /* Configure all GPIO as analog to reduce current consumption on non used IOs */
+ /* Enable GPIOs clock */
+ __HAL_RCC_GPIOA_CLK_ENABLE();
+ __HAL_RCC_GPIOB_CLK_ENABLE();
+ __HAL_RCC_GPIOC_CLK_ENABLE();
+ __HAL_RCC_GPIOH_CLK_ENABLE();
+
+ GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
+ GPIO_InitStruct.Pull = GPIO_NOPULL;
+ /* All GPIOs except debug pins (SWCLK and SWD) */
+ GPIO_InitStruct.Pin = GPIO_PIN_All & (~(GPIO_PIN_13 | GPIO_PIN_14));
+ HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
+
+ /* All GPIOs */
+ GPIO_InitStruct.Pin = GPIO_PIN_All;
+ HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
+ HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
+ HAL_GPIO_Init(GPIOH, &GPIO_InitStruct);
+
+ /* Disable GPIOs clock */
+ __HAL_RCC_GPIOA_CLK_DISABLE();
+ __HAL_RCC_GPIOB_CLK_DISABLE();
+ __HAL_RCC_GPIOC_CLK_DISABLE();
+ __HAL_RCC_GPIOH_CLK_DISABLE();
+}
+
+/* Disable StopMode when traces need to be printed */
+void UTIL_ADV_TRACE_PreSendHook (void)
+{
+#if LOW_POWER_MODE_ENBLE
+ UTIL_LPM_SetStopMode(LPM_UART_TX_Id , UTIL_LPM_DISABLE );
+#endif
+}
+/* Reenable StopMode when traces have been printed */
+void UTIL_ADV_TRACE_PostSendHook (void)
+{
+#if LOW_POWER_MODE_ENBLE
+ UTIL_LPM_SetStopMode(LPM_UART_TX_Id , UTIL_LPM_ENABLE );
+#endif
+}
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
+
diff --git a/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/app_system.h b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/app_system.h
new file mode 100644
index 00000000..ff147310
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/app_system.h
@@ -0,0 +1,71 @@
+/**
+ ******************************************************************************
+ * @file app_system.h
+ * @author MCD Application Team
+ * @brief function prototypes for app_system.c file
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under Ultimate Liberty license
+ * SLA0044, the "License"; You may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at:
+ * www.st.com/SLA0044
+ *
+ ******************************************************************************
+ */
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __APP_SYSTEM_H__
+#define __APP_SYSTEM_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+#include
+#include "app_conf.h"
+
+#include "mw_log_conf.h"
+#include
+
+/* Exported types ------------------------------------------------------------*/
+/* Exported constants --------------------------------------------------------*/
+
+/* External variables --------------------------------------------------------*/
+/* Exported macros -----------------------------------------------------------*/
+
+#if defined (APP_LOG_ENABLED) && (APP_LOG_ENABLED == 1)
+ #define APP_LOG LOG_PRINT
+ #define APP_PPRINTF printf
+ #define APP_TPRINTF printf
+ #define APP_PRINTF printf
+#elif defined (APP_LOG_ENABLED) && (APP_LOG_ENABLED == 0)
+ #define APP_LOG(...)
+ #define APP_PPRINTF(...)
+ #define APP_TPRINTF(...)
+ #define APP_PRINTF(...)
+#else
+ #error "APP_LOG_ENABLED not defined or out of range <0,1>"
+#endif
+
+/* Defines -------------------------------------------------------------------*/
+/* Exported functions ------------------------------------------------------- */
+
+/**
+ * @brief initialises the system (dbg pins, trace, systiemr, LPM, ...)
+ * @param none
+ * @retval none
+ */
+void SystemApp_Init(void);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __APP_SYSTEM_H__ */
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/lorawan_version.h b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/lorawan_version.h
new file mode 100644
index 00000000..429ad4b7
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/lorawan_version.h
@@ -0,0 +1,52 @@
+/******************************************************************************
+ * @file lorawan_version.h
+ * @author MCD Application Team
+ * @brief defines the lora mac version
+ ******************************************************************************
+* @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under Ultimate Liberty license
+ * SLA0044, the "License"; You may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at:
+ * www.st.com/SLA0044
+ *
+ ******************************************************************************
+ */
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __LORAWAN_VERSION_H__
+#define __LORAWAN_VERSION_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+#include "lora_mac_version.h"
+/* Exported constants --------------------------------------------------------*/
+#define __APP_VERSION_MAIN (0x00U) /*!< [31:24] main version */
+#define __APP_VERSION_SUB1 (0x04U) /*!< [23:16] sub1 version */
+#define __APP_VERSION_SUB2 (0x00U) /*!< [15:8] sub2 version */
+#define __APP_VERSION_RC (0x00U) /*!< [7:0] release candidate */
+#define __APP_VERSION ((__APP_VERSION_MAIN <<24)\
+ |(__APP_VERSION_SUB1 << 16)\
+ |(__APP_VERSION_SUB2 << 8 )\
+ |(__APP_VERSION_RC))
+
+#define __MAC_VERSION (uint32_t) (LORA_MAC_VERSION )
+
+/* Exported types ------------------------------------------------------------*/
+/* External variables --------------------------------------------------------*/
+/* Exported macros -----------------------------------------------------------*/
+/* Exported functions ------------------------------------------------------- */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*__LORAWAN_VERSION_H__*/
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/lorawan_conf.h b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/lorawan_conf.h
new file mode 100644
index 00000000..d2ae10af
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/lorawan_conf.h
@@ -0,0 +1,91 @@
+/**
+ ******************************************************************************
+ * @file lorawan_conf.h
+ * @author MCD Application Team
+ * @brief configures lorawan instances
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under Ultimate Liberty license
+ * SLA0044, the "License"; You may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at:
+ * www.st.com/SLA0044
+ *
+ ******************************************************************************
+ */
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __LORAWAN_CONF_H__
+#define __LORAWAN_CONF_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/* Includes ------------------------------------------------------------------*/
+#include "stm32_systime.h"
+#include "app_conf.h"
+
+/* Exported types ------------------------------------------------------------*/
+/* Exported constants --------------------------------------------------------*/
+/* External variables --------------------------------------------------------*/
+/* Exported macros -----------------------------------------------------------*/
+/* Exported functions ------------------------------------------------------- */
+/* Defines ------------------------------------------------------------------*/
+#ifndef CRITICAL_SECTION_BEGIN
+#define CRITICAL_SECTION_BEGIN( ) UTILS_ENTER_CRITICAL_SECTION( )
+#endif
+#ifndef CRITICAL_SECTION_END
+#define CRITICAL_SECTION_END( ) UTILS_EXIT_CRITICAL_SECTION( )
+#endif
+
+/* Region ------------------------------------*/
+/* the region listed here will be linked in the MW code */
+/* the applic (on app_conf.h) shall just configure one region at the time */
+//#define REGION_AS923
+//#define REGION_AU915
+#define REGION_CN470
+#define REGION_CN470ALI
+//#define REGION_CN779
+//#define REGION_EU433
+#define REGION_EU868
+//#define REGION_KR920
+//#define REGION_IN865
+#define REGION_US915
+//#define REGION_RU864
+
+
+
+
+#ifdef LORAMAC_CLASSB_ENABLED
+/* CLASS B LSE crystall calibration*/
+ /*!
+ * \brief Temperature coefficient of the clock source
+ */
+#define RTC_TEMP_COEFFICIENT ( -0.035 )
+
+/*!
+ * \brief Temperature coefficient deviation of the clock source
+ */
+#define RTC_TEMP_DEV_COEFFICIENT ( 0.0035 )
+
+/*!
+ * \brief Turnover temperature of the clock source
+ */
+#define RTC_TEMP_TURNOVER ( 25.0 )
+
+/*!
+ * \brief Turnover temperature deviation of the clock source
+ */
+#define RTC_TEMP_DEV_TURNOVER ( 5.0 )
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LORAWAN_CONF_H__ */
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
+
diff --git a/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/mw_log_conf.h b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/mw_log_conf.h
new file mode 100644
index 00000000..af4aa905
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/mw_log_conf.h
@@ -0,0 +1,73 @@
+/**
+ ******************************************************************************
+ * @file mw_log_conf.h
+ * @author MCD Application Team
+ * @brief Interface layer CM4 System to MBMUX (Mailbox Multiplexer)
+ *******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under Ultimate Liberty license
+ * SLA0044, the "License"; You may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at:
+ * www.st.com/SLA0044
+ *
+ ******************************************************************************
+ */
+
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __MW_LOG_CONF_H__
+#define __MW_LOG_CONF_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+#include
+#include
+#include "app_conf.h"
+/* Exported types ------------------------------------------------------------*/
+/* Exported constants --------------------------------------------------------*/
+
+/* Exported macros -----------------------------------------------------------*/
+
+#if LOG_LEVEL == 0
+ #define LOG_PRINT printf
+#elif LOG_LEVEL == 1
+ #define LOG_PRINT(...) do{printf("<>\r\n"\
+ ,__LINE__, __FUNCTION__);\
+ printf(__VA_ARGS__);}while(0);
+#elif LOG_LEVEL == 2
+ #define LOG_PRINT(...) do{printf("<<<%s ->line:%d ;function:%s >>>\r\n"\
+ ,__FILE__,__LINE__, __FUNCTION__);\
+ printf(__VA_ARGS__);}while(0);
+#endif
+
+#if defined (MW_LOG_ENABLED) && (MW_LOG_ENABLED == 1)
+ #define PPRINTF printf
+ #define TPRINTF printf
+ #define PRINTF printf
+ #define MW_LOG LOG_PRINT
+#else
+ #define PPRINTF(...)
+ #define TPRINTF(...)
+ #define PRINTF(...)
+ #define MW_LOG(...)
+#endif
+
+/* Defines -------------------------------------------------------------------*/
+/* Exported functions ------------------------------------------------------- */
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*__MW_LOG_CONF_H__ */
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/radio_conf.h b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/radio_conf.h
new file mode 100644
index 00000000..14ef11a7
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/radio_conf.h
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * @file radio_conf.h
+ * @author MCD Application Team
+ * @brief Header of radio configuration
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under Ultimate Liberty license
+ * SLA0044, the "License"; You may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at:
+ * www.st.com/SLA0044
+ *
+ ******************************************************************************
+ */
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __RADIO_CONF_H__
+#define __RADIO_CONF_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+#include "platform.h"
+#include "mw_log_conf.h"
+
+#include "radio_driver.h"
+#include "app_conf.h"
+#include "stm32_mem.h"
+
+#ifndef CRITICAL_SECTION_BEGIN
+#define CRITICAL_SECTION_BEGIN( ) UTILS_ENTER_CRITICAL_SECTION( )
+#endif
+#ifndef CRITICAL_SECTION_END
+#define CRITICAL_SECTION_END( ) UTILS_EXIT_CRITICAL_SECTION( )
+#endif
+/* Exported types ------------------------------------------------------------*/
+/* Defines ------------------------*/
+#define RADIO_RX_BUF_SIZE 255
+
+//#define LET_SUBGHZ_MW_USING_DGB_LINE1_PIN
+//#define LET_SUBGHZ_MW_USING_DGB_LINE2_PIN
+
+/* Function mapping */
+#define RADIO_DELAY_MS HAL_Delay
+#define RADIO_MEMSET8( dest, value, size ) UTIL_MEM_set_8(dest, value, size )
+
+
+/* Exported constants --------------------------------------------------------*/
+/* External variables --------------------------------------------------------*/
+/* Exported macros -----------------------------------------------------------*/
+/* Exported functions ------------------------------------------------------- */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __RADIO_CONF_H__*/
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/systime.h b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/systime.h
new file mode 100644
index 00000000..31ab25db
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/systime.h
@@ -0,0 +1,41 @@
+/******************************************************************************
+ * @file systime.h
+ * @author MCD Application Team
+ * @brief map middleware systime
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under Ultimate Liberty license
+ * SLA0044, the "License"; You may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at:
+ * www.st.com/SLA0044
+ *
+ ******************************************************************************
+ */
+/* Define to prevent recursive inclusion -------------------------------------*/
+
+#ifndef __SYSTIME_H__
+#define __SYSTIME_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+#include "stm32_systime.h"
+/* Exported constants --------------------------------------------------------*/
+/* Exported types ------------------------------------------------------------*/
+/* External variables --------------------------------------------------------*/
+/* Exported macros -----------------------------------------------------------*/
+/* Exported functions ------------------------------------------------------- */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*__SYSTIME_H__*/
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/timer.h b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/timer.h
new file mode 100644
index 00000000..97912016
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/timer.h
@@ -0,0 +1,72 @@
+/******************************************************************************
+ * @file timer.h
+ * @author MCD Application Team
+ * @brief wrapper to timer server
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under Ultimate Liberty license
+ * SLA0044, the "License"; You may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at:
+ * www.st.com/SLA0044
+ *
+ ******************************************************************************
+ */
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __TIMER_H__
+#define __TIMER_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+//#include "stm32_timer.h"
+#include
+
+/* Exported types ------------------------------------------------------------*/
+/* Exported constants --------------------------------------------------------*/
+#define TIMER_MAX_VAL 0xFFFFFFFFU
+
+/* External variables --------------------------------------------------------*/
+/* Exported macros -----------------------------------------------------------*/
+#define TimerTime_t k_tick_t
+
+#define TimerEvent_t k_timer_t
+
+#define TimerInit(HANDLE, CB) do {\
+ tos_timer_create( HANDLE, TIMER_MAX_VAL, 0, CB, NULL, TOS_OPT_TIMER_ONESHOT);\
+ } while(0)
+
+#define TimerSetValue(HANDLE, TIMEOUT) do{ \
+ tos_timer_delay_change(HANDLE, TIMEOUT);\
+ } while(0)
+
+#define TimerStart(HANDLE) do {\
+ tos_timer_start(HANDLE);\
+ } while(0)
+
+#define TimerStop(HANDLE) do {\
+ tos_timer_stop(HANDLE);\
+ } while(0)
+
+#define TimerGetCurrentTime TIMER_GetCurrentTime
+
+#define TimerGetElapsedTime TIMER_GetElapsedTime
+
+uint8_t TIMER_GetCurrentTime(void);
+
+uint8_t TIMER_GetElapsedTime(TimerTime_t past);
+
+/* Exported functions ------------------------------------------------------- */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __TIMER_H__*/
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/timer_wrapper.c b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/timer_wrapper.c
new file mode 100644
index 00000000..7e541fab
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/timer_wrapper.c
@@ -0,0 +1,18 @@
+#include
+#include "timer.h"
+#include "rtc_if.h"
+
+
+uint8_t TIMER_GetCurrentTime(void)
+{
+ uint32_t now = RTC_GetTimerValue( );
+ return RTC_Tick2ms(now);
+}
+
+uint8_t TIMER_GetElapsedTime(TimerTime_t past)
+{
+ uint32_t nowInTicks = RTC_GetTimerValue();
+ uint32_t pastInTicks = RTC_ms2Tick(past);
+ /* intentional wrap around. Works Ok if tick duation below 1ms */
+ return RTC_Tick2ms(nowInTicks - pastInTicks);
+}
\ No newline at end of file
diff --git a/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/utilities_conf.h b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/utilities_conf.h
new file mode 100644
index 00000000..914fa6f5
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/utilities_conf.h
@@ -0,0 +1,118 @@
+/**
+ ******************************************************************************
+ * @file utilities_conf.h
+ * @author MCD Application Team
+ * @brief Configuration file to utilities
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2019 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under BSD 3-Clause license,
+ * the "License"; You may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ * opensource.org/licenses/BSD-3-Clause
+ *
+ ******************************************************************************
+ */
+
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef UTILITIES_CONF_H
+#define UTILITIES_CONF_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+
+/* Includes ------------------------------------------------------------------*/
+#include "stm32wlxx.h"
+
+/* definitions to be provided to "sequencer" utility */
+#include "stm32_mem.h"
+/* definition and callback for tiny_vsnprintf */
+#include "stm32_tiny_vsnprintf.h"
+
+/* Exported types ------------------------------------------------------------*/
+/* Exported constants --------------------------------------------------------*/
+
+/******************************************************************************
+ * Common
+ ******************************************************************************/
+#if defined(__CC_ARM)
+#define UTIL_PLACE_IN_SECTION( __x__ ) __attribute__((section (__x__), zero_init))
+#elif defined(__ICCARM__)
+#define UTIL_PLACE_IN_SECTION( __x__ ) __attribute__((section (__x__)))
+#else /* __GNUC__ */
+#define UTIL_PLACE_IN_SECTION( __x__ ) __attribute__((section (__x__)))
+#endif
+
+#undef ALIGN
+#ifdef WIN32
+#define ALIGN(n)
+#else
+#define ALIGN(n) __attribute__((aligned(n)))
+#endif
+
+/*---------------------------------------------------------------------------*/
+/* sequencer configuration */
+/*---------------------------------------------------------------------------*/
+#define UTIL_SEQ_RFU 0
+
+#define UTIL_SEQ_INIT_CRITICAL_SECTION( ) UTILS_INIT_CRITICAL_SECTION()
+#define UTIL_SEQ_ENTER_CRITICAL_SECTION( ) UTILS_ENTER_CRITICAL_SECTION()
+#define UTIL_SEQ_EXIT_CRITICAL_SECTION( ) UTILS_EXIT_CRITICAL_SECTION()
+#define UTIL_SEQ_MEMSET8( dest, value, size ) UTIL_MEM_set_8( dest, value, size )
+
+
+/* External variables --------------------------------------------------------*/
+/* Exported macros -----------------------------------------------------------*/
+#define UTILS_INIT_CRITICAL_SECTION()
+#define UTILS_ENTER_CRITICAL_SECTION() uint32_t primask_bit= __get_PRIMASK();\
+ __disable_irq()
+#define UTILS_EXIT_CRITICAL_SECTION() __set_PRIMASK(primask_bit)
+
+
+/* Exported functions ------------------------------------------------------- */
+/******************************************************************************
+ * trace\advanced
+ * the define option
+ * UTIL_ADV_TRACE_CONDITIONNAL shall be defined if you want use conditionnal function
+ * UTIL_ADV_TRACE_UNCHUNK_MODE shall be defined if you want use the unchunk mode
+ *
+ ******************************************************************************/
+
+
+#define VLEVEL_OFF 0
+#define VLEVEL_L 1 /* just essential traces */
+#define VLEVEL_M 2
+#define VLEVEL_H 3 /* all traces */
+
+#define TS_OFF 0 /*Log without TimeStamp*/
+#define TS_ON 1 /*Log with TimeStamp*/
+
+#define T_REG_OFF 0 /*Log without bitmask*/
+
+#define UTIL_ADV_TRACE_CONDITIONNAL
+#define UTIL_ADV_TRACE_UNCHUNK_MODE
+#define UTIL_ADV_TRACE_DEBUG(...)
+#define UTIL_ADV_TRACE_INIT_CRITICAL_SECTION( ) UTILS_INIT_CRITICAL_SECTION()
+#define UTIL_ADV_TRACE_ENTER_CRITICAL_SECTION( ) UTILS_ENTER_CRITICAL_SECTION()
+#define UTIL_ADV_TRACE_EXIT_CRITICAL_SECTION( ) UTILS_EXIT_CRITICAL_SECTION()
+#define UTIL_ADV_TRACE_TMP_BUF_SIZE (256U)
+#define UTIL_ADV_TRACE_TMP_MAX_TIMESTMAP_SIZE (15U)
+#define UTIL_ADV_TRACE_FIFO_SIZE (512U)
+#define UTIL_ADV_TRACE_MEMSET8( dest, value, size) UTIL_MEM_set_8((dest),(value),(size))
+#define UTIL_ADV_TRACE_VSNPRINTF(...) tiny_vsnprintf_like(__VA_ARGS__)
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*UTILITIES_CONF_H */
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/TencentOS_Tiny.uvoptx b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/TencentOS_Tiny.uvoptx
new file mode 100644
index 00000000..3eb26311
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/TencentOS_Tiny.uvoptx
@@ -0,0 +1,1666 @@
+
+
+
+ 1.0
+
+ ### uVision Project, (C) Keil Software
+
+
+ *.c
+ *.s*; *.src; *.a*
+ *.obj; *.o
+ *.lib
+ *.txt; *.h; *.inc
+ *.plm
+ *.cpp
+ 0
+
+
+
+ 0
+ 0
+
+
+
+ TencentOS_Tiny
+ 0x4
+ ARM-ADS
+
+ 12000000
+
+ 1
+ 1
+ 0
+ 1
+ 0
+
+
+ 1
+ 65535
+ 0
+ 0
+ 0
+
+
+ 79
+ 66
+ 8
+ .\List\
+
+
+ 1
+ 1
+ 1
+ 0
+ 1
+ 1
+ 0
+ 1
+ 0
+ 0
+ 0
+ 0
+
+
+ 1
+ 1
+ 1
+ 1
+ 1
+ 1
+ 1
+ 0
+ 0
+
+
+ 1
+ 0
+ 1
+
+ 18
+
+ 0
+ 1
+ 1
+ 1
+ 1
+ 1
+ 1
+ 1
+ 1
+ 1
+ 1
+ 1
+ 1
+ 1
+ 1
+ 1
+ 1
+ 1
+ 1
+ 0
+ 0
+ 1
+ 0
+ 0
+ 6
+
+
+
+
+
+
+
+
+
+
+ STLink\ST-LINKIII-KEIL_SWO.dll
+
+
+
+ 0
+ JL2CM3
+ -U260106173 -O78 -S2 -ZTIFSpeedSel5000 -A0 -C0 -JU1 -JI127.0.0.1 -JP0 -RST0 -TO18 -TC10000000 -TP21 -TDS8000 -TDT0 -TDC1F -TIEFFFFFFFF -TIP8 -TB1 -TFE0 -FO15 -FD20000000 -FC1000 -FN1 -FF0STM32WLxx_CM4.FLM -FS08000000 -FL040000 -FP0($$Device:STM32WLE5JCIx$CMSIS\Flash\STM32WLxx_CM4.FLM)
+
+
+ 0
+ UL2CM3
+ UL2CM3(-S0 -C0 -P0 ) -FN1 -FC1000 -FD20000000 -FF0STM32WLxx_CM4 -FL040000 -FS08000000 -FP0($$Device:STM32WLE5JCIx$CMSIS\Flash\STM32WLxx_CM4.FLM)
+
+
+ 0
+ ST-LINKIII-KEIL_SWO
+ -U066DFF495056805087213734 -O206 -SF4000 -C0 -A0 -I0 -HNlocalhost -HP7184 -P1 -N00("ARM CoreSight SW-DP") -D00(6BA02477) -L00(0) -TO18 -TC10000000 -TP21 -TDS8000 -TDT0 -TDC1F -TIEFFFFFFFF -TIP8 -FO15 -FD20000000 -FC1000 -FN1 -FF0STM32WLxx_CM4.FLM -FS08000000 -FL040000 -FP0($$Device:STM32WLE5JCIx$CMSIS\Flash\STM32WLxx_CM4.FLM)
+
+
+ 0
+ ARMRTXEVENTFLAGS
+ -L70 -Z18 -C0 -M0 -T1
+
+
+ 0
+ DLGTARM
+ (1010=-1,-1,-1,-1,0)(1007=-1,-1,-1,-1,0)(1008=-1,-1,-1,-1,0)(1009=-1,-1,-1,-1,0)(1012=-1,-1,-1,-1,0)
+
+
+ 0
+ ARMDBGFLAGS
+
+
+
+ 0
+ DLGUARM
+ (105=-1,-1,-1,-1,0)
+
+
+
+
+ 0
+ 0
+ 122
+ 0
+ 134244104
+ 0
+ 0
+ 0
+ 0
+ 0
+ 1
+ ..\..\..\..\kernel\core\tos_task.c
+
+ \\TencentOS_Tiny\../../../../kernel/core/tos_task.c\122
+
+
+ 1
+ 0
+ 105
+ 1
+ 134218200
+ 0
+ 0
+ 0
+ 0
+ 0
+ 1
+ ..\..\..\..\arch\arm\arm-v7m\cortex-m4\armcc\port_s.S
+
+ \\TencentOS_Tiny\../../../../arch/arm/arm-v7m/cortex-m4/armcc/port_s.S\105
+
+
+ 2
+ 0
+ 97
+ 1
+ 134218196
+ 0
+ 0
+ 0
+ 0
+ 0
+ 1
+ ..\..\..\..\arch\arm\arm-v7m\cortex-m4\armcc\port_s.S
+
+ \\TencentOS_Tiny\../../../../arch/arm/arm-v7m/cortex-m4/armcc/port_s.S\97
+
+
+
+
+ 0
+ 1
+ task
+
+
+
+ 0
+
+
+ 0
+ 1
+ 1
+ 0
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+
+
+
+ 0
+ 0
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
+ Application/MDK-ARM
+ 0
+ 0
+ 0
+ 0
+
+ 1
+ 1
+ 2
+ 0
+ 0
+ 0
+ .\startup_stm32wle5xx.s
+ startup_stm32wle5xx.s
+ 0
+ 0
+
+
+
+
+ Application/User
+ 1
+ 0
+ 0
+ 0
+
+ 2
+ 2
+ 1
+ 0
+ 0
+ 0
+ ..\..\BSP\Src\adc.c
+ adc.c
+ 0
+ 0
+
+
+ 2
+ 3
+ 1
+ 0
+ 0
+ 0
+ ..\..\BSP\Src\adc_if.c
+ adc_if.c
+ 0
+ 0
+
+
+ 2
+ 4
+ 1
+ 0
+ 0
+ 0
+ ..\..\BSP\Src\dma.c
+ dma.c
+ 0
+ 0
+
+
+ 2
+ 5
+ 1
+ 0
+ 0
+ 0
+ ..\..\BSP\Src\gpio.c
+ gpio.c
+ 0
+ 0
+
+
+ 2
+ 6
+ 1
+ 0
+ 0
+ 0
+ ..\..\BSP\Src\main.c
+ main.c
+ 0
+ 0
+
+
+ 2
+ 7
+ 1
+ 0
+ 0
+ 0
+ ..\..\BSP\Src\mcu_init.c
+ mcu_init.c
+ 0
+ 0
+
+
+ 2
+ 8
+ 1
+ 0
+ 0
+ 0
+ ..\..\BSP\Src\rtc.c
+ rtc.c
+ 0
+ 0
+
+
+ 2
+ 9
+ 1
+ 0
+ 0
+ 0
+ ..\..\BSP\Src\rtc_if.c
+ rtc_if.c
+ 0
+ 0
+
+
+ 2
+ 10
+ 1
+ 0
+ 0
+ 0
+ ..\..\BSP\Src\stm32_lpm_if.c
+ stm32_lpm_if.c
+ 0
+ 0
+
+
+ 2
+ 11
+ 1
+ 0
+ 0
+ 0
+ ..\..\BSP\Src\stm32wlxx_hal_msp.c
+ stm32wlxx_hal_msp.c
+ 0
+ 0
+
+
+ 2
+ 12
+ 1
+ 0
+ 0
+ 0
+ ..\..\BSP\Src\stm32wlxx_it.c
+ stm32wlxx_it.c
+ 0
+ 0
+
+
+ 2
+ 13
+ 1
+ 0
+ 0
+ 0
+ ..\..\BSP\Src\usart.c
+ usart.c
+ 0
+ 0
+
+
+ 2
+ 14
+ 1
+ 0
+ 0
+ 0
+ ..\..\BSP\Src\usart_if.c
+ usart_if.c
+ 0
+ 0
+
+
+
+
+ Drivers/STM32WLxx_HAL_Driver
+ 0
+ 0
+ 0
+ 0
+
+ 3
+ 15
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal.c
+ stm32wlxx_hal.c
+ 0
+ 0
+
+
+ 3
+ 16
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_cortex.c
+ stm32wlxx_hal_cortex.c
+ 0
+ 0
+
+
+ 3
+ 17
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_dma.c
+ stm32wlxx_hal_dma.c
+ 0
+ 0
+
+
+ 3
+ 18
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_dma_ex.c
+ stm32wlxx_hal_dma_ex.c
+ 0
+ 0
+
+
+ 3
+ 19
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_exti.c
+ stm32wlxx_hal_exti.c
+ 0
+ 0
+
+
+ 3
+ 20
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_flash.c
+ stm32wlxx_hal_flash.c
+ 0
+ 0
+
+
+ 3
+ 21
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_flash_ex.c
+ stm32wlxx_hal_flash_ex.c
+ 0
+ 0
+
+
+ 3
+ 22
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_gpio.c
+ stm32wlxx_hal_gpio.c
+ 0
+ 0
+
+
+ 3
+ 23
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_pwr.c
+ stm32wlxx_hal_pwr.c
+ 0
+ 0
+
+
+ 3
+ 24
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_pwr_ex.c
+ stm32wlxx_hal_pwr_ex.c
+ 0
+ 0
+
+
+ 3
+ 25
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_rcc.c
+ stm32wlxx_hal_rcc.c
+ 0
+ 0
+
+
+ 3
+ 26
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_rcc_ex.c
+ stm32wlxx_hal_rcc_ex.c
+ 0
+ 0
+
+
+ 3
+ 27
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_tim.c
+ stm32wlxx_hal_tim.c
+ 0
+ 0
+
+
+ 3
+ 28
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_tim_ex.c
+ stm32wlxx_hal_tim_ex.c
+ 0
+ 0
+
+
+ 3
+ 29
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_uart.c
+ stm32wlxx_hal_uart.c
+ 0
+ 0
+
+
+ 3
+ 30
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_uart_ex.c
+ stm32wlxx_hal_uart_ex.c
+ 0
+ 0
+
+
+ 3
+ 31
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_adc.c
+ stm32wlxx_hal_adc.c
+ 0
+ 0
+
+
+ 3
+ 32
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_adc_ex.c
+ stm32wlxx_hal_adc_ex.c
+ 0
+ 0
+
+
+ 3
+ 33
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_rtc.c
+ stm32wlxx_hal_rtc.c
+ 0
+ 0
+
+
+ 3
+ 34
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_rtc_ex.c
+ stm32wlxx_hal_rtc_ex.c
+ 0
+ 0
+
+
+ 3
+ 35
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_subghz.c
+ stm32wlxx_hal_subghz.c
+ 0
+ 0
+
+
+
+
+ Drivers/CMSIS
+ 0
+ 0
+ 0
+ 0
+
+ 4
+ 36
+ 1
+ 0
+ 0
+ 0
+ ..\..\BSP\Src\system_stm32wlxx.c
+ system_stm32wlxx.c
+ 0
+ 0
+
+
+
+
+ tos/arch
+ 0
+ 0
+ 0
+ 0
+
+ 5
+ 37
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\arch\arm\arm-v7m\common\tos_cpu.c
+ tos_cpu.c
+ 0
+ 0
+
+
+ 5
+ 38
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\arch\arm\arm-v7m\cortex-m4\armcc\port_c.c
+ port_c.c
+ 0
+ 0
+
+
+ 5
+ 39
+ 2
+ 0
+ 0
+ 0
+ ..\..\..\..\arch\arm\arm-v7m\cortex-m4\armcc\port_s.S
+ port_s.S
+ 0
+ 0
+
+
+
+
+ tos/kernel
+ 0
+ 0
+ 0
+ 0
+
+ 6
+ 40
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\kernel\core\tos_barrier.c
+ tos_barrier.c
+ 0
+ 0
+
+
+ 6
+ 41
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\kernel\core\tos_binary_heap.c
+ tos_binary_heap.c
+ 0
+ 0
+
+
+ 6
+ 42
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\kernel\core\tos_bitmap.c
+ tos_bitmap.c
+ 0
+ 0
+
+
+ 6
+ 43
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\kernel\core\tos_char_fifo.c
+ tos_char_fifo.c
+ 0
+ 0
+
+
+ 6
+ 44
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\kernel\core\tos_completion.c
+ tos_completion.c
+ 0
+ 0
+
+
+ 6
+ 45
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\kernel\core\tos_countdownlatch.c
+ tos_countdownlatch.c
+ 0
+ 0
+
+
+ 6
+ 46
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\kernel\core\tos_event.c
+ tos_event.c
+ 0
+ 0
+
+
+ 6
+ 47
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\kernel\core\tos_global.c
+ tos_global.c
+ 0
+ 0
+
+
+ 6
+ 48
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\kernel\core\tos_mail_queue.c
+ tos_mail_queue.c
+ 0
+ 0
+
+
+ 6
+ 49
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\kernel\core\tos_message_queue.c
+ tos_message_queue.c
+ 0
+ 0
+
+
+ 6
+ 50
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\kernel\core\tos_mmblk.c
+ tos_mmblk.c
+ 0
+ 0
+
+
+ 6
+ 51
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\kernel\core\tos_mmheap.c
+ tos_mmheap.c
+ 0
+ 0
+
+
+ 6
+ 52
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\kernel\core\tos_mutex.c
+ tos_mutex.c
+ 0
+ 0
+
+
+ 6
+ 53
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\kernel\core\tos_pend.c
+ tos_pend.c
+ 0
+ 0
+
+
+ 6
+ 54
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\kernel\core\tos_priority_mail_queue.c
+ tos_priority_mail_queue.c
+ 0
+ 0
+
+
+ 6
+ 55
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\kernel\core\tos_priority_message_queue.c
+ tos_priority_message_queue.c
+ 0
+ 0
+
+
+ 6
+ 56
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\kernel\core\tos_priority_queue.c
+ tos_priority_queue.c
+ 0
+ 0
+
+
+ 6
+ 57
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\kernel\core\tos_ring_queue.c
+ tos_ring_queue.c
+ 0
+ 0
+
+
+ 6
+ 58
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\kernel\core\tos_robin.c
+ tos_robin.c
+ 0
+ 0
+
+
+ 6
+ 59
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\kernel\core\tos_rwlock.c
+ tos_rwlock.c
+ 0
+ 0
+
+
+ 6
+ 60
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\kernel\core\tos_sched.c
+ tos_sched.c
+ 0
+ 0
+
+
+ 6
+ 61
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\kernel\core\tos_sem.c
+ tos_sem.c
+ 0
+ 0
+
+
+ 6
+ 62
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\kernel\core\tos_stopwatch.c
+ tos_stopwatch.c
+ 0
+ 0
+
+
+ 6
+ 63
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\kernel\core\tos_sys.c
+ tos_sys.c
+ 0
+ 0
+
+
+ 6
+ 64
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\kernel\core\tos_task.c
+ tos_task.c
+ 0
+ 0
+
+
+ 6
+ 65
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\kernel\core\tos_tick.c
+ tos_tick.c
+ 0
+ 0
+
+
+ 6
+ 66
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\kernel\core\tos_time.c
+ tos_time.c
+ 0
+ 0
+
+
+ 6
+ 67
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\kernel\core\tos_timer.c
+ tos_timer.c
+ 0
+ 0
+
+
+
+
+ tos/cmsis
+ 0
+ 0
+ 0
+ 0
+
+ 7
+ 68
+ 1
+ 0
+ 0
+ 0
+ ..\..\..\..\osal\cmsis_os\cmsis_os.c
+ cmsis_os.c
+ 0
+ 0
+
+
+
+
+ examples
+ 1
+ 0
+ 0
+ 0
+
+
+
+ TOS-CONFIG
+ 0
+ 0
+ 0
+ 0
+
+ 9
+ 69
+ 5
+ 0
+ 0
+ 0
+ ..\..\TOS_CONFIG\tos_config.h
+ tos_config.h
+ 0
+ 0
+
+
+
+
+ APP
+ 1
+ 0
+ 0
+ 0
+
+ 10
+ 70
+ 1
+ 0
+ 0
+ 0
+ .\App\app_lorawan.c
+ app_lorawan.c
+ 0
+ 0
+
+
+ 10
+ 71
+ 1
+ 0
+ 0
+ 0
+ .\App\app_system.c
+ app_system.c
+ 0
+ 0
+
+
+ 10
+ 72
+ 5
+ 0
+ 0
+ 0
+ .\App\app_conf.h
+ app_conf.h
+ 0
+ 0
+
+
+ 10
+ 73
+ 5
+ 0
+ 0
+ 0
+ .\App\Commissioning.h
+ Commissioning.h
+ 0
+ 0
+
+
+ 10
+ 74
+ 1
+ 0
+ 0
+ 0
+ .\Target\timer_wrapper.c
+ timer_wrapper.c
+ 0
+ 0
+
+
+
+
+ lorawan/pattern
+ 0
+ 0
+ 0
+ 0
+
+ 11
+ 75
+ 1
+ 0
+ 0
+ 0
+ ..\..\BSP\Middlewares\LoRaWAN\Patterns\Basic\lora.c
+ lora.c
+ 0
+ 0
+
+
+ 11
+ 76
+ 1
+ 0
+ 0
+ 0
+ ..\..\BSP\Middlewares\LoRaWAN\Patterns\Basic\lorawan_info.c
+ lorawan_info.c
+ 0
+ 0
+
+
+
+
+ lorawan/mac
+ 0
+ 0
+ 0
+ 0
+
+ 12
+ 77
+ 1
+ 0
+ 0
+ 0
+ ..\..\BSP\Middlewares\LoRaWAN\Crypto\aes.c
+ aes.c
+ 0
+ 0
+
+
+ 12
+ 78
+ 1
+ 0
+ 0
+ 0
+ ..\..\BSP\Middlewares\LoRaWAN\Crypto\cmac.c
+ cmac.c
+ 0
+ 0
+
+
+ 12
+ 79
+ 1
+ 0
+ 0
+ 0
+ ..\..\BSP\Middlewares\LoRaWAN\Crypto\soft-se.c
+ soft-se.c
+ 0
+ 0
+
+
+ 12
+ 80
+ 1
+ 0
+ 0
+ 0
+ ..\..\BSP\Middlewares\LoRaWAN\Mac\LoRaMac.c
+ LoRaMac.c
+ 0
+ 0
+
+
+ 12
+ 81
+ 1
+ 0
+ 0
+ 0
+ ..\..\BSP\Middlewares\LoRaWAN\Mac\LoRaMacAdr.c
+ LoRaMacAdr.c
+ 0
+ 0
+
+
+ 12
+ 82
+ 1
+ 0
+ 0
+ 0
+ ..\..\BSP\Middlewares\LoRaWAN\Mac\LoRaMacClassB.c
+ LoRaMacClassB.c
+ 0
+ 0
+
+
+ 12
+ 83
+ 1
+ 0
+ 0
+ 0
+ ..\..\BSP\Middlewares\LoRaWAN\Mac\LoRaMacCommands.c
+ LoRaMacCommands.c
+ 0
+ 0
+
+
+ 12
+ 84
+ 1
+ 0
+ 0
+ 0
+ ..\..\BSP\Middlewares\LoRaWAN\Mac\LoRaMacConfirmQueue.c
+ LoRaMacConfirmQueue.c
+ 0
+ 0
+
+
+ 12
+ 85
+ 1
+ 0
+ 0
+ 0
+ ..\..\BSP\Middlewares\LoRaWAN\Mac\LoRaMacCrypto.c
+ LoRaMacCrypto.c
+ 0
+ 0
+
+
+ 12
+ 86
+ 1
+ 0
+ 0
+ 0
+ ..\..\BSP\Middlewares\LoRaWAN\Mac\LoRaMacParser.c
+ LoRaMacParser.c
+ 0
+ 0
+
+
+ 12
+ 87
+ 1
+ 0
+ 0
+ 0
+ ..\..\BSP\Middlewares\LoRaWAN\Mac\LoRaMacSerializer.c
+ LoRaMacSerializer.c
+ 0
+ 0
+
+
+
+
+ lorawan/region
+ 0
+ 0
+ 0
+ 0
+
+ 13
+ 88
+ 1
+ 0
+ 0
+ 0
+ ..\..\BSP\Middlewares\LoRaWAN\Mac\region\Region.c
+ Region.c
+ 0
+ 0
+
+
+ 13
+ 89
+ 1
+ 0
+ 0
+ 0
+ ..\..\BSP\Middlewares\LoRaWAN\Mac\region\RegionAS923.c
+ RegionAS923.c
+ 0
+ 0
+
+
+ 13
+ 90
+ 1
+ 0
+ 0
+ 0
+ ..\..\BSP\Middlewares\LoRaWAN\Mac\region\RegionAU915.c
+ RegionAU915.c
+ 0
+ 0
+
+
+ 13
+ 91
+ 1
+ 0
+ 0
+ 0
+ ..\..\BSP\Middlewares\LoRaWAN\Mac\region\RegionCN470.c
+ RegionCN470.c
+ 0
+ 0
+
+
+ 13
+ 92
+ 1
+ 0
+ 0
+ 0
+ ..\..\BSP\Middlewares\LoRaWAN\Mac\region\RegionCN470ALI.c
+ RegionCN470ALI.c
+ 0
+ 0
+
+
+ 13
+ 93
+ 1
+ 0
+ 0
+ 0
+ ..\..\BSP\Middlewares\LoRaWAN\Mac\region\RegionCN779.c
+ RegionCN779.c
+ 0
+ 0
+
+
+ 13
+ 94
+ 1
+ 0
+ 0
+ 0
+ ..\..\BSP\Middlewares\LoRaWAN\Mac\region\RegionCommon.c
+ RegionCommon.c
+ 0
+ 0
+
+
+ 13
+ 95
+ 1
+ 0
+ 0
+ 0
+ ..\..\BSP\Middlewares\LoRaWAN\Mac\region\RegionEU433.c
+ RegionEU433.c
+ 0
+ 0
+
+
+ 13
+ 96
+ 1
+ 0
+ 0
+ 0
+ ..\..\BSP\Middlewares\LoRaWAN\Mac\region\RegionEU868.c
+ RegionEU868.c
+ 0
+ 0
+
+
+ 13
+ 97
+ 1
+ 0
+ 0
+ 0
+ ..\..\BSP\Middlewares\LoRaWAN\Mac\region\RegionIN865.c
+ RegionIN865.c
+ 0
+ 0
+
+
+ 13
+ 98
+ 1
+ 0
+ 0
+ 0
+ ..\..\BSP\Middlewares\LoRaWAN\Mac\region\RegionKR920.c
+ RegionKR920.c
+ 0
+ 0
+
+
+ 13
+ 99
+ 1
+ 0
+ 0
+ 0
+ ..\..\BSP\Middlewares\LoRaWAN\Mac\region\RegionRU864.c
+ RegionRU864.c
+ 0
+ 0
+
+
+ 13
+ 100
+ 1
+ 0
+ 0
+ 0
+ ..\..\BSP\Middlewares\LoRaWAN\Mac\region\RegionUS915.c
+ RegionUS915.c
+ 0
+ 0
+
+
+
+
+ lorawan/common
+ 0
+ 0
+ 0
+ 0
+
+ 14
+ 101
+ 1
+ 0
+ 0
+ 0
+ ..\..\BSP\Middlewares\LoRaWAN\Utilities\utilities.c
+ utilities.c
+ 0
+ 0
+
+
+
+
+ SubGHz_Phy/radio
+ 0
+ 0
+ 0
+ 0
+
+ 15
+ 102
+ 1
+ 0
+ 0
+ 0
+ ..\..\BSP\Middlewares\SubGHz_Phy\stm32_radio_driver\radio.c
+ radio.c
+ 0
+ 0
+
+
+ 15
+ 103
+ 1
+ 0
+ 0
+ 0
+ ..\..\BSP\Middlewares\SubGHz_Phy\stm32_radio_driver\radio_driver.c
+ radio_driver.c
+ 0
+ 0
+
+
+
+
+ Utilities
+ 0
+ 0
+ 0
+ 0
+
+ 16
+ 104
+ 1
+ 0
+ 0
+ 0
+ ..\..\BSP\Utilities\lpm\tiny_lpm\stm32_lpm.c
+ stm32_lpm.c
+ 0
+ 0
+
+
+ 16
+ 105
+ 1
+ 0
+ 0
+ 0
+ ..\..\BSP\Utilities\misc\stm32_mem.c
+ stm32_mem.c
+ 0
+ 0
+
+
+ 16
+ 106
+ 1
+ 0
+ 0
+ 0
+ ..\..\BSP\Utilities\misc\stm32_systime.c
+ stm32_systime.c
+ 0
+ 0
+
+
+
+
+ ::CMSIS
+ 0
+ 0
+ 0
+ 1
+
+
+
diff --git a/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/TencentOS_Tiny.uvprojx b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/TencentOS_Tiny.uvprojx
new file mode 100644
index 00000000..7c730423
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/TencentOS_Tiny.uvprojx
@@ -0,0 +1,1026 @@
+
+
+
+ 2.1
+
+ ### uVision Project, (C) Keil Software
+
+
+
+ TencentOS_Tiny
+ 0x4
+ ARM-ADS
+ 6140000::V6.14::ARMCLANG
+ 1
+
+
+ STM32WLE5JCIx
+ STMicroelectronics
+ Keil.STM32WLxx_DFP.1.0.2.OEM
+ http://www.keil.com/pack
+ IRAM(0x20000000,0x10000) IROM(0x08000000,0x40000) CPUTYPE("Cortex-M4") FPU2 CLOCK(12000000) ELITTLE
+
+
+ UL2CM3(-S0 -C0 -P0 -FD20000000 -FC1000 -FN1 -FF0STM32WLxx_CM4 -FS08000000 -FL040000 -FP0($$Device:STM32WLE5JCIx$Drivers\CMSIS\Flash\STM32WLxx_CM4.FLM))
+ 0
+ $$Device:STM32WLE5JCIx$Drivers\CMSIS\Device\ST\STM32WLxx\Include\stm32wlxx.h
+
+
+
+
+
+
+
+
+
+ $$Device:STM32WLE5JCIx$Drivers\CMSIS\SVD\STM32WL5x_CM4.svd
+ 0
+ 0
+
+
+
+
+
+
+ 0
+ 0
+ 0
+ 0
+ 1
+
+ .\Obj\
+ TencentOS_Tiny
+ 1
+ 0
+ 0
+ 1
+ 0
+ .\List\
+ 1
+ 0
+ 0
+
+ 0
+ 0
+
+
+ 0
+ 0
+ 0
+ 0
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+ 0
+ 0
+
+
+ 0
+ 0
+
+
+ 0
+ 0
+ 0
+ 0
+
+ 0
+
+
+
+ 0
+ 0
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 0
+ 3
+
+
+ 0
+
+
+ SARMCM3.DLL
+ -REMAP -MPU
+ DCM.DLL
+ -pCM4
+ SARMCM3.DLL
+ -MPU
+ TCM.DLL
+ -pCM4
+
+
+
+ 1
+ 0
+ 0
+ 0
+ 16
+
+
+
+
+ 1
+ 0
+ 0
+ 1
+ 1
+ 4107
+
+ 1
+ BIN\UL2CM3.DLL
+
+
+
+
+
+ 0
+
+
+
+ 0
+ 1
+ 1
+ 1
+ 1
+ 1
+ 1
+ 1
+ 0
+ 1
+ 1
+ 0
+ 1
+ 1
+ 0
+ 0
+ 1
+ 1
+ 1
+ 1
+ 1
+ 1
+ 1
+ 1
+ 1
+ 0
+ 0
+ "Cortex-M4"
+
+ 0
+ 0
+ 0
+ 1
+ 1
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 0
+ 8
+ 1
+ 0
+ 0
+ 0
+ 3
+ 3
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 0
+ 1
+ 0
+
+
+ 0
+ 0x0
+ 0x0
+
+
+ 0
+ 0x0
+ 0x0
+
+
+ 0
+ 0x0
+ 0x0
+
+
+ 0
+ 0x0
+ 0x0
+
+
+ 0
+ 0x0
+ 0x0
+
+
+ 0
+ 0x0
+ 0x0
+
+
+ 0
+ 0x20000000
+ 0x10000
+
+
+ 1
+ 0x8000000
+ 0x40000
+
+
+ 0
+ 0x0
+ 0x0
+
+
+ 1
+ 0x0
+ 0x0
+
+
+ 1
+ 0x0
+ 0x0
+
+
+ 1
+ 0x0
+ 0x0
+
+
+ 1
+ 0x8000000
+ 0x40000
+
+
+ 1
+ 0x0
+ 0x0
+
+
+ 0
+ 0x0
+ 0x0
+
+
+ 0
+ 0x0
+ 0x0
+
+
+ 0
+ 0x0
+ 0x0
+
+
+ 0
+ 0x20000000
+ 0x10000
+
+
+ 0
+ 0x0
+ 0x0
+
+
+
+
+
+ 1
+ 7
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 0
+ 0
+ 3
+ 0
+ 0
+ 1
+ 0
+ 0
+ 3
+ 3
+ 1
+ 1
+ 0
+ 0
+ 0
+
+
+ USE_HAL_DRIVER,STM32WLE5xx,CORE_CM4
+
+ ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Inc;..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Inc\Legacy;..\..\..\..\platform\vendor_bsp\st\CMSIS\Device\ST\STM32WLxx\Include;..\..\..\..\platform\vendor_bsp\st\CMSIS\Include;..\..\..\..\arch\arm\arm-v7m\common\include;..\..\..\..\arch\arm\arm-v7m\cortex-m4\armcc;..\..\..\..\kernel\core\include;..\..\..\..\kernel\pm\include;..\..\..\..\osal\cmsis_os;..\..\TOS_CONFIG;..\..\BSP\Middlewares\LoRaWAN\Conf;..\..\BSP\Middlewares\LoRaWAN\Crypto;..\..\BSP\Middlewares\LoRaWAN\Mac;..\..\BSP\Middlewares\LoRaWAN\Patterns\Basic;..\..\BSP\Middlewares\LoRaWAN\Utilities;..\..\BSP\Middlewares\SubGHz_Phy;..\..\BSP\Middlewares\SubGHz_Phy\stm32_radio_driver;..\..\BSP\Inc;.\App;.\Target;..\..\BSP\Utilities\lpm\tiny_lpm;..\..\BSP\Utilities\misc
+
+
+
+ 1
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 4
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0x08000000
+ 0x20000000
+
+
+
+
+ --diag_suppress L6314W
+
+
+
+
+
+
+
+ Application/MDK-ARM
+
+
+ startup_stm32wle5xx.s
+ 2
+ .\startup_stm32wle5xx.s
+
+
+
+
+ Application/User
+
+
+ adc.c
+ 1
+ ..\..\BSP\Src\adc.c
+
+
+ adc_if.c
+ 1
+ ..\..\BSP\Src\adc_if.c
+
+
+ dma.c
+ 1
+ ..\..\BSP\Src\dma.c
+
+
+ gpio.c
+ 1
+ ..\..\BSP\Src\gpio.c
+
+
+ main.c
+ 1
+ ..\..\BSP\Src\main.c
+
+
+ mcu_init.c
+ 1
+ ..\..\BSP\Src\mcu_init.c
+
+
+ rtc.c
+ 1
+ ..\..\BSP\Src\rtc.c
+
+
+ rtc_if.c
+ 1
+ ..\..\BSP\Src\rtc_if.c
+
+
+ stm32_lpm_if.c
+ 1
+ ..\..\BSP\Src\stm32_lpm_if.c
+
+
+ stm32wlxx_hal_msp.c
+ 1
+ ..\..\BSP\Src\stm32wlxx_hal_msp.c
+
+
+ stm32wlxx_it.c
+ 1
+ ..\..\BSP\Src\stm32wlxx_it.c
+
+
+ usart.c
+ 1
+ ..\..\BSP\Src\usart.c
+
+
+ usart_if.c
+ 1
+ ..\..\BSP\Src\usart_if.c
+
+
+
+
+ Drivers/STM32WLxx_HAL_Driver
+
+
+ stm32wlxx_hal.c
+ 1
+ ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal.c
+
+
+ stm32wlxx_hal_cortex.c
+ 1
+ ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_cortex.c
+
+
+ stm32wlxx_hal_dma.c
+ 1
+ ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_dma.c
+
+
+ stm32wlxx_hal_dma_ex.c
+ 1
+ ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_dma_ex.c
+
+
+ stm32wlxx_hal_exti.c
+ 1
+ ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_exti.c
+
+
+ stm32wlxx_hal_flash.c
+ 1
+ ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_flash.c
+
+
+ stm32wlxx_hal_flash_ex.c
+ 1
+ ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_flash_ex.c
+
+
+ stm32wlxx_hal_gpio.c
+ 1
+ ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_gpio.c
+
+
+ stm32wlxx_hal_pwr.c
+ 1
+ ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_pwr.c
+
+
+ stm32wlxx_hal_pwr_ex.c
+ 1
+ ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_pwr_ex.c
+
+
+ stm32wlxx_hal_rcc.c
+ 1
+ ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_rcc.c
+
+
+ stm32wlxx_hal_rcc_ex.c
+ 1
+ ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_rcc_ex.c
+
+
+ stm32wlxx_hal_tim.c
+ 1
+ ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_tim.c
+
+
+ stm32wlxx_hal_tim_ex.c
+ 1
+ ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_tim_ex.c
+
+
+ stm32wlxx_hal_uart.c
+ 1
+ ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_uart.c
+
+
+ stm32wlxx_hal_uart_ex.c
+ 1
+ ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_uart_ex.c
+
+
+ stm32wlxx_hal_adc.c
+ 1
+ ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_adc.c
+
+
+ stm32wlxx_hal_adc_ex.c
+ 1
+ ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_adc_ex.c
+
+
+ stm32wlxx_hal_rtc.c
+ 1
+ ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_rtc.c
+
+
+ stm32wlxx_hal_rtc_ex.c
+ 1
+ ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_rtc_ex.c
+
+
+ stm32wlxx_hal_subghz.c
+ 1
+ ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_subghz.c
+
+
+
+
+ Drivers/CMSIS
+
+
+ system_stm32wlxx.c
+ 1
+ ..\..\BSP\Src\system_stm32wlxx.c
+
+
+
+
+ tos/arch
+
+
+ tos_cpu.c
+ 1
+ ..\..\..\..\arch\arm\arm-v7m\common\tos_cpu.c
+
+
+ port_c.c
+ 1
+ ..\..\..\..\arch\arm\arm-v7m\cortex-m4\armcc\port_c.c
+
+
+ port_s.S
+ 2
+ ..\..\..\..\arch\arm\arm-v7m\cortex-m4\armcc\port_s.S
+
+
+
+
+ tos/kernel
+
+
+ tos_barrier.c
+ 1
+ ..\..\..\..\kernel\core\tos_barrier.c
+
+
+ tos_binary_heap.c
+ 1
+ ..\..\..\..\kernel\core\tos_binary_heap.c
+
+
+ tos_bitmap.c
+ 1
+ ..\..\..\..\kernel\core\tos_bitmap.c
+
+
+ tos_char_fifo.c
+ 1
+ ..\..\..\..\kernel\core\tos_char_fifo.c
+
+
+ tos_completion.c
+ 1
+ ..\..\..\..\kernel\core\tos_completion.c
+
+
+ tos_countdownlatch.c
+ 1
+ ..\..\..\..\kernel\core\tos_countdownlatch.c
+
+
+ tos_event.c
+ 1
+ ..\..\..\..\kernel\core\tos_event.c
+
+
+ tos_global.c
+ 1
+ ..\..\..\..\kernel\core\tos_global.c
+
+
+ tos_mail_queue.c
+ 1
+ ..\..\..\..\kernel\core\tos_mail_queue.c
+
+
+ tos_message_queue.c
+ 1
+ ..\..\..\..\kernel\core\tos_message_queue.c
+
+
+ tos_mmblk.c
+ 1
+ ..\..\..\..\kernel\core\tos_mmblk.c
+
+
+ tos_mmheap.c
+ 1
+ ..\..\..\..\kernel\core\tos_mmheap.c
+
+
+ tos_mutex.c
+ 1
+ ..\..\..\..\kernel\core\tos_mutex.c
+
+
+ tos_pend.c
+ 1
+ ..\..\..\..\kernel\core\tos_pend.c
+
+
+ tos_priority_mail_queue.c
+ 1
+ ..\..\..\..\kernel\core\tos_priority_mail_queue.c
+
+
+ tos_priority_message_queue.c
+ 1
+ ..\..\..\..\kernel\core\tos_priority_message_queue.c
+
+
+ tos_priority_queue.c
+ 1
+ ..\..\..\..\kernel\core\tos_priority_queue.c
+
+
+ tos_ring_queue.c
+ 1
+ ..\..\..\..\kernel\core\tos_ring_queue.c
+
+
+ tos_robin.c
+ 1
+ ..\..\..\..\kernel\core\tos_robin.c
+
+
+ tos_rwlock.c
+ 1
+ ..\..\..\..\kernel\core\tos_rwlock.c
+
+
+ tos_sched.c
+ 1
+ ..\..\..\..\kernel\core\tos_sched.c
+
+
+ tos_sem.c
+ 1
+ ..\..\..\..\kernel\core\tos_sem.c
+
+
+ tos_stopwatch.c
+ 1
+ ..\..\..\..\kernel\core\tos_stopwatch.c
+
+
+ tos_sys.c
+ 1
+ ..\..\..\..\kernel\core\tos_sys.c
+
+
+ tos_task.c
+ 1
+ ..\..\..\..\kernel\core\tos_task.c
+
+
+ tos_tick.c
+ 1
+ ..\..\..\..\kernel\core\tos_tick.c
+
+
+ tos_time.c
+ 1
+ ..\..\..\..\kernel\core\tos_time.c
+
+
+ tos_timer.c
+ 1
+ ..\..\..\..\kernel\core\tos_timer.c
+
+
+
+
+ tos/cmsis
+
+
+ cmsis_os.c
+ 1
+ ..\..\..\..\osal\cmsis_os\cmsis_os.c
+
+
+
+
+ examples
+
+
+ TOS-CONFIG
+
+
+ tos_config.h
+ 5
+ ..\..\TOS_CONFIG\tos_config.h
+
+
+
+
+ APP
+
+
+ app_lorawan.c
+ 1
+ .\App\app_lorawan.c
+
+
+ app_system.c
+ 1
+ .\App\app_system.c
+
+
+ app_conf.h
+ 5
+ .\App\app_conf.h
+
+
+ Commissioning.h
+ 5
+ .\App\Commissioning.h
+
+
+ timer_wrapper.c
+ 1
+ .\Target\timer_wrapper.c
+
+
+
+
+ lorawan/pattern
+
+
+ lora.c
+ 1
+ ..\..\BSP\Middlewares\LoRaWAN\Patterns\Basic\lora.c
+
+
+ lorawan_info.c
+ 1
+ ..\..\BSP\Middlewares\LoRaWAN\Patterns\Basic\lorawan_info.c
+
+
+
+
+ lorawan/mac
+
+
+ aes.c
+ 1
+ ..\..\BSP\Middlewares\LoRaWAN\Crypto\aes.c
+
+
+ cmac.c
+ 1
+ ..\..\BSP\Middlewares\LoRaWAN\Crypto\cmac.c
+
+
+ soft-se.c
+ 1
+ ..\..\BSP\Middlewares\LoRaWAN\Crypto\soft-se.c
+
+
+ LoRaMac.c
+ 1
+ ..\..\BSP\Middlewares\LoRaWAN\Mac\LoRaMac.c
+
+
+ LoRaMacAdr.c
+ 1
+ ..\..\BSP\Middlewares\LoRaWAN\Mac\LoRaMacAdr.c
+
+
+ LoRaMacClassB.c
+ 1
+ ..\..\BSP\Middlewares\LoRaWAN\Mac\LoRaMacClassB.c
+
+
+ LoRaMacCommands.c
+ 1
+ ..\..\BSP\Middlewares\LoRaWAN\Mac\LoRaMacCommands.c
+
+
+ LoRaMacConfirmQueue.c
+ 1
+ ..\..\BSP\Middlewares\LoRaWAN\Mac\LoRaMacConfirmQueue.c
+
+
+ LoRaMacCrypto.c
+ 1
+ ..\..\BSP\Middlewares\LoRaWAN\Mac\LoRaMacCrypto.c
+
+
+ LoRaMacParser.c
+ 1
+ ..\..\BSP\Middlewares\LoRaWAN\Mac\LoRaMacParser.c
+
+
+ LoRaMacSerializer.c
+ 1
+ ..\..\BSP\Middlewares\LoRaWAN\Mac\LoRaMacSerializer.c
+
+
+
+
+ lorawan/region
+
+
+ Region.c
+ 1
+ ..\..\BSP\Middlewares\LoRaWAN\Mac\region\Region.c
+
+
+ RegionAS923.c
+ 1
+ ..\..\BSP\Middlewares\LoRaWAN\Mac\region\RegionAS923.c
+
+
+ RegionAU915.c
+ 1
+ ..\..\BSP\Middlewares\LoRaWAN\Mac\region\RegionAU915.c
+
+
+ RegionCN470.c
+ 1
+ ..\..\BSP\Middlewares\LoRaWAN\Mac\region\RegionCN470.c
+
+
+ RegionCN470ALI.c
+ 1
+ ..\..\BSP\Middlewares\LoRaWAN\Mac\region\RegionCN470ALI.c
+
+
+ RegionCN779.c
+ 1
+ ..\..\BSP\Middlewares\LoRaWAN\Mac\region\RegionCN779.c
+
+
+ RegionCommon.c
+ 1
+ ..\..\BSP\Middlewares\LoRaWAN\Mac\region\RegionCommon.c
+
+
+ RegionEU433.c
+ 1
+ ..\..\BSP\Middlewares\LoRaWAN\Mac\region\RegionEU433.c
+
+
+ RegionEU868.c
+ 1
+ ..\..\BSP\Middlewares\LoRaWAN\Mac\region\RegionEU868.c
+
+
+ RegionIN865.c
+ 1
+ ..\..\BSP\Middlewares\LoRaWAN\Mac\region\RegionIN865.c
+
+
+ RegionKR920.c
+ 1
+ ..\..\BSP\Middlewares\LoRaWAN\Mac\region\RegionKR920.c
+
+
+ RegionRU864.c
+ 1
+ ..\..\BSP\Middlewares\LoRaWAN\Mac\region\RegionRU864.c
+
+
+ RegionUS915.c
+ 1
+ ..\..\BSP\Middlewares\LoRaWAN\Mac\region\RegionUS915.c
+
+
+
+
+ lorawan/common
+
+
+ utilities.c
+ 1
+ ..\..\BSP\Middlewares\LoRaWAN\Utilities\utilities.c
+
+
+
+
+ SubGHz_Phy/radio
+
+
+ radio.c
+ 1
+ ..\..\BSP\Middlewares\SubGHz_Phy\stm32_radio_driver\radio.c
+
+
+ radio_driver.c
+ 1
+ ..\..\BSP\Middlewares\SubGHz_Phy\stm32_radio_driver\radio_driver.c
+
+
+
+
+ Utilities
+
+
+ stm32_lpm.c
+ 1
+ ..\..\BSP\Utilities\lpm\tiny_lpm\stm32_lpm.c
+
+
+ stm32_mem.c
+ 1
+ ..\..\BSP\Utilities\misc\stm32_mem.c
+
+
+ stm32_systime.c
+ 1
+ ..\..\BSP\Utilities\misc\stm32_systime.c
+
+
+
+
+ ::CMSIS
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <Project Info>
+
+
+
+
+
+ 0
+ 1
+
+
+
+
+
diff --git a/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/startup_stm32wle5xx.s b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/startup_stm32wle5xx.s
new file mode 100644
index 00000000..7f965b48
--- /dev/null
+++ b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/startup_stm32wle5xx.s
@@ -0,0 +1,359 @@
+;******************************************************************************
+;* File Name : startup_stm32wle5xx_cm4.s
+;* Author : MCD Application Team
+;* Description : STM32WLE5xx devices vector table for MDK-ARM toolchain.
+;* This module performs:
+;* - Set the initial SP
+;* - Set the initial PC == Reset_Handler
+;* - Set the vector table entries with the exceptions ISR address
+;* - Branches to __main in the C library (which eventually
+;* calls main()).
+;* After Reset the CortexM4 processor is in Thread mode,
+;* priority is Privileged, and the Stack is set to Main.
+;* <<< Use Configuration Wizard in Context Menu >>>
+;******************************************************************************
+;* @attention
+;*
+;* Copyright (c) 2019 STMicroelectronics. All rights reserved.
+;*
+;* This software component is licensed by ST under BSD 3-Clause license,
+;* the "License"; You may not use this file except in compliance with the
+;* License. You may obtain a copy of the License at:
+;* opensource.org/licenses/BSD-3-Clause
+;*
+;******************************************************************************
+
+; Amount of memory (in bytes) allocated for Stack
+; Tailor this value to your application needs
+; Stack Configuration
+; Stack Size (in Bytes) <0x0-0xFFFFFFFF:8>
+;
+
+Stack_Size EQU 0x00000800
+
+ AREA STACK, NOINIT, READWRITE, ALIGN=3
+Stack_Mem SPACE Stack_Size
+__initial_sp
+
+
+; Heap Configuration
+; Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
+;
+
+Heap_Size EQU 0x00000200
+
+ AREA HEAP, NOINIT, READWRITE, ALIGN=3
+__heap_base
+Heap_Mem SPACE Heap_Size
+__heap_limit
+
+ PRESERVE8
+ THUMB
+
+
+; Vector Table Mapped to Address 0 at Reset
+ AREA RESET, DATA, READONLY
+ EXPORT __Vectors
+ EXPORT __Vectors_End
+ EXPORT __Vectors_Size
+
+__Vectors DCD __initial_sp ; Top of Stack
+ DCD Reset_Handler ; Reset Handler
+ DCD NMI_Handler ; NMI Handler
+ DCD HardFault_Handler ; Hard Fault Handler
+ DCD MemManage_Handler ; MPU Fault Handler
+ DCD BusFault_Handler ; Bus Fault Handler
+ DCD UsageFault_Handler ; Usage Fault Handler
+ DCD 0 ; Reserved
+ DCD 0 ; Reserved
+ DCD 0 ; Reserved
+ DCD 0 ; Reserved
+ DCD SVC_Handler ; SVCall Handler
+ DCD DebugMon_Handler ; Debug Monitor Handler
+ DCD 0 ; Reserved
+ DCD PendSV_Handler ; PendSV Handler
+ DCD SysTick_Handler ; SysTick Handler
+
+ ; External Interrupts
+ DCD WWDG_IRQHandler ; Window WatchDog
+ DCD PVD_PVM_IRQHandler ; PVD and PVM detector
+ DCD TAMP_STAMP_LSECSS_SSRU_IRQHandler ; RTC Tamper, RTC TimeStamp, LSECSS and RTC SSR Underflow Interrupts
+ DCD RTC_WKUP_IRQHandler ; RTC Wakeup Interrupt
+ DCD FLASH_IRQHandler ; FLASH global Interrupt
+ DCD RCC_IRQHandler ; RCC Interrupt
+ DCD EXTI0_IRQHandler ; EXTI Line 0 Interrupt
+ DCD EXTI1_IRQHandler ; EXTI Line 1 Interrupt
+ DCD EXTI2_IRQHandler ; EXTI Line 2 Interrupt
+ DCD EXTI3_IRQHandler ; EXTI Line 3 Interrup
+ DCD EXTI4_IRQHandler ; EXTI Line 4 Interrupt
+ DCD DMA1_Channel1_IRQHandler ; DMA1 Channel 1 Interrupt
+ DCD DMA1_Channel2_IRQHandler ; DMA1 Channel 2 Interrupt
+ DCD DMA1_Channel3_IRQHandler ; DMA1 Channel 3 Interrupt
+ DCD DMA1_Channel4_IRQHandler ; DMA1 Channel 4 Interrupt
+ DCD DMA1_Channel5_IRQHandler ; DMA1 Channel 5 Interrupt
+ DCD DMA1_Channel6_IRQHandler ; DMA1 Channel 6 Interrupt
+ DCD DMA1_Channel7_IRQHandler ; DMA1 Channel 7 Interrupt
+ DCD ADC1_IRQHandler ; ADC1 Interrupt
+ DCD DAC_IRQHandler ; DAC Interrupt
+ DCD 0 ; Reserved
+ DCD COMP_IRQHandler ; COMP1 and COMP2 Interrupts
+ DCD EXTI9_5_IRQHandler ; EXTI Lines [9:5] Interrupt
+ DCD TIM1_BRK_IRQHandler ; TIM1 Break Interrupt
+ DCD TIM1_UP_IRQHandler ; TIM1 Update Interrupts
+ DCD TIM1_TRG_COM_IRQHandler ; TIM1 Trigger and Communication Interrupts
+ DCD TIM1_CC_IRQHandler ; TIM1 Capture Compare Interrupt
+ DCD TIM2_IRQHandler ; TIM2 Global Interrupt
+ DCD TIM16_IRQHandler ; TIM16 Global Interrupt
+ DCD TIM17_IRQHandler ; TIM17 Global Interrupt
+ DCD I2C1_EV_IRQHandler ; I2C1 Event Interrupt
+ DCD I2C1_ER_IRQHandler ; I2C1 Error Interrupt
+ DCD I2C2_EV_IRQHandler ; I2C2 Event Interrupt
+ DCD I2C2_ER_IRQHandler ; I2C2 Error Interrupt
+ DCD SPI1_IRQHandler ; SPI1 Interrupt
+ DCD SPI2_IRQHandler ; SPI2 Interrupt
+ DCD USART1_IRQHandler ; USART1 Interrupt
+ DCD USART2_IRQHandler ; USART2 Interrupt
+ DCD LPUART1_IRQHandler ; LPUART1 Interrupt
+ DCD LPTIM1_IRQHandler ; LPTIM1 Interrupt
+ DCD LPTIM2_IRQHandler ; LPTIM2 Interrupt
+ DCD EXTI15_10_IRQHandler ; EXTI Lines1[15:10 ]Interrupts
+ DCD RTC_Alarm_IRQHandler ; RTC Alarms (A and B) Interrupt
+ DCD LPTIM3_IRQHandler ; LPTIM3 Interrupt
+ DCD SUBGHZSPI_IRQHandler ; SUBGHZSPI Interrupt
+ DCD 0 ; Reserved
+ DCD 0 ; Reserved
+ DCD HSEM_IRQHandler ; HSEM0 Interrupt
+ DCD I2C3_EV_IRQHandler ; I2C3 Event Interrupt
+ DCD I2C3_ER_IRQHandler ; I2C3 Error Interrupt
+ DCD Radio_IRQHandler ; Radio Interrupt
+ DCD AES_IRQHandler ; AES Interrupt
+ DCD RNG_IRQHandler ; RNG1 Interrupt
+ DCD PKA_IRQHandler ; PKA Interrupt
+ DCD DMA2_Channel1_IRQHandler ; DMA2 Channel 1 Interrupt
+ DCD DMA2_Channel2_IRQHandler ; DMA2 Channel 2 Interrupt
+ DCD DMA2_Channel3_IRQHandler ; DMA2 Channel 3 Interrupt
+ DCD DMA2_Channel4_IRQHandler ; DMA2 Channel 4 Interrupt
+ DCD DMA2_Channel5_IRQHandler ; DMA2 Channel 5 Interrupt
+ DCD DMA2_Channel6_IRQHandler ; DMA2 Channel 6 Interrupt
+ DCD DMA2_Channel7_IRQHandler ; DMA2 Channel 7 Interrupt
+ DCD DMAMUX1_OVR_IRQHandler ; DMAMUX overrun Interrupt
+
+__Vectors_End
+
+__Vectors_Size EQU __Vectors_End - __Vectors
+
+ AREA |.text|, CODE, READONLY
+
+; Reset handler
+Reset_Handler PROC
+ EXPORT Reset_Handler [WEAK]
+ IMPORT SystemInit
+ IMPORT __main
+
+ LDR R0, =SystemInit
+ BLX R0
+ LDR R0, =__main
+ BX R0
+ ENDP
+
+; Dummy Exception Handlers (infinite loops which can be modified)
+
+NMI_Handler PROC
+ EXPORT NMI_Handler [WEAK]
+ B .
+ ENDP
+HardFault_Handler\
+ PROC
+ EXPORT HardFault_Handler [WEAK]
+ B .
+ ENDP
+MemManage_Handler\
+ PROC
+ EXPORT MemManage_Handler [WEAK]
+ B .
+ ENDP
+BusFault_Handler\
+ PROC
+ EXPORT BusFault_Handler [WEAK]
+ B .
+ ENDP
+UsageFault_Handler\
+ PROC
+ EXPORT UsageFault_Handler [WEAK]
+ B .
+ ENDP
+SVC_Handler PROC
+ EXPORT SVC_Handler [WEAK]
+ B .
+ ENDP
+DebugMon_Handler\
+ PROC
+ EXPORT DebugMon_Handler [WEAK]
+ B .
+ ENDP
+PendSV_Handler PROC
+ EXPORT PendSV_Handler [WEAK]
+ B .
+ ENDP
+SysTick_Handler PROC
+ EXPORT SysTick_Handler [WEAK]
+ B .
+ ENDP
+
+Default_Handler PROC
+
+ EXPORT WWDG_IRQHandler [WEAK]
+ EXPORT PVD_PVM_IRQHandler [WEAK]
+ EXPORT TAMP_STAMP_LSECSS_SSRU_IRQHandler [WEAK]
+ EXPORT RTC_WKUP_IRQHandler [WEAK]
+ EXPORT FLASH_IRQHandler [WEAK]
+ EXPORT RCC_IRQHandler [WEAK]
+ EXPORT EXTI0_IRQHandler [WEAK]
+ EXPORT EXTI1_IRQHandler [WEAK]
+ EXPORT EXTI2_IRQHandler [WEAK]
+ EXPORT EXTI3_IRQHandler [WEAK]
+ EXPORT EXTI4_IRQHandler [WEAK]
+ EXPORT DMA1_Channel1_IRQHandler [WEAK]
+ EXPORT DMA1_Channel2_IRQHandler [WEAK]
+ EXPORT DMA1_Channel3_IRQHandler [WEAK]
+ EXPORT DMA1_Channel4_IRQHandler [WEAK]
+ EXPORT DMA1_Channel5_IRQHandler [WEAK]
+ EXPORT DMA1_Channel6_IRQHandler [WEAK]
+ EXPORT DMA1_Channel7_IRQHandler [WEAK]
+ EXPORT ADC1_IRQHandler [WEAK]
+ EXPORT DAC_IRQHandler [WEAK]
+ EXPORT COMP_IRQHandler [WEAK]
+ EXPORT EXTI9_5_IRQHandler [WEAK]
+ EXPORT TIM1_BRK_IRQHandler [WEAK]
+ EXPORT TIM1_UP_IRQHandler [WEAK]
+ EXPORT TIM1_TRG_COM_IRQHandler [WEAK]
+ EXPORT TIM1_CC_IRQHandler [WEAK]
+ EXPORT TIM2_IRQHandler [WEAK]
+ EXPORT TIM16_IRQHandler [WEAK]
+ EXPORT TIM17_IRQHandler [WEAK]
+ EXPORT I2C1_EV_IRQHandler [WEAK]
+ EXPORT I2C1_ER_IRQHandler [WEAK]
+ EXPORT I2C2_EV_IRQHandler [WEAK]
+ EXPORT I2C2_ER_IRQHandler [WEAK]
+ EXPORT SPI1_IRQHandler [WEAK]
+ EXPORT SPI2_IRQHandler [WEAK]
+ EXPORT USART1_IRQHandler [WEAK]
+ EXPORT USART2_IRQHandler [WEAK]
+ EXPORT LPUART1_IRQHandler [WEAK]
+ EXPORT LPTIM1_IRQHandler [WEAK]
+ EXPORT LPTIM2_IRQHandler [WEAK]
+ EXPORT EXTI15_10_IRQHandler [WEAK]
+ EXPORT RTC_Alarm_IRQHandler [WEAK]
+ EXPORT LPTIM3_IRQHandler [WEAK]
+ EXPORT SUBGHZSPI_IRQHandler [WEAK]
+ EXPORT HSEM_IRQHandler [WEAK]
+ EXPORT I2C3_EV_IRQHandler [WEAK]
+ EXPORT I2C3_ER_IRQHandler [WEAK]
+ EXPORT Radio_IRQHandler [WEAK]
+ EXPORT AES_IRQHandler [WEAK]
+ EXPORT RNG_IRQHandler [WEAK]
+ EXPORT PKA_IRQHandler [WEAK]
+ EXPORT DMA2_Channel1_IRQHandler [WEAK]
+ EXPORT DMA2_Channel2_IRQHandler [WEAK]
+ EXPORT DMA2_Channel3_IRQHandler [WEAK]
+ EXPORT DMA2_Channel4_IRQHandler [WEAK]
+ EXPORT DMA2_Channel5_IRQHandler [WEAK]
+ EXPORT DMA2_Channel6_IRQHandler [WEAK]
+ EXPORT DMA2_Channel7_IRQHandler [WEAK]
+ EXPORT DMAMUX1_OVR_IRQHandler [WEAK]
+
+WWDG_IRQHandler
+PVD_PVM_IRQHandler
+TAMP_STAMP_LSECSS_SSRU_IRQHandler
+RTC_WKUP_IRQHandler
+FLASH_IRQHandler
+RCC_IRQHandler
+EXTI0_IRQHandler
+EXTI1_IRQHandler
+EXTI2_IRQHandler
+EXTI3_IRQHandler
+EXTI4_IRQHandler
+DMA1_Channel1_IRQHandler
+DMA1_Channel2_IRQHandler
+DMA1_Channel3_IRQHandler
+DMA1_Channel4_IRQHandler
+DMA1_Channel5_IRQHandler
+DMA1_Channel6_IRQHandler
+DMA1_Channel7_IRQHandler
+ADC1_IRQHandler
+DAC_IRQHandler
+COMP_IRQHandler
+EXTI9_5_IRQHandler
+TIM1_BRK_IRQHandler
+TIM1_UP_IRQHandler
+TIM1_TRG_COM_IRQHandler
+TIM1_CC_IRQHandler
+TIM2_IRQHandler
+TIM16_IRQHandler
+TIM17_IRQHandler
+I2C1_EV_IRQHandler
+I2C1_ER_IRQHandler
+I2C2_EV_IRQHandler
+I2C2_ER_IRQHandler
+SPI1_IRQHandler
+SPI2_IRQHandler
+USART1_IRQHandler
+USART2_IRQHandler
+LPUART1_IRQHandler
+LPTIM1_IRQHandler
+LPTIM2_IRQHandler
+EXTI15_10_IRQHandler
+RTC_Alarm_IRQHandler
+LPTIM3_IRQHandler
+SUBGHZSPI_IRQHandler
+HSEM_IRQHandler
+I2C3_EV_IRQHandler
+I2C3_ER_IRQHandler
+Radio_IRQHandler
+AES_IRQHandler
+RNG_IRQHandler
+PKA_IRQHandler
+DMA2_Channel1_IRQHandler
+DMA2_Channel2_IRQHandler
+DMA2_Channel3_IRQHandler
+DMA2_Channel4_IRQHandler
+DMA2_Channel5_IRQHandler
+DMA2_Channel6_IRQHandler
+DMA2_Channel7_IRQHandler
+DMAMUX1_OVR_IRQHandler
+
+ B .
+
+ ENDP
+
+ ALIGN
+
+;*******************************************************************************
+; User Stack and Heap initialization
+;*******************************************************************************
+ IF :DEF:__MICROLIB
+
+ EXPORT __initial_sp
+ EXPORT __heap_base
+ EXPORT __heap_limit
+
+ ELSE
+
+ IMPORT __use_two_region_memory
+ EXPORT __user_initial_stackheap
+
+__user_initial_stackheap
+
+ LDR R0, = Heap_Mem
+ LDR R1, =(Stack_Mem + Stack_Size)
+ LDR R2, = (Heap_Mem + Heap_Size)
+ LDR R3, = Stack_Mem
+ BX LR
+
+ ALIGN
+
+ ENDIF
+
+ END
+
+;************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE*****