From 8b876c7052a7b76065bf3b865898c69412973bad Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Wed, 14 Sep 2022 14:29:19 +0200 Subject: [PATCH 01/73] astyle: Add configuration This adds the .astylerc configuration in the project root, so you can also run astyle locally. Unfortunately, astyle does not pick it up automatically and always needs file patterns specified on the commandline, so to run astyle you need something like: astyle --project=.astylerc --recursive '*.c' '*.h' '*.ino' (you can also set `ARTISTIC_STYLE_PROJECT_OPTIONS=.astylerc` in your environment and omit `--project`) --- .astylerc | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 .astylerc diff --git a/.astylerc b/.astylerc new file mode 100644 index 0000000..2e4e63c --- /dev/null +++ b/.astylerc @@ -0,0 +1,67 @@ +# STM32duino code style definition file for astyle + +# Don't create backup files, let git handle it +suffix=none + +# K&R style +style=kr + +# 1 TBS addition to k&r, add braces to one liners +# Use -j as it was changed in astyle from brackets to braces, this way it is compatible with older astyle versions +-j + +# 2 spaces, convert tabs to spaces +indent=spaces=2 +convert-tabs +lineend=linux + +# Indent switches and cases +indent-classes +indent-switches +indent-cases +indent-col1-comments +indent-preproc-block + +# Remove spaces in and around parentheses +unpad-paren + +# Insert a space after if, while, for, and around operators +pad-header +pad-oper + +# Pointer/reference operators go next to the name (on the right) +align-pointer=name +align-reference=name + +# Attach { for classes and namespaces +attach-namespaces +attach-classes + +# Extend longer lines, define maximum 120 value. This results in aligned code, +# otherwise the lines are broken and not consistent +max-continuation-indent=120 + +# if you like one-liners, keep them +keep-one-line-statements + +#remove-comment-prefix + +# Do not error our when a directory is excluded that does not contain +# any files to process +ignore-exclude-errors + +# Exclude some files +exclude=.git +exclude=docs +exclude=api-docs +# These files are taken verbatim from STM32CubeWL, so minimize changes +# to them +exclude=src/STM32CubeWL +exclude=src/BSP/timer_if.c +exclude=src/BSP/timer_if.h +exclude=src/BSP/rtc.c +exclude=src/BSP/rtc.h + +# Allow oneline blocks, to allow very small functions to be defined +# on a single line in header files. +keep-one-line-blocks From 601b44408491ab538f5cb0c7cc5b3098d3a5d999 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 23 Jun 2022 23:35:43 +0200 Subject: [PATCH 02/73] Add BSP code These are various pieces of glue that provide a "board support package" to the STM32CubeWL codebase. These mostly contain configuration for the STM32CubeWL code or initialization for HAL components used by STM32CubeWL or other parts of the code. Note that these files reference some STM32CubeWL files that are not yet added to the repo yet, these will be added later (but ordering things like this makes it easier to add those files later and update their references to these glue files directly). Most of these files are based on various template files from the LoRaWAN or SubGhz middlewares in STM32CubeWL and licensed under the BSD-3-Clause license. The origin of the files added in this commit is as follows (original paths reference to files from STM32CubeWL). - src/Glue/radio_conf.h Based on Middlewares/Third_Party/SubGHz_Phy/Conf/radio_conf_template.h - src/Glue/mw_log_conf.h Based on Middlewares/Third_Party/SubGHz_Phy/Conf/mw_log_conf_template.h - src/Glue/lorawan_conf.h Based on Middlewares/Third_Party/LoRaWAN/Conf/lorawan_conf_template.h - src/Glue/timer.h Largely taken verbatim from Middlewares/Third_Party/LoRaWAN/Conf/timer_template.h - src/Glue/se-identity.h Based on ./Middlewares/Third_Party/LoRaWAN/Conf/se-identity_template.h - src/Glue/subghz.[ch] Based on ./Middlewares/Third_Party/SubGHz_Phy/Conf/subghz_template.[ch] - src/Glue/radio_board_if.h Based on Projects/NUCLEO-WL55JC/Applications/LoRaWAN/LoRaWAN_End_Node/LoRaWAN/Target BSD-licensed version provided privately by ST. - src/Glue/utilities_conf.h src/Glue/systime.h src/Glue/radio_board_if.c New implementation. --- src/BSP/lorawan_conf.h | 139 ++++++++++++++++ src/BSP/mw_log.cpp | 47 ++++++ src/BSP/mw_log_conf.h | 75 +++++++++ src/BSP/radio_board_if.c | 138 ++++++++++++++++ src/BSP/radio_board_if.h | 108 +++++++++++++ src/BSP/radio_conf.h | 155 ++++++++++++++++++ src/BSP/se-identity.h | 341 +++++++++++++++++++++++++++++++++++++++ src/BSP/subghz.c | 79 +++++++++ src/BSP/subghz.h | 59 +++++++ src/BSP/systime.h | 41 +++++ src/BSP/timer.h | 111 +++++++++++++ src/BSP/utilities_conf.h | 60 +++++++ 12 files changed, 1353 insertions(+) create mode 100644 src/BSP/lorawan_conf.h create mode 100644 src/BSP/mw_log.cpp create mode 100644 src/BSP/mw_log_conf.h create mode 100644 src/BSP/radio_board_if.c create mode 100644 src/BSP/radio_board_if.h create mode 100644 src/BSP/radio_conf.h create mode 100644 src/BSP/se-identity.h create mode 100644 src/BSP/subghz.c create mode 100644 src/BSP/subghz.h create mode 100644 src/BSP/systime.h create mode 100644 src/BSP/timer.h create mode 100644 src/BSP/utilities_conf.h diff --git a/src/BSP/lorawan_conf.h b/src/BSP/lorawan_conf.h new file mode 100644 index 0000000..1fa72d8 --- /dev/null +++ b/src/BSP/lorawan_conf.h @@ -0,0 +1,139 @@ +/** + ****************************************************************************** + * @file lorawan_conf.h + * @author MCD Application Team + * @brief Header for LoRaWAN middleware instances + ****************************************************************************** + * @attention + * + * Copyright (c) 2021 STMicroelectronics. + * All rights reserved. + * + * Revised BSD License - https://spdx.org/licenses/BSD-3-Clause.html + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. + * + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __LORAWAN_CONF_H__ +#define __LORAWAN_CONF_H__ + +/*! + * @brief LoRaWAN version definition + * @note possible values: 0x01000300 or 0x01000400 + */ +#define LORAMAC_SPECIFICATION_VERSION 0x01000300 +// TODO: What do we need here? 1.0.3 for random nonces? +// - It seems this value defines the default value for the LoRaWAN +// version to use, which can be changed using +// MIB_ABP_LORAWAN_VERSION. That seems to apply that for OTAA, the +// version is autodetected based on the joinAccept, but that does not +// seem to be the case. Also the value of this define also controls +// a lot of conditional compilation, so it is not *just* the default +// for MIB_ABP_LORAWAN_VERSION. +// - This value also controls USE_RANDOM_DEV_NONCE in LoRaMacCrypto.h +// (random for 1.0.3, sequential for 1.0.4) + +/* These regions are enabled, one of them can be selected at runtime. */ +#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 + +/** + * \brief Limits the number usable channels by default for AU915, CN470 and US915 regions + * \note the default channel mask with this option activates the first 8 channels. \ + * this default mask can be modified in the RegionXXXXXInitDefaults function associated with the active region. + */ +#define HYBRID_ENABLED 0 + +/** + * \brief Define the read access of the keys in memory + * Enabled to allow reading session keys from the main application. + */ +#define KEY_EXTRACTABLE 1 + +/*! + * Enables/Disables the context storage management storage. + * Must be enabled for LoRaWAN 1.0.4 or later. + * TODO: What do we need? This enables a DeleteAllDynamicKeys() in + * SecureElementInit() (but only if LORAWAN_KMS), and enables LmHander NVM + * storage handling. + */ +#define CONTEXT_MANAGEMENT_ENABLED 1 + +/* Class B ------------------------------------*/ +#define LORAMAC_CLASSB_ENABLED 0 + +#if ( LORAMAC_CLASSB_ENABLED == 1 ) + /* CLASS B LSE crystal 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 /* LORAMAC_CLASSB_ENABLED == 1 */ + +/** + * \brief Disable the ClassA receive windows after Tx (after the Join Accept if OTAA mode defined) + * \note Behavior to reduce power consumption but not compliant with LoRa Alliance recommendations. + * All device parameters (Spreading Factor, channels selection, Tx Power, ...) should be fixed + * and the adaptive datarate should be disabled. + * /warning This limitation may have consequences for the proper functioning of the device, + if the LoRaMac ever generates MAC commands that require a response. + */ +#define DISABLE_LORAWAN_RX_WINDOW 0 + +// TODO +/* Exported macro ------------------------------------------------------------*/ +#ifndef CRITICAL_SECTION_BEGIN + #define CRITICAL_SECTION_BEGIN( ) UTILS_ENTER_CRITICAL_SECTION( ) +#endif /* !CRITICAL_SECTION_BEGIN */ +#ifndef CRITICAL_SECTION_END + #define CRITICAL_SECTION_END( ) UTILS_EXIT_CRITICAL_SECTION( ) +#endif /* !CRITICAL_SECTION_END */ + +#endif /* __LORAWAN_CONF_H__ */ diff --git a/src/BSP/mw_log.cpp b/src/BSP/mw_log.cpp new file mode 100644 index 0000000..9ad1da2 --- /dev/null +++ b/src/BSP/mw_log.cpp @@ -0,0 +1,47 @@ +/** + ****************************************************************************** + * @file mw_log.cpp + * @author Matthijs Kooijman + * @brief Logging function for STM32CubeWL code + ****************************************************************************** + * Copyright (c) 2022 STMicroelectronics. + * All rights reserved. + * + * Revised BSD License - https://spdx.org/licenses/BSD-3-Clause.html + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. + */ +#include "mw_log_conf.h" + +#include +#include + +void MW_LOG([[gnu::unused]] MwLogTimestamp_t ts, [[gnu::unused]] MwLogLevel_t level, const char* fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vcore_debug(fmt, ap); + va_end(ap); +} diff --git a/src/BSP/mw_log_conf.h b/src/BSP/mw_log_conf.h new file mode 100644 index 0000000..98ff637 --- /dev/null +++ b/src/BSP/mw_log_conf.h @@ -0,0 +1,75 @@ +/** + ****************************************************************************** + * @file mw_log_conf.h + * @author MCD Application Team + * @brief Configure (enable/disable) traces for CM0 + ****************************************************************************** + * @attention + * + * Copyright (c) 2021 STMicroelectronics. + * All rights reserved. + * + * Revised BSD License - https://spdx.org/licenses/BSD-3-Clause.html + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. + * + ****************************************************************************** + */ + +#ifndef __MW_LOG_CONF_H__ +#define __MW_LOG_CONF_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#define MW_LOG_ENABLED + +// These enums were defines in utilities_conf.h in STM32CubeWL +// +// They are defined here for compatibility with existing code, but +// are ignored when passed to MW_LOG, which just does a dumb forward +// of the message. +typedef enum { + VLEVEL_ALWAYS = 0, /*!< used as message params, if this level is given + trace will be printed even when UTIL_ADV_TRACE_SetVerboseLevel(OFF) */ + VLEVEL_L = 1, /*!< just essential traces */ + VLEVEL_M = 2, /*!< functional traces */ + VLEVEL_H = 3, /*!< all traces */ +} MwLogLevel_t; + +typedef enum { + TS_OFF = 0, /*!< Log without TimeStamp */ + TS_ON = 1, /*!< Log with TimeStamp */ +} MwLogTimestamp_t; + +__attribute__((format(printf, 3, 4))) +void MW_LOG(MwLogTimestamp_t ts, MwLogLevel_t level, const char *fmt, ...); + +#ifdef __cplusplus +} +#endif + +#endif /*__MW_LOG_CONF_H__ */ diff --git a/src/BSP/radio_board_if.c b/src/BSP/radio_board_if.c new file mode 100644 index 0000000..6f15e3b --- /dev/null +++ b/src/BSP/radio_board_if.c @@ -0,0 +1,138 @@ +/** + ****************************************************************************** + * @file radio_board_if.c + * @author Matthijs Kooijman + * @brief This file provides an interface layer between MW and Radio Board + ****************************************************************************** + * Copyright (c) 2022 STMicroelectronics. + * + * Revised BSD License - https://spdx.org/licenses/BSD-3-Clause.html + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. + ****************************************************************************** + */ + +/* Includes ------------------------------------------------------------------*/ +#include "radio_board_if.h" +#include "Arduino.h" + +/* Board configuration --------------------------------------------------------*/ + +/* This section defines the board configuration values used. This + * initially just hardcodes the values for the Nucleo WL55JC1 board, but + * by using defines, these can potentially be overridden by + * board-specifc defines on the commandline or in the variant files. + */ + +// Is a TCXO present on the board? +#if !defined(LORAWAN_BOARD_HAS_TCXO) + #define LORAWAN_BOARD_HAS_TCXO 1U +#endif + +// Is circuitry for DCDC (SMPS) mode present on the board? +#if !defined(LORAWAN_BOARD_HAS_DCDC) + #define LORAWAN_BOARD_HAS_DCDC 1U +#endif + +// Maximum output power supported by output circuitry in LP mode +#if !defined(LORAWAN_RFO_LP_MAX_POWER ) + #define LORAWAN_RFO_LP_MAX_POWER 15 /* dBm */ +#endif + +// Maximum output power supported by output circuitry in HP mode +#if !defined(LORAWAN_RFO_HP_MAX_POWER ) + #define LORAWAN_RFO_HP_MAX_POWER 22 /* dBm */ +#endif + +// Supported TX modes (LP/HP or both) +#if !defined(LORAWAN_TX_CONFIG) + #define LORAWAN_TX_CONFIG RBI_CONF_RFO_LP_HP +#endif + +#if !defined(LORAWAN_RFSWITCH_PINS) + #define LORAWAN_RFSWITCH_PINS PC3,PC4,PC5 + #define LORAWAN_RFSWITCH_PIN_COUNT 3 + #define LORAWAN_RFSWITCH_OFF_VALUES LOW,LOW,LOW + #define LORAWAN_RFSWITCH_RX_VALUES HIGH,HIGH,LOW + #define LORAWAN_RFSWITCH_RFO_LP_VALUES HIGH,HIGH,HIGH + #define LORAWAN_RFSWITCH_RFO_HP_VALUES HIGH,LOW,HIGH +#endif + +/* Static variables --------------------------------------------------------*/ +static unsigned lorawan_rfswitch_pins[LORAWAN_RFSWITCH_PIN_COUNT] = {LORAWAN_RFSWITCH_PINS}; +static unsigned lorawan_rfswitch_values[][LORAWAN_RFSWITCH_PIN_COUNT] = { + [RBI_SWITCH_OFF] = {LORAWAN_RFSWITCH_OFF_VALUES}, + [RBI_SWITCH_RX] = {LORAWAN_RFSWITCH_RX_VALUES}, + [RBI_SWITCH_RFO_LP] = {LORAWAN_RFSWITCH_RFO_LP_VALUES}, + [RBI_SWITCH_RFO_HP] = {LORAWAN_RFSWITCH_RFO_HP_VALUES}, +}; + +/* Exported functions --------------------------------------------------------*/ +int32_t RBI_Init(void) +{ + for (unsigned i = 0; i < LORAWAN_RFSWITCH_PIN_COUNT; ++i) { + pinMode(lorawan_rfswitch_pins[i], OUTPUT); + digitalWrite(lorawan_rfswitch_pins[i], lorawan_rfswitch_values[RBI_SWITCH_OFF][i]); + } + + return 0; +} + +int32_t RBI_DeInit(void) +{ + return 0; +} + +int32_t RBI_ConfigRFSwitch(RBI_Switch_TypeDef Config) +{ + for (unsigned i = 0; i < LORAWAN_RFSWITCH_PIN_COUNT; ++i) { + digitalWrite(lorawan_rfswitch_pins[i], lorawan_rfswitch_values[Config][i]); + } + return 0; +} + +int32_t RBI_GetTxConfig(void) +{ + return LORAWAN_TX_CONFIG; +} + +int32_t RBI_IsTCXO(void) +{ + return LORAWAN_BOARD_HAS_TCXO; +} + +int32_t RBI_IsDCDC(void) +{ + return LORAWAN_BOARD_HAS_DCDC; +} + +int32_t RBI_GetRFOMaxPowerConfig(RBI_RFOMaxPowerConfig_TypeDef Config) +{ + if (Config == RBI_RFO_LP_MAXPOWER) { + return LORAWAN_RFO_LP_MAX_POWER; + } else { + return LORAWAN_RFO_HP_MAX_POWER; + } +} diff --git a/src/BSP/radio_board_if.h b/src/BSP/radio_board_if.h new file mode 100644 index 0000000..50acdb0 --- /dev/null +++ b/src/BSP/radio_board_if.h @@ -0,0 +1,108 @@ +/** + ****************************************************************************** + * @file radio_board_if.h + * @author MCD Application Team + * @brief Header for Radio interface configuration + ****************************************************************************** + * @attention + * + * Copyright (c) 2021 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_BOARD_IF_H +#define RADIO_BOARD_IF_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Exported types ------------------------------------------------------------*/ + +// These were defines in STM32CubeWL for unclear reasons, converted to +// cleaner enum for STM32LoRaWAN Arduino library. +typedef enum { + RBI_CONF_RFO_LP_HP = 0, + RBI_CONF_RFO_LP = 1, + RBI_CONF_RFO_HP = 2, +} RBI_TxConfig_TypeDef; + +typedef enum { + RBI_SWITCH_OFF = 0, + RBI_SWITCH_RX = 1, + RBI_SWITCH_RFO_LP = 2, + RBI_SWITCH_RFO_HP = 3, +} RBI_Switch_TypeDef; + +typedef enum { + RBI_RFO_LP_MAXPOWER = 0, + RBI_RFO_HP_MAXPOWER = 1, +} RBI_RFOMaxPowerConfig_TypeDef; + +/* Exported functions ------------------------------------------------------- */ +/** + * @brief Init Radio Switch + * @return BSP status + */ +int32_t RBI_Init(void); + +/** + * @brief DeInit Radio Switch + * @return BSP status + */ +int32_t RBI_DeInit(void); + +/** + * @brief Configure Radio Switch. + * @param Config: Specifies the Radio RF switch path to be set. + * This parameter can be one of following parameters: + * @arg RBI_SWITCH_OFF + * @arg RBI_SWITCH_RX + * @arg RBI_SWITCH_RFO_LP + * @arg RBI_SWITCH_RFO_HP + * @return BSP status + */ +int32_t RBI_ConfigRFSwitch(RBI_Switch_TypeDef Config); + +/** + * @brief Return Board Configuration + * @retval RBI_CONF_RFO_LP_HP + * @retval RBI_CONF_RFO_LP + * @retval RBI_CONF_RFO_HP + */ +int32_t RBI_GetTxConfig(void); + +/** + * @brief Get If TCXO is to be present on board + * @retval return 1 if present, 0 if not present + */ +int32_t RBI_IsTCXO(void); + +/** + * @brief Get If DCDC is to be present on board + * @retval return 1 if present, 0 if not present + */ +int32_t RBI_IsDCDC(void); + +/** + * @brief Return RF Output Max Power Configuration of matching circuit + * @retval return Max Power configuration of matching circuit for Low Power or High Power mode in dBm + */ +int32_t RBI_GetRFOMaxPowerConfig(RBI_RFOMaxPowerConfig_TypeDef Config); + + +#ifdef __cplusplus +} +#endif + +#endif /* RADIO_BOARD_IF_H */ diff --git a/src/BSP/radio_conf.h b/src/BSP/radio_conf.h new file mode 100644 index 0000000..951ff7a --- /dev/null +++ b/src/BSP/radio_conf.h @@ -0,0 +1,155 @@ +/** + ****************************************************************************** + * @file radio_conf.h + * @author MCD Application Team + * @brief Header of Radio configuration + ****************************************************************************** + * @attention + * + * Copyright (c) 2021 STMicroelectronics. + * All rights reserved. + * + * Revised BSD License - https://spdx.org/licenses/BSD-3-Clause.html + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. + * + ****************************************************************************** + */ +#ifndef __RADIO_CONF_H__ +#define __RADIO_CONF_H__ + +/* Includes ------------------------------------------------------------------*/ +#include +#include "subghz.h" +#include "radio_board_if.h" +#include "utilities_conf.h" /* For UTILS_*_CRITICAL_SECTION */ + +/** + * @brief drive value used anytime radio is NOT in TX low power mode + * @note override the default configuration of radio_driver.c + */ +#define SMPS_DRIVE_SETTING_DEFAULT SMPS_DRV_40 + +/** + * @brief drive value used anytime radio is in TX low power mode + * TX low power mode is the worst case because the PA sinks from SMPS + * while in high power mode, current is sunk directly from the battery + * @note override the default configuration of radio_driver.c + */ +#define SMPS_DRIVE_SETTING_MAX SMPS_DRV_60 + +/** + * @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 + * @note override the default configuration of radio_driver.c + */ +#define XTAL_FREQ ( 32000000UL ) + +/** + * @brief in XO mode, set internal capacitor (from 0x00 to 0x2F starting 11.2pF with 0.47pF steps) + * @note override the default configuration of radio_driver.c + */ +#define XTAL_DEFAULT_CAP_VALUE ( 0x20UL ) + +/** + * @brief voltage of vdd tcxo. + * @note override the default configuration of radio_driver.c + */ +#define TCXO_CTRL_VOLTAGE TCXO_CTRL_1_7V + +/** + * @brief Radio maximum wakeup time (in ms) + * @note override the default configuration of radio_driver.c + */ +#define RF_WAKEUP_TIME ( 1UL ) + +/** + * @brief DCDC is enabled + * @remark this define is only used if the DCDC is present on the board + * (as indicated by RBI_IsDCDC()) + * @note override the default configuration of radio_driver.c + */ +#define DCDC_ENABLE ( 1UL ) + +/** + * @brief disable the Sigfox radio modulation + * @note enabled by default + */ +#define RADIO_SIGFOX_ENABLE 0 + +/** + * @brief disable the radio generic features + * @note enabled by default + */ +#define RADIO_GENERIC_CONFIG_ENABLE 0 + +/** + * @brief Set RX pin to high or low level + */ +#define DBG_GPIO_RADIO_RX(set_rst) + +/** + * @brief Set TX pin to high or low level + */ +#define DBG_GPIO_RADIO_TX(set_rst) + + +/* Exported macros -----------------------------------------------------------*/ +#ifndef CRITICAL_SECTION_BEGIN + // TODO + /** + * @brief macro used to enter the critical section + */ + #define CRITICAL_SECTION_BEGIN( ) UTILS_ENTER_CRITICAL_SECTION( ) +#endif /* !CRITICAL_SECTION_BEGIN */ +#ifndef CRITICAL_SECTION_END + /** + * @brief macro used to exit the critical section + */ + #define CRITICAL_SECTION_END( ) UTILS_EXIT_CRITICAL_SECTION( ) +#endif /* !CRITICAL_SECTION_END */ + +/* Function mapping */ +/** + * @brief SUBGHZ interface init to radio Middleware + */ +#define RADIO_INIT MX_SUBGHZ_Init + +/** + * @brief Delay interface to radio Middleware + */ +#define RADIO_DELAY_MS HAL_Delay + +/** + * @brief Memset utilities interface to radio Middleware + */ +#define RADIO_MEMSET8( dest, value, size ) memset( dest, value, size ) + +/** + * @brief Memcpy utilities interface to radio Middleware + */ +#define RADIO_MEMCPY8( dest, src, size ) memcpy( dest, src, size ) + +#endif /* __RADIO_CONF_H__*/ diff --git a/src/BSP/se-identity.h b/src/BSP/se-identity.h new file mode 100644 index 0000000..08d28df --- /dev/null +++ b/src/BSP/se-identity.h @@ -0,0 +1,341 @@ +/*! + * \file se-identity.h + * + * \brief Secure Element identity and keys + * + * \copyright Revised BSD License + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2020 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + */ +/** + ****************************************************************************** + * + * (C)2020 Semtech + * Portions COPYRIGHT 2020 STMicroelectronics + * + * @file se-identity.h + * @author MCD Application Team + * @brief Secure Element identity and keys + ****************************************************************************** + * Revised BSD License - https://spdx.org/licenses/BSD-3-Clause.html + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __SOFT_SE_IDENTITY_H__ +#define __SOFT_SE_IDENTITY_H__ + +/*! + ****************************************************************************** + ********************************** WARNING *********************************** + ****************************************************************************** + The secure-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 | + +---------------------+-------------------------+ + | LORAWAN_GEN_APP_KEY | 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 | + +---------------------+-------------------------+ + ****************************************************************************** + ****************************************************************************** + ****************************************************************************** + */ + +// TODO: Set to 1 and preset detected EUI through Mib instead of using the complex callback scheme? +/*! + * When set to 1 DevEui is LORAWAN_DEVICE_EUI + * When set to 0 DevEui is automatically set with a value provided by MCU platform + */ +#define STATIC_DEVICE_EUI 0 + +/*! + * end-device IEEE EUI (big endian) + */ +#define LORAWAN_DEVICE_EUI { } + +/*! + * App/Join server IEEE EUI (big endian) + */ +#define LORAWAN_JOIN_EUI { } + +#if (USE_LRWAN_1_1_X_CRYPTO == 1) +#define SESSION_KEYS_LIST \ + { \ + /*! \ + * Join session integrity key (Dynamically updated) \ + * WARNING: NOT USED FOR 1.0.x DEVICES \ + */ \ + .KeyID = J_S_INT_KEY, \ + .KeyValue = { }, \ + }, \ + { \ + /*! \ + * Join session encryption key (Dynamically updated) \ + * WARNING: NOT USED FOR 1.0.x DEVICES \ + */ \ + .KeyID = J_S_ENC_KEY, \ + .KeyValue = { }, \ + }, \ + { \ + /*! \ + * Forwarding Network session integrity key \ + * WARNING: NWK_S_KEY FOR 1.0.x DEVICES \ + */ \ + .KeyID = F_NWK_S_INT_KEY, \ + .KeyValue = { }, \ + }, \ + { \ + /*! \ + * Serving Network session integrity key \ + * WARNING: NOT USED FOR 1.0.x DEVICES. MUST BE THE SAME AS \ref LORAWAN_F_NWK_S_INT_KEY \ + */ \ + .KeyID = S_NWK_S_INT_KEY, \ + .KeyValue = { }, \ + }, \ + { \ + /*! \ + * Network session encryption key \ + * WARNING: NOT USED FOR 1.0.x DEVICES. MUST BE THE SAME AS \ref LORAWAN_F_NWK_S_INT_KEY \ + */ \ + .KeyID = NWK_S_ENC_KEY, \ + .KeyValue = { }, \ + }, \ + { \ + /*! \ + * Application session key \ + */ \ + .KeyID = APP_S_KEY, \ + .KeyValue = { }, \ + }, +#else /* USE_LRWAN_1_1_X_CRYPTO == 0 */ +#define SESSION_KEYS_LIST \ + { \ + /*! \ + * Network session key \ + */ \ + .KeyID = NWK_S_KEY, \ + .KeyValue = { }, \ + }, \ + { \ + /*! \ + * Application session key \ + */ \ + .KeyID = APP_S_KEY, \ + .KeyValue = { }, \ + }, +#endif /* USE_LRWAN_1_1_X_CRYPTO */ + +#if (LORAMAC_MAX_MC_CTX == 1) +#define SESSION_MC_KEYS_LIST \ + { \ + /*! \ + * Multicast group #0 root key (Dynamically updated) \ + */ \ + .KeyID = MC_KEY_0, \ + .KeyValue = { }, \ + }, \ + { \ + /*! \ + * Multicast group #0 application session key (Dynamically updated) \ + */ \ + .KeyID = MC_APP_S_KEY_0, \ + .KeyValue = { }, \ + }, \ + { \ + /*! \ + * Multicast group #0 network session key (Dynamically updated) \ + */ \ + .KeyID = MC_NWK_S_KEY_0, \ + .KeyValue = { }, \ + }, +#else /* LORAMAC_MAX_MC_CTX > 1 */ +#define SESSION_MC_KEYS_LIST \ + { \ + /*! \ + * Multicast group #0 root key (Dynamically updated) \ + */ \ + .KeyID = MC_KEY_0, \ + .KeyValue = { }, \ + }, \ + { \ + /*! \ + * Multicast group #0 application session key (Dynamically updated) \ + */ \ + .KeyID = MC_APP_S_KEY_0, \ + .KeyValue = { }, \ + }, \ + { \ + /*! \ + * Multicast group #0 network session key (Dynamically updated) \ + */ \ + .KeyID = MC_NWK_S_KEY_0, \ + .KeyValue = { }, \ + }, \ + { \ + /*! \ + * Multicast group #1 root key (Dynamically updated) \ + */ \ + .KeyID = MC_KEY_1, \ + .KeyValue = { }, \ + }, \ + { \ + /*! \ + * Multicast group #1 application session key (Dynamically updated) \ + */ \ + .KeyID = MC_APP_S_KEY_1, \ + .KeyValue = { }, \ + }, \ + { \ + /*! \ + * Multicast group #1 network session key (Dynamically updated) \ + */ \ + .KeyID = MC_NWK_S_KEY_1, \ + .KeyValue = { }, \ + }, \ + { \ + /*! \ + * Multicast group #2 root key (Dynamically updated) \ + */ \ + .KeyID = MC_KEY_2, \ + .KeyValue = { }, \ + }, \ + { \ + /*! \ + * Multicast group #2 application session key (Dynamically updated) \ + */ \ + .KeyID = MC_APP_S_KEY_2, \ + .KeyValue = { }, \ + }, \ + { \ + /*! \ + * Multicast group #2 network session key (Dynamically updated) \ + */ \ + .KeyID = MC_NWK_S_KEY_2, \ + .KeyValue = { }, \ + }, \ + { \ + /*! \ + * Multicast group #3 root key (Dynamically updated) \ + */ \ + .KeyID = MC_KEY_3, \ + .KeyValue = { }, \ + }, \ + { \ + /*! \ + * Multicast group #3 application session key (Dynamically updated) \ + */ \ + .KeyID = MC_APP_S_KEY_3, \ + .KeyValue = { }, \ + }, \ + { \ + /*! \ + * Multicast group #3 network session key (Dynamically updated) \ + */ \ + .KeyID = MC_NWK_S_KEY_3, \ + .KeyValue = { }, \ + }, +#endif /* LORAMAC_MAX_MC_CTX */ + +#define SOFT_SE_KEY_LIST \ + { \ + { \ + /*! \ + * Application root key \ + * WARNING: FOR 1.0.x DEVICES IT IS THE \ref LORAWAN_GEN_APP_KEY \ + */ \ + .KeyID = APP_KEY, \ + .KeyValue = { }, \ + }, \ + { \ + /*! \ + * Network root key \ + * WARNING: FOR 1.0.x DEVICES IT IS THE \ref LORAWAN_APP_KEY \ + */ \ + .KeyID = NWK_KEY, \ + .KeyValue = { }, \ + }, \ + SESSION_KEYS_LIST \ + { \ + /*! \ + * Multicast root key (Dynamically updated) \ + */ \ + .KeyID = MC_ROOT_KEY, \ + .KeyValue = { }, \ + }, \ + { \ + /*! \ + * Multicast key encryption key (Dynamically updated) \ + */ \ + .KeyID = MC_KE_KEY, \ + .KeyValue = { }, \ + }, \ + SESSION_MC_KEYS_LIST \ + { \ + /*! \ + * All zeros key. (ClassB usage)(constant) \ + */ \ + .KeyID = SLOT_RAND_ZERO_KEY, \ + .KeyValue = { }, \ + }, \ + } + +#ifdef __cplusplus +} +#endif + +#endif /* __SOFT_SE_IDENTITY_H__ */ diff --git a/src/BSP/subghz.c b/src/BSP/subghz.c new file mode 100644 index 0000000..2c1b6a1 --- /dev/null +++ b/src/BSP/subghz.c @@ -0,0 +1,79 @@ +/** + ****************************************************************************** + * @file subghz.c + * @brief This file provides code for the configuration + * of the SUBGHZ instances. + ****************************************************************************** + * @attention + * + * Copyright (c) 2021 STMicroelectronics. + * All rights reserved. + * + * Revised BSD License - https://spdx.org/licenses/BSD-3-Clause.html + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. + * + ****************************************************************************** + */ +/* Includes ------------------------------------------------------------------*/ +#include "subghz.h" + +SUBGHZ_HandleTypeDef hsubghz; + +void MX_SUBGHZ_Init(void) +{ + hsubghz.Init.BaudratePrescaler = SUBGHZSPI_BAUDRATEPRESCALER_4; + if (HAL_SUBGHZ_Init(&hsubghz) != HAL_OK) { + Error_Handler(); + } +} + +/* Callback called by the HAL_SUBGHZ layer based on its name */ +void HAL_SUBGHZ_MspInit(SUBGHZ_HandleTypeDef *subghzHandle) +{ + (void)subghzHandle; // unused + + __HAL_RCC_SUBGHZSPI_CLK_ENABLE(); + + HAL_NVIC_SetPriority(SUBGHZ_Radio_IRQn, 0, 0); + HAL_NVIC_EnableIRQ(SUBGHZ_Radio_IRQn); +} + +/* Callback called by the HAL_SUBGHZ layer based on its name */ +void HAL_SUBGHZ_MspDeInit(SUBGHZ_HandleTypeDef *subghzHandle) +{ + (void)subghzHandle; // unused + + /* Peripheral clock disable */ + __HAL_RCC_SUBGHZSPI_CLK_DISABLE(); + + /* SUBGHZ interrupt Deinit */ + HAL_NVIC_DisableIRQ(SUBGHZ_Radio_IRQn); +} + +void SUBGHZ_Radio_IRQHandler(void) +{ + HAL_SUBGHZ_IRQHandler(&hsubghz); +} diff --git a/src/BSP/subghz.h b/src/BSP/subghz.h new file mode 100644 index 0000000..3a6bbec --- /dev/null +++ b/src/BSP/subghz.h @@ -0,0 +1,59 @@ +/** + ****************************************************************************** + * @file subghz.h + * @brief This file contains all the function prototypes for + * the subghz.c file + ****************************************************************************** + * @attention + * + * Copyright (c) 2021 STMicroelectronics. + * All rights reserved. + * + * Revised BSD License - https://spdx.org/licenses/BSD-3-Clause.html + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. + * + ****************************************************************************** + */ + +#ifndef __SUBGHZ_H__ +#define __SUBGHZ_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +extern SUBGHZ_HandleTypeDef hsubghz; + +void MX_SUBGHZ_Init(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __SUBGHZ_H__ */ + diff --git a/src/BSP/systime.h b/src/BSP/systime.h new file mode 100644 index 0000000..afde345 --- /dev/null +++ b/src/BSP/systime.h @@ -0,0 +1,41 @@ +/** + ****************************************************************************** + * @file systime.h + * @author Matthijs Kooijman + * @brief Dummy header for systime utilities + * + * To allow using other files without changes, this just includes the + * appropriate header. + * + ****************************************************************************** + * Copyright (c) 2022 STMicroelectronics. + * All rights reserved. + * + * Revised BSD License - https://spdx.org/licenses/BSD-3-Clause.html + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. + */ + +#include "../STM32CubeWL/Utilities/misc/stm32_systime.h" diff --git a/src/BSP/timer.h b/src/BSP/timer.h new file mode 100644 index 0000000..f8b3d1f --- /dev/null +++ b/src/BSP/timer.h @@ -0,0 +1,111 @@ +/** + ****************************************************************************** + * @file timer.h + * @author MCD Application Team + * @brief Wrapper to timer server + ****************************************************************************** + * @attention + * + * Copyright (c) 2021 STMicroelectronics. + * All rights reserved. + * + * Revised BSD License - https://spdx.org/licenses/BSD-3-Clause.html + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __TIMER_H__ +#define __TIMER_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "../STM32CubeWL/Utilities/timer/stm32_timer.h" + +/** + * @brief Max timer mask + */ +#define TIMERTIME_T_MAX ( ( uint32_t )~0 ) + +/* Exported macro ------------------------------------------------------------*/ + +/** + * @brief Timer value on 32 bits + */ +#define TimerTime_t UTIL_TIMER_Time_t + +/** + * @brief Timer object description + */ +#define TimerEvent_t UTIL_TIMER_Object_t + +/** + * @brief Create the timer object + */ +#define TimerInit(HANDLE, CB) do {\ + UTIL_TIMER_Create( HANDLE, TIMERTIME_T_MAX, UTIL_TIMER_ONESHOT, CB, NULL);\ + } while(0) + +/** + * @brief update the period and start the timer + */ +#define TimerSetValue(HANDLE, TIMEOUT) do{ \ + UTIL_TIMER_SetPeriod(HANDLE, TIMEOUT);\ + } while(0) + +/** + * @brief Start and adds the timer object to the list of timer events + */ +#define TimerStart(HANDLE) do {\ + UTIL_TIMER_Start(HANDLE);\ + } while(0) + +/** + * @brief Stop and removes the timer object from the list of timer events + */ +#define TimerStop(HANDLE) do {\ + UTIL_TIMER_Stop(HANDLE);\ + } while(0) + +/** + * @brief return the current time + */ +#define TimerGetCurrentTime UTIL_TIMER_GetCurrentTime + +/** + * @brief return the elapsed time + */ +#define TimerGetElapsedTime UTIL_TIMER_GetElapsedTime + +#ifdef __cplusplus +} +#endif + +#endif /* __TIMER_H__*/ diff --git a/src/BSP/utilities_conf.h b/src/BSP/utilities_conf.h new file mode 100644 index 0000000..66f68a1 --- /dev/null +++ b/src/BSP/utilities_conf.h @@ -0,0 +1,60 @@ +/** + ****************************************************************************** + * @file utilities_conf.h + * @author Matthijs Kooijman + * @brief Header for configuration file to utilities + ****************************************************************************** + * Copyright (c) 2022 STMicroelectronics. + * + * Revised BSD License - https://spdx.org/licenses/BSD-3-Clause.html + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. + ****************************************************************************** + */ +#ifndef __UTILITIES_CONF_H__ +#define __UTILITIES_CONF_H__ + +// We do not really need these two includes, but parts of the +// STM32CubeWL code depend on us declaring this. +#include +#include + +// TODO: are these critical section macros ok? +// +/** + * @brief macro used to initialize the critical section + */ +#define UTILS_INIT_CRITICAL_SECTION() + +/** + * @brief macro used to enter the critical section + */ +#define UTILS_ENTER_CRITICAL_SECTION() uint32_t primask_bit= __get_PRIMASK();\ + __disable_irq() + +/** + * @brief macro used to exit the critical section + */ +#define UTILS_EXIT_CRITICAL_SECTION() __set_PRIMASK(primask_bit) + +#endif /*__UTILITIES_CONF_H__ */ From 44cc7e78801bc62f631c00f78e5905ed290cb29d Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 23 Jun 2022 23:21:59 +0200 Subject: [PATCH 03/73] update.sh: Add script This script copies selected files from the STM32CubeWL package (unpacked zip or git clone) into this library as needed. It can be used for the initial copy as well as for later updates. --- tools/update.sh | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100755 tools/update.sh diff --git a/tools/update.sh b/tools/update.sh new file mode 100755 index 0000000..4478fc8 --- /dev/null +++ b/tools/update.sh @@ -0,0 +1,15 @@ +#!/bin/sh -e + +if [ ! -d "$1" ] || ! grep STM32CubeWL "$1/Package_license.md" 1>/dev/null 2>/dev/null; then + echo "No argument passed, or is not an extracted STM32CubeWL package directory" + exit 1; +fi +CUBE=$1 +ROOT=$(cd $(dirname $0)/.. && pwd) +DEST="$ROOT/src/STM32CubeWL" + +rsync -av "$CUBE/Package_license.md" "$DEST/" +rsync -av "$CUBE/Middlewares/Third_Party/LoRaWAN" "$DEST/" --exclude '*_template.[ch]' --exclude "/LoRaWAN/LmHandler/*" +rsync -av "$CUBE/Middlewares/Third_Party/SubGHz_Phy" "$DEST/" --exclude '*_template.[ch]' +rsync -av "$CUBE/Utilities/timer" --exclude '*_template.[ch]' "$DEST/Utilities" +rsync -av "$CUBE/Utilities/misc" "$DEST/Utilities" --include '/misc/stm32_systime.[ch]' --include '/misc/*.html' --include '/misc/*.txt' --exclude '/misc/*' From fc8f214cf5ba664ef23fd4b460fa1040e7287213 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 23 Jun 2022 23:23:37 +0200 Subject: [PATCH 04/73] fix-includes.py: Add script This script can be used to, after an update, fix up various include paths. The STM32CubeWL code assumes a lot of files are in the include path, but this is not the case in the Arduino build. So this script modifies the STM32CubeWL files (and any other source files) to resolve any include files using relative paths for any header files that are available within this library. This script is intended to be ran whenever update.sh is ran. --- tools/fix-includes.py | 84 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100755 tools/fix-includes.py diff --git a/tools/fix-includes.py b/tools/fix-includes.py new file mode 100755 index 0000000..483b9c0 --- /dev/null +++ b/tools/fix-includes.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 + +# This script can be used to transform include directives to use +# relative paths. This is helpful for moving code that assumes other +# files are on the compiler include path into an environment that has no +# such include path setup (such as within a single Arduio library). +# +# This script simply lists the filenames of all header files in the +# (given) source directory, then finds all include directives containing +# any of these filenames, replacing them with relative includes. +# +# Includes of specific files will be removed, this list of files is +# currently hardcoded below. + +import argparse +import glob +import os +import re + +root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +src_path = os.path.relpath(os.path.join(root, 'src')) + +parser = argparse.ArgumentParser( + formatter_class=argparse.ArgumentDefaultsHelpFormatter, +) +parser.add_argument('-v', '--verbose', action='store_true') +parser.add_argument('-s', '--src', help="Path to directory to process", default=src_path) +args = parser.parse_args() + +header_glob = os.path.join(args.src, "**/*.h") +header_source_glob = os.path.join(args.src, "**/*.[ch]") +headers = { + os.path.basename(f): f for f in glob.glob(header_glob, recursive=True) +} + +# These headers can be omitted +headers['stm32_lpm.h'] = False +headers['utilities_def.h'] = False + +for filename in glob.glob(header_source_glob, recursive=True): + with open(filename, 'r+') as f: + curdir = os.path.dirname(filename) + contents = f.read() + prints = [] + + def fix_include(match): + original = match.group(0) + include = match.group(3) + new_path = headers.get(os.path.basename(include)) + if os.path.exists(os.path.join(curdir, include)): + if args.verbose: + prints.append(f"Same directory: {original}") + return original + elif new_path is None: + if args.verbose: + prints.append(f"Not found: {original}") + return original + elif new_path is False: + prints.append(f"Removed: {original}") + return '// ' + original + elif new_path[0] == '<': + repl = match.group(1) + new_path + prints.append(f"{original} -> {repl}") + return repl + else: + rel_path = os.path.relpath(new_path, curdir) + repl = match.group(1) + match.group(2) + rel_path + match.group(4) + prints.append(f"{original} -> {repl}") + return repl + + # This matches "" in addition to <>, since the former *also* + # searches the include path and is often used for non-local + # includes as well. + new_contents = re.sub(r'^(#include )(["<])([^>"]*)([">])', fix_include, contents, flags=re.MULTILINE) + + if prints or args.verbose: + print(filename) + for p in prints: + print(" {}".format(p)) + + if new_contents != contents: + f.seek(0) + f.truncate() + f.write(new_contents) From 681f7cd4f390f6bca06768c04f9db217fb2c08c7 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Fri, 24 Jun 2022 00:02:13 +0200 Subject: [PATCH 05/73] Add STM32CubeWL 1.2.0 code verbatim This used the previously added update.sh script to add the relevant bits of the STM32CubeWL code verbatim, and uses the fix-includes.py script to fix the include directives. No other changes were made to these files. This code was downloaded from https://www.st.com/en/embedded-software/stm32cubewl.html as version 1.2.0 was not published to https://github.com/STMicroelectronics/STM32CubeWL yet. --- src/STM32CubeWL/LoRaWAN/Crypto/cmac.c | 153 + src/STM32CubeWL/LoRaWAN/Crypto/cmac.h | 71 + src/STM32CubeWL/LoRaWAN/Crypto/lorawan_aes.c | 936 +++ src/STM32CubeWL/LoRaWAN/Crypto/lorawan_aes.h | 170 + src/STM32CubeWL/LoRaWAN/Crypto/soft-se.c | 1321 ++++ src/STM32CubeWL/LoRaWAN/LICENSE | 27 + src/STM32CubeWL/LoRaWAN/LICENSE.txt | 6 + src/STM32CubeWL/LoRaWAN/Mac/LoRaMac.c | 5891 +++++++++++++++++ src/STM32CubeWL/LoRaWAN/Mac/LoRaMac.h | 483 ++ src/STM32CubeWL/LoRaWAN/Mac/LoRaMacAdr.c | 207 + src/STM32CubeWL/LoRaWAN/Mac/LoRaMacAdr.h | 151 + src/STM32CubeWL/LoRaWAN/Mac/LoRaMacClassB.c | 2083 ++++++ src/STM32CubeWL/LoRaWAN/Mac/LoRaMacClassB.h | 508 ++ .../LoRaWAN/Mac/LoRaMacClassBConfig.h | 126 + .../LoRaWAN/Mac/LoRaMacClassBNvm.h | 128 + src/STM32CubeWL/LoRaWAN/Mac/LoRaMacCommands.c | 621 ++ src/STM32CubeWL/LoRaWAN/Mac/LoRaMacCommands.h | 212 + .../LoRaWAN/Mac/LoRaMacConfirmQueue.c | 339 + .../LoRaWAN/Mac/LoRaMacConfirmQueue.h | 176 + src/STM32CubeWL/LoRaWAN/Mac/LoRaMacCrypto.c | 1635 +++++ src/STM32CubeWL/LoRaWAN/Mac/LoRaMacCrypto.h | 346 + .../LoRaWAN/Mac/LoRaMacCryptoNvm.h | 122 + .../LoRaWAN/Mac/LoRaMacHeaderTypes.h | 327 + .../LoRaWAN/Mac/LoRaMacInterfaces.h | 3029 +++++++++ .../LoRaWAN/Mac/LoRaMacMessageTypes.h | 300 + src/STM32CubeWL/LoRaWAN/Mac/LoRaMacParser.c | 130 + src/STM32CubeWL/LoRaWAN/Mac/LoRaMacParser.h | 93 + .../LoRaWAN/Mac/LoRaMacSerializer.c | 195 + .../LoRaWAN/Mac/LoRaMacSerializer.h | 108 + src/STM32CubeWL/LoRaWAN/Mac/LoRaMacTest.h | 60 + src/STM32CubeWL/LoRaWAN/Mac/LoRaMacTypes.h | 861 +++ src/STM32CubeWL/LoRaWAN/Mac/LoRaMacVersion.h | 40 + src/STM32CubeWL/LoRaWAN/Mac/Region/Region.c | 1063 +++ src/STM32CubeWL/LoRaWAN/Mac/Region/Region.h | 1234 ++++ .../LoRaWAN/Mac/Region/RegionAS923.c | 1237 ++++ .../LoRaWAN/Mac/Region/RegionAS923.h | 588 ++ .../LoRaWAN/Mac/Region/RegionAU915.c | 1106 ++++ .../LoRaWAN/Mac/Region/RegionAU915.h | 543 ++ .../LoRaWAN/Mac/Region/RegionBaseUS.c | 151 + .../LoRaWAN/Mac/Region/RegionBaseUS.h | 98 + .../LoRaWAN/Mac/Region/RegionCN470.c | 1420 ++++ .../LoRaWAN/Mac/Region/RegionCN470.h | 599 ++ .../LoRaWAN/Mac/Region/RegionCN470A20.c | 192 + .../LoRaWAN/Mac/Region/RegionCN470A20.h | 229 + .../LoRaWAN/Mac/Region/RegionCN470A26.c | 169 + .../LoRaWAN/Mac/Region/RegionCN470A26.h | 211 + .../LoRaWAN/Mac/Region/RegionCN470B20.c | 142 + .../LoRaWAN/Mac/Region/RegionCN470B20.h | 253 + .../LoRaWAN/Mac/Region/RegionCN470B26.c | 93 + .../LoRaWAN/Mac/Region/RegionCN470B26.h | 211 + .../LoRaWAN/Mac/Region/RegionCN779.c | 1064 +++ .../LoRaWAN/Mac/Region/RegionCN779.h | 493 ++ .../LoRaWAN/Mac/Region/RegionCommon.c | 714 ++ .../LoRaWAN/Mac/Region/RegionCommon.h | 661 ++ .../LoRaWAN/Mac/Region/RegionEU433.c | 1068 +++ .../LoRaWAN/Mac/Region/RegionEU433.h | 494 ++ .../LoRaWAN/Mac/Region/RegionEU868.c | 1104 +++ .../LoRaWAN/Mac/Region/RegionEU868.h | 522 ++ .../LoRaWAN/Mac/Region/RegionIN865.c | 1100 +++ .../LoRaWAN/Mac/Region/RegionIN865.h | 517 ++ .../LoRaWAN/Mac/Region/RegionKR920.c | 1087 +++ .../LoRaWAN/Mac/Region/RegionKR920.h | 506 ++ .../LoRaWAN/Mac/Region/RegionNvm.h | 155 + .../LoRaWAN/Mac/Region/RegionRU864.c | 1070 +++ .../LoRaWAN/Mac/Region/RegionRU864.h | 483 ++ .../LoRaWAN/Mac/Region/RegionUS915.c | 1121 ++++ .../LoRaWAN/Mac/Region/RegionUS915.h | 492 ++ .../LoRaWAN/Mac/Region/RegionVersion.h | 48 + .../LoRaWAN/Mac/secure-element-nvm.h | 145 + src/STM32CubeWL/LoRaWAN/Mac/secure-element.h | 308 + src/STM32CubeWL/LoRaWAN/Release_Notes.html | 462 ++ src/STM32CubeWL/LoRaWAN/Utilities/utilities.c | 159 + src/STM32CubeWL/LoRaWAN/Utilities/utilities.h | 216 + .../LoRaWAN/_htmresc/Add button.svg | 2 + src/STM32CubeWL/LoRaWAN/_htmresc/Update.svg | 2 + src/STM32CubeWL/LoRaWAN/_htmresc/favicon.png | Bin 0 -> 4126 bytes .../LoRaWAN/_htmresc/mini-st_2020.css | 1711 +++++ .../LoRaWAN/_htmresc/st_logo_2020.png | Bin 0 -> 7520 bytes src/STM32CubeWL/LoRaWAN/readme.md | 412 ++ src/STM32CubeWL/Package_license.md | 179 + src/STM32CubeWL/SubGHz_Phy/LICENSE | 27 + src/STM32CubeWL/SubGHz_Phy/LICENSE.txt | 6 + src/STM32CubeWL/SubGHz_Phy/Release_Notes.html | 106 + .../SubGHz_Phy/_htmresc/Add button.svg | 2 + .../SubGHz_Phy/_htmresc/Update.svg | 2 + .../SubGHz_Phy/_htmresc/favicon.png | Bin 0 -> 4126 bytes .../SubGHz_Phy/_htmresc/mini-st_2020.css | 1711 +++++ .../SubGHz_Phy/_htmresc/st_logo_2020.png | Bin 0 -> 7520 bytes src/STM32CubeWL/SubGHz_Phy/radio.h | 488 ++ src/STM32CubeWL/SubGHz_Phy/radio_ex.h | 311 + .../SubGHz_Phy/stm32_radio_driver/radio.c | 2232 +++++++ .../stm32_radio_driver/radio_driver.c | 1225 ++++ .../stm32_radio_driver/radio_driver.h | 1275 ++++ .../SubGHz_Phy/stm32_radio_driver/radio_fw.c | 1102 +++ .../SubGHz_Phy/stm32_radio_driver/radio_fw.h | 150 + .../stm32_radio_driver/subghz_phy_version.h | 53 + src/STM32CubeWL/Utilities/misc/LICENSE.txt | 6 + .../Utilities/misc/Release_notes.html | 123 + .../Utilities/misc/stm32_systime.c | 496 ++ .../Utilities/misc/stm32_systime.h | 252 + src/STM32CubeWL/Utilities/timer/LICENSE.txt | 6 + .../Utilities/timer/Release_Notes.html | 248 + .../Utilities/timer/_htmresc/mini-st.css | 1700 +++++ .../Utilities/timer/_htmresc/st_logo.png | Bin 0 -> 18616 bytes src/STM32CubeWL/Utilities/timer/stm32_timer.c | 542 ++ src/STM32CubeWL/Utilities/timer/stm32_timer.h | 292 + 106 files changed, 59044 insertions(+) create mode 100644 src/STM32CubeWL/LoRaWAN/Crypto/cmac.c create mode 100644 src/STM32CubeWL/LoRaWAN/Crypto/cmac.h create mode 100644 src/STM32CubeWL/LoRaWAN/Crypto/lorawan_aes.c create mode 100644 src/STM32CubeWL/LoRaWAN/Crypto/lorawan_aes.h create mode 100644 src/STM32CubeWL/LoRaWAN/Crypto/soft-se.c create mode 100644 src/STM32CubeWL/LoRaWAN/LICENSE create mode 100644 src/STM32CubeWL/LoRaWAN/LICENSE.txt create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/LoRaMac.c create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/LoRaMac.h create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/LoRaMacAdr.c create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/LoRaMacAdr.h create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/LoRaMacClassB.c create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/LoRaMacClassB.h create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/LoRaMacClassBConfig.h create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/LoRaMacClassBNvm.h create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/LoRaMacCommands.c create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/LoRaMacCommands.h create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/LoRaMacConfirmQueue.c create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/LoRaMacConfirmQueue.h create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/LoRaMacCrypto.c create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/LoRaMacCrypto.h create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/LoRaMacCryptoNvm.h create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/LoRaMacHeaderTypes.h create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/LoRaMacInterfaces.h create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/LoRaMacMessageTypes.h create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/LoRaMacParser.c create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/LoRaMacParser.h create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/LoRaMacSerializer.c create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/LoRaMacSerializer.h create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/LoRaMacTest.h create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/LoRaMacTypes.h create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/LoRaMacVersion.h create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/Region/Region.c create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/Region/Region.h create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/Region/RegionAS923.c create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/Region/RegionAS923.h create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/Region/RegionAU915.c create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/Region/RegionAU915.h create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/Region/RegionBaseUS.c create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/Region/RegionBaseUS.h create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCN470.c create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCN470.h create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCN470A20.c create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCN470A20.h create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCN470A26.c create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCN470A26.h create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCN470B20.c create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCN470B20.h create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCN470B26.c create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCN470B26.h create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCN779.c create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCN779.h create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCommon.c create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCommon.h create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/Region/RegionEU433.c create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/Region/RegionEU433.h create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/Region/RegionEU868.c create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/Region/RegionEU868.h create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/Region/RegionIN865.c create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/Region/RegionIN865.h create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/Region/RegionKR920.c create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/Region/RegionKR920.h create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/Region/RegionNvm.h create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/Region/RegionRU864.c create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/Region/RegionRU864.h create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/Region/RegionUS915.c create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/Region/RegionUS915.h create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/Region/RegionVersion.h create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/secure-element-nvm.h create mode 100644 src/STM32CubeWL/LoRaWAN/Mac/secure-element.h create mode 100644 src/STM32CubeWL/LoRaWAN/Release_Notes.html create mode 100644 src/STM32CubeWL/LoRaWAN/Utilities/utilities.c create mode 100644 src/STM32CubeWL/LoRaWAN/Utilities/utilities.h create mode 100644 src/STM32CubeWL/LoRaWAN/_htmresc/Add button.svg create mode 100644 src/STM32CubeWL/LoRaWAN/_htmresc/Update.svg create mode 100644 src/STM32CubeWL/LoRaWAN/_htmresc/favicon.png create mode 100644 src/STM32CubeWL/LoRaWAN/_htmresc/mini-st_2020.css create mode 100644 src/STM32CubeWL/LoRaWAN/_htmresc/st_logo_2020.png create mode 100644 src/STM32CubeWL/LoRaWAN/readme.md create mode 100644 src/STM32CubeWL/Package_license.md create mode 100644 src/STM32CubeWL/SubGHz_Phy/LICENSE create mode 100644 src/STM32CubeWL/SubGHz_Phy/LICENSE.txt create mode 100644 src/STM32CubeWL/SubGHz_Phy/Release_Notes.html create mode 100644 src/STM32CubeWL/SubGHz_Phy/_htmresc/Add button.svg create mode 100644 src/STM32CubeWL/SubGHz_Phy/_htmresc/Update.svg create mode 100644 src/STM32CubeWL/SubGHz_Phy/_htmresc/favicon.png create mode 100644 src/STM32CubeWL/SubGHz_Phy/_htmresc/mini-st_2020.css create mode 100644 src/STM32CubeWL/SubGHz_Phy/_htmresc/st_logo_2020.png create mode 100644 src/STM32CubeWL/SubGHz_Phy/radio.h create mode 100644 src/STM32CubeWL/SubGHz_Phy/radio_ex.h create mode 100644 src/STM32CubeWL/SubGHz_Phy/stm32_radio_driver/radio.c create mode 100644 src/STM32CubeWL/SubGHz_Phy/stm32_radio_driver/radio_driver.c create mode 100644 src/STM32CubeWL/SubGHz_Phy/stm32_radio_driver/radio_driver.h create mode 100644 src/STM32CubeWL/SubGHz_Phy/stm32_radio_driver/radio_fw.c create mode 100644 src/STM32CubeWL/SubGHz_Phy/stm32_radio_driver/radio_fw.h create mode 100644 src/STM32CubeWL/SubGHz_Phy/stm32_radio_driver/subghz_phy_version.h create mode 100644 src/STM32CubeWL/Utilities/misc/LICENSE.txt create mode 100644 src/STM32CubeWL/Utilities/misc/Release_notes.html create mode 100644 src/STM32CubeWL/Utilities/misc/stm32_systime.c create mode 100644 src/STM32CubeWL/Utilities/misc/stm32_systime.h create mode 100644 src/STM32CubeWL/Utilities/timer/LICENSE.txt create mode 100644 src/STM32CubeWL/Utilities/timer/Release_Notes.html create mode 100644 src/STM32CubeWL/Utilities/timer/_htmresc/mini-st.css create mode 100644 src/STM32CubeWL/Utilities/timer/_htmresc/st_logo.png create mode 100644 src/STM32CubeWL/Utilities/timer/stm32_timer.c create mode 100644 src/STM32CubeWL/Utilities/timer/stm32_timer.h diff --git a/src/STM32CubeWL/LoRaWAN/Crypto/cmac.c b/src/STM32CubeWL/LoRaWAN/Crypto/cmac.c new file mode 100644 index 0000000..5aa64fa --- /dev/null +++ b/src/STM32CubeWL/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 "lorawan_aes.h" +#include "cmac.h" +#include "../Utilities/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] ) +{ + lorawan_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 ); + + memcpy1( in, &ctx->X[0], 16 ); // Otherwise it does not look good + lorawan_aes_encrypt( in, in, &ctx->rijndael ); + memcpy1( &ctx->X[0], in, 16 ); + + data += mlen; + len -= mlen; + } + while( len > 16 ) + { /* not last block */ + + XOR( data, ctx->X ); + + memcpy1( in, &ctx->X[0], 16 ); // Otherwise it does not look good + lorawan_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 ); + + lorawan_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 ); + + memcpy1( in, &ctx->X[0], 16 ); // Otherwise it does not look good + lorawan_aes_encrypt( in, digest, &ctx->rijndael ); + memset1( K, 0, sizeof K ); +} diff --git a/src/STM32CubeWL/LoRaWAN/Crypto/cmac.h b/src/STM32CubeWL/LoRaWAN/Crypto/cmac.h new file mode 100644 index 0000000..eda7a3f --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Crypto/cmac.h @@ -0,0 +1,71 @@ +/************************************************************************** +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_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lorawan_aes.h" + +#define AES_CMAC_KEY_LENGTH 16 +#define AES_CMAC_DIGEST_LENGTH 16 + +typedef struct _AES_CMAC_CTX { + lorawan_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 + +#ifdef __cplusplus +} +#endif + +#endif /* _CMAC_H_ */ + diff --git a/src/STM32CubeWL/LoRaWAN/Crypto/lorawan_aes.c b/src/STM32CubeWL/LoRaWAN/Crypto/lorawan_aes.c new file mode 100644 index 0000000..b99db3e --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Crypto/lorawan_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 "lorawan_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 */ +static 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 +} + +static 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 lorawan_aes_set_key( const uint8_t key[], length_type keylen, lorawan_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 lorawan_aes_encrypt( const uint8_t in[N_BLOCK], uint8_t out[N_BLOCK], const lorawan_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 lorawan_aes_cbc_encrypt( const uint8_t *in, uint8_t *out, + int32_t n_block, uint8_t iv[N_BLOCK], const lorawan_aes_context ctx[1] ) +{ + + while(n_block--) + { + xor_block(iv, in); + if(lorawan_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 lorawan_aes_decrypt( const uint8_t in[N_BLOCK], uint8_t out[N_BLOCK], const lorawan_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 lorawan_aes_cbc_decrypt( const uint8_t *in, uint8_t *out, + int32_t n_block, uint8_t iv[N_BLOCK], const lorawan_aes_context ctx[1] ) +{ + while(n_block--) + { uint8_t tmp[N_BLOCK]; + + //memcpy(tmp, in, N_BLOCK); + block_copy(tmp, in); + if(lorawan_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 lorawan_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 lorawan_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 lorawan_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 lorawan_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/src/STM32CubeWL/LoRaWAN/Crypto/lorawan_aes.h b/src/STM32CubeWL/LoRaWAN/Crypto/lorawan_aes.h new file mode 100644 index 0000000..8bd79bc --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Crypto/lorawan_aes.h @@ -0,0 +1,170 @@ +/* + --------------------------------------------------------------------------- + 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 LORAWAN_AES_H +#define LORAWAN_AES_H + +#ifdef __cplusplus +extern "C" { +#endif + +#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; +} lorawan_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 lorawan_aes_set_key( const uint8_t key[], + length_type keylen, + lorawan_aes_context ctx[1] ); +#endif + +#if defined( AES_ENC_PREKEYED ) + +return_type lorawan_aes_encrypt( const uint8_t in[N_BLOCK], + uint8_t out[N_BLOCK], + const lorawan_aes_context ctx[1] ); + +return_type lorawan_aes_cbc_encrypt( const uint8_t *in, + uint8_t *out, + int32_t n_block, + uint8_t iv[N_BLOCK], + const lorawan_aes_context ctx[1] ); +#endif + +#if defined( AES_DEC_PREKEYED ) + +return_type lorawan_aes_decrypt( const uint8_t in[N_BLOCK], + uint8_t out[N_BLOCK], + const lorawan_aes_context ctx[1] ); + +return_type lorawan_aes_cbc_decrypt( const uint8_t *in, + uint8_t *out, + int32_t n_block, + uint8_t iv[N_BLOCK], + const lorawan_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 lorawan_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 lorawan_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 lorawan_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 lorawan_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 + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/STM32CubeWL/LoRaWAN/Crypto/soft-se.c b/src/STM32CubeWL/LoRaWAN/Crypto/soft-se.c new file mode 100644 index 0000000..82f9a7e --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Crypto/soft-se.c @@ -0,0 +1,1321 @@ +/*! + * \file soft-se.c + * + * \brief Secure Element software implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2020 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + */ + +/** + ****************************************************************************** + * + * Portions COPYRIGHT 2020 STMicroelectronics + * + * @file soft-se.c + * @author MCD Application Team + * @brief Secure Element software implementation + ****************************************************************************** + */ + +/* Includes ------------------------------------------------------------------*/ +#include "../../../BSP/lorawan_conf.h" /* LORAWAN_KMS */ +#include "../../SubGHz_Phy/radio.h" /* needed for Random */ +#include "../Utilities/utilities.h" +#include "../../../BSP/mw_log_conf.h" /* needed for MW_LOG */ +#if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) +#include "lorawan_aes.h" +#include "cmac.h" +#else /* LORAWAN_KMS == 1 */ +#include "kms_if.h" +#endif /* LORAWAN_KMS */ + +#include "../Mac/LoRaMacHeaderTypes.h" +#include "../Mac/LoRaMacVersion.h" + +#include "../Mac/secure-element.h" +#include "../Mac/secure-element-nvm.h" +#include "../../../BSP/se-identity.h" + +/* Private constants ---------------------------------------------------------*/ +/*! + * MIC computation offset + * \remark required for 1.1.x support + */ +#define CRYPTO_MIC_COMPUTATION_OFFSET ( JOIN_REQ_TYPE_SIZE\ + + LORAMAC_JOIN_EUI_FIELD_SIZE + DEV_NONCE_SIZE + LORAMAC_MHDR_FIELD_SIZE ) + +#if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) +#else /* LORAWAN_KMS == 1 */ +#define DERIVED_OBJECT_HANDLE_RESET_VAL 0x0UL +#define PAYLOAD_MAX_SIZE 270UL /* 270 PHYPayload: 1+(22+1+242)+4 */ +#endif /* LORAWAN_KMS */ + +/* Private macro -------------------------------------------------------------*/ +/*! + * Hex 8 split buffer + */ +#define HEX8(X) X[0], X[1], X[2], X[3], X[4], X[5], X[6], X[7] + +/*! + * Hex 16 split buffer + */ +#define HEX16(X) HEX8(X), X[8], X[9], X[10], X[11], X[12], X[13], X[14], X[15] + +/*Can be overloaded in lorawan_conf.h*/ +#ifndef SOFT_SE_PLACE_IN_NVM_START +#define SOFT_SE_PLACE_IN_NVM_START +#endif +#ifndef SOFT_SE_PLACE_IN_NVM_STOP +#define SOFT_SE_PLACE_IN_NVM_STOP +#endif + +/* Private Types -------------------------------------------------------------*/ +/* Private variables ---------------------------------------------------------*/ +/*! + * Secure element context + */ +static SecureElementNvmData_t* SeNvm; + +/*if defined, place seNvmInit in memory + * this allows device commissioning + */ +SOFT_SE_PLACE_IN_NVM_START +static const SecureElementNvmData_t seNvmInit = +{ + /*! + * end-device IEEE EUI (big endian) + * + * \remark In this application the value is automatically generated by + * calling BoardGetUniqueId function + */ + .DevEui = LORAWAN_DEVICE_EUI, + /*! + * App/Join server IEEE EUI (big endian) + */ + .JoinEui = LORAWAN_JOIN_EUI, +#if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) + /*! + * LoRaWAN key list + */ + .KeyList = SOFT_SE_KEY_LIST +#endif +}; +SOFT_SE_PLACE_IN_NVM_STOP + +#if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) +#else /* LORAWAN_KMS == 1 */ +/* WARNING: Should be modified at the end of product development */ +static const CK_ULONG GlobalTemplateLabel = 0x444E524CU; + +/* + * Intermediate buffer used for two reasons: + * - align to 32 bits and + * - for Cmac combine InitVector + input buff + */ +static uint8_t input_align_combined_buf[PAYLOAD_MAX_SIZE + SE_KEY_SIZE] ALIGN(4); + +static uint8_t output_align[PAYLOAD_MAX_SIZE] ALIGN(4); + +static uint8_t tag[SE_KEY_SIZE] ALIGN(4) = {0}; +#endif /* LORAWAN_KMS */ + +/* Private functions prototypes ---------------------------------------------------*/ +#if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) +static SecureElementStatus_t GetKeyByID( KeyIdentifier_t keyID, Key_t **keyItem ); +#else /* LORAWAN_KMS == 1 */ +static SecureElementStatus_t GetKeyIndexByID( KeyIdentifier_t keyID, CK_OBJECT_HANDLE *keyIndex ); + +static SecureElementStatus_t GetSpecificLabelByID( KeyIdentifier_t keyID, uint32_t *specificLabel ); + +#if !defined( CONTEXT_MANAGEMENT_ENABLED ) || ( CONTEXT_MANAGEMENT_ENABLED == 0 ) +static SecureElementStatus_t DeleteAllDynamicKeys( void ); +#endif /* CONTEXT_MANAGEMENT_ENABLED == 0 */ +#endif /* LORAWAN_KMS */ + +static void PrintKey( KeyIdentifier_t key ); + +static SecureElementStatus_t ComputeCmac(uint8_t *micBxBuffer, uint8_t *buffer, uint16_t size, KeyIdentifier_t keyID, + uint32_t *cmac); + +/* Private functions ---------------------------------------------------------*/ +static void PrintKey( KeyIdentifier_t key ) +{ +#if (defined (KEY_EXTRACTABLE) && (KEY_EXTRACTABLE == 1)) + SecureElementStatus_t retval = SECURE_ELEMENT_ERROR; +#if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) + Key_t *keyItem; + retval = SecureElementGetKeyByID(key, &keyItem); +#else + uint8_t extractable_key[16] = {0}; + retval = SecureElementGetKeyByID(key, (uint8_t*)extractable_key); +#endif /* LORAWAN_KMS */ + if (retval == SECURE_ELEMENT_SUCCESS) + { + if (key == APP_KEY) + { + MW_LOG(TS_OFF, VLEVEL_M, "###### AppKey: "); + } + else if (key == NWK_KEY) + { + MW_LOG(TS_OFF, VLEVEL_M, "###### NwkKey: "); + } + else if (key == APP_S_KEY) + { + MW_LOG(TS_OFF, VLEVEL_M, "###### AppSKey: "); + } + else if (key == NWK_S_KEY) + { + MW_LOG(TS_OFF, VLEVEL_M, "###### NwkSKey: "); + } + else if (key == MC_ROOT_KEY) + { + MW_LOG(TS_OFF, VLEVEL_M, "###### MCRootKey: "); + } + else if (key == MC_KE_KEY) + { + MW_LOG(TS_OFF, VLEVEL_M, "###### MCKEKey: "); + } + else if (key == MC_KEY_0) + { + MW_LOG(TS_OFF, VLEVEL_M, "###### MCKey_0: "); + } + else if (key == MC_APP_S_KEY_0) + { + MW_LOG(TS_OFF, VLEVEL_M, "###### MCAppSKey_0: "); + } + else if (key == MC_NWK_S_KEY_0) + { + MW_LOG(TS_OFF, VLEVEL_M, "###### MCNwkSKey_0: "); + } +#if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) + MW_LOG(TS_OFF, VLEVEL_M, "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\r\n", + HEX16(keyItem->KeyValue)); +#else + MW_LOG(TS_OFF, VLEVEL_M, "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\r\n", + HEX16(extractable_key)); +#endif /* LORAWAN_KMS */ + } +#endif /* KEY_EXTRACTABLE */ +} + +#if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) +/* + * Gets key item from key list. + * + * \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( SeNvm->KeyList[i].KeyID == keyID ) + { + *keyItem = &( SeNvm->KeyList[i] ); + return SECURE_ELEMENT_SUCCESS; + } + } + return SECURE_ELEMENT_ERROR_INVALID_KEY_ID; +} + +#else /* LORAWAN_KMS == 1 */ + +/* + * Gets key Index from key list in KMS table + * + * \param [in] keyID - Key identifier + * \param [out] keyIndex - Key item reference + * \retval - Status of the operation + */ +static SecureElementStatus_t GetKeyIndexByID( KeyIdentifier_t keyID, CK_OBJECT_HANDLE *keyIndex ) +{ + for (uint8_t i = 0; i < NUM_OF_KEYS; i++) + { + if (SeNvm->KeyList[i].KeyID == keyID) + { + *keyIndex = SeNvm->KeyList[i].Object_Index; + return SECURE_ELEMENT_SUCCESS; + } + } + return SECURE_ELEMENT_ERROR_INVALID_KEY_ID; +} + +static SecureElementStatus_t GetSpecificLabelByID( KeyIdentifier_t keyID, uint32_t *specificLabel ) +{ + SecureElementStatus_t retval = SECURE_ELEMENT_SUCCESS; + switch (keyID) + { + case APP_KEY: + *specificLabel = 0x5F505041U; + break; + case NWK_KEY: + *specificLabel = 0x5F4B574EU; + break; +#if ( USE_LRWAN_1_1_X_CRYPTO == 1 ) + case J_S_INT_KEY: + *specificLabel = 0x314B574EU; + break; + case J_S_ENC_KEY: + *specificLabel = 0x324B574EU; + break; + case F_NWK_S_INT_KEY: + *specificLabel = 0x334B574EU; + break; + case S_NWK_S_INT_KEY: + *specificLabel = 0x344B574EU; + break; + case NWK_S_ENC_KEY: + *specificLabel = 0x354B574EU; + break; +#else /* USE_LRWAN_1_1_X_CRYPTO == 0 */ + case NWK_S_KEY: + *specificLabel = 0x534B574EU; + break; +#endif /* USE_LRWAN_1_1_X_CRYPTO */ + case APP_S_KEY: + *specificLabel = 0x53505041U; + break; + case MC_ROOT_KEY: + *specificLabel = 0x5452434DU; + break; + case MC_KE_KEY: + *specificLabel = 0x454B434DU; + break; + case MC_KEY_0: + *specificLabel = 0x304B434DU; + break; + case MC_APP_S_KEY_0: + *specificLabel = 0x3053414DU; + break; + case MC_NWK_S_KEY_0: + *specificLabel = 0x30534E4DU; + break; +#if ( LORAMAC_MAX_MC_CTX > 1 ) + case MC_KEY_1: + *specificLabel = 0x314B434DU; + break; + case MC_APP_S_KEY_1: + *specificLabel = 0x3153414DU; + break; + case MC_NWK_S_KEY_1: + *specificLabel = 0x31534E4DU; + break; + case MC_KEY_2: + *specificLabel = 0x324B434DU; + break; + case MC_APP_S_KEY_2: + *specificLabel = 0x3253414DU; + break; + case MC_NWK_S_KEY_2: + *specificLabel = 0x32534E4DU; + break; + case MC_KEY_3: + *specificLabel = 0x334B434DU; + break; + case MC_APP_S_KEY_3: + *specificLabel = 0x3353414DU; + break; + case MC_NWK_S_KEY_3: + *specificLabel = 0x33534E4DU; + break; +#endif /* LORAMAC_MAX_MC_CTX > 1 */ + default: + retval = SECURE_ELEMENT_ERROR_INVALID_KEY_ID; + break; + } + return retval; +} + +#if !defined( CONTEXT_MANAGEMENT_ENABLED ) || ( CONTEXT_MANAGEMENT_ENABLED == 0 ) +static SecureElementStatus_t DeleteAllDynamicKeys( void ) +{ + CK_RV rv; + CK_SESSION_HANDLE session; + CK_FLAGS session_flags = CKF_SERIAL_SESSION; /* Read ONLY session */ + CK_OBJECT_HANDLE hObject[NUM_OF_KEYS]; + uint32_t ulCount = 0; + + /* Open session with KMS */ + rv = C_OpenSession(0, session_flags, NULL, 0, &session); + + /* Get all keys handle */ + if (rv == CKR_OK) + { + rv = C_FindObjectsInit(session, NULL, 0); + } + + /* Find all existing keys handle */ + if (rv == CKR_OK) + { + rv = C_FindObjects(session, hObject, NUM_OF_KEYS, (CK_ULONG *) &ulCount); + } + + if (rv == CKR_OK) + { + rv = C_FindObjectsFinal(session); + } + + if (ulCount <= NUM_OF_KEYS) + { + for (uint8_t i = 0; i < ulCount; i++) + { + /* Exclude all Embedded keys */ + if (hObject[i] > LAST_KMS_KEY_OBJECT_HANDLE) + { + if (rv == CKR_OK) + { + rv = C_DestroyObject(session, hObject[i]); + } + } + } + } + + /* Close sessions */ + if (session > 0) + { + (void)C_CloseSession(session); + } + + if (rv != CKR_OK) + { + return SECURE_ELEMENT_ERROR; + } + return SECURE_ELEMENT_SUCCESS; +} +#endif /* CONTEXT_MANAGEMENT_ENABLED == 0 */ +#endif /* LORAWAN_KMS */ + +/* + * 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; + } + +#if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) + uint8_t Cmac[16]; + AES_CMAC_CTX aesCmacCtx[1]; + + AES_CMAC_Init( aesCmacCtx ); + + Key_t* keyItem; + SecureElementStatus_t retval = GetKeyByID( keyID, &keyItem ); + + if( retval == SECURE_ELEMENT_SUCCESS ) + { + AES_CMAC_SetKey( aesCmacCtx, keyItem->KeyValue ); + + if( micBxBuffer != NULL ) + { + AES_CMAC_Update( aesCmacCtx, micBxBuffer, 16 ); + } + + AES_CMAC_Update( aesCmacCtx, buffer, size ); + + AES_CMAC_Final( Cmac, 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] ); + } +#else /* LORAWAN_KMS == 1 */ + CK_RV rv; + CK_SESSION_HANDLE session; + CK_FLAGS session_flags = CKF_SERIAL_SESSION; /* Read ONLY session */ + uint32_t tag_length = 0; + CK_OBJECT_HANDLE key_handle ; + + /* AES CMAC Authentication variables */ + CK_MECHANISM aes_cmac_mechanism = { CKM_AES_CMAC, (CK_VOID_PTR)NULL, 0 }; + + SecureElementStatus_t retval = GetKeyIndexByID(keyID, &key_handle); + if (retval != SECURE_ELEMENT_SUCCESS) + { + return retval; + } + + /* Open session with KMS */ + rv = C_OpenSession(0, session_flags, NULL, 0, &session); + + /* Configure session to Authentication message in AES CMAC with settings included into the mechanism */ + if (rv == CKR_OK) + { + rv = C_SignInit(session, &aes_cmac_mechanism, key_handle); + } + + /* Encrypt clear message */ + if (rv == CKR_OK) + { + /* work around : need to double-check if possible to use micBxBuffer as IV for Sign */ + if (micBxBuffer != NULL) + { + memcpy1((uint8_t *) &input_align_combined_buf[0], (uint8_t *) micBxBuffer, SE_KEY_SIZE); + memcpy1((uint8_t *) &input_align_combined_buf[SE_KEY_SIZE], (uint8_t *) buffer, size); + } + else + { + memcpy1((uint8_t *) &input_align_combined_buf[0], (uint8_t *) buffer, size); + } + } + + if (rv == CKR_OK) + { + if (micBxBuffer != NULL) + { + rv = C_Sign(session, (CK_BYTE_PTR)&input_align_combined_buf[0], size + SE_KEY_SIZE, &tag[0], + (CK_ULONG_PTR)&tag_length); + } + else + { + rv = C_Sign(session, (CK_BYTE_PTR)&input_align_combined_buf[0], size, &tag[0], + (CK_ULONG_PTR)&tag_length); + } + } + + /* Close session with KMS */ + (void)C_CloseSession(session); + + /* combine to a 32bit authentication word (MIC) */ + *cmac = (uint32_t)((uint32_t) tag[3] << 24 | (uint32_t) tag[2] << 16 | (uint32_t) tag[1] << 8 | + (uint32_t) tag[0]); + + if (rv != CKR_OK) + { + retval = SECURE_ELEMENT_ERROR; + } +#endif /* LORAWAN_KMS */ + return retval; +} + +/* Exported functions ---------------------------------------------------------*/ +/* + * API functions + */ +/* ST_WORKAROUND: Add unique ID callback as input parameter */ +SecureElementStatus_t SecureElementInit( SecureElementNvmData_t *nvm, SecureElementGetUniqueId seGetUniqueId ) +{ +#if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) + if( nvm == NULL ) + { + return SECURE_ELEMENT_ERROR_NPE; + } + + // Initialize nvm pointer + SeNvm = nvm; + + // Initialize data + memcpy1( ( uint8_t* )SeNvm, ( uint8_t* )&seNvmInit, sizeof( seNvmInit ) ); +#else /* LORAWAN_KMS == 1 */ + uint8_t itr = 0; + + if (nvm == NULL) + { + return SECURE_ELEMENT_ERROR_NPE; + } + + /* Initialize nvm pointer */ + SeNvm = nvm; + + /* Initialize with defaults */ + memcpy1((uint8_t *)SeNvm->DevEui, seNvmInit.DevEui, SE_EUI_SIZE); + memcpy1((uint8_t *)SeNvm->JoinEui, seNvmInit.JoinEui , SE_EUI_SIZE); + + SeNvm->KeyList[itr++].KeyID = APP_KEY; + SeNvm->KeyList[itr++].KeyID = NWK_KEY; +#if ( USE_LRWAN_1_1_X_CRYPTO == 1 ) + SeNvm->KeyList[itr++].KeyID = J_S_INT_KEY; + SeNvm->KeyList[itr++].KeyID = J_S_ENC_KEY; + SeNvm->KeyList[itr++].KeyID = F_NWK_S_INT_KEY; + SeNvm->KeyList[itr++].KeyID = S_NWK_S_INT_KEY; + SeNvm->KeyList[itr++].KeyID = NWK_S_ENC_KEY; +#else /* USE_LRWAN_1_1_X_CRYPTO == 0 */ + SeNvm->KeyList[itr++].KeyID = NWK_S_KEY; +#endif /* USE_LRWAN_1_1_X_CRYPTO */ + SeNvm->KeyList[itr++].KeyID = APP_S_KEY; + SeNvm->KeyList[itr++].KeyID = MC_ROOT_KEY; + SeNvm->KeyList[itr++].KeyID = MC_KE_KEY; + SeNvm->KeyList[itr++].KeyID = MC_KEY_0; + SeNvm->KeyList[itr++].KeyID = MC_APP_S_KEY_0; + SeNvm->KeyList[itr++].KeyID = MC_NWK_S_KEY_0; +#if ( LORAMAC_MAX_MC_CTX > 1 ) + SeNvm->KeyList[itr++].KeyID = MC_KEY_1; + SeNvm->KeyList[itr++].KeyID = MC_APP_S_KEY_1; + SeNvm->KeyList[itr++].KeyID = MC_NWK_S_KEY_1; + SeNvm->KeyList[itr++].KeyID = MC_KEY_2; + SeNvm->KeyList[itr++].KeyID = MC_APP_S_KEY_2; + SeNvm->KeyList[itr++].KeyID = MC_NWK_S_KEY_2; + SeNvm->KeyList[itr++].KeyID = MC_KEY_3; + SeNvm->KeyList[itr++].KeyID = MC_APP_S_KEY_3; + SeNvm->KeyList[itr++].KeyID = MC_NWK_S_KEY_3; +#endif /*LORAMAC_MAX_MC_CTX > 1 */ + SeNvm->KeyList[itr].KeyID = SLOT_RAND_ZERO_KEY; + +#if !defined( CONTEXT_MANAGEMENT_ENABLED ) || ( CONTEXT_MANAGEMENT_ENABLED == 0 ) + /* Delete all obsolete keys in NVM */ + DeleteAllDynamicKeys(); +#endif /* CONTEXT_MANAGEMENT_ENABLED == 0 */ + + SecureElementSetObjHandler(APP_KEY, KMS_APP_KEY_OBJECT_HANDLE); + SecureElementSetObjHandler(NWK_KEY, KMS_NWK_KEY_OBJECT_HANDLE); + SecureElementSetObjHandler(APP_S_KEY, KMS_APP_S_KEY_OBJECT_HANDLE); + SecureElementSetObjHandler(NWK_S_KEY, KMS_NWK_S_KEY_OBJECT_HANDLE); +#if ( LORAMAC_CLASSB_ENABLED == 1 ) + SecureElementSetObjHandler(SLOT_RAND_ZERO_KEY, KMS_ZERO_KEY_OBJECT_HANDLE); +#endif /* LORAMAC_CLASSB_ENABLED */ +#endif /* LORAWAN_KMS */ + +#if !defined( SECURE_ELEMENT_PRE_PROVISIONED ) +#if( STATIC_DEVICE_EUI == 0 ) + if (seGetUniqueId != NULL) + { + // Get a DevEUI from MCU unique ID + seGetUniqueId(SeNvm->DevEui); + } +#endif /* STATIC_DEVICE_EUI */ +#endif /* !SECURE_ELEMENT_PRE_PROVISIONED */ + return SECURE_ELEMENT_SUCCESS; +} + +/* ST_WORKAROUND_BEGIN: Add KMS specific functions */ +#if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) +SecureElementStatus_t SecureElementGetKeyByID( KeyIdentifier_t keyID, Key_t **keyItem) +#else +SecureElementStatus_t SecureElementGetKeyByID( KeyIdentifier_t keyID, uint8_t* extractable_key ) +#endif /* LORAWAN_KMS */ +{ +#if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) +#if (defined (KEY_EXTRACTABLE) && (KEY_EXTRACTABLE == 1)) + for (uint8_t i = 0; i < NUM_OF_KEYS; i++) + { + if (SeNvm->KeyList[i].KeyID == keyID) + { + *keyItem = &(SeNvm->KeyList[i]); + return SECURE_ELEMENT_SUCCESS; + } + } +#endif /* KEY_EXTRACTABLE */ + return SECURE_ELEMENT_ERROR_INVALID_KEY_ID; +#else /* LORAWAN_KMS == 1 */ + CK_RV rv; + CK_SESSION_HANDLE session; + CK_FLAGS session_flags = CKF_SERIAL_SESSION; /* Read ONLY session */ + CK_OBJECT_HANDLE key_handle = (CK_OBJECT_HANDLE)(~0UL); + CK_ULONG derive_key_template_class = CKO_SECRET_KEY; + + CK_ATTRIBUTE key_attribute_template = {CKA_VALUE, (CK_VOID_PTR) &derive_key_template_class, 16UL}; + uint8_t index_keylist = 0; + for (index_keylist = 0; index_keylist < NUM_OF_KEYS; index_keylist++) + { + if (SeNvm->KeyList[index_keylist].KeyID == keyID) + { + key_handle = SeNvm->KeyList[index_keylist].Object_Index; + break; + } + } + if (key_handle == (CK_OBJECT_HANDLE)(~0UL)) + { + return SECURE_ELEMENT_ERROR_INVALID_KEY_ID; + } + + /* Open session with KMS */ + rv = C_OpenSession(0, session_flags, NULL, 0, &session); + + /* Get key to display */ + if (rv == CKR_OK) + { + key_attribute_template.pValue = extractable_key; + rv = C_GetAttributeValue(session, key_handle, &key_attribute_template, 1UL); + } + + /* Close sessions */ + (void)C_CloseSession(session); + + if (rv != CKR_OK) + { + return SECURE_ELEMENT_ERROR; + } + return SECURE_ELEMENT_SUCCESS; + +#endif /* LORAWAN_KMS == 1 */ +} + +SecureElementStatus_t SecureElementPrintKeys( void ) +{ +#if (defined (KEY_EXTRACTABLE) && (KEY_EXTRACTABLE == 1)) + MW_LOG(TS_OFF, VLEVEL_M, "###### OTAA ######\r\n"); + PrintKey(APP_KEY); + PrintKey(NWK_KEY); + MW_LOG(TS_OFF, VLEVEL_M, "###### ABP ######\r\n"); + PrintKey(APP_S_KEY); + PrintKey(NWK_S_KEY); +#endif /* KEY_EXTRACTABLE */ + MW_LOG(TS_OFF, VLEVEL_M, "###### IDs ######\r\n"); + MW_LOG(TS_OFF, VLEVEL_M, "###### DevEui: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\r\n", + HEX8(SeNvm->DevEui)); + MW_LOG(TS_OFF, VLEVEL_M, "###### AppEui: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\r\n", + HEX8(SeNvm->JoinEui)); + return SECURE_ELEMENT_SUCCESS; +} + +SecureElementStatus_t SecureElementPrintSessionKeys( void ) +{ +#if (defined (KEY_EXTRACTABLE) && (KEY_EXTRACTABLE == 1)) + PrintKey(MC_ROOT_KEY); + PrintKey(MC_KE_KEY); +#if( USE_LRWAN_1_1_X_CRYPTO == 1 ) + PrintKey(F_NWK_S_INT_KEY); + PrintKey(S_NWK_S_INT_KEY); + PrintKey(NWK_S_ENC_KEY); +#else + PrintKey(NWK_S_KEY); +#endif /* USE_LRWAN_1_1_X_CRYPTO == 1 */ + PrintKey(APP_S_KEY); +#endif /* KEY_EXTRACTABLE */ + return SECURE_ELEMENT_SUCCESS; +} + +SecureElementStatus_t SecureElementDeleteDynamicKeys( KeyIdentifier_t keyID, uint32_t *key_label ) +{ +#if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) + return SECURE_ELEMENT_ERROR; +#else /* LORAWAN_KMS == 1 */ + CK_RV rv; + CK_SESSION_HANDLE session; + CK_FLAGS session_flags = CKF_SERIAL_SESSION; /* Read ONLY session */ + CK_OBJECT_HANDLE hObject[NUM_OF_KEYS]; + CK_ULONG local_template_label[] = {GlobalTemplateLabel, 0UL}; + CK_ATTRIBUTE dynamic_key_template = {CKA_LABEL, (CK_VOID_PTR)local_template_label, sizeof(local_template_label)}; + uint32_t ulCount = 0; + + if (SECURE_ELEMENT_SUCCESS != GetSpecificLabelByID(keyID, &local_template_label[1])) + { + return SECURE_ELEMENT_ERROR; + } + *key_label = local_template_label[1]; + + /* Open session with KMS */ + rv = C_OpenSession(0, session_flags, NULL, 0, &session); + + /* Search from Template pattern */ + if (rv == CKR_OK) + { + rv = C_FindObjectsInit(session, &dynamic_key_template, sizeof(dynamic_key_template) / sizeof(CK_ATTRIBUTE)); + } + + /* Find all existing keys handle Template pattern */ + if (rv == CKR_OK) + { + rv = C_FindObjects(session, hObject, NUM_OF_KEYS, (CK_ULONG *) &ulCount); + } + + if (rv == CKR_OK) + { + rv = C_FindObjectsFinal(session); + } + + if (ulCount <= NUM_OF_KEYS) + { + for (uint8_t i = 0; i < ulCount; i++) + { + if (rv == CKR_OK) + { + rv = C_DestroyObject(session, hObject[i]); + } + } + } + + /* Close sessions */ + if (session > 0) + { + (void)C_CloseSession(session); + } + + if (rv != CKR_OK) + { + return SECURE_ELEMENT_ERROR; + } + return SECURE_ELEMENT_SUCCESS; +#endif /* LORAWAN_KMS == 1 */ +} + +SecureElementStatus_t SecureElementSetObjHandler( KeyIdentifier_t keyID, uint32_t keyIndex ) +{ +#if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) + return SECURE_ELEMENT_ERROR; +#else /* LORAWAN_KMS == 1 */ + for (uint8_t i = 0; i < NUM_OF_KEYS; i++) + { + if (SeNvm->KeyList[i].KeyID == keyID) + { + SeNvm->KeyList[i].Object_Index = (CK_OBJECT_HANDLE) keyIndex; + return SECURE_ELEMENT_SUCCESS; + } + } + return SECURE_ELEMENT_ERROR_INVALID_KEY_ID; +#endif /* LORAWAN_KMS */ +} +/* ST_WORKAROUND_END */ + +SecureElementStatus_t SecureElementSetKey( KeyIdentifier_t keyID, uint8_t* key ) +{ + if( key == NULL ) + { + return SECURE_ELEMENT_ERROR_NPE; + } + +#if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) + for( uint8_t i = 0; i < NUM_OF_KEYS; i++ ) + { + if( SeNvm->KeyList[i].KeyID == keyID ) + { + /* ST_WORKAROUND_BEGIN: reduced LORAMAC_MAX_MC_CTX */ +#if ( LORAMAC_MAX_MC_CTX == 1 ) + if ( keyID == MC_KEY_0 ) +#else /* LORAMAC_MAX_MC_CTX > 1 */ + if( ( keyID == MC_KEY_0 ) || ( keyID == MC_KEY_1 ) || ( keyID == MC_KEY_2 ) || ( keyID == MC_KEY_3 ) ) +#endif /* LORAMAC_MAX_MC_CTX */ + /* ST_WORKAROUND_END */ + { // 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( SeNvm->KeyList[i].KeyValue, decryptedKey, SE_KEY_SIZE ); + return retval; + } + else + { + memcpy1( SeNvm->KeyList[i].KeyValue, key, SE_KEY_SIZE ); + return SECURE_ELEMENT_SUCCESS; + } + } + } + + return SECURE_ELEMENT_ERROR_INVALID_KEY_ID; +#else /* LORAWAN_KMS == 1 */ + SecureElementStatus_t retval = SECURE_ELEMENT_ERROR; + CK_RV rv; + CK_SESSION_HANDLE session; + CK_FLAGS session_flags = CKF_SERIAL_SESSION; /* Read ONLY session */ + CK_OBJECT_HANDLE key_handle; + CK_ULONG template_class = CKO_SECRET_KEY; + CK_ULONG template_type = CKK_AES; +#if (defined (KEY_EXTRACTABLE) && (KEY_EXTRACTABLE == 1)) + CK_ULONG template_true = CK_TRUE; +#else + CK_ULONG template_false = CK_FALSE; +#endif /* KEY_EXTRACTABLE */ + uint32_t key_ui32[] = + { + key[3] | (key[2] << 8) | (key[1] << 16) | (key[0] << 24), + key[7] | (key[6] << 8) | (key[5] << 16) | (key[4] << 24), + key[11] | (key[10] << 8) | (key[9] << 16) | (key[8] << 24), + key[15] | (key[14] << 8) | (key[13] << 16) | (key[12] << 24), + }; + + uint32_t specific_label[] = {GlobalTemplateLabel, 0UL}; + CK_ATTRIBUTE key_attribute_template[] = + { + { CKA_CLASS, (CK_VOID_PTR) &template_class, sizeof(CK_BBOOL) }, + { CKA_KEY_TYPE, (CK_VOID_PTR) &template_type, sizeof(CK_BBOOL) }, + { CKA_VALUE, (CK_VOID_PTR)key_ui32, sizeof(key_ui32) }, +#if (defined (KEY_EXTRACTABLE) && (KEY_EXTRACTABLE == 1)) + { CKA_EXTRACTABLE, (CK_VOID_PTR) &template_true, sizeof(CK_BBOOL) }, +#else + { CKA_EXTRACTABLE, (CK_VOID_PTR) &template_false, sizeof(CK_BBOOL) }, +#endif /* KEY_EXTRACTABLE */ + { CKA_LABEL, (CK_VOID_PTR)specific_label, sizeof(specific_label)}, + }; + + /* ST_WORKAROUND_BEGIN: reduced LORAMAC_MAX_MC_CTX */ +#if ( LORAMAC_MAX_MC_CTX == 1 ) + if ( keyID == MC_KEY_0 ) +#else /* LORAMAC_MAX_MC_CTX > 1 */ + if( ( keyID == MC_KEY_0 ) || ( keyID == MC_KEY_1 ) || ( keyID == MC_KEY_2 ) || ( keyID == MC_KEY_3 ) ) +#endif /* LORAMAC_MAX_MC_CTX */ + /* ST_WORKAROUND_END */ + { // Decrypt the key if its a Mckey + uint8_t decryptedKey[16] = { 0 }; + if (SECURE_ELEMENT_SUCCESS != SecureElementAesEncrypt( key, 16, MC_KE_KEY, decryptedKey )) + { + return SECURE_ELEMENT_ERROR; + } + + key_ui32[0] = decryptedKey[3] | (decryptedKey[2] << 8) | (decryptedKey[1] << 16) | (decryptedKey[0] << 24); + key_ui32[1] = decryptedKey[7] | (decryptedKey[6] << 8) | (decryptedKey[5] << 16) | (decryptedKey[4] << 24); + key_ui32[2] = decryptedKey[11] | (decryptedKey[10] << 8) | (decryptedKey[9] << 16) | (decryptedKey[8] << 24); + key_ui32[3] = decryptedKey[15] | (decryptedKey[14] << 8) | (decryptedKey[13] << 16) | (decryptedKey[12] << 24); + } + + if (SECURE_ELEMENT_SUCCESS != GetKeyIndexByID(keyID, &key_handle)) + { + return SECURE_ELEMENT_ERROR; + } + + if (SECURE_ELEMENT_SUCCESS != SecureElementDeleteDynamicKeys(keyID, &specific_label[1])) + { + return SECURE_ELEMENT_ERROR; + } + + /* Open session with KMS */ + rv = C_OpenSession(0, session_flags, NULL, 0, &session); + + /* Get key to display */ + if (rv == CKR_OK) + { + rv = C_CreateObject(session, key_attribute_template, sizeof(key_attribute_template) / sizeof(CK_ATTRIBUTE), + &key_handle); + } + + if (rv == CKR_OK) + { + retval = SecureElementSetObjHandler(keyID, key_handle); + } + + /* Close sessions */ + (void)C_CloseSession(session); + + if (rv != CKR_OK) + { + retval = SECURE_ELEMENT_ERROR; + } + return retval; +#endif /* LORAWAN_KMS */ +} + +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; +#if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) + 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; + } +#else /* LORAWAN_KMS == 1 */ + CK_RV rv; + CK_SESSION_HANDLE session; + CK_FLAGS session_flags = CKF_SERIAL_SESSION; /* Read ONLY session */ + CK_OBJECT_HANDLE object_handle; + + if (buffer == NULL) + { + return SECURE_ELEMENT_ERROR_NPE; + } + + /* AES CMAC Authentication variables */ + CK_MECHANISM aes_cmac_mechanism = { CKM_AES_CMAC, (CK_VOID_PTR)NULL, 0 }; + + retval = GetKeyIndexByID(keyID, &object_handle); + if (retval != SECURE_ELEMENT_SUCCESS) + { + return retval; + } + + /* Open session with KMS */ + rv = C_OpenSession(0, session_flags, NULL, 0, &session); + + /* Configure session to Verify the message in AES CMAC with settings included into the mechanism */ + if (rv == CKR_OK) + { + rv = C_VerifyInit(session, &aes_cmac_mechanism, object_handle); + } + + /* Verify the message */ + if (rv == CKR_OK) + { + memcpy1(input_align_combined_buf, buffer, size); + rv = C_Verify(session, (CK_BYTE_PTR)input_align_combined_buf, size, (CK_BYTE_PTR)&expectedCmac, 4); + } + + (void)C_CloseSession(session); + + if (rv != CKR_OK) + { + retval = SECURE_ELEMENT_ERROR; + } + +#endif /* LORAWAN_KMS */ + + 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; + } + +#if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) + lorawan_aes_context aesContext; + memset1( aesContext.ksch, '\0', 240 ); + + Key_t* pItem; + SecureElementStatus_t retval = GetKeyByID( keyID, &pItem ); + + if( retval == SECURE_ELEMENT_SUCCESS ) + { + lorawan_aes_set_key(pItem->KeyValue, 16, &aesContext); + + uint8_t block = 0; + + while( size != 0 ) + { + lorawan_aes_encrypt(&buffer[block], &encBuffer[block], &aesContext); + block = block + 16; + size = size - 16; + } + } +#else /* LORAWAN_KMS == 1 */ + CK_RV rv; + CK_SESSION_HANDLE session; + CK_FLAGS session_flags = CKF_SERIAL_SESSION; /* Read ONLY session */ + uint32_t encrypted_length = 0; + CK_OBJECT_HANDLE object_handle; + uint8_t dummy_tag[SE_KEY_SIZE] = {0}; + uint32_t dummy_tag_length = 0; + + CK_MECHANISM aes_ecb_mechanism = { CKM_AES_ECB, (CK_VOID_PTR *) NULL, 0 }; + + SecureElementStatus_t retval = GetKeyIndexByID(keyID, &object_handle); + if (retval != SECURE_ELEMENT_SUCCESS) + { + return retval; + } + + /* Open session with KMS */ + rv = C_OpenSession(0, session_flags, NULL, 0, &session); + + /* Configure session to encrypt message in AES ECB with settings included into the mechanism */ + if (rv == CKR_OK) + { + rv = C_EncryptInit(session, &aes_ecb_mechanism, object_handle); + } + + /* Encrypt clear message */ + if (rv == CKR_OK) + { + memcpy1(input_align_combined_buf, buffer, size); + encrypted_length = sizeof(output_align); + rv = C_EncryptUpdate(session, (CK_BYTE_PTR)input_align_combined_buf, size, + output_align, (CK_ULONG_PTR)&encrypted_length); + memcpy1(encBuffer, output_align, size); + } + + /* In this case C_EncryptFinal is just called to Free the Alloc mem */ + if (rv == CKR_OK) + { + dummy_tag_length = sizeof(tag); + rv = C_EncryptFinal(session, &dummy_tag[0], (CK_ULONG_PTR)&dummy_tag_length); + } + + /* Close session with KMS */ + (void)C_CloseSession(session); + + if (rv != CKR_OK) + { + retval = SECURE_ELEMENT_ERROR; + } +#endif /* LORAWAN_KMS */ + + return retval; +} + +SecureElementStatus_t SecureElementDeriveAndStoreKey( uint8_t* input, KeyIdentifier_t rootKeyID, + KeyIdentifier_t targetKeyID ) +{ + if( input == NULL ) + { + return SECURE_ELEMENT_ERROR_NPE; + } + + SecureElementStatus_t retval = SECURE_ELEMENT_ERROR; + + // In case of MC_KE_KEY, only McRootKey can be used as root key + if( targetKeyID == MC_KE_KEY ) + { + if( rootKeyID != MC_ROOT_KEY ) + { + return SECURE_ELEMENT_ERROR_INVALID_KEY_ID; + } + } + +#if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) + uint8_t key[16] = { 0 }; + // 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; +#else /* LORAWAN_KMS == 1 */ + CK_RV rv; + CK_SESSION_HANDLE session; + CK_FLAGS session_flags = CKF_SERIAL_SESSION; /* Read ONLY session */ + /* Key derivation */ + CK_MECHANISM mech = {CKM_AES_ECB_ENCRYPT_DATA, input, SE_KEY_SIZE}; + CK_OBJECT_HANDLE derived_object_handle; + CK_OBJECT_HANDLE rootkey_object_handle; + uint32_t specific_label[] = {GlobalTemplateLabel, 0UL}; + CK_ATTRIBUTE DeriveKey_template = {CKA_LABEL, (CK_VOID_PTR)specific_label, sizeof(specific_label)}; + + /* Derive key */ + if (SECURE_ELEMENT_SUCCESS != GetKeyIndexByID(rootKeyID, &rootkey_object_handle)) + { + return SECURE_ELEMENT_ERROR; + } + + if (SECURE_ELEMENT_SUCCESS != GetKeyIndexByID(targetKeyID, &derived_object_handle)) + { + return SECURE_ELEMENT_ERROR; + } + + if (SECURE_ELEMENT_SUCCESS != SecureElementDeleteDynamicKeys(targetKeyID, &specific_label[1])) + { + return SECURE_ELEMENT_ERROR; + } + + /* Open session with KMS */ + rv = C_OpenSession(0, session_flags, NULL, 0, &session); + + /* Derive key with pass phrase */ + if (rv == CKR_OK) + { + rv = C_DeriveKey(session, &(mech), rootkey_object_handle, + &DeriveKey_template, sizeof(DeriveKey_template) / sizeof(CK_ATTRIBUTE), &derived_object_handle); + } + + if (rv == CKR_OK) + { + /* Store Derived Index in table */ + retval = SecureElementSetObjHandler(targetKeyID, derived_object_handle); + } + + /* Close session with KMS */ + (void)C_CloseSession(session); + + if (rv != CKR_OK) + { + retval = SECURE_ELEMENT_ERROR; + } + return retval; +#endif /* LORAWAN_KMS */ +} + +SecureElementStatus_t SecureElementProcessJoinAccept( JoinReqIdentifier_t joinReqType, uint8_t* joinEui, + uint16_t devNonce, uint8_t* encJoinAccept, + uint8_t encJoinAcceptSize, uint8_t* decJoinAccept, + uint8_t* versionMinor ) +{ + if( ( encJoinAccept == NULL ) || ( decJoinAccept == NULL ) || ( versionMinor == NULL ) ) + { + return SECURE_ELEMENT_ERROR_NPE; + } + + // Check that frame size isn't bigger than a JoinAccept with CFList size + if( encJoinAcceptSize > LORAMAC_JOIN_ACCEPT_FRAME_MAX_SIZE ) + { + return SECURE_ELEMENT_ERROR_BUF_SIZE; + } + + // Determine decryption key + KeyIdentifier_t encKeyID = NWK_KEY; + + /* ST_WORKAROUND_BEGIN: Keep NWK_KEY if 1.1.x keys are not defined */ +#if ( USE_LRWAN_1_1_X_CRYPTO == 1 ) + if( joinReqType != JOIN_REQ ) + { + encKeyID = J_S_ENC_KEY; + } +#endif /* USE_LRWAN_1_1_X_CRYPTO == 1 */ + + memcpy1( decJoinAccept, encJoinAccept, encJoinAcceptSize ); + + // Decrypt JoinAccept, skip MHDR + if( SecureElementAesEncrypt( encJoinAccept + LORAMAC_MHDR_FIELD_SIZE, encJoinAcceptSize - LORAMAC_MHDR_FIELD_SIZE, + encKeyID, decJoinAccept + LORAMAC_MHDR_FIELD_SIZE ) != SECURE_ELEMENT_SUCCESS ) + { + return SECURE_ELEMENT_FAIL_ENCRYPT; + } + + *versionMinor = ( ( decJoinAccept[11] & 0x80 ) == 0x80 ) ? 1 : 0; + + uint32_t mic = 0; + + mic = ( ( uint32_t ) decJoinAccept[encJoinAcceptSize - LORAMAC_MIC_FIELD_SIZE] << 0 ); + mic |= ( ( uint32_t ) decJoinAccept[encJoinAcceptSize - LORAMAC_MIC_FIELD_SIZE + 1] << 8 ); + mic |= ( ( uint32_t ) decJoinAccept[encJoinAcceptSize - LORAMAC_MIC_FIELD_SIZE + 2] << 16 ); + mic |= ( ( uint32_t ) decJoinAccept[encJoinAcceptSize - LORAMAC_MIC_FIELD_SIZE + 3] << 24 ); + + // - Header buffer to be used for MIC computation + // - LoRaWAN 1.0.x : micHeader = [MHDR(1)] + // - LoRaWAN 1.1.x : micHeader = [JoinReqType(1), JoinEUI(8), DevNonce(2), MHDR(1)] + + // Verify mic + if( *versionMinor == 0 ) + { + // For LoRaWAN 1.0.x + // cmac = aes128_cmac(NwkKey, MHDR | JoinNonce | NetID | DevAddr | DLSettings | RxDelay | CFList | + // CFListType) + if( SecureElementVerifyAesCmac( decJoinAccept, ( encJoinAcceptSize - LORAMAC_MIC_FIELD_SIZE ), mic, NWK_KEY ) != + SECURE_ELEMENT_SUCCESS ) + { + return SECURE_ELEMENT_FAIL_CMAC; + } + } +#if( USE_LRWAN_1_1_X_CRYPTO == 1 ) + else if( *versionMinor == 1 ) + { + uint8_t micHeader11[JOIN_ACCEPT_MIC_COMPUTATION_OFFSET] = { 0 }; + uint16_t bufItr = 0; + + micHeader11[bufItr++] = ( uint8_t ) joinReqType; + + memcpyr( micHeader11 + bufItr, joinEui, LORAMAC_JOIN_EUI_FIELD_SIZE ); + bufItr += LORAMAC_JOIN_EUI_FIELD_SIZE; + + micHeader11[bufItr++] = devNonce & 0xFF; + micHeader11[bufItr++] = ( devNonce >> 8 ) & 0xFF; + + // For LoRaWAN 1.1.x 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) + uint8_t localBuffer[LORAMAC_JOIN_ACCEPT_FRAME_MAX_SIZE + JOIN_ACCEPT_MIC_COMPUTATION_OFFSET] = { 0 }; + + memcpy1( localBuffer, micHeader11, JOIN_ACCEPT_MIC_COMPUTATION_OFFSET ); + memcpy1( localBuffer + JOIN_ACCEPT_MIC_COMPUTATION_OFFSET - 1, decJoinAccept, encJoinAcceptSize ); + + if( SecureElementVerifyAesCmac( localBuffer, + encJoinAcceptSize + JOIN_ACCEPT_MIC_COMPUTATION_OFFSET - + LORAMAC_MHDR_FIELD_SIZE - LORAMAC_MIC_FIELD_SIZE, + mic, J_S_INT_KEY ) != SECURE_ELEMENT_SUCCESS ) + { + return SECURE_ELEMENT_FAIL_CMAC; + } + } +#endif /* USE_LRWAN_1_1_X_CRYPTO == 1 */ + else + { + return SECURE_ELEMENT_ERROR_INVALID_LORAWAM_SPEC_VERSION; + } + + return SECURE_ELEMENT_SUCCESS; +} + +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) +SecureElementStatus_t SecureElementRandomNumber( uint32_t* randomNum ) +{ + if( randomNum == NULL ) + { + return SECURE_ELEMENT_ERROR_NPE; + } + *randomNum = Radio.Random(); + return SECURE_ELEMENT_SUCCESS; +} +#endif /* LORAMAC_VERSION */ + +SecureElementStatus_t SecureElementSetDevEui( uint8_t* devEui ) +{ + if( devEui == NULL ) + { + return SECURE_ELEMENT_ERROR_NPE; + } + memcpy1( SeNvm->DevEui, devEui, SE_EUI_SIZE ); + return SECURE_ELEMENT_SUCCESS; +} + +uint8_t* SecureElementGetDevEui( void ) +{ + return SeNvm->DevEui; +} + +SecureElementStatus_t SecureElementSetJoinEui( uint8_t* joinEui ) +{ + if( joinEui == NULL ) + { + return SECURE_ELEMENT_ERROR_NPE; + } + memcpy1( SeNvm->JoinEui, joinEui, SE_EUI_SIZE ); + return SECURE_ELEMENT_SUCCESS; +} + +uint8_t* SecureElementGetJoinEui( void ) +{ + return SeNvm->JoinEui; +} + diff --git a/src/STM32CubeWL/LoRaWAN/LICENSE b/src/STM32CubeWL/LoRaWAN/LICENSE new file mode 100644 index 0000000..1a60a54 --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/LICENSE @@ -0,0 +1,27 @@ +The Clear BSD License +Copyright Semtech Corporation 2021. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted (subject to the limitations in the disclaimer +below) 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. + +NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY +THIS LICENSE. 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 CORPORATION 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/src/STM32CubeWL/LoRaWAN/LICENSE.txt b/src/STM32CubeWL/LoRaWAN/LICENSE.txt new file mode 100644 index 0000000..3edc4d1 --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/LICENSE.txt @@ -0,0 +1,6 @@ +This software component is provided to you as part of a software package and +applicable license terms are in the Package_license file. If you received this +software component outside of a package or without applicable license terms, +the terms of the BSD-3-Clause license shall apply. +You may obtain a copy of the BSD-3-Clause at: +https://opensource.org/licenses/BSD-3-Clause diff --git a/src/STM32CubeWL/LoRaWAN/Mac/LoRaMac.c b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMac.c new file mode 100644 index 0000000..f66f68e --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMac.c @@ -0,0 +1,5891 @@ +/*! + * \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 ) + */ +/** + ****************************************************************************** + * + * Portions COPYRIGHT 2020 STMicroelectronics + * + * @file LoRaMac.c + * @author MCD Application Team + * @brief LoRa MAC layer implementation + ****************************************************************************** + */ +#include "../Utilities/utilities.h" +#include "Region/Region.h" +#include "LoRaMacClassB.h" +#include "secure-element.h" +#include "LoRaMacTest.h" +#include "LoRaMacConfirmQueue.h" +#include "LoRaMacMessageTypes.h" +#include "LoRaMacParser.h" +#include "LoRaMacCommands.h" +#include "LoRaMacAdr.h" +#include "LoRaMacSerializer.h" +#include "LoRaMacVersion.h" +#include "../../SubGHz_Phy/radio.h" + +#include "LoRaMac.h" +#include "../../../BSP/mw_log_conf.h" + +#if (defined( LORAMAC_VERSION ) && (( LORAMAC_VERSION == 0x01000300 ) || ( LORAMAC_VERSION == 0x01000400 ))) +#else +#error LORAMAC_VERSION not valid +#endif /* LORAMAC_VERSION */ +/*! + * Maximum PHY layer payload size + */ +#define LORAMAC_PHY_MAXPAYLOAD 255 + +/*! + * 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 + +/*! + * Maximum value for the ADR ack counter + */ +#define ADR_ACK_COUNTER_MAX 0xFFFFFFFF + +#if defined(__ICCARM__) +#ifndef __NO_INIT +#define __NO_INIT __no_init +#endif +#ifndef __ROOT +#define __ROOT __root +#endif +#else +#ifndef __NO_INIT +#define __NO_INIT +#endif +#ifndef __ROOT +#define __ROOT +#endif +#endif +/*! + * 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 }; + +/*! + * 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 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 downlink response before the ADRACKReq bit will be set. + */ + uint16_t AdrAckLimit; + /*! + * Limit of uplinks without any downlink 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. + */ +#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) + TimerEvent_t AckTimeoutTimer; +#elif (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + TimerEvent_t RetransmitTimeoutTimer; +#endif /* LORAMAC_VERSION */ + /*! + * Uplink messages repetitions counter + */ + uint8_t ChannelsNbTransCounter; +#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) + /* + * 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; +#elif (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + /*! + * Indicates if the AckTimeout timer has expired or not + */ + bool RetransmitTimeoutRetry; +#endif /* LORAMAC_VERSION */ + /*! + * 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; + /*! + * Structure to hold global Rx Status. + */ + LoRaMacRxStatus_t RxStatus; + /*! + * 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; + /*! + * Duty cycle wait time + */ + TimerTime_t DutyCycleWaitTime; +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + /*! + * Start time of the response timeout + */ + TimerTime_t ResponseTimeoutStartTime; +#endif /* LORAMAC_VERSION */ + /*! + * Buffer containing the MAC layer commands + */ + uint8_t MacCommandsBuffer[LORA_MAC_COMMAND_MAX_LENGTH]; +}LoRaMacCtx_t; + +/*! + * Module context. + */ +static LoRaMacCtx_t MacCtx; + +#if defined(__ICCARM__) +__NO_INIT __ROOT static LoRaMacNvmData_t Nvm @ ".LW_NVM_RAM"; +#elif defined(__GNUC__) +__attribute__((section(".bss.LW_NVM_RAM"))) +__NO_INIT __ROOT static LoRaMacNvmData_t Nvm; +#else +#warning NVM RAM placement not defined +__NO_INIT __ROOT static LoRaMacNvmData_t Nvm; +#endif + +#if defined(__ICCARM__) +__NO_INIT __ROOT static LoRaMacNvmData_t NvmBackup @ ".LW_NVM_BACKUP_RAM"; +#elif defined(__GNUC__) +__attribute__((section(".bss.LW_NVM_BACKUP_RAM"))) +__NO_INIT __ROOT static LoRaMacNvmData_t NvmBackup; +#else +#warning NVM RAM placement not defined +__NO_INIT __ROOT static LoRaMacNvmData_t NvmBackup; +#endif + +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) +static Band_t RegionBands[REGION_NVM_MAX_NB_BANDS]; +#endif /* LORAMAC_VERSION */ + +/* ST_WORKAROUND_BEGIN: reduced LORAMAC_MAX_MC_CTX */ +static const KeyIdentifier_t MCKeys[LORAMAC_MAX_MC_CTX] = { +#if ( LORAMAC_MAX_MC_CTX > 0 ) + MC_KEY_0, +#endif /* LORAMAC_MAX_MC_CTX > 0 */ +#if ( LORAMAC_MAX_MC_CTX > 1 ) + MC_KEY_1, +#endif /* LORAMAC_MAX_MC_CTX > 1 */ +#if ( LORAMAC_MAX_MC_CTX > 2 ) + MC_KEY_2, +#endif /* LORAMAC_MAX_MC_CTX > 2 */ +#if ( LORAMAC_MAX_MC_CTX > 3 ) + MC_KEY_3, +#endif /* LORAMAC_MAX_MC_CTX > 3 */ +}; + +static const KeyIdentifier_t MCAppSKeys[LORAMAC_MAX_MC_CTX] = { +#if ( LORAMAC_MAX_MC_CTX > 0 ) + MC_APP_S_KEY_0, +#endif /* LORAMAC_MAX_MC_CTX > 0 */ +#if ( LORAMAC_MAX_MC_CTX > 1 ) + MC_APP_S_KEY_1, +#endif /* LORAMAC_MAX_MC_CTX > 1 */ +#if ( LORAMAC_MAX_MC_CTX > 2 ) + MC_APP_S_KEY_2, +#endif /* LORAMAC_MAX_MC_CTX > 2 */ +#if ( LORAMAC_MAX_MC_CTX > 3 ) + MC_APP_S_KEY_3, +#endif /* LORAMAC_MAX_MC_CTX > 3 */ +}; +static const KeyIdentifier_t MCNwkSKeys[LORAMAC_MAX_MC_CTX] = { +#if ( LORAMAC_MAX_MC_CTX > 0 ) + MC_NWK_S_KEY_0, +#endif /* LORAMAC_MAX_MC_CTX > 0 */ +#if ( LORAMAC_MAX_MC_CTX > 1 ) + MC_NWK_S_KEY_1, +#endif /* LORAMAC_MAX_MC_CTX > 1 */ +#if ( LORAMAC_MAX_MC_CTX > 2 ) + MC_NWK_S_KEY_2, +#endif /* LORAMAC_MAX_MC_CTX > 2 */ +#if ( LORAMAC_MAX_MC_CTX > 3 ) + MC_NWK_S_KEY_3, +#endif /* LORAMAC_MAX_MC_CTX > 3 */ +}; +/* ST_WORKAROUND_END */ + +/*! + * Defines the LoRaMac radio events status + */ +typedef union uLoRaMacRadioEvents +{ + uint32_t Value; + struct sEvents + { +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + uint32_t RxProcessPending : 1; +#endif /* LORAMAC_VERSION */ + 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 + */ +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) +static void OnAckTimeoutTimerEvent( void* context ); +#elif ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) +static void OnRetransmitTimeoutTimerEvent( void* context ); +#endif /* LORAMAC_VERSION */ + +/*ST_WORKAROUND_BEGIN: remove unnecessary mlme operation to prevent uplinks burst */ +/*! + * \brief Configures the events to trigger an MLME-Indication with + * a MLME type of MLME_SCHEDULE_UPLINK. + */ +/* static void SetMlmeScheduleUplinkIndication( void );*/ +/*ST_WORKAROUND_END */ + +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) +/*! + * 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 ); +#elif ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) +/*! + * 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 [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, + FCntIdentifier_t* fCntID, uint32_t* currentDown ); +#endif /* LORAMAC_VERSION */ + +/*! + * \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 ); + +/* ST_WORKAROUND_BEGIN: Update Send request with new input parameter to allow delayed tx */ +/*! + * \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 + * \param [in] allowDelayedTx When set to true, the frame will be delayed + * \retval status Status of the operation. + */ +static LoRaMacStatus_t Send( LoRaMacHeader_t* macHdr, uint8_t fPort, void* fBuffer, uint16_t fBufferSize, bool allowDelayedTx ); +/* ST_WORKAROUND_END */ + +/*! + * \brief LoRaMAC layer send join/rejoin request + * + * \param [in] joinReqType Type of join-request or rejoin + * + * \retval status Status of the operation. + */ +static 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] 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. + */ +static 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 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 aggregated back off time. + */ +static void CalculateBackOff( void ); + +/*! + * \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. + */ +static LoRaMacStatus_t SendFrameOnChannel( uint8_t channel ); + +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) +/*! + * \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. + */ +static 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. + */ +static LoRaMacStatus_t SetTxContinuousWave1( uint16_t timeout, uint32_t frequency, uint8_t power ); +#elif ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) +/*! + * \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. + */ +static LoRaMacStatus_t SetTxContinuousWave( uint16_t timeout, uint32_t frequency, uint8_t power ); +#endif /* LORAMAC_VERSION */ + +/*! + * \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 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, + */ +static LoRaMacStatus_t RestoreNvmData( void ); + +/*! + * \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, + */ +static LoRaMacStatus_t DetermineFrameType( LoRaMacMessageData_t* macMsg, FType_t* fType ); + +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) +/*! + * \brief Verifies, if the retransmission counter has reached the limit + * + * \param [in] counter Current retransmission counter + * \param [in] limit Retransmission counter limit + * + * \retval Returns true if the number of retransmissions have reached the limit. + */ +static bool CheckRetrans( uint8_t counter, uint8_t limit ); +#endif /* LORAMAC_VERSION */ + +/*! + * \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 ); + +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) +/*! + * \brief Increases the ADR ack counter. Takes the maximum + * value into account. + * + * \param [in] counter Current counter value. + * + * \retval Returns the next counter value. + */ +static uint32_t IncreaseAdrAckCounter( uint32_t counter ); +#endif /* LORAMAC_VERSION */ + +/*! + * \brief Stops the uplink retransmission + * + * \retval Returns true if successful. + */ +static bool StopRetransmission( void ); + +/*! + * \brief Calls the callback to indicate that a context changed + */ +static void CallNvmDataChangeCallback( uint16_t notifyFlags ); + +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) +/*! + * \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 ); +#endif /* LORAMAC_VERSION */ + +/*! + * \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 ); + +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) +/*! + * \brief Returns true, if the device must apply the minimum datarate + * + * \param [in] adr ADR status bit + * + * \param [in] activation Activation type of the device + * + * \param [in] datarateChanged Set to true, if the datarate was changed + * with the LinkAdrReq. + */ +static bool CheckForMinimumAbpDatarate( bool adr, ActivationType_t activation, bool datarateChanged ); +#endif /* LORAMAC_VERSION */ + +/*! + * \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 ); + +/*! + * \brief This function handles callback events for NVM updates + * + * \param [in] nvmData Data structure containing NVM data. + */ +static void LoRaMacHandleNvm( LoRaMacNvmData_t* nvmData ); + +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) +/*! + * \brief This function verifies if the response timeout has been elapsed. If + * this is the case, the status of Nvm.MacGroup1.SrvAckRequested will be + * reset. + * + * \param [in] timeoutInMs Timeout [ms] to be compared. + * + * \param [in] startTimeInMs Start time [ms] used as a base. If set to 0, + * no comparison will be done. + * + * \retval true: Response timeout has been elapsed, false: Response timeout + * has not been elapsed or startTimeInMs is 0. + */ +static bool LoRaMacHandleResponseTimeout( TimerTime_t timeoutInMs, TimerTime_t startTimeInMs ); +#endif /* LORAMAC_VERSION */ + +/*! + * 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( ); + } + MW_LOG(TS_ON, VLEVEL_M, "MAC txDone\r\n" ); +} + +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 ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + LoRaMacRadioEvents.Events.RxProcessPending = 1; +#endif /* LORAMAC_VERSION */ + + if( ( MacCtx.MacCallbacks != NULL ) && ( MacCtx.MacCallbacks->MacProcessNotify != NULL ) ) + { + MacCtx.MacCallbacks->MacProcessNotify( ); + } + MW_LOG(TS_ON, VLEVEL_M, "MAC rxDone\r\n" ); +} + +static void OnRadioTxTimeout( void ) +{ + LoRaMacRadioEvents.Events.TxTimeout = 1; + + if( ( MacCtx.MacCallbacks != NULL ) && ( MacCtx.MacCallbacks->MacProcessNotify != NULL ) ) + { + MacCtx.MacCallbacks->MacProcessNotify( ); + } + MW_LOG(TS_ON, VLEVEL_M, "MAC txTimeOut\r\n" ); +} + +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( ); + } + MW_LOG(TS_ON, VLEVEL_M, "MAC rxTimeOut\r\n" ); +} + +static void UpdateRxSlotIdleState( void ) +{ + if( Nvm.MacGroup2.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( Nvm.MacGroup2.DeviceClass != CLASS_C ) + { + Radio.Sleep( ); + } +#if ( !defined(DISABLE_LORAWAN_RX_WINDOW) || (DISABLE_LORAWAN_RX_WINDOW == 0) ) + // Setup timers + TimerSetValue( &MacCtx.RxWindowTimer1, MacCtx.RxWindow1Delay ); + TimerStart( &MacCtx.RxWindowTimer1 ); + TimerSetValue( &MacCtx.RxWindowTimer2, MacCtx.RxWindow2Delay ); + TimerStart( &MacCtx.RxWindowTimer2 ); +#else + if (Nvm.MacGroup2.NetworkActivation == ACTIVATION_TYPE_NONE) + { + // Setup timers + TimerSetValue( &MacCtx.RxWindowTimer1, MacCtx.RxWindow1Delay ); + TimerStart( &MacCtx.RxWindowTimer1 ); + TimerSetValue( &MacCtx.RxWindowTimer2, MacCtx.RxWindow2Delay ); + TimerStart( &MacCtx.RxWindowTimer2 ); + } + else + { + MacCtx.MacState |= LORAMAC_RX_ABORT; + MacCtx.MacFlags.Bits.MacDone = 1; + } +#endif /* DISABLE_LORAWAN_RX_WINDOW */ + +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) + if( ( Nvm.MacGroup2.DeviceClass == CLASS_C ) || ( MacCtx.NodeAckRequested == true ) ) + { + getPhy.Attribute = PHY_ACK_TIMEOUT; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + TimerSetValue( &MacCtx.AckTimeoutTimer, MacCtx.RxWindow2Delay + phyParam.Value ); + TimerStart( &MacCtx.AckTimeoutTimer ); + } +#elif ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + if( MacCtx.NodeAckRequested == true ) + { + getPhy.Attribute = PHY_RETRANSMIT_TIMEOUT; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + TimerSetValue( &MacCtx.RetransmitTimeoutTimer, MacCtx.RxWindow2Delay + phyParam.Value ); + TimerStart( &MacCtx.RetransmitTimeoutTimer ); + } + else + { + // Transmission successful, setup status directly + MacCtx.McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_OK; + } +#endif /* LORAMAC_VERSION */ + + // Update Aggregated last tx done time + Nvm.MacGroup1.LastTxDoneTime = TxDoneParams.CurTime; + + // Update last tx done time for the current channel + txDone.Channel = MacCtx.Channel; + txDone.LastTxDoneTime = TxDoneParams.CurTime; + txDone.ElapsedTimeSinceStartUp = SysTimeSub( SysTimeGetMcuTime( ), Nvm.MacGroup2.InitializationTime ); + txDone.LastTxAirTime = MacCtx.TxTimeOnAir; + txDone.Joined = true; + if( Nvm.MacGroup2.NetworkActivation == ACTIVATION_TYPE_NONE ) + { + txDone.Joined = false; + } + + RegionSetBandTxDone( Nvm.MacGroup2.Region, &txDone ); + +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) + if( MacCtx.NodeAckRequested == false ) + { + MacCtx.McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_OK; + } +#endif /* LORAMAC_VERSION */ +} + +static void PrepareRxDoneAbort( void ) +{ + MacCtx.MacState |= LORAMAC_RX_ABORT; + + if( MacCtx.NodeAckRequested == true ) + { +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) + OnAckTimeoutTimerEvent( NULL ); +#elif ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + OnRetransmitTimeoutTimerEvent( NULL ); +#endif /* LORAMAC_VERSION */ + } + + MacCtx.MacFlags.Bits.McpsInd = 1; + MacCtx.MacFlags.Bits.MacDone = 1; + + UpdateRxSlotIdleState( ); +} + +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 = Nvm.MacGroup2.DevAddr; + uint8_t multicast = 0; + AddressIdentifier_t addrID = UNICAST_DEV_ADDR; + FCntIdentifier_t fCntID; + +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + LoRaMacRadioEvents.Events.RxProcessPending = 0; +#endif /* LORAMAC_VERSION */ + + MacCtx.McpsConfirm.AckReceived = false; + MacCtx.RxStatus.Rssi = rssi; + MacCtx.RxStatus.Snr = snr; + MacCtx.RxStatus.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; +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + MacCtx.McpsIndication.ResponseTimeout = 0; +#endif /* LORAMAC_VERSION */ + + Radio.Sleep( ); + +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) + TimerStop( &MacCtx.RxWindowTimer2 ); +#elif ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + if( MacCtx.RxStatus.RxSlot == RX_SLOT_WIN_1 ) + { + TimerStop( &MacCtx.RxWindowTimer2 ); + } +#endif /* LORAMAC_VERSION */ + + // 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( Nvm.MacGroup2.DeviceClass == CLASS_B ) + { + if( LoRaMacClassBIsPingExpected( ) == true ) + { + LoRaMacClassBSetPingSlotState( PINGSLOT_STATE_CALC_PING_OFFSET ); + LoRaMacClassBPingSlotTimerEvent( NULL ); + MacCtx.RxStatus.RxSlot = RX_SLOT_WIN_CLASS_B_PING_SLOT; + } + else if( LoRaMacClassBIsMulticastExpected( ) == true ) + { + LoRaMacClassBSetMulticastSlotState( PINGSLOT_STATE_CALC_PING_OFFSET ); + LoRaMacClassBMulticastSlotTimerEvent( NULL ); + MacCtx.RxStatus.RxSlot = RX_SLOT_WIN_CLASS_B_MULTICAST_SLOT; + } + } + + macHdr.Value = payload[pktHeaderLen++]; + + switch( macHdr.Bits.MType ) + { + case FRAME_TYPE_JOIN_ACCEPT: + // Check if the received frame size is valid + if( size < LORAMAC_JOIN_ACCEPT_FRAME_MIN_SIZE ) + { + MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; + PrepareRxDoneAbort( ); + return; + } + macMsgJoinAccept.Buffer = payload; + macMsgJoinAccept.BufSize = size; + + // Abort in case if the device isn't joined yet and no rejoin request is ongoing. + if( Nvm.MacGroup2.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 + Nvm.MacGroup2.NetID = ( uint32_t ) macMsgJoinAccept.NetID[0]; + Nvm.MacGroup2.NetID |= ( ( uint32_t ) macMsgJoinAccept.NetID[1] << 8 ); + Nvm.MacGroup2.NetID |= ( ( uint32_t ) macMsgJoinAccept.NetID[2] << 16 ); + + // Device Address + Nvm.MacGroup2.DevAddr = macMsgJoinAccept.DevAddr; + + // DLSettings + Nvm.MacGroup2.MacParams.Rx1DrOffset = macMsgJoinAccept.DLSettings.Bits.RX1DRoffset; + Nvm.MacGroup2.MacParams.Rx2Channel.Datarate = macMsgJoinAccept.DLSettings.Bits.RX2DataRate; + Nvm.MacGroup2.MacParams.RxCChannel.Datarate = macMsgJoinAccept.DLSettings.Bits.RX2DataRate; + + // RxDelay + Nvm.MacGroup2.MacParams.ReceiveDelay1 = macMsgJoinAccept.RxDelay; + if( Nvm.MacGroup2.MacParams.ReceiveDelay1 == 0 ) + { + Nvm.MacGroup2.MacParams.ReceiveDelay1 = 1; + } + Nvm.MacGroup2.MacParams.ReceiveDelay1 *= 1000; + Nvm.MacGroup2.MacParams.ReceiveDelay2 = Nvm.MacGroup2.MacParams.ReceiveDelay1 + 1000; + + Nvm.MacGroup2.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; +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + // Apply the last tx channel + applyCFList.JoinChannel = MacCtx.Channel; +#endif /* LORAMAC_VERSION */ + + RegionApplyCFList( Nvm.MacGroup2.Region, &applyCFList ); + + Nvm.MacGroup2.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 ); + } + } + MacCtx.MacFlags.Bits.MlmeInd = 1; + 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 = Nvm.MacGroup2.MacParams.DownlinkDwellTime; + getPhy.Datarate = MacCtx.McpsIndication.RxDatarate; + getPhy.Attribute = PHY_MAX_PAYLOAD; + + /* ST_WORKAROUND_BEGIN: Keep repeater feature */ + // Get the maximum payload length + if( Nvm.MacGroup2.MacParams.RepeaterSupport == true ) + { + getPhy.Attribute = PHY_MAX_PAYLOAD_REPEATER; + } + /* ST_WORKAROUND_END */ + + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + if( ( MAX( 0, ( int16_t )( ( int16_t ) size - ( int16_t ) LORAMAC_FRAME_PAYLOAD_OVERHEAD_SIZE ) ) > ( int16_t )phyParam.Value ) || + ( size < LORAMAC_FRAME_PAYLOAD_MIN_SIZE ) ) + { + 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; + } + +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + // Handle Class B + // Check if we expect a ping or a multicast slot. + if( Nvm.MacGroup2.DeviceClass == CLASS_B ) + { + if( LoRaMacClassBIsPingExpected( ) == true ) + { + LoRaMacClassBSetPingSlotState( PINGSLOT_STATE_CALC_PING_OFFSET ); + LoRaMacClassBPingSlotTimerEvent( NULL ); + MacCtx.RxStatus.RxSlot = RX_SLOT_WIN_CLASS_B_PING_SLOT; + LoRaMacClassBSetFPendingBit( macMsgData.FHDR.DevAddr, ( uint8_t ) macMsgData.FHDR.FCtrl.Bits.FPending ); + } + else if( LoRaMacClassBIsMulticastExpected( ) == true ) + { + LoRaMacClassBSetMulticastSlotState( PINGSLOT_STATE_CALC_PING_OFFSET ); + LoRaMacClassBMulticastSlotTimerEvent( NULL ); + MacCtx.RxStatus.RxSlot = RX_SLOT_WIN_CLASS_B_MULTICAST_SLOT; + LoRaMacClassBSetFPendingBit( macMsgData.FHDR.DevAddr, ( uint8_t ) macMsgData.FHDR.FCtrl.Bits.FPending ); + } + } +#endif /* LORAMAC_VERSION */ + + // 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( ( Nvm.MacGroup2.MulticastChannelList[i].ChannelParams.Address == macMsgData.FHDR.DevAddr ) && + ( Nvm.MacGroup2.MulticastChannelList[i].ChannelParams.IsEnabled == true ) ) + { + multicast = 1; + addrID = Nvm.MacGroup2.MulticastChannelList[i].ChannelParams.GroupID; + downLinkCounter = *( Nvm.MacGroup2.MulticastChannelList[i].DownLinkCounter ); + address = Nvm.MacGroup2.MulticastChannelList[i].ChannelParams.Address; + if( Nvm.MacGroup2.DeviceClass == CLASS_C ) + { + MacCtx.RxStatus.RxSlot = RX_SLOT_WIN_CLASS_C_MULTICAST; + } + break; + } + } + + // Filter messages according to multicast downlink exceptions + if( ( multicast == 1 ) && ( ( fType != FRAME_TYPE_D ) || + ( macMsgData.FHDR.FCtrl.Bits.Ack != 0 ) || + ( macMsgData.FHDR.FCtrl.Bits.AdrAckReq != 0 ) ) ) + { + MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; + PrepareRxDoneAbort( ); + return; + } + +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) + // Get maximum allowed counter difference + getPhy.Attribute = PHY_MAX_FCNT_GAP; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + + // Get downlink frame counter value + macCryptoStatus = GetFCntDown( addrID, fType, &macMsgData, Nvm.MacGroup2.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( ( Nvm.MacGroup2.Version.Fields.Minor == 0 ) && ( macHdr.Bits.MType == FRAME_TYPE_DATA_CONFIRMED_DOWN ) && ( Nvm.MacGroup1.LastRxMic == macMsgData.MIC ) ) + { + Nvm.MacGroup1.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; + MacCtx.MlmeIndication.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; + } +#elif ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + // Get downlink frame counter value + macCryptoStatus = GetFCntDown( addrID, fType, &macMsgData, Nvm.MacGroup2.Version, &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; + } + else + { + // Other errors + MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; + } + MacCtx.McpsIndication.DownLinkCounter = downLinkCounter; + MacCtx.MlmeIndication.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; + } +#endif /* LORAMAC_VERSION */ + + 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.MlmeIndication.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.RxStatus.RxSlot == RX_SLOT_WIN_1 ) || + ( MacCtx.RxStatus.RxSlot == RX_SLOT_WIN_2 ) ) + { + Nvm.MacGroup1.AdrAckCounter = 0; +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + Nvm.MacGroup2.DownlinkReceived = true; +#endif /* LORAMAC_VERSION */ + } + + // MCPS Indication and ack requested handling + if( multicast == 1 ) + { + MacCtx.McpsIndication.McpsIndication = MCPS_MULTICAST; + } + else + { + if( macHdr.Bits.MType == FRAME_TYPE_DATA_CONFIRMED_DOWN ) + { + Nvm.MacGroup1.SrvAckRequested = true; + if( Nvm.MacGroup2.Version.Fields.Minor == 0 ) + { + Nvm.MacGroup1.LastRxMic = macMsgData.MIC; + } + MacCtx.McpsIndication.McpsIndication = MCPS_CONFIRMED; + +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + // Handle response timeout for class c and class b downlinks + if( ( MacCtx.RxStatus.RxSlot != RX_SLOT_WIN_1 ) && + ( MacCtx.RxStatus.RxSlot != RX_SLOT_WIN_2 ) ) + { + // Calculate timeout + MacCtx.McpsIndication.ResponseTimeout = Nvm.MacGroup2.MacParams.RxBCTimeout; + MacCtx.ResponseTimeoutStartTime = RxDoneParams.LastRxDone; + } +#endif /* LORAMAC_VERSION */ + } + else + { + Nvm.MacGroup1.SrvAckRequested = false; + MacCtx.McpsIndication.McpsIndication = MCPS_UNCONFIRMED; + } + } + + RemoveMacCommands( MacCtx.RxStatus.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.RxStatus.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.RxStatus.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.RxStatus.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; + } + +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + if( ( macMsgData.FPort == LORAMAC_CERT_FPORT ) && ( Nvm.MacGroup2.IsCertPortOn == false ) ) + { // Do not notify the upper layer of data reception on FPort LORAMAC_CERT_FPORT if the port + // handling is disabled. + MacCtx.McpsIndication.Port = macMsgData.FPort; + MacCtx.McpsIndication.Buffer = NULL; + MacCtx.McpsIndication.BufferSize = 0; + MacCtx.McpsIndication.RxData = false; + } +#endif /* LORAMAC_VERSION */ + + // 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; + } + +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) + // Verify if we need to disable the AckTimeoutTimer + if( MacCtx.NodeAckRequested == true ) + { + if( MacCtx.McpsConfirm.AckReceived == true ) + { + OnAckTimeoutTimerEvent( NULL ); + } + } + else + { + if( Nvm.MacGroup2.DeviceClass == CLASS_C ) + { + OnAckTimeoutTimerEvent( NULL ); + } + } + MacCtx.MacFlags.Bits.MacDone = 1; +#elif ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + // Verify if we need to disable the RetransmitTimeoutTimer + // Only aplies if downlink is received on Rx1 or Rx2 windows. + if( ( MacCtx.RxStatus.RxSlot == RX_SLOT_WIN_1 ) || + ( MacCtx.RxStatus.RxSlot == RX_SLOT_WIN_2 ) ) + { + if( MacCtx.NodeAckRequested == true ) + { + if( MacCtx.McpsConfirm.AckReceived == true ) + { + OnRetransmitTimeoutTimerEvent( NULL ); + } + } + } + + if( MacCtx.RxStatus.RxSlot != RX_SLOT_WIN_CLASS_C ) + { + MacCtx.MacFlags.Bits.MacDone = 1; + } +#endif /* LORAMAC_VERSION */ + + UpdateRxSlotIdleState( ); +} + +static void ProcessRadioTxTimeout( void ) +{ + if( Nvm.MacGroup2.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 ) + { +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) + MacCtx.AckTimeoutRetry = true; +#elif ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + MacCtx.RetransmitTimeoutRetry = true; +#endif /* LORAMAC_VERSION */ + } + MacCtx.MacFlags.Bits.MacDone = 1; +} + +static void HandleRadioRxErrorTimeout( LoRaMacEventInfoStatus_t rx1EventInfoStatus, LoRaMacEventInfoStatus_t rx2EventInfoStatus ) +{ + bool classBRx = false; + + if( Nvm.MacGroup2.DeviceClass != CLASS_C ) + { + Radio.Sleep( ); + } + + if( LoRaMacClassBIsBeaconExpected( ) == true ) + { + LoRaMacClassBSetBeaconState( BEACON_STATE_TIMEOUT ); + LoRaMacClassBBeaconTimerEvent( NULL ); + classBRx = true; + } + if( Nvm.MacGroup2.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( Nvm.MacGroup1.LastTxDoneTime ) >= MacCtx.RxWindow2Delay ) + { + TimerStop( &MacCtx.RxWindowTimer2 ); + MacCtx.MacFlags.Bits.MacDone = 1; + } + } + else + { + if( MacCtx.NodeAckRequested == true ) + { + MacCtx.McpsConfirm.Status = rx2EventInfoStatus; + } + LoRaMacConfirmQueueSetStatusCmn( rx2EventInfoStatus ); + +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) + if( Nvm.MacGroup2.DeviceClass != CLASS_C ) + { + MacCtx.MacFlags.Bits.MacDone = 1; + } +#elif ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + MacCtx.MacFlags.Bits.MacDone = 1; +#endif /* LORAMAC_VERSION */ + } + } + + 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 ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + if( LoRaMacRadioEvents.Events.RxProcessPending == 1 ) + { + return true; + } +#endif /* LORAMAC_VERSION */ + + 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 + /* ST_WORKAROUND: remove unnecessary mlme operation to prevent uplinks burst */ + //SetMlmeScheduleUplinkIndication( ); + } + } +} + +static void LoRaMacHandleIndicationEvents( void ) +{ + // Handle MLME indication + if( MacCtx.MacFlags.Bits.MlmeInd == 1 ) + { + MacCtx.MacFlags.Bits.MlmeInd = 0; + MacCtx.MacPrimitives->MacMlmeIndication( &MacCtx.MlmeIndication, &MacCtx.RxStatus ); + } + + /*ST_WORKAROUND_BEGIN: remove unnecessary mlme operation to prevent uplinks burst */ + /* + if( MacCtx.MacFlags.Bits.MlmeSchedUplinkInd == 1 ) + { + MlmeIndication_t scheduleUplinkIndication; + scheduleUplinkIndication.MlmeIndication = MLME_SCHEDULE_UPLINK; + scheduleUplinkIndication.Status = LORAMAC_EVENT_INFO_STATUS_OK; + + MacCtx.MacPrimitives->MacMlmeIndication( &scheduleUplinkIndication, &MacCtx.RxStatus ); + MacCtx.MacFlags.Bits.MlmeSchedUplinkInd = 0; + } + */ + /*ST_WORKAROUND_END */ + + // Handle MCPS indication + if( MacCtx.MacFlags.Bits.McpsInd == 1 ) + { + MacCtx.MacFlags.Bits.McpsInd = 0; + MacCtx.MacPrimitives->MacMcpsIndication( &MacCtx.McpsIndication, &MacCtx.RxStatus ); + } +} + +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 ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) + if( MacCtx.AckTimeoutRetry == true ) + { + stopRetransmission = CheckRetransConfirmedUplink( ); + + if( Nvm.MacGroup2.Version.Fields.Minor == 0 ) + { + if( stopRetransmission == false ) + { + AckTimeoutRetriesProcess( ); + } + else + { + AckTimeoutRetriesFinalize( ); + } + } + } + else + { + waitForRetransmission = true; + } +#elif ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + if( MacCtx.RetransmitTimeoutRetry == true ) + { + stopRetransmission = CheckRetransConfirmedUplink( ); + } + else + { + waitForRetransmission = true; + } +#endif /* LORAMAC_VERSION */ + } + + 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 +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) + MacCtx.AckTimeoutRetry = false; +#elif ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + MacCtx.RetransmitTimeoutRetry = false; +#endif /* LORAMAC_VERSION */ + // 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; + } +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) + else if( ( LoRaMacConfirmQueueIsCmdActive( MLME_TXCW ) == true ) || + ( LoRaMacConfirmQueueIsCmdActive( MLME_TXCW_1 ) == true ) ) + { + MacCtx.MacState &= ~LORAMAC_TX_RUNNING; + } +#elif ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + else if( LoRaMacConfirmQueueIsCmdActive( MLME_TXCW ) == true ) + { + MacCtx.MacState &= ~LORAMAC_TX_RUNNING; + } +#endif /* LORAMAC_VERSION */ + } +} + +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; +} + +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) +static bool CheckForMinimumAbpDatarate( bool adr, ActivationType_t activation, bool datarateChanged ) +{ + if( ( adr == true ) && + ( activation == ACTIVATION_TYPE_ABP ) && + ( datarateChanged == false ) ) + { + return true; + } + return false; +} +#endif /* LORAMAC_VERSION */ + +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; + } +} + +static void LoRaMacHandleNvm( LoRaMacNvmData_t* nvmData ) +{ + uint32_t crc = 0; + uint16_t notifyFlags = LORAMAC_NVM_NOTIFY_FLAG_NONE; + + if( MacCtx.MacState != LORAMAC_IDLE ) + { + return; + } + + // Crypto + crc = Crc32( ( uint8_t* ) &nvmData->Crypto, sizeof( nvmData->Crypto ) - + sizeof( nvmData->Crypto.Crc32 ) ); + if( crc != nvmData->Crypto.Crc32 ) + { + nvmData->Crypto.Crc32 = crc; + notifyFlags |= LORAMAC_NVM_NOTIFY_FLAG_CRYPTO; + } + + // MacGroup1 + crc = Crc32( ( uint8_t* ) &nvmData->MacGroup1, sizeof( nvmData->MacGroup1 ) - + sizeof( nvmData->MacGroup1.Crc32 ) ); + if( crc != nvmData->MacGroup1.Crc32 ) + { + nvmData->MacGroup1.Crc32 = crc; + notifyFlags |= LORAMAC_NVM_NOTIFY_FLAG_MAC_GROUP1; + } + + // MacGroup2 + crc = Crc32( ( uint8_t* ) &nvmData->MacGroup2, sizeof( nvmData->MacGroup2 ) - + sizeof( nvmData->MacGroup2.Crc32 ) ); + if( crc != nvmData->MacGroup2.Crc32 ) + { + nvmData->MacGroup2.Crc32 = crc; + notifyFlags |= LORAMAC_NVM_NOTIFY_FLAG_MAC_GROUP2; + } + + // Secure Element + crc = Crc32( ( uint8_t* ) &nvmData->SecureElement, sizeof( nvmData->SecureElement ) - + sizeof( nvmData->SecureElement.Crc32 ) ); + if( crc != nvmData->SecureElement.Crc32 ) + { + nvmData->SecureElement.Crc32 = crc; + notifyFlags |= LORAMAC_NVM_NOTIFY_FLAG_SECURE_ELEMENT; + } + + // Region + crc = Crc32( ( uint8_t* ) &nvmData->RegionGroup1, sizeof( nvmData->RegionGroup1 ) - + sizeof( nvmData->RegionGroup1.Crc32 ) ); + if( crc != nvmData->RegionGroup1.Crc32 ) + { + nvmData->RegionGroup1.Crc32 = crc; + notifyFlags |= LORAMAC_NVM_NOTIFY_FLAG_REGION_GROUP1; + } + + crc = Crc32( ( uint8_t* ) &nvmData->RegionGroup2, sizeof( nvmData->RegionGroup2 ) - + sizeof( nvmData->RegionGroup2.Crc32 ) ); + if( crc != nvmData->RegionGroup2.Crc32 ) + { + nvmData->RegionGroup2.Crc32 = crc; + notifyFlags |= LORAMAC_NVM_NOTIFY_FLAG_REGION_GROUP2; + } + + // ClassB + crc = Crc32( ( uint8_t* ) &nvmData->ClassB, sizeof( nvmData->ClassB ) - + sizeof( nvmData->ClassB.Crc32 ) ); + if( crc != nvmData->ClassB.Crc32 ) + { + nvmData->ClassB.Crc32 = crc; + notifyFlags |= LORAMAC_NVM_NOTIFY_FLAG_CLASS_B; + } + + CallNvmDataChangeCallback( notifyFlags ); +} + +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) +static bool LoRaMacHandleResponseTimeout( TimerTime_t timeoutInMs, TimerTime_t startTimeInMs ) +{ + if( startTimeInMs != 0 ) + { + TimerTime_t elapsedTime = TimerGetElapsedTime( startTimeInMs ); + if( elapsedTime > timeoutInMs ) + { + Nvm.MacGroup1.SrvAckRequested = false; + return true; + } + } + return false; +} +#endif /* LORAMAC_VERSION */ + +void LoRaMacProcess( void ) +{ + 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 ); + MacCtx.MacFlags.Bits.NvmHandle = 1; + } + LoRaMacHandleIndicationEvents( ); + if( MacCtx.RxSlot == RX_SLOT_WIN_CLASS_C ) + { + OpenContinuousRxCWindow( ); + } + if( MacCtx.MacFlags.Bits.NvmHandle == 1 ) + { + MacCtx.MacFlags.Bits.NvmHandle = 0; + LoRaMacHandleNvm( &Nvm ); + } +} + +static void OnTxDelayedTimerEvent( void* context ) +{ + TimerStop( &MacCtx.TxDelayedTimer ); + MacCtx.MacState &= ~LORAMAC_TX_DELAYED; +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + if( LoRaMacHandleResponseTimeout( Nvm.MacGroup2.MacParams.RxBCTimeout, + MacCtx.ResponseTimeoutStartTime ) == true ) + { + // Skip retransmission + return; + } +#endif /* LORAMAC_VERSION */ + + // 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 = Nvm.MacGroup1.ChannelsDatarate; +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) + MacCtx.McpsConfirm.NbRetries = MacCtx.AckTimeoutRetriesCounter; +#elif ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + MacCtx.McpsConfirm.NbTrans = MacCtx.ChannelsNbTransCounter; +#endif /* LORAMAC_VERSION */ + 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 = Nvm.MacGroup2.MacParams.Rx1DrOffset; + MacCtx.RxWindow1Config.DownlinkDwellTime = Nvm.MacGroup2.MacParams.DownlinkDwellTime; + MacCtx.RxWindow1Config.RepeaterSupport = Nvm.MacGroup2.MacParams.RepeaterSupport; /* ST_WORKAROUND: Keep repeater feature */ + MacCtx.RxWindow1Config.RxContinuous = false; + MacCtx.RxWindow1Config.RxSlot = RX_SLOT_WIN_1; +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + MacCtx.RxWindow1Config.NetworkActivation = Nvm.MacGroup2.NetworkActivation; +#endif /* LORAMAC_VERSION */ + + 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 = Nvm.MacGroup2.MacParams.Rx2Channel.Frequency; + MacCtx.RxWindow2Config.DownlinkDwellTime = Nvm.MacGroup2.MacParams.DownlinkDwellTime; + MacCtx.RxWindow2Config.RepeaterSupport = Nvm.MacGroup2.MacParams.RepeaterSupport; /* ST_WORKAROUND: Keep repeater feature */ + MacCtx.RxWindow2Config.RxContinuous = false; + MacCtx.RxWindow2Config.RxSlot = RX_SLOT_WIN_2; +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + MacCtx.RxWindow2Config.NetworkActivation = Nvm.MacGroup2.NetworkActivation; +#endif /* LORAMAC_VERSION */ + + RxWindowSetup( &MacCtx.RxWindowTimer2, &MacCtx.RxWindow2Config ); +} +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) +static void OnAckTimeoutTimerEvent( void* context ) +{ + TimerStop( &MacCtx.AckTimeoutTimer ); + + if( MacCtx.NodeAckRequested == true ) + { + MacCtx.AckTimeoutRetry = true; + } + if( Nvm.MacGroup2.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; + /* ST_WORKAROUND_BEGIN: reduced LORAMAC_MAX_MC_CTX */ +#if ( LORAMAC_MAX_MC_CTX > 0 ) + case MULTICAST_0_ADDR: + *fCntID = MC_FCNT_DOWN_0; + break; +#endif /* LORAMAC_MAX_MC_CTX > 0 */ +#if ( LORAMAC_MAX_MC_CTX > 1 ) + case MULTICAST_1_ADDR: + *fCntID = MC_FCNT_DOWN_1; + break; +#endif /* LORAMAC_MAX_MC_CTX > 1 */ +#if ( LORAMAC_MAX_MC_CTX > 2 ) + case MULTICAST_2_ADDR: + *fCntID = MC_FCNT_DOWN_2; + break; +#endif /* LORAMAC_MAX_MC_CTX > 2 */ +#if ( LORAMAC_MAX_MC_CTX > 3 ) + case MULTICAST_3_ADDR: + *fCntID = MC_FCNT_DOWN_3; + break; +#endif /* LORAMAC_MAX_MC_CTX > 3 */ + /* ST_WORKAROUND_END */ + default: + return LORAMAC_CRYPTO_FAIL_FCNT_ID; + } + + return LoRaMacCryptoGetFCntDown( *fCntID, maxFCntGap, macMsg->FHDR.FCnt, currentDown ); +} +#elif ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) +static void OnRetransmitTimeoutTimerEvent( void* context ) +{ + TimerStop( &MacCtx.RetransmitTimeoutTimer ); + + if( MacCtx.NodeAckRequested == true ) + { + MacCtx.RetransmitTimeoutRetry = true; + } + 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, + 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; + /* ST_WORKAROUND_BEGIN: reduced LORAMAC_MAX_MC_CTX */ +#if ( LORAMAC_MAX_MC_CTX > 0 ) + case MULTICAST_0_ADDR: + *fCntID = MC_FCNT_DOWN_0; + break; +#endif /* LORAMAC_MAX_MC_CTX > 0 */ +#if ( LORAMAC_MAX_MC_CTX > 1 ) + case MULTICAST_1_ADDR: + *fCntID = MC_FCNT_DOWN_1; + break; +#endif /* LORAMAC_MAX_MC_CTX > 1 */ +#if ( LORAMAC_MAX_MC_CTX > 2 ) + case MULTICAST_2_ADDR: + *fCntID = MC_FCNT_DOWN_2; + break; +#endif /* LORAMAC_MAX_MC_CTX > 2 */ +#if ( LORAMAC_MAX_MC_CTX > 3 ) + case MULTICAST_3_ADDR: + *fCntID = MC_FCNT_DOWN_3; + break; +#endif /* LORAMAC_MAX_MC_CTX > 3 */ + /* ST_WORKAROUND_END */ + default: + return LORAMAC_CRYPTO_FAIL_FCNT_ID; + } + + return LoRaMacCryptoGetFCntDown( *fCntID, macMsg->FHDR.FCnt, currentDown ); +} +#endif /* LORAMAC_VERSION */ + +static LoRaMacStatus_t SwitchClass( DeviceClass_t deviceClass ) +{ + LoRaMacStatus_t status = LORAMAC_STATUS_PARAMETER_INVALID; + + switch( Nvm.MacGroup2.DeviceClass ) + { + case CLASS_A: + { + if( deviceClass == CLASS_A ) + { + // Revert back RxC parameters + Nvm.MacGroup2.MacParams.RxCChannel = Nvm.MacGroup2.MacParams.Rx2Channel; + } + if( deviceClass == CLASS_B ) + { + status = LoRaMacClassBSwitchClass( deviceClass ); + if( status == LORAMAC_STATUS_OK ) + { + Nvm.MacGroup2.DeviceClass = deviceClass; + } + } + + if( deviceClass == CLASS_C ) + { + Nvm.MacGroup2.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( Nvm.MacGroup2.MulticastChannelList[i].ChannelParams.IsEnabled == true ) + { + Nvm.MacGroup2.MacParams.RxCChannel.Frequency = Nvm.MacGroup2.MulticastChannelList[i].ChannelParams.RxParams.ClassC.Frequency; + Nvm.MacGroup2.MacParams.RxCChannel.Datarate = Nvm.MacGroup2.MulticastChannelList[i].ChannelParams.RxParams.ClassC.Datarate; + + MacCtx.RxWindowCConfig.Channel = MacCtx.Channel; + MacCtx.RxWindowCConfig.Frequency = Nvm.MacGroup2.MacParams.RxCChannel.Frequency; + MacCtx.RxWindowCConfig.DownlinkDwellTime = Nvm.MacGroup2.MacParams.DownlinkDwellTime; + MacCtx.RxWindowCConfig.RepeaterSupport = Nvm.MacGroup2.MacParams.RepeaterSupport; /* ST_WORKAROUND: Keep repeater feature */ + 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( ); + + OpenContinuousRxCWindow( ); + + status = LORAMAC_STATUS_OK; + } + break; + } + case CLASS_B: + { + status = LoRaMacClassBSwitchClass( deviceClass ); + if( status == LORAMAC_STATUS_OK ) + { + Nvm.MacGroup2.DeviceClass = deviceClass; + } + break; + } + case CLASS_C: + { + if( deviceClass == CLASS_A ) + { + Nvm.MacGroup2.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 = Nvm.MacGroup2.MacParams.UplinkDwellTime; + getPhy.Datarate = datarate; + getPhy.Attribute = PHY_MAX_PAYLOAD; + /* ST_WORKAROUND_BEGIN: Keep repeater feature */ + // Get the maximum payload length + if( Nvm.MacGroup2.MacParams.RepeaterSupport == true ) + { + getPhy.Attribute = PHY_MAX_PAYLOAD_REPEATER; + } + /* ST_WORKAROUND_END */ + phyParam = RegionGetPhyParam( Nvm.MacGroup2.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; +} + +/*ST_WORKAROUND_BEGIN: remove unnecessary mlme operation to prevent uplinks burst */ +/* +static void SetMlmeScheduleUplinkIndication( void ) +{ + MacCtx.MacFlags.Bits.MlmeSchedUplinkInd = 1; +} +*/ +/*ST_WORKAROUND_END */ + +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 }; + +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + if( ( rxSlot != RX_SLOT_WIN_1 ) && ( rxSlot != RX_SLOT_WIN_2 ) ) + { + // Do only parse MAC commands for Class A RX windows + return; + } +#endif /* LORAMAC_VERSION */ + + while( macIndex < commandsSize ) + { + // Make sure to parse only complete MAC commands + if( ( LoRaMacCommandsGetCmdSize( payload[macIndex] ) + macIndex ) > commandsSize ) + { + return; + } + + // 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; + + // The end node is allowed to process one block of LinkAdrRequests. + // It must ignore subsequent blocks +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) + if( adrBlockFound == false ) + { + adrBlockFound = true; + + // Fill parameter structure + linkAdrReq.Payload = &payload[macIndex - 1]; + linkAdrReq.PayloadSize = commandsSize - ( macIndex - 1 ); + linkAdrReq.AdrEnabled = Nvm.MacGroup2.AdrCtrlOn; + linkAdrReq.UplinkDwellTime = Nvm.MacGroup2.MacParams.UplinkDwellTime; + linkAdrReq.CurrentDatarate = Nvm.MacGroup1.ChannelsDatarate; + linkAdrReq.CurrentTxPower = Nvm.MacGroup1.ChannelsTxPower; + linkAdrReq.CurrentNbRep = Nvm.MacGroup2.MacParams.ChannelsNbTrans; + linkAdrReq.Version = Nvm.MacGroup2.Version; + + // Process the ADR requests + status = RegionLinkAdrReq( Nvm.MacGroup2.Region, &linkAdrReq, &linkAdrDatarate, + &linkAdrTxPower, &linkAdrNbRep, &linkAdrNbBytesParsed ); + + if( ( status & 0x07 ) == 0x07 ) + { + Nvm.MacGroup1.ChannelsDatarate = linkAdrDatarate; + Nvm.MacGroup1.ChannelsTxPower = linkAdrTxPower; + Nvm.MacGroup2.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; + } +#elif ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + if( adrBlockFound == false ) + { + adrBlockFound = true; + + do + { + // Fill parameter structure + linkAdrReq.Payload = &payload[macIndex - 1]; + linkAdrReq.AdrEnabled = Nvm.MacGroup2.AdrCtrlOn; + linkAdrReq.UplinkDwellTime = Nvm.MacGroup2.MacParams.UplinkDwellTime; + linkAdrReq.CurrentDatarate = Nvm.MacGroup1.ChannelsDatarate; + linkAdrReq.CurrentTxPower = Nvm.MacGroup1.ChannelsTxPower; + linkAdrReq.CurrentNbRep = Nvm.MacGroup2.MacParams.ChannelsNbTrans; + linkAdrReq.Version = Nvm.MacGroup2.Version; + + // There is a fundamental difference in reporting the status + // of the LinkAdrRequests when ADR is on or off. When ADR is on, every + // LinkAdrAns contains the same value. This does not hold when ADR is off, + // where every LinkAdrAns requires an individual status. + if( Nvm.MacGroup2.AdrCtrlOn == true ) + { + // When ADR is on, the function RegionLinkAdrReq will take care + // about the parsing and interpretation of the LinkAdrRequest block and + // it provides one status which shall be applied to every LinkAdrAns + linkAdrReq.PayloadSize = commandsSize - ( macIndex - 1 ); + } + else + { + // When ADR is off, this function will loop over the individual LinkAdrRequests + // and will call RegionLinkAdrReq for each individually, as every request + // requires an individual answer. + // When ADR is off, the function RegionLinkAdrReq ignores the new values for + // ChannelsDatarate, ChannelsTxPower and ChannelsNbTrans. + linkAdrReq.PayloadSize = 5; + } + + // Process the ADR requests + status = RegionLinkAdrReq( Nvm.MacGroup2.Region, &linkAdrReq, &linkAdrDatarate, + &linkAdrTxPower, &linkAdrNbRep, &linkAdrNbBytesParsed ); + + if( ( status & 0x07 ) == 0x07 ) + { + // Set the status that the datarate has been increased + if( linkAdrDatarate > Nvm.MacGroup1.ChannelsDatarate ) + { + Nvm.MacGroup2.ChannelsDatarateChangedLinkAdrReq = true; + } + Nvm.MacGroup1.ChannelsDatarate = linkAdrDatarate; + Nvm.MacGroup1.ChannelsTxPower = linkAdrTxPower; + Nvm.MacGroup2.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; + + // Check to prevent invalid access + if( macIndex >= commandsSize ) + break; + + } while( payload[macIndex++] == SRV_MAC_LINK_ADR_REQ ); + + if( macIndex < commandsSize ) + { + // Decrease the index such that it points to the next MAC command + macIndex--; + } + } + else + { + // Increase the index by the MAC command size (without command) + macIndex += 4; + } +#endif /* LORAMAC_VERSION */ + break; + } + case SRV_MAC_DUTY_CYCLE_REQ: + { + Nvm.MacGroup2.MaxDCycle = payload[macIndex++] & 0x0F; + Nvm.MacGroup2.AggregatedDCycle = 1 << Nvm.MacGroup2.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( Nvm.MacGroup2.Region, &rxParamSetupReq ); + + if( ( status & 0x07 ) == 0x07 ) + { + Nvm.MacGroup2.MacParams.Rx2Channel.Datarate = rxParamSetupReq.Datarate; + Nvm.MacGroup2.MacParams.RxCChannel.Datarate = rxParamSetupReq.Datarate; + Nvm.MacGroup2.MacParams.Rx2Channel.Frequency = rxParamSetupReq.Frequency; + Nvm.MacGroup2.MacParams.RxCChannel.Frequency = rxParamSetupReq.Frequency; + Nvm.MacGroup2.MacParams.Rx1DrOffset = rxParamSetupReq.DrOffset; + } + macCmdPayload[0] = status; + LoRaMacCommandsAddCmd( MOTE_MAC_RX_PARAM_SETUP_ANS, macCmdPayload, 1 ); + /*ST_WORKAROUND_BEGIN: remove unnecessary mlme operation to prevent uplinks burst */ + // Setup indication to inform the application + /* SetMlmeScheduleUplinkIndication( ); */ + /*ST_WORKAROUND_END */ + 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 = ( uint8_t )RegionNewChannelReq( Nvm.MacGroup2.Region, &newChannelReq ); + + if( ( int8_t )status >= 0 ) + { + 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++; + } + Nvm.MacGroup2.MacParams.ReceiveDelay1 = delay * 1000; + Nvm.MacGroup2.MacParams.ReceiveDelay2 = Nvm.MacGroup2.MacParams.ReceiveDelay1 + 1000; + LoRaMacCommandsAddCmd( MOTE_MAC_RX_TIMING_SETUP_ANS, macCmdPayload, 0 ); + /*ST_WORKAROUND_BEGIN: remove unnecessary mlme operation to prevent uplinks burst */ + // Setup indication to inform the application + /* SetMlmeScheduleUplinkIndication( ); */ + /*ST_WORKAROUND_END */ + 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( Nvm.MacGroup2.Region, &txParamSetupReq ) != -1 ) + { + // Accept command + Nvm.MacGroup2.MacParams.UplinkDwellTime = txParamSetupReq.UplinkDwellTime; + Nvm.MacGroup2.MacParams.DownlinkDwellTime = txParamSetupReq.DownlinkDwellTime; + Nvm.MacGroup2.MacParams.MaxEirp = LoRaMacMaxEirpTable[txParamSetupReq.MaxEirp]; + // Update the datarate in case of the new configuration limits it + getPhy.Attribute = PHY_MIN_TX_DR; + getPhy.UplinkDwellTime = Nvm.MacGroup2.MacParams.UplinkDwellTime; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + Nvm.MacGroup1.ChannelsDatarate = MAX( Nvm.MacGroup1.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 = ( uint8_t )RegionDlChannelReq( Nvm.MacGroup2.Region, &dlChannelReq ); + + if( ( int8_t )status >= 0 ) + { + macCmdPayload[0] = status; + LoRaMacCommandsAddCmd( MOTE_MAC_DL_CHANNEL_ANS, macCmdPayload, 1 ); + /*ST_WORKAROUND_BEGIN: remove unnecessary mlme operation to prevent uplinks burst */ + // Setup indication to inform the application + /* SetMlmeScheduleUplinkIndication( ); */ + /*ST_WORKAROUND_END */ + } + break; + } + case SRV_MAC_DEVICE_TIME_ANS: + { + // The mote time can be updated only when the time is received in classA + // receive windows only. + if( LoRaMacConfirmQueueIsCmdActive( MLME_DEVICE_TIME ) == true ) + { + LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_OK, MLME_DEVICE_TIME ); + + 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 epoch 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; + } + else + { +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + // In case of other receive windows the Device Time Answer is not received. + MacCtx.McpsIndication.DeviceTimeAnsReceived = false; +#endif /* LORAMAC_VERSION */ + } + break; + } + case SRV_MAC_PING_SLOT_INFO_ANS: + { + if( LoRaMacConfirmQueueIsCmdActive( MLME_PING_SLOT_INFO ) == true ) + { + LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_OK, MLME_PING_SLOT_INFO ); + // 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; +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) + LoRaMacCommandsAddCmd( MOTE_MAC_PING_SLOT_FREQ_ANS, macCmdPayload, 1 ); +#elif ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + LoRaMacCommandsAddCmd( MOTE_MAC_PING_SLOT_CHANNEL_ANS, macCmdPayload, 1 ); + /*ST_WORKAROUND_BEGIN: remove unnecessary mlme operation to prevent uplinks burst */ + // Setup indication to inform the application + /* SetMlmeScheduleUplinkIndication( ); */ + /*ST_WORKAROUND_END */ +#endif /* LORAMAC_VERSION */ + break; + } + case SRV_MAC_BEACON_TIMING_ANS: + { + if( LoRaMacConfirmQueueIsCmdActive( MLME_BEACON_TIMING ) == true ) + { + LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_OK, MLME_BEACON_TIMING ); + 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; + } + } +} + +/* ST_WORKAROUND: Update Send request with new input parameter to allow delayed tx */ +static LoRaMacStatus_t Send( LoRaMacHeader_t* macHdr, uint8_t fPort, void* fBuffer, uint16_t fBufferSize, bool allowDelayedTx ) +{ + LoRaMacFrameCtrl_t fCtrl; + LoRaMacStatus_t status = LORAMAC_STATUS_PARAMETER_INVALID; + int8_t datarate = Nvm.MacGroup1.ChannelsDatarate; + int8_t txPower = Nvm.MacGroup1.ChannelsTxPower; + uint32_t adrAckCounter = Nvm.MacGroup1.AdrAckCounter; + CalcNextAdrParams_t adrNext; + + // Check if we are joined + if( Nvm.MacGroup2.NetworkActivation == ACTIVATION_TYPE_NONE ) + { + return LORAMAC_STATUS_NO_NETWORK_JOINED; + } + if( Nvm.MacGroup2.MaxDCycle == 0 ) + { + Nvm.MacGroup1.AggregatedTimeOff = 0; + } + + fCtrl.Value = 0; + fCtrl.Bits.FOptsLen = 0; + fCtrl.Bits.Adr = Nvm.MacGroup2.AdrCtrlOn; + + // Check class b + if( Nvm.MacGroup2.DeviceClass == CLASS_B ) + { + fCtrl.Bits.FPending = 1; + } + else + { + fCtrl.Bits.FPending = 0; + } + + // Check server ack + if( Nvm.MacGroup1.SrvAckRequested == true ) + { + fCtrl.Bits.Ack = 1; + } + + // ADR next request +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) + adrNext.Version = Nvm.MacGroup2.Version; + adrNext.UpdateChanMask = true; + adrNext.AdrEnabled = fCtrl.Bits.Adr; + adrNext.AdrAckCounter = Nvm.MacGroup1.AdrAckCounter; + adrNext.AdrAckLimit = MacCtx.AdrAckLimit; + adrNext.AdrAckDelay = MacCtx.AdrAckDelay; + adrNext.Datarate = Nvm.MacGroup1.ChannelsDatarate; + adrNext.TxPower = Nvm.MacGroup1.ChannelsTxPower; + adrNext.UplinkDwellTime = Nvm.MacGroup2.MacParams.UplinkDwellTime; + adrNext.Region = Nvm.MacGroup2.Region; + + fCtrl.Bits.AdrAckReq = LoRaMacAdrCalcNext( &adrNext, &Nvm.MacGroup1.ChannelsDatarate, + &Nvm.MacGroup1.ChannelsTxPower, &adrAckCounter ); +#elif ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + adrNext.UpdateChanMask = true; + adrNext.AdrEnabled = fCtrl.Bits.Adr; + adrNext.AdrAckCounter = Nvm.MacGroup1.AdrAckCounter; + adrNext.AdrAckLimit = MacCtx.AdrAckLimit; + adrNext.AdrAckDelay = MacCtx.AdrAckDelay; + adrNext.Datarate = Nvm.MacGroup1.ChannelsDatarate; + adrNext.TxPower = Nvm.MacGroup1.ChannelsTxPower; + adrNext.NbTrans = Nvm.MacGroup2.MacParams.ChannelsNbTrans; + adrNext.UplinkDwellTime = Nvm.MacGroup2.MacParams.UplinkDwellTime; + adrNext.Region = Nvm.MacGroup2.Region; + + fCtrl.Bits.AdrAckReq = LoRaMacAdrCalcNext( &adrNext, &Nvm.MacGroup1.ChannelsDatarate, + &Nvm.MacGroup1.ChannelsTxPower, + &Nvm.MacGroup2.MacParams.ChannelsNbTrans, &adrAckCounter ); +#endif /* LORAMAC_VERSION */ + + // 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( allowDelayedTx ); /* ST_WORKAROUND: Update Send request with new input parameter to allow delayed tx */ + } + + // Post processing + if( status != LORAMAC_STATUS_OK ) + { + // Bad case - restore + // Store local variables + Nvm.MacGroup1.ChannelsDatarate = datarate; + Nvm.MacGroup1.ChannelsTxPower = txPower; + } + else + { + // Good case + Nvm.MacGroup1.SrvAckRequested = false; + Nvm.MacGroup1.AdrAckCounter = adrAckCounter; + // Remove all none sticky MAC commands + if( LoRaMacCommandsRemoveNoneStickyCmds( ) != LORAMAC_COMMANDS_SUCCESS ) + { + return LORAMAC_STATUS_MAC_COMMAD_ERROR; + } + } + return status; +} + +static 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( Nvm.MacGroup2.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 void ComputeRxWindowParameters( void ) +{ + // Compute Rx1 windows parameters + RegionComputeRxWindowParameters( Nvm.MacGroup2.Region, + RegionApplyDrOffset( Nvm.MacGroup2.Region, + Nvm.MacGroup2.MacParams.DownlinkDwellTime, + Nvm.MacGroup1.ChannelsDatarate, + Nvm.MacGroup2.MacParams.Rx1DrOffset ), + Nvm.MacGroup2.MacParams.MinRxSymbols, + Nvm.MacGroup2.MacParams.SystemMaxRxError, + &MacCtx.RxWindow1Config ); + // Compute Rx2 windows parameters + RegionComputeRxWindowParameters( Nvm.MacGroup2.Region, + Nvm.MacGroup2.MacParams.Rx2Channel.Datarate, + Nvm.MacGroup2.MacParams.MinRxSymbols, + Nvm.MacGroup2.MacParams.SystemMaxRxError, + &MacCtx.RxWindow2Config ); + + // Default setup, in case the device joined + MacCtx.RxWindow1Delay = Nvm.MacGroup2.MacParams.ReceiveDelay1 + MacCtx.RxWindow1Config.WindowOffset; + MacCtx.RxWindow2Delay = Nvm.MacGroup2.MacParams.ReceiveDelay2 + MacCtx.RxWindow2Config.WindowOffset; + + if( Nvm.MacGroup2.NetworkActivation == ACTIVATION_TYPE_NONE ) + { + MacCtx.RxWindow1Delay = Nvm.MacGroup2.MacParams.JoinAcceptDelay1 + MacCtx.RxWindow1Config.WindowOffset; + MacCtx.RxWindow2Delay = Nvm.MacGroup2.MacParams.JoinAcceptDelay2 + MacCtx.RxWindow2Config.WindowOffset; + } +} + +static LoRaMacStatus_t VerifyTxFrame( void ) +{ + size_t macCmdsSize = 0; + + if( Nvm.MacGroup2.NetworkActivation != ACTIVATION_TYPE_NONE ) + { + if( LoRaMacCommandsGetSizeSerializedCmds( &macCmdsSize ) != LORAMAC_COMMANDS_SUCCESS ) + { + return LORAMAC_STATUS_MAC_COMMAD_ERROR; + } + + if( ValidatePayloadLength( MacCtx.AppDataSize, Nvm.MacGroup1.ChannelsDatarate, macCmdsSize ) == false ) + { + return LORAMAC_STATUS_LENGTH_ERROR; + } + } + return LORAMAC_STATUS_OK; +} + +static LoRaMacStatus_t SerializeTxFrame( void ) +{ + LoRaMacSerializerStatus_t serializeStatus; + + switch( MacCtx.TxMsg.Type ) + { + case LORAMAC_MSG_TYPE_JOIN_REQUEST: + serializeStatus = LoRaMacSerializerJoinRequest( &MacCtx.TxMsg.Message.JoinReq ); + if( LORAMAC_SERIALIZER_SUCCESS != serializeStatus ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + MacCtx.PktBufferLen = MacCtx.TxMsg.Message.JoinReq.BufSize; + break; + case LORAMAC_MSG_TYPE_DATA: + serializeStatus = LoRaMacSerializerData( &MacCtx.TxMsg.Message.Data ); + if( LORAMAC_SERIALIZER_SUCCESS != serializeStatus ) + { + 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 LoRaMacStatus_t ScheduleTx( bool allowDelayedTx ) +{ + LoRaMacStatus_t status = LORAMAC_STATUS_PARAMETER_INVALID; + NextChanParams_t nextChan; + + // Check class b collisions + status = CheckForClassBCollision( ); + if( status != LORAMAC_STATUS_OK ) + { + return status; + } + + // Update back-off + CalculateBackOff( ); + + // Serialize frame + status = SerializeTxFrame( ); + if( status != LORAMAC_STATUS_OK ) + { + return status; + } + + nextChan.AggrTimeOff = Nvm.MacGroup1.AggregatedTimeOff; + nextChan.Datarate = Nvm.MacGroup1.ChannelsDatarate; + nextChan.DutyCycleEnabled = Nvm.MacGroup2.DutyCycleOn; + nextChan.ElapsedTimeSinceStartUp = SysTimeSub( SysTimeGetMcuTime( ), Nvm.MacGroup2.InitializationTime ); + nextChan.LastAggrTx = Nvm.MacGroup1.LastTxDoneTime; + nextChan.LastTxIsJoinRequest = false; + nextChan.Joined = true; + nextChan.PktLen = MacCtx.PktBufferLen; + + // Setup the parameters based on the join status + if( Nvm.MacGroup2.NetworkActivation == ACTIVATION_TYPE_NONE ) + { + nextChan.LastTxIsJoinRequest = true; + nextChan.Joined = false; + } + + // Select channel + status = RegionNextChannel( Nvm.MacGroup2.Region, &nextChan, &MacCtx.Channel, &MacCtx.DutyCycleWaitTime, &Nvm.MacGroup1.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( MacCtx.DutyCycleWaitTime != 0 ) + {// Send later - prepare timer + MacCtx.MacState |= LORAMAC_TX_DELAYED; + TimerSetValue( &MacCtx.TxDelayedTimer, MacCtx.DutyCycleWaitTime ); + TimerStart( &MacCtx.TxDelayedTimer ); + } + return LORAMAC_STATUS_OK; + } + else + {// State where the MAC cannot send a frame + return status; + } + } + + // Compute window parameters, offsets, rx symbols, system errors etc. + ComputeRxWindowParameters( ); + + // Verify TX frame + status = VerifyTxFrame( ); + if( status != LORAMAC_STATUS_OK ) + { + return status; + } + + // 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 ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) + if( ( MacCtx.ChannelsNbTransCounter >= 1 ) || ( MacCtx.AckTimeoutRetriesCounter > 1 ) ) +#elif ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + if( MacCtx.ChannelsNbTransCounter >= 1 ) +#endif /* LORAMAC_VERSION */ + { + 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( void ) +{ + // Make sure that the calculation of the backoff time for the aggregated time off will only be done in + // case the value is zero. It will be set to zero in the function RegionNextChannel. + if( Nvm.MacGroup1.AggregatedTimeOff == 0 ) + { + // 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 + Nvm.MacGroup1.AggregatedTimeOff = ( MacCtx.TxTimeOnAir * Nvm.MacGroup2.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 ) +{ + LoRaMacClassBCallback_t classBCallbacks; + LoRaMacClassBParams_t classBParams; + + Nvm.MacGroup2.NetworkActivation = ACTIVATION_TYPE_NONE; + + // ADR counter + Nvm.MacGroup1.AdrAckCounter = 0; + + MacCtx.ChannelsNbTransCounter = 0; +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) + MacCtx.AckTimeoutRetries = 1; + MacCtx.AckTimeoutRetriesCounter = 1; + MacCtx.AckTimeoutRetry = false; +#elif ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + MacCtx.RetransmitTimeoutRetry = false; + MacCtx.ResponseTimeoutStartTime = 0; +#endif /* LORAMAC_VERSION */ + + Nvm.MacGroup2.MaxDCycle = 0; + Nvm.MacGroup2.AggregatedDCycle = 1; + + Nvm.MacGroup1.ChannelsTxPower = Nvm.MacGroup2.ChannelsTxPowerDefault; + Nvm.MacGroup1.ChannelsDatarate = Nvm.MacGroup2.ChannelsDatarateDefault; + Nvm.MacGroup2.MacParams.Rx1DrOffset = Nvm.MacGroup2.MacParamsDefaults.Rx1DrOffset; + Nvm.MacGroup2.MacParams.Rx2Channel = Nvm.MacGroup2.MacParamsDefaults.Rx2Channel; + Nvm.MacGroup2.MacParams.RxCChannel = Nvm.MacGroup2.MacParamsDefaults.RxCChannel; + Nvm.MacGroup2.MacParams.UplinkDwellTime = Nvm.MacGroup2.MacParamsDefaults.UplinkDwellTime; + Nvm.MacGroup2.MacParams.DownlinkDwellTime = Nvm.MacGroup2.MacParamsDefaults.DownlinkDwellTime; + Nvm.MacGroup2.MacParams.MaxEirp = Nvm.MacGroup2.MacParamsDefaults.MaxEirp; + Nvm.MacGroup2.MacParams.AntennaGain = Nvm.MacGroup2.MacParamsDefaults.AntennaGain; + + MacCtx.NodeAckRequested = false; + Nvm.MacGroup1.SrvAckRequested = false; +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + Nvm.MacGroup2.ChannelsDatarateChangedLinkAdrReq = false; + Nvm.MacGroup2.DownlinkReceived = false; +#endif /* LORAMAC_VERSION */ + + // Reset to application defaults + InitDefaultsParams_t params; + params.Type = INIT_TYPE_RESET_TO_DEFAULT_CHANNELS; + params.NvmGroup1 = &Nvm.RegionGroup1; + params.NvmGroup2 = &Nvm.RegionGroup2; +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + params.Bands = &RegionBands; +#endif /* LORAMAC_VERSION */ + RegionInitDefaults( Nvm.MacGroup2.Region, ¶ms ); + + // Initialize channel index. + MacCtx.Channel = 0; + + // Initialize Rx2 config parameters. + MacCtx.RxWindow2Config.Channel = MacCtx.Channel; + MacCtx.RxWindow2Config.Frequency = Nvm.MacGroup2.MacParams.Rx2Channel.Frequency; + MacCtx.RxWindow2Config.DownlinkDwellTime = Nvm.MacGroup2.MacParams.DownlinkDwellTime; + MacCtx.RxWindow2Config.RepeaterSupport = Nvm.MacGroup2.MacParams.RepeaterSupport; /* ST_WORKAROUND: Keep repeater feature */ + MacCtx.RxWindow2Config.RxContinuous = false; + MacCtx.RxWindow2Config.RxSlot = RX_SLOT_WIN_2; +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + MacCtx.RxWindow2Config.NetworkActivation = Nvm.MacGroup2.NetworkActivation; +#endif /* LORAMAC_VERSION */ + + // Initialize RxC config parameters. + MacCtx.RxWindowCConfig = MacCtx.RxWindow2Config; + MacCtx.RxWindowCConfig.RxContinuous = true; + MacCtx.RxWindowCConfig.RxSlot = RX_SLOT_WIN_CLASS_C; + + // Initialize class b + // Apply callback + classBCallbacks.GetTemperatureLevel = NULL; + classBCallbacks.MacProcessNotify = NULL; + + if( MacCtx.MacCallbacks != NULL ) + { + classBCallbacks.GetTemperatureLevel = MacCtx.MacCallbacks->GetTemperatureLevel; + classBCallbacks.MacProcessNotify = MacCtx.MacCallbacks->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 = &Nvm.MacGroup2.DevAddr; + classBParams.LoRaMacRegion = &Nvm.MacGroup2.Region; + classBParams.LoRaMacParams = &Nvm.MacGroup2.MacParams; + classBParams.MulticastChannels = &Nvm.MacGroup2.MulticastChannelList[0]; +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + classBParams.NetworkActivation = &Nvm.MacGroup2.NetworkActivation; +#endif /* LORAMAC_VERSION */ + + LoRaMacClassBInit( &classBParams, &classBCallbacks, &Nvm.ClassB ); +} + +/*! + * \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( Nvm.MacGroup2.Region, rxConfig, ( int8_t* )&MacCtx.McpsIndication.RxDatarate ) == true ) + { + MacCtx.MlmeIndication.RxDatarate = MacCtx.McpsIndication.RxDatarate; + Radio.Rx( Nvm.MacGroup2.MacParams.MaxRxWindow ); + MacCtx.RxSlot = rxConfig->RxSlot; + } +} + +static void OpenContinuousRxCWindow( void ) +{ + // Compute RxC windows parameters + RegionComputeRxWindowParameters( Nvm.MacGroup2.Region, + Nvm.MacGroup2.MacParams.RxCChannel.Datarate, + Nvm.MacGroup2.MacParams.MinRxSymbols, + Nvm.MacGroup2.MacParams.SystemMaxRxError, + &MacCtx.RxWindowCConfig ); + + MacCtx.RxWindowCConfig.RxSlot = RX_SLOT_WIN_CLASS_C; +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + MacCtx.RxWindowCConfig.NetworkActivation = Nvm.MacGroup2.NetworkActivation; +#endif /* LORAMAC_VERSION */ + // 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( Nvm.MacGroup2.Region, &MacCtx.RxWindowCConfig, ( int8_t* )&MacCtx.McpsIndication.RxDatarate ) == true ) + { + MacCtx.MlmeIndication.RxDatarate = MacCtx.McpsIndication.RxDatarate; + Radio.Rx( 0 ); // Continuous mode + MacCtx.RxSlot = MacCtx.RxWindowCConfig.RxSlot; + } +} + +static 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 = Nvm.MacGroup2.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 +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) + MacCtx.McpsConfirm.NbRetries = 0; +#elif ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + MacCtx.McpsConfirm.NbTrans = 0; +#endif /* LORAMAC_VERSION */ + 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( Nvm.MacGroup1.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.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.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.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; +} + +static LoRaMacStatus_t SendFrameOnChannel( uint8_t channel ) +{ + LoRaMacStatus_t status = LORAMAC_STATUS_PARAMETER_INVALID; + TxConfigParams_t txConfig; + int8_t txPower = 0; + + txConfig.Channel = channel; + txConfig.Datarate = Nvm.MacGroup1.ChannelsDatarate; + txConfig.TxPower = Nvm.MacGroup1.ChannelsTxPower; + txConfig.MaxEirp = Nvm.MacGroup2.MacParams.MaxEirp; + txConfig.AntennaGain = Nvm.MacGroup2.MacParams.AntennaGain; + txConfig.PktLen = MacCtx.PktBufferLen; +#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + txConfig.NetworkActivation = Nvm.MacGroup2.NetworkActivation; +#endif /* LORAMAC_VERSION */ + + RegionTxConfig( Nvm.MacGroup2.Region, &txConfig, &txPower, &MacCtx.TxTimeOnAir ); + + MacCtx.McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; + MacCtx.McpsConfirm.Datarate = Nvm.MacGroup1.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( Nvm.MacGroup2.DeviceClass == CLASS_B ) + { + // Stop slots for class b + LoRaMacClassBStopRxSlots( ); + } + + LoRaMacClassBHaltBeaconing( ); + + // Secure frame + status = SecureFrame( Nvm.MacGroup1.ChannelsDatarate, MacCtx.Channel ); + if( status != LORAMAC_STATUS_OK ) + { + return status; + } + + MacCtx.MacState |= LORAMAC_TX_RUNNING; +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) + if( MacCtx.NodeAckRequested == false ) + { + MacCtx.ChannelsNbTransCounter++; + } +#elif ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + MacCtx.ChannelsNbTransCounter++; + MacCtx.McpsConfirm.NbTrans = MacCtx.ChannelsNbTransCounter; + MacCtx.ResponseTimeoutStartTime = 0; +#endif /* LORAMAC_VERSION */ + + // Send now + Radio.Send( MacCtx.PktBuffer, MacCtx.PktBufferLen ); + + return LORAMAC_STATUS_OK; +} + +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) +static LoRaMacStatus_t SetTxContinuousWave( uint16_t timeout ) +{ + ContinuousWaveParams_t continuousWave; + + continuousWave.Channel = MacCtx.Channel; + continuousWave.Datarate = Nvm.MacGroup1.ChannelsDatarate; + continuousWave.TxPower = Nvm.MacGroup1.ChannelsTxPower; + continuousWave.MaxEirp = Nvm.MacGroup2.MacParams.MaxEirp; + continuousWave.AntennaGain = Nvm.MacGroup2.MacParams.AntennaGain; + continuousWave.Timeout = timeout; + + RegionSetContinuousWave( Nvm.MacGroup2.Region, &continuousWave ); + + MacCtx.MacState |= LORAMAC_TX_RUNNING; + + return LORAMAC_STATUS_OK; +} + +static 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; +} +#elif ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) +static LoRaMacStatus_t SetTxContinuousWave( uint16_t timeout, uint32_t frequency, uint8_t power ) +{ + Radio.SetTxContinuousWave( frequency, power, timeout ); + + MacCtx.MacState |= LORAMAC_TX_RUNNING; + + return LORAMAC_STATUS_OK; +} +#endif /* LORAMAC_VERSION */ + +static LoRaMacStatus_t RestoreNvmData( void ) +{ + uint32_t crc = 0; + + // Status and parameter validation + if( MacCtx.MacState != LORAMAC_STOPPED ) + { + return LORAMAC_STATUS_BUSY; + } + + // Crypto + crc = Crc32( ( uint8_t* ) &(NvmBackup.Crypto), sizeof( NvmBackup.Crypto ) - + sizeof( NvmBackup.Crypto.Crc32 ) ); + if( crc != NvmBackup.Crypto.Crc32 ) + { + return LORAMAC_STATUS_NVM_DATA_INCONSISTENT; + } + + // MacGroup1 + crc = Crc32( ( uint8_t* ) &(NvmBackup.MacGroup1), sizeof( NvmBackup.MacGroup1 ) - + sizeof( NvmBackup.MacGroup1.Crc32 ) ); + if( crc != NvmBackup.MacGroup1.Crc32 ) + { + return LORAMAC_STATUS_NVM_DATA_INCONSISTENT; + } + + // MacGroup2 + crc = Crc32( ( uint8_t* ) &(NvmBackup.MacGroup2), sizeof( NvmBackup.MacGroup2 ) - + sizeof( NvmBackup.MacGroup2.Crc32 ) ); + if( crc != NvmBackup.MacGroup2.Crc32 ) + { + return LORAMAC_STATUS_NVM_DATA_INCONSISTENT; + } + + // Secure Element + crc = Crc32( ( uint8_t* ) &(NvmBackup.SecureElement), sizeof( NvmBackup.SecureElement ) - + sizeof( NvmBackup.SecureElement.Crc32 ) ); + if( crc != NvmBackup.SecureElement.Crc32 ) + { + return LORAMAC_STATUS_NVM_DATA_INCONSISTENT; + } + + // Region + crc = Crc32( ( uint8_t* ) &(NvmBackup.RegionGroup1), sizeof( NvmBackup.RegionGroup1 ) - + sizeof( NvmBackup.RegionGroup1.Crc32 ) ); + if( crc != NvmBackup.RegionGroup1.Crc32 ) + { + return LORAMAC_STATUS_NVM_DATA_INCONSISTENT; + } + + crc = Crc32( ( uint8_t* ) &(NvmBackup.ClassB), sizeof( NvmBackup.ClassB ) - + sizeof( NvmBackup.ClassB.Crc32 ) ); + if( crc != NvmBackup.ClassB.Crc32 ) + { + return LORAMAC_STATUS_NVM_DATA_INCONSISTENT; + } + + memcpy1( ( uint8_t* ) &Nvm, ( uint8_t* ) &NvmBackup, sizeof( LoRaMacNvmData_t ) ); + memset1( ( uint8_t* ) &NvmBackup, 0, sizeof( LoRaMacNvmData_t ) ); + + // Initialize RxC config parameters. + MacCtx.RxWindowCConfig.Channel = MacCtx.Channel; + MacCtx.RxWindowCConfig.Frequency = Nvm.MacGroup2.MacParams.RxCChannel.Frequency; + MacCtx.RxWindowCConfig.DownlinkDwellTime = Nvm.MacGroup2.MacParams.DownlinkDwellTime; + MacCtx.RxWindowCConfig.RxContinuous = true; + MacCtx.RxWindowCConfig.RxSlot = RX_SLOT_WIN_CLASS_C; + + return LORAMAC_STATUS_OK; +} + +static 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; +} + +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) +static bool CheckRetransUnconfirmedUplink( void ) +{ + // Unconfirmed uplink, when all retransmissions are done. + if( MacCtx.ChannelsNbTransCounter >= + Nvm.MacGroup2.MacParams.ChannelsNbTrans ) + { + return true; + } + else if( MacCtx.MacFlags.Bits.McpsInd == 1 ) + { + // For Class A stop in each case + if( Nvm.MacGroup2.DeviceClass == CLASS_A ) + { + return true; + } + else + {// For Class B & C stop only if the frame was received in RX1 window + if( MacCtx.RxStatus.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; +} +#elif ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) +static bool CheckRetrans( uint8_t counter, uint8_t limit ) +{ + if( counter >= limit ) + { + return true; + } + return false; +} + +static bool CheckRetransUnconfirmedUplink( void ) +{ + // Verify, if the max number of retransmissions have been reached + if( CheckRetrans( MacCtx.ChannelsNbTransCounter, + Nvm.MacGroup2.MacParams.ChannelsNbTrans ) == true ) + { + return true; + } + + if( MacCtx.MacFlags.Bits.McpsInd == 1 ) + { + // Stop the retransmissions, if a valid downlink is received + // a class A RX window. This holds also for class B and C. + if( ( MacCtx.RxStatus.RxSlot == RX_SLOT_WIN_1 ) || + ( MacCtx.RxStatus.RxSlot == RX_SLOT_WIN_2 ) ) + { + return true; + } + } + return false; +} + +static bool CheckRetransConfirmedUplink( void ) +{ + // Verify, if the max number of retransmissions have been reached + if( CheckRetrans( MacCtx.ChannelsNbTransCounter, + Nvm.MacGroup2.MacParams.ChannelsNbTrans ) == true ) + { + return true; + } + + if( MacCtx.MacFlags.Bits.McpsInd == 1 ) + { + if( MacCtx.McpsConfirm.AckReceived == true ) + { + return true; + } + } + return false; +} + +static uint32_t IncreaseAdrAckCounter( uint32_t counter ) +{ + if( counter < ADR_ACK_COUNTER_MAX ) + { + counter++; + } + return counter; +} +#endif /* LORAMAC_VERSION */ + +static bool StopRetransmission( void ) +{ + if( ( MacCtx.MacFlags.Bits.McpsInd == 0 ) || + ( ( MacCtx.RxStatus.RxSlot != RX_SLOT_WIN_1 ) && + ( MacCtx.RxStatus.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( Nvm.MacGroup2.AdrCtrlOn == true ) + { +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) + Nvm.MacGroup1.AdrAckCounter++; +#elif ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + Nvm.MacGroup1.AdrAckCounter = IncreaseAdrAckCounter( Nvm.MacGroup1.AdrAckCounter ); +#endif /* LORAMAC_VERSION */ + } + } + + MacCtx.ChannelsNbTransCounter = 0; + MacCtx.NodeAckRequested = false; +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) + MacCtx.AckTimeoutRetry = false; +#elif ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + MacCtx.RetransmitTimeoutRetry = false; +#endif /* LORAMAC_VERSION */ + MacCtx.MacState &= ~LORAMAC_TX_RUNNING; + + return true; +} + +static void CallNvmDataChangeCallback( uint16_t notifyFlags ) +{ + if( ( MacCtx.MacCallbacks != NULL ) && + ( MacCtx.MacCallbacks->NvmDataChange != NULL ) ) + { + MacCtx.MacCallbacks->NvmDataChange ( notifyFlags ); + } +} + +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) +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 = Nvm.MacGroup2.MacParams.UplinkDwellTime; + getPhy.Datarate = Nvm.MacGroup1.ChannelsDatarate; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + Nvm.MacGroup1.ChannelsDatarate = phyParam.Value; + } + } +} + +static void AckTimeoutRetriesFinalize( void ) +{ + if( MacCtx.McpsConfirm.AckReceived == false ) + { + InitDefaultsParams_t params; + params.Type = INIT_TYPE_ACTIVATE_DEFAULT_CHANNELS; + params.NvmGroup1 = &Nvm.RegionGroup1; + params.NvmGroup2 = &Nvm.RegionGroup2; + RegionInitDefaults( Nvm.MacGroup2.Region, ¶ms ); + + MacCtx.NodeAckRequested = false; + MacCtx.McpsConfirm.AckReceived = false; + } + MacCtx.McpsConfirm.NbRetries = MacCtx.AckTimeoutRetriesCounter; +} +#endif /* LORAMAC_VERSION */ + +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; + + 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 ); + + // Initialize the module context with zeros + memset1( ( uint8_t* ) &Nvm, 0x00, sizeof( LoRaMacNvmData_t ) ); + memset1( ( uint8_t* ) &MacCtx, 0x00, sizeof( LoRaMacCtx_t ) ); + + // Set non zero variables to its default value +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) + MacCtx.AckTimeoutRetriesCounter = 1; + MacCtx.AckTimeoutRetries = 1; +#endif /* LORAMAC_VERSION */ + Nvm.MacGroup2.Region = region; + Nvm.MacGroup2.DeviceClass = CLASS_A; + Nvm.MacGroup2.MacParams.RepeaterSupport = false; /* ST_WORKAROUND: Keep repeater feature */ + + // Setup version + Nvm.MacGroup2.Version.Value = LORAMAC_VERSION; +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + InitDefaultsParams_t params; + params.Type = INIT_TYPE_DEFAULTS; + params.NvmGroup1 = &Nvm.RegionGroup1; + params.NvmGroup2 = &Nvm.RegionGroup2; + params.Bands = &RegionBands; + RegionInitDefaults( Nvm.MacGroup2.Region, ¶ms ); +#endif /* LORAMAC_VERSION */ + + // Reset to defaults + getPhy.Attribute = PHY_DUTY_CYCLE; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + Nvm.MacGroup2.DutyCycleOn = ( bool ) phyParam.Value; + + getPhy.Attribute = PHY_DEF_TX_POWER; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + Nvm.MacGroup2.ChannelsTxPowerDefault = phyParam.Value; + + getPhy.Attribute = PHY_DEF_TX_DR; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + Nvm.MacGroup2.ChannelsDatarateDefault = phyParam.Value; + + getPhy.Attribute = PHY_MAX_RX_WINDOW; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + Nvm.MacGroup2.MacParamsDefaults.MaxRxWindow = phyParam.Value; + + getPhy.Attribute = PHY_RECEIVE_DELAY1; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + Nvm.MacGroup2.MacParamsDefaults.ReceiveDelay1 = phyParam.Value; + + getPhy.Attribute = PHY_RECEIVE_DELAY2; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + Nvm.MacGroup2.MacParamsDefaults.ReceiveDelay2 = phyParam.Value; + + getPhy.Attribute = PHY_JOIN_ACCEPT_DELAY1; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + Nvm.MacGroup2.MacParamsDefaults.JoinAcceptDelay1 = phyParam.Value; + + getPhy.Attribute = PHY_JOIN_ACCEPT_DELAY2; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + Nvm.MacGroup2.MacParamsDefaults.JoinAcceptDelay2 = phyParam.Value; + + getPhy.Attribute = PHY_DEF_DR1_OFFSET; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + Nvm.MacGroup2.MacParamsDefaults.Rx1DrOffset = phyParam.Value; + + getPhy.Attribute = PHY_DEF_RX2_FREQUENCY; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + Nvm.MacGroup2.MacParamsDefaults.Rx2Channel.Frequency = phyParam.Value; + Nvm.MacGroup2.MacParamsDefaults.RxCChannel.Frequency = phyParam.Value; + + getPhy.Attribute = PHY_DEF_RX2_DR; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + Nvm.MacGroup2.MacParamsDefaults.Rx2Channel.Datarate = phyParam.Value; + Nvm.MacGroup2.MacParamsDefaults.RxCChannel.Datarate = phyParam.Value; + + getPhy.Attribute = PHY_DEF_UPLINK_DWELL_TIME; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + Nvm.MacGroup2.MacParamsDefaults.UplinkDwellTime = phyParam.Value; + + getPhy.Attribute = PHY_DEF_DOWNLINK_DWELL_TIME; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + Nvm.MacGroup2.MacParamsDefaults.DownlinkDwellTime = phyParam.Value; + + getPhy.Attribute = PHY_DEF_MAX_EIRP; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + Nvm.MacGroup2.MacParamsDefaults.MaxEirp = phyParam.fValue; + + getPhy.Attribute = PHY_DEF_ANTENNA_GAIN; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + Nvm.MacGroup2.MacParamsDefaults.AntennaGain = phyParam.fValue; + + getPhy.Attribute = PHY_DEF_ADR_ACK_LIMIT; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + MacCtx.AdrAckLimit = phyParam.Value; + + getPhy.Attribute = PHY_DEF_ADR_ACK_DELAY; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + MacCtx.AdrAckDelay = phyParam.Value; + + // Init parameters which are not set in function ResetMacParameters + Nvm.MacGroup2.MacParamsDefaults.ChannelsNbTrans = 1; + Nvm.MacGroup2.MacParamsDefaults.SystemMaxRxError = 10; + Nvm.MacGroup2.MacParamsDefaults.MinRxSymbols = 6; + + Nvm.MacGroup2.MacParams.SystemMaxRxError = Nvm.MacGroup2.MacParamsDefaults.SystemMaxRxError; + Nvm.MacGroup2.MacParams.MinRxSymbols = Nvm.MacGroup2.MacParamsDefaults.MinRxSymbols; + Nvm.MacGroup2.MacParams.MaxRxWindow = Nvm.MacGroup2.MacParamsDefaults.MaxRxWindow; + Nvm.MacGroup2.MacParams.ReceiveDelay1 = Nvm.MacGroup2.MacParamsDefaults.ReceiveDelay1; + Nvm.MacGroup2.MacParams.ReceiveDelay2 = Nvm.MacGroup2.MacParamsDefaults.ReceiveDelay2; + Nvm.MacGroup2.MacParams.JoinAcceptDelay1 = Nvm.MacGroup2.MacParamsDefaults.JoinAcceptDelay1; + Nvm.MacGroup2.MacParams.JoinAcceptDelay2 = Nvm.MacGroup2.MacParamsDefaults.JoinAcceptDelay2; + Nvm.MacGroup2.MacParams.ChannelsNbTrans = Nvm.MacGroup2.MacParamsDefaults.ChannelsNbTrans; + +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) + InitDefaultsParams_t params; + params.Type = INIT_TYPE_DEFAULTS; + params.NvmGroup1 = &Nvm.RegionGroup1; + params.NvmGroup2 = &Nvm.RegionGroup2; + RegionInitDefaults( Nvm.MacGroup2.Region, ¶ms ); +#elif ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + // FPort 224 is enabled by default. + Nvm.MacGroup2.IsCertPortOn = true; +#endif /* LORAMAC_VERSION */ + + MacCtx.MacCallbacks = callbacks; + ResetMacParameters( ); + + Nvm.MacGroup2.PublicNetwork = true; + + MacCtx.MacPrimitives = primitives; + MacCtx.MacFlags.Value = 0; + MacCtx.MacState = LORAMAC_STOPPED; + + // Reset duty cycle times + Nvm.MacGroup1.LastTxDoneTime = 0; + Nvm.MacGroup1.AggregatedTimeOff = 0; + + // Initialize timers + TimerInit( &MacCtx.TxDelayedTimer, OnTxDelayedTimerEvent ); + TimerInit( &MacCtx.RxWindowTimer1, OnRxWindow1TimerEvent ); + TimerInit( &MacCtx.RxWindowTimer2, OnRxWindow2TimerEvent ); +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) + TimerInit( &MacCtx.AckTimeoutTimer, OnAckTimeoutTimerEvent ); +#elif ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + TimerInit( &MacCtx.RetransmitTimeoutTimer, OnRetransmitTimeoutTimerEvent ); +#endif /* LORAMAC_VERSION */ + + // Store the current initialization time + Nvm.MacGroup2.InitializationTime = SysTimeGetMcuTime( ); + +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + // Initialize MAC radio events + LoRaMacRadioEvents.Value = 0; +#endif /* LORAMAC_VERSION */ + + // 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 ); + + // Initialize the Secure Element driver + if( SecureElementInit( &Nvm.SecureElement, callbacks->GetUniqueId ) != SECURE_ELEMENT_SUCCESS ) /* ST_WORKAROUND: Add unique ID callback as input parameter */ + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + + // Initialize Crypto module + if( LoRaMacCryptoInit( &Nvm.Crypto ) != LORAMAC_CRYPTO_SUCCESS ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + + // Initialize MAC commands module + if( LoRaMacCommandsInit( ) != LORAMAC_COMMANDS_SUCCESS ) + { + return LORAMAC_STATUS_MAC_COMMAD_ERROR; + } + + // Set multicast downlink counter reference + if( LoRaMacCryptoSetMulticastReference( Nvm.MacGroup2.MulticastChannelList ) != LORAMAC_CRYPTO_SUCCESS ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + + // Random seed initialization + srand1( Radio.Random( ) ); + + Radio.SetPublicNetwork( Nvm.MacGroup2.PublicNetwork ); + Radio.Sleep( ); + + 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 LoRaMacHalt( void ) +{ + // Stop Timers + TimerStop( &MacCtx.TxDelayedTimer ); + TimerStop( &MacCtx.RxWindowTimer1 ); + TimerStop( &MacCtx.RxWindowTimer2 ); +#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) + TimerStop( &MacCtx.AckTimeoutTimer ); +#elif (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + TimerStop( &MacCtx.RetransmitTimeoutTimer ); +#endif /* LORAMAC_VERSION */ + + // Take care about class B + LoRaMacClassBHaltBeaconing( ); + + // Switch off Radio + Radio.Sleep( ); + + MacCtx.MacState = LORAMAC_IDLE; + + LoRaMacHandleNvm( &Nvm ); + + // Preserve the Nvm context if data retention + memcpy1( ( uint8_t* ) &NvmBackup, ( uint8_t* ) &Nvm, sizeof( LoRaMacNvmData_t ) ); + + MacCtx.MacState = LORAMAC_STOPPED; + + return LORAMAC_STATUS_OK; +} + +LoRaMacStatus_t LoRaMacQueryTxPossible( uint8_t size, LoRaMacTxInfo_t* txInfo ) +{ + CalcNextAdrParams_t adrNext; + uint32_t adrAckCounter = Nvm.MacGroup1.AdrAckCounter; + int8_t datarate = Nvm.MacGroup2.ChannelsDatarateDefault; + int8_t txPower = Nvm.MacGroup2.ChannelsTxPowerDefault; +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + uint8_t nbTrans = MacCtx.ChannelsNbTransCounter; +#endif /* LORAMAC_VERSION */ + size_t macCmdsSize = 0; + + if( txInfo == NULL ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + // Setup ADR request +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) + adrNext.Version = Nvm.MacGroup2.Version; +#endif /* LORAMAC_VERSION */ + adrNext.UpdateChanMask = false; + adrNext.AdrEnabled = Nvm.MacGroup2.AdrCtrlOn; + adrNext.AdrAckCounter = Nvm.MacGroup1.AdrAckCounter; + adrNext.AdrAckLimit = MacCtx.AdrAckLimit; + adrNext.AdrAckDelay = MacCtx.AdrAckDelay; + adrNext.Datarate = Nvm.MacGroup1.ChannelsDatarate; + adrNext.TxPower = Nvm.MacGroup1.ChannelsTxPower; +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + adrNext.NbTrans = MacCtx.ChannelsNbTransCounter; +#endif /* LORAMAC_VERSION */ + adrNext.UplinkDwellTime = Nvm.MacGroup2.MacParams.UplinkDwellTime; + adrNext.Region = Nvm.MacGroup2.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. +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) + LoRaMacAdrCalcNext( &adrNext, &datarate, &txPower, &adrAckCounter ); +#elif ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + LoRaMacAdrCalcNext( &adrNext, &datarate, &txPower, &nbTrans, &adrAckCounter ); +#endif /* LORAMAC_VERSION */ + + 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 = Nvm.MacGroup2.DeviceClass; + break; + } + case MIB_NETWORK_ACTIVATION: + { + mibGet->Param.NetworkActivation = Nvm.MacGroup2.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 = Nvm.MacGroup2.AdrCtrlOn; + break; + } + case MIB_NET_ID: + { + mibGet->Param.NetID = Nvm.MacGroup2.NetID; + break; + } + case MIB_DEV_ADDR: + { + mibGet->Param.DevAddr = Nvm.MacGroup2.DevAddr; + break; + } + case MIB_PUBLIC_NETWORK: + { + mibGet->Param.EnablePublicNetwork = Nvm.MacGroup2.PublicNetwork; + break; + } + /* ST_WORKAROUND_BEGIN: Keep repeater feature */ + case MIB_REPEATER_SUPPORT: + { + mibGet->Param.EnableRepeaterSupport = Nvm.MacGroup2.MacParams.RepeaterSupport; + break; + } + /* ST_WORKAROUND_END */ + case MIB_CHANNELS: + { + getPhy.Attribute = PHY_CHANNELS; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + + mibGet->Param.ChannelList = phyParam.Channels; + break; + } + case MIB_RX2_CHANNEL: + { + mibGet->Param.Rx2Channel = Nvm.MacGroup2.MacParams.Rx2Channel; + break; + } + case MIB_RX2_DEFAULT_CHANNEL: + { + mibGet->Param.Rx2Channel = Nvm.MacGroup2.MacParamsDefaults.Rx2Channel; + break; + } + case MIB_RXC_CHANNEL: + { + mibGet->Param.RxCChannel = Nvm.MacGroup2.MacParams.RxCChannel; + break; + } + case MIB_RXC_DEFAULT_CHANNEL: + { + mibGet->Param.RxCChannel = Nvm.MacGroup2.MacParamsDefaults.RxCChannel; + break; + } + case MIB_CHANNELS_DEFAULT_MASK: + { + getPhy.Attribute = PHY_CHANNELS_DEFAULT_MASK; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + + mibGet->Param.ChannelsDefaultMask = phyParam.ChannelsMask; + break; + } + case MIB_CHANNELS_MASK: + { + getPhy.Attribute = PHY_CHANNELS_MASK; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + + mibGet->Param.ChannelsMask = phyParam.ChannelsMask; + break; + } + case MIB_CHANNELS_NB_TRANS: + { + mibGet->Param.ChannelsNbTrans = Nvm.MacGroup2.MacParams.ChannelsNbTrans; + break; + } + case MIB_MAX_RX_WINDOW_DURATION: + { + mibGet->Param.MaxRxWindow = Nvm.MacGroup2.MacParams.MaxRxWindow; + break; + } + case MIB_RECEIVE_DELAY_1: + { + mibGet->Param.ReceiveDelay1 = Nvm.MacGroup2.MacParams.ReceiveDelay1; + break; + } + case MIB_RECEIVE_DELAY_2: + { + mibGet->Param.ReceiveDelay2 = Nvm.MacGroup2.MacParams.ReceiveDelay2; + break; + } + case MIB_JOIN_ACCEPT_DELAY_1: + { + mibGet->Param.JoinAcceptDelay1 = Nvm.MacGroup2.MacParams.JoinAcceptDelay1; + break; + } + case MIB_JOIN_ACCEPT_DELAY_2: + { + mibGet->Param.JoinAcceptDelay2 = Nvm.MacGroup2.MacParams.JoinAcceptDelay2; + break; + } +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + case MIB_CHANNELS_MIN_TX_DATARATE: + { + getPhy.Attribute = PHY_MIN_TX_DR; + getPhy.UplinkDwellTime = Nvm.MacGroup2.MacParams.UplinkDwellTime; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + + mibGet->Param.ChannelsMinTxDatarate = phyParam.Value; + break; + } +#endif /* LORAMAC_VERSION */ + case MIB_CHANNELS_DEFAULT_DATARATE: + { + mibGet->Param.ChannelsDefaultDatarate = Nvm.MacGroup2.ChannelsDatarateDefault; + break; + } + case MIB_CHANNELS_DATARATE: + { + mibGet->Param.ChannelsDatarate = Nvm.MacGroup1.ChannelsDatarate; + break; + } + case MIB_CHANNELS_DEFAULT_TX_POWER: + { + mibGet->Param.ChannelsDefaultTxPower = Nvm.MacGroup2.ChannelsTxPowerDefault; + break; + } + case MIB_CHANNELS_TX_POWER: + { + mibGet->Param.ChannelsTxPower = Nvm.MacGroup1.ChannelsTxPower; + break; + } + case MIB_SYSTEM_MAX_RX_ERROR: + { + mibGet->Param.SystemMaxRxError = Nvm.MacGroup2.MacParams.SystemMaxRxError; + break; + } + case MIB_MIN_RX_SYMBOLS: + { + mibGet->Param.MinRxSymbols = Nvm.MacGroup2.MacParams.MinRxSymbols; + break; + } + case MIB_ANTENNA_GAIN: + { + mibGet->Param.AntennaGain = Nvm.MacGroup2.MacParams.AntennaGain; + break; + } + case MIB_NVM_CTXS: + { + mibGet->Param.Contexts = &Nvm; + break; + } + case MIB_NVM_BKP_CTXS: + { + mibGet->Param.BackupContexts = &NvmBackup; + break; + } + case MIB_DEFAULT_ANTENNA_GAIN: + { + mibGet->Param.DefaultAntennaGain = Nvm.MacGroup2.MacParamsDefaults.AntennaGain; + break; + } + case MIB_LORAWAN_VERSION: + { + mibGet->Param.LrWanVersion.LoRaWan = Nvm.MacGroup2.Version; + mibGet->Param.LrWanVersion.LoRaWanRegion = RegionGetVersion( ); + break; + } + case MIB_RXB_C_TIMEOUT: + { + mibGet->Param.RxBCTimeout = Nvm.MacGroup2.MacParams.RxBCTimeout; + break; + } +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + case MIB_IS_CERT_FPORT_ON: + { + mibGet->Param.IsCertPortOn = Nvm.MacGroup2.IsCertPortOn; + break; + } +#endif /* LORAMAC_VERSION */ + 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 ) + { + Nvm.MacGroup2.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: + { + Nvm.MacGroup2.AdrCtrlOn = mibSet->Param.AdrEnable; + break; + } + case MIB_NET_ID: + { + Nvm.MacGroup2.NetID = mibSet->Param.NetID; + break; + } + case MIB_DEV_ADDR: + { + Nvm.MacGroup2.DevAddr = mibSet->Param.DevAddr; + 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; + } +#if ( USE_LRWAN_1_1_X_CRYPTO == 1 ) /* ST_WORKAROUND: integrate 1.1.x keys only if required */ + 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; + } +#else /* USE_LRWAN_1_1_X_CRYPTO == 0 */ + case MIB_NWK_S_KEY: + { + if( mibSet->Param.NwkSKey != NULL ) + { + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( NWK_S_KEY, mibSet->Param.NwkSKey ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } +#endif /* USE_LRWAN_1_1_X_CRYPTO */ + 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; + } +#if ( LORAMAC_MAX_MC_CTX > 0 ) /* ST_WORKAROUND: reduced LORAMAC_MAX_MC_CTX */ + 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; + } +#endif /* LORAMAC_MAX_MC_CTX > 0 */ +#if ( LORAMAC_MAX_MC_CTX > 1 ) + 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; + } +#endif /* LORAMAC_MAX_MC_CTX > 1 */ +#if ( LORAMAC_MAX_MC_CTX > 2 ) + 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; + } +#endif /* LORAMAC_MAX_MC_CTX > 2 */ +#if ( LORAMAC_MAX_MC_CTX > 3 ) + 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; + } +#endif /* LORAMAC_MAX_MC_CTX > 3 */ + case MIB_PUBLIC_NETWORK: + { + Nvm.MacGroup2.PublicNetwork = mibSet->Param.EnablePublicNetwork; + Radio.SetPublicNetwork( Nvm.MacGroup2.PublicNetwork ); + /* ST_WORAROUND_BEGIN: Required to avoid keeping the radio active after init */ + Radio.Sleep( ); + /* ST_WORKAROUND_END */ + break; + } + /* ST_WORKAROUND_BEGIN: Keep repeater feature */ + case MIB_REPEATER_SUPPORT: + { + Nvm.MacGroup2.MacParams.RepeaterSupport = mibSet->Param.EnableRepeaterSupport; + break; + } + /* ST_WORKAROUND_END */ + case MIB_RX2_CHANNEL: + { + verify.DatarateParams.Datarate = mibSet->Param.Rx2Channel.Datarate; + verify.DatarateParams.DownlinkDwellTime = Nvm.MacGroup2.MacParams.DownlinkDwellTime; + /* ST_WORAROUND_BEGIN: Check also the Rx Frequency parameter */ + if( RegionVerify( Nvm.MacGroup2.Region, &verify, PHY_RX_DR ) != true ) + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + else + { + verify.Frequency = mibSet->Param.Rx2Channel.Frequency; + if( RegionVerify( Nvm.MacGroup2.Region, &verify, PHY_FREQUENCY ) != true ) + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + else + { + Nvm.MacGroup2.MacParams.Rx2Channel = mibSet->Param.Rx2Channel; + } + } + /* ST_WORKAROUND_END */ + break; + } + case MIB_RX2_DEFAULT_CHANNEL: + { + verify.DatarateParams.Datarate = mibSet->Param.Rx2Channel.Datarate; + verify.DatarateParams.DownlinkDwellTime = Nvm.MacGroup2.MacParams.DownlinkDwellTime; + + if( RegionVerify( Nvm.MacGroup2.Region, &verify, PHY_RX_DR ) == true ) + { + Nvm.MacGroup2.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 = Nvm.MacGroup2.MacParams.DownlinkDwellTime; + + if( RegionVerify( Nvm.MacGroup2.Region, &verify, PHY_RX_DR ) == true ) + { + Nvm.MacGroup2.MacParams.RxCChannel = mibSet->Param.RxCChannel; + + if( ( Nvm.MacGroup2.DeviceClass == CLASS_C ) && ( Nvm.MacGroup2.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( ); + + OpenContinuousRxCWindow( ); + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_RXC_DEFAULT_CHANNEL: + { + verify.DatarateParams.Datarate = mibSet->Param.RxCChannel.Datarate; + verify.DatarateParams.DownlinkDwellTime = Nvm.MacGroup2.MacParams.DownlinkDwellTime; + + if( RegionVerify( Nvm.MacGroup2.Region, &verify, PHY_RX_DR ) == true ) + { + Nvm.MacGroup2.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( Nvm.MacGroup2.Region, &chanMaskSet ) == false ) + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_CHANNELS_MASK: + { + chanMaskSet.ChannelsMaskIn = mibSet->Param.ChannelsMask; + chanMaskSet.ChannelsMaskType = CHANNELS_MASK; + + if( RegionChanMaskSet( Nvm.MacGroup2.Region, &chanMaskSet ) == false ) + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_CHANNELS_NB_TRANS: + { + if( ( mibSet->Param.ChannelsNbTrans >= 1 ) && + ( mibSet->Param.ChannelsNbTrans <= 15 ) ) + { + Nvm.MacGroup2.MacParams.ChannelsNbTrans = mibSet->Param.ChannelsNbTrans; + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_MAX_RX_WINDOW_DURATION: + { + Nvm.MacGroup2.MacParams.MaxRxWindow = mibSet->Param.MaxRxWindow; + break; + } + case MIB_RECEIVE_DELAY_1: + { + Nvm.MacGroup2.MacParams.ReceiveDelay1 = mibSet->Param.ReceiveDelay1; + break; + } + case MIB_RECEIVE_DELAY_2: + { + Nvm.MacGroup2.MacParams.ReceiveDelay2 = mibSet->Param.ReceiveDelay2; + break; + } + case MIB_JOIN_ACCEPT_DELAY_1: + { + Nvm.MacGroup2.MacParams.JoinAcceptDelay1 = mibSet->Param.JoinAcceptDelay1; + break; + } + case MIB_JOIN_ACCEPT_DELAY_2: + { + Nvm.MacGroup2.MacParams.JoinAcceptDelay2 = mibSet->Param.JoinAcceptDelay2; + break; + } + case MIB_CHANNELS_DEFAULT_DATARATE: + { + verify.DatarateParams.Datarate = mibSet->Param.ChannelsDefaultDatarate; + + if( RegionVerify( Nvm.MacGroup2.Region, &verify, PHY_DEF_TX_DR ) == true ) + { + Nvm.MacGroup2.ChannelsDatarateDefault = verify.DatarateParams.Datarate; + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_CHANNELS_DATARATE: + { + verify.DatarateParams.Datarate = mibSet->Param.ChannelsDatarate; + verify.DatarateParams.UplinkDwellTime = Nvm.MacGroup2.MacParams.UplinkDwellTime; + + if( RegionVerify( Nvm.MacGroup2.Region, &verify, PHY_TX_DR ) == true ) + { + Nvm.MacGroup1.ChannelsDatarate = verify.DatarateParams.Datarate; + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_CHANNELS_DEFAULT_TX_POWER: + { + verify.TxPower = mibSet->Param.ChannelsDefaultTxPower; + + if( RegionVerify( Nvm.MacGroup2.Region, &verify, PHY_DEF_TX_POWER ) == true ) + { + Nvm.MacGroup2.ChannelsTxPowerDefault = verify.TxPower; + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_CHANNELS_TX_POWER: + { + verify.TxPower = mibSet->Param.ChannelsTxPower; + + if( RegionVerify( Nvm.MacGroup2.Region, &verify, PHY_TX_POWER ) == true ) + { + Nvm.MacGroup1.ChannelsTxPower = verify.TxPower; + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_SYSTEM_MAX_RX_ERROR: + { + Nvm.MacGroup2.MacParams.SystemMaxRxError = Nvm.MacGroup2.MacParamsDefaults.SystemMaxRxError = mibSet->Param.SystemMaxRxError; + break; + } + case MIB_MIN_RX_SYMBOLS: + { + Nvm.MacGroup2.MacParams.MinRxSymbols = Nvm.MacGroup2.MacParamsDefaults.MinRxSymbols = mibSet->Param.MinRxSymbols; + break; + } + case MIB_ANTENNA_GAIN: + { + Nvm.MacGroup2.MacParams.AntennaGain = mibSet->Param.AntennaGain; + break; + } + case MIB_DEFAULT_ANTENNA_GAIN: + { + Nvm.MacGroup2.MacParamsDefaults.AntennaGain = mibSet->Param.DefaultAntennaGain; + break; + } + case MIB_NVM_CTXS: + { + status = RestoreNvmData( ); + break; + } + case MIB_ABP_LORAWAN_VERSION: + { + if( mibSet->Param.AbpLrWanVersion.Fields.Minor <= 1 ) + { + Nvm.MacGroup2.Version = mibSet->Param.AbpLrWanVersion; + + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetLrWanVersion( mibSet->Param.AbpLrWanVersion ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_RXB_C_TIMEOUT: + { + Nvm.MacGroup2.MacParams.RxBCTimeout = mibSet->Param.RxBCTimeout; + break; + } +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + case MIB_IS_CERT_FPORT_ON: + { + Nvm.MacGroup2.IsCertPortOn = mibSet->Param.IsCertPortOn; + break; + } +#endif /* LORAMAC_VERSION */ + default: + { + status = LoRaMacMibClassBSetRequestConfirm( mibSet ); + break; + } + } + +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + if( status == LORAMAC_STATUS_OK ) + { + // Handle NVM potential changes + MacCtx.MacFlags.Bits.NvmHandle = 1; + } +#endif /* LORAMAC_VERSION */ + 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; + return RegionChannelAdd( Nvm.MacGroup2.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( Nvm.MacGroup2.Region, &channelRemove ) == false ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + 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; + } + + Nvm.MacGroup2.MulticastChannelList[channel->GroupID].ChannelParams = *channel; + + if( channel->IsRemotelySetup == true ) + { + if( LoRaMacCryptoSetKey( MCKeys[channel->GroupID], channel->McKeys.McKeyE ) != LORAMAC_CRYPTO_SUCCESS ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + + if( LoRaMacCryptoDeriveMcSessionKeyPair( channel->GroupID, channel->Address ) != LORAMAC_CRYPTO_SUCCESS ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MCAppSKeys[channel->GroupID], channel->McKeys.Session.McAppSKey ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MCNwkSKeys[channel->GroupID], channel->McKeys.Session.McNwkSKey ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + + if( channel->Class == CLASS_B ) + { + // Calculate class b parameters + LoRaMacClassBSetMulticastPeriodicity( &Nvm.MacGroup2.MulticastChannelList[channel->GroupID] ); + } + + // Reset multicast channel downlink counter to initial value. + *Nvm.MacGroup2.MulticastChannelList[channel->GroupID].DownLinkCounter = FCNT_DOWN_INITAL_VALUE; + 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 ) || + ( Nvm.MacGroup2.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 ) ); + + Nvm.MacGroup2.MulticastChannelList[groupID].ChannelParams = channel; + return LORAMAC_STATUS_OK; +} + +uint8_t LoRaMacMcChannelGetGroupId( uint32_t mcAddress ) +{ + for( uint8_t i = 0; i < LORAMAC_MAX_MC_CTX; i++ ) + { + if( mcAddress == Nvm.MacGroup2.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 = Nvm.MacGroup2.MulticastChannelList[groupID].ChannelParams.Class; + if( ( devClass == CLASS_A ) || ( devClass > CLASS_C ) ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + if( ( groupID >= LORAMAC_MAX_MC_CTX ) || + ( Nvm.MacGroup2.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 = Nvm.MacGroup2.MacParams.DownlinkDwellTime; + + if( RegionVerify( Nvm.MacGroup2.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( Nvm.MacGroup2.Region, &verify, PHY_FREQUENCY ) == true ) + { + *status &= 0xF7; // frequency OK + } + + if( *status == ( groupID & 0x03 ) ) + { + // Apply parameters + Nvm.MacGroup2.MulticastChannelList[groupID].ChannelParams.RxParams = *rxParams; + } + 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 ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + // Initialize mlmeRequest->ReqReturn.DutyCycleWaitTime to 0 in order to + // return a valid value in case the MAC is busy. + mlmeRequest->ReqReturn.DutyCycleWaitTime = 0; +#endif /* LORAMAC_VERSION */ + + 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; +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + queueElement.ReadyToHandle = false; +#endif /* LORAMAC_VERSION */ + + switch( mlmeRequest->Type ) + { + case MLME_JOIN: + { + if( ( MacCtx.MacState & LORAMAC_TX_DELAYED ) == LORAMAC_TX_DELAYED ) + { + return LORAMAC_STATUS_BUSY; + } + +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) + ResetMacParameters( ); + + Nvm.MacGroup1.ChannelsDatarate = RegionAlternateDr( Nvm.MacGroup2.Region, mlmeRequest->Req.Join.Datarate, ALTERNATE_DR ); + + queueElement.Status = LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL; + + status = SendReJoinReq( JOIN_REQ ); + + if( status != LORAMAC_STATUS_OK ) + { + // Revert back the previous datarate ( mainly used for US915 like regions ) + Nvm.MacGroup1.ChannelsDatarate = RegionAlternateDr( Nvm.MacGroup2.Region, mlmeRequest->Req.Join.Datarate, ALTERNATE_DR_RESTORE ); + } +#elif ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + if( mlmeRequest->Req.Join.NetworkActivation == ACTIVATION_TYPE_OTAA ) + { + ResetMacParameters( ); + + Nvm.MacGroup1.ChannelsDatarate = RegionAlternateDr( Nvm.MacGroup2.Region, mlmeRequest->Req.Join.Datarate, ALTERNATE_DR ); + + queueElement.Status = LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL; + + status = SendReJoinReq( JOIN_REQ ); + + if( status != LORAMAC_STATUS_OK ) + { + // Revert back the previous datarate ( mainly used for US915 like regions ) + Nvm.MacGroup1.ChannelsDatarate = RegionAlternateDr( Nvm.MacGroup2.Region, mlmeRequest->Req.Join.Datarate, ALTERNATE_DR_RESTORE ); + } + } + else if( mlmeRequest->Req.Join.NetworkActivation == ACTIVATION_TYPE_ABP ) + { + // Restore default value for ChannelsDatarateChangedLinkAdrReq + Nvm.MacGroup2.ChannelsDatarateChangedLinkAdrReq = false; + + // Activate the default channels + InitDefaultsParams_t params; + params.Type = INIT_TYPE_ACTIVATE_DEFAULT_CHANNELS; + RegionInitDefaults( Nvm.MacGroup2.Region, ¶ms ); + + Nvm.MacGroup2.NetworkActivation = mlmeRequest->Req.Join.NetworkActivation; + queueElement.Status = LORAMAC_EVENT_INFO_STATUS_OK; + queueElement.ReadyToHandle = true; + MacCtx.MacCallbacks->MacProcessNotify( ); + MacCtx.MacFlags.Bits.MacDone = 1; + status = LORAMAC_STATUS_OK; + } +#endif /* LORAMAC_VERSION */ + 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; + } +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) + 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; + } +#elif ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + case MLME_TXCW: + { + status = SetTxContinuousWave( mlmeRequest->Req.TxCw.Timeout, mlmeRequest->Req.TxCw.Frequency, mlmeRequest->Req.TxCw.Power ); + break; + } +#endif /* LORAMAC_VERSION */ + 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( Nvm.MacGroup2.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; + } + + // Fill return structure + mlmeRequest->ReqReturn.DutyCycleWaitTime = MacCtx.DutyCycleWaitTime; + + if( status != LORAMAC_STATUS_OK ) + { + if( LoRaMacConfirmQueueGetCnt( ) == 0 ) + { + MacCtx.NodeAckRequested = false; + MacCtx.MacFlags.Bits.MlmeReq = 0; + } + } + else + { + LoRaMacConfirmQueueAdd( &queueElement ); + } + return status; +} + +/* ST_WORKAROUND_BEGIN: Update MCPS request with new input parameter to allow delayed tx */ +LoRaMacStatus_t LoRaMacMcpsRequest( McpsReq_t* mcpsRequest, bool allowDelayedTx ) +{ + GetPhyParams_t getPhy; + PhyParam_t phyParam; + LoRaMacStatus_t status = LORAMAC_STATUS_SERVICE_UNKNOWN; + LoRaMacHeader_t macHdr; + VerifyParams_t verify; + uint8_t fPort = 0; + /* ST_WORKAROUND_BEGIN: remove GCC9 warning */ + void* fBuffer = NULL; + /* ST_WORKAROUND_END */ + uint16_t fBufferSize; + int8_t datarate = DR_0; + bool readyToSend = false; + + if( mcpsRequest == NULL ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) + 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 = Nvm.MacGroup2.MacParams.UplinkDwellTime; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.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( Nvm.MacGroup2.AdrCtrlOn == false ) + { + verify.DatarateParams.Datarate = datarate; + verify.DatarateParams.UplinkDwellTime = Nvm.MacGroup2.MacParams.UplinkDwellTime; + + if( RegionVerify( Nvm.MacGroup2.Region, &verify, PHY_TX_DR ) == true ) + { + Nvm.MacGroup1.ChannelsDatarate = verify.DatarateParams.Datarate; + } + else + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + } + + status = Send( &macHdr, fPort, fBuffer, fBufferSize, allowDelayedTx ); /* ST_WORKAROUND: Update Send request with new input parameter to allow delayed tx */ + if( status == LORAMAC_STATUS_OK ) + { + MacCtx.McpsConfirm.McpsRequest = mcpsRequest->Type; + MacCtx.MacFlags.Bits.McpsReq = 1; + } + else + { + MacCtx.NodeAckRequested = false; + } + } +#elif ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + // Initialize mcpsRequest->ReqReturn.DutyCycleWaitTime to 0 in order to + // return a valid value in case the MAC is busy. + mcpsRequest->ReqReturn.DutyCycleWaitTime = 0; + + if( LoRaMacIsBusy( ) == true ) + { + return LORAMAC_STATUS_BUSY; + } + + McpsReq_t request = *mcpsRequest; + + macHdr.Value = 0; + memset1( ( uint8_t* ) &MacCtx.McpsConfirm, 0, sizeof( MacCtx.McpsConfirm ) ); + MacCtx.McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; + + // Apply confirmed downlinks, if the device has not received a valid + // downlink after a join accept. + if( ( Nvm.MacGroup2.NetworkActivation == ACTIVATION_TYPE_OTAA ) && + ( Nvm.MacGroup2.DeviceClass == CLASS_C ) && + ( Nvm.MacGroup2.DownlinkReceived == false ) && + ( request.Type == MCPS_UNCONFIRMED ) ) + { + request.Type = MCPS_CONFIRMED; + } + + switch( request.Type ) + { + case MCPS_UNCONFIRMED: + { + readyToSend = true; + + macHdr.Bits.MType = FRAME_TYPE_DATA_UNCONFIRMED_UP; + fPort = request.Req.Unconfirmed.fPort; + fBuffer = request.Req.Unconfirmed.fBuffer; + fBufferSize = request.Req.Unconfirmed.fBufferSize; + datarate = request.Req.Unconfirmed.Datarate; + break; + } + case MCPS_CONFIRMED: + { + readyToSend = true; + + macHdr.Bits.MType = FRAME_TYPE_DATA_CONFIRMED_UP; + fPort = request.Req.Confirmed.fPort; + fBuffer = request.Req.Confirmed.fBuffer; + fBufferSize = request.Req.Confirmed.fBufferSize; + datarate = request.Req.Confirmed.Datarate; + break; + } + case MCPS_PROPRIETARY: + { + readyToSend = true; + + macHdr.Bits.MType = FRAME_TYPE_PROPRIETARY; + fBuffer = request.Req.Proprietary.fBuffer; + fBufferSize = request.Req.Proprietary.fBufferSize; + datarate = request.Req.Proprietary.Datarate; + break; + } + default: + break; + } + + // Make sure that the input datarate is compliant + // to the regional specification. + getPhy.Attribute = PHY_MIN_TX_DR; + getPhy.UplinkDwellTime = Nvm.MacGroup2.MacParams.UplinkDwellTime; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + // Apply the minimum possible datarate. + // Some regions have limitations for the minimum datarate. + datarate = MAX( datarate, ( int8_t )phyParam.Value ); + + // Apply minimum datarate in this special case. + if( CheckForMinimumAbpDatarate( Nvm.MacGroup2.AdrCtrlOn, Nvm.MacGroup2.NetworkActivation, + Nvm.MacGroup2.ChannelsDatarateChangedLinkAdrReq ) == true ) + { + datarate = ( int8_t )phyParam.Value; + } + + if( readyToSend == true ) + { + if( ( Nvm.MacGroup2.AdrCtrlOn == false ) || + ( CheckForMinimumAbpDatarate( Nvm.MacGroup2.AdrCtrlOn, Nvm.MacGroup2.NetworkActivation, + Nvm.MacGroup2.ChannelsDatarateChangedLinkAdrReq ) == true ) ) + { + verify.DatarateParams.Datarate = datarate; + verify.DatarateParams.UplinkDwellTime = Nvm.MacGroup2.MacParams.UplinkDwellTime; + + if( RegionVerify( Nvm.MacGroup2.Region, &verify, PHY_TX_DR ) == true ) + { + Nvm.MacGroup1.ChannelsDatarate = verify.DatarateParams.Datarate; + } + else + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + } + + // Verification of response timeout for class b and class c + LoRaMacHandleResponseTimeout( Nvm.MacGroup2.MacParams.RxBCTimeout, + MacCtx.ResponseTimeoutStartTime ); + + status = Send( &macHdr, fPort, fBuffer, fBufferSize, allowDelayedTx ); /* ST_WORKAROUND: Update Send request with new input parameter to allow delayed tx */ + if( status == LORAMAC_STATUS_OK ) + { + MacCtx.McpsConfirm.McpsRequest = request.Type; + MacCtx.MacFlags.Bits.McpsReq = 1; + } + else + { + MacCtx.NodeAckRequested = false; + } + } +#endif /* LORAMAC_VERSION */ + + // Fill return structure + mcpsRequest->ReqReturn.DutyCycleWaitTime = MacCtx.DutyCycleWaitTime; + + return status; +} +/* ST_WORKAROUND_END */ + +void LoRaMacTestSetDutyCycleOn( bool enable ) +{ + VerifyParams_t verify; + + verify.DutyCycle = enable; + + if( RegionVerify( Nvm.MacGroup2.Region, &verify, PHY_DUTY_CYCLE ) == true ) + { + Nvm.MacGroup2.DutyCycleOn = enable; + } +} + +LoRaMacStatus_t LoRaMacDeInitialization( void ) +{ + // Check the current state of the LoRaMac + if ( LoRaMacStop( ) == LORAMAC_STATUS_OK ) + { + // Stop Timers + TimerStop( &MacCtx.TxDelayedTimer ); + TimerStop( &MacCtx.RxWindowTimer1 ); + TimerStop( &MacCtx.RxWindowTimer2 ); +#if ( defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) + TimerStop( &MacCtx.AckTimeoutTimer ); +#endif /* LORAMAC_VERSION */ + + // Take care about class B + LoRaMacClassBHaltBeaconing( ); + + // Reset Mac parameters + ResetMacParameters( ); + + // Switch off Radio + Radio.Sleep( ); + + // Return success + return LORAMAC_STATUS_OK; + } + else + { + return LORAMAC_STATUS_BUSY; + } +} diff --git a/src/STM32CubeWL/LoRaWAN/Mac/LoRaMac.h b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMac.h new file mode 100644 index 0000000..7092494 --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMac.h @@ -0,0 +1,483 @@ +/*! + * \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 periodic-uplink-lpp/B-L072Z-LRWAN1/main.c + * LoRaWAN class A/B/C application example for the B-L072Z-LRWAN1. + * + * \example periodic-uplink-lpp/NAMote72/main.c + * LoRaWAN class A/B/C application example for the NAMote72. + * + * \example periodic-uplink-lpp/NucleoL073/main.c + * LoRaWAN class A/B/C application example for the NucleoL073. + * + * \example periodic-uplink-lpp/NucleoL152/main.c + * LoRaWAN class A/B/C application example for the NucleoL152. + * + * \example periodic-uplink-lpp/NucleoL476/main.c + * LoRaWAN class A/B/C application example for the NucleoL476. + * + * \example periodic-uplink-lpp/SAMR34/main.c + * LoRaWAN class A/B/C application example for the SAMR34. + * + * \example periodic-uplink-lpp/SKiM880B/main.c + * LoRaWAN class A/B/C application example for the SKiM880B. + * + * \example periodic-uplink-lpp/SKiM881AXL/main.c + * LoRaWAN class A/B/C application example for the SKiM881AXL. + * + * \example periodic-uplink-lpp/SKiM980A/main.c + * LoRaWAN class A/B/C application example for the SKiM980A. + */ +/** + ****************************************************************************** + * + * Portions COPYRIGHT 2020 STMicroelectronics + * + * @file LoRaMac.h + * @author MCD Application Team + * @brief Header for LoRa MAC Layer + ****************************************************************************** + */ +#ifndef __LORAMAC_H__ +#define __LORAMAC_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "LoRaMacInterfaces.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 + +/*! + * LoRaMac MLME-Confirm queue length + */ +#define LORA_MAC_MLME_CONFIRM_QUEUE_LEN 5 + +/*! + * Start value for multicast keys enumeration + */ +#define LORAMAC_CRYPTO_MULTICAST_KEYS 127 + +/*! + * Maximum MAC commands buffer size + */ +#define LORA_MAC_COMMAND_MAX_LENGTH 128 + +/*! + * Bitmap value + */ +#define LORAMAC_NVM_NOTIFY_FLAG_NONE 0x00 + +/*! + * Bitmap value for the NVM group crypto. + */ +#define LORAMAC_NVM_NOTIFY_FLAG_CRYPTO 0x01 + +/*! + * Bitmap value for the NVM group MAC 1. + */ +#define LORAMAC_NVM_NOTIFY_FLAG_MAC_GROUP1 0x02 + +/*! + * Bitmap value for the NVM group MAC 2. + */ +#define LORAMAC_NVM_NOTIFY_FLAG_MAC_GROUP2 0x04 + +/*! + * Bitmap value for the NVM group secure element. + */ +#define LORAMAC_NVM_NOTIFY_FLAG_SECURE_ELEMENT 0x08 + +/*! + * Bitmap value for the NVM group 1 region. + */ +#define LORAMAC_NVM_NOTIFY_FLAG_REGION_GROUP1 0x10 + +/*! + * Bitmap value for the NVM group 2 region. + */ +#define LORAMAC_NVM_NOTIFY_FLAG_REGION_GROUP2 0x20 + +/*! + * Bitmap value for the NVM group class b. + */ +#define LORAMAC_NVM_NOTIFY_FLAG_CLASS_B 0x40 + +/*! + * LoRaWAN compliance certification protocol port number. + * + * LoRaWAN Specification V1.x.x, chapter 4.3.2 + */ +#define LORAMAC_CERT_FPORT 224 + +/*! + * \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 ); + +LoRaMacStatus_t LoRaMacHalt( 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 ); + +/*! + * \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] mibGet - 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] mibSet - 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 ); + +/* ST_WORKAROUND_BEGIN: Update Mcps request with new input parameter to allow delayed tx */ +/*! + * \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. + * \param [in] allowDelayedTx - When set to true, the frame will be delayed + * + * \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, bool allowDelayedTx ); +/* ST_WORKAROUND_END */ + +/*! + * \brief LoRaMAC deinitialization + * + * \details This function stops the timers, re-initializes MAC & regional parameters to default + * and sets radio into sleep state. + * + * \retval LoRaMacStatus_t Status of the operation. Possible returns are: + * \ref LORAMAC_STATUS_OK, + * \ref LORAMAC_STATUS_BUSY + */ +LoRaMacStatus_t LoRaMacDeInitialization( void ); + +/*! \} defgroup LORAMAC */ + +#ifdef __cplusplus +} +#endif + +#endif // __LORAMAC_H__ diff --git a/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacAdr.c b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacAdr.c new file mode 100644 index 0000000..d739c10 --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacAdr.c @@ -0,0 +1,207 @@ +/*! + * \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 "Region/Region.h" +#include "LoRaMacAdr.h" +#include "LoRaMacVersion.h" + +#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) +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_ACTIVATE_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; +} +#elif (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) +bool LoRaMacAdrCalcNext( CalcNextAdrParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, + uint8_t* nbTransOut, uint32_t* adrAckCounter ) +{ + bool adrAckReq = false; + int8_t datarate = adrNext->Datarate; + int8_t txPower = adrNext->TxPower; + uint8_t nbTrans = adrNext->NbTrans; + 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 ); + + // Verify if ADR ack req bit needs to be set. + if( adrNext->AdrAckCounter >= adrNext->AdrAckLimit ) + { + adrAckReq = true; + } + + // Verify, if we need to set the TX power to default + if( adrNext->AdrAckCounter >= ( adrNext->AdrAckLimit + adrNext->AdrAckDelay ) ) + { + // Set TX Power to default + getPhy.Attribute = PHY_DEF_TX_POWER; + phyParam = RegionGetPhyParam( adrNext->Region, &getPhy ); + txPower = phyParam.Value; + } + + // Verify, if we need to decrease the data rate + if( adrNext->AdrAckCounter >= ( uint32_t )( adrNext->AdrAckLimit + ( adrNext->AdrAckDelay << 1 ) ) ) + { + // Perform actions with every adrNext->AdrAckDelay only + if( ( ( adrNext->AdrAckCounter - adrNext->AdrAckLimit ) % adrNext->AdrAckDelay ) == 0 ) + { + if( datarate == minTxDatarate ) + { + // Restore the channel mask + if( adrNext->UpdateChanMask == true ) + { + InitDefaultsParams_t params; + params.Type = INIT_TYPE_ACTIVATE_DEFAULT_CHANNELS; + RegionInitDefaults( adrNext->Region, ¶ms ); + } + + // Restore NbTrans + nbTrans = 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; + } + } + } + + *drOut = datarate; + *txPowOut = txPower; + *nbTransOut = nbTrans; + return adrAckReq; +} +#endif /* LORAMAC_VERSION */ diff --git a/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacAdr.h b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacAdr.h new file mode 100644 index 0000000..55666e1 --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacAdr.h @@ -0,0 +1,151 @@ +/*! + * \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__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*! \} defgroup LORAMACADR */ + +/* + * Parameter structure for the function CalcNextAdr. + */ +typedef struct sCalcNextAdrParams +{ +#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) + /*! + * LoRaWAN Minor Version 1.X + */ + Version_t Version; +#endif /* LORAMAC_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; +#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + /*! + * NbTrans counter used currently. + */ + uint8_t NbTrans; +#endif /* LORAMAC_VERSION */ + /*! + * UplinkDwellTime + */ + uint8_t UplinkDwellTime; + /*! + * Region + */ + LoRaMacRegion_t Region; +}CalcNextAdrParams_t; + +#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) +/*! + * \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 ); +#elif (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) +/*! + * \brief Calculates the next datarate to set, when ADR is on or off. + * + * \details Here is a summary of the actions: + * + * | ADR_ACK_CNT | Action | + * | ----------- | --------------------------------------------------------- | + * | 0... 63 | Do nothing | + * | 64...95 | Set ADR ack bit | + * | 96...127 | Set TX power to default (if already default, do nothing) | + * | 128...159 | Set data rate to default (if already default, do nothing) | + * | >=160 | Set NbTrans to 1, re-enable default channels | + * + * \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] nbTransOut The NbTrans counter. + * + * \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, + uint8_t* nbTransOut, uint32_t* adrAckCounter ); +#endif /* LORAMAC_VERSION */ + +#ifdef __cplusplus +} +#endif + +#endif // __LORAMACADR_H__ diff --git a/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacClassB.c b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacClassB.c new file mode 100644 index 0000000..d728940 --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacClassB.c @@ -0,0 +1,2083 @@ +/*! + * \file LoRaMacClassB.c + * + * \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 ) + */ +/** + ****************************************************************************** + * + * Portions COPYRIGHT 2020 STMicroelectronics + * + * @file LoRaMacClassB.c + * @author MCD Application Team + * @brief LoRa MAC Class B layer implementation + ****************************************************************************** + */ +#include +#include "../Utilities/utilities.h" +#include "secure-element.h" +#include "LoRaMac.h" +#include "LoRaMacVersion.h" +#include "LoRaMacClassB.h" +#include "LoRaMacClassBNvm.h" +#include "LoRaMacClassBConfig.h" +#include "LoRaMacConfirmQueue.h" +#include "../../SubGHz_Phy/radio.h" +#include "Region/Region.h" +#include "../../../BSP/mw_log_conf.h" + +#if ( LORAMAC_CLASSB_ENABLED == 1 ) + +/* + * 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; +} LoRaMacClassBCtx_t; + +/*! + * 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 }; + +/* + * Module context. + */ +static LoRaMacClassBCtx_t Ctx; + +/* + * Beacon transmit time precision in milliseconds. + * The usage of these values shall be determined by the + * prec value in param field received in a beacon frame. + * As the time base is milli seconds, the precision will be either 0 ms or 1 ms. + */ +static const uint8_t BeaconPrecTimeValue[4] = { 0, 1, 1, 1 }; + +/*! + * Data structure which holds the parameters which needs to be stored + * in the NVM. + */ +static LoRaMacClassBNvmData_t* ClassBNvm; + +// The CRC calculation follows CRC16-CCITT +static const uint16_t polynom = 0x1021; + +/* ST_WORKAROUND_BEGIN: Move timer function (ClassB specific) */ +/*! + * \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, int16_t temperature ); + +static TimerTime_t TimerTempCompensation( TimerTime_t period, int16_t 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.0f; + float ppm = 0.0f; + + if (k < 0.0f) + { + ppm = (k - kDev); + } + else + { + ppm = (k + kDev); + } + interim = (float)temperature - (t - tDev); + ppm *= interim * interim; + + // Calculate the drift in time + interim = ((float) period * ppm) / 1000000.0f; + // Calculate the resulting time period + interim += period; + interim = floor(interim); + + if (interim < 0.0f) + { + interim = (float)period; + } + + // Calculate the resulting period + return ( TimerTime_t ) interim; +} +/* ST_WORKAROUND_END */ + +/*! + * 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. + * + * \param [in] isBeacon Set to true, if the function shall + * calculate the frequency for a beacon. + * + * \retval The downlink frequency + */ +static uint32_t CalcDownlinkFrequency( uint8_t channel, bool isBeacon ) +{ + GetPhyParams_t getPhy; + PhyParam_t phyParam; + + getPhy.Attribute = PHY_PING_SLOT_CHANNEL_FREQ; + + if( isBeacon == true ) + { + getPhy.Attribute = PHY_BEACON_CHANNEL_FREQ; + } + getPhy.Channel = channel; + phyParam = RegionGetPhyParam( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &getPhy ); + + return phyParam.Value; +} + +/*! + * \brief Calculates the downlink channel for the beacon and for + * ping slot downlinks. + * + * \param [in] devAddr The address of the device. Assign 0 if its a beacon. + * + * \param [in] beaconTime The beacon time of the beacon. + * + * \param [in] beaconInterval The beacon interval. + * + * \param [in] isBeacon Set to true, if the function shall + * calculate the frequency for a beacon. + * + * \retval The downlink channel + */ +static uint32_t CalcDownlinkChannelAndFrequency( uint32_t devAddr, TimerTime_t beaconTime, + TimerTime_t beaconInterval, bool isBeacon ) +{ + GetPhyParams_t getPhy; + PhyParam_t phyParam; + uint32_t channel = 0; + uint8_t nbChannels = 0; + uint8_t offset = 0; + + // Default initialization - ping slot channels + getPhy.Attribute = PHY_PING_SLOT_NB_CHANNELS; + + if( isBeacon == true ) + { + // Beacon channels + getPhy.Attribute = PHY_BEACON_NB_CHANNELS; + } + phyParam = RegionGetPhyParam( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &getPhy ); + nbChannels = ( uint8_t ) phyParam.Value; + + // nbChannels is > 1, when the channel plan requires more than one possible channel + // defined by the calculation below. + if( nbChannels > 1 ) + { + getPhy.Attribute = PHY_BEACON_CHANNEL_OFFSET; + phyParam = RegionGetPhyParam( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &getPhy ); + offset = ( uint8_t ) phyParam.Value; + + // Calculate the channel for the next downlink + channel = devAddr + ( beaconTime / ( beaconInterval / 1000 ) ); + channel = channel % nbChannels; + channel += offset; + } + + // Calculate the frequency for the next downlink. This holds + // for beacons and ping slots. + return CalcDownlinkFrequency( channel, isBeacon ); +} + +/*! + * \brief Calculates the correct frequency and opens up the beacon reception window. Please + * note that the variable WindowTimeout and WindowOffset will be updated according + * to the current settings. Also, the function perform a calculation only, when + * Ctx.BeaconCtx.Ctrl.BeaconAcquired OR Ctx.BeaconCtx.Ctrl.AcquisitionPending is + * set to 1. + * + * \param [in] rxConfig Reception parameters for the beacon window. + * + * \param [in] currentSymbolTimeout Current symbol timeout. + */ +static void CalculateBeaconRxWindowConfig( RxConfigParams_t* rxConfig, uint16_t currentSymbolTimeout ) +{ + GetPhyParams_t getPhy; + PhyParam_t phyParam; +#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + uint32_t maxRxError = 0; +#endif /* LORAMAC_VERSION */ + + rxConfig->WindowTimeout = currentSymbolTimeout; + rxConfig->WindowOffset = 0; + + 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 ); + +#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) + // Calculate downlink symbols + RegionComputeRxWindowParameters( *Ctx.LoRaMacClassBParams.LoRaMacRegion, + ( int8_t )phyParam.Value, // datarate + Ctx.LoRaMacClassBParams.LoRaMacParams->MinRxSymbols, + Ctx.LoRaMacClassBParams.LoRaMacParams->SystemMaxRxError, + rxConfig ); + } +#elif (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + // Compare and assign the maximum between the region specific rx error window time + // and time precision received from beacon frame format. + maxRxError = MAX( Ctx.LoRaMacClassBParams.LoRaMacParams->SystemMaxRxError, + ( uint32_t ) Ctx.BeaconCtx.BeaconTimePrecision.SubSeconds ); + + // Calculate downlink symbols + RegionComputeRxWindowParameters( *Ctx.LoRaMacClassBParams.LoRaMacRegion, + ( int8_t )phyParam.Value, // datarate + Ctx.LoRaMacClassBParams.LoRaMacParams->MinRxSymbols, + maxRxError, + rxConfig ); + } +#endif /* LORAMAC_VERSION */ +} + +/*! + * \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 + * + * \param [in] symbolTimeout Symbol timeout + */ +static void RxBeaconSetup( TimerTime_t rxTime, bool activateDefaultChannel, uint16_t symbolTimeout ) +{ + RxBeaconSetup_t rxBeaconSetup; + uint32_t frequency = 0; + + 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, true ); + } + else + { + // This is the frequency according to the channel plan + frequency = CalcDownlinkChannelAndFrequency( 0, Ctx.BeaconCtx.BeaconTime.Seconds + ( CLASSB_BEACON_INTERVAL / 1000 ), + CLASSB_BEACON_INTERVAL, true ); + } + + if( ClassBNvm->BeaconCtx.Ctrl.CustomFreq == 1 ) + { + // Set the frequency from the BeaconFreqReq + frequency = ClassBNvm->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, true ); + } + + rxBeaconSetup.SymbolTimeout = symbolTimeout; + 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 ) +{ + // 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 GetTemperature( LoRaMacClassBCallback_t *callbacks, BeaconContext_t *beaconCtx ) +{ + // Measure temperature, if available + if( ( callbacks != NULL ) && ( callbacks->GetTemperatureLevel != NULL ) ) + { + beaconCtx->Temperature = callbacks->GetTemperatureLevel( ); + } +} + +static void InitClassB( void ) +{ + GetPhyParams_t getPhy; + PhyParam_t phyParam; + + // Init events + LoRaMacClassBEvents.Value = 0; + + // Init variables to default + memset1( ( uint8_t* ) ClassBNvm, 0, sizeof( LoRaMacClassBNvmData_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; + GetTemperature( &Ctx.LoRaMacClassBCallbacks, &Ctx.BeaconCtx ); + + // Setup default ping slot datarate + getPhy.Attribute = PHY_PING_SLOT_CHANNEL_DR; + phyParam = RegionGetPhyParam( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &getPhy ); + ClassBNvm->PingSlotCtx.Datarate = ( int8_t )( phyParam.Value ); + +#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + // Setup default FPending bit + ClassBNvm->PingSlotCtx.FPendingSet = 0; +#endif /* LORAMAC_VERSION */ + + // Setup default states + Ctx.BeaconState = BEACON_STATE_ACQUISITION; + Ctx.PingSlotState = PINGSLOT_STATE_CALC_PING_OFFSET; + Ctx.MulticastSlotState = PINGSLOT_STATE_CALC_PING_OFFSET; +} + +static void InitClassBDefaults( void ) +{ + // This function shall reset the Class B settings to default, + // but should keep important configurations + LoRaMacClassBBeaconNvmData_t beaconCtx = ClassBNvm->BeaconCtx; + LoRaMacClassBPingSlotNvmData_t pingSlotCtx = ClassBNvm->PingSlotCtx; + + InitClassB( ); + + // Parameters from BeaconFreqReq + ClassBNvm->BeaconCtx.Frequency = beaconCtx.Frequency; + ClassBNvm->BeaconCtx.Ctrl.CustomFreq = beaconCtx.Ctrl.CustomFreq; + + // Parameters from PingSlotChannelReq + ClassBNvm->PingSlotCtx.Ctrl.CustomFreq = pingSlotCtx.Ctrl.CustomFreq; + ClassBNvm->PingSlotCtx.Frequency = pingSlotCtx.Frequency; + ClassBNvm->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; +} + +#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) +static bool CheckSlotPriority( uint32_t currentAddress, uint8_t currentFPendingSet, uint8_t currentIsMulticast, + uint32_t address, uint8_t fPendingSet, uint8_t isMulticast ) +{ + if( currentFPendingSet != fPendingSet ) + { + if( currentFPendingSet < fPendingSet ) + { + // New slot sequence has priority. It does not matter + // which type it is + return true; + } + return false; + } + else + { + // FPendingSet has the same priority level, decide + // based on multicast or unicast setting + if( currentIsMulticast != isMulticast ) + { + if( currentIsMulticast < isMulticast ) + { + // New slot sequence has priority. Multicasts have + // more priority than unicasts + return true; + } + return false; + } + else + { + // IsMulticast has the same priority level, decide + // based on the highest address + if( currentAddress < address ) + { + // New slot sequence has priority. The sequence with + // the highest address has priority + return true; + } + } + } + return false; +} +#endif /* LORAMAC_VERSION */ + +#endif /* LORAMAC_CLASSB_ENABLED */ + +void LoRaMacClassBInit( LoRaMacClassBParams_t *classBParams, LoRaMacClassBCallback_t *callbacks, LoRaMacClassBNvmData_t* nvm ) +{ +#if ( LORAMAC_CLASSB_ENABLED == 1 ) + // Assign non-volatile context + if( nvm == NULL ) + { + return; + } + ClassBNvm = nvm; + + // Store callbacks + Ctx.LoRaMacClassBCallbacks = *callbacks; + + // Store parameter pointers + Ctx.LoRaMacClassBParams = *classBParams; + + // Initialize timers + TimerInit( &Ctx.BeaconTimer, LoRaMacClassBBeaconTimerEvent ); + TimerInit( &Ctx.PingSlotTimer, LoRaMacClassBPingSlotTimerEvent ); + TimerInit( &Ctx.MulticastSlotTimer, LoRaMacClassBMulticastSlotTimerEvent ); + + InitClassB( ); +#endif /* LORAMAC_CLASSB_ENABLED */ +} + +void LoRaMacClassBSetBeaconState( BeaconState_t beaconState ) +{ +#if ( LORAMAC_CLASSB_ENABLED == 1 ) + 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; + } + } +#endif /* LORAMAC_CLASSB_ENABLED */ +} + +void LoRaMacClassBSetPingSlotState( PingSlotState_t pingSlotState ) +{ +#if ( LORAMAC_CLASSB_ENABLED == 1 ) + Ctx.PingSlotState = pingSlotState; +#endif /* LORAMAC_CLASSB_ENABLED */ +} + +void LoRaMacClassBSetMulticastSlotState( PingSlotState_t multicastSlotState ) +{ +#if ( LORAMAC_CLASSB_ENABLED == 1 ) + Ctx.MulticastSlotState = multicastSlotState; +#endif /* LORAMAC_CLASSB_ENABLED */ +} + +bool LoRaMacClassBIsAcquisitionInProgress( void ) +{ +#if ( LORAMAC_CLASSB_ENABLED == 1 ) + 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 ) +{ +#if ( LORAMAC_CLASSB_ENABLED == 1 ) + Ctx.BeaconCtx.TimeStamp = TimerGetCurrentTime( ); + TimerStop( &Ctx.BeaconTimer ); + LoRaMacClassBEvents.Events.Beacon = 1; + + if( Ctx.LoRaMacClassBCallbacks.MacProcessNotify != NULL ) + { + Ctx.LoRaMacClassBCallbacks.MacProcessNotify( ); + } +#endif /* LORAMAC_CLASSB_ENABLED */ +} + +#if ( LORAMAC_CLASSB_ENABLED == 1 ) +static void LoRaMacClassBProcessBeacon( void ) +{ + bool activateTimer = false; + TimerTime_t beaconEventTime = 1; + RxConfigParams_t beaconRxConfig; + 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 ) + { + // The goal is to calculate beaconRxConfig.WindowTimeout + CalculateBeaconRxWindowConfig( &beaconRxConfig, Ctx.BeaconCtx.SymbolTimeout ); + + if( Ctx.BeaconCtx.BeaconTimingDelay > 0 ) + { + if( SysTimeToMs( Ctx.BeaconCtx.NextBeaconRx ) > currentTime ) + { + // Calculate the time when we expect the next beacon + beaconEventTime = TimerTempCompensation( SysTimeToMs( Ctx.BeaconCtx.NextBeaconRx ) - currentTime, Ctx.BeaconCtx.Temperature ); + + if( ( int32_t ) beaconEventTime > beaconRxConfig.WindowOffset ) + { + // Apply the offset of the system error respectively beaconing precision setting + beaconEventTime += beaconRxConfig.WindowOffset; + } + } + 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, beaconRxConfig.WindowTimeout ); + } + } + 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; + + // The goal is to calculate beaconRxConfig.WindowTimeout + CalculateBeaconRxWindowConfig( &beaconRxConfig, Ctx.BeaconCtx.SymbolTimeout ); + + // 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, beaconRxConfig.WindowTimeout ); + } + 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_NOT_FOUND, + 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; + GetTemperature( &Ctx.LoRaMacClassBCallbacks, &Ctx.BeaconCtx ); + beaconEventTime = Ctx.BeaconCtx.NextBeaconRxAdjusted - Radio.GetWakeupTime( ); + currentTime = TimerGetCurrentTime( ); + + // The goal is to calculate beaconRxConfig.WindowTimeout and beaconRxConfig.WindowOffset + CalculateBeaconRxWindowConfig( &beaconRxConfig, Ctx.BeaconCtx.SymbolTimeout ); + + if( beaconEventTime > currentTime ) + { + Ctx.BeaconState = BEACON_STATE_GUARD; + beaconEventTime -= currentTime; + beaconEventTime = TimerTempCompensation( beaconEventTime, Ctx.BeaconCtx.Temperature ); + + if( ( int32_t ) beaconEventTime > beaconRxConfig.WindowOffset ) + { + // Apply the offset of the system error respectively beaconing precision setting + beaconEventTime += beaconRxConfig.WindowOffset; + } + } + 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, beaconRxConfig.WindowTimeout ); + 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; + } + } + MW_LOG(TS_ON, VLEVEL_H, "beacon state %d\r\n", Ctx.BeaconState); + + if( activateTimer == true ) + { + TimerSetValue( &Ctx.BeaconTimer, beaconEventTime ); + TimerStart( &Ctx.BeaconTimer ); + } +} +#endif /* LORAMAC_CLASSB_ENABLED */ + +void LoRaMacClassBPingSlotTimerEvent( void* context ) +{ +#if ( LORAMAC_CLASSB_ENABLED == 1 ) + LoRaMacClassBEvents.Events.PingSlot = 1; + + if( Ctx.LoRaMacClassBCallbacks.MacProcessNotify != NULL ) + { + Ctx.LoRaMacClassBCallbacks.MacProcessNotify( ); + } +#endif /* LORAMAC_CLASSB_ENABLED */ +} + +#if ( LORAMAC_CLASSB_ENABLED == 1 ) +static void LoRaMacClassBProcessPingSlot( void ) +{ + static RxConfigParams_t pingSlotRxConfig; + TimerTime_t pingSlotTime = 0; +#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + uint32_t maxRxError = 0; + bool slotHasPriority = false; +#endif /* LORAMAC_VERSION */ + + switch( Ctx.PingSlotState ) + { + case PINGSLOT_STATE_CALC_PING_OFFSET: + { + ComputePingOffset( Ctx.BeaconCtx.BeaconTime.Seconds, + *Ctx.LoRaMacClassBParams.LoRaMacDevAddr, + ClassBNvm->PingSlotCtx.PingPeriod, + &( Ctx.PingSlotCtx.PingOffset ) ); + Ctx.PingSlotState = PINGSLOT_STATE_SET_TIMER; + } + // Intentional fall through + case PINGSLOT_STATE_SET_TIMER: + { + if( CalcNextSlotTime( Ctx.PingSlotCtx.PingOffset, ClassBNvm->PingSlotCtx.PingPeriod, ClassBNvm->PingSlotCtx.PingNb, &pingSlotTime ) == true ) + { + if( Ctx.BeaconCtx.Ctrl.BeaconAcquired == 1 ) + { +#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) + // 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, + ClassBNvm->PingSlotCtx.Datarate, + Ctx.LoRaMacClassBParams.LoRaMacParams->MinRxSymbols, + Ctx.LoRaMacClassBParams.LoRaMacParams->SystemMaxRxError, + &pingSlotRxConfig ); +#elif (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + // Compare and assign the maximum between the region specific rx error window time + // and time precision received from beacon frame format. + maxRxError = MAX( Ctx.LoRaMacClassBParams.LoRaMacParams->SystemMaxRxError , + ( uint32_t ) Ctx.BeaconCtx.BeaconTimePrecision.SubSeconds ); + + // 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, + ClassBNvm->PingSlotCtx.Datarate, + Ctx.LoRaMacClassBParams.LoRaMacParams->MinRxSymbols, + maxRxError, + &pingSlotRxConfig ); +#endif /* LORAMAC_VERSION */ + 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 = ClassBNvm->PingSlotCtx.Frequency; + + // Apply a custom frequency if the following bit is set + if( ClassBNvm->PingSlotCtx.Ctrl.CustomFreq == 0 ) + { + // Restore floor plan + frequency = CalcDownlinkChannelAndFrequency( *Ctx.LoRaMacClassBParams.LoRaMacDevAddr, Ctx.BeaconCtx.BeaconTime.Seconds, + CLASSB_BEACON_INTERVAL, false ); + } + +#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) + // 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 = ClassBNvm->PingSlotCtx.Datarate; + pingSlotRxConfig.DownlinkDwellTime = Ctx.LoRaMacClassBParams.LoRaMacParams->DownlinkDwellTime; + pingSlotRxConfig.RepeaterSupport = Ctx.LoRaMacClassBParams.LoRaMacParams->RepeaterSupport; /* ST_WORKAROUND: keep repeater feature */ + 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 ); + } +#elif (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + if( Ctx.PingSlotCtx.NextMulticastChannel != NULL ) + { + // Verify, if the unicast has priority. + slotHasPriority = CheckSlotPriority( *Ctx.LoRaMacClassBParams.LoRaMacDevAddr, ClassBNvm->PingSlotCtx.FPendingSet, 0, + Ctx.PingSlotCtx.NextMulticastChannel->ChannelParams.Address, Ctx.PingSlotCtx.NextMulticastChannel->FPendingSet, 1 ); + } + + // Open the ping slot window only, if there is no multicast ping slot + // open or if the unicast has priority. + if( ( Ctx.MulticastSlotState != PINGSLOT_STATE_RX ) || ( slotHasPriority == true ) ) + { + if( Ctx.MulticastSlotState == PINGSLOT_STATE_RX ) + { + // Close multicast slot window, if necessary. Multicast slots have priority + Radio.Standby( ); + Ctx.MulticastSlotState = PINGSLOT_STATE_CALC_PING_OFFSET; + TimerSetValue( &Ctx.MulticastSlotTimer, CLASSB_PING_SLOT_WINDOW ); + TimerStart( &Ctx.MulticastSlotTimer ); + } + + Ctx.PingSlotState = PINGSLOT_STATE_RX; + + pingSlotRxConfig.Datarate = ClassBNvm->PingSlotCtx.Datarate; + pingSlotRxConfig.DownlinkDwellTime = Ctx.LoRaMacClassBParams.LoRaMacParams->DownlinkDwellTime; + pingSlotRxConfig.RepeaterSupport = Ctx.LoRaMacClassBParams.LoRaMacParams->RepeaterSupport; /* ST_WORKAROUND: keep repeater feature */ + pingSlotRxConfig.Frequency = frequency; + pingSlotRxConfig.RxContinuous = false; + pingSlotRxConfig.RxSlot = RX_SLOT_WIN_CLASS_B_PING_SLOT; + pingSlotRxConfig.NetworkActivation = *Ctx.LoRaMacClassBParams.NetworkActivation; + + 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 ); + } +#endif /* LORAMAC_VERSION */ + break; + } + default: + { + Ctx.PingSlotState = PINGSLOT_STATE_CALC_PING_OFFSET; + break; + } + } +} +#endif /* LORAMAC_CLASSB_ENABLED */ + +void LoRaMacClassBMulticastSlotTimerEvent( void* context ) +{ +#if ( LORAMAC_CLASSB_ENABLED == 1 ) + LoRaMacClassBEvents.Events.MulticastSlot = 1; + + if( Ctx.LoRaMacClassBCallbacks.MacProcessNotify != NULL ) + { + Ctx.LoRaMacClassBCallbacks.MacProcessNotify( ); + } +#endif /* LORAMAC_CLASSB_ENABLED */ +} + +#if ( LORAMAC_CLASSB_ENABLED == 1 ) +static void LoRaMacClassBProcessMulticastSlot( void ) +{ + static RxConfigParams_t multicastSlotRxConfig; + TimerTime_t multicastSlotTime = 0; + TimerTime_t slotTime = 0; + MulticastCtx_t *cur = Ctx.LoRaMacClassBParams.MulticastChannels; +#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + uint32_t maxRxError = 0; + bool slotHasPriority = false; +#endif /* LORAMAC_VERSION */ + + 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 < LORAMAC_MAX_MC_CTX; i++ ) /* ST_WORKAROUND: reduced LORAMAC_MAX_MC_CTX */ + { + 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 < LORAMAC_MAX_MC_CTX; 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 ) + { +#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) + RegionComputeRxWindowParameters( *Ctx.LoRaMacClassBParams.LoRaMacRegion, + ClassBNvm->PingSlotCtx.Datarate, + Ctx.LoRaMacClassBParams.LoRaMacParams->MinRxSymbols, + Ctx.LoRaMacClassBParams.LoRaMacParams->SystemMaxRxError, + &multicastSlotRxConfig ); +#elif (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + // Compare and assign the maximum between the region specific rx error window time + // and time precision received from beacon frame format. + maxRxError = MAX( Ctx.LoRaMacClassBParams.LoRaMacParams->SystemMaxRxError , + ( uint32_t ) Ctx.BeaconCtx.BeaconTimePrecision.SubSeconds ); + + RegionComputeRxWindowParameters( *Ctx.LoRaMacClassBParams.LoRaMacRegion, + ClassBNvm->PingSlotCtx.Datarate, + Ctx.LoRaMacClassBParams.LoRaMacParams->MinRxSymbols, + maxRxError, + &multicastSlotRxConfig ); +#endif /* LORAMAC_VERSION */ + 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, false ); + } + +#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) + 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; /* ST_WORKAROUND: keep repeater feature */ + 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 + } +#elif (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + // Verify, if the unicast has priority. + slotHasPriority = CheckSlotPriority( Ctx.PingSlotCtx.NextMulticastChannel->ChannelParams.Address, Ctx.PingSlotCtx.NextMulticastChannel->FPendingSet, 1, + *Ctx.LoRaMacClassBParams.LoRaMacDevAddr, ClassBNvm->PingSlotCtx.FPendingSet, 0 ); + + // Open the ping slot window only, if there is no multicast ping slot + // open or if the unicast has priority. + if( ( Ctx.PingSlotState != PINGSLOT_STATE_RX ) || ( slotHasPriority == true ) ) + { + 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 ); + } + + Ctx.MulticastSlotState = PINGSLOT_STATE_RX; + + multicastSlotRxConfig.Datarate = Ctx.PingSlotCtx.NextMulticastChannel->ChannelParams.RxParams.ClassB.Datarate; + multicastSlotRxConfig.DownlinkDwellTime = Ctx.LoRaMacClassBParams.LoRaMacParams->DownlinkDwellTime; + multicastSlotRxConfig.Frequency = frequency; + multicastSlotRxConfig.RxContinuous = false; + multicastSlotRxConfig.RxSlot = RX_SLOT_WIN_CLASS_B_MULTICAST_SLOT; + multicastSlotRxConfig.NetworkActivation = *Ctx.LoRaMacClassBParams.NetworkActivation; + + RegionRxConfig( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &multicastSlotRxConfig, ( int8_t* )&Ctx.LoRaMacClassBParams.McpsIndication->RxDatarate ); + + if( multicastSlotRxConfig.RxContinuous == false ) + { + Radio.Rx( Ctx.LoRaMacClassBParams.LoRaMacParams->MaxRxWindow ); + } + else + { + Radio.Rx( 0 ); // Continuous mode + } + } + else + { + // Unicast slots have priority. Skip Rx + Ctx.MulticastSlotState = PINGSLOT_STATE_CALC_PING_OFFSET; + TimerSetValue( &Ctx.MulticastSlotTimer, CLASSB_PING_SLOT_WINDOW ); + TimerStart( &Ctx.MulticastSlotTimer ); + } +#endif /* LORAMAC_VERSION */ + break; + } + default: + { + Ctx.MulticastSlotState = PINGSLOT_STATE_CALC_PING_OFFSET; + break; + } + } +} +#endif /* LORAMAC_CLASSB_ENABLED */ + +bool LoRaMacClassBRxBeacon( uint8_t *payload, uint16_t size ) +{ +#if ( LORAMAC_CLASSB_ENABLED == 1 ) + 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 ) + { +#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) + // 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 ); + } +#elif (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + // A beacon frame is defined as: + // Bytes: | x | 1 | 4 | 2 | 7 | y | 2 | + // |------|-------|------|------|------------|------|------| + // Field: | RFU1 | Param | 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 + 1 + 4] ) & 0x00FF; + beaconCrc0 |= ( ( uint16_t )payload[phyParam.BeaconFormat.Rfu1Size + 1 + 4 + 1] << 8 ) & 0xFF00; + crc0 = BeaconCrc( payload, phyParam.BeaconFormat.Rfu1Size + 1 + 4 ); + + // Validate the first crc of the beacon frame + if( crc0 == beaconCrc0 ) + { + // Copy the param field for app layer + Ctx.LoRaMacClassBParams.MlmeIndication->BeaconInfo.Param = ( payload[phyParam.BeaconFormat.Rfu1Size] ); + // Fetch the precise time value in milliseconds that will be used for Rx ping slot delay. + Ctx.BeaconCtx.BeaconTimePrecision.SubSeconds = BeaconPrecTimeValue[Ctx.LoRaMacClassBParams.MlmeIndication->BeaconInfo.Param]; + + // Read Time field from the frame + Ctx.BeaconCtx.BeaconTime.Seconds = ( ( uint32_t )payload[phyParam.BeaconFormat.Rfu1Size + 1] ) & 0x000000FF; + Ctx.BeaconCtx.BeaconTime.Seconds |= ( ( uint32_t )( payload[phyParam.BeaconFormat.Rfu1Size + 2] << 8 ) ) & 0x0000FF00; + Ctx.BeaconCtx.BeaconTime.Seconds |= ( ( uint32_t )( payload[phyParam.BeaconFormat.Rfu1Size + 3] << 16 ) ) & 0x00FF0000; + Ctx.BeaconCtx.BeaconTime.Seconds |= ( ( uint32_t )( payload[phyParam.BeaconFormat.Rfu1Size + 4] << 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 + 1 + 4 + 2 + 7 + phyParam.BeaconFormat.Rfu2Size] ) & 0x00FF; + beaconCrc1 |= ( ( uint16_t )payload[phyParam.BeaconFormat.Rfu1Size + 1 + 4 + 2 + 7 + phyParam.BeaconFormat.Rfu2Size + 1] << 8 ) & 0xFF00; + crc1 = BeaconCrc( &payload[phyParam.BeaconFormat.Rfu1Size + 1 + 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 + 1 + 4 + 2]; + memcpy1( Ctx.LoRaMacClassBParams.MlmeIndication->BeaconInfo.GwSpecific.Info, &payload[phyParam.BeaconFormat.Rfu1Size + 1 + 4 + 2 + 1], 6 ); + } +#endif /* LORAMAC_VERSION */ + + // Reset beacon variables, if one of the crc is valid + if( beaconProcessed == true ) + { + uint32_t spreadingFactor = 0; + uint32_t bandwidth = 0; + + getPhy.Attribute = PHY_BEACON_CHANNEL_DR; + phyParam = RegionGetPhyParam( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &getPhy ); + + getPhy.Attribute = PHY_SF_FROM_DR; + getPhy.Datarate = phyParam.Value; + phyParam = RegionGetPhyParam( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &getPhy ); + spreadingFactor = phyParam.Value; + + getPhy.Attribute = PHY_BW_FROM_DR; + phyParam = RegionGetPhyParam( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &getPhy ); + bandwidth = phyParam.Value; + + TimerTime_t time = Radio.TimeOnAir( MODEM_LORA, bandwidth, spreadingFactor, 1, 10, true, size, false ); + 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.BeaconCtx.Ctrl.BeaconAcquired = 1; + Ctx.BeaconCtx.Ctrl.BeaconMode = 1; + ResetWindowTimeout( ); + Ctx.BeaconState = BEACON_STATE_LOCKED; + + LoRaMacClassBBeaconTimerEvent( NULL ); + } + } + + if( Ctx.BeaconState == BEACON_STATE_RX ) + { + Ctx.BeaconState = BEACON_STATE_TIMEOUT; + 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 ) +{ +#if ( LORAMAC_CLASSB_ENABLED == 1 ) + 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 ) +{ +#if ( LORAMAC_CLASSB_ENABLED == 1 ) + if( Ctx.PingSlotState == PINGSLOT_STATE_RX ) + { + return true; + } + return false; +#else + return false; +#endif /* LORAMAC_CLASSB_ENABLED */ +} + +bool LoRaMacClassBIsMulticastExpected( void ) +{ +#if ( LORAMAC_CLASSB_ENABLED == 1 ) + if( Ctx.MulticastSlotState == PINGSLOT_STATE_RX ) + { + return true; + } + return false; +#else + return false; +#endif /* LORAMAC_CLASSB_ENABLED */ +} + +bool LoRaMacClassBIsAcquisitionPending( void ) +{ +#if ( LORAMAC_CLASSB_ENABLED == 1 ) + if( Ctx.BeaconCtx.Ctrl.AcquisitionPending == 1 ) + { + return true; + } + return false; +#else + return false; +#endif /* LORAMAC_CLASSB_ENABLED */ +} + +bool LoRaMacClassBIsBeaconModeActive( void ) +{ +#if ( LORAMAC_CLASSB_ENABLED == 1 ) + 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 ) +{ +#if ( LORAMAC_CLASSB_ENABLED == 1 ) + ClassBNvm->PingSlotCtx.PingNb = CalcPingNb( periodicity ); + ClassBNvm->PingSlotCtx.PingPeriod = CalcPingPeriod( ClassBNvm->PingSlotCtx.PingNb ); +#endif /* LORAMAC_CLASSB_ENABLED */ +} + +void LoRaMacClassBHaltBeaconing( void ) +{ +#if ( LORAMAC_CLASSB_ENABLED == 1 ) + 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; + + // Halt ping and multicast slot state machines + LoRaMacClassBStopRxSlots( ); + } +#endif /* LORAMAC_CLASSB_ENABLED */ +} + +void LoRaMacClassBResumeBeaconing( void ) +{ +#if ( LORAMAC_CLASSB_ENABLED == 1 ) + 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; + } + + LoRaMacClassBBeaconTimerEvent( NULL ); + } +#endif /* LORAMAC_CLASSB_ENABLED */ +} + +LoRaMacStatus_t LoRaMacClassBSwitchClass( DeviceClass_t nextClass ) +{ +#if ( LORAMAC_CLASSB_ENABLED == 1 ) + if( nextClass == CLASS_B ) + {// Switch to from class a to class b + if( ( Ctx.BeaconCtx.Ctrl.BeaconMode == 1 ) && ( ClassBNvm->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 ) +{ +#if ( LORAMAC_CLASSB_ENABLED == 1 ) + LoRaMacStatus_t status = LORAMAC_STATUS_OK; + + switch( mibGet->Type ) + { + case MIB_PING_SLOT_DATARATE: + { + mibGet->Param.PingSlotDatarate = ClassBNvm->PingSlotCtx.Datarate; + break; + } + case MIB_BEACON_STATE: + { + mibGet->Param.BeaconState = Ctx.BeaconState; + 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 ) +{ +#if ( LORAMAC_CLASSB_ENABLED == 1 ) + LoRaMacStatus_t status = LORAMAC_STATUS_OK; + + switch( mibSet->Type ) + { + case MIB_PING_SLOT_DATARATE: + { + ClassBNvm->PingSlotCtx.Datarate = mibSet->Param.PingSlotDatarate; + break; + } + default: + { + status = LORAMAC_STATUS_SERVICE_UNKNOWN; + break; + } + } + return status; +#else + return LORAMAC_STATUS_SERVICE_UNKNOWN; +#endif /* LORAMAC_CLASSB_ENABLED */ +} + +void LoRaMacClassBPingSlotInfoAns( void ) +{ +#if ( LORAMAC_CLASSB_ENABLED == 1 ) + if( LoRaMacConfirmQueueIsCmdActive( MLME_PING_SLOT_INFO ) == true ) + { + LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_OK, MLME_PING_SLOT_INFO ); + ClassBNvm->PingSlotCtx.Ctrl.Assigned = 1; + } +#endif /* LORAMAC_CLASSB_ENABLED */ +} + +uint8_t LoRaMacClassBPingSlotChannelReq( uint8_t datarate, uint32_t frequency ) +{ +#if ( LORAMAC_CLASSB_ENABLED == 1 ) + 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 ) + { + ClassBNvm->PingSlotCtx.Ctrl.CustomFreq = 1; + ClassBNvm->PingSlotCtx.Frequency = frequency; + } + else + { + ClassBNvm->PingSlotCtx.Ctrl.CustomFreq = 0; + ClassBNvm->PingSlotCtx.Frequency = 0; + } + ClassBNvm->PingSlotCtx.Datarate = datarate; + } + + return status; +#else + return 0; +#endif /* LORAMAC_CLASSB_ENABLED */ +} + +void LoRaMacClassBBeaconTimingAns( uint16_t beaconTimingDelay, uint8_t beaconTimingChannel, TimerTime_t lastRxDone ) +{ +#if ( LORAMAC_CLASSB_ENABLED == 1 ) + 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 ) +{ +#if ( LORAMAC_CLASSB_ENABLED == 1 ) + + 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 ) +{ +#if ( LORAMAC_CLASSB_ENABLED == 1 ) + VerifyParams_t verify; + + if( frequency != 0 ) + { + verify.Frequency = frequency; + + if( RegionVerify( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &verify, PHY_FREQUENCY ) == true ) + { + ClassBNvm->BeaconCtx.Ctrl.CustomFreq = 1; + ClassBNvm->BeaconCtx.Frequency = frequency; + return true; + } + } + else + { + ClassBNvm->BeaconCtx.Ctrl.CustomFreq = 0; + return true; + } + return false; +#else + return false; +#endif /* LORAMAC_CLASSB_ENABLED */ +} + +TimerTime_t LoRaMacClassBIsUplinkCollision( TimerTime_t txTimeOnAir ) +{ +#if ( LORAMAC_CLASSB_ENABLED == 1 ) + 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 ) +{ +#if ( LORAMAC_CLASSB_ENABLED == 1 ) + 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 ) +{ +#if ( LORAMAC_CLASSB_ENABLED == 1 ) + if( ClassBNvm->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 ) +{ +#if ( LORAMAC_CLASSB_ENABLED == 1 ) + if( multicastChannel != NULL ) + { + multicastChannel->PingNb = CalcPingNb( multicastChannel->ChannelParams.RxParams.ClassB.Periodicity ); + multicastChannel->PingPeriod = CalcPingPeriod( multicastChannel->PingNb ); + } +#endif /* LORAMAC_CLASSB_ENABLED */ +} + +#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) +void LoRaMacClassBSetFPendingBit( uint32_t address, uint8_t fPendingSet ) +{ +#if ( LORAMAC_CLASSB_ENABLED == 1 ) + MulticastCtx_t *cur = Ctx.LoRaMacClassBParams.MulticastChannels; + + if( address == *Ctx.LoRaMacClassBParams.LoRaMacDevAddr ) + { + // Unicast + ClassBNvm->PingSlotCtx.FPendingSet = fPendingSet; + } + else + { + for( uint8_t i = 0; i < LORAMAC_MAX_MC_CTX; i++ ) + { + if( cur != NULL ) + { + // Set the fPending bit, if its a multicast + if( address == cur->ChannelParams.Address ) + { + cur->FPendingSet = fPendingSet; + } + } + cur++; + } + } +#endif /* LORAMAC_CLASSB_ENABLED */ +} +#endif /* LORAMAC_VERSION */ + +void LoRaMacClassBProcess( void ) +{ +#if ( LORAMAC_CLASSB_ENABLED == 1 ) + 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/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacClassB.h b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacClassB.h new file mode 100644 index 0000000..4d86515 --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacClassB.h @@ -0,0 +1,508 @@ +/*! + * \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. + * \{ + */ +/** + ****************************************************************************** + * + * Portions COPYRIGHT 2020 STMicroelectronics + * + * @file LoRaMacClassB.h + * @author MCD Application Team + * @brief LoRa MAC Class B layer implementation + ****************************************************************************** + */ +#ifndef __LORAMACCLASSB_H__ +#define __LORAMACCLASSB_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "LoRaMacTypes.h" +#include "LoRaMacVersion.h" + +/*! + * 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 + */ + int16_t 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; +#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + /*! + * Beacons transmit time precision determined using + * param field of beacon frame format. + */ + SysTime_t BeaconTimePrecision; +#endif /* LORAMAC_VERSION */ +}BeaconContext_t; + +/*! + * Data structure which contains the callbacks + */ +typedef struct sLoRaMacClassBCallback +{ + /* ST_WORKAROUND_BEGIN: Return temperature into int16_t instead float */ + /*! + * \brief Measures the temperature level + * + * \retval Temperature level + */ + int16_t ( *GetTemperatureLevel )( void ); + /* ST_WORKAROUND_END */ + /*! + *\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; +#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + /*! + * Pointer to the activation type + */ + ActivationType_t *NetworkActivation; +#endif /* LORAMAC_VERSION */ +}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] nvm Pointer to an external non-volatile memory data structure. + */ +void LoRaMacClassBInit( LoRaMacClassBParams_t *classBParams, LoRaMacClassBCallback_t *callbacks, + LoRaMacClassBNvmData_t* nvm ); + +/*! + * \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] multicastSlotState 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] mibGet - 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] mibSet - 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] multicastChannel Related multicast channel + */ +void LoRaMacClassBSetMulticastPeriodicity( MulticastCtx_t* multicastChannel ); + +#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) +/*! + * \brief Sets the FPending bit status of the related downlink slot + * + * \param [in] address Slot address, could be unicast or multicast + * + * \param [in] fPendingSet Set to 1, if the fPending bit in the + * sequence is set, otherwise 0. + */ +void LoRaMacClassBSetFPendingBit( uint32_t address, uint8_t fPendingSet ); +#endif /* LORAMAC_VERSION */ + +/*! + * \brief Class B process function. + */ +void LoRaMacClassBProcess( void ); + +/*! \} defgroup LORAMACCLASSB */ + +#ifdef __cplusplus +} +#endif + +#endif // __LORAMACCLASSB_H__ diff --git a/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacClassBConfig.h b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacClassBConfig.h new file mode 100644 index 0000000..087e938 --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacClassBConfig.h @@ -0,0 +1,126 @@ +/*! + * \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 LORAMACCLASSBCONFIG 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__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*! + * 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 number 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 + +/*! \} defgroup LORAMACCLASSBCONFIG */ + +#ifdef __cplusplus +} +#endif + +#endif // __LORAMACCLASSBCONFIG_H__ diff --git a/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacClassBNvm.h b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacClassBNvm.h new file mode 100644 index 0000000..e6e917d --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacClassBNvm.h @@ -0,0 +1,128 @@ +/*! + * \file LoRaMacClassBNvm.h + * + * \brief LoRa MAC Class B non-volatile data. + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \addtogroup LORAMACCLASSB + * + * \{ + */ +#ifndef __LORAMACCLASSBNVM_H__ +#define __LORAMACCLASSBNVM_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "LoRaMacVersion.h" + +/*! + * LoRaMac Class B Context structure for NVM parameters + * related to ping slots + */ +typedef struct sLoRaMacClassBPingSlotNvmData +{ + 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; +#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + /*! + * Set to 1, if the FPending bit is set + */ + uint8_t FPendingSet; +#endif /* LORAMAC_VERSION */ +} LoRaMacClassBPingSlotNvmData_t; + +/*! + * LoRaMac Class B Context structure for NVM parameters + * related to beaconing + */ +typedef struct sLoRaMacClassBBeaconNvmData +{ + 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; +} LoRaMacClassBBeaconNvmData_t; + +/*! + * LoRaMac Class B Context structure + */ +typedef struct sLoRaMacClassBNvmData +{ + /*! + * Class B ping slot context + */ + LoRaMacClassBPingSlotNvmData_t PingSlotCtx; + /*! + * Class B beacon context + */ + LoRaMacClassBBeaconNvmData_t BeaconCtx; + /*! + * CRC32 value of the ClassB data structure. + */ + uint32_t Crc32; +} LoRaMacClassBNvmData_t; + +/*! \} defgroup LORAMACCLASSB */ + +#ifdef __cplusplus +} +#endif + +#endif // __LORAMACCLASSBNVM_H__ diff --git a/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacCommands.c b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacCommands.c new file mode 100644 index 0000000..184ab9e --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacCommands.c @@ -0,0 +1,621 @@ +/*! + * \file LoRaMacCommands.c + * + * \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 ) + */ +#include "../Utilities/utilities.h" +#include "LoRaMacCommands.h" +#include "LoRaMacConfirmQueue.h" +#include "LoRaMacVersion.h" + +#ifndef NUM_OF_MAC_COMMANDS +/*! + * Number of MAC Command slots + */ +#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) +#define NUM_OF_MAC_COMMANDS 15 +#elif (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) +#define NUM_OF_MAC_COMMANDS 32 +#endif /* LORAMAC_VERSION */ +#endif + +/*! + * 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; + +/*! + * Non-volatile module context. + */ +static LoRaMacCommandsCtx_t CommandsCtx; + +/* 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* )&CommandsCtx.MacCommandSlots[itr] ) == false ) + { + itr++; + if( itr == NUM_OF_MAC_COMMANDS ) + { + return NULL; + } + } + + return &CommandsCtx.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 == NULL ) + { + 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 == NULL ) + { + return false; + } + + list->First = NULL; + list->Last = NULL; + + 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 == NULL ) || ( element == NULL ) ) + { + return false; + } + + // Check if this is the first entry to enter the list. + if( list->First == NULL ) + { + 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 = NULL; + + // 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 == NULL ) || ( element == NULL ) ) + { + 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 == NULL ) || ( element == NULL ) ) + { + 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: + case MOTE_MAC_TX_PARAM_SETUP_ANS: +#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + case MOTE_MAC_PING_SLOT_CHANNEL_ANS: +#endif /* LORAMAC_VERSION */ + return true; + default: + return false; + } +} + +LoRaMacCommandStatus_t LoRaMacCommandsInit( void ) +{ + // Initialize with default + memset1( ( uint8_t* )&CommandsCtx, 0, sizeof( CommandsCtx ) ); + + LinkedListInit( &CommandsCtx.MacCommandList ); + + return LORAMAC_COMMANDS_SUCCESS; +} + +LoRaMacCommandStatus_t LoRaMacCommandsAddCmd( uint8_t cid, uint8_t* payload, size_t payloadSize ) +{ + if( payload == NULL ) + { + return LORAMAC_COMMANDS_ERROR_NPE; + } + MacCommand_t* newCmd; + + // Allocate a memory slot + newCmd = MallocNewMacCommandSlot( ); + + if( newCmd == NULL ) + { + return LORAMAC_COMMANDS_ERROR_MEMORY; + } + + // Add it to the list of Mac commands + if( LinkedListAdd( &CommandsCtx.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 ); + + CommandsCtx.SerializedCmdsSize += ( CID_FIELD_SIZE + payloadSize ); + + 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( &CommandsCtx.MacCommandList, macCmd ) == false ) + { + return LORAMAC_COMMANDS_ERROR_CMD_NOT_FOUND; + } + + CommandsCtx.SerializedCmdsSize -= ( CID_FIELD_SIZE + macCmd->PayloadSize ); + + // Free the MacCommand Slot + if( FreeMacCommandSlot( macCmd ) == false ) + { + return LORAMAC_COMMANDS_ERROR; + } + + return LORAMAC_COMMANDS_SUCCESS; +} + +LoRaMacCommandStatus_t LoRaMacCommandsGetCmd( uint8_t cid, MacCommand_t** macCmd ) +{ + MacCommand_t* curElement; + + // Start at the head of the list + curElement = CommandsCtx.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 = CommandsCtx.MacCommandList.First; + + // Loop through all elements + while( curElement != NULL ) + { + if( curElement->IsSticky == false ) + { + nexElement = curElement->Next; + LoRaMacCommandsRemoveCmd( curElement ); + curElement = nexElement; + } + else + { + curElement = curElement->Next; + } + } + + return LORAMAC_COMMANDS_SUCCESS; +} + +LoRaMacCommandStatus_t LoRaMacCommandsRemoveStickyAnsCmds( void ) +{ + MacCommand_t* curElement; + MacCommand_t* nexElement; + + // Start at the head of the list + curElement = CommandsCtx.MacCommandList.First; + + // Loop through all elements + while( curElement != NULL ) + { + nexElement = curElement->Next; + if( IsSticky( curElement->CID ) == true ) + { + LoRaMacCommandsRemoveCmd( curElement ); + } + curElement = nexElement; + } + + return LORAMAC_COMMANDS_SUCCESS; +} + +LoRaMacCommandStatus_t LoRaMacCommandsGetSizeSerializedCmds( size_t* size ) +{ + if( size == NULL ) + { + return LORAMAC_COMMANDS_ERROR_NPE; + } + *size = CommandsCtx.SerializedCmdsSize; + return LORAMAC_COMMANDS_SUCCESS; +} + +LoRaMacCommandStatus_t LoRaMacCommandsSerializeCmds( size_t availableSize, size_t* effectiveSize, uint8_t* buffer ) +{ + MacCommand_t* curElement = CommandsCtx.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 = CommandsCtx.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; +} + +uint8_t LoRaMacCommandsGetCmdSize( uint8_t cid ) +{ + uint8_t cidSize = 0; + + // Decode Frame MAC commands + switch( cid ) + { + case SRV_MAC_LINK_CHECK_ANS: + { + // cid + Margin + GwCnt + cidSize = 3; + break; + } + case SRV_MAC_LINK_ADR_REQ: + { + // cid + DataRate_TXPower + ChMask (2) + Redundancy + cidSize = 5; + break; + } + case SRV_MAC_DUTY_CYCLE_REQ: + { + // cid + DutyCyclePL + cidSize = 2; + break; + } + case SRV_MAC_RX_PARAM_SETUP_REQ: + { + // cid + DLsettings + Frequency (3) + cidSize = 5; + break; + } + case SRV_MAC_DEV_STATUS_REQ: + { + // cid + cidSize = 1; + break; + } + case SRV_MAC_NEW_CHANNEL_REQ: + { + // cid + ChIndex + Frequency (3) + DrRange + cidSize = 6; + break; + } + case SRV_MAC_RX_TIMING_SETUP_REQ: + { + // cid + Settings + cidSize = 2; + break; + } + case SRV_MAC_TX_PARAM_SETUP_REQ: + { + // cid + EIRP_DwellTime + cidSize = 2; + break; + } + case SRV_MAC_DL_CHANNEL_REQ: + { + // cid + ChIndex + Frequency (3) + cidSize = 5; + break; + } + case SRV_MAC_DEVICE_TIME_ANS: + { + // cid + Seconds (4) + Fractional seconds (1) + cidSize = 6; + break; + } + case SRV_MAC_PING_SLOT_INFO_ANS: + { + // cid + cidSize = 1; + break; + } + case SRV_MAC_PING_SLOT_CHANNEL_REQ: + { + // cid + Frequency (3) + DR + cidSize = 5; + break; + } + case SRV_MAC_BEACON_TIMING_ANS: + { + // cid + TimingDelay (2) + Channel + cidSize = 4; + break; + } + case SRV_MAC_BEACON_FREQ_REQ: + { + // cid + Frequency (3) + cidSize = 4; + break; + } + default: + { + // Unknown command. ABORT MAC commands processing + break; + } + } + return cidSize; +} diff --git a/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacCommands.h b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacCommands.h new file mode 100644 index 0000000..3f828a9 --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacCommands.h @@ -0,0 +1,212 @@ +/*! + * \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__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#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 + * + * \retval - Status of the operation + */ +LoRaMacCommandStatus_t LoRaMacCommandsInit( void ); + +/*! + * \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] macCmd - 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] macCmd - 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 ); + +/*! + * \brief Get the MAC command size with corresponding CID. + * + * \param [in] cid - MAC command identifier + * + * \retval Size of the command. + */ +uint8_t LoRaMacCommandsGetCmdSize( uint8_t cid ); + +/*! \} addtogroup LORAMAC */ + +#ifdef __cplusplus +} +#endif + +#endif // __LORAMAC_COMMANDS_H__ + diff --git a/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacConfirmQueue.c b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacConfirmQueue.c new file mode 100644 index 0000000..c426fbe --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacConfirmQueue.c @@ -0,0 +1,339 @@ +/*! + * \file LoRaMacConfirmQueue.c + * + * \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 ) + */ +#include "../Utilities/utilities.h" +#include "LoRaMacConfirmQueue.h" +#include "LoRaMacVersion.h" + +/* + * LoRaMac Confirm Queue Context NVM structure + */ +typedef struct sLoRaMacConfirmQueueNvmData +{ + /*! + * 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; +} LoRaMacConfirmQueueNvmData_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; + /*! + * Non-volatile module context. + */ + LoRaMacConfirmQueueNvmData_t Nvm; +} LoRaMacConfirmQueueCtx_t; + +/* + * Module context. + */ +static LoRaMacConfirmQueueCtx_t ConfirmQueueCtx; + +static MlmeConfirmQueue_t* IncreaseBufferPointer( MlmeConfirmQueue_t* bufferPointer ) +{ + if( bufferPointer == &ConfirmQueueCtx.Nvm.MlmeConfirmQueue[LORA_MAC_MLME_CONFIRM_QUEUE_LEN - 1] ) + { + // Reset to the first element + bufferPointer = ConfirmQueueCtx.Nvm.MlmeConfirmQueue; + } + else + { + // Increase + bufferPointer++; + } + return bufferPointer; +} + +static MlmeConfirmQueue_t* DecreaseBufferPointer( MlmeConfirmQueue_t* bufferPointer ) +{ + if( bufferPointer == ConfirmQueueCtx.Nvm.MlmeConfirmQueue ) + { + // Reset to the last element + bufferPointer = &ConfirmQueueCtx.Nvm.MlmeConfirmQueue[LORA_MAC_MLME_CONFIRM_QUEUE_LEN - 1]; + } + else + { + bufferPointer--; + } + return bufferPointer; +} + +static bool IsListEmpty( uint8_t count ) +{ + if( count == 0 ) + { + return true; + } + return false; +} + +static bool IsListFull( uint8_t count ) +{ + if( count >= LORA_MAC_MLME_CONFIRM_QUEUE_LEN ) + { + return true; + } + return false; +} + +static MlmeConfirmQueue_t* GetElement( Mlme_t request, MlmeConfirmQueue_t* bufferStart, MlmeConfirmQueue_t* bufferEnd ) +{ + MlmeConfirmQueue_t* element = bufferStart; + + if( IsListEmpty( ConfirmQueueCtx.Nvm.MlmeConfirmQueueCnt ) == true ) + { + return NULL; + } + + for( uint8_t elementCnt = 0; elementCnt < ConfirmQueueCtx.Nvm.MlmeConfirmQueueCnt; elementCnt++ ) + { + if( element->Request == request ) + { + // We have found the element + return element; + } + element = IncreaseBufferPointer( element ); + } + + return NULL; +} + +void LoRaMacConfirmQueueInit( LoRaMacPrimitives_t* primitives ) +{ + ConfirmQueueCtx.Primitives = primitives; + + // Init counter + ConfirmQueueCtx.Nvm.MlmeConfirmQueueCnt = 0; + + // Init buffer + ConfirmQueueCtx.BufferStart = ConfirmQueueCtx.Nvm.MlmeConfirmQueue; + ConfirmQueueCtx.BufferEnd = ConfirmQueueCtx.Nvm.MlmeConfirmQueue; + + memset1( ( uint8_t* )ConfirmQueueCtx.Nvm.MlmeConfirmQueue, 0xFF, sizeof( ConfirmQueueCtx.Nvm.MlmeConfirmQueue ) ); + + // Common status + ConfirmQueueCtx.Nvm.CommonStatus = LORAMAC_EVENT_INFO_STATUS_ERROR; +} + +bool LoRaMacConfirmQueueAdd( MlmeConfirmQueue_t* mlmeConfirm ) +{ + if( IsListFull( ConfirmQueueCtx.Nvm.MlmeConfirmQueueCnt ) == true ) + { + // 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; +#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) + ConfirmQueueCtx.BufferEnd->ReadyToHandle = false; +#elif (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + ConfirmQueueCtx.BufferEnd->ReadyToHandle = mlmeConfirm->ReadyToHandle; +#endif /* LORAMAC_VERSION */ + // Increase counter + ConfirmQueueCtx.Nvm.MlmeConfirmQueueCnt++; + // Update end pointer + ConfirmQueueCtx.BufferEnd = IncreaseBufferPointer( ConfirmQueueCtx.BufferEnd ); + + return true; +} + +bool LoRaMacConfirmQueueRemoveLast( void ) +{ + if( IsListEmpty( ConfirmQueueCtx.Nvm.MlmeConfirmQueueCnt ) == true ) + { + return false; + } + + // Increase counter + ConfirmQueueCtx.Nvm.MlmeConfirmQueueCnt--; + // Update start pointer + ConfirmQueueCtx.BufferEnd = DecreaseBufferPointer( ConfirmQueueCtx.BufferEnd ); + + return true; +} + +bool LoRaMacConfirmQueueRemoveFirst( void ) +{ + if( IsListEmpty( ConfirmQueueCtx.Nvm.MlmeConfirmQueueCnt ) == true ) + { + return false; + } + + // Increase counter + ConfirmQueueCtx.Nvm.MlmeConfirmQueueCnt--; + // Update start pointer + ConfirmQueueCtx.BufferStart = IncreaseBufferPointer( ConfirmQueueCtx.BufferStart ); + + return true; +} + +void LoRaMacConfirmQueueSetStatus( LoRaMacEventInfoStatus_t status, Mlme_t request ) +{ + MlmeConfirmQueue_t* element = NULL; + + if( IsListEmpty( ConfirmQueueCtx.Nvm.MlmeConfirmQueueCnt ) == false ) + { + 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( IsListEmpty( ConfirmQueueCtx.Nvm.MlmeConfirmQueueCnt ) == false ) + { + 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.Nvm.CommonStatus = status; + + if( IsListEmpty( ConfirmQueueCtx.Nvm.MlmeConfirmQueueCnt ) == false ) + { + 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.Nvm.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.Nvm.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.Nvm.MlmeConfirmQueueCnt; +} + +bool LoRaMacConfirmQueueIsFull( void ) +{ + if( IsListFull( ConfirmQueueCtx.Nvm.MlmeConfirmQueueCnt ) == true ) + { + return true; + } + else + { + return false; + } +} diff --git a/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacConfirmQueue.h b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacConfirmQueue.h new file mode 100644 index 0000000..55c0420 --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacConfirmQueue.h @@ -0,0 +1,176 @@ +/*! + * \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__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "LoRaMacInterfaces.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; + +/*! + * \brief Initializes the confirm queue + * + * \param [in] primitive - Pointer to the LoRaMac primitives. + */ +void LoRaMacConfirmQueueInit( LoRaMacPrimitives_t* primitive ); + +/*! + * \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 ); + +/*! \} defgroup LORAMACCONFIRMQUEUE */ + +#ifdef __cplusplus +} +#endif + +#endif // __LORAMAC_CONFIRMQUEUE_H__ diff --git a/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacCrypto.c b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacCrypto.c new file mode 100644 index 0000000..f2d555d --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacCrypto.c @@ -0,0 +1,1635 @@ +/*! + * \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 ) + */ +/** + ****************************************************************************** + * + * Portions COPYRIGHT 2020 STMicroelectronics + * + * @file LoRaMacCrypto.c + * @author MCD Application Team + * @brief LoRa MAC layer cryptography implementation + ****************************************************************************** + */ +#include "../Utilities/utilities.h" +#include "secure-element.h" + +#include "LoRaMacParser.h" +#include "LoRaMacSerializer.h" +#include "LoRaMacVersion.h" + +/* + * 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 + +/* ST_WORKAROUND_BEGIN: reduced LORAMAC_MAX_MC_CTX */ +/* + * Number of security context entries + */ +#define NUM_OF_SEC_CTX LORAMAC_MAX_MC_CTX + 1 +/* ST_WORKAROUND_END */ + +/* + * 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 + +/* + * 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; + +#if( USE_LRWAN_1_1_X_CRYPTO == 1 ) +/* + * RJcount0 is a counter incremented with every Type 0 or 2 Rejoin frame transmitted. + */ +static uint16_t RJcount0; +#endif /* USE_LRWAN_1_1_X_CRYPTO == 1 */ + +/* + * Non volatile module context. + */ +static LoRaMacCryptoNvmData_t* CryptoNvm; + +/* + * Key-Address list + */ +static KeyAddr_t KeyAddrList[NUM_OF_SEC_CTX] = + { +/* ST_WORKAROUND_BEGIN: reduced LORAMAC_MAX_MC_CTX */ +#if ( LORAMAC_MAX_MC_CTX > 0 ) + { MULTICAST_0_ADDR, MC_APP_S_KEY_0, MC_NWK_S_KEY_0, MC_KEY_0 }, +#endif /* LORAMAC_MAX_MC_CTX > 0 */ +#if ( LORAMAC_MAX_MC_CTX > 1 ) + { MULTICAST_1_ADDR, MC_APP_S_KEY_1, MC_NWK_S_KEY_1, MC_KEY_1 }, +#endif /* LORAMAC_MAX_MC_CTX > 1 */ +#if ( LORAMAC_MAX_MC_CTX > 2 ) + { MULTICAST_2_ADDR, MC_APP_S_KEY_2, MC_NWK_S_KEY_2, MC_KEY_2 }, +#endif /* LORAMAC_MAX_MC_CTX > 2 */ +#if ( LORAMAC_MAX_MC_CTX > 3 ) + { MULTICAST_3_ADDR, MC_APP_S_KEY_3, MC_NWK_S_KEY_3, MC_KEY_3 }, +#endif /* LORAMAC_MAX_MC_CTX > 3 */ +/* ST_WORKAROUND_END */ +#if ( USE_LRWAN_1_1_X_CRYPTO == 1 ) /* ST_WORKAROUND: integrate 1.1.x keys only if required */ + { UNICAST_DEV_ADDR, APP_S_KEY, S_NWK_S_INT_KEY, NO_KEY } +#else /* USE_LRWAN_1_1_X_CRYPTO == 0 */ + { UNICAST_DEV_ADDR, APP_S_KEY, NWK_S_KEY, NO_KEY } +#endif /* USE_LRWAN_1_1_X_CRYPTO */ + }; + +/* + * 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( CryptoNvm->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( CryptoNvm->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 /* USE_LRWAN_1_1_X_CRYPTO == 1 */ + +/* + * 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; + /* ST_WORAROUND_BEGIN: These bytes are only used for the LRWAN 1.1.x */ +#if ( USE_LRWAN_1_1_X_CRYPTO == 1 ) + 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 )( CryptoNvm->FCntList.FCntUp % 65536 ); + + b0[1] = confFCnt & 0xFF; + b0[2] = ( confFCnt >> 8 ) & 0xFF; + } + else +#endif /* USE_LRWAN_1_1_X_CRYPTO == 1 */ + /* ST_WORAROUND_END */ + { + 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 )( *CryptoNvm->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 /* USE_LRWAN_1_1_X_CRYPTO == 1 */ + +/* + * 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, uint32_t joinNonce, uint32_t netID, uint16_t devNonce ) +{ + uint8_t compBase[16] = { 0 }; + + /* ST_WORKAROUND_BEGIN: integrate 1.1.x keys only if required */ + switch( keyID ) + { +#if ( USE_LRWAN_1_1_X_CRYPTO == 1 ) + case F_NWK_S_INT_KEY: + case S_NWK_S_INT_KEY: + case NWK_S_ENC_KEY: +#else /* USE_LRWAN_1_1_X_CRYPTO == 0 */ + case NWK_S_KEY: +#endif /* USE_LRWAN_1_1_X_CRYPTO */ + compBase[0] = 0x01; + break; + case APP_S_KEY: + compBase[0] = 0x02; + break; + default: + return LORAMAC_CRYPTO_ERROR_INVALID_KEY_ID; + } + /* ST_WORKAROUND_END */ + + compBase[1] = ( uint8_t )( ( joinNonce >> 0 ) & 0xFF ); + compBase[2] = ( uint8_t )( ( joinNonce >> 8 ) & 0xFF ); + compBase[3] = ( uint8_t )( ( joinNonce >> 16 ) & 0xFF ); + + compBase[4] = ( uint8_t )( ( netID >> 0 ) & 0xFF ); + compBase[5] = ( uint8_t )( ( netID >> 8 ) & 0xFF ); + compBase[6] = ( uint8_t )( ( netID >> 16 ) & 0xFF ); + + compBase[7] = ( uint8_t )( ( devNonce >> 0 ) & 0xFF ); + compBase[8] = ( uint8_t )( ( devNonce >> 8 ) & 0xFF ); + + if( SecureElementDeriveAndStoreKey( 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, uint32_t joinNonce, uint8_t* joinEUI, uint16_t devNonce ) +{ + if( joinEUI == 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; + } + + compBase[1] = ( uint8_t )( ( joinNonce >> 0 ) & 0xFF ); + compBase[2] = ( uint8_t )( ( joinNonce >> 8 ) & 0xFF ); + compBase[3] = ( uint8_t )( ( joinNonce >> 16 ) & 0xFF ); + + memcpyr( compBase + 4, joinEUI, 8 ); + + compBase[12] = ( uint8_t )( ( devNonce >> 0 ) & 0xFF ); + compBase[13] = ( uint8_t )( ( devNonce >> 8 ) & 0xFF ); + + if( SecureElementDeriveAndStoreKey( 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( compBase, NWK_KEY, keyID ) != SECURE_ELEMENT_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC; + } + + return LORAMAC_CRYPTO_SUCCESS; +} +#endif /* USE_LRWAN_1_1_X_CRYPTO == 1 */ + +/* + * 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 = CryptoNvm->FCntList.NFCntDown; + CryptoNvm->LastDownFCnt = CryptoNvm->FCntList.NFCntDown; + break; + case A_FCNT_DOWN: + *lastDown = CryptoNvm->FCntList.AFCntDown; + CryptoNvm->LastDownFCnt = CryptoNvm->FCntList.AFCntDown; + break; + case FCNT_DOWN: + *lastDown = CryptoNvm->FCntList.FCntDown; + CryptoNvm->LastDownFCnt = CryptoNvm->FCntList.FCntDown; + break; +#if ( LORAMAC_MAX_MC_CTX > 0 ) + case MC_FCNT_DOWN_0: + *lastDown = CryptoNvm->FCntList.McFCntDown[0]; + break; +#endif /* LORAMAC_MAX_MC_CTX > 0 */ +#if ( LORAMAC_MAX_MC_CTX > 1 ) + case MC_FCNT_DOWN_1: + *lastDown = CryptoNvm->FCntList.McFCntDown[1]; + break; +#endif /* LORAMAC_MAX_MC_CTX > 1 */ +#if ( LORAMAC_MAX_MC_CTX > 2 ) + case MC_FCNT_DOWN_2: + *lastDown = CryptoNvm->FCntList.McFCntDown[2]; + break; +#endif /* LORAMAC_MAX_MC_CTX > 2 */ +#if ( LORAMAC_MAX_MC_CTX > 3 ) + case MC_FCNT_DOWN_3: + *lastDown = CryptoNvm->FCntList.McFCntDown[3]; + break; +#endif /* LORAMAC_MAX_MC_CTX > 3 */ + 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: + CryptoNvm->FCntList.NFCntDown = currentDown; + break; + case A_FCNT_DOWN: + CryptoNvm->FCntList.AFCntDown = currentDown; + break; + case FCNT_DOWN: + CryptoNvm->FCntList.FCntDown = currentDown; + break; +#if ( LORAMAC_MAX_MC_CTX > 0 ) + case MC_FCNT_DOWN_0: + CryptoNvm->FCntList.McFCntDown[0] = currentDown; + break; +#endif /* LORAMAC_MAX_MC_CTX > 0 */ +#if ( LORAMAC_MAX_MC_CTX > 1 ) + case MC_FCNT_DOWN_1: + CryptoNvm->FCntList.McFCntDown[1] = currentDown; + break; +#endif /* LORAMAC_MAX_MC_CTX > 1 */ +#if ( LORAMAC_MAX_MC_CTX > 2 ) + case MC_FCNT_DOWN_2: + CryptoNvm->FCntList.McFCntDown[2] = currentDown; + break; +#endif /* LORAMAC_MAX_MC_CTX > 2 */ +#if ( LORAMAC_MAX_MC_CTX > 3 ) + case MC_FCNT_DOWN_3: + CryptoNvm->FCntList.McFCntDown[3] = currentDown; + break; +#endif /* LORAMAC_MAX_MC_CTX > 3 */ + default: + break; + } +} + +/*! + * Resets the frame counters + */ +static void ResetFCnts( void ) +{ + CryptoNvm->FCntList.FCntUp = 0; + CryptoNvm->FCntList.NFCntDown = FCNT_DOWN_INITAL_VALUE; + CryptoNvm->FCntList.AFCntDown = FCNT_DOWN_INITAL_VALUE; + CryptoNvm->FCntList.FCntDown = FCNT_DOWN_INITAL_VALUE; + CryptoNvm->LastDownFCnt = CryptoNvm->FCntList.FCntDown; + + for( int32_t i = 0; i < LORAMAC_MAX_MC_CTX; i++ ) + { + CryptoNvm->FCntList.McFCntDown[i] = FCNT_DOWN_INITAL_VALUE; + } +} + +/* + * API functions + */ +LoRaMacCryptoStatus_t LoRaMacCryptoInit( LoRaMacCryptoNvmData_t* nvm ) +{ + if( nvm == NULL ) + { + return LORAMAC_CRYPTO_FAIL_PARAM; + } + + // Assign non volatile context + CryptoNvm = nvm; + + // Initialize with default + memset1( ( uint8_t* )CryptoNvm, 0, sizeof( LoRaMacCryptoNvmData_t ) ); + + // Set default LoRaWAN version + CryptoNvm->LrWanVersion.Fields.Major = 1; + CryptoNvm->LrWanVersion.Fields.Minor = 1; + CryptoNvm->LrWanVersion.Fields.Patch = 1; + CryptoNvm->LrWanVersion.Fields.Revision = 0; + + // Reset frame counters + ResetFCnts( ); + + return LORAMAC_CRYPTO_SUCCESS; +} + +LoRaMacCryptoStatus_t LoRaMacCryptoSetLrWanVersion( Version_t version ) +{ + CryptoNvm->LrWanVersion = version; + return LORAMAC_CRYPTO_SUCCESS; +} + +LoRaMacCryptoStatus_t LoRaMacCryptoGetFCntUp( uint32_t* currentUp ) +{ + if( currentUp == NULL ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + + *currentUp = CryptoNvm->FCntList.FCntUp + 1; + + return LORAMAC_CRYPTO_SUCCESS; +} +#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) +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( CryptoNvm->LrWanVersion.Fields.Minor == 0 ) + { + if( ( ( int64_t )*currentDown - ( int64_t )lastDown ) >= maxFCntGap ) + { + return LORAMAC_CRYPTO_FAIL_MAX_GAP_FCNT; + } + } + + return LORAMAC_CRYPTO_SUCCESS; +} +#elif (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) +LoRaMacCryptoStatus_t LoRaMacCryptoGetFCntDown( FCntIdentifier_t fCntID, 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; + } + } + + return LORAMAC_CRYPTO_SUCCESS; +} +#endif /* LORAMAC_VERSION */ + +#if( USE_LRWAN_1_1_X_CRYPTO == 1 ) +LoRaMacCryptoStatus_t LoRaMacCryptoGetRJcount( FCntIdentifier_t fCntID, uint16_t* rJcount ) +{ + if( rJcount == 0 ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + switch( fCntID ) + { + case RJ_COUNT_0: + *rJcount = RJcount0 + 1; + break; + case RJ_COUNT_1: + *rJcount = CryptoNvm->FCntList.RJcount1 + 1; + break; + default: + return LORAMAC_CRYPTO_FAIL_FCNT_ID; + } + return LORAMAC_CRYPTO_SUCCESS; +} +#endif /* USE_LRWAN_1_1_X_CRYPTO == 1 */ + +LoRaMacCryptoStatus_t LoRaMacCryptoSetMulticastReference( MulticastCtx_t* multicastList ) +{ + if( multicastList == NULL ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + + for( int32_t i = 0; i < LORAMAC_MAX_MC_CTX; i++ ) + { + multicastList[i].DownLinkCounter = &CryptoNvm->FCntList.McFCntDown[i]; + } + + 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 == APP_KEY ) + { + // Derive lifetime keys + if( LoRaMacCryptoDeriveMcRootKey( CryptoNvm->LrWanVersion.Fields.Minor, 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 ); + CryptoNvm->DevNonce = devNonce; +#else + CryptoNvm->DevNonce++; +#endif /* USE_RANDOM_DEV_NONCE */ + macMsg->DevNonce = CryptoNvm->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 /* USE_LRWAN_1_1_X_CRYPTO == 1 */ + + // 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; +} + +#if( USE_LRWAN_1_1_X_CRYPTO == 1 ) +LoRaMacCryptoStatus_t LoRaMacCryptoPrepareReJoinType1( LoRaMacMessageReJoinType1_t* macMsg ) +{ + if( macMsg == 0 ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + + // Check for RJcount1 overflow + if( CryptoNvm->FCntList.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 + CryptoNvm->FCntList.RJcount1++; + + return LORAMAC_CRYPTO_SUCCESS; +} + +LoRaMacCryptoStatus_t LoRaMacCryptoPrepareReJoinType0or2( LoRaMacMessageReJoinType0or2_t* macMsg ) +{ + if( macMsg == 0 ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + + // Check for RJcount0 overflow + if( 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; + } + + // Re-serialize message to add the MIC + if( LoRaMacSerializerReJoinType0or2( macMsg ) != LORAMAC_SERIALIZER_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SERIALIZER; + } + + // Increment RJcount0 + RJcount0++; + + return LORAMAC_CRYPTO_SUCCESS; +} +#endif /* USE_LRWAN_1_1_X_CRYPTO == 1 */ + +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; + uint8_t decJoinAccept[LORAMAC_JOIN_ACCEPT_FRAME_MAX_SIZE] = { 0 }; + uint8_t versionMinor = 0; + uint16_t nonce = CryptoNvm->DevNonce; + + // Nonce selection depending on JoinReqType + // JOIN_REQ : CryptoNvm->DevNonce + // REJOIN_REQ_0 : RJcount0 + // REJOIN_REQ_1 : CryptoCtx.RJcount1 + // REJOIN_REQ_2 : RJcount0 + if( joinReqType == JOIN_REQ ) + { + // Nothing to be done + } +#if( USE_LRWAN_1_1_X_CRYPTO == 1 ) + else + { + // 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 ) ) + { + nonce = RJcount0; + } + else + { + nonce = CryptoNvm->FCntList.RJcount1; + } + } +#endif /* USE_LRWAN_1_1_X_CRYPTO == 1 */ + + if( SecureElementProcessJoinAccept( joinReqType, joinEUI, nonce, macMsg->Buffer, + macMsg->BufSize, decJoinAccept, + &versionMinor ) != SECURE_ELEMENT_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC; + } + + memcpy1( macMsg->Buffer, decJoinAccept, macMsg->BufSize ); + + // Parse the message + if( LoRaMacParserJoinAccept( macMsg ) != LORAMAC_PARSER_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_PARSER; + } + + uint32_t currentJoinNonce; + + currentJoinNonce = ( uint32_t )macMsg->JoinNonce[0]; + currentJoinNonce |= ( ( uint32_t )macMsg->JoinNonce[1] << 8 ); + currentJoinNonce |= ( ( uint32_t )macMsg->JoinNonce[2] << 16 ); + +#if( USE_JOIN_NONCE_COUNTER_CHECK == 1 ) + // Check if the JoinNonce is greater as the previous one + if( currentJoinNonce > CryptoNvm->JoinNonce ) +#else + // Check if the JoinNonce is different from the previous one + if( currentJoinNonce != CryptoNvm->JoinNonce ) +#endif /* USE_JOIN_NONCE_COUNTER_CHECK */ + { + CryptoNvm->JoinNonce = currentJoinNonce; + } + else + { + return LORAMAC_CRYPTO_FAIL_JOIN_NONCE; + } + + // Derive lifetime keys + retval = LoRaMacCryptoDeriveMcRootKey( versionMinor, APP_KEY ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + + retval = LoRaMacCryptoDeriveMcKEKey( MC_ROOT_KEY ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + +#if( USE_LRWAN_1_1_X_CRYPTO == 1 ) + if( versionMinor == 1 ) + { + // Operating in LoRaWAN 1.1.x mode + + retval = DeriveSessionKey11x( F_NWK_S_INT_KEY, currentJoinNonce, joinEUI, nonce ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + + retval = DeriveSessionKey11x( S_NWK_S_INT_KEY, currentJoinNonce, joinEUI, nonce ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + + retval = DeriveSessionKey11x( NWK_S_ENC_KEY, currentJoinNonce, joinEUI, nonce ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + + retval = DeriveSessionKey11x( APP_S_KEY, currentJoinNonce, joinEUI, nonce ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + } + else +#endif /* USE_LRWAN_1_1_X_CRYPTO == 1 */ + { + // Operating in LoRaWAN 1.0.x mode + + uint32_t netID; + + netID = ( uint32_t )macMsg->NetID[0]; + netID |= ( ( uint32_t )macMsg->NetID[1] << 8 ); + netID |= ( ( uint32_t )macMsg->NetID[2] << 16 ); + + retval = DeriveSessionKey10x( APP_S_KEY, currentJoinNonce, netID, nonce ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + + /* ST_WORKAROUND_BEGIN: integrate 1.1.x keys only if required */ +#if ( USE_LRWAN_1_1_X_CRYPTO == 1 ) + retval = DeriveSessionKey10x( NWK_S_ENC_KEY, currentJoinNonce, netID, nonce ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + + retval = DeriveSessionKey10x( F_NWK_S_INT_KEY, currentJoinNonce, netID, nonce ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + + retval = DeriveSessionKey10x( S_NWK_S_INT_KEY, currentJoinNonce, netID, nonce ); +#else + retval = DeriveSessionKey10x( NWK_S_KEY, currentJoinNonce, netID, nonce ); +#endif /* USE_LRWAN_1_1_X_CRYPTO */ + /* ST_WORKAROUND_END */ + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + } + + // Join-Accept is successfully processed + // Save LoRaWAN specification version + CryptoNvm->LrWanVersion.Fields.Minor = versionMinor; + + // Reset frame counters +#if( USE_LRWAN_1_1_X_CRYPTO == 1 ) + RJcount0 = 0; +#endif /* USE_LRWAN_1_1_X_CRYPTO == 1 */ + CryptoNvm->FCntList.FCntUp = 0; + CryptoNvm->FCntList.FCntDown = FCNT_DOWN_INITAL_VALUE; + CryptoNvm->FCntList.NFCntDown = FCNT_DOWN_INITAL_VALUE; + CryptoNvm->FCntList.AFCntDown = FCNT_DOWN_INITAL_VALUE; + + 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 < CryptoNvm->FCntList.FCntUp ) + { + return LORAMAC_CRYPTO_FAIL_FCNT_SMALLER; + } + + // Encrypt payload + if( macMsg->FPort == 0 ) + { + // Use network session key + /* ST_WORKAROUND_BEGIN: integrate 1.1.x keys only if required */ +#if ( USE_LRWAN_1_1_X_CRYPTO == 1 ) + payloadDecryptionKeyID = NWK_S_ENC_KEY; +#else /* USE_LRWAN_1_1_X_CRYPTO == 0 */ + payloadDecryptionKeyID = NWK_S_KEY; +#endif /* USE_LRWAN_1_1_X_CRYPTO */ + /* ST_WORKAROUND_END */ + } + + if( fCntUp > CryptoNvm->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( CryptoNvm->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 /* USE_LRWAN_1_1_X_CRYPTO */ + } + + // Serialize message + if( LoRaMacSerializerData( macMsg ) != LORAMAC_SERIALIZER_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SERIALIZER; + } + + // Compute mic +#if( USE_LRWAN_1_1_X_CRYPTO == 1 ) + if( CryptoNvm->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 /* USE_LRWAN_1_1_X_CRYPTO */ + { + // Use network session key + /* ST_WORKAROUND_BEGIN: integrate 1.1.x keys only if required */ +#if ( USE_LRWAN_1_1_X_CRYPTO == 1 ) + payloadDecryptionKeyID = NWK_S_ENC_KEY; +#else /* USE_LRWAN_1_1_X_CRYPTO == 0 */ + payloadDecryptionKeyID = NWK_S_KEY; +#endif /* USE_LRWAN_1_1_X_CRYPTO */ + /* ST_WORKAROUND_END */ + // 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 ), payloadDecryptionKeyID, 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; + } + + CryptoNvm->FCntList.FCntUp = fCntUp; + + 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; + /* ST_WORKAROUND_BEGIN: integrate 1.1.x keys only if required */ +#if ( USE_LRWAN_1_1_X_CRYPTO == 1 ) + KeyIdentifier_t micComputationKeyID = S_NWK_S_INT_KEY; +#else /* USE_LRWAN_1_1_X_CRYPTO == 0 */ + KeyIdentifier_t micComputationKeyID = NWK_S_KEY; +#endif /* USE_LRWAN_1_1_X_CRYPTO */ + /* ST_WORKAROUND_END */ + 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( CryptoNvm->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 + /* ST_WORKAROUND_BEGIN: integrate 1.1.x keys only if required */ +#if ( USE_LRWAN_1_1_X_CRYPTO == 1 ) + payloadDecryptionKeyID = NWK_S_ENC_KEY; +#else /* USE_LRWAN_1_1_X_CRYPTO == 0 */ + payloadDecryptionKeyID = NWK_S_KEY; +#endif /* USE_LRWAN_1_1_X_CRYPTO */ + /* ST_WORKAROUND_END */ + } + 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( CryptoNvm->LrWanVersion.Fields.Minor == 1 ) + { + if( addrID == UNICAST_DEV_ADDR ) + { + // 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( uint8_t versionMinor, KeyIdentifier_t keyID ) +{ + // Prevent other keys than AppKey + if( keyID != APP_KEY ) + { + return LORAMAC_CRYPTO_ERROR_INVALID_KEY_ID; + } + uint8_t compBase[16] = { 0 }; + + if( versionMinor == 1 ) + { + compBase[0] = 0x20; + } + if( SecureElementDeriveAndStoreKey( 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( 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( compBaseAppS, curItem->RootKey, curItem->AppSkey ) != SECURE_ELEMENT_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC; + } + + if( SecureElementDeriveAndStoreKey( compBaseNwkS, curItem->RootKey, curItem->NwkSkey ) != SECURE_ELEMENT_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC; + } + + return LORAMAC_CRYPTO_SUCCESS; +} diff --git a/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacCrypto.h b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacCrypto.h new file mode 100644 index 0000000..784db34 --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacCrypto.h @@ -0,0 +1,346 @@ +/*! + * \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__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "../Utilities/utilities.h" +#include "LoRaMacTypes.h" +#include "LoRaMacMessageTypes.h" +#include "LoRaMacCryptoNvm.h" +#include "LoRaMacVersion.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 + */ +#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) +#define USE_RANDOM_DEV_NONCE 1 +#elif (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) +#define USE_RANDOM_DEV_NONCE 0 +#endif /* LORAMAC_VERSION */ + +/*! + * Indicates if JoinNonce is counter based and requires to be checked + */ +#define USE_JOIN_NONCE_COUNTER_CHECK 0 + +/*! + * Initial value of the frame counters + */ +#define FCNT_DOWN_INITAL_VALUE 0xFFFFFFFF + +/*! + * LoRaMac Crypto 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, +#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) + /*! + * MAX_GAP_FCNT check failed + */ + LORAMAC_CRYPTO_FAIL_MAX_GAP_FCNT, +#endif /* LORAMAC_VERSION */ + /*! + * 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] nvm - Pointer to the non-volatile memory data + * structure. + * \retval - Status of the operation + */ +LoRaMacCryptoStatus_t LoRaMacCryptoInit( LoRaMacCryptoNvmData_t* nvm ); + +/*! + * 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 ); + +#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) +/*! + * 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 ); +#elif (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) +/*! + * Returns updated fCntID downlink counter value. + * + * \param [in] fCntID - Frame counter identifier + * \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, uint32_t frameFcnt, uint32_t* currentDown ); +#endif /* LORAMAC_VERSION */ + +/*! + * 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 AppKey. + * + * 1.0.x + * McRootKey = aes128_encrypt(AppKey, 0x00 | pad16) + * + * 1.1.x + * McRootKey = aes128_encrypt(AppKey, 0x20 | pad16) + * + * \param [in] versionMinor - LoRaWAN specification minor version to be used. + * \param [in] keyID - Key identifier of the root key to use to perform the derivation ( AppKey ) + * \retval - Status of the operation + */ +LoRaMacCryptoStatus_t LoRaMacCryptoDeriveMcRootKey( uint8_t versionMinor, 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 */ + +#ifdef __cplusplus +} +#endif + +#endif // __LORAMAC_CRYPTO_H__ diff --git a/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacCryptoNvm.h b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacCryptoNvm.h new file mode 100644 index 0000000..b7ac46d --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacCryptoNvm.h @@ -0,0 +1,122 @@ +/*! + * \file LoRaMacCryptoNvm.h + * + * \brief LoRa MAC layer cryptographic NVM data. + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * addtogroup LORAMAC + * \{ + * + */ +#ifndef __LORAMAC_CRYPTO_NVM_H__ +#define __LORAMAC_CRYPTO_NVM_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "../Utilities/utilities.h" +#include "LoRaMacTypes.h" + +/*! + * 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 counters + */ + uint32_t McFCntDown[LORAMAC_MAX_MC_CTX]; +#if( USE_LRWAN_1_1_X_CRYPTO == 1 ) + /*! + * RJcount1 is a counter incremented with every Rejoin request Type 1 frame transmitted. + */ + uint16_t RJcount1; +#endif +}FCntList_t; + +/*! + * LoRaMac Crypto Non Volatile Context structure + */ +typedef struct sLoRaMacCryptoNvmData +{ + /*! + * 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; + /*! + * LastDownFCnt stores the information which frame counter was used to + * decrypt the last frame. This information is needed to compute ConfFCnt in + * B1 block for the MIC. + */ + uint32_t LastDownFCnt; + /*! + * CRC32 value of the Crypto data structure. + */ + uint32_t Crc32; +}LoRaMacCryptoNvmData_t; + +/*! \} addtogroup LORAMAC */ + +#ifdef __cplusplus +} +#endif + +#endif // __LORAMAC_CRYPTO_NVM_H__ diff --git a/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacHeaderTypes.h b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacHeaderTypes.h new file mode 100644 index 0000000..fedbe99 --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacHeaderTypes.h @@ -0,0 +1,327 @@ +/*! + * \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__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + +/*! MAC header field size */ +#define LORAMAC_MHDR_FIELD_SIZE 1 + +/*! ReJoinType field size */ +#define LORAMAC_JOIN_TYPE_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 + +/*! End-device nonce field size */ +#define LORAMAC_DEV_NONCE_FIELD_SIZE 2 + +/*! Join-server nonce field size */ +#define LORAMAC_JOIN_NONCE_FIELD_SIZE 3 + +/*! RJcount0 field size */ +#define LORAMAC_RJCOUNT_0_FIELD_SIZE 2 + +/*! RJcount1 field size */ +#define LORAMAC_RJCOUNT_1_FIELD_SIZE 2 + +/*! Network ID field size */ +#define LORAMAC_NET_ID_FIELD_SIZE 3 + +/*! Device address field size */ +#define LORAMAC_DEV_ADDR_FIELD_SIZE 4 + +/*! DLSettings field size */ +#define LORAMAC_DL_SETTINGS_FIELD_SIZE 1 + +/*! RxDelay field size */ +#define LORAMAC_RX_DELAY_FIELD_SIZE 1 + +/*! CFList field size */ +#define LORAMAC_CF_LIST_FIELD_SIZE 16 + +/*! FHDR Device address field size */ +#define LORAMAC_FHDR_DEV_ADDR_FIELD_SIZE LORAMAC_DEV_ADDR_FIELD_SIZE + +/*! 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 + +/*! Port field size */ +#define LORAMAC_F_PORT_FIELD_SIZE 1 + +/*! Port field size */ +#define LORAMAC_MAC_PAYLOAD_FIELD_MAX_SIZE 242 + +/*! MIC field size */ +#define LORAMAC_MIC_FIELD_SIZE 4 + +/*! + * JoinRequest frame size + * + * MHDR(1) + JoinEUI(8) + DevEUI(8) + DevNonce(2) + MIC(4) + */ +#define LORAMAC_JOIN_REQ_MSG_SIZE ( LORAMAC_MHDR_FIELD_SIZE + LORAMAC_JOIN_EUI_FIELD_SIZE + \ + LORAMAC_DEV_EUI_FIELD_SIZE + LORAMAC_DEV_NONCE_FIELD_SIZE + \ + LORAMAC_MIC_FIELD_SIZE ) + +/*! + * ReJoinRequest type 1 frame size + * + * MHDR(1) + ReJoinType(1) + JoinEUI(8) + DevEUI(8) + RJcount1(2) + MIC(4) + */ +#define LORAMAC_RE_JOIN_1_MSG_SIZE ( LORAMAC_MHDR_FIELD_SIZE + LORAMAC_JOIN_TYPE_FIELD_SIZE + \ + LORAMAC_JOIN_EUI_FIELD_SIZE + LORAMAC_DEV_EUI_FIELD_SIZE + \ + LORAMAC_RJCOUNT_1_FIELD_SIZE + \ + LORAMAC_MIC_FIELD_SIZE ) + +/*! + * ReJoinRequest type 0 or 2 frame size + * + * MHDR(1) + ReJoinType(1) + NetID(3) + DevEUI(8) + RJcount0(2) + MIC(4) + */ +#define LORAMAC_RE_JOIN_0_2_MSG_SIZE ( LORAMAC_MHDR_FIELD_SIZE + LORAMAC_JOIN_TYPE_FIELD_SIZE + \ + LORAMAC_NET_ID_FIELD_SIZE + LORAMAC_DEV_EUI_FIELD_SIZE + \ + LORAMAC_RJCOUNT_0_FIELD_SIZE + \ + LORAMAC_MIC_FIELD_SIZE ) + +/*! + * JoinAccept frame minimum size + * + * MHDR(1) + AppNonce(3) + NetID(3) + DevAddr(4) + DLSettings(1) + RxDelay(1) + MIC(4) + */ +#define LORAMAC_JOIN_ACCEPT_FRAME_MIN_SIZE ( LORAMAC_MHDR_FIELD_SIZE + LORAMAC_JOIN_NONCE_FIELD_SIZE + \ + LORAMAC_NET_ID_FIELD_SIZE + LORAMAC_DEV_ADDR_FIELD_SIZE + \ + LORAMAC_DL_SETTINGS_FIELD_SIZE + LORAMAC_RX_DELAY_FIELD_SIZE + \ + LORAMAC_MIC_FIELD_SIZE ) + +/*! + * JoinAccept frame maximum size + * + * MHDR(1) + AppNonce(3) + NetID(3) + DevAddr(4) + DLSettings(1) + RxDelay(1) + CFList(16) + MIC(4) + */ +#define LORAMAC_JOIN_ACCEPT_FRAME_MAX_SIZE ( LORAMAC_MHDR_FIELD_SIZE + LORAMAC_JOIN_NONCE_FIELD_SIZE + \ + LORAMAC_NET_ID_FIELD_SIZE + LORAMAC_DEV_ADDR_FIELD_SIZE + \ + LORAMAC_DL_SETTINGS_FIELD_SIZE + LORAMAC_RX_DELAY_FIELD_SIZE + \ + LORAMAC_CF_LIST_FIELD_SIZE + LORAMAC_MIC_FIELD_SIZE ) + +/*! + * MIC computation offset + * \remark required for 1.1.x support + */ +#define JOIN_ACCEPT_MIC_COMPUTATION_OFFSET \ + ( LORAMAC_MHDR_FIELD_SIZE + LORAMAC_JOIN_TYPE_FIELD_SIZE + LORAMAC_JOIN_EUI_FIELD_SIZE + \ + LORAMAC_DEV_NONCE_FIELD_SIZE ) + +/*! + * FRMPayload overhead to be used when setting the Radio.SetMaxPayloadLength + * + * Overhead to be used when setting the Radio.SetMaxPayloadLength in RxWindowSetup function. + * + * MHDR(1) + FHDR(7) + Port(1) + MIC(4) + * + * Maximum PHYPayload = MaxPayloadOfDatarate + LORAMAC_FRAME_PAYLOAD_OVERHEAD_SIZE + */ +#define LORAMAC_FRAME_PAYLOAD_OVERHEAD_SIZE ( LORAMAC_MHDR_FIELD_SIZE + ( LORAMAC_FHDR_DEV_ADDR_FIELD_SIZE + \ + LORAMAC_FHDR_F_CTRL_FIELD_SIZE + LORAMAC_FHDR_F_CNT_FIELD_SIZE ) + \ + LORAMAC_F_PORT_FIELD_SIZE + LORAMAC_MIC_FIELD_SIZE ) + +/*! + * FRMPayload minimum size + * + * MHDR(1) + FHDR(7) + MIC(4) + */ +#define LORAMAC_FRAME_PAYLOAD_MIN_SIZE ( LORAMAC_MHDR_FIELD_SIZE + ( LORAMAC_FHDR_DEV_ADDR_FIELD_SIZE + \ + LORAMAC_FHDR_F_CTRL_FIELD_SIZE + LORAMAC_FHDR_F_CNT_FIELD_SIZE ) + \ + LORAMAC_MIC_FIELD_SIZE ) +/*! + * FRMPayload maximum possible size + * + * MHDR(1) + FHDR(7) + Port(1) + MACPayload(242) + MIC(4) + */ +#define LORAMAC_FRAME_PAYLOAD_MAX_SIZE ( LORAMAC_MHDR_FIELD_SIZE + ( LORAMAC_FHDR_DEV_ADDR_FIELD_SIZE + \ + LORAMAC_FHDR_F_CTRL_FIELD_SIZE + LORAMAC_FHDR_F_CNT_FIELD_SIZE ) + \ + LORAMAC_F_PORT_FIELD_SIZE + LORAMAC_MAC_PAYLOAD_FIELD_MAX_SIZE + \ + LORAMAC_MIC_FIELD_SIZE ) + +/*! + * 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 */ + +#ifdef __cplusplus +} +#endif + +#endif // __LORAMAC_HEADER_TYPES_H__ diff --git a/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacInterfaces.h b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacInterfaces.h new file mode 100644 index 0000000..5aee09f --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacInterfaces.h @@ -0,0 +1,3029 @@ +/*! + * \file LoRaMacInterfaces.h + * + * \brief LoRa MAC layer external types definition + * + * \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. + * \{ + */ +/** + ****************************************************************************** + * + * Portions COPYRIGHT 2020 STMicroelectronics + * + * @file LoRaMacInterfaces.h + * @author MCD Application Team + * @brief LoRa MAC layer external types definition + ****************************************************************************** + */ +#ifndef __LORAMAC_INTERFACES_H__ +#define __LORAMAC_INTERFACES_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "LoRaMacTypes.h" +#include "LoRaMacVersion.h" +#include "Region/RegionNvm.h" +#include "LoRaMacCryptoNvm.h" +#include "secure-element-nvm.h" +#include "LoRaMacClassBNvm.h" +#include "../../../BSP/lorawan_conf.h" + +/*! + * 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 + +/*! + * 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 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; + +/*! + * Global MAC layer parameters + */ +typedef struct sLoRaMacParams +{ + /*! + * 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; + /* ST_WORKAROUND_BEGIN: Keep repeater feature */ + /*! + * Indicates if the node supports repeaters + */ + bool RepeaterSupport; + /* ST_WORKAROUND_END */ + /*! + * Default response timeout for class b and class c confirmed downlink frames in milli seconds. + */ + uint32_t RxBCTimeout; +}LoRaMacParams_t; + +/* ST_WORKAROUND_BEGIN: Move Rx Status from McpsIndication_t as generic struct */ +/*! + * Global RX layer status + */ +typedef struct sLoRaMacRxStatus +{ + /* + * Rssi of the received packet + */ + int16_t Rssi; + /* + * Snr of the received packet + */ + int8_t Snr; + /* + * Holds the current rx window slot + */ + LoRaMacRxSlot_t RxSlot; +}LoRaMacRxStatus_t; +/* ST_WORKAROUND_END */ + +/*! + * 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; +#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + /*! + * Param + * | Bits | 7:2 | 1:0 | + * |-------|-----|------| + * | Param | RFU | Prec | + * + * Prec field is used to interpret the precision of beacon's transmit time + * as 10^(-6+prec) and the default value is 0. + * RFU will be set to Zero and Prec can take values between 0:3. + */ + uint8_t Param; +#endif /* LORAMAC_VERSION */ + /*! + * 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, +#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) + /*! + * The node has lost MAX_FCNT_GAP or more frames. + */ + LORAMAC_EVENT_INFO_STATUS_DOWNLINK_TOO_MANY_FRAMES_LOSS, +#endif /* LORAMAC_VERSION */ + /*! + * An address error occurred + */ + LORAMAC_EVENT_INFO_STATUS_ADDRESS_FAIL, + /*! + * Message integrity check failure + */ + LORAMAC_EVENT_INFO_STATUS_MIC_FAIL, + /*! + * Not used + */ + LORAMAC_EVENT_INFO_STATUS_MULTICAST_FAIL, + /*! + * The node has received a beacon. Acquisition is no longer pending + */ + LORAMAC_EVENT_INFO_STATUS_BEACON_LOCKED, + /*! + * The node has not received a beacon for at least CLASSB_MAX_BEACON_LESS_PERIOD + */ + LORAMAC_EVENT_INFO_STATUS_BEACON_LOST, + /*! + * The node has not received a beacon after the CLASSB_BEACON_INTERVAL + */ + 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; + /*! + * Indicate if a NVM handling is required + */ + uint8_t NvmHandle : 1; + }Bits; +}LoRaMacFlags_t; + +/*! + * LoRaMAC region enumeration + */ +typedef enum eLoRaMacRegion +{ + /*! + * AS band on 923MHz + */ + LORAMAC_REGION_AS923, + /*! + * Australian band on 915MHz + */ + LORAMAC_REGION_AU915, + /*! + * Chinese band on 470MHz + */ + LORAMAC_REGION_CN470, + /*! + * Chinese band on 779MHz + */ + LORAMAC_REGION_CN779, + /*! + * European band on 433MHz + */ + LORAMAC_REGION_EU433, + /*! + * European band on 868MHz + */ + LORAMAC_REGION_EU868, + /*! + * South korean band on 920MHz + */ + LORAMAC_REGION_KR920, + /*! + * India band on 865MHz + */ + LORAMAC_REGION_IN865, + /*! + * North american band on 915MHz + */ + LORAMAC_REGION_US915, + /*! + * Russia band on 864MHz + */ + LORAMAC_REGION_RU864, +}LoRaMacRegion_t; + +typedef struct sLoRaMacNvmDataGroup1 +{ + /*! + * Counts the number of missed ADR acknowledgements + */ + uint32_t AdrAckCounter; + /*! + * Last transmission time. + */ + TimerTime_t LastTxDoneTime; + /*! + * Aggregated time off. + */ + TimerTime_t AggregatedTimeOff; + /*! + * Last received Message integrity Code (MIC) + */ + uint32_t LastRxMic; + /*! + * Channels TX power + */ + int8_t ChannelsTxPower; + /*! + * Channels data rate + */ + int8_t ChannelsDatarate; + /*! + * 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; + /*! + * CRC32 value of the MacGroup1 data structure. + */ + uint32_t Crc32; +}LoRaMacNvmDataGroup1_t; + +typedef struct sLoRaMacNvmDataGroup2 +{ + /*! + * LoRaMac region. + */ + LoRaMacRegion_t Region; + /*! + * LoRaMac parameters + */ + LoRaMacParams_t MacParams; + /*! + * LoRaMac default parameters + */ + LoRaMacParams_t MacParamsDefaults; + /*! + * Channels TX power + */ + int8_t ChannelsTxPowerDefault; + /*! + * Channels data rate + */ + int8_t ChannelsDatarateDefault; + /*! + * 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; + /*! + * Maximum duty cycle + * \remark Possibility to shutdown the device. + */ + uint8_t MaxDCycle; + /*! + * Enables/Disables duty cycle management (Test only) + */ + bool DutyCycleOn; +#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + /*! + * Set to true, if the datarate was increased + * with a link adr request. + */ + bool ChannelsDatarateChangedLinkAdrReq; + /*! + * The stack will set this variable to true, if a downlink has been received. + */ + bool DownlinkReceived; + /*! + * Enables/disable FPort 224 processing (certification port) + */ + bool IsCertPortOn; +#endif /* LORAMAC_VERSION */ + /*! + * Aggregated duty cycle management + */ + uint16_t AggregatedDCycle; + /*! + * Stores the time at LoRaMac initialization. + * + * \remark Used for the BACKOFF_DC computation. + */ + SysTime_t InitializationTime; + /*! + * Current LoRaWAN Version + */ + Version_t Version; + /*! + * End-Device network activation + */ + ActivationType_t NetworkActivation; + /*! + * CRC32 value of the MacGroup2 data structure. + */ + uint32_t Crc32; +}LoRaMacNvmDataGroup2_t; + +/*! + * LoRaMAC data structure for non-volatile memory (NVM). + * This structure contains data which must be stored in NVM. + */ +typedef struct sLoRaMacNvmData +{ + /*! + * Parameters related to the crypto layer. Change with every TX/RX + * procedure. + */ + LoRaMacCryptoNvmData_t Crypto; + /*! + * Parameters related to the MAC which change with high probability after + * every TX/RX procedure. + */ + LoRaMacNvmDataGroup1_t MacGroup1; + /*! + * Parameters related to the MAC which do not change very likely with every + * TX/RX procedure. + */ + LoRaMacNvmDataGroup2_t MacGroup2; + /*! + * Parameters related to the secure-element. + */ + SecureElementNvmData_t SecureElement; + /*! + * Parameters related to the regional implementation which change with high + * probability after every TX/RX procedure. + */ + RegionNvmDataGroup1_t RegionGroup1; + /*! + * Parameters related to the regional implementation which do not change + * very likely with every TX/RX procedure. + */ + RegionNvmDataGroup2_t RegionGroup2; + /*! + * Parameters related to class b. + */ + LoRaMacClassBNvmData_t ClassB; +}LoRaMacNvmData_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; + +/*! + * Structure which defines return parameters for requests. + */ +typedef struct sRequestReturnParam +{ + /*! + * This value reports the time in milliseconds which + * an application must wait before its possible to send + * the next uplink. + */ + TimerTime_t DutyCycleWaitTime; +}RequestReturnParam_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; +#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) + /*! + * 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; +#endif /* LORAMAC_VERSION */ +}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; + + /*! + * MCPS-Request return parameters + */ + RequestReturnParam_t ReqReturn; +}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 + */ +#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) + uint8_t NbRetries; +#elif (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + uint8_t NbTrans; +#endif /* LORAMAC_VERSION */ + /*! + * 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; + /*! + * 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; +#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + /*! + * Response timeout for a class b or c device when a + * confirmed downlink has been received. In all other + * cases this variable is 0. + */ + TimerTime_t ResponseTimeout; +#endif /* LORAMAC_VERSION */ +}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 +{ + /*! + * An unknown MLME service + */ + MLME_UNKNOWN, + /*! + * 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, +#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) + /*! + * Sets Tx continuous wave mode (new LoRa-Alliance CC definition) + * + * LoRaWAN end-device certification + */ + MLME_TXCW_1, +#endif /* LORAMAC_VERSION */ + /*! + * 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 +{ + /*! + * LoRaWAN Network End-Device Activation ( ACTIVATION_TYPE_NONE, ACTIVATION_TYPE_ABP or ACTIVATION_TYPE_OTAA ) + * + * Related MIB type: \ref MIB_NETWORK_ACTIVATION + */ + ActivationType_t NetworkActivation; + /*! + * 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) + */ + int8_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; + + /*! + * MLME-Request return parameters + */ + RequestReturnParam_t ReqReturn; +}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; + /*! + * Downlink datarate + */ + uint8_t RxDatarate; + /*! + * The downlink counter value for the received frame + */ + uint32_t DownLinkCounter; + /*! + * 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_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_MIN_TX_DATARATE | YES | NO + * \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_NVM_BKP_CTXS | YES | YES + * \ref MIB_ABP_LORAWAN_VERSION | NO | YES + * \ref MIB_LORAWAN_VERSION | YES | NO + * \ref MIB_IS_CERT_FPORT_ON | YES | YES + * \ref MIB_BEACON_STATE | YES | NO + * + * 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 + * + * 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, + /* ST_WORKAROUND_BEGIN: integrate 1.1.x keys only if required */ +#if ( USE_LRWAN_1_1_X_CRYPTO == 1 ) + /*! + * 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, +#else /* USE_LRWAN_1_1_X_CRYPTO == 0 */ + /*! + * Network session key + */ + MIB_NWK_S_KEY, +#endif /* USE_LRWAN_1_1_X_CRYPTO */ + /* ST_WORKAROUND_END */ + /*! + * 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, + /* ST_WORKAROUND_BEGIN: reduced LORAMAC_MAX_MC_CTX */ +#if ( LORAMAC_MAX_MC_CTX > 0 ) + /*! + * 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, +#endif /* LORAMAC_MAX_MC_CTX > 0 */ +#if ( LORAMAC_MAX_MC_CTX > 1 ) + /*! + * 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, +#endif /* LORAMAC_MAX_MC_CTX > 1 */ +#if ( LORAMAC_MAX_MC_CTX > 2 ) + /*! + * 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, +#endif /* LORAMAC_MAX_MC_CTX > 2 */ +#if ( LORAMAC_MAX_MC_CTX > 3 ) + /*! + * 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, +#endif /* LORAMAC_MAX_MC_CTX > 3 */ + /* ST_WORKAROUND_END */ + /*! + * Set the network type to public or private + * + * LoRaWAN Regional Parameters V1.0.2rB + * + * [true: public network, false: private network] + */ + MIB_PUBLIC_NETWORK, + /* ST_WORKAROUND_BEGIN: Keep repeater feature */ + /*! + * Support the operation with repeaters + * + * LoRaWAN Regional Parameters V1.0.2rB + * + * [true: repeater support enabled, false: repeater support disabled] + */ + MIB_REPEATER_SUPPORT, + /* ST_WORKAROUND_END */ + /*! + * 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, +#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + /*! + * Minimum Data rate of a channel + * + * LoRaWAN Regional Parameters V1.0.2rB + * + * The possible values are region specific. Please refer to \ref DR_0 to \ref DR_15 for details. + */ + MIB_CHANNELS_MIN_TX_DATARATE, +#endif /* LORAMAC_VERSION */ + /*! + * 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, + /*! + * Structure holding pointers to internal backup contexts and its size + */ + MIB_NVM_BKP_CTXS, + /*! + * LoRaWAN MAC layer operating version when activated by ABP. + */ + MIB_ABP_LORAWAN_VERSION, + /*! + * LoRaWAN MAC and regional parameter version. + */ + MIB_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, + /*! + * Default response timeout for class b and class c confirmed downlink frames in milli seconds. + */ + MIB_RXB_C_TIMEOUT, +#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + /*! + * LoRaWAN certification FPort handling state (ON/OFF) + */ + MIB_IS_CERT_FPORT_ON, +#endif /* LORAMAC_VERSION */ + /* ST_WORKAROUND_BEGIN: Retrieve the Beacon state */ + /*! + * Beacon state + */ + MIB_BEACON_STATE, + /* ST_WORKAROUND_END */ +}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 EUI + * + * Related MIB type: \ref MIB_DEV_EUI + */ + uint8_t* DevEui; + /*! + * LoRaWAN Join server EUI + * + * 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 + * + * Related MIB type: \ref MIB_APP_KEY + */ + uint8_t* AppKey; + /*! + * Network root key + * + * Related MIB type: \ref MIB_NWK_KEY + */ + uint8_t* NwkKey; + /* ST_WORKAROUND_BEGIN: integrate 1.1.x keys only if required */ +#if ( USE_LRWAN_1_1_X_CRYPTO == 1 ) + /*! + * 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; +#else /* USE_LRWAN_1_1_X_CRYPTO == 0 */ + /*! + * Network session key + * + * Related MIB type: \ref MIB_NWK_S_KEY + */ + uint8_t* NwkSKey; +#endif /* USE_LRWAN_1_1_X_CRYPTO */ + /* ST_WORKAROUND_END */ + /*! + * 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; + /* ST_WORKAROUND_BEGIN: Keep repeater feature */ + /*! + * Enable or disable repeater support + * + * Related MIB type: \ref MIB_REPEATER_SUPPORT + */ + bool EnableRepeaterSupport; + /* ST_WORKAROUND_END */ + /*! + * 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; +#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + /*! + * Channels minimum tx data rate + * + * Related MIB type: \ref MIB_CHANNELS_MIN_TX_DATARATE + */ + int8_t ChannelsMinTxDatarate; +#endif /* LORAMAC_VERSION */ + /*! + * 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; + /*! + * Returns a pointer to the structure holding all data which shall be stored + * in the NVM. + * + * Related MIB type: \ref MIB_NVM_CTXS + */ + LoRaMacNvmData_t* Contexts; + /*! + * Returns a pointer to the backup structure holding all data which shall be stored + * in the NVM. + * + * Related MIB type: \ref MIB_NVM_CTXS + */ + LoRaMacNvmData_t* BackupContexts; + /*! + * LoRaWAN MAC layer operating version when activated by ABP. + * + * Related MIB type: \ref MIB_ABP_LORAWAN_VERSION + */ + Version_t AbpLrWanVersion; + /*! + * LoRaWAN MAC regional parameter version. + * + * Related MIB type: \ref MIB_LORAWAN_VERSION + */ + struct sLrWanVersion + { + Version_t LoRaWan; + Version_t LoRaWanRegion; + }LrWanVersion; + /*! + * 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; + /*! + * Default response timeout for class b and class c confirmed + * + * Related MIB type: \ref MIB_RXB_C_TIMEOUT + */ + uint32_t RxBCTimeout; +#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + /*! + * LoRaWAN certification FPort handling state (ON/OFF) + * + * Related MIB type: \ref MIB_IS_CERT_FPORT_ON + */ + bool IsCertPortOn; +#endif /* LORAMAC_VERSION */ + /* ST_WORKAROUND_BEGIN: Retrieve the Beacon state */ + /*! + * State of the beaconing mechanism + * + * Related MIB type: \ref MIB_BEACON_STATE + */ + BeaconState_t BeaconState; + /* ST_WORKAROUND_END */ +}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, + /*! + * An MCPS or MLME request can return this status. In this case, + * the MAC cannot send the frame, as the duty cycle limits all + * available bands. When a request returns this value, the + * variable "DutyCycleWaitTime" in "ReqReturn" of the input + * parameters contains the remaining time to wait. If the + * value is constant and does not change, the expected time + * on air for this frame is exceeding the maximum permitted + * time according to the duty cycle time period, defined + * in Region.h, DUTY_CYCLE_TIME_PERIOD. By default this time + * is 1 hour, and a band with 1% duty cycle is then allowed + * to use an air time of 36 seconds. + */ + LORAMAC_STATUS_DUTYCYCLE_RESTRICTED, + /*! + * No active channel is available to send a request + */ + LORAMAC_STATUS_NO_CHANNEL_FOUND, + /*! + * Even if one or more channels are available according to the channel plan, + * no free channel was found during the LBT procedure + */ + LORAMAC_STATUS_NO_FREE_CHANNEL_FOUND, + /*! + * Next request cannot be sent because the RX window is required by the beacon + */ + LORAMAC_STATUS_BUSY_BEACON_RESERVED_TIME, + /*! + * Next request cannot be sent because the RX window is required by the ping slot + */ + LORAMAC_STATUS_BUSY_PING_SLOT_WINDOW_TIME, + /*! + * Next beacon will be received during the next uplink + */ + 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, + /*! + * NVM Data inconsistent + */ + LORAMAC_STATUS_NVM_DATA_INCONSISTENT, + /*! + * Undefined error occurred + */ + LORAMAC_STATUS_ERROR +}LoRaMacStatus_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, LoRaMacRxStatus_t* RxStatus ); + /*! + * \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, LoRaMacRxStatus_t* RxStatus ); +}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 ); + /* ST_WORKAROUND_BEGIN: Return temperature into int16_t instead float */ + /*! + * \brief Measures the temperature level + * + * \retval Temperature level + */ + int16_t ( *GetTemperatureLevel )( void ); + /* ST_WORKAROUND_END */ + /* ST_WORKAROUND_BEGIN: Add unique ID callback */ + /*! + * \brief Get the board 64 bits unique ID + * + * \param [out] id unique + */ + void ( *GetUniqueId )(uint8_t *id); + /* ST_WORKAROUND_END */ + /*! + * \brief Will be called when an attribute has changed in one of the context. + * + * \param notifyFlags Bitmap that contains the modules which changed. + * Refer to \ref LoRaMacNvmData_t. + */ + void ( *NvmDataChange )( uint16_t notifyFlags ); + /*! + *\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; + +/*! \} defgroup LORAMAC */ + +#ifdef __cplusplus +} +#endif + +#endif // __LORAMAC_INTERFACES_H__ diff --git a/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacMessageTypes.h b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacMessageTypes.h new file mode 100644 index 0000000..af7411a --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacMessageTypes.h @@ -0,0 +1,300 @@ +/*! + * \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__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "LoRaMacHeaderTypes.h" + +/*! + * 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 */ + +#ifdef __cplusplus +} +#endif + +#endif // __LORAMAC_MESSAGE_TYPES_H__ diff --git a/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacParser.c b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacParser.c new file mode 100644 index 0000000..5283f03 --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacParser.c @@ -0,0 +1,130 @@ +/*! + * \file LoRaMacParser.c + * + * \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 ) + */ +#include "LoRaMacParser.h" +#include "../Utilities/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_CF_LIST_FIELD_SIZE ) + { + memcpy1( macMsg->CFList, &macMsg->Buffer[bufItr], LORAMAC_CF_LIST_FIELD_SIZE ); + bufItr = bufItr + LORAMAC_CF_LIST_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/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacParser.h b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacParser.h new file mode 100644 index 0000000..1aca285 --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacParser.h @@ -0,0 +1,93 @@ +/*! + * \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__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#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 */ + +#ifdef __cplusplus +} +#endif + +#endif // __LORAMAC_PARSER_H__ + diff --git a/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacSerializer.c b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacSerializer.c new file mode 100644 index 0000000..b901f91 --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacSerializer.c @@ -0,0 +1,195 @@ +/*! + * \file LoRaMacSerializer.c + * + * \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 ) + */ +#include "LoRaMacSerializer.h" +#include "../Utilities/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_ADDR_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/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacSerializer.h b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacSerializer.h new file mode 100644 index 0000000..3d1dc6d --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacSerializer.h @@ -0,0 +1,108 @@ +/*! + * \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__ + +#ifdef __cplusplus +extern "C" +{ +#endif +#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 */ + +#ifdef __cplusplus +} +#endif + +#endif // __LORAMAC_SERIALIZER_H__ + diff --git a/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacTest.h b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacTest.h new file mode 100644 index 0000000..5d05d27 --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacTest.h @@ -0,0 +1,60 @@ +/*! + * \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__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*! + * \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 */ + +#ifdef __cplusplus +} +#endif + +#endif // __LORAMACTEST_H__ diff --git a/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacTypes.h b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacTypes.h new file mode 100644 index 0000000..de99537 --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacTypes.h @@ -0,0 +1,861 @@ +/*! + * \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 + * \{ + * + */ +/** + ****************************************************************************** + * + * Portions COPYRIGHT 2020 STMicroelectronics + * + * @file LoRaMacTypes.h + * @author MCD Application Team + * @brief LoRa MAC layer internal types definition. Please do not include in application sources. + ****************************************************************************** + */ +#ifndef __LORAMAC_TYPES_H__ +#define __LORAMAC_TYPES_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include +#include +#include "../../../BSP/timer.h" +#include "../../../BSP/systime.h" +#include "LoRaMacVersion.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 + +/*! + * Maximum number of multicast context + */ +#define LORAMAC_MAX_MC_CTX 1 /* ST_WORKAROUND: reduced LORAMAC_MAX_MC_CTX */ + +/*! + * 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, + /* ST_WORKAROUND_BEGIN: reduced LORAMAC_MAX_MC_CTX */ +#if ( LORAMAC_MAX_MC_CTX > 0 ) + /*! + * Multicast downlink counter for index 0 + */ + MC_FCNT_DOWN_0, +#endif /* LORAMAC_MAX_MC_CTX > 0 */ +#if ( LORAMAC_MAX_MC_CTX > 1 ) + /*! + * Multicast downlink counter for index 1 + */ + MC_FCNT_DOWN_1, +#endif /* LORAMAC_MAX_MC_CTX > 1 */ +#if ( LORAMAC_MAX_MC_CTX > 2 ) + /*! + * Multicast downlink counter for index 2 + */ + MC_FCNT_DOWN_2, +#endif /* LORAMAC_MAX_MC_CTX > 2 */ +#if ( LORAMAC_MAX_MC_CTX > 3 ) + /*! + * Multicast downlink counter for index 3 + */ + MC_FCNT_DOWN_3, +#endif /* LORAMAC_MAX_MC_CTX > 3 */ + /* ST_WORKAROUND_BEGIN: reduced LORAMAC_MAX_MC_CTX */ +}FCntIdentifier_t; + +/*! + * LoRaMac Key identifier + */ +typedef enum eKeyIdentifier +{ + /*! + * Application root key + */ + APP_KEY = 0, + /*! + * Network root key + */ + NWK_KEY, +#if ( USE_LRWAN_1_1_X_CRYPTO == 1 ) /* ST_WORKAROUND: integrate 1.1.x keys only if required */ + /*! + * 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, +#else /* USE_LRWAN_1_1_X_CRYPTO == 0 */ + /*! + * Network session key + */ + NWK_S_KEY, +#endif /* USE_LRWAN_1_1_X_CRYPTO */ + /*! + * Application session key + */ + APP_S_KEY, + /*! + * Multicast root key + */ + MC_ROOT_KEY, + /*! + * Multicast key encryption key + */ + MC_KE_KEY = LORAMAC_CRYPTO_MULTICAST_KEYS, + /* ST_WORKAROUND_BEGIN: reduced LORAMAC_MAX_MC_CTX */ +#if ( LORAMAC_MAX_MC_CTX > 0 ) + /*! + * 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, +#endif /* LORAMAC_MAX_MC_CTX > 0 */ +#if ( LORAMAC_MAX_MC_CTX > 1 ) + /*! + * 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, +#endif /* LORAMAC_MAX_MC_CTX > 1 */ +#if ( LORAMAC_MAX_MC_CTX > 2 ) + /*! + * 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, +#endif /* LORAMAC_MAX_MC_CTX > 2 */ +#if ( LORAMAC_MAX_MC_CTX > 3 ) + /*! + * 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, +#endif /* LORAMAC_MAX_MC_CTX > 3 */ + /* ST_WORKAROUND_END */ + /*! + * 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 +{ + /* ST_WORKAROUND_BEGIN: reduced LORAMAC_MAX_MC_CTX */ +#if ( LORAMAC_MAX_MC_CTX > 0 ) + /*! + * Multicast Address for index 0 + */ + MULTICAST_0_ADDR = 0, +#endif /* LORAMAC_MAX_MC_CTX > 0 */ +#if ( LORAMAC_MAX_MC_CTX > 1 ) + /*! + * Multicast Address for index 1 + */ + MULTICAST_1_ADDR, +#endif /* LORAMAC_MAX_MC_CTX > 1 */ +#if ( LORAMAC_MAX_MC_CTX > 2 ) + /*! + * Multicast Address for index 2 + */ + MULTICAST_2_ADDR, +#endif /* LORAMAC_MAX_MC_CTX > 2 */ +#if ( LORAMAC_MAX_MC_CTX > 3 ) + /*! + * Multicast Address for index 3 + */ + MULTICAST_3_ADDR, +#endif /* LORAMAC_MAX_MC_CTX > 3 */ + /*! + * Unicast End-device address + */ + UNICAST_DEV_ADDR, + /* ST_WORKAROUND_END */ +}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 +{ + /*! + * Indicate if the multicast channel is being setup remotely or locally. + * Indicates which set of keys are to be used. \ref uMcKeys + */ + bool IsRemotelySetup; + /*! + * 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; + /*! + * Multicast keys + */ + union uMcKeys + { + /*! + * Encrypted multicast key - Used when IsRemotelySetup equals `true`. + * MC_KEY is decrypted and then the session keys ar derived. + */ + uint8_t *McKeyE; + /*! + * Multicast Session keys - Used when IsRemotelySetup equals `false` + */ + struct + { + /*! + * Multicast application session key + */ + uint8_t *McAppSKey; + /*! + * Multicast network session key + */ + uint8_t *McNwkSKey; + }Session; + }McKeys; + /*! + * 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; +#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + /*! + * Set to 1, if the FPending bit is set + */ + uint8_t FPendingSet; +#endif /* LORAMAC_VERSION */ +}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 + */ +#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) + MOTE_MAC_PING_SLOT_FREQ_ANS = 0x11, +#elif (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + MOTE_MAC_PING_SLOT_CHANNEL_ANS = 0x11, +#endif /* LORAMAC_VERSION */ + /*! + * 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; + /*! + * The last time the band has been + * synchronized with the current time + */ + TimerTime_t LastBandUpdateTime; + /*! + * The last time we have assigned the max + * credits for the 24h interval. + */ + TimerTime_t LastMaxCreditAssignTime; + /*! + * Current time credits which are available. This + * is a value in ms + */ + TimerTime_t TimeCredits; + /*! + * Maximum time credits which are available. This + * is a value in ms + */ + TimerTime_t MaxTimeCredits; + /*! + * Set to true when the band is ready for use. + */ + bool ReadyForTransmission; +}Band_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 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; + +/*! + * 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; + +/*! \} defgroup LORAMAC */ + +#ifdef __cplusplus +} +#endif + +#endif // __LORAMAC_TYPES_H__ + diff --git a/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacVersion.h b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacVersion.h new file mode 100644 index 0000000..a5d018b --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/LoRaMacVersion.h @@ -0,0 +1,40 @@ +/** + ****************************************************************************** + * @file LoRaMacVersion.h + * @author MCD Application Team + * @brief Identifies the version of LoRaMAC + ****************************************************************************** + * @attention + * + * Copyright (c) 2020(-2021) STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ +#ifndef __LORAMAC_VERSION_H__ +#define __LORAMAC_VERSION_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "../../../BSP/lorawan_conf.h" + +#ifndef LORAMAC_VERSION +/*! + * @brief LoRaWAN version definition. + * @note TS001-1.0.4 : https://lora-alliance.org/resource_hub/lorawan-104-specification-package/ + */ +#define LORAMAC_VERSION LORAMAC_SPECIFICATION_VERSION +#endif + +#ifdef __cplusplus +} +#endif + +#endif // __LORAMAC_VERSION_H__ diff --git a/src/STM32CubeWL/LoRaWAN/Mac/Region/Region.c b/src/STM32CubeWL/LoRaWAN/Mac/Region/Region.c new file mode 100644 index 0000000..1cb6821 --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/Region/Region.c @@ -0,0 +1,1063 @@ +/*! + * \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 "../LoRaMacInterfaces.h" +#include "RegionVersion.h" + +#if (defined( REGION_VERSION ) && (( REGION_VERSION == 0x01010003 ) || ( REGION_VERSION == 0x02010001 ))) +#else +#error REGION_VERSION not valid +#endif /* REGION_VERSION */ + +// 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_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_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 ); } +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +#define AS923_SET_CONTINUOUS_WAVE( ) AS923_CASE { RegionAS923SetContinuousWave( continuousWave ); break; } +#endif /* REGION_VERSION */ +#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_NEXT_CHANNEL( ) +#define AS923_CHANNEL_ADD( ) +#define AS923_CHANNEL_REMOVE( ) +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +#define AS923_SET_CONTINUOUS_WAVE( ) +#endif /* REGION_VERSION */ +#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_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_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 ); } +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +#define AU915_SET_CONTINUOUS_WAVE( ) AU915_CASE { RegionAU915SetContinuousWave( continuousWave ); break; } +#endif /* REGION_VERSION */ +#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_NEXT_CHANNEL( ) +#define AU915_CHANNEL_ADD( ) +#define AU915_CHANNEL_REMOVE( ) +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +#define AU915_SET_CONTINUOUS_WAVE( ) +#endif /* REGION_VERSION */ +#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_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_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 ); } +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +#define CN470_SET_CONTINUOUS_WAVE( ) CN470_CASE { RegionCN470SetContinuousWave( continuousWave ); break; } +#endif /* REGION_VERSION */ +#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_NEXT_CHANNEL( ) +#define CN470_CHANNEL_ADD( ) +#define CN470_CHANNEL_REMOVE( ) +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +#define CN470_SET_CONTINUOUS_WAVE( ) +#endif /* REGION_VERSION */ +#define CN470_APPLY_DR_OFFSET( ) +#define CN470_RX_BEACON_SETUP( ) +#endif + +#ifdef REGION_CN779 +#include "RegionCN779.h" +#define CN779_CASE case LORAMAC_REGION_CN779: +#define CN779_IS_ACTIVE( ) CN779_CASE { return true; } +#define CN779_GET_PHY_PARAM( ) CN779_CASE { return RegionCN779GetPhyParam( getPhy ); } +#define CN779_SET_BAND_TX_DONE( ) CN779_CASE { RegionCN779SetBandTxDone( txDone ); break; } +#define CN779_INIT_DEFAULTS( ) CN779_CASE { RegionCN779InitDefaults( params ); break; } +#define CN779_VERIFY( ) CN779_CASE { return RegionCN779Verify( verify, phyAttribute ); } +#define CN779_APPLY_CF_LIST( ) CN779_CASE { RegionCN779ApplyCFList( applyCFList ); break; } +#define CN779_CHAN_MASK_SET( ) CN779_CASE { return RegionCN779ChanMaskSet( chanMaskSet ); } +#define CN779_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_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 ); } +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +#define CN779_SET_CONTINUOUS_WAVE( ) CN779_CASE { RegionCN779SetContinuousWave( continuousWave ); break; } +#endif /* REGION_VERSION */ +#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_NEXT_CHANNEL( ) +#define CN779_CHANNEL_ADD( ) +#define CN779_CHANNEL_REMOVE( ) +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +#define CN779_SET_CONTINUOUS_WAVE( ) +#endif /* REGION_VERSION */ +#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_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_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 ); } +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +#define EU433_SET_CONTINUOUS_WAVE( ) EU433_CASE { RegionEU433SetContinuousWave( continuousWave ); break; } +#endif /* REGION_VERSION */ +#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_NEXT_CHANNEL( ) +#define EU433_CHANNEL_ADD( ) +#define EU433_CHANNEL_REMOVE( ) +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +#define EU433_SET_CONTINUOUS_WAVE( ) +#endif /* REGION_VERSION */ +#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_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_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 ); } +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +#define EU868_SET_CONTINUOUS_WAVE( ) EU868_CASE { RegionEU868SetContinuousWave( continuousWave ); break; } +#endif /* REGION_VERSION */ +#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_NEXT_CHANNEL( ) +#define EU868_CHANNEL_ADD( ) +#define EU868_CHANNEL_REMOVE( ) +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +#define EU868_SET_CONTINUOUS_WAVE( ) +#endif /* REGION_VERSION */ +#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_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_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 ); } +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +#define KR920_SET_CONTINUOUS_WAVE( ) KR920_CASE { RegionKR920SetContinuousWave( continuousWave ); break; } +#endif /* REGION_VERSION */ +#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_NEXT_CHANNEL( ) +#define KR920_CHANNEL_ADD( ) +#define KR920_CHANNEL_REMOVE( ) +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +#define KR920_SET_CONTINUOUS_WAVE( ) +#endif /* REGION_VERSION */ +#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_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_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 ); } +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +#define IN865_SET_CONTINUOUS_WAVE( ) IN865_CASE { RegionIN865SetContinuousWave( continuousWave ); break; } +#endif /* REGION_VERSION */ +#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_NEXT_CHANNEL( ) +#define IN865_CHANNEL_ADD( ) +#define IN865_CHANNEL_REMOVE( ) +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +#define IN865_SET_CONTINUOUS_WAVE( ) +#endif /* REGION_VERSION */ +#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_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_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 ); } +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +#define US915_SET_CONTINUOUS_WAVE( ) US915_CASE { RegionUS915SetContinuousWave( continuousWave ); break; } +#endif /* REGION_VERSION */ +#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_NEXT_CHANNEL( ) +#define US915_CHANNEL_ADD( ) +#define US915_CHANNEL_REMOVE( ) +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +#define US915_SET_CONTINUOUS_WAVE( ) +#endif /* REGION_VERSION */ +#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_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_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 ); } +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +#define RU864_SET_CONTINUOUS_WAVE( ) RU864_CASE { RegionRU864SetContinuousWave( continuousWave ); break; } +#endif /* REGION_VERSION */ +#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_NEXT_CHANNEL( ) +#define RU864_CHANNEL_ADD( ) +#define RU864_CHANNEL_REMOVE( ) +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +#define RU864_SET_CONTINUOUS_WAVE( ) +#endif /* REGION_VERSION */ +#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( ); + 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( ); + 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( ); + 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( ); + CN779_INIT_DEFAULTS( ); + EU433_INIT_DEFAULTS( ); + EU868_INIT_DEFAULTS( ); + KR920_INIT_DEFAULTS( ); + IN865_INIT_DEFAULTS( ); + US915_INIT_DEFAULTS( ); + RU864_INIT_DEFAULTS( ); + default: + { + break; + } + } +} + +bool RegionVerify( LoRaMacRegion_t region, VerifyParams_t* verify, PhyAttribute_t phyAttribute ) +{ + switch( region ) + { + AS923_VERIFY( ); + AU915_VERIFY( ); + CN470_VERIFY( ); + CN779_VERIFY( ); + EU433_VERIFY( ); + EU868_VERIFY( ); + KR920_VERIFY( ); + IN865_VERIFY( ); + US915_VERIFY( ); + 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( ); + 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( ); + 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( ); + 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( ); + 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( ); + 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( ); + 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( ); + 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; + } + } +} + +int8_t RegionNewChannelReq( LoRaMacRegion_t region, NewChannelReqParams_t* newChannelReq ) +{ + switch( region ) + { + AS923_NEW_CHANNEL_REQ( ); + AU915_NEW_CHANNEL_REQ( ); + CN470_NEW_CHANNEL_REQ( ); + CN779_NEW_CHANNEL_REQ( ); + EU433_NEW_CHANNEL_REQ( ); + EU868_NEW_CHANNEL_REQ( ); + KR920_NEW_CHANNEL_REQ( ); + IN865_NEW_CHANNEL_REQ( ); + US915_NEW_CHANNEL_REQ( ); + 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( ); + 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; + } + } +} + +int8_t RegionDlChannelReq( LoRaMacRegion_t region, DlChannelReqParams_t* dlChannelReq ) +{ + switch( region ) + { + AS923_DL_CHANNEL_REQ( ); + AU915_DL_CHANNEL_REQ( ); + CN470_DL_CHANNEL_REQ( ); + CN779_DL_CHANNEL_REQ( ); + EU433_DL_CHANNEL_REQ( ); + EU868_DL_CHANNEL_REQ( ); + KR920_DL_CHANNEL_REQ( ); + IN865_DL_CHANNEL_REQ( ); + US915_DL_CHANNEL_REQ( ); + 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( ); + CN779_ALTERNATE_DR( ); + EU433_ALTERNATE_DR( ); + EU868_ALTERNATE_DR( ); + KR920_ALTERNATE_DR( ); + IN865_ALTERNATE_DR( ); + US915_ALTERNATE_DR( ); + RU864_ALTERNATE_DR( ); + default: + { + return 0; + } + } +} + +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( ); + 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( ); + 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( ); + CN779_CHANNEL_REMOVE( ); + EU433_CHANNEL_REMOVE( ); + EU868_CHANNEL_REMOVE( ); + KR920_CHANNEL_REMOVE( ); + IN865_CHANNEL_REMOVE( ); + US915_CHANNEL_REMOVE( ); + RU864_CHANNEL_REMOVE( ); + default: + { + return false; + } + } +} + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +void RegionSetContinuousWave( LoRaMacRegion_t region, ContinuousWaveParams_t* continuousWave ) +{ + switch( region ) + { + AS923_SET_CONTINUOUS_WAVE( ); + AU915_SET_CONTINUOUS_WAVE( ); + CN470_SET_CONTINUOUS_WAVE( ); + CN779_SET_CONTINUOUS_WAVE( ); + EU433_SET_CONTINUOUS_WAVE( ); + EU868_SET_CONTINUOUS_WAVE( ); + KR920_SET_CONTINUOUS_WAVE( ); + IN865_SET_CONTINUOUS_WAVE( ); + US915_SET_CONTINUOUS_WAVE( ); + RU864_SET_CONTINUOUS_WAVE( ); + default: + { + break; + } + } +} +#endif /* REGION_VERSION */ + +uint8_t RegionApplyDrOffset( LoRaMacRegion_t region, uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) +{ + switch( region ) + { + AS923_APPLY_DR_OFFSET( ); + AU915_APPLY_DR_OFFSET( ); + CN470_APPLY_DR_OFFSET( ); + CN779_APPLY_DR_OFFSET( ); + EU433_APPLY_DR_OFFSET( ); + EU868_APPLY_DR_OFFSET( ); + KR920_APPLY_DR_OFFSET( ); + IN865_APPLY_DR_OFFSET( ); + US915_APPLY_DR_OFFSET( ); + 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( ); + 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; + } + } +} + +Version_t RegionGetVersion( void ) +{ + Version_t version; + + version.Value = REGION_VERSION; + + return version; +} + diff --git a/src/STM32CubeWL/LoRaWAN/Mac/Region/Region.h b/src/STM32CubeWL/LoRaWAN/Mac/Region/Region.h new file mode 100644 index 0000000..7ea894c --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/Region/Region.h @@ -0,0 +1,1234 @@ +/*! + * \file Region.h + * + * \brief Region definition. + * + * \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: + * - #REGION_AS923 + * - #REGION_AU915 + * - #REGION_CN470 + * - #REGION_CN779 + * - #REGION_EU433 + * - #REGION_EU868 + * - #REGION_KR920 + * - #REGION_IN865 + * - #REGION_US915 + * - #REGION_RU864 + * + * \{ + */ +#ifndef __REGION_H__ +#define __REGION_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "../../Utilities/utilities.h" +#include "RegionCommon.h" +#include "RegionVersion.h" + +/*! + * Macro to compute bit of a channel index. + */ +#define LC( channelIndex ) ( uint16_t )( 1 << ( channelIndex - 1 ) ) + +/*! + * 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, + /* ST_WORKAROUND_BEGIN: Keep repeater feature */ + /*! + * Maximum payload possible when repeater support is enabled. + */ + PHY_MAX_PAYLOAD_REPEATER, + /* ST_WORKAROUND_END */ + /*! + * 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, +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + /*! + * Maximum frame counter gap. + */ + PHY_MAX_FCNT_GAP, + /*! + * Acknowledgement time out. + */ + PHY_ACK_TIMEOUT, +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + /*! + * Acknowledgement time out. + */ + PHY_RETRANSMIT_TIMEOUT, +#endif /* REGION_VERSION */ + /*! + * 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 number 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 number of channels for the beacon reception. + */ + PHY_BEACON_NB_CHANNELS, + /*! + * The static offset for the downlink channel calculation. + */ + PHY_BEACON_CHANNEL_OFFSET, + /*! + * Ping slot channel frequency. + */ + PHY_PING_SLOT_CHANNEL_FREQ, + /*! + * The datarate of a ping slot channel. + */ + PHY_PING_SLOT_CHANNEL_DR, + /*! + * The number of channels for the ping slot reception. + */ + PHY_PING_SLOT_NB_CHANNELS, + /*! + * The equivalent spreading factor value from datarate + */ + PHY_SF_FROM_DR, + /*! + * The equivalent bandwidth index from datarate + */ + PHY_BW_FROM_DR, +}PhyAttribute_t; + +/*! + * Enumeration of initialization types. + */ +typedef enum eInitType +{ + /*! + * Initializes the regional default settings for the band, + * channel and default channels mask. Some regions also initiate + * other default configurations. In general, this type is intended + * to be called once during the initialization. + */ + INIT_TYPE_DEFAULTS, + /*! + * Resets the channels mask to the default channels. Deactivates + * all other channels. + */ + INIT_TYPE_RESET_TO_DEFAULT_CHANNELS, + /*! + * Activates the default channels. Leaves all other active channels + * active. + */ + INIT_TYPE_ACTIVATE_DEFAULT_CHANNELS +}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; + /*! + * Duty Cycle Period + */ + TimerTime_t DutyCycleTimePeriod; +}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, PHY_SF_FROM_DR, PHY_BW_FROM_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; + /*! + * Specification of the downlink channel. Used in Class B only. + * The parameter is needed for the following queries: + * PHY_BEACON_CHANNEL_FREQ, PHY_PING_SLOT_CHANNEL_FREQ + */ + uint8_t Channel; +}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; + /*! + * Time-on-air of the last transmission. + */ + TimerTime_t LastTxAirTime; + /*! + * Elapsed time since initialization. + */ + SysTime_t ElapsedTimeSinceStartUp; +}SetBandTxDoneParams_t; + +/*! + * Parameter structure for the function RegionInitDefaults. + */ +typedef struct sInitDefaultsParams +{ + /*! + * Pointer to region NVM group1. + */ + void* NvmGroup1; + /*! + * Pointer to region NVM group2. + */ + void* NvmGroup2; +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + /*! + * Pointer to common region band storage. + */ + void* Bands; +#endif /* REGION_VERSION */ + /*! + * Sets the initialization type. + */ + InitType_t Type; +}InitDefaultsParams_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 +{ +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + uint8_t JoinChannel; +#endif /* REGION_VERSION */ + /*! + * 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; + /* ST_WORKAROUND_BEGIN: Keep repeater feature */ + /*! + * Set to true, if a repeater is supported. + */ + bool RepeaterSupport; + /* ST_WORKAROUND_END */ + /*! + * Set to true, if RX should be continuous. + */ + bool RxContinuous; + /*! + * Sets the RX window. + */ + LoRaMacRxSlot_t RxSlot; +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + /*! + * LoRaWAN Network End-Device Activation ( ACTIVATION_TYPE_NONE, ACTIVATION_TYPE_ABP + * or ACTIVATION_TYPE_OTTA ) + * + * Related MIB type: \ref MIB_NETWORK_ACTIVATION + */ + ActivationType_t NetworkActivation; +#endif /* REGION_VERSION */ +}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; +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + /*! + * LoRaWAN Network End-Device Activation ( ACTIVATION_TYPE_NONE, ACTIVATION_TYPE_ABP + * or ACTIVATION_TYPE_OTTA ) + * + * Related MIB type: \ref MIB_NETWORK_ACTIVATION + */ + ActivationType_t NetworkActivation; +#endif /* REGION_VERSION */ +}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 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; + /*! + * Elapsed time since the start of the node. + */ + SysTime_t ElapsedTimeSinceStartUp; + /*! + * Joined Set to true, if the last uplink was a join request + */ + bool LastTxIsJoinRequest; + /*! + * Payload length of the next frame + */ + uint16_t PktLen; +}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; + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +/*! + * 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; +#endif /* REGION_VERSION */ + +/*! + * 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 Verifies a parameter. + * + * \param [in] region LoRaWAN region. + * + * \param [in] verify Pointer to the function parameters. + * + * \param [in] phyAttribute 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. + */ +int8_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. + */ +int8_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 Searches and set the next random available channel + * + * \param [in] region LoRaWAN region. + * + * \param [in] nextChanParams pointer of selected channel parameters + * + * \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 ); + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +/*! + * \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 ); +#endif /* REGION_VERSION */ + +/*! + * \brief Computes new datarate according to the given offset + * + * \param [in] region LoRaWAN region. + * + * \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] region LoRaWAN region. + * + * \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 ); + +/*! + * \brief Gets the version of the regional parameters implementation. + * + * \retval Version of the regional parameters. + */ +Version_t RegionGetVersion( void ); + +/*! \} defgroup REGION */ + +#ifdef __cplusplus +} +#endif + +#endif // __REGION_H__ diff --git a/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionAS923.c b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionAS923.c new file mode 100644 index 0000000..c257d36 --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionAS923.c @@ -0,0 +1,1237 @@ +/*! + * \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 ) +*/ +/** + ****************************************************************************** + * + * Portions COPYRIGHT 2020 STMicroelectronics + * + * @file RegionAS923.c + * @author MCD Application Team + * @brief Region implementation for AS923 + ****************************************************************************** + */ +#include "../../../SubGHz_Phy/radio.h" +#include "RegionAS923.h" + +// Definitions +#define CHANNELS_MASK_SIZE 1 + +#ifndef REGION_AS923_DEFAULT_CHANNEL_PLAN +#define REGION_AS923_DEFAULT_CHANNEL_PLAN CHANNEL_PLAN_GROUP_AS923_1 +#endif + +#if( REGION_AS923_DEFAULT_CHANNEL_PLAN == CHANNEL_PLAN_GROUP_AS923_1 ) + +// Channel plan CHANNEL_PLAN_GROUP_AS923_1 + +#define REGION_AS923_FREQ_OFFSET 0 + +#define AS923_MIN_RF_FREQUENCY 915000000 +#define AS923_MAX_RF_FREQUENCY 928000000 + +#elif ( REGION_AS923_DEFAULT_CHANNEL_PLAN == CHANNEL_PLAN_GROUP_AS923_2 ) + +// Channel plan CHANNEL_PLAN_GROUP_AS923_2 +// -1.8MHz +#define REGION_AS923_FREQ_OFFSET ( ( ~( 0xFFFFB9B0 ) + 1 ) * 100 ) + +#define AS923_MIN_RF_FREQUENCY 915000000 +#define AS923_MAX_RF_FREQUENCY 928000000 + +#elif ( REGION_AS923_DEFAULT_CHANNEL_PLAN == CHANNEL_PLAN_GROUP_AS923_3 ) + +// Channel plan CHANNEL_PLAN_GROUP_AS923_3 +// -6.6MHz +#define REGION_AS923_FREQ_OFFSET ( ( ~( 0xFFFEFE30 ) + 1 ) * 100 ) + +#define AS923_MIN_RF_FREQUENCY 915000000 +#define AS923_MAX_RF_FREQUENCY 928000000 + +#elif ( REGION_AS923_DEFAULT_CHANNEL_PLAN == CHANNEL_PLAN_GROUP_AS923_1_JP ) + +// Channel plan CHANNEL_PLAN_GROUP_AS923_1_JP + +#define REGION_AS923_FREQ_OFFSET 0 + +/*! + * Restrict AS923 frequencies to channels 24 to 38 + * Center frequencies 920.6 MHz to 923.4 MHz @ 200 kHz max bandwidth + */ +#define AS923_MIN_RF_FREQUENCY 920600000 +#define AS923_MAX_RF_FREQUENCY 923400000 + +/*! + * Specifies the reception bandwidth to be used while executing the LBT + * Max channel bandwidth is 200 kHz + */ +#define AS923_LBT_RX_BANDWIDTH 200000 + +#undef AS923_TX_MAX_DATARATE +#define AS923_TX_MAX_DATARATE DR_5 + +#undef AS923_RX_MAX_DATARATE +#define AS923_RX_MAX_DATARATE DR_5 + +#undef AS923_DEFAULT_UPLINK_DWELL_TIME +#define AS923_DEFAULT_UPLINK_DWELL_TIME 0 + +#undef AS923_DEFAULT_DOWNLINK_DWELL_TIME +#define AS923_DEFAULT_DOWNLINK_DWELL_TIME 0 + +#undef AS923_DEFAULT_MAX_EIRP +#define AS923_DEFAULT_MAX_EIRP 13.0f + +#endif /* REGION_AS923_DEFAULT_CHANNEL_PLAN */ + +#if defined( REGION_AS923 ) +/* + * Non-volatile module context. + */ +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +static RegionNvmDataGroup1_t* RegionNvmGroup1; +static RegionNvmDataGroup2_t* RegionNvmGroup2; +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) +// static RegionNvmDataGroup1_t* RegionNvmGroup1; /* Unused for this region */ +static RegionNvmDataGroup2_t* RegionNvmGroup2; +static Band_t* RegionBands; +#endif /* REGION_VERSION */ + +// Static functions +static bool VerifyRfFreq( uint32_t freq ) +{ + // Check radio driver support + if( Radio.CheckRfFrequency( freq ) == false ) + { + return false; + } + + if( ( freq < AS923_MIN_RF_FREQUENCY ) || ( freq > AS923_MAX_RF_FREQUENCY ) ) + { + return false; + } + return true; +} + +static TimerTime_t GetTimeOnAir( int8_t datarate, uint16_t pktLen ) +{ + int8_t phyDr = DataratesAS923[datarate]; + uint32_t bandwidth = RegionCommonGetBandwidth( datarate, BandwidthsAS923 ); + TimerTime_t timeOnAir = 0; + + if( datarate == DR_7 ) + { // High Speed FSK channel + timeOnAir = Radio.TimeOnAir( MODEM_FSK, bandwidth, phyDr * 1000, 0, 5, false, pktLen, true ); + } + else + { + timeOnAir = Radio.TimeOnAir( MODEM_LORA, bandwidth, phyDr, 1, 8, false, pktLen, true ); + } + return timeOnAir; +} +#endif /* REGION_AS923 */ + +PhyParam_t RegionAS923GetPhyParam( GetPhyParams_t* getPhy ) +{ + PhyParam_t phyParam = { 0 }; + +#if defined( REGION_AS923 ) + 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: + { + RegionCommonGetNextLowerTxDrParams_t nextLowerTxDrParams = + { + .CurrentDr = getPhy->Datarate, + .MaxDr = ( int8_t )AS923_TX_MAX_DATARATE, + .MinDr = ( int8_t )( ( getPhy->UplinkDwellTime == 0 ) ? AS923_TX_MIN_DATARATE : AS923_DWELL_LIMIT_DATARATE ), + .NbChannels = AS923_MAX_NB_CHANNELS, + .ChannelsMask = RegionNvmGroup2->ChannelsMask, + .Channels = RegionNvmGroup2->Channels, + }; + phyParam.Value = RegionCommonGetNextLowerTxDr( &nextLowerTxDrParams ); + 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 = REGION_COMMON_DEFAULT_ADR_ACK_LIMIT; + break; + } + case PHY_DEF_ADR_ACK_DELAY: + { + phyParam.Value = REGION_COMMON_DEFAULT_ADR_ACK_DELAY; + break; + } + case PHY_MAX_PAYLOAD: + { + if( getPhy->UplinkDwellTime == 0 ) + { + phyParam.Value = MaxPayloadOfDatarateDwell0AS923[getPhy->Datarate]; + } + else + { + phyParam.Value = MaxPayloadOfDatarateDwell1AS923[getPhy->Datarate]; + } + break; + } + /* ST_WORKAROUND_BEGIN: Keep repeater feature */ + case PHY_MAX_PAYLOAD_REPEATER: + { + if( getPhy->UplinkDwellTime == 0 ) + { + phyParam.Value = MaxPayloadOfDatarateRepeaterDwell0AS923[getPhy->Datarate]; + } + else + { + phyParam.Value = MaxPayloadOfDatarateDwell1AS923[getPhy->Datarate]; + } + break; + } + /* ST_WORKAROUND_END */ + 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 = REGION_COMMON_DEFAULT_RECEIVE_DELAY1; + break; + } + case PHY_RECEIVE_DELAY2: + { + phyParam.Value = REGION_COMMON_DEFAULT_RECEIVE_DELAY2; + break; + } + case PHY_JOIN_ACCEPT_DELAY1: + { + phyParam.Value = REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY1; + break; + } + case PHY_JOIN_ACCEPT_DELAY2: + { + phyParam.Value = REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY2; + break; + } +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + case PHY_MAX_FCNT_GAP: + { + phyParam.Value = REGION_COMMON_DEFAULT_MAX_FCNT_GAP; + break; + } + case PHY_ACK_TIMEOUT: + { + phyParam.Value = ( REGION_COMMON_DEFAULT_ACK_TIMEOUT + randr( -REGION_COMMON_DEFAULT_ACK_TIMEOUT_RND, REGION_COMMON_DEFAULT_ACK_TIMEOUT_RND ) ); + break; + } +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + case PHY_RETRANSMIT_TIMEOUT: + { + phyParam.Value = ( REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT + randr( -REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT_RND, REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT_RND ) ); + break; + } +#endif /* REGION_VERSION */ + case PHY_DEF_DR1_OFFSET: + { + phyParam.Value = REGION_COMMON_DEFAULT_RX1_DR_OFFSET; + break; + } + case PHY_DEF_RX2_FREQUENCY: + { + phyParam.Value = AS923_RX_WND_2_FREQ - REGION_AS923_FREQ_OFFSET; + break; + } + case PHY_DEF_RX2_DR: + { + phyParam.Value = AS923_RX_WND_2_DR; + break; + } + case PHY_CHANNELS_MASK: + { + phyParam.ChannelsMask = RegionNvmGroup2->ChannelsMask; + break; + } + case PHY_CHANNELS_DEFAULT_MASK: + { + phyParam.ChannelsMask = RegionNvmGroup2->ChannelsDefaultMask; + break; + } + case PHY_MAX_NB_CHANNELS: + { + phyParam.Value = AS923_MAX_NB_CHANNELS; + break; + } + case PHY_CHANNELS: + { + phyParam.Channels = RegionNvmGroup2->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 - REGION_AS923_FREQ_OFFSET; + 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_FREQ: + { +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + phyParam.Value = AS923_PING_SLOT_CHANNEL_FREQ; +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + phyParam.Value = AS923_PING_SLOT_CHANNEL_FREQ - REGION_AS923_FREQ_OFFSET; +#endif /* REGION_VERSION */ + break; + } + case PHY_PING_SLOT_CHANNEL_DR: + { + phyParam.Value = AS923_PING_SLOT_CHANNEL_DR; + break; + } + case PHY_SF_FROM_DR: + { + phyParam.Value = DataratesAS923[getPhy->Datarate]; + break; + } + case PHY_BW_FROM_DR: + { + phyParam.Value = RegionCommonGetBandwidth( getPhy->Datarate, BandwidthsAS923 ); + break; + } + default: + { + break; + } + } + +#endif /* REGION_AS923 */ + return phyParam; +} + +void RegionAS923SetBandTxDone( SetBandTxDoneParams_t* txDone ) +{ +#if defined( REGION_AS923 ) +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + RegionCommonSetBandTxDone( &RegionNvmGroup1->Bands[RegionNvmGroup2->Channels[txDone->Channel].Band], + txDone->LastTxAirTime, txDone->Joined, txDone->ElapsedTimeSinceStartUp ); +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + RegionCommonSetBandTxDone( &RegionBands[RegionNvmGroup2->Channels[txDone->Channel].Band], + txDone->LastTxAirTime, txDone->Joined, txDone->ElapsedTimeSinceStartUp ); +#endif /* REGION_VERSION */ +#endif /* REGION_AS923 */ +} + +void RegionAS923InitDefaults( InitDefaultsParams_t* params ) +{ +#if defined( REGION_AS923 ) + Band_t bands[AS923_MAX_NB_BANDS] = + { + AS923_BAND0 + }; + + switch( params->Type ) + { + case INIT_TYPE_DEFAULTS: + { + if( ( params->NvmGroup1 == NULL ) || ( params->NvmGroup2 == NULL ) ) + { + return; + } + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + RegionNvmGroup1 = (RegionNvmDataGroup1_t*) params->NvmGroup1; + RegionNvmGroup2 = (RegionNvmDataGroup2_t*) params->NvmGroup2; + + // Default bands + memcpy1( ( uint8_t* )RegionNvmGroup1->Bands, ( uint8_t* )bands, sizeof( Band_t ) * AS923_MAX_NB_BANDS ); +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + RegionNvmGroup2 = (RegionNvmDataGroup2_t*) params->NvmGroup2; + RegionBands = (Band_t*) params->Bands; + + // Default bands + memcpy1( ( uint8_t* )RegionBands, ( uint8_t* )bands, sizeof( Band_t ) * AS923_MAX_NB_BANDS ); +#endif /* REGION_VERSION */ + + // Default channels + RegionNvmGroup2->Channels[0] = ( ChannelParams_t ) AS923_LC1; + RegionNvmGroup2->Channels[1] = ( ChannelParams_t ) AS923_LC2; + + // Apply frequency offset + RegionNvmGroup2->Channels[0].Frequency -= REGION_AS923_FREQ_OFFSET; + RegionNvmGroup2->Channels[1].Frequency -= REGION_AS923_FREQ_OFFSET; + + // Default ChannelsMask + RegionNvmGroup2->ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ); + + // Update the channels mask + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, RegionNvmGroup2->ChannelsDefaultMask, CHANNELS_MASK_SIZE ); + break; + } + case INIT_TYPE_RESET_TO_DEFAULT_CHANNELS: + { + // Reset Channels Rx1Frequency to default 0 + RegionNvmGroup2->Channels[0].Rx1Frequency = 0; + RegionNvmGroup2->Channels[1].Rx1Frequency = 0; + // Update the channels mask + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, RegionNvmGroup2->ChannelsDefaultMask, CHANNELS_MASK_SIZE ); + break; + } + case INIT_TYPE_ACTIVATE_DEFAULT_CHANNELS: + { + // Activate channels default mask + RegionNvmGroup2->ChannelsMask[0] |= RegionNvmGroup2->ChannelsDefaultMask[0]; + break; + } + default: + { + break; + } + } +#endif /* REGION_AS923 */ +} + +bool RegionAS923Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ) +{ +#if defined( REGION_AS923 ) + 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; + } +#else + return false; +#endif /* REGION_AS923 */ +} + +void RegionAS923ApplyCFList( ApplyCFListParams_t* applyCFList ) +{ +#if defined( REGION_AS923 ) + 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 ); + } + } +#endif /* REGION_AS923 */ +} + +bool RegionAS923ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ) +{ +#if defined( REGION_AS923 ) + switch( chanMaskSet->ChannelsMaskType ) + { + case CHANNELS_MASK: + { + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + case CHANNELS_DEFAULT_MASK: + { + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + default: + return false; + } + return true; +#else + return false; +#endif /* REGION_AS923 */ +} + +void RegionAS923ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ) +{ +#if defined( REGION_AS923 ) + uint32_t tSymbolInUs = 0; + + // Get the datarate, perform a boundary check + rxConfigParams->Datarate = MIN( datarate, AS923_RX_MAX_DATARATE ); + rxConfigParams->Bandwidth = RegionCommonGetBandwidth( rxConfigParams->Datarate, BandwidthsAS923 ); + + if( rxConfigParams->Datarate == DR_7 ) + { // FSK + tSymbolInUs = RegionCommonComputeSymbolTimeFsk( DataratesAS923[rxConfigParams->Datarate] ); + } + else + { // LoRa + tSymbolInUs = RegionCommonComputeSymbolTimeLoRa( DataratesAS923[rxConfigParams->Datarate], BandwidthsAS923[rxConfigParams->Datarate] ); + } + + RegionCommonComputeRxWindowParameters( tSymbolInUs, minRxSymbols, rxError, Radio.GetWakeupTime( ), &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); +#endif /* REGION_AS923 */ +} + +bool RegionAS923RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) +{ +#if defined( REGION_AS923 ) + RadioModems_t modem; + int8_t dr = rxConfig->Datarate; + uint8_t maxPayload = 0; + int8_t phyDr = 0; + uint32_t frequency = rxConfig->Frequency; + + if( Radio.GetStatus( ) != RF_IDLE ) + { + return false; + } + + if( rxConfig->RxSlot == RX_SLOT_WIN_1 ) + { + // Apply window 1 frequency + frequency = RegionNvmGroup2->Channels[rxConfig->Channel].Frequency; + // Apply the alternative RX 1 window frequency, if it is available + if( RegionNvmGroup2->Channels[rxConfig->Channel].Rx1Frequency != 0 ) + { + frequency = RegionNvmGroup2->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 ); + } + + /* ST_WORKAROUND_BEGIN: Keep repeater feature */ + if( rxConfig->RepeaterSupport == true ) + { + maxPayload = MaxPayloadOfDatarateRepeaterDwell0AS923[dr]; + } + else + { + maxPayload = MaxPayloadOfDatarateDwell0AS923[dr]; + } + + Radio.SetMaxPayloadLength( modem, maxPayload + LORAMAC_FRAME_PAYLOAD_OVERHEAD_SIZE ); + /* ST_WORKAROUND_END */ + + /* ST_WORKAROUND_BEGIN: Print Rx config */ + RegionCommonRxConfigPrint(rxConfig->RxSlot, frequency, dr); + /* ST_WORKAROUND_END */ + + *datarate = (uint8_t) dr; + return true; +#else + return false; +#endif /* REGION_AS923 */ +} + +bool RegionAS923TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ) +{ +#if defined( REGION_AS923 ) + RadioModems_t modem; + int8_t phyDr = DataratesAS923[txConfig->Datarate]; +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + int8_t txPowerLimited = RegionCommonLimitTxPower( txConfig->TxPower, RegionNvmGroup1->Bands[RegionNvmGroup2->Channels[txConfig->Channel].Band].TxMaxPower ); +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + int8_t txPowerLimited = RegionCommonLimitTxPower( txConfig->TxPower, RegionBands[RegionNvmGroup2->Channels[txConfig->Channel].Band].TxMaxPower ); +#endif /* REGION_VERSION */ + uint32_t bandwidth = RegionCommonGetBandwidth( txConfig->Datarate, BandwidthsAS923 ); + int8_t phyTxPower = 0; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain ); + + // Setup the radio frequency + Radio.SetChannel( RegionNvmGroup2->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 ); + } + /* ST_WORKAROUND_BEGIN: Print Tx config */ + RegionCommonTxConfigPrint(RegionNvmGroup2->Channels[txConfig->Channel].Frequency, txConfig->Datarate); + /* ST_WORKAROUND_END */ + + // Update time-on-air + *txTimeOnAir = GetTimeOnAir( txConfig->Datarate, txConfig->PktLen ); + + // Setup maximum payload length of the radio driver + Radio.SetMaxPayloadLength( modem, txConfig->PktLen ); + + *txPower = txPowerLimited; + return true; +#else + return false; +#endif /* REGION_AS923 */ +} + +uint8_t RegionAS923LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ) +{ + uint8_t status = 0x07; +#if defined( REGION_AS923 ) + RegionCommonLinkAdrParams_t linkAdrParams = { 0 }; + 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( RegionNvmGroup2->Channels[i].Frequency != 0 ) + { + chMask |= 1 << i; + } + } + else + { + if( ( ( chMask & ( 1 << i ) ) != 0 ) && + ( RegionNvmGroup2->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 = RegionNvmGroup2->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* ) RegionNvmGroup2->ChannelsMask, 0, sizeof( RegionNvmGroup2->ChannelsMask ) ); + // Update the channels mask + RegionNvmGroup2->ChannelsMask[0] = chMask; + } + + // Update status variables + *drOut = linkAdrParams.Datarate; + *txPowOut = linkAdrParams.TxPower; + *nbRepOut = linkAdrParams.NbRep; + *nbBytesParsed = bytesProcessed; + +#endif /* REGION_AS923 */ + return status; +} + +uint8_t RegionAS923RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ) +{ + uint8_t status = 0x07; +#if defined( REGION_AS923 ) + + // 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 + } + +#endif /* REGION_AS923 */ + return status; +} + +int8_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; +} + +int8_t RegionAS923DlChannelReq( DlChannelReqParams_t* dlChannelReq ) +{ + uint8_t status = 0x03; + +#if defined( REGION_AS923 ) + // Verify if the frequency is supported + if( VerifyRfFreq( dlChannelReq->Rx1Frequency ) == false ) + { + status &= 0xFE; + } + + // Verify if an uplink frequency exists + if( RegionNvmGroup2->Channels[dlChannelReq->ChannelId].Frequency == 0 ) + { + status &= 0xFD; + } + + // Apply Rx1 frequency, if the status is OK + if( status == 0x03 ) + { + RegionNvmGroup2->Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency; + } + +#endif /* REGION_AS923 */ + return status; +} + +int8_t RegionAS923AlternateDr( int8_t currentDr, AlternateDrType_t type ) +{ +#if defined( REGION_AS923 ) + // Only AS923_DWELL_LIMIT_DATARATE is supported + return AS923_DWELL_LIMIT_DATARATE; +#else + return -1; +#endif /* REGION_AS923 */ +} + +LoRaMacStatus_t RegionAS923NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ) +{ +#if defined( REGION_AS923 ) + uint8_t nbEnabledChannels = 0; + uint8_t nbRestrictedChannels = 0; + uint8_t enabledChannels[AS923_MAX_NB_CHANNELS] = { 0 }; + RegionCommonIdentifyChannelsParam_t identifyChannelsParam; + RegionCommonCountNbOfEnabledChannelsParams_t countChannelsParams; + LoRaMacStatus_t status = LORAMAC_STATUS_NO_CHANNEL_FOUND; + uint16_t joinChannels = AS923_JOIN_CHANNELS; + + if( RegionCommonCountChannels( RegionNvmGroup2->ChannelsMask, 0, 1 ) == 0 ) + { // Reactivate default channels + RegionNvmGroup2->ChannelsMask[0] |= LC( 1 ) + LC( 2 ); + } + + // Search how many channels are enabled + countChannelsParams.Joined = nextChanParams->Joined; + countChannelsParams.Datarate = nextChanParams->Datarate; + countChannelsParams.ChannelsMask = RegionNvmGroup2->ChannelsMask; + countChannelsParams.Channels = RegionNvmGroup2->Channels; +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + countChannelsParams.Bands = RegionNvmGroup1->Bands; +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + countChannelsParams.Bands = RegionBands; +#endif /* REGION_VERSION */ + countChannelsParams.MaxNbChannels = AS923_MAX_NB_CHANNELS; + countChannelsParams.JoinChannels = &joinChannels; + + identifyChannelsParam.AggrTimeOff = nextChanParams->AggrTimeOff; + identifyChannelsParam.LastAggrTx = nextChanParams->LastAggrTx; + identifyChannelsParam.DutyCycleEnabled = nextChanParams->DutyCycleEnabled; + identifyChannelsParam.MaxBands = AS923_MAX_NB_BANDS; + + identifyChannelsParam.ElapsedTimeSinceStartUp = nextChanParams->ElapsedTimeSinceStartUp; + identifyChannelsParam.LastTxIsJoinRequest = nextChanParams->LastTxIsJoinRequest; + identifyChannelsParam.ExpectedTimeOnAir = GetTimeOnAir( nextChanParams->Datarate, nextChanParams->PktLen ); + + identifyChannelsParam.CountNbOfEnabledChannelsParam = &countChannelsParams; + + status = RegionCommonIdentifyChannels( &identifyChannelsParam, aggregatedTimeOff, enabledChannels, + &nbEnabledChannels, &nbRestrictedChannels, time ); + + if( status == LORAMAC_STATUS_OK ) + { +#if ( REGION_AS923_DEFAULT_CHANNEL_PLAN == CHANNEL_PLAN_GROUP_AS923_1_JP ) + // Executes the LBT algorithm when operating in Japan + uint8_t channelNext = 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( RegionNvmGroup2->Channels[channelNext].Frequency, AS923_LBT_RX_BANDWIDTH, AS923_RSSI_FREE_TH, AS923_CARRIER_SENSE_TIME ) == true ) + { + // Free channel found + *channel = channelNext; + return LORAMAC_STATUS_OK; + } + } + // Even if one or more channels are available according to the channel plan, no free channel + // was found during the LBT procedure. + status = LORAMAC_STATUS_NO_FREE_CHANNEL_FOUND; +#else + // We found a valid channel + *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )]; +#endif + } + else if( status == LORAMAC_STATUS_NO_CHANNEL_FOUND ) + { + // Datarate not supported by any channel, restore defaults + RegionNvmGroup2->ChannelsMask[0] |= LC( 1 ) + LC( 2 ); + } + return status; +#else + return LORAMAC_STATUS_NO_CHANNEL_FOUND; +#endif /* REGION_AS923 */ +} + +LoRaMacStatus_t RegionAS923ChannelAdd( ChannelAddParams_t* channelAdd ) +{ +#if defined( REGION_AS923 ) + 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* ) &(RegionNvmGroup2->Channels[id]), ( uint8_t* ) channelAdd->NewChannel, sizeof( RegionNvmGroup2->Channels[id] ) ); + RegionNvmGroup2->Channels[id].Band = 0; + RegionNvmGroup2->ChannelsMask[0] |= ( 1 << id ); + return LORAMAC_STATUS_OK; +#else + return LORAMAC_STATUS_NO_CHANNEL_FOUND; +#endif /* REGION_AS923 */ +} + +bool RegionAS923ChannelsRemove( ChannelRemoveParams_t* channelRemove ) +{ +#if defined( REGION_AS923 ) + uint8_t id = channelRemove->ChannelId; + + if( id < AS923_NUMB_DEFAULT_CHANNELS ) + { + return false; + } + + // Remove the channel from the list of channels + RegionNvmGroup2->Channels[id] = ( ChannelParams_t ){ 0, 0, { 0 }, 0 }; + + return RegionCommonChanDisable( RegionNvmGroup2->ChannelsMask, id, AS923_MAX_NB_CHANNELS ); +#else + return false; +#endif /* REGION_AS923 */ +} + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +void RegionAS923SetContinuousWave( ContinuousWaveParams_t* continuousWave ) +{ +#if defined( REGION_AS923 ) + int8_t txPowerLimited = RegionCommonLimitTxPower( continuousWave->TxPower, RegionNvmGroup1->Bands[RegionNvmGroup2->Channels[continuousWave->Channel].Band].TxMaxPower ); + int8_t phyTxPower = 0; + uint32_t frequency = RegionNvmGroup2->Channels[continuousWave->Channel].Frequency; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, continuousWave->MaxEirp, continuousWave->AntennaGain ); + + Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout ); +#endif /* REGION_AS923 */ +} + +uint8_t RegionAS923ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) +{ +#if defined( REGION_AS923 ) + // 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] ) ); +#else + return 0; +#endif /* REGION_AS923 */ + +} +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) +uint8_t RegionAS923ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) +{ +#if defined( REGION_AS923 ) + // Initialize minDr + int8_t minDr; + + if( downlinkDwellTime == 0 ) + { + // Update the minDR for a downlink dwell time configuration of 0 + minDr = EffectiveRx1DrOffsetDownlinkDwell0AS923[dr][drOffset]; + } + else + { + // Update the minDR for a downlink dwell time configuration of 1 + minDr = EffectiveRx1DrOffsetDownlinkDwell1AS923[dr][drOffset]; + } + + return minDr; +#else + return 0; +#endif /* REGION_AS923 */ + +} +#endif /* REGION_VERSION */ + +void RegionAS923RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ) +{ +#if defined( REGION_AS923 ) + 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; +#endif /* REGION_AS923 */ +} diff --git a/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionAS923.h b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionAS923.h new file mode 100644 index 0000000..41bc674 --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionAS923.h @@ -0,0 +1,588 @@ +/*! + * \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. + * \{ + */ +/** + ****************************************************************************** + * + * Portions COPYRIGHT 2020 STMicroelectronics + * + * @file RegionAS923.h + * @author MCD Application Team + * @brief Region definition for AS923 + ****************************************************************************** + */ +#ifndef __REGION_AS923_H__ +#define __REGION_AS923_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "Region.h" + +/*! + * Channel plan group AS923-1 + * AS923_FREQ_OFFSET = 0 + */ +#define CHANNEL_PLAN_GROUP_AS923_1 1 + +/*! + * Channel plan group AS923-2 + * AS923_FREQ_OFFSET = -1.8MHz + */ +#define CHANNEL_PLAN_GROUP_AS923_2 2 + +/*! + * Channel plan group AS923-3 + * AS923_FREQ_OFFSET = -6.6MHz + */ +#define CHANNEL_PLAN_GROUP_AS923_3 3 + +/*! + * Channel plan group AS923-1 for Japan + * AS923_FREQ_OFFSET = 0 + */ +#define CHANNEL_PLAN_GROUP_AS923_1_JP 4 + +/*! + * 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 + +/*! + * 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 + */ +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +#define AS923_DEFAULT_DOWNLINK_DWELL_TIME 1 +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) +#define AS923_DEFAULT_DOWNLINK_DWELL_TIME REGION_COMMON_DEFAULT_DOWNLINK_DWELL_TIME +#endif /* REGION_VERSION */ + +/*! + * Default Max EIRP + */ +#define AS923_DEFAULT_MAX_EIRP 16.0f + +/*! + * Default antenna gain + */ +#define AS923_DEFAULT_ANTENNA_GAIN 2.15f + +/*! + * Enabled or disabled the duty cycle + */ +#define AS923_DUTY_CYCLE_ENABLED 0 + +/*! + * Maximum RX window duration + */ +#define AS923_MAX_RX_WINDOW 3000 + +#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 + +/*! + * Ping slot channel frequency + */ +#define AS923_PING_SLOT_CHANNEL_FREQ 923400000 + +/*! + * Payload size of a beacon frame + */ +#define AS923_BEACON_SIZE 17 + +/*! + * Size of RFU 1 field + */ +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +#define AS923_RFU1_SIZE 2 +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) +#define AS923_RFU1_SIZE 1 +#endif /* REGION_VERSION */ + +/*! + * Size of RFU 2 field + */ +#define AS923_RFU2_SIZE 0 + +/*! + * Datarate of the beacon channel + */ +#define AS923_BEACON_CHANNEL_DR DR_3 + +/*! + * Bandwidth 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 + * Band = { DutyCycle, TxMaxPower, LastBandUpdateTime, LastMaxCreditAssignTime, TimeCredits, MaxTimeCredits, ReadyForTransmission } + */ +#define AS923_BAND0 { 100, AS923_MAX_TX_POWER, 0, 0, 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 -80 + +/*! + * Specifies the time the node performs a carrier sense + */ +#define AS923_CARRIER_SENSE_TIME 5 + +/*! + * 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 }; + +/* ST_WORKAROUND_BEGIN: Keep repeater feature */ +/*! + * 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. + */ +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +static const uint8_t MaxPayloadOfDatarateDwell0AS923[] = { 51, 51, 51, 115, 242, 242, 242, 242 }; +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) +static const uint8_t MaxPayloadOfDatarateDwell0AS923[] = { 51, 51, 115, 115, 242, 242, 242, 242 }; +#endif /* REGION_VERSION */ + +/*! + * 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. + */ +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +static const uint8_t MaxPayloadOfDatarateRepeaterDwell0AS923[] = { 51, 51, 51, 115, 222, 222, 222, 222 }; +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) +static const uint8_t MaxPayloadOfDatarateRepeaterDwell0AS923[] = { 51, 51, 115, 115, 222, 222, 222, 222 }; +#endif /* REGION_VERSION */ +/* ST_WORKAROUND_END */ + +/*! + * Maximum payload with respect to the datarate index. + * The table is only valid for uplinks. + */ +static const uint8_t MaxPayloadOfDatarateDwell1AS923[] = { 0, 0, 11, 53, 125, 242, 242, 242 }; + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +/*! + * Effective datarate offsets for receive window 1. + */ +static const int8_t EffectiveRx1DrOffsetAS923[] = { 0, 1, 2, 3, 4, 5, -1, -2 }; +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) +/*! + * Effective datarate offsets for receive window 1 when downlink dwell time is zero. + */ +static const int8_t EffectiveRx1DrOffsetDownlinkDwell0AS923[8][8] = + { + { DR_0 , DR_0 , DR_0 , DR_0 , DR_0 , DR_0 , DR_1 , DR_2 }, // DR_0 + { DR_1 , DR_0 , DR_0 , DR_0 , DR_0 , DR_0 , DR_2 , DR_3 }, // DR_1 + { DR_2 , DR_1 , DR_0 , DR_0 , DR_0 , DR_0 , DR_3 , DR_4 }, // DR_2 + { DR_3 , DR_2 , DR_1 , DR_0 , DR_0 , DR_0 , DR_4 , DR_5 }, // DR_3 + { DR_4 , DR_3 , DR_2 , DR_1 , DR_0 , DR_0 , DR_5 , DR_6 }, // DR_4 + { DR_5 , DR_4 , DR_3 , DR_2 , DR_1 , DR_0 , DR_6 , DR_7 }, // DR_5 + { DR_6 , DR_5 , DR_4 , DR_3 , DR_2 , DR_1 , DR_7 , DR_7 }, // DR_6 + { DR_7 , DR_6 , DR_5 , DR_4 , DR_3 , DR_2 , DR_7 , DR_7 }, // DR_7 + }; + +/*! + * Effective datarate offsets for receive window 1 when downlink dwell time is one. + */ +static const int8_t EffectiveRx1DrOffsetDownlinkDwell1AS923[8][8] = + { + { DR_2 , DR_2 , DR_2 , DR_2 , DR_2 , DR_2 , DR_2 , DR_2 }, // DR_0 + { DR_2 , DR_2 , DR_2 , DR_2 , DR_2 , DR_2 , DR_2 , DR_3 }, // DR_1 + { DR_2 , DR_2 , DR_2 , DR_2 , DR_2 , DR_2 , DR_3 , DR_4 }, // DR_2 + { DR_3 , DR_2 , DR_2 , DR_2 , DR_2 , DR_2 , DR_4 , DR_5 }, // DR_3 + { DR_4 , DR_3 , DR_2 , DR_2 , DR_2 , DR_2 , DR_5 , DR_6 }, // DR_4 + { DR_5 , DR_4 , DR_3 , DR_2 , DR_2 , DR_2 , DR_6 , DR_7 }, // DR_5 + { DR_6 , DR_5 , DR_4 , DR_3 , DR_2 , DR_2 , DR_7 , DR_7 }, // DR_6 + { DR_7 , DR_6 , DR_5 , DR_4 , DR_3 , DR_2 , DR_7 , DR_7 }, // DR_7 + }; + +#endif /* REGION_VERSION */ + +/*! + * \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] params Sets the initialization type. + */ +void RegionAS923InitDefaults( InitDefaultsParams_t* params ); + +/*! + * \brief Verifies a parameter. + * + * \param [in] verify Pointer to the function parameters. + * + * \param [in] phyAttribute 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. + * + * \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 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. + */ +int8_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. + */ +int8_t RegionAS923DlChannelReq( 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 RegionAS923AlternateDr( int8_t currentDr, AlternateDrType_t type ); + +/*! + * \brief Searches and set the next random available channel + * + * \param [in] nextChanParams pointer of selected channel parameters + * + * \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 ); + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +/*! + * \brief Sets the radio into continuous wave mode. + * + * \param [IN] continuousWave Pointer to the function parameters. + */ +void RegionAS923SetContinuousWave( ContinuousWaveParams_t* continuousWave ); +#endif /* REGION_VERSION */ + +/*! + * \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 + * + * \param [out] outDr Datarate used to receive the beacon + */ + void RegionAS923RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ); + +/*! \} defgroup REGIONAS923 */ + +#ifdef __cplusplus +} +#endif + +#endif // __REGION_AS923_H__ diff --git a/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionAU915.c b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionAU915.c new file mode 100644 index 0000000..c6d64a5 --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionAU915.c @@ -0,0 +1,1106 @@ +/*! + * \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 ) +*/ +/** + ****************************************************************************** + * + * Portions COPYRIGHT 2020 STMicroelectronics + * + * @file RegionAU915.c + * @author MCD Application Team + * @brief Region implementation for AU915 + ****************************************************************************** + */ +#include "../../../SubGHz_Phy/radio.h" +#include "RegionAU915.h" +#include "RegionBaseUS.h" + +// Definitions +#define CHANNELS_MASK_SIZE 6 + +// A mask to select only valid 500KHz channels +#define CHANNELS_MASK_500KHZ_MASK 0x00FF + +/* The HYBRID_DEFAULT_MASKx define the enabled channels in Hybrid mode*/ +/* Note: they can be redefined in lorawan_conf.h*/ +#ifndef HYBRID_DEFAULT_MASK0 /*enabled channels from channel 15 down to channel 0*/ +#define HYBRID_DEFAULT_MASK0 0x00FF /*channel 7 down to channel 0 enabled*/ +#endif +#ifndef HYBRID_DEFAULT_MASK1 /*enabled channels from channel 31 down to channel 16*/ +#define HYBRID_DEFAULT_MASK1 0x0000 +#endif +#ifndef HYBRID_DEFAULT_MASK2 /*enabled channels from channel 47 down to channel 32*/ +#define HYBRID_DEFAULT_MASK2 0x0000 +#endif +#ifndef HYBRID_DEFAULT_MASK3 /*enabled channels from channel 63 down to channel 48*/ +#define HYBRID_DEFAULT_MASK3 0x0000 +#endif +#ifndef HYBRID_DEFAULT_MASK4 /*enabled channels from channel 71 down to channel 64*/ +#define HYBRID_DEFAULT_MASK4 0x0001 +#endif + +#if defined( REGION_AU915 ) +/* + * Non-volatile module context. + */ +static RegionNvmDataGroup1_t* RegionNvmGroup1; +static RegionNvmDataGroup2_t* RegionNvmGroup2; +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) +static Band_t* RegionBands; +#endif /* REGION_VERSION */ + +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 TimerTime_t GetTimeOnAir( int8_t datarate, uint16_t pktLen ) +{ + int8_t phyDr = DataratesAU915[datarate]; + uint32_t bandwidth = RegionCommonGetBandwidth( datarate, BandwidthsAU915 ); + + return Radio.TimeOnAir( MODEM_LORA, bandwidth, phyDr, 1, 8, false, pktLen, true ); +} +#endif /* REGION_AU915 */ + +PhyParam_t RegionAU915GetPhyParam( GetPhyParams_t* getPhy ) +{ + PhyParam_t phyParam = { 0 }; + +#if defined( REGION_AU915 ) + 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: + { + RegionCommonGetNextLowerTxDrParams_t nextLowerTxDrParams = + { + .CurrentDr = getPhy->Datarate, + .MaxDr = ( int8_t )AU915_TX_MAX_DATARATE, + .MinDr = ( int8_t )( ( getPhy->UplinkDwellTime == 0 ) ? AU915_TX_MIN_DATARATE : AU915_DWELL_LIMIT_DATARATE ), + .NbChannels = AU915_MAX_NB_CHANNELS, + .ChannelsMask = RegionNvmGroup2->ChannelsMask, + .Channels = RegionNvmGroup2->Channels, + }; + phyParam.Value = RegionCommonGetNextLowerTxDr( &nextLowerTxDrParams ); + 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 = REGION_COMMON_DEFAULT_ADR_ACK_LIMIT; + break; + } + case PHY_DEF_ADR_ACK_DELAY: + { + phyParam.Value = REGION_COMMON_DEFAULT_ADR_ACK_DELAY; + break; + } + case PHY_MAX_PAYLOAD: + { + if( getPhy->UplinkDwellTime == 0 ) + { + phyParam.Value = MaxPayloadOfDatarateDwell0AU915[getPhy->Datarate]; + } + else + { + phyParam.Value = MaxPayloadOfDatarateDwell1AU915[getPhy->Datarate]; + } + break; + } + /* ST_WORKAROUND_BEGIN: Keep repeater feature */ + case PHY_MAX_PAYLOAD_REPEATER: + { + if( getPhy->UplinkDwellTime == 0) + { + phyParam.Value = MaxPayloadOfDatarateRepeaterDwell0AU915[getPhy->Datarate]; + } + else + { + phyParam.Value = MaxPayloadOfDatarateRepeaterDwell1AU915[getPhy->Datarate]; + } + break; + } + /* ST_WORKAROUND_END */ + 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 = REGION_COMMON_DEFAULT_RECEIVE_DELAY1; + break; + } + case PHY_RECEIVE_DELAY2: + { + phyParam.Value = REGION_COMMON_DEFAULT_RECEIVE_DELAY2; + break; + } + case PHY_JOIN_ACCEPT_DELAY1: + { + phyParam.Value = REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY1; + break; + } + case PHY_JOIN_ACCEPT_DELAY2: + { + phyParam.Value = REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY2; + break; + } +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + case PHY_MAX_FCNT_GAP: + { + phyParam.Value = REGION_COMMON_DEFAULT_MAX_FCNT_GAP; + break; + } + case PHY_ACK_TIMEOUT: + { + phyParam.Value = ( REGION_COMMON_DEFAULT_ACK_TIMEOUT + randr( -REGION_COMMON_DEFAULT_ACK_TIMEOUT_RND, REGION_COMMON_DEFAULT_ACK_TIMEOUT_RND ) ); + break; + } +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + case PHY_RETRANSMIT_TIMEOUT: + { + phyParam.Value = ( REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT + randr( -REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT_RND, REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT_RND ) ); + break; + } +#endif /* REGION_VERSION */ + case PHY_DEF_DR1_OFFSET: + { + phyParam.Value = REGION_COMMON_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 = RegionNvmGroup2->ChannelsMask; + break; + } + case PHY_CHANNELS_DEFAULT_MASK: + { + phyParam.ChannelsMask = RegionNvmGroup2->ChannelsDefaultMask; + break; + } + case PHY_MAX_NB_CHANNELS: + { + phyParam.Value = AU915_MAX_NB_CHANNELS; + break; + } + case PHY_CHANNELS: + { + phyParam.Channels = RegionNvmGroup2->Channels; + break; + } + case PHY_DEF_UPLINK_DWELL_TIME: + { + phyParam.Value = AU915_DEFAULT_UPLINK_DWELL_TIME; + break; + } + case PHY_DEF_DOWNLINK_DWELL_TIME: + { + phyParam.Value = REGION_COMMON_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_CHANNEL_FREQ: + { + phyParam.Value = RegionBaseUSCalcDownlinkFrequency( getPhy->Channel, + AU915_BEACON_CHANNEL_FREQ, + AU915_BEACON_CHANNEL_STEPWIDTH ); + 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_NB_CHANNELS: + { + phyParam.Value = AU915_BEACON_NB_CHANNELS; + break; + } + case PHY_PING_SLOT_CHANNEL_FREQ: + { + phyParam.Value = RegionBaseUSCalcDownlinkFrequency( getPhy->Channel, + AU915_PING_SLOT_CHANNEL_FREQ, + AU915_BEACON_CHANNEL_STEPWIDTH ); + break; + } + case PHY_PING_SLOT_CHANNEL_DR: + { + phyParam.Value = AU915_PING_SLOT_CHANNEL_DR; + break; + } + case PHY_PING_SLOT_NB_CHANNELS: + { + phyParam.Value = AU915_BEACON_NB_CHANNELS; + break; + } + case PHY_SF_FROM_DR: + { + phyParam.Value = DataratesAU915[getPhy->Datarate]; + break; + } + case PHY_BW_FROM_DR: + { + phyParam.Value = RegionCommonGetBandwidth( getPhy->Datarate, BandwidthsAU915 ); + break; + } + default: + { + break; + } + } + +#endif /* REGION_AU915 */ + return phyParam; +} + +void RegionAU915SetBandTxDone( SetBandTxDoneParams_t* txDone ) +{ +#if defined( REGION_AU915 ) +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + RegionCommonSetBandTxDone( &RegionNvmGroup1->Bands[RegionNvmGroup2->Channels[txDone->Channel].Band], + txDone->LastTxAirTime, txDone->Joined, txDone->ElapsedTimeSinceStartUp ); +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + RegionCommonSetBandTxDone( &RegionBands[RegionNvmGroup2->Channels[txDone->Channel].Band], + txDone->LastTxAirTime, txDone->Joined, txDone->ElapsedTimeSinceStartUp ); +#endif /* REGION_VERSION */ +#endif /* REGION_AU915 */ +} + +void RegionAU915InitDefaults( InitDefaultsParams_t* params ) +{ +#if defined( REGION_AU915 ) + Band_t bands[AU915_MAX_NB_BANDS] = + { + AU915_BAND0 + }; + + switch( params->Type ) + { + case INIT_TYPE_DEFAULTS: + { + if( ( params->NvmGroup1 == NULL ) || ( params->NvmGroup2 == NULL ) ) + { + return; + } + + RegionNvmGroup1 = (RegionNvmDataGroup1_t*) params->NvmGroup1; + RegionNvmGroup2 = (RegionNvmDataGroup2_t*) params->NvmGroup2; +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + RegionBands = (Band_t*) params->Bands; +#endif /* REGION_VERSION */ + + // Initialize 8 bit channel groups index + RegionNvmGroup1->JoinChannelGroupsCurrentIndex = 0; + + // Initialize the join trials counter + RegionNvmGroup1->JoinTrialsCounter = 0; + + // Default bands +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + memcpy1( ( uint8_t* )RegionNvmGroup1->Bands, ( uint8_t* )bands, sizeof( Band_t ) * AU915_MAX_NB_BANDS ); +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + memcpy1( ( uint8_t* )RegionBands, ( uint8_t* )bands, sizeof( Band_t ) * AU915_MAX_NB_BANDS ); +#endif /* REGION_VERSION */ + + // Channels + for( uint8_t i = 0; i < AU915_MAX_NB_CHANNELS - 8; i++ ) + { + // 125 kHz channels + RegionNvmGroup2->Channels[i].Frequency = 915200000 + i * 200000; + RegionNvmGroup2->Channels[i].DrRange.Value = ( DR_5 << 4 ) | DR_0; + RegionNvmGroup2->Channels[i].Band = 0; + } + for( uint8_t i = AU915_MAX_NB_CHANNELS - 8; i < AU915_MAX_NB_CHANNELS; i++ ) + { + // 500 kHz channels + RegionNvmGroup2->Channels[i].Frequency = 915900000 + ( i - ( AU915_MAX_NB_CHANNELS - 8 ) ) * 1600000; + RegionNvmGroup2->Channels[i].DrRange.Value = ( DR_6 << 4 ) | DR_6; + RegionNvmGroup2->Channels[i].Band = 0; + } + + // Initialize channels default mask + /* ST_WORKAROUND_BEGIN: Hybrid mode */ +#if ( HYBRID_ENABLED == 1 ) + RegionNvmGroup2->ChannelsDefaultMask[0] = HYBRID_DEFAULT_MASK0; + RegionNvmGroup2->ChannelsDefaultMask[1] = HYBRID_DEFAULT_MASK1; + RegionNvmGroup2->ChannelsDefaultMask[2] = HYBRID_DEFAULT_MASK2; + RegionNvmGroup2->ChannelsDefaultMask[3] = HYBRID_DEFAULT_MASK3; + RegionNvmGroup2->ChannelsDefaultMask[4] = HYBRID_DEFAULT_MASK4; + RegionNvmGroup2->ChannelsDefaultMask[5] = 0x0000; +#else + RegionNvmGroup2->ChannelsDefaultMask[0] = 0xFFFF; + RegionNvmGroup2->ChannelsDefaultMask[1] = 0xFFFF; + RegionNvmGroup2->ChannelsDefaultMask[2] = 0xFFFF; + RegionNvmGroup2->ChannelsDefaultMask[3] = 0xFFFF; + RegionNvmGroup2->ChannelsDefaultMask[4] = 0x00FF; + RegionNvmGroup2->ChannelsDefaultMask[5] = 0x0000; +#endif /* HYBRID_ENABLED == 1 */ + /* ST_WORKAROUND_END */ + + // Copy channels default mask + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, RegionNvmGroup2->ChannelsDefaultMask, CHANNELS_MASK_SIZE ); + + // Copy into channels mask remaining + RegionCommonChanMaskCopy( RegionNvmGroup1->ChannelsMaskRemaining, RegionNvmGroup2->ChannelsMask, CHANNELS_MASK_SIZE ); + break; + } + case INIT_TYPE_RESET_TO_DEFAULT_CHANNELS: + { + // Intentional fallthrough + } + case INIT_TYPE_ACTIVATE_DEFAULT_CHANNELS: + { + // Copy channels default mask + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, RegionNvmGroup2->ChannelsDefaultMask, CHANNELS_MASK_SIZE ); + + for( uint8_t i = 0; i < CHANNELS_MASK_SIZE; i++ ) + { // Copy-And the channels mask + RegionNvmGroup1->ChannelsMaskRemaining[i] &= RegionNvmGroup2->ChannelsMask[i]; + } + break; + } + default: + { + break; + } + } +#endif /* REGION_AU915 */ +} + +bool RegionAU915Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ) +{ +#if defined( REGION_AU915 ) + 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; + } +#else + return false; +#endif /* REGION_AU915 */ +} + +void RegionAU915ApplyCFList( ApplyCFListParams_t* applyCFList ) +{ +#if defined( REGION_AU915 ) + // 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 ) + { + RegionNvmGroup2->ChannelsMask[chMaskItr] = (uint16_t) (0x00FF & applyCFList->Payload[cntPayload]); + RegionNvmGroup2->ChannelsMask[chMaskItr] |= (uint16_t) (applyCFList->Payload[cntPayload+1] << 8); + if( chMaskItr == 4 ) + { + RegionNvmGroup2->ChannelsMask[chMaskItr] = RegionNvmGroup2->ChannelsMask[chMaskItr] & CHANNELS_MASK_500KHZ_MASK; + } + // Set the channel mask to the remaining + RegionNvmGroup1->ChannelsMaskRemaining[chMaskItr] &= RegionNvmGroup2->ChannelsMask[chMaskItr]; + } +#endif /* REGION_AU915 */ +} + +bool RegionAU915ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ) +{ +#if defined( REGION_AU915 ) + switch( chanMaskSet->ChannelsMaskType ) + { + case CHANNELS_MASK: + { + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, chanMaskSet->ChannelsMaskIn, CHANNELS_MASK_SIZE ); + + RegionNvmGroup2->ChannelsDefaultMask[4] = RegionNvmGroup2->ChannelsDefaultMask[4] & CHANNELS_MASK_500KHZ_MASK; + RegionNvmGroup2->ChannelsDefaultMask[5] = 0x0000; + + for( uint8_t i = 0; i < 6; i++ ) + { // Copy-And the channels mask + RegionNvmGroup1->ChannelsMaskRemaining[i] &= RegionNvmGroup2->ChannelsMask[i]; + } + break; + } + case CHANNELS_DEFAULT_MASK: + { + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, CHANNELS_MASK_SIZE ); + break; + } + default: + return false; + } + return true; +#else + return false; +#endif /* REGION_AU915 */ +} + +void RegionAU915ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ) +{ +#if defined( REGION_AU915 ) + uint32_t tSymbolInUs = 0; + + // Get the datarate, perform a boundary check + rxConfigParams->Datarate = MIN( datarate, AU915_RX_MAX_DATARATE ); + rxConfigParams->Bandwidth = RegionCommonGetBandwidth( rxConfigParams->Datarate, BandwidthsAU915 ); + + tSymbolInUs = RegionCommonComputeSymbolTimeLoRa( DataratesAU915[rxConfigParams->Datarate], BandwidthsAU915[rxConfigParams->Datarate] ); + + RegionCommonComputeRxWindowParameters( tSymbolInUs, minRxSymbols, rxError, Radio.GetWakeupTime( ), &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); +#endif /* REGION_AU915 */ +} + +bool RegionAU915RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) +{ +#if defined( REGION_AU915 ) + int8_t dr = rxConfig->Datarate; + uint8_t maxPayload = 0; + int8_t phyDr = 0; + uint32_t frequency = rxConfig->Frequency; + + if( Radio.GetStatus( ) != RF_IDLE ) + { + return false; + } + + if( rxConfig->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 ); + + /* ST_WORKAROUND_BEGIN: Keep repeater feature */ + if( rxConfig->RepeaterSupport == true ) + { + maxPayload = MaxPayloadOfDatarateRepeaterDwell0AU915[dr]; + } + else + { + maxPayload = MaxPayloadOfDatarateDwell0AU915[dr]; + } + Radio.SetMaxPayloadLength( MODEM_LORA, maxPayload + LORAMAC_FRAME_PAYLOAD_OVERHEAD_SIZE ); + /* ST_WORKAROUND_END */ + + /* ST_WORKAROUND_BEGIN: Print Rx config */ + RegionCommonRxConfigPrint(rxConfig->RxSlot, frequency, dr); + /* ST_WORKAROUND_END */ + + *datarate = (uint8_t) dr; + return true; +#else + return false; +#endif /* REGION_AU915 */ +} + +bool RegionAU915TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ) +{ +#if defined( REGION_AU915 ) + int8_t phyDr = DataratesAU915[txConfig->Datarate]; +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + int8_t txPowerLimited = RegionCommonLimitTxPower( txConfig->TxPower, RegionNvmGroup1->Bands[RegionNvmGroup2->Channels[txConfig->Channel].Band].TxMaxPower ); +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + int8_t txPowerLimited = RegionCommonLimitTxPower( txConfig->TxPower, RegionBands[RegionNvmGroup2->Channels[txConfig->Channel].Band].TxMaxPower ); +#endif /* REGION_VERSION */ + uint32_t bandwidth = RegionCommonGetBandwidth( txConfig->Datarate, BandwidthsAU915 ); + int8_t phyTxPower = 0; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain ); + + // Setup the radio frequency + Radio.SetChannel( RegionNvmGroup2->Channels[txConfig->Channel].Frequency ); + + Radio.SetTxConfig( MODEM_LORA, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 4000 ); + /* ST_WORKAROUND_BEGIN: Print Tx config */ + RegionCommonTxConfigPrint(RegionNvmGroup2->Channels[txConfig->Channel].Frequency, txConfig->Datarate); + /* ST_WORKAROUND_END */ + + // Setup maximum payload length of the radio driver + Radio.SetMaxPayloadLength( MODEM_LORA, txConfig->PktLen ); + + // Update time-on-air + *txTimeOnAir = GetTimeOnAir( txConfig->Datarate, txConfig->PktLen ); + + *txPower = txPowerLimited; + return true; +#else + return false; +#endif /* REGION_AU915 */ +} + +uint8_t RegionAU915LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ) +{ + uint8_t status = 0x07; +#if defined( REGION_AU915 ) + RegionCommonLinkAdrParams_t linkAdrParams = { 0 }; + 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, RegionNvmGroup2->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 comparison + 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 = RegionNvmGroup2->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( RegionNvmGroup2->ChannelsMask, channelsMask, 6 ); + + RegionNvmGroup1->ChannelsMaskRemaining[0] &= RegionNvmGroup2->ChannelsMask[0]; + RegionNvmGroup1->ChannelsMaskRemaining[1] &= RegionNvmGroup2->ChannelsMask[1]; + RegionNvmGroup1->ChannelsMaskRemaining[2] &= RegionNvmGroup2->ChannelsMask[2]; + RegionNvmGroup1->ChannelsMaskRemaining[3] &= RegionNvmGroup2->ChannelsMask[3]; + RegionNvmGroup1->ChannelsMaskRemaining[4] = RegionNvmGroup2->ChannelsMask[4]; + RegionNvmGroup1->ChannelsMaskRemaining[5] = RegionNvmGroup2->ChannelsMask[5]; + } + + // Update status variables + *drOut = linkAdrParams.Datarate; + *txPowOut = linkAdrParams.TxPower; + *nbRepOut = linkAdrParams.NbRep; + *nbBytesParsed = bytesProcessed; + +#endif /* REGION_AU915 */ + return status; +} + +uint8_t RegionAU915RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ) +{ + uint8_t status = 0x07; +#if defined( REGION_AU915 ) + + // 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 + } + +#endif /* REGION_AU915 */ + return status; +} + +int8_t RegionAU915NewChannelReq( NewChannelReqParams_t* newChannelReq ) +{ + // Do not accept the request + return -1; +} + +int8_t RegionAU915TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ) +{ + // Accept the request + return 0; +} + +int8_t RegionAU915DlChannelReq( DlChannelReqParams_t* dlChannelReq ) +{ + // Do not accept the request + return -1; +} + +int8_t RegionAU915AlternateDr( int8_t currentDr, AlternateDrType_t type ) +{ +#if defined( REGION_AU915 ) + // Alternates the data rate according to the channel sequence: + // Eight times a 125kHz DR_2 and then one 500kHz DR_6 channel + if( type == ALTERNATE_DR ) + { + RegionNvmGroup1->JoinTrialsCounter++; + } + else + { + RegionNvmGroup1->JoinTrialsCounter--; + } + + if( RegionNvmGroup1->JoinTrialsCounter % 9 == 0 ) + { + // Use DR_6 every 9th times. + currentDr = DR_6; + } + else + { + currentDr = DR_2; + } + return currentDr; +#else + return -1; +#endif /* REGION_AU915 */ +} + +LoRaMacStatus_t RegionAU915NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ) +{ +#if defined( REGION_AU915 ) + uint8_t nbEnabledChannels = 0; + uint8_t nbRestrictedChannels = 0; + uint8_t enabledChannels[AU915_MAX_NB_CHANNELS] = { 0 }; + RegionCommonIdentifyChannelsParam_t identifyChannelsParam; + RegionCommonCountNbOfEnabledChannelsParams_t countChannelsParams; + LoRaMacStatus_t status = LORAMAC_STATUS_NO_CHANNEL_FOUND; + + // Count 125kHz channels + if( RegionCommonCountChannels( RegionNvmGroup1->ChannelsMaskRemaining, 0, 4 ) == 0 ) + { // Reactivate default channels + RegionCommonChanMaskCopy( RegionNvmGroup1->ChannelsMaskRemaining, RegionNvmGroup2->ChannelsMask, 4 ); + + RegionNvmGroup1->JoinChannelGroupsCurrentIndex = 0; + } + // Check other channels + if( nextChanParams->Datarate >= DR_6 ) + { + if( ( RegionNvmGroup1->ChannelsMaskRemaining[4] & CHANNELS_MASK_500KHZ_MASK ) == 0 ) + { + RegionNvmGroup1->ChannelsMaskRemaining[4] = RegionNvmGroup2->ChannelsMask[4]; + } + } + + // Search how many channels are enabled + countChannelsParams.Joined = nextChanParams->Joined; + countChannelsParams.Datarate = nextChanParams->Datarate; + countChannelsParams.ChannelsMask = RegionNvmGroup1->ChannelsMaskRemaining; + countChannelsParams.Channels = RegionNvmGroup2->Channels; +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + countChannelsParams.Bands = RegionNvmGroup1->Bands; +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + countChannelsParams.Bands = RegionBands; +#endif /* REGION_VERSION */ + countChannelsParams.MaxNbChannels = AU915_MAX_NB_CHANNELS; + countChannelsParams.JoinChannels = NULL; + + identifyChannelsParam.AggrTimeOff = nextChanParams->AggrTimeOff; + identifyChannelsParam.LastAggrTx = nextChanParams->LastAggrTx; + identifyChannelsParam.DutyCycleEnabled = nextChanParams->DutyCycleEnabled; + identifyChannelsParam.MaxBands = AU915_MAX_NB_BANDS; + + identifyChannelsParam.ElapsedTimeSinceStartUp = nextChanParams->ElapsedTimeSinceStartUp; + identifyChannelsParam.LastTxIsJoinRequest = nextChanParams->LastTxIsJoinRequest; + identifyChannelsParam.ExpectedTimeOnAir = GetTimeOnAir( nextChanParams->Datarate, nextChanParams->PktLen ); + + identifyChannelsParam.CountNbOfEnabledChannelsParam = &countChannelsParams; + + status = RegionCommonIdentifyChannels( &identifyChannelsParam, aggregatedTimeOff, enabledChannels, + &nbEnabledChannels, &nbRestrictedChannels, time ); + + if( status == LORAMAC_STATUS_OK ) + { + 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) DR2 + if( nextChanParams->Datarate == DR_2 ) + { + if( RegionBaseUSComputeNext125kHzJoinChannel( ( uint16_t* ) RegionNvmGroup1->ChannelsMaskRemaining, + &RegionNvmGroup1->JoinChannelGroupsCurrentIndex, channel ) == LORAMAC_STATUS_PARAMETER_INVALID ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + } + // 500kHz Channels (64 - 71) DR6 + else + { + // Choose the next available channel + uint8_t i = 0; + while( ( ( RegionNvmGroup1->ChannelsMaskRemaining[4] & CHANNELS_MASK_500KHZ_MASK ) & ( 1 << i ) ) == 0 ) + { + i++; + } + *channel = 64 + i; + } + } + + // Disable the channel in the mask + RegionCommonChanDisable( RegionNvmGroup1->ChannelsMaskRemaining, *channel, AU915_MAX_NB_CHANNELS ); + } + return status; +#else + return LORAMAC_STATUS_NO_CHANNEL_FOUND; +#endif /* REGION_AU915 */ +} + +LoRaMacStatus_t RegionAU915ChannelAdd( ChannelAddParams_t* channelAdd ) +{ + return LORAMAC_STATUS_PARAMETER_INVALID; +} + +bool RegionAU915ChannelsRemove( ChannelRemoveParams_t* channelRemove ) +{ + return LORAMAC_STATUS_PARAMETER_INVALID; +} + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +void RegionAU915SetContinuousWave( ContinuousWaveParams_t* continuousWave ) +{ +#if defined( REGION_AU915 ) + int8_t txPowerLimited = RegionCommonLimitTxPower( continuousWave->TxPower, RegionNvmGroup1->Bands[RegionNvmGroup2->Channels[continuousWave->Channel].Band].TxMaxPower ); + int8_t phyTxPower = 0; + uint32_t frequency = RegionNvmGroup2->Channels[continuousWave->Channel].Frequency; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, continuousWave->MaxEirp, continuousWave->AntennaGain ); + + Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout ); +#endif /* REGION_AU915 */ +} +#endif /* REGION_VERSION */ + +uint8_t RegionAU915ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) +{ +#if defined( REGION_AU915 ) + 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; +#else + return 0; +#endif /* REGION_AU915 */ +} + +void RegionAU915RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ) +{ +#if defined( REGION_AU915 ) + 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; +#endif /* REGION_AU915 */ +} diff --git a/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionAU915.h b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionAU915.h new file mode 100644 index 0000000..15c9049 --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionAU915.h @@ -0,0 +1,543 @@ +/*! + * \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. + * \{ + */ +/** + ****************************************************************************** + * + * Portions COPYRIGHT 2020 STMicroelectronics + * + * @file RegionAU915.h + * @author MCD Application Team + * @brief Region definition for AU915 + ****************************************************************************** + */ +#ifndef __REGION_AU915_H__ +#define __REGION_AU915_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "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 + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +/*! + * Maximal datarate that can be used by the node + */ +#define AU915_TX_MAX_DATARATE DR_6 +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) +/*! + * Maximal datarate that can be used by the node + */ +#define AU915_TX_MAX_DATARATE DR_13 +#endif /* REGION_VERSION */ + +/*! + * 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 5 + +/*! + * 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 Max EIRP + */ +#define AU915_DEFAULT_MAX_EIRP 30.0f + +/*! + * Default antenna gain + */ +#define AU915_DEFAULT_ANTENNA_GAIN 2.15f + +/*! + * Enabled or disabled the duty cycle + */ +#define AU915_DUTY_CYCLE_ENABLED 0 + +/*! + * Maximum RX window duration + */ +#define AU915_MAX_RX_WINDOW 3000 + +/*! + * 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 + +/*! + * Ping slot channel frequency + */ +#define AU915_PING_SLOT_CHANNEL_FREQ 923300000 + +/*! + * Number of possible beacon channels + */ +#define AU915_BEACON_NB_CHANNELS 8 + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +/* ST_WORKAROUND_BEGIN: Regional Parameters 1.0.3.revA ERRATA not yet available */ +/*! + * Payload size of a beacon frame + */ +#define AU915_BEACON_SIZE 19 +/* #define AU915_BEACON_SIZE 23 */ + +/*! + * Size of RFU 1 field + */ +#define AU915_RFU1_SIZE 3 +/* #define AU915_RFU1_SIZE 5 */ + +/*! + * Size of RFU 2 field + */ +#define AU915_RFU2_SIZE 1 +/* #define AU915_RFU2_SIZE 3 */ +/* ST_WORKAROUND_END */ +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) +/*! + * Payload size of a beacon frame + */ +#define AU915_BEACON_SIZE 23 + +/*! + * Size of RFU 1 field + */ +#define AU915_RFU1_SIZE 4 + +/*! + * Size of RFU 2 field + */ +#define AU915_RFU2_SIZE 3 +#endif /* REGION_VERSION */ +/*! + * Datarate of the beacon channel + */ +#define AU915_BEACON_CHANNEL_DR DR_8 + +/*! + * Bandwidth of the beacon channel + */ +#define AU915_BEACON_CHANNEL_BW 2 + +/*! + * Ping slot channel datarate + */ +#define AU915_PING_SLOT_CHANNEL_DR DR_8 + +/*! + * LoRaMac maximum number of bands + */ +#define AU915_MAX_NB_BANDS 1 + +/*! + * Band 0 definition + * Band = { DutyCycle, TxMaxPower, LastBandUpdateTime, LastMaxCreditAssignTime, TimeCredits, MaxTimeCredits, ReadyForTransmission } + */ +#define AU915_BAND0 { 1, AU915_MAX_TX_POWER, 0, 0, 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 +}; + +/* ST_WORKAROUND_BEGIN: Keep repeater feature */ +/*! + * 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. + */ +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +static const uint8_t MaxPayloadOfDatarateRepeaterDwell1AU915[] = { 0, 0, 11, 53, 125, 242, 242, 0, 33, 109, 222, 222, 222, 222 }; +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) +static const uint8_t MaxPayloadOfDatarateRepeaterDwell1AU915[] = { 0, 0, 11, 53, 125, 222, 222, 0, 33, 109, 222, 222, 222, 222 }; +#endif /* REGION_VERSION */ +/* ST_WORKAROUND_END */ + +/*! + * \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] params Sets the initialization type. + */ +void RegionAU915InitDefaults( InitDefaultsParams_t* params ); + +/*! + * \brief Verifies a parameter. + * + * \param [in] verify Pointer to the function parameters. + * + * \param [in] phyAttribute 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. + * + * \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 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. + */ +int8_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. + */ +int8_t RegionAU915DlChannelReq( 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 RegionAU915AlternateDr( int8_t currentDr, AlternateDrType_t type ); + +/*! + * \brief Searches and set the next random available channel + * + * \param [in] nextChanParams pointer of selected channel parameters + * + * \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 ); + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +/*! + * \brief Sets the radio into continuous wave mode. + * + * \param [IN] continuousWave Pointer to the function parameters. + */ +void RegionAU915SetContinuousWave( ContinuousWaveParams_t* continuousWave ); +#endif /* REGION_VERSION */ + +/*! + * \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 + * + * \param [out] outDr Datarate used to receive the beacon + */ + void RegionAU915RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ); + +/*! \} defgroup REGIONAU915 */ + +#ifdef __cplusplus +} +#endif + +#endif // __REGION_AU915_H__ diff --git a/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionBaseUS.c b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionBaseUS.c new file mode 100644 index 0000000..2724a69 --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionBaseUS.c @@ -0,0 +1,151 @@ +/*! + * \file RegionBaseUS.c + * + * \brief Implementations common with US region. + * + * \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 Phanindra Kumar Yellapu ( STACKFORCE ) + */ +#include "../LoRaMacTypes.h" +#include "Region.h" +#include "RegionBaseUS.h" + +/*! + * \brief Searches for available 125 kHz channels in the given channel mask. + * + * \param [in] currentChannelMaskLeft 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( uint16_t currentChannelMaskLeft, + uint8_t* findAvailableChannelsIndex, 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( ( currentChannelMaskLeft & ( 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; +} + +LoRaMacStatus_t RegionBaseUSComputeNext125kHzJoinChannel( uint16_t* channelsMaskRemaining, + uint8_t* groupsCurrentIndex, uint8_t* newChannelIndex ) +{ + uint8_t currentChannelMaskLeftIndex; + uint16_t currentChannelMaskLeft; + uint8_t findAvailableChannelsIndex[8] = { 0 }; + uint8_t availableChannels = 0; + uint8_t startIndex; + + // Null pointer check + if( channelsMaskRemaining == NULL || groupsCurrentIndex == NULL || newChannelIndex == NULL ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + // copy the current index. + startIndex = *groupsCurrentIndex; + + do + { + // Current ChannelMaskRemaining, two groups per channel mask. For example Group 0 and 1 (8 bit) are ChannelMaskRemaining 0 (16 bit), etc. + currentChannelMaskLeftIndex = (uint8_t) startIndex / 2; + + // For even numbers we need the 8 LSBs and for uneven the 8 MSBs + if( ( startIndex % 2 ) == 0 ) + { + currentChannelMaskLeft = ( channelsMaskRemaining[currentChannelMaskLeftIndex] & 0x00FF ); + } + else + { + currentChannelMaskLeft = ( ( channelsMaskRemaining[currentChannelMaskLeftIndex] >> 8 ) & 0x00FF ); + } + + if( FindAvailable125kHzChannels( currentChannelMaskLeft, findAvailableChannelsIndex, &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 != *groupsCurrentIndex ) ); + + if ( availableChannels > 0 ) + { + *groupsCurrentIndex = startIndex; + return LORAMAC_STATUS_OK; + } + + return LORAMAC_STATUS_PARAMETER_INVALID; +} + +bool RegionBaseUSVerifyFrequencyGroup( uint32_t freq, uint32_t minFreq, uint32_t maxFreq, uint32_t stepwidth ) +{ + if( ( freq < minFreq ) || + ( freq > maxFreq ) || + ( ( ( freq - ( uint32_t ) minFreq ) % ( uint32_t ) stepwidth ) != 0 ) ) + { + return false; + } + return true; +} + +uint32_t RegionBaseUSCalcDownlinkFrequency( uint8_t channel, uint32_t frequency, + uint32_t stepwidth ) +{ + // Calculate the frequency + return frequency + ( channel * stepwidth ); +} diff --git a/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionBaseUS.h b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionBaseUS.h new file mode 100644 index 0000000..d92ba11 --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionBaseUS.h @@ -0,0 +1,98 @@ +/*! + * \file RegionBaseUS.h + * + * \brief Definitions common with US region. + * + * \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 Phanindra Kumar Yellapu ( STACKFORCE ) + * + * \defgroup REGIONBASEUS US region common implementations. + * \{ + */ +#ifndef __REGIONBASEUS_H__ +#define __REGIONBASEUS_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "../LoRaMac.h" + +/*! + * \brief Computes the next 125kHz channel used for join requests. + * And it returns all the parameters updated. + * + * \param [in] channelsMaskRemaining pointer to remaining channels. + * + * \param [in] groupsCurrentIndex Index of current channel. + * + * \param [out] newChannelIndex Index of next available channel. + * + * \retval Status + */ +LoRaMacStatus_t RegionBaseUSComputeNext125kHzJoinChannel( uint16_t* channelsMaskRemaining, + uint8_t* groupsCurrentIndex, uint8_t* newChannelIndex ); + +/*! + * \brief Verifies if the frequency is in the correct range with a + * specific stepwidth. + * + * \param [in] freq Frequency to verify. + * + * \param [in] minFreq Minimum frequency. + * + * \param [in] maxFreq Maximum frequency. + * + * \param [in] stepwidth Frequency stepwidth. + * + * \retval True, if the frequency is valid, false if not. + */ +bool RegionBaseUSVerifyFrequencyGroup( uint32_t freq, uint32_t minFreq, uint32_t maxFreq, uint32_t stepwidth ); + +/*! + * \brief Calculates the downlink frequency for a given channel. This + * function is used in class B only. + * + * \param [in] channel The channel according to the channel plan. + * + * \param [in] frequency The base frequency. + * + * \param [in] stepwidth The frequency stepwidth. + * + * \retval The downlink frequency. + */ +uint32_t RegionBaseUSCalcDownlinkFrequency( uint8_t channel, uint32_t frequency, + uint32_t stepwidth ); + +/*! \} defgroup REGIONBASEUS */ + +#ifdef __cplusplus +} +#endif + +#endif // __REGIONBASEUS_H__ diff --git a/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCN470.c b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCN470.c new file mode 100644 index 0000000..2ac30b3 --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCN470.c @@ -0,0 +1,1420 @@ +/*! + * \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 ) +*/ +/** + ****************************************************************************** + * + * Portions COPYRIGHT 2020 STMicroelectronics + * + * @file RegionCN470.c + * @author MCD Application Team + * @brief Region implementation for CN470 + ****************************************************************************** + */ +#include "../../../SubGHz_Phy/radio.h" +#include "RegionCN470.h" +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +#include "RegionBaseUS.h" +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) +#include "RegionCN470A20.h" +#include "RegionCN470B20.h" +#include "RegionCN470A26.h" +#include "RegionCN470B26.h" +#endif /* REGION_VERSION */ + +// Definitions +#define CHANNELS_MASK_SIZE 6 + +/* The HYBRID_DEFAULT_MASKx define the enabled channels in Hybrid mode*/ +/* Note: they can be redefined in lorawan_conf.h*/ +#ifndef HYBRID_DEFAULT_MASK0 /*enabled channels from channel 15 down to channel 0*/ +#define HYBRID_DEFAULT_MASK0 0x00FF /*channel 7 down to channel 0 enabled*/ +#endif +#ifndef HYBRID_DEFAULT_MASK1 /*enabled channels from channel 31 down to channel 16*/ +#define HYBRID_DEFAULT_MASK1 0x0000 +#endif +#ifndef HYBRID_DEFAULT_MASK2 /*enabled channels from channel 47 down to channel 32*/ +#define HYBRID_DEFAULT_MASK2 0x0000 +#endif +#ifndef HYBRID_DEFAULT_MASK3 /*enabled channels from channel 63 down to channel 48*/ +#define HYBRID_DEFAULT_MASK3 0x0000 +#endif +#ifndef HYBRID_DEFAULT_MASK4 /*enabled channels from channel 79 down to channel 64*/ +#define HYBRID_DEFAULT_MASK4 0x0000 +#endif +#ifndef HYBRID_DEFAULT_MASK5 /*enabled channels from channel 95 down to channel 80*/ +#define HYBRID_DEFAULT_MASK5 0x0000 +#endif + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) +#ifndef REGION_CN470_DEFAULT_CHANNEL_PLAN +#define REGION_CN470_DEFAULT_CHANNEL_PLAN CHANNEL_PLAN_20MHZ_TYPE_A +#endif + +#ifndef REGION_CN470_DEFAULT_RX_WND_2_FREQ +#define REGION_CN470_DEFAULT_RX_WND_2_FREQ CN470_A20_RX_WND_2_FREQ_ABP +#endif + +ChannelParams_t CommonJoinChannels[] = CN470_COMMON_JOIN_CHANNELS; + +/*! + * Definition of the regional channel plan. + */ +typedef struct sRegionCN470ChannelPlanCtx +{ + /*! + * Size of the channels mask. Must be smaller + * or equal than CHANNELS_MASK_SIZE. + */ + uint8_t ChannelsMaskSize; + /*! + * Number of elements in the join accept list. + */ + uint8_t JoinAcceptListSize; + /*! + * Number of available channels for beaconing. + */ + uint8_t NbBeaconChannels; + /*! + * Number of available channels for ping slots. + */ + uint8_t NbPingSlotChannels; + /*! + * \brief Calculation of the beacon frequency. + * + * \param [in] channel The Beacon channel number. + * + * \param [in] joinChannelIndex The join channel index. + * + * \param [in] isPingSlot Set to true, if its a ping slot. + * + * \retval Returns the beacon frequency. + */ + uint32_t ( *GetDownlinkFrequency )( uint8_t channel, uint8_t joinChannelIndex, bool isPingSlot ); + /*! + * \brief Performs the update of the channelsMask based on the input parameters. + * + * \param [in] joinChannelIndex The join channel index. + * + * \retval Returns the offset for the given join channel. + */ + uint8_t ( *GetBeaconChannelOffset )( uint8_t joinChannelIndex ); + /*! + * \brief Performs the update of the channelsMask based on the input parameters. + * + * \param [in] channelsMask A pointer to the channels mask. + * + * \param [in] chMaskCntl The value of the chMaskCntl field of the LinkAdrReq. + * + * \param [in] chanMask The value of the chanMask field of the LinkAdrReq. + * + * \param [in] channels A pointer to the available channels. + * + * \retval Status of the operation. Return 0x07 if the channels mask is valid. + */ + uint8_t ( *LinkAdrChMaskUpdate )( uint16_t* channelsMask, uint8_t chMaskCntl, + uint16_t chanMask, ChannelParams_t* channels ); + /*! + * \brief Verifies if the frequency provided is valid. + * + * \param [in] frequency The frequency to verify. + * + * \retval Returns true, if the frequency is valid. + */ + bool ( *VerifyRfFreq )( uint32_t frequency ); + /*! + * \brief Initializes all channels, datarates, frequencies and bands. + * + * \param [in] channels A pointer to the available channels. + */ + void ( *InitializeChannels )( ChannelParams_t* channels ); + /*! + * \brief Initializes the channels mask and the channels default mask. + * + * \param [in] channelsDefaultMask A pointer to the channels default mask. + */ + void ( *InitializeChannelsMask )( uint16_t* channelsDefaultMask ); + /*! + * \brief Computes the frequency for the RX1 window. + * + * \param [in] channel The channel utilized currently. + * + * \retval Returns the frequency which shall be used. + */ + uint32_t ( *GetRx1Frequency )( uint8_t channel ); + /*! + * \brief Computes the frequency for the RX2 window. + * + * \param [in] joinChannelIndex The join channel index. + * + * \param [in] isOtaaDevice Set to true, if the device is an OTAA device. + * + * \retval Returns the frequency which shall be used. + */ + uint32_t ( *GetRx2Frequency )( uint8_t joinChannelIndex, bool isOtaaDevice ); +}RegionCN470ChannelPlanCtx_t; +#endif /* REGION_VERSION */ + +#if defined( REGION_CN470 ) +/* + * Non-volatile module context. + */ +static RegionNvmDataGroup1_t* RegionNvmGroup1; +static RegionNvmDataGroup2_t* RegionNvmGroup2; +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) +static Band_t* RegionBands; + +/* + * Context for the current channel plan. + */ +static RegionCN470ChannelPlanCtx_t ChannelPlanCtx; + +// Static functions +static void ApplyChannelPlanConfig( RegionCN470ChannelPlan_t channelPlan, RegionCN470ChannelPlanCtx_t* ctx ) +{ + switch( channelPlan ) + { + case CHANNEL_PLAN_20MHZ_TYPE_A: + { + ctx->ChannelsMaskSize = CN470_A20_CHANNELS_MASK_SIZE; + ctx->JoinAcceptListSize = CN470_A20_JOIN_ACCEPT_LIST_SIZE; + ctx->NbBeaconChannels = CN470_A20_BEACON_NB_CHANNELS; + ctx->NbPingSlotChannels = CN470_A20_PING_SLOT_NB_CHANNELS; + ctx->GetDownlinkFrequency = RegionCN470A20GetDownlinkFrequency; + ctx->GetBeaconChannelOffset = RegionCN470A20GetBeaconChannelOffset; + ctx->LinkAdrChMaskUpdate = RegionCN470A20LinkAdrChMaskUpdate; + ctx->VerifyRfFreq = RegionCN470A20VerifyRfFreq; + ctx->InitializeChannels = RegionCN470A20InitializeChannels; + ctx->InitializeChannelsMask = RegionCN470A20InitializeChannelsMask; + ctx->GetRx1Frequency = RegionCN470A20GetRx1Frequency; + ctx->GetRx2Frequency = RegionCN470A20GetRx2Frequency; + break; + } + case CHANNEL_PLAN_20MHZ_TYPE_B: + { + ctx->ChannelsMaskSize = CN470_B20_CHANNELS_MASK_SIZE; + ctx->JoinAcceptListSize = CN470_B20_JOIN_ACCEPT_LIST_SIZE; + ctx->NbBeaconChannels = CN470_B20_BEACON_NB_CHANNELS; + ctx->NbPingSlotChannels = CN470_B20_PING_SLOT_NB_CHANNELS; + ctx->GetDownlinkFrequency = RegionCN470B20GetDownlinkFrequency; + ctx->GetBeaconChannelOffset = RegionCN470B20GetBeaconChannelOffset; + ctx->LinkAdrChMaskUpdate = RegionCN470B20LinkAdrChMaskUpdate; + ctx->VerifyRfFreq = RegionCN470B20VerifyRfFreq; + ctx->InitializeChannels = RegionCN470B20InitializeChannels; + ctx->InitializeChannelsMask = RegionCN470B20InitializeChannelsMask; + ctx->GetRx1Frequency = RegionCN470B20GetRx1Frequency; + ctx->GetRx2Frequency = RegionCN470B20GetRx2Frequency; + break; + } + case CHANNEL_PLAN_26MHZ_TYPE_A: + { + ctx->ChannelsMaskSize = CN470_A26_CHANNELS_MASK_SIZE; + ctx->JoinAcceptListSize = CN470_A26_JOIN_ACCEPT_LIST_SIZE; + ctx->NbBeaconChannels = CN470_A26_BEACON_NB_CHANNELS; + ctx->NbPingSlotChannels = CN470_A26_PING_SLOT_NB_CHANNELS; + ctx->GetDownlinkFrequency = RegionCN470A26GetDownlinkFrequency; + ctx->GetBeaconChannelOffset = RegionCN470A26GetBeaconChannelOffset; + ctx->LinkAdrChMaskUpdate = RegionCN470A26LinkAdrChMaskUpdate; + ctx->VerifyRfFreq = RegionCN470A26VerifyRfFreq; + ctx->InitializeChannels = RegionCN470A26InitializeChannels; + ctx->InitializeChannelsMask = RegionCN470A26InitializeChannelsMask; + ctx->GetRx1Frequency = RegionCN470A26GetRx1Frequency; + ctx->GetRx2Frequency = RegionCN470A26GetRx2Frequency; + break; + } + case CHANNEL_PLAN_26MHZ_TYPE_B: + { + ctx->ChannelsMaskSize = CN470_B26_CHANNELS_MASK_SIZE; + ctx->JoinAcceptListSize = CN470_B26_JOIN_ACCEPT_LIST_SIZE; + ctx->NbBeaconChannels = CN470_B26_BEACON_NB_CHANNELS; + ctx->NbPingSlotChannels = CN470_B26_PING_SLOT_NB_CHANNELS; + ctx->GetDownlinkFrequency = RegionCN470B26GetDownlinkFrequency; + ctx->GetBeaconChannelOffset = RegionCN470B26GetBeaconChannelOffset; + ctx->LinkAdrChMaskUpdate = RegionCN470B26LinkAdrChMaskUpdate; + ctx->VerifyRfFreq = RegionCN470B26VerifyRfFreq; + ctx->InitializeChannels = RegionCN470B26InitializeChannels; + ctx->InitializeChannelsMask = RegionCN470B26InitializeChannelsMask; + ctx->GetRx1Frequency = RegionCN470B26GetRx1Frequency; + ctx->GetRx2Frequency = RegionCN470B26GetRx2Frequency; + break; + } + default: + { + // Apply CHANNEL_PLAN_20MHZ_TYPE_A + ctx->ChannelsMaskSize = CN470_A20_CHANNELS_MASK_SIZE; + ctx->JoinAcceptListSize = CN470_A20_JOIN_ACCEPT_LIST_SIZE; + ctx->NbBeaconChannels = CN470_A20_BEACON_NB_CHANNELS; + ctx->NbPingSlotChannels = CN470_A20_PING_SLOT_NB_CHANNELS; + ctx->GetDownlinkFrequency = RegionCN470A20GetDownlinkFrequency; + ctx->GetBeaconChannelOffset = RegionCN470A20GetBeaconChannelOffset; + ctx->LinkAdrChMaskUpdate = RegionCN470A20LinkAdrChMaskUpdate; + ctx->VerifyRfFreq = RegionCN470A20VerifyRfFreq; + ctx->InitializeChannels = RegionCN470A20InitializeChannels; + ctx->InitializeChannelsMask = RegionCN470A20InitializeChannelsMask; + ctx->GetRx1Frequency = RegionCN470A20GetRx1Frequency; + ctx->GetRx2Frequency = RegionCN470A20GetRx2Frequency; + break; + } + } +} + +static RegionCN470ChannelPlan_t IdentifyChannelPlan( uint8_t joinChannel ) +{ + RegionCN470ChannelPlan_t channelPlan = CHANNEL_PLAN_UNKNOWN; + + if( joinChannel <= 7 ) + { + channelPlan = CHANNEL_PLAN_20MHZ_TYPE_A; + } + else if ( ( joinChannel <= 9 ) && ( joinChannel >= 8 ) ) + { + channelPlan = CHANNEL_PLAN_20MHZ_TYPE_B; + } + else if ( ( joinChannel <= 14 ) && ( joinChannel >= 10 ) ) + { + channelPlan = CHANNEL_PLAN_26MHZ_TYPE_A; + } + else if( ( joinChannel <= 19 ) && ( joinChannel >= 15 ) ) + { + channelPlan = CHANNEL_PLAN_26MHZ_TYPE_B; + } + return channelPlan; +} +#endif /* REGION_VERSION */ + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +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 frequencies into account + if( ( freq < 470300000 ) || ( freq > 509700000 ) ) + { + return false; + } + return true; +} +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) +static bool VerifyRfFreq( uint32_t frequency ) +{ + // Check radio driver support + if( Radio.CheckRfFrequency( frequency ) == false ) + { + return false; + } + + return ChannelPlanCtx.VerifyRfFreq( frequency ); +} +#endif /* REGION_VERSION */ + +static TimerTime_t GetTimeOnAir( int8_t datarate, uint16_t pktLen ) +{ + int8_t phyDr = DataratesCN470[datarate]; + uint32_t bandwidth = RegionCommonGetBandwidth( datarate, BandwidthsCN470 ); + + return Radio.TimeOnAir( MODEM_LORA, bandwidth, phyDr, 1, 8, false, pktLen, true ); +} +#endif /* REGION_CN470 */ + +PhyParam_t RegionCN470GetPhyParam( GetPhyParams_t* getPhy ) +{ + PhyParam_t phyParam = { 0 }; + +#if defined( REGION_CN470 ) + 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: + { + RegionCommonGetNextLowerTxDrParams_t nextLowerTxDrParams = + { + .CurrentDr = getPhy->Datarate, + .MaxDr = ( int8_t )CN470_TX_MAX_DATARATE, + .MinDr = ( int8_t )CN470_TX_MIN_DATARATE, + .NbChannels = CN470_MAX_NB_CHANNELS, + .ChannelsMask = RegionNvmGroup2->ChannelsMask, + .Channels = RegionNvmGroup2->Channels, + }; + phyParam.Value = RegionCommonGetNextLowerTxDr( &nextLowerTxDrParams ); + 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 = REGION_COMMON_DEFAULT_ADR_ACK_LIMIT; + break; + } + case PHY_DEF_ADR_ACK_DELAY: + { + phyParam.Value = REGION_COMMON_DEFAULT_ADR_ACK_DELAY; + break; + } + case PHY_MAX_PAYLOAD: + { + phyParam.Value = MaxPayloadOfDatarateCN470[getPhy->Datarate]; + break; + } + /* ST_WORKAROUND_BEGIN: Keep repeater feature */ + case PHY_MAX_PAYLOAD_REPEATER: + { + phyParam.Value = MaxPayloadOfDatarateRepeaterCN470[getPhy->Datarate]; + break; + } + /* ST_WORKAROUND_END */ + 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 = REGION_COMMON_DEFAULT_RECEIVE_DELAY1; + break; + } + case PHY_RECEIVE_DELAY2: + { + phyParam.Value = REGION_COMMON_DEFAULT_RECEIVE_DELAY2; + break; + } + case PHY_JOIN_ACCEPT_DELAY1: + { + phyParam.Value = REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY1; + break; + } + case PHY_JOIN_ACCEPT_DELAY2: + { + phyParam.Value = REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY2; + break; + } +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + case PHY_MAX_FCNT_GAP: + { + phyParam.Value = REGION_COMMON_DEFAULT_MAX_FCNT_GAP; + break; + } + case PHY_ACK_TIMEOUT: + { + phyParam.Value = ( REGION_COMMON_DEFAULT_ACK_TIMEOUT + randr( -REGION_COMMON_DEFAULT_ACK_TIMEOUT_RND, REGION_COMMON_DEFAULT_ACK_TIMEOUT_RND ) ); + break; + } +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + case PHY_RETRANSMIT_TIMEOUT: + { + phyParam.Value = ( REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT + randr( -REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT_RND, REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT_RND ) ); + break; + } +#endif /* REGION_VERSION */ + case PHY_DEF_DR1_OFFSET: + { + phyParam.Value = REGION_COMMON_DEFAULT_RX1_DR_OFFSET; + break; + } + case PHY_DEF_RX2_FREQUENCY: + { +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + phyParam.Value = CN470_RX_WND_2_FREQ; +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + phyParam.Value = REGION_CN470_DEFAULT_RX_WND_2_FREQ; + + if( RegionNvmGroup2->ChannelPlan != CHANNEL_PLAN_UNKNOWN ) + { + phyParam.Value = ChannelPlanCtx.GetRx2Frequency( RegionNvmGroup2->CommonJoinChannelIndex, RegionNvmGroup2->IsOtaaDevice ); + } +#endif /* REGION_VERSION */ + break; + } + case PHY_DEF_RX2_DR: + { + phyParam.Value = CN470_RX_WND_2_DR; + break; + } + case PHY_CHANNELS_MASK: + { + phyParam.ChannelsMask = RegionNvmGroup2->ChannelsMask; + break; + } + case PHY_CHANNELS_DEFAULT_MASK: + { + phyParam.ChannelsMask = RegionNvmGroup2->ChannelsDefaultMask; + break; + } + case PHY_MAX_NB_CHANNELS: + { + phyParam.Value = CN470_MAX_NB_CHANNELS; + break; + } + case PHY_CHANNELS: + { + phyParam.Channels = RegionNvmGroup2->Channels; + break; + } + case PHY_DEF_UPLINK_DWELL_TIME: + { + phyParam.Value = CN470_DEFAULT_UPLINK_DWELL_TIME; + break; + } + case PHY_DEF_DOWNLINK_DWELL_TIME: + { + phyParam.Value = REGION_COMMON_DEFAULT_DOWNLINK_DWELL_TIME; + 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_CHANNEL_FREQ: + { +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + phyParam.Value = RegionBaseUSCalcDownlinkFrequency( getPhy->Channel, + CN470_BEACON_CHANNEL_FREQ, + CN470_BEACON_CHANNEL_STEPWIDTH ); +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + phyParam.Value = REGION_CN470_DEFAULT_RX_WND_2_FREQ; + + // Implementation depending on the join channel + if( RegionNvmGroup2->ChannelPlan != CHANNEL_PLAN_UNKNOWN ) + { + phyParam.Value = ChannelPlanCtx.GetDownlinkFrequency( getPhy->Channel, + RegionNvmGroup2->CommonJoinChannelIndex, + false ); + } +#endif /* REGION_VERSION */ + 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; + } +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + case PHY_BEACON_NB_CHANNELS: + { + phyParam.Value = CN470_BEACON_NB_CHANNELS; + break; + } + case PHY_PING_SLOT_CHANNEL_FREQ: + { + phyParam.Value = RegionBaseUSCalcDownlinkFrequency( getPhy->Channel, + CN470_PING_SLOT_CHANNEL_FREQ, + CN470_BEACON_CHANNEL_STEPWIDTH ); + break; + } + case PHY_PING_SLOT_CHANNEL_DR: + { + phyParam.Value = CN470_PING_SLOT_CHANNEL_DR; + break; + } + case PHY_PING_SLOT_NB_CHANNELS: + { + phyParam.Value = CN470_BEACON_NB_CHANNELS; + break; + } +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + case PHY_BEACON_NB_CHANNELS: + { + // Implementation depending on the join channel + if( RegionNvmGroup2->ChannelPlan != CHANNEL_PLAN_UNKNOWN ) + { + phyParam.Value = ChannelPlanCtx.NbBeaconChannels; + } + break; + } + + case PHY_BEACON_CHANNEL_OFFSET: + { + // Implementation depending on the join channel + if( RegionNvmGroup2->ChannelPlan != CHANNEL_PLAN_UNKNOWN ) + { + phyParam.Value = ChannelPlanCtx.GetBeaconChannelOffset( RegionNvmGroup2->CommonJoinChannelIndex ); + } + break; + } + case PHY_PING_SLOT_CHANNEL_FREQ: + { + phyParam.Value = REGION_CN470_DEFAULT_RX_WND_2_FREQ; + + // Implementation depending on the join channel + if( RegionNvmGroup2->ChannelPlan != CHANNEL_PLAN_UNKNOWN ) + { + phyParam.Value = ChannelPlanCtx.GetDownlinkFrequency( getPhy->Channel, + RegionNvmGroup2->CommonJoinChannelIndex, + true ); + } + break; + } + case PHY_PING_SLOT_CHANNEL_DR: + { + phyParam.Value = CN470_PING_SLOT_CHANNEL_DR; + break; + } + case PHY_PING_SLOT_NB_CHANNELS: + { + // Implementation depending on the join channel + if( RegionNvmGroup2->ChannelPlan != CHANNEL_PLAN_UNKNOWN ) + { + phyParam.Value = ChannelPlanCtx.NbPingSlotChannels; + } + break; + } +#endif /* REGION_VERSION */ + case PHY_SF_FROM_DR: + { + phyParam.Value = DataratesCN470[getPhy->Datarate]; + break; + } + case PHY_BW_FROM_DR: + { + phyParam.Value = RegionCommonGetBandwidth( getPhy->Datarate, BandwidthsCN470 ); + break; + } + default: + { + break; + } + } + +#endif /* REGION_CN470 */ + return phyParam; +} + +void RegionCN470SetBandTxDone( SetBandTxDoneParams_t* txDone ) +{ +#if defined( REGION_CN470 ) +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + RegionCommonSetBandTxDone( &RegionNvmGroup1->Bands[RegionNvmGroup2->Channels[txDone->Channel].Band], + txDone->LastTxAirTime, txDone->Joined, txDone->ElapsedTimeSinceStartUp ); +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + RegionCommonSetBandTxDone( &RegionBands[RegionNvmGroup2->Channels[txDone->Channel].Band], + txDone->LastTxAirTime, txDone->Joined, txDone->ElapsedTimeSinceStartUp ); +#endif /* REGION_VERSION */ +#endif /* REGION_CN470 */ +} + +void RegionCN470InitDefaults( InitDefaultsParams_t* params ) +{ +#if defined( REGION_CN470 ) + Band_t bands[CN470_MAX_NB_BANDS] = + { + CN470_BAND0 + }; + + switch( params->Type ) + { + case INIT_TYPE_DEFAULTS: + { + if( ( params->NvmGroup1 == NULL ) || ( params->NvmGroup2 == NULL ) ) + { + return; + } + + RegionNvmGroup1 = (RegionNvmDataGroup1_t*) params->NvmGroup1; + RegionNvmGroup2 = (RegionNvmDataGroup2_t*) params->NvmGroup2; +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + + // Default bands + memcpy1( ( uint8_t* )RegionNvmGroup1->Bands, ( uint8_t* )bands, sizeof( Band_t ) * CN470_MAX_NB_BANDS ); + + // Default channels + for( uint8_t i = 0; i < CN470_MAX_NB_CHANNELS; i++ ) + { + // 125 kHz channels + RegionNvmGroup2->Channels[i].Frequency = 470300000 + i * 200000; + RegionNvmGroup2->Channels[i].DrRange.Value = ( DR_5 << 4 ) | DR_0; + RegionNvmGroup2->Channels[i].Band = 0; + } + + // Default ChannelsMask + /* ST_WORKAROUND_BEGIN: Hybrid mode */ +#if ( HYBRID_ENABLED == 1 ) + RegionNvmGroup2->ChannelsDefaultMask[0] = HYBRID_DEFAULT_MASK0; + RegionNvmGroup2->ChannelsDefaultMask[1] = HYBRID_DEFAULT_MASK1; + RegionNvmGroup2->ChannelsDefaultMask[2] = HYBRID_DEFAULT_MASK2; + RegionNvmGroup2->ChannelsDefaultMask[3] = HYBRID_DEFAULT_MASK3; + RegionNvmGroup2->ChannelsDefaultMask[4] = HYBRID_DEFAULT_MASK4; + RegionNvmGroup2->ChannelsDefaultMask[5] = HYBRID_DEFAULT_MASK5; +#else + RegionNvmGroup2->ChannelsDefaultMask[0] = 0xFFFF; + RegionNvmGroup2->ChannelsDefaultMask[1] = 0xFFFF; + RegionNvmGroup2->ChannelsDefaultMask[2] = 0xFFFF; + RegionNvmGroup2->ChannelsDefaultMask[3] = 0xFFFF; + RegionNvmGroup2->ChannelsDefaultMask[4] = 0xFFFF; + RegionNvmGroup2->ChannelsDefaultMask[5] = 0xFFFF; +#endif /* HYBRID_ENABLED == 1 */ + /* ST_WORKAROUND_END */ + + // Copy channels default mask + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, RegionNvmGroup2->ChannelsDefaultMask, CHANNELS_MASK_SIZE ); +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + RegionBands = (Band_t*) params->Bands; + + // Default bands + memcpy1( ( uint8_t* )RegionBands, ( uint8_t* )bands, sizeof( Band_t ) * CN470_MAX_NB_BANDS ); + + // 125 kHz channels + RegionNvmGroup2->ChannelPlan = REGION_CN470_DEFAULT_CHANNEL_PLAN; + RegionNvmGroup2->CommonJoinChannelIndex = 0; + RegionNvmGroup2->IsOtaaDevice = false; + + // Apply the channel plan configuration + ApplyChannelPlanConfig( RegionNvmGroup2->ChannelPlan, &ChannelPlanCtx ); + + // Default channels + ChannelPlanCtx.InitializeChannels( RegionNvmGroup2->Channels ); + + // Default ChannelsMask + ChannelPlanCtx.InitializeChannelsMask( RegionNvmGroup2->ChannelsDefaultMask ); + + // Copy channels default mask + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, RegionNvmGroup2->ChannelsDefaultMask, CHANNELS_MASK_SIZE ); + + // Copy into channels mask remaining + RegionCommonChanMaskCopy( RegionNvmGroup1->ChannelsMaskRemaining, RegionNvmGroup2->ChannelsMask, CHANNELS_MASK_SIZE ); +#endif /* REGION_VERSION */ + break; + } + case INIT_TYPE_RESET_TO_DEFAULT_CHANNELS: + { + // Intentional fallthrough + } + case INIT_TYPE_ACTIVATE_DEFAULT_CHANNELS: + { +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + // Copy channels default mask + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, RegionNvmGroup2->ChannelsDefaultMask, CHANNELS_MASK_SIZE ); +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + // Restore channels default mask + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, RegionNvmGroup2->ChannelsDefaultMask, CHANNELS_MASK_SIZE ); + + for( uint8_t i = 0; i < CHANNELS_MASK_SIZE; i++ ) + { // Copy-And the channels mask + RegionNvmGroup1->ChannelsMaskRemaining[i] &= RegionNvmGroup2->ChannelsMask[i]; + } +#endif /* REGION_VERSION */ + break; + } + default: + { + break; + } + } +#endif /* REGION_CN470 */ +} + +bool RegionCN470Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ) +{ +#if defined( REGION_CN470 ) + 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; + } +#else + return false; +#endif /* REGION_CN470 */ +} + +void RegionCN470ApplyCFList( ApplyCFListParams_t* applyCFList ) +{ +#if defined( REGION_CN470 ) +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + // Setup the channel plan based on the join channel + RegionNvmGroup2->CommonJoinChannelIndex = applyCFList->JoinChannel; + RegionNvmGroup2->IsOtaaDevice = true; + RegionNvmGroup2->ChannelPlan = IdentifyChannelPlan( RegionNvmGroup2->CommonJoinChannelIndex ); + + if( RegionNvmGroup2->ChannelPlan == CHANNEL_PLAN_UNKNOWN ) + { + // Invalid channel plan, fallback to default + RegionNvmGroup2->ChannelPlan = REGION_CN470_DEFAULT_CHANNEL_PLAN; + } + // Apply the configuration for the channel plan + ApplyChannelPlanConfig( RegionNvmGroup2->ChannelPlan, &ChannelPlanCtx ); +#endif /* REGION_VERSION */ + + // 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) +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + for( uint8_t chMaskItr = 0, cntPayload = 0; chMaskItr <= 5; chMaskItr++, cntPayload+=2 ) + { + RegionNvmGroup2->ChannelsMask[chMaskItr] = (uint16_t) (0x00FF & applyCFList->Payload[cntPayload]); + RegionNvmGroup2->ChannelsMask[chMaskItr] |= (uint16_t) (applyCFList->Payload[cntPayload+1] << 8); + } +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + for( uint8_t chMaskItr = 0, cntPayload = 0; chMaskItr < ChannelPlanCtx.JoinAcceptListSize; chMaskItr++, cntPayload+=2 ) + { + RegionNvmGroup2->ChannelsMask[chMaskItr] = (uint16_t) (0x00FF & applyCFList->Payload[cntPayload]); + RegionNvmGroup2->ChannelsMask[chMaskItr] |= (uint16_t) (applyCFList->Payload[cntPayload+1] << 8); + + // Set the channel mask to the remaining + RegionNvmGroup1->ChannelsMaskRemaining[chMaskItr] &= RegionNvmGroup2->ChannelsMask[chMaskItr]; + } +#endif /* REGION_VERSION */ +#endif /* REGION_CN470 */ +} + +bool RegionCN470ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ) +{ +#if defined( REGION_CN470 ) + switch( chanMaskSet->ChannelsMaskType ) + { + case CHANNELS_MASK: + { + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, chanMaskSet->ChannelsMaskIn, CHANNELS_MASK_SIZE ); + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + for( uint8_t i = 0; i < CHANNELS_MASK_SIZE; i++ ) + { // Copy-And the channels mask + RegionNvmGroup1->ChannelsMaskRemaining[i] &= RegionNvmGroup2->ChannelsMask[i]; + } +#endif /* REGION_VERSION */ + break; + } + case CHANNELS_DEFAULT_MASK: + { + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, CHANNELS_MASK_SIZE ); + break; + } + default: + return false; + } + return true; +#else + return false; +#endif /* REGION_CN470 */ +} + +void RegionCN470ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ) +{ +#if defined( REGION_CN470 ) + uint32_t tSymbolInUs = 0; + + // Get the datarate, perform a boundary check + rxConfigParams->Datarate = MIN( datarate, CN470_RX_MAX_DATARATE ); + rxConfigParams->Bandwidth = RegionCommonGetBandwidth( rxConfigParams->Datarate, BandwidthsCN470 ); + + tSymbolInUs = RegionCommonComputeSymbolTimeLoRa( DataratesCN470[rxConfigParams->Datarate], BandwidthsCN470[rxConfigParams->Datarate] ); + + RegionCommonComputeRxWindowParameters( tSymbolInUs, minRxSymbols, rxError, Radio.GetWakeupTime( ), &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); +#endif /* REGION_CN470 */ +} + +bool RegionCN470RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) +{ +#if defined( REGION_CN470 ) + int8_t dr = rxConfig->Datarate; + uint8_t maxPayload = 0; + int8_t phyDr = 0; + uint32_t frequency = rxConfig->Frequency; + + if( Radio.GetStatus( ) != RF_IDLE ) + { + return false; + } + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + if( rxConfig->RxSlot == RX_SLOT_WIN_1 ) + { + // Apply window 1 frequency + frequency = CN470_FIRST_RX1_CHANNEL + ( rxConfig->Channel % 48 ) * CN470_STEPWIDTH_RX1_CHANNEL; + } +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + // The RX configuration depends on whether the device has joined or not. + if( rxConfig->NetworkActivation != ACTIVATION_TYPE_NONE ) + { + // Update the downlink frequency in case of RX_SLOT_WIN_1 or RX_SLOT_WIN_2. + // Keep the frequency for all other cases. + if( rxConfig->RxSlot == RX_SLOT_WIN_1 ) + { + // Apply window 1 frequency + frequency = ChannelPlanCtx.GetRx1Frequency( rxConfig->Channel ); + } + else if( rxConfig->RxSlot == RX_SLOT_WIN_2 ) + { + // Apply window 2 frequency + frequency = ChannelPlanCtx.GetRx2Frequency( RegionNvmGroup2->CommonJoinChannelIndex, RegionNvmGroup2->IsOtaaDevice ); + } + } + else + { + // In this case, only RX_SLOT_WIN_1 and RX_SLOT_WIN_2 is possible. There is + // no need to verify it. The end device is not joined and is an OTAA device. + frequency = CommonJoinChannels[rxConfig->Channel].Rx1Frequency; + } +#endif /* REGION_VERSION */ + + // 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 ); + /* ST_WORKAROUND_BEGIN: Keep repeater feature */ + if( rxConfig->RepeaterSupport == true ) + { + maxPayload = MaxPayloadOfDatarateRepeaterCN470[dr]; + } + else + { + maxPayload = MaxPayloadOfDatarateCN470[dr]; + } + Radio.SetMaxPayloadLength( MODEM_LORA, maxPayload + LORAMAC_FRAME_PAYLOAD_OVERHEAD_SIZE ); + /* ST_WORKAROUND_END */ + + /* ST_WORKAROUND_BEGIN: Print Rx config */ + RegionCommonRxConfigPrint(rxConfig->RxSlot, frequency, dr); + /* ST_WORKAROUND_END */ + + *datarate = (uint8_t) dr; + return true; +#else + return false; +#endif /* REGION_CN470 */ +} + +bool RegionCN470TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ) +{ +#if defined( REGION_CN470 ) +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + int8_t phyDr = DataratesCN470[txConfig->Datarate]; + int8_t txPowerLimited = RegionCommonLimitTxPower( txConfig->TxPower, RegionNvmGroup1->Bands[RegionNvmGroup2->Channels[txConfig->Channel].Band].TxMaxPower ); + uint32_t bandwidth = RegionCommonGetBandwidth( txConfig->Datarate, BandwidthsCN470 ); + int8_t phyTxPower = 0; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain ); + + // Setup the radio frequency + Radio.SetChannel( RegionNvmGroup2->Channels[txConfig->Channel].Frequency ); + + Radio.SetTxConfig( MODEM_LORA, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 4000 ); + /* ST_WORKAROUND_BEGIN: Print Tx config */ + RegionCommonTxConfigPrint(RegionNvmGroup2->Channels[txConfig->Channel].Frequency, txConfig->Datarate); + /* ST_WORKAROUND_END */ + + // Setup maximum payload length of the radio driver + Radio.SetMaxPayloadLength( MODEM_LORA, txConfig->PktLen ); +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + RadioModems_t modem; + uint32_t frequency; + uint8_t band; + int8_t phyDr = DataratesCN470[txConfig->Datarate]; + + // The TX configuration depends on whether the device has joined or not. + if( txConfig->NetworkActivation != ACTIVATION_TYPE_NONE ) + { + frequency = RegionNvmGroup2->Channels[txConfig->Channel].Frequency; + band = RegionNvmGroup2->Channels[txConfig->Channel].Band; + } + else + { + // The end device is not joined and is an OTAA device. + frequency = CommonJoinChannels[txConfig->Channel].Frequency; + band = CommonJoinChannels[txConfig->Channel].Band; + } + + int8_t txPowerLimited = RegionCommonLimitTxPower( txConfig->TxPower, RegionBands[band].TxMaxPower ); + uint32_t bandwidth = RegionCommonGetBandwidth( txConfig->Datarate, BandwidthsCN470 ); + int8_t phyTxPower = 0; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain ); + + // Setup the radio frequency + Radio.SetChannel( 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 ); + } + /* ST_WORKAROUND_BEGIN: Print Tx config */ + RegionCommonTxConfigPrint(frequency, txConfig->Datarate); + /* ST_WORKAROUND_END */ + + // Setup maximum payload length of the radio driver + Radio.SetMaxPayloadLength( modem, txConfig->PktLen ); +#endif /* REGION_VERSION */ + // Update time-on-air + *txTimeOnAir = GetTimeOnAir( txConfig->Datarate, txConfig->PktLen ); + + *txPower = txPowerLimited; + return true; +#else + return false; +#endif /* REGION_CN470 */ +} + +uint8_t RegionCN470LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ) +{ + uint8_t status = 0x07; +#if defined( REGION_CN470 ) + RegionCommonLinkAdrParams_t linkAdrParams = { 0 }; + uint8_t nextIndex = 0; + uint8_t bytesProcessed = 0; + uint16_t channelsMask[CHANNELS_MASK_SIZE] = { 0, 0, 0, 0, 0, 0 }; + GetPhyParams_t getPhy; + PhyParam_t phyParam; + RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams; + + // Initialize local copy of channels mask + RegionCommonChanMaskCopy( channelsMask, RegionNvmGroup2->ChannelsMask, CHANNELS_MASK_SIZE ); + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + 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 ) && + ( RegionNvmGroup2->Channels[linkAdrParams.ChMaskCtrl * 16 + i].Frequency == 0 ) ) + {// Trying to enable an undefined channel + status &= 0xFE; // Channel mask KO + } + } + channelsMask[linkAdrParams.ChMaskCtrl] = linkAdrParams.ChMask; + } + } +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + 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; + + // Update the channel plan + status = ChannelPlanCtx.LinkAdrChMaskUpdate( channelsMask, linkAdrParams.ChMaskCtrl, + linkAdrParams.ChMask, RegionNvmGroup2->Channels ); + } + + // Make sure at least one channel is active + if( RegionCommonCountChannels( channelsMask, 0, ChannelPlanCtx.ChannelsMaskSize ) == 0 ) + { + status &= 0xFE; // Channel mask KO + } +#endif /* REGION_VERSION */ + + // 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 = RegionNvmGroup2->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 ) + { +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + // Copy Mask + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, channelsMask, 6 ); +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + // Copy Mask + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, channelsMask, CHANNELS_MASK_SIZE ); + + RegionNvmGroup1->ChannelsMaskRemaining[0] &= RegionNvmGroup2->ChannelsMask[0]; + RegionNvmGroup1->ChannelsMaskRemaining[1] &= RegionNvmGroup2->ChannelsMask[1]; + RegionNvmGroup1->ChannelsMaskRemaining[2] &= RegionNvmGroup2->ChannelsMask[2]; + RegionNvmGroup1->ChannelsMaskRemaining[3] &= RegionNvmGroup2->ChannelsMask[3]; + RegionNvmGroup1->ChannelsMaskRemaining[4] = RegionNvmGroup2->ChannelsMask[4]; + RegionNvmGroup1->ChannelsMaskRemaining[5] = RegionNvmGroup2->ChannelsMask[5]; +#endif /* REGION_VERSION */ + } + + // Update status variables + *drOut = linkAdrParams.Datarate; + *txPowOut = linkAdrParams.TxPower; + *nbRepOut = linkAdrParams.NbRep; + *nbBytesParsed = bytesProcessed; + +#endif /* REGION_CN470 */ + return status; +} + +uint8_t RegionCN470RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ) +{ + uint8_t status = 0x07; +#if defined( REGION_CN470 ) + + // 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 + } + +#endif /* REGION_CN470 */ + return status; +} + +int8_t RegionCN470NewChannelReq( NewChannelReqParams_t* newChannelReq ) +{ + // Do not accept the request + return -1; +} + +int8_t RegionCN470TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ) +{ + // Do not accept the request + return -1; +} + +int8_t RegionCN470DlChannelReq( DlChannelReqParams_t* dlChannelReq ) +{ + // Do not accept the request + return -1; +} + +int8_t RegionCN470AlternateDr( int8_t currentDr, AlternateDrType_t type ) +{ +#if defined( REGION_CN470 ) + return MAX(CN470_TX_MIN_DATARATE, currentDr); +#else + return -1; +#endif /* REGION_CN470 */ +} + +LoRaMacStatus_t RegionCN470NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ) +{ +#if defined( REGION_CN470 ) +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + uint8_t nbEnabledChannels = 0; + uint8_t nbRestrictedChannels = 0; + uint8_t enabledChannels[CN470_MAX_NB_CHANNELS] = { 0 }; + RegionCommonIdentifyChannelsParam_t identifyChannelsParam; + RegionCommonCountNbOfEnabledChannelsParams_t countChannelsParams; + LoRaMacStatus_t status = LORAMAC_STATUS_NO_CHANNEL_FOUND; + + // Count 125kHz channels + if( RegionCommonCountChannels( RegionNvmGroup2->ChannelsMask, 0, CHANNELS_MASK_SIZE ) == 0 ) + { // Reactivate default channels + RegionNvmGroup2->ChannelsMask[0] = 0xFFFF; + RegionNvmGroup2->ChannelsMask[1] = 0xFFFF; + RegionNvmGroup2->ChannelsMask[2] = 0xFFFF; + RegionNvmGroup2->ChannelsMask[3] = 0xFFFF; + RegionNvmGroup2->ChannelsMask[4] = 0xFFFF; + RegionNvmGroup2->ChannelsMask[5] = 0xFFFF; + } + + // Search how many channels are enabled + countChannelsParams.Joined = nextChanParams->Joined; + countChannelsParams.Datarate = nextChanParams->Datarate; + countChannelsParams.ChannelsMask = RegionNvmGroup2->ChannelsMask; + countChannelsParams.Channels = RegionNvmGroup2->Channels; + countChannelsParams.Bands = RegionNvmGroup1->Bands; + countChannelsParams.MaxNbChannels = CN470_MAX_NB_CHANNELS; + countChannelsParams.JoinChannels = NULL; +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + uint8_t nbEnabledChannels = 0; + uint8_t nbRestrictedChannels = 0; + uint8_t enabledChannels[CN470_MAX_NB_CHANNELS] = { 0 }; + uint16_t joinChannelsMask[2] = CN470_JOIN_CHANNELS; + RegionCommonIdentifyChannelsParam_t identifyChannelsParam; + RegionCommonCountNbOfEnabledChannelsParams_t countChannelsParams; + LoRaMacStatus_t status = LORAMAC_STATUS_NO_CHANNEL_FOUND; + + // Count 125kHz channels + if( RegionCommonCountChannels( RegionNvmGroup1->ChannelsMaskRemaining, 0, ChannelPlanCtx.ChannelsMaskSize ) == 0 ) + { // Reactivate default channels + RegionNvmGroup2->ChannelsMask[0] = 0xFFFF; + RegionNvmGroup2->ChannelsMask[1] = 0xFFFF; + RegionNvmGroup2->ChannelsMask[2] = 0xFFFF; + RegionNvmGroup2->ChannelsMask[3] = 0xFFFF; + RegionNvmGroup2->ChannelsMask[4] = 0xFFFF; + RegionNvmGroup2->ChannelsMask[5] = 0xFFFF; + RegionCommonChanMaskCopy( RegionNvmGroup1->ChannelsMaskRemaining, RegionNvmGroup2->ChannelsMask, ChannelPlanCtx.ChannelsMaskSize ); + } + + // Search how many channels are enabled + countChannelsParams.Joined = nextChanParams->Joined; + countChannelsParams.Datarate = nextChanParams->Datarate; + countChannelsParams.ChannelsMask = RegionNvmGroup1->ChannelsMaskRemaining; + countChannelsParams.Channels = RegionNvmGroup2->Channels; + countChannelsParams.Bands = RegionBands; + countChannelsParams.MaxNbChannels = CN470_MAX_NB_CHANNELS; + countChannelsParams.JoinChannels = NULL; + + // Apply a different channel selection if the device is not joined yet + // In this case the device shall not follow the individual channel plans for the + // different type, but instead shall follow the common join channel plan. + if( countChannelsParams.Joined == false ) + { + countChannelsParams.ChannelsMask = joinChannelsMask; + countChannelsParams.Channels = CommonJoinChannels; + countChannelsParams.MaxNbChannels = CN470_COMMON_JOIN_CHANNELS_SIZE; + countChannelsParams.JoinChannels = joinChannelsMask; + } +#endif /* REGION_VERSION */ + + identifyChannelsParam.AggrTimeOff = nextChanParams->AggrTimeOff; + identifyChannelsParam.LastAggrTx = nextChanParams->LastAggrTx; + identifyChannelsParam.DutyCycleEnabled = nextChanParams->DutyCycleEnabled; + identifyChannelsParam.MaxBands = CN470_MAX_NB_BANDS; + + identifyChannelsParam.ElapsedTimeSinceStartUp = nextChanParams->ElapsedTimeSinceStartUp; + identifyChannelsParam.LastTxIsJoinRequest = nextChanParams->LastTxIsJoinRequest; + identifyChannelsParam.ExpectedTimeOnAir = GetTimeOnAir( nextChanParams->Datarate, nextChanParams->PktLen ); + + identifyChannelsParam.CountNbOfEnabledChannelsParam = &countChannelsParams; + + status = RegionCommonIdentifyChannels( &identifyChannelsParam, aggregatedTimeOff, enabledChannels, + &nbEnabledChannels, &nbRestrictedChannels, time ); + + if( status == LORAMAC_STATUS_OK ) + { + // We found a valid channel. Selection is random. + *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )]; + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + // Disable the channel in the mask + RegionCommonChanDisable( RegionNvmGroup1->ChannelsMaskRemaining, *channel, ChannelPlanCtx.ChannelsMaskSize ); +#endif /* REGION_VERSION */ + } + return status; +#else + return LORAMAC_STATUS_NO_CHANNEL_FOUND; +#endif /* REGION_CN470 */ +} + +LoRaMacStatus_t RegionCN470ChannelAdd( ChannelAddParams_t* channelAdd ) +{ + return LORAMAC_STATUS_PARAMETER_INVALID; +} + +bool RegionCN470ChannelsRemove( ChannelRemoveParams_t* channelRemove ) +{ + return LORAMAC_STATUS_PARAMETER_INVALID; +} + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +void RegionCN470SetContinuousWave( ContinuousWaveParams_t* continuousWave ) +{ +#if defined( REGION_CN470 ) + int8_t txPowerLimited = RegionCommonLimitTxPower( continuousWave->TxPower, RegionNvmGroup1->Bands[RegionNvmGroup2->Channels[continuousWave->Channel].Band].TxMaxPower ); + int8_t phyTxPower = 0; + uint32_t frequency = RegionNvmGroup2->Channels[continuousWave->Channel].Frequency; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, continuousWave->MaxEirp, continuousWave->AntennaGain ); + + Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout ); +#endif /* REGION_CN470 */ +} +#endif /* REGION_VERSION */ + +uint8_t RegionCN470ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) +{ +#if defined( REGION_CN470 ) +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + int8_t datarate = dr - drOffset; +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + int8_t datarate = DatarateOffsetsCN470[dr][drOffset]; +#endif /* REGION_VERSION */ + if( datarate < 0 ) + { + datarate = DR_0; + } + return datarate; +#else + return 0; +#endif /* REGION_CN470 */ +} + +void RegionCN470RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ) +{ +#if defined( REGION_CN470 ) + 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; +#endif /* REGION_CN470 */ +} diff --git a/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCN470.h b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCN470.h new file mode 100644 index 0000000..bc59b1b --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCN470.h @@ -0,0 +1,599 @@ +/*! + * \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. + * \{ + */ +/** + ****************************************************************************** + * + * Portions COPYRIGHT 2020 STMicroelectronics + * + * @file RegionCN470.h + * @author MCD Application Team + * @brief Region definition for CN470 + ****************************************************************************** + */ +#ifndef __REGION_CN470_H__ +#define __REGION_CN470_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "Region.h" + +/*! + * LoRaMac maximum number of channels + */ +#define CN470_MAX_NB_CHANNELS 96 + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +/*! + * 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 +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) +/*! + * Minimal datarate that can be used by the node + */ +#define CN470_TX_MIN_DATARATE DR_1 + +/*! + * 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_1 + +/*! + * 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_1 +#endif /* REGION_VERSION */ + +/*! + * Minimal Rx1 receive datarate offset + */ +#define CN470_MIN_RX1_DR_OFFSET 0 + +/*! + * Maximal Rx1 receive datarate offset + */ +#define CN470_MAX_RX1_DR_OFFSET 5 + +/*! + * 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 + +/*! + * Enabled or disabled the duty cycle + */ +#define CN470_DUTY_CYCLE_ENABLED 0 + +/*! + * Maximum RX window duration + */ +#define CN470_MAX_RX_WINDOW 3000 + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +/*! + * 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 +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) +/*! + * Second reception window channel datarate definition. + */ +#define CN470_RX_WND_2_DR DR_1 +#endif /* REGION_VERSION */ + +/*! + * Default uplink dwell time configuration + */ +#define CN470_DEFAULT_UPLINK_DWELL_TIME 0 + +/* + * CLASS B + */ +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +/*! + * Beacon frequency + */ +#define CN470_BEACON_CHANNEL_FREQ 508300000 + +/*! + * Beacon frequency channel stepwidth + */ +#define CN470_BEACON_CHANNEL_STEPWIDTH 200000 + +/*! + * Ping slot channel frequency + */ +#define CN470_PING_SLOT_CHANNEL_FREQ 508300000 + +/*! + * 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 +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) +/*! + * Payload size of a beacon frame + */ +#define CN470_BEACON_SIZE 19 + +/*! + * Size of RFU 1 field + */ +#define CN470_RFU1_SIZE 2 +#endif /* REGION_VERSION */ + +/*! + * Size of RFU 2 field + */ +#define CN470_RFU2_SIZE 1 + +/*! + * Datarate of the beacon channel + */ +#define CN470_BEACON_CHANNEL_DR DR_2 + +/*! + * Bandwidth 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 + * Band = { DutyCycle, TxMaxPower, LastBandUpdateTime, LastMaxCreditAssignTime, TimeCredits, MaxTimeCredits, ReadyForTransmission } + */ +#define CN470_BAND0 { 1, CN470_MAX_TX_POWER, 0, 0, 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 ) + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) +#define CN470_DEFAULT_DR_RANGE { .Value = ( CN470_TX_MAX_DATARATE << 4 ) | CN470_TX_MIN_DATARATE } + +#define CN470_COMMON_JOIN_CHANNELS \ +{ \ + { .Frequency = 470900000, .Rx1Frequency = 484500000, CN470_DEFAULT_DR_RANGE, .Band = 0 }, \ + { .Frequency = 472500000, .Rx1Frequency = 486100000, CN470_DEFAULT_DR_RANGE, .Band = 0 }, \ + { .Frequency = 474100000, .Rx1Frequency = 487700000, CN470_DEFAULT_DR_RANGE, .Band = 0 }, \ + { .Frequency = 475700000, .Rx1Frequency = 489300000, CN470_DEFAULT_DR_RANGE, .Band = 0 }, \ + { .Frequency = 504100000, .Rx1Frequency = 490900000, CN470_DEFAULT_DR_RANGE, .Band = 0 }, \ + { .Frequency = 505700000, .Rx1Frequency = 492500000, CN470_DEFAULT_DR_RANGE, .Band = 0 }, \ + { .Frequency = 507300000, .Rx1Frequency = 494100000, CN470_DEFAULT_DR_RANGE, .Band = 0 }, \ + { .Frequency = 508900000, .Rx1Frequency = 495700000, CN470_DEFAULT_DR_RANGE, .Band = 0 }, \ +\ + { .Frequency = 479900000, .Rx1Frequency = 479900000, CN470_DEFAULT_DR_RANGE, .Band = 0 }, \ + { .Frequency = 499900000, .Rx1Frequency = 499900000, CN470_DEFAULT_DR_RANGE, .Band = 0 }, \ +\ + { .Frequency = 470300000, .Rx1Frequency = 492500000, CN470_DEFAULT_DR_RANGE, .Band = 0 }, \ + { .Frequency = 472300000, .Rx1Frequency = 492500000, CN470_DEFAULT_DR_RANGE, .Band = 0 }, \ + { .Frequency = 474300000, .Rx1Frequency = 492500000, CN470_DEFAULT_DR_RANGE, .Band = 0 }, \ + { .Frequency = 476300000, .Rx1Frequency = 492500000, CN470_DEFAULT_DR_RANGE, .Band = 0 }, \ + { .Frequency = 478300000, .Rx1Frequency = 492500000, CN470_DEFAULT_DR_RANGE, .Band = 0 }, \ +\ + { .Frequency = 480300000, .Rx1Frequency = 502500000, CN470_DEFAULT_DR_RANGE, .Band = 0 }, \ + { .Frequency = 482300000, .Rx1Frequency = 502500000, CN470_DEFAULT_DR_RANGE, .Band = 0 }, \ + { .Frequency = 484300000, .Rx1Frequency = 502500000, CN470_DEFAULT_DR_RANGE, .Band = 0 }, \ + { .Frequency = 486300000, .Rx1Frequency = 502500000, CN470_DEFAULT_DR_RANGE, .Band = 0 }, \ + { .Frequency = 488300000, .Rx1Frequency = 502500000, CN470_DEFAULT_DR_RANGE, .Band = 0 }, \ +} + +#define CN470_COMMON_JOIN_CHANNELS_SIZE 20 + +#if ( HYBRID_ENABLED == 1 ) +#define CN470_JOIN_CHANNELS { 0x0001, 0x0000 } +#else +#define CN470_JOIN_CHANNELS { 0xFFFF, 0x000F } +#endif /* HYBRID_ENABLED */ +#endif /* REGION_VERSION */ + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +/*! + * 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 }; + +/* ST_WORKAROUND_BEGIN: Keep repeater feature */ +/*! + * 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 }; +/* ST_WORKAROUND_END */ +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) +/*! + * Data rates table definition + */ +static const uint8_t DataratesCN470[] = { 12, 11, 10, 9, 8, 7, 7, 50 }; + +/*! + * Bandwidths table definition in Hz + */ +static const uint32_t BandwidthsCN470[] = { 125000, 125000, 125000, 125000, 125000, 125000, 500000, 0 }; + +/*! + * Up/Down link data rates offset definition + */ +static const int8_t DatarateOffsetsCN470[8][6] = +{ + { DR_0, DR_0, DR_0, DR_0, DR_0, DR_0 }, // DR_0 + { DR_1, DR_1, DR_1, DR_1, DR_1, DR_1 }, // DR_1 + { DR_2, DR_1, DR_1, DR_1, DR_1, DR_1 }, // DR_2 + { DR_3, DR_2, DR_1, DR_1, DR_1, DR_1 }, // DR_3 + { DR_4, DR_3, DR_2, DR_1, DR_1, DR_1 }, // DR_4 + { DR_5, DR_4, DR_3, DR_2, DR_1, DR_1 }, // DR_5 + { DR_6, DR_5, DR_4, DR_3, DR_2, DR_1 }, // DR_6 + { DR_7, DR_6, DR_5, DR_4, DR_3, DR_2 }, // DR_7 +}; + +/* ST_WORKAROUND_BEGIN: Keep repeater feature */ +/*! + * Maximum payload with respect to the datarate index. Cannot operate with repeater. + */ +static const uint8_t MaxPayloadOfDatarateCN470[] = { 0, 23, 86, 184, 242, 242, 242, 242 }; + +/*! + * Maximum payload with respect to the datarate index. Can operate with repeater. + */ +static const uint8_t MaxPayloadOfDatarateRepeaterCN470[] = { 0, 23, 86, 164, 222, 222, 222, 222 }; +/* ST_WORKAROUND_END */ +#endif /* REGION_VERSION */ + +/*! + * \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] params Sets the initialization type. + */ +void RegionCN470InitDefaults( InitDefaultsParams_t* params ); + +/*! + * \brief Verifies a parameter. + * + * \param [in] verify Pointer to the function parameters. + * + * \param [in] phyAttribute 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. + * + * \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 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. + */ +int8_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. + */ +int8_t RegionCN470DlChannelReq( 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 RegionCN470AlternateDr( int8_t currentDr, AlternateDrType_t type ); + +/*! + * \brief Searches and set the next random available channel + * + * \param [in] nextChanParams pointer of selected channel parameters + * + * \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 ); + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +/*! + * \brief Sets the radio into continuous wave mode. + * + * \param [IN] continuousWave Pointer to the function parameters. + */ +void RegionCN470SetContinuousWave( ContinuousWaveParams_t* continuousWave ); +#endif /* REGION_VERSION */ + +/*! + * \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 + * + * \param [out] outDr Datarate used to receive the beacon + */ +void RegionCN470RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ); + +/*! \} defgroup REGIONCN470 */ + +#ifdef __cplusplus +} +#endif + +#endif // __REGION_CN470_H__ diff --git a/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCN470A20.c b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCN470A20.c new file mode 100644 index 0000000..ba7cfb8 --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCN470A20.c @@ -0,0 +1,192 @@ +/*! + * \file RegionCN470A20.c + * + * \brief Specific Region implementation of CN470 Channel plan type A, 20MHz. + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \addtogroup REGIONCN470 + * + * \{ + */ +#include "RegionCN470.h" +#include "RegionBaseUS.h" +#include "RegionCN470A20.h" + +/* The HYBRID_DEFAULT_MASKx define the enabled channels in Hybrid mode*/ +/* Note: they can be redefined in lorawan_conf.h*/ +#ifndef HYBRID_DEFAULT_MASK0 /*enabled channels from channel 15 down to channel 0*/ +#define HYBRID_DEFAULT_MASK0 0x00FF /*channel 7 down to channel 0 enabled*/ +#endif +#ifndef HYBRID_DEFAULT_MASK1 /*enabled channels from channel 31 down to channel 16*/ +#define HYBRID_DEFAULT_MASK1 0x0000 +#endif +#ifndef HYBRID_DEFAULT_MASK2 /*enabled channels from channel 47 down to channel 32*/ +#define HYBRID_DEFAULT_MASK2 0x0000 +#endif +#ifndef HYBRID_DEFAULT_MASK3 /*enabled channels from channel 63 down to channel 48*/ +#define HYBRID_DEFAULT_MASK3 0x0000 +#endif +#ifndef HYBRID_DEFAULT_MASK4 /*enabled channels from channel 79 down to channel 64*/ +#define HYBRID_DEFAULT_MASK4 0x0000 +#endif +#ifndef HYBRID_DEFAULT_MASK5 /*enabled channels from channel 95 down to channel 80*/ +#define HYBRID_DEFAULT_MASK5 0x0000 +#endif + +uint32_t RegionCN470A20GetDownlinkFrequency( uint8_t channel, uint8_t joinChannelIndex, bool isPingSlot ) +{ + return RegionCN470A20GetRx1Frequency( channel ); +} + +uint8_t RegionCN470A20GetBeaconChannelOffset( uint8_t joinChannelIndex ) +{ + return ( joinChannelIndex * 8 ); +} + +uint8_t RegionCN470A20LinkAdrChMaskUpdate( uint16_t* channelsMask, uint8_t chMaskCntl, + uint16_t chanMask, ChannelParams_t* channels ) +{ + uint8_t status = 0x07; + + if( ( chMaskCntl == 4 ) || ( chMaskCntl == 5 ) ) + { + // RFU + status &= 0xFE; // Channel mask KO + } + else if( chMaskCntl == 6 ) + { + // Enable all channels + channelsMask[0] = 0xFFFF; + channelsMask[1] = 0xFFFF; + channelsMask[2] = 0xFFFF; + channelsMask[3] = 0xFFFF; + channelsMask[4] = 0x0000; + channelsMask[5] = 0x0000; + } + else if( chMaskCntl == 7 ) + { + // Disable all channels + channelsMask[0] = 0x0000; + channelsMask[1] = 0x0000; + channelsMask[2] = 0x0000; + channelsMask[3] = 0x0000; + channelsMask[4] = 0x0000; + channelsMask[5] = 0x0000; + } + else + { + // chMaskCntl 0 to 3 + for( uint8_t i = 0; i < 16; i++ ) + { + if( ( ( chanMask & ( 1 << i ) ) != 0 ) && + ( channels[chMaskCntl * 16 + i].Frequency == 0 ) ) + {// Trying to enable an undefined channel + status &= 0xFE; // Channel mask KO + } + } + channelsMask[chMaskCntl] = chanMask; + } + return status; +} + +bool RegionCN470A20VerifyRfFreq( uint32_t freq ) +{ + // Downstream group 1 and 2 + if( RegionBaseUSVerifyFrequencyGroup( freq, CN470_A20_FIRST_RX_CHANNEL, + CN470_A20_LAST_RX_CHANNEL, + CN470_A20_STEPWIDTH_RX_CHANNEL ) == false ) + { + return false; + } + return true; +} + +void RegionCN470A20InitializeChannels( ChannelParams_t* channels ) +{ + // Upstream group 1 + for( uint8_t i = 0; i < 32; i++ ) + { + channels[i].Frequency = CN470_A20_FIRST_TX1_CHANNEL + i * CN470_A20_STEPWIDTH_RX_CHANNEL; + channels[i].DrRange.Value = ( CN470_TX_MAX_DATARATE << 4 ) | CN470_TX_MIN_DATARATE; + channels[i].Band = 0; + } + // Upstream group 2 + for( uint8_t i = 32; i < 64; i++ ) + { + channels[i].Frequency = CN470_A20_FIRST_TX2_CHANNEL + ( i - 32 ) * CN470_A20_STEPWIDTH_RX_CHANNEL; + channels[i].DrRange.Value = ( CN470_TX_MAX_DATARATE << 4 ) | CN470_TX_MIN_DATARATE; + channels[i].Band = 0; + } +} +void RegionCN470A20InitializeChannelsMask( uint16_t* channelsDefaultMask ) +{ + /* ST_WORKAROUND_BEGIN: Hybrid mode */ +#if ( HYBRID_ENABLED == 1 ) + channelsDefaultMask[0] = HYBRID_DEFAULT_MASK0; + channelsDefaultMask[1] = HYBRID_DEFAULT_MASK1; + channelsDefaultMask[2] = HYBRID_DEFAULT_MASK2; + channelsDefaultMask[3] = HYBRID_DEFAULT_MASK3; + channelsDefaultMask[4] = HYBRID_DEFAULT_MASK4; + channelsDefaultMask[5] = HYBRID_DEFAULT_MASK5; +#else + // Enable all possible channels + channelsDefaultMask[0] = 0xFFFF; + channelsDefaultMask[1] = 0xFFFF; + channelsDefaultMask[2] = 0xFFFF; + channelsDefaultMask[3] = 0xFFFF; + channelsDefaultMask[4] = 0x0000; + channelsDefaultMask[5] = 0x0000; +#endif /* HYBRID_ENABLED == 1 */ + /* ST_WORKAROUND_END */ +} + +uint32_t RegionCN470A20GetRx1Frequency( uint8_t channel ) +{ + // Base frequency for downstream group 1 + uint32_t baseFrequency = CN470_A20_FIRST_RX_CHANNEL; + uint8_t offset = 0; + + if( channel >= 32 ) + { + // Base frequency for downstream group 2 + baseFrequency = 490300000; + offset = 32; + } + return ( baseFrequency + ( ( channel - offset ) * CN470_A20_STEPWIDTH_RX_CHANNEL ) ); +} + +uint32_t RegionCN470A20GetRx2Frequency( uint8_t joinChannelIndex, bool isOtaaDevice ) +{ + uint32_t otaaFrequencies[] = CN470_A20_RX_WND_2_FREQ_OTAA; + + if( isOtaaDevice == true ) + { + return otaaFrequencies[joinChannelIndex]; + } + // ABP device + return CN470_A20_RX_WND_2_FREQ_ABP; +} + +/*! \} defgroup LORAMACCLASSB */ diff --git a/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCN470A20.h b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCN470A20.h new file mode 100644 index 0000000..7ee3a23 --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCN470A20.h @@ -0,0 +1,229 @@ +/*! + * \file RegionCN470A20.h + * + * \brief Specific Region definition of CN470 Channel plan type A, 20MHz. + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \addtogroup REGIONCN470 + * + * \{ + */ +#ifndef __REGION_CN470_A20_H__ +#define __REGION_CN470_A20_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "Region.h" + +/*! + * The maximum number of channels. + * Channel plan type A, 20MHz. + */ +#define CN470_A20_CHANNELS_MASK_SIZE 4 + +/*! + * The number of entries in the join accept list. + * Channel plan type A, 20MHz. + */ +#define CN470_A20_JOIN_ACCEPT_LIST_SIZE 4 + +/*! + * This is a number which is used to calculate the + * beacon channel in case of frequency hopping. + * Channel plan type A, 20MHz. + */ +#define CN470_A20_BEACON_NB_CHANNELS 8 + +/*! + * This is a number which is used to calculate the + * ping slot channel in case of frequency hopping. + * Channel plan type A, 20MHz. + */ +#define CN470_A20_PING_SLOT_NB_CHANNELS 8 + +/*! + * The first RX channel, downstream group 1 and 2. + * Channel plan type A, 20MHz. + */ +#define CN470_A20_FIRST_RX_CHANNEL 483900000 + +/*! + * The last RX channel, downstream group 1 and 2. + * Channel plan type A, 20MHz. + */ +#define CN470_A20_LAST_RX_CHANNEL 496500000 + +/*! + * The frequency stepwidth between RX channels, + * downstream group 1 and 2. + * Channel plan type A, 20MHz. + */ +#define CN470_A20_STEPWIDTH_RX_CHANNEL 200000 + +/*! + * The first TX channel, upstream group 1. + * Channel plan type A, 20MHz. + */ +#define CN470_A20_FIRST_TX1_CHANNEL 470300000 + +/*! + * The frequency stepwidth between RX channels, + * upstream group 1. + * Channel plan type A, 20MHz. + */ +#define CN470_A20_STEPWIDTH_TX1_CHANNEL 200000 + +/*! + * The first TX channel, upstream group 2. + * Channel plan type A, 20MHz. + */ +#define CN470_A20_FIRST_TX2_CHANNEL 503500000 + +/*! + * The last TX channel, upstream group 2. + * Channel plan type A, 20MHz. + */ +#define CN470_A20_LAST_TX2_CHANNEL 509700000 + +/*! + * The frequency stepwidth between RX channels, + * upstream group 2. + * Channel plan type A, 20MHz. + */ +#define CN470_A20_STEPWIDTH_TX2_CHANNEL 200000 + +/*! + * The default frequency for RX window 2, when its + * an ABP device. + * Channel plan type A, 20MHz. + */ +#define CN470_A20_RX_WND_2_FREQ_ABP 486900000 + +/*! + * The channel plan frequencies for RX window 2, + * when its an OTAA device. + * Channel plan type A, 20MHz. + */ +#define CN470_A20_RX_WND_2_FREQ_OTAA { 485300000, 486900000, 488500000, 490100000, \ + 491700000, 493300000, 494000000, 496500000 } + +/*! + * \brief Calculation of the beacon frequency. + * + * \param [in] channel The Beacon channel number. + * + * \param [in] joinChannelIndex The join channel index. + * + * \param [in] isPingSlot Set to true, if its a ping slot. + * + * \retval Returns the beacon frequency. + */ +uint32_t RegionCN470A20GetDownlinkFrequency( uint8_t channel, uint8_t joinChannelIndex, bool isPingSlot ); + +/*! + * \brief Performs the update of the channelsMask based on the input parameters + * for the channel plan type A, 20MHz. + * + * \param [in] joinChannelIndex The join channel index. + * + * \retval Returns the offset for the given join channel. + */ +uint8_t RegionCN470A20GetBeaconChannelOffset( uint8_t joinChannelIndex ); + +/*! + * \brief Performs the update of the channelsMask based on the input parameters + * for the channel plan type A, 20MHz. + * + * \param [in] channelsMask A pointer to the channels mask. + * + * \param [in] chMaskCntl The value of the chMaskCntl field of the LinkAdrReq. + * + * \param [in] chanMask The value of the chanMask field of the LinkAdrReq. + * + * \param [in] channels A pointer to the available channels. + * + * \retval Status of the operation. Return 0x07 if the channels mask is valid. + */ +uint8_t RegionCN470A20LinkAdrChMaskUpdate( uint16_t* channelsMask, uint8_t chMaskCntl, + uint16_t chanMask, ChannelParams_t* channels ); + +/*! + * \brief Verifies if the frequency provided is valid + * for the channel plan type A, 20MHz. + * + * \param [in] frequency The frequency to verify. + * + * \retval Returns true, if the frequency is valid. + */ +bool RegionCN470A20VerifyRfFreq( uint32_t frequency ); + +/*! + * \brief Initializes all channels, datarates, frequencies and bands + * for the channel plan type A, 20MHz. + * + * \param [in] channels A pointer to the available channels. + */ +void RegionCN470A20InitializeChannels( ChannelParams_t* channels ); + +/*! + * \brief Initializes the channels default mask + * for the channel plan type A, 20MHz. + * + * \param [in] channelsDefaultMask A pointer to the channels default mask. + */ +void RegionCN470A20InitializeChannelsMask( uint16_t* channelsDefaultMask ); + +/*! + * \brief Computes the frequency for the RX1 window + * for the channel plan type A, 20MHz. + * + * \param [in] channel The channel utilized currently. + * + * \retval Returns the frequency which shall be used. + */ +uint32_t RegionCN470A20GetRx1Frequency( uint8_t channel ); + +/*! + * \brief Computes the frequency for the RX2 window + * for the channel plan type A, 20MHz. + * + * \param [in] joinChannelIndex The join channel index. + * + * \param [in] isOtaaDevice Set to true, if the device is an OTAA device. + * + * \retval Returns the frequency which shall be used. + */ +uint32_t RegionCN470A20GetRx2Frequency( uint8_t joinChannelIndex, bool isOtaaDevice ); + +/*! \} defgroup REGIONCN470 */ + +#ifdef __cplusplus +} +#endif + +#endif // __REGION_CN470_A20_H__ diff --git a/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCN470A26.c b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCN470A26.c new file mode 100644 index 0000000..14169d9 --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCN470A26.c @@ -0,0 +1,169 @@ +/*! + * \file RegionCN470A26.c + * + * \brief Specific Region implementation of CN470 Channel plan type A, 26MHz. + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \addtogroup REGIONCN470 + * + * \{ + */ +#include "RegionCN470.h" +#include "RegionBaseUS.h" +#include "RegionCN470A26.h" + +/* The HYBRID_DEFAULT_MASKx define the enabled channels in Hybrid mode*/ +/* Note: they can be redefined in lorawan_conf.h*/ +#ifndef HYBRID_DEFAULT_MASK0 /*enabled channels from channel 15 down to channel 0*/ +#define HYBRID_DEFAULT_MASK0 0x00FF /*channel 7 down to channel 0 enabled*/ +#endif +#ifndef HYBRID_DEFAULT_MASK1 /*enabled channels from channel 31 down to channel 16*/ +#define HYBRID_DEFAULT_MASK1 0x0000 +#endif +#ifndef HYBRID_DEFAULT_MASK2 /*enabled channels from channel 47 down to channel 32*/ +#define HYBRID_DEFAULT_MASK2 0x0000 +#endif +#ifndef HYBRID_DEFAULT_MASK3 /*enabled channels from channel 63 down to channel 48*/ +#define HYBRID_DEFAULT_MASK3 0x0000 +#endif +#ifndef HYBRID_DEFAULT_MASK4 /*enabled channels from channel 79 down to channel 64*/ +#define HYBRID_DEFAULT_MASK4 0x0000 +#endif +#ifndef HYBRID_DEFAULT_MASK5 /*enabled channels from channel 95 down to channel 80*/ +#define HYBRID_DEFAULT_MASK5 0x0000 +#endif + +uint32_t RegionCN470A26GetDownlinkFrequency( uint8_t channel, uint8_t joinChannelIndex, bool isPingSlot ) +{ + return CN470_A26_BEACON_FREQ; +} + +uint8_t RegionCN470A26GetBeaconChannelOffset( uint8_t joinChannelIndex ) +{ + return 0; +} + +uint8_t RegionCN470A26LinkAdrChMaskUpdate( uint16_t* channelsMask, uint8_t chMaskCntl, + uint16_t chanMask, ChannelParams_t* channels ) +{ + uint8_t status = 0x07; + + if( ( chMaskCntl == 5 ) || ( chMaskCntl == 6 ) || ( chMaskCntl == 7 ) ) + { + // RFU + status &= 0xFE; // Channel mask KO + } + else if( chMaskCntl == 3 ) + { + // Enable all channels + channelsMask[0] = 0xFFFF; + channelsMask[1] = 0xFFFF; + channelsMask[2] = 0xFFFF; + channelsMask[3] = 0xFFFF; + channelsMask[4] = 0x0000; + channelsMask[5] = 0x0000; + } + else if( chMaskCntl == 4 ) + { + // Disable all channels + channelsMask[0] = 0x0000; + channelsMask[1] = 0x0000; + channelsMask[2] = 0x0000; + channelsMask[3] = 0x0000; + channelsMask[4] = 0x0000; + channelsMask[5] = 0x0000; + } + else + { + // chMaskCntl 0 to 2 + for( uint8_t i = 0; i < 16; i++ ) + { + if( ( ( chanMask & ( 1 << i ) ) != 0 ) && + ( channels[chMaskCntl * 16 + i].Frequency == 0 ) ) + {// Trying to enable an undefined channel + status &= 0xFE; // Channel mask KO + } + } + channelsMask[chMaskCntl] = chanMask; + } + return status; +} + +bool RegionCN470A26VerifyRfFreq( uint32_t freq ) +{ + // Downstream group 1 + if( RegionBaseUSVerifyFrequencyGroup( freq, CN470_A26_FIRST_RX_CHANNEL, + CN470_A26_LAST_RX_CHANNEL, + CN470_A26_STEPWIDTH_RX_CHANNEL ) == false ) + { + return false; + } + return true; +} + +void RegionCN470A26InitializeChannels( ChannelParams_t* channels ) +{ + // Upstream group 1 + for( uint8_t i = 0; i < 48; i++ ) + { + channels[i].Frequency = CN470_A26_FIRST_TX_CHANNEL + i * CN470_A26_STEPWIDTH_RX_CHANNEL; + channels[i].DrRange.Value = ( CN470_TX_MAX_DATARATE << 4 ) | CN470_TX_MIN_DATARATE; + channels[i].Band = 0; + } +} + +void RegionCN470A26InitializeChannelsMask( uint16_t* channelsDefaultMask ) +{ + /* ST_WORKAROUND_BEGIN: Hybrid mode */ +#if ( HYBRID_ENABLED == 1 ) + channelsDefaultMask[0] = HYBRID_DEFAULT_MASK0; + channelsDefaultMask[1] = HYBRID_DEFAULT_MASK1; + channelsDefaultMask[2] = HYBRID_DEFAULT_MASK2; + channelsDefaultMask[3] = HYBRID_DEFAULT_MASK3; + channelsDefaultMask[4] = HYBRID_DEFAULT_MASK4; + channelsDefaultMask[5] = HYBRID_DEFAULT_MASK5; +#else + // Enable all possible channels + channelsDefaultMask[0] = 0xFFFF; + channelsDefaultMask[1] = 0xFFFF; + channelsDefaultMask[2] = 0xFFFF; + channelsDefaultMask[3] = 0x0000; + channelsDefaultMask[4] = 0x0000; + channelsDefaultMask[5] = 0x0000; +#endif /* HYBRID_ENABLED == 1 */ + /* ST_WORKAROUND_END */ +} + +uint32_t RegionCN470A26GetRx1Frequency( uint8_t channel ) +{ + return ( CN470_A26_FIRST_RX_CHANNEL + ( ( channel % 24 ) * CN470_A26_STEPWIDTH_RX_CHANNEL ) ); +} + +uint32_t RegionCN470A26GetRx2Frequency( uint8_t joinChannelIndex, bool isOtaaDevice ) +{ + return CN470_A26_RX_WND_2_FREQ; +} + +/*! \} defgroup LORAMACCLASSB */ diff --git a/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCN470A26.h b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCN470A26.h new file mode 100644 index 0000000..472c6af --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCN470A26.h @@ -0,0 +1,211 @@ +/*! + * \file RegionCN470A26.h + * + * \brief Specific Region definition of CN470 Channel plan type A, 26MHz. + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \addtogroup REGIONCN470 + * + * \{ + */ +#ifndef __REGION_CN470_A26_H__ +#define __REGION_CN470_A26_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "Region.h" + +/*! + * The maximum number of channels. + * Channel plan type A, 26MHz. + */ +#define CN470_A26_CHANNELS_MASK_SIZE 3 + +/*! + * The number of entries in the join accept list. + * Channel plan type A, 26MHz. + */ +#define CN470_A26_JOIN_ACCEPT_LIST_SIZE 3 + +/*! + * The number of channels available for the beacon. + * Channel plan type A, 26MHz. + */ +#define CN470_A26_BEACON_NB_CHANNELS 1 + +/*! + * The number of channels available for the ping slots. + * Channel plan type A, 26MHz. + */ +#define CN470_A26_PING_SLOT_NB_CHANNELS 1 + +/*! + * The first RX channel, downstream group 1. + * Channel plan type A, 26MHz. + */ +#define CN470_A26_FIRST_RX_CHANNEL 490100000 + +/*! + * The last RX channel, downstream group 1. + * Channel plan type A, 26MHz. + */ +#define CN470_A26_LAST_RX_CHANNEL 494700000 + +/*! + * The frequency stepwidth between RX channels, + * downstream group 1. + * Channel plan type A, 26MHz. + */ +#define CN470_A26_STEPWIDTH_RX_CHANNEL 200000 + +/*! + * The first TX channel, upstream group 1. + * Channel plan type A, 26MHz. + */ +#define CN470_A26_FIRST_TX_CHANNEL 470300000 + +/*! + * The last TX channel, upstream group 1. + * Channel plan type A, 26MHz. + */ +#define CN470_A26_LAST_TX_CHANNEL 479700000 + +/*! + * The frequency stepwidth between RX channels, + * upstream group 1. + * Channel plan type A, 26MHz. + */ +#define CN470_A26_STEPWIDTH_TX_CHANNEL 200000 + +/*! + * The default frequency for RX window 2 + * Channel plan type A, 26MHz. + */ +#define CN470_A26_RX_WND_2_FREQ 492500000 + +/*! + * The default frequency for beacon. + * Channel plan type A, 26MHz. + */ +#define CN470_A26_BEACON_FREQ 494900000 + +/*! + * \brief Calculation of the beacon frequency. + * + * \param [in] channel The Beacon channel number. + * + * \param [in] joinChannelIndex The join channel index. + * + * \param [in] isPingSlot Set to true, if its a ping slot. + * + * \retval Returns the beacon frequency. + */ +uint32_t RegionCN470A26GetDownlinkFrequency( uint8_t channel, uint8_t joinChannelIndex, bool isPingSlot ); + +/*! + * \brief Performs the update of the channelsMask based on the input parameters + * for the Channel plan type A, 26MHz. + * + * \param [in] joinChannelIndex The join channel index. + * + * \retval Returns the offset for the given join channel. + */ +uint8_t RegionCN470A26GetBeaconChannelOffset( uint8_t joinChannelIndex ); + +/*! + * \brief Performs the update of the channelsMask based on the input parameters + * for the Channel plan type A, 26MHz. + * + * \param [in] channelsMask A pointer to the channels mask. + * + * \param [in] chMaskCntl The value of the chMaskCntl field of the LinkAdrReq. + * + * \param [in] chanMask The value of the chanMask field of the LinkAdrReq. + * + * \param [in] channels A pointer to the available channels. + * + * \retval Status of the operation. Return 0x07 if the channels mask is valid. + */ +uint8_t RegionCN470A26LinkAdrChMaskUpdate( uint16_t* channelsMask, uint8_t chMaskCntl, + uint16_t chanMask, ChannelParams_t* channels ); + +/*! + * \brief Verifies if the frequency provided is valid + * for the Channel plan type A, 26MHz. + * + * \param [in] frequency The frequency to verify. + * + * \retval Returns true, if the frequency is valid. + */ +bool RegionCN470A26VerifyRfFreq( uint32_t frequency ); + +/*! + * \brief Initializes all channels, datarates, frequencies and bands + * for the Channel plan type A, 26MHz. + * + * \param [in] channels A pointer to the available channels. + */ +void RegionCN470A26InitializeChannels( ChannelParams_t* channels ); + +/*! + * \brief Initializes the channels default mask + * for the Channel plan type A, 26MHz. + * + * \param [in] channelsDefaultMask A pointer to the channels default mask. + */ +void RegionCN470A26InitializeChannelsMask( uint16_t* channelsDefaultMask ); + +/*! + * \brief Computes the frequency for the RX1 window + * for the Channel plan type A, 26MHz. + * + * \param [in] channel The channel utilized currently. + * + * \retval Returns the frequency which shall be used. + */ +uint32_t RegionCN470A26GetRx1Frequency( uint8_t channel ); + +/*! + * \brief Computes the frequency for the RX2 window + * for the Channel plan type A, 26MHz. + * + * \param [in] joinChannelIndex The join channel index. + * + * \param [in] isOtaaDevice Set to true, if the device is an OTAA device. + * + * \retval Returns the frequency which shall be used. + */ +uint32_t RegionCN470A26GetRx2Frequency( uint8_t joinChannelIndex, bool isOtaaDevice ); + +/*! \} defgroup REGIONCN470 */ + +#ifdef __cplusplus +} +#endif + +#endif // __REGION_CN470_A26_H__ diff --git a/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCN470B20.c b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCN470B20.c new file mode 100644 index 0000000..1efb45c --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCN470B20.c @@ -0,0 +1,142 @@ +/*! + * \file RegionCN470B20.c + * + * \brief Specific Region implementation of CN470 Channel plan type B, 20MHz. + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \addtogroup REGIONCN470 + * + * \{ + */ +#include "RegionCN470.h" +#include "RegionBaseUS.h" +#include "RegionCN470B20.h" +#include "RegionCN470A20.h" + +uint32_t RegionCN470B20GetDownlinkFrequency( uint8_t channel, uint8_t joinChannelIndex, bool isPingSlot ) +{ + if( isPingSlot == true) + { + return RegionCN470B20GetRx1Frequency( channel ); + } + else + { + if( joinChannelIndex == 8 ) + { + return RegionCN470B20GetRx1Frequency( 23 ); + } + else + { + return RegionCN470B20GetRx1Frequency( 55 ); + } + } +} + +uint8_t RegionCN470B20GetBeaconChannelOffset( uint8_t joinChannelIndex ) +{ + return ( joinChannelIndex - 8 ) * 32; +} + +uint8_t RegionCN470B20LinkAdrChMaskUpdate( uint16_t* channelsMask, uint8_t chMaskCntl, + uint16_t chanMask, ChannelParams_t* channels ) +{ + // It follows the same implementation as type A + return RegionCN470A20LinkAdrChMaskUpdate( channelsMask, chMaskCntl, + chanMask, channels ); +} + +bool RegionCN470B20VerifyRfFreq( uint32_t freq ) +{ + bool group1Status = false; + bool group2Status = false; + + // Downstream group 1 + group1Status = RegionBaseUSVerifyFrequencyGroup( freq, CN470_B20_FIRST_RX1_CHANNEL, + CN470_B20_LAST_RX1_CHANNEL, + CN470_B20_STEPWIDTH_RX1_CHANNEL ); + // Downstream group 2 + group2Status = RegionBaseUSVerifyFrequencyGroup( freq, CN470_B20_FIRST_RX2_CHANNEL, + CN470_B20_LAST_RX2_CHANNEL, + CN470_B20_STEPWIDTH_RX2_CHANNEL ); + + // The frequency must be available in one of the groups + if( ( group1Status == false ) && ( group2Status == false ) ) + { + return false; + } + return true; +} + +void RegionCN470B20InitializeChannels( ChannelParams_t* channels ) +{ + // Upstream group 1 + for( uint8_t i = 0; i < 32; i++ ) + { + channels[i].Frequency = CN470_B20_FIRST_TX1_CHANNEL + i * CN470_B20_STEPWIDTH_TX1_CHANNEL; + channels[i].DrRange.Value = ( CN470_TX_MAX_DATARATE << 4 ) | CN470_TX_MIN_DATARATE; + channels[i].Band = 0; + } + // Upstream group 2 + for( uint8_t i = 32; i < 64; i++ ) + { + channels[i].Frequency = CN470_B20_FIRST_TX2_CHANNEL + ( i - 32 ) * CN470_B20_STEPWIDTH_TX2_CHANNEL; + channels[i].DrRange.Value = ( CN470_TX_MAX_DATARATE << 4 ) | CN470_TX_MIN_DATARATE; + channels[i].Band = 0; + } +} + +void RegionCN470B20InitializeChannelsMask( uint16_t* channelsDefaultMask ) +{ + RegionCN470A20InitializeChannelsMask( channelsDefaultMask ); +} + +uint32_t RegionCN470B20GetRx1Frequency( uint8_t channel ) +{ + // Base frequency for downstream group 1 + uint32_t baseFrequency = CN470_B20_FIRST_RX1_CHANNEL; + uint8_t offset = 0; + + if( channel >= 32 ) + { + // Base frequency for downstream group 2 + baseFrequency = CN470_B20_FIRST_RX2_CHANNEL; + offset = 32; + } + return ( baseFrequency + ( ( channel - offset ) * CN470_B20_STEPWIDTH_RX1_CHANNEL ) ); +} + +uint32_t RegionCN470B20GetRx2Frequency( uint8_t joinChannelIndex, bool isOtaaDevice ) +{ + uint32_t otaaFrequencies[] = CN470_B20_RX_WND_2_FREQ_OTAA; + + if( isOtaaDevice == true ) + { + return otaaFrequencies[joinChannelIndex - 8]; + } + // ABP device + return CN470_B20_RX_WND_2_FREQ_ABP; +} + +/*! \} defgroup LORAMACCLASSB */ diff --git a/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCN470B20.h b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCN470B20.h new file mode 100644 index 0000000..52571c2 --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCN470B20.h @@ -0,0 +1,253 @@ +/*! + * \file RegionCN470B20.h + * + * \brief Specific Region definition of CN470 Channel plan type B, 20MHz. + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \addtogroup REGIONCN470 + * + * \{ + */ +#ifndef __REGION_CN470_B20_H__ +#define __REGION_CN470_B20_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "Region.h" + +/*! + * The maximum number of channels. + * Channel plan type B, 20MHz. + */ +#define CN470_B20_CHANNELS_MASK_SIZE 4 + +/*! + * The number of entries in the join accept list. + * Channel plan type B, 20MHz. + */ +#define CN470_B20_JOIN_ACCEPT_LIST_SIZE 4 + +/*! + * This is a number which is used to calculate the + * beacon channel in case of frequency hopping. + * Channel plan type B, 20MHz. + */ +#define CN470_B20_BEACON_NB_CHANNELS 1 + +/*! + * This is a number which is used to calculate the + * ping slot channel in case of frequency hopping. + * Channel plan type B, 20MHz. + */ +#define CN470_B20_PING_SLOT_NB_CHANNELS 32 + +/*! + * The first RX channel, downstream group 1. + * Channel plan type B, 20MHz. + */ +#define CN470_B20_FIRST_RX1_CHANNEL 476900000 + +/*! + * The last RX channel, downstream group 1. + * Channel plan type B, 20MHz. + */ +#define CN470_B20_LAST_RX1_CHANNEL 483100000 + +/*! + * The frequency stepwidth between RX channels, + * downstream group 1. + * Channel plan type B, 20MHz. + */ +#define CN470_B20_STEPWIDTH_RX1_CHANNEL 200000 + +/*! + * The first RX channel, downstream group 2. + * Channel plan type B, 20MHz. + */ +#define CN470_B20_FIRST_RX2_CHANNEL 496900000 + +/*! + * The last RX channel, downstream group 2. + * Channel plan type B, 20MHz. + */ +#define CN470_B20_LAST_RX2_CHANNEL 503100000 + +/*! + * The frequency stepwidth between RX channels, + * downstream group 2. + * Channel plan type B, 20MHz. + */ +#define CN470_B20_STEPWIDTH_RX2_CHANNEL 200000 + +/*! + * The first TX channel, upstream group 1. + * Channel plan type B, 20MHz. + */ +#define CN470_B20_FIRST_TX1_CHANNEL CN470_B20_FIRST_RX1_CHANNEL + +/*! + * The last TX channel, upstream group 1. + * Channel plan type B, 20MHz. + */ +#define CN470_B20_LAST_TX1_CHANNEL CN470_B20_LAST_RX1_CHANNEL + +/*! + * The frequency stepwidth between RX channels, + * upstream group 1. + * Channel plan type B, 20MHz. + */ +#define CN470_B20_STEPWIDTH_TX1_CHANNEL CN470_B20_STEPWIDTH_RX1_CHANNEL + +/*! + * The first TX channel, upstream group 2. + * Channel plan type B, 20MHz. + */ +#define CN470_B20_FIRST_TX2_CHANNEL CN470_B20_FIRST_RX2_CHANNEL + +/*! + * The last TX channel, upstream group 2. + * Channel plan type B, 20MHz. + */ +#define CN470_B20_LAST_TX2_CHANNEL CN470_B20_LAST_RX2_CHANNEL + +/*! + * The frequency stepwidth between RX channels, + * upstream group 2. + * Channel plan type B, 20MHz. + */ +#define CN470_B20_STEPWIDTH_TX2_CHANNEL CN470_B20_STEPWIDTH_RX2_CHANNEL + +/*! + * The default frequency for RX window 2, when its + * an ABP device. + * Channel plan type B, 20MHz. + */ +#define CN470_B20_RX_WND_2_FREQ_ABP 498300000 + +/*! + * The channel plan frequencies for RX window 2, + * when its an OTAA device. + * Channel plan type B, 20MHz. + */ +#define CN470_B20_RX_WND_2_FREQ_OTAA { 478300000, 498300000 } + +/*! + * \brief Calculation of the beacon frequency. + * + * \param [in] channel The Beacon channel number. + * + * \param [in] joinChannelIndex The join channel index. + * + * \param [in] isPingSlot Set to true, if its a ping slot. + * + * \retval Returns the beacon frequency. + */ +uint32_t RegionCN470B20GetDownlinkFrequency( uint8_t channel, uint8_t joinChannelIndex, bool isPingSlot ); + +/*! + * \brief Performs the update of the channelsMask based on the input parameters + * for the Channel plan type B, 20MHz. + * + * \param [in] joinChannelIndex The join channel index. + * + * \retval Returns the offset for the given join channel. + */ +uint8_t RegionCN470B20GetBeaconChannelOffset( uint8_t joinChannelIndex ); + +/*! + * \brief Performs the update of the channelsMask based on the input parameters + * for the Channel plan type B, 20MHz. + * + * \param [in] channelsMask A pointer to the channels mask. + * + * \param [in] chMaskCntl The value of the chMaskCntl field of the LinkAdrReq. + * + * \param [in] chanMask The value of the chanMask field of the LinkAdrReq. + * + * \param [in] channels A pointer to the available channels. + * + * \retval Status of the operation. Return 0x07 if the channels mask is valid. + */ +uint8_t RegionCN470B20LinkAdrChMaskUpdate( uint16_t* channelsMask, uint8_t chMaskCntl, + uint16_t chanMask, ChannelParams_t* channels ); + +/*! + * \brief Verifies if the frequency provided is valid + * for the Channel plan type B, 20MHz. + * + * \param [in] frequency The frequency to verify. + * + * \retval Returns true, if the frequency is valid. + */ +bool RegionCN470B20VerifyRfFreq( uint32_t frequency ); + +/*! + * \brief Initializes all channels, datarates, frequencies and bands + * for the Channel plan type B, 20MHz. + * + * \param [in] channels A pointer to the available channels. + */ +void RegionCN470B20InitializeChannels( ChannelParams_t* channels ); + +/*! + * \brief Initializes the channels default mask + * for the Channel plan type B, 20MHz. + * + * \param [in] channelsDefaultMask A pointer to the channels default mask. + */ +void RegionCN470B20InitializeChannelsMask( uint16_t* channelsDefaultMask ); + +/*! + * \brief Computes the frequency for the RX1 window + * for the Channel plan type B, 20MHz. + * + * \param [in] channel The channel utilized currently. + * + * \retval Returns the frequency which shall be used. + */ +uint32_t RegionCN470B20GetRx1Frequency( uint8_t channel ); + +/*! + * \brief Computes the frequency for the RX2 window + * for the Channel plan type B, 20MHz. + * + * \param [in] joinChannelIndex The join channel index. + * + * \param [in] isOtaaDevice Set to true, if the device is an OTAA device. + * + * \retval Returns the frequency which shall be used. + */ +uint32_t RegionCN470B20GetRx2Frequency( uint8_t joinChannelIndex, bool isOtaaDevice ); + +/*! \} defgroup REGIONCN470 */ + +#ifdef __cplusplus +} +#endif + +#endif // __REGION_CN470_B20_H__ diff --git a/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCN470B26.c b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCN470B26.c new file mode 100644 index 0000000..5c11aba --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCN470B26.c @@ -0,0 +1,93 @@ +/*! + * \file RegionCN470B26.c + * + * \brief Specific Region implementation of CN470 Channel plan type B, 26MHz. + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \addtogroup REGIONCN470 + * + * \{ + */ +#include "RegionCN470.h" +#include "RegionBaseUS.h" +#include "RegionCN470A26.h" +#include "RegionCN470B26.h" + +uint32_t RegionCN470B26GetDownlinkFrequency( uint8_t channel, uint8_t joinChannelIndex, bool isPingSlot ) +{ + return CN470_B26_BEACON_FREQ; +} + +uint8_t RegionCN470B26GetBeaconChannelOffset( uint8_t joinChannelIndex ) +{ + return 0; +} + +uint8_t RegionCN470B26LinkAdrChMaskUpdate( uint16_t* channelsMask, uint8_t chMaskCntl, + uint16_t chanMask, ChannelParams_t* channels ) +{ + return RegionCN470A26LinkAdrChMaskUpdate( channelsMask, chMaskCntl, + chanMask, channels ); +} + +bool RegionCN470B26VerifyRfFreq( uint32_t freq ) +{ + // Downstream group 1 + if( RegionBaseUSVerifyFrequencyGroup( freq, CN470_B26_FIRST_RX_CHANNEL, + CN470_B26_LAST_RX_CHANNEL, + CN470_B26_STEPWIDTH_RX_CHANNEL ) == false ) + { + return false; + } + return true; +} + +void RegionCN470B26InitializeChannels( ChannelParams_t* channels ) +{ + // Upstream group 1 + for( uint8_t i = 0; i < 48; i++ ) + { + channels[i].Frequency = CN470_B26_FIRST_TX_CHANNEL + i * CN470_B26_STEPWIDTH_RX_CHANNEL; + channels[i].DrRange.Value = ( CN470_TX_MAX_DATARATE << 4 ) | CN470_TX_MIN_DATARATE; + channels[i].Band = 0; + } +} + +void RegionCN470B26InitializeChannelsMask( uint16_t* channelsDefaultMask ) +{ + RegionCN470A26InitializeChannelsMask( channelsDefaultMask ); +} + +uint32_t RegionCN470B26GetRx1Frequency( uint8_t channel ) +{ + return ( CN470_B26_FIRST_RX_CHANNEL + ( ( channel % 24 ) * CN470_B26_STEPWIDTH_RX_CHANNEL ) ); +} + +uint32_t RegionCN470B26GetRx2Frequency( uint8_t joinChannelIndex, bool isOtaaDevice ) +{ + return CN470_B26_RX_WND_2_FREQ; +} + +/*! \} defgroup LORAMACCLASSB */ diff --git a/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCN470B26.h b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCN470B26.h new file mode 100644 index 0000000..31656ff --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCN470B26.h @@ -0,0 +1,211 @@ +/*! + * \file RegionCN470B26.h + * + * \brief Specific Region definition of CN470 Channel plan type B, 26MHz. + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \addtogroup REGIONCN470 + * + * \{ + */ +#ifndef __REGION_CN470_B26_H__ +#define __REGION_CN470_B26_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "Region.h" + +/*! + * The maximum number of channels. + * Channel plan type B, 26MHz. + */ +#define CN470_B26_CHANNELS_MASK_SIZE 3 + +/*! + * The number of entries in the join accept list. + * Channel plan type B, 26MHz. + */ +#define CN470_B26_JOIN_ACCEPT_LIST_SIZE 3 + +/*! + * The number of channels available for the beacon. + * Channel plan type B, 26MHz. + */ +#define CN470_B26_BEACON_NB_CHANNELS 1 + +/*! + * The number of channels available for the ping slots. + * Channel plan type B, 26MHz. + */ +#define CN470_B26_PING_SLOT_NB_CHANNELS 1 + +/*! + * The first RX channel, downstream group 1. + * Channel plan type B, 26MHz. + */ +#define CN470_B26_FIRST_RX_CHANNEL 500100000 + +/*! + * The last RX channel, downstream group 1. + * Channel plan type B, 26MHz. + */ +#define CN470_B26_LAST_RX_CHANNEL 504700000 + +/*! + * The frequency stepwidth between RX channels, + * downstream group 1 and 2. + * Channel plan type B, 26MHz. + */ +#define CN470_B26_STEPWIDTH_RX_CHANNEL 200000 + +/*! + * The first TX channel, upstream group 1. + * Channel plan type B, 26MHz. + */ +#define CN470_B26_FIRST_TX_CHANNEL 480300000 + +/*! + * The last TX channel, upstream group 1. + * Channel plan type B, 26MHz. + */ +#define CN470_B26_LAST_TX_CHANNEL 489700000 + +/*! + * The frequency stepwidth between RX channels, + * upstream group 1. + * Channel plan type B, 26MHz. + */ +#define CN470_B26_STEPWIDTH_TX_CHANNEL 200000 + +/*! + * The default frequency for RX window 2, + * Channel plan type B, 26MHz. + */ +#define CN470_B26_RX_WND_2_FREQ 502500000 + +/*! + * The default frequency for beacon, + * Channel plan type B, 26MHz. + */ +#define CN470_B26_BEACON_FREQ 504900000 + +/*! + * \brief Calculation of the beacon frequency. + * + * \param [in] channel The Beacon channel number. + * + * \param [in] joinChannelIndex The join channel index. + * + * \param [in] isPingSlot Set to true, if its a ping slot. + * + * \retval Returns the beacon frequency. + */ +uint32_t RegionCN470B26GetDownlinkFrequency( uint8_t channel, uint8_t joinChannelIndex, bool isPingSlot ); + +/*! + * \brief Performs the update of the channelsMask based on the input parameters + * for the Channel plan type B, 26MHz. + * + * \param [in] joinChannelIndex The join channel index. + * + * \retval Returns the offset for the given join channel. + */ +uint8_t RegionCN470B26GetBeaconChannelOffset( uint8_t joinChannelIndex ); + +/*! + * \brief Performs the update of the channelsMask based on the input parameters + * for the Channel plan type B, 26MHz. + * + * \param [in] channelsMask A pointer to the channels mask. + * + * \param [in] chMaskCntl The value of the chMaskCntl field of the LinkAdrReq. + * + * \param [in] chanMask The value of the chanMask field of the LinkAdrReq. + * + * \param [in] channels A pointer to the available channels. + * + * \retval Status of the operation. Return 0x07 if the channels mask is valid. + */ +uint8_t RegionCN470B26LinkAdrChMaskUpdate( uint16_t* channelsMask, uint8_t chMaskCntl, + uint16_t chanMask, ChannelParams_t* channels ); + +/*! + * \brief Verifies if the frequency provided is valid + * for the Channel plan type B, 26MHz. + * + * \param [in] frequency The frequency to verify. + * + * \retval Returns true, if the frequency is valid. + */ +bool RegionCN470B26VerifyRfFreq( uint32_t frequency ); + +/*! + * \brief Initializes all channels, datarates, frequencies and bands + * for the Channel plan type B, 26MHz. + * + * \param [in] channels A pointer to the available channels. + */ +void RegionCN470B26InitializeChannels( ChannelParams_t* channels ); + +/*! + * \brief Initializes the channels mask and the channels default mask + * for the Channel plan type B, 26MHz. + * + * \param [in] channelsDefaultMask A pointer to the channels default mask. + */ +void RegionCN470B26InitializeChannelsMask( uint16_t* channelsDefaultMask ); + +/*! + * \brief Computes the frequency for the RX1 window + * for the Channel plan type B, 26MHz. + * + * \param [in] channel The channel utilized currently. + * + * \retval Returns the frequency which shall be used. + */ +uint32_t RegionCN470B26GetRx1Frequency( uint8_t channel ); + +/*! + * \brief Computes the frequency for the RX2 window + * for the Channel plan type B, 26MHz. + * + * \param [in] joinChannelIndex The join channel index. + * + * \param [in] isOtaaDevice Set to true, if the device is an OTAA device. + * + * \retval Returns the frequency which shall be used. + */ +uint32_t RegionCN470B26GetRx2Frequency( uint8_t joinChannelIndex, bool isOtaaDevice ); + +/*! \} defgroup REGIONCN470 */ + +#ifdef __cplusplus +} +#endif + +#endif // __REGION_CN470_B26_H__ diff --git a/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCN779.c b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCN779.c new file mode 100644 index 0000000..c82d423 --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCN779.c @@ -0,0 +1,1064 @@ +/*! + * \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 ) +*/ +/** + ****************************************************************************** + * + * Portions COPYRIGHT 2020 STMicroelectronics + * + * @file RegionCN779.c + * @author MCD Application Team + * @brief Region implementation for CN779 + ****************************************************************************** + */ +#include "../../../SubGHz_Phy/radio.h" +#include "RegionCN779.h" + +// Definitions +#define CHANNELS_MASK_SIZE 1 + +#if defined( REGION_CN779 ) +/* + * Non-volatile module context. + */ +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +static RegionNvmDataGroup1_t* RegionNvmGroup1; +static RegionNvmDataGroup2_t* RegionNvmGroup2; +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) +// static RegionNvmDataGroup1_t* RegionNvmGroup1; /* Unused for this region */ +static RegionNvmDataGroup2_t* RegionNvmGroup2; +static Band_t* RegionBands; +#endif /* REGION_VERSION */ + +// Static functions +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 TimerTime_t GetTimeOnAir( int8_t datarate, uint16_t pktLen ) +{ + int8_t phyDr = DataratesCN779[datarate]; + uint32_t bandwidth = RegionCommonGetBandwidth( datarate, BandwidthsCN779 ); + TimerTime_t timeOnAir = 0; + + if( datarate == DR_7 ) + { // High Speed FSK channel + timeOnAir = Radio.TimeOnAir( MODEM_FSK, bandwidth, phyDr * 1000, 0, 5, false, pktLen, true ); + } + else + { + timeOnAir = Radio.TimeOnAir( MODEM_LORA, bandwidth, phyDr, 1, 8, false, pktLen, true ); + } + return timeOnAir; +} +#endif /* REGION_CN779 */ + +PhyParam_t RegionCN779GetPhyParam( GetPhyParams_t* getPhy ) +{ + PhyParam_t phyParam = { 0 }; + +#if defined( REGION_CN779 ) + 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: + { + RegionCommonGetNextLowerTxDrParams_t nextLowerTxDrParams = + { + .CurrentDr = getPhy->Datarate, + .MaxDr = ( int8_t )CN779_TX_MAX_DATARATE, + .MinDr = ( int8_t )CN779_TX_MIN_DATARATE, + .NbChannels = CN779_MAX_NB_CHANNELS, + .ChannelsMask = RegionNvmGroup2->ChannelsMask, + .Channels = RegionNvmGroup2->Channels, + }; + phyParam.Value = RegionCommonGetNextLowerTxDr( &nextLowerTxDrParams ); + 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 = REGION_COMMON_DEFAULT_ADR_ACK_LIMIT; + break; + } + case PHY_DEF_ADR_ACK_DELAY: + { + phyParam.Value = REGION_COMMON_DEFAULT_ADR_ACK_DELAY; + break; + } + case PHY_MAX_PAYLOAD: + { + phyParam.Value = MaxPayloadOfDatarateCN779[getPhy->Datarate]; + break; + } + /* ST_WORKAROUND_BEGIN: Keep repeater feature */ + case PHY_MAX_PAYLOAD_REPEATER: + { + phyParam.Value = MaxPayloadOfDatarateRepeaterCN779[getPhy->Datarate]; + break; + } + /* ST_WORKAROUND_END */ + 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 = REGION_COMMON_DEFAULT_RECEIVE_DELAY1; + break; + } + case PHY_RECEIVE_DELAY2: + { + phyParam.Value = REGION_COMMON_DEFAULT_RECEIVE_DELAY2; + break; + } + case PHY_JOIN_ACCEPT_DELAY1: + { + phyParam.Value = REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY1; + break; + } + case PHY_JOIN_ACCEPT_DELAY2: + { + phyParam.Value = REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY2; + break; + } +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + case PHY_MAX_FCNT_GAP: + { + phyParam.Value = REGION_COMMON_DEFAULT_MAX_FCNT_GAP; + break; + } + case PHY_ACK_TIMEOUT: + { + phyParam.Value = ( REGION_COMMON_DEFAULT_ACK_TIMEOUT + randr( -REGION_COMMON_DEFAULT_ACK_TIMEOUT_RND, REGION_COMMON_DEFAULT_ACK_TIMEOUT_RND ) ); + break; + } +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + case PHY_RETRANSMIT_TIMEOUT: + { + phyParam.Value = ( REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT + randr( -REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT_RND, REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT_RND ) ); + break; + } +#endif /* REGION_VERSION */ + case PHY_DEF_DR1_OFFSET: + { + phyParam.Value = REGION_COMMON_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 = RegionNvmGroup2->ChannelsMask; + break; + } + case PHY_CHANNELS_DEFAULT_MASK: + { + phyParam.ChannelsMask = RegionNvmGroup2->ChannelsDefaultMask; + break; + } + case PHY_MAX_NB_CHANNELS: + { + phyParam.Value = CN779_MAX_NB_CHANNELS; + break; + } + case PHY_CHANNELS: + { + phyParam.Channels = RegionNvmGroup2->Channels; + break; + } + case PHY_DEF_UPLINK_DWELL_TIME: + { + phyParam.Value = CN779_DEFAULT_UPLINK_DWELL_TIME; + break; + } + case PHY_DEF_DOWNLINK_DWELL_TIME: + { + phyParam.Value = REGION_COMMON_DEFAULT_DOWNLINK_DWELL_TIME; + 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_FREQ: + { + phyParam.Value = CN779_PING_SLOT_CHANNEL_FREQ; + break; + } + case PHY_PING_SLOT_CHANNEL_DR: + { + phyParam.Value = CN779_PING_SLOT_CHANNEL_DR; + break; + } + case PHY_SF_FROM_DR: + { + phyParam.Value = DataratesCN779[getPhy->Datarate]; + break; + } + case PHY_BW_FROM_DR: + { + phyParam.Value = RegionCommonGetBandwidth( getPhy->Datarate, BandwidthsCN779 ); + break; + } + default: + { + break; + } + } + +#endif /* REGION_CN779 */ + return phyParam; +} + +void RegionCN779SetBandTxDone( SetBandTxDoneParams_t* txDone ) +{ +#if defined( REGION_CN779 ) +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + RegionCommonSetBandTxDone( &RegionNvmGroup1->Bands[RegionNvmGroup2->Channels[txDone->Channel].Band], + txDone->LastTxAirTime, txDone->Joined, txDone->ElapsedTimeSinceStartUp ); +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + RegionCommonSetBandTxDone( &RegionBands[RegionNvmGroup2->Channels[txDone->Channel].Band], + txDone->LastTxAirTime, txDone->Joined, txDone->ElapsedTimeSinceStartUp ); +#endif /* REGION_VERSION */ +#endif /* REGION_CN779 */ +} + +void RegionCN779InitDefaults( InitDefaultsParams_t* params ) +{ +#if defined( REGION_CN779 ) + Band_t bands[CN779_MAX_NB_BANDS] = + { + CN779_BAND0 + }; + + switch( params->Type ) + { + case INIT_TYPE_DEFAULTS: + { + if( ( params->NvmGroup1 == NULL ) || ( params->NvmGroup2 == NULL ) ) + { + return; + } + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + RegionNvmGroup1 = (RegionNvmDataGroup1_t*) params->NvmGroup1; + RegionNvmGroup2 = (RegionNvmDataGroup2_t*) params->NvmGroup2; + + // Default bands + memcpy1( ( uint8_t* )RegionNvmGroup1->Bands, ( uint8_t* )bands, sizeof( Band_t ) * CN779_MAX_NB_BANDS ); +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + RegionNvmGroup2 = (RegionNvmDataGroup2_t*) params->NvmGroup2; + RegionBands = (Band_t*) params->Bands; + + // Default bands + memcpy1( ( uint8_t* )RegionBands, ( uint8_t* )bands, sizeof( Band_t ) * CN779_MAX_NB_BANDS ); +#endif /* REGION_VERSION */ + + // Default channels + RegionNvmGroup2->Channels[0] = ( ChannelParams_t ) CN779_LC1; + RegionNvmGroup2->Channels[1] = ( ChannelParams_t ) CN779_LC2; + RegionNvmGroup2->Channels[2] = ( ChannelParams_t ) CN779_LC3; + + // Default ChannelsMask + RegionNvmGroup2->ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 ); + + // Update the channels mask + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, RegionNvmGroup2->ChannelsDefaultMask, CHANNELS_MASK_SIZE ); + break; + } + case INIT_TYPE_RESET_TO_DEFAULT_CHANNELS: + { + // Reset Channels Rx1Frequency to default 0 + RegionNvmGroup2->Channels[0].Rx1Frequency = 0; + RegionNvmGroup2->Channels[1].Rx1Frequency = 0; + RegionNvmGroup2->Channels[2].Rx1Frequency = 0; + // Update the channels mask + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, RegionNvmGroup2->ChannelsDefaultMask, CHANNELS_MASK_SIZE ); + break; + } + case INIT_TYPE_ACTIVATE_DEFAULT_CHANNELS: + { + // Restore channels default mask + RegionNvmGroup2->ChannelsMask[0] |= RegionNvmGroup2->ChannelsDefaultMask[0]; + break; + } + default: + { + break; + } + } +#endif /* REGION_CN779 */ +} + +bool RegionCN779Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ) +{ +#if defined( REGION_CN779 ) + 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; + } +#else + return false; +#endif /* REGION_CN779 */ +} + +void RegionCN779ApplyCFList( ApplyCFListParams_t* applyCFList ) +{ +#if defined( REGION_CN779 ) + 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 ); + } + } +#endif /* REGION_CN779 */ +} + +bool RegionCN779ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ) +{ +#if defined( REGION_CN779 ) + switch( chanMaskSet->ChannelsMaskType ) + { + case CHANNELS_MASK: + { + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + case CHANNELS_DEFAULT_MASK: + { + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + default: + return false; + } + return true; +#else + return false; +#endif /* REGION_CN779 */ +} + +void RegionCN779ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ) +{ +#if defined( REGION_CN779 ) + uint32_t tSymbolInUs = 0; + + // Get the datarate, perform a boundary check + rxConfigParams->Datarate = MIN( datarate, CN779_RX_MAX_DATARATE ); + rxConfigParams->Bandwidth = RegionCommonGetBandwidth( rxConfigParams->Datarate, BandwidthsCN779 ); + + if( rxConfigParams->Datarate == DR_7 ) + { // FSK + tSymbolInUs = RegionCommonComputeSymbolTimeFsk( DataratesCN779[rxConfigParams->Datarate] ); + } + else + { // LoRa + tSymbolInUs = RegionCommonComputeSymbolTimeLoRa( DataratesCN779[rxConfigParams->Datarate], BandwidthsCN779[rxConfigParams->Datarate] ); + } + + RegionCommonComputeRxWindowParameters( tSymbolInUs, minRxSymbols, rxError, Radio.GetWakeupTime( ), &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); +#endif /* REGION_CN779 */ +} + +bool RegionCN779RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) +{ +#if defined( REGION_CN779 ) + RadioModems_t modem; + int8_t dr = rxConfig->Datarate; + uint8_t maxPayload = 0; + int8_t phyDr = 0; + uint32_t frequency = rxConfig->Frequency; + + if( Radio.GetStatus( ) != RF_IDLE ) + { + return false; + } + + if( rxConfig->RxSlot == RX_SLOT_WIN_1 ) + { + // Apply window 1 frequency + frequency = RegionNvmGroup2->Channels[rxConfig->Channel].Frequency; + // Apply the alternative RX 1 window frequency, if it is available + if( RegionNvmGroup2->Channels[rxConfig->Channel].Rx1Frequency != 0 ) + { + frequency = RegionNvmGroup2->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 ); + } + + /* ST_WORKAROUND_BEGIN: Keep repeater feature */ + if( rxConfig->RepeaterSupport == true ) + { + maxPayload = MaxPayloadOfDatarateRepeaterCN779[dr]; + } + else + { + maxPayload = MaxPayloadOfDatarateCN779[dr]; + } + Radio.SetMaxPayloadLength( modem, maxPayload + LORAMAC_FRAME_PAYLOAD_OVERHEAD_SIZE ); + /* ST_WORKAROUND_END */ + + /* ST_WORKAROUND_BEGIN: Print Rx config */ + RegionCommonRxConfigPrint(rxConfig->RxSlot, frequency, dr); + /* ST_WORKAROUND_END */ + + *datarate = (uint8_t) dr; + return true; +#else + return false; +#endif /* REGION_CN779 */ +} + +bool RegionCN779TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ) +{ +#if defined( REGION_CN779 ) + RadioModems_t modem; + int8_t phyDr = DataratesCN779[txConfig->Datarate]; +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + int8_t txPowerLimited = RegionCommonLimitTxPower( txConfig->TxPower, RegionNvmGroup1->Bands[RegionNvmGroup2->Channels[txConfig->Channel].Band].TxMaxPower ); +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + int8_t txPowerLimited = RegionCommonLimitTxPower( txConfig->TxPower, RegionBands[RegionNvmGroup2->Channels[txConfig->Channel].Band].TxMaxPower ); +#endif /* REGION_VERSION */ + uint32_t bandwidth = RegionCommonGetBandwidth( txConfig->Datarate, BandwidthsCN779 ); + int8_t phyTxPower = 0; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain ); + + // Setup the radio frequency + Radio.SetChannel( RegionNvmGroup2->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 ); + } + /* ST_WORKAROUND_BEGIN: Print Tx config */ + RegionCommonTxConfigPrint(RegionNvmGroup2->Channels[txConfig->Channel].Frequency, txConfig->Datarate); + /* ST_WORKAROUND_END */ + + // Update time-on-air + *txTimeOnAir = GetTimeOnAir( txConfig->Datarate, txConfig->PktLen ); + + // Setup maximum payload length of the radio driver + Radio.SetMaxPayloadLength( modem, txConfig->PktLen ); + + *txPower = txPowerLimited; + return true; +#else + return false; +#endif /* REGION_CN779 */ +} + +uint8_t RegionCN779LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ) +{ + uint8_t status = 0x07; +#if defined( REGION_CN779 ) + RegionCommonLinkAdrParams_t linkAdrParams = { 0 }; + 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( RegionNvmGroup2->Channels[i].Frequency != 0 ) + { + chMask |= 1 << i; + } + } + else + { + if( ( ( chMask & ( 1 << i ) ) != 0 ) && + ( RegionNvmGroup2->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 = RegionNvmGroup2->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* ) RegionNvmGroup2->ChannelsMask, 0, sizeof( RegionNvmGroup2->ChannelsMask ) ); + // Update the channels mask + RegionNvmGroup2->ChannelsMask[0] = chMask; + } + + // Update status variables + *drOut = linkAdrParams.Datarate; + *txPowOut = linkAdrParams.TxPower; + *nbRepOut = linkAdrParams.NbRep; + *nbBytesParsed = bytesProcessed; + +#endif /* REGION_CN779 */ + return status; +} + +uint8_t RegionCN779RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ) +{ + uint8_t status = 0x07; +#if defined( REGION_CN779 ) + + // 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 + } + +#endif /* REGION_CN779 */ + return status; +} + +int8_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 ) +{ + // Do not accept the request + return -1; +} + +int8_t RegionCN779DlChannelReq( DlChannelReqParams_t* dlChannelReq ) +{ + uint8_t status = 0x03; +#if defined( REGION_CN779 ) + + // Verify if the frequency is supported + if( VerifyRfFreq( dlChannelReq->Rx1Frequency ) == false ) + { + status &= 0xFE; + } + + // Verify if an uplink frequency exists + if( RegionNvmGroup2->Channels[dlChannelReq->ChannelId].Frequency == 0 ) + { + status &= 0xFD; + } + + // Apply Rx1 frequency, if the status is OK + if( status == 0x03 ) + { + RegionNvmGroup2->Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency; + } + +#endif /* REGION_CN779 */ + return status; +} + +int8_t RegionCN779AlternateDr( int8_t currentDr, AlternateDrType_t type ) +{ + return currentDr; +} + +LoRaMacStatus_t RegionCN779NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ) +{ +#if defined( REGION_CN779 ) + uint8_t nbEnabledChannels = 0; + uint8_t nbRestrictedChannels = 0; + uint8_t enabledChannels[CN779_MAX_NB_CHANNELS] = { 0 }; + RegionCommonIdentifyChannelsParam_t identifyChannelsParam; + RegionCommonCountNbOfEnabledChannelsParams_t countChannelsParams; + LoRaMacStatus_t status = LORAMAC_STATUS_NO_CHANNEL_FOUND; + uint16_t joinChannels = CN779_JOIN_CHANNELS; + + if( RegionCommonCountChannels( RegionNvmGroup2->ChannelsMask, 0, 1 ) == 0 ) + { // Reactivate default channels + RegionNvmGroup2->ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); + } + + // Search how many channels are enabled + countChannelsParams.Joined = nextChanParams->Joined; + countChannelsParams.Datarate = nextChanParams->Datarate; + countChannelsParams.ChannelsMask = RegionNvmGroup2->ChannelsMask; + countChannelsParams.Channels = RegionNvmGroup2->Channels; +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + countChannelsParams.Bands = RegionNvmGroup1->Bands; +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + countChannelsParams.Bands = RegionBands; +#endif /* REGION_VERSION */ + countChannelsParams.MaxNbChannels = CN779_MAX_NB_CHANNELS; + countChannelsParams.JoinChannels = &joinChannels; + + identifyChannelsParam.AggrTimeOff = nextChanParams->AggrTimeOff; + identifyChannelsParam.LastAggrTx = nextChanParams->LastAggrTx; + identifyChannelsParam.DutyCycleEnabled = nextChanParams->DutyCycleEnabled; + identifyChannelsParam.MaxBands = CN779_MAX_NB_BANDS; + + identifyChannelsParam.ElapsedTimeSinceStartUp = nextChanParams->ElapsedTimeSinceStartUp; + identifyChannelsParam.LastTxIsJoinRequest = nextChanParams->LastTxIsJoinRequest; + identifyChannelsParam.ExpectedTimeOnAir = GetTimeOnAir( nextChanParams->Datarate, nextChanParams->PktLen ); + + identifyChannelsParam.CountNbOfEnabledChannelsParam = &countChannelsParams; + + status = RegionCommonIdentifyChannels( &identifyChannelsParam, aggregatedTimeOff, enabledChannels, + &nbEnabledChannels, &nbRestrictedChannels, time ); + + if( status == LORAMAC_STATUS_OK ) + { + // We found a valid channel + *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )]; + } + else if( status == LORAMAC_STATUS_NO_CHANNEL_FOUND ) + { + // Datarate not supported by any channel, restore defaults + RegionNvmGroup2->ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); + } + return status; +#else + return LORAMAC_STATUS_NO_CHANNEL_FOUND; +#endif /* REGION_CN779 */ +} + +LoRaMacStatus_t RegionCN779ChannelAdd( ChannelAddParams_t* channelAdd ) +{ +#if defined( REGION_CN779 ) + 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* ) &(RegionNvmGroup2->Channels[id]), ( uint8_t* ) channelAdd->NewChannel, sizeof( RegionNvmGroup2->Channels[id] ) ); + RegionNvmGroup2->Channels[id].Band = 0; + RegionNvmGroup2->ChannelsMask[0] |= ( 1 << id ); + return LORAMAC_STATUS_OK; +#else + return LORAMAC_STATUS_NO_CHANNEL_FOUND; +#endif /* REGION_CN779 */ +} + +bool RegionCN779ChannelsRemove( ChannelRemoveParams_t* channelRemove ) +{ +#if defined( REGION_CN779 ) + uint8_t id = channelRemove->ChannelId; + + if( id < CN779_NUMB_DEFAULT_CHANNELS ) + { + return false; + } + + // Remove the channel from the list of channels + RegionNvmGroup2->Channels[id] = ( ChannelParams_t ){ 0, 0, { 0 }, 0 }; + + return RegionCommonChanDisable( RegionNvmGroup2->ChannelsMask, id, CN779_MAX_NB_CHANNELS ); +#else + return false; +#endif /* REGION_CN779 */ +} + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +void RegionCN779SetContinuousWave( ContinuousWaveParams_t* continuousWave ) +{ +#if defined( REGION_CN779 ) + int8_t txPowerLimited = RegionCommonLimitTxPower( continuousWave->TxPower, RegionNvmGroup1->Bands[RegionNvmGroup2->Channels[continuousWave->Channel].Band].TxMaxPower ); + int8_t phyTxPower = 0; + uint32_t frequency = RegionNvmGroup2->Channels[continuousWave->Channel].Frequency; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, continuousWave->MaxEirp, continuousWave->AntennaGain ); + + Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout ); +#endif /* REGION_CN779 */ +} +#endif /* REGION_VERSION */ + +uint8_t RegionCN779ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) +{ +#if defined( REGION_CN779 ) + int8_t datarate = dr - drOffset; + + if( datarate < 0 ) + { + datarate = DR_0; + } + return datarate; +#else + return 0; +#endif /* REGION_CN779 */ +} + +void RegionCN779RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ) +{ +#if defined( REGION_CN779 ) + 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; +#endif /* REGION_CN779 */ +} diff --git a/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCN779.h b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCN779.h new file mode 100644 index 0000000..a175a40 --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCN779.h @@ -0,0 +1,493 @@ +/*! + * \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. + * \{ + */ +/** + ****************************************************************************** + * + * Portions COPYRIGHT 2020 STMicroelectronics + * + * @file RegionCN779.h + * @author MCD Application Team + * @brief Region definition for CN779 + ****************************************************************************** + */ +#ifndef __REGION_CN779_H__ +#define __REGION_CN779_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "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 + +/*! + * 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 + +/*! + * Enabled or disabled the duty cycle + */ +#define CN779_DUTY_CYCLE_ENABLED 1 + +/*! + * Maximum RX window duration + */ +#define CN779_MAX_RX_WINDOW 3000 + +/*! + * 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 + +/*! + * Default uplink dwell time configuration + */ +#define CN779_DEFAULT_UPLINK_DWELL_TIME 0 + +/* + * CLASS B + */ +/*! + * Beacon frequency + */ +#define CN779_BEACON_CHANNEL_FREQ 785000000 + +/*! + * Ping slot channel frequency + */ +#define CN779_PING_SLOT_CHANNEL_FREQ 785000000 + +/*! + * Payload size of a beacon frame + */ +#define CN779_BEACON_SIZE 17 + +/*! + * Size of RFU 1 field + */ +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +#define CN779_RFU1_SIZE 2 +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) +#define CN779_RFU1_SIZE 1 +#endif /* REGION_VERSION */ + +/*! + * Size of RFU 2 field + */ +#define CN779_RFU2_SIZE 0 + +/*! + * Datarate of the beacon channel + */ +#define CN779_BEACON_CHANNEL_DR DR_3 + +/*! + * Bandwidth 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 + * Band = { DutyCycle, TxMaxPower, LastBandUpdateTime, LastMaxCreditAssignTime, TimeCredits, MaxTimeCredits, ReadyForTransmission } + */ +#define CN779_BAND0 { 100, CN779_MAX_TX_POWER, 0, 0, 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 }; + +/* ST_WORKAROUND_BEGIN: Keep repeater feature */ +/*! + * 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 }; +/* ST_WORKAROUND_END */ + +/*! + * \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] params Sets the initialization type. + */ +void RegionCN779InitDefaults( InitDefaultsParams_t* params ); + +/*! + * \brief Verifies a parameter. + * + * \param [in] verify Pointer to the function parameters. + * + * \param [in] phyAttribute 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. + * + * \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 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. + */ +int8_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. + */ +int8_t RegionCN779DlChannelReq( 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 RegionCN779AlternateDr( int8_t currentDr, AlternateDrType_t type ); + +/*! + * \brief Searches and set the next random available channel + * + * \param [in] nextChanParams pointer of selected channel parameters + * + * \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 ); + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +/*! + * \brief Sets the radio into continuous wave mode. + * + * \param [IN] continuousWave Pointer to the function parameters. + */ +void RegionCN779SetContinuousWave( ContinuousWaveParams_t* continuousWave ); +#endif /* REGION_VERSION */ + +/*! + * \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 + * + * \param [out] outDr Datarate used to receive the beacon + */ + void RegionCN779RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ); + +/*! \} defgroup REGIONCN779 */ + +#ifdef __cplusplus +} +#endif + +#endif // __REGION_CN779_H__ diff --git a/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCommon.c b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCommon.c new file mode 100644 index 0000000..7886785 --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCommon.c @@ -0,0 +1,714 @@ +/*! + * \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 ) + */ +/** + ****************************************************************************** + * + * Portions COPYRIGHT 2020 STMicroelectronics + * + * @file RegionCommon.c + * @author MCD Application Team + * @brief LoRa MAC common region implementation + ****************************************************************************** + */ +#include +#include "../../../SubGHz_Phy/radio.h" +#include "../../Utilities/utilities.h" +#include "RegionCommon.h" +#include "../../../../BSP/systime.h" +#include "../../../../BSP/mw_log_conf.h" + +#define BACKOFF_DC_1_HOUR 100 +#define BACKOFF_DC_10_HOURS 1000 +#define BACKOFF_DC_24_HOURS 10000 + +#define BACKOFF_DUTY_CYCLE_1_HOUR_IN_S 3600 +#define BACKOFF_DUTY_CYCLE_10_HOURS_IN_S ( BACKOFF_DUTY_CYCLE_1_HOUR_IN_S + ( BACKOFF_DUTY_CYCLE_1_HOUR_IN_S * 10 ) ) +#define BACKOFF_DUTY_CYCLE_24_HOURS_IN_S ( BACKOFF_DUTY_CYCLE_10_HOURS_IN_S + ( BACKOFF_DUTY_CYCLE_1_HOUR_IN_S * 24 ) ) +#define BACKOFF_24_HOURS_IN_S ( BACKOFF_DUTY_CYCLE_1_HOUR_IN_S * 24 ) + +#ifndef DUTY_CYCLE_TIME_PERIOD +/*! + * Default duty cycle observation time period + * + * \remark The ETSI observation time period is 1 hour (3600000 ms) but, the implemented algorithm may violate the + * defined duty-cycle restrictions. In order to ensure that these restrictions never get violated we changed the + * default duty cycle observation time period to 1/2 hour (1800000 ms). + */ +#define DUTY_CYCLE_TIME_PERIOD 1800000 +#endif + +/*! + * \brief Returns `N / D` rounded to the smallest integer value greater than or equal to `N / D` + * + * \warning when `D == 0`, the result is undefined + * + * \remark `N` and `D` can be signed or unsigned + * + * \param [in] N the numerator, which can have any sign + * \param [in] D the denominator, which can have any sign + * \retval N / D with any fractional part rounded to the smallest integer value greater than or equal to `N / D` + */ +#define DIV_CEIL( N, D ) \ + ( \ + ( N > 0 ) ? \ + ( ( ( N ) + ( D ) - 1 ) / ( D ) ) : \ + ( ( N ) / ( D ) ) \ + ) + +#ifdef MW_LOG_ENABLED +static const char *EventRXSlotStrings[] = { "1", "2", "C", "Multi_C", "P", "Multi_P" }; +#endif + +static uint16_t GetDutyCycle( Band_t* band, bool joined, SysTime_t elapsedTimeSinceStartup ) +{ + uint16_t dutyCycle = band->DCycle; + + if( joined == false ) + { + uint16_t joinDutyCycle = BACKOFF_DC_24_HOURS; + + if( elapsedTimeSinceStartup.Seconds < BACKOFF_DUTY_CYCLE_1_HOUR_IN_S ) + { + joinDutyCycle = BACKOFF_DC_1_HOUR; + } + else if( elapsedTimeSinceStartup.Seconds < BACKOFF_DUTY_CYCLE_10_HOURS_IN_S ) + { + joinDutyCycle = BACKOFF_DC_10_HOURS; + } + else + { + joinDutyCycle = BACKOFF_DC_24_HOURS; + } + // Take the most restrictive duty cycle + dutyCycle = MAX( dutyCycle, joinDutyCycle ); + } + + // Prevent value of 0 + if( dutyCycle == 0 ) + { + dutyCycle = 1; + } + + return dutyCycle; +} + +static uint16_t SetMaxTimeCredits( Band_t* band, bool joined, SysTime_t elapsedTimeSinceStartup, + bool dutyCycleEnabled, bool lastTxIsJoinRequest ) +{ + uint16_t dutyCycle = band->DCycle; + TimerTime_t maxCredits = DUTY_CYCLE_TIME_PERIOD; + TimerTime_t elapsedTime = SysTimeToMs( elapsedTimeSinceStartup ); + SysTime_t timeDiff = { 0 }; + + // Get the band duty cycle. If not joined, the function either returns the join duty cycle + // or the band duty cycle, whichever is more restrictive. + dutyCycle = GetDutyCycle( band, joined, elapsedTimeSinceStartup ); + + if( joined == false ) + { + if( dutyCycle == BACKOFF_DC_1_HOUR ) + { + maxCredits = DUTY_CYCLE_TIME_PERIOD; + band->LastMaxCreditAssignTime = elapsedTime; + } + else if( dutyCycle == BACKOFF_DC_10_HOURS ) + { + maxCredits = DUTY_CYCLE_TIME_PERIOD * 10; + band->LastMaxCreditAssignTime = elapsedTime; + } + else + { + maxCredits = DUTY_CYCLE_TIME_PERIOD * 24; + } + + timeDiff = SysTimeSub( elapsedTimeSinceStartup, SysTimeFromMs( band->LastMaxCreditAssignTime ) ); + + // Verify if we have to assign the maximum credits in cases + // of the preconditions have changed. + if( ( ( dutyCycleEnabled == false ) && ( lastTxIsJoinRequest == false ) ) || + ( band->MaxTimeCredits != maxCredits ) || + ( timeDiff.Seconds >= BACKOFF_24_HOURS_IN_S ) ) + { + band->TimeCredits = maxCredits; + + if( elapsedTimeSinceStartup.Seconds >= BACKOFF_DUTY_CYCLE_24_HOURS_IN_S ) + { + timeDiff.Seconds = ( elapsedTimeSinceStartup.Seconds - BACKOFF_DUTY_CYCLE_24_HOURS_IN_S ) / BACKOFF_24_HOURS_IN_S; + timeDiff.Seconds *= BACKOFF_24_HOURS_IN_S; + timeDiff.Seconds += BACKOFF_DUTY_CYCLE_24_HOURS_IN_S; + timeDiff.SubSeconds = 0; + band->LastMaxCreditAssignTime = SysTimeToMs( timeDiff ); + } + } + } + else + { + if( dutyCycleEnabled == false ) + { + // Assign max credits when the duty cycle is disabled. + band->TimeCredits = maxCredits; + } + } + + // Assign the max credits if its the first time + if( band->LastBandUpdateTime == 0 ) + { + band->TimeCredits = maxCredits; + } + + // Setup the maximum allowed credits. We can assign them + // safely all the time. + band->MaxTimeCredits = maxCredits; + + return dutyCycle; +} + +static uint16_t UpdateTimeCredits( Band_t* band, bool joined, bool dutyCycleEnabled, + bool lastTxIsJoinRequest, SysTime_t elapsedTimeSinceStartup, + TimerTime_t currentTime ) +{ + uint16_t dutyCycle = SetMaxTimeCredits( band, joined, elapsedTimeSinceStartup, + dutyCycleEnabled, lastTxIsJoinRequest ); + + if( joined == true ) + { + // Apply a sliding window for the duty cycle with collection and speding + // credits. + band->TimeCredits += TimerGetElapsedTime( band->LastBandUpdateTime ); + } + + // Limit band credits to maximum + if( band->TimeCredits > band->MaxTimeCredits ) + { + band->TimeCredits = band->MaxTimeCredits; + } + + // Synchronize update time + band->LastBandUpdateTime = currentTime; + + return dutyCycle; +} + +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; +} + +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( Band_t* band, TimerTime_t lastTxAirTime, bool joined, SysTime_t elapsedTimeSinceStartup ) +{ + // Get the band duty cycle. If not joined, the function either returns the join duty cycle + // or the band duty cycle, whichever is more restrictive. + uint16_t dutyCycle = GetDutyCycle( band, joined, elapsedTimeSinceStartup ); + + // Reduce with transmission time + if( band->TimeCredits > ( lastTxAirTime * dutyCycle ) ) + { + // Reduce time credits by the time of air + band->TimeCredits -= ( lastTxAirTime * dutyCycle ); + } + else + { + band->TimeCredits = 0; + } +} + +TimerTime_t RegionCommonUpdateBandTimeOff( bool joined, Band_t* bands, + uint8_t nbBands, bool dutyCycleEnabled, + bool lastTxIsJoinRequest, SysTime_t elapsedTimeSinceStartup, + TimerTime_t expectedTimeOnAir ) +{ + TimerTime_t minTimeToWait = TIMERTIME_T_MAX; + TimerTime_t currentTime = TimerGetCurrentTime( ); + TimerTime_t creditCosts = 0; + uint16_t dutyCycle = 1; + uint8_t validBands = 0; + + for( uint8_t i = 0; i < nbBands; i++ ) + { + // Synchronization of bands and credits + dutyCycle = UpdateTimeCredits( &bands[i], joined, dutyCycleEnabled, + lastTxIsJoinRequest, elapsedTimeSinceStartup, + currentTime ); + + // Calculate the credit costs for the next transmission + // with the duty cycle and the expected time on air + creditCosts = expectedTimeOnAir * dutyCycle; + + // Check if the band is ready for transmission. Its ready, + // when the duty cycle is off, or the TimeCredits of the band + // is higher than the credit costs for the transmission. + if( ( bands[i].TimeCredits > creditCosts ) || + ( ( dutyCycleEnabled == false ) && ( joined == true ) ) ) + { + bands[i].ReadyForTransmission = true; + // This band is a potential candidate for an + // upcoming transmission, so increase the counter. + validBands++; + } + else + { + // In this case, the band has not enough credits + // for the next transmission. + bands[i].ReadyForTransmission = false; + + if( bands[i].MaxTimeCredits > creditCosts ) + { + // The band can only be taken into account, if the maximum credits + // of the band are higher than the credit costs. + // We calculate the minTimeToWait among the bands which are not + // ready for transmission and which are potentially available + // for a transmission in the future. + minTimeToWait = MIN( minTimeToWait, ( creditCosts - bands[i].TimeCredits ) ); + // This band is a potential candidate for an + // upcoming transmission (even if its time credits are not enough + // at the moment), so increase the counter. + validBands++; + } + + // Apply a special calculation if the device is not joined. + if( joined == false ) + { + SysTime_t backoffTimeRange = { + .Seconds = 0, + .SubSeconds = 0, + }; + // Get the backoff time range based on the duty cycle definition + if( dutyCycle == BACKOFF_DC_1_HOUR ) + { + backoffTimeRange.Seconds = BACKOFF_DUTY_CYCLE_1_HOUR_IN_S; + } + else if( dutyCycle == BACKOFF_DC_10_HOURS ) + { + backoffTimeRange.Seconds = BACKOFF_DUTY_CYCLE_10_HOURS_IN_S; + } + else + { + backoffTimeRange.Seconds = BACKOFF_DUTY_CYCLE_24_HOURS_IN_S; + } + // Calculate the time to wait. + if( elapsedTimeSinceStartup.Seconds > BACKOFF_DUTY_CYCLE_24_HOURS_IN_S ) + { + backoffTimeRange.Seconds += BACKOFF_24_HOURS_IN_S * ( ( ( elapsedTimeSinceStartup.Seconds - BACKOFF_DUTY_CYCLE_24_HOURS_IN_S ) / BACKOFF_24_HOURS_IN_S ) + 1 ); + } + // Calculate the time difference between now and the next range + backoffTimeRange = SysTimeSub( backoffTimeRange, elapsedTimeSinceStartup ); + minTimeToWait = SysTimeToMs( backoffTimeRange ); + } + } + } + + if( validBands == 0 ) + { + // There is no valid band available to handle a transmission + // in the given DUTY_CYCLE_TIME_PERIOD. + return TIMERTIME_T_MAX; + } + return minTimeToWait; +} + +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( datarate == 0x0F ) + { // 0xF means that the device MUST ignore that field, and keep the current parameter value. + datarate = verifyParams->CurrentDatarate; + } + else if( RegionCommonChanVerifyDr( verifyParams->NbChannels, verifyParams->ChannelsMask, datarate, + verifyParams->MinDatarate, verifyParams->MaxDatarate, verifyParams->Channels ) == false ) + { + status &= 0xFD; // Datarate KO + } + + // Verify tx power + if( txPower == 0x0F ) + { // 0xF means that the device MUST ignore that field, and keep the current parameter value. + txPower = verifyParams->CurrentTxPower; + } + else 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 ) + { // Set nbRep to the default value of 1. + nbRepetitions = 1; + } + } + + // Apply changes + *dr = datarate; + *txPow = txPower; + *nbRep = nbRepetitions; + + return status; +} + +uint32_t RegionCommonComputeSymbolTimeLoRa( uint8_t phyDr, uint32_t bandwidthInHz ) +{ + return ( 1 << phyDr ) * 1000000 / bandwidthInHz; +} + +uint32_t RegionCommonComputeSymbolTimeFsk( uint8_t phyDrInKbps ) +{ + return 8000 / ( uint32_t )phyDrInKbps; // 1 symbol equals 1 byte +} + +void RegionCommonComputeRxWindowParameters( uint32_t tSymbolInUs, uint8_t minRxSymbols, uint32_t rxErrorInMs, uint32_t wakeUpTimeInMs, uint32_t* windowTimeoutInSymbols, int32_t* windowOffsetInMs ) +{ + *windowTimeoutInSymbols = MAX( DIV_CEIL( ( ( 2 * minRxSymbols - 8 ) * tSymbolInUs + 2 * ( rxErrorInMs * 1000 ) ), tSymbolInUs ), minRxSymbols ); // Computed number of symbols + *windowOffsetInMs = ( int32_t )DIV_CEIL( ( int32_t )( 4 * tSymbolInUs ) - + ( int32_t )DIV_CEIL( ( *windowTimeoutInSymbols * tSymbolInUs ), 2 ) - + ( int32_t )( wakeUpTimeInMs * 1000 ), 1000 ); +} + +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 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 ); + /* ST_WORKAROUND_BEGIN: Print Beacon parameters */ + MW_LOG(TS_ON, VLEVEL_M, "RX_BC on freq %d Hz at DR %d\r\n", rxBeaconSetupParams->Frequency, rxBeaconSetupParams->BeaconDatarate ); + /* ST_WORKAROUND_END */ +} + +void RegionCommonCountNbOfEnabledChannels( RegionCommonCountNbOfEnabledChannelsParams_t* countNbOfEnabledChannelsParams, + uint8_t* enabledChannels, uint8_t* nbEnabledChannels, uint8_t* nbRestrictedChannels ) +{ + uint8_t nbChannelCount = 0; + uint8_t nbRestrictedChannelsCount = 0; + + for( uint8_t i = 0, k = 0; i < countNbOfEnabledChannelsParams->MaxNbChannels; i += 16, k++ ) + { + for( uint8_t j = 0; j < 16; j++ ) + { + if( ( countNbOfEnabledChannelsParams->ChannelsMask[k] & ( 1 << j ) ) != 0 ) + { + if( countNbOfEnabledChannelsParams->Channels[i + j].Frequency == 0 ) + { // Check if the channel is enabled + continue; + } + if( ( countNbOfEnabledChannelsParams->Joined == false ) && + ( countNbOfEnabledChannelsParams->JoinChannels != NULL ) ) + { + if( ( countNbOfEnabledChannelsParams->JoinChannels[k] & ( 1 << j ) ) == 0 ) + { + continue; + } + } + if( RegionCommonValueInRange( countNbOfEnabledChannelsParams->Datarate, + countNbOfEnabledChannelsParams->Channels[i + j].DrRange.Fields.Min, + countNbOfEnabledChannelsParams->Channels[i + j].DrRange.Fields.Max ) == false ) + { // Check if the current channel selection supports the given datarate + continue; + } + if( countNbOfEnabledChannelsParams->Bands[countNbOfEnabledChannelsParams->Channels[i + j].Band].ReadyForTransmission == false ) + { // Check if the band is available for transmission + nbRestrictedChannelsCount++; + continue; + } + enabledChannels[nbChannelCount++] = i + j; + } + } + } + *nbEnabledChannels = nbChannelCount; + *nbRestrictedChannels = nbRestrictedChannelsCount; +} + +LoRaMacStatus_t RegionCommonIdentifyChannels( RegionCommonIdentifyChannelsParam_t* identifyChannelsParam, + TimerTime_t* aggregatedTimeOff, uint8_t* enabledChannels, + uint8_t* nbEnabledChannels, uint8_t* nbRestrictedChannels, + TimerTime_t* nextTxDelay ) +{ + TimerTime_t elapsed = TimerGetElapsedTime( identifyChannelsParam->LastAggrTx ); + *nextTxDelay = identifyChannelsParam->AggrTimeOff - elapsed; + *nbRestrictedChannels = 1; + *nbEnabledChannels = 0; + + if( ( identifyChannelsParam->LastAggrTx == 0 ) || + ( identifyChannelsParam->AggrTimeOff <= elapsed ) ) + { + // Reset Aggregated time off + *aggregatedTimeOff = 0; + + // Update bands Time OFF + *nextTxDelay = RegionCommonUpdateBandTimeOff( identifyChannelsParam->CountNbOfEnabledChannelsParam->Joined, + identifyChannelsParam->CountNbOfEnabledChannelsParam->Bands, + identifyChannelsParam->MaxBands, + identifyChannelsParam->DutyCycleEnabled, + identifyChannelsParam->LastTxIsJoinRequest, + identifyChannelsParam->ElapsedTimeSinceStartUp, + identifyChannelsParam->ExpectedTimeOnAir ); + + RegionCommonCountNbOfEnabledChannels( identifyChannelsParam->CountNbOfEnabledChannelsParam, enabledChannels, + nbEnabledChannels, nbRestrictedChannels ); + } + + if( *nbEnabledChannels > 0 ) + { + *nextTxDelay = 0; + return LORAMAC_STATUS_OK; + } + else if( *nbRestrictedChannels > 0 ) + { + return LORAMAC_STATUS_DUTYCYCLE_RESTRICTED; + } + else + { + return LORAMAC_STATUS_NO_CHANNEL_FOUND; + } +} + +int8_t RegionCommonGetNextLowerTxDr( RegionCommonGetNextLowerTxDrParams_t *params ) +{ + int8_t drLocal = params->CurrentDr; + + if( params->CurrentDr == params->MinDr ) + { + return params->MinDr; + } + else + { + do + { + drLocal = ( drLocal - 1 ); + } while( ( drLocal != params->MinDr ) && + ( RegionCommonChanVerifyDr( params->NbChannels, params->ChannelsMask, drLocal, params->MinDr, params->MaxDr, params->Channels ) == false ) ); + + return drLocal; + } +} + +int8_t RegionCommonLimitTxPower( int8_t txPower, int8_t maxBandTxPower ) +{ + // Limit tx power to the band max + return MAX( txPower, maxBandTxPower ); +} + +uint32_t RegionCommonGetBandwidth( uint32_t drIndex, const uint32_t* bandwidths ) +{ + switch( bandwidths[drIndex] ) + { + default: + case 125000: + return 0; + case 250000: + return 1; + case 500000: + return 2; + } +} + +/* ST_WORKAROUND_BEGIN: Print Tx/Rx config */ +void RegionCommonRxConfigPrint(LoRaMacRxSlot_t rxSlot, uint32_t frequency, int8_t dr) +{ + if ( rxSlot < RX_SLOT_NONE ) + { + MW_LOG(TS_ON, VLEVEL_M, "RX_%s on freq %d Hz at DR %d\r\n", EventRXSlotStrings[rxSlot], frequency, dr ); + } + else + { + MW_LOG(TS_ON, VLEVEL_M, "RX on freq %d Hz at DR %d\r\n", frequency, dr ); + } +} + +void RegionCommonTxConfigPrint(uint32_t frequency, int8_t dr) +{ + MW_LOG(TS_ON, VLEVEL_M, "TX on freq %d Hz at DR %d\r\n", frequency, dr ); +} +/* ST_WORKAROUND_END */ diff --git a/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCommon.h b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCommon.h new file mode 100644 index 0000000..6d9cc2e --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionCommon.h @@ -0,0 +1,661 @@ +/*! + * \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. + * \{ + */ +/** + ****************************************************************************** + * + * Portions COPYRIGHT 2020 STMicroelectronics + * + * @file RegionCommon.h + * @author MCD Application Team + * @brief Region independent implementations which are common to all regions. + ****************************************************************************** + */ +#ifndef __REGIONCOMMON_H__ +#define __REGIONCOMMON_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "../LoRaMacInterfaces.h" +#include "../LoRaMacHeaderTypes.h" +#include "RegionNvm.h" +#include "RegionVersion.h" + +// Constants that are common to all the regions. + +/*! + * Receive delay of 1 second. + */ +#define REGION_COMMON_DEFAULT_RECEIVE_DELAY1 1000 + +/*! + * Receive delay of 2 seconds. + */ +#define REGION_COMMON_DEFAULT_RECEIVE_DELAY2 ( REGION_COMMON_DEFAULT_RECEIVE_DELAY1 + 1000 ) + +/*! + * Join accept delay of 5 seconds. + */ +#define REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY1 5000 + +/*! + * Join accept delay of 6 seconds. + */ +#define REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY2 ( REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY1 + 1000 ) + +/*! + * ADR ack limit. + */ +#define REGION_COMMON_DEFAULT_ADR_ACK_LIMIT 64 + +/*! + * ADR ack delay. + */ +#define REGION_COMMON_DEFAULT_ADR_ACK_DELAY 32 + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +/*! + * Maximum frame counter gap + */ +#define REGION_COMMON_DEFAULT_MAX_FCNT_GAP 16384 + +/*! + * Retransmission timeout for ACK in milliseconds. + */ +#define REGION_COMMON_DEFAULT_ACK_TIMEOUT 2000 + +/*! + * Rounding limit for generating random retransmission timeout for ACK. + * In milliseconds. + */ +#define REGION_COMMON_DEFAULT_ACK_TIMEOUT_RND 1000 +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) +/*! + * Retransmission timeout for ACK in milliseconds. + */ +#define REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT 2000 + +/*! + * Rounding limit for generating random retransmission timeout for ACK. + * In milliseconds. + */ +#define REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT_RND 1000 +#endif /* REGION_VERSION */ + +/*! + * Default Rx1 receive datarate offset + */ +#define REGION_COMMON_DEFAULT_RX1_DR_OFFSET 0 + +/*! + * Default downlink dwell time configuration + */ +#define REGION_COMMON_DEFAULT_DOWNLINK_DWELL_TIME 0 + +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 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; + +typedef struct sRegionCommonCountNbOfEnabledChannelsParams +{ + /*! + * Set to true, if the device is joined. + */ + bool Joined; + /*! + * The datarate to count the available channels. + */ + uint8_t Datarate; + /*! + * A pointer to the channels mask to verify. + */ + uint16_t* ChannelsMask; + /*! + * A pointer to the channels. + */ + ChannelParams_t* Channels; + /*! + * A pointer to the bands. + */ + Band_t* Bands; + /*! + * The number of available channels. + */ + uint16_t MaxNbChannels; + /*! + * A pointer to the bitmask containing the + * join channels. Shall have the same dimension as the + * ChannelsMask with a number of MaxNbChannels channels. + */ + uint16_t* JoinChannels; +}RegionCommonCountNbOfEnabledChannelsParams_t; + +typedef struct sRegionCommonIdentifyChannelsParam +{ + /*! + * Aggregated time-off time. + */ + TimerTime_t AggrTimeOff; + /*! + * Time of the last aggregated TX. + */ + TimerTime_t LastAggrTx; + /*! + * Set to true, if the duty cycle is enabled, otherwise false. + */ + bool DutyCycleEnabled; + /*! + * Maximum number of bands. + */ + uint8_t MaxBands; + /*! + * Elapsed time since the start of the node. + */ + SysTime_t ElapsedTimeSinceStartUp; + /*! + * Joined Set to true, if the last uplink was a join request + */ + bool LastTxIsJoinRequest; + /*! + * Expected time-on-air + */ + TimerTime_t ExpectedTimeOnAir; + /*! + * Pointer to a structure of RegionCommonCountNbOfEnabledChannelsParams_t. + */ + RegionCommonCountNbOfEnabledChannelsParams_t* CountNbOfEnabledChannelsParam; +}RegionCommonIdentifyChannelsParam_t; + +typedef struct sRegionCommonSetDutyCycleParams +{ + /*! + * Duty cycle period. + */ + TimerTime_t DutyCycleTimePeriod; + /*! + * Number of bands available. + */ + uint8_t MaxBands; + /*! + * A pointer to the bands. + */ + Band_t* Bands; +}RegionCommonSetDutyCycleParams_t; + +typedef struct sRegionCommonGetNextLowerTxDrParams +{ + int8_t CurrentDr; + int8_t MaxDr; + int8_t MinDr; + uint8_t NbChannels; + uint16_t* ChannelsMask; + ChannelParams_t* Channels; +}RegionCommonGetNextLowerTxDrParams_t; + +/*! + * \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] band The band to be updated. + * + * \param [in] lastTxAirTime The time on air of the last TX frame. + * + * \param [in] joined Set to true if the device has joined. + * + * \param [in] elapsedTimeSinceStartup Elapsed time since initialization. + */ +void RegionCommonSetBandTxDone( Band_t* band, TimerTime_t lastTxAirTime, bool joined, SysTime_t elapsedTimeSinceStartup ); + +/*! + * \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] bands A pointer to the bands. + * + * \param [in] nbBands The number of bands available. + * + * \param [in] dutyCycleEnabled Set to true, if the duty cycle is enabled. + * + * \param [in] lastTxIsJoinRequest Set to true, if the last TX is a join request. + * + * \param [in] elapsedTimeSinceStartup Elapsed time since start up. + * + * \param [in] expectedTimeOnAir Expected time on air for the next transmission. + * + * \retval Returns the time which must be waited to perform the next uplink. + */ +TimerTime_t RegionCommonUpdateBandTimeOff( bool joined, Band_t* bands, + uint8_t nbBands, bool dutyCycleEnabled, + bool lastTxIsJoinRequest, SysTime_t elapsedTimeSinceStartup, + TimerTime_t expectedTimeOnAir ); + +/*! + * \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] bandwidthInHz Bandwidth to use. + * + * \retval Returns the symbol time in microseconds. + */ +uint32_t RegionCommonComputeSymbolTimeLoRa( uint8_t phyDr, uint32_t bandwidthInHz ); + +/*! + * \brief Computes the symbol time for FSK modulation. + * + * \param [in] phyDrInKbps Physical datarate to use. + * + * \retval Returns the symbol time in microseconds. + */ +uint32_t RegionCommonComputeSymbolTimeFsk( uint8_t phyDrInKbps ); + +/*! + * \brief Computes the RX window timeout and the RX window offset. + * + * \param [in] tSymbolInUs Symbol timeout. + * + * \param [in] minRxSymbols Minimum required number of symbols to detect an Rx frame. + * + * \param [in] rxErrorInMs System maximum timing error of the receiver. In milliseconds + * The receiver will turn on in a [-rxErrorInMs : +rxErrorInMs] ms interval around RxOffset. + * + * \param [in] wakeUpTimeInMs Wakeup time of the system. + * + * \param [out] windowTimeoutInSymbols RX window timeout. + * + * \param [out] windowOffsetInMs RX window time offset to be applied to the RX delay. + */ +void RegionCommonComputeRxWindowParameters( uint32_t tSymbolInUs, uint8_t minRxSymbols, uint32_t rxErrorInMs, uint32_t wakeUpTimeInMs, uint32_t* windowTimeoutInSymbols, int32_t* windowOffsetInMs ); + +/*! + * \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] txPowerIndex 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 Sets up the radio into RX beacon mode. + * + * \param [in] rxBeaconSetupParams A pointer to the input parameters. + */ +void RegionCommonRxBeaconSetup( RegionCommonRxBeaconSetupParams_t* rxBeaconSetupParams ); + +/*! + * \brief Counts the number of enabled channels. + * + * \param [in] countNbOfEnabledChannelsParams A pointer to the input parameters. + * + * \param [out] enabledChannels A pointer to an array of size XX_MAX_NB_CHANNELS. The function + * stores the available channels into this array. + * + * \param [out] nbEnabledChannels The number of available channels found. + * + * \param [out] nbRestrictedChannels It contains the number of channel + * which are available, but restricted due to duty cycle. + */ +void RegionCommonCountNbOfEnabledChannels( RegionCommonCountNbOfEnabledChannelsParams_t* countNbOfEnabledChannelsParams, + uint8_t* enabledChannels, uint8_t* nbEnabledChannels, uint8_t* nbRestrictedChannels ); + +/*! + * \brief Identifies all channels which are available currently. + * + * \param [in] identifyChannelsParam A pointer to the input parameters. + * + * \param [out] aggregatedTimeOff The new value of the aggregatedTimeOff. The function + * may resets it to 0. + * + * \param [out] enabledChannels A pointer to an array of size XX_MAX_NB_CHANNELS. The function + * stores the available channels into this array. + * + * \param [out] nbEnabledChannels The number of available channels found. + * + * \param [out] nbRestrictedChannels It contains the number of channel + * which are available, but restricted due to duty cycle. + * + * \param [out] nextTxDelay Holds the time which has to be waited for the next possible + * uplink transmission. + * + *\retval Status of the operation. + */ +LoRaMacStatus_t RegionCommonIdentifyChannels( RegionCommonIdentifyChannelsParam_t* identifyChannelsParam, + TimerTime_t* aggregatedTimeOff, uint8_t* enabledChannels, + uint8_t* nbEnabledChannels, uint8_t* nbRestrictedChannels, + TimerTime_t* nextTxDelay ); + +/*! + * \brief Selects the next lower datarate. + * + * \param [in] params Data structure providing parameters based on \ref RegionCommonGetNextLowerTxDrParams_t + * + * \retval The next lower datarate. + */ +int8_t RegionCommonGetNextLowerTxDr( RegionCommonGetNextLowerTxDrParams_t *params ); + +/*! + * \brief Limits the TX power. + * + * \param [in] txPower Current TX power. + * + * \param [in] maxBandTxPower Maximum possible TX power. + * + * \retval Limited TX power. + */ +int8_t RegionCommonLimitTxPower( int8_t txPower, int8_t maxBandTxPower ); + +/*! + * \brief Gets the bandwidth. + * + * \param [in] drIndex Datarate index. + * + * \param [in] bandwidths A pointer to the bandwidth table. + * + * \retval Bandwidth. + */ +uint32_t RegionCommonGetBandwidth( uint32_t drIndex, const uint32_t* bandwidths ); + +/* ST_WORKAROUND_BEGIN: Print Tx/Rx config */ +/*! + * \brief Print the current RX configuration + * + * \param [in] rxSlot rx slot + * + * \param [in] frequency rf frequency + * + * \param [in] dr datarate + * + */ +void RegionCommonRxConfigPrint(LoRaMacRxSlot_t rxSlot, + uint32_t frequency, + int8_t dr); + +/*! + * \brief Print the current TX configuration + * + * \param [in] frequency rf frequency + * + * \param [in] dr datarate + * + */ +void RegionCommonTxConfigPrint(uint32_t frequency, int8_t dr); +/* ST_WORKAROUND_END */ +/*! \} defgroup REGIONCOMMON */ + +#ifdef __cplusplus +} +#endif + +#endif // __REGIONCOMMON_H__ diff --git a/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionEU433.c b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionEU433.c new file mode 100644 index 0000000..f181563 --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionEU433.c @@ -0,0 +1,1068 @@ +/*! + * \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 ) +*/ +/** + ****************************************************************************** + * + * Portions COPYRIGHT 2020 STMicroelectronics + * + * @file RegionEU433.c + * @author MCD Application Team + * @brief Region implementation for EU433 + ****************************************************************************** + */ +#include "../../../SubGHz_Phy/radio.h" +#include "RegionEU433.h" + +// Definitions +#define CHANNELS_MASK_SIZE 1 + +#if defined( REGION_EU433 ) +/* + * Non-volatile module context. + */ +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +static RegionNvmDataGroup1_t* RegionNvmGroup1; +static RegionNvmDataGroup2_t* RegionNvmGroup2; +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) +// static RegionNvmDataGroup1_t* RegionNvmGroup1; /* Unused for this region */ +static RegionNvmDataGroup2_t* RegionNvmGroup2; +static Band_t* RegionBands; +#endif /* REGION_VERSION */ + +// Static functions +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 TimerTime_t GetTimeOnAir( int8_t datarate, uint16_t pktLen ) +{ + int8_t phyDr = DataratesEU433[datarate]; + uint32_t bandwidth = RegionCommonGetBandwidth( datarate, BandwidthsEU433 ); + TimerTime_t timeOnAir = 0; + + if( datarate == DR_7 ) + { // High Speed FSK channel + timeOnAir = Radio.TimeOnAir( MODEM_FSK, bandwidth, phyDr * 1000, 0, 5, false, pktLen, true ); + } + else + { + timeOnAir = Radio.TimeOnAir( MODEM_LORA, bandwidth, phyDr, 1, 8, false, pktLen, true ); + } + return timeOnAir; +} +#endif /* REGION_EU433 */ + +PhyParam_t RegionEU433GetPhyParam( GetPhyParams_t* getPhy ) +{ + PhyParam_t phyParam = { 0 }; + +#if defined( REGION_EU433 ) + 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: + { + RegionCommonGetNextLowerTxDrParams_t nextLowerTxDrParams = + { + .CurrentDr = getPhy->Datarate, + .MaxDr = ( int8_t )EU433_TX_MAX_DATARATE, + .MinDr = ( int8_t )EU433_TX_MIN_DATARATE, + .NbChannels = EU433_MAX_NB_CHANNELS, + .ChannelsMask = RegionNvmGroup2->ChannelsMask, + .Channels = RegionNvmGroup2->Channels, + }; + phyParam.Value = RegionCommonGetNextLowerTxDr( &nextLowerTxDrParams ); + 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 = REGION_COMMON_DEFAULT_ADR_ACK_LIMIT; + break; + } + case PHY_DEF_ADR_ACK_DELAY: + { + phyParam.Value = REGION_COMMON_DEFAULT_ADR_ACK_DELAY; + break; + } + case PHY_MAX_PAYLOAD: + { + phyParam.Value = MaxPayloadOfDatarateEU433[getPhy->Datarate]; + break; + } + /* ST_WORKAROUND_BEGIN: Keep repeater feature */ + case PHY_MAX_PAYLOAD_REPEATER: + { + phyParam.Value = MaxPayloadOfDatarateRepeaterEU433[getPhy->Datarate]; + break; + } + /* ST_WORKAROUND_END */ + 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 = REGION_COMMON_DEFAULT_RECEIVE_DELAY1; + break; + } + case PHY_RECEIVE_DELAY2: + { + phyParam.Value = REGION_COMMON_DEFAULT_RECEIVE_DELAY2; + break; + } + case PHY_JOIN_ACCEPT_DELAY1: + { + phyParam.Value = REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY1; + break; + } + case PHY_JOIN_ACCEPT_DELAY2: + { + phyParam.Value = REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY2; + break; + } +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + case PHY_MAX_FCNT_GAP: + { + phyParam.Value = REGION_COMMON_DEFAULT_MAX_FCNT_GAP; + break; + } + case PHY_ACK_TIMEOUT: + { + phyParam.Value = ( REGION_COMMON_DEFAULT_ACK_TIMEOUT + randr( -REGION_COMMON_DEFAULT_ACK_TIMEOUT_RND, REGION_COMMON_DEFAULT_ACK_TIMEOUT_RND ) ); + break; + } +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + case PHY_RETRANSMIT_TIMEOUT: + { + phyParam.Value = ( REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT + randr( -REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT_RND, REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT_RND ) ); + break; + } +#endif /* REGION_VERSION */ + case PHY_DEF_DR1_OFFSET: + { + phyParam.Value = REGION_COMMON_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 = RegionNvmGroup2->ChannelsMask; + break; + } + case PHY_CHANNELS_DEFAULT_MASK: + { + phyParam.ChannelsMask = RegionNvmGroup2->ChannelsDefaultMask; + break; + } + case PHY_MAX_NB_CHANNELS: + { + phyParam.Value = EU433_MAX_NB_CHANNELS; + break; + } + case PHY_CHANNELS: + { + phyParam.Channels = RegionNvmGroup2->Channels; + break; + } + case PHY_DEF_UPLINK_DWELL_TIME: + { + phyParam.Value = EU433_DEFAULT_UPLINK_DWELL_TIME; + break; + } + case PHY_DEF_DOWNLINK_DWELL_TIME: + { + phyParam.Value = REGION_COMMON_DEFAULT_DOWNLINK_DWELL_TIME; + 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_FREQ: + { + phyParam.Value = EU433_PING_SLOT_CHANNEL_FREQ; + break; + } + case PHY_PING_SLOT_CHANNEL_DR: + { + phyParam.Value = EU433_PING_SLOT_CHANNEL_DR; + break; + } + case PHY_SF_FROM_DR: + { + phyParam.Value = DataratesEU433[getPhy->Datarate]; + break; + } + case PHY_BW_FROM_DR: + { + phyParam.Value = RegionCommonGetBandwidth( getPhy->Datarate, BandwidthsEU433 ); + break; + } + default: + { + break; + } + } + +#endif /* REGION_EU433 */ + return phyParam; +} + +void RegionEU433SetBandTxDone( SetBandTxDoneParams_t* txDone ) +{ +#if defined( REGION_EU433 ) +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + RegionCommonSetBandTxDone( &RegionNvmGroup1->Bands[RegionNvmGroup2->Channels[txDone->Channel].Band], + txDone->LastTxAirTime, txDone->Joined, txDone->ElapsedTimeSinceStartUp ); +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + RegionCommonSetBandTxDone( &RegionBands[RegionNvmGroup2->Channels[txDone->Channel].Band], + txDone->LastTxAirTime, txDone->Joined, txDone->ElapsedTimeSinceStartUp ); +#endif /* REGION_VERSION */ +#endif /* REGION_EU433 */ +} + +void RegionEU433InitDefaults( InitDefaultsParams_t* params ) +{ +#if defined( REGION_EU433 ) + Band_t bands[EU433_MAX_NB_BANDS] = + { + EU433_BAND0 + }; + + switch( params->Type ) + { + case INIT_TYPE_DEFAULTS: + { + if( ( params->NvmGroup1 == NULL ) || ( params->NvmGroup2 == NULL ) ) + { + return; + } + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + RegionNvmGroup1 = (RegionNvmDataGroup1_t*) params->NvmGroup1; + RegionNvmGroup2 = (RegionNvmDataGroup2_t*) params->NvmGroup2; + + // Default bands + memcpy1( ( uint8_t* )RegionNvmGroup1->Bands, ( uint8_t* )bands, sizeof( Band_t ) * EU433_MAX_NB_BANDS ); +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + RegionNvmGroup2 = (RegionNvmDataGroup2_t*) params->NvmGroup2; + RegionBands = (Band_t*) params->Bands; + + // Default bands + memcpy1( ( uint8_t* )RegionBands, ( uint8_t* )bands, sizeof( Band_t ) * EU433_MAX_NB_BANDS ); +#endif /* REGION_VERSION */ + + // Default channels + RegionNvmGroup2->Channels[0] = ( ChannelParams_t ) EU433_LC1; + RegionNvmGroup2->Channels[1] = ( ChannelParams_t ) EU433_LC2; + RegionNvmGroup2->Channels[2] = ( ChannelParams_t ) EU433_LC3; + + // Default ChannelsMask + RegionNvmGroup2->ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 ); + + // Update the channels mask + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, RegionNvmGroup2->ChannelsDefaultMask, CHANNELS_MASK_SIZE ); + break; + } + case INIT_TYPE_RESET_TO_DEFAULT_CHANNELS: + { + // Reset Channels Rx1Frequency to default 0 + RegionNvmGroup2->Channels[0].Rx1Frequency = 0; + RegionNvmGroup2->Channels[1].Rx1Frequency = 0; + RegionNvmGroup2->Channels[2].Rx1Frequency = 0; + // Update the channels mask + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, RegionNvmGroup2->ChannelsDefaultMask, CHANNELS_MASK_SIZE ); + break; + } + case INIT_TYPE_ACTIVATE_DEFAULT_CHANNELS: + { + // Restore channels default mask + RegionNvmGroup2->ChannelsMask[0] |= RegionNvmGroup2->ChannelsDefaultMask[0]; + break; + } + default: + { + break; + } + } +#endif /* REGION_EU433 */ +} + +bool RegionEU433Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ) +{ +#if defined( REGION_EU433 ) + 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; + } +#else + return false; +#endif /* REGION_EU433 */ +} + +void RegionEU433ApplyCFList( ApplyCFListParams_t* applyCFList ) +{ +#if defined( REGION_EU433 ) + 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 ); + } + } +#endif /* REGION_EU433 */ +} + +bool RegionEU433ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ) +{ +#if defined( REGION_EU433 ) + switch( chanMaskSet->ChannelsMaskType ) + { + case CHANNELS_MASK: + { + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + case CHANNELS_DEFAULT_MASK: + { + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + default: + return false; + } + return true; +#else + return false; +#endif /* REGION_EU433 */ +} + +void RegionEU433ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ) +{ +#if defined( REGION_EU433 ) + uint32_t tSymbolInUs = 0; + + // Get the datarate, perform a boundary check + rxConfigParams->Datarate = MIN( datarate, EU433_RX_MAX_DATARATE ); + rxConfigParams->Bandwidth = RegionCommonGetBandwidth( rxConfigParams->Datarate, BandwidthsEU433 ); + + if( rxConfigParams->Datarate == DR_7 ) + { // FSK + tSymbolInUs = RegionCommonComputeSymbolTimeFsk( DataratesEU433[rxConfigParams->Datarate] ); + } + else + { // LoRa + tSymbolInUs = RegionCommonComputeSymbolTimeLoRa( DataratesEU433[rxConfigParams->Datarate], BandwidthsEU433[rxConfigParams->Datarate] ); + } + + RegionCommonComputeRxWindowParameters( tSymbolInUs, minRxSymbols, rxError, Radio.GetWakeupTime( ), &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); +#endif /* REGION_EU433 */ +} + +bool RegionEU433RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) +{ +#if defined( REGION_EU433 ) + RadioModems_t modem; + int8_t dr = rxConfig->Datarate; + uint8_t maxPayload = 0; + int8_t phyDr = 0; + uint32_t frequency = rxConfig->Frequency; + + if( Radio.GetStatus( ) != RF_IDLE ) + { + return false; + } + + if( rxConfig->RxSlot == RX_SLOT_WIN_1 ) + { + // Apply window 1 frequency + frequency = RegionNvmGroup2->Channels[rxConfig->Channel].Frequency; + // Apply the alternative RX 1 window frequency, if it is available + if( RegionNvmGroup2->Channels[rxConfig->Channel].Rx1Frequency != 0 ) + { + frequency = RegionNvmGroup2->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 ); + } + + /* ST_WORKAROUND_BEGIN: Keep repeater feature */ + if( rxConfig->RepeaterSupport == true ) + { + maxPayload = MaxPayloadOfDatarateRepeaterEU433[dr]; + } + else + { + maxPayload = MaxPayloadOfDatarateEU433[dr]; + } + Radio.SetMaxPayloadLength( modem, maxPayload + LORAMAC_FRAME_PAYLOAD_OVERHEAD_SIZE ); + /* ST_WORKAROUND_END */ + + /* ST_WORKAROUND_BEGIN: Print Rx config */ + RegionCommonRxConfigPrint(rxConfig->RxSlot, frequency, dr); + /* ST_WORKAROUND_END */ + + *datarate = (uint8_t) dr; + return true; +#else + return false; +#endif /* REGION_EU433 */ +} + +bool RegionEU433TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ) +{ +#if defined( REGION_EU433 ) + RadioModems_t modem; + int8_t phyDr = DataratesEU433[txConfig->Datarate]; +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + int8_t txPowerLimited = RegionCommonLimitTxPower( txConfig->TxPower, RegionNvmGroup1->Bands[RegionNvmGroup2->Channels[txConfig->Channel].Band].TxMaxPower ); +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + int8_t txPowerLimited = RegionCommonLimitTxPower( txConfig->TxPower, RegionBands[RegionNvmGroup2->Channels[txConfig->Channel].Band].TxMaxPower ); +#endif /* REGION_VERSION */ + uint32_t bandwidth = RegionCommonGetBandwidth( txConfig->Datarate, BandwidthsEU433 ); + int8_t phyTxPower = 0; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain ); + + // Setup the radio frequency + Radio.SetChannel( RegionNvmGroup2->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 ); + } + /* ST_WORKAROUND_BEGIN: Print Tx config */ + RegionCommonTxConfigPrint(RegionNvmGroup2->Channels[txConfig->Channel].Frequency, txConfig->Datarate); + /* ST_WORKAROUND_END */ + + // Update time-on-air + *txTimeOnAir = GetTimeOnAir( txConfig->Datarate, txConfig->PktLen ); + + // Setup maximum payload length of the radio driver + Radio.SetMaxPayloadLength( modem, txConfig->PktLen ); + + *txPower = txPowerLimited; + return true; +#else + return false; +#endif /* REGION_EU433 */ +} + +uint8_t RegionEU433LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ) +{ + uint8_t status = 0x07; +#if defined( REGION_EU433 ) + RegionCommonLinkAdrParams_t linkAdrParams = { 0 }; + 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( RegionNvmGroup2->Channels[i].Frequency != 0 ) + { + chMask |= 1 << i; + } + } + else + { + if( ( ( chMask & ( 1 << i ) ) != 0 ) && + ( RegionNvmGroup2->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 = RegionNvmGroup2->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* ) RegionNvmGroup2->ChannelsMask, 0, sizeof( RegionNvmGroup2->ChannelsMask ) ); + // Update the channels mask + RegionNvmGroup2->ChannelsMask[0] = chMask; + } + + // Update status variables + *drOut = linkAdrParams.Datarate; + *txPowOut = linkAdrParams.TxPower; + *nbRepOut = linkAdrParams.NbRep; + *nbBytesParsed = bytesProcessed; + +#endif /* REGION_EU433 */ + return status; +} + +uint8_t RegionEU433RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ) +{ + uint8_t status = 0x07; +#if defined( REGION_EU433 ) + + // 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 + } + +#endif /* REGION_EU433 */ + return status; +} + +int8_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 ) +{ + // Do not accept the request + return -1; +} + +int8_t RegionEU433DlChannelReq( DlChannelReqParams_t* dlChannelReq ) +{ + uint8_t status = 0x03; +#if defined( REGION_EU433 ) + + // Verify if the frequency is supported + if( VerifyRfFreq( dlChannelReq->Rx1Frequency ) == false ) + { + status &= 0xFE; + } + + // Verify if an uplink frequency exists + if( RegionNvmGroup2->Channels[dlChannelReq->ChannelId].Frequency == 0 ) + { + status &= 0xFD; + } + + // Apply Rx1 frequency, if the status is OK + if( status == 0x03 ) + { + RegionNvmGroup2->Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency; + } + +#endif /* REGION_EU433 */ + return status; +} + +int8_t RegionEU433AlternateDr( int8_t currentDr, AlternateDrType_t type ) +{ +#if defined( REGION_EU433 ) + return currentDr; +#else + return -1; +#endif /* REGION_EU433 */ +} + +LoRaMacStatus_t RegionEU433NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ) +{ +#if defined( REGION_EU433 ) + uint8_t nbEnabledChannels = 0; + uint8_t nbRestrictedChannels = 0; + uint8_t enabledChannels[EU433_MAX_NB_CHANNELS] = { 0 }; + RegionCommonIdentifyChannelsParam_t identifyChannelsParam; + RegionCommonCountNbOfEnabledChannelsParams_t countChannelsParams; + LoRaMacStatus_t status = LORAMAC_STATUS_NO_CHANNEL_FOUND; + uint16_t joinChannels = EU433_JOIN_CHANNELS; + + if( RegionCommonCountChannels( RegionNvmGroup2->ChannelsMask, 0, 1 ) == 0 ) + { // Reactivate default channels + RegionNvmGroup2->ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); + } + + // Search how many channels are enabled + countChannelsParams.Joined = nextChanParams->Joined; + countChannelsParams.Datarate = nextChanParams->Datarate; + countChannelsParams.ChannelsMask = RegionNvmGroup2->ChannelsMask; + countChannelsParams.Channels = RegionNvmGroup2->Channels; +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + countChannelsParams.Bands = RegionNvmGroup1->Bands; +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + countChannelsParams.Bands = RegionBands; +#endif /* REGION_VERSION */ + countChannelsParams.MaxNbChannels = EU433_MAX_NB_CHANNELS; + countChannelsParams.JoinChannels = &joinChannels; + + identifyChannelsParam.AggrTimeOff = nextChanParams->AggrTimeOff; + identifyChannelsParam.LastAggrTx = nextChanParams->LastAggrTx; + identifyChannelsParam.DutyCycleEnabled = nextChanParams->DutyCycleEnabled; + identifyChannelsParam.MaxBands = EU433_MAX_NB_BANDS; + + identifyChannelsParam.ElapsedTimeSinceStartUp = nextChanParams->ElapsedTimeSinceStartUp; + identifyChannelsParam.LastTxIsJoinRequest = nextChanParams->LastTxIsJoinRequest; + identifyChannelsParam.ExpectedTimeOnAir = GetTimeOnAir( nextChanParams->Datarate, nextChanParams->PktLen ); + + identifyChannelsParam.CountNbOfEnabledChannelsParam = &countChannelsParams; + + status = RegionCommonIdentifyChannels( &identifyChannelsParam, aggregatedTimeOff, enabledChannels, + &nbEnabledChannels, &nbRestrictedChannels, time ); + + if( status == LORAMAC_STATUS_OK ) + { + // We found a valid channel + *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )]; + } + else if( status == LORAMAC_STATUS_NO_CHANNEL_FOUND ) + { + // Datarate not supported by any channel, restore defaults + RegionNvmGroup2->ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); + } + return status; +#else + return LORAMAC_STATUS_NO_CHANNEL_FOUND; +#endif /* REGION_EU433 */ +} + +LoRaMacStatus_t RegionEU433ChannelAdd( ChannelAddParams_t* channelAdd ) +{ +#if defined( REGION_EU433 ) + 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* ) &(RegionNvmGroup2->Channels[id]), ( uint8_t* ) channelAdd->NewChannel, sizeof( RegionNvmGroup2->Channels[id] ) ); + RegionNvmGroup2->Channels[id].Band = 0; + RegionNvmGroup2->ChannelsMask[0] |= ( 1 << id ); + return LORAMAC_STATUS_OK; +#else + return LORAMAC_STATUS_NO_CHANNEL_FOUND; +#endif /* REGION_EU433 */ +} + +bool RegionEU433ChannelsRemove( ChannelRemoveParams_t* channelRemove ) +{ +#if defined( REGION_EU433 ) + uint8_t id = channelRemove->ChannelId; + + if( id < EU433_NUMB_DEFAULT_CHANNELS ) + { + return false; + } + + // Remove the channel from the list of channels + RegionNvmGroup2->Channels[id] = ( ChannelParams_t ){ 0, 0, { 0 }, 0 }; + + return RegionCommonChanDisable( RegionNvmGroup2->ChannelsMask, id, EU433_MAX_NB_CHANNELS ); +#else + return false; +#endif /* REGION_EU433 */ +} + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +void RegionEU433SetContinuousWave( ContinuousWaveParams_t* continuousWave ) +{ +#if defined( REGION_EU433 ) + int8_t txPowerLimited = RegionCommonLimitTxPower( continuousWave->TxPower, RegionNvmGroup1->Bands[RegionNvmGroup2->Channels[continuousWave->Channel].Band].TxMaxPower ); + int8_t phyTxPower = 0; + uint32_t frequency = RegionNvmGroup2->Channels[continuousWave->Channel].Frequency; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, continuousWave->MaxEirp, continuousWave->AntennaGain ); + + Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout ); +#endif /* REGION_EU433 */ +} +#endif /* REGION_VERSION */ + +uint8_t RegionEU433ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) +{ +#if defined( REGION_EU433 ) + int8_t datarate = dr - drOffset; + + if( datarate < 0 ) + { + datarate = DR_0; + } + return datarate; +#else + return 0; +#endif /* REGION_EU433 */ +} + +void RegionEU433RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ) +{ +#if defined( REGION_EU433 ) + 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; +#endif /* REGION_EU433 */ +} diff --git a/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionEU433.h b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionEU433.h new file mode 100644 index 0000000..352ca83 --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionEU433.h @@ -0,0 +1,494 @@ +/*! + * \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. + * \{ + */ +/** + ****************************************************************************** + * + * Portions COPYRIGHT 2020 STMicroelectronics + * + * @file RegionEU433.h + * @author MCD Application Team + * @brief Region definition for EU433 + ****************************************************************************** + */ +#ifndef __REGION_EU433_H__ +#define __REGION_EU433_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "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 + +/*! + * 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 + +/*! + * Enabled or disabled the duty cycle + */ +#define EU433_DUTY_CYCLE_ENABLED 1 + +/*! + * Maximum RX window duration + */ +#define EU433_MAX_RX_WINDOW 3000 + +/*! + * 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 + +/*! + * Default uplink dwell time configuration + */ +#define EU433_DEFAULT_UPLINK_DWELL_TIME 0 + +/* + * CLASS B + */ +/*! + * Beacon frequency + */ +#define EU433_BEACON_CHANNEL_FREQ 434665000 + +/*! + * Ping slot channel frequency + */ +#define EU433_PING_SLOT_CHANNEL_FREQ 434665000 + +/*! + * Payload size of a beacon frame + */ +#define EU433_BEACON_SIZE 17 + +/*! + * Size of RFU 1 field + */ +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +#define EU433_RFU1_SIZE 2 +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) +#define EU433_RFU1_SIZE 1 +#endif /* REGION_VERSION */ + +/*! + * Size of RFU 2 field + */ +#define EU433_RFU2_SIZE 0 + +/*! + * Datarate of the beacon channel + */ +#define EU433_BEACON_CHANNEL_DR DR_3 + +/*! + * Bandwidth 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 + * Band = { DutyCycle, TxMaxPower, LastBandUpdateTime, LastMaxCreditAssignTime, TimeCredits, MaxTimeCredits, ReadyForTransmission } + */ +#define EU433_BAND0 { 100, EU433_MAX_TX_POWER, 0, 0, 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 }; + +/* ST_WORKAROUND_BEGIN: Keep repeater feature */ +/*! + * 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 }; +/* ST_WORKAROUND_END */ + +/*! + * \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] params Sets the initialization type. + */ +void RegionEU433InitDefaults( InitDefaultsParams_t* params ); + +/*! + * \brief Verifies a parameter. + * + * \param [in] verify Pointer to the function parameters. + * + * \param [in] phyAttribute 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. + * + * \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 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. + */ +int8_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. + */ +int8_t RegionEU433DlChannelReq( 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 RegionEU433AlternateDr( int8_t currentDr, AlternateDrType_t type ); + +/*! + * \brief Searches and set the next random available channel + * + * \param [in] nextChanParams pointer of selected channel parameters + * + * \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 ); + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +/*! + * \brief Sets the radio into continuous wave mode. + * + * \param [IN] continuousWave Pointer to the function parameters. + */ +void RegionEU433SetContinuousWave( ContinuousWaveParams_t* continuousWave ); +#endif /* REGION_VERSION */ + +/*! + * \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 + * + * \param [out] outDr Datarate used to receive the beacon + */ +void RegionEU433RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ); + +/*! \} defgroup REGIONEU433 */ + +#ifdef __cplusplus +} +#endif + +#endif // __REGION_EU433_H__ diff --git a/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionEU868.c b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionEU868.c new file mode 100644 index 0000000..135c3de --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionEU868.c @@ -0,0 +1,1104 @@ +/*! + * \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 ) +*/ +/** + ****************************************************************************** + * + * Portions COPYRIGHT 2020 STMicroelectronics + * + * @file RegionEU868.c + * @author MCD Application Team + * @brief Region implementation for EU868 + ****************************************************************************** + */ +#include "../../../SubGHz_Phy/radio.h" +#include "RegionEU868.h" + +// Definitions +#define CHANNELS_MASK_SIZE 1 + +#if defined( REGION_EU868 ) +/* + * Non-volatile module context. + */ +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +static RegionNvmDataGroup1_t* RegionNvmGroup1; +static RegionNvmDataGroup2_t* RegionNvmGroup2; +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) +// static RegionNvmDataGroup1_t* RegionNvmGroup1; /* Unused for this region */ +static RegionNvmDataGroup2_t* RegionNvmGroup2; +static Band_t* RegionBands; +#endif /* REGION_VERSION */ + +// Static functions +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 TimerTime_t GetTimeOnAir( int8_t datarate, uint16_t pktLen ) +{ + int8_t phyDr = DataratesEU868[datarate]; + uint32_t bandwidth = RegionCommonGetBandwidth( datarate, BandwidthsEU868 ); + TimerTime_t timeOnAir = 0; + + if( datarate == DR_7 ) + { // High Speed FSK channel + timeOnAir = Radio.TimeOnAir( MODEM_FSK, bandwidth, phyDr * 1000, 0, 5, false, pktLen, true ); + } + else + { + timeOnAir = Radio.TimeOnAir( MODEM_LORA, bandwidth, phyDr, 1, 8, false, pktLen, true ); + } + return timeOnAir; +} +#endif /* REGION_EU868 */ + +PhyParam_t RegionEU868GetPhyParam( GetPhyParams_t* getPhy ) +{ + PhyParam_t phyParam = { 0 }; + +#if defined( REGION_EU868 ) + 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: + { + RegionCommonGetNextLowerTxDrParams_t nextLowerTxDrParams = + { + .CurrentDr = getPhy->Datarate, + .MaxDr = ( int8_t )EU868_TX_MAX_DATARATE, + .MinDr = ( int8_t )EU868_TX_MIN_DATARATE, + .NbChannels = EU868_MAX_NB_CHANNELS, + .ChannelsMask = RegionNvmGroup2->ChannelsMask, + .Channels = RegionNvmGroup2->Channels, + }; + phyParam.Value = RegionCommonGetNextLowerTxDr( &nextLowerTxDrParams ); + 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 = REGION_COMMON_DEFAULT_ADR_ACK_LIMIT; + break; + } + case PHY_DEF_ADR_ACK_DELAY: + { + phyParam.Value = REGION_COMMON_DEFAULT_ADR_ACK_DELAY; + break; + } + case PHY_MAX_PAYLOAD: + { + phyParam.Value = MaxPayloadOfDatarateEU868[getPhy->Datarate]; + break; + } + /* ST_WORKAROUND_BEGIN: Keep repeater feature */ + case PHY_MAX_PAYLOAD_REPEATER: + { + phyParam.Value = MaxPayloadOfDatarateRepeaterEU868[getPhy->Datarate]; + break; + } + /* ST_WORKAROUND_END */ + 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 = REGION_COMMON_DEFAULT_RECEIVE_DELAY1; + break; + } + case PHY_RECEIVE_DELAY2: + { + phyParam.Value = REGION_COMMON_DEFAULT_RECEIVE_DELAY2; + break; + } + case PHY_JOIN_ACCEPT_DELAY1: + { + phyParam.Value = REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY1; + break; + } + case PHY_JOIN_ACCEPT_DELAY2: + { + phyParam.Value = REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY2; + break; + } +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + case PHY_MAX_FCNT_GAP: + { + phyParam.Value = REGION_COMMON_DEFAULT_MAX_FCNT_GAP; + break; + } + case PHY_ACK_TIMEOUT: + { + phyParam.Value = ( REGION_COMMON_DEFAULT_ACK_TIMEOUT + randr( -REGION_COMMON_DEFAULT_ACK_TIMEOUT_RND, REGION_COMMON_DEFAULT_ACK_TIMEOUT_RND ) ); + break; + } +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + + case PHY_RETRANSMIT_TIMEOUT: + { + phyParam.Value = ( REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT + randr( -REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT_RND, REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT_RND ) ); + break; + } +#endif /* REGION_VERSION */ + case PHY_DEF_DR1_OFFSET: + { + phyParam.Value = REGION_COMMON_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 = RegionNvmGroup2->ChannelsMask; + break; + } + case PHY_CHANNELS_DEFAULT_MASK: + { + phyParam.ChannelsMask = RegionNvmGroup2->ChannelsDefaultMask; + break; + } + case PHY_MAX_NB_CHANNELS: + { + phyParam.Value = EU868_MAX_NB_CHANNELS; + break; + } + case PHY_CHANNELS: + { + phyParam.Channels = RegionNvmGroup2->Channels; + break; + } + case PHY_DEF_UPLINK_DWELL_TIME: + { + phyParam.Value = EU868_DEFAULT_UPLINK_DWELL_TIME; + break; + } + case PHY_DEF_DOWNLINK_DWELL_TIME: + { + phyParam.Value = REGION_COMMON_DEFAULT_DOWNLINK_DWELL_TIME; + 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_FREQ: + { + phyParam.Value = EU868_PING_SLOT_CHANNEL_FREQ; + break; + } + case PHY_PING_SLOT_CHANNEL_DR: + { + phyParam.Value = EU868_PING_SLOT_CHANNEL_DR; + break; + } + case PHY_SF_FROM_DR: + { + phyParam.Value = DataratesEU868[getPhy->Datarate]; + break; + } + case PHY_BW_FROM_DR: + { + phyParam.Value = RegionCommonGetBandwidth( getPhy->Datarate, BandwidthsEU868 ); + break; + } + default: + { + break; + } + } + +#endif /* REGION_EU868 */ + return phyParam; +} + +void RegionEU868SetBandTxDone( SetBandTxDoneParams_t* txDone ) +{ +#if defined( REGION_EU868 ) +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + RegionCommonSetBandTxDone( &RegionNvmGroup1->Bands[RegionNvmGroup2->Channels[txDone->Channel].Band], + txDone->LastTxAirTime, txDone->Joined, txDone->ElapsedTimeSinceStartUp ); +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + RegionCommonSetBandTxDone( &RegionBands[RegionNvmGroup2->Channels[txDone->Channel].Band], + txDone->LastTxAirTime, txDone->Joined, txDone->ElapsedTimeSinceStartUp ); +#endif /* REGION_VERSION */ +#endif /* REGION_EU868 */ +} + +void RegionEU868InitDefaults( InitDefaultsParams_t* params ) +{ +#if defined( REGION_EU868 ) + 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_DEFAULTS: + { + if( ( params->NvmGroup1 == NULL ) || ( params->NvmGroup2 == NULL ) ) + { + return; + } + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + RegionNvmGroup1 = (RegionNvmDataGroup1_t*) params->NvmGroup1; + RegionNvmGroup2 = (RegionNvmDataGroup2_t*) params->NvmGroup2; + + // Default bands + memcpy1( ( uint8_t* )RegionNvmGroup1->Bands, ( uint8_t* )bands, sizeof( Band_t ) * EU868_MAX_NB_BANDS ); +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + RegionNvmGroup2 = (RegionNvmDataGroup2_t*) params->NvmGroup2; + RegionBands = (Band_t*) params->Bands; + + // Default bands + memcpy1( ( uint8_t* )RegionBands, ( uint8_t* )bands, sizeof( Band_t ) * EU868_MAX_NB_BANDS ); +#endif /* REGION_VERSION */ + + // Default channels + RegionNvmGroup2->Channels[0] = ( ChannelParams_t ) EU868_LC1; + RegionNvmGroup2->Channels[1] = ( ChannelParams_t ) EU868_LC2; + RegionNvmGroup2->Channels[2] = ( ChannelParams_t ) EU868_LC3; + + // Default ChannelsMask + RegionNvmGroup2->ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 ); + + // Update the channels mask + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, RegionNvmGroup2->ChannelsDefaultMask, CHANNELS_MASK_SIZE ); + break; + } + case INIT_TYPE_RESET_TO_DEFAULT_CHANNELS: + { + // Reset Channels Rx1Frequency to default 0 + RegionNvmGroup2->Channels[0].Rx1Frequency = 0; + RegionNvmGroup2->Channels[1].Rx1Frequency = 0; + RegionNvmGroup2->Channels[2].Rx1Frequency = 0; + // Update the channels mask + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, RegionNvmGroup2->ChannelsDefaultMask, CHANNELS_MASK_SIZE ); + break; + } + case INIT_TYPE_ACTIVATE_DEFAULT_CHANNELS: + { + // Restore channels default mask + RegionNvmGroup2->ChannelsMask[0] |= RegionNvmGroup2->ChannelsDefaultMask[0]; + break; + } + default: + { + break; + } + } +#endif /* REGION_EU868 */ +} + +bool RegionEU868Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ) +{ +#if defined( REGION_EU868 ) + 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; + } +#else + return false; +#endif /* REGION_EU868 */ +} + +void RegionEU868ApplyCFList( ApplyCFListParams_t* applyCFList ) +{ +#if defined( REGION_EU868 ) + 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 ); + } + } +#endif /* REGION_EU868 */ +} + +bool RegionEU868ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ) +{ +#if defined( REGION_EU868 ) + switch( chanMaskSet->ChannelsMaskType ) + { + case CHANNELS_MASK: + { + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, chanMaskSet->ChannelsMaskIn, CHANNELS_MASK_SIZE ); + break; + } + case CHANNELS_DEFAULT_MASK: + { + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, CHANNELS_MASK_SIZE ); + break; + } + default: + return false; + } + return true; +#else + return false; +#endif /* REGION_EU868 */ +} + +void RegionEU868ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ) +{ +#if defined( REGION_EU868 ) + uint32_t tSymbolInUs = 0; + + // Get the datarate, perform a boundary check + rxConfigParams->Datarate = MIN( datarate, EU868_RX_MAX_DATARATE ); + rxConfigParams->Bandwidth = RegionCommonGetBandwidth( rxConfigParams->Datarate, BandwidthsEU868 ); + + if( rxConfigParams->Datarate == DR_7 ) + { // FSK + tSymbolInUs = RegionCommonComputeSymbolTimeFsk( DataratesEU868[rxConfigParams->Datarate] ); + } + else + { // LoRa + tSymbolInUs = RegionCommonComputeSymbolTimeLoRa( DataratesEU868[rxConfigParams->Datarate], BandwidthsEU868[rxConfigParams->Datarate] ); + } + + RegionCommonComputeRxWindowParameters( tSymbolInUs, minRxSymbols, rxError, Radio.GetWakeupTime( ), &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); +#endif /* REGION_EU868 */ +} + +bool RegionEU868RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) +{ +#if defined( REGION_EU868 ) + RadioModems_t modem; + int8_t dr = rxConfig->Datarate; + uint8_t maxPayload = 0; + int8_t phyDr = 0; + uint32_t frequency = rxConfig->Frequency; + + if( Radio.GetStatus( ) != RF_IDLE ) + { + return false; + } + + if( rxConfig->RxSlot == RX_SLOT_WIN_1 ) + { + // Apply window 1 frequency + frequency = RegionNvmGroup2->Channels[rxConfig->Channel].Frequency; + // Apply the alternative RX 1 window frequency, if it is available + if( RegionNvmGroup2->Channels[rxConfig->Channel].Rx1Frequency != 0 ) + { + frequency = RegionNvmGroup2->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 ); + } + + /* ST_WORKAROUND_BEGIN: Keep repeater feature */ + if( rxConfig->RepeaterSupport == true ) + { + maxPayload = MaxPayloadOfDatarateRepeaterEU868[dr]; + } + else + { + maxPayload = MaxPayloadOfDatarateEU868[dr]; + } + + Radio.SetMaxPayloadLength( modem, maxPayload + LORAMAC_FRAME_PAYLOAD_OVERHEAD_SIZE ); + /* ST_WORKAROUND_END */ + + /* ST_WORKAROUND_BEGIN: Print Rx config */ + RegionCommonRxConfigPrint(rxConfig->RxSlot, frequency, dr); + /* ST_WORKAROUND_END */ + + *datarate = (uint8_t) dr; + return true; +#else + return false; +#endif /* REGION_EU868 */ +} + +bool RegionEU868TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ) +{ +#if defined( REGION_EU868 ) + RadioModems_t modem; + int8_t phyDr = DataratesEU868[txConfig->Datarate]; +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + int8_t txPowerLimited = RegionCommonLimitTxPower( txConfig->TxPower, RegionNvmGroup1->Bands[RegionNvmGroup2->Channels[txConfig->Channel].Band].TxMaxPower ); +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + int8_t txPowerLimited = RegionCommonLimitTxPower( txConfig->TxPower, RegionBands[RegionNvmGroup2->Channels[txConfig->Channel].Band].TxMaxPower ); +#endif /* REGION_VERSION */ + uint32_t bandwidth = RegionCommonGetBandwidth( txConfig->Datarate, BandwidthsEU868 ); + int8_t phyTxPower = 0; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain ); + + // Setup the radio frequency + Radio.SetChannel( RegionNvmGroup2->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 ); + } + /* ST_WORKAROUND_BEGIN: Print Tx config */ + RegionCommonTxConfigPrint(RegionNvmGroup2->Channels[txConfig->Channel].Frequency, txConfig->Datarate); + /* ST_WORKAROUND_END */ + + // Update time-on-air + *txTimeOnAir = GetTimeOnAir( txConfig->Datarate, txConfig->PktLen ); + + // Setup maximum payload length of the radio driver + Radio.SetMaxPayloadLength( modem, txConfig->PktLen ); + + *txPower = txPowerLimited; + return true; +#else + return false; +#endif /* REGION_EU868 */ +} + +uint8_t RegionEU868LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ) +{ + uint8_t status = 0x07; +#if defined( REGION_EU868 ) + RegionCommonLinkAdrParams_t linkAdrParams = { 0 }; + 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( RegionNvmGroup2->Channels[i].Frequency != 0 ) + { + chMask |= 1 << i; + } + } + else + { + if( ( ( chMask & ( 1 << i ) ) != 0 ) && + ( RegionNvmGroup2->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 = RegionNvmGroup2->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* ) RegionNvmGroup2->ChannelsMask, 0, sizeof( RegionNvmGroup2->ChannelsMask ) ); + // Update the channels mask + RegionNvmGroup2->ChannelsMask[0] = chMask; + } + + // Update status variables + *drOut = linkAdrParams.Datarate; + *txPowOut = linkAdrParams.TxPower; + *nbRepOut = linkAdrParams.NbRep; + *nbBytesParsed = bytesProcessed; + +#endif /* REGION_EU868 */ + return status; +} + +uint8_t RegionEU868RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ) +{ + uint8_t status = 0x07; +#if defined( REGION_EU868 ) + 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 + } + +#endif /* REGION_EU868 */ + return status; +} + +int8_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 ) +{ + // Do not accept the request + return -1; +} + +int8_t RegionEU868DlChannelReq( DlChannelReqParams_t* dlChannelReq ) +{ + uint8_t status = 0x03; +#if defined( REGION_EU868 ) + 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( RegionNvmGroup2->Channels[dlChannelReq->ChannelId].Frequency == 0 ) + { + status &= 0xFD; + } + + // Apply Rx1 frequency, if the status is OK + if( status == 0x03 ) + { + RegionNvmGroup2->Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency; + } + +#endif /* REGION_EU868 */ + return status; +} + +int8_t RegionEU868AlternateDr( int8_t currentDr, AlternateDrType_t type ) +{ +#if defined( REGION_EU868 ) + return currentDr; +#else + return -1; +#endif /* REGION_EU868 */ +} + +LoRaMacStatus_t RegionEU868NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ) +{ +#if defined( REGION_EU868 ) + uint8_t nbEnabledChannels = 0; + uint8_t nbRestrictedChannels = 0; + uint8_t enabledChannels[EU868_MAX_NB_CHANNELS] = { 0 }; + RegionCommonIdentifyChannelsParam_t identifyChannelsParam; + RegionCommonCountNbOfEnabledChannelsParams_t countChannelsParams; + LoRaMacStatus_t status = LORAMAC_STATUS_NO_CHANNEL_FOUND; + uint16_t joinChannels = EU868_JOIN_CHANNELS; + + if( RegionCommonCountChannels( RegionNvmGroup2->ChannelsMask, 0, 1 ) == 0 ) + { // Reactivate default channels + RegionNvmGroup2->ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); + } + + // Search how many channels are enabled + countChannelsParams.Joined = nextChanParams->Joined; + countChannelsParams.Datarate = nextChanParams->Datarate; + countChannelsParams.ChannelsMask = RegionNvmGroup2->ChannelsMask; + countChannelsParams.Channels = RegionNvmGroup2->Channels; +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + countChannelsParams.Bands = RegionNvmGroup1->Bands; +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + countChannelsParams.Bands = RegionBands; +#endif /* REGION_VERSION */ + countChannelsParams.MaxNbChannels = EU868_MAX_NB_CHANNELS; + countChannelsParams.JoinChannels = &joinChannels; + + identifyChannelsParam.AggrTimeOff = nextChanParams->AggrTimeOff; + identifyChannelsParam.LastAggrTx = nextChanParams->LastAggrTx; + identifyChannelsParam.DutyCycleEnabled = nextChanParams->DutyCycleEnabled; + identifyChannelsParam.MaxBands = EU868_MAX_NB_BANDS; + + identifyChannelsParam.ElapsedTimeSinceStartUp = nextChanParams->ElapsedTimeSinceStartUp; + identifyChannelsParam.LastTxIsJoinRequest = nextChanParams->LastTxIsJoinRequest; + identifyChannelsParam.ExpectedTimeOnAir = GetTimeOnAir( nextChanParams->Datarate, nextChanParams->PktLen ); + + identifyChannelsParam.CountNbOfEnabledChannelsParam = &countChannelsParams; + + status = RegionCommonIdentifyChannels( &identifyChannelsParam, aggregatedTimeOff, enabledChannels, + &nbEnabledChannels, &nbRestrictedChannels, time ); + + if( status == LORAMAC_STATUS_OK ) + { + // We found a valid channel + *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )]; + } + else if( status == LORAMAC_STATUS_NO_CHANNEL_FOUND ) + { + // Datarate not supported by any channel, restore defaults + RegionNvmGroup2->ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); + } + return status; +#else + return LORAMAC_STATUS_NO_CHANNEL_FOUND; +#endif /* REGION_EU868 */ +} + +LoRaMacStatus_t RegionEU868ChannelAdd( ChannelAddParams_t* channelAdd ) +{ +#if defined( REGION_EU868 ) + 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* ) &(RegionNvmGroup2->Channels[id]), ( uint8_t* ) channelAdd->NewChannel, sizeof( RegionNvmGroup2->Channels[id] ) ); + RegionNvmGroup2->Channels[id].Band = band; + RegionNvmGroup2->ChannelsMask[0] |= ( 1 << id ); + return LORAMAC_STATUS_OK; +#else + return LORAMAC_STATUS_NO_CHANNEL_FOUND; +#endif /* REGION_EU868 */ +} + +bool RegionEU868ChannelsRemove( ChannelRemoveParams_t* channelRemove ) +{ +#if defined( REGION_EU868 ) + uint8_t id = channelRemove->ChannelId; + + if( id < EU868_NUMB_DEFAULT_CHANNELS ) + { + return false; + } + + // Remove the channel from the list of channels + RegionNvmGroup2->Channels[id] = ( ChannelParams_t ){ 0, 0, { 0 }, 0 }; + + return RegionCommonChanDisable( RegionNvmGroup2->ChannelsMask, id, EU868_MAX_NB_CHANNELS ); +#else + return false; +#endif /* REGION_EU868 */ +} + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +void RegionEU868SetContinuousWave( ContinuousWaveParams_t* continuousWave ) +{ +#if defined( REGION_EU868 ) + int8_t txPowerLimited = RegionCommonLimitTxPower( continuousWave->TxPower, RegionNvmGroup1->Bands[RegionNvmGroup2->Channels[continuousWave->Channel].Band].TxMaxPower ); + int8_t phyTxPower = 0; + uint32_t frequency = RegionNvmGroup2->Channels[continuousWave->Channel].Frequency; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, continuousWave->MaxEirp, continuousWave->AntennaGain ); + + Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout ); +#endif /* REGION_EU868 */ +} +#endif /* REGION_VERSION */ + +uint8_t RegionEU868ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) +{ +#if defined( REGION_EU868 ) + int8_t datarate = dr - drOffset; + + if( datarate < 0 ) + { + datarate = DR_0; + } + return datarate; +#else + return 0; +#endif /* REGION_EU868 */ +} + +void RegionEU868RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ) +{ +#if defined( REGION_EU868 ) + 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; +#endif /* REGION_EU868 */ +} diff --git a/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionEU868.h b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionEU868.h new file mode 100644 index 0000000..a1a603b --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionEU868.h @@ -0,0 +1,522 @@ +/*! + * \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. + * \{ + */ +/** + ****************************************************************************** + * + * Portions COPYRIGHT 2020 STMicroelectronics + * + * @file RegionEU868.h + * @author MCD Application Team + * @brief Region definition for EU868 + ****************************************************************************** + */ +#ifndef __REGION_EU868_H__ +#define __REGION_EU868_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "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 + +/*! + * 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 + +/*! + * Enabled or disabled the duty cycle + */ +#define EU868_DUTY_CYCLE_ENABLED 1 + +/*! + * Maximum RX window duration + */ +#define EU868_MAX_RX_WINDOW 3000 + +#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 + +/*! + * Default uplink dwell time configuration + */ +#define EU868_DEFAULT_UPLINK_DWELL_TIME 0 + +/* + * CLASS B + */ +/*! + * Beacon frequency + */ +#define EU868_BEACON_CHANNEL_FREQ 869525000 + +/*! + * Ping slot channel frequency + */ +#define EU868_PING_SLOT_CHANNEL_FREQ 869525000 + +/*! + * Payload size of a beacon frame + */ +#define EU868_BEACON_SIZE 17 + +/*! + * Size of RFU 1 field + */ +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +#define EU868_RFU1_SIZE 2 +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) +#define EU868_RFU1_SIZE 1 +#endif /* REGION_VERSION */ + +/*! + * Size of RFU 2 field + */ +#define EU868_RFU2_SIZE 0 + +/*! + * Datarate of the beacon channel + */ +#define EU868_BEACON_CHANNEL_DR DR_3 + +/*! + * Bandwidth 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 + * Band = { DutyCycle, TxMaxPower, LastBandUpdateTime, LastMaxCreditAssignTime, TimeCredits, MaxTimeCredits, ReadyForTransmission } + */ +#define EU868_BAND0 { 100 , EU868_MAX_TX_POWER, 0, 0, 0, 0, 0 } // 1.0 % + +/*! + * Band 1 definition + * Band = { DutyCycle, TxMaxPower, LastBandUpdateTime, LastMaxCreditAssignTime, TimeCredits, MaxTimeCredits, ReadyForTransmission } + */ +#define EU868_BAND1 { 100 , EU868_MAX_TX_POWER, 0, 0, 0, 0, 0 } // 1.0 % + +/*! + * Band 2 definition + * Band = { DutyCycle, TxMaxPower, LastBandUpdateTime, LastMaxCreditAssignTime, TimeCredits, MaxTimeCredits, ReadyForTransmission } + */ +#define EU868_BAND2 { 1000, EU868_MAX_TX_POWER, 0, 0, 0, 0, 0 } // 0.1 % + +/*! + * Band 3 definition + * Band = { DutyCycle, TxMaxPower, LastBandUpdateTime, LastMaxCreditAssignTime, TimeCredits, MaxTimeCredits, ReadyForTransmission } + */ +#define EU868_BAND3 { 10 , EU868_MAX_TX_POWER, 0, 0, 0, 0, 0 } // 10.0 % + +/*! + * Band 4 definition + * Band = { DutyCycle, TxMaxPower, LastBandUpdateTime, LastMaxCreditAssignTime, TimeCredits, MaxTimeCredits, ReadyForTransmission } + */ +#define EU868_BAND4 { 100 , EU868_MAX_TX_POWER, 0, 0, 0, 0, 0 } // 1.0 % + +/*! + * Band 5 definition + * Band = { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff, + * DutyCycleTimePeriod, MaxAllowedTimeOnAir, AggregatedTimeOnAir, StartTimeOfPeriod } + */ +#define EU868_BAND5 { 1000, EU868_MAX_TX_POWER, 0, 0, 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 }; + +/* ST_WORKAROUND_BEGIN: Keep repeater feature */ +/*! + * 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 }; +/* ST_WORKAROUND_END */ + +/*! + * \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] params Sets the initialization type. + */ +void RegionEU868InitDefaults( InitDefaultsParams_t* params ); + +/*! + * \brief Verifies a parameter. + * + * \param [in] verify Pointer to the function parameters. + * + * \param [in] phyAttribute 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. + * + * \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 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. + */ +int8_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. + */ +int8_t RegionEU868DlChannelReq( 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 RegionEU868AlternateDr( int8_t currentDr, AlternateDrType_t type ); + +/*! + * \brief Searches and set the next random available channel + * + * \param [in] nextChanParams pointer of selected channel parameters + * + * \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 ); + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +/*! + * \brief Sets the radio into continuous wave mode. + * + * \param [IN] continuousWave Pointer to the function parameters. + */ +void RegionEU868SetContinuousWave( ContinuousWaveParams_t* continuousWave ); +#endif /* REGION_VERSION */ + +/*! + * \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 + * + * \param [out] outDr Datarate used to receive the beacon + */ +void RegionEU868RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ); + +/*! \} defgroup REGIONEU868 */ + +#ifdef __cplusplus +} +#endif + +#endif // __REGION_EU868_H__ diff --git a/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionIN865.c b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionIN865.c new file mode 100644 index 0000000..3c85cb2 --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionIN865.c @@ -0,0 +1,1100 @@ +/*! + * \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 ) +*/ +/** + ****************************************************************************** + * + * Portions COPYRIGHT 2020 STMicroelectronics + * + * @file RegionIN865.c + * @author MCD Application Team + * @brief Region implementation for IN865 + ****************************************************************************** + */ +#include "../../../SubGHz_Phy/radio.h" +#include "RegionIN865.h" + +// Definitions +#define CHANNELS_MASK_SIZE 1 + +#if defined( REGION_IN865 ) +/* + * Non-volatile module context. + */ +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +static RegionNvmDataGroup1_t* RegionNvmGroup1; +static RegionNvmDataGroup2_t* RegionNvmGroup2; +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) +// static RegionNvmDataGroup1_t* RegionNvmGroup1; /* Unused for this region */ +static RegionNvmDataGroup2_t* RegionNvmGroup2; +static Band_t* RegionBands; +#endif /* REGION_VERSION */ + +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 TimerTime_t GetTimeOnAir( int8_t datarate, uint16_t pktLen ) +{ + int8_t phyDr = DataratesIN865[datarate]; + uint32_t bandwidth = RegionCommonGetBandwidth( datarate, BandwidthsIN865 ); + TimerTime_t timeOnAir = 0; + + if( datarate == DR_7 ) + { // High Speed FSK channel + timeOnAir = Radio.TimeOnAir( MODEM_FSK, bandwidth, phyDr * 1000, 0, 5, false, pktLen, true ); + } + else + { + timeOnAir = Radio.TimeOnAir( MODEM_LORA, bandwidth, phyDr, 1, 8, false, pktLen, true ); + } + return timeOnAir; +} +#endif /* REGION_IN865 */ + +PhyParam_t RegionIN865GetPhyParam( GetPhyParams_t* getPhy ) +{ + PhyParam_t phyParam = { 0 }; + +#if defined( REGION_IN865 ) + 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: + { + RegionCommonGetNextLowerTxDrParams_t nextLowerTxDrParams = + { + .CurrentDr = getPhy->Datarate, + .MaxDr = ( int8_t )IN865_TX_MAX_DATARATE, + .MinDr = ( int8_t )IN865_TX_MIN_DATARATE, + .NbChannels = IN865_MAX_NB_CHANNELS, + .ChannelsMask = RegionNvmGroup2->ChannelsMask, + .Channels = RegionNvmGroup2->Channels, + }; + phyParam.Value = RegionCommonGetNextLowerTxDr( &nextLowerTxDrParams ); + 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 = REGION_COMMON_DEFAULT_ADR_ACK_LIMIT; + break; + } + case PHY_DEF_ADR_ACK_DELAY: + { + phyParam.Value = REGION_COMMON_DEFAULT_ADR_ACK_DELAY; + break; + } + case PHY_MAX_PAYLOAD: + { + phyParam.Value = MaxPayloadOfDatarateIN865[getPhy->Datarate]; + break; + } + /* ST_WORKAROUND_BEGIN: Keep repeater feature */ + case PHY_MAX_PAYLOAD_REPEATER: + { + phyParam.Value = MaxPayloadOfDatarateRepeaterIN865[getPhy->Datarate]; + break; + } + /* ST_WORKAROUND_END */ + 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 = REGION_COMMON_DEFAULT_RECEIVE_DELAY1; + break; + } + case PHY_RECEIVE_DELAY2: + { + phyParam.Value = REGION_COMMON_DEFAULT_RECEIVE_DELAY2; + break; + } + case PHY_JOIN_ACCEPT_DELAY1: + { + phyParam.Value = REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY1; + break; + } + case PHY_JOIN_ACCEPT_DELAY2: + { + phyParam.Value = REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY2; + break; + } +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + case PHY_MAX_FCNT_GAP: + { + phyParam.Value = REGION_COMMON_DEFAULT_MAX_FCNT_GAP; + break; + } + case PHY_ACK_TIMEOUT: + { + phyParam.Value = ( REGION_COMMON_DEFAULT_ACK_TIMEOUT + randr( -REGION_COMMON_DEFAULT_ACK_TIMEOUT_RND, REGION_COMMON_DEFAULT_ACK_TIMEOUT_RND ) ); + break; + } +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + case PHY_RETRANSMIT_TIMEOUT: + { + phyParam.Value = ( REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT + randr( -REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT_RND, REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT_RND ) ); + break; + } +#endif /* REGION_VERSION */ + case PHY_DEF_DR1_OFFSET: + { + phyParam.Value = REGION_COMMON_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 = RegionNvmGroup2->ChannelsMask; + break; + } + case PHY_CHANNELS_DEFAULT_MASK: + { + phyParam.ChannelsMask = RegionNvmGroup2->ChannelsDefaultMask; + break; + } + case PHY_MAX_NB_CHANNELS: + { + phyParam.Value = IN865_MAX_NB_CHANNELS; + break; + } + case PHY_CHANNELS: + { + phyParam.Channels = RegionNvmGroup2->Channels; + break; + } + case PHY_DEF_UPLINK_DWELL_TIME: + { + phyParam.Value = IN865_DEFAULT_UPLINK_DWELL_TIME; + break; + } + case PHY_DEF_DOWNLINK_DWELL_TIME: + { + phyParam.Value = REGION_COMMON_DEFAULT_DOWNLINK_DWELL_TIME; + 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_FREQ: + { + phyParam.Value = IN865_PING_SLOT_CHANNEL_FREQ; + break; + } + case PHY_PING_SLOT_CHANNEL_DR: + { + phyParam.Value = IN865_PING_SLOT_CHANNEL_DR; + break; + } + case PHY_SF_FROM_DR: + { + phyParam.Value = DataratesIN865[getPhy->Datarate]; + break; + } + case PHY_BW_FROM_DR: + { + phyParam.Value = RegionCommonGetBandwidth( getPhy->Datarate, BandwidthsIN865 ); + break; + } + default: + { + break; + } + } + +#endif /* REGION_IN865 */ + return phyParam; +} + +void RegionIN865SetBandTxDone( SetBandTxDoneParams_t* txDone ) +{ +#if defined( REGION_IN865 ) +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + RegionCommonSetBandTxDone( &RegionNvmGroup1->Bands[RegionNvmGroup2->Channels[txDone->Channel].Band], + txDone->LastTxAirTime, txDone->Joined, txDone->ElapsedTimeSinceStartUp ); +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + RegionCommonSetBandTxDone( &RegionBands[RegionNvmGroup2->Channels[txDone->Channel].Band], + txDone->LastTxAirTime, txDone->Joined, txDone->ElapsedTimeSinceStartUp ); +#endif /* REGION_VERSION */ +#endif /* REGION_IN865 */ +} + +void RegionIN865InitDefaults( InitDefaultsParams_t* params ) +{ +#if defined( REGION_IN865 ) + Band_t bands[IN865_MAX_NB_BANDS] = + { + IN865_BAND0 + }; + + switch( params->Type ) + { + case INIT_TYPE_DEFAULTS: + { + if( ( params->NvmGroup1 == NULL ) || ( params->NvmGroup2 == NULL ) ) + { + return; + } + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + RegionNvmGroup1 = (RegionNvmDataGroup1_t*) params->NvmGroup1; + RegionNvmGroup2 = (RegionNvmDataGroup2_t*) params->NvmGroup2; + + // Initialize bands + memcpy1( ( uint8_t* )RegionNvmGroup1->Bands, ( uint8_t* )bands, sizeof( Band_t ) * IN865_MAX_NB_BANDS ); +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + RegionNvmGroup2 = (RegionNvmDataGroup2_t*) params->NvmGroup2; + RegionBands = (Band_t*) params->Bands; + + // Initialize bands + memcpy1( ( uint8_t* )RegionBands, ( uint8_t* )bands, sizeof( Band_t ) * IN865_MAX_NB_BANDS ); +#endif /* REGION_VERSION */ + + // Default channels + RegionNvmGroup2->Channels[0] = ( ChannelParams_t ) IN865_LC1; + RegionNvmGroup2->Channels[1] = ( ChannelParams_t ) IN865_LC2; + RegionNvmGroup2->Channels[2] = ( ChannelParams_t ) IN865_LC3; + + // Initialize the channels default mask + RegionNvmGroup2->ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 ); + + // Default ChannelsMask + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, RegionNvmGroup2->ChannelsDefaultMask, CHANNELS_MASK_SIZE ); + break; + } + case INIT_TYPE_RESET_TO_DEFAULT_CHANNELS: + { + // Reset Channels Rx1Frequency to default 0 + RegionNvmGroup2->Channels[0].Rx1Frequency = 0; + RegionNvmGroup2->Channels[1].Rx1Frequency = 0; + RegionNvmGroup2->Channels[2].Rx1Frequency = 0; + // Default ChannelsMask + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, RegionNvmGroup2->ChannelsDefaultMask, CHANNELS_MASK_SIZE ); + break; + } + case INIT_TYPE_ACTIVATE_DEFAULT_CHANNELS: + { + // Restore channels default mask + RegionNvmGroup2->ChannelsMask[0] |= RegionNvmGroup2->ChannelsDefaultMask[0]; + break; + } + default: + { + break; + } + } +#endif /* REGION_IN865 */ +} + +bool RegionIN865Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ) +{ +#if defined( REGION_IN865 ) + 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; + } +#else + return false; +#endif /* REGION_IN865 */ +} + +void RegionIN865ApplyCFList( ApplyCFListParams_t* applyCFList ) +{ +#if defined( REGION_IN865 ) + 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 ); + } + } +#endif /* REGION_IN865 */ +} + +bool RegionIN865ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ) +{ +#if defined( REGION_IN865 ) + switch( chanMaskSet->ChannelsMaskType ) + { + case CHANNELS_MASK: + { + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + case CHANNELS_DEFAULT_MASK: + { + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + default: + return false; + } + return true; +#else + return false; +#endif /* REGION_IN865 */ +} + +void RegionIN865ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ) +{ +#if defined( REGION_IN865 ) + uint32_t tSymbolInUs = 0; + + // Get the datarate, perform a boundary check + rxConfigParams->Datarate = MIN( datarate, IN865_RX_MAX_DATARATE ); + rxConfigParams->Bandwidth = RegionCommonGetBandwidth( rxConfigParams->Datarate, BandwidthsIN865 ); + + if( rxConfigParams->Datarate == DR_7 ) + { // FSK + tSymbolInUs = RegionCommonComputeSymbolTimeFsk( DataratesIN865[rxConfigParams->Datarate] ); + } + else + { // LoRa + tSymbolInUs = RegionCommonComputeSymbolTimeLoRa( DataratesIN865[rxConfigParams->Datarate], BandwidthsIN865[rxConfigParams->Datarate] ); + } + + RegionCommonComputeRxWindowParameters( tSymbolInUs, minRxSymbols, rxError, Radio.GetWakeupTime( ), &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); +#endif /* REGION_IN865 */ +} + +bool RegionIN865RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) +{ +#if defined( REGION_IN865 ) + RadioModems_t modem; + int8_t dr = rxConfig->Datarate; + uint8_t maxPayload = 0; + int8_t phyDr = 0; + uint32_t frequency = rxConfig->Frequency; + + if( Radio.GetStatus( ) != RF_IDLE ) + { + return false; + } + + if( rxConfig->RxSlot == RX_SLOT_WIN_1 ) + { + // Apply window 1 frequency + frequency = RegionNvmGroup2->Channels[rxConfig->Channel].Frequency; + // Apply the alternative RX 1 window frequency, if it is available + if( RegionNvmGroup2->Channels[rxConfig->Channel].Rx1Frequency != 0 ) + { + frequency = RegionNvmGroup2->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 ); + } + + /* ST_WORKAROUND_BEGIN: Keep repeater feature */ + if( rxConfig->RepeaterSupport == true ) + { + maxPayload = MaxPayloadOfDatarateRepeaterIN865[dr]; + } + else + { + maxPayload = MaxPayloadOfDatarateIN865[dr]; + } + Radio.SetMaxPayloadLength( modem, maxPayload + LORAMAC_FRAME_PAYLOAD_OVERHEAD_SIZE ); + /* ST_WORKAROUND_END */ + + /* ST_WORKAROUND_BEGIN: Print Rx config */ + RegionCommonRxConfigPrint(rxConfig->RxSlot, frequency, dr); + /* ST_WORKAROUND_END */ + + *datarate = (uint8_t) dr; + return true; +#else + return false; +#endif /* REGION_IN865 */ +} + +bool RegionIN865TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ) +{ +#if defined( REGION_IN865 ) + RadioModems_t modem; + int8_t phyDr = DataratesIN865[txConfig->Datarate]; +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + int8_t txPowerLimited = RegionCommonLimitTxPower( txConfig->TxPower, RegionNvmGroup1->Bands[RegionNvmGroup2->Channels[txConfig->Channel].Band].TxMaxPower ); +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + int8_t txPowerLimited = RegionCommonLimitTxPower( txConfig->TxPower, RegionBands[RegionNvmGroup2->Channels[txConfig->Channel].Band].TxMaxPower ); +#endif /* REGION_VERSION */ + uint32_t bandwidth = RegionCommonGetBandwidth( txConfig->Datarate, BandwidthsIN865 ); + int8_t phyTxPower = 0; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain ); + + // Setup the radio frequency + Radio.SetChannel( RegionNvmGroup2->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 ); + } + /* ST_WORKAROUND_BEGIN: Print Tx config */ + RegionCommonTxConfigPrint(RegionNvmGroup2->Channels[txConfig->Channel].Frequency, txConfig->Datarate); + /* ST_WORKAROUND_END */ + + // Update time-on-air + *txTimeOnAir = GetTimeOnAir( txConfig->Datarate, txConfig->PktLen ); + + // Setup maximum payload length of the radio driver + Radio.SetMaxPayloadLength( modem, txConfig->PktLen ); + + *txPower = txPowerLimited; + return true; +#else + return false; +#endif /* REGION_IN865 */ +} + +uint8_t RegionIN865LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ) +{ + uint8_t status = 0x07; +#if defined( REGION_IN865 ) + RegionCommonLinkAdrParams_t linkAdrParams = { 0 }; + 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( RegionNvmGroup2->Channels[i].Frequency != 0 ) + { + chMask |= 1 << i; + } + } + else + { + if( ( ( chMask & ( 1 << i ) ) != 0 ) && + ( RegionNvmGroup2->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 = RegionNvmGroup2->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* ) RegionNvmGroup2->ChannelsMask, 0, sizeof( RegionNvmGroup2->ChannelsMask ) ); + // Update the channels mask + RegionNvmGroup2->ChannelsMask[0] = chMask; + } + + // Update status variables + *drOut = linkAdrParams.Datarate; + *txPowOut = linkAdrParams.TxPower; + *nbRepOut = linkAdrParams.NbRep; + *nbBytesParsed = bytesProcessed; + +#endif /* REGION_IN865 */ + return status; +} + +uint8_t RegionIN865RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ) +{ + uint8_t status = 0x07; +#if defined( REGION_IN865 ) + + // 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 ) || + // DR_6 is not supported by this region + ( rxParamSetupReq->Datarate == DR_6 ) ) + { + 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 + } + +#endif /* REGION_IN865 */ + return status; +} + +int8_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 ) +{ + // Do not accept the request + return -1; +} + +int8_t RegionIN865DlChannelReq( DlChannelReqParams_t* dlChannelReq ) +{ + uint8_t status = 0x03; +#if defined( REGION_IN865 ) + + // Verify if the frequency is supported + if( VerifyRfFreq( dlChannelReq->Rx1Frequency ) == false ) + { + status &= 0xFE; + } + + // Verify if an uplink frequency exists + if( RegionNvmGroup2->Channels[dlChannelReq->ChannelId].Frequency == 0 ) + { + status &= 0xFD; + } + + // Apply Rx1 frequency, if the status is OK + if( status == 0x03 ) + { + RegionNvmGroup2->Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency; + } + +#endif /* REGION_IN865 */ + return status; +} + +int8_t RegionIN865AlternateDr( int8_t currentDr, AlternateDrType_t type ) +{ +#if defined( REGION_IN865 ) + return currentDr; +#else + return -1; +#endif /* REGION_IN865 */ +} + +LoRaMacStatus_t RegionIN865NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ) +{ +#if defined( REGION_IN865 ) + uint8_t nbEnabledChannels = 0; + uint8_t nbRestrictedChannels = 0; + uint8_t enabledChannels[IN865_MAX_NB_CHANNELS] = { 0 }; + RegionCommonIdentifyChannelsParam_t identifyChannelsParam; + RegionCommonCountNbOfEnabledChannelsParams_t countChannelsParams; + LoRaMacStatus_t status = LORAMAC_STATUS_NO_CHANNEL_FOUND; + uint16_t joinChannels = IN865_JOIN_CHANNELS; + + if( RegionCommonCountChannels( RegionNvmGroup2->ChannelsMask, 0, 1 ) == 0 ) + { // Reactivate default channels + RegionNvmGroup2->ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); + } + + // Search how many channels are enabled + countChannelsParams.Joined = nextChanParams->Joined; + countChannelsParams.Datarate = nextChanParams->Datarate; + countChannelsParams.ChannelsMask = RegionNvmGroup2->ChannelsMask; + countChannelsParams.Channels = RegionNvmGroup2->Channels; +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + countChannelsParams.Bands = RegionNvmGroup1->Bands; +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + countChannelsParams.Bands = RegionBands; +#endif /* REGION_VERSION */ + countChannelsParams.MaxNbChannels = IN865_MAX_NB_CHANNELS; + countChannelsParams.JoinChannels = &joinChannels; + + identifyChannelsParam.AggrTimeOff = nextChanParams->AggrTimeOff; + identifyChannelsParam.LastAggrTx = nextChanParams->LastAggrTx; + identifyChannelsParam.DutyCycleEnabled = nextChanParams->DutyCycleEnabled; + identifyChannelsParam.MaxBands = IN865_MAX_NB_BANDS; + + identifyChannelsParam.ElapsedTimeSinceStartUp = nextChanParams->ElapsedTimeSinceStartUp; + identifyChannelsParam.LastTxIsJoinRequest = nextChanParams->LastTxIsJoinRequest; + identifyChannelsParam.ExpectedTimeOnAir = GetTimeOnAir( nextChanParams->Datarate, nextChanParams->PktLen ); + + identifyChannelsParam.CountNbOfEnabledChannelsParam = &countChannelsParams; + + status = RegionCommonIdentifyChannels( &identifyChannelsParam, aggregatedTimeOff, enabledChannels, + &nbEnabledChannels, &nbRestrictedChannels, time ); + + if( status == LORAMAC_STATUS_OK ) + { + // We found a valid channel + *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )]; + } + else if( status == LORAMAC_STATUS_NO_CHANNEL_FOUND ) + { + // Datarate not supported by any channel, restore defaults + RegionNvmGroup2->ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); + } + return status; +#else + return LORAMAC_STATUS_NO_CHANNEL_FOUND; +#endif /* REGION_IN865 */ +} + +LoRaMacStatus_t RegionIN865ChannelAdd( ChannelAddParams_t* channelAdd ) +{ +#if defined( REGION_IN865 ) + 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* ) &(RegionNvmGroup2->Channels[id]), ( uint8_t* ) channelAdd->NewChannel, sizeof( RegionNvmGroup2->Channels[id] ) ); + RegionNvmGroup2->Channels[id].Band = 0; + RegionNvmGroup2->ChannelsMask[0] |= ( 1 << id ); + return LORAMAC_STATUS_OK; +#else + return LORAMAC_STATUS_NO_CHANNEL_FOUND; +#endif /* REGION_IN865 */ +} + +bool RegionIN865ChannelsRemove( ChannelRemoveParams_t* channelRemove ) +{ +#if defined( REGION_IN865 ) + uint8_t id = channelRemove->ChannelId; + + if( id < IN865_NUMB_DEFAULT_CHANNELS ) + { + return false; + } + + // Remove the channel from the list of channels + RegionNvmGroup2->Channels[id] = ( ChannelParams_t ){ 0, 0, { 0 }, 0 }; + + return RegionCommonChanDisable( RegionNvmGroup2->ChannelsMask, id, IN865_MAX_NB_CHANNELS ); +#else + return false; +#endif /* REGION_IN865 */ +} + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +void RegionIN865SetContinuousWave( ContinuousWaveParams_t* continuousWave ) +{ +#if defined( REGION_IN865 ) + int8_t txPowerLimited = RegionCommonLimitTxPower( continuousWave->TxPower, RegionNvmGroup1->Bands[RegionNvmGroup2->Channels[continuousWave->Channel].Band].TxMaxPower ); + int8_t phyTxPower = 0; + uint32_t frequency = RegionNvmGroup2->Channels[continuousWave->Channel].Frequency; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, continuousWave->MaxEirp, continuousWave->AntennaGain ); + + Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout ); +#endif /* REGION_IN865 */ +} + +uint8_t RegionIN865ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) +{ +#if defined( REGION_IN865 ) + // Apply offset formula + return MIN( DR_5, MAX( DR_0, dr - EffectiveRx1DrOffsetIN865[drOffset] ) ); +#else + return 0; +#endif /* REGION_IN865 */ +} +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) +uint8_t RegionIN865ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) +{ +#if defined( REGION_IN865 ) + int8_t datarate = EffectiveRx1DrOffsetIN865[dr][drOffset]; + + if( ( datarate < 0 ) || ( dr == DR_6 ) ) + { + datarate = DR_0; + } + return datarate; +#else + return 0; +#endif /* REGION_IN865 */ +} +#endif /* REGION_VERSION */ + +void RegionIN865RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ) +{ +#if defined( REGION_IN865 ) + 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; +#endif /* REGION_IN865 */ +} diff --git a/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionIN865.h b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionIN865.h new file mode 100644 index 0000000..76f5c14 --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionIN865.h @@ -0,0 +1,517 @@ +/*! + * \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. + * \{ + */ +/** + ****************************************************************************** + * + * Portions COPYRIGHT 2020 STMicroelectronics + * + * @file RegionIN865.h + * @author MCD Application Team + * @brief Region definition for IN865 + ****************************************************************************** + */ +#ifndef __REGION_IN865_H__ +#define __REGION_IN865_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "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 + +/*! + * 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 + +/*! + * Enabled or disabled the duty cycle + */ +#define IN865_DUTY_CYCLE_ENABLED 0 + +/*! + * Maximum RX window duration + */ +#define IN865_MAX_RX_WINDOW 3000 + +#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 + +/*! + * Default uplink dwell time configuration + */ +#define IN865_DEFAULT_UPLINK_DWELL_TIME 0 + +/* + * CLASS B + */ +/*! + * Beacon frequency + */ +#define IN865_BEACON_CHANNEL_FREQ 866550000 + +/*! + * Ping slot channel frequency + */ +#define IN865_PING_SLOT_CHANNEL_FREQ 866550000 + +/*! + * Payload size of a beacon frame + */ +#define IN865_BEACON_SIZE 19 + +/*! + * Size of RFU 1 field + */ +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +#define IN865_RFU1_SIZE 1 +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) +#define IN865_RFU1_SIZE 0 +#endif /* REGION_VERSION */ + +/*! + * Size of RFU 2 field + */ +#define IN865_RFU2_SIZE 3 + +/*! + * Datarate of the beacon channel + */ +#define IN865_BEACON_CHANNEL_DR DR_4 + +/*! + * Bandwidth 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 + * Band = { DutyCycle, TxMaxPower, LastBandUpdateTime, LastMaxCreditAssignTime, TimeCredits, MaxTimeCredits, ReadyForTransmission } + */ +#define IN865_BAND0 { 1 , IN865_MAX_TX_POWER, 0, 0, 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 ) ) + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) +/*! + * RFU value + */ +#define IN865_DR_RFU_VALUE { 0, 0, 0, 0, 0, 0, 0, 0 } +#endif /* REGION_VERSION */ + +/*! + * 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 }; + +/* ST_WORKAROUND_BEGIN: Keep repeater feature */ +/*! + * 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 }; +/* ST_WORKAROUND_END */ + +/*! + * Effective datarate offsets for receive window 1. + */ +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +static const int8_t EffectiveRx1DrOffsetIN865[] = { 0, 1, 2, 3, 4, 5, -1, -2 }; +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) +static const int8_t EffectiveRx1DrOffsetIN865[8][8] = +{ + { DR_0 , DR_0 , DR_0 , DR_0 , DR_0 , DR_0 , DR_1 , DR_2 }, // DR_0 + { DR_1 , DR_0 , DR_0 , DR_0 , DR_0 , DR_0 , DR_2 , DR_3 }, // DR_1 + { DR_2 , DR_1 , DR_0 , DR_0 , DR_0 , DR_0 , DR_3 , DR_4 }, // DR_2 + { DR_3 , DR_2 , DR_1 , DR_0 , DR_0 , DR_0 , DR_4 , DR_5 }, // DR_3 + { DR_4 , DR_3 , DR_2 , DR_1 , DR_0 , DR_0 , DR_5 , DR_5 }, // DR_4 + { DR_5 , DR_4 , DR_3 , DR_2 , DR_1 , DR_0 , DR_5 , DR_7 }, // DR_5 + IN865_DR_RFU_VALUE , // DR_6 + { DR_7 , DR_5 , DR_5 , DR_4 , DR_3 , DR_2 , DR_7 , DR_7 }, // DR_7 +}; +#endif /* REGION_VERSION */ + +/*! + * \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] params Sets the initialization type. + */ +void RegionIN865InitDefaults( InitDefaultsParams_t* params ); + +/*! + * \brief Verifies a parameter. + * + * \param [in] verify Pointer to the function parameters. + * + * \param [in] phyAttribute 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. + * + * \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 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. + */ +int8_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. + */ +int8_t RegionIN865DlChannelReq( 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 RegionIN865AlternateDr( int8_t currentDr, AlternateDrType_t type ); + +/*! + * \brief Searches and set the next random available channel + * + * \param [in] nextChanParams pointer of selected channel parameters + * + * \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 ); + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +/*! + * \brief Sets the radio into continuous wave mode. + * + * \param [IN] continuousWave Pointer to the function parameters. + */ +void RegionIN865SetContinuousWave( ContinuousWaveParams_t* continuousWave ); +#endif /* REGION_VERSION */ + +/*! + * \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 + * + * \param [out] outDr Datarate used to receive the beacon + */ + void RegionIN865RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ); + +/*! \} defgroup REGIONIN865 */ + +#ifdef __cplusplus +} +#endif + +#endif // __REGION_IN865_H__ diff --git a/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionKR920.c b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionKR920.c new file mode 100644 index 0000000..0cc2cef --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionKR920.c @@ -0,0 +1,1087 @@ +/*! + * \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 ) +*/ +/** + ****************************************************************************** + * + * Portions COPYRIGHT 2020 STMicroelectronics + * + * @file RegionKR920.c + * @author MCD Application Team + * @brief Region implementation for KR920 + ****************************************************************************** + */ +#include "../../../SubGHz_Phy/radio.h" +#include "RegionKR920.h" + +// Definitions +#define CHANNELS_MASK_SIZE 1 + +/*! + * Specifies the reception bandwidth to be used while executing the LBT + * Max channel bandwidth is 200 kHz + */ +#define KR920_LBT_RX_BANDWIDTH 200000 + +#if defined( REGION_KR920 ) +/* + * Non-volatile module context. + */ +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +static RegionNvmDataGroup1_t* RegionNvmGroup1; +static RegionNvmDataGroup2_t* RegionNvmGroup2; +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) +// static RegionNvmDataGroup1_t* RegionNvmGroup1; /* Unused for this region */ +static RegionNvmDataGroup2_t* RegionNvmGroup2; +static Band_t* RegionBands; +#endif /* REGION_VERSION */ + +// Static functions +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 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 TimerTime_t GetTimeOnAir( int8_t datarate, uint16_t pktLen ) +{ + int8_t phyDr = DataratesKR920[datarate]; + uint32_t bandwidth = RegionCommonGetBandwidth( datarate, BandwidthsKR920 ); + + return Radio.TimeOnAir( MODEM_LORA, bandwidth, phyDr, 1, 8, false, pktLen, true ); +} +#endif /* REGION_KR920 */ + +PhyParam_t RegionKR920GetPhyParam( GetPhyParams_t* getPhy ) +{ + PhyParam_t phyParam = { 0 }; + +#if defined( REGION_KR920 ) + 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: + { + RegionCommonGetNextLowerTxDrParams_t nextLowerTxDrParams = + { + .CurrentDr = getPhy->Datarate, + .MaxDr = ( int8_t )KR920_TX_MAX_DATARATE, + .MinDr = ( int8_t )KR920_TX_MIN_DATARATE, + .NbChannels = KR920_MAX_NB_CHANNELS, + .ChannelsMask = RegionNvmGroup2->ChannelsMask, + .Channels = RegionNvmGroup2->Channels, + }; + phyParam.Value = RegionCommonGetNextLowerTxDr( &nextLowerTxDrParams ); + 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 = REGION_COMMON_DEFAULT_ADR_ACK_LIMIT; + break; + } + case PHY_DEF_ADR_ACK_DELAY: + { + phyParam.Value = REGION_COMMON_DEFAULT_ADR_ACK_DELAY; + break; + } + case PHY_MAX_PAYLOAD: + { + phyParam.Value = MaxPayloadOfDatarateKR920[getPhy->Datarate]; + break; + } + /* ST_WORKAROUND_BEGIN: Keep repeater feature */ + case PHY_MAX_PAYLOAD_REPEATER: + { + phyParam.Value = MaxPayloadOfDatarateRepeaterKR920[getPhy->Datarate]; + break; + } + /* ST_WORKAROUND_END */ + 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 = REGION_COMMON_DEFAULT_RECEIVE_DELAY1; + break; + } + case PHY_RECEIVE_DELAY2: + { + phyParam.Value = REGION_COMMON_DEFAULT_RECEIVE_DELAY2; + break; + } + case PHY_JOIN_ACCEPT_DELAY1: + { + phyParam.Value = REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY1; + break; + } + case PHY_JOIN_ACCEPT_DELAY2: + { + phyParam.Value = REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY2; + break; + } +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + case PHY_MAX_FCNT_GAP: + { + phyParam.Value = REGION_COMMON_DEFAULT_MAX_FCNT_GAP; + break; + } + case PHY_ACK_TIMEOUT: + { + phyParam.Value = ( REGION_COMMON_DEFAULT_ACK_TIMEOUT + randr( -REGION_COMMON_DEFAULT_ACK_TIMEOUT_RND, REGION_COMMON_DEFAULT_ACK_TIMEOUT_RND ) ); + break; + } +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + case PHY_RETRANSMIT_TIMEOUT: + { + phyParam.Value = ( REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT + randr( -REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT_RND, REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT_RND ) ); + break; + } +#endif /* REGION_VERSION */ + case PHY_DEF_DR1_OFFSET: + { + phyParam.Value = REGION_COMMON_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 = RegionNvmGroup2->ChannelsMask; + break; + } + case PHY_CHANNELS_DEFAULT_MASK: + { + phyParam.ChannelsMask = RegionNvmGroup2->ChannelsDefaultMask; + break; + } + case PHY_MAX_NB_CHANNELS: + { + phyParam.Value = KR920_MAX_NB_CHANNELS; + break; + } + case PHY_CHANNELS: + { + phyParam.Channels = RegionNvmGroup2->Channels; + break; + } + case PHY_DEF_UPLINK_DWELL_TIME: + { + phyParam.Value = KR920_DEFAULT_UPLINK_DWELL_TIME; + break; + } + case PHY_DEF_DOWNLINK_DWELL_TIME: + { + phyParam.Value = REGION_COMMON_DEFAULT_DOWNLINK_DWELL_TIME; + 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_FREQ: + { + phyParam.Value = KR920_PING_SLOT_CHANNEL_FREQ; + break; + } + case PHY_PING_SLOT_CHANNEL_DR: + { + phyParam.Value = KR920_PING_SLOT_CHANNEL_DR; + break; + } + case PHY_SF_FROM_DR: + { + phyParam.Value = DataratesKR920[getPhy->Datarate]; + break; + } + case PHY_BW_FROM_DR: + { + phyParam.Value = RegionCommonGetBandwidth( getPhy->Datarate, BandwidthsKR920 ); + break; + } + default: + { + break; + } + } + +#endif /* REGION_KR920 */ + return phyParam; +} + +void RegionKR920SetBandTxDone( SetBandTxDoneParams_t* txDone ) +{ +#if defined( REGION_KR920 ) +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + RegionCommonSetBandTxDone( &RegionNvmGroup1->Bands[RegionNvmGroup2->Channels[txDone->Channel].Band], + txDone->LastTxAirTime, txDone->Joined, txDone->ElapsedTimeSinceStartUp ); +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + RegionCommonSetBandTxDone( &RegionBands[RegionNvmGroup2->Channels[txDone->Channel].Band], + txDone->LastTxAirTime, txDone->Joined, txDone->ElapsedTimeSinceStartUp ); +#endif /* REGION_VERSION */ +#endif /* REGION_KR920 */ +} + +void RegionKR920InitDefaults( InitDefaultsParams_t* params ) +{ +#if defined( REGION_KR920 ) + Band_t bands[KR920_MAX_NB_BANDS] = + { + KR920_BAND0 + }; + + switch( params->Type ) + { + case INIT_TYPE_DEFAULTS: + { + if( ( params->NvmGroup1 == NULL ) || ( params->NvmGroup2 == NULL ) ) + { + return; + } + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + RegionNvmGroup1 = (RegionNvmDataGroup1_t*) params->NvmGroup1; + RegionNvmGroup2 = (RegionNvmDataGroup2_t*) params->NvmGroup2; + + // Initialize bands + memcpy1( ( uint8_t* )RegionNvmGroup1->Bands, ( uint8_t* )bands, sizeof( Band_t ) * KR920_MAX_NB_BANDS ); +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + RegionNvmGroup2 = (RegionNvmDataGroup2_t*) params->NvmGroup2; + RegionBands = (Band_t*) params->Bands; + + // Initialize bands + memcpy1( ( uint8_t* )RegionBands, ( uint8_t* )bands, sizeof( Band_t ) * KR920_MAX_NB_BANDS ); +#endif /* REGION_VERSION */ + + // Default channels + RegionNvmGroup2->Channels[0] = ( ChannelParams_t ) KR920_LC1; + RegionNvmGroup2->Channels[1] = ( ChannelParams_t ) KR920_LC2; + RegionNvmGroup2->Channels[2] = ( ChannelParams_t ) KR920_LC3; + + // Default ChannelsMask + RegionNvmGroup2->ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 ); + + // Update the channels mask + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, RegionNvmGroup2->ChannelsDefaultMask, CHANNELS_MASK_SIZE ); + break; + } + case INIT_TYPE_RESET_TO_DEFAULT_CHANNELS: + { + // Reset Channels Rx1Frequency to default 0 + RegionNvmGroup2->Channels[0].Rx1Frequency = 0; + RegionNvmGroup2->Channels[1].Rx1Frequency = 0; + RegionNvmGroup2->Channels[2].Rx1Frequency = 0; + // Update the channels mask + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, RegionNvmGroup2->ChannelsDefaultMask, CHANNELS_MASK_SIZE ); + break; + } + case INIT_TYPE_ACTIVATE_DEFAULT_CHANNELS: + { + // Restore channels default mask + RegionNvmGroup2->ChannelsMask[0] |= RegionNvmGroup2->ChannelsDefaultMask[0]; + break; + } + default: + { + break; + } + } +#endif /* REGION_KR920 */ +} + +bool RegionKR920Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ) +{ +#if defined( REGION_KR920 ) + 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; + } +#else + return false; +#endif /* REGION_KR920 */ +} + +void RegionKR920ApplyCFList( ApplyCFListParams_t* applyCFList ) +{ +#if defined( REGION_KR920 ) + 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 ); + } + } +#endif /* REGION_KR920 */ +} + +bool RegionKR920ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ) +{ +#if defined( REGION_KR920 ) + switch( chanMaskSet->ChannelsMaskType ) + { + case CHANNELS_MASK: + { + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + case CHANNELS_DEFAULT_MASK: + { + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + default: + return false; + } + return true; +#else + return false; +#endif /* REGION_KR920 */ +} + +void RegionKR920ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ) +{ +#if defined( REGION_KR920 ) + uint32_t tSymbolInUs = 0; + + // Get the datarate, perform a boundary check + rxConfigParams->Datarate = MIN( datarate, KR920_RX_MAX_DATARATE ); + rxConfigParams->Bandwidth = RegionCommonGetBandwidth( rxConfigParams->Datarate, BandwidthsKR920 ); + + tSymbolInUs = RegionCommonComputeSymbolTimeLoRa( DataratesKR920[rxConfigParams->Datarate], BandwidthsKR920[rxConfigParams->Datarate] ); + + RegionCommonComputeRxWindowParameters( tSymbolInUs, minRxSymbols, rxError, Radio.GetWakeupTime( ), &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); +#endif /* REGION_KR920 */ +} + +bool RegionKR920RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) +{ +#if defined( REGION_KR920 ) + int8_t dr = rxConfig->Datarate; + uint8_t maxPayload = 0; + int8_t phyDr = 0; + uint32_t frequency = rxConfig->Frequency; + + if( Radio.GetStatus( ) != RF_IDLE ) + { + return false; + } + + if( rxConfig->RxSlot == RX_SLOT_WIN_1 ) + { + // Apply window 1 frequency + frequency = RegionNvmGroup2->Channels[rxConfig->Channel].Frequency; + // Apply the alternative RX 1 window frequency, if it is available + if( RegionNvmGroup2->Channels[rxConfig->Channel].Rx1Frequency != 0 ) + { + frequency = RegionNvmGroup2->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 ); + + /* ST_WORKAROUND_BEGIN: Keep repeater feature */ + if( rxConfig->RepeaterSupport == true ) + { + maxPayload = MaxPayloadOfDatarateRepeaterKR920[dr]; + } + else + { + maxPayload = MaxPayloadOfDatarateKR920[dr]; + } + + Radio.SetMaxPayloadLength( MODEM_LORA, maxPayload + LORAMAC_FRAME_PAYLOAD_OVERHEAD_SIZE ); + /* ST_WORKAROUND_END */ + + /* ST_WORKAROUND_BEGIN: Print Rx config */ + RegionCommonRxConfigPrint(rxConfig->RxSlot, frequency, dr); + /* ST_WORKAROUND_END */ + + *datarate = (uint8_t) dr; + return true; +#else + return false; +#endif /* REGION_KR920 */ +} + +bool RegionKR920TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ) +{ +#if defined( REGION_KR920 ) + int8_t phyDr = DataratesKR920[txConfig->Datarate]; +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + int8_t txPowerLimited = RegionCommonLimitTxPower( txConfig->TxPower, RegionNvmGroup1->Bands[RegionNvmGroup2->Channels[txConfig->Channel].Band].TxMaxPower ); +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + int8_t txPowerLimited = RegionCommonLimitTxPower( txConfig->TxPower, RegionBands[RegionNvmGroup2->Channels[txConfig->Channel].Band].TxMaxPower ); +#endif /* REGION_VERSION */ + uint32_t bandwidth = RegionCommonGetBandwidth( txConfig->Datarate, BandwidthsKR920 ); + float maxEIRP = GetMaxEIRP( RegionNvmGroup2->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( RegionNvmGroup2->Channels[txConfig->Channel].Frequency ); + + Radio.SetTxConfig( MODEM_LORA, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 4000 ); + /* ST_WORKAROUND_BEGIN: Print Tx config */ + RegionCommonTxConfigPrint(RegionNvmGroup2->Channels[txConfig->Channel].Frequency, txConfig->Datarate); + /* ST_WORKAROUND_END */ + + // Setup maximum payload length of the radio driver + Radio.SetMaxPayloadLength( MODEM_LORA, txConfig->PktLen ); + // Update time-on-air + *txTimeOnAir = GetTimeOnAir( txConfig->Datarate, txConfig->PktLen ); + + *txPower = txPowerLimited; + return true; +#else + return false; +#endif /* REGION_KR920 */ +} + +uint8_t RegionKR920LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ) +{ + uint8_t status = 0x07; +#if defined( REGION_KR920 ) + RegionCommonLinkAdrParams_t linkAdrParams = { 0 }; + 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( RegionNvmGroup2->Channels[i].Frequency != 0 ) + { + chMask |= 1 << i; + } + } + else + { + if( ( ( chMask & ( 1 << i ) ) != 0 ) && + ( RegionNvmGroup2->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 = RegionNvmGroup2->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* ) RegionNvmGroup2->ChannelsMask, 0, sizeof( RegionNvmGroup2->ChannelsMask ) ); + // Update the channels mask + RegionNvmGroup2->ChannelsMask[0] = chMask; + } + + // Update status variables + *drOut = linkAdrParams.Datarate; + *txPowOut = linkAdrParams.TxPower; + *nbRepOut = linkAdrParams.NbRep; + *nbBytesParsed = bytesProcessed; + +#endif /* REGION_KR920 */ + return status; +} + +uint8_t RegionKR920RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ) +{ + uint8_t status = 0x07; +#if defined( REGION_KR920 ) + + // 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 + } + +#endif /* REGION_KR920 */ + return status; +} + +int8_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 ) +{ + // Do not accept the request + return -1; +} + +int8_t RegionKR920DlChannelReq( DlChannelReqParams_t* dlChannelReq ) +{ + uint8_t status = 0x03; +#if defined( REGION_KR920 ) + + // Verify if the frequency is supported + if( VerifyRfFreq( dlChannelReq->Rx1Frequency ) == false ) + { + status &= 0xFE; + } + + // Verify if an uplink frequency exists + if( RegionNvmGroup2->Channels[dlChannelReq->ChannelId].Frequency == 0 ) + { + status &= 0xFD; + } + + // Apply Rx1 frequency, if the status is OK + if( status == 0x03 ) + { + RegionNvmGroup2->Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency; + } + +#endif /* REGION_KR920 */ + return status; +} + +int8_t RegionKR920AlternateDr( int8_t currentDr, AlternateDrType_t type ) +{ +#if defined( REGION_KR920 ) + return currentDr; +#else + return -1; +#endif /* REGION_KR920 */ +} + +LoRaMacStatus_t RegionKR920NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ) +{ +#if defined( REGION_KR920 ) + uint8_t channelNext = 0; + uint8_t nbEnabledChannels = 0; + uint8_t nbRestrictedChannels = 0; + uint8_t enabledChannels[KR920_MAX_NB_CHANNELS] = { 0 }; + RegionCommonIdentifyChannelsParam_t identifyChannelsParam; + RegionCommonCountNbOfEnabledChannelsParams_t countChannelsParams; + LoRaMacStatus_t status = LORAMAC_STATUS_NO_CHANNEL_FOUND; + uint16_t joinChannels = KR920_JOIN_CHANNELS; + + if( RegionCommonCountChannels( RegionNvmGroup2->ChannelsMask, 0, 1 ) == 0 ) + { // Reactivate default channels + RegionNvmGroup2->ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); + } + + // Search how many channels are enabled + countChannelsParams.Joined = nextChanParams->Joined; + countChannelsParams.Datarate = nextChanParams->Datarate; + countChannelsParams.ChannelsMask = RegionNvmGroup2->ChannelsMask; + countChannelsParams.Channels = RegionNvmGroup2->Channels; +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + countChannelsParams.Bands = RegionNvmGroup1->Bands; +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + countChannelsParams.Bands = RegionBands; +#endif /* REGION_VERSION */ + countChannelsParams.MaxNbChannels = KR920_MAX_NB_CHANNELS; + countChannelsParams.JoinChannels = &joinChannels; + + identifyChannelsParam.AggrTimeOff = nextChanParams->AggrTimeOff; + identifyChannelsParam.LastAggrTx = nextChanParams->LastAggrTx; + identifyChannelsParam.DutyCycleEnabled = nextChanParams->DutyCycleEnabled; + identifyChannelsParam.MaxBands = KR920_MAX_NB_BANDS; + + identifyChannelsParam.ElapsedTimeSinceStartUp = nextChanParams->ElapsedTimeSinceStartUp; + identifyChannelsParam.LastTxIsJoinRequest = nextChanParams->LastTxIsJoinRequest; + identifyChannelsParam.ExpectedTimeOnAir = GetTimeOnAir( nextChanParams->Datarate, nextChanParams->PktLen ); + + identifyChannelsParam.CountNbOfEnabledChannelsParam = &countChannelsParams; + + status = RegionCommonIdentifyChannels( &identifyChannelsParam, aggregatedTimeOff, enabledChannels, + &nbEnabledChannels, &nbRestrictedChannels, time ); + + if( status == LORAMAC_STATUS_OK ) + { + 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( RegionNvmGroup2->Channels[channelNext].Frequency, KR920_LBT_RX_BANDWIDTH, KR920_RSSI_FREE_TH, KR920_CARRIER_SENSE_TIME ) == true ) + { + // Free channel found + *channel = channelNext; + return LORAMAC_STATUS_OK; + } + } + // Even if one or more channels are available according to the channel plan, no free channel + // was found during the LBT procedure. + status = LORAMAC_STATUS_NO_FREE_CHANNEL_FOUND; + } + else if( status == LORAMAC_STATUS_NO_CHANNEL_FOUND ) + { + // Datarate not supported by any channel, restore defaults + RegionNvmGroup2->ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); + } + return status; +#else + return LORAMAC_STATUS_NO_CHANNEL_FOUND; +#endif /* REGION_KR920 */ +} + +LoRaMacStatus_t RegionKR920ChannelAdd( ChannelAddParams_t* channelAdd ) +{ +#if defined( REGION_KR920 ) + 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* ) &(RegionNvmGroup2->Channels[id]), ( uint8_t* ) channelAdd->NewChannel, sizeof( RegionNvmGroup2->Channels[id] ) ); + RegionNvmGroup2->Channels[id].Band = 0; + RegionNvmGroup2->ChannelsMask[0] |= ( 1 << id ); + return LORAMAC_STATUS_OK; +#else + return LORAMAC_STATUS_NO_CHANNEL_FOUND; +#endif /* REGION_KR920 */ +} + +bool RegionKR920ChannelsRemove( ChannelRemoveParams_t* channelRemove ) +{ +#if defined( REGION_KR920 ) + uint8_t id = channelRemove->ChannelId; + + if( id < KR920_NUMB_DEFAULT_CHANNELS ) + { + return false; + } + + // Remove the channel from the list of channels + RegionNvmGroup2->Channels[id] = ( ChannelParams_t ){ 0, 0, { 0 }, 0 }; + + return RegionCommonChanDisable( RegionNvmGroup2->ChannelsMask, id, KR920_MAX_NB_CHANNELS ); +#else + return false; +#endif /* REGION_KR920 */ +} + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +void RegionKR920SetContinuousWave( ContinuousWaveParams_t* continuousWave ) +{ +#if defined( REGION_KR920 ) + int8_t txPowerLimited = RegionCommonLimitTxPower( continuousWave->TxPower, RegionNvmGroup1->Bands[RegionNvmGroup2->Channels[continuousWave->Channel].Band].TxMaxPower ); + float maxEIRP = GetMaxEIRP( RegionNvmGroup2->Channels[continuousWave->Channel].Frequency ); + int8_t phyTxPower = 0; + uint32_t frequency = RegionNvmGroup2->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 ); +#endif /* REGION_KR920 */ +} +#endif /* REGION_VERSION */ + +uint8_t RegionKR920ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) +{ +#if defined( REGION_KR920 ) + int8_t datarate = dr - drOffset; + + if( datarate < 0 ) + { + datarate = DR_0; + } + return datarate; +#else + return 0; +#endif /* REGION_KR920 */ +} + +void RegionKR920RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ) +{ +#if defined( REGION_KR920 ) + 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; +#endif /* REGION_KR920 */ +} diff --git a/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionKR920.h b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionKR920.h new file mode 100644 index 0000000..40e1c8d --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionKR920.h @@ -0,0 +1,506 @@ +/*! + * \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. + * \{ + */ +/** + ****************************************************************************** + * + * Portions COPYRIGHT 2020 STMicroelectronics + * + * @file RegionKR920.h + * @author MCD Application Team + * @brief Region definition for KR920 + ****************************************************************************** + */ +#ifndef __REGION_KR920_H__ +#define __REGION_KR920_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "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 + +/*! + * 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 + +/*! + * Enabled or disabled the duty cycle + */ +#define KR920_DUTY_CYCLE_ENABLED 0 + +/*! + * Maximum RX window duration + */ +#define KR920_MAX_RX_WINDOW 4000 + +#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 + +/*! + * Default uplink dwell time configuration + */ +#define KR920_DEFAULT_UPLINK_DWELL_TIME 0 + +/* + * CLASS B + */ +/*! + * Beacon frequency + */ +#define KR920_BEACON_CHANNEL_FREQ 923100000 + +/*! + * Ping slot channel frequency + */ +#define KR920_PING_SLOT_CHANNEL_FREQ 923100000 + +/*! + * Payload size of a beacon frame + */ +#define KR920_BEACON_SIZE 17 + +/*! + * Size of RFU 1 field + */ +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +#define KR920_RFU1_SIZE 2 +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) +#define KR920_RFU1_SIZE 1 +#endif /* REGION_VERSION */ + +/*! + * Size of RFU 2 field + */ +#define KR920_RFU2_SIZE 0 + +/*! + * Datarate of the beacon channel + */ +#define KR920_BEACON_CHANNEL_DR DR_3 + +/*! + * Bandwidth 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 + * Band = { DutyCycle, TxMaxPower, LastBandUpdateTime, LastMaxCreditAssignTime, TimeCredits, MaxTimeCredits, ReadyForTransmission } + */ +#define KR920_BAND0 { 1 , KR920_MAX_TX_POWER, 0, 0, 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 }; + +/* ST_WORKAROUND_BEGIN: Keep repeater feature */ +/*! + * 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 }; +/* ST_WORKAROUND_END */ + +/*! + * \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] params Sets the initialization type. + */ +void RegionKR920InitDefaults( InitDefaultsParams_t* params ); + +/*! + * \brief Verifies a parameter. + * + * \param [in] verify Pointer to the function parameters. + * + * \param [in] phyAttribute 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. + * + * \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 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. + */ +int8_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. + */ +int8_t RegionKR920DlChannelReq( 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 RegionKR920AlternateDr( int8_t currentDr, AlternateDrType_t type ); + +/*! + * \brief Searches and set the next random available channel + * + * \param [in] nextChanParams pointer of selected channel parameters + * + * \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 ); + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +/*! + * \brief Sets the radio into continuous wave mode. + * + * \param [IN] continuousWave Pointer to the function parameters. + */ +void RegionKR920SetContinuousWave( ContinuousWaveParams_t* continuousWave ); +#endif /* REGION_VERSION */ + +/*! + * \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 + * + * \param [out] outDr Datarate used to receive the beacon + */ + void RegionKR920RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ); + +/*! \} defgroup REGIONKR920 */ + +#ifdef __cplusplus +} +#endif + +#endif // __REGION_KR920_H__ diff --git a/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionNvm.h b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionNvm.h new file mode 100644 index 0000000..f17a05b --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionNvm.h @@ -0,0 +1,155 @@ +/*! + * \file RegionNvm.h + * + * \brief Region independent non-volatile data. + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \addtogroup REGIONCOMMON + * + * \{ + */ +#ifndef __REGIONNVM_H__ +#define __REGIONNVM_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "../../../../BSP/lorawan_conf.h" +#include "../LoRaMacTypes.h" +#include "RegionVersion.h" + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) +/*! + * Channel plan for region CN470 + */ +typedef enum eRegionCN470ChannelPlan +{ + CHANNEL_PLAN_UNKNOWN, + CHANNEL_PLAN_20MHZ_TYPE_A, + CHANNEL_PLAN_20MHZ_TYPE_B, + CHANNEL_PLAN_26MHZ_TYPE_A, + CHANNEL_PLAN_26MHZ_TYPE_B +}RegionCN470ChannelPlan_t; + +// Selection of REGION_NVM_MAX_NB_CHANNELS ( MAX = REGION_US915 | REGION_AU915 ) +#define REGION_NVM_MAX_NB_CHANNELS 72 + +#else +// Selection of REGION_NVM_MAX_NB_CHANNELS ( MAX = REGION_CN470 ) +#define REGION_NVM_MAX_NB_CHANNELS 96 + +#endif /* REGION_VERSION */ + +// Selection of REGION_NVM_MAX_NB_BANDS +#define REGION_NVM_MAX_NB_BANDS 6 + +// Selection of REGION_NVM_CHANNELS_MASK_SIZE +#define REGION_NVM_CHANNELS_MASK_SIZE 6 + +/*! + * Region specific data which must be stored in the NVM. + */ +typedef struct sRegionNvmDataGroup1 +{ +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + /*! + * LoRaMac bands + */ + Band_t Bands[ REGION_NVM_MAX_NB_BANDS ]; +#endif /* REGION_VERSION */ +#if defined( REGION_US915 ) || defined( REGION_AU915 ) || defined( REGION_CN470 ) + /*! + * LoRaMac channels remaining + */ + uint16_t ChannelsMaskRemaining[ REGION_NVM_CHANNELS_MASK_SIZE ]; +#endif +#if defined( REGION_US915 ) || defined( REGION_AU915 ) + /*! + * 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 datarates. + */ + uint8_t JoinTrialsCounter; +#endif + /*! + * CRC32 value of the Region data structure. + */ + uint32_t Crc32; +}RegionNvmDataGroup1_t; + +/*! + * Region specific data which must be stored in the NVM. + * Parameters which do not change very frequently. + */ +typedef struct sRegionNvmDataGroup2 +{ + /*! + * LoRaMAC channels + */ + ChannelParams_t Channels[ REGION_NVM_MAX_NB_CHANNELS ]; + /*! + * LoRaMac channels mask + */ + uint16_t ChannelsMask[ REGION_NVM_CHANNELS_MASK_SIZE ]; + /*! + * LoRaMac channels default mask + */ + uint16_t ChannelsDefaultMask[ REGION_NVM_CHANNELS_MASK_SIZE ]; +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) +#if defined( REGION_CN470 ) + /*! + * Holds the channel plan. + */ + RegionCN470ChannelPlan_t ChannelPlan; + /*! + * Holds the common join channel, if its an OTAA device, otherwise + * this value is 0. + */ + uint8_t CommonJoinChannelIndex; + /*! + * Identifier which specifies if the device is an OTAA device. Set + * to true, if its an OTAA device. + */ + bool IsOtaaDevice; +#endif +#endif /* REGION_VERSION */ + /*! + * CRC32 value of the Region data structure. + */ + uint32_t Crc32; +}RegionNvmDataGroup2_t; + +/*! \} addtogroup REGIONCOMMON */ + +#ifdef __cplusplus +} +#endif + +#endif // __REGIONNVM_H__ diff --git a/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionRU864.c b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionRU864.c new file mode 100644 index 0000000..e6da8da --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionRU864.c @@ -0,0 +1,1070 @@ +/*! + * \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 ) +*/ +/** + ****************************************************************************** + * + * Portions COPYRIGHT 2020 STMicroelectronics + * + * @file RegionRU864.c + * @author MCD Application Team + * @brief Region implementation for RU864 + ****************************************************************************** + */ +#include "../../../SubGHz_Phy/radio.h" +#include "RegionRU864.h" + +// Definitions +#define CHANNELS_MASK_SIZE 1 + +#if defined( REGION_RU864 ) +/* + * Non-volatile module context. + */ +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +static RegionNvmDataGroup1_t* RegionNvmGroup1; +static RegionNvmDataGroup2_t* RegionNvmGroup2; +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) +// static RegionNvmDataGroup1_t* RegionNvmGroup1; /* Unused for this region */ +static RegionNvmDataGroup2_t* RegionNvmGroup2; +static Band_t* RegionBands; +#endif /* REGION_VERSION */ + +// Static functions +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 TimerTime_t GetTimeOnAir( int8_t datarate, uint16_t pktLen ) +{ + int8_t phyDr = DataratesRU864[datarate]; + uint32_t bandwidth = RegionCommonGetBandwidth( datarate, BandwidthsRU864 ); + TimerTime_t timeOnAir = 0; + + if( datarate == DR_7 ) + { // High Speed FSK channel + timeOnAir = Radio.TimeOnAir( MODEM_FSK, bandwidth, phyDr * 1000, 0, 5, false, pktLen, true ); + } + else + { + timeOnAir = Radio.TimeOnAir( MODEM_LORA, bandwidth, phyDr, 1, 8, false, pktLen, true ); + } + return timeOnAir; +} +#endif /* REGION_RU864 */ + +PhyParam_t RegionRU864GetPhyParam( GetPhyParams_t* getPhy ) +{ + PhyParam_t phyParam = { 0 }; + +#if defined( REGION_RU864 ) + 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: + { + RegionCommonGetNextLowerTxDrParams_t nextLowerTxDrParams = + { + .CurrentDr = getPhy->Datarate, + .MaxDr = ( int8_t )RU864_TX_MAX_DATARATE, + .MinDr = ( int8_t )RU864_TX_MIN_DATARATE, + .NbChannels = RU864_MAX_NB_CHANNELS, + .ChannelsMask = RegionNvmGroup2->ChannelsMask, + .Channels = RegionNvmGroup2->Channels, + }; + phyParam.Value = RegionCommonGetNextLowerTxDr( &nextLowerTxDrParams ); + 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 = REGION_COMMON_DEFAULT_ADR_ACK_LIMIT; + break; + } + case PHY_DEF_ADR_ACK_DELAY: + { + phyParam.Value = REGION_COMMON_DEFAULT_ADR_ACK_DELAY; + break; + } + case PHY_MAX_PAYLOAD: + { + phyParam.Value = MaxPayloadOfDatarateRU864[getPhy->Datarate]; + break; + } + /* ST_WORKAROUND_BEGIN: Keep repeater feature */ + case PHY_MAX_PAYLOAD_REPEATER: + { + phyParam.Value = MaxPayloadOfDatarateRepeaterRU864[getPhy->Datarate]; + break; + } + /* ST_WORKAROUND_END */ + 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 = REGION_COMMON_DEFAULT_RECEIVE_DELAY1; + break; + } + case PHY_RECEIVE_DELAY2: + { + phyParam.Value = REGION_COMMON_DEFAULT_RECEIVE_DELAY2; + break; + } + case PHY_JOIN_ACCEPT_DELAY1: + { + phyParam.Value = REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY1; + break; + } + case PHY_JOIN_ACCEPT_DELAY2: + { + phyParam.Value = REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY2; + break; + } +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + case PHY_MAX_FCNT_GAP: + { + phyParam.Value = REGION_COMMON_DEFAULT_MAX_FCNT_GAP; + break; + } + case PHY_ACK_TIMEOUT: + { + phyParam.Value = ( REGION_COMMON_DEFAULT_ACK_TIMEOUT + randr( -REGION_COMMON_DEFAULT_ACK_TIMEOUT_RND, REGION_COMMON_DEFAULT_ACK_TIMEOUT_RND ) ); + break; + } +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + case PHY_RETRANSMIT_TIMEOUT: + { + phyParam.Value = ( REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT + randr( -REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT_RND, REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT_RND ) ); + break; + } +#endif /* REGION_VERSION */ + + case PHY_DEF_DR1_OFFSET: + { + phyParam.Value = REGION_COMMON_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 = RegionNvmGroup2->ChannelsMask; + break; + } + case PHY_CHANNELS_DEFAULT_MASK: + { + phyParam.ChannelsMask = RegionNvmGroup2->ChannelsDefaultMask; + break; + } + case PHY_MAX_NB_CHANNELS: + { + phyParam.Value = RU864_MAX_NB_CHANNELS; + break; + } + case PHY_CHANNELS: + { + phyParam.Channels = RegionNvmGroup2->Channels; + break; + } + case PHY_DEF_UPLINK_DWELL_TIME: + { + phyParam.Value = RU864_DEFAULT_UPLINK_DWELL_TIME; + break; + } + case PHY_DEF_DOWNLINK_DWELL_TIME: + { + phyParam.Value = REGION_COMMON_DEFAULT_DOWNLINK_DWELL_TIME; + 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_FREQ: + { + phyParam.Value = RU864_PING_SLOT_CHANNEL_FREQ; + break; + } + case PHY_PING_SLOT_CHANNEL_DR: + { + phyParam.Value = RU864_PING_SLOT_CHANNEL_DR; + break; + } + case PHY_SF_FROM_DR: + { + phyParam.Value = DataratesRU864[getPhy->Datarate]; + break; + } + case PHY_BW_FROM_DR: + { + phyParam.Value = RegionCommonGetBandwidth( getPhy->Datarate, BandwidthsRU864 ); + break; + } + default: + { + break; + } + } + +#endif /* REGION_RU864 */ + return phyParam; +} + +void RegionRU864SetBandTxDone( SetBandTxDoneParams_t* txDone ) +{ +#if defined( REGION_RU864 ) +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + RegionCommonSetBandTxDone( &RegionNvmGroup1->Bands[RegionNvmGroup2->Channels[txDone->Channel].Band], + txDone->LastTxAirTime, txDone->Joined, txDone->ElapsedTimeSinceStartUp ); +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + RegionCommonSetBandTxDone( &RegionBands[RegionNvmGroup2->Channels[txDone->Channel].Band], + txDone->LastTxAirTime, txDone->Joined, txDone->ElapsedTimeSinceStartUp ); +#endif /* REGION_VERSION */ + +#endif /* REGION_RU864 */ +} + +void RegionRU864InitDefaults( InitDefaultsParams_t* params ) +{ +#if defined( REGION_RU864 ) + Band_t bands[RU864_MAX_NB_BANDS] = + { + RU864_BAND0 + }; + + switch( params->Type ) + { + case INIT_TYPE_DEFAULTS: + { + if( ( params->NvmGroup1 == NULL ) || ( params->NvmGroup2 == NULL ) ) + { + return; + } + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + RegionNvmGroup1 = (RegionNvmDataGroup1_t*) params->NvmGroup1; + RegionNvmGroup2 = (RegionNvmDataGroup2_t*) params->NvmGroup2; + + // Default bands + memcpy1( ( uint8_t* )RegionNvmGroup1->Bands, ( uint8_t* )bands, sizeof( Band_t ) * RU864_MAX_NB_BANDS ); +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + RegionNvmGroup2 = (RegionNvmDataGroup2_t*) params->NvmGroup2; + RegionBands = (Band_t*) params->Bands; + + // Default bands + memcpy1( ( uint8_t* )RegionBands, ( uint8_t* )bands, sizeof( Band_t ) * RU864_MAX_NB_BANDS ); +#endif /* REGION_VERSION */ + + // Default channels + RegionNvmGroup2->Channels[0] = ( ChannelParams_t ) RU864_LC1; + RegionNvmGroup2->Channels[1] = ( ChannelParams_t ) RU864_LC2; + + // Default ChannelsMask + RegionNvmGroup2->ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ); + + // Update the channels mask + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, RegionNvmGroup2->ChannelsDefaultMask, CHANNELS_MASK_SIZE ); + break; + } + case INIT_TYPE_RESET_TO_DEFAULT_CHANNELS: + { + // Reset Channels Rx1Frequency to default 0 + RegionNvmGroup2->Channels[0].Rx1Frequency = 0; + RegionNvmGroup2->Channels[1].Rx1Frequency = 0; + // Update the channels mask + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, RegionNvmGroup2->ChannelsDefaultMask, CHANNELS_MASK_SIZE ); + break; + } + case INIT_TYPE_ACTIVATE_DEFAULT_CHANNELS: + { + // Restore channels default mask + RegionNvmGroup2->ChannelsMask[0] |= RegionNvmGroup2->ChannelsDefaultMask[0]; + break; + } + default: + { + break; + } + } +#endif /* REGION_RU864 */ +} + +bool RegionRU864Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ) +{ +#if defined( REGION_RU864 ) + 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; + } +#else + return false; +#endif /* REGION_RU864 */ +} + +void RegionRU864ApplyCFList( ApplyCFListParams_t* applyCFList ) +{ +#if defined( REGION_RU864 ) + 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 ); + } + } +#endif /* REGION_RU864 */ +} + +bool RegionRU864ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ) +{ +#if defined( REGION_RU864 ) + switch( chanMaskSet->ChannelsMaskType ) + { + case CHANNELS_MASK: + { + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + case CHANNELS_DEFAULT_MASK: + { + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + default: + return false; + } + return true; +#else + return false; +#endif /* REGION_RU864 */ +} + +void RegionRU864ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ) +{ +#if defined( REGION_RU864 ) + uint32_t tSymbolInUs = 0; + + // Get the datarate, perform a boundary check + rxConfigParams->Datarate = MIN( datarate, RU864_RX_MAX_DATARATE ); + rxConfigParams->Bandwidth = RegionCommonGetBandwidth( rxConfigParams->Datarate, BandwidthsRU864 ); + + if( rxConfigParams->Datarate == DR_7 ) + { // FSK + tSymbolInUs = RegionCommonComputeSymbolTimeFsk( DataratesRU864[rxConfigParams->Datarate] ); + } + else + { // LoRa + tSymbolInUs = RegionCommonComputeSymbolTimeLoRa( DataratesRU864[rxConfigParams->Datarate], BandwidthsRU864[rxConfigParams->Datarate] ); + } + + RegionCommonComputeRxWindowParameters( tSymbolInUs, minRxSymbols, rxError, Radio.GetWakeupTime( ), &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); +#endif /* REGION_RU864 */ +} + +bool RegionRU864RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) +{ +#if defined( REGION_RU864 ) + RadioModems_t modem; + int8_t dr = rxConfig->Datarate; + uint8_t maxPayload = 0; + int8_t phyDr = 0; + uint32_t frequency = rxConfig->Frequency; + + if( Radio.GetStatus( ) != RF_IDLE ) + { + return false; + } + + if( rxConfig->RxSlot == RX_SLOT_WIN_1 ) + { + // Apply window 1 frequency + frequency = RegionNvmGroup2->Channels[rxConfig->Channel].Frequency; + // Apply the alternative RX 1 window frequency, if it is available + if( RegionNvmGroup2->Channels[rxConfig->Channel].Rx1Frequency != 0 ) + { + frequency = RegionNvmGroup2->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 ); + } + + /* ST_WORKAROUND_BEGIN: Keep repeater feature */ + if( rxConfig->RepeaterSupport == true ) + { + maxPayload = MaxPayloadOfDatarateRepeaterRU864[dr]; + } + else + { + maxPayload = MaxPayloadOfDatarateRU864[dr]; + } + + Radio.SetMaxPayloadLength( modem, maxPayload + LORAMAC_FRAME_PAYLOAD_OVERHEAD_SIZE ); + /* ST_WORKAROUND_END */ + + /* ST_WORKAROUND_BEGIN: Print Rx config */ + RegionCommonRxConfigPrint(rxConfig->RxSlot, frequency, dr); + /* ST_WORKAROUND_END */ + + *datarate = (uint8_t) dr; + return true; +#else + return false; +#endif /* REGION_RU864 */ +} + +bool RegionRU864TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ) +{ +#if defined( REGION_RU864 ) + RadioModems_t modem; + int8_t phyDr = DataratesRU864[txConfig->Datarate]; +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + int8_t txPowerLimited = RegionCommonLimitTxPower( txConfig->TxPower, RegionNvmGroup1->Bands[RegionNvmGroup2->Channels[txConfig->Channel].Band].TxMaxPower ); +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + int8_t txPowerLimited = RegionCommonLimitTxPower( txConfig->TxPower, RegionBands[RegionNvmGroup2->Channels[txConfig->Channel].Band].TxMaxPower ); +#endif /* REGION_VERSION */ + uint32_t bandwidth = RegionCommonGetBandwidth( txConfig->Datarate, BandwidthsRU864 ); + int8_t phyTxPower = 0; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain ); + + // Setup the radio frequency + Radio.SetChannel( RegionNvmGroup2->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 ); + } + /* ST_WORKAROUND_BEGIN: Print Tx config */ + RegionCommonTxConfigPrint(RegionNvmGroup2->Channels[txConfig->Channel].Frequency, txConfig->Datarate); + /* ST_WORKAROUND_END */ + + // Update time-on-air + *txTimeOnAir = GetTimeOnAir( txConfig->Datarate, txConfig->PktLen ); + + // Setup maximum payload length of the radio driver + Radio.SetMaxPayloadLength( modem, txConfig->PktLen ); + + *txPower = txPowerLimited; + return true; +#else + return false; +#endif /* REGION_RU864 */ +} + +uint8_t RegionRU864LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ) +{ + uint8_t status = 0x07; +#if defined( REGION_RU864 ) + RegionCommonLinkAdrParams_t linkAdrParams = { 0 }; + 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( RegionNvmGroup2->Channels[i].Frequency != 0 ) + { + chMask |= 1 << i; + } + } + else + { + if( ( ( chMask & ( 1 << i ) ) != 0 ) && + ( RegionNvmGroup2->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 = RegionNvmGroup2->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* ) RegionNvmGroup2->ChannelsMask, 0, sizeof( RegionNvmGroup2->ChannelsMask ) ); + // Update the channels mask + RegionNvmGroup2->ChannelsMask[0] = chMask; + } + + // Update status variables + *drOut = linkAdrParams.Datarate; + *txPowOut = linkAdrParams.TxPower; + *nbRepOut = linkAdrParams.NbRep; + *nbBytesParsed = bytesProcessed; + +#endif /* REGION_RU864 */ + return status; +} + +uint8_t RegionRU864RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ) +{ + uint8_t status = 0x07; +#if defined( REGION_RU864 ) + + // 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 + } + +#endif /* REGION_RU864 */ + return status; +} + +int8_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 ) +{ + // Do not accept the request + return -1; +} + +int8_t RegionRU864DlChannelReq( DlChannelReqParams_t* dlChannelReq ) +{ + uint8_t status = 0x03; +#if defined( REGION_RU864 ) + + // Verify if the frequency is supported + if( VerifyRfFreq( dlChannelReq->Rx1Frequency ) == false ) + { + status &= 0xFE; + } + + // Verify if an uplink frequency exists + if( RegionNvmGroup2->Channels[dlChannelReq->ChannelId].Frequency == 0 ) + { + status &= 0xFD; + } + + // Apply Rx1 frequency, if the status is OK + if( status == 0x03 ) + { + RegionNvmGroup2->Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency; + } + +#endif /* REGION_RU864 */ + return status; +} + +int8_t RegionRU864AlternateDr( int8_t currentDr, AlternateDrType_t type ) +{ +#if defined( REGION_RU864 ) + return currentDr; +#else + return -1; +#endif /* REGION_RU864 */ +} + +LoRaMacStatus_t RegionRU864NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ) +{ +#if defined( REGION_RU864 ) + uint8_t nbEnabledChannels = 0; + uint8_t nbRestrictedChannels = 0; + uint8_t enabledChannels[RU864_MAX_NB_CHANNELS] = { 0 }; + RegionCommonIdentifyChannelsParam_t identifyChannelsParam; + RegionCommonCountNbOfEnabledChannelsParams_t countChannelsParams; + LoRaMacStatus_t status = LORAMAC_STATUS_NO_CHANNEL_FOUND; + uint16_t joinChannels = RU864_JOIN_CHANNELS; + + if( RegionCommonCountChannels( RegionNvmGroup2->ChannelsMask, 0, 1 ) == 0 ) + { // Reactivate default channels + RegionNvmGroup2->ChannelsMask[0] |= LC( 1 ) + LC( 2 ); + } + + // Search how many channels are enabled + countChannelsParams.Joined = nextChanParams->Joined; + countChannelsParams.Datarate = nextChanParams->Datarate; + countChannelsParams.ChannelsMask = RegionNvmGroup2->ChannelsMask; + countChannelsParams.Channels = RegionNvmGroup2->Channels; +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + countChannelsParams.Bands = RegionNvmGroup1->Bands; +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + countChannelsParams.Bands = RegionBands; +#endif /* REGION_VERSION */ + countChannelsParams.MaxNbChannels = RU864_MAX_NB_CHANNELS; + countChannelsParams.JoinChannels = &joinChannels; + + identifyChannelsParam.AggrTimeOff = nextChanParams->AggrTimeOff; + identifyChannelsParam.LastAggrTx = nextChanParams->LastAggrTx; + identifyChannelsParam.DutyCycleEnabled = nextChanParams->DutyCycleEnabled; + identifyChannelsParam.MaxBands = RU864_MAX_NB_BANDS; + + identifyChannelsParam.ElapsedTimeSinceStartUp = nextChanParams->ElapsedTimeSinceStartUp; + identifyChannelsParam.LastTxIsJoinRequest = nextChanParams->LastTxIsJoinRequest; + identifyChannelsParam.ExpectedTimeOnAir = GetTimeOnAir( nextChanParams->Datarate, nextChanParams->PktLen ); + + identifyChannelsParam.CountNbOfEnabledChannelsParam = &countChannelsParams; + + status = RegionCommonIdentifyChannels( &identifyChannelsParam, aggregatedTimeOff, enabledChannels, + &nbEnabledChannels, &nbRestrictedChannels, time ); + + if( status == LORAMAC_STATUS_OK ) + { + // We found a valid channel + *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )]; + } + else if( status == LORAMAC_STATUS_NO_CHANNEL_FOUND ) + { + // Datarate not supported by any channel, restore defaults + RegionNvmGroup2->ChannelsMask[0] |= LC( 1 ) + LC( 2 ); + } + return status; +#else + return LORAMAC_STATUS_NO_CHANNEL_FOUND; +#endif /* REGION_RU864 */ +} + +LoRaMacStatus_t RegionRU864ChannelAdd( ChannelAddParams_t* channelAdd ) +{ +#if defined( REGION_RU864 ) + 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* ) &(RegionNvmGroup2->Channels[id]), ( uint8_t* ) channelAdd->NewChannel, sizeof( RegionNvmGroup2->Channels[id] ) ); + RegionNvmGroup2->Channels[id].Band = 0; + RegionNvmGroup2->ChannelsMask[0] |= ( 1 << id ); + return LORAMAC_STATUS_OK; +#else + return LORAMAC_STATUS_NO_CHANNEL_FOUND; +#endif /* REGION_RU864 */ +} + +bool RegionRU864ChannelsRemove( ChannelRemoveParams_t* channelRemove ) +{ +#if defined( REGION_RU864 ) + uint8_t id = channelRemove->ChannelId; + + if( id < RU864_NUMB_DEFAULT_CHANNELS ) + { + return false; + } + + // Remove the channel from the list of channels + RegionNvmGroup2->Channels[id] = ( ChannelParams_t ){ 0, 0, { 0 }, 0 }; + + return RegionCommonChanDisable( RegionNvmGroup2->ChannelsMask, id, RU864_MAX_NB_CHANNELS ); +#else + return false; +#endif /* REGION_RU864 */ +} + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +void RegionRU864SetContinuousWave( ContinuousWaveParams_t* continuousWave ) +{ +#if defined( REGION_RU864 ) + int8_t txPowerLimited = RegionCommonLimitTxPower( continuousWave->TxPower, RegionNvmGroup1->Bands[RegionNvmGroup2->Channels[continuousWave->Channel].Band].TxMaxPower ); + int8_t phyTxPower = 0; + uint32_t frequency = RegionNvmGroup2->Channels[continuousWave->Channel].Frequency; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, continuousWave->MaxEirp, continuousWave->AntennaGain ); + + Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout ); +#endif /* REGION_RU864 */ +} +#endif /* REGION_VERSION */ + +uint8_t RegionRU864ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) +{ +#if defined( REGION_RU864 ) + int8_t datarate = dr - drOffset; + + if( datarate < 0 ) + { + datarate = DR_0; + } + return datarate; +#else + return 0; +#endif /* REGION_RU864 */ +} + +void RegionRU864RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ) +{ +#if defined( REGION_RU864 ) + 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; +#endif /* REGION_RU864 */ +} diff --git a/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionRU864.h b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionRU864.h new file mode 100644 index 0000000..75d1709 --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionRU864.h @@ -0,0 +1,483 @@ +/*! + * \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. + * \{ + */ +/** + ****************************************************************************** + * + * Portions COPYRIGHT 2020 STMicroelectronics + * + * @file RegionRU864.h + * @author MCD Application Team + * @brief Region definition for RU864 + ****************************************************************************** + */ +#ifndef __REGION_RU864_H__ +#define __REGION_RU864_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "Region.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 + +/*! + * 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 + +/*! + * Enabled or disabled the duty cycle + */ +#define RU864_DUTY_CYCLE_ENABLED 1 + +/*! + * Maximum RX window duration + */ +#define RU864_MAX_RX_WINDOW 3000 + +#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 + +/*! + * Default uplink dwell time configuration + */ +#define RU864_DEFAULT_UPLINK_DWELL_TIME 0 + +/* + * CLASS B + */ +/*! + * Beacon frequency + */ +#define RU864_BEACON_CHANNEL_FREQ 869100000 + +/*! + * Ping slot channel frequency + */ +#define RU864_PING_SLOT_CHANNEL_FREQ 868900000 + +/*! + * Payload size of a beacon frame + */ +#define RU864_BEACON_SIZE 17 + +/*! + * Size of RFU 1 field + */ +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +#define RU864_RFU1_SIZE 2 +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) +#define RU864_RFU1_SIZE 1 +#endif /* REGION_VERSION */ + +/*! + * Size of RFU 2 field + */ +#define RU864_RFU2_SIZE 0 + +/*! + * Datarate of the beacon channel + */ +#define RU864_BEACON_CHANNEL_DR DR_3 + +/*! + * Bandwidth of the beacon channel (Index of BandwidthsRU864[]) + */ +#define RU864_BEACON_CHANNEL_BW 0 + +/*! + * Datarate of the ping slot channel + */ +#define RU864_PING_SLOT_CHANNEL_DR DR_3 + +/*! + * Maximum number of bands + */ +#define RU864_MAX_NB_BANDS 1 + +/*! + * Band 0 definition + * Band = { DutyCycle, TxMaxPower, LastBandUpdateTime, LastMaxCreditAssignTime, TimeCredits, MaxTimeCredits, ReadyForTransmission } + */ +#define RU864_BAND0 { 100 , RU864_MAX_TX_POWER, 0, 0, 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 }; + +/* ST_WORKAROUND_BEGIN: Keep repeater feature */ +/*! + * 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 }; +/* ST_WORKAROUND_END */ + +/*! + * \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] params Sets the initialization type. + */ +void RegionRU864InitDefaults( InitDefaultsParams_t* params ); + +/*! + * \brief Verifies a parameter. + * + * \param [in] verify Pointer to the function parameters. + * + * \param [in] phyAttribute 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. + * + * \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 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. + */ +int8_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. + */ +int8_t RegionRU864DlChannelReq( 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 RegionRU864AlternateDr( int8_t currentDr, AlternateDrType_t type ); + +/*! + * \brief Searches and set the next random available channel + * + * \param [in] nextChanParams pointer of selected channel parameters + * + * \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 ); + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +/*! + * \brief Sets the radio into continuous wave mode. + * + * \param [IN] continuousWave Pointer to the function parameters. + */ +void RegionRU864SetContinuousWave( ContinuousWaveParams_t* continuousWave ); +#endif /* REGION_VERSION */ + +/*! + * \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 + * + * \param [out] outDr Datarate used to receive the beacon + */ +void RegionRU864RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ); + +/*! \} defgroup REGIONRU864 */ + +#ifdef __cplusplus +} +#endif + +#endif // __REGION_RU864_H__ diff --git a/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionUS915.c b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionUS915.c new file mode 100644 index 0000000..b665b07 --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionUS915.c @@ -0,0 +1,1121 @@ +/*! + * \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 ) +*/ +/** + ****************************************************************************** + * + * Portions COPYRIGHT 2020 STMicroelectronics + * + * @file RegionUS915.c + * @author MCD Application Team + * @brief Region implementation for US915 + ****************************************************************************** + */ +#include "../../../SubGHz_Phy/radio.h" +#include "RegionUS915.h" +#include "RegionBaseUS.h" + +// Definitions +#define CHANNELS_MASK_SIZE 6 + +// A mask to select only valid 500KHz channels +#define CHANNELS_MASK_500KHZ_MASK 0x00FF + +/* The HYBRID_DEFAULT_MASKx define the enabled channels in Hybrid mode*/ +/* Note: they can be redefined in lorawan_conf.h*/ +#ifndef HYBRID_DEFAULT_MASK0 /*enabled channels from channel 15 down to channel 0*/ +#define HYBRID_DEFAULT_MASK0 0x00FF /*channel 7 down to channel 0 enabled*/ +#endif +#ifndef HYBRID_DEFAULT_MASK1 /*enabled channels from channel 31 down to channel 16*/ +#define HYBRID_DEFAULT_MASK1 0x0000 +#endif +#ifndef HYBRID_DEFAULT_MASK2 /*enabled channels from channel 47 down to channel 32*/ +#define HYBRID_DEFAULT_MASK2 0x0000 +#endif +#ifndef HYBRID_DEFAULT_MASK3 /*enabled channels from channel 63 down to channel 48*/ +#define HYBRID_DEFAULT_MASK3 0x0000 +#endif +#ifndef HYBRID_DEFAULT_MASK4 /*enabled channels from channel 71 down to channel 64*/ +#define HYBRID_DEFAULT_MASK4 0x0001 +#endif + +#if defined( REGION_US915 ) +/* + * Non-volatile module context. + */ +static RegionNvmDataGroup1_t* RegionNvmGroup1; +static RegionNvmDataGroup2_t* RegionNvmGroup2; +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) +static Band_t* RegionBands; +#endif /* REGION_VERSION */ + +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 = RegionCommonLimitTxPower( 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 frequencies into account + if( ( freq < 902300000 ) || ( freq > 927500000 ) ) + { + return false; + } + return true; +} + +static TimerTime_t GetTimeOnAir( int8_t datarate, uint16_t pktLen ) +{ + int8_t phyDr = DataratesUS915[datarate]; + uint32_t bandwidth = RegionCommonGetBandwidth( datarate, BandwidthsUS915 ); + + return Radio.TimeOnAir( MODEM_LORA, bandwidth, phyDr, 1, 8, false, pktLen, true ); +} +#endif /* REGION_US915 */ + +PhyParam_t RegionUS915GetPhyParam( GetPhyParams_t* getPhy ) +{ + PhyParam_t phyParam = { 0 }; + +#if defined( REGION_US915 ) + 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: + { + RegionCommonGetNextLowerTxDrParams_t nextLowerTxDrParams = + { + .CurrentDr = getPhy->Datarate, + .MaxDr = ( int8_t )US915_TX_MAX_DATARATE, + .MinDr = ( int8_t )US915_TX_MIN_DATARATE, + .NbChannels = US915_MAX_NB_CHANNELS, + .ChannelsMask = RegionNvmGroup2->ChannelsMask, + .Channels = RegionNvmGroup2->Channels, + }; + phyParam.Value = RegionCommonGetNextLowerTxDr( &nextLowerTxDrParams ); + 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 = REGION_COMMON_DEFAULT_ADR_ACK_LIMIT; + break; + } + case PHY_DEF_ADR_ACK_DELAY: + { + phyParam.Value = REGION_COMMON_DEFAULT_ADR_ACK_DELAY; + break; + } + case PHY_MAX_PAYLOAD: + { + phyParam.Value = MaxPayloadOfDatarateUS915[getPhy->Datarate]; + break; + } + /* ST_WORKAROUND_BEGIN: Keep repeater feature */ + case PHY_MAX_PAYLOAD_REPEATER: + { + phyParam.Value = MaxPayloadOfDatarateRepeaterUS915[getPhy->Datarate]; + break; + } + /* ST_WORKAROUND_END */ + 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 = REGION_COMMON_DEFAULT_RECEIVE_DELAY1; + break; + } + case PHY_RECEIVE_DELAY2: + { + phyParam.Value = REGION_COMMON_DEFAULT_RECEIVE_DELAY2; + break; + } + case PHY_JOIN_ACCEPT_DELAY1: + { + phyParam.Value = REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY1; + break; + } + case PHY_JOIN_ACCEPT_DELAY2: + { + phyParam.Value = REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY2; + break; + } +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + case PHY_MAX_FCNT_GAP: + { + phyParam.Value = REGION_COMMON_DEFAULT_MAX_FCNT_GAP; + break; + } + case PHY_ACK_TIMEOUT: + { + phyParam.Value = ( REGION_COMMON_DEFAULT_ACK_TIMEOUT + randr( -REGION_COMMON_DEFAULT_ACK_TIMEOUT_RND, REGION_COMMON_DEFAULT_ACK_TIMEOUT_RND ) ); + break; + } +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + case PHY_RETRANSMIT_TIMEOUT: + { + phyParam.Value = ( REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT + randr( -REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT_RND, REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT_RND ) ); + break; + } +#endif /* REGION_VERSION */ + case PHY_DEF_DR1_OFFSET: + { + phyParam.Value = REGION_COMMON_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 = RegionNvmGroup2->ChannelsMask; + break; + } + case PHY_CHANNELS_DEFAULT_MASK: + { + phyParam.ChannelsMask = RegionNvmGroup2->ChannelsDefaultMask; + break; + } + case PHY_MAX_NB_CHANNELS: + { + phyParam.Value = US915_MAX_NB_CHANNELS; + break; + } + case PHY_CHANNELS: + { + phyParam.Channels = RegionNvmGroup2->Channels; + break; + } + case PHY_DEF_UPLINK_DWELL_TIME: + { + phyParam.Value = US915_DEFAULT_UPLINK_DWELL_TIME; + break; + } + case PHY_DEF_DOWNLINK_DWELL_TIME: + { + phyParam.Value = REGION_COMMON_DEFAULT_DOWNLINK_DWELL_TIME; + break; + } + case PHY_DEF_MAX_EIRP: + { + phyParam.fValue = US915_DEFAULT_MAX_ERP + 2.15f; + break; + } + case PHY_DEF_ANTENNA_GAIN: + { + phyParam.fValue = 0; + break; + } + case PHY_BEACON_CHANNEL_FREQ: + { + phyParam.Value = RegionBaseUSCalcDownlinkFrequency( getPhy->Channel, + US915_BEACON_CHANNEL_FREQ, + US915_BEACON_CHANNEL_STEPWIDTH ); + 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_NB_CHANNELS: + { + phyParam.Value = US915_BEACON_NB_CHANNELS; + break; + } + case PHY_PING_SLOT_CHANNEL_FREQ: + { + phyParam.Value = RegionBaseUSCalcDownlinkFrequency( getPhy->Channel, + US915_PING_SLOT_CHANNEL_FREQ, + US915_BEACON_CHANNEL_STEPWIDTH ); + break; + } + case PHY_PING_SLOT_CHANNEL_DR: + { + phyParam.Value = US915_PING_SLOT_CHANNEL_DR; + break; + } + case PHY_PING_SLOT_NB_CHANNELS: + { + phyParam.Value = US915_BEACON_NB_CHANNELS; + break; + } + case PHY_SF_FROM_DR: + { + phyParam.Value = DataratesUS915[getPhy->Datarate]; + break; + } + case PHY_BW_FROM_DR: + { + phyParam.Value = RegionCommonGetBandwidth( getPhy->Datarate, BandwidthsUS915 ); + break; + } + default: + { + break; + } + } + +#endif /* REGION_US915 */ + return phyParam; +} + +void RegionUS915SetBandTxDone( SetBandTxDoneParams_t* txDone ) +{ +#if defined( REGION_US915 ) +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + RegionCommonSetBandTxDone( &RegionNvmGroup1->Bands[RegionNvmGroup2->Channels[txDone->Channel].Band], + txDone->LastTxAirTime, txDone->Joined, txDone->ElapsedTimeSinceStartUp ); +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + RegionCommonSetBandTxDone( &RegionBands[RegionNvmGroup2->Channels[txDone->Channel].Band], + txDone->LastTxAirTime, txDone->Joined, txDone->ElapsedTimeSinceStartUp ); +#endif /* REGION_VERSION */ +#endif /* REGION_US915 */ +} + +void RegionUS915InitDefaults( InitDefaultsParams_t* params ) +{ +#if defined( REGION_US915 ) + Band_t bands[US915_MAX_NB_BANDS] = + { + US915_BAND0 + }; + + switch( params->Type ) + { + case INIT_TYPE_DEFAULTS: + { + if( ( params->NvmGroup1 == NULL ) || ( params->NvmGroup2 == NULL ) ) + { + return; + } + + RegionNvmGroup1 = (RegionNvmDataGroup1_t*) params->NvmGroup1; + RegionNvmGroup2 = (RegionNvmDataGroup2_t*) params->NvmGroup2; + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + // Initialize 8 bit channel groups index + RegionNvmGroup1->JoinChannelGroupsCurrentIndex = 0; + + // Initialize the join trials counter + RegionNvmGroup1->JoinTrialsCounter = 0; + + // Default bands + memcpy1( ( uint8_t* )RegionNvmGroup1->Bands, ( uint8_t* )bands, sizeof( Band_t ) * US915_MAX_NB_BANDS ); +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + RegionBands = (Band_t*) params->Bands; + + // Initialize 8 bit channel groups index + RegionNvmGroup1->JoinChannelGroupsCurrentIndex = 0; + + // Initialize the join trials counter + RegionNvmGroup1->JoinTrialsCounter = 0; + + // Default bands + memcpy1( ( uint8_t* )RegionBands, ( uint8_t* )bands, sizeof( Band_t ) * US915_MAX_NB_BANDS ); +#endif /* REGION_VERSION */ + + // Default channels + for( uint8_t i = 0; i < US915_MAX_NB_CHANNELS - 8; i++ ) + { + // 125 kHz channels + RegionNvmGroup2->Channels[i].Frequency = 902300000 + i * 200000; + RegionNvmGroup2->Channels[i].DrRange.Value = ( DR_3 << 4 ) | DR_0; + RegionNvmGroup2->Channels[i].Band = 0; + } + for( uint8_t i = US915_MAX_NB_CHANNELS - 8; i < US915_MAX_NB_CHANNELS; i++ ) + { + // 500 kHz channels + RegionNvmGroup2->Channels[i].Frequency = 903000000 + ( i - ( US915_MAX_NB_CHANNELS - 8 ) ) * 1600000; + RegionNvmGroup2->Channels[i].DrRange.Value = ( DR_4 << 4 ) | DR_4; + RegionNvmGroup2->Channels[i].Band = 0; + } + + // Default ChannelsMask + /* ST_WORKAROUND_BEGIN: Hybrid mode */ +#if ( HYBRID_ENABLED == 1 ) + RegionNvmGroup2->ChannelsDefaultMask[0] = HYBRID_DEFAULT_MASK0; + RegionNvmGroup2->ChannelsDefaultMask[1] = HYBRID_DEFAULT_MASK1; + RegionNvmGroup2->ChannelsDefaultMask[2] = HYBRID_DEFAULT_MASK2; + RegionNvmGroup2->ChannelsDefaultMask[3] = HYBRID_DEFAULT_MASK3; + RegionNvmGroup2->ChannelsDefaultMask[4] = HYBRID_DEFAULT_MASK4; + RegionNvmGroup2->ChannelsDefaultMask[5] = 0x0000; +#else + RegionNvmGroup2->ChannelsDefaultMask[0] = 0xFFFF; + RegionNvmGroup2->ChannelsDefaultMask[1] = 0xFFFF; + RegionNvmGroup2->ChannelsDefaultMask[2] = 0xFFFF; + RegionNvmGroup2->ChannelsDefaultMask[3] = 0xFFFF; + RegionNvmGroup2->ChannelsDefaultMask[4] = 0x00FF; + RegionNvmGroup2->ChannelsDefaultMask[5] = 0x0000; +#endif /* HYBRID_ENABLED == 1 */ + /* ST_WORKAROUND_END */ + + // Copy channels default mask + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, RegionNvmGroup2->ChannelsDefaultMask, CHANNELS_MASK_SIZE ); + + // Copy into channels mask remaining + RegionCommonChanMaskCopy( RegionNvmGroup1->ChannelsMaskRemaining, RegionNvmGroup2->ChannelsMask, CHANNELS_MASK_SIZE ); + break; + } + case INIT_TYPE_RESET_TO_DEFAULT_CHANNELS: + { + // Intentional fallthrough + } + case INIT_TYPE_ACTIVATE_DEFAULT_CHANNELS: + { + // Copy channels default mask + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, RegionNvmGroup2->ChannelsDefaultMask, CHANNELS_MASK_SIZE ); + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + for( uint8_t i = 0; i < CHANNELS_MASK_SIZE; i++ ) +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + for( uint8_t i = 0; i < 6; i++ ) +#endif /* REGION_VERSION */ + { // Copy-And the channels mask + RegionNvmGroup1->ChannelsMaskRemaining[i] &= RegionNvmGroup2->ChannelsMask[i]; + } + break; + } + default: + { + break; + } + } +#endif /* REGION_US915 */ +} + +bool RegionUS915Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ) +{ +#if defined( REGION_US915 ) + switch( phyAttribute ) + { + case PHY_FREQUENCY: + { + return VerifyRfFreq( verify->Frequency ); + } +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + case PHY_TX_DR: + case PHY_DEF_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, US915_TX_MIN_DATARATE, US915_TX_MAX_DATARATE ); + } +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + 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 ); + } +#endif /* REGION_VERSION */ + 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; + } +#else + return false; +#endif /* REGION_US915 */ +} + +void RegionUS915ApplyCFList( ApplyCFListParams_t* applyCFList ) +{ +#if defined( REGION_US915 ) + // 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 ) + { + RegionNvmGroup2->ChannelsMask[chMaskItr] = (uint16_t) (0x00FF & applyCFList->Payload[cntPayload]); + RegionNvmGroup2->ChannelsMask[chMaskItr] |= (uint16_t) (applyCFList->Payload[cntPayload+1] << 8); + if( chMaskItr == 4 ) + { + RegionNvmGroup2->ChannelsMask[chMaskItr] = RegionNvmGroup2->ChannelsMask[chMaskItr] & CHANNELS_MASK_500KHZ_MASK; + } + // Set the channel mask to the remaining + RegionNvmGroup1->ChannelsMaskRemaining[chMaskItr] &= RegionNvmGroup2->ChannelsMask[chMaskItr]; + } +#endif /* REGION_US915 */ +} + +bool RegionUS915ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ) +{ +#if defined( REGION_US915 ) + 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( RegionNvmGroup2->ChannelsMask, chanMaskSet->ChannelsMaskIn, CHANNELS_MASK_SIZE ); + + RegionNvmGroup2->ChannelsDefaultMask[4] = RegionNvmGroup2->ChannelsDefaultMask[4] & CHANNELS_MASK_500KHZ_MASK; + RegionNvmGroup2->ChannelsDefaultMask[5] = 0x0000; + + for( uint8_t i = 0; i < CHANNELS_MASK_SIZE; i++ ) + { // Copy-And the channels mask + RegionNvmGroup1->ChannelsMaskRemaining[i] &= RegionNvmGroup2->ChannelsMask[i]; + } + break; + } + case CHANNELS_DEFAULT_MASK: + { + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, CHANNELS_MASK_SIZE ); + break; + } + default: + return false; + } + return true; +#else + return false; +#endif /* REGION_US915 */ +} + +void RegionUS915ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ) +{ +#if defined( REGION_US915 ) + uint32_t tSymbolInUs = 0; + + // Get the datarate, perform a boundary check + rxConfigParams->Datarate = MIN( datarate, US915_RX_MAX_DATARATE ); + rxConfigParams->Bandwidth = RegionCommonGetBandwidth( rxConfigParams->Datarate, BandwidthsUS915 ); + + tSymbolInUs = RegionCommonComputeSymbolTimeLoRa( DataratesUS915[rxConfigParams->Datarate], BandwidthsUS915[rxConfigParams->Datarate] ); + + RegionCommonComputeRxWindowParameters( tSymbolInUs, minRxSymbols, rxError, Radio.GetWakeupTime( ), &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); +#endif /* REGION_US915 */ +} + +bool RegionUS915RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) +{ +#if defined( REGION_US915 ) + int8_t dr = rxConfig->Datarate; + uint8_t maxPayload = 0; + int8_t phyDr = 0; + uint32_t frequency = rxConfig->Frequency; + + if( Radio.GetStatus( ) != RF_IDLE ) + { + return false; + } + + if( rxConfig->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 ); + + /* ST_WORKAROUND_BEGIN: Keep repeater feature */ + if( rxConfig->RepeaterSupport == true ) + { + maxPayload = MaxPayloadOfDatarateRepeaterUS915[dr]; + } + else + { + maxPayload = MaxPayloadOfDatarateUS915[dr]; + } + + Radio.SetMaxPayloadLength( MODEM_LORA, maxPayload + LORAMAC_FRAME_PAYLOAD_OVERHEAD_SIZE ); + /* ST_WORKAROUND_END */ + + /* ST_WORKAROUND_BEGIN: Print Rx config */ + RegionCommonRxConfigPrint(rxConfig->RxSlot, frequency, dr); + /* ST_WORKAROUND_END */ + + *datarate = (uint8_t) dr; + return true; +#else + return false; +#endif /* REGION_US915 */ +} + +bool RegionUS915TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ) +{ +#if defined( REGION_US915 ) + int8_t phyDr = DataratesUS915[txConfig->Datarate]; +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, RegionNvmGroup1->Bands[RegionNvmGroup2->Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, RegionNvmGroup2->ChannelsMask ); +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, RegionBands[RegionNvmGroup2->Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, RegionNvmGroup2->ChannelsMask ); +#endif /* REGION_VERSION */ + + uint32_t bandwidth = RegionCommonGetBandwidth( txConfig->Datarate, BandwidthsUS915 ); + int8_t phyTxPower = 0; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, US915_DEFAULT_MAX_ERP, 0 ); + + // Setup the radio frequency + Radio.SetChannel( RegionNvmGroup2->Channels[txConfig->Channel].Frequency ); + + Radio.SetTxConfig( MODEM_LORA, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 4000 ); + /* ST_WORKAROUND_BEGIN: Print Tx config */ + RegionCommonTxConfigPrint(RegionNvmGroup2->Channels[txConfig->Channel].Frequency, txConfig->Datarate); + /* ST_WORKAROUND_END */ + + // Setup maximum payload length of the radio driver + Radio.SetMaxPayloadLength( MODEM_LORA, txConfig->PktLen ); + + // Update time-on-air + *txTimeOnAir = GetTimeOnAir( txConfig->Datarate, txConfig->PktLen ); + + *txPower = txPowerLimited; + return true; +#else + return false; +#endif /* REGION_US915 */ +} + +uint8_t RegionUS915LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ) +{ + uint8_t status = 0x07; +#if defined( REGION_US915 ) + RegionCommonLinkAdrParams_t linkAdrParams = { 0 }; + uint8_t nextIndex = 0; + uint8_t bytesProcessed = 0; +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + uint16_t channelsMask[CHANNELS_MASK_SIZE] = { 0, 0, 0, 0, 0, 0 }; +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + uint16_t channelsMask[6] = { 0, 0, 0, 0, 0, 0 }; +#endif /* REGION_VERSION */ + GetPhyParams_t getPhy; + PhyParam_t phyParam; + RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams; + + // Initialize local copy of channels mask + RegionCommonChanMaskCopy( channelsMask, RegionNvmGroup2->ChannelsMask, CHANNELS_MASK_SIZE ); + + 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 comparison + 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 = RegionNvmGroup2->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( RegionNvmGroup2->ChannelsMask, channelsMask, 6 ); + + RegionNvmGroup1->ChannelsMaskRemaining[0] &= RegionNvmGroup2->ChannelsMask[0]; + RegionNvmGroup1->ChannelsMaskRemaining[1] &= RegionNvmGroup2->ChannelsMask[1]; + RegionNvmGroup1->ChannelsMaskRemaining[2] &= RegionNvmGroup2->ChannelsMask[2]; + RegionNvmGroup1->ChannelsMaskRemaining[3] &= RegionNvmGroup2->ChannelsMask[3]; + RegionNvmGroup1->ChannelsMaskRemaining[4] = RegionNvmGroup2->ChannelsMask[4]; + RegionNvmGroup1->ChannelsMaskRemaining[5] = RegionNvmGroup2->ChannelsMask[5]; + } + + // Update status variables + *drOut = linkAdrParams.Datarate; + *txPowOut = linkAdrParams.TxPower; + *nbRepOut = linkAdrParams.NbRep; + *nbBytesParsed = bytesProcessed; + +#endif /* REGION_US915 */ + return status; +} + +uint8_t RegionUS915RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ) +{ + uint8_t status = 0x07; +#if defined( REGION_US915 ) + + // 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 + } + +#endif /* REGION_US915 */ + return status; +} + +int8_t RegionUS915NewChannelReq( NewChannelReqParams_t* newChannelReq ) +{ + // Do not accept the request + return -1; +} + +int8_t RegionUS915TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ) +{ + // Do not accept the request + return -1; +} + +int8_t RegionUS915DlChannelReq( DlChannelReqParams_t* dlChannelReq ) +{ + // Do not accept the request + return -1; +} + +int8_t RegionUS915AlternateDr( int8_t currentDr, AlternateDrType_t type ) +{ +#if defined( REGION_US915 ) + // 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 ) + { + RegionNvmGroup1->JoinTrialsCounter++; + } + else + { + RegionNvmGroup1->JoinTrialsCounter--; + } + + if( RegionNvmGroup1->JoinTrialsCounter % 9 == 0 ) + { + // Use DR_4 every 9th times. + currentDr = DR_4; + } + else + { + currentDr = DR_0; + } + return currentDr; +#else + return -1; +#endif /* REGION_US915 */ +} + +LoRaMacStatus_t RegionUS915NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ) +{ +#if defined( REGION_US915 ) + uint8_t nbEnabledChannels = 0; + uint8_t nbRestrictedChannels = 0; + uint8_t enabledChannels[US915_MAX_NB_CHANNELS] = { 0 }; + RegionCommonIdentifyChannelsParam_t identifyChannelsParam; + RegionCommonCountNbOfEnabledChannelsParams_t countChannelsParams; + LoRaMacStatus_t status = LORAMAC_STATUS_NO_CHANNEL_FOUND; + + // Count 125kHz channels + if( RegionCommonCountChannels( RegionNvmGroup1->ChannelsMaskRemaining, 0, 4 ) == 0 ) + { // Reactivate default channels + RegionCommonChanMaskCopy( RegionNvmGroup1->ChannelsMaskRemaining, RegionNvmGroup2->ChannelsMask, 4 ); + + RegionNvmGroup1->JoinChannelGroupsCurrentIndex = 0; + } + // Check other channels + if( nextChanParams->Datarate >= DR_4 ) + { + if( ( RegionNvmGroup1->ChannelsMaskRemaining[4] & CHANNELS_MASK_500KHZ_MASK ) == 0 ) + { + RegionNvmGroup1->ChannelsMaskRemaining[4] = RegionNvmGroup2->ChannelsMask[4]; + } + } + + // Search how many channels are enabled + countChannelsParams.Joined = nextChanParams->Joined; + countChannelsParams.Datarate = nextChanParams->Datarate; + countChannelsParams.ChannelsMask = RegionNvmGroup1->ChannelsMaskRemaining; + countChannelsParams.Channels = RegionNvmGroup2->Channels; +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + countChannelsParams.Bands = RegionNvmGroup1->Bands; +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + countChannelsParams.Bands = RegionBands; +#endif /* REGION_VERSION */ + countChannelsParams.MaxNbChannels = US915_MAX_NB_CHANNELS; + countChannelsParams.JoinChannels = NULL; + + identifyChannelsParam.AggrTimeOff = nextChanParams->AggrTimeOff; + identifyChannelsParam.LastAggrTx = nextChanParams->LastAggrTx; + identifyChannelsParam.DutyCycleEnabled = nextChanParams->DutyCycleEnabled; + identifyChannelsParam.MaxBands = US915_MAX_NB_BANDS; + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) + identifyChannelsParam.ElapsedTimeSinceStartUp = nextChanParams->ElapsedTimeSinceStartUp; + identifyChannelsParam.LastTxIsJoinRequest = nextChanParams->LastTxIsJoinRequest; + identifyChannelsParam.ExpectedTimeOnAir = GetTimeOnAir( nextChanParams->Datarate, nextChanParams->PktLen ); + + identifyChannelsParam.CountNbOfEnabledChannelsParam = &countChannelsParams; +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) + identifyChannelsParam.CountNbOfEnabledChannelsParam = &countChannelsParams; + + identifyChannelsParam.ElapsedTimeSinceStartUp = nextChanParams->ElapsedTimeSinceStartUp; + identifyChannelsParam.LastTxIsJoinRequest = nextChanParams->LastTxIsJoinRequest; + identifyChannelsParam.ExpectedTimeOnAir = GetTimeOnAir( nextChanParams->Datarate, nextChanParams->PktLen ); +#endif /* REGION_VERSION */ + + status = RegionCommonIdentifyChannels( &identifyChannelsParam, aggregatedTimeOff, enabledChannels, + &nbEnabledChannels, &nbRestrictedChannels, time ); + + if( status == LORAMAC_STATUS_OK ) + { + 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( RegionBaseUSComputeNext125kHzJoinChannel( ( uint16_t* ) RegionNvmGroup1->ChannelsMaskRemaining, + &RegionNvmGroup1->JoinChannelGroupsCurrentIndex, channel ) == LORAMAC_STATUS_PARAMETER_INVALID ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + } + // 500kHz Channels (64 - 71) DR4 + else + { + // Choose the next available channel + uint8_t i = 0; + while( ( ( RegionNvmGroup1->ChannelsMaskRemaining[4] & CHANNELS_MASK_500KHZ_MASK ) & ( 1 << i ) ) == 0 ) + { + i++; + } + *channel = 64 + i; + } + } + + // Disable the channel in the mask + RegionCommonChanDisable( RegionNvmGroup1->ChannelsMaskRemaining, *channel, US915_MAX_NB_CHANNELS ); + } + return status; +#else + return LORAMAC_STATUS_NO_CHANNEL_FOUND; +#endif /* REGION_US915 */ +} + +LoRaMacStatus_t RegionUS915ChannelAdd( ChannelAddParams_t* channelAdd ) +{ + return LORAMAC_STATUS_PARAMETER_INVALID; +} + +bool RegionUS915ChannelsRemove( ChannelRemoveParams_t* channelRemove ) +{ + return LORAMAC_STATUS_PARAMETER_INVALID; +} + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +void RegionUS915SetContinuousWave( ContinuousWaveParams_t* continuousWave ) +{ +#if defined( REGION_US915 ) + int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, RegionNvmGroup1->Bands[RegionNvmGroup2->Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, RegionNvmGroup2->ChannelsMask ); + int8_t phyTxPower = 0; + uint32_t frequency = RegionNvmGroup2->Channels[continuousWave->Channel].Frequency; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, US915_DEFAULT_MAX_ERP, 0 ); + + Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout ); +#endif /* REGION_US915 */ +} +#endif /* REGION_VERSION */ + +uint8_t RegionUS915ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) +{ +#if defined( REGION_US915 ) + int8_t datarate = DatarateOffsetsUS915[dr][drOffset]; + + if( datarate < 0 ) + { + datarate = DR_0; + } + return datarate; +#else + return 0; +#endif /* REGION_US915 */ +} + +void RegionUS915RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ) +{ +#if defined( REGION_US915 ) + 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; +#endif /* REGION_US915 */ +} diff --git a/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionUS915.h b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionUS915.h new file mode 100644 index 0000000..98b63d9 --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionUS915.h @@ -0,0 +1,492 @@ +/*! + * \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. + * \{ + */ +/** + ****************************************************************************** + * + * Portions COPYRIGHT 2020 STMicroelectronics + * + * @file RegionUS915.h + * @author MCD Application Team + * @brief Region definition for US915 + ****************************************************************************** + */ +#ifndef __REGION_US915_H__ +#define __REGION_US915_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "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 + +/* ST_WORKAROUND_BEGIN: Certification requirement */ +/*! + * Minimal Tx output power that can be used by the node + */ +#if ( defined(CERTIF_LORAWAN_VERSION) && (CERTIF_LORAWAN_VERSION == 102) ) +#define US915_MIN_TX_POWER TX_POWER_10 +#else +#define US915_MIN_TX_POWER TX_POWER_14 +#endif /* CERTIF_LORAWAN_VERSION */ +/* ST_WORKAROUND_END */ + +/*! + * 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 + +/*! + * Enabled or disabled the duty cycle + */ +#define US915_DUTY_CYCLE_ENABLED 0 + +/*! + * Maximum RX window duration + */ +#define US915_MAX_RX_WINDOW 3000 + +/*! + * 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 + +/*! + * Default uplink dwell time configuration + */ +#define US915_DEFAULT_UPLINK_DWELL_TIME 0 + +/* + * CLASS B + */ +/*! + * Beacon frequency + */ +#define US915_BEACON_CHANNEL_FREQ 923300000 + +/*! + * Beacon frequency channel stepwidth + */ +#define US915_BEACON_CHANNEL_STEPWIDTH 600000 + +/*! + * Ping slot channel frequency + */ +#define US915_PING_SLOT_CHANNEL_FREQ 923300000 + +/*! + * 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 + */ +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +#define US915_RFU1_SIZE 5 +#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 )) +#define US915_RFU1_SIZE 4 +#endif /* REGION_VERSION */ + +/*! + * Size of RFU 2 field + */ +#define US915_RFU2_SIZE 3 + +/*! + * Datarate of the beacon channel + */ +#define US915_BEACON_CHANNEL_DR DR_8 + +/*! + * Bandwidth 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 + * Band = { DutyCycle, TxMaxPower, LastBandUpdateTime, LastMaxCreditAssignTime, TimeCredits, MaxTimeCredits, ReadyForTransmission } + */ +#define US915_BAND0 { 1, US915_MAX_TX_POWER, 0, 0, 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 +}; + +/* ST_WORKAROUND_BEGIN: Keep repeater feature */ +/*! + * 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 }; +/* ST_WORKAROUND_END */ + +/*! + * \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] params Sets the initialization type. + */ +void RegionUS915InitDefaults( InitDefaultsParams_t* params ); + +/*! + * \brief Verifies a parameter. + * + * \param [in] verify Pointer to the function parameters. + * + * \param [in] phyAttribute 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. + * + * \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 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. + */ +int8_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. + */ +int8_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 Searches and set the next random available channel + * + * \param [in] nextChanParams pointer of selected channel parameters + * + * \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 ); + +#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 )) +/*! + * \brief Sets the radio into continuous wave mode. + * + * \param [IN] continuousWave Pointer to the function parameters. + */ +void RegionUS915SetContinuousWave( ContinuousWaveParams_t* continuousWave ); +#endif /* REGION_VERSION */ + +/*! + * \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 + * + * \param [out] outDr Datarate used to receive the beacon + */ +void RegionUS915RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ); + +/*! \} defgroup REGIONUS915 */ + +#ifdef __cplusplus +} +#endif + +#endif // __REGION_US915_H__ diff --git a/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionVersion.h b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionVersion.h new file mode 100644 index 0000000..cb7d711 --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/Region/RegionVersion.h @@ -0,0 +1,48 @@ +/** + ****************************************************************************** + * @file RegionVersion.h + * @author MCD Application Team + * @brief Identifies the version of Regional Parameters + ****************************************************************************** + * @attention + * + * Copyright (c) 2020(-2021) STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ +#ifndef __REGION_VERSION_H__ +#define __REGION_VERSION_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "../LoRaMacVersion.h" + +#ifndef REGION_VERSION +#if (defined(LORAMAC_VERSION) && (LORAMAC_VERSION == 0x01000300)) +/*! + * @brief Regional parameters version definition. + * @note RP001-1.0.3 : + */ +#define REGION_VERSION 0x01010003 +#elif (defined(LORAMAC_VERSION) && (LORAMAC_VERSION == 0x01000400)) +/*! + * @brief Regional parameters version definition. + * @note RP002-1.0.1 : https://lora-alliance.org/resource_hub/rp2-101-lorawan-regional-parameters-2/ + */ +#define REGION_VERSION 0x02010001 +#endif /* LORAMAC_VERSION */ +#endif + +#ifdef __cplusplus +} +#endif + +#endif // __REGION_VERSION_H__ diff --git a/src/STM32CubeWL/LoRaWAN/Mac/secure-element-nvm.h b/src/STM32CubeWL/LoRaWAN/Mac/secure-element-nvm.h new file mode 100644 index 0000000..73bac3e --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/secure-element-nvm.h @@ -0,0 +1,145 @@ +/*! + * \file secure-element-nvm.h + * + * \brief Secure Element non-volatile data. + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \addtogroup SECUREELEMENT + * + * \{ + * + */ +/** + ****************************************************************************** + * + * Portions COPYRIGHT 2020 STMicroelectronics + * + * @file secure-element-nvm.h + * @author MCD Application Team + * @brief Secure Element non-volatile data. + ****************************************************************************** + */ +#ifndef __SECURE_ELEMENT_NVM_H__ +#define __SECURE_ELEMENT_NVM_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "LoRaMacTypes.h" +#include "../../../BSP/lorawan_conf.h" /* LORAWAN_KMS */ +#if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) +#else /* LORAWAN_KMS == 1 */ +#include "kms_if.h" +#endif /* LORAWAN_KMS */ + +/*! + * Secure-element keys size in bytes + */ +#define SE_KEY_SIZE 16 + +/*! + * Secure-element EUI size in bytes + */ +#define SE_EUI_SIZE 8 + +/* ST_WORKAROUND_BEGIN: Dynamic number of keys definition */ +/*! + * Number of supported crypto keys for the soft-se + */ +#if ( USE_LRWAN_1_1_X_CRYPTO == 1 ) +#if ( LORAMAC_MAX_MC_CTX > 3 ) +#define NUM_OF_KEYS 23UL +#elif ( LORAMAC_MAX_MC_CTX > 2 ) +#define NUM_OF_KEYS 20UL +#elif ( LORAMAC_MAX_MC_CTX > 1 ) +#define NUM_OF_KEYS 17UL +#else /* LORAMAC_MAX_MC_CTX == 0 */ +#define NUM_OF_KEYS 14UL +#endif /* LORAMAC_MAX_MC_CTX */ +#else /* USE_LRWAN_1_1_X_CRYPTO == 0 */ +#if ( LORAMAC_MAX_MC_CTX > 3 ) +#define NUM_OF_KEYS 19UL +#elif ( LORAMAC_MAX_MC_CTX > 2 ) +#define NUM_OF_KEYS 16UL +#elif ( LORAMAC_MAX_MC_CTX > 1 ) +#define NUM_OF_KEYS 13UL +#else /* LORAMAC_MAX_MC_CTX == 0 */ +#define NUM_OF_KEYS 10UL +#endif /* LORAMAC_MAX_MC_CTX */ +#endif /* USE_LRWAN_1_1_X_CRYPTO */ +/* ST_WORKAROUND_END */ + +/*! + * Key structure definition for the soft-se + */ +typedef struct sKey +{ + /*! + * Key identifier + */ + KeyIdentifier_t KeyID; +#if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) + /*! + * Key value + */ + uint8_t KeyValue[SE_KEY_SIZE]; +#else /* LORAWAN_KMS == 1 */ + /*! + * Key object index in the above list + */ + CK_OBJECT_HANDLE Object_Index; +#endif /* LORAWAN_KMS */ +} Key_t; + +typedef struct sSecureElementNvCtx +{ + /*! + * DevEUI storage + */ + uint8_t DevEui[SE_EUI_SIZE]; + /*! + * Join EUI storage + */ + uint8_t JoinEui[SE_EUI_SIZE]; + /*! + * The key list is required for the soft-se only. All other secure-elements + * handle the storage on their own. + */ + Key_t KeyList[NUM_OF_KEYS]; + /*! + * CRC32 value of the SecureElement data structure. + */ + uint32_t Crc32; +} SecureElementNvmData_t; + +/*! \} addtogroup SECUREELEMENT */ + +#ifdef __cplusplus +} +#endif + +#endif // __SECURE_ELEMENT_NVM_H__ diff --git a/src/STM32CubeWL/LoRaWAN/Mac/secure-element.h b/src/STM32CubeWL/LoRaWAN/Mac/secure-element.h new file mode 100644 index 0000000..e549fc8 --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Mac/secure-element.h @@ -0,0 +1,308 @@ +/*! + * \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 + * + * \{ + * + */ +/** + ****************************************************************************** + * + * Portions COPYRIGHT 2020 STMicroelectronics + * + * @file secure-element.h + * @author MCD Application Team + * @brief Secure Element driver API + ****************************************************************************** + */ +#ifndef __SECURE_ELEMENT_H__ +#define __SECURE_ELEMENT_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "LoRaMacCrypto.h" +#include "secure-element-nvm.h" +#include "LoRaMacVersion.h" + +/*! + * 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, + /*! + * Failed to encrypt + */ + SECURE_ELEMENT_FAIL_ENCRYPT, +}SecureElementStatus_t; + +/* ST_WORKAROUND_BEGIN: Add unique ID callback as input parameter */ +/*! + * \brief get the board 64 bits unique ID + * + * \param [out] id unique + */ +typedef void ( *SecureElementGetUniqueId )(uint8_t *id); + +/*! + * Initialization of Secure Element driver + * + * \param [in] nvm - Pointer to the non-volatile memory data + * structure. + * \param [in] seGetUniqueId - Get unique ID callback + * \retval - Status of the operation + */ +SecureElementStatus_t SecureElementInit( SecureElementNvmData_t* nvm, SecureElementGetUniqueId seGetUniqueId ); +/* ST_WORKAROUND_END */ + +/*! + * Print Root and Session keys if available + * + * \retval - Status of the operation + */ +SecureElementStatus_t SecureElementPrintKeys( void ); + +/*! + * Print OTAA Session keys if available + * + * \retval - Status of the operation + */ +SecureElementStatus_t SecureElementPrintSessionKeys( void ); + +/* ST_WORKAROUND_BEGIN: Add KMS specific functions */ +#if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) +/*! + * Gets key item from key list. + * + * \param [in] keyID - Key identifier + * \param [out] keyItem - Key item reference + * \retval - Status of the operation + */ +#else +/*! + * Gets key item from key list. + * + * \param [in] keyID - Key identifier + * \param [out] extractable_key - Key item pointer + * \retval - Status of the operation + */ +#endif /* LORAWAN_KMS */ +#if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) +SecureElementStatus_t SecureElementGetKeyByID( KeyIdentifier_t keyID, Key_t **keyItem); +#else +SecureElementStatus_t SecureElementGetKeyByID( KeyIdentifier_t keyID, uint8_t* extractable_key ); +#endif /* LORAWAN_KMS */ + +/*! + * Remove previously generated dynamic keys with "label" from memory + * + * \param [in] keyID - Key identifier + * \param [out] key_label - string of char to identifying targetKeyID label + * \retval - Status of the operation + */ +SecureElementStatus_t SecureElementDeleteDynamicKeys( KeyIdentifier_t keyID, uint32_t *key_label ); + +/*! + * Sets a the KMS object handler for a given keyID (reserved to Kms) + * + * \param [in] keyID - Key identifier + * \param [in] keyIndex - Key index value + * \retval - Status of the operation + */ +SecureElementStatus_t SecureElementSetObjHandler( KeyIdentifier_t keyID, uint32_t keyIndex ); +/* ST_WORKAROUND_END */ + +/*! + * 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] 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( uint8_t* input, KeyIdentifier_t rootKeyID, KeyIdentifier_t targetKeyID ); + +/*! + * Process JoinAccept message. + * + * \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] devNonce - Random value generated by Network Server to prevent replay attacks with previous Join session params + * \param [in] encJoinAccept - Received encrypted JoinAccept message + * \param [in] encJoinAcceptSize - Received encrypted JoinAccept message Size + * \param [out] decJoinAccept - Decrypted and validated JoinAccept message + * \param [out] versionMinor - Detected LoRaWAN specification version minor field. + * - 0 -> LoRaWAN 1.0.x + * - 1 -> LoRaWAN 1.1.x + * \retval - Status of the operation + */ +SecureElementStatus_t SecureElementProcessJoinAccept( JoinReqIdentifier_t joinReqType, uint8_t* joinEui, + uint16_t devNonce, uint8_t* encJoinAccept, + uint8_t encJoinAcceptSize, uint8_t* decJoinAccept, + uint8_t* versionMinor ); + +#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) +/*! + * Generates a random number + * + * \param[OUT] randomNum - 32 bit random number + * \retval - Status of the operation + */ +SecureElementStatus_t SecureElementRandomNumber( uint32_t* randomNum ); +#endif /* LORAMAC_VERSION */ + +/*! + * Sets the DevEUI + * + * \param [in] devEui - Pointer to the 8-byte devEUI + * \retval - Status of the operation + */ +SecureElementStatus_t SecureElementSetDevEui( uint8_t* devEui ); + +/*! + * Gets the DevEUI + * + * \retval - Pointer to the 8-byte devEUI + */ +uint8_t* SecureElementGetDevEui( void ); + +/*! + * Sets the JoinEUI + * + * \param [in] joinEui - Pointer to the 8-byte joinEui + * \retval - Status of the operation + */ +SecureElementStatus_t SecureElementSetJoinEui( uint8_t* joinEui ); + +/*! + * Gets the DevEUI + * + * \retval - Pointer to the 8-byte joinEui + */ +uint8_t* SecureElementGetJoinEui( void ); + +/*! + * Sets the pin + * + * \param [in] pin - Pointer to the 4-byte pin + * \retval - Status of the operation + */ +SecureElementStatus_t SecureElementSetPin( uint8_t* pin ); + +/*! + * Gets the Pin + * + * \retval - Pointer to the 4-byte pin + */ +uint8_t* SecureElementGetPin( void ); + +/*! \} defgroup SECUREELEMENT */ + +#ifdef __cplusplus +} +#endif + +#endif // __SECURE_ELEMENT_H__ diff --git a/src/STM32CubeWL/LoRaWAN/Release_Notes.html b/src/STM32CubeWL/LoRaWAN/Release_Notes.html new file mode 100644 index 0000000..a6d4932 --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Release_Notes.html @@ -0,0 +1,462 @@ + + + + + + + Release Notes for LoRaWAN Middleware + + + + + + +
+
+
+

Release Notes for

+

LoRaWAN Middleware

+

Copyright © 2020 STMicroelectronics
+

+ +
+

Purpose

+

This Middleware provides the LoRaWAN Stack. This stack is based on the LoRaMac from Semtech/StackForce (see readme.md)

+

This driver is composed of 5 directories:

+
    +
  • Utilities : It provides LoRaWAN utilities
  • +
  • Mac : It provides LoRaWAN stack and regions algorithm
  • +
  • LmHandler : It provides LoRaWAN stack interface to the upper layer
  • +
  • Crypto : It manages LoRaWAN cryptographic, optionally interfacing the KMS
  • +
  • Conf : Configuration template files
  • +
+
+
+

Update History

+
+ +
+

Main Changes

+
    +
  • Feature: Integration of LoRaWAN v1.0.4 from LoRa Mac Semtech/StackForce develop branch (31-May-2021 commits, version 4.5.2)
  • +
  • Feature: NVM Context store/restore implementation with Flash usage and SRAM2 retention (required by Certification code)
  • +
  • Feature: US915/CN470/AU915 HYBRID Frequency sub-band can be overloaded in lorawan_conf.h
  • +
  • Fix: Negative sensor Temperature
  • +
  • Fix: LmHandlerSend function returns an error when the App data is not sent because piggyback FOpts MAC commands is set as priority in the uplink payload
  • +
  • Fix: FragDecoder fragment max size update to support upper DRs
  • +
  • Licensing update: New way to declare licenses
  • +
  • Release Notes update
  • +
+

Known limitations:

+

None

+
+
+
+ +
+

Main Changes

+
    +
  • Implements LoRa Mac from Semtech/StackForce develop branch (18-Jan-2021 commits, version 4.4.7)
  • +
  • SecureElementDeleteDerivedKeys dynamic implementation in using KMS APIs
  • +
  • Cleanup NVM key attributes with KMS default values definition
  • +
  • Printable Keys (Root and derived) with KEY_EXTRACTABLE
  • +
  • Remove GCCv9 compiler warnings
  • +
  • Fix SetAppEUI/SetDevEUI functions
  • +
  • Add LinkCheckReq and DeviceTimeReq support
  • +
  • Fragmentation processing move to application layer
  • +
  • Release Notes update
  • +
+

Known limitations:

+

None

+
+
+
+ +
+

Main Changes

+
    +
  • Multiple tools write to the same file’ if AES + LoRaWAN + option .c/.h
  • +
  • Update file-license
  • +
  • remove KMS init from LoRaWAN Middleware (move to application part)
  • +
  • Update Key List usage with a reworked init part: set a static const + memcpy to prevent a Stop/Restart of LoRaMAC
  • +
  • Release Notes update
  • +
+

Known limitations:

+

None

+
+
+
+ +
+

Main Changes

+
    +
  • Implements LoRa Mac from Semtech/StackForce develop branch (26-May-2020 commits, version 4.4.4)
  • +
  • Fixed SetLoRaSymbNumTimeout. RX Continuous bug on ClassC
  • +
  • revert AU915 regional parameters 1.0.3 errata feature
  • +
  • Remove LoRaWAN 1.1 + MC Group[1..3] Keys
  • +
  • Add missing ping_slot_nb_channels case to getPhy regions (hopping feature)
  • +
  • RepeaterSupport feature set back
  • +
  • LmHandlerSend error codes implementation
  • +
+

Known limitations:

+

None

+
+
+
+ +
+

Main Changes

+
    +
  • Moves the contents of folder Patterns/Advanced/ to LmHandler/
  • +
  • Imports some features from Semtech/StackForce develop branch: +
      +
    • Remove repeater support. Will be removed from future Regional Parameters specifications.
    • +
    • Add Ping_Slot channel freq to prevent the offset between the RU864 Beacon and Ping_Slot frequencies
    • +
    • Update of TimeOnAir calculation
    • +
    • Update of CryptoJoinAccept with fix of security vulnerabilities
    • +
    • Fix uplink messages burst with ClassB usage
    • +
  • +
  • New LmHandler services addition: Firmware Management and Data Distribution
  • +
  • Fix some Keil-related issues
  • +
  • Update files license and copyright year
  • +
+

Known limitations:

+
    +
  • IN865 ClassB beacon reception
  • +
+
+
+
+ +
+

Main Changes

+
    +
  • readme.md update to version 4.4.3
  • +
+

Known limitations:

+

None

+
+
+
+ +
+

Main Changes

+
    +
  • Implements LoRa Mac from Semtech/StackForce develop branch (17-Dec-2019 commits, version 4.4.3)
  • +
  • Patterns: +
      +
    • integration of Semtech LmHandler
    • +
    • ST specific certif features factorization
    • +
    • rearchitecture : removal of lora.c
    • +
    • new services addition: compliance, clock sync, multicast & fragmentation
    • +
  • +
  • KMS integration done in Crypto & LmHandler folders (mbed-crypto called)
  • +
+

Known limitations:

+
    +
  • IN865 ClassB beacon reception
  • +
+
+
+
+ +
+

Main Changes

+
    +
  • 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
    • +
  • +
+

Known limitations:

+

None

+
+
+
+ +
+

Main Changes

+ +

Known limitations:

+

None

+
+
+
+ +
+

Main Changes

+
    +
  • Trace logging & LoRa Mac version display updates
  • +
  • Some function prototypes added
  • +
+

Known limitations:

+

None

+
+
+
+ +
+

Main Changes

+
    +
  • implements LoRa Mac 4.4.2-rc.5 from Semtech/StackForce develop branch
  • +
+

Known limitations:

+

None

+
+
+
+ +
+

Main Changes

+
    +
  • 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
  • +
+

Known limitations:

+

None

+
+
+
+ +
+

Main Changes

+
    +
  • Implements LoRa Mac 4.4.1 from Semtech/StackForce
  • +
+

Known limitations:

+

None

+
+
+
+ +
+

Main Changes

+
    +
  • Bug fix in McpsIndication related to downlink Application data buffer
  • +
+

Known limitations:

+

None

+
+
+
+ +
+

Main Changes

+ +

Known limitations:

+

None

+
+
+
+ +
+

Main Changes

+
    +
  • 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
  • +
+

Known limitations:

+

None

+
+
+
+ +
+

Main Changes

+
    +
  • 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.
  • +
+

Known limitations:

+

None

+
+
+
+ +
+

Main Changes

+
    +
  • Implements LoRa Mac 4.4.0 from Semtech/StackForce from the develop branchr
  • +
+

Known limitations:

+

None

+
+
+
+ +
+

Main Changes

+
    +
  • Read date between 2 successive read time to make sure date is ok
  • +
+

Known limitations:

+

None

+
+
+
+ +
+

Main Changes

+
    +
  • 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
  • +
+

Known limitations:

+

None

+
+
+
+ +
+

Main Changes

+
    +
  • Implements LoRa Mac 4.3.0 from Semtech/StackForce
  • +
+

Known limitations:

+

None

+
+
+
+ +
+

Main Changes

+
    +
  • First V1.0.0 customized version for STM32Cube solution.
  • +
  • Commissioning_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 Commissioning.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 +
      +
    • .c : replace floating exponent e3 and e6 by int number
    • +
    • .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
  • +
+

Known limitations:

+

None

+
+
+
+
+
+
+
+

For complete documentation on STM32WLxx, visit: www.st.com/stm32wl

+

This release note uses up to date web standards and, for this reason, should not be opened with Internet Explorer but preferably with popular browsers such as Google Chrome, Mozilla Firefox, Opera or Microsoft Edge.

+
+

Info

+
+
+
+ + diff --git a/src/STM32CubeWL/LoRaWAN/Utilities/utilities.c b/src/STM32CubeWL/LoRaWAN/Utilities/utilities.c new file mode 100644 index 0000000..23e560d --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Utilities/utilities.c @@ -0,0 +1,159 @@ +/*! + * \file utilities.c + * + * \brief Helper functions implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + */ +/** + ****************************************************************************** + * + * Portions COPYRIGHT 2020 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 + +// CRC32 reversed polynomial 0xEDB88320 +static const uint32_t reversedPolynom = 0xEDB88320; + +static uint32_t next = 1; + +static int32_t rand1( void ); + +static 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 '?'; + } +} + +uint32_t Crc32( uint8_t *buffer, uint16_t length ) +{ + // CRC initial value + uint32_t crc = 0xFFFFFFFF; + + if( buffer == NULL ) + { + return 0; + } + + for( uint16_t i = 0; i < length; ++i ) + { + crc ^= ( uint32_t )buffer[i]; + for( uint16_t i = 0; i < 8; i++ ) + { + crc = ( crc >> 1 ) ^ ( reversedPolynom & ~( ( crc & 0x01 ) - 1 ) ); + } + } + + return ~crc; +} + +uint32_t Crc32Init( void ) +{ + return 0xFFFFFFFF; +} + +uint32_t Crc32Update( uint32_t crcInit, uint8_t *buffer, uint16_t length ) +{ + // CRC initial value + uint32_t crc = crcInit; + + if( buffer == NULL ) + { + return 0; + } + + for( uint16_t i = 0; i < length; ++i ) + { + crc ^= ( uint32_t )buffer[i]; + for( uint16_t i = 0; i < 8; i++ ) + { + crc = ( crc >> 1 ) ^ ( reversedPolynom & ~( ( crc & 0x01 ) - 1 ) ); + } + } + return crc; +} + +uint32_t Crc32Finalize( uint32_t crc ) +{ + return ~crc; +} diff --git a/src/STM32CubeWL/LoRaWAN/Utilities/utilities.h b/src/STM32CubeWL/LoRaWAN/Utilities/utilities.h new file mode 100644 index 0000000..8c38f39 --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/Utilities/utilities.h @@ -0,0 +1,216 @@ +/*! + * \file utilities.h + * + * \brief Helper functions implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + */ +/** + ****************************************************************************** + * + * Portions COPYRIGHT 2020 STMicroelectronics + * + * @file utilities.h + * @author MCD Application Team + * @brief Helper functions implementation + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __UTILITIES_H__ +#define __UTILITIES_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "../../../BSP/utilities_conf.h" + +/* Exported types ------------------------------------------------------------*/ +/*! + * LMN (LoRaMac-node) status + */ +typedef enum LmnStatus_e +{ + LMN_STATUS_ERROR = 0, + LMN_STATUS_OK = !LMN_STATUS_ERROR +} LmnStatus_t; + +/* Exported constants --------------------------------------------------------*/ +/* External variables --------------------------------------------------------*/ +/* Exported macros -----------------------------------------------------------*/ +/* Defines -------------------------------------------------------------------*/ +/*! + * \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 + +/* ST_WORKAROUND_BEGIN: Add global ceiling div macro */ +/** + * \brief Calculates ceiling( X / N ) + * + * \param [in] X numerator + * \param [in] N denominator + * + */ +#ifndef DIVC +#define DIVC( X, N ) ( ( ( X ) + ( N ) - 1 ) / ( N ) ) +#endif +/* ST_WORKAROUND_END */ + +/*! + * \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 ) + +/*! + * Version + */ +typedef union Version_u +{ + struct Version_s + { + uint8_t Revision; + uint8_t Patch; + uint8_t Minor; + uint8_t Major; + }Fields; + uint32_t Value; +}Version_t; + +/*! + * \brief Initializes the pseudo random generator initial value + * + * \param [in] seed Pseudo random generator initial value + */ +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 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 + */ +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 + */ +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 + */ +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 ); + +/*! + * \brief Computes a CCITT 32 bits CRC + * + * \param [in] buffer Data buffer used to compute the CRC + * \param [in] length Data buffer length + * + * \retval crc The computed buffer of length CRC + */ +uint32_t Crc32( uint8_t *buffer, uint16_t length ); + +/*! + * \brief Computes the initial value of the CCITT 32 bits CRC. This function + * can be used with functions \ref Crc32Update and \ref Crc32Finalize. + * + * \retval crc Initial crc value. + */ +uint32_t Crc32Init( void ); + +/*! + * \brief Updates the value of the crc value. + * + * \param [in] crcInit Previous or initial crc value. + * \param [in] buffer Data pointer. + * \param [in] length Length of the data. + * + * \retval crc Updated crc value. + */ +uint32_t Crc32Update( uint32_t crcInit, uint8_t *buffer, uint16_t length ); + +/*! + * \brief Finalizes the crc value after the calls to \ref Crc32Update. + * + * \param [in] crc Recent crc value. + * + * \retval crc Updated crc value. + */ +uint32_t Crc32Finalize( uint32_t crc ); + +/* ST_WORKAROUND: Moved CRITICAL_SECTION_ macro from utilities to *_conf.h header files */ +#ifdef __cplusplus +} +#endif + +#endif // __UTILITIES_H__ diff --git a/src/STM32CubeWL/LoRaWAN/_htmresc/Add button.svg b/src/STM32CubeWL/LoRaWAN/_htmresc/Add button.svg new file mode 100644 index 0000000..dc4a222 --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/_htmresc/Add button.svg @@ -0,0 +1,2 @@ + + diff --git a/src/STM32CubeWL/LoRaWAN/_htmresc/Update.svg b/src/STM32CubeWL/LoRaWAN/_htmresc/Update.svg new file mode 100644 index 0000000..f88381f --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/_htmresc/Update.svg @@ -0,0 +1,2 @@ + + diff --git a/src/STM32CubeWL/LoRaWAN/_htmresc/favicon.png b/src/STM32CubeWL/LoRaWAN/_htmresc/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..06713eec4974e141c6e9b4156d34e61e89f282ca GIT binary patch literal 4126 zcmV+(5aI8MP)ZIGU@QfPy~$kHP}Vz9c$a)g>fT6hB^OaK4>c|MGS0000HbW%=J|NsC0|NsC0 z|NsC0|NsC0041%NVgLXD!bwCyRCwCllie1CAP9sJ&9ooo{hxLjw5@YC+xxf~%TE}{ zNd5%935b--eKa7{Q8)vZ;eI6pGFH>rL)887WOA={e(b_&0g-g6to#Hm2B3vqWV>1u z@z7*I(bdWdx-Y=OT@|og)oZEgAbc7ep|Gf{$7j1Oa#T&r4>0xv(+}tR_4biWa z1Q-BfcsgeLIJPbT0000rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z000F^NklQWRUVq~pvFslpiNq83Yr*1(ELa!!kppKfxQKEkPzz^I>0W)!71xam z(B64`)5a~kHf9SBOp*Wvo{xA*e4|Kv3g6TCo+fF8q^C$|g>#NlXyY$%lmkmCh$x1Z zFbJ_hiDG`37w>KPVB83d7E3>R@-M9$`|}|QFF}1qSk!nanPdVd3K38edo3y+D*=Uo zfOWCwj(F@GSjPG&B<0O;H!Zm0g>eDeK09{b@xB};mEy; z;Gp5H_Cr65qKM1uYIu6x&16!Ei=BHfJLe)1IUnFqc6d#D=ZVQmV9C73vy1=x$SK;s z=*CYZP+Ei1D5Vjl#u88v5dv#jId?iu)8mM}{mD`GcMXtAC5ap?ZoKr&iYuqTKHe$t zTR%R$ZZwxec?l+0r_LIVbe-mzH+D0*ZYsu4BE~~JA2A+AD?BAArO=g8ZfvXtU~o9c zZ(Bd-RFK5jh*DvM1v6^41HB@0K0qn7E8E&Xz1mk6hvmx?*|WB_H!dcO;5R$=<4b~s z5zr3N4zxlkMKOrDeny(PGpEM6^hGyc7lhg>MWtM!af*pn%&q_PxI(n^(-Zd{ICPAp z(WE@Zz5|EZyt{MEDy&l5$Cba^zU!9k&;Vs7lk$@o02LOwb#e2{#3%Cn2>kQF(RKUU z+tW!;FT0@d+TX^L;>@3RytlTP&ts$pqA}TwENBlg9?$-D zF9Sn4URZx8)hVBw<~NY=h3=so7{jEhG%Zc_0QBSvY~K4BS|W5MAem5XSb6m}VDN$f zy+b3n9qjIJoHM5pqYa`IPH9AIoM=!AE1Er>r|3E}#Jq-S)cTrfD&TZjAuN@+V}3mi zlhOce+q<8>Y?i93G=*Z3Web`ri!Q%p%LR*(bB?;|)B`&=J&ab0Z(UKpbzyZV5o*#& z0FLzzaI$v*-#WB)+`n>BoJ*1AwU0XSu;@w&Hu1WYXVR#!8itC%68CzMeiXhL#`9W$9J30NB-Wg!ex`hBlg9F5u@&o4>hd`TWPv z{r{JbyvQGZzbyvPS}zAifbhF49z~X|@9v|!=No>qsF|P~vf=g>7#%0yk<U?Ha8*Z4HW>#8w>z$A3 z>^uQlk;$a-6CQ(uc@RM)Cbss!xuPVl2@c1u-5tEUw}U8OfP_J+QYhfu$B<0Cj3xm7 c?*aZZ00ulYFs-m=@&Et;07*qoM6N<$f~=14i~s-t literal 0 HcmV?d00001 diff --git a/src/STM32CubeWL/LoRaWAN/_htmresc/mini-st_2020.css b/src/STM32CubeWL/LoRaWAN/_htmresc/mini-st_2020.css new file mode 100644 index 0000000..986f4d4 --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/_htmresc/mini-st_2020.css @@ -0,0 +1,1711 @@ +@charset "UTF-8"; +/* + Flavor name: Custom (mini-custom) + Generated online - https://minicss.org/flavors + mini.css version: v3.0.1 +*/ +/* + Browsers resets and base typography. +*/ +/* Core module CSS variable definitions */ +:root { + --fore-color: #03234b; + --secondary-fore-color: #03234b; + --back-color: #ffffff; + --secondary-back-color: #ffffff; + --blockquote-color: #e6007e; + --pre-color: #e6007e; + --border-color: #3cb4e6; + --secondary-border-color: #3cb4e6; + --heading-ratio: 1.2; + --universal-margin: 0.5rem; + --universal-padding: 0.25rem; + --universal-border-radius: 0.075rem; + --background-margin: 1.5%; + --a-link-color: #3cb4e6; + --a-visited-color: #8c0078; } + +html { + font-size: 13.5px; } + +a, b, del, em, i, ins, q, span, strong, u { + font-size: 1em; } + +html, * { + font-family: -apple-system, BlinkMacSystemFont, Helvetica, arial, sans-serif; + line-height: 1.25; + -webkit-text-size-adjust: 100%; } + +* { + font-size: 1rem; } + +body { + margin: 0; + color: var(--fore-color); + @background: var(--back-color); + background: var(--back-color) linear-gradient(#ffd200, #ffd200) repeat-y left top; + background-size: var(--background-margin); + } + +details { + display: block; } + +summary { + display: list-item; } + +abbr[title] { + border-bottom: none; + text-decoration: underline dotted; } + +input { + overflow: visible; } + +img { + max-width: 100%; + height: auto; } + +h1, h2, h3, h4, h5, h6 { + line-height: 1.25; + margin: calc(1.5 * var(--universal-margin)) var(--universal-margin); + font-weight: 400; } + h1 small, h2 small, h3 small, h4 small, h5 small, h6 small { + color: var(--secondary-fore-color); + display: block; + margin-top: -0.25rem; } + +h1 { + font-size: calc(1rem * var(--heading-ratio) * var(--heading-ratio) * var(--heading-ratio)); } + +h2 { + font-size: calc(1rem * var(--heading-ratio) * var(--heading-ratio) ); + border-style: none none solid none ; + border-width: thin; + border-color: var(--border-color); } +h3 { + font-size: calc(1rem * var(--heading-ratio) ); } + +h4 { + font-size: calc(1rem * var(--heading-ratio)); } + +h5 { + font-size: 1rem; } + +h6 { + font-size: calc(1rem / var(--heading-ratio)); } + +p { + margin: var(--universal-margin); } + +ol, ul { + margin: var(--universal-margin); + padding-left: calc(3 * var(--universal-margin)); } + +b, strong { + font-weight: 700; } + +hr { + box-sizing: content-box; + border: 0; + line-height: 1.25em; + margin: var(--universal-margin); + height: 0.0714285714rem; + background: linear-gradient(to right, transparent, var(--border-color) 20%, var(--border-color) 80%, transparent); } + +blockquote { + display: block; + position: relative; + font-style: italic; + color: var(--secondary-fore-color); + margin: var(--universal-margin); + padding: calc(3 * var(--universal-padding)); + border: 0.0714285714rem solid var(--secondary-border-color); + border-left: 0.3rem solid var(--blockquote-color); + border-radius: 0 var(--universal-border-radius) var(--universal-border-radius) 0; } + blockquote:before { + position: absolute; + top: calc(0rem - var(--universal-padding)); + left: 0; + font-family: sans-serif; + font-size: 2rem; + font-weight: 800; + content: "\201c"; + color: var(--blockquote-color); } + blockquote[cite]:after { + font-style: normal; + font-size: 0.75em; + font-weight: 700; + content: "\a— " attr(cite); + white-space: pre; } + +code, kbd, pre, samp { + font-family: Menlo, Consolas, monospace; + font-size: 0.85em; } + +code { + background: var(--secondary-back-color); + border-radius: var(--universal-border-radius); + padding: calc(var(--universal-padding) / 4) calc(var(--universal-padding) / 2); } + +kbd { + background: var(--fore-color); + color: var(--back-color); + border-radius: var(--universal-border-radius); + padding: calc(var(--universal-padding) / 4) calc(var(--universal-padding) / 2); } + +pre { + overflow: auto; + background: var(--secondary-back-color); + padding: calc(1.5 * var(--universal-padding)); + margin: var(--universal-margin); + border: 0.0714285714rem solid var(--secondary-border-color); + border-left: 0.2857142857rem solid var(--pre-color); + border-radius: 0 var(--universal-border-radius) var(--universal-border-radius) 0; } + +sup, sub, code, kbd { + line-height: 0; + position: relative; + vertical-align: baseline; } + +small, sup, sub, figcaption { + font-size: 0.75em; } + +sup { + top: -0.5em; } + +sub { + bottom: -0.25em; } + +figure { + margin: var(--universal-margin); } + +figcaption { + color: var(--secondary-fore-color); } + +a { + text-decoration: none; } + a:link { + color: var(--a-link-color); } + a:visited { + color: var(--a-visited-color); } + a:hover, a:focus { + text-decoration: underline; } + +/* + Definitions for the grid system, cards and containers. +*/ +.container { + margin: 0 auto; + padding: 0 calc(1.5 * var(--universal-padding)); } + +.row { + box-sizing: border-box; + display: flex; + flex: 0 1 auto; + flex-flow: row wrap; + margin: 0 0 0 var(--background-margin); } + +.col-sm, +[class^='col-sm-'], +[class^='col-sm-offset-'], +.row[class*='cols-sm-'] > * { + box-sizing: border-box; + flex: 0 0 auto; + padding: 0 calc(var(--universal-padding) / 2); } + +.col-sm, +.row.cols-sm > * { + max-width: 100%; + flex-grow: 1; + flex-basis: 0; } + +.col-sm-1, +.row.cols-sm-1 > * { + max-width: 8.3333333333%; + flex-basis: 8.3333333333%; } + +.col-sm-offset-0 { + margin-left: 0; } + +.col-sm-2, +.row.cols-sm-2 > * { + max-width: 16.6666666667%; + flex-basis: 16.6666666667%; } + +.col-sm-offset-1 { + margin-left: 8.3333333333%; } + +.col-sm-3, +.row.cols-sm-3 > * { + max-width: 25%; + flex-basis: 25%; } + +.col-sm-offset-2 { + margin-left: 16.6666666667%; } + +.col-sm-4, +.row.cols-sm-4 > * { + max-width: 33.3333333333%; + flex-basis: 33.3333333333%; } + +.col-sm-offset-3 { + margin-left: 25%; } + +.col-sm-5, +.row.cols-sm-5 > * { + max-width: 41.6666666667%; + flex-basis: 41.6666666667%; } + +.col-sm-offset-4 { + margin-left: 33.3333333333%; } + +.col-sm-6, +.row.cols-sm-6 > * { + max-width: 50%; + flex-basis: 50%; } + +.col-sm-offset-5 { + margin-left: 41.6666666667%; } + +.col-sm-7, +.row.cols-sm-7 > * { + max-width: 58.3333333333%; + flex-basis: 58.3333333333%; } + +.col-sm-offset-6 { + margin-left: 50%; } + +.col-sm-8, +.row.cols-sm-8 > * { + max-width: 66.6666666667%; + flex-basis: 66.6666666667%; } + +.col-sm-offset-7 { + margin-left: 58.3333333333%; } + +.col-sm-9, +.row.cols-sm-9 > * { + max-width: 75%; + flex-basis: 75%; } + +.col-sm-offset-8 { + margin-left: 66.6666666667%; } + +.col-sm-10, +.row.cols-sm-10 > * { + max-width: 83.3333333333%; + flex-basis: 83.3333333333%; } + +.col-sm-offset-9 { + margin-left: 75%; } + +.col-sm-11, +.row.cols-sm-11 > * { + max-width: 91.6666666667%; + flex-basis: 91.6666666667%; } + +.col-sm-offset-10 { + margin-left: 83.3333333333%; } + +.col-sm-12, +.row.cols-sm-12 > * { + max-width: 100%; + flex-basis: 100%; } + +.col-sm-offset-11 { + margin-left: 91.6666666667%; } + +.col-sm-normal { + order: initial; } + +.col-sm-first { + order: -999; } + +.col-sm-last { + order: 999; } + +@media screen and (min-width: 500px) { + .col-md, + [class^='col-md-'], + [class^='col-md-offset-'], + .row[class*='cols-md-'] > * { + box-sizing: border-box; + flex: 0 0 auto; + padding: 0 calc(var(--universal-padding) / 2); } + + .col-md, + .row.cols-md > * { + max-width: 100%; + flex-grow: 1; + flex-basis: 0; } + + .col-md-1, + .row.cols-md-1 > * { + max-width: 8.3333333333%; + flex-basis: 8.3333333333%; } + + .col-md-offset-0 { + margin-left: 0; } + + .col-md-2, + .row.cols-md-2 > * { + max-width: 16.6666666667%; + flex-basis: 16.6666666667%; } + + .col-md-offset-1 { + margin-left: 8.3333333333%; } + + .col-md-3, + .row.cols-md-3 > * { + max-width: 25%; + flex-basis: 25%; } + + .col-md-offset-2 { + margin-left: 16.6666666667%; } + + .col-md-4, + .row.cols-md-4 > * { + max-width: 33.3333333333%; + flex-basis: 33.3333333333%; } + + .col-md-offset-3 { + margin-left: 25%; } + + .col-md-5, + .row.cols-md-5 > * { + max-width: 41.6666666667%; + flex-basis: 41.6666666667%; } + + .col-md-offset-4 { + margin-left: 33.3333333333%; } + + .col-md-6, + .row.cols-md-6 > * { + max-width: 50%; + flex-basis: 50%; } + + .col-md-offset-5 { + margin-left: 41.6666666667%; } + + .col-md-7, + .row.cols-md-7 > * { + max-width: 58.3333333333%; + flex-basis: 58.3333333333%; } + + .col-md-offset-6 { + margin-left: 50%; } + + .col-md-8, + .row.cols-md-8 > * { + max-width: 66.6666666667%; + flex-basis: 66.6666666667%; } + + .col-md-offset-7 { + margin-left: 58.3333333333%; } + + .col-md-9, + .row.cols-md-9 > * { + max-width: 75%; + flex-basis: 75%; } + + .col-md-offset-8 { + margin-left: 66.6666666667%; } + + .col-md-10, + .row.cols-md-10 > * { + max-width: 83.3333333333%; + flex-basis: 83.3333333333%; } + + .col-md-offset-9 { + margin-left: 75%; } + + .col-md-11, + .row.cols-md-11 > * { + max-width: 91.6666666667%; + flex-basis: 91.6666666667%; } + + .col-md-offset-10 { + margin-left: 83.3333333333%; } + + .col-md-12, + .row.cols-md-12 > * { + max-width: 100%; + flex-basis: 100%; } + + .col-md-offset-11 { + margin-left: 91.6666666667%; } + + .col-md-normal { + order: initial; } + + .col-md-first { + order: -999; } + + .col-md-last { + order: 999; } } +@media screen and (min-width: 1280px) { + .col-lg, + [class^='col-lg-'], + [class^='col-lg-offset-'], + .row[class*='cols-lg-'] > * { + box-sizing: border-box; + flex: 0 0 auto; + padding: 0 calc(var(--universal-padding) / 2); } + + .col-lg, + .row.cols-lg > * { + max-width: 100%; + flex-grow: 1; + flex-basis: 0; } + + .col-lg-1, + .row.cols-lg-1 > * { + max-width: 8.3333333333%; + flex-basis: 8.3333333333%; } + + .col-lg-offset-0 { + margin-left: 0; } + + .col-lg-2, + .row.cols-lg-2 > * { + max-width: 16.6666666667%; + flex-basis: 16.6666666667%; } + + .col-lg-offset-1 { + margin-left: 8.3333333333%; } + + .col-lg-3, + .row.cols-lg-3 > * { + max-width: 25%; + flex-basis: 25%; } + + .col-lg-offset-2 { + margin-left: 16.6666666667%; } + + .col-lg-4, + .row.cols-lg-4 > * { + max-width: 33.3333333333%; + flex-basis: 33.3333333333%; } + + .col-lg-offset-3 { + margin-left: 25%; } + + .col-lg-5, + .row.cols-lg-5 > * { + max-width: 41.6666666667%; + flex-basis: 41.6666666667%; } + + .col-lg-offset-4 { + margin-left: 33.3333333333%; } + + .col-lg-6, + .row.cols-lg-6 > * { + max-width: 50%; + flex-basis: 50%; } + + .col-lg-offset-5 { + margin-left: 41.6666666667%; } + + .col-lg-7, + .row.cols-lg-7 > * { + max-width: 58.3333333333%; + flex-basis: 58.3333333333%; } + + .col-lg-offset-6 { + margin-left: 50%; } + + .col-lg-8, + .row.cols-lg-8 > * { + max-width: 66.6666666667%; + flex-basis: 66.6666666667%; } + + .col-lg-offset-7 { + margin-left: 58.3333333333%; } + + .col-lg-9, + .row.cols-lg-9 > * { + max-width: 75%; + flex-basis: 75%; } + + .col-lg-offset-8 { + margin-left: 66.6666666667%; } + + .col-lg-10, + .row.cols-lg-10 > * { + max-width: 83.3333333333%; + flex-basis: 83.3333333333%; } + + .col-lg-offset-9 { + margin-left: 75%; } + + .col-lg-11, + .row.cols-lg-11 > * { + max-width: 91.6666666667%; + flex-basis: 91.6666666667%; } + + .col-lg-offset-10 { + margin-left: 83.3333333333%; } + + .col-lg-12, + .row.cols-lg-12 > * { + max-width: 100%; + flex-basis: 100%; } + + .col-lg-offset-11 { + margin-left: 91.6666666667%; } + + .col-lg-normal { + order: initial; } + + .col-lg-first { + order: -999; } + + .col-lg-last { + order: 999; } } +/* Card component CSS variable definitions */ +:root { + --card-back-color: #3cb4e6; + --card-fore-color: #03234b; + --card-border-color: #03234b; } + +.card { + display: flex; + flex-direction: column; + justify-content: space-between; + align-self: center; + position: relative; + width: 100%; + background: var(--card-back-color); + color: var(--card-fore-color); + border: 0.0714285714rem solid var(--card-border-color); + border-radius: var(--universal-border-radius); + margin: var(--universal-margin); + overflow: hidden; } + @media screen and (min-width: 320px) { + .card { + max-width: 320px; } } + .card > .sectione { + background: var(--card-back-color); + color: var(--card-fore-color); + box-sizing: border-box; + margin: 0; + border: 0; + border-radius: 0; + border-bottom: 0.0714285714rem solid var(--card-border-color); + padding: var(--universal-padding); + width: 100%; } + .card > .sectione.media { + height: 200px; + padding: 0; + -o-object-fit: cover; + object-fit: cover; } + .card > .sectione:last-child { + border-bottom: 0; } + +/* + Custom elements for card elements. +*/ +@media screen and (min-width: 240px) { + .card.small { + max-width: 240px; } } +@media screen and (min-width: 480px) { + .card.large { + max-width: 480px; } } +.card.fluid { + max-width: 100%; + width: auto; } + +.card.warning { + --card-back-color: #e5b8b7; + --card-fore-color: #3b234b; + --card-border-color: #8c0078; } + +.card.error { + --card-back-color: #464650; + --card-fore-color: #ffffff; + --card-border-color: #8c0078; } + +.card > .sectione.dark { + --card-back-color: #3b234b; + --card-fore-color: #ffffff; } + +.card > .sectione.double-padded { + padding: calc(1.5 * var(--universal-padding)); } + +/* + Definitions for forms and input elements. +*/ +/* Input_control module CSS variable definitions */ +:root { + --form-back-color: #ffe97f; + --form-fore-color: #03234b; + --form-border-color: #3cb4e6; + --input-back-color: #ffffff; + --input-fore-color: #03234b; + --input-border-color: #3cb4e6; + --input-focus-color: #0288d1; + --input-invalid-color: #d32f2f; + --button-back-color: #e2e2e2; + --button-hover-back-color: #dcdcdc; + --button-fore-color: #212121; + --button-border-color: transparent; + --button-hover-border-color: transparent; + --button-group-border-color: rgba(124, 124, 124, 0.54); } + +form { + background: var(--form-back-color); + color: var(--form-fore-color); + border: 0.0714285714rem solid var(--form-border-color); + border-radius: var(--universal-border-radius); + margin: var(--universal-margin); + padding: calc(2 * var(--universal-padding)) var(--universal-padding); } + +fieldset { + border: 0.0714285714rem solid var(--form-border-color); + border-radius: var(--universal-border-radius); + margin: calc(var(--universal-margin) / 4); + padding: var(--universal-padding); } + +legend { + box-sizing: border-box; + display: table; + max-width: 100%; + white-space: normal; + font-weight: 500; + padding: calc(var(--universal-padding) / 2); } + +label { + padding: calc(var(--universal-padding) / 2) var(--universal-padding); } + +.input-group { + display: inline-block; } + .input-group.fluid { + display: flex; + align-items: center; + justify-content: center; } + .input-group.fluid > input { + max-width: 100%; + flex-grow: 1; + flex-basis: 0px; } + @media screen and (max-width: 499px) { + .input-group.fluid { + align-items: stretch; + flex-direction: column; } } + .input-group.vertical { + display: flex; + align-items: stretch; + flex-direction: column; } + .input-group.vertical > input { + max-width: 100%; + flex-grow: 1; + flex-basis: 0px; } + +[type="number"]::-webkit-inner-spin-button, [type="number"]::-webkit-outer-spin-button { + height: auto; } + +[type="search"] { + -webkit-appearance: textfield; + outline-offset: -2px; } + +[type="search"]::-webkit-search-cancel-button, +[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; } + +input:not([type]), [type="text"], [type="email"], [type="number"], [type="search"], +[type="password"], [type="url"], [type="tel"], [type="checkbox"], [type="radio"], textarea, select { + box-sizing: border-box; + background: var(--input-back-color); + color: var(--input-fore-color); + border: 0.0714285714rem solid var(--input-border-color); + border-radius: var(--universal-border-radius); + margin: calc(var(--universal-margin) / 2); + padding: var(--universal-padding) calc(1.5 * var(--universal-padding)); } + +input:not([type="button"]):not([type="submit"]):not([type="reset"]):hover, input:not([type="button"]):not([type="submit"]):not([type="reset"]):focus, textarea:hover, textarea:focus, select:hover, select:focus { + border-color: var(--input-focus-color); + box-shadow: none; } +input:not([type="button"]):not([type="submit"]):not([type="reset"]):invalid, input:not([type="button"]):not([type="submit"]):not([type="reset"]):focus:invalid, textarea:invalid, textarea:focus:invalid, select:invalid, select:focus:invalid { + border-color: var(--input-invalid-color); + box-shadow: none; } +input:not([type="button"]):not([type="submit"]):not([type="reset"])[readonly], textarea[readonly], select[readonly] { + background: var(--secondary-back-color); } + +select { + max-width: 100%; } + +option { + overflow: hidden; + text-overflow: ellipsis; } + +[type="checkbox"], [type="radio"] { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + position: relative; + height: calc(1rem + var(--universal-padding) / 2); + width: calc(1rem + var(--universal-padding) / 2); + vertical-align: text-bottom; + padding: 0; + flex-basis: calc(1rem + var(--universal-padding) / 2) !important; + flex-grow: 0 !important; } + [type="checkbox"]:checked:before, [type="radio"]:checked:before { + position: absolute; } + +[type="checkbox"]:checked:before { + content: '\2713'; + font-family: sans-serif; + font-size: calc(1rem + var(--universal-padding) / 2); + top: calc(0rem - var(--universal-padding)); + left: calc(var(--universal-padding) / 4); } + +[type="radio"] { + border-radius: 100%; } + [type="radio"]:checked:before { + border-radius: 100%; + content: ''; + top: calc(0.0714285714rem + var(--universal-padding) / 2); + left: calc(0.0714285714rem + var(--universal-padding) / 2); + background: var(--input-fore-color); + width: 0.5rem; + height: 0.5rem; } + +:placeholder-shown { + color: var(--input-fore-color); } + +::-ms-placeholder { + color: var(--input-fore-color); + opacity: 0.54; } + +button::-moz-focus-inner, [type="button"]::-moz-focus-inner, [type="reset"]::-moz-focus-inner, [type="submit"]::-moz-focus-inner { + border-style: none; + padding: 0; } + +button, html [type="button"], [type="reset"], [type="submit"] { + -webkit-appearance: button; } + +button { + overflow: visible; + text-transform: none; } + +button, [type="button"], [type="submit"], [type="reset"], +a.button, label.button, .button, +a[role="button"], label[role="button"], [role="button"] { + display: inline-block; + background: var(--button-back-color); + color: var(--button-fore-color); + border: 0.0714285714rem solid var(--button-border-color); + border-radius: var(--universal-border-radius); + padding: var(--universal-padding) calc(1.5 * var(--universal-padding)); + margin: var(--universal-margin); + text-decoration: none; + cursor: pointer; + transition: background 0.3s; } + button:hover, button:focus, [type="button"]:hover, [type="button"]:focus, [type="submit"]:hover, [type="submit"]:focus, [type="reset"]:hover, [type="reset"]:focus, + a.button:hover, + a.button:focus, label.button:hover, label.button:focus, .button:hover, .button:focus, + a[role="button"]:hover, + a[role="button"]:focus, label[role="button"]:hover, label[role="button"]:focus, [role="button"]:hover, [role="button"]:focus { + background: var(--button-hover-back-color); + border-color: var(--button-hover-border-color); } + +input:disabled, input[disabled], textarea:disabled, textarea[disabled], select:disabled, select[disabled], button:disabled, button[disabled], .button:disabled, .button[disabled], [role="button"]:disabled, [role="button"][disabled] { + cursor: not-allowed; + opacity: 0.75; } + +.button-group { + display: flex; + border: 0.0714285714rem solid var(--button-group-border-color); + border-radius: var(--universal-border-radius); + margin: var(--universal-margin); } + .button-group > button, .button-group [type="button"], .button-group > [type="submit"], .button-group > [type="reset"], .button-group > .button, .button-group > [role="button"] { + margin: 0; + max-width: 100%; + flex: 1 1 auto; + text-align: center; + border: 0; + border-radius: 0; + box-shadow: none; } + .button-group > :not(:first-child) { + border-left: 0.0714285714rem solid var(--button-group-border-color); } + @media screen and (max-width: 499px) { + .button-group { + flex-direction: column; } + .button-group > :not(:first-child) { + border: 0; + border-top: 0.0714285714rem solid var(--button-group-border-color); } } + +/* + Custom elements for forms and input elements. +*/ +button.primary, [type="button"].primary, [type="submit"].primary, [type="reset"].primary, .button.primary, [role="button"].primary { + --button-back-color: #1976d2; + --button-fore-color: #f8f8f8; } + button.primary:hover, button.primary:focus, [type="button"].primary:hover, [type="button"].primary:focus, [type="submit"].primary:hover, [type="submit"].primary:focus, [type="reset"].primary:hover, [type="reset"].primary:focus, .button.primary:hover, .button.primary:focus, [role="button"].primary:hover, [role="button"].primary:focus { + --button-hover-back-color: #1565c0; } + +button.secondary, [type="button"].secondary, [type="submit"].secondary, [type="reset"].secondary, .button.secondary, [role="button"].secondary { + --button-back-color: #d32f2f; + --button-fore-color: #f8f8f8; } + button.secondary:hover, button.secondary:focus, [type="button"].secondary:hover, [type="button"].secondary:focus, [type="submit"].secondary:hover, [type="submit"].secondary:focus, [type="reset"].secondary:hover, [type="reset"].secondary:focus, .button.secondary:hover, .button.secondary:focus, [role="button"].secondary:hover, [role="button"].secondary:focus { + --button-hover-back-color: #c62828; } + +button.tertiary, [type="button"].tertiary, [type="submit"].tertiary, [type="reset"].tertiary, .button.tertiary, [role="button"].tertiary { + --button-back-color: #308732; + --button-fore-color: #f8f8f8; } + button.tertiary:hover, button.tertiary:focus, [type="button"].tertiary:hover, [type="button"].tertiary:focus, [type="submit"].tertiary:hover, [type="submit"].tertiary:focus, [type="reset"].tertiary:hover, [type="reset"].tertiary:focus, .button.tertiary:hover, .button.tertiary:focus, [role="button"].tertiary:hover, [role="button"].tertiary:focus { + --button-hover-back-color: #277529; } + +button.inverse, [type="button"].inverse, [type="submit"].inverse, [type="reset"].inverse, .button.inverse, [role="button"].inverse { + --button-back-color: #212121; + --button-fore-color: #f8f8f8; } + button.inverse:hover, button.inverse:focus, [type="button"].inverse:hover, [type="button"].inverse:focus, [type="submit"].inverse:hover, [type="submit"].inverse:focus, [type="reset"].inverse:hover, [type="reset"].inverse:focus, .button.inverse:hover, .button.inverse:focus, [role="button"].inverse:hover, [role="button"].inverse:focus { + --button-hover-back-color: #111; } + +button.small, [type="button"].small, [type="submit"].small, [type="reset"].small, .button.small, [role="button"].small { + padding: calc(0.5 * var(--universal-padding)) calc(0.75 * var(--universal-padding)); + margin: var(--universal-margin); } + +button.large, [type="button"].large, [type="submit"].large, [type="reset"].large, .button.large, [role="button"].large { + padding: calc(1.5 * var(--universal-padding)) calc(2 * var(--universal-padding)); + margin: var(--universal-margin); } + +/* + Definitions for navigation elements. +*/ +/* Navigation module CSS variable definitions */ +:root { + --header-back-color: #03234b; + --header-hover-back-color: #ffd200; + --header-fore-color: #ffffff; + --header-border-color: #3cb4e6; + --nav-back-color: #ffffff; + --nav-hover-back-color: #ffe97f; + --nav-fore-color: #e6007e; + --nav-border-color: #3cb4e6; + --nav-link-color: #3cb4e6; + --footer-fore-color: #ffffff; + --footer-back-color: #03234b; + --footer-border-color: #3cb4e6; + --footer-link-color: #3cb4e6; + --drawer-back-color: #ffffff; + --drawer-hover-back-color: #ffe97f; + --drawer-border-color: #3cb4e6; + --drawer-close-color: #e6007e; } + +header { + height: 2.75rem; + background: var(--header-back-color); + color: var(--header-fore-color); + border-bottom: 0.0714285714rem solid var(--header-border-color); + padding: calc(var(--universal-padding) / 4) 0; + white-space: nowrap; + overflow-x: auto; + overflow-y: hidden; } + header.row { + box-sizing: content-box; } + header .logo { + color: var(--header-fore-color); + font-size: 1.75rem; + padding: var(--universal-padding) calc(2 * var(--universal-padding)); + text-decoration: none; } + header button, header [type="button"], header .button, header [role="button"] { + box-sizing: border-box; + position: relative; + top: calc(0rem - var(--universal-padding) / 4); + height: calc(3.1875rem + var(--universal-padding) / 2); + background: var(--header-back-color); + line-height: calc(3.1875rem - var(--universal-padding) * 1.5); + text-align: center; + color: var(--header-fore-color); + border: 0; + border-radius: 0; + margin: 0; + text-transform: uppercase; } + header button:hover, header button:focus, header [type="button"]:hover, header [type="button"]:focus, header .button:hover, header .button:focus, header [role="button"]:hover, header [role="button"]:focus { + background: var(--header-hover-back-color); } + +nav { + background: var(--nav-back-color); + color: var(--nav-fore-color); + border: 0.0714285714rem solid var(--nav-border-color); + border-radius: var(--universal-border-radius); + margin: var(--universal-margin); } + nav * { + padding: var(--universal-padding) calc(1.5 * var(--universal-padding)); } + nav a, nav a:visited { + display: block; + color: var(--nav-link-color); + border-radius: var(--universal-border-radius); + transition: background 0.3s; } + nav a:hover, nav a:focus, nav a:visited:hover, nav a:visited:focus { + text-decoration: none; + background: var(--nav-hover-back-color); } + nav .sublink-1 { + position: relative; + margin-left: calc(2 * var(--universal-padding)); } + nav .sublink-1:before { + position: absolute; + left: calc(var(--universal-padding) - 1 * var(--universal-padding)); + top: -0.0714285714rem; + content: ''; + height: 100%; + border: 0.0714285714rem solid var(--nav-border-color); + border-left: 0; } + nav .sublink-2 { + position: relative; + margin-left: calc(4 * var(--universal-padding)); } + nav .sublink-2:before { + position: absolute; + left: calc(var(--universal-padding) - 3 * var(--universal-padding)); + top: -0.0714285714rem; + content: ''; + height: 100%; + border: 0.0714285714rem solid var(--nav-border-color); + border-left: 0; } + +footer { + background: var(--footer-back-color); + color: var(--footer-fore-color); + border-top: 0.0714285714rem solid var(--footer-border-color); + padding: calc(2 * var(--universal-padding)) var(--universal-padding); + font-size: 0.875rem; } + footer a, footer a:visited { + color: var(--footer-link-color); } + +header.sticky { + position: -webkit-sticky; + position: sticky; + z-index: 1101; + top: 0; } + +footer.sticky { + position: -webkit-sticky; + position: sticky; + z-index: 1101; + bottom: 0; } + +.drawer-toggle:before { + display: inline-block; + position: relative; + vertical-align: bottom; + content: '\00a0\2261\00a0'; + font-family: sans-serif; + font-size: 1.5em; } +@media screen and (min-width: 500px) { + .drawer-toggle:not(.persistent) { + display: none; } } + +[type="checkbox"].drawer { + height: 1px; + width: 1px; + margin: -1px; + overflow: hidden; + position: absolute; + clip: rect(0 0 0 0); + -webkit-clip-path: inset(100%); + clip-path: inset(100%); } + [type="checkbox"].drawer + * { + display: block; + box-sizing: border-box; + position: fixed; + top: 0; + width: 320px; + height: 100vh; + overflow-y: auto; + background: var(--drawer-back-color); + border: 0.0714285714rem solid var(--drawer-border-color); + border-radius: 0; + margin: 0; + z-index: 1110; + right: -320px; + transition: right 0.3s; } + [type="checkbox"].drawer + * .drawer-close { + position: absolute; + top: var(--universal-margin); + right: var(--universal-margin); + z-index: 1111; + width: 2rem; + height: 2rem; + border-radius: var(--universal-border-radius); + padding: var(--universal-padding); + margin: 0; + cursor: pointer; + transition: background 0.3s; } + [type="checkbox"].drawer + * .drawer-close:before { + display: block; + content: '\00D7'; + color: var(--drawer-close-color); + position: relative; + font-family: sans-serif; + font-size: 2rem; + line-height: 1; + text-align: center; } + [type="checkbox"].drawer + * .drawer-close:hover, [type="checkbox"].drawer + * .drawer-close:focus { + background: var(--drawer-hover-back-color); } + @media screen and (max-width: 320px) { + [type="checkbox"].drawer + * { + width: 100%; } } + [type="checkbox"].drawer:checked + * { + right: 0; } + @media screen and (min-width: 500px) { + [type="checkbox"].drawer:not(.persistent) + * { + position: static; + height: 100%; + z-index: 1100; } + [type="checkbox"].drawer:not(.persistent) + * .drawer-close { + display: none; } } + +/* + Definitions for the responsive table component. +*/ +/* Table module CSS variable definitions. */ +:root { + --table-border-color: #03234b; + --table-border-separator-color: #03234b; + --table-head-back-color: #03234b; + --table-head-fore-color: #ffffff; + --table-body-back-color: #ffffff; + --table-body-fore-color: #03234b; + --table-body-alt-back-color: #f4f4f4; } + +table { + border-collapse: separate; + border-spacing: 0; + margin: 0; + display: flex; + flex: 0 1 auto; + flex-flow: row wrap; + padding: var(--universal-padding); + padding-top: 0; } + table caption { + font-size: 1rem; + margin: calc(2 * var(--universal-margin)) 0; + max-width: 100%; + flex: 0 0 100%; } + table thead, table tbody { + display: flex; + flex-flow: row wrap; + border: 0.0714285714rem solid var(--table-border-color); } + table thead { + z-index: 999; + border-radius: var(--universal-border-radius) var(--universal-border-radius) 0 0; + border-bottom: 0.0714285714rem solid var(--table-border-separator-color); } + table tbody { + border-top: 0; + margin-top: calc(0 - var(--universal-margin)); + border-radius: 0 0 var(--universal-border-radius) var(--universal-border-radius); } + table tr { + display: flex; + padding: 0; } + table th, table td { + padding: calc(0.5 * var(--universal-padding)); + font-size: 0.9rem; } + table th { + text-align: left; + background: var(--table-head-back-color); + color: var(--table-head-fore-color); } + table td { + background: var(--table-body-back-color); + color: var(--table-body-fore-color); + border-top: 0.0714285714rem solid var(--table-border-color); } + +table:not(.horizontal) { + overflow: auto; + max-height: 100%; } + table:not(.horizontal) thead, table:not(.horizontal) tbody { + max-width: 100%; + flex: 0 0 100%; } + table:not(.horizontal) tr { + flex-flow: row wrap; + flex: 0 0 100%; } + table:not(.horizontal) th, table:not(.horizontal) td { + flex: 1 0 0%; + overflow: hidden; + text-overflow: ellipsis; } + table:not(.horizontal) thead { + position: sticky; + top: 0; } + table:not(.horizontal) tbody tr:first-child td { + border-top: 0; } + +table.horizontal { + border: 0; } + table.horizontal thead, table.horizontal tbody { + border: 0; + flex: .2 0 0; + flex-flow: row nowrap; } + table.horizontal tbody { + overflow: auto; + justify-content: space-between; + flex: .8 0 0; + margin-left: 0; + padding-bottom: calc(var(--universal-padding) / 4); } + table.horizontal tr { + flex-direction: column; + flex: 1 0 auto; } + table.horizontal th, table.horizontal td { + width: auto; + border: 0; + border-bottom: 0.0714285714rem solid var(--table-border-color); } + table.horizontal th:not(:first-child), table.horizontal td:not(:first-child) { + border-top: 0; } + table.horizontal th { + text-align: right; + border-left: 0.0714285714rem solid var(--table-border-color); + border-right: 0.0714285714rem solid var(--table-border-separator-color); } + table.horizontal thead tr:first-child { + padding-left: 0; } + table.horizontal th:first-child, table.horizontal td:first-child { + border-top: 0.0714285714rem solid var(--table-border-color); } + table.horizontal tbody tr:last-child td { + border-right: 0.0714285714rem solid var(--table-border-color); } + table.horizontal tbody tr:last-child td:first-child { + border-top-right-radius: 0.25rem; } + table.horizontal tbody tr:last-child td:last-child { + border-bottom-right-radius: 0.25rem; } + table.horizontal thead tr:first-child th:first-child { + border-top-left-radius: 0.25rem; } + table.horizontal thead tr:first-child th:last-child { + border-bottom-left-radius: 0.25rem; } + +@media screen and (max-width: 499px) { + table, table.horizontal { + border-collapse: collapse; + border: 0; + width: 100%; + display: table; } + table thead, table th, table.horizontal thead, table.horizontal th { + border: 0; + height: 1px; + width: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + clip: rect(0 0 0 0); + -webkit-clip-path: inset(100%); + clip-path: inset(100%); } + table tbody, table.horizontal tbody { + border: 0; + display: table-row-group; } + table tr, table.horizontal tr { + display: block; + border: 0.0714285714rem solid var(--table-border-color); + border-radius: var(--universal-border-radius); + background: #ffffff; + padding: var(--universal-padding); + margin: var(--universal-margin); + margin-bottom: calc(1 * var(--universal-margin)); } + table th, table td, table.horizontal th, table.horizontal td { + width: auto; } + table td, table.horizontal td { + display: block; + border: 0; + text-align: right; } + table td:before, table.horizontal td:before { + content: attr(data-label); + float: left; + font-weight: 600; } + table th:first-child, table td:first-child, table.horizontal th:first-child, table.horizontal td:first-child { + border-top: 0; } + table tbody tr:last-child td, table.horizontal tbody tr:last-child td { + border-right: 0; } } +table tr:nth-of-type(2n) > td { + background: var(--table-body-alt-back-color); } + +@media screen and (max-width: 500px) { + table tr:nth-of-type(2n) { + background: var(--table-body-alt-back-color); } } +:root { + --table-body-hover-back-color: #90caf9; } + +table.hoverable tr:hover, table.hoverable tr:hover > td, table.hoverable tr:focus, table.hoverable tr:focus > td { + background: var(--table-body-hover-back-color); } + +@media screen and (max-width: 500px) { + table.hoverable tr:hover, table.hoverable tr:hover > td, table.hoverable tr:focus, table.hoverable tr:focus > td { + background: var(--table-body-hover-back-color); } } +/* + Definitions for contextual background elements, toasts and tooltips. +*/ +/* Contextual module CSS variable definitions */ +:root { + --mark-back-color: #3cb4e6; + --mark-fore-color: #ffffff; } + +mark { + background: var(--mark-back-color); + color: var(--mark-fore-color); + font-size: 0.95em; + line-height: 1em; + border-radius: var(--universal-border-radius); + padding: calc(var(--universal-padding) / 4) var(--universal-padding); } + mark.inline-block { + display: inline-block; + font-size: 1em; + line-height: 1.4; + padding: calc(var(--universal-padding) / 2) var(--universal-padding); } + +:root { + --toast-back-color: #424242; + --toast-fore-color: #fafafa; } + +.toast { + position: fixed; + bottom: calc(var(--universal-margin) * 3); + left: 50%; + transform: translate(-50%, -50%); + z-index: 1111; + color: var(--toast-fore-color); + background: var(--toast-back-color); + border-radius: calc(var(--universal-border-radius) * 16); + padding: var(--universal-padding) calc(var(--universal-padding) * 3); } + +:root { + --tooltip-back-color: #212121; + --tooltip-fore-color: #fafafa; } + +.tooltip { + position: relative; + display: inline-block; } + .tooltip:before, .tooltip:after { + position: absolute; + opacity: 0; + clip: rect(0 0 0 0); + -webkit-clip-path: inset(100%); + clip-path: inset(100%); + transition: all 0.3s; + z-index: 1010; + left: 50%; } + .tooltip:not(.bottom):before, .tooltip:not(.bottom):after { + bottom: 75%; } + .tooltip.bottom:before, .tooltip.bottom:after { + top: 75%; } + .tooltip:hover:before, .tooltip:hover:after, .tooltip:focus:before, .tooltip:focus:after { + opacity: 1; + clip: auto; + -webkit-clip-path: inset(0%); + clip-path: inset(0%); } + .tooltip:before { + content: ''; + background: transparent; + border: var(--universal-margin) solid transparent; + left: calc(50% - var(--universal-margin)); } + .tooltip:not(.bottom):before { + border-top-color: #212121; } + .tooltip.bottom:before { + border-bottom-color: #212121; } + .tooltip:after { + content: attr(aria-label); + color: var(--tooltip-fore-color); + background: var(--tooltip-back-color); + border-radius: var(--universal-border-radius); + padding: var(--universal-padding); + white-space: nowrap; + transform: translateX(-50%); } + .tooltip:not(.bottom):after { + margin-bottom: calc(2 * var(--universal-margin)); } + .tooltip.bottom:after { + margin-top: calc(2 * var(--universal-margin)); } + +:root { + --modal-overlay-color: rgba(0, 0, 0, 0.45); + --modal-close-color: #e6007e; + --modal-close-hover-color: #ffe97f; } + +[type="checkbox"].modal { + height: 1px; + width: 1px; + margin: -1px; + overflow: hidden; + position: absolute; + clip: rect(0 0 0 0); + -webkit-clip-path: inset(100%); + clip-path: inset(100%); } + [type="checkbox"].modal + div { + position: fixed; + top: 0; + left: 0; + display: none; + width: 100vw; + height: 100vh; + background: var(--modal-overlay-color); } + [type="checkbox"].modal + div .card { + margin: 0 auto; + max-height: 50vh; + overflow: auto; } + [type="checkbox"].modal + div .card .modal-close { + position: absolute; + top: 0; + right: 0; + width: 1.75rem; + height: 1.75rem; + border-radius: var(--universal-border-radius); + padding: var(--universal-padding); + margin: 0; + cursor: pointer; + transition: background 0.3s; } + [type="checkbox"].modal + div .card .modal-close:before { + display: block; + content: '\00D7'; + color: var(--modal-close-color); + position: relative; + font-family: sans-serif; + font-size: 1.75rem; + line-height: 1; + text-align: center; } + [type="checkbox"].modal + div .card .modal-close:hover, [type="checkbox"].modal + div .card .modal-close:focus { + background: var(--modal-close-hover-color); } + [type="checkbox"].modal:checked + div { + display: flex; + flex: 0 1 auto; + z-index: 1200; } + [type="checkbox"].modal:checked + div .card .modal-close { + z-index: 1211; } + +:root { + --collapse-label-back-color: #03234b; + --collapse-label-fore-color: #ffffff; + --collapse-label-hover-back-color: #3cb4e6; + --collapse-selected-label-back-color: #3cb4e6; + --collapse-border-color: var(--collapse-label-back-color); + --collapse-selected-border-color: #ceecf8; + --collapse-content-back-color: #ffffff; + --collapse-selected-label-border-color: #3cb4e6; } + +.collapse { + width: calc(100% - 2 * var(--universal-margin)); + opacity: 1; + display: flex; + flex-direction: column; + margin: var(--universal-margin); + border-radius: var(--universal-border-radius); } + .collapse > [type="radio"], .collapse > [type="checkbox"] { + height: 1px; + width: 1px; + margin: -1px; + overflow: hidden; + position: absolute; + clip: rect(0 0 0 0); + -webkit-clip-path: inset(100%); + clip-path: inset(100%); } + .collapse > label { + flex-grow: 1; + display: inline-block; + height: 1.25rem; + cursor: pointer; + transition: background 0.2s; + color: var(--collapse-label-fore-color); + background: var(--collapse-label-back-color); + border: 0.0714285714rem solid var(--collapse-selected-border-color); + padding: calc(1.25 * var(--universal-padding)); } + .collapse > label:hover, .collapse > label:focus { + background: var(--collapse-label-hover-back-color); } + .collapse > label + div { + flex-basis: auto; + height: 1px; + width: 1px; + margin: -1px; + overflow: hidden; + position: absolute; + clip: rect(0 0 0 0); + -webkit-clip-path: inset(100%); + clip-path: inset(100%); + transition: max-height 0.3s; + max-height: 1px; } + .collapse > :checked + label { + background: var(--collapse-selected-label-back-color); + border-color: var(--collapse-selected-label-border-color); } + .collapse > :checked + label + div { + box-sizing: border-box; + position: relative; + width: 100%; + height: auto; + overflow: auto; + margin: 0; + background: var(--collapse-content-back-color); + border: 0.0714285714rem solid var(--collapse-selected-border-color); + border-top: 0; + padding: var(--universal-padding); + clip: auto; + -webkit-clip-path: inset(0%); + clip-path: inset(0%); + max-height: 100%; } + .collapse > label:not(:first-of-type) { + border-top: 0; } + .collapse > label:first-of-type { + border-radius: var(--universal-border-radius) var(--universal-border-radius) 0 0; } + .collapse > label:last-of-type:not(:first-of-type) { + border-radius: 0 0 var(--universal-border-radius) var(--universal-border-radius); } + .collapse > label:last-of-type:first-of-type { + border-radius: var(--universal-border-radius); } + .collapse > :checked:last-of-type:not(:first-of-type) + label { + border-radius: 0; } + .collapse > :checked:last-of-type + label + div { + border-radius: 0 0 var(--universal-border-radius) var(--universal-border-radius); } + +/* + Custom elements for contextual background elements, toasts and tooltips. +*/ +mark.tertiary { + --mark-back-color: #3cb4e6; } + +mark.tag { + padding: calc(var(--universal-padding)/2) var(--universal-padding); + border-radius: 1em; } + +/* + Definitions for progress elements and spinners. +*/ +/* Progress module CSS variable definitions */ +:root { + --progress-back-color: #3cb4e6; + --progress-fore-color: #555; } + +progress { + display: block; + vertical-align: baseline; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + height: 0.75rem; + width: calc(100% - 2 * var(--universal-margin)); + margin: var(--universal-margin); + border: 0; + border-radius: calc(2 * var(--universal-border-radius)); + background: var(--progress-back-color); + color: var(--progress-fore-color); } + progress::-webkit-progress-value { + background: var(--progress-fore-color); + border-top-left-radius: calc(2 * var(--universal-border-radius)); + border-bottom-left-radius: calc(2 * var(--universal-border-radius)); } + progress::-webkit-progress-bar { + background: var(--progress-back-color); } + progress::-moz-progress-bar { + background: var(--progress-fore-color); + border-top-left-radius: calc(2 * var(--universal-border-radius)); + border-bottom-left-radius: calc(2 * var(--universal-border-radius)); } + progress[value="1000"]::-webkit-progress-value { + border-radius: calc(2 * var(--universal-border-radius)); } + progress[value="1000"]::-moz-progress-bar { + border-radius: calc(2 * var(--universal-border-radius)); } + progress.inline { + display: inline-block; + vertical-align: middle; + width: 60%; } + +:root { + --spinner-back-color: #ddd; + --spinner-fore-color: #555; } + +@keyframes spinner-donut-anim { + 0% { + transform: rotate(0deg); } + 100% { + transform: rotate(360deg); } } +.spinner { + display: inline-block; + margin: var(--universal-margin); + border: 0.25rem solid var(--spinner-back-color); + border-left: 0.25rem solid var(--spinner-fore-color); + border-radius: 50%; + width: 1.25rem; + height: 1.25rem; + animation: spinner-donut-anim 1.2s linear infinite; } + +/* + Custom elements for progress bars and spinners. +*/ +progress.primary { + --progress-fore-color: #1976d2; } + +progress.secondary { + --progress-fore-color: #d32f2f; } + +progress.tertiary { + --progress-fore-color: #308732; } + +.spinner.primary { + --spinner-fore-color: #1976d2; } + +.spinner.secondary { + --spinner-fore-color: #d32f2f; } + +.spinner.tertiary { + --spinner-fore-color: #308732; } + +/* + Definitions for icons - powered by Feather (https://feathericons.com/). +*/ +span[class^='icon-'] { + display: inline-block; + height: 1em; + width: 1em; + vertical-align: -0.125em; + background-size: contain; + margin: 0 calc(var(--universal-margin) / 4); } + span[class^='icon-'].secondary { + -webkit-filter: invert(25%); + filter: invert(25%); } + span[class^='icon-'].inverse { + -webkit-filter: invert(100%); + filter: invert(100%); } + +span.icon-alert { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%2303234b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12' y2='16'%3E%3C/line%3E%3C/svg%3E"); } +span.icon-bookmark { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%2303234b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z'%3E%3C/path%3E%3C/svg%3E"); } +span.icon-calendar { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%2303234b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E"); } +span.icon-credit { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%2303234b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='1' y='4' width='22' height='16' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='1' y1='10' x2='23' y2='10'%3E%3C/line%3E%3C/svg%3E"); } +span.icon-edit { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%2303234b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 14.66V20a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h5.34'%3E%3C/path%3E%3Cpolygon points='18 2 22 6 12 16 8 16 8 12 18 2'%3E%3C/polygon%3E%3C/svg%3E"); } +span.icon-link { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%2303234b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6'%3E%3C/path%3E%3Cpolyline points='15 3 21 3 21 9'%3E%3C/polyline%3E%3Cline x1='10' y1='14' x2='21' y2='3'%3E%3C/line%3E%3C/svg%3E"); } +span.icon-help { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%2303234b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3'%3E%3C/path%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='17' x2='12' y2='17'%3E%3C/line%3E%3C/svg%3E"); } +span.icon-home { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%2303234b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z'%3E%3C/path%3E%3Cpolyline points='9 22 9 12 15 12 15 22'%3E%3C/polyline%3E%3C/svg%3E"); } +span.icon-info { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%2303234b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='16' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='8' x2='12' y2='8'%3E%3C/line%3E%3C/svg%3E"); } +span.icon-lock { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%2303234b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='11' width='18' height='11' rx='2' ry='2'%3E%3C/rect%3E%3Cpath d='M7 11V7a5 5 0 0 1 10 0v4'%3E%3C/path%3E%3C/svg%3E"); } +span.icon-mail { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%2303234b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z'%3E%3C/path%3E%3Cpolyline points='22,6 12,13 2,6'%3E%3C/polyline%3E%3C/svg%3E"); } +span.icon-location { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%2303234b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z'%3E%3C/path%3E%3Ccircle cx='12' cy='10' r='3'%3E%3C/circle%3E%3C/svg%3E"); } +span.icon-phone { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%2303234b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z'%3E%3C/path%3E%3C/svg%3E"); } +span.icon-rss { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%2303234b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 11a9 9 0 0 1 9 9'%3E%3C/path%3E%3Cpath d='M4 4a16 16 0 0 1 16 16'%3E%3C/path%3E%3Ccircle cx='5' cy='19' r='1'%3E%3C/circle%3E%3C/svg%3E"); } +span.icon-search { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%2303234b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E"); } +span.icon-settings { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%2303234b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='3'%3E%3C/circle%3E%3Cpath d='M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z'%3E%3C/path%3E%3C/svg%3E"); } +span.icon-share { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%2303234b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='18' cy='5' r='3'%3E%3C/circle%3E%3Ccircle cx='6' cy='12' r='3'%3E%3C/circle%3E%3Ccircle cx='18' cy='19' r='3'%3E%3C/circle%3E%3Cline x1='8.59' y1='13.51' x2='15.42' y2='17.49'%3E%3C/line%3E%3Cline x1='15.41' y1='6.51' x2='8.59' y2='10.49'%3E%3C/line%3E%3C/svg%3E"); } +span.icon-cart { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%2303234b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='9' cy='21' r='1'%3E%3C/circle%3E%3Ccircle cx='20' cy='21' r='1'%3E%3C/circle%3E%3Cpath d='M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6'%3E%3C/path%3E%3C/svg%3E"); } +span.icon-upload { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%2303234b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4'%3E%3C/path%3E%3Cpolyline points='17 8 12 3 7 8'%3E%3C/polyline%3E%3Cline x1='12' y1='3' x2='12' y2='15'%3E%3C/line%3E%3C/svg%3E"); } +span.icon-user { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%2303234b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2'%3E%3C/path%3E%3Ccircle cx='12' cy='7' r='4'%3E%3C/circle%3E%3C/svg%3E"); } + +/* + Definitions for STMicroelectronics icons (https://brandportal.st.com/document/26). +*/ +span.icon-st-update { + background-image: url("Update.svg"); } +span.icon-st-add { + background-image: url("Add button.svg"); } + +/* + Definitions for utilities and helper classes. +*/ +/* Utility module CSS variable definitions */ +:root { + --generic-border-color: rgba(0, 0, 0, 0.3); + --generic-box-shadow: 0 0.2857142857rem 0.2857142857rem 0 rgba(0, 0, 0, 0.125), 0 0.1428571429rem 0.1428571429rem -0.1428571429rem rgba(0, 0, 0, 0.125); } + +.hidden { + display: none !important; } + +.visually-hidden { + position: absolute !important; + width: 1px !important; + height: 1px !important; + margin: -1px !important; + border: 0 !important; + padding: 0 !important; + clip: rect(0 0 0 0) !important; + -webkit-clip-path: inset(100%) !important; + clip-path: inset(100%) !important; + overflow: hidden !important; } + +.bordered { + border: 0.0714285714rem solid var(--generic-border-color) !important; } + +.rounded { + border-radius: var(--universal-border-radius) !important; } + +.circular { + border-radius: 50% !important; } + +.shadowed { + box-shadow: var(--generic-box-shadow) !important; } + +.responsive-margin { + margin: calc(var(--universal-margin) / 4) !important; } + @media screen and (min-width: 500px) { + .responsive-margin { + margin: calc(var(--universal-margin) / 2) !important; } } + @media screen and (min-width: 1280px) { + .responsive-margin { + margin: var(--universal-margin) !important; } } + +.responsive-padding { + padding: calc(var(--universal-padding) / 4) !important; } + @media screen and (min-width: 500px) { + .responsive-padding { + padding: calc(var(--universal-padding) / 2) !important; } } + @media screen and (min-width: 1280px) { + .responsive-padding { + padding: var(--universal-padding) !important; } } + +@media screen and (max-width: 499px) { + .hidden-sm { + display: none !important; } } +@media screen and (min-width: 500px) and (max-width: 1279px) { + .hidden-md { + display: none !important; } } +@media screen and (min-width: 1280px) { + .hidden-lg { + display: none !important; } } +@media screen and (max-width: 499px) { + .visually-hidden-sm { + position: absolute !important; + width: 1px !important; + height: 1px !important; + margin: -1px !important; + border: 0 !important; + padding: 0 !important; + clip: rect(0 0 0 0) !important; + -webkit-clip-path: inset(100%) !important; + clip-path: inset(100%) !important; + overflow: hidden !important; } } +@media screen and (min-width: 500px) and (max-width: 1279px) { + .visually-hidden-md { + position: absolute !important; + width: 1px !important; + height: 1px !important; + margin: -1px !important; + border: 0 !important; + padding: 0 !important; + clip: rect(0 0 0 0) !important; + -webkit-clip-path: inset(100%) !important; + clip-path: inset(100%) !important; + overflow: hidden !important; } } +@media screen and (min-width: 1280px) { + .visually-hidden-lg { + position: absolute !important; + width: 1px !important; + height: 1px !important; + margin: -1px !important; + border: 0 !important; + padding: 0 !important; + clip: rect(0 0 0 0) !important; + -webkit-clip-path: inset(100%) !important; + clip-path: inset(100%) !important; + overflow: hidden !important; } } + +/*# sourceMappingURL=mini-custom.css.map */ + +img[alt="ST logo"] { display: block; margin: auto; width: 75%; max-width: 250px; min-width: 71px; } +img[alt="Cube logo"] { float: right; width: 30%; max-width: 10rem; min-width: 8rem; padding-right: 1rem;} + +.figure { + display: block; + margin-left: auto; + margin-right: auto; + text-align: center; +} \ No newline at end of file diff --git a/src/STM32CubeWL/LoRaWAN/_htmresc/st_logo_2020.png b/src/STM32CubeWL/LoRaWAN/_htmresc/st_logo_2020.png new file mode 100644 index 0000000000000000000000000000000000000000..d6cebb5ac70e0594cbe37a6b60036a80ef3bed37 GIT binary patch literal 7520 zcmcI|WmKC@*KTl!K!Z~t6qn*&DDF_CK%q#B6QmH_DHL}I6b%;K-J$eBN|2xh0+bea zmtyV5bG|?CpZBcu=iF<}z4q*xz1LhL*PcBwx;m;Pgmi=e0DweYO-UaBz)*UWZ}4#+ zCC-V!SC16}H#HLv0Dy?%--0o{5_}H;Jf%=ql7H=+dziOk_)KzUSaiQ9L<^g!49k}} z?4y$V zrsn3Ul^TY`00G_J_8;F7Sr5c3Y)f-yOYl{fS0avfKJg7R%r!y-ohcBkr6XBZBkE)( z_t+tVV2}G1_CN!)yWes*wa-AGwk31N_T1`)4COlfz+o(9S90e?6jHV4)!>`j+oRqp z)gb?rtSdw<#&c?q!OKB+Ncn!kr5JR2EYan8HAPXBw*Oh-F(6GmaC!`$p7fl!_Cdu> z5@PUMR_K5&jgevDepWqepZVdMHR$q>ga=7k0ltW$HHVn>HRa!#(+BW_jN$5rx^MtR zzWT7tNWQeFzEp+86jOYI=I)*GZYEiXlf-Mw%tJ^ptL%2)%#PmUjxC4wx@%KxQ)8kO?|7E1 zd@5gd9_$*6nx?fj*iF_EJT*PYLFP}d8*Fw>cu3@&nm%V_C}V7HMmg|Nl#1J1^#)uz zZag|X>{6j-myvL}3(H8o3resNBq_jcuJuyT!QuXnNN&zG^tG? z>y*dy1#XfwyCE#UiLT(~LYAVwp)epErJLsHBA|l0xo)kxLAPob+7tFlr{7x8(#v|O zknK}QC6|~=cTJ0;x9MWMKua;LdTKIHX>35a@7{5y0MQ|zqsP64mM~`gJ&&R{pSUBA zf3Y>k>d|xdzBFztjtO~{y%@LUdwSgzEj=69hH3-NNWx7<|49%2%lvTEeE3_|$~iDd zlktnxf|NB>Bui)yY;%TXgWA;rhODxR!-4GzxKd473I)3;j zS4-Vun<^vqZ4)HbwVzq%5%)S!>Q(Y&PkF ze1ab_oy?_$PZT-mKJ3K^;6MrB*Wc$lkYJ_460AFYEq{AjlDxm;5GRwOsm>doJy3Z;a$FSu-q`X)Hw&o~?_`Dnpx4dOj6#)bGOdZbj4hD`l|&#v~K+WrGO zpLCVQg=*h=4H1sK)D*Pxtb#p`c+g-eK65k!CEZjLIrfm}5<4@KVqZ#8(S$vs{?Xv) zN(}CKeeLR%I)W!p(l-`utKmBbJk-On{)ciqUN5%?TS8uwKU(8}Mxhsb&qHL~DDcB@qi} z4z%efV#K69a#lShPt5}*7ME@^xunFR{k;|qp z`_Hgdj_Qle?%Ydlr3f^SdRkUncIj7qTiu85tA$v`#419Nlof8tjqUjO z!Rwyq$Krfh`(@MiE+)yie&jU^crprYV&?zN!2b33S2qtW+zB$nahDhVZiQ12bmcwYBcT8KS>v`jsuI@X zy^k^7>TM5Ndp@}pSl6vmkk*u`@C`z(gq||K2>y=Y#w1i`Nk2zAP7{_^ACq;bQmT<4 z;b~K3=?mhZX#~jQnZjdU={NFp^HRr~;^PXQ*UIHzkq}Xsn!56*ZCOOEt|^ zawUwGqs;zCEGKTz&(a(@h#0sCE+@`3Y<&}9!(;Pw&`C+d#D?`##&!@*ERaH0fksrh zfk2svh3!d~h{SC6BNKN2j6(lzC>S^_*eh{@gR zP$rV4$nqV1y|zYpd;nQr-`Vlp(5sQXx9AibC?xc7pUV)v2cWfHu3{KLbfP;;;`I&)mWqLcXt6s+5fps9R?K>hGR$5;=( zo#;zzQ5!w+(99p1_gh^?F#$tpMd+4%G|K`XG_#@A>30WD2L9!0a>wRASg`M?^z#)| zifb$d^KUD&3qLsr-@}r_7p${gdmRqhy819bj?yI-v({I?0o3_8d>CZmCzqH4{{QtD z|6dvgm<^~668G`6wLw4C4y-o9E9ZG3RiH^&;rrKSBT@bMR+!TlYo*9P;?Z>xORMV3ndQp9EyNn!!~j&x&$gDVke|t#!{QoegHm+sT7cSjwSu z)jZlx1pHrrx0&YCZWJ4xC&U%r_IwfJOxnYqYMcZn&_hgnKuls z>(Y$qjIhcweo^A_VVq_#UR&urF%UbI^><5L5zbV9owMQviM*_{@i|dke!!v8a_yG! zl%#ay{(6U_N3-)yeif3tx6>aQSf1r zO~OuNW<8a~bi6Xby0@tKw0@f1R!@+3S2nf#F&j~l0mrNCtjoZdPYob?g!Vx4F-uA2 z4uS>8eR?dA9i>C|cm-m5lO0m@$f(DB6Z<9Pe07xR`l4$ks3eZ@v5}1?^YNQy-tB(` zgUNZrGVf#b~`}jew;MQG1O~VDk_i*<)p9DfFfd;vLy4QZT zMYh|DxJg3-!{szUN*uo&`&DUkzq4qG2`Lj;Ar46DYUymcviS{Unhzmx z=LwZ-Lr{t1z?9Oip%!z{j+Z%_@u?C78I`V4fC`icG1c|2;`EoLK$ z)9|wrAV&V1Vgts~tQCw0X?X{74W"NJQ zW3BP!5c~4zzHJScwQK9L_%m%L`*q{1aW_3rObki~B~=Dwp`;@w`>xZ6;L_6oR^ecEcmup`yb}lgsmv zQByrxc)Qqm(Hwmq%fLjqcrJ5QR?z_*vb~Iy~RXUY8u-D8AqTzetwNNA8~N z$j&1>#IrkSslUqXW|l}q_TlTVKAjRI@~7(GLf4M>GM!3u_)y6ORe6BQUmrQ16%OdZKo(?AYXK$u@=)TPUi1EZaLBu zuEB(5GX2vcoHS%zMUW!DZ)&xRo@mhouk|hMgHIG>cO-Ah(0AMNlq@?cPpy==-!#qR zBov7l3G?P*KM(vU;Apo-(-Bw;sdQJkh~r)W$KRQKd!#(A8M34^MhD10Ra={AqSvjy z3>PkgA}f^@Rg1$dX=jaEB_V%_vn(W<111_s!g>CYrcwJTJ63Eeg6{_C`aMQIgKctR zHMjkr+y6gK!v6=6;CGl#?PqmiX`A@dmmvZ}-X$*q8}G(xq|voo`=$YZuQAa8f9ApK z1VQiEUQC=OO{|Nc8ZrXH208X|XCzIRO?+Nb55Jt@va9hqBpv)uY6ma-csvSlJoJOC zK;w~s?#r)K@gm*;((JsWHU-KvFTF)q>AzwPq)I+}OrSpxh!-CM1VEMfw1fpfVL7p{ z)I$rNp5kPYDsvXL>8nV{#0fa*lm`Z;0Z=c^1qveY2uk&7nhMRl`0EsvUuvwdF&o)PCVN3wmFBPegWzooF6y~b} zY>X2;LO~&rbvx59?(4^aR+s1C6hI4r&x9Q9jL9);KZEw_x%TWZXaMzK6|2XG9$IU% zkEq}t^YOaa4s`%7F31X-#SfMgNp+4R=g2G|O^W)6^2fEsmkTTaVhKCip+3qWuN8?y zaSD_k6%-@Ifhm`z02=ZWA-pi5`A=7ztHWpP2K5rCW{>!wIUOYrH``#bz>qVSH76=8 zyJ!sNBxt;dMTn}yzUTCq1!w;N7pzb?WJrQ50W+`meL{k8i0VhFsPUz(07k`l` zg1Ifl1fc-^p^MQCpK~Jk&L!*mWfNA!&MSu`sPmti>K-;I5Dk00nB3vOl4!JwjEx^X zWQsIp2Ea_>`9f@%zGl4i3FAO9=e$2^b_>_I*dqiLJ=@TejStM?^yX$9dW`#ha)PEF zPr0zUJX!j-juYK*olG_9axQniXhF|E}oSTG_S)FZ3sN4T@q zy{l;!{UGc};YVaT97tx3s7P$WsW2^*I+Hy;vqndG!9S?tZtDIRif1=bBsqtW-n7$O zWy}Z%jKSkgLX#1p>|#4l-!$c+kA`++Ixv(Rm`;Ilb3q3tD|QjaPQ8)BJ=8R*15?nL zJR{yuqOG&!JrSZ${#NY#b!k)yDajQ$A}fUQhe7ueN6h2Pj3wY5eo|7$PaJD zM69(ee1qlSyLn)Ln6)o53Lj)elqG}gb^c}qd(q&$8B5N%nSvEjUIoEXO^obv=A7QGZzNzjO*H=*b2!86Wnqdy~8ePkq@1D1)Q^O)JG;fUCV z`rgD}dJ{7BUyBbgoTL91w^q-CfBpDr?0R38`L%p9&Q#%r*@=9p-Ioqy+WscLq?jq5 zfyYNkxr7w)-(!c85mt!FjNJ4UE6P8p8sc#Kb4L1N3!yaCo9@t3n8s}K)1!w8x77xU zo-NY3SR*Pgt#~7F;y^J&Y%z^+C9wu;hN|TC7dqSHOB&kE)Q)6Ad(l&+tA@$@w0bhJ z6xc6EJ6nm{=|V7Vo&qcXc4i)D?X0Uk$p7~iI#ci?#fxvM z78`u%m5S@^_F;_foidufzP z&ueh-zU1yM&8~0lmOfkio-gBex`)?hCJcI8Jv*R0c|WUqSq z?RU?Rmm=3t_2C%%UW9c*D%T{T{EjryCKnsz9-*Bs0}M*xS&-odhCK5eS_Eqi&c6J4o|f&;uUT=p7Fedw#EUl4%Jv{w zq-cCF^otBvw~~$yj6O>>;VqU)3Ml&Atm$B(Ht8=+&x2VS5aWD&t*+04{@k^Gn~yHY zWYC%@ld;g_qz=k*;Iv&xs5M85cEZCU&n2Baf>R6*bN>V|!)eF&l#C93eAMLD=ZF7= zOISFfB5P;F!ngWD3jfMJE_53F&dFc{h2TwSbT*(h6!c}_5_UFeB_*DiyCrtQ7Byr@ z-aCWVt?J}YP7-lfelbrCgZ5mdv(^W&Yf^OvpI}#NbS1Lc`r4&t#3kM!utm&#a;?GR z-1FusfX7&?a9h`%8wf-ub7UXp44xm4w04)S$}}_hPqn-#IabO^bo6$K07>?%G5Xs( zA$*lz_K>lAJ_o<;fsP-*-%~O(716KRqVq+X@=5J~Z~bba(yWL`;EN`@Mr2it&Lkgb z&I|R5!`ZSk%)xFX*FYDaAh=5qWSY@rx1Du9J$$=lh#!~NcFJM&aOv)eqg_@`i^zJCR9a%_{k_i%msw5q z*3!~#Xx3r|VaAwiG}~9M#c`=$Uk6~xe)47ymW+;0DD_lV67L#ouJBa7mF{)X^?eIvPdOMu4ksr`jUP$~{x@?ev2 zdX#ite2RMim01PEg`;qfwg^;v8nx9F!CtNCvz)V{PVgCs*;_@_Rk*oLx@f7hJ7^^1 zu66b)mb!ZMXLZ%8dj-+JCMoWS-Wji-Q-~U7Pt~UiXMej8JcNjk<6?Wk9eLBIhu&Vo^0;AH> z;Q5^a!NZhP(vDfBv&?jdnQavv#8N0E{ZEgs>|1)qYo^t5 zIV$g~`C2%g$*(z4)8hJlK2mF-MJW#3%Cs6`uAvsgRtTo8n!Et)1pZx@_9I18 zw7ER9v~DyrC*R$do9aEw5r@B)YzHOLB^-c9Eu9D!sFmI8^VljVKE89{zY_8PTSF+J b^}%0^qYmp2WD(pA|JtZ4>nPPKybJpu3@X?! literal 0 HcmV?d00001 diff --git a/src/STM32CubeWL/LoRaWAN/readme.md b/src/STM32CubeWL/LoRaWAN/readme.md new file mode 100644 index 0000000..cf7f77a --- /dev/null +++ b/src/STM32CubeWL/LoRaWAN/readme.md @@ -0,0 +1,412 @@ +# LoRaWAN end-device stack implementation and example projects + + ______ _ + / _____) _ | | + ( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | + (______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013-2021 Semtech + + ___ _____ _ ___ _ _____ ___ ___ ___ ___ + / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + embedded.connectivity.solutions=============== + +## Introduction + +The aim of this project is to show an example of an end-device LoRaWAN stack implementation. + +This project has 1 active branches in place. + +| Branch | L2 spec | RP spec | Tag/Milestone | Class | Comments | +| ------------- |:-------------:|:---------:|:---------:|:---------:|:--------------| +| | [1.0.4](https://lora-alliance.org/resource-hub/lorawan-104-specification-package) | [2-1.0.1](https://lora-alliance.org/sites/default/files/2020-02/rp_2-1.0.1.pdf) | [v4.5.2](https://github.com/Lora-net/LoRaMac-node/releases/tag/v4.5.2) | A/B/C | LoRaWAN L2 1.0.4 - **_Released_** | +| | [1.0.3](https://lora-alliance.org/resource-hub/lorawanr-specification-v103) | [v1.0.3revA](https://www.lora-alliance.org/resource-hub/lorawanr-regional-parameters-v103reva) | [v4.4.7](https://github.com/Lora-net/LoRaMac-node/releases/tag/v4.4.7) | A/B/C | LoRaWAN L2 1.0.3 - **_Released_ (last release based on 1.0.3)** | +| [master](https://github.com/Lora-net/LoRaMac-node/tree/master) | [1.0.4](https://lora-alliance.org/resource-hub/lorawan-104-specification-package) / [1.1.1](https://lora-alliance.org/resource-hub/lorawanr-specification-v11) | [2-1.0.1](https://lora-alliance.org/sites/default/files/2020-02/rp_2-1.0.1.pdf) | [M 4.6.0](https://github.com/Lora-net/LoRaMac-node/milestone/3) | A/B/C | LoRaWAN L2 1.0.4 / 1.1.1 | + +This project fully implements ClassA, ClassB and ClassC end-device classes and it also provides SX1272/73, SX1276/77/78/79, SX1261/2 and LR1110 radio drivers. + +For each currently supported platform example applications are provided. + +* **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/\*) 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](https://stackforce.github.io/LoRaMac-doc/LoRaMac-doc-v4.5.1/_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) + +* SAMR34 + * [SAMR34 platform documentation](doc/SAMR34-platform.md) + +## Getting Started + +### Prerequisites + +Please follow instructions provided by [Development environment](doc/development-environment.md) document. + +### Cloning the repository + +Clone the repository from GitHub + +```bash +$ git clone https://github.com/lora-net/loramac-node.git loramac-node +``` + +LoRaMac-node project contains Git submodules that must be initialized + +```bash +$ cd loramac-node +$ git submodule update --init +``` + +### Secure-element commissioning + +This project currently supports 3 different secure-elements `soft-se`, `lr1110-se` +and `atecc608a-tnglora-se` implementations. + +In order to personalize the MCU binary file with LoRaWAN EUIs or/and AES128 keys +one must follow the instructions provided by [soft-se](####soft-se), + [lr1110-se](####lr1110-se) and [atecc608a-tnglora-se](####atecc608a-tnglora-se) chapters + +#### soft-se + +*soft-se* is a pure software emulation of a secure-element. It means that everything is located on the host MCU memories. The `DevEUI`, `JoinEUI` and `AES128 keys` may be stored on a non-volatile memory through dedicated APIs. + +In order to update the end-device identity (`DevEUI`, `JoinEUI` and `AES128 keys`) one must update the `se-identity.h` file located under `./src/peripherals/soft-se/` directory. + +**Note:** In previous versions of this project this was done inside `Commissioning.h` files located under each provided example directory. + +#### lr1110-se + +*lr1110-se* abstraction implementation handles all the required exchanges with the LR1110 radio crypto-engine. + +All LR1110 radio chips are pre-provisioned out of factory in order to be used with [LoRa Cloud Device Join Service](https://www.loracloud.com/documentation/join_service). + +In case other Join Servers are to be used the `DevEUI`, `Pin`, `JoinEUI` and `AES128 keys` can be updated by following the instructions provided on chapter "13. LR1110 Provisioning" of the [LR1110 User Manual](https://semtech.my.salesforce.com/sfc/p/#E0000000JelG/a/2R000000Q2PM/KGm1YHDoHhtaicNYHCIAnh0CbG3yodEuWWJ2WrFRafM). + +When the compile option `SECURE_ELEMENT_PRE_PROVISIONED` is set to `ON` the *lr1110-se* will use the factory provisioned data (`DevEUI`, `JoinEUI` and `AES128 keys`). +When the compile option `SECURE_ELEMENT_PRE_PROVISIONED` is set to `OFF` the *lr1110-se* has to be provisioned by following one of the methods described on chapter "13. LR1110 Provisioning" of the [LR1110 User Manual](https://semtech.my.salesforce.com/sfc/p/#E0000000JelG/a/2R000000Q2PM/KGm1YHDoHhtaicNYHCIAnh0CbG3yodEuWWJ2WrFRafM). +The `DevEUI`, `Pin` and `JoinEUI` can be changed by editing the `se-identity.h` file located in `./src/peripherals/lr1110-se/` directory. + +#### atecc608a-tnglora-se + +The *atecc608a-tnglora-se* abstraction implementation handles all the required exchanges with the ATECC608A-TNGLORA secure-element. + +ATECC608A-TNGLORA secure-element is always pre-provisioned and its contents can't be changed. + +### Building Process + +#### Command line + +**periodic-uplink-lpp** example for NucleoL476 platform with LR1110MB1DIS MBED shield and using LR1110 pre-provisioned secure-element + +```bash +$ mkdir build +$ cd build +$ cmake -DCMAKE_BUILD_TYPE=Release \ + -DTOOLCHAIN_PREFIX="" \ + -DCMAKE_TOOLCHAIN_FILE="../cmake/toolchain-arm-none-eabi.cmake" \ + -DAPPLICATION="LoRaMac" \ + -DSUB_PROJECT="periodic-uplink-lpp" \ + -DCLASSB_ENABLED="ON" \ + -DACTIVE_REGION="LORAMAC_REGION_EU868" \ + -DREGION_EU868="ON" \ + -DREGION_US915="OFF" \ + -DREGION_CN779="OFF" \ + -DREGION_EU433="OFF" \ + -DREGION_AU915="OFF" \ + -DREGION_AS923="OFF" \ + -DREGION_CN470="OFF" \ + -DREGION_KR920="OFF" \ + -DREGION_IN865="OFF" \ + -DREGION_RU864="OFF" \ + -DBOARD="NucleoL476" \ + -DMBED_RADIO_SHIELD="LR1110MB1XXS" \ + -DSECURE_ELEMENT="LR1110_SE" \ + -DSECURE_ELEMENT_PRE_PROVISIONED="ON" \ + -DUSE_RADIO_DEBUG="ON" .. +$ make +``` + +**ping-pong** example using LoRa modulation for NucleoL476 platform with LR1110MB1DIS MBED shield + +```bash +$ mkdir build +$ cd build +$ cmake -DCMAKE_BUILD_TYPE=Release \ + -DTOOLCHAIN_PREFIX="" \ + -DCMAKE_TOOLCHAIN_FILE="../cmake/toolchain-arm-none-eabi.cmake" \ + -DAPPLICATION="ping-pong" \ + -DMODULATION:"LORA" \ + -DREGION_EU868="ON" \ + -DREGION_US915="OFF" \ + -DREGION_CN779="OFF" \ + -DREGION_EU433="OFF" \ + -DREGION_AU915="OFF" \ + -DREGION_AS923="OFF" \ + -DREGION_CN470="OFF" \ + -DREGION_KR920="OFF" \ + -DREGION_IN865="OFF" \ + -DREGION_RU864="OFF" \ + -DBOARD="NucleoL476" \ + -DMBED_RADIO_SHIELD="LR1110MB1XXS" \ + -DUSE_RADIO_DEBUG="ON" .. +$ make +``` + +#### VSCode + +**periodic-uplink-lpp** example for NucleoL476 platform with LR1110MB1DIS MBED shield and using LR1110 pre-provisioned secure-element + +* Please edit .vscode/settings.json file + +
+ Click to expand! +

+ +```json +// Place your settings in this file to overwrite default and user settings. +{ + "cmake.configureSettings": { + + // In case your GNU ARM-Toolchain is not installed under the default + // path: + // Windows : No default path. Specify the path where the + // toolchain is installed. i.e: + // "C:/PROGRA~2/GNUTOO~1/92019-~1". + // Linux : /usr + // OSX : /usr/local + // It is required to uncomment and to fill the following line. + "TOOLCHAIN_PREFIX":"/path/to/toolchain", + + // In case your OpenOCD is not installed under the default path: + // Windows : C:/openocd/bin/openocd.exe + // Linux : /usr/bin/openocd + // OSX : /usr/local/bin/openocd + // Please uncomment the following line and fill it accordingly. + //"OPENOCD_BIN":"C:/openocd/bin/openocd.exe", + + // Specifies the path to the CMAKE toolchain file. + "CMAKE_TOOLCHAIN_FILE":"cmake/toolchain-arm-none-eabi.cmake", + + // Determines the application. You can choose between: + // LoRaMac (Default), ping-pong, rx-sensi, tx-cw. + "APPLICATION":"LoRaMac", + + // Select LoRaMac sub project. You can choose between: + // periodic-uplink-lpp, fuota-test-01. + "SUB_PROJECT":"periodic-uplink-lpp", + + // Switch for Class B support of LoRaMac: + "CLASSB_ENABLED":"ON", + + // Select the active region for which the stack will be initialized. + // You can choose between: + // LORAMAC_REGION_EU868, LORAMAC_REGION_US915, .. + "ACTIVE_REGION":"LORAMAC_REGION_EU868", + + // Select the type of modulation, applicable to the ping-pong or + // rx-sensi applications. You can choose between: + // LORA or FSK + "MODULATION":"LORA", + + // Target board, the following boards are supported: + // NAMote72, NucleoL073 (Default), NucleoL152, NucleoL476, SAMR34, SKiM880B, SKiM980A, SKiM881AXL, B-L072Z-LRWAN1. + "BOARD":"NucleoL476", + + // MBED Radio shield selection. (Applies only to Nucleo platforms) + // The following shields are supported: + // SX1272MB2DAS, SX1276MB1LAS, SX1276MB1MAS, SX1261MBXBAS(Default), SX1262MBXCAS, SX1262MBXDAS, LR1110MB1XXS. + "MBED_RADIO_SHIELD":"LR1110MB1XXS", + + // Secure element type selection the following are supported + // SOFT_SE(Default), LR1110_SE, ATECC608A_TNGLORA_SE + "SECURE_ELEMENT":"LR1110_SE", + + // Secure element is pre-provisioned + "SECURE_ELEMENT_PRE_PROVISIONED":"ON", + + // Region support activation, Select the ones you want to support. + // By default only REGION_EU868 support is enabled. + "REGION_EU868":"ON", + "REGION_US915":"OFF", + "REGION_CN779":"OFF", + "REGION_EU433":"OFF", + "REGION_AU915":"OFF", + "REGION_AS923":"OFF", + "REGION_CN470":"OFF", + "REGION_KR920":"OFF", + "REGION_IN865":"OFF", + "REGION_RU864":"OFF", + "USE_RADIO_DEBUG":"ON" + } +} +``` + +

+
+ +* Click on "CMake: Debug: Ready" and select build type Debug or Release. +![cmake configure](doc/images/vscode-cmake-configure.png) +* Wait for configuration process to finish +* Click on "Build" to build the project. +![cmake build](doc/images/vscode-cmake-build.png) +* Wait for build process to finish +* Binary files will be available under `./build/src/apps/LoRaMac/` + * LoRaMac-periodic-uplink-lpp - elf format + * LoRaMac-periodic-uplink-lpp.bin - binary format + * LoRaMac-periodic-uplink-lpp.hex - hex format + +**ping-pong** example using LoRa modulation for NucleoL476 platform with LR1110MB1DIS MBED shield + +* Please edit .vscode/settings.json file + +
+ Click to expand! +

+ +```json +// Place your settings in this file to overwrite default and user settings. +{ + "cmake.configureSettings": { + + // In case your GNU ARM-Toolchain is not installed under the default + // path: + // Windows : No default path. Specify the path where the + // toolchain is installed. i.e: + // "C:/PROGRA~2/GNUTOO~1/92019-~1". + // Linux : /usr + // OSX : /usr/local + // It is required to uncomment and to fill the following line. + "TOOLCHAIN_PREFIX":"/path/to/toolchain", + + // In case your OpenOCD is not installed under the default path: + // Windows : C:/openocd/bin/openocd.exe + // Linux : /usr/bin/openocd + // OSX : /usr/local/bin/openocd + // Please uncomment the following line and fill it accordingly. + //"OPENOCD_BIN":"C:/openocd/bin/openocd.exe", + + // Specifies the path to the CMAKE toolchain file. + "CMAKE_TOOLCHAIN_FILE":"cmake/toolchain-arm-none-eabi.cmake", + + // Determines the application. You can choose between: + // LoRaMac (Default), ping-pong, rx-sensi, tx-cw. + "APPLICATION":"ping-pong", + + // Select LoRaMac sub project. You can choose between: + // periodic-uplink-lpp, fuota-test-01. + "SUB_PROJECT":"periodic-uplink-lpp", + + // Switch for Class B support of LoRaMac: + "CLASSB_ENABLED":"ON", + + // Select the active region for which the stack will be initialized. + // You can choose between: + // LORAMAC_REGION_EU868, LORAMAC_REGION_US915, .. + "ACTIVE_REGION":"LORAMAC_REGION_EU868", + + // Select the type of modulation, applicable to the ping-pong or + // rx-sensi applications. You can choose between: + // LORA or FSK + "MODULATION":"LORA", + + // Target board, the following boards are supported: + // NAMote72, NucleoL073 (Default), NucleoL152, NucleoL476, SAMR34, SKiM880B, SKiM980A, SKiM881AXL, B-L072Z-LRWAN1. + "BOARD":"NucleoL476", + + // MBED Radio shield selection. (Applies only to Nucleo platforms) + // The following shields are supported: + // SX1272MB2DAS, SX1276MB1LAS, SX1276MB1MAS, SX1261MBXBAS(Default), SX1262MBXCAS, SX1262MBXDAS, LR1110MB1XXS. + "MBED_RADIO_SHIELD":"SX1261MBXBAS", + + // Secure element type selection the following are supported + // SOFT_SE(Default), LR1110_SE, ATECC608A_TNGLORA_SE + "SECURE_ELEMENT":"SOFT_SE", + + // Secure element is pre-provisioned + "SECURE_ELEMENT_PRE_PROVISIONED":"ON", + + // Region support activation, Select the ones you want to support. + // By default only REGION_EU868 support is enabled. + "REGION_EU868":"ON", + "REGION_US915":"OFF", + "REGION_CN779":"OFF", + "REGION_EU433":"OFF", + "REGION_AU915":"OFF", + "REGION_AS923":"OFF", + "REGION_CN470":"OFF", + "REGION_KR920":"OFF", + "REGION_IN865":"OFF", + "REGION_RU864":"OFF", + "USE_RADIO_DEBUG":"ON" + } +} +``` + +

+
+ +* Click on "CMake: Debug: Ready" and select build type Debug or Release. +![cmake configure](doc/images/vscode-cmake-configure.png) +* Wait for configuration process to finish +* Click on "Build" to build the project. +![cmake build](doc/images/vscode-cmake-build.png) +* Wait for build process to finish +* Binary files will be available under `./build/src/apps/ping-pong/` + * ping-pong - elf format + * ping-pong.bin - binary format + * ping-pong.hex - hex format + +### Serial console NVM management + +The `periodic-uplink-lpp` and `fuota-test-01` examples allow to reset the NVM storage through the serial interface. + +In order to reset the NVM contents one must hit `ESC` + `N` keyboard keys on a serial terminal. + +The serial terminal will show the following after `ESC` + `N` keyboard keys are hit. After resetting the end-device the clean NVM will be used. + +```text +ESC + N + + +NVM factory reset succeed + + +PLEASE RESET THE END-DEVICE +``` + +## 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. +* [The Things Industries](https://www.thethingsindustries.com/) for providing + Microchip/Atmel SAMR34 platform and ATECC608A-TNGLORA secure-element support. +* Tencent Blade Team for security breach findings and solving propositions. diff --git a/src/STM32CubeWL/Package_license.md b/src/STM32CubeWL/Package_license.md new file mode 100644 index 0000000..2788f54 --- /dev/null +++ b/src/STM32CubeWL/Package_license.md @@ -0,0 +1,179 @@ +--- +pagetitle: Package License Terms for STM32CubeWL +lang: en +header-includes: +--- + +::: {.row} +::: {.col-sm-12 .col-lg-3} + +
+# These are full license terms for + +# STM32CubeWL + +Copyright © 2021 STMicroelectronics + +All rights reserved + +[![ST logo](_htmresc/st_logo_2020.png)](https://www.st.com){.logo} +
+::: + +::: {.col-sm-12 .col-lg-9} +::: {.collapse} + + +
+ +SLA0048 Rev4/March 2018 + +## Software license agreement + +### __SOFTWARE PACKAGE LICENSE AGREEMENT__ + +BY INSTALLING COPYING, DOWNLOADING, ACCESSING OR OTHERWISE USING THIS SOFTWARE PACKAGE OR ANY +PART THEREOF (AND THE RELATED DOCUMENTATION) FROM STMICROELECTRONICS INTERNATIONAL N.V, SWISS +BRANCH AND/OR ITS AFFILIATED COMPANIES (STMICROELECTRONICS), THE RECIPIENT, ON BEHALF OF HIMSELF +OR HERSELF, OR ON BEHALF OF ANY ENTITY BY WHICH SUCH RECIPIENT IS EMPLOYED AND/OR ENGAGED +AGREES TO BE BOUND BY THIS SOFTWARE PACKAGE LICENSE AGREEMENT. + +Under STMicroelectronics’ intellectual property rights and subject to applicable licensing terms for any third-party software +incorporated in this software package and applicable Open Source Terms (as defined here below), the redistribution, +reproduction and use in source and binary forms of the software package or any part thereof, with or without modification, are +permitted provided that the following conditions are met: + +1. Redistribution of source code (modified or not) must retain any copyright notice, this list of conditions and the following +disclaimer. + +2. Redistributions in binary form, except as embedded into microcontroller or microprocessor device manufactured by or for +STMicroelectronics or a software update for such device, 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. + +3. Neither the name of STMicroelectronics nor the names of other contributors to this software package may be used to +endorse or promote products derived from this software package or part thereof without specific written permission. + +4. This software package or any part thereof, including modifications and/or derivative works of this software package, must +be used and execute solely and exclusively on or in combination with a microcontroller or a microprocessor devices +manufactured by or for STMicroelectronics. + +5. No use, reproduction or redistribution of this software package partially or totally may be done in any manner that would +subject this software package to any Open Source Terms (as defined below). + +6. Some portion of the software package may contain software subject to Open Source Terms (as defined below) applicable +for each such portion (“Open Source Software”), as further specified in the software package. Such Open Source Software +is supplied under the applicable Open Source Terms and is not subject to the terms and conditions of license hereunder. +“Open Source Terms” shall mean any open source license which requires as part of distribution of software that the source +code of such software is distributed therewith or otherwise made available, or open source license that substantially +complies with the Open Source definition specified at www.opensource.org and any other comparable open source license +such as for example GNU General Public License (GPL), Eclipse Public License (EPL), Apache Software License, BSD +license and MIT license. + +7. This software package may also include third party software as expressly specified in the software package subject to +specific license terms from such third parties. Such third party software is supplied under such specific license terms and is +not subject to the terms and conditions of license hereunder. By installing copying, downloading, accessing or otherwise +using this software package, the recipient agrees to be bound by such license terms with regard to such third party +software. + +8. STMicroelectronics has no obligation to provide any maintenance, support or updates for the software package. + +9. The software package is and will remain the exclusive property of STMicroelectronics and its licensors. The recipient will +not take any action that jeopardizes STMicroelectronics and its licensors' proprietary rights or acquire any rights in the +software package, except the limited rights specified hereunder. + +10. The recipient shall comply with all applicable laws and regulations affecting the use of the software package or any part +thereof including any applicable export control law or regulation. + +11. Redistribution and use of this software package partially or any part thereof other than as permitted under this license is +void and will automatically terminate your rights under this license. + +THIS SOFTWARE PACKAGE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY +INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT +SHALL STMICROELECTRONICS OR CONTRIBUTORS 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 PACKAGE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +EXCEPT AS EXPRESSLY PERMITTED HEREUNDER AND SUBJECT TO THE APPLICABLE LICENSING TERMS FOR ANY +THIRD-PARTY SOFTWARE INCORPORATED IN THE SOFTWARE PACKAGE AND OPEN SOURCE TERMS AS +APPLICABLE, NO LICENSE OR OTHER RIGHTS, WHETHER EXPRESS OR IMPLIED, ARE GRANTED UNDER ANY +PATENT OR OTHER INTELLECTUAL PROPERTY RIGHTS OF STMICROELECTRONICS OR ANY THIRD PARTY. + +
+::: + +::: {.collapse} + + +
+Component Copyright License +--------- --------- ------- +CMSIS ARM Limited [Apache-2.0](https://opensource.org/licenses/Apache-2.0) +CMSIS Device ARM Limited - STMicroelectronics [Apache-2.0](https://opensource.org/licenses/Apache-2.0) +STM32WL HAL STMicroelectronics [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) +BSP NUCLEO-WL55JC STMicroelectronics [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) +FreeRTOS kernel Amazon.com, Inc. or its affiliates [MIT](https://opensource.org/licenses/MIT) +FatFS ChaN - STMicroelectronics [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) +SubGHz_Phy Semtech - STMicroelectronics [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) +LoRaWAN Semtech - STMicroelectronics [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) +Sigfox Sigfox - STMicroelectronics Proprietary and See ANNEX 2 +mbed-crypto ARM Limited (or its affiliates) [Apache-2.0](https://opensource.org/licenses/Apache-2.0) +STM32_Key_Management_Services STMicroelectronics Proprietary +STM32_Secure_Engine STMicroelectronics Proprietary +STM32 Projects STMicroelectronics Proprietary ([BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) for basic Examples) +STM32 Utilities STMicroelectronics [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) + +
+::: + +::: {.collapse} + + +
+ +By downloading, or attempting to download, the SIGFOX protocol library "SFX_LIB_V2.10.0_SE_FDL_MON.a" and/or +"SFX_ADDON_RFP_V0.8.0_SE_FDL_MON_ASYNC.a" , you are hereby informed and you acknowledge and agree that any use of +the licensed materials is subject to all terms and conditions required by SIGFOX and listed below: +• You shall + o respect the Partner Certification Process, described in the SIGFOX developer platform at + http://build.SIGFOX.com to embed applicable authentication keys; + o be in compliance with all applicable laws, regulations (including any applicable + export control law or regulation) and industry standards in connection with the manufacture, sale + and/or distribution of certified products; +• You shall not: + o modify or create without SIGFOX written prior agreement any derivative work of any part of the + SIGFOX protocol library + o market, sell, license, distribute, publish, display, reproduce, rent, lease, loan, assign or otherwise + transfer to a third party the SIGFOX protocol library or any copy thereof, in whole or in part; + o install or otherwise incorporate the SIGFOX protocol library into a product except as set + forth in and in accordance with this document; + o use the SIGFOX protocol library in any way that would not be in compliance with applicable laws; + o except to the extent permitted by law, permit the disassembly, decompilation or reverse engineering + of the SIGFOX protocol library or otherwise attempt to gain access to the source code to the SIGFOX + protocol library (or the underlying ideas, algorithms, structure or organization of the object code + in the SIGFOX protocol library) + o incorporate any open source software into any product, including with respect to certified products + sold or distributed, that creates or purports to create any obligation to disclose to a third party + any source code relating to the SIGFOX protocol library or grants or purports to grant to any third + party any intellectual property of SIGFOX; + o develop, sell or distribute any product that undermines SIGFOX network security and integrity and/or + introduces in any SIGFOX network any viruses, worms, Trojan horses or similar harmful or malicious + code, or otherwise harms any SIGFOX network; + o remove or attempt to remove any SIGFOX authentication keys, + o incorporate the SIGFOX protocol library in any product for connection on another network than SIGFOX + network. You shall inform any of your third party that such infringement to the obligations below + would cause significant harm to SIGFOX. + +You shall have no rights to any source code for the SIGFOX protocol library. You agree that only SIGFOX +shall have the right to maintain, enhance or otherwise modify the SIGFOX protocol library. + + +
+::: + +::: +::: \ No newline at end of file diff --git a/src/STM32CubeWL/SubGHz_Phy/LICENSE b/src/STM32CubeWL/SubGHz_Phy/LICENSE new file mode 100644 index 0000000..1a60a54 --- /dev/null +++ b/src/STM32CubeWL/SubGHz_Phy/LICENSE @@ -0,0 +1,27 @@ +The Clear BSD License +Copyright Semtech Corporation 2021. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted (subject to the limitations in the disclaimer +below) 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. + +NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY +THIS LICENSE. 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 CORPORATION 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/src/STM32CubeWL/SubGHz_Phy/LICENSE.txt b/src/STM32CubeWL/SubGHz_Phy/LICENSE.txt new file mode 100644 index 0000000..3edc4d1 --- /dev/null +++ b/src/STM32CubeWL/SubGHz_Phy/LICENSE.txt @@ -0,0 +1,6 @@ +This software component is provided to you as part of a software package and +applicable license terms are in the Package_license file. If you received this +software component outside of a package or without applicable license terms, +the terms of the BSD-3-Clause license shall apply. +You may obtain a copy of the BSD-3-Clause at: +https://opensource.org/licenses/BSD-3-Clause diff --git a/src/STM32CubeWL/SubGHz_Phy/Release_Notes.html b/src/STM32CubeWL/SubGHz_Phy/Release_Notes.html new file mode 100644 index 0000000..5b0b3e4 --- /dev/null +++ b/src/STM32CubeWL/SubGHz_Phy/Release_Notes.html @@ -0,0 +1,106 @@ + + + + + + + Release Notes for STM32WLxx SubGHz Physical Layer Middleware + + + + + + +
+
+
+

Release Notes for

+

STM32WLxx SubGHz Physical Layer Middleware

+

Copyright © 2021 STMicroelectronics
+

+ +
+

Purpose

+

This Middleware provides the SubGHz Physical Layer for the stm32wlxx products. This covers

+
    +
  • STM32WLxx devices
  • +
+

This driver is composed of the radio driver and radio interface under “stm32_radio_driver” directory.

+
+
+

Update History

+
+ +
+

Main Changes

+
    +
  • Feature: Add defines RADIO_SIGFOX_ENABLE and RADIO_GENERIC_CONFIG_ENABLE to reduce code size when not needed
  • +
  • Feature: Add Tx GMSK support
  • +
  • Chore: Align register names with Reference Manual
  • +
  • Chore: Clarify node/broadcast address
  • +
  • Fix: Set_RxDutyCycle() function (Rx timeout Issue)
  • +
  • Licensing update: New way to declare licenses
  • +
  • Release Notes update
  • +
+

Known limitations:

+

None

+
+
+
+ +
+

Main Changes

+
    +
  • fix: SMPS drive level init
  • +
  • fix: GENERIC_BPSK in RadioSetTxGenericConfig function (test mode)
  • +
  • fix: MODEM_BPSK is not handled in RadioSetModem in radio.c
  • +
  • feature: add cfo calculation
  • +
  • fix: reduce interrupt scope to minimize privilege stack code execution
  • +
  • feature:add long packet in radio_fw
  • +
  • feature: Support IBM whitening by firmware radio_fw
  • +
  • fix: Channel Activity Detection: antenna switch set in rx mode
  • +
  • fix: Deadlock of WL RF driver
  • +
  • fix: PingPong RxErrors when SF12, BW500, CR 4/8
  • +
  • feature: move GetFskBandwidthRegValue in radio_driver
  • +
  • fix: Incorrect LoRa PER Results
  • +
  • feature: align to LoRaWAN Semtech stack v4.4.7 integration - Radio Part
  • +
+

Known limitations:

+

None

+
+
+
+ +
+

Main Changes

+
    +
  • First Release
  • +
+

Known limitations:

+

None

+
+
+
+
+
+
+
+

For complete documentation on STM32WLxx, visit: www.st.com/stm32wl

+

This release note uses up to date web standards and, for this reason, should not be opened with Internet Explorer but preferably with popular browsers such as Google Chrome, Mozilla Firefox, Opera or Microsoft Edge.

+
+

Info

+
+
+
+ + diff --git a/src/STM32CubeWL/SubGHz_Phy/_htmresc/Add button.svg b/src/STM32CubeWL/SubGHz_Phy/_htmresc/Add button.svg new file mode 100644 index 0000000..dc4a222 --- /dev/null +++ b/src/STM32CubeWL/SubGHz_Phy/_htmresc/Add button.svg @@ -0,0 +1,2 @@ + + diff --git a/src/STM32CubeWL/SubGHz_Phy/_htmresc/Update.svg b/src/STM32CubeWL/SubGHz_Phy/_htmresc/Update.svg new file mode 100644 index 0000000..f88381f --- /dev/null +++ b/src/STM32CubeWL/SubGHz_Phy/_htmresc/Update.svg @@ -0,0 +1,2 @@ + + diff --git a/src/STM32CubeWL/SubGHz_Phy/_htmresc/favicon.png b/src/STM32CubeWL/SubGHz_Phy/_htmresc/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..06713eec4974e141c6e9b4156d34e61e89f282ca GIT binary patch literal 4126 zcmV+(5aI8MP)ZIGU@QfPy~$kHP}Vz9c$a)g>fT6hB^OaK4>c|MGS0000HbW%=J|NsC0|NsC0 z|NsC0|NsC0041%NVgLXD!bwCyRCwCllie1CAP9sJ&9ooo{hxLjw5@YC+xxf~%TE}{ zNd5%935b--eKa7{Q8)vZ;eI6pGFH>rL)887WOA={e(b_&0g-g6to#Hm2B3vqWV>1u z@z7*I(bdWdx-Y=OT@|og)oZEgAbc7ep|Gf{$7j1Oa#T&r4>0xv(+}tR_4biWa z1Q-BfcsgeLIJPbT0000rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z000F^NklQWRUVq~pvFslpiNq83Yr*1(ELa!!kppKfxQKEkPzz^I>0W)!71xam z(B64`)5a~kHf9SBOp*Wvo{xA*e4|Kv3g6TCo+fF8q^C$|g>#NlXyY$%lmkmCh$x1Z zFbJ_hiDG`37w>KPVB83d7E3>R@-M9$`|}|QFF}1qSk!nanPdVd3K38edo3y+D*=Uo zfOWCwj(F@GSjPG&B<0O;H!Zm0g>eDeK09{b@xB};mEy; z;Gp5H_Cr65qKM1uYIu6x&16!Ei=BHfJLe)1IUnFqc6d#D=ZVQmV9C73vy1=x$SK;s z=*CYZP+Ei1D5Vjl#u88v5dv#jId?iu)8mM}{mD`GcMXtAC5ap?ZoKr&iYuqTKHe$t zTR%R$ZZwxec?l+0r_LIVbe-mzH+D0*ZYsu4BE~~JA2A+AD?BAArO=g8ZfvXtU~o9c zZ(Bd-RFK5jh*DvM1v6^41HB@0K0qn7E8E&Xz1mk6hvmx?*|WB_H!dcO;5R$=<4b~s z5zr3N4zxlkMKOrDeny(PGpEM6^hGyc7lhg>MWtM!af*pn%&q_PxI(n^(-Zd{ICPAp z(WE@Zz5|EZyt{MEDy&l5$Cba^zU!9k&;Vs7lk$@o02LOwb#e2{#3%Cn2>kQF(RKUU z+tW!;FT0@d+TX^L;>@3RytlTP&ts$pqA}TwENBlg9?$-D zF9Sn4URZx8)hVBw<~NY=h3=so7{jEhG%Zc_0QBSvY~K4BS|W5MAem5XSb6m}VDN$f zy+b3n9qjIJoHM5pqYa`IPH9AIoM=!AE1Er>r|3E}#Jq-S)cTrfD&TZjAuN@+V}3mi zlhOce+q<8>Y?i93G=*Z3Web`ri!Q%p%LR*(bB?;|)B`&=J&ab0Z(UKpbzyZV5o*#& z0FLzzaI$v*-#WB)+`n>BoJ*1AwU0XSu;@w&Hu1WYXVR#!8itC%68CzMeiXhL#`9W$9J30NB-Wg!ex`hBlg9F5u@&o4>hd`TWPv z{r{JbyvQGZzbyvPS}zAifbhF49z~X|@9v|!=No>qsF|P~vf=g>7#%0yk<U?Ha8*Z4HW>#8w>z$A3 z>^uQlk;$a-6CQ(uc@RM)Cbss!xuPVl2@c1u-5tEUw}U8OfP_J+QYhfu$B<0Cj3xm7 c?*aZZ00ulYFs-m=@&Et;07*qoM6N<$f~=14i~s-t literal 0 HcmV?d00001 diff --git a/src/STM32CubeWL/SubGHz_Phy/_htmresc/mini-st_2020.css b/src/STM32CubeWL/SubGHz_Phy/_htmresc/mini-st_2020.css new file mode 100644 index 0000000..986f4d4 --- /dev/null +++ b/src/STM32CubeWL/SubGHz_Phy/_htmresc/mini-st_2020.css @@ -0,0 +1,1711 @@ +@charset "UTF-8"; +/* + Flavor name: Custom (mini-custom) + Generated online - https://minicss.org/flavors + mini.css version: v3.0.1 +*/ +/* + Browsers resets and base typography. +*/ +/* Core module CSS variable definitions */ +:root { + --fore-color: #03234b; + --secondary-fore-color: #03234b; + --back-color: #ffffff; + --secondary-back-color: #ffffff; + --blockquote-color: #e6007e; + --pre-color: #e6007e; + --border-color: #3cb4e6; + --secondary-border-color: #3cb4e6; + --heading-ratio: 1.2; + --universal-margin: 0.5rem; + --universal-padding: 0.25rem; + --universal-border-radius: 0.075rem; + --background-margin: 1.5%; + --a-link-color: #3cb4e6; + --a-visited-color: #8c0078; } + +html { + font-size: 13.5px; } + +a, b, del, em, i, ins, q, span, strong, u { + font-size: 1em; } + +html, * { + font-family: -apple-system, BlinkMacSystemFont, Helvetica, arial, sans-serif; + line-height: 1.25; + -webkit-text-size-adjust: 100%; } + +* { + font-size: 1rem; } + +body { + margin: 0; + color: var(--fore-color); + @background: var(--back-color); + background: var(--back-color) linear-gradient(#ffd200, #ffd200) repeat-y left top; + background-size: var(--background-margin); + } + +details { + display: block; } + +summary { + display: list-item; } + +abbr[title] { + border-bottom: none; + text-decoration: underline dotted; } + +input { + overflow: visible; } + +img { + max-width: 100%; + height: auto; } + +h1, h2, h3, h4, h5, h6 { + line-height: 1.25; + margin: calc(1.5 * var(--universal-margin)) var(--universal-margin); + font-weight: 400; } + h1 small, h2 small, h3 small, h4 small, h5 small, h6 small { + color: var(--secondary-fore-color); + display: block; + margin-top: -0.25rem; } + +h1 { + font-size: calc(1rem * var(--heading-ratio) * var(--heading-ratio) * var(--heading-ratio)); } + +h2 { + font-size: calc(1rem * var(--heading-ratio) * var(--heading-ratio) ); + border-style: none none solid none ; + border-width: thin; + border-color: var(--border-color); } +h3 { + font-size: calc(1rem * var(--heading-ratio) ); } + +h4 { + font-size: calc(1rem * var(--heading-ratio)); } + +h5 { + font-size: 1rem; } + +h6 { + font-size: calc(1rem / var(--heading-ratio)); } + +p { + margin: var(--universal-margin); } + +ol, ul { + margin: var(--universal-margin); + padding-left: calc(3 * var(--universal-margin)); } + +b, strong { + font-weight: 700; } + +hr { + box-sizing: content-box; + border: 0; + line-height: 1.25em; + margin: var(--universal-margin); + height: 0.0714285714rem; + background: linear-gradient(to right, transparent, var(--border-color) 20%, var(--border-color) 80%, transparent); } + +blockquote { + display: block; + position: relative; + font-style: italic; + color: var(--secondary-fore-color); + margin: var(--universal-margin); + padding: calc(3 * var(--universal-padding)); + border: 0.0714285714rem solid var(--secondary-border-color); + border-left: 0.3rem solid var(--blockquote-color); + border-radius: 0 var(--universal-border-radius) var(--universal-border-radius) 0; } + blockquote:before { + position: absolute; + top: calc(0rem - var(--universal-padding)); + left: 0; + font-family: sans-serif; + font-size: 2rem; + font-weight: 800; + content: "\201c"; + color: var(--blockquote-color); } + blockquote[cite]:after { + font-style: normal; + font-size: 0.75em; + font-weight: 700; + content: "\a— " attr(cite); + white-space: pre; } + +code, kbd, pre, samp { + font-family: Menlo, Consolas, monospace; + font-size: 0.85em; } + +code { + background: var(--secondary-back-color); + border-radius: var(--universal-border-radius); + padding: calc(var(--universal-padding) / 4) calc(var(--universal-padding) / 2); } + +kbd { + background: var(--fore-color); + color: var(--back-color); + border-radius: var(--universal-border-radius); + padding: calc(var(--universal-padding) / 4) calc(var(--universal-padding) / 2); } + +pre { + overflow: auto; + background: var(--secondary-back-color); + padding: calc(1.5 * var(--universal-padding)); + margin: var(--universal-margin); + border: 0.0714285714rem solid var(--secondary-border-color); + border-left: 0.2857142857rem solid var(--pre-color); + border-radius: 0 var(--universal-border-radius) var(--universal-border-radius) 0; } + +sup, sub, code, kbd { + line-height: 0; + position: relative; + vertical-align: baseline; } + +small, sup, sub, figcaption { + font-size: 0.75em; } + +sup { + top: -0.5em; } + +sub { + bottom: -0.25em; } + +figure { + margin: var(--universal-margin); } + +figcaption { + color: var(--secondary-fore-color); } + +a { + text-decoration: none; } + a:link { + color: var(--a-link-color); } + a:visited { + color: var(--a-visited-color); } + a:hover, a:focus { + text-decoration: underline; } + +/* + Definitions for the grid system, cards and containers. +*/ +.container { + margin: 0 auto; + padding: 0 calc(1.5 * var(--universal-padding)); } + +.row { + box-sizing: border-box; + display: flex; + flex: 0 1 auto; + flex-flow: row wrap; + margin: 0 0 0 var(--background-margin); } + +.col-sm, +[class^='col-sm-'], +[class^='col-sm-offset-'], +.row[class*='cols-sm-'] > * { + box-sizing: border-box; + flex: 0 0 auto; + padding: 0 calc(var(--universal-padding) / 2); } + +.col-sm, +.row.cols-sm > * { + max-width: 100%; + flex-grow: 1; + flex-basis: 0; } + +.col-sm-1, +.row.cols-sm-1 > * { + max-width: 8.3333333333%; + flex-basis: 8.3333333333%; } + +.col-sm-offset-0 { + margin-left: 0; } + +.col-sm-2, +.row.cols-sm-2 > * { + max-width: 16.6666666667%; + flex-basis: 16.6666666667%; } + +.col-sm-offset-1 { + margin-left: 8.3333333333%; } + +.col-sm-3, +.row.cols-sm-3 > * { + max-width: 25%; + flex-basis: 25%; } + +.col-sm-offset-2 { + margin-left: 16.6666666667%; } + +.col-sm-4, +.row.cols-sm-4 > * { + max-width: 33.3333333333%; + flex-basis: 33.3333333333%; } + +.col-sm-offset-3 { + margin-left: 25%; } + +.col-sm-5, +.row.cols-sm-5 > * { + max-width: 41.6666666667%; + flex-basis: 41.6666666667%; } + +.col-sm-offset-4 { + margin-left: 33.3333333333%; } + +.col-sm-6, +.row.cols-sm-6 > * { + max-width: 50%; + flex-basis: 50%; } + +.col-sm-offset-5 { + margin-left: 41.6666666667%; } + +.col-sm-7, +.row.cols-sm-7 > * { + max-width: 58.3333333333%; + flex-basis: 58.3333333333%; } + +.col-sm-offset-6 { + margin-left: 50%; } + +.col-sm-8, +.row.cols-sm-8 > * { + max-width: 66.6666666667%; + flex-basis: 66.6666666667%; } + +.col-sm-offset-7 { + margin-left: 58.3333333333%; } + +.col-sm-9, +.row.cols-sm-9 > * { + max-width: 75%; + flex-basis: 75%; } + +.col-sm-offset-8 { + margin-left: 66.6666666667%; } + +.col-sm-10, +.row.cols-sm-10 > * { + max-width: 83.3333333333%; + flex-basis: 83.3333333333%; } + +.col-sm-offset-9 { + margin-left: 75%; } + +.col-sm-11, +.row.cols-sm-11 > * { + max-width: 91.6666666667%; + flex-basis: 91.6666666667%; } + +.col-sm-offset-10 { + margin-left: 83.3333333333%; } + +.col-sm-12, +.row.cols-sm-12 > * { + max-width: 100%; + flex-basis: 100%; } + +.col-sm-offset-11 { + margin-left: 91.6666666667%; } + +.col-sm-normal { + order: initial; } + +.col-sm-first { + order: -999; } + +.col-sm-last { + order: 999; } + +@media screen and (min-width: 500px) { + .col-md, + [class^='col-md-'], + [class^='col-md-offset-'], + .row[class*='cols-md-'] > * { + box-sizing: border-box; + flex: 0 0 auto; + padding: 0 calc(var(--universal-padding) / 2); } + + .col-md, + .row.cols-md > * { + max-width: 100%; + flex-grow: 1; + flex-basis: 0; } + + .col-md-1, + .row.cols-md-1 > * { + max-width: 8.3333333333%; + flex-basis: 8.3333333333%; } + + .col-md-offset-0 { + margin-left: 0; } + + .col-md-2, + .row.cols-md-2 > * { + max-width: 16.6666666667%; + flex-basis: 16.6666666667%; } + + .col-md-offset-1 { + margin-left: 8.3333333333%; } + + .col-md-3, + .row.cols-md-3 > * { + max-width: 25%; + flex-basis: 25%; } + + .col-md-offset-2 { + margin-left: 16.6666666667%; } + + .col-md-4, + .row.cols-md-4 > * { + max-width: 33.3333333333%; + flex-basis: 33.3333333333%; } + + .col-md-offset-3 { + margin-left: 25%; } + + .col-md-5, + .row.cols-md-5 > * { + max-width: 41.6666666667%; + flex-basis: 41.6666666667%; } + + .col-md-offset-4 { + margin-left: 33.3333333333%; } + + .col-md-6, + .row.cols-md-6 > * { + max-width: 50%; + flex-basis: 50%; } + + .col-md-offset-5 { + margin-left: 41.6666666667%; } + + .col-md-7, + .row.cols-md-7 > * { + max-width: 58.3333333333%; + flex-basis: 58.3333333333%; } + + .col-md-offset-6 { + margin-left: 50%; } + + .col-md-8, + .row.cols-md-8 > * { + max-width: 66.6666666667%; + flex-basis: 66.6666666667%; } + + .col-md-offset-7 { + margin-left: 58.3333333333%; } + + .col-md-9, + .row.cols-md-9 > * { + max-width: 75%; + flex-basis: 75%; } + + .col-md-offset-8 { + margin-left: 66.6666666667%; } + + .col-md-10, + .row.cols-md-10 > * { + max-width: 83.3333333333%; + flex-basis: 83.3333333333%; } + + .col-md-offset-9 { + margin-left: 75%; } + + .col-md-11, + .row.cols-md-11 > * { + max-width: 91.6666666667%; + flex-basis: 91.6666666667%; } + + .col-md-offset-10 { + margin-left: 83.3333333333%; } + + .col-md-12, + .row.cols-md-12 > * { + max-width: 100%; + flex-basis: 100%; } + + .col-md-offset-11 { + margin-left: 91.6666666667%; } + + .col-md-normal { + order: initial; } + + .col-md-first { + order: -999; } + + .col-md-last { + order: 999; } } +@media screen and (min-width: 1280px) { + .col-lg, + [class^='col-lg-'], + [class^='col-lg-offset-'], + .row[class*='cols-lg-'] > * { + box-sizing: border-box; + flex: 0 0 auto; + padding: 0 calc(var(--universal-padding) / 2); } + + .col-lg, + .row.cols-lg > * { + max-width: 100%; + flex-grow: 1; + flex-basis: 0; } + + .col-lg-1, + .row.cols-lg-1 > * { + max-width: 8.3333333333%; + flex-basis: 8.3333333333%; } + + .col-lg-offset-0 { + margin-left: 0; } + + .col-lg-2, + .row.cols-lg-2 > * { + max-width: 16.6666666667%; + flex-basis: 16.6666666667%; } + + .col-lg-offset-1 { + margin-left: 8.3333333333%; } + + .col-lg-3, + .row.cols-lg-3 > * { + max-width: 25%; + flex-basis: 25%; } + + .col-lg-offset-2 { + margin-left: 16.6666666667%; } + + .col-lg-4, + .row.cols-lg-4 > * { + max-width: 33.3333333333%; + flex-basis: 33.3333333333%; } + + .col-lg-offset-3 { + margin-left: 25%; } + + .col-lg-5, + .row.cols-lg-5 > * { + max-width: 41.6666666667%; + flex-basis: 41.6666666667%; } + + .col-lg-offset-4 { + margin-left: 33.3333333333%; } + + .col-lg-6, + .row.cols-lg-6 > * { + max-width: 50%; + flex-basis: 50%; } + + .col-lg-offset-5 { + margin-left: 41.6666666667%; } + + .col-lg-7, + .row.cols-lg-7 > * { + max-width: 58.3333333333%; + flex-basis: 58.3333333333%; } + + .col-lg-offset-6 { + margin-left: 50%; } + + .col-lg-8, + .row.cols-lg-8 > * { + max-width: 66.6666666667%; + flex-basis: 66.6666666667%; } + + .col-lg-offset-7 { + margin-left: 58.3333333333%; } + + .col-lg-9, + .row.cols-lg-9 > * { + max-width: 75%; + flex-basis: 75%; } + + .col-lg-offset-8 { + margin-left: 66.6666666667%; } + + .col-lg-10, + .row.cols-lg-10 > * { + max-width: 83.3333333333%; + flex-basis: 83.3333333333%; } + + .col-lg-offset-9 { + margin-left: 75%; } + + .col-lg-11, + .row.cols-lg-11 > * { + max-width: 91.6666666667%; + flex-basis: 91.6666666667%; } + + .col-lg-offset-10 { + margin-left: 83.3333333333%; } + + .col-lg-12, + .row.cols-lg-12 > * { + max-width: 100%; + flex-basis: 100%; } + + .col-lg-offset-11 { + margin-left: 91.6666666667%; } + + .col-lg-normal { + order: initial; } + + .col-lg-first { + order: -999; } + + .col-lg-last { + order: 999; } } +/* Card component CSS variable definitions */ +:root { + --card-back-color: #3cb4e6; + --card-fore-color: #03234b; + --card-border-color: #03234b; } + +.card { + display: flex; + flex-direction: column; + justify-content: space-between; + align-self: center; + position: relative; + width: 100%; + background: var(--card-back-color); + color: var(--card-fore-color); + border: 0.0714285714rem solid var(--card-border-color); + border-radius: var(--universal-border-radius); + margin: var(--universal-margin); + overflow: hidden; } + @media screen and (min-width: 320px) { + .card { + max-width: 320px; } } + .card > .sectione { + background: var(--card-back-color); + color: var(--card-fore-color); + box-sizing: border-box; + margin: 0; + border: 0; + border-radius: 0; + border-bottom: 0.0714285714rem solid var(--card-border-color); + padding: var(--universal-padding); + width: 100%; } + .card > .sectione.media { + height: 200px; + padding: 0; + -o-object-fit: cover; + object-fit: cover; } + .card > .sectione:last-child { + border-bottom: 0; } + +/* + Custom elements for card elements. +*/ +@media screen and (min-width: 240px) { + .card.small { + max-width: 240px; } } +@media screen and (min-width: 480px) { + .card.large { + max-width: 480px; } } +.card.fluid { + max-width: 100%; + width: auto; } + +.card.warning { + --card-back-color: #e5b8b7; + --card-fore-color: #3b234b; + --card-border-color: #8c0078; } + +.card.error { + --card-back-color: #464650; + --card-fore-color: #ffffff; + --card-border-color: #8c0078; } + +.card > .sectione.dark { + --card-back-color: #3b234b; + --card-fore-color: #ffffff; } + +.card > .sectione.double-padded { + padding: calc(1.5 * var(--universal-padding)); } + +/* + Definitions for forms and input elements. +*/ +/* Input_control module CSS variable definitions */ +:root { + --form-back-color: #ffe97f; + --form-fore-color: #03234b; + --form-border-color: #3cb4e6; + --input-back-color: #ffffff; + --input-fore-color: #03234b; + --input-border-color: #3cb4e6; + --input-focus-color: #0288d1; + --input-invalid-color: #d32f2f; + --button-back-color: #e2e2e2; + --button-hover-back-color: #dcdcdc; + --button-fore-color: #212121; + --button-border-color: transparent; + --button-hover-border-color: transparent; + --button-group-border-color: rgba(124, 124, 124, 0.54); } + +form { + background: var(--form-back-color); + color: var(--form-fore-color); + border: 0.0714285714rem solid var(--form-border-color); + border-radius: var(--universal-border-radius); + margin: var(--universal-margin); + padding: calc(2 * var(--universal-padding)) var(--universal-padding); } + +fieldset { + border: 0.0714285714rem solid var(--form-border-color); + border-radius: var(--universal-border-radius); + margin: calc(var(--universal-margin) / 4); + padding: var(--universal-padding); } + +legend { + box-sizing: border-box; + display: table; + max-width: 100%; + white-space: normal; + font-weight: 500; + padding: calc(var(--universal-padding) / 2); } + +label { + padding: calc(var(--universal-padding) / 2) var(--universal-padding); } + +.input-group { + display: inline-block; } + .input-group.fluid { + display: flex; + align-items: center; + justify-content: center; } + .input-group.fluid > input { + max-width: 100%; + flex-grow: 1; + flex-basis: 0px; } + @media screen and (max-width: 499px) { + .input-group.fluid { + align-items: stretch; + flex-direction: column; } } + .input-group.vertical { + display: flex; + align-items: stretch; + flex-direction: column; } + .input-group.vertical > input { + max-width: 100%; + flex-grow: 1; + flex-basis: 0px; } + +[type="number"]::-webkit-inner-spin-button, [type="number"]::-webkit-outer-spin-button { + height: auto; } + +[type="search"] { + -webkit-appearance: textfield; + outline-offset: -2px; } + +[type="search"]::-webkit-search-cancel-button, +[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; } + +input:not([type]), [type="text"], [type="email"], [type="number"], [type="search"], +[type="password"], [type="url"], [type="tel"], [type="checkbox"], [type="radio"], textarea, select { + box-sizing: border-box; + background: var(--input-back-color); + color: var(--input-fore-color); + border: 0.0714285714rem solid var(--input-border-color); + border-radius: var(--universal-border-radius); + margin: calc(var(--universal-margin) / 2); + padding: var(--universal-padding) calc(1.5 * var(--universal-padding)); } + +input:not([type="button"]):not([type="submit"]):not([type="reset"]):hover, input:not([type="button"]):not([type="submit"]):not([type="reset"]):focus, textarea:hover, textarea:focus, select:hover, select:focus { + border-color: var(--input-focus-color); + box-shadow: none; } +input:not([type="button"]):not([type="submit"]):not([type="reset"]):invalid, input:not([type="button"]):not([type="submit"]):not([type="reset"]):focus:invalid, textarea:invalid, textarea:focus:invalid, select:invalid, select:focus:invalid { + border-color: var(--input-invalid-color); + box-shadow: none; } +input:not([type="button"]):not([type="submit"]):not([type="reset"])[readonly], textarea[readonly], select[readonly] { + background: var(--secondary-back-color); } + +select { + max-width: 100%; } + +option { + overflow: hidden; + text-overflow: ellipsis; } + +[type="checkbox"], [type="radio"] { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + position: relative; + height: calc(1rem + var(--universal-padding) / 2); + width: calc(1rem + var(--universal-padding) / 2); + vertical-align: text-bottom; + padding: 0; + flex-basis: calc(1rem + var(--universal-padding) / 2) !important; + flex-grow: 0 !important; } + [type="checkbox"]:checked:before, [type="radio"]:checked:before { + position: absolute; } + +[type="checkbox"]:checked:before { + content: '\2713'; + font-family: sans-serif; + font-size: calc(1rem + var(--universal-padding) / 2); + top: calc(0rem - var(--universal-padding)); + left: calc(var(--universal-padding) / 4); } + +[type="radio"] { + border-radius: 100%; } + [type="radio"]:checked:before { + border-radius: 100%; + content: ''; + top: calc(0.0714285714rem + var(--universal-padding) / 2); + left: calc(0.0714285714rem + var(--universal-padding) / 2); + background: var(--input-fore-color); + width: 0.5rem; + height: 0.5rem; } + +:placeholder-shown { + color: var(--input-fore-color); } + +::-ms-placeholder { + color: var(--input-fore-color); + opacity: 0.54; } + +button::-moz-focus-inner, [type="button"]::-moz-focus-inner, [type="reset"]::-moz-focus-inner, [type="submit"]::-moz-focus-inner { + border-style: none; + padding: 0; } + +button, html [type="button"], [type="reset"], [type="submit"] { + -webkit-appearance: button; } + +button { + overflow: visible; + text-transform: none; } + +button, [type="button"], [type="submit"], [type="reset"], +a.button, label.button, .button, +a[role="button"], label[role="button"], [role="button"] { + display: inline-block; + background: var(--button-back-color); + color: var(--button-fore-color); + border: 0.0714285714rem solid var(--button-border-color); + border-radius: var(--universal-border-radius); + padding: var(--universal-padding) calc(1.5 * var(--universal-padding)); + margin: var(--universal-margin); + text-decoration: none; + cursor: pointer; + transition: background 0.3s; } + button:hover, button:focus, [type="button"]:hover, [type="button"]:focus, [type="submit"]:hover, [type="submit"]:focus, [type="reset"]:hover, [type="reset"]:focus, + a.button:hover, + a.button:focus, label.button:hover, label.button:focus, .button:hover, .button:focus, + a[role="button"]:hover, + a[role="button"]:focus, label[role="button"]:hover, label[role="button"]:focus, [role="button"]:hover, [role="button"]:focus { + background: var(--button-hover-back-color); + border-color: var(--button-hover-border-color); } + +input:disabled, input[disabled], textarea:disabled, textarea[disabled], select:disabled, select[disabled], button:disabled, button[disabled], .button:disabled, .button[disabled], [role="button"]:disabled, [role="button"][disabled] { + cursor: not-allowed; + opacity: 0.75; } + +.button-group { + display: flex; + border: 0.0714285714rem solid var(--button-group-border-color); + border-radius: var(--universal-border-radius); + margin: var(--universal-margin); } + .button-group > button, .button-group [type="button"], .button-group > [type="submit"], .button-group > [type="reset"], .button-group > .button, .button-group > [role="button"] { + margin: 0; + max-width: 100%; + flex: 1 1 auto; + text-align: center; + border: 0; + border-radius: 0; + box-shadow: none; } + .button-group > :not(:first-child) { + border-left: 0.0714285714rem solid var(--button-group-border-color); } + @media screen and (max-width: 499px) { + .button-group { + flex-direction: column; } + .button-group > :not(:first-child) { + border: 0; + border-top: 0.0714285714rem solid var(--button-group-border-color); } } + +/* + Custom elements for forms and input elements. +*/ +button.primary, [type="button"].primary, [type="submit"].primary, [type="reset"].primary, .button.primary, [role="button"].primary { + --button-back-color: #1976d2; + --button-fore-color: #f8f8f8; } + button.primary:hover, button.primary:focus, [type="button"].primary:hover, [type="button"].primary:focus, [type="submit"].primary:hover, [type="submit"].primary:focus, [type="reset"].primary:hover, [type="reset"].primary:focus, .button.primary:hover, .button.primary:focus, [role="button"].primary:hover, [role="button"].primary:focus { + --button-hover-back-color: #1565c0; } + +button.secondary, [type="button"].secondary, [type="submit"].secondary, [type="reset"].secondary, .button.secondary, [role="button"].secondary { + --button-back-color: #d32f2f; + --button-fore-color: #f8f8f8; } + button.secondary:hover, button.secondary:focus, [type="button"].secondary:hover, [type="button"].secondary:focus, [type="submit"].secondary:hover, [type="submit"].secondary:focus, [type="reset"].secondary:hover, [type="reset"].secondary:focus, .button.secondary:hover, .button.secondary:focus, [role="button"].secondary:hover, [role="button"].secondary:focus { + --button-hover-back-color: #c62828; } + +button.tertiary, [type="button"].tertiary, [type="submit"].tertiary, [type="reset"].tertiary, .button.tertiary, [role="button"].tertiary { + --button-back-color: #308732; + --button-fore-color: #f8f8f8; } + button.tertiary:hover, button.tertiary:focus, [type="button"].tertiary:hover, [type="button"].tertiary:focus, [type="submit"].tertiary:hover, [type="submit"].tertiary:focus, [type="reset"].tertiary:hover, [type="reset"].tertiary:focus, .button.tertiary:hover, .button.tertiary:focus, [role="button"].tertiary:hover, [role="button"].tertiary:focus { + --button-hover-back-color: #277529; } + +button.inverse, [type="button"].inverse, [type="submit"].inverse, [type="reset"].inverse, .button.inverse, [role="button"].inverse { + --button-back-color: #212121; + --button-fore-color: #f8f8f8; } + button.inverse:hover, button.inverse:focus, [type="button"].inverse:hover, [type="button"].inverse:focus, [type="submit"].inverse:hover, [type="submit"].inverse:focus, [type="reset"].inverse:hover, [type="reset"].inverse:focus, .button.inverse:hover, .button.inverse:focus, [role="button"].inverse:hover, [role="button"].inverse:focus { + --button-hover-back-color: #111; } + +button.small, [type="button"].small, [type="submit"].small, [type="reset"].small, .button.small, [role="button"].small { + padding: calc(0.5 * var(--universal-padding)) calc(0.75 * var(--universal-padding)); + margin: var(--universal-margin); } + +button.large, [type="button"].large, [type="submit"].large, [type="reset"].large, .button.large, [role="button"].large { + padding: calc(1.5 * var(--universal-padding)) calc(2 * var(--universal-padding)); + margin: var(--universal-margin); } + +/* + Definitions for navigation elements. +*/ +/* Navigation module CSS variable definitions */ +:root { + --header-back-color: #03234b; + --header-hover-back-color: #ffd200; + --header-fore-color: #ffffff; + --header-border-color: #3cb4e6; + --nav-back-color: #ffffff; + --nav-hover-back-color: #ffe97f; + --nav-fore-color: #e6007e; + --nav-border-color: #3cb4e6; + --nav-link-color: #3cb4e6; + --footer-fore-color: #ffffff; + --footer-back-color: #03234b; + --footer-border-color: #3cb4e6; + --footer-link-color: #3cb4e6; + --drawer-back-color: #ffffff; + --drawer-hover-back-color: #ffe97f; + --drawer-border-color: #3cb4e6; + --drawer-close-color: #e6007e; } + +header { + height: 2.75rem; + background: var(--header-back-color); + color: var(--header-fore-color); + border-bottom: 0.0714285714rem solid var(--header-border-color); + padding: calc(var(--universal-padding) / 4) 0; + white-space: nowrap; + overflow-x: auto; + overflow-y: hidden; } + header.row { + box-sizing: content-box; } + header .logo { + color: var(--header-fore-color); + font-size: 1.75rem; + padding: var(--universal-padding) calc(2 * var(--universal-padding)); + text-decoration: none; } + header button, header [type="button"], header .button, header [role="button"] { + box-sizing: border-box; + position: relative; + top: calc(0rem - var(--universal-padding) / 4); + height: calc(3.1875rem + var(--universal-padding) / 2); + background: var(--header-back-color); + line-height: calc(3.1875rem - var(--universal-padding) * 1.5); + text-align: center; + color: var(--header-fore-color); + border: 0; + border-radius: 0; + margin: 0; + text-transform: uppercase; } + header button:hover, header button:focus, header [type="button"]:hover, header [type="button"]:focus, header .button:hover, header .button:focus, header [role="button"]:hover, header [role="button"]:focus { + background: var(--header-hover-back-color); } + +nav { + background: var(--nav-back-color); + color: var(--nav-fore-color); + border: 0.0714285714rem solid var(--nav-border-color); + border-radius: var(--universal-border-radius); + margin: var(--universal-margin); } + nav * { + padding: var(--universal-padding) calc(1.5 * var(--universal-padding)); } + nav a, nav a:visited { + display: block; + color: var(--nav-link-color); + border-radius: var(--universal-border-radius); + transition: background 0.3s; } + nav a:hover, nav a:focus, nav a:visited:hover, nav a:visited:focus { + text-decoration: none; + background: var(--nav-hover-back-color); } + nav .sublink-1 { + position: relative; + margin-left: calc(2 * var(--universal-padding)); } + nav .sublink-1:before { + position: absolute; + left: calc(var(--universal-padding) - 1 * var(--universal-padding)); + top: -0.0714285714rem; + content: ''; + height: 100%; + border: 0.0714285714rem solid var(--nav-border-color); + border-left: 0; } + nav .sublink-2 { + position: relative; + margin-left: calc(4 * var(--universal-padding)); } + nav .sublink-2:before { + position: absolute; + left: calc(var(--universal-padding) - 3 * var(--universal-padding)); + top: -0.0714285714rem; + content: ''; + height: 100%; + border: 0.0714285714rem solid var(--nav-border-color); + border-left: 0; } + +footer { + background: var(--footer-back-color); + color: var(--footer-fore-color); + border-top: 0.0714285714rem solid var(--footer-border-color); + padding: calc(2 * var(--universal-padding)) var(--universal-padding); + font-size: 0.875rem; } + footer a, footer a:visited { + color: var(--footer-link-color); } + +header.sticky { + position: -webkit-sticky; + position: sticky; + z-index: 1101; + top: 0; } + +footer.sticky { + position: -webkit-sticky; + position: sticky; + z-index: 1101; + bottom: 0; } + +.drawer-toggle:before { + display: inline-block; + position: relative; + vertical-align: bottom; + content: '\00a0\2261\00a0'; + font-family: sans-serif; + font-size: 1.5em; } +@media screen and (min-width: 500px) { + .drawer-toggle:not(.persistent) { + display: none; } } + +[type="checkbox"].drawer { + height: 1px; + width: 1px; + margin: -1px; + overflow: hidden; + position: absolute; + clip: rect(0 0 0 0); + -webkit-clip-path: inset(100%); + clip-path: inset(100%); } + [type="checkbox"].drawer + * { + display: block; + box-sizing: border-box; + position: fixed; + top: 0; + width: 320px; + height: 100vh; + overflow-y: auto; + background: var(--drawer-back-color); + border: 0.0714285714rem solid var(--drawer-border-color); + border-radius: 0; + margin: 0; + z-index: 1110; + right: -320px; + transition: right 0.3s; } + [type="checkbox"].drawer + * .drawer-close { + position: absolute; + top: var(--universal-margin); + right: var(--universal-margin); + z-index: 1111; + width: 2rem; + height: 2rem; + border-radius: var(--universal-border-radius); + padding: var(--universal-padding); + margin: 0; + cursor: pointer; + transition: background 0.3s; } + [type="checkbox"].drawer + * .drawer-close:before { + display: block; + content: '\00D7'; + color: var(--drawer-close-color); + position: relative; + font-family: sans-serif; + font-size: 2rem; + line-height: 1; + text-align: center; } + [type="checkbox"].drawer + * .drawer-close:hover, [type="checkbox"].drawer + * .drawer-close:focus { + background: var(--drawer-hover-back-color); } + @media screen and (max-width: 320px) { + [type="checkbox"].drawer + * { + width: 100%; } } + [type="checkbox"].drawer:checked + * { + right: 0; } + @media screen and (min-width: 500px) { + [type="checkbox"].drawer:not(.persistent) + * { + position: static; + height: 100%; + z-index: 1100; } + [type="checkbox"].drawer:not(.persistent) + * .drawer-close { + display: none; } } + +/* + Definitions for the responsive table component. +*/ +/* Table module CSS variable definitions. */ +:root { + --table-border-color: #03234b; + --table-border-separator-color: #03234b; + --table-head-back-color: #03234b; + --table-head-fore-color: #ffffff; + --table-body-back-color: #ffffff; + --table-body-fore-color: #03234b; + --table-body-alt-back-color: #f4f4f4; } + +table { + border-collapse: separate; + border-spacing: 0; + margin: 0; + display: flex; + flex: 0 1 auto; + flex-flow: row wrap; + padding: var(--universal-padding); + padding-top: 0; } + table caption { + font-size: 1rem; + margin: calc(2 * var(--universal-margin)) 0; + max-width: 100%; + flex: 0 0 100%; } + table thead, table tbody { + display: flex; + flex-flow: row wrap; + border: 0.0714285714rem solid var(--table-border-color); } + table thead { + z-index: 999; + border-radius: var(--universal-border-radius) var(--universal-border-radius) 0 0; + border-bottom: 0.0714285714rem solid var(--table-border-separator-color); } + table tbody { + border-top: 0; + margin-top: calc(0 - var(--universal-margin)); + border-radius: 0 0 var(--universal-border-radius) var(--universal-border-radius); } + table tr { + display: flex; + padding: 0; } + table th, table td { + padding: calc(0.5 * var(--universal-padding)); + font-size: 0.9rem; } + table th { + text-align: left; + background: var(--table-head-back-color); + color: var(--table-head-fore-color); } + table td { + background: var(--table-body-back-color); + color: var(--table-body-fore-color); + border-top: 0.0714285714rem solid var(--table-border-color); } + +table:not(.horizontal) { + overflow: auto; + max-height: 100%; } + table:not(.horizontal) thead, table:not(.horizontal) tbody { + max-width: 100%; + flex: 0 0 100%; } + table:not(.horizontal) tr { + flex-flow: row wrap; + flex: 0 0 100%; } + table:not(.horizontal) th, table:not(.horizontal) td { + flex: 1 0 0%; + overflow: hidden; + text-overflow: ellipsis; } + table:not(.horizontal) thead { + position: sticky; + top: 0; } + table:not(.horizontal) tbody tr:first-child td { + border-top: 0; } + +table.horizontal { + border: 0; } + table.horizontal thead, table.horizontal tbody { + border: 0; + flex: .2 0 0; + flex-flow: row nowrap; } + table.horizontal tbody { + overflow: auto; + justify-content: space-between; + flex: .8 0 0; + margin-left: 0; + padding-bottom: calc(var(--universal-padding) / 4); } + table.horizontal tr { + flex-direction: column; + flex: 1 0 auto; } + table.horizontal th, table.horizontal td { + width: auto; + border: 0; + border-bottom: 0.0714285714rem solid var(--table-border-color); } + table.horizontal th:not(:first-child), table.horizontal td:not(:first-child) { + border-top: 0; } + table.horizontal th { + text-align: right; + border-left: 0.0714285714rem solid var(--table-border-color); + border-right: 0.0714285714rem solid var(--table-border-separator-color); } + table.horizontal thead tr:first-child { + padding-left: 0; } + table.horizontal th:first-child, table.horizontal td:first-child { + border-top: 0.0714285714rem solid var(--table-border-color); } + table.horizontal tbody tr:last-child td { + border-right: 0.0714285714rem solid var(--table-border-color); } + table.horizontal tbody tr:last-child td:first-child { + border-top-right-radius: 0.25rem; } + table.horizontal tbody tr:last-child td:last-child { + border-bottom-right-radius: 0.25rem; } + table.horizontal thead tr:first-child th:first-child { + border-top-left-radius: 0.25rem; } + table.horizontal thead tr:first-child th:last-child { + border-bottom-left-radius: 0.25rem; } + +@media screen and (max-width: 499px) { + table, table.horizontal { + border-collapse: collapse; + border: 0; + width: 100%; + display: table; } + table thead, table th, table.horizontal thead, table.horizontal th { + border: 0; + height: 1px; + width: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + clip: rect(0 0 0 0); + -webkit-clip-path: inset(100%); + clip-path: inset(100%); } + table tbody, table.horizontal tbody { + border: 0; + display: table-row-group; } + table tr, table.horizontal tr { + display: block; + border: 0.0714285714rem solid var(--table-border-color); + border-radius: var(--universal-border-radius); + background: #ffffff; + padding: var(--universal-padding); + margin: var(--universal-margin); + margin-bottom: calc(1 * var(--universal-margin)); } + table th, table td, table.horizontal th, table.horizontal td { + width: auto; } + table td, table.horizontal td { + display: block; + border: 0; + text-align: right; } + table td:before, table.horizontal td:before { + content: attr(data-label); + float: left; + font-weight: 600; } + table th:first-child, table td:first-child, table.horizontal th:first-child, table.horizontal td:first-child { + border-top: 0; } + table tbody tr:last-child td, table.horizontal tbody tr:last-child td { + border-right: 0; } } +table tr:nth-of-type(2n) > td { + background: var(--table-body-alt-back-color); } + +@media screen and (max-width: 500px) { + table tr:nth-of-type(2n) { + background: var(--table-body-alt-back-color); } } +:root { + --table-body-hover-back-color: #90caf9; } + +table.hoverable tr:hover, table.hoverable tr:hover > td, table.hoverable tr:focus, table.hoverable tr:focus > td { + background: var(--table-body-hover-back-color); } + +@media screen and (max-width: 500px) { + table.hoverable tr:hover, table.hoverable tr:hover > td, table.hoverable tr:focus, table.hoverable tr:focus > td { + background: var(--table-body-hover-back-color); } } +/* + Definitions for contextual background elements, toasts and tooltips. +*/ +/* Contextual module CSS variable definitions */ +:root { + --mark-back-color: #3cb4e6; + --mark-fore-color: #ffffff; } + +mark { + background: var(--mark-back-color); + color: var(--mark-fore-color); + font-size: 0.95em; + line-height: 1em; + border-radius: var(--universal-border-radius); + padding: calc(var(--universal-padding) / 4) var(--universal-padding); } + mark.inline-block { + display: inline-block; + font-size: 1em; + line-height: 1.4; + padding: calc(var(--universal-padding) / 2) var(--universal-padding); } + +:root { + --toast-back-color: #424242; + --toast-fore-color: #fafafa; } + +.toast { + position: fixed; + bottom: calc(var(--universal-margin) * 3); + left: 50%; + transform: translate(-50%, -50%); + z-index: 1111; + color: var(--toast-fore-color); + background: var(--toast-back-color); + border-radius: calc(var(--universal-border-radius) * 16); + padding: var(--universal-padding) calc(var(--universal-padding) * 3); } + +:root { + --tooltip-back-color: #212121; + --tooltip-fore-color: #fafafa; } + +.tooltip { + position: relative; + display: inline-block; } + .tooltip:before, .tooltip:after { + position: absolute; + opacity: 0; + clip: rect(0 0 0 0); + -webkit-clip-path: inset(100%); + clip-path: inset(100%); + transition: all 0.3s; + z-index: 1010; + left: 50%; } + .tooltip:not(.bottom):before, .tooltip:not(.bottom):after { + bottom: 75%; } + .tooltip.bottom:before, .tooltip.bottom:after { + top: 75%; } + .tooltip:hover:before, .tooltip:hover:after, .tooltip:focus:before, .tooltip:focus:after { + opacity: 1; + clip: auto; + -webkit-clip-path: inset(0%); + clip-path: inset(0%); } + .tooltip:before { + content: ''; + background: transparent; + border: var(--universal-margin) solid transparent; + left: calc(50% - var(--universal-margin)); } + .tooltip:not(.bottom):before { + border-top-color: #212121; } + .tooltip.bottom:before { + border-bottom-color: #212121; } + .tooltip:after { + content: attr(aria-label); + color: var(--tooltip-fore-color); + background: var(--tooltip-back-color); + border-radius: var(--universal-border-radius); + padding: var(--universal-padding); + white-space: nowrap; + transform: translateX(-50%); } + .tooltip:not(.bottom):after { + margin-bottom: calc(2 * var(--universal-margin)); } + .tooltip.bottom:after { + margin-top: calc(2 * var(--universal-margin)); } + +:root { + --modal-overlay-color: rgba(0, 0, 0, 0.45); + --modal-close-color: #e6007e; + --modal-close-hover-color: #ffe97f; } + +[type="checkbox"].modal { + height: 1px; + width: 1px; + margin: -1px; + overflow: hidden; + position: absolute; + clip: rect(0 0 0 0); + -webkit-clip-path: inset(100%); + clip-path: inset(100%); } + [type="checkbox"].modal + div { + position: fixed; + top: 0; + left: 0; + display: none; + width: 100vw; + height: 100vh; + background: var(--modal-overlay-color); } + [type="checkbox"].modal + div .card { + margin: 0 auto; + max-height: 50vh; + overflow: auto; } + [type="checkbox"].modal + div .card .modal-close { + position: absolute; + top: 0; + right: 0; + width: 1.75rem; + height: 1.75rem; + border-radius: var(--universal-border-radius); + padding: var(--universal-padding); + margin: 0; + cursor: pointer; + transition: background 0.3s; } + [type="checkbox"].modal + div .card .modal-close:before { + display: block; + content: '\00D7'; + color: var(--modal-close-color); + position: relative; + font-family: sans-serif; + font-size: 1.75rem; + line-height: 1; + text-align: center; } + [type="checkbox"].modal + div .card .modal-close:hover, [type="checkbox"].modal + div .card .modal-close:focus { + background: var(--modal-close-hover-color); } + [type="checkbox"].modal:checked + div { + display: flex; + flex: 0 1 auto; + z-index: 1200; } + [type="checkbox"].modal:checked + div .card .modal-close { + z-index: 1211; } + +:root { + --collapse-label-back-color: #03234b; + --collapse-label-fore-color: #ffffff; + --collapse-label-hover-back-color: #3cb4e6; + --collapse-selected-label-back-color: #3cb4e6; + --collapse-border-color: var(--collapse-label-back-color); + --collapse-selected-border-color: #ceecf8; + --collapse-content-back-color: #ffffff; + --collapse-selected-label-border-color: #3cb4e6; } + +.collapse { + width: calc(100% - 2 * var(--universal-margin)); + opacity: 1; + display: flex; + flex-direction: column; + margin: var(--universal-margin); + border-radius: var(--universal-border-radius); } + .collapse > [type="radio"], .collapse > [type="checkbox"] { + height: 1px; + width: 1px; + margin: -1px; + overflow: hidden; + position: absolute; + clip: rect(0 0 0 0); + -webkit-clip-path: inset(100%); + clip-path: inset(100%); } + .collapse > label { + flex-grow: 1; + display: inline-block; + height: 1.25rem; + cursor: pointer; + transition: background 0.2s; + color: var(--collapse-label-fore-color); + background: var(--collapse-label-back-color); + border: 0.0714285714rem solid var(--collapse-selected-border-color); + padding: calc(1.25 * var(--universal-padding)); } + .collapse > label:hover, .collapse > label:focus { + background: var(--collapse-label-hover-back-color); } + .collapse > label + div { + flex-basis: auto; + height: 1px; + width: 1px; + margin: -1px; + overflow: hidden; + position: absolute; + clip: rect(0 0 0 0); + -webkit-clip-path: inset(100%); + clip-path: inset(100%); + transition: max-height 0.3s; + max-height: 1px; } + .collapse > :checked + label { + background: var(--collapse-selected-label-back-color); + border-color: var(--collapse-selected-label-border-color); } + .collapse > :checked + label + div { + box-sizing: border-box; + position: relative; + width: 100%; + height: auto; + overflow: auto; + margin: 0; + background: var(--collapse-content-back-color); + border: 0.0714285714rem solid var(--collapse-selected-border-color); + border-top: 0; + padding: var(--universal-padding); + clip: auto; + -webkit-clip-path: inset(0%); + clip-path: inset(0%); + max-height: 100%; } + .collapse > label:not(:first-of-type) { + border-top: 0; } + .collapse > label:first-of-type { + border-radius: var(--universal-border-radius) var(--universal-border-radius) 0 0; } + .collapse > label:last-of-type:not(:first-of-type) { + border-radius: 0 0 var(--universal-border-radius) var(--universal-border-radius); } + .collapse > label:last-of-type:first-of-type { + border-radius: var(--universal-border-radius); } + .collapse > :checked:last-of-type:not(:first-of-type) + label { + border-radius: 0; } + .collapse > :checked:last-of-type + label + div { + border-radius: 0 0 var(--universal-border-radius) var(--universal-border-radius); } + +/* + Custom elements for contextual background elements, toasts and tooltips. +*/ +mark.tertiary { + --mark-back-color: #3cb4e6; } + +mark.tag { + padding: calc(var(--universal-padding)/2) var(--universal-padding); + border-radius: 1em; } + +/* + Definitions for progress elements and spinners. +*/ +/* Progress module CSS variable definitions */ +:root { + --progress-back-color: #3cb4e6; + --progress-fore-color: #555; } + +progress { + display: block; + vertical-align: baseline; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + height: 0.75rem; + width: calc(100% - 2 * var(--universal-margin)); + margin: var(--universal-margin); + border: 0; + border-radius: calc(2 * var(--universal-border-radius)); + background: var(--progress-back-color); + color: var(--progress-fore-color); } + progress::-webkit-progress-value { + background: var(--progress-fore-color); + border-top-left-radius: calc(2 * var(--universal-border-radius)); + border-bottom-left-radius: calc(2 * var(--universal-border-radius)); } + progress::-webkit-progress-bar { + background: var(--progress-back-color); } + progress::-moz-progress-bar { + background: var(--progress-fore-color); + border-top-left-radius: calc(2 * var(--universal-border-radius)); + border-bottom-left-radius: calc(2 * var(--universal-border-radius)); } + progress[value="1000"]::-webkit-progress-value { + border-radius: calc(2 * var(--universal-border-radius)); } + progress[value="1000"]::-moz-progress-bar { + border-radius: calc(2 * var(--universal-border-radius)); } + progress.inline { + display: inline-block; + vertical-align: middle; + width: 60%; } + +:root { + --spinner-back-color: #ddd; + --spinner-fore-color: #555; } + +@keyframes spinner-donut-anim { + 0% { + transform: rotate(0deg); } + 100% { + transform: rotate(360deg); } } +.spinner { + display: inline-block; + margin: var(--universal-margin); + border: 0.25rem solid var(--spinner-back-color); + border-left: 0.25rem solid var(--spinner-fore-color); + border-radius: 50%; + width: 1.25rem; + height: 1.25rem; + animation: spinner-donut-anim 1.2s linear infinite; } + +/* + Custom elements for progress bars and spinners. +*/ +progress.primary { + --progress-fore-color: #1976d2; } + +progress.secondary { + --progress-fore-color: #d32f2f; } + +progress.tertiary { + --progress-fore-color: #308732; } + +.spinner.primary { + --spinner-fore-color: #1976d2; } + +.spinner.secondary { + --spinner-fore-color: #d32f2f; } + +.spinner.tertiary { + --spinner-fore-color: #308732; } + +/* + Definitions for icons - powered by Feather (https://feathericons.com/). +*/ +span[class^='icon-'] { + display: inline-block; + height: 1em; + width: 1em; + vertical-align: -0.125em; + background-size: contain; + margin: 0 calc(var(--universal-margin) / 4); } + span[class^='icon-'].secondary { + -webkit-filter: invert(25%); + filter: invert(25%); } + span[class^='icon-'].inverse { + -webkit-filter: invert(100%); + filter: invert(100%); } + +span.icon-alert { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%2303234b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12' y2='16'%3E%3C/line%3E%3C/svg%3E"); } +span.icon-bookmark { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%2303234b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z'%3E%3C/path%3E%3C/svg%3E"); } +span.icon-calendar { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%2303234b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E"); } +span.icon-credit { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%2303234b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='1' y='4' width='22' height='16' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='1' y1='10' x2='23' y2='10'%3E%3C/line%3E%3C/svg%3E"); } +span.icon-edit { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%2303234b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 14.66V20a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h5.34'%3E%3C/path%3E%3Cpolygon points='18 2 22 6 12 16 8 16 8 12 18 2'%3E%3C/polygon%3E%3C/svg%3E"); } +span.icon-link { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%2303234b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6'%3E%3C/path%3E%3Cpolyline points='15 3 21 3 21 9'%3E%3C/polyline%3E%3Cline x1='10' y1='14' x2='21' y2='3'%3E%3C/line%3E%3C/svg%3E"); } +span.icon-help { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%2303234b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3'%3E%3C/path%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='17' x2='12' y2='17'%3E%3C/line%3E%3C/svg%3E"); } +span.icon-home { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%2303234b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z'%3E%3C/path%3E%3Cpolyline points='9 22 9 12 15 12 15 22'%3E%3C/polyline%3E%3C/svg%3E"); } +span.icon-info { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%2303234b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='16' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='8' x2='12' y2='8'%3E%3C/line%3E%3C/svg%3E"); } +span.icon-lock { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%2303234b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='11' width='18' height='11' rx='2' ry='2'%3E%3C/rect%3E%3Cpath d='M7 11V7a5 5 0 0 1 10 0v4'%3E%3C/path%3E%3C/svg%3E"); } +span.icon-mail { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%2303234b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z'%3E%3C/path%3E%3Cpolyline points='22,6 12,13 2,6'%3E%3C/polyline%3E%3C/svg%3E"); } +span.icon-location { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%2303234b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z'%3E%3C/path%3E%3Ccircle cx='12' cy='10' r='3'%3E%3C/circle%3E%3C/svg%3E"); } +span.icon-phone { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%2303234b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z'%3E%3C/path%3E%3C/svg%3E"); } +span.icon-rss { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%2303234b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 11a9 9 0 0 1 9 9'%3E%3C/path%3E%3Cpath d='M4 4a16 16 0 0 1 16 16'%3E%3C/path%3E%3Ccircle cx='5' cy='19' r='1'%3E%3C/circle%3E%3C/svg%3E"); } +span.icon-search { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%2303234b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E"); } +span.icon-settings { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%2303234b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='3'%3E%3C/circle%3E%3Cpath d='M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z'%3E%3C/path%3E%3C/svg%3E"); } +span.icon-share { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%2303234b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='18' cy='5' r='3'%3E%3C/circle%3E%3Ccircle cx='6' cy='12' r='3'%3E%3C/circle%3E%3Ccircle cx='18' cy='19' r='3'%3E%3C/circle%3E%3Cline x1='8.59' y1='13.51' x2='15.42' y2='17.49'%3E%3C/line%3E%3Cline x1='15.41' y1='6.51' x2='8.59' y2='10.49'%3E%3C/line%3E%3C/svg%3E"); } +span.icon-cart { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%2303234b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='9' cy='21' r='1'%3E%3C/circle%3E%3Ccircle cx='20' cy='21' r='1'%3E%3C/circle%3E%3Cpath d='M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6'%3E%3C/path%3E%3C/svg%3E"); } +span.icon-upload { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%2303234b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4'%3E%3C/path%3E%3Cpolyline points='17 8 12 3 7 8'%3E%3C/polyline%3E%3Cline x1='12' y1='3' x2='12' y2='15'%3E%3C/line%3E%3C/svg%3E"); } +span.icon-user { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%2303234b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2'%3E%3C/path%3E%3Ccircle cx='12' cy='7' r='4'%3E%3C/circle%3E%3C/svg%3E"); } + +/* + Definitions for STMicroelectronics icons (https://brandportal.st.com/document/26). +*/ +span.icon-st-update { + background-image: url("Update.svg"); } +span.icon-st-add { + background-image: url("Add button.svg"); } + +/* + Definitions for utilities and helper classes. +*/ +/* Utility module CSS variable definitions */ +:root { + --generic-border-color: rgba(0, 0, 0, 0.3); + --generic-box-shadow: 0 0.2857142857rem 0.2857142857rem 0 rgba(0, 0, 0, 0.125), 0 0.1428571429rem 0.1428571429rem -0.1428571429rem rgba(0, 0, 0, 0.125); } + +.hidden { + display: none !important; } + +.visually-hidden { + position: absolute !important; + width: 1px !important; + height: 1px !important; + margin: -1px !important; + border: 0 !important; + padding: 0 !important; + clip: rect(0 0 0 0) !important; + -webkit-clip-path: inset(100%) !important; + clip-path: inset(100%) !important; + overflow: hidden !important; } + +.bordered { + border: 0.0714285714rem solid var(--generic-border-color) !important; } + +.rounded { + border-radius: var(--universal-border-radius) !important; } + +.circular { + border-radius: 50% !important; } + +.shadowed { + box-shadow: var(--generic-box-shadow) !important; } + +.responsive-margin { + margin: calc(var(--universal-margin) / 4) !important; } + @media screen and (min-width: 500px) { + .responsive-margin { + margin: calc(var(--universal-margin) / 2) !important; } } + @media screen and (min-width: 1280px) { + .responsive-margin { + margin: var(--universal-margin) !important; } } + +.responsive-padding { + padding: calc(var(--universal-padding) / 4) !important; } + @media screen and (min-width: 500px) { + .responsive-padding { + padding: calc(var(--universal-padding) / 2) !important; } } + @media screen and (min-width: 1280px) { + .responsive-padding { + padding: var(--universal-padding) !important; } } + +@media screen and (max-width: 499px) { + .hidden-sm { + display: none !important; } } +@media screen and (min-width: 500px) and (max-width: 1279px) { + .hidden-md { + display: none !important; } } +@media screen and (min-width: 1280px) { + .hidden-lg { + display: none !important; } } +@media screen and (max-width: 499px) { + .visually-hidden-sm { + position: absolute !important; + width: 1px !important; + height: 1px !important; + margin: -1px !important; + border: 0 !important; + padding: 0 !important; + clip: rect(0 0 0 0) !important; + -webkit-clip-path: inset(100%) !important; + clip-path: inset(100%) !important; + overflow: hidden !important; } } +@media screen and (min-width: 500px) and (max-width: 1279px) { + .visually-hidden-md { + position: absolute !important; + width: 1px !important; + height: 1px !important; + margin: -1px !important; + border: 0 !important; + padding: 0 !important; + clip: rect(0 0 0 0) !important; + -webkit-clip-path: inset(100%) !important; + clip-path: inset(100%) !important; + overflow: hidden !important; } } +@media screen and (min-width: 1280px) { + .visually-hidden-lg { + position: absolute !important; + width: 1px !important; + height: 1px !important; + margin: -1px !important; + border: 0 !important; + padding: 0 !important; + clip: rect(0 0 0 0) !important; + -webkit-clip-path: inset(100%) !important; + clip-path: inset(100%) !important; + overflow: hidden !important; } } + +/*# sourceMappingURL=mini-custom.css.map */ + +img[alt="ST logo"] { display: block; margin: auto; width: 75%; max-width: 250px; min-width: 71px; } +img[alt="Cube logo"] { float: right; width: 30%; max-width: 10rem; min-width: 8rem; padding-right: 1rem;} + +.figure { + display: block; + margin-left: auto; + margin-right: auto; + text-align: center; +} \ No newline at end of file diff --git a/src/STM32CubeWL/SubGHz_Phy/_htmresc/st_logo_2020.png b/src/STM32CubeWL/SubGHz_Phy/_htmresc/st_logo_2020.png new file mode 100644 index 0000000000000000000000000000000000000000..d6cebb5ac70e0594cbe37a6b60036a80ef3bed37 GIT binary patch literal 7520 zcmcI|WmKC@*KTl!K!Z~t6qn*&DDF_CK%q#B6QmH_DHL}I6b%;K-J$eBN|2xh0+bea zmtyV5bG|?CpZBcu=iF<}z4q*xz1LhL*PcBwx;m;Pgmi=e0DweYO-UaBz)*UWZ}4#+ zCC-V!SC16}H#HLv0Dy?%--0o{5_}H;Jf%=ql7H=+dziOk_)KzUSaiQ9L<^g!49k}} z?4y$V zrsn3Ul^TY`00G_J_8;F7Sr5c3Y)f-yOYl{fS0avfKJg7R%r!y-ohcBkr6XBZBkE)( z_t+tVV2}G1_CN!)yWes*wa-AGwk31N_T1`)4COlfz+o(9S90e?6jHV4)!>`j+oRqp z)gb?rtSdw<#&c?q!OKB+Ncn!kr5JR2EYan8HAPXBw*Oh-F(6GmaC!`$p7fl!_Cdu> z5@PUMR_K5&jgevDepWqepZVdMHR$q>ga=7k0ltW$HHVn>HRa!#(+BW_jN$5rx^MtR zzWT7tNWQeFzEp+86jOYI=I)*GZYEiXlf-Mw%tJ^ptL%2)%#PmUjxC4wx@%KxQ)8kO?|7E1 zd@5gd9_$*6nx?fj*iF_EJT*PYLFP}d8*Fw>cu3@&nm%V_C}V7HMmg|Nl#1J1^#)uz zZag|X>{6j-myvL}3(H8o3resNBq_jcuJuyT!QuXnNN&zG^tG? z>y*dy1#XfwyCE#UiLT(~LYAVwp)epErJLsHBA|l0xo)kxLAPob+7tFlr{7x8(#v|O zknK}QC6|~=cTJ0;x9MWMKua;LdTKIHX>35a@7{5y0MQ|zqsP64mM~`gJ&&R{pSUBA zf3Y>k>d|xdzBFztjtO~{y%@LUdwSgzEj=69hH3-NNWx7<|49%2%lvTEeE3_|$~iDd zlktnxf|NB>Bui)yY;%TXgWA;rhODxR!-4GzxKd473I)3;j zS4-Vun<^vqZ4)HbwVzq%5%)S!>Q(Y&PkF ze1ab_oy?_$PZT-mKJ3K^;6MrB*Wc$lkYJ_460AFYEq{AjlDxm;5GRwOsm>doJy3Z;a$FSu-q`X)Hw&o~?_`Dnpx4dOj6#)bGOdZbj4hD`l|&#v~K+WrGO zpLCVQg=*h=4H1sK)D*Pxtb#p`c+g-eK65k!CEZjLIrfm}5<4@KVqZ#8(S$vs{?Xv) zN(}CKeeLR%I)W!p(l-`utKmBbJk-On{)ciqUN5%?TS8uwKU(8}Mxhsb&qHL~DDcB@qi} z4z%efV#K69a#lShPt5}*7ME@^xunFR{k;|qp z`_Hgdj_Qle?%Ydlr3f^SdRkUncIj7qTiu85tA$v`#419Nlof8tjqUjO z!Rwyq$Krfh`(@MiE+)yie&jU^crprYV&?zN!2b33S2qtW+zB$nahDhVZiQ12bmcwYBcT8KS>v`jsuI@X zy^k^7>TM5Ndp@}pSl6vmkk*u`@C`z(gq||K2>y=Y#w1i`Nk2zAP7{_^ACq;bQmT<4 z;b~K3=?mhZX#~jQnZjdU={NFp^HRr~;^PXQ*UIHzkq}Xsn!56*ZCOOEt|^ zawUwGqs;zCEGKTz&(a(@h#0sCE+@`3Y<&}9!(;Pw&`C+d#D?`##&!@*ERaH0fksrh zfk2svh3!d~h{SC6BNKN2j6(lzC>S^_*eh{@gR zP$rV4$nqV1y|zYpd;nQr-`Vlp(5sQXx9AibC?xc7pUV)v2cWfHu3{KLbfP;;;`I&)mWqLcXt6s+5fps9R?K>hGR$5;=( zo#;zzQ5!w+(99p1_gh^?F#$tpMd+4%G|K`XG_#@A>30WD2L9!0a>wRASg`M?^z#)| zifb$d^KUD&3qLsr-@}r_7p${gdmRqhy819bj?yI-v({I?0o3_8d>CZmCzqH4{{QtD z|6dvgm<^~668G`6wLw4C4y-o9E9ZG3RiH^&;rrKSBT@bMR+!TlYo*9P;?Z>xORMV3ndQp9EyNn!!~j&x&$gDVke|t#!{QoegHm+sT7cSjwSu z)jZlx1pHrrx0&YCZWJ4xC&U%r_IwfJOxnYqYMcZn&_hgnKuls z>(Y$qjIhcweo^A_VVq_#UR&urF%UbI^><5L5zbV9owMQviM*_{@i|dke!!v8a_yG! zl%#ay{(6U_N3-)yeif3tx6>aQSf1r zO~OuNW<8a~bi6Xby0@tKw0@f1R!@+3S2nf#F&j~l0mrNCtjoZdPYob?g!Vx4F-uA2 z4uS>8eR?dA9i>C|cm-m5lO0m@$f(DB6Z<9Pe07xR`l4$ks3eZ@v5}1?^YNQy-tB(` zgUNZrGVf#b~`}jew;MQG1O~VDk_i*<)p9DfFfd;vLy4QZT zMYh|DxJg3-!{szUN*uo&`&DUkzq4qG2`Lj;Ar46DYUymcviS{Unhzmx z=LwZ-Lr{t1z?9Oip%!z{j+Z%_@u?C78I`V4fC`icG1c|2;`EoLK$ z)9|wrAV&V1Vgts~tQCw0X?X{74W"NJQ zW3BP!5c~4zzHJScwQK9L_%m%L`*q{1aW_3rObki~B~=Dwp`;@w`>xZ6;L_6oR^ecEcmup`yb}lgsmv zQByrxc)Qqm(Hwmq%fLjqcrJ5QR?z_*vb~Iy~RXUY8u-D8AqTzetwNNA8~N z$j&1>#IrkSslUqXW|l}q_TlTVKAjRI@~7(GLf4M>GM!3u_)y6ORe6BQUmrQ16%OdZKo(?AYXK$u@=)TPUi1EZaLBu zuEB(5GX2vcoHS%zMUW!DZ)&xRo@mhouk|hMgHIG>cO-Ah(0AMNlq@?cPpy==-!#qR zBov7l3G?P*KM(vU;Apo-(-Bw;sdQJkh~r)W$KRQKd!#(A8M34^MhD10Ra={AqSvjy z3>PkgA}f^@Rg1$dX=jaEB_V%_vn(W<111_s!g>CYrcwJTJ63Eeg6{_C`aMQIgKctR zHMjkr+y6gK!v6=6;CGl#?PqmiX`A@dmmvZ}-X$*q8}G(xq|voo`=$YZuQAa8f9ApK z1VQiEUQC=OO{|Nc8ZrXH208X|XCzIRO?+Nb55Jt@va9hqBpv)uY6ma-csvSlJoJOC zK;w~s?#r)K@gm*;((JsWHU-KvFTF)q>AzwPq)I+}OrSpxh!-CM1VEMfw1fpfVL7p{ z)I$rNp5kPYDsvXL>8nV{#0fa*lm`Z;0Z=c^1qveY2uk&7nhMRl`0EsvUuvwdF&o)PCVN3wmFBPegWzooF6y~b} zY>X2;LO~&rbvx59?(4^aR+s1C6hI4r&x9Q9jL9);KZEw_x%TWZXaMzK6|2XG9$IU% zkEq}t^YOaa4s`%7F31X-#SfMgNp+4R=g2G|O^W)6^2fEsmkTTaVhKCip+3qWuN8?y zaSD_k6%-@Ifhm`z02=ZWA-pi5`A=7ztHWpP2K5rCW{>!wIUOYrH``#bz>qVSH76=8 zyJ!sNBxt;dMTn}yzUTCq1!w;N7pzb?WJrQ50W+`meL{k8i0VhFsPUz(07k`l` zg1Ifl1fc-^p^MQCpK~Jk&L!*mWfNA!&MSu`sPmti>K-;I5Dk00nB3vOl4!JwjEx^X zWQsIp2Ea_>`9f@%zGl4i3FAO9=e$2^b_>_I*dqiLJ=@TejStM?^yX$9dW`#ha)PEF zPr0zUJX!j-juYK*olG_9axQniXhF|E}oSTG_S)FZ3sN4T@q zy{l;!{UGc};YVaT97tx3s7P$WsW2^*I+Hy;vqndG!9S?tZtDIRif1=bBsqtW-n7$O zWy}Z%jKSkgLX#1p>|#4l-!$c+kA`++Ixv(Rm`;Ilb3q3tD|QjaPQ8)BJ=8R*15?nL zJR{yuqOG&!JrSZ${#NY#b!k)yDajQ$A}fUQhe7ueN6h2Pj3wY5eo|7$PaJD zM69(ee1qlSyLn)Ln6)o53Lj)elqG}gb^c}qd(q&$8B5N%nSvEjUIoEXO^obv=A7QGZzNzjO*H=*b2!86Wnqdy~8ePkq@1D1)Q^O)JG;fUCV z`rgD}dJ{7BUyBbgoTL91w^q-CfBpDr?0R38`L%p9&Q#%r*@=9p-Ioqy+WscLq?jq5 zfyYNkxr7w)-(!c85mt!FjNJ4UE6P8p8sc#Kb4L1N3!yaCo9@t3n8s}K)1!w8x77xU zo-NY3SR*Pgt#~7F;y^J&Y%z^+C9wu;hN|TC7dqSHOB&kE)Q)6Ad(l&+tA@$@w0bhJ z6xc6EJ6nm{=|V7Vo&qcXc4i)D?X0Uk$p7~iI#ci?#fxvM z78`u%m5S@^_F;_foidufzP z&ueh-zU1yM&8~0lmOfkio-gBex`)?hCJcI8Jv*R0c|WUqSq z?RU?Rmm=3t_2C%%UW9c*D%T{T{EjryCKnsz9-*Bs0}M*xS&-odhCK5eS_Eqi&c6J4o|f&;uUT=p7Fedw#EUl4%Jv{w zq-cCF^otBvw~~$yj6O>>;VqU)3Ml&Atm$B(Ht8=+&x2VS5aWD&t*+04{@k^Gn~yHY zWYC%@ld;g_qz=k*;Iv&xs5M85cEZCU&n2Baf>R6*bN>V|!)eF&l#C93eAMLD=ZF7= zOISFfB5P;F!ngWD3jfMJE_53F&dFc{h2TwSbT*(h6!c}_5_UFeB_*DiyCrtQ7Byr@ z-aCWVt?J}YP7-lfelbrCgZ5mdv(^W&Yf^OvpI}#NbS1Lc`r4&t#3kM!utm&#a;?GR z-1FusfX7&?a9h`%8wf-ub7UXp44xm4w04)S$}}_hPqn-#IabO^bo6$K07>?%G5Xs( zA$*lz_K>lAJ_o<;fsP-*-%~O(716KRqVq+X@=5J~Z~bba(yWL`;EN`@Mr2it&Lkgb z&I|R5!`ZSk%)xFX*FYDaAh=5qWSY@rx1Du9J$$=lh#!~NcFJM&aOv)eqg_@`i^zJCR9a%_{k_i%msw5q z*3!~#Xx3r|VaAwiG}~9M#c`=$Uk6~xe)47ymW+;0DD_lV67L#ouJBa7mF{)X^?eIvPdOMu4ksr`jUP$~{x@?ev2 zdX#ite2RMim01PEg`;qfwg^;v8nx9F!CtNCvz)V{PVgCs*;_@_Rk*oLx@f7hJ7^^1 zu66b)mb!ZMXLZ%8dj-+JCMoWS-Wji-Q-~U7Pt~UiXMej8JcNjk<6?Wk9eLBIhu&Vo^0;AH> z;Q5^a!NZhP(vDfBv&?jdnQavv#8N0E{ZEgs>|1)qYo^t5 zIV$g~`C2%g$*(z4)8hJlK2mF-MJW#3%Cs6`uAvsgRtTo8n!Et)1pZx@_9I18 zw7ER9v~DyrC*R$do9aEw5r@B)YzHOLB^-c9Eu9D!sFmI8^VljVKE89{zY_8PTSF+J b^}%0^qYmp2WD(pA|JtZ4>nPPKybJpu3@X?! literal 0 HcmV?d00001 diff --git a/src/STM32CubeWL/SubGHz_Phy/radio.h b/src/STM32CubeWL/SubGHz_Phy/radio.h new file mode 100644 index 0000000..81023ba --- /dev/null +++ b/src/STM32CubeWL/SubGHz_Phy/radio.h @@ -0,0 +1,488 @@ +/*! + * \file radio.h + * + * \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 2020 STMicroelectronics + * + * @file radio.h + * @author MCD Application Team + * @brief Radio driver API definition + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __RADIO_H__ +#define __RADIO_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif +/* Includes ------------------------------------------------------------------*/ + +#include +#include + +/* Private typedef -----------------------------------------------------------*/ +/*! + * Radio driver supported modems + */ +typedef enum +{ + MODEM_FSK = 0, + MODEM_LORA, + MODEM_MSK, + /* ST_WORKAROUND_BEGIN: Upgraded modulations list */ + MODEM_BPSK, + MODEM_SIGFOX_TX, + MODEM_SIGFOX_RX, + /* ST_WORKAROUND_END */ +}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] LoraSnr_FskCfo + * FSK : Carrier Frequency Offset in kHz + * LoRa: SNR value in dB + */ + void ( *RxDone )( uint8_t *payload, uint16_t size, int16_t rssi, int8_t LoraSnr_FskCfo ); + /*! + * \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; + +#include "radio_ex.h" /* ST_WORKAROUND: extended radio functions */ + +/*! + * \brief Radio driver definition + */ +struct Radio_s +{ + /*! + * \brief Initializes the radio + * + * \param [in] events Structure containing the driver callback functions + */ + void ( *Init )( RadioEvents_t *events ); + /*! + * Return current radio status + * + * \return 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 + * + * \remark The FSK modem is always used for this task as we can select the Rx bandwidth at will. + * + * \param [in] freq Channel RF frequency in Hertz + * \param [in] rxBandwidth Rx bandwidth in Hertz + * \param [in] rssiThresh RSSI threshold in dBm + * \param [in] maxCarrierSenseTime Max time in milliseconds while the RSSI is measured + * + * \retval isFree [true: Channel is free, false: Channel is not free] + */ + bool ( *IsChannelFree )( uint32_t freq, uint32_t rxBandwidth, 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] 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] 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] payloadLen Sets payload length when fixed length is used + * \param [in] crcOn Enables/Disables the CRC [0: OFF, 1: ON] + * + * \retval airTime Computed airTime (ms) for the given packet payload length + */ + uint32_t ( *TimeOnAir )( RadioModems_t modem, uint32_t bandwidth, + uint32_t datarate, uint8_t coderate, + uint16_t preambleLen, bool fixLen, uint8_t payloadLen, + bool crcOn ); + /*! + * \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 ); + /* ST_WORKAROUND_BEGIN: Force register addr to uint16_t */ + /*! + * \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 ( *WriteRegisters )( 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 ( *ReadRegisters )( uint16_t addr, uint8_t *buffer, uint8_t size ); + /* ST_WORKAROUND_END */ + /*! + * \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 ); + /*! + * \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 ( *RxBoosted )( 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 ( *SetRxDutyCycle )( uint32_t rxTime, uint32_t sleepTime ); + /* ST_WORKAROUND_BEGIN: extended radio functions */ + /*! + * @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 un-modulated Carrier mode at power dBm + * + * \param [in] power Tx power in dBm + */ + void ( *TxCw )( int8_t power ); + /*! + * \brief Sets the reception parameters + * + * \param [in] modem Radio modem to be used [GENERIC_FSK or GENERIC_FSK] + * \param [in] config configuration of receiver + * fsk field to be used if modem =GENERIC_FSK + * lora field to be used if modem =GENERIC_LORA + * \param [in] rxContinuous Sets the reception in continuous mode + * [0: single mode, otherwise continuous mode] + * \param [in] symbTimeout Sets the RxSingle timeout value + * FSK : timeout in number of bytes + * LoRa: timeout in symbols + * \return 0 when no parameters error, -1 otherwise + */ + int32_t ( *RadioSetRxGenericConfig )( GenericModems_t modem, RxConfigGeneric_t* config, uint32_t rxContinuous, uint32_t symbTimeout ); + /*! + * \brief Sets the transmission parameters + * + * \param [in] modem Radio modem to be used [GENERIC_FSK or GENERIC_FSK or GENERIC_BPSK] + * \param [in] config configuration of receiver + * fsk field to be used if modem =GENERIC_FSK + * lora field to be used if modem =GENERIC_LORA + * bpsk field to be used if modem =GENERIC_BPSK + * \param [in] power Sets the output power [dBm] + * \param [in] timeout Reception timeout [ms] + * \return 0 when no parameters error, -1 otherwise + */ + int32_t ( *RadioSetTxGenericConfig )( GenericModems_t modem, TxConfigGeneric_t* config, int8_t power, uint32_t timeout ); + /*! + * \brief Starts sending long Packet, packet maybe short + * + * \param [in] payload_size total payload size to be sent + * \param [in] timeout in ms + * \param [in] TxLongPacketGetNextChunkCb callback to be implemented on user side to feed partial chunk + * buffer: source buffer allocated by the app + * size: size in bytes to feed + * \return 0 when no parameters error, -1 otherwise + */ + int32_t ( *TransmitLongPacket )( uint16_t payload_size, uint32_t timeout,void (*TxLongPacketGetNextChunkCb) ( uint8_t** buffer, uint8_t buffer_size ) ); + /*! + * \brief Starts receiving long Packet, packet maybe short + * + * \param [in] boosted_mode boosted_mode: 0 normal Rx, 1:improved sensitivity + * \param [in] timeout Reception timeout [ms] + * \param [in] RxLongStorePacketChunkCb callback to be implemented on user side to record partial chunk in the application + * buffer: source buffer allocated in the radio driver + * size: size in bytes to record + * \return 0 when no parameters error, -1 otherwise + */ + int32_t ( *ReceiveLongPacket )( uint8_t boosted_mode, uint32_t timeout, void (*RxLongStorePacketChunkCb) ( uint8_t* buffer, uint8_t chunk_size ) ); + /* ST_WORKAROUND_END */ +}; + +/*! + * \brief Radio driver + * + * \remark This variable is defined and initialized in the specific radio + * board implementation + */ +extern const struct Radio_s Radio; + +#ifdef __cplusplus +} +#endif + +#endif // __RADIO_H__ diff --git a/src/STM32CubeWL/SubGHz_Phy/radio_ex.h b/src/STM32CubeWL/SubGHz_Phy/radio_ex.h new file mode 100644 index 0000000..9a4d221 --- /dev/null +++ b/src/STM32CubeWL/SubGHz_Phy/radio_ex.h @@ -0,0 +1,311 @@ +/** + ****************************************************************************** + * @file radio_ex.h + * @author MCD Application Team + * @brief Extends radio capabilities (whitening, long packet) + ****************************************************************************** + * @attention + * + * Copyright (c) 2020(-2021) STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __RADIO_EX_H__ +#define __RADIO_EX_H__ + +#ifdef __cplusplus +extern "C" { +#endif +/* Includes ------------------------------------------------------------------*/ + +/*******************************************Radio LORA enum*****************************************/ +typedef enum +{ + GENERIC_FSK = 0, + GENERIC_LORA, + GENERIC_BPSK, /*Tx only. In this mode, only payload is generated at the antenna (e.g. preamble nor syncword is generated and must be placed into the payload*/ + GENERIC_MSK, /*Tx only. For Rx, FSK must be used*/ +}GenericModems_t; + +/*! + * @brief Represents the possible spreading factor values in LoRa packet types + */ +typedef enum +{ + RADIO_LORA_SF5 = 0x05, + RADIO_LORA_SF6 = 0x06, + RADIO_LORA_SF7 = 0x07, + RADIO_LORA_SF8 = 0x08, + RADIO_LORA_SF9 = 0x09, + RADIO_LORA_SF10 = 0x0A, + RADIO_LORA_SF11 = 0x0B, + RADIO_LORA_SF12 = 0x0C, +}RADIO_LoRaSpreadingFactors_t; + +/*! + * @brief Represents the coding rate values for LoRa packet type + */ +typedef enum +{ + RADIO_LORA_CR_4_5 = 0x01, + RADIO_LORA_CR_4_6 = 0x02, + RADIO_LORA_CR_4_7 = 0x03, + RADIO_LORA_CR_4_8 = 0x04, +}RADIO_LoRaCodingRates_t; +/*! + * @brief Represents the bandwidth values for LoRa packet type + */ +typedef enum +{ + RADIO_LORA_BW_500 = 6, + RADIO_LORA_BW_250 = 5, + RADIO_LORA_BW_125 = 4, + RADIO_LORA_BW_062 = 3, + RADIO_LORA_BW_041 = 10, + RADIO_LORA_BW_031 = 2, + RADIO_LORA_BW_020 = 9, + RADIO_LORA_BW_015 = 1, + RADIO_LORA_BW_010 = 8, + RADIO_LORA_BW_007 = 0, +}RADIO_LoRaBandwidths_t; + +/*! + * @brief Holds the lengths mode of a LoRa packet type + */ +typedef enum +{ + RADIO_LORA_PACKET_VARIABLE_LENGTH = 0x00, //!< The packet is on variable size, header included + RADIO_LORA_PACKET_FIXED_LENGTH = 0x01, //!< The packet is known on both sides, no header included in the packet + RADIO_LORA_PACKET_EXPLICIT = RADIO_LORA_PACKET_VARIABLE_LENGTH, + RADIO_LORA_PACKET_IMPLICIT = RADIO_LORA_PACKET_FIXED_LENGTH, +}RADIO_LoRaPacketLengthsMode_t; + +/*! + * @brief Represents the CRC mode for LoRa packet type + */ +typedef enum +{ + RADIO_LORA_CRC_ON = 0x01, //!< CRC activated + RADIO_LORA_CRC_OFF = 0x00, //!< CRC not used +}RADIO_LoRaCrcModes_t; + +/*! + * @brief Represents the IQ mode for LoRa packet type + */ +typedef enum +{ + RADIO_LORA_IQ_NORMAL = 0x00, + RADIO_LORA_IQ_INVERTED = 0x01, +}RADIO_LoRaIQModes_t; + +/*! + * @brief Represents the IQ mode for LoRa packet type + */ +typedef enum +{ + RADIO_LORA_LOWDR_OPT_OFF = 0x00, /*Forced to 0*/ + RADIO_LORA_LOWDR_OPT_ON = 0x01, /*Forced to 1*/ + RADIO_LORA_LOWDR_OPT_AUTO = 0x02, /*Forced to 1 when SF11 or SF12, 0 otherwise*/ +}RADIO_Ld_Opt_t; + +/*******************************************Radio FSK enum*****************************************/ + +/*! + * @brief Represents the modulation shaping parameter + */ +typedef enum +{ + RADIO_FSK_MOD_SHAPING_OFF = 0x00, + RADIO_FSK_MOD_SHAPING_G_BT_03 = 0x08, + RADIO_FSK_MOD_SHAPING_G_BT_05 = 0x09, + RADIO_FSK_MOD_SHAPING_G_BT_07 = 0x0A, + RADIO_FSK_MOD_SHAPING_G_BT_1 = 0x0B, +}RADIO_FSK_ModShapings_t; + +/*! + * @brief Represents the preamble length used to detect the packet on Rx side + */ +typedef enum +{ + RADIO_FSK_PREAMBLE_DETECTOR_OFF = 0x00, //!< Preamble detection length off + RADIO_FSK_PREAMBLE_DETECTOR_08_BITS = 0x04, //!< Preamble detection length 8 bits + RADIO_FSK_PREAMBLE_DETECTOR_16_BITS = 0x05, //!< Preamble detection length 16 bits + RADIO_FSK_PREAMBLE_DETECTOR_24_BITS = 0x06, //!< Preamble detection length 24 bits + RADIO_FSK_PREAMBLE_DETECTOR_32_BITS = 0x07, //!< Preamble detection length 32 bit +}RADIO_FSK_PreambleDetection_t; + +/*! + * @brief Represents the possible combinations of SyncWord correlators activated + */ +typedef enum +{ + RADIO_FSK_ADDRESSCOMP_FILT_OFF = 0x00, //!< No correlator turned on, i.e. do not search for SyncWord + RADIO_FSK_ADDRESSCOMP_FILT_NODE = 0x01, + RADIO_FSK_ADDRESSCOMP_FILT_NODE_BROAD = 0x02, +}RADIO_FSK_AddressComp_t; + +/*! + * @brief Radio packet length mode + */ +typedef enum +{ + RADIO_FSK_PACKET_FIXED_LENGTH = 0x00, //!< The packet is known on both sides, no header included in the packet + RADIO_FSK_PACKET_VARIABLE_LENGTH = 0x01, //!< 1 byte packet length field inserted after the sync word*/ + RADIO_FSK_PACKET_2BYTES_LENGTH = 0x02 //!< 2 bytes packet length field inserted after the sync word, payload size greater than 255 bytes */ +}RADIO_FSK_PacketLengthModes_t; + +/*! + * @brief Represents the CRC length + */ +typedef enum +{ + RADIO_FSK_CRC_OFF = 0x01, //!< No CRC in use + RADIO_FSK_CRC_1_BYTES = 0x00, + RADIO_FSK_CRC_2_BYTES = 0x02, + RADIO_FSK_CRC_1_BYTES_INV = 0x04, + RADIO_FSK_CRC_2_BYTES_INV = 0x06, + RADIO_FSK_CRC_2_BYTES_IBM = 0xF1, + RADIO_FSK_CRC_2_BYTES_CCIT = 0xF2, +}RADIO_FSK_CrcTypes_t; + +/*! + * @brief Radio whitening mode Off, CCIT or ibm + */ +typedef enum +{ + RADIO_FSK_DC_FREE_OFF = 0x00, /*whitening Off*/ + RADIO_FSK_DC_FREEWHITENING = 0x01, /*whitening CCIT*/ + RADIO_FSK_DC_IBM_WHITENING = 0x02, /*whitening IBM*/ +}RADIO_FSK_DcFree_t; + +/*! + * @brief Radio Lora generic Rx parameters + */ +typedef struct +{ + uint32_t StopTimerOnPreambleDetect; /*0 inactive, otherwise active*/ + RADIO_LoRaSpreadingFactors_t SpreadingFactor; + RADIO_LoRaBandwidths_t Bandwidth; + RADIO_LoRaCodingRates_t Coderate; + RADIO_Ld_Opt_t LowDatarateOptimize;/*0 inactive, 1 active, otherwise auto (active for SF11 and SF12)*/ + uint16_t PreambleLen; + RADIO_LoRaPacketLengthsMode_t LengthMode; + uint8_t MaxPayloadLength; + RADIO_LoRaCrcModes_t CrcMode; + RADIO_LoRaIQModes_t IqInverted; +} generic_param_rx_lora_t; + +/*! + * @brief Radio FSK generic Rx parameters + */ +typedef struct +{ + uint32_t StopTimerOnPreambleDetect; + uint32_t Bandwidth; + uint32_t BitRate; /* BitRate */ + uint32_t PreambleLen; /* Preamble length in Byte */ + uint8_t* SyncWord; /* SyncWord Buffer, 8 bytes max */ + uint32_t MaxPayloadLength; /* maximum Payload length to listen */ + uint16_t CrcPolynomial; /* Polynomial of the Crc*/ + uint16_t CrcSeed; /* Seed of the Crc*/ + uint16_t whiteSeed; /* WhiteningSeed, whitening can also be disabled by setting this field to 0 */ + uint8_t SyncWordLength; /* SyncWord Buffer length in Byte*/ + RADIO_FSK_PreambleDetection_t PreambleMinDetect; + RADIO_FSK_ModShapings_t ModulationShaping; + RADIO_FSK_AddressComp_t AddrComp; + RADIO_FSK_PacketLengthModes_t LengthMode; /* If the header is explicit, it will be transmitted in the GFSK packet. If the header is implicit, it will not be transmitted */ + RADIO_FSK_CrcTypes_t CrcLength; /* Size of the CRC block in the GFSK packet */ + RADIO_FSK_DcFree_t Whitening; /* whitening type*/ +} generic_param_rx_fsk_t; + +/*! + * @brief Radio generic Rx Configuration + */ +typedef struct +{ + generic_param_rx_fsk_t fsk; + generic_param_rx_lora_t lora; +} RxConfigGeneric_t; + +/*! + * @brief Radio BPSK generic Tx parameters + */ +typedef struct +{ + uint32_t BitRate; /*BitRate*/ +} generic_param_tx_bpsk_t; + +/*! + * @brief Radio Lora generic Tx parameters + */ +typedef struct +{ + RADIO_LoRaSpreadingFactors_t SpreadingFactor; + RADIO_LoRaBandwidths_t Bandwidth; + RADIO_LoRaCodingRates_t Coderate; + RADIO_Ld_Opt_t LowDatarateOptimize; /*0 inactive, otherwise active*/ + uint16_t PreambleLen; + RADIO_LoRaPacketLengthsMode_t LengthMode; + RADIO_LoRaCrcModes_t CrcMode; + RADIO_LoRaIQModes_t IqInverted; +} generic_param_tx_lora_t; + +/*! + * @brief Radio FSK generic Tx parameters + */ +typedef struct +{ + uint32_t BitRate; /* BitRate */ + uint32_t PreambleLen; /* in Byte */ + uint8_t* SyncWord; /* SyncWord Buffer, 8 bytes max */ + uint16_t CrcPolynomial; + uint16_t CrcSeed; + uint16_t whiteSeed; /* Whitening seed, whitening can be disabled by setting this field to 0 */ + uint8_t SyncWordLength; /* in Byte */ + RADIO_FSK_ModShapings_t ModulationShaping; + RADIO_FSK_PacketLengthModes_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 */ + RADIO_FSK_CrcTypes_t CrcLength; /* Size of the CRC block in the GFSK packet */ + RADIO_FSK_DcFree_t Whitening; + uint32_t FrequencyDeviation; /* FrequencyDeviation */ +} generic_param_tx_fsk_t; + +/*! + * @brief Radio MSK generic Tx parameters + */ +typedef struct +{ + uint32_t BitRate; /* BitRate */ + uint32_t PreambleLen; /* in Byte */ + uint8_t* SyncWord; /* SyncWord Buffer, 8 bytes max */ + uint16_t CrcPolynomial; + uint16_t CrcSeed; + uint16_t whiteSeed; /* Whitening seed, whitening can be disabled by setting this field to 0 */ + uint8_t SyncWordLength; /* in Byte */ + RADIO_FSK_ModShapings_t ModulationShaping; + RADIO_FSK_PacketLengthModes_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 */ + RADIO_FSK_CrcTypes_t CrcLength; /* Size of the CRC block in the GFSK packet */ + RADIO_FSK_DcFree_t Whitening; +} generic_param_tx_msk_t; +/*! + * @brief Radio generic Tx Configuration + */ +typedef union +{ + generic_param_tx_fsk_t fsk; + generic_param_tx_lora_t lora; + generic_param_tx_bpsk_t bpsk; + generic_param_tx_msk_t msk; +} TxConfigGeneric_t; + +#ifdef __cplusplus +} +#endif + +#endif // __RADIO_EX_H__ diff --git a/src/STM32CubeWL/SubGHz_Phy/stm32_radio_driver/radio.c b/src/STM32CubeWL/SubGHz_Phy/stm32_radio_driver/radio.c new file mode 100644 index 0000000..bd55a77 --- /dev/null +++ b/src/STM32CubeWL/SubGHz_Phy/stm32_radio_driver/radio.c @@ -0,0 +1,2232 @@ +/*! + * \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 2020 STMicroelectronics + * + * @file radio.c + * @author MCD Application Team + * @brief Radio driver API definition + ****************************************************************************** + */ +/* Includes ------------------------------------------------------------------*/ +#include +#include "../../../BSP/timer.h" +#include "../radio.h" +#include "radio_fw.h" +#include "radio_driver.h" +#include "../../../BSP/radio_conf.h" +#include "../../../BSP/mw_log_conf.h" + +/* Private typedef -----------------------------------------------------------*/ +/* ST_WORKAROUND_BEGIN: Replace global variables by typedef struct */ +/*! + * Radio hardware and global parameters + */ +typedef struct SubgRf_s +{ + RadioModems_t Modem; + bool RxContinuous; + uint32_t TxTimeout; + uint32_t RxTimeout; + struct + { + bool Previous; + bool Current; + } PublicNetwork; + PacketParams_t PacketParams; + PacketStatus_t PacketStatus; + ModulationParams_t ModulationParams; + RadioIrqMasks_t RadioIrq; + uint8_t AntSwitchPaSelect; + uint32_t RxDcPreambleDetectTimeout; /* 0:RxDutyCycle is off, otherwise on with 2*rxTime + sleepTime (See STM32WL Errata: RadioSetRxDutyCycle)*/ +} SubgRf_t; +/* ST_WORKAROUND_END */ + +/* Private macro -------------------------------------------------------------*/ +/* ST_WORKAROUND_BEGIN: Add utilities macro to prevent missing header file */ +#define RADIO_BIT_MASK(__n) (~(1<<__n)) + +/** + * \brief Calculates ceiling division of ( X / N ) + * + * \param [in] X numerator + * \param [in] N denominator + * + */ +#ifndef DIVC +#define DIVC( X, N ) ( ( ( X ) + ( N ) - 1 ) / ( N ) ) +#endif + +/** + * \brief Calculates rounding division of ( X / N ) + * + * \param [in] X numerator + * \param [in] N denominator + * + */ +#ifndef DIVR +#define DIVR( X, N ) ( ( ( X ) + ( ((X)>0?(N):(N))>>1 ) ) / ( N ) ) +#endif +/* ST_WORKAROUND_END */ + +/* Private define ------------------------------------------------------------*/ +/* ST_WORKAROUND_BEGIN: Add radio defines to prevent missing header file */ +/*can be overridden in radio_conf.h*/ +#ifndef XTAL_FREQ +#define XTAL_FREQ 32000000UL +#endif +/*can be overridden in radio_conf.h*/ +#ifndef RADIO_IRQ_PROCESS_INIT +#define RADIO_IRQ_PROCESS_INIT() +#endif +/*can be overridden in radio_conf.h*/ +#ifndef RADIO_IRQ_PROCESS +#define RADIO_IRQ_PROCESS() RadioIrqProcess() +#endif +/*can be overridden in radio_conf.h*/ +#ifndef RADIO_RX_TIMEOUT_PROCESS +#define RADIO_RX_TIMEOUT_PROCESS() RadioOnRxTimeoutProcess() +#endif +/*can be overridden in radio_conf.h*/ +#ifndef RADIO_TX_TIMEOUT_PROCESS +#define RADIO_TX_TIMEOUT_PROCESS() RadioOnTxTimeoutProcess() +#endif +/*can be overridden in radio_conf.h*/ +#ifndef IRQ_TX_DBG +#define IRQ_TX_DBG ((uint16_t) 0) +#endif +/*can be overridden in radio_conf.h*/ +#ifndef IRQ_RX_DBG +#define IRQ_RX_DBG ((uint16_t) 0) +#endif +/*can be overridden in radio_conf.h*/ +#ifndef RADIO_SIGFOX_ENABLE +#define RADIO_SIGFOX_ENABLE 1 +#endif +/*can be overridden in radio_conf.h*/ +#ifndef RADIO_GENERIC_CONFIG_ENABLE +#define RADIO_GENERIC_CONFIG_ENABLE 1 +#endif + +/*can be overridden in radio_conf.h*/ +#ifndef DBG_GPIO_RADIO_RX +#define DBG_GPIO_RADIO_RX(set_rst) +#endif + +/*can be overridden in radio_conf.h*/ +#ifndef DBG_GPIO_RADIO_TX +#define DBG_GPIO_RADIO_TX(set_rst) +#endif + +#define RADIO_BUF_SIZE 255 +/* ST_WORKAROUND_END */ + +/* Private function prototypes -----------------------------------------------*/ +/*! + * \brief Initializes the radio + * + * \param [in] events Structure containing the driver callback functions + */ +static void RadioInit( RadioEvents_t *events ); + +/*! + * Return current radio status + * + * \return status Radio status.[RF_IDLE, RF_RX_RUNNING, RF_TX_RUNNING] + */ +static RadioState_t RadioGetStatus( void ); + +/*! + * \brief Configures the radio with the given modem + * + * \param [in] modem Modem to be used [0: FSK, 1: LoRa] + */ +static void RadioSetModem( RadioModems_t modem ); + +/*! + * \brief Sets the channel frequency + * + * \param [in] freq Channel RF frequency + */ +static void RadioSetChannel( uint32_t freq ); + +/*! + * \brief Checks if the channel is free for the given time + * + * \remark The FSK modem is always used for this task as we can select the Rx bandwidth at will. + * + * \param [in] freq Channel RF frequency in Hertz + * \param [in] rxBandwidth Rx bandwidth in Hertz + * \param [in] rssiThresh RSSI threshold in dBm + * \param [in] maxCarrierSenseTime Max time in milliseconds while the RSSI is measured + * + * \retval isFree [true: Channel is free, false: Channel is not free] + */ +static bool RadioIsChannelFree( uint32_t freq, uint32_t rxBandwidth, 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 + */ +static 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] + */ +static 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] + */ +static 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] + */ +static 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] 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] 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] payloadLen Sets payload length when fixed length is used + * \param [in] crcOn Enables/Disables the CRC [0: OFF, 1: ON] + * + * \retval airTime Computed airTime (ms) for the given packet payload length + */ +static uint32_t RadioTimeOnAir( RadioModems_t modem, uint32_t bandwidth, + uint32_t datarate, uint8_t coderate, + uint16_t preambleLen, bool fixLen, uint8_t payloadLen, + bool crcOn ); + +/*! + * \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 + */ +static void RadioSend( uint8_t *buffer, uint8_t size ); + +/*! + * \brief Sets the radio in sleep mode + */ +static void RadioSleep( void ); + +/*! + * \brief Sets the radio in standby mode + */ +static void RadioStandby( void ); + +/*! + * \brief Sets the radio in reception mode for the given time + * \param [in] timeout Reception timeout [ms] + * [0: continuous, others timeout] + */ +static void RadioRx( uint32_t timeout ); + +/*! + * \brief Start a Channel Activity Detection + */ +static 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] + */ +static void RadioSetTxContinuousWave( uint32_t freq, int8_t power, uint16_t time ); + +/*! + * \brief Reads the current RSSI value + * + * \retval rssiValue Current RSSI value in [dBm] + */ +static int16_t RadioRssi( RadioModems_t modem ); + +/* ST_WORKAROUND_BEGIN: Force register addr to uint16_t */ +/*! + * \brief Writes the radio register at the specified address + * + * \param [in] addr Register address + * \param [in] data New register value + */ +static 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 + */ +static 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 + */ +static void RadioWriteRegisters( 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 + */ +static void RadioReadRegisters( uint16_t addr, uint8_t *buffer, uint8_t size ); +/* ST_WORKAROUND_END */ + +/*! + * \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 + */ +static 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 + */ +static 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. + */ +static uint32_t RadioGetWakeupTime( void ); + +/*! + * \brief Process radio irq + */ +static void RadioIrqProcess( 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] + */ +static 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 + */ +static void RadioSetRxDutyCycle( uint32_t rxTime, uint32_t sleepTime ); + +/*! + * \brief radio IRQ callback + * + * \param [in] radioIrq mask of radio irq + */ +static void RadioOnDioIrq( RadioIrqMasks_t radioIrq ); + +/*! + * \brief Tx timeout timer callback + * + * \param [in] context context of the interrupt + */ +static void RadioOnTxTimeoutIrq( void *context ); + +/*! + * \brief Rx timeout timer callback + * + * \param [in] context context of the interrupt + */ +static void RadioOnRxTimeoutIrq( void *context ); + +/*! + * \brief Rx timeout timer process + */ +static void RadioOnRxTimeoutProcess( void ); + +/*! + * \brief Tx timeout timer process + */ +static void RadioOnTxTimeoutProcess( void ); + +#if (RADIO_SIGFOX_ENABLE == 1) +/* ST_WORKAROUND_BEGIN: extended radio functions */ +/*! + * @brief D-BPSK to BPSK + * + * @param [out] outBuffer buffer with frame encoded + * @param [in] inBuffer buffer with frame to encode + * @param [in] size size of the payload to encode + */ +static void payload_integration( uint8_t *outBuffer, uint8_t *inBuffer, uint8_t size ); +#endif /*RADIO_SIGFOX_ENABLE == 1*/ +/*! + * \brief Sets the Transmitter in continuous PRBS mode + */ +static void RadioTxPrbs( void ); + +/*! + * \brief Sets the Transmitter in continuous un-modulated Carrier mode at power dBm + * + * \param [in] power Tx power in dBm + */ +static void RadioTxCw( int8_t power ); + +/*! + * \brief Sets the reception parameters + * + * \param [in] modem Radio modem to be used [GENERIC_FSK or GENERIC_FSK] + * \param [in] config configuration of receiver + * fsk field to be used if modem =GENERIC_FSK +* lora field to be used if modem =GENERIC_LORA + * \param [in] rxContinuous Sets the reception in continuous mode + * [0: single mode, otherwise continuous mode] + * \param [in] symbTimeout Sets the RxSingle timeout value + * FSK : timeout in number of bytes + * LoRa: timeout in symbols + * \return 0 when no parameters error, -1 otherwise + */ +static int32_t RadioSetRxGenericConfig( GenericModems_t modem, RxConfigGeneric_t *config, + uint32_t rxContinuous, uint32_t symbTimeout ); + +/*! + * \brief Sets the transmission parameters + * + * \param [in] modem Radio modem to be used [GENERIC_FSK or GENERIC_FSK or GENERIC_BPSK] + * \param [in] config configuration of receiver + * fsk field to be used if modem =GENERIC_FSK +* lora field to be used if modem =GENERIC_LORA + bpsk field to be used if modem =GENERIC_BPSK + * \param [in] power Sets the output power [dBm] + * \param [in] timeout Transmission timeout [ms] + * \return 0 when no parameters error, -1 otherwise + */ +static int32_t RadioSetTxGenericConfig( GenericModems_t modem, TxConfigGeneric_t *config, + int8_t power, uint32_t timeout ); + +/*! + * \brief Convert the bandwidth enum to Hz value + * + * \param [in] bw RF frequency to be checked + * \retval bandwidthInHz bandwidth value in Hertz + */ +static uint32_t RadioGetLoRaBandwidthInHz( RadioLoRaBandwidths_t bw ); + +/*! + * \brief Computes the time on air GFSK numerator + * + * \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] payloadLen Sets payload length when fixed length is used + * \param [in] crcOn Enables/Disables the CRC [0: OFF, 1: ON] + * \retval numerator time on air GFSK numerator + */ +static uint32_t RadioGetGfskTimeOnAirNumerator( uint32_t datarate, uint8_t coderate, + uint16_t preambleLen, bool fixLen, uint8_t payloadLen, + bool crcOn ); + +/*! + * \brief Computes the time on air LoRa numerator + * + * \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] 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] payloadLen Sets payload length when fixed length is used + * \param [in] crcOn Enables/Disables the CRC [0: OFF, 1: ON] + * \retval numerator time on air LoRa numerator + */ +static uint32_t RadioGetLoRaTimeOnAirNumerator( uint32_t bandwidth, + uint32_t datarate, uint8_t coderate, + uint16_t preambleLen, bool fixLen, uint8_t payloadLen, + bool crcOn ); +/* ST_WORKAROUND_END */ + +/* Private variables ---------------------------------------------------------*/ +/*! + * 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, + RadioWriteRegisters, + RadioReadRegisters, + RadioSetMaxPayloadLength, + RadioSetPublicNetwork, + RadioGetWakeupTime, + RadioIrqProcess, + RadioRxBoosted, + RadioSetRxDutyCycle, + /* ST_WORKAROUND_BEGIN: extended radio functions */ + RadioTxPrbs, + RadioTxCw, + RadioSetRxGenericConfig, + RadioSetTxGenericConfig, + RFW_TransmitLongPacket, + RFW_ReceiveLongPacket + /* ST_WORKAROUND_END */ +}; + +const RadioLoRaBandwidths_t Bandwidths[] = { LORA_BW_125, LORA_BW_250, LORA_BW_500 }; + +static uint8_t MaxPayloadLength = RADIO_BUF_SIZE; + +static uint8_t RadioBuffer[RADIO_BUF_SIZE]; + +/* + * Radio callbacks variable + */ +static RadioEvents_t *RadioEvents; + +/*! + * Radio hardware and global parameters + */ +SubgRf_t SubgRf; + +/*! + * Tx and Rx timers + */ +TimerEvent_t TxTimeoutTimer; +TimerEvent_t RxTimeoutTimer; + +/* Private functions ---------------------------------------------------------*/ + +static void RadioInit( RadioEvents_t *events ) +{ + RadioEvents = events; + + SubgRf.RxContinuous = false; + SubgRf.TxTimeout = 0; + SubgRf.RxTimeout = 0; + /*See STM32WL Errata: RadioSetRxDutyCycle*/ + SubgRf.RxDcPreambleDetectTimeout = 0; + + SUBGRF_Init( RadioOnDioIrq ); + /*SubgRf.publicNetwork set to false*/ + SubgRf.PublicNetwork.Current = false; + SubgRf.PublicNetwork.Previous = false; + + RADIO_IRQ_PROCESS_INIT(); + + SUBGRF_SetRegulatorMode( ); + + SUBGRF_SetBufferBaseAddress( 0x00, 0x00 ); + SUBGRF_SetTxParams( RFO_LP, 0, RADIO_RAMP_200_US ); + SUBGRF_SetDioIrqParams( IRQ_RADIO_ALL, IRQ_RADIO_ALL, IRQ_RADIO_NONE, IRQ_RADIO_NONE ); + + /* ST_WORKAROUND_BEGIN: Sleep radio */ + RadioSleep(); + /* ST_WORKAROUND_END */ + // Initialize driver timeout timers + TimerInit( &TxTimeoutTimer, RadioOnTxTimeoutIrq ); + TimerInit( &RxTimeoutTimer, RadioOnRxTimeoutIrq ); + TimerStop( &TxTimeoutTimer ); + TimerStop( &RxTimeoutTimer ); +} + +static RadioState_t RadioGetStatus( void ) +{ + switch( SUBGRF_GetOperatingMode( ) ) + { + case MODE_TX: + return RF_TX_RUNNING; + case MODE_RX: + return RF_RX_RUNNING; + case MODE_CAD: + return RF_CAD; + default: + return RF_IDLE; + } +} + +static void RadioSetModem( RadioModems_t modem ) +{ + SubgRf.Modem = modem; + RFW_SetRadioModem( modem ); + switch( modem ) + { + default: + case MODEM_MSK: + SUBGRF_SetPacketType( PACKET_TYPE_GMSK ); + // When switching to GFSK mode the LoRa SyncWord register value is reset + // Thus, we also reset the RadioPublicNetwork variable + SubgRf.PublicNetwork.Current = false; + break; + 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 + SubgRf.PublicNetwork.Current = false; + break; + case MODEM_LORA: + SUBGRF_SetPacketType( PACKET_TYPE_LORA ); + // Public/Private network register is reset when switching modems + if( SubgRf.PublicNetwork.Current != SubgRf.PublicNetwork.Previous ) + { + SubgRf.PublicNetwork.Current = SubgRf.PublicNetwork.Previous; + RadioSetPublicNetwork( SubgRf.PublicNetwork.Current ); + } + break; + case MODEM_BPSK: + SUBGRF_SetPacketType( PACKET_TYPE_BPSK ); + // When switching to BPSK mode the LoRa SyncWord register value is reset + // Thus, we also reset the RadioPublicNetwork variable + SubgRf.PublicNetwork.Current = false; + break; +#if (RADIO_SIGFOX_ENABLE == 1) + case MODEM_SIGFOX_TX: + SUBGRF_SetPacketType( PACKET_TYPE_BPSK ); + // When switching to BPSK mode the LoRa SyncWord register value is reset + // Thus, we also reset the RadioPublicNetwork variable + SubgRf.PublicNetwork.Current = false; + break; + case MODEM_SIGFOX_RX: + SUBGRF_SetPacketType( PACKET_TYPE_GFSK ); + // When switching to GFSK mode the LoRa SyncWord register value is reset + // Thus, we also reset the RadioPublicNetwork variable + SubgRf.PublicNetwork.Current = false; + break; +#endif /*RADIO_SIGFOX_ENABLE == 1*/ + } +} + +static void RadioSetChannel( uint32_t freq ) +{ + SUBGRF_SetRfFrequency( freq ); +} + +static bool RadioIsChannelFree( uint32_t freq, uint32_t rxBandwidth, int16_t rssiThresh, uint32_t maxCarrierSenseTime ) +{ + bool status = true; + int16_t rssi = 0; + uint32_t carrierSenseTime = 0; + + RadioStandby( ); /* ST_WORKAROUND: Prevent multiple sleeps with TXCO delay */ + + RadioSetModem( MODEM_FSK ); + + RadioSetChannel( freq ); + + // Set Rx bandwidth. Other parameters are not used. + RadioSetRxConfig( MODEM_FSK, rxBandwidth, 600, 0, rxBandwidth, 3, 0, false, + 0, false, 0, 0, false, true ); + RadioRx( 0 ); + + RADIO_DELAY_MS( RadioGetWakeupTime( ) ); + + carrierSenseTime = TimerGetCurrentTime( ); + + // Perform carrier sense for maxCarrierSenseTime + while( TimerGetElapsedTime( carrierSenseTime ) < maxCarrierSenseTime ) + { + rssi = RadioRssi( MODEM_FSK ); + + if( rssi > rssiThresh ) + { + status = false; + break; + } + } + RadioStandby( ); /* ST_WORKAROUND: Prevent multiple sleeps with TXCO delay */ + + return status; +} + +static uint32_t RadioRandom( void ) +{ + uint32_t rnd = 0; + + /* + * Radio setup for random number generation + */ + // Disable modem interrupts + SUBGRF_SetDioIrqParams( IRQ_RADIO_NONE, IRQ_RADIO_NONE, IRQ_RADIO_NONE, IRQ_RADIO_NONE ); + + rnd = SUBGRF_GetRandom(); + + return rnd; +} + +static 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 ) +{ +#if (RADIO_SIGFOX_ENABLE == 1) + uint8_t modReg; +#endif + SubgRf.RxContinuous = rxContinuous; + RFW_DeInit(); /* ST_WORKAROUND: Switch Off FwPacketDecoding by default */ + if( rxContinuous == true ) + { + symbTimeout = 0; + } + if( fixLen == true ) + { + MaxPayloadLength = payloadLen; + } + else + { + MaxPayloadLength = 0xFF; + } + + switch( modem ) + { +#if (RADIO_SIGFOX_ENABLE == 1) + 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 = SUBGRF_GetFskBandwidthRegValue( 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; + + 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(SUBGHZ_AGCGFORSTCFGR); + modReg&=RADIO_BIT_MASK(4); + RadioWrite(SUBGHZ_AGCGFORSTCFGR, modReg); + /* Lower the threshold of cfo_reset */ + RadioWrite(SUBGHZ_AGCGFORSTPOWTHR, 0x4 ); + + /* Bigger rssi_len (stability AGC). Reg 0x89b, bits[2 :4] = 0x1 */ + modReg= RadioRead(SUBGHZ_AGCRSSICTL0R); + modReg&=( RADIO_BIT_MASK(2) & RADIO_BIT_MASK(3) & RADIO_BIT_MASK(4) ); + RadioWrite(SUBGHZ_AGCRSSICTL0R, (modReg| (0x1<<3) ) ); + + /* Bigger afc_pbl_len (better frequency correction). Reg 0x6d1, bits[3 :4] = 0x3 */ + modReg= RadioRead(SUBGHZ_GAFCR); + modReg&=( RADIO_BIT_MASK(3) & RADIO_BIT_MASK(4) ); + RadioWrite(SUBGHZ_GAFCR, (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(SUBGHZ_GBSYNCR); + modReg&=( RADIO_BIT_MASK(4) & RADIO_BIT_MASK(5) & RADIO_BIT_MASK(6) ); + RadioWrite(SUBGHZ_GBSYNCR, (modReg| (0x5<<4) )); + /*timeout unused when SubgRf.RxContinuous*/ + SubgRf.RxTimeout = ( uint32_t )(( symbTimeout * 8 * 1000 ) /datarate); + break; +#endif /*RADIO_SIGFOX_ENABLE == 1*/ + 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 = SUBGRF_GetFskBandwidthRegValue( 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( MODEM_FSK ); + SUBGRF_SetModulationParams( &SubgRf.ModulationParams ); + SUBGRF_SetPacketParams( &SubgRf.PacketParams ); + SUBGRF_SetSyncWord( ( uint8_t[] ){ 0xC1, 0x94, 0xC1, 0x00, 0x00, 0x00, 0x00, 0x00 } ); + SUBGRF_SetWhiteningSeed( 0x01FF ); + + /*timeout unused when SubgRf.RxContinuous*/ + SubgRf.RxTimeout = ( uint32_t )(( symbTimeout * 8 * 1000 ) /datarate); + break; + + case MODEM_LORA: + SUBGRF_SetStopRxTimerOnPreambleDetect( false ); + 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( MODEM_LORA ); + SUBGRF_SetModulationParams( &SubgRf.ModulationParams ); + SUBGRF_SetPacketParams( &SubgRf.PacketParams ); + SUBGRF_SetLoRaSymbNumTimeout( symbTimeout ); + + // WORKAROUND - Optimizing the Inverted IQ Operation, see STM32WL Erratasheet + if( SubgRf.PacketParams.Params.LoRa.InvertIQ == LORA_IQ_INVERTED ) + { + // RegIqPolaritySetup = @address 0x0736 + SUBGRF_WriteRegister( SUBGHZ_LIQPOLR, SUBGRF_ReadRegister( SUBGHZ_LIQPOLR ) & ~( 1 << 2 ) ); + } + else + { + // RegIqPolaritySetup @address 0x0736 + SUBGRF_WriteRegister( SUBGHZ_LIQPOLR, SUBGRF_ReadRegister( SUBGHZ_LIQPOLR ) | ( 1 << 2 ) ); + } + // WORKAROUND END + + // Timeout Max, Timeout handled directly in SetRx function + SubgRf.RxTimeout = 0xFFFF; + + break; + default: + break; + } +} + +static 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 ) +{ + RFW_DeInit(); /* ST_WORKAROUND: Switch Off FwPacketDecoding by default */ + switch( modem ) + { + 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 = SUBGRF_GetFskBandwidthRegValue( 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( MODEM_FSK ); + 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( MODEM_LORA ); + SUBGRF_SetModulationParams( &SubgRf.ModulationParams ); + SUBGRF_SetPacketParams( &SubgRf.PacketParams ); + break; +#if (RADIO_SIGFOX_ENABLE == 1) + 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 ); + break; +#endif /*RADIO_SIGFOX_ENABLE == 1*/ + default: + break; + } + + SubgRf.AntSwitchPaSelect = SUBGRF_SetRfTxPower( power ); + RFW_SetAntSwitch( SubgRf.AntSwitchPaSelect ); /* ST_WORKAROUND: ?????? */ + SubgRf.TxTimeout = timeout; +} + +static bool RadioCheckRfFrequency( uint32_t frequency ) +{ + return true; +} + +static uint32_t RadioGetLoRaBandwidthInHz( RadioLoRaBandwidths_t bw ) +{ + uint32_t bandwidthInHz = 0; + + switch( bw ) + { + case LORA_BW_007: + bandwidthInHz = 7812UL; + break; + case LORA_BW_010: + bandwidthInHz = 10417UL; + break; + case LORA_BW_015: + bandwidthInHz = 15625UL; + break; + case LORA_BW_020: + bandwidthInHz = 20833UL; + break; + case LORA_BW_031: + bandwidthInHz = 31250UL; + break; + case LORA_BW_041: + bandwidthInHz = 41667UL; + break; + case LORA_BW_062: + bandwidthInHz = 62500UL; + break; + case LORA_BW_125: + bandwidthInHz = 125000UL; + break; + case LORA_BW_250: + bandwidthInHz = 250000UL; + break; + case LORA_BW_500: + bandwidthInHz = 500000UL; + break; + } + + return bandwidthInHz; +} + +static uint32_t RadioGetGfskTimeOnAirNumerator( uint32_t datarate, uint8_t coderate, + uint16_t preambleLen, bool fixLen, uint8_t payloadLen, + bool crcOn ) +{ + /* ST_WORKAROUND_BEGIN: Simplified calculation without const values */ + return ( preambleLen << 3 ) + + ( ( fixLen == false ) ? 8 : 0 ) + 24 + + ( ( payloadLen + ( ( crcOn == true ) ? 2 : 0 ) ) << 3 ); + /* ST_WORKAROUND_END */ +} + +static uint32_t RadioGetLoRaTimeOnAirNumerator( uint32_t bandwidth, + uint32_t datarate, uint8_t coderate, + uint16_t preambleLen, bool fixLen, uint8_t payloadLen, + bool crcOn ) +{ + int32_t crDenom = coderate + 4; + bool lowDatareOptimize = false; + + // Ensure that the preamble length is at least 12 symbols when using SF5 or SF6 + if( ( datarate == 5 ) || ( datarate == 6 ) ) + { + if( preambleLen < 12 ) + { + preambleLen = 12; + } + } + + if( ( ( bandwidth == 0 ) && ( ( datarate == 11 ) || ( datarate == 12 ) ) ) || + ( ( bandwidth == 1 ) && ( datarate == 12 ) ) ) + { + lowDatareOptimize = true; + } + + int32_t ceilDenominator; + int32_t ceilNumerator = ( payloadLen << 3 ) + + ( crcOn ? 16 : 0 ) - + ( 4 * datarate ) + + ( fixLen ? 0 : 20 ); + + if( datarate <= 6 ) + { + ceilDenominator = 4 * datarate; + } + else + { + ceilNumerator += 8; + + if( lowDatareOptimize == true ) + { + ceilDenominator = 4 * ( datarate - 2 ); + } + else + { + ceilDenominator = 4 * datarate; + } + } + + if( ceilNumerator < 0 ) + { + ceilNumerator = 0; + } + + // Perform integral ceil() + int32_t intermediate = + ( ( ceilNumerator + ceilDenominator - 1 ) / ceilDenominator ) * crDenom + preambleLen + 12; + + if( datarate <= 6 ) + { + intermediate += 2; + } + + return ( uint32_t )( ( 4 * intermediate + 1 ) * ( 1 << ( datarate - 2 ) ) ); +} + +static uint32_t RadioTimeOnAir( RadioModems_t modem, uint32_t bandwidth, + uint32_t datarate, uint8_t coderate, + uint16_t preambleLen, bool fixLen, uint8_t payloadLen, + bool crcOn ) +{ + uint32_t numerator = 0; + uint32_t denominator = 1; + + switch( modem ) + { + case MODEM_FSK: + { + numerator = 1000U * RadioGetGfskTimeOnAirNumerator( datarate, coderate, + preambleLen, fixLen, + payloadLen, crcOn ); + denominator = datarate; + } + break; + case MODEM_LORA: + { + numerator = 1000U * RadioGetLoRaTimeOnAirNumerator( bandwidth, datarate, + coderate, preambleLen, + fixLen, payloadLen, crcOn ); + denominator = RadioGetLoRaBandwidthInHz( Bandwidths[bandwidth] ); + } + break; + default: + break; + } + // Perform integral ceil() + return DIVC( numerator, denominator ); /* ST_WORKAROUND : simplified calculation with macro usage */ +} + +static void RadioSend( uint8_t *buffer, uint8_t size ) +{ + /* ST_WORKAROUND_BEGIN : Set the debug pin and update the radio switch */ + SUBGRF_SetDioIrqParams( IRQ_TX_DONE | IRQ_RX_TX_TIMEOUT | IRQ_TX_DBG, + IRQ_TX_DONE | IRQ_RX_TX_TIMEOUT | IRQ_TX_DBG, + IRQ_RADIO_NONE, + IRQ_RADIO_NONE ); + /* ST_WORKAROUND_END */ + + /* ST_WORKAROUND_BEGIN : Set the debug pin and update the radio switch */ + /* Set DBG pin */ + DBG_GPIO_RADIO_TX( SET ); + + /* Set RF switch */ + SUBGRF_SetSwitch( SubgRf.AntSwitchPaSelect, RFSWITCH_TX ); + /* WORKAROUND - Modulation Quality with 500 kHz LoRaTM Bandwidth*/ + /* RegTxModulation = @address 0x0889 */ + if( ( SubgRf.Modem == MODEM_LORA ) && ( SubgRf.ModulationParams.Params.LoRa.Bandwidth == LORA_BW_500 ) ) + { + SUBGRF_WriteRegister( SUBGHZ_SDCFG0R, SUBGRF_ReadRegister( SUBGHZ_SDCFG0R ) & ~( 1 << 2 ) ); + } + else + { + SUBGRF_WriteRegister( SUBGHZ_SDCFG0R, SUBGRF_ReadRegister( SUBGHZ_SDCFG0R ) | ( 1 << 2 ) ); + } + /* WORKAROUND END */ + switch( SubgRf.Modem ) + { + case MODEM_LORA: + { + SubgRf.PacketParams.Params.LoRa.PayloadLength = size; + SUBGRF_SetPacketParams( &SubgRf.PacketParams ); + SUBGRF_SendPayload( buffer, size, 0 ); + break; + } + case MODEM_MSK: + case MODEM_FSK: + { + if ( 1UL == RFW_Is_Init( ) ) + { + uint8_t outsize; + if ( 0UL == RFW_TransmitInit( buffer,size, &outsize ) ) + { + SubgRf.PacketParams.Params.Gfsk.PayloadLength = outsize; + SUBGRF_SetPacketParams( &SubgRf.PacketParams ); + SUBGRF_SendPayload( buffer, outsize, 0 ); + } + else + { + MW_LOG( TS_ON, VLEVEL_M, "RadioSend Oversize\r\n"); + return; + } + } + else + { + 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; + } +#if (RADIO_SIGFOX_ENABLE == 1) + case MODEM_SIGFOX_TX: + { + /* from bpsk to dbpsk */ + /* first 1 bit duplicated */ + /* RadioBuffer is 1 bytes more */ + payload_integration( RadioBuffer, buffer, size ); + + SubgRf.PacketParams.PacketType = PACKET_TYPE_BPSK; + SubgRf.PacketParams.Params.Bpsk.PayloadLength = size + 1; + SUBGRF_SetPacketParams( &SubgRf.PacketParams ); + + RadioWrite( SUBGHZ_RAM_RAMPUPL, 0 ); // clean start-up LSB + RadioWrite( SUBGHZ_RAM_RAMPUPH, 0 ); // clean start-up MSB + if( SubgRf.ModulationParams.Params.Bpsk.BitRate == 100 ) + { + RadioWrite( SUBGHZ_RAM_RAMPDNL, 0x70 ); // clean end of frame LSB + RadioWrite( SUBGHZ_RAM_RAMPDNH, 0x1D ); // clean end of frame MSB + } + else // 600 bps + { + RadioWrite( SUBGHZ_RAM_RAMPDNL, 0xE1 ); // clean end of frame LSB + RadioWrite( SUBGHZ_RAM_RAMPDNH, 0x04 ); // clean end of frame MSB + } + + uint16_t bitNum = ( size * 8 ) + 2; + RadioWrite( SUBGHZ_RAM_FRAMELIMH, ( bitNum >> 8 ) & 0x00FF ); // limit frame + RadioWrite( SUBGHZ_RAM_FRAMELIML, bitNum & 0x00FF ); // limit frame + SUBGRF_SendPayload( RadioBuffer, size+1 , 0xFFFFFF ); + break; + } +#endif /*RADIO_SIGFOX_ENABLE == 1*/ + default: + break; + } + + TimerSetValue( &TxTimeoutTimer, SubgRf.TxTimeout ); + TimerStart( &TxTimeoutTimer ); +} + +static void RadioSleep( void ) +{ + SleepParams_t params = { 0 }; + + params.Fields.WarmStart = 1; + SUBGRF_SetSleep( params ); + + RADIO_DELAY_MS( 2 ); +} + +static void RadioStandby( void ) +{ + SUBGRF_SetStandby( STDBY_RC ); +} + +static void RadioRx( uint32_t timeout ) +{ + if( 1UL == RFW_Is_Init( ) ) + { + RFW_ReceiveInit( ); + } + else + { + SUBGRF_SetDioIrqParams( IRQ_RX_DONE | IRQ_RX_TX_TIMEOUT | IRQ_CRC_ERROR | IRQ_HEADER_ERROR | IRQ_RX_DBG, + IRQ_RX_DONE | IRQ_RX_TX_TIMEOUT | IRQ_CRC_ERROR | IRQ_HEADER_ERROR | IRQ_RX_DBG, + IRQ_RADIO_NONE, + IRQ_RADIO_NONE ); + } + + if( timeout != 0 ) + { + TimerSetValue( &RxTimeoutTimer, timeout ); + TimerStart( &RxTimeoutTimer ); + } + /* ST_WORKAROUND_BEGIN : Set the debug pin and update the radio switch */ + /* switch off RxDcPreambleDetect See STM32WL Errata: RadioSetRxDutyCycle*/ + SubgRf.RxDcPreambleDetectTimeout = 0; + /* Set DBG pin */ + DBG_GPIO_RADIO_RX( SET ); + /* RF switch configuration */ + SUBGRF_SetSwitch( SubgRf.AntSwitchPaSelect, RFSWITCH_RX ); + /* ST_WORKAROUND_END */ + + if( SubgRf.RxContinuous == true ) + { + SUBGRF_SetRx( 0xFFFFFF ); // Rx Continuous + } + else + { + SUBGRF_SetRx( SubgRf.RxTimeout << 6 ); + } +} + +static void RadioRxBoosted( uint32_t timeout ) +{ + if( 1UL == RFW_Is_Init() ) + { + RFW_ReceiveInit(); + } + else + { + SUBGRF_SetDioIrqParams( IRQ_RX_DONE | IRQ_RX_TX_TIMEOUT | IRQ_CRC_ERROR | IRQ_HEADER_ERROR | IRQ_RX_DBG, + IRQ_RX_DONE | IRQ_RX_TX_TIMEOUT | IRQ_CRC_ERROR | IRQ_HEADER_ERROR | IRQ_RX_DBG, + IRQ_RADIO_NONE, + IRQ_RADIO_NONE ); + } + if( timeout != 0 ) + { + TimerSetValue( &RxTimeoutTimer, timeout ); + TimerStart( &RxTimeoutTimer ); + } + /* ST_WORKAROUND_BEGIN : Set the debug pin and update the radio switch */ + /* switch off RxDcPreambleDetect See STM32WL Errata: RadioSetRxDutyCycle*/ + SubgRf.RxDcPreambleDetectTimeout = 0; + /* Set DBG pin */ + DBG_GPIO_RADIO_RX( SET ); + /* RF switch configuration */ + SUBGRF_SetSwitch( SubgRf.AntSwitchPaSelect, RFSWITCH_RX ); + /* ST_WORKAROUND_END */ + + if( SubgRf.RxContinuous == true ) + { + SUBGRF_SetRxBoosted( 0xFFFFFF ); // Rx Continuous + } + else + { + SUBGRF_SetRxBoosted( SubgRf.RxTimeout << 6 ); + } +} + +static void RadioSetRxDutyCycle( uint32_t rxTime, uint32_t sleepTime ) +{ + /*See STM32WL Errata: RadioSetRxDutyCycle*/ + SubgRf.RxDcPreambleDetectTimeout = 2 * rxTime + sleepTime; + /*Enable also the IRQ_PREAMBLE_DETECTED*/ + SUBGRF_SetDioIrqParams( IRQ_RADIO_ALL, IRQ_RADIO_ALL, IRQ_RADIO_NONE, IRQ_RADIO_NONE ); + /* RF switch configuration */ + SUBGRF_SetSwitch( SubgRf.AntSwitchPaSelect, RFSWITCH_RX ); + /* Start Rx DutyCycle*/ + SUBGRF_SetRxDutyCycle( rxTime, sleepTime ); +} + +static void RadioStartCad( void ) +{ + /* RF switch configuration */ + SUBGRF_SetSwitch( SubgRf.AntSwitchPaSelect, RFSWITCH_RX ); + + SUBGRF_SetDioIrqParams( IRQ_CAD_CLEAR | IRQ_CAD_DETECTED, + IRQ_CAD_CLEAR | IRQ_CAD_DETECTED, + IRQ_RADIO_NONE, + IRQ_RADIO_NONE ); + SUBGRF_SetCad( ); +} + +static void RadioSetTxContinuousWave( uint32_t freq, int8_t power, uint16_t time ) +{ + uint32_t timeout = ( uint32_t )time * 1000; + uint8_t antswitchpow; + + SUBGRF_SetRfFrequency( freq ); + + antswitchpow = SUBGRF_SetRfTxPower( power ); + + /* Set RF switch */ + SUBGRF_SetSwitch( antswitchpow, RFSWITCH_TX ); + + SUBGRF_SetTxContinuousWave( ); + + TimerSetValue( &TxTimeoutTimer, timeout ); + TimerStart( &TxTimeoutTimer ); +} + +static int16_t RadioRssi( RadioModems_t modem ) +{ + return SUBGRF_GetRssiInst( ); +} + +static void RadioWrite( uint16_t addr, uint8_t data ) +{ + SUBGRF_WriteRegister( addr, data ); +} + +static uint8_t RadioRead( uint16_t addr ) +{ + return SUBGRF_ReadRegister( addr ); +} + +static void RadioWriteRegisters( uint16_t addr, uint8_t *buffer, uint8_t size ) +{ + SUBGRF_WriteRegisters( addr, buffer, size ); +} + +static void RadioReadRegisters( uint16_t addr, uint8_t *buffer, uint8_t size ) +{ + SUBGRF_ReadRegisters( addr, buffer, size ); +} + +static 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 ); + } + } +} + +static void RadioSetPublicNetwork( bool enable ) +{ + SubgRf.PublicNetwork.Current = SubgRf.PublicNetwork.Previous = enable; + + RadioSetModem( MODEM_LORA ); + if( enable == true ) + { + // Change LoRa modem SyncWord + SUBGRF_WriteRegister( REG_LR_SYNCWORD, ( LORA_MAC_PUBLIC_SYNCWORD >> 8 ) & 0xFF ); + SUBGRF_WriteRegister( REG_LR_SYNCWORD + 1, LORA_MAC_PUBLIC_SYNCWORD & 0xFF ); + } + else + { + // Change LoRa modem SyncWord + SUBGRF_WriteRegister( REG_LR_SYNCWORD, ( LORA_MAC_PRIVATE_SYNCWORD >> 8 ) & 0xFF ); + SUBGRF_WriteRegister( REG_LR_SYNCWORD + 1, LORA_MAC_PRIVATE_SYNCWORD & 0xFF ); + } +} + +static uint32_t RadioGetWakeupTime( void ) +{ + return SUBGRF_GetRadioWakeUpTime() + RADIO_WAKEUP_TIME; +} + +static void RadioOnTxTimeoutIrq( void *context ) +{ + RADIO_TX_TIMEOUT_PROCESS(); +} + +static void RadioOnRxTimeoutIrq( void *context ) +{ + RADIO_RX_TIMEOUT_PROCESS(); +} + +static void RadioOnTxTimeoutProcess( void ) +{ + /* ST_WORKAROUND_BEGIN: Reset DBG pin */ + DBG_GPIO_RADIO_TX( RST ); + /* ST_WORKAROUND_END */ + + if( ( RadioEvents != NULL ) && ( RadioEvents->TxTimeout != NULL ) ) + { + RadioEvents->TxTimeout( ); + } +} + +static void RadioOnRxTimeoutProcess( void ) +{ + /* ST_WORKAROUND_BEGIN: Reset DBG pin */ + DBG_GPIO_RADIO_RX( RST ); + /* ST_WORKAROUND_END */ + + if( ( RadioEvents != NULL ) && ( RadioEvents->RxTimeout != NULL ) ) + { + RadioEvents->RxTimeout( ); + } +} + +static void RadioOnDioIrq( RadioIrqMasks_t radioIrq ) +{ + SubgRf.RadioIrq = radioIrq; + + RADIO_IRQ_PROCESS(); +} + +static void RadioIrqProcess( void ) +{ + uint8_t size = 0; + int32_t cfo = 0; + + switch( SubgRf.RadioIrq ) + { + case IRQ_TX_DONE: + /* ST_WORKAROUND_BEGIN: Reset DBG pin */ + DBG_GPIO_RADIO_TX( RST ); + /* ST_WORKAROUND_END */ + + TimerStop( &TxTimeoutTimer ); + //!< Update operating mode state to a value lower than \ref MODE_STDBY_XOSC + SUBGRF_SetStandby( STDBY_RC ); + + if( RFW_Is_LongPacketModeEnabled() == 1 ) + { + RFW_DeInit_TxLongPacket( ); + } + + if( ( RadioEvents != NULL ) && ( RadioEvents->TxDone != NULL ) ) + { + RadioEvents->TxDone( ); + } + break; + + case IRQ_RX_DONE: + /* ST_WORKAROUND_BEGIN: Reset DBG pin */ + DBG_GPIO_RADIO_RX( RST ); + /* ST_WORKAROUND_END */ + + 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 STM32WL Erratasheet + SUBGRF_WriteRegister( SUBGHZ_RTCCTLR, 0x00 ); + SUBGRF_WriteRegister( SUBGHZ_EVENTMASKR, SUBGRF_ReadRegister( SUBGHZ_EVENTMASKR ) | ( 1 << 1 ) ); + // WORKAROUND END + } + SUBGRF_GetPayload( RadioBuffer, &size, 255 ); + SUBGRF_GetPacketStatus( &( SubgRf.PacketStatus ) ); + if( ( RadioEvents != NULL ) && ( RadioEvents->RxDone != NULL ) ) + { + switch( SubgRf.PacketStatus.packetType ) + { + case PACKET_TYPE_LORA: + RadioEvents->RxDone( RadioBuffer, size, SubgRf.PacketStatus.Params.LoRa.RssiPkt, SubgRf.PacketStatus.Params.LoRa.SnrPkt ); + break; + default: + SUBGRF_GetCFO( SubgRf.ModulationParams.Params.Gfsk.BitRate, &cfo ); + RadioEvents->RxDone( RadioBuffer, size, SubgRf.PacketStatus.Params.Gfsk.RssiAvg, (int8_t) DIVR(cfo, 1000) ); + break; + } + } + break; + + case IRQ_CAD_CLEAR: + //!< 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( false ); + } + break; + case IRQ_CAD_DETECTED: + //!< 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( true ); + } + break; + + case IRQ_RX_TX_TIMEOUT: + MW_LOG( TS_ON, VLEVEL_M, "IRQ_RX_TX_TIMEOUT\r\n" ); + if( SUBGRF_GetOperatingMode( ) == MODE_TX ) + { + /* ST_WORKAROUND_BEGIN: Reset DBG pin */ + DBG_GPIO_RADIO_TX( RST ); + /* ST_WORKAROUND_END */ + + 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 ) + { + /* ST_WORKAROUND_BEGIN: Reset DBG pin */ + DBG_GPIO_RADIO_RX( RST ); + /* ST_WORKAROUND_END */ + + 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( TS_ON, VLEVEL_M, "PRE OK\r\n" ); + /*See STM32WL Errata: RadioSetRxDutyCycle*/ + if( SubgRf.RxDcPreambleDetectTimeout != 0 ) + { + /* Update Radio RTC period */ + Radio.Write(SUBGHZ_RTCPRDR2, (SubgRf.RxDcPreambleDetectTimeout>>16)&0xFF); /*Update Radio RTC Period MSB*/ + Radio.Write(SUBGHZ_RTCPRDR1, (SubgRf.RxDcPreambleDetectTimeout>>8)&0xFF); /*Update Radio RTC Period MidByte*/ + Radio.Write(SUBGHZ_RTCPRDR0, (SubgRf.RxDcPreambleDetectTimeout)&0xFF); /*Update Radio RTC Period lsb*/ + Radio.Write(SUBGHZ_RTCCTLR, Radio.Read(SUBGHZ_RTCCTLR)|0x1); /*restart Radio RTC*/ + SubgRf.RxDcPreambleDetectTimeout = 0; + /*Clear IRQ_PREAMBLE_DETECTED mask*/ + SUBGRF_SetDioIrqParams( IRQ_RX_DONE | IRQ_RX_TX_TIMEOUT | IRQ_CRC_ERROR | IRQ_HEADER_ERROR | IRQ_RX_DBG, + IRQ_RX_DONE | IRQ_RX_TX_TIMEOUT | IRQ_CRC_ERROR | IRQ_HEADER_ERROR | IRQ_RX_DBG, + IRQ_RADIO_NONE, + IRQ_RADIO_NONE ); + + } + break; + + case IRQ_SYNCWORD_VALID: + MW_LOG( TS_ON, VLEVEL_M, "SYNC OK\r\n" ); + if( 1UL == RFW_Is_Init( ) ) + { + RFW_ReceivePayload( ); + } + break; + + case IRQ_HEADER_VALID: + MW_LOG( TS_ON, VLEVEL_M, "HDR OK\r\n" ); + 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( TS_ON, VLEVEL_M, "HDR KO\r\n" ); + } + break; + + case IRQ_CRC_ERROR: + MW_LOG( TS_ON, VLEVEL_M, "IRQ_CRC_ERROR\r\n" ); + + 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; + default: + break; + } +} + +static void RadioTxPrbs( void ) +{ + SUBGRF_SetSwitch( SubgRf.AntSwitchPaSelect, RFSWITCH_TX ); + Radio.Write( SUBGHZ_GPKTCTL1AR, 0x2d ); // sel mode prbs9 instead of preamble + SUBGRF_SetTxInfinitePreamble( ); + SUBGRF_SetTx( 0x0fffff ); +} + +static void RadioTxCw( int8_t power ) +{ + uint8_t paselect = SUBGRF_SetRfTxPower( power ); + SUBGRF_SetSwitch( paselect, RFSWITCH_TX ); + SUBGRF_SetTxContinuousWave( ); +} + +#if (RADIO_SIGFOX_ENABLE == 1) +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; + int32_t i = 0; + + for( i = 0; i < size; i++ ) + { + /* reverse all inputs */ + inBuffer[i] = ~inBuffer[i]; + /* init outBuffer */ + outBuffer[i] = 0; + } + + for( i = 0; i < ( size * 8 ); i++ ) + { + /* index to take bit in inBuffer */ + index_bit = 7 - ( i % 8 ); + index_byte = i / 8; + /* index to place bit in outBuffer is shifted 1 bit right */ + index_bit_out = 7 - ( ( i + 1 ) % 8 ); + index_byte_out = ( i + 1 ) / 8; + /* extract current bit from input */ + currBit = ( inBuffer[index_byte] >> 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 ) ; +} +#endif /*RADIO_SIGFOX_ENABLE == 1*/ + +static int32_t RadioSetRxGenericConfig( GenericModems_t modem, RxConfigGeneric_t* config, uint32_t rxContinuous, uint32_t symbTimeout ) +{ +#if (RADIO_GENERIC_CONFIG_ENABLE == 1) + int32_t status = 0; + uint8_t syncword[8] = {0}; + uint8_t MaxPayloadLength; + + RFW_DeInit( ); /* switch Off FwPacketDecoding by default */ + + if( rxContinuous != 0 ) + { + symbTimeout = 0; + } + SubgRf.RxContinuous = ( rxContinuous == 0 ) ? false : true; + + switch( modem ) + { + case GENERIC_FSK: + if( ( config->fsk.BitRate == 0 ) || ( config->fsk.PreambleLen == 0 ) ) + { + return -1; + } + if( config->fsk.SyncWordLength > 8 ) + { + return -1; + } + else + { + RADIO_MEMCPY8( syncword, config->fsk.SyncWord, config->fsk.SyncWordLength ); + } + + SUBGRF_SetStopRxTimerOnPreambleDetect( ( config->fsk.StopTimerOnPreambleDetect == 0 ) ? false : true ); + + SubgRf.ModulationParams.PacketType = PACKET_TYPE_GFSK; + SubgRf.ModulationParams.Params.Gfsk.BitRate = config->fsk.BitRate; + SubgRf.ModulationParams.Params.Gfsk.ModulationShaping = (RadioModShapings_t) config->fsk.ModulationShaping; + SubgRf.ModulationParams.Params.Gfsk.Bandwidth = SUBGRF_GetFskBandwidthRegValue( config->fsk.Bandwidth ); + + SubgRf.PacketParams.PacketType = PACKET_TYPE_GFSK; + SubgRf.PacketParams.Params.Gfsk.PreambleLength = ( config->fsk.PreambleLen ) << 3 ; // convert byte into bit + SubgRf.PacketParams.Params.Gfsk.PreambleMinDetect = ( RadioPreambleDetection_t ) config->fsk.PreambleMinDetect; + SubgRf.PacketParams.Params.Gfsk.SyncWordLength = ( config->fsk.SyncWordLength ) << 3; // convert byte into bit + SubgRf.PacketParams.Params.Gfsk.AddrComp = ( RadioAddressComp_t ) config->fsk.AddrComp; + + if( config->fsk.LengthMode == RADIO_FSK_PACKET_FIXED_LENGTH ) + { + SubgRf.PacketParams.Params.Gfsk.PayloadLength = config->fsk.MaxPayloadLength; + } + else if( config->fsk.LengthMode == RADIO_FSK_PACKET_2BYTES_LENGTH ) + { + /* Set max in the radio, in long packet mode will be tuned based dynamically on received chunk */ + SubgRf.PacketParams.Params.Gfsk.PayloadLength = 0xFF; + } + else + { + /* Set max in the radio */ + SubgRf.PacketParams.Params.Gfsk.PayloadLength = 0xFF; + } + + if( ( config->fsk.Whitening == RADIO_FSK_DC_IBM_WHITENING ) || ( config->fsk.LengthMode == RADIO_FSK_PACKET_2BYTES_LENGTH ) ) + { + /* Supports only RADIO_FSK_CRC_2_BYTES_IBM or RADIO_FSK_CRC_2_BYTES_CCIT*/ + if( ( config->fsk.CrcLength != RADIO_FSK_CRC_2_BYTES_IBM ) && ( config->fsk.CrcLength != RADIO_FSK_CRC_2_BYTES_CCIT ) && ( config->fsk.CrcLength != RADIO_FSK_CRC_OFF ) ) + { + return -1; + } + ConfigGeneric_t ConfigGeneric; + ConfigGeneric.rtx = CONFIG_RX; + ConfigGeneric.RxConfig = config; + if( 0UL != RFW_Init( &ConfigGeneric, RadioEvents, &RxTimeoutTimer ) ) + { + return -1; + } + /* Whitening off, will be processed by FW, switch off built-in radio whitening*/ + SubgRf.PacketParams.Params.Gfsk.DcFree = ( RadioDcFree_t ) RADIO_FSK_DC_FREE_OFF; + /* Crc off, Crc processed by FW, switch off built-in radio Crc*/ + SubgRf.PacketParams.Params.Gfsk.CrcLength = ( RadioCrcTypes_t ) RADIO_CRC_OFF; + /* Length contained in Tx, but will be processed by FW after de-whitening*/ + SubgRf.PacketParams.Params.Gfsk.HeaderType = ( RadioPacketLengthModes_t ) RADIO_PACKET_FIXED_LENGTH; + } + else + { + SubgRf.PacketParams.Params.Gfsk.CrcLength = ( RadioCrcTypes_t ) config->fsk.CrcLength; + SubgRf.PacketParams.Params.Gfsk.DcFree = ( RadioDcFree_t ) config->fsk.Whitening; + SubgRf.PacketParams.Params.Gfsk.HeaderType = ( RadioPacketLengthModes_t ) config->fsk.LengthMode; + } + + RadioStandby( ); + RadioSetModem( MODEM_FSK ); + SUBGRF_SetModulationParams( &SubgRf.ModulationParams ); + SUBGRF_SetPacketParams( &SubgRf.PacketParams ); + SUBGRF_SetSyncWord( syncword ); + SUBGRF_SetWhiteningSeed( config->fsk.whiteSeed ); + SUBGRF_SetCrcPolynomial( config->fsk.CrcPolynomial ); + /* timeout unused when SubgRf.RxContinuous */ + SubgRf.RxTimeout = ( uint32_t )( ( symbTimeout * 1000 * 8 ) / config->fsk.BitRate ); + break; + case GENERIC_LORA: + if( config->lora.PreambleLen == 0 ) + { + return -1; + } + + if( config->lora.LengthMode == RADIO_LORA_PACKET_FIXED_LENGTH ) + { + MaxPayloadLength = config->fsk.MaxPayloadLength; + } + else + { + MaxPayloadLength = 0xFF; + } + SUBGRF_SetStopRxTimerOnPreambleDetect( ( config->lora.StopTimerOnPreambleDetect == 0 ) ? false : true ); + SUBGRF_SetLoRaSymbNumTimeout( symbTimeout ); + + SubgRf.ModulationParams.PacketType = PACKET_TYPE_LORA; + SubgRf.ModulationParams.Params.LoRa.SpreadingFactor = ( RadioLoRaSpreadingFactors_t ) config->lora.SpreadingFactor; + SubgRf.ModulationParams.Params.LoRa.Bandwidth = ( RadioLoRaBandwidths_t ) config->lora.Bandwidth; + SubgRf.ModulationParams.Params.LoRa.CodingRate = ( RadioLoRaCodingRates_t ) config->lora.Coderate; + switch( config->lora.LowDatarateOptimize ) + { + case RADIO_LORA_LOWDR_OPT_OFF: + SubgRf.ModulationParams.Params.LoRa.LowDatarateOptimize = 0; + break; + case RADIO_LORA_LOWDR_OPT_ON: + SubgRf.ModulationParams.Params.LoRa.LowDatarateOptimize = 1; + break; + case RADIO_LORA_LOWDR_OPT_AUTO: + if( ( config->lora.SpreadingFactor == RADIO_LORA_SF11 ) || ( config->lora.SpreadingFactor == RADIO_LORA_SF12 ) ) + { + SubgRf.ModulationParams.Params.LoRa.LowDatarateOptimize = 1; + } + else + { + SubgRf.ModulationParams.Params.LoRa.LowDatarateOptimize = 0; + } + break; + default: + break; + } + + SubgRf.PacketParams.PacketType = PACKET_TYPE_LORA; + SubgRf.PacketParams.Params.LoRa.PreambleLength = config->lora.PreambleLen; + SubgRf.PacketParams.Params.LoRa.HeaderType = ( RadioLoRaPacketLengthsMode_t ) config->lora.LengthMode; + SubgRf.PacketParams.Params.LoRa.PayloadLength = MaxPayloadLength; + SubgRf.PacketParams.Params.LoRa.CrcMode = ( RadioLoRaCrcModes_t ) config->lora.CrcMode; + SubgRf.PacketParams.Params.LoRa.InvertIQ = ( RadioLoRaIQModes_t ) config->lora.IqInverted; + + RadioStandby( ); + RadioSetModem( MODEM_LORA ); + SUBGRF_SetModulationParams( &SubgRf.ModulationParams ); + SUBGRF_SetPacketParams( &SubgRf.PacketParams ); + + /* WORKAROUND - Optimizing the Inverted IQ Operation, see STM32WL Erratasheet*/ + if( SubgRf.PacketParams.Params.LoRa.InvertIQ == LORA_IQ_INVERTED ) + { + SUBGRF_WriteRegister( SUBGHZ_LIQPOLR, SUBGRF_ReadRegister( SUBGHZ_LIQPOLR ) & ~( 1 << 2 ) ); + } + else + { + SUBGRF_WriteRegister( SUBGHZ_LIQPOLR, SUBGRF_ReadRegister( SUBGHZ_LIQPOLR ) | ( 1 << 2 ) ); + } + // WORKAROUND END + + // Timeout Max, Timeout handled directly in SetRx function + SubgRf.RxTimeout = 0xFFFF; + break; + default: + break; + } + return status; +#else /* RADIO_GENERIC_CONFIG_ENABLE == 1*/ + return -1; +#endif /* RADIO_GENERIC_CONFIG_ENABLE == 0*/ +} + +static int32_t RadioSetTxGenericConfig( GenericModems_t modem, TxConfigGeneric_t* config, int8_t power, uint32_t timeout ) +{ +#if (RADIO_GENERIC_CONFIG_ENABLE == 1) + uint8_t syncword[8] = {0}; + RadioModems_t radio_modem; + RFW_DeInit( ); /* switch Off FwPacketDecoding by default */ + switch( modem ) + { + case GENERIC_MSK: + if( config->msk.SyncWordLength > 8 ) + { + return -1; + } + else + { + RADIO_MEMCPY8(syncword, config->msk.SyncWord, config->msk.SyncWordLength); + } + if( ( config->msk.BitRate == 0 ) ) + { + return -1; + } + else if (config->msk.BitRate<= 10000) + { + /*max msk modulator datarate is 10kbps*/ + radio_modem= MODEM_MSK; + SubgRf.PacketParams.PacketType = PACKET_TYPE_GMSK; + SubgRf.ModulationParams.PacketType = PACKET_TYPE_GMSK; + SubgRf.ModulationParams.Params.Gfsk.BitRate = config->msk.BitRate; + SubgRf.ModulationParams.Params.Gfsk.ModulationShaping = ( RadioModShapings_t ) config->msk.ModulationShaping; + } + else + { + radio_modem= MODEM_FSK; + SubgRf.PacketParams.PacketType = PACKET_TYPE_GFSK; + SubgRf.ModulationParams.PacketType = PACKET_TYPE_GFSK; + SubgRf.ModulationParams.Params.Gfsk.BitRate = config->msk.BitRate; + SubgRf.ModulationParams.Params.Gfsk.ModulationShaping = ( RadioModShapings_t ) config->msk.ModulationShaping; + /*do msk with gfsk modulator*/ + SubgRf.ModulationParams.Params.Gfsk.Fdev = config->msk.BitRate/4; + } + + SubgRf.PacketParams.Params.Gfsk.PreambleLength = ( config->msk.PreambleLen ) << 3; // convert byte into bit + SubgRf.PacketParams.Params.Gfsk.PreambleMinDetect = RADIO_PREAMBLE_DETECTOR_08_BITS; // don't care in tx + SubgRf.PacketParams.Params.Gfsk.SyncWordLength = ( config->msk.SyncWordLength ) << 3; // convert byte into bit + SubgRf.PacketParams.Params.Gfsk.AddrComp = RADIO_ADDRESSCOMP_FILT_OFF; // don't care in tx + + if( ( config->msk.Whitening == RADIO_FSK_DC_IBM_WHITENING ) || ( config->msk.HeaderType == RADIO_FSK_PACKET_2BYTES_LENGTH ) ) + { + /* Supports only RADIO_FSK_CRC_2_BYTES_IBM or RADIO_FSK_CRC_2_BYTES_CCIT */ + if( ( config->msk.CrcLength != RADIO_FSK_CRC_2_BYTES_IBM ) && ( config->msk.CrcLength != RADIO_FSK_CRC_2_BYTES_CCIT ) &&( config->msk.CrcLength != RADIO_FSK_CRC_OFF ) ) + { + return -1; + } + ConfigGeneric_t ConfigGeneric; + /*msk and fsk are union, no need for copy as fsk/msk struct are on same address*/ + ConfigGeneric.TxConfig= config; + ConfigGeneric.rtx = CONFIG_TX; + if( 0UL != RFW_Init( &ConfigGeneric, RadioEvents, &TxTimeoutTimer ) ) + { + return -1; + } + /* whitening off, will be processed by FW, switch off built-in radio whitening */ + SubgRf.PacketParams.Params.Gfsk.DcFree = ( RadioDcFree_t ) RADIO_FSK_DC_FREE_OFF; + /* Crc processed by FW, switch off built-in radio Crc */ + SubgRf.PacketParams.Params.Gfsk.CrcLength = (RadioCrcTypes_t) RADIO_CRC_OFF; + /* length contained in Tx, but will be processed by FW after de-whitening */ + SubgRf.PacketParams.Params.Gfsk.HeaderType = ( RadioPacketLengthModes_t ) RADIO_PACKET_FIXED_LENGTH; + } + else + { + SubgRf.PacketParams.Params.Gfsk.CrcLength = ( RadioCrcTypes_t ) config->msk.CrcLength; + SubgRf.PacketParams.Params.Gfsk.DcFree = ( RadioDcFree_t ) config->msk.Whitening; + SubgRf.PacketParams.Params.Gfsk.HeaderType = ( RadioPacketLengthModes_t ) config->msk.HeaderType; + } + + RadioStandby( ); + RadioSetModem( radio_modem ); + + SUBGRF_SetModulationParams( &SubgRf.ModulationParams ); + SUBGRF_SetPacketParams( &SubgRf.PacketParams ); + SUBGRF_SetSyncWord( syncword ); + SUBGRF_SetWhiteningSeed( config->msk.whiteSeed ); + SUBGRF_SetCrcPolynomial(config->msk.CrcPolynomial ); + break; + case GENERIC_FSK: + if( config->fsk.BitRate == 0 ) + { + return -1; + } + if( config->fsk.SyncWordLength > 8 ) + { + return -1; + } + else + { + RADIO_MEMCPY8(syncword, config->fsk.SyncWord, config->fsk.SyncWordLength); + } + SubgRf.ModulationParams.PacketType = PACKET_TYPE_GFSK; + SubgRf.ModulationParams.Params.Gfsk.BitRate = config->fsk.BitRate; + SubgRf.ModulationParams.Params.Gfsk.ModulationShaping = ( RadioModShapings_t ) config->fsk.ModulationShaping; + SubgRf.ModulationParams.Params.Gfsk.Fdev = config->fsk.FrequencyDeviation; + + SubgRf.PacketParams.PacketType = PACKET_TYPE_GFSK; + SubgRf.PacketParams.Params.Gfsk.PreambleLength = ( config->fsk.PreambleLen ) << 3; // convert byte into bit + SubgRf.PacketParams.Params.Gfsk.PreambleMinDetect = RADIO_PREAMBLE_DETECTOR_08_BITS; // don't care in tx + SubgRf.PacketParams.Params.Gfsk.SyncWordLength = ( config->fsk.SyncWordLength ) << 3; // convert byte into bit + SubgRf.PacketParams.Params.Gfsk.AddrComp = RADIO_ADDRESSCOMP_FILT_OFF; // don't care in tx + + if( ( config->fsk.Whitening == RADIO_FSK_DC_IBM_WHITENING ) || ( config->fsk.HeaderType == RADIO_FSK_PACKET_2BYTES_LENGTH ) ) + { + /* Supports only RADIO_FSK_CRC_2_BYTES_IBM or RADIO_FSK_CRC_2_BYTES_CCIT */ + if( ( config->fsk.CrcLength != RADIO_FSK_CRC_2_BYTES_IBM ) && ( config->fsk.CrcLength != RADIO_FSK_CRC_2_BYTES_CCIT ) &&( config->fsk.CrcLength != RADIO_FSK_CRC_OFF ) ) + { + return -1; + } + ConfigGeneric_t ConfigGeneric; + ConfigGeneric.rtx = CONFIG_TX; + ConfigGeneric.TxConfig = config; + if( 0UL != RFW_Init( &ConfigGeneric, RadioEvents, &TxTimeoutTimer ) ) + { + return -1; + } + /* whitening off, will be processed by FW, switch off built-in radio whitening */ + SubgRf.PacketParams.Params.Gfsk.DcFree = ( RadioDcFree_t ) RADIO_FSK_DC_FREE_OFF; + /* Crc processed by FW, switch off built-in radio Crc */ + SubgRf.PacketParams.Params.Gfsk.CrcLength = (RadioCrcTypes_t) RADIO_CRC_OFF; + /* length contained in Tx, but will be processed by FW after de-whitening */ + SubgRf.PacketParams.Params.Gfsk.HeaderType = ( RadioPacketLengthModes_t ) RADIO_PACKET_FIXED_LENGTH; + } + else + { + SubgRf.PacketParams.Params.Gfsk.CrcLength = ( RadioCrcTypes_t ) config->fsk.CrcLength; + SubgRf.PacketParams.Params.Gfsk.DcFree = ( RadioDcFree_t ) config->fsk.Whitening; + SubgRf.PacketParams.Params.Gfsk.HeaderType = ( RadioPacketLengthModes_t ) config->fsk.HeaderType; + } + + RadioStandby( ); + RadioSetModem( MODEM_FSK ); + SUBGRF_SetModulationParams( &SubgRf.ModulationParams ); + SUBGRF_SetPacketParams( &SubgRf.PacketParams ); + SUBGRF_SetSyncWord( syncword ); + SUBGRF_SetWhiteningSeed( config->fsk.whiteSeed ); + SUBGRF_SetCrcPolynomial(config->fsk.CrcPolynomial ); + break; + case GENERIC_LORA: + SubgRf.ModulationParams.PacketType = PACKET_TYPE_LORA; + SubgRf.ModulationParams.Params.LoRa.SpreadingFactor = ( RadioLoRaSpreadingFactors_t ) config->lora.SpreadingFactor; + SubgRf.ModulationParams.Params.LoRa.Bandwidth = ( RadioLoRaBandwidths_t ) config->lora.Bandwidth; + SubgRf.ModulationParams.Params.LoRa.CodingRate = ( RadioLoRaCodingRates_t ) config->lora.Coderate; + switch( config->lora.LowDatarateOptimize ) + { + case RADIO_LORA_LOWDR_OPT_OFF: + SubgRf.ModulationParams.Params.LoRa.LowDatarateOptimize = 0; + break; + case RADIO_LORA_LOWDR_OPT_ON: + SubgRf.ModulationParams.Params.LoRa.LowDatarateOptimize = 1; + break; + case RADIO_LORA_LOWDR_OPT_AUTO: + if( ( config->lora.SpreadingFactor == RADIO_LORA_SF11 ) || ( config->lora.SpreadingFactor == RADIO_LORA_SF12 ) ) + { + SubgRf.ModulationParams.Params.LoRa.LowDatarateOptimize = 1; + } + else + { + SubgRf.ModulationParams.Params.LoRa.LowDatarateOptimize = 0; + } + break; + default: + break; + } + + SubgRf.PacketParams.PacketType = PACKET_TYPE_LORA; + SubgRf.PacketParams.Params.LoRa.PreambleLength = config->lora.PreambleLen; + SubgRf.PacketParams.Params.LoRa.HeaderType = ( RadioLoRaPacketLengthsMode_t ) config->lora.LengthMode; + SubgRf.PacketParams.Params.LoRa.CrcMode = ( RadioLoRaCrcModes_t ) config->lora.CrcMode; + SubgRf.PacketParams.Params.LoRa.InvertIQ = ( RadioLoRaIQModes_t ) config->lora.IqInverted; + + RadioStandby( ); + RadioSetModem( MODEM_LORA ); + SUBGRF_SetModulationParams( &SubgRf.ModulationParams ); + SUBGRF_SetPacketParams( &SubgRf.PacketParams ); + + // WORKAROUND - Modulation Quality with 500 kHz LoRa Bandwidth, see STM32WL Erratasheet + if( SubgRf.ModulationParams.Params.LoRa.Bandwidth == LORA_BW_500 ) + { + // RegTxModulation = @address 0x0889 + SUBGRF_WriteRegister( SUBGHZ_SDCFG0R, SUBGRF_ReadRegister( SUBGHZ_SDCFG0R ) & ~( 1 << 2 ) ); + } + else + { + // RegTxModulation = @address 0x0889 + SUBGRF_WriteRegister( SUBGHZ_SDCFG0R, SUBGRF_ReadRegister( SUBGHZ_SDCFG0R ) | ( 1 << 2 ) ); + } + // WORKAROUND END + break; + case GENERIC_BPSK: + if( ( config->bpsk.BitRate == 0 ) || ( config->bpsk.BitRate > 1000 ) ) + { + return -1; + } + RadioSetModem( MODEM_BPSK ); + SubgRf.ModulationParams.PacketType = PACKET_TYPE_BPSK; + SubgRf.ModulationParams.Params.Bpsk.BitRate = config->bpsk.BitRate; + SubgRf.ModulationParams.Params.Bpsk.ModulationShaping = MOD_SHAPING_DBPSK; + SUBGRF_SetModulationParams( &SubgRf.ModulationParams ); + break; + default: + break; + } + + SubgRf.AntSwitchPaSelect = SUBGRF_SetRfTxPower( power ); + RFW_SetAntSwitch( SubgRf.AntSwitchPaSelect ); + SubgRf.TxTimeout = timeout; + return 0; +#else /* RADIO_GENERIC_CONFIG_ENABLE == 1*/ + return -1; +#endif /* RADIO_GENERIC_CONFIG_ENABLE == 0*/ +} diff --git a/src/STM32CubeWL/SubGHz_Phy/stm32_radio_driver/radio_driver.c b/src/STM32CubeWL/SubGHz_Phy/stm32_radio_driver/radio_driver.c new file mode 100644 index 0000000..272c708 --- /dev/null +++ b/src/STM32CubeWL/SubGHz_Phy/stm32_radio_driver/radio_driver.c @@ -0,0 +1,1225 @@ +/*! + * \file radio_driver.c + * + * \brief radio driver implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + */ +/** + ****************************************************************************** + * + * Portions COPYRIGHT 2020 STMicroelectronics + * + * @file radio_driver.c + * @author MCD Application Team + * @brief radio driver implementation + ****************************************************************************** + */ +/* Includes ------------------------------------------------------------------*/ +#include "../../../BSP/radio_conf.h" +#include "radio_driver.h" +#include "../../../BSP/mw_log_conf.h" + +/* External variables ---------------------------------------------------------*/ +/*! + * \brief Sughz handler + */ +extern SUBGHZ_HandleTypeDef hsubghz; + +/* Private typedef -----------------------------------------------------------*/ +/*! + * FSK bandwidth definition + */ +typedef struct FskBandwidth_s +{ + uint32_t bandwidth; + uint8_t RegValue; +} FskBandwidth_t; +/* Private define ------------------------------------------------------------*/ +/** + * @brief drive value used anytime radio is NOT in TX low power mode + * @note SMPS_DRIVE_SETTING_DEFAULT can be redefined in radio_conf.h + */ +#ifndef SMPS_DRIVE_SETTING_DEFAULT +#define SMPS_DRIVE_SETTING_DEFAULT SMPS_DRV_40 +#endif /* SMPS_DRIVE_SETTING_DEFAULT */ + +/** + * @brief drive value used anytime radio is in TX low power mode + * TX low power mode is the worst case because the PA sinks from SMPS + * while in high power mode, current is sunk directly from the battery + * @note SMPS_DRIVE_SETTING_MAX can be redefined in radio_conf.h + */ +#ifndef SMPS_DRIVE_SETTING_MAX +#define SMPS_DRIVE_SETTING_MAX SMPS_DRV_60 +#endif /* SMPS_DRIVE_SETTING_MAX */ + +/** + * @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 + * @note XTAL_FREQ can be redefined in radio_conf.h + */ +#ifndef XTAL_FREQ +#define XTAL_FREQ ( 32000000UL ) +#endif /* XTAL_FREQ */ + +/** + * @brief in XO mode, set internal capacitor (from 0x00 to 0x2F starting 11.2pF with 0.47pF steps) + * @note XTAL_DEFAULT_CAP_VALUE can be redefined in radio_conf.h + */ +#ifndef XTAL_DEFAULT_CAP_VALUE +#define XTAL_DEFAULT_CAP_VALUE ( 0x20UL ) +#endif /* XTAL_DEFAULT_CAP_VALUE */ + +/** + * @brief voltage of vdd tcxo. + * @note TCXO_CTRL_VOLTAGE can be redefined in radio_conf.h + */ +#ifndef TCXO_CTRL_VOLTAGE +#define TCXO_CTRL_VOLTAGE TCXO_CTRL_1_7V +#endif /* TCXO_CTRL_VOLTAGE */ + +/** + * @brief Radio maximum wakeup time (in ms) + * @note RF_WAKEUP_TIME can be redefined in radio_conf.h + */ +#ifndef RF_WAKEUP_TIME +#define RF_WAKEUP_TIME ( 10UL ) +#endif /* RF_WAKEUP_TIME */ + +/** + * @brief DCDC is present and enabled + * @remark this define is only used if the DCDC is present on the board + * @note DCDC_ENABLE can be redefined in radio_conf.h + */ +#ifndef DCDC_ENABLE +#define DCDC_ENABLE ( 1UL ) +#endif /* DCDC_ENABLE */ + +/* Private macro -------------------------------------------------------------*/ + +#define SX_FREQ_TO_CHANNEL( channel, freq ) \ +do \ +{ \ + channel = (uint32_t) ((((uint64_t) freq)<<25)/(XTAL_FREQ) ); \ +}while( 0 ) + +/* Private variables ---------------------------------------------------------*/ +/*! + * \brief Holds the internal operating mode of the radio + */ +static RadioOperatingModes_t OperatingMode; + +/*! + * \brief Stores the current packet type set in the radio + */ +static RadioPacketTypes_t PacketType; + +/*! + * \brief Stores the current packet header type set in the radio + */ +static volatile RadioLoRaPacketLengthsMode_t LoRaHeaderType; + +/*! + * \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; + +/*! + * Precomputed FSK bandwidth registers values + */ +static 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 +}; + +/* Private function prototypes -----------------------------------------------*/ + +/*! + * \brief This set SMPS drive capability wrt. RF mode + * + * \param [in] level SMPS maximum drive capability level + */ +static void Radio_SMPS_Set( uint8_t level ); + +/*! + * \brief IRQ Callback radio function + */ +static DioIrqHandler RadioOnDioIrqCb; + +/*! + * \brief Write command to the radio + * + * \param [in] Command The Write Command + * \param [out] pBuffer A pointer command buffer + * \param [in] Size Size in byte of the command buffer + */ +static void SUBGRF_WriteCommand( SUBGHZ_RadioSetCmd_t Command, uint8_t *pBuffer, + uint16_t Size ); +/*! + * \brief Read command to the radio + * + * \param [in] Command The Read Command + * \param [out] pBuffer A pointer command buffer + * \param [in] Size Size in byte of the command buffer + */ +static void SUBGRF_ReadCommand( SUBGHZ_RadioGetCmd_t Command, uint8_t *pBuffer, + uint16_t Size ); + +/* Exported functions ---------------------------------------------------------*/ +void SUBGRF_Init( DioIrqHandler dioIrq ) +{ + if ( dioIrq != NULL) + { + RadioOnDioIrqCb = dioIrq; + } + + RADIO_INIT(); + + /* set default SMPS current drive to default*/ + Radio_SMPS_Set(SMPS_DRIVE_SETTING_DEFAULT); + + ImageCalibrated = false; + + SUBGRF_SetStandby( STDBY_RC ); + + // Initialize TCXO control + if (1U == RBI_IsTCXO() ) + { + SUBGRF_SetTcxoMode( TCXO_CTRL_VOLTAGE, RF_WAKEUP_TIME << 6 );// 100 ms + SUBGRF_WriteRegister( REG_XTA_TRIM, 0x00 ); + + /*enable calibration for cut1.1 and later*/ + CalibrationParams_t calibParam; + calibParam.Value = 0x7F; + SUBGRF_Calibrate( calibParam ); + } + else + { + SUBGRF_WriteRegister( REG_XTA_TRIM, XTAL_DEFAULT_CAP_VALUE ); + SUBGRF_WriteRegister( REG_XTB_TRIM, XTAL_DEFAULT_CAP_VALUE ); + } + /* Init RF Switch */ + RBI_Init(); + + OperatingMode = MODE_STDBY_RC; +} + +RadioOperatingModes_t SUBGRF_GetOperatingMode( void ) +{ + return OperatingMode; +} + +void SUBGRF_SetPayload( uint8_t *payload, uint8_t size ) +{ + SUBGRF_WriteBuffer( 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; + } + SUBGRF_ReadBuffer( offset, buffer, *size ); + + return 0; +} + +void SUBGRF_SendPayload( uint8_t *payload, uint8_t size, uint32_t timeout) +{ + SUBGRF_SetPayload( payload, size ); + SUBGRF_SetTx( timeout ); +} + +uint8_t SUBGRF_SetSyncWord( uint8_t *syncWord ) +{ + SUBGRF_WriteRegisters( 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: + SUBGRF_WriteRegisters( 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: + SUBGRF_WriteRegisters( 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: + regValue = SUBGRF_ReadRegister( REG_LR_WHITSEEDBASEADDR_MSB ) & 0xFE; + regValue = ( ( seed >> 8 ) & 0x01 ) | regValue; + SUBGRF_WriteRegister( REG_LR_WHITSEEDBASEADDR_MSB, regValue ); // only 1 bit. + SUBGRF_WriteRegister( REG_LR_WHITSEEDBASEADDR_LSB, (uint8_t)seed ); + break; + + default: + break; + } +} + +uint32_t SUBGRF_GetRandom( void ) +{ + uint32_t number = 0; + uint8_t regAnaLna = 0; + uint8_t regAnaMixer = 0; + + regAnaLna = SUBGRF_ReadRegister( REG_ANA_LNA ); + SUBGRF_WriteRegister( REG_ANA_LNA, regAnaLna & ~( 1 << 0 ) ); + + regAnaMixer = SUBGRF_ReadRegister( REG_ANA_MIXER ); + SUBGRF_WriteRegister( REG_ANA_MIXER, regAnaMixer & ~( 1 << 7 ) ); + + // Set radio in continuous reception + SUBGRF_SetRx( 0xFFFFFF ); // Rx Continuous + + SUBGRF_ReadRegisters( RANDOM_NUMBER_GENERATORBASEADDR, ( uint8_t* )&number, 4 ); + + SUBGRF_SetStandby( STDBY_RC ); + + SUBGRF_WriteRegister( REG_ANA_LNA, regAnaLna ); + SUBGRF_WriteRegister( REG_ANA_MIXER, regAnaMixer ); + + return number; +} + +void SUBGRF_SetSleep( SleepParams_t sleepConfig ) +{ + /* switch the antenna OFF by SW */ + RBI_ConfigRFSwitch(RBI_SWITCH_OFF); + + Radio_SMPS_Set(SMPS_DRIVE_SETTING_DEFAULT); + + uint8_t value = ( ( ( uint8_t )sleepConfig.Fields.WarmStart << 2 ) | + ( ( uint8_t )sleepConfig.Fields.Reset << 1 ) | + ( ( uint8_t )sleepConfig.Fields.WakeUpRTC ) ); + SUBGRF_WriteCommand( RADIO_SET_SLEEP, &value, 1 ); + OperatingMode = MODE_SLEEP; +} + +void SUBGRF_SetStandby( RadioStandbyModes_t standbyConfig ) +{ + SUBGRF_WriteCommand( RADIO_SET_STANDBY, ( uint8_t* )&standbyConfig, 1 ); + if( standbyConfig == STDBY_RC ) + { + OperatingMode = MODE_STDBY_RC; + } + else + { + OperatingMode = MODE_STDBY_XOSC; + } +} + +void SUBGRF_SetFs( void ) +{ + SUBGRF_WriteCommand( 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 ); + SUBGRF_WriteCommand( 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 ); + SUBGRF_WriteCommand( RADIO_SET_RX, buf, 3 ); +} + +void SUBGRF_SetRxBoosted( uint32_t timeout ) +{ + uint8_t buf[3]; + + OperatingMode = MODE_RX; + + /* ST_WORKAROUND_BEGIN: Sigfox patch > 0x96 replaced by 0x97 */ + SUBGRF_WriteRegister( REG_RX_GAIN, 0x97 ); // max LNA gain, increase current by ~2mA for around ~3dB in sensitivity + /* ST_WORKAROUND_END */ + + buf[0] = ( uint8_t )( ( timeout >> 16 ) & 0xFF ); + buf[1] = ( uint8_t )( ( timeout >> 8 ) & 0xFF ); + buf[2] = ( uint8_t )( timeout & 0xFF ); + SUBGRF_WriteCommand( 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 ); + SUBGRF_WriteCommand( RADIO_SET_RXDUTYCYCLE, buf, 6 ); + OperatingMode = MODE_RX_DC; +} + +void SUBGRF_SetCad( void ) +{ + SUBGRF_WriteCommand( RADIO_SET_CAD, 0, 0 ); + OperatingMode = MODE_CAD; +} + +void SUBGRF_SetTxContinuousWave( void ) +{ + SUBGRF_WriteCommand( RADIO_SET_TXCONTINUOUSWAVE, 0, 0 ); +} + +void SUBGRF_SetTxInfinitePreamble( void ) +{ + SUBGRF_WriteCommand( RADIO_SET_TXCONTINUOUSPREAMBLE, 0, 0 ); +} + +void SUBGRF_SetStopRxTimerOnPreambleDetect( bool enable ) +{ + SUBGRF_WriteCommand( RADIO_SET_STOPRXTIMERONPREAMBLE, ( uint8_t* )&enable, 1 ); +} + +void SUBGRF_SetLoRaSymbNumTimeout( uint8_t symbNum ) +{ + SUBGRF_WriteCommand( RADIO_SET_LORASYMBTIMEOUT, &symbNum, 1 ); + + if( symbNum >= 64 ) + { + uint8_t mant = symbNum >> 1; + uint8_t exp = 0; + uint8_t reg = 0; + + while( mant > 31 ) + { + mant >>= 2; + exp++; + } + + reg = exp + ( mant << 3 ); + SUBGRF_WriteRegister( REG_LR_SYNCH_TIMEOUT, reg ); + } +} + +void SUBGRF_SetRegulatorMode( void ) +{ + /* ST_WORKAROUND_BEGIN: Get RegulatorMode value from RBI */ + RadioRegulatorMode_t mode; + + if ( ( 1UL == RBI_IsDCDC() ) && ( 1UL == DCDC_ENABLE ) ) + { + mode = USE_DCDC ; + } + else + { + mode = USE_LDO ; + } + /* ST_WORKAROUND_END */ + SUBGRF_WriteCommand( RADIO_SET_REGULATORMODE, ( uint8_t* )&mode, 1 ); +} + +void SUBGRF_Calibrate( CalibrationParams_t calibParam ) +{ + uint8_t value = ( ( ( uint8_t )calibParam.Fields.ImgEnable << 6 ) | + ( ( uint8_t )calibParam.Fields.ADCBulkPEnable << 5 ) | + ( ( uint8_t )calibParam.Fields.ADCBulkNEnable << 4 ) | + ( ( uint8_t )calibParam.Fields.ADCPulseEnable << 3 ) | + ( ( uint8_t )calibParam.Fields.PLLEnable << 2 ) | + ( ( uint8_t )calibParam.Fields.RC13MEnable << 1 ) | + ( ( uint8_t )calibParam.Fields.RC64KEnable ) ); + + SUBGRF_WriteCommand( RADIO_CALIBRATE, &value, 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] = 0xDB; + } + 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; + } + SUBGRF_WriteCommand( 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; + SUBGRF_WriteCommand( RADIO_SET_PACONFIG, buf, 4 ); +} + +void SUBGRF_SetRxTxFallbackMode( uint8_t fallbackMode ) +{ + SUBGRF_WriteCommand( 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 ); + SUBGRF_WriteCommand( RADIO_CFG_DIOIRQ, buf, 8 ); +} + +uint16_t SUBGRF_GetIrqStatus( void ) +{ + uint8_t irqStatus[2]; + + SUBGRF_ReadCommand( 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 ); + + SUBGRF_WriteCommand( RADIO_SET_TCXOMODE, buf, 4 ); +} + +void SUBGRF_SetRfFrequency( uint32_t frequency ) +{ + uint8_t buf[4]; + uint32_t chan = 0; + + if( ImageCalibrated == false ) + { + SUBGRF_CalibrateImage( frequency ); + ImageCalibrated = true; + } + /* ST_WORKAROUND_BEGIN: Simplified frequency calculation */ + SX_FREQ_TO_CHANNEL(chan, frequency); + /* ST_WORKAROUND_END */ + 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 ); + SUBGRF_WriteCommand( RADIO_SET_RFFREQUENCY, buf, 4 ); +} + +void SUBGRF_SetPacketType( RadioPacketTypes_t packetType ) +{ + // Save packet type internally to avoid questioning the radio + PacketType = packetType; + + if( packetType == PACKET_TYPE_GFSK ) + { + SUBGRF_WriteRegister( REG_BIT_SYNC, 0x00 ); + } + SUBGRF_WriteCommand( RADIO_SET_PACKETTYPE, ( uint8_t* )&packetType, 1 ); +} + +RadioPacketTypes_t SUBGRF_GetPacketType( void ) +{ + return PacketType; +} + +void SUBGRF_SetTxParams( uint8_t paSelect, int8_t power, RadioRampTimes_t rampTime ) +{ + uint8_t buf[2]; + int32_t max_power; + + if (paSelect == RFO_LP) + { + max_power = RBI_GetRFOMaxPowerConfig(RBI_RFO_LP_MAXPOWER); + if (power > max_power) + { + power = max_power; + } + if (max_power == 14) + { + SUBGRF_SetPaConfig(0x04, 0x00, 0x01, 0x01); + power = 0x0E - (max_power - power); + } + else if (max_power == 10) + { + SUBGRF_SetPaConfig(0x01, 0x00, 0x01, 0x01); + power = 0x0D - (max_power - power); + } + else /*default 15dBm*/ + { + SUBGRF_SetPaConfig(0x06, 0x00, 0x01, 0x01); + power = 0x0E - (max_power - power); + } + if (power < -17) + { + power = -17; + } + SUBGRF_WriteRegister(REG_OCP, 0x18); /* current max is 80 mA for the whole device*/ + } + else /* rfo_hp*/ + { + /* WORKAROUND - Better Resistance of the RFO High Power Tx to Antenna Mismatch, see STM32WL Erratasheet*/ + SUBGRF_WriteRegister(REG_TX_CLAMP, SUBGRF_ReadRegister(REG_TX_CLAMP) | (0x0F << 1)); + /* WORKAROUND END*/ + max_power = RBI_GetRFOMaxPowerConfig(RBI_RFO_HP_MAXPOWER); + if (power > max_power) + { + power = max_power; + } + if (max_power == 20) + { + SUBGRF_SetPaConfig(0x03, 0x05, 0x00, 0x01); + power = 0x16 - (max_power - power); + } + else if (max_power == 17) + { + SUBGRF_SetPaConfig(0x02, 0x03, 0x00, 0x01); + power = 0x16 - (max_power - power); + } + else if (max_power == 14) + { + SUBGRF_SetPaConfig(0x02, 0x02, 0x00, 0x01); + power = 0x0E - (max_power - power); + } + else /*22dBm*/ + { + SUBGRF_SetPaConfig(0x04, 0x07, 0x00, 0x01); + power = 0x16 - (max_power - power); + } + if (power < -9) + { + power = -9; + } + SUBGRF_WriteRegister(REG_OCP, 0x38); /*current max 160mA for the whole device*/ + } + buf[0] = power; + buf[1] = (uint8_t)rampTime; + SUBGRF_WriteCommand(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 * XTAL_FREQ ) / 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; + /* ST_WORKAROUND_BEGIN: Simplified frequency calculation */ + SX_FREQ_TO_CHANNEL(tempVal, modulationParams->Params.Gfsk.Fdev); + /* ST_WORKAROUND_END */ + buf[5] = ( tempVal >> 16 ) & 0xFF; + buf[6] = ( tempVal >> 8 ) & 0xFF; + buf[7] = ( tempVal& 0xFF ); + SUBGRF_WriteCommand( RADIO_SET_MODULATIONPARAMS, buf, n ); + break; + case PACKET_TYPE_BPSK: + n = 4; + tempVal = ( uint32_t ) (( 32 * XTAL_FREQ) / modulationParams->Params.Bpsk.BitRate ); + buf[0] = ( tempVal >> 16 ) & 0xFF; + buf[1] = ( tempVal >> 8 ) & 0xFF; + buf[2] = tempVal & 0xFF; + buf[3] = modulationParams->Params.Bpsk.ModulationShaping; + SUBGRF_WriteCommand( 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; + + SUBGRF_WriteCommand( RADIO_SET_MODULATIONPARAMS, buf, n ); + + break; + case PACKET_TYPE_GMSK: + n = 5; + tempVal = ( uint32_t )(( 32 *XTAL_FREQ) / 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; + SUBGRF_WriteCommand( 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] = LoRaHeaderType = 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; + } + SUBGRF_WriteCommand( 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 ); + SUBGRF_WriteCommand( RADIO_SET_CADPARAMS, buf, 7 ); + OperatingMode = MODE_CAD; +} + +void SUBGRF_SetBufferBaseAddress( uint8_t txBaseAddress, uint8_t rxBaseAddress ) +{ + uint8_t buf[2]; + + buf[0] = txBaseAddress; + buf[1] = rxBaseAddress; + SUBGRF_WriteCommand( RADIO_SET_BUFFERBASEADDRESS, buf, 2 ); +} + +RadioStatus_t SUBGRF_GetStatus( void ) +{ + uint8_t stat = 0; + RadioStatus_t status = { .Value = 0 }; + + /* ST_WORKAROUND_BEGIN: Read the Device Status by the GET_STATUS command (HAL limitations) */ + SUBGRF_ReadCommand( RADIO_GET_STATUS, &stat, 1 ); + /* ST_WORKAROUND_END */ + status.Fields.CmdStatus = ( stat & ( 0x07 << 1 ) ) >> 1; + status.Fields.ChipMode = ( stat & ( 0x07 << 4 ) ) >> 4; + return status; +} + +int8_t SUBGRF_GetRssiInst( void ) +{ + uint8_t buf[1]; + int8_t rssi = 0; + + SUBGRF_ReadCommand( RADIO_GET_RSSIINST, buf, 1 ); + rssi = -buf[0] >> 1; + return rssi; +} + +void SUBGRF_GetRxBufferStatus( uint8_t *payloadLength, uint8_t *rxStartBufferPointer ) +{ + uint8_t status[2]; + + SUBGRF_ReadCommand( RADIO_GET_RXBUFFERSTATUS, status, 2 ); + + // In case of LORA fixed header, the payloadLength is obtained by reading + // the register REG_LR_PAYLOADLENGTH + if( ( SUBGRF_GetPacketType( ) == PACKET_TYPE_LORA ) && ( LoRaHeaderType == LORA_PACKET_FIXED_LENGTH ) ) + { + *payloadLength = SUBGRF_ReadRegister( REG_LR_PAYLOADLENGTH ); + } + else + { + *payloadLength = status[0]; + } + *rxStartBufferPointer = status[1]; +} + +void SUBGRF_GetPacketStatus( PacketStatus_t *pktStatus ) +{ + uint8_t status[3]; + + SUBGRF_ReadCommand( 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; + // Returns SNR value [dB] rounded to the nearest integer value + pktStatus->Params.LoRa.SnrPkt = ( ( ( int8_t )status[1] ) + 2 ) >> 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( pktStatus, 0, sizeof( PacketStatus_t ) ); + pktStatus->packetType = PACKET_TYPE_NONE; + break; + } +} + +RadioError_t SUBGRF_GetDeviceErrors( void ) +{ + uint8_t err[] = { 0, 0 }; + RadioError_t error = { .Value = 0 }; + + SUBGRF_ReadCommand( RADIO_GET_ERROR, ( uint8_t * )err, 2 ); + error.Fields.PaRamp = ( err[0] & ( 1 << 0 ) ) >> 0; + error.Fields.PllLock = ( err[1] & ( 1 << 6 ) ) >> 6; + error.Fields.XoscStart = ( err[1] & ( 1 << 5 ) ) >> 5; + error.Fields.ImgCalib = ( err[1] & ( 1 << 4 ) ) >> 4; + error.Fields.AdcCalib = ( err[1] & ( 1 << 3 ) ) >> 3; + error.Fields.PllCalib = ( err[1] & ( 1 << 2 ) ) >> 2; + error.Fields.Rc13mCalib = ( err[1] & ( 1 << 1 ) ) >> 1; + error.Fields.Rc64kCalib = ( err[1] & ( 1 << 0 ) ) >> 0; + return error; +} + +void SUBGRF_ClearDeviceErrors( void ) +{ + uint8_t buf[2] = { 0x00, 0x00 }; + SUBGRF_WriteCommand( RADIO_CLR_ERROR, buf, 2 ); +} + +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 ); + SUBGRF_WriteCommand( 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 address, uint8_t *buffer, uint16_t size ) +{ + CRITICAL_SECTION_BEGIN(); + HAL_SUBGHZ_WriteRegisters( &hsubghz, address, buffer, size ); + CRITICAL_SECTION_END(); +} + +void SUBGRF_ReadRegisters( uint16_t address, uint8_t *buffer, uint16_t size ) +{ + CRITICAL_SECTION_BEGIN(); + HAL_SUBGHZ_ReadRegisters( &hsubghz, address, buffer, size ); + CRITICAL_SECTION_END(); +} + +void SUBGRF_WriteBuffer( uint8_t offset, uint8_t *buffer, uint8_t size ) +{ + CRITICAL_SECTION_BEGIN(); + HAL_SUBGHZ_WriteBuffer( &hsubghz, offset, buffer, size ); + CRITICAL_SECTION_END(); +} + +void SUBGRF_ReadBuffer( uint8_t offset, uint8_t *buffer, uint8_t size ) +{ + CRITICAL_SECTION_BEGIN(); + HAL_SUBGHZ_ReadBuffer( &hsubghz, offset, buffer, size ); + CRITICAL_SECTION_END(); +} + +void SUBGRF_WriteCommand( SUBGHZ_RadioSetCmd_t Command, uint8_t *pBuffer, + uint16_t Size ) +{ + CRITICAL_SECTION_BEGIN(); + HAL_SUBGHZ_ExecSetCmd( &hsubghz, Command, pBuffer, Size ); + CRITICAL_SECTION_END(); +} + +void SUBGRF_ReadCommand( SUBGHZ_RadioGetCmd_t Command, uint8_t *pBuffer, + uint16_t Size ) +{ + CRITICAL_SECTION_BEGIN(); + HAL_SUBGHZ_ExecGetCmd( &hsubghz, Command, pBuffer, Size ); + CRITICAL_SECTION_END(); +} + +void SUBGRF_SetSwitch( uint8_t paSelect, RFState_t rxtx ) +{ + RBI_Switch_TypeDef state = RBI_SWITCH_RX; + + if (rxtx == RFSWITCH_TX) + { + if (paSelect == RFO_LP) + { + state = RBI_SWITCH_RFO_LP; + Radio_SMPS_Set(SMPS_DRIVE_SETTING_MAX); + } + if (paSelect == RFO_HP) + { + state = RBI_SWITCH_RFO_HP; + } + } + else + { + if (rxtx == RFSWITCH_RX) + { + state = RBI_SWITCH_RX; + } + } + RBI_ConfigRFSwitch(state); +} + +uint8_t SUBGRF_SetRfTxPower( int8_t power ) +{ + uint8_t paSelect= RFO_LP; + + int32_t TxConfig = RBI_GetTxConfig(); + + switch (TxConfig) + { + case RBI_CONF_RFO_LP_HP: + { + if (power > 15) + { + paSelect = RFO_HP; + } + else + { + paSelect = RFO_LP; + } + break; + } + case RBI_CONF_RFO_LP: + { + paSelect = RFO_LP; + break; + } + case RBI_CONF_RFO_HP: + { + paSelect = RFO_HP; + break; + } + default: + break; + } + + SUBGRF_SetTxParams( paSelect, power, RADIO_RAMP_40_US ); + + return paSelect; +} + +uint32_t SUBGRF_GetRadioWakeUpTime( void ) +{ + return RF_WAKEUP_TIME; +} + +/* 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_CADStatusCallback(SUBGHZ_HandleTypeDef *hsubghz, HAL_SUBGHZ_CadStatusTypeDef cadstatus) +{ + switch (cadstatus) + { + case HAL_SUBGHZ_CAD_CLEAR: + RadioOnDioIrqCb( IRQ_CAD_CLEAR); + break; + case HAL_SUBGHZ_CAD_DETECTED: + RadioOnDioIrqCb( IRQ_CAD_DETECTED); + break; + default: + break; + } +} + +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 Radio_SMPS_Set(uint8_t level) +{ + if ( 1U == RBI_IsDCDC() ) + { + uint8_t modReg; + modReg= SUBGRF_ReadRegister(SUBGHZ_SMPSC2R); + modReg&= (~SMPS_DRV_MASK); + SUBGRF_WriteRegister(SUBGHZ_SMPSC2R, modReg | level); + } +} + +uint8_t SUBGRF_GetFskBandwidthRegValue( uint32_t bandwidth ) +{ + uint8_t i; + + if( bandwidth == 0 ) + { + return( 0x1F ); + } + + /* ST_WORKAROUND_BEGIN: Simplified loop */ + for( i = 0; i < ( sizeof( FskBandwidths ) / sizeof( FskBandwidth_t ) ); i++ ) + { + if ( bandwidth < FskBandwidths[i].bandwidth ) + { + return FskBandwidths[i].RegValue; + } + } + /* ST_WORKAROUND_END */ + // ERROR: Value not found + while( 1 ); +} +void SUBGRF_GetCFO( uint32_t bitRate, int32_t *cfo) +{ + uint8_t BwMant[] = {4, 8, 10, 12}; + /* read demod bandwidth: mant bit4:3, exp bits 2:0 */ + uint8_t reg = (SUBGRF_ReadRegister( SUBGHZ_BWSELR )); + uint8_t bandwidth_mant = BwMant[( reg >> 3 ) & 0x3]; + uint8_t bandwidth_exp = reg & 0x7; + uint32_t cf_fs = XTAL_FREQ / ( bandwidth_mant * ( 1 << ( bandwidth_exp - 1 ))); + uint32_t cf_osr = cf_fs / bitRate; + uint8_t interp = 1; + /* calculate demod interpolation factor */ + if (cf_osr * interp < 8) + { + interp = 2; + } + if (cf_osr * interp < 4) + { + interp = 4; + } + /* calculate demod sampling frequency */ + uint32_t fs = cf_fs* interp; + /* get the cfo registers */ + int32_t cfo_bin = ( SUBGRF_ReadRegister( SUBGHZ_GCFORH ) & 0xF ) << 8; + cfo_bin |= SUBGRF_ReadRegister( SUBGHZ_GCFORL ); + /* negate if 12 bits sign bit is 1 */ + if (( cfo_bin & 0x800 ) == 0x800 ) + { + cfo_bin |= 0xFFFFF000; + } + /* calculate cfo in Hz */ + /* shift by 5 first to not saturate, cfo_bin on 12bits */ + *cfo = ((int32_t)( cfo_bin * ( fs >> 5 ))) >> ( 12 - 5 ); +} diff --git a/src/STM32CubeWL/SubGHz_Phy/stm32_radio_driver/radio_driver.h b/src/STM32CubeWL/SubGHz_Phy/stm32_radio_driver/radio_driver.h new file mode 100644 index 0000000..79690ff --- /dev/null +++ b/src/STM32CubeWL/SubGHz_Phy/stm32_radio_driver/radio_driver.h @@ -0,0 +1,1275 @@ +/*! + * \file radio_driver.h + * + * \brief STM32WL_SubGHz_Phy driver implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + */ +/** + ****************************************************************************** + * + * Portions COPYRIGHT 2020 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__ + +#ifdef __cplusplus +extern "C" { +#endif +/* Includes ------------------------------------------------------------------*/ + +#include +#include + +/* Exported constants --------------------------------------------------------*/ +#define RFO_LP 1 +#define RFO_HP 2 + +/*! + * Generic SUBGRF_ error code + */ +#define SUBGRF_OK 0 +#define SUBGRF_ERROR -1 + +/*! + * \brief 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 address of the register holding the re-calculated number of symbols + */ +#define REG_LR_SYNCH_TIMEOUT 0x0706 + +/*! + * \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 + +/*! + * \brief Syncword for Private LoRa networks + */ +#define LORA_MAC_PRIVATE_SYNCWORD 0x1424 + +/*! + * \brief Syncword for Public LoRa networks + */ +#define LORA_MAC_PUBLIC_SYNCWORD 0x3444 + +/*! + * \brief The address of the register giving a 32-bit random number + */ +#define RANDOM_NUMBER_GENERATORBASEADDR 0x0819 + +/*! + * \brief The address of the register used to disable the LNA + */ +#define REG_ANA_LNA 0x08E2 + +/*! + * The address of the register used to disable the mixer + */ +#define REG_ANA_MIXER 0x08E5 + +/*! + * \brief The address of the register holding RX Gain value (0x94: power saving, 0x96: rx boosted) + */ +#define REG_RX_GAIN 0x08AC + +/*! + * \brief The address of the register holding Bit Sync configuration + */ +#define REG_BIT_SYNC 0x06AC + +/*! + * \brief Change the value on the device internal trimming capacitor + */ +#define REG_XTA_TRIM 0x0911 + +/*! + * \brief Change the value on the device internal trimming capacitor + */ +#define REG_XTB_TRIM 0x0912 + +/*! + * \brief Set the current max value in the over current protection + */ +#define REG_OCP 0x08E7 + +/*! + * \brief PA Clamping threshold + */ +#define REG_TX_CLAMP 0x08D8 + +/** + * @brief Sub-GHz radio RAM definition + * @note The sub-GHz radio peripheral RAM address can be accessed by sub-GHz radio command + * SUBGRF_WriteRegisters() and SUBGRF_ReadRegisters() " + * @note These RAM addresses are used to control accurately ramp-up, ramp-down and length of a frame + */ +/*Sub-GHz radio Ramp Up High register*/ +#define SUBGHZ_RAM_RAMPUPH 0x00F0 +/*Sub-GHz radio Ramp Up Low register*/ +#define SUBGHZ_RAM_RAMPUPL 0x00F1 +/*Sub-GHz radio Ramp Down High register*/ +#define SUBGHZ_RAM_RAMPDNH 0x00F2 +/*Sub-GHz radio Ramp Down Low register*/ +#define SUBGHZ_RAM_RAMPDNL 0x00F3 +/*Sub-GHz radio frame limit High register*/ +#define SUBGHZ_RAM_FRAMELIMH 0x00F4 +/*Sub-GHz radio frame limit Low register*/ +#define SUBGHZ_RAM_FRAMELIML 0x00F5 + +/** + * @brief Sub-GHz radio register (re) definition + * @note The sub-GHz radio peripheral registers can be accessed by sub-GHz radio command + * SUBGRF_WriteRegisters() and SUBGRF_ReadRegisters() " + */ + +/*Sub-GHz radio generic bit synchronization register*/ +#define SUBGHZ_GBSYNCR REG_BIT_SYNC +/*Sub-GHz radio generic CFO High register */ +#define SUBGHZ_GCFORH 0x06B0 +/*Sub-GHz radio generic CFO Low register */ +#define SUBGHZ_GCFORL 0x06B1 +/*Sub-GHz radio generic pktCtl1 register*/ +#define SUBGHZ_GPKTCTL1R 0x06B4 +/*Sub-GHz radio generic packet control 1A register*/ +#define SUBGHZ_GPKTCTL1AR REG_LR_WHITSEEDBASEADDR_MSB +/*Sub-GHz radio generic whitening LSB register*/ +#define SUBGHZ_GWHITEINIRL REG_LR_WHITSEEDBASEADDR_LSB +/*Sub-GHz radio generic rtx register*/ +#define SUBGHZ_GRTXPLDLEN 0x06BB +/*Sub-GHz radio generic CRC initial MSB register*/ +#define SUBGHZ_GCRCINIRH REG_LR_CRCSEEDBASEADDR +/*Sub-GHz radio generic CRC initial LSB register*/ +#define SUBGHZ_GCRCINIRL 0x06BD +/*Sub-GHz radio generic CRC polynomial MSB register*/ +#define SUBGHZ_GCRCPOLRH REG_LR_CRCPOLYBASEADDR +/*Sub-GHz radio generic CRC polynomial LSB register*/ +#define SUBGHZ_GCRCPOLRL 0x06BF +/*Sub-GHz radio generic synchronization word control register 7*/ +#define SUBGHZ_GSYNCR7 REG_LR_SYNCWORDBASEADDRESS +/*Sub-GHz radio generic synchronization word control register 6*/ +#define SUBGHZ_GSYNCR6 0x06C1 +/*Sub-GHz radio generic synchronization word control register 5*/ +#define SUBGHZ_GSYNCR5 0x06C2 +/*Sub-GHz radio generic synchronization word control register 4*/ +#define SUBGHZ_GSYNCR4 0x06C3 +/*Sub-GHz radio generic synchronization word control register 3*/ +#define SUBGHZ_GSYNCR3 0x06C4 +/*Sub-GHz radio generic synchronization word control register 2*/ +#define SUBGHZ_GSYNCR2 0x06C5 +/*Sub-GHz radio generic synchronization word control register 1*/ +#define SUBGHZ_GSYNCR1 0x06C6 +/*Sub-GHz radio generic synchronization word control register 0*/ +#define SUBGHZ_GSYNCR0 0x06C7 +/*Sub-GHz radio generic node address register*/ +#define SUBGHZ_GNODEADDR 0x06CD +/*Sub-GHz radio generic broadacst address register*/ +#define SUBGHZ_GBCASTADDR 0x06CE +/*Sub-GHz radio generic Afc register*/ +#define SUBGHZ_GAFCR 0x06D1 +/*Sub-GHz radio Lora Payload Length */ +#define SUBGHZ_LPLDLENR REG_LR_PAYLOADLENGTH +/*Sub-GHz radio Lora synchro timeout */ +#define SUBGHZ_LSYNCTIMEOUTR REG_LR_SYNCH_TIMEOUT +/*Sub-GHz radio Lora IQ polarity register*/ +#define SUBGHZ_LIQPOLR 0x0736 +/*Sub-GHz radio LoRa synchronization word MSB register*/ +#define SUBGHZ_LSYNCRH REG_LR_SYNCWORD +/*Sub-GHz radio LoRa synchronization word LSB register*/ +#define SUBGHZ_LSYNCRL 0x0741 +/*Sub-GHz radio txAddrPtr register*/ +#define SUBGHZ_TXADRPTR 0x0802 +/*Sub-GHz radio rxAddrPtr register*/ +#define SUBGHZ_RXADRPTR 0x0803 +/*Sub-GHz radio Rx Bandwidth selector register */ +#define SUBGHZ_BWSELR 0x0807 +/*Sub-GHz radio random number register 3*/ +#define SUBGHZ_RNGR3 RANDOM_NUMBER_GENERATORBASEADDR +/*Sub-GHz radio random number register 2*/ +#define SUBGHZ_RNGR2 0x081A +/*Sub-GHz radio random number register 1*/ +#define SUBGHZ_RNGR1 0x081B +/*Sub-GHz radio random number register 0*/ +#define SUBGHZ_RNGR0 0x081C +/*Sub-GHz radio SD resolution*/ +#define SUBGHZ_SDCFG0R 0x0889 +/*Sub-GHz radio Agc Gfo Reset Rssi Control register*/ +#define SUBGHZ_AGCRSSICTL0R 0x089B +/*Sub-GHz radio receiver gain control register*/ +#define SUBGHZ_RXGAINCR REG_RX_GAIN +/*Sub-GHz radio Agc Gfo Reset Config register*/ +#define SUBGHZ_AGCGFORSTCFGR 0x08B8 +/*Sub-GHz radio Agc Gfo Reset Power Threshold register*/ +#define SUBGHZ_AGCGFORSTPOWTHR 0x08B9 +/*Sub-GHz radio Tx clamp register*/ +#define SUBGHZ_TXCLAMPR REG_TX_CLAMP +/*Sub-GHz radio PA over current protection register*/ +#define SUBGHZ_PAOCPR REG_OCP +/*Sub-GHz radio rtc control register*/ +#define SUBGHZ_RTCCTLR 0x0902 +/*Sub-GHz radio rtc period register*/ +#define SUBGHZ_RTCPRDR2 0x0903 +#define SUBGHZ_RTCPRDR1 0x0904 +#define SUBGHZ_RTCPRDR0 0x0905 +/*Sub-GHz radio HSE32 OSC_IN capacitor trim register*/ +#define SUBGHZ_HSEINTRIMR REG_XTA_TRIM +/*Sub-GHz radio HSE32 OSC_OUT capacitor trim register*/ +#define SUBGHZ_HSEOUTTRIMR REG_XTB_TRIM +/*Sub-GHz radio SMPS control 0 register */ +#define SUBGHZ_SMPSC0R 0x0916 +/*Sub-GHz radio power control register*/ +#define SUBGHZ_PCR 0x091A +/*Sub-GHz radio SMPS control 2 register */ +#define SUBGHZ_SMPSC2R 0x0923 +/*Sub-GHz event mask register*/ +#define SUBGHZ_EVENTMASKR 0x0944 + +#define SMPS_CLK_DET_ENABLE ((uint8_t) (1<<6)) + +#define SMPS_DRV_20 ((uint8_t) ((0x0)<<1)) +#define SMPS_DRV_40 ((uint8_t) ((0x1)<<1)) +#define SMPS_DRV_60 ((uint8_t) ((0x2)<<1)) +#define SMPS_DRV_100 ((uint8_t) ((0x3)<<1)) +#define SMPS_DRV_MASK ((uint8_t) ((0x3)<<1)) + +/* 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 GFSK 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 Radio lengths mode for the 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 voltage used to control the TCXO on/off VDD_TCXO + */ +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_CLEAR = 0x0080, + IRQ_CAD_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; + +/*! + * \brief 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; + +/*! + * Hardware IO IRQ callback function definition + */ +typedef void ( *DioIrqHandler )( RadioIrqMasks_t radioIrq ); + +#define RX_BUFFER_SIZE 256 + +/* External variables --------------------------------------------------------*/ +/* Exported macros -----------------------------------------------------------*/ +/* Exported functions ------------------------------------------------------- */ + +/*! + * ============================================================================ + * Public functions prototypes + * ============================================================================ + */ + +/*! + * \brief Initializes the bus for SUBG_RF driver communication + */ +void SUBGRF_Init( DioIrqHandler dioIrq ); + + /*! + * \brief Gets the current Operation Mode of the Radio + * + * \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 + */ +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 + */ +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 + */ +void SUBGRF_SendPayload( uint8_t *payload, uint8_t size, uint32_t timeout ); + +/*! + * \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 ) + * + */ +void SUBGRF_SetCrcSeed( uint16_t seed ); + +/*! + * \brief Sets the seed used for the CRC calculation + * + * \param [in] polynomial The polynomial value + * + */ +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 + */ +void SUBGRF_SetWhiteningSeed( uint16_t seed ); + +/*! + * \brief Gets a 32-bit random value generated by the radio + * + * \remark A valid packet type must have been configured with \ref SUBGRF_SetPacketType + * before using this command. + * + * \remark The radio must be in reception mode before executing this function + * This code can potentially result in interrupt generation. It is the responsibility of + * the calling code to disable radio interrupts before calling this function, + * and re-enable them afterwards if necessary, or be certain that any interrupts + * generated during this process will not cause undesired side-effects in the software. + * + * Please note that the random numbers produced by the generator do not have a uniform or Gaussian distribution. If + * uniformity is needed, perform appropriate software post-processing. + * + * \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 + */ +void SUBGRF_SetSleep( SleepParams_t sleepConfig ); + +/*! + * \brief Sets the radio in configuration mode + * + * \param [in] mode The standby mode to put the radio into + */ +void SUBGRF_SetStandby( RadioStandbyModes_t mode ); + +/*! + * \brief Sets the radio in FS mode + */ +void SUBGRF_SetFs( void ); + +/*! + * \brief Sets the radio in transmission mode + * + * \param [in] timeout Structure describing the transmission timeout value + */ +void SUBGRF_SetTx( uint32_t timeout ); + +/*! + * \brief Sets the radio in reception mode + * + * \param [in] timeout Structure describing the reception timeout value + */ +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 + */ +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 + */ +void SUBGRF_SetRxDutyCycle( uint32_t rxTime, uint32_t sleepTime ); + +/*! + * \brief Sets the radio in CAD mode + */ +void SUBGRF_SetCad( void ); + +/*! + * \brief Sets the radio in continuous wave transmission mode + */ +void SUBGRF_SetTxContinuousWave( void ); + +/*! + * \brief Sets the radio in continuous preamble transmission mode + */ +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] + */ +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 + */ +void SUBGRF_SetLoRaSymbNumTimeout( uint8_t symbNum ); + +/*! + * \brief Sets the power regulators operating mode + */ +void SUBGRF_SetRegulatorMode( void ); + +/*! + * \brief Calibrates the given radio block + * + * \param [in] calibParam The description of blocks to be calibrated + */ +void SUBGRF_Calibrate( CalibrationParams_t calibParam ); + +/*! + * \brief Calibrates the Image rejection depending of the frequency + * + * \param [in] freq The operating frequency + */ +void SUBGRF_CalibrateImage( uint32_t freq ); + +/*! + * \brief Activate the extension of the timeout when long preamble is used + * + * \param [in] enable The radio will extend the timeout to cope with long preamble + */ +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 + */ +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 + */ +void SUBGRF_SetRxTxFallbackMode( uint8_t fallbackMode ); + +/*! + * \brief Write data to the radio memory + * + * \param [in] address The address of the first byte to write in the radio + * \param [in] buffer The data to be written in radio's memory + * \param [in] size The number of bytes to write in radio's memory + */ +void SUBGRF_WriteRegisters( uint16_t address, uint8_t *buffer, uint16_t size ); + +/*! + * \brief Read data from the radio memory + * + * \param [in] address The address of the first byte to read from the radio + * \param [out] buffer The buffer that holds data read from radio + * \param [in] size The number of bytes to read from radio's memory + */ +void SUBGRF_ReadRegisters( uint16_t address, uint8_t *buffer, uint16_t size ); + +/*! + * \brief Write data to the buffer holding the payload in the radio + * + * \param [in] offset The offset to start writing the payload + * \param [in] buffer The data to be written (the payload) + * \param [in] size The number of byte to be written + */ +void SUBGRF_WriteBuffer( uint8_t offset, uint8_t *buffer, uint8_t size ); + +/*! + * \brief Read data from the buffer holding the payload in the radio + * + * \param [in] offset The offset to start reading the payload + * \param [out] buffer A pointer to a buffer holding the data from the radio + * \param [in] size The number of byte to be read + */ +void SUBGRF_ReadBuffer( uint8_t offset, uint8_t *buffer, uint8_t size ); + +/*! + * \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 + */ +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 + */ +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 + */ +void SUBGRF_SetTcxoMode( RadioTcxoCtrlVoltage_t tcxoVoltage, uint32_t timeout ); + +/*! + * \brief Sets the RF frequency + * + * \param [in] frequency RF frequency [Hz] + */ +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 + */ +void SUBGRF_SetPacketType( RadioPacketTypes_t packetType ); + +/*! + * \brief Gets the current radio protocol + * + * \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 + */ +void SUBGRF_SetTxParams( uint8_t paSelect, int8_t power, RadioRampTimes_t rampTime ); + +/*! + * \brief Set the modulation parameters + * + * \param [in] modParams A structure describing the modulation parameters + */ +void SUBGRF_SetModulationParams( ModulationParams_t *modParams ); + +/*! + * \brief Sets the packet parameters + * + * \param [in] packetParams A structure describing the packet parameters + */ +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 Limit 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 + */ +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 + */ +void SUBGRF_SetBufferBaseAddress( uint8_t txBaseAddress, uint8_t rxBaseAddress ); + +/*! + * \brief Gets the current radio status + * + * \retval status Radio status + */ +RadioStatus_t SUBGRF_GetStatus( void ); + +/*! + * \brief Returns the instantaneous RSSI value for the last packet received + * + * \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 + */ +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 + */ +void SUBGRF_GetPacketStatus( PacketStatus_t *pktStatus ); + +/*! + * \brief Returns the possible system errors + * + * \retval sysErrors Value representing the possible sys failures + */ +RadioError_t SUBGRF_GetDeviceErrors( void ); + +/*! + * \brief Clear all the errors in the device + */ +void SUBGRF_ClearDeviceErrors( void ); + +/*! + * \brief Clears the IRQs + * + * \param [in] irq IRQ(s) to be cleared + */ +void SUBGRF_ClearIrqStatus( uint16_t irq ); + +/*! + * \brief Write radio register + * \param [in] address The address of the register + * \param [in] data data to write + */ +void SUBGRF_WriteRegister( uint16_t address, uint8_t data ); + +/*! + * \brief Read radio register + * \param [in] address The address of the register + * \retval Data read + */ +uint8_t SUBGRF_ReadRegister( uint16_t address ); + +/*! + * \brief Sets RF switch for TX & RX + * \param [in] paSelect Low Power or High Power board + * \param [in] rxtx RX/TX mode + */ +void SUBGRF_SetSwitch (uint8_t paSelect, RFState_t rxtx); + +/*! + * \brief Set the Tx End Device conducted power + * \param [in] power Tx power level [0..15] + * \retval paSelect [RFO_LP, RFO_HP] + */ +uint8_t SUBGRF_SetRfTxPower( int8_t power ); + +/*! + * \brief Service to get the radio wake-up time. + * \return Value of the radio wake-up time. + */ +uint32_t SUBGRF_GetRadioWakeUpTime( void ); + +/*! + * \brief Returns the known FSK bandwidth registers value + * + * \param [in] bandwidth Bandwidth value in Hz + * \retval regValue Bandwidth register value. + */ +uint8_t SUBGRF_GetFskBandwidthRegValue( uint32_t bandwidth ); +/*! + * \brief SUBGRF_GetCFO get the frequency offset between the remote transmitter and the radio receiver + * + * \param [in] bitrate gfsk bitrate + * \param [out] cfo carrier frequency offset in Hertz + */ +void SUBGRF_GetCFO( uint32_t bitrate, int32_t *cfo); + +#ifdef __cplusplus +} +#endif + +#endif // __RADIO_DRIVER_H__ diff --git a/src/STM32CubeWL/SubGHz_Phy/stm32_radio_driver/radio_fw.c b/src/STM32CubeWL/SubGHz_Phy/stm32_radio_driver/radio_fw.c new file mode 100644 index 0000000..f3d765e --- /dev/null +++ b/src/STM32CubeWL/SubGHz_Phy/stm32_radio_driver/radio_fw.c @@ -0,0 +1,1102 @@ +/** + ****************************************************************************** + * @file radio_fw.c + * @author MCD Application Team + * @brief extended radio functionality SubGHz_Phy Middleware + ****************************************************************************** + * @attention + * + * Copyright (c) 2020(-2021) STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + +/* Includes ------------------------------------------------------------------*/ +#include +#include "../../../BSP/timer.h" +#include "../radio.h" +#include "radio_fw.h" +#include "radio_driver.h" +#include "../../../BSP/radio_conf.h" +#include "../../../BSP/mw_log_conf.h" + +/* External variables --------------------------------------------------------*/ +/* Private typedef -----------------------------------------------------------*/ + +/** + * Radio RFWPacket and global parameters + * In RFWPacket mode, the packet is 'Fixed length' without Hw Crc check and without Hw (de-)whitening + */ +typedef struct +{ + uint8_t Enable; /* RFWPacket mode On: 1 or Off: 0*/ + uint8_t PayloadLengthFieldSize; /* Size of the packet length Field*/ + uint8_t CrcEnable; /* enable crc calculation and check*/ + uint8_t CrcFieldSize; /* size of the packet length Field*/ + uint16_t CrcPolynomial; /* Init Crc polynomial, to set before running RFW_CrcRun*/ + uint16_t CrcSeed; /* Init Crc seed to set before running RFW_CrcRun*/ + RADIO_FSK_CrcTypes_t CrcType; /* Init Crc types to set before running RFW_CrcRun*/ + uint16_t WhiteSeed; /* Init whitening seed, to set before running Radio_FwWhiteRun*/ + uint16_t LongPacketMaxRxLength; /* Maximum expected amount of bytes in payload*/ + RadioModems_t Modem; + RadioEvents_t* RadioEvents; +} RFwInit_t; + +typedef struct +{ + RFwInit_t Init; /*Init structure, set at Rx or Tx config*/ + uint16_t CrcLfsrState; /*State of LFSR crc, set from CrcSeed at beginning of each payload*/ + uint16_t WhiteLfsrState; /*State of LFSR whitening, set from WhiteSeed at beginning of each payload + use to save LFSR state after rx payload length de-withening*/ + uint16_t PayloadLength; /*In Rx, Payload length is first byte(s) of the payload excluding CrcFieldSize and PayloadLengthFieldSize*/ + uint8_t LongPacketModeEnable; /* set to one when RFW_TransmitLongPacket or RFW_ReceiveLongPacket. 0 otherwise*/ + TimerEvent_t Timer; /*Timer to get/Set Rx(Tx)Bytes*/ + uint16_t LongPacketRemainingBytes; /* Count remaining bytes to send receive (including crc)*/ + uint8_t RadioBufferOffset; /* Radio buffer offset*/ + uint16_t RxPayloadOffset; /* RxPayloadOffset buffer offset*/ + void (*RxLongPacketStoreChunkCb) (uint8_t* buffer, uint8_t buffer_size); + void (*TxLongPacketGetNextChunkCb) (uint8_t** buffer, uint8_t buffer_size); + uint8_t AntSwitchPaSelect; + uint32_t BitRate; + TimerEvent_t* RxTimeoutTimer; + TimerEvent_t* TxTimeoutTimer; +} RadioFw_t; + +/* Private define ------------------------------------------------------------*/ + +#define RADIO_BUF_SIZE 255 //bytes SubG IP internal Buffer + +#define LONGPACKET_CHUNK_LENGTH_BYTES ((int32_t) 128) //bytes (half Radio fifo) + +/* Private macro -------------------------------------------------------------*/ +/** + * @brief Calculates ceiling division of ( X / N ) + * + * @param [in] X numerator + * @param [in] N denominator + * + */ +#ifndef DIVC +#define DIVC( X, N ) ( ( ( X ) + ( N ) - 1 ) / ( N ) ) +#endif + +/** + * @brief Calculates rounding division of ( X / N ) + * + * @param [in] X numerator + * @param [in] N denominator + * + */ +#ifndef DIVR +#define DIVR( X, N ) ( ( ( X ) + ( ((X)>0?(N):(N))>>1 ) ) / ( N ) ) +#endif + +/*can be overridden in radio_conf.h*/ +#ifndef RFW_IT_INIT +#define RFW_IT_INIT() +#endif + +#ifndef RFW_GET_PAYLOAD_PROCESS +#define RFW_GET_PAYLOAD_PROCESS() RFW_GetPayloadProcess() +#endif +/*can be overridden in radio_conf.h*/ +#ifndef RFW_TRANSMIT_LONGPACKET_TX_CHUNK_PROCESS +#define RFW_TRANSMIT_LONGPACKET_TX_CHUNK_PROCESS() RFW_TransmitLongPacket_TxChunkProcess() +#endif + +#ifndef RFW_MW_LOG_ENABLE +#define RFW_MW_LOG(...) +#else +#define RFW_MW_LOG MW_LOG +#endif + +#ifndef RFW_ENABLE +#define RFW_ENABLE 0 +#endif + +#ifndef RFW_LONGPACKET_ENABLE +#define RFW_LONGPACKET_ENABLE 0 +#endif + +#ifndef RADIO_MEMCPY8 +#define RADIO_MEMCPY8 memcpy +#endif + +#ifndef RADIO_MEMSET8 +#define RADIO_MEMSET8 memset +#endif + +#if ((RFW_LONGPACKET_ENABLE==1)&&(RFW_ENABLE==0)) +#error RFW_ENABLE must be defined to 1 if RFW_LONGPACKET_ENABLE is defined to 1 +#endif + +/* Private variables ---------------------------------------------------------*/ +#if (RFW_ENABLE ==1 ) +static RadioFw_t RFWPacket={0}; +/*Radio buffer chunk*/ +static uint8_t ChunkBuffer[RADIO_BUF_SIZE]; +/*Radio buffer chunk for packet <=RADIO_BUF_SIZE and */ +static uint8_t RxBuffer[RADIO_BUF_SIZE]; +#endif +/* Private function prototypes -----------------------------------------------*/ +#if (RFW_ENABLE ==1 ) +/** + * @brief Record the seed of the (de-)Whitening algorithm + * + * @param [in] RFwInit_t the whitening Init structure + * @param [in] WhiteSeed the Initial seed of the Whitening algorithm + */ +static void RFW_WhiteInitState(RFwInit_t* Init, uint16_t WhiteSeed); + +/** + * @brief Set the state of the Whitening algorithm from the seed + * + * @param [in] RFWPacket the whitening structure + * @param [in] whiteSeed the Initial seed of the Whitening algorithm + */ +static void RFW_WhiteSetState(RadioFw_t* RFWPacket); + +/** + * @brief Initialize seed of the Crc algorithm + * + * @param [in] RFWPacket the whitening Init structure + * @param [in] CrcPolynomial the Initial seed of the Crc algorithm + * @param [in] CrcSeed the Initial seed of the Crc seed + * @param [in] CrcSeed the Initial seed of the Crc types (Ibm/CCITT) + */ +static void RFW_CrcInitState(RFwInit_t* Init, const uint16_t CrcPolynomial, const uint16_t CrcSeed, const RADIO_FSK_CrcTypes_t CrcTypes); + +/** + * @brief Run the Crc algorithm + * @param [in] RFWPacket the whitening structure + */ +static void RFW_CrcSetState(RadioFw_t* RFWPacket); +/** + * @brief Run the Whitening algorithm + * + * @param [in] RFWPacket the whitening structure + * @param [in,out] Payload the Payload to (de-)Whiten and the payload result + * @param [in] Size the Payload size + */ +static void RFW_WhiteRun(RadioFw_t* RFWPacket, uint8_t* Payload, uint32_t Size); + +/** + * @brief Run the Crc algorithm + * + * @param [in] RFWPacket the whitening structure + * @param [in] Payload the Payload to calculate the Crc + * @param [in] Size the Payload size + * @param [out] CrcResult the result of the Crc calculation + */ +static int32_t RFW_CrcRun(RadioFw_t* const RFWPacket, const uint8_t* Payload, const uint32_t Size, uint8_t CrcResult[2]); + +/** + * @brief Compute Crc Byte + * + * @param [in] crc Crc bytes + * @param [in] dataByte the data byte + * @param [out] polynomial the polynomial to use + * @return crc bytes out + */ +uint16_t RFW_CrcRun1Byte( uint16_t Crc, uint8_t DataByte, uint16_t Polynomial ); + +/** + * @brief Get the payload length after sync + * + * @param [out] PayloadLength the length of PayloadOnly excluding CrcLengthField + * @return 0 when no parameters error, -1 otherwise + */ +static int32_t RFW_GetPacketLength(uint16_t* PayloadLength); + +/** + * @brief RFW_GetPayloadTimerEvent TimerEvent to get the payload data, de-whitening, and crc verification + * + * @Note calls RFW_GetPayloadProcess directly inside IRQ (default) or in background with sequencer or OS + * @param [in] context context of the timer + */ +static void RFW_GetPayloadTimerEvent( void * context); + +/** + * @brief RFW_GetPayloadProcess process to get the payload data, de-whitening, and crc verification + */ +static void RFW_GetPayloadProcess( void ); + +#if (RFW_LONGPACKET_ENABLE ==1 ) +/** + * @brief RFW_TransmitLongPacket_NewTxChunkTimer: long packet Tx timer callback to process intermediate chunk of TxData + * @Note calls RFW_TransmitLongPacket_TxChunkProcess directly inside IRQ (default) or in background with sequencer or any Rtos + * + * @param [in] param unused + */ +static void RFW_TransmitLongPacket_NewTxChunkTimerEvent( void * param ); + +/** + * @brief RFW_TransmitLongPacket_TxChunkProcess: tx long packet process intermediate chunk of TxData + */ +static void RFW_TransmitLongPacket_TxChunkProcess( void ); +#endif + +/** + * @brief RFW_GetPayload get the payload of + * + * @param [in] Offset Offset of the + * @param [in] Length carrier frequency offset in Hertz + */ +static void RFW_GetPayload( uint8_t Offset, uint8_t Length ); +#endif + +/* Exported functions --------------------------------------------------------*/ +int32_t RFW_TransmitLongPacket( uint16_t payload_size, uint32_t timeout, void (*TxLongPacketGetNextChunkCb) (uint8_t** buffer, uint8_t buffer_size) ) +{ + int32_t status=0; +#if (RFW_LONGPACKET_ENABLE ==1 ) + uint32_t total_size= payload_size+ RFWPacket.Init.PayloadLengthFieldSize+ RFWPacket.Init.CrcFieldSize; + + RFW_MW_LOG( TS_ON, VLEVEL_M, "RevID=%04X\r\n", LL_DBGMCU_GetRevisionID() ); + + if ((TxLongPacketGetNextChunkCb==NULL)|| + (payload_size> (1<<(8*RFWPacket.Init.PayloadLengthFieldSize))-1) ||/*check that size fits inside the packetLengthField*/ + (RFWPacket.Init.Enable==0) ||/* Can only be used when after RadioSetTxGenericConfig*/ + (LL_DBGMCU_GetRevisionID() < 0x1003 ) ) /* Only available from stm32wl revision Y*/ + { + status= -1; + } + else + { + /*chunk buffer pointer fed by the application*/ + uint8_t* app_chunk_buffer_ptr=NULL; + /*size of the chunk to be sent*/ + uint8_t chunk_size; + uint8_t crc_size; + /*timeout for next chunk*/ + uint32_t chunk_timeout; + /*Records call back*/ + RFWPacket.TxLongPacketGetNextChunkCb=TxLongPacketGetNextChunkCb; + + /* 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 ); + + /* ST_WORKAROUND_BEGIN : Set the debug pin and update the radio switch */ + /* Set DBG pin */ + DBG_GPIO_RADIO_TX(SET); + /* Set RF switch */ + SUBGRF_SetSwitch(RFWPacket.AntSwitchPaSelect, RFSWITCH_TX); + /* ST_WORKAROUND_END */ + + switch(RFWPacket.Init.Modem) + { + case MODEM_FSK: + case MODEM_MSK: + { + if (RFWPacket.Init.Enable==1) + { + /*crc will be calculated on the fly along with packet chunk transmission*/ + uint8_t crc_result[2]; + /*init radio buffer offset*/ + RFWPacket.RadioBufferOffset=0; + /*long packet mode enable*/ + RFWPacket.LongPacketModeEnable=1; + /*Remaining bytes to transmit*/ + RFWPacket.LongPacketRemainingBytes=total_size; + /*Records total payload bytes to transmit*/ + RFWPacket.PayloadLength=total_size; + if (total_size> RADIO_BUF_SIZE) + { + /*cut in chunk*/ + if (total_size>8); + ChunkBuffer[1]=(uint8_t) ((payload_size)&0xFF); + } + /* Get Tx chunk from app*/ + TxLongPacketGetNextChunkCb(&app_chunk_buffer_ptr, chunk_size); + + /* Copy first chunk in ChunkBuffer Buffer*/ + RADIO_MEMCPY8(&ChunkBuffer[RFWPacket.Init.PayloadLengthFieldSize], app_chunk_buffer_ptr, chunk_size); + + if (RFWPacket.Init.CrcEnable== 1) + { + /* Set the state of the Crc to crc_seed*/ + RFW_CrcSetState(&RFWPacket); + /* Run the crc calculation on payload length and payload*/ + RFW_CrcRun(&RFWPacket, ChunkBuffer, RFWPacket.Init.PayloadLengthFieldSize+ chunk_size, crc_result); + /* Append the crc result after the payload if total_size<= RADIO_BUF_SIZE*/ + RADIO_MEMCPY8(&ChunkBuffer[RFWPacket.Init.PayloadLengthFieldSize+ chunk_size], crc_result, crc_size); + } + /* Init whitening at beginning of the packet*/ + RFW_WhiteSetState(&RFWPacket); + /* Run the whitening calculation on payload length, payload and crc if crc fits inside 1st chunk*/ + RFW_WhiteRun(&RFWPacket, &ChunkBuffer[0],RFWPacket.Init.PayloadLengthFieldSize+ chunk_size+ crc_size); + /* Configure the Transmitter to send all*/ + /* Init radio buffer */ + SUBGRF_WriteRegister(SUBGHZ_GRTXPLDLEN,RFWPacket.Init.PayloadLengthFieldSize+ chunk_size+ crc_size); + SUBGRF_WriteRegister(SUBGHZ_TXADRPTR,0); + /* Send*/ + SUBGRF_SendPayload( ChunkBuffer, RFWPacket.Init.PayloadLengthFieldSize+ chunk_size+ crc_size, 0 ); + if (total_size> RADIO_BUF_SIZE) + { + /*in case total size is greater than RADIO_BUF_SIZE, need to program a timer to get next chunk*/ + //RFWPacket.LongPacketRemainingBytes-= RFWPacket.Init.PayloadLengthFieldSize+ chunk_size+ crc_size; + /*Initialize Timer to get new chunk and update radio ptr*/ + chunk_timeout = ( LONGPACKET_CHUNK_LENGTH_BYTES* 8 * 1000)/RFWPacket.BitRate; + RFW_MW_LOG( TS_ON, VLEVEL_M, "Timeout=%d,\r\n", chunk_timeout); + TimerInit( &RFWPacket.Timer, RFW_TransmitLongPacket_NewTxChunkTimerEvent ); + TimerSetValue( &RFWPacket.Timer, chunk_timeout ); + TimerStart( &RFWPacket.Timer); + /*Write bit infinite_sequence = 1, required for long packet*/ + uint8_t reg = SUBGRF_ReadRegister(SUBGHZ_GPKTCTL1AR); + SUBGRF_WriteRegister(SUBGHZ_GPKTCTL1AR, reg | 0x02); + + TimerSetValue( RFWPacket.RxTimeoutTimer, timeout ); + TimerStart( RFWPacket.RxTimeoutTimer); + } + } + else + { + /* error*/ + status= -1; + } + break; + } + case MODEM_LORA: + { + /* not supported by the radio Ip*/ + status= -2; + break; + } + case MODEM_BPSK: + { + /* not supported by the FW*/ + status= -2; + break; + } + case MODEM_SIGFOX_TX: + { + /* not supported by the FW*/ + status= -2; + break; + } + default: + break; + } + } +#else + status= -1; +#endif + return status; +} + +int32_t RFW_ReceiveLongPacket( uint8_t boosted_mode, uint32_t timeout, void (*RxLongPacketStoreChunkCb) (uint8_t* buffer, uint8_t chunk_size) ) +{ + int32_t status=0; +#if (RFW_LONGPACKET_ENABLE ==1 ) + if ((RxLongPacketStoreChunkCb==NULL) || + (RFWPacket.Init.Enable==0)) /* Can only be used when after RadioSetRxGenericConfig*/ + { + status= -1; + } + else + { + /*Records call back*/ + RFWPacket.RxLongPacketStoreChunkCb=RxLongPacketStoreChunkCb; + SUBGRF_SetDioIrqParams( IRQ_SYNCWORD_VALID | IRQ_RX_TX_TIMEOUT, + IRQ_SYNCWORD_VALID | IRQ_RX_TX_TIMEOUT, + IRQ_RADIO_NONE, + IRQ_RADIO_NONE ); + SUBGRF_SetSwitch(RFWPacket.AntSwitchPaSelect, RFSWITCH_RX); + /*init radio buffer offset*/ + RFWPacket.RadioBufferOffset=0; + /* Init whitening at beginning of the packet*/ + RFW_WhiteSetState(&RFWPacket); + /* Set the state of the Crc to crc_seed*/ + RFW_CrcSetState(&RFWPacket); + /* Init radio buffer */ + SUBGRF_WriteRegister(SUBGHZ_GRTXPLDLEN,255); + SUBGRF_WriteRegister(SUBGHZ_RXADRPTR,0); + /*enable long packet*/ + RFWPacket.LongPacketModeEnable=1; + + if( timeout != 0 ) + { + TimerSetValue( RFWPacket.RxTimeoutTimer, timeout ); + TimerStart( RFWPacket.RxTimeoutTimer ); + } + DBG_GPIO_RADIO_RX(SET); + if (boosted_mode ==1) + { + SUBGRF_SetRxBoosted( 0xFFFFFF ); // Rx Continuous + } + else + { + SUBGRF_SetRx( 0xFFFFFF ); // Rx Continuous + } + } +#else + status= -1; +#endif + return status; +} + +int32_t RFW_Init( ConfigGeneric_t* config, RadioEvents_t* RadioEvents, TimerEvent_t* TimeoutTimerEvent) +{ +#if (RFW_ENABLE ==1 ) + RADIO_FSK_PacketLengthModes_t HeaderType; + uint32_t RxMaxPayloadLength= 0; + RADIO_FSK_CrcTypes_t CrcLength; + uint16_t whiteSeed; + uint16_t CrcPolynomial; + uint16_t CrcSeed; + if (config->rtx ==CONFIG_TX) + { + HeaderType= config->TxConfig->fsk.HeaderType; + CrcLength= config->TxConfig->fsk.CrcLength; + whiteSeed= config->TxConfig->fsk.whiteSeed; + CrcPolynomial= config->TxConfig->fsk.CrcPolynomial; + CrcSeed= config->TxConfig->fsk.CrcSeed; + RFWPacket.BitRate= config->TxConfig->fsk.BitRate; + RFWPacket.TxTimeoutTimer= TimeoutTimerEvent; + } + else + { + HeaderType= config->RxConfig->fsk.LengthMode; + CrcLength= config->RxConfig->fsk.CrcLength; + RxMaxPayloadLength = config->RxConfig->fsk.MaxPayloadLength; + whiteSeed= config->RxConfig->fsk.whiteSeed; + CrcPolynomial= config->RxConfig->fsk.CrcPolynomial; + CrcSeed= config->RxConfig->fsk.CrcSeed; + RFWPacket.BitRate= config->RxConfig->fsk.BitRate; + RFWPacket.RxTimeoutTimer= TimeoutTimerEvent; + } + if( ( RadioEvents != NULL ) && ( RadioEvents->RxError ) ) + { + RFWPacket.Init.RadioEvents=RadioEvents; + } + else + { + return -1; + } + if (HeaderType==RADIO_FSK_PACKET_2BYTES_LENGTH) + { +#if (RFW_LONGPACKET_ENABLE ==1 ) + RFWPacket.Init.PayloadLengthFieldSize= 2; +#else + return -1; +#endif + } + else + { + RFWPacket.Init.PayloadLengthFieldSize= 1; + } + /*record, used to reject packet in length decoded at sync time out greater than LongPacketMaxRxLength*/ + RFWPacket.Init.LongPacketMaxRxLength= RxMaxPayloadLength; + if (CrcLength== RADIO_FSK_CRC_OFF) + { + RFWPacket.Init.CrcEnable =0; + RFWPacket.Init.CrcFieldSize= 0; + } + else + { + RFWPacket.Init.CrcEnable =1; + RFWPacket.Init.CrcFieldSize= 2; + } + /*Macro can be used to init interrupt behaviour*/ + RFW_IT_INIT(); + /*Initialise whitening Seed*/ + RFW_WhiteInitState(&RFWPacket.Init, whiteSeed); + /*Initialise Crc Seed*/ + RFW_CrcInitState(&RFWPacket.Init, CrcPolynomial, CrcSeed, CrcLength); + /*Enable the RFWPacket decoding*/ + RFWPacket.Init.Enable=1; + /* Initialize Timer for end of fixed packet, started at sync*/ + TimerInit( &RFWPacket.Timer, RFW_GetPayloadTimerEvent ); + return 0; +#else + return -1; +#endif +} + +void RFW_DeInit( void) +{ +#if (RFW_ENABLE ==1 ) + RFWPacket.Init.Enable=0; /*Disable the RFWPacket decoding*/ +#endif +} + +uint8_t RFW_Is_Init( void) +{ +#if (RFW_ENABLE ==1 ) + return RFWPacket.Init.Enable; +#else + return 0; +#endif +} + +uint8_t RFW_Is_LongPacketModeEnabled( void) +{ +#if (RFW_ENABLE ==1 ) + return RFWPacket.LongPacketModeEnable; +#else + return 0; +#endif +} + +void RFW_SetAntSwitch( uint8_t AntSwitch) +{ +#if (RFW_ENABLE ==1 ) + RFWPacket.AntSwitchPaSelect=AntSwitch; +#endif +} + +int32_t RFW_TransmitInit(uint8_t* inOutBuffer, uint8_t size, uint8_t* outSize) +{ + int32_t status=-1; +#if (RFW_ENABLE ==1 ) + uint8_t crc_result[2]; + if (size+RFWPacket.Init.PayloadLengthFieldSize+ RFWPacket.Init.CrcFieldSize> RADIO_BUF_SIZE) + { + RFW_MW_LOG( TS_ON, VLEVEL_M, "RadioSend Oversize\r\n"); + status= -1; + } + else + { + /* Copy tx buffer in payload*/ + RADIO_MEMCPY8(&ChunkBuffer[RFWPacket.Init.PayloadLengthFieldSize], inOutBuffer, size); + /* Calculate the crc on */ + /* Payload Size without the packet length field nor the CRC */ + /* Prepend payload size before Payload*/ + if (RFWPacket.Init.PayloadLengthFieldSize==1) + { + ChunkBuffer[0]=size; + } + else + { + ChunkBuffer[0]=0; + ChunkBuffer[1]=size; + } + if (RFWPacket.Init.CrcEnable== 1) + { + /* Set the state of the Crc to crc_seed*/ + RFW_CrcSetState(&RFWPacket); + /*Run the crc calculation on payload length and payload*/ + RFW_CrcRun(&RFWPacket, &ChunkBuffer[0],size+ RFWPacket.Init.PayloadLengthFieldSize, crc_result); + /*append the crc result afater the payload*/ + RADIO_MEMCPY8(&ChunkBuffer[size+ RFWPacket.Init.PayloadLengthFieldSize], crc_result, RFWPacket.Init.CrcFieldSize); + } + /*init whitening at beginning of the packet*/ + RFW_WhiteSetState(&RFWPacket); + /*Run the whitening calculation on payload length, payload and crc*/ + RFW_WhiteRun(&RFWPacket, &ChunkBuffer[0],size+ RFWPacket.Init.PayloadLengthFieldSize+ RFWPacket.Init.CrcFieldSize); + /*Configure the Transmitter to send all*/ + *outSize=(uint8_t) size+ RFWPacket.Init.PayloadLengthFieldSize+ RFWPacket.Init.CrcFieldSize; + /*copy result*/ + RADIO_MEMCPY8(inOutBuffer, ChunkBuffer, *outSize); + + RFWPacket.LongPacketModeEnable=0; + + status= 0; + } +#endif + return status; +} + +int32_t RFW_ReceiveInit( void ) +{ +#if (RFW_ENABLE ==1 ) + /* Radio IRQ is set to DIO1 by default */ + SUBGRF_SetDioIrqParams( IRQ_RADIO_ALL&(~IRQ_RX_DONE), //IRQ_RX_DONE | IRQ_RX_TX_TIMEOUT, + IRQ_RADIO_ALL&(~IRQ_RX_DONE), //IRQ_RX_DONE | IRQ_RX_TX_TIMEOUT, + IRQ_RADIO_NONE, + IRQ_RADIO_NONE ); +/*init whitening at beginning of the packet*/ + RFW_WhiteSetState(&RFWPacket); +/* Set the state of the Crc to crc_seed*/ + RFW_CrcSetState(&RFWPacket); + + RFWPacket.RxPayloadOffset=0; + + RFWPacket.LongPacketModeEnable=0; + return 0; +#else + return -1; +#endif +} + +void RFW_DeInit_TxLongPacket(void) +{ +#if (RFW_LONGPACKET_ENABLE ==1 ) + /*long packet WA*/ + uint8_t reg = SUBGRF_ReadRegister(SUBGHZ_GPKTCTL1AR); + SUBGRF_WriteRegister(SUBGHZ_GPKTCTL1AR, reg & ~0x02);//clear infinite_sequence bit + SUBGRF_WriteRegister(SUBGHZ_GRTXPLDLEN, 0xFF); //RxTxPldLen: reset to 0xFF +#endif +} + +void RFW_ReceivePayload( void ) +{ +#if (RFW_ENABLE ==1 ) + uint16_t PayloadLength= 0; + if (RFW_GetPacketLength(&PayloadLength)==0) + { + uint32_t timeout; + uint32_t packet_length= PayloadLength+RFWPacket.Init.CrcFieldSize; + /*record payload length*/ + RFWPacket.PayloadLength= PayloadLength; + /*record remaining payload length*/ + RFWPacket.LongPacketRemainingBytes = (uint16_t) packet_length; + /*record rx buffer offset*/ + RFWPacket.RadioBufferOffset = RFWPacket.Init.PayloadLengthFieldSize; + /*if decoded PayloadLength is longer than LongPacketMaxRxLength, reject packet*/ + if (PayloadLength>RFWPacket.Init.LongPacketMaxRxLength) + { + SUBGRF_SetStandby( STDBY_RC ); + RFWPacket.Init.RadioEvents->RxError( ); + return; + } + if (packet_lengthRxTimeout( ); + } +#endif +} + +void RFW_SetRadioModem(RadioModems_t Modem) +{ +#if (RFW_ENABLE ==1 ) + RFWPacket.Init.Modem= Modem; +#endif +} + +/* Private Functions Definition -----------------------------------------------*/ +#if (RFW_LONGPACKET_ENABLE ==1 ) +static void RFW_TransmitLongPacket_NewTxChunkTimerEvent( void * param ) +{ + RFW_TRANSMIT_LONGPACKET_TX_CHUNK_PROCESS(); +} + +static void RFW_TransmitLongPacket_TxChunkProcess( void ) +{ + uint8_t* app_chunk_buffer_ptr=NULL; + uint8_t chunk_size=0; + uint8_t crc_result[2]={0}; + uint8_t crc_size; + uint32_t timeout;/*timeout for next chunk*/ + /*records how much has been sent*/ + uint8_t read_ptr= SUBGRF_ReadRegister(SUBGHZ_TXADRPTR); /*radio has transmitted up to read_ptr*/ + uint8_t write_ptr= SUBGRF_ReadRegister(SUBGHZ_GRTXPLDLEN); /*from read_ptr to write_ptr still to be transmitted*/ + /*calculates how much bytes were sent since previous radio loading*/ + uint8_t bytes_sent =read_ptr-RFWPacket.RadioBufferOffset; + /*bytes already loaded in the radio to send*/ + uint8_t bytes_loaded= write_ptr-read_ptr; + + /* Update offset tx, intentional wrap around*/ + RFWPacket.RadioBufferOffset+= bytes_sent; + /*record payload remaining bytes to send*/ + RFWPacket.LongPacketRemainingBytes-= bytes_sent; + RFW_MW_LOG( TS_ON, VLEVEL_M, "read_ptr=%d, write_ptr=%d, bytes_sent=%d, bytes_loaded=%d,remaining to send=%d\r\n", + read_ptr, write_ptr,bytes_sent, bytes_loaded,RFWPacket.LongPacketRemainingBytes); + if ( RFWPacket.LongPacketRemainingBytes> 256) + { + /*get next chunk */ + /*make sure that at least full CrcFieldSize will be loaded for the last chunk*/ + if ( RFWPacket.LongPacketRemainingBytes>256+RFWPacket.Init.CrcFieldSize) + { + chunk_size = bytes_sent; + } + else + { + chunk_size = bytes_sent-RFWPacket.Init.CrcFieldSize; + } + /*no crc since it is not the last chunk*/ + crc_size= 0; + /*calculate timeout for next chunk*/ + timeout = DIVR( chunk_size* 8 * 1000 , RFWPacket.BitRate); + + TimerSetValue( &RFWPacket.Timer, timeout ); + TimerStart( &RFWPacket.Timer); + } + else + { + /*last chunk to send*/ + + if (RFWPacket.LongPacketRemainingBytes>bytes_loaded) + { + chunk_size = RFWPacket.LongPacketRemainingBytes-bytes_loaded; + } + else/* nothing to load anymore*/ + { + chunk_size =RFWPacket.Init.CrcFieldSize; + } + /* crc, since it is the last chunk*/ + crc_size= RFWPacket.Init.CrcFieldSize; + /*no more bytes to send*/ + RFWPacket.LongPacketRemainingBytes= 0; + /*no need to program another timer, Tx done will complete the Tx process*/ + } + /*get new chunk from the app*/ + RFWPacket.TxLongPacketGetNextChunkCb( &app_chunk_buffer_ptr, chunk_size-crc_size); + /* Copy app_chunk_buffer_ptr in ChunkBuffer Buffer*/ + RADIO_MEMCPY8(ChunkBuffer, app_chunk_buffer_ptr, chunk_size-crc_size); + if (RFWPacket.Init.CrcEnable== 1) + { + /* Run the crc calculation on payload length and payload*/ + RFW_CrcRun(&RFWPacket, ChunkBuffer, chunk_size-crc_size, crc_result); + /* Append the crc result after the payload (if last chunk)*/ + RADIO_MEMCPY8(&ChunkBuffer[chunk_size-crc_size], crc_result, crc_size); + } + /* Run the whitening calculation on payload length, payload and crc*/ + RFW_WhiteRun(&RFWPacket, ChunkBuffer, chunk_size); + /*write next chunk*/ + SUBGRF_WriteBuffer( write_ptr, ChunkBuffer, chunk_size ); + + /*update end ptr*/ + SUBGRF_WriteRegister(SUBGHZ_GRTXPLDLEN, (uint8_t)(chunk_size + write_ptr)); + + RFW_MW_LOG( TS_ON, VLEVEL_M, "next chunk size=%d, new write ptr=%d\n\r",chunk_size+ crc_size, (uint8_t)(chunk_size+ crc_size + write_ptr)); +} +#endif + +#if (RFW_ENABLE ==1 ) +static void RFW_WhiteInitState(RFwInit_t* Init, uint16_t WhiteSeed) +{ + Init->WhiteSeed= WhiteSeed; +} + +static void RFW_WhiteSetState(RadioFw_t* RFWPacket) +{ + RFWPacket->WhiteLfsrState= RFWPacket->Init.WhiteSeed; +} + +static void RFW_CrcInitState(RFwInit_t* Init, const uint16_t CrcPolynomial, const uint16_t CrcSeed, const RADIO_FSK_CrcTypes_t CrcType) +{ + Init->CrcPolynomial= CrcPolynomial; + Init->CrcSeed= CrcSeed; + Init->CrcType= CrcType; +} + +static void RFW_CrcSetState(RadioFw_t* RFWPacket) +{ + RFWPacket->CrcLfsrState= RFWPacket->Init.CrcSeed; +} + +static void RFW_WhiteRun(RadioFw_t* RFWPacket, uint8_t* Payload, uint32_t Size) +{ + /*run the whitening algo on Size bytes*/ + uint16_t ibmwhite_state= RFWPacket->WhiteLfsrState; + for(int32_t i=0;i>5)&0x1)^((ibmwhite_state>>0)&0x1); + ibmwhite_state= ((msb<<8) | (ibmwhite_state>>1) ); + } + } + RFWPacket->WhiteLfsrState=ibmwhite_state; +} + +static int32_t RFW_CrcRun(RadioFw_t* const RFWPacket, const uint8_t* Payload, const uint32_t Size, uint8_t CrcResult[2]) +{ + int32_t status=0; + int32_t i = 0; + uint16_t polynomial = RFWPacket->Init.CrcPolynomial; + /* Restore state from previous chunk*/ + uint16_t crc = RFWPacket->CrcLfsrState; + for( i = 0; i < Size; i++ ) + { + crc = RFW_CrcRun1Byte( crc, Payload[i], polynomial ); + } + /*Save state for next chunk*/ + RFWPacket->CrcLfsrState=crc; + + if( RFWPacket->Init.CrcType == RADIO_FSK_CRC_2_BYTES_IBM ) + { + CrcResult[1]=crc&0xFF; + CrcResult[0]=crc>>8; + } + else + { + crc= ~crc ; + CrcResult[1]=crc&0xFF; + CrcResult[0]=crc>>8; + } + return status; +} + +uint16_t RFW_CrcRun1Byte( uint16_t Crc, uint8_t DataByte, uint16_t Polynomial) +{ + uint8_t i; + for( i = 0; i < 8; i++ ) + { + if( ( ( ( Crc & 0x8000 ) >> 8 ) ^ ( DataByte & 0x80 ) ) != 0 ) + { + Crc <<= 1; + Crc ^= Polynomial; + } + else + { + Crc <<= 1; + } + DataByte <<= 1; + } + return Crc; +} + +static int32_t RFW_PollRxBytes(uint32_t bytes) +{ + uint32_t now = TimerGetCurrentTime( ); + uint8_t reg_buff_ptr_ref= SUBGRF_ReadRegister(SUBGHZ_RXADRPTR); + uint8_t reg_buff_ptr=reg_buff_ptr_ref; + uint32_t timeout = DIVC( bytes* 8 * 1000 , RFWPacket.BitRate); + // Wait that packet length is received + while( (reg_buff_ptr-reg_buff_ptr_ref) < bytes) + { + /*reading rx address pointer*/ + reg_buff_ptr=SUBGRF_ReadRegister(SUBGHZ_RXADRPTR); + if( TimerGetElapsedTime( now ) > timeout) + { + /*timeout*/ + return -1; + } + } + return 0; +} + +static int32_t RFW_GetPacketLength(uint16_t* PayloadLength) +{ + if ( 0UL!= RFW_PollRxBytes(RFWPacket.Init.PayloadLengthFieldSize)) + { + return -1; + } + /* Get buffer from Radio*/ + SUBGRF_ReadBuffer( 0, ChunkBuffer, RFWPacket.Init.PayloadLengthFieldSize ); + /* De-whiten packet length*/ + RFW_WhiteRun(&RFWPacket, ChunkBuffer ,RFWPacket.Init.PayloadLengthFieldSize); + /*do crc 1st calculation packetLengthField and store intermediate result */ + if (RFWPacket.Init.CrcEnable== 1) + { + /*run Crc algo on payloadLengthField*/ + uint8_t crc_dummy[2]; + RFW_CrcRun(&RFWPacket, ChunkBuffer, RFWPacket.Init.PayloadLengthFieldSize, crc_dummy); + } + if (RFWPacket.Init.PayloadLengthFieldSize==1) + { + *PayloadLength= (uint16_t) ChunkBuffer[0]; + } + else + { + /*packet length is 2 bytes*/ + *PayloadLength= (((uint16_t) ChunkBuffer[0])<<8) |ChunkBuffer[1]; + } + RFW_MW_LOG( TS_ON, VLEVEL_M, "PayloadLength=%d,\r\n", *PayloadLength); + return 0; +} + +static void RFW_GetPayloadTimerEvent( void * context) +{ + RFW_GET_PAYLOAD_PROCESS(); +} + +static void RFW_GetPayloadProcess( void ) +{ + /*long packet mode*/ + uint8_t read_ptr= SUBGRF_ReadRegister(SUBGHZ_RXADRPTR); + uint8_t size=read_ptr-RFWPacket.RadioBufferOffset; + uint32_t Timeout; + /*check remaining size*/ + if (RFWPacket.LongPacketRemainingBytes>size) + { + /* update LongPacketRemainingBytes*/ + RFWPacket.LongPacketRemainingBytes-=size; + /*intermediate chunk*/ + RFW_MW_LOG( TS_ON, VLEVEL_M, "RxTxPldLen=0x%02X,\r\n",SUBGRF_ReadRegister(SUBGHZ_GRTXPLDLEN)); + RFW_MW_LOG( TS_ON, VLEVEL_M, "RxAddrPtr=0x%02X,\r\n",read_ptr); + RFW_MW_LOG( TS_ON, VLEVEL_M, "offset= %d, size=%d, remaining=%d,\r\n",RFWPacket.RadioBufferOffset, size, RFWPacket.LongPacketRemainingBytes); + /*update pld length so that not reached*/ + SUBGRF_WriteRegister(SUBGHZ_GRTXPLDLEN, read_ptr-1); + /* read data from radio*/ + SUBGRF_ReadBuffer( RFWPacket.RadioBufferOffset, ChunkBuffer, size ); + /* update buffer Offset, with intentional wrap around*/ + RFWPacket.RadioBufferOffset+=size; + /*Run the de-whitening on current chunk*/ + RFW_WhiteRun(&RFWPacket, ChunkBuffer, size); + if (RFWPacket.Init.CrcEnable== 1) + { + /*run Crc algo on partial chunk*/ + uint8_t crc_dummy[2]; + RFW_CrcRun(&RFWPacket, ChunkBuffer, size, crc_dummy); + } + + if (RFWPacket.LongPacketModeEnable ==1) + { + /*report rx data chunk to application*/ + RFWPacket.RxLongPacketStoreChunkCb( ChunkBuffer, size); + } + else + { + if (RFWPacket.RxPayloadOffset+=sizeRxError( ); + return; + } + } + /*calculate next timer timeout*/ + if (RFWPacket.LongPacketRemainingBytesRxError( ); + return; + } + } + TimerStop( RFWPacket.RxTimeoutTimer ); + /* CRC check*/ + RFW_MW_LOG( TS_ON, VLEVEL_M, "crc_result= 0x%02X%02X, crc_payload=0x%02X%02X\r\n",crc_result[0],crc_result[1],ChunkBuffer[Length-2],ChunkBuffer[Length-1]); + if (((crc_result[0]==ChunkBuffer[Length-2]) && + (crc_result[1]==ChunkBuffer[Length-1])) || + (RFWPacket.Init.CrcEnable== 0)) + { + /*read Rssi sampled at Sync*/ + uint8_t rssi_sync = SUBGRF_ReadRegister(0x06CA); + /* Get Carrier Frequency Offset*/ + int32_t cfo; + SUBGRF_GetCFO(RFWPacket.BitRate, &cfo); + /*ChunkBuffer[1] to remove packet Length*/ + RFWPacket.Init.RadioEvents->RxDone( RxBuffer, + RFWPacket.RxPayloadOffset, + -(rssi_sync>>1), + (int8_t) DIVR(cfo,1000)); + } + else + { + /*report CRC error*/ + RFWPacket.Init.RadioEvents->RxError( ); + } + DBG_GPIO_RADIO_RX(RST); +} +#endif /* RFW_ENABLE */ diff --git a/src/STM32CubeWL/SubGHz_Phy/stm32_radio_driver/radio_fw.h b/src/STM32CubeWL/SubGHz_Phy/stm32_radio_driver/radio_fw.h new file mode 100644 index 0000000..86a0a9b --- /dev/null +++ b/src/STM32CubeWL/SubGHz_Phy/stm32_radio_driver/radio_fw.h @@ -0,0 +1,150 @@ +/** + ****************************************************************************** + * @file radio_fw.h + * @author MCD Application Team + * @brief Extends radio capabilities (whitening, long packet) + ****************************************************************************** + * @attention + * + * Copyright (c) 2020(-2021) STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __RADIO_FW_H__ +#define __RADIO_FW_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +/* Exported types ------------------------------------------------------------*/ + +/*reserved for SubGHz_Phy internal MW communication*/ +typedef enum +{ + CONFIG_RX = 0, + CONFIG_TX, +}ConfigGenericRTx_t; + +typedef struct{ + TxConfigGeneric_t* TxConfig; + RxConfigGeneric_t* RxConfig; + ConfigGenericRTx_t rtx; +} ConfigGeneric_t; + +/* Exported constants --------------------------------------------------------*/ +/* External variables --------------------------------------------------------*/ +/* Exported macro ------------------------------------------------------------*/ +/* Exported functions prototypes ---------------------------------------------*/ + +/*! + * @brief Initialise the RFW module and enables custom whithing, optionally long packet feature + * + * @param [in] config rx or tx config from the application + * @param [in] RadioEvents from the radio + * @param [in] TimeoutTimerEvent Timer for Rx or Tx timeout event + * @return 0 when no parameters error, -1 otherwise + */ +int32_t RFW_Init( ConfigGeneric_t* config, RadioEvents_t* RadioEvents, TimerEvent_t* TimeoutTimerEvent); + +/*! + * @brief Return whether the RFW module is enabled + * + * @return 0 when not initialised, -1 otherwise + */ +uint8_t RFW_Is_Init( void); + +/*! + * @brief Return whether the RFW module long packet is enabled + * + * @return 0 when not initialised, -1 otherwise + */ +uint8_t RFW_Is_LongPacketModeEnabled( void); + +/*! + * @brief Return whether the RFW module long packet is enabled + * + * @param [in] Modem set in the radio + */ +void RFW_SetRadioModem(RadioModems_t Modem); + +/*! + * @brief DeInitialise the RFW module and enable custom whithing and optionally long packet feature + * + */ +void RFW_DeInit( void); + +/*! + * @brief DeInitialise the TxLongPacket + * + */ +void RFW_DeInit_TxLongPacket(void); + +/*! + * @brief Set antenna switch output to be used in Tx + * + * @param [in] AntSwitch RFO_LP or FRO_HP + * + */ +void RFW_SetAntSwitch( uint8_t AntSwitch); + +/*! + * @brief Initialise reception for IBM whitening case + * + * @return 0 when RFW_ENABLE exists, -1 otherwise + */ +int32_t RFW_ReceiveInit( void ); + +/*! + * @brief Initialise transmission for IBM whitening case + * + * @param [in,out] inOutBuffer pointer of exchange buffer to send or receive data + * @param [in] size input buffer size + * @param [out] outSize output buffer size + * + */ +int32_t RFW_TransmitInit(uint8_t* inOutBuffer, uint8_t size, uint8_t* outSize); + +/*! + * @brief Starts receiving payload. Called at Rx Sync IRQ + * + */ +void RFW_ReceivePayload(void ); + +/*! + * @brief Starts transmitting long Packet, note packet length may be on 1 bytes depending on config + * + * @param [in] payload_size total payload size to be sent + * @param [in] timeout Reception timeout [ms] + * @param [in] TxLongPacketGetNextChunkCb callback to be implemented on user side to feed partial chunk + * buffer: source buffer allocated by the app + * size: size in bytes to feed. User to implement the offset based on previous chunk request + * @return 0 when no parameters error, -1 otherwise + */ +int32_t RFW_TransmitLongPacket( uint16_t payload_size, uint32_t timeout, void (*TxLongPacketGetNextChunkCb) (uint8_t** buffer, uint8_t buffer_size) ); + +/*! + * @brief Starts receiving long Packet, packet maybe short + * + * @param [in] boosted_mode boosted_mode: 0 normal Rx, 1:improved sensitivity + * @param [in] timeout Reception timeout [ms] + * @param [in] RxLongStorePacketChunkCb callback to be implemented on user side to record partial chunk in the application + * buffer: source buffer allocated in the radio driver + * size: size in bytes to record + * @return 0 when no parameters error, -1 otherwise + */ +int32_t RFW_ReceiveLongPacket( uint8_t boosted_mode, uint32_t timeout, void (*RxLongStorePacketChunkCb) (uint8_t* buffer, uint8_t chunk_size) ); + +#ifdef __cplusplus +} +#endif + +#endif /*__RADIO_FW_H__*/ diff --git a/src/STM32CubeWL/SubGHz_Phy/stm32_radio_driver/subghz_phy_version.h b/src/STM32CubeWL/SubGHz_Phy/stm32_radio_driver/subghz_phy_version.h new file mode 100644 index 0000000..07713e3 --- /dev/null +++ b/src/STM32CubeWL/SubGHz_Phy/stm32_radio_driver/subghz_phy_version.h @@ -0,0 +1,53 @@ +/****************************************************************************** + * @file subghz_phy_version.h + * @author MCD Application Team + * @brief defines the radio driver version + ****************************************************************************** + * @attention + * + * Copyright (c) 2020(-2021) STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ + +#ifndef __SUBGHZ_PHY_VERSION_H__ +#define __SUBGHZ_PHY_VERSION_H__ + +#ifdef __cplusplus + extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ + +/* __SUBGHZ_PHY_TYPE: 0x01 STM32WL + 0x61 SX126X + 0x72 SX1272 + 0x76 SX1276 */ + +#define SUBGHZ_PHY_VERSION_MAIN (0x01U) /*!< [31:24] main version */ +#define SUBGHZ_PHY_VERSION_SUB1 (0x02U) /*!< [23:16] sub1 version */ +#define SUBGHZ_PHY_VERSION_SUB2 (0x00U) /*!< [15:8] sub2 version */ +#define SUBGHZ_PHY_TYPE (0x01U) /*!< [7:0] type version */ +#define SUBGHZ_PHY_VERSION ((SUBGHZ_PHY_VERSION_MAIN << 24) \ + |(SUBGHZ_PHY_VERSION_SUB1 << 16) \ + |(SUBGHZ_PHY_VERSION_SUB2 << 8) \ + |(SUBGHZ_PHY_TYPE)) + +/* Exported types ------------------------------------------------------------*/ +/* External variables --------------------------------------------------------*/ +/* Exported macros -----------------------------------------------------------*/ +/* Exported functions ------------------------------------------------------- */ + +#ifdef __cplusplus +} +#endif + +#endif /*__SUBGHZ_PHY_VERSION_H__*/ diff --git a/src/STM32CubeWL/Utilities/misc/LICENSE.txt b/src/STM32CubeWL/Utilities/misc/LICENSE.txt new file mode 100644 index 0000000..3edc4d1 --- /dev/null +++ b/src/STM32CubeWL/Utilities/misc/LICENSE.txt @@ -0,0 +1,6 @@ +This software component is provided to you as part of a software package and +applicable license terms are in the Package_license file. If you received this +software component outside of a package or without applicable license terms, +the terms of the BSD-3-Clause license shall apply. +You may obtain a copy of the BSD-3-Clause at: +https://opensource.org/licenses/BSD-3-Clause diff --git a/src/STM32CubeWL/Utilities/misc/Release_notes.html b/src/STM32CubeWL/Utilities/misc/Release_notes.html new file mode 100644 index 0000000..1d702dc --- /dev/null +++ b/src/STM32CubeWL/Utilities/misc/Release_notes.html @@ -0,0 +1,123 @@ + + + + + + + Release Notes for STM32 miscellaneous utilities + + + + + + +
+
+
+

Release Notes for

+

STM32 miscellaneous utilities

+

Copyright © 2021 STMicroelectronics
+

+ +
+

Purpose

+

The source code delivered is a utility to help managing miscellaneous utilities within an embedded application: memory, system time and vsnprintf.

+
+
+

Update History

+
+ +
+

Main Changes

+
    +
  • [WL] Licensing information should be updated AND missing header user section code should be added
  • +
+

Contents

+
    +
  • Update release note format
  • +
+

Known limitations

+

Development Toolchains and Compilers

+

Supported Devices and boards

+

backward compatibility

+

Dependencies

+
+
+
+ +
+

Main Changes

+
    +
  • release V1.0.2
  • +
+

Contents

+
    +
  • Update release note format
  • +
+

Known limitations

+

Development Toolchains and Compilers

+

Supported Devices and boards

+

backward compatibility

+

Dependencies

+
+
+
+ +
+

Main Changes

+
    +
  • release V1.0.1
  • +
+

Contents

+
    +
  • add empty line at the end of stm32_mem.h
  • +
+

Known limitations

+

Development Toolchains and Compilers

+

Supported Devices and boards

+

backward compatibility

+

Dependencies

+
+
+
+ +
+

Main Changes

+
    +
  • release V1.0.0
  • +
+

Contents

+
    +
  • First release
  • +
+

known limitations

+

Development Toolchains and Compilers

+

Supported Devices and boards

+

backward compatibility

+

Dependencies

+
+
+
+
+
+
+
+

For complete documentation on STM32WLxx, visit: www.st.com/stm32wl

+

This release note uses up to date web standards and, for this reason, should not be opened with Internet Explorer but preferably with popular browsers such as Google Chrome, Mozilla Firefox, Opera or Microsoft Edge.

+
+

Info

+
+
+
+ + diff --git a/src/STM32CubeWL/Utilities/misc/stm32_systime.c b/src/STM32CubeWL/Utilities/misc/stm32_systime.c new file mode 100644 index 0000000..3423889 --- /dev/null +++ b/src/STM32CubeWL/Utilities/misc/stm32_systime.c @@ -0,0 +1,496 @@ +/*! + * \file stm32_systime.c + * + * \brief System time functions implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2018 Semtech - STMicroelectronics + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author MCD Application Team ( STMicroelectronics International ) + */ +/** + ****************************************************************************** + * @file stm32_systime.c + * @author MCD Application Team + * @brief System time functions implementation + ****************************************************************************** + * @attention + * + * Copyright (c) 2019 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + +/* Includes ------------------------------------------------------------------*/ +#include +#include "stm32_systime.h" + +/** @addtogroup SYS_TIME + * @{ + */ + +/* Private defines -----------------------------------------------------------*/ +/** + * @defgroup SYS_TIME_private_defines SYS_TIME private defines + * @{ + */ + +/** + * @brief number of day in leap year up to the end of February + * + */ +#define END_OF_FEBRUARY_LEAP 60 //31+29 + +/** + * @brief number of day in leap year up to the end of july + * + */ +#define END_OF_JULY_LEAP 213 //31+29+... + +/** + * @brief number of day in normal year up to the end of February + * + */ +#define END_OF_FEBRUARY_NORM 59 //31+28 + +/** + * @brief number of day in normal year up to the end of july + * + */ +#define END_OF_JULY_NORM 212 //31+28+... + +/** + * @brief delta is referenced to Unix time + * @note UNIX time 0 = starts at 01:00:00, 01/01/1970 + * but calculation is easier from Monday 1st January 1968 + * + */ +#define CALC_REF_YEAR 68 + +/** + * @brief delta is referenced to Unix time + * @note UNIX time 0 = starts at 01:00:00, 01/01/1970 + * but calculation is easier from Monday 1st January 1968 + * + */ +#define CALC_REF_YEAR_TO_UNIX_REF_YEAR_COMPENSATION_IN_SECONDS ( ( TM_DAYS_IN_LEAP_YEAR + TM_DAYS_IN_YEAR ) * TM_SECONDS_IN_1DAY ) + +/** + * @brief month correction table of a normal year. To calculate the day number within a year + * @note error compensation is between 0 and 2 days. 2 bits per month + * + */ +#define DAYS_IN_MONTH_CORRECTION_NORM ( (uint32_t )0x99AAA0 ) + +/** + * @brief month correction table of a leap year. To calculate the day number within a year + * @note error compensation is between 0 and 2 days. 2 bits per month + * + */ +#define DAYS_IN_MONTH_CORRECTION_LEAP ( (uint32_t )0x445550 ) + +/** + * @brief find X/365.25 + * + */ +/* 365.25 = (366 + 365 + 365 + 365)/4 */ +#define DIV_365_25( X ) ( ( ( X ) * 91867 + 22750 ) >> 25 ) + +/** + * @brief find the nearest quotient of X/86400 (8640 number of seconds in one week) + * + */ +#define DIV_APPROX_86400( X ) ( ( ( X ) >> 18 ) + ( ( X ) >> 17 ) ) + +/** + * @brief find the nearest quotient of X/1000 + * + */ +#define DIV_APPROX_1000( X ) ( ( ( X ) >> 10 ) +( ( X ) >> 16 ) + ( ( X ) >> 17 ) ) + +/** + * @brief find the nearest quotient of X/60 + * + */ +#define DIV_APPROX_60( X ) ( ( ( X ) * 17476 ) >> 20 ) + +/** + * @brief find the nearest quotient of X/61 + * + */ +#define DIV_APPROX_61( X ) ( ( ( X ) * 68759 ) >> 22 ) + +/** + * @brief Calculates mod(x,7) + * + */ +#define MODULO_7( X ) ( ( X ) -( ( ( ( ( X ) + 1 ) * 299593 ) >> 21 ) * 7 ) ) + +/** + * @brief Calculates ceiling( X / N ) + * + */ +#define DIVC( X, N ) ( ( ( X ) + ( N ) -1 ) / ( N ) ) + +/** + * @brief Calculates ceiling( X / 4 ) + * + */ +#define DIVC_BY_4( X ) ( ( ( X ) + 3 ) >>2 ) + +/** + * @brief Calculates ceiling( X / 2 ) + * + */ +#define DIVC_BY_2( X ) ( ( ( X ) + 1 ) >> 1 ) +/** + * @} + */ + +/* Private constants -----------------------------------------------------------*/ +/** + * @defgroup SYSTIME_private_variable SYSTIME private constants + * @{ + */ +const char *WeekDayString[]={ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; +/** + * @} + */ + +/* Private function prototypes -----------------------------------------------*/ + +/** + * @defgroup SYSTIME_private_function_prototypes SYSTIME private function prototypes + * @{ + */ +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 ); +/** + * @} + */ + +/* Functions Definition ------------------------------------------------------*/ +/** + * @addtogroup SYSTIME_exported_function + * @{ + */ + +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 = UTIL_SYSTIMDriver.GetCalendarTime( ( uint16_t* )&calendarTime.SubSeconds ); + + // sysTime is UNIX epoch + DeltaTime = SysTimeSub( sysTime, calendarTime ); + + UTIL_SYSTIMDriver.BKUPWrite_Seconds( DeltaTime.Seconds ); + UTIL_SYSTIMDriver.BKUPWrite_SubSeconds( ( 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 = UTIL_SYSTIMDriver.GetCalendarTime( ( uint16_t* )&calendarTime.SubSeconds ); + + DeltaTime.SubSeconds = (int16_t)UTIL_SYSTIMDriver.BKUPRead_SubSeconds(); + DeltaTime.Seconds = UTIL_SYSTIMDriver.BKUPRead_Seconds(); + + sysTime = SysTimeAdd( DeltaTime, calendarTime ); + + return sysTime; +} + + +SysTime_t SysTimeGetMcuTime( void ) +{ + SysTime_t calendarTime = { .Seconds = 0, .SubSeconds = 0 }; + + calendarTime.Seconds = UTIL_SYSTIMDriver.GetCalendarTime( ( uint16_t* )&calendarTime.SubSeconds ); + + return calendarTime; +} + +uint32_t SysTimeToMs( SysTime_t sysTime ) +{ + SysTime_t DeltaTime; + DeltaTime.SubSeconds = (int16_t)UTIL_SYSTIMDriver.BKUPRead_SubSeconds(); + DeltaTime.Seconds = UTIL_SYSTIMDriver.BKUPRead_Seconds(); + + 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 }; + + DeltaTime.SubSeconds = (int16_t)UTIL_SYSTIMDriver.BKUPRead_SubSeconds(); + DeltaTime.Seconds = UTIL_SYSTIMDriver.BKUPRead_Seconds(); + return SysTimeAdd( sysTime, DeltaTime ); +} + +uint32_t SysTimeMkTime( const struct tm* localtime ) +{ + uint32_t nbdays; + uint32_t nbsecs; + uint32_t year = localtime->tm_year - CALC_REF_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 - CALC_REF_YEAR_TO_UNIX_REF_YEAR_COMPENSATION_IN_SECONDS; +} + +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 + CALC_REF_YEAR_TO_UNIX_REF_YEAR_COMPENSATION_IN_SECONDS, &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 += CALC_REF_YEAR; + + localtime->tm_isdst = -1; +} + +/** + * @} + */ + +/**************************** Private functions *******************************/ + +/** + * @addtogroup SYSTIME_private_function + * + * @{ + */ +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 +} +/** + * @} + */ + +/** + * @} + */ diff --git a/src/STM32CubeWL/Utilities/misc/stm32_systime.h b/src/STM32CubeWL/Utilities/misc/stm32_systime.h new file mode 100644 index 0000000..5e219d3 --- /dev/null +++ b/src/STM32CubeWL/Utilities/misc/stm32_systime.h @@ -0,0 +1,252 @@ +/*! + * \file stm32_systime.h + * + * \brief System time functions implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2018 Semtech - STMicroelectronics + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author MCD Application Team ( STMicroelectronics International ) + */ +/** + ****************************************************************************** + * @file stm32_systime.h + * @author MCD Application Team + * @brief System time functions implementation + ****************************************************************************** + * @attention + * + * Copyright (c) 2019 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32_SYS_TIME_H__ +#define __STM32_SYS_TIME_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** @defgroup SYSTIME timer server + * @{ + */ + +/* Includes ------------------------------------------------------------------*/ +#include +#include "time.h" + + +/* Exported constants --------------------------------------------------------*/ +/** @defgroup SYSTIME_exported_constants SYSTIME 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 epoch and GPS epoch +*/ +#define UNIX_GPS_EPOCH_OFFSET 315964800 + +/** + * @} + */ + +/* External Typedef --------------------------------------------------------*/ + +/** @defgroup SYSTIME_exported_TypeDef SYSTIME exported Typedef + * @{ + */ +/** + * @brief Structure holding the system time in seconds and milliseconds. + */ +typedef struct SysTime_s +{ +uint32_t Seconds; +int16_t SubSeconds; +}SysTime_t; + +/** + * @brief SysTime driver definition + */ +typedef struct +{ + void (*BKUPWrite_Seconds) ( uint32_t Seconds); /*!< Set the timer differencebetween real time and rtc time */ + uint32_t (*BKUPRead_Seconds) ( void ); /*!< Get the timer differencebetween real time and rtc time */ + void (*BKUPWrite_SubSeconds) ( uint32_t SubSeconds); /*!< Set the timer differencebetween real time and rtc time */ + uint32_t (*BKUPRead_SubSeconds) ( void ); /*!< Get the timer differencebetween real time and rtc time */ + uint32_t (*GetCalendarTime)( uint16_t* SubSeconds ); /*!< Set the rtc time */ +} UTIL_SYSTIM_Driver_s; + +/** + * @} + */ + +/* Exported macros -----------------------------------------------------------*/ +/* Exported variables ------------------------------------------------------------*/ + +/** @defgroup SYSTIME_exported_Variable SYSTIME exported Variable + * @{ + */ +/** + * @brief low layer interface to handle systim + * + * @remark This structure is defined and initialized in the specific platform + * timer implementation e.g rtc + */ +extern const UTIL_SYSTIM_Driver_s UTIL_SYSTIMDriver; +/** + * @} + */ + +/* Exported functions ------------------------------------------------------- */ +/** @defgroup SYSTIME_exported_function SYSTIME exported function + * @{ + */ + +/*! +* @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__ */ diff --git a/src/STM32CubeWL/Utilities/timer/LICENSE.txt b/src/STM32CubeWL/Utilities/timer/LICENSE.txt new file mode 100644 index 0000000..3edc4d1 --- /dev/null +++ b/src/STM32CubeWL/Utilities/timer/LICENSE.txt @@ -0,0 +1,6 @@ +This software component is provided to you as part of a software package and +applicable license terms are in the Package_license file. If you received this +software component outside of a package or without applicable license terms, +the terms of the BSD-3-Clause license shall apply. +You may obtain a copy of the BSD-3-Clause at: +https://opensource.org/licenses/BSD-3-Clause diff --git a/src/STM32CubeWL/Utilities/timer/Release_Notes.html b/src/STM32CubeWL/Utilities/timer/Release_Notes.html new file mode 100644 index 0000000..390c151 --- /dev/null +++ b/src/STM32CubeWL/Utilities/timer/Release_Notes.html @@ -0,0 +1,248 @@ + + + + + + + Release Notes for STM32 timer server + + + + + +
+
+
+
+
+

Release Notes for STM32 timer server

+

Copyright © 2019 STMicroelectronics
+

+ +
+
+
+

Purpose

+

The source code delivered is a utility to help managing timer services within an embedded application.

+
+
+

Update History

+
+ +
+

Main Changes

+

Contents

+ + + + + + + + + + + + +
Fixed bugs list
+
Headline
Ticket 115234 - [Licensing] Update the way to declare licenses in Cube and X-CUBE components
+

known limitations

+

Development Toolchains and Compilers

+
    +
  • IAR Embedded Workbench for ARM (EWARM) toolchain V8.20.2
  • +
  • RealView Microcontroller Development Kit (MDK-ARM) toolchain V5.25
  • +
  • System Workbench STM32 (SW4STM32) toolchain V2.7.2
  • +
+

Supported Devices and boards

+

backward compatibility

+

Dependencies

+
+
+
+ +
+

Main Changes

+

release V1.1.2

+

Contents

+ + + + + + + + + + + + +
Fixed bugs list
+
Headline
Ticket 86948 - CodeSpell : Correct some english words typo
+

known limitations

+

Development Toolchains and Compilers

+
    +
  • IAR Embedded Workbench for ARM (EWARM) toolchain V8.20.2
  • +
  • RealView Microcontroller Development Kit (MDK-ARM) toolchain V5.25
  • +
  • System Workbench STM32 (SW4STM32) toolchain V2.7.2
  • +
+

Supported Devices and boards

+

backward compatibility

+

Dependencies

+
+
+
+ +
+

Main Changes

+

release V1.1.1

+

Contents

+ + + + + + + + + + + + +
Fixed bugs list
+
Headline
Ticket 89624 - Utilities Releases Notes Improvements - Link on openorg BSD-3-Clause
+

known limitations

+

Development Toolchains and Compilers

+
    +
  • IAR Embedded Workbench for ARM (EWARM) toolchain V8.20.2
  • +
  • RealView Microcontroller Development Kit (MDK-ARM) toolchain V5.25
  • +
  • System Workbench STM32 (SW4STM32) toolchain V2.7.2
  • +
+

Supported Devices and boards

+

backward compatibility

+

Dependencies

+
+
+
+ +
+

Main Changes

+

release V1.1.0

+

Contents

+ + + + + + + + + + + + + + + + + + + + + +
Fixed bugs list
+
Headline
Ticket 86948 - CodeSpell : Correct some english words typo
Ticket 75887 - [UTILITIES] add TIMER_START_XXXXXX function with parameter Period + Type
MISRA update
Doxygen tag update
+

known limitations

+

Development Toolchains and Compilers

+
    +
  • IAR Embedded Workbench for ARM (EWARM) toolchain V8.20.2
  • +
  • RealView Microcontroller Development Kit (MDK-ARM) toolchain V5.25
  • +
  • System Workbench STM32 (SW4STM32) toolchain V2.7.2
  • +
+

Supported Devices and boards

+

backward compatibility

+

Dependencies

+
+
+
+ +
+

Main Changes

+

release V1.0.1

+

Contents

+ + + + + + + + + + + + +
Fixed bugs list
+
Headline
Release note correction
+

known limitations

+

Development Toolchains and Compilers

+
    +
  • IAR Embedded Workbench for ARM (EWARM) toolchain V8.20.2
  • +
  • RealView Microcontroller Development Kit (MDK-ARM) toolchain V5.25
  • +
  • System Workbench STM32 (SW4STM32) toolchain V2.7.2
  • +
+

Supported Devices and boards

+

backward compatibility

+

Dependencies

+
+
+
+ +
+

Main Changes

+

release V1.0.0

+

Contents

+ + + + + + + + + + + + +
Fixed bugs list
+
Headline
[Ticket 73263] insert default definition for the critical section
+

known limitations

+

Development Toolchains and Compilers

+
    +
  • IAR Embedded Workbench for ARM (EWARM) toolchain V8.20.2
  • +
  • RealView Microcontroller Development Kit (MDK-ARM) toolchain V5.25
  • +
  • System Workbench STM32 (SW4STM32) toolchain V2.7.2
  • +
+

Supported Devices and boards

+

backward compatibility

+

Dependencies

+
+
+
+
+

:::

+
+

For complete documentation on STM32,visit: [www.st.com/stm32]

+This release note uses up to date web standards and, for this reason, should not be opened with Internet Explorer but preferably with popular browsers such as Google Chrome, Mozilla Firefox, Opera or Microsoft Edge. +
+ + diff --git a/src/STM32CubeWL/Utilities/timer/_htmresc/mini-st.css b/src/STM32CubeWL/Utilities/timer/_htmresc/mini-st.css new file mode 100644 index 0000000..eb41d56 --- /dev/null +++ b/src/STM32CubeWL/Utilities/timer/_htmresc/mini-st.css @@ -0,0 +1,1700 @@ +@charset "UTF-8"; +/* + Flavor name: Default (mini-default) + Author: Angelos Chalaris (chalarangelo@gmail.com) + Maintainers: Angelos Chalaris + mini.css version: v3.0.0-alpha.3 +*/ +/* + Browsers resets and base typography. +*/ +/* Core module CSS variable definitions */ +:root { + --fore-color: #111; + --secondary-fore-color: #444; + --back-color: #f8f8f8; + --secondary-back-color: #f0f0f0; + --blockquote-color: #f57c00; + --pre-color: #1565c0; + --border-color: #aaa; + --secondary-border-color: #ddd; + --heading-ratio: 1.19; + --universal-margin: 0.5rem; + --universal-padding: 0.125rem; + --universal-border-radius: 0.125rem; + --a-link-color: #0277bd; + --a-visited-color: #01579b; } + +html { + font-size: 14px; } + +a, b, del, em, i, ins, q, span, strong, u { + font-size: 1em; } + +html, * { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, "Helvetica Neue", Helvetica, sans-serif; + line-height: 1.4; + -webkit-text-size-adjust: 100%; } + +* { + font-size: 1rem; } + +body { + margin: 0; + color: var(--fore-color); + background: var(--back-color); } + +details { + display: block; } + +summary { + display: list-item; } + +abbr[title] { + border-bottom: none; + text-decoration: underline dotted; } + +input { + overflow: visible; } + +img { + max-width: 100%; + height: auto; } + +h1, h2, h3, h4, h5, h6 { + line-height: 1.2; + margin: calc(1.5 * var(--universal-margin)) var(--universal-margin); + font-weight: 500; } + h1 small, h2 small, h3 small, h4 small, h5 small, h6 small { + color: var(--secondary-fore-color); + display: block; + margin-top: -0.25rem; } + +h1 { + font-size: calc(1rem * var(--heading-ratio) * var(--heading-ratio) * var(--heading-ratio)); } + +h2 { + font-size: calc(1rem * var(--heading-ratio) * var(--heading-ratio); ); + background: var(--mark-back-color); + font-weight: 600; + padding: 0.1em 0.5em 0.2em 0.5em; + color: var(--mark-fore-color); } + +h3 { + font-size: calc(1rem * var(--heading-ratio)); + padding-left: calc(2 * var(--universal-margin)); + /* background: var(--border-color); */ + } + +h4 { + font-size: 1rem;); + padding-left: calc(4 * var(--universal-margin)); } + +h5 { + font-size: 1rem; } + +h6 { + font-size: calc(1rem / var(--heading-ratio)); } + +p { + margin: var(--universal-margin); } + +ol, ul { + margin: var(--universal-margin); + padding-left: calc(6 * var(--universal-margin)); } + +b, strong { + font-weight: 700; } + +hr { + box-sizing: content-box; + border: 0; + line-height: 1.25em; + margin: var(--universal-margin); + height: 0.0625rem; + background: linear-gradient(to right, transparent, var(--border-color) 20%, var(--border-color) 80%, transparent); } + +blockquote { + display: block; + position: relative; + font-style: italic; + color: var(--secondary-fore-color); + margin: var(--universal-margin); + padding: calc(3 * var(--universal-padding)); + border: 0.0625rem solid var(--secondary-border-color); + border-left: 0.375rem solid var(--blockquote-color); + border-radius: 0 var(--universal-border-radius) var(--universal-border-radius) 0; } + blockquote:before { + position: absolute; + top: calc(0rem - var(--universal-padding)); + left: 0; + font-family: sans-serif; + font-size: 3rem; + font-weight: 700; + content: "\201c"; + color: var(--blockquote-color); } + blockquote[cite]:after { + font-style: normal; + font-size: 0.75em; + font-weight: 700; + content: "\a— " attr(cite); + white-space: pre; } + +code, kbd, pre, samp { + font-family: Menlo, Consolas, monospace; + font-size: 0.85em; } + +code { + background: var(--secondary-back-color); + border-radius: var(--universal-border-radius); + padding: calc(var(--universal-padding) / 4) calc(var(--universal-padding) / 2); } + +kbd { + background: var(--fore-color); + color: var(--back-color); + border-radius: var(--universal-border-radius); + padding: calc(var(--universal-padding) / 4) calc(var(--universal-padding) / 2); } + +pre { + overflow: auto; + background: var(--secondary-back-color); + padding: calc(1.5 * var(--universal-padding)); + margin: var(--universal-margin); + border: 0.0625rem solid var(--secondary-border-color); + border-left: 0.25rem solid var(--pre-color); + border-radius: 0 var(--universal-border-radius) var(--universal-border-radius) 0; } + +sup, sub, code, kbd { + line-height: 0; + position: relative; + vertical-align: baseline; } + +small, sup, sub, figcaption { + font-size: 0.75em; } + +sup { + top: -0.5em; } + +sub { + bottom: -0.25em; } + +figure { + margin: var(--universal-margin); } + +figcaption { + color: var(--secondary-fore-color); } + +a { + text-decoration: none; } + a:link { + color: var(--a-link-color); } + a:visited { + color: var(--a-visited-color); } + a:hover, a:focus { + text-decoration: underline; } + +/* + Definitions for the grid system, cards and containers. +*/ +.container { + margin: 0 auto; + padding: 0 calc(1.5 * var(--universal-padding)); } + +.row { + box-sizing: border-box; + display: flex; + flex: 0 1 auto; + flex-flow: row wrap; } + +.col-sm, +[class^='col-sm-'], +[class^='col-sm-offset-'], +.row[class*='cols-sm-'] > * { + box-sizing: border-box; + flex: 0 0 auto; + padding: 0 calc(var(--universal-padding) / 2); } + +.col-sm, +.row.cols-sm > * { + max-width: 100%; + flex-grow: 1; + flex-basis: 0; } + +.col-sm-1, +.row.cols-sm-1 > * { + max-width: 8.3333333333%; + flex-basis: 8.3333333333%; } + +.col-sm-offset-0 { + margin-left: 0; } + +.col-sm-2, +.row.cols-sm-2 > * { + max-width: 16.6666666667%; + flex-basis: 16.6666666667%; } + +.col-sm-offset-1 { + margin-left: 8.3333333333%; } + +.col-sm-3, +.row.cols-sm-3 > * { + max-width: 25%; + flex-basis: 25%; } + +.col-sm-offset-2 { + margin-left: 16.6666666667%; } + +.col-sm-4, +.row.cols-sm-4 > * { + max-width: 33.3333333333%; + flex-basis: 33.3333333333%; } + +.col-sm-offset-3 { + margin-left: 25%; } + +.col-sm-5, +.row.cols-sm-5 > * { + max-width: 41.6666666667%; + flex-basis: 41.6666666667%; } + +.col-sm-offset-4 { + margin-left: 33.3333333333%; } + +.col-sm-6, +.row.cols-sm-6 > * { + max-width: 50%; + flex-basis: 50%; } + +.col-sm-offset-5 { + margin-left: 41.6666666667%; } + +.col-sm-7, +.row.cols-sm-7 > * { + max-width: 58.3333333333%; + flex-basis: 58.3333333333%; } + +.col-sm-offset-6 { + margin-left: 50%; } + +.col-sm-8, +.row.cols-sm-8 > * { + max-width: 66.6666666667%; + flex-basis: 66.6666666667%; } + +.col-sm-offset-7 { + margin-left: 58.3333333333%; } + +.col-sm-9, +.row.cols-sm-9 > * { + max-width: 75%; + flex-basis: 75%; } + +.col-sm-offset-8 { + margin-left: 66.6666666667%; } + +.col-sm-10, +.row.cols-sm-10 > * { + max-width: 83.3333333333%; + flex-basis: 83.3333333333%; } + +.col-sm-offset-9 { + margin-left: 75%; } + +.col-sm-11, +.row.cols-sm-11 > * { + max-width: 91.6666666667%; + flex-basis: 91.6666666667%; } + +.col-sm-offset-10 { + margin-left: 83.3333333333%; } + +.col-sm-12, +.row.cols-sm-12 > * { + max-width: 100%; + flex-basis: 100%; } + +.col-sm-offset-11 { + margin-left: 91.6666666667%; } + +.col-sm-normal { + order: initial; } + +.col-sm-first { + order: -999; } + +.col-sm-last { + order: 999; } + +@media screen and (min-width: 500px) { + .col-md, + [class^='col-md-'], + [class^='col-md-offset-'], + .row[class*='cols-md-'] > * { + box-sizing: border-box; + flex: 0 0 auto; + padding: 0 calc(var(--universal-padding) / 2); } + + .col-md, + .row.cols-md > * { + max-width: 100%; + flex-grow: 1; + flex-basis: 0; } + + .col-md-1, + .row.cols-md-1 > * { + max-width: 8.3333333333%; + flex-basis: 8.3333333333%; } + + .col-md-offset-0 { + margin-left: 0; } + + .col-md-2, + .row.cols-md-2 > * { + max-width: 16.6666666667%; + flex-basis: 16.6666666667%; } + + .col-md-offset-1 { + margin-left: 8.3333333333%; } + + .col-md-3, + .row.cols-md-3 > * { + max-width: 25%; + flex-basis: 25%; } + + .col-md-offset-2 { + margin-left: 16.6666666667%; } + + .col-md-4, + .row.cols-md-4 > * { + max-width: 33.3333333333%; + flex-basis: 33.3333333333%; } + + .col-md-offset-3 { + margin-left: 25%; } + + .col-md-5, + .row.cols-md-5 > * { + max-width: 41.6666666667%; + flex-basis: 41.6666666667%; } + + .col-md-offset-4 { + margin-left: 33.3333333333%; } + + .col-md-6, + .row.cols-md-6 > * { + max-width: 50%; + flex-basis: 50%; } + + .col-md-offset-5 { + margin-left: 41.6666666667%; } + + .col-md-7, + .row.cols-md-7 > * { + max-width: 58.3333333333%; + flex-basis: 58.3333333333%; } + + .col-md-offset-6 { + margin-left: 50%; } + + .col-md-8, + .row.cols-md-8 > * { + max-width: 66.6666666667%; + flex-basis: 66.6666666667%; } + + .col-md-offset-7 { + margin-left: 58.3333333333%; } + + .col-md-9, + .row.cols-md-9 > * { + max-width: 75%; + flex-basis: 75%; } + + .col-md-offset-8 { + margin-left: 66.6666666667%; } + + .col-md-10, + .row.cols-md-10 > * { + max-width: 83.3333333333%; + flex-basis: 83.3333333333%; } + + .col-md-offset-9 { + margin-left: 75%; } + + .col-md-11, + .row.cols-md-11 > * { + max-width: 91.6666666667%; + flex-basis: 91.6666666667%; } + + .col-md-offset-10 { + margin-left: 83.3333333333%; } + + .col-md-12, + .row.cols-md-12 > * { + max-width: 100%; + flex-basis: 100%; } + + .col-md-offset-11 { + margin-left: 91.6666666667%; } + + .col-md-normal { + order: initial; } + + .col-md-first { + order: -999; } + + .col-md-last { + order: 999; } } +@media screen and (min-width: 1280px) { + .col-lg, + [class^='col-lg-'], + [class^='col-lg-offset-'], + .row[class*='cols-lg-'] > * { + box-sizing: border-box; + flex: 0 0 auto; + padding: 0 calc(var(--universal-padding) / 2); } + + .col-lg, + .row.cols-lg > * { + max-width: 100%; + flex-grow: 1; + flex-basis: 0; } + + .col-lg-1, + .row.cols-lg-1 > * { + max-width: 8.3333333333%; + flex-basis: 8.3333333333%; } + + .col-lg-offset-0 { + margin-left: 0; } + + .col-lg-2, + .row.cols-lg-2 > * { + max-width: 16.6666666667%; + flex-basis: 16.6666666667%; } + + .col-lg-offset-1 { + margin-left: 8.3333333333%; } + + .col-lg-3, + .row.cols-lg-3 > * { + max-width: 25%; + flex-basis: 25%; } + + .col-lg-offset-2 { + margin-left: 16.6666666667%; } + + .col-lg-4, + .row.cols-lg-4 > * { + max-width: 33.3333333333%; + flex-basis: 33.3333333333%; } + + .col-lg-offset-3 { + margin-left: 25%; } + + .col-lg-5, + .row.cols-lg-5 > * { + max-width: 41.6666666667%; + flex-basis: 41.6666666667%; } + + .col-lg-offset-4 { + margin-left: 33.3333333333%; } + + .col-lg-6, + .row.cols-lg-6 > * { + max-width: 50%; + flex-basis: 50%; } + + .col-lg-offset-5 { + margin-left: 41.6666666667%; } + + .col-lg-7, + .row.cols-lg-7 > * { + max-width: 58.3333333333%; + flex-basis: 58.3333333333%; } + + .col-lg-offset-6 { + margin-left: 50%; } + + .col-lg-8, + .row.cols-lg-8 > * { + max-width: 66.6666666667%; + flex-basis: 66.6666666667%; } + + .col-lg-offset-7 { + margin-left: 58.3333333333%; } + + .col-lg-9, + .row.cols-lg-9 > * { + max-width: 75%; + flex-basis: 75%; } + + .col-lg-offset-8 { + margin-left: 66.6666666667%; } + + .col-lg-10, + .row.cols-lg-10 > * { + max-width: 83.3333333333%; + flex-basis: 83.3333333333%; } + + .col-lg-offset-9 { + margin-left: 75%; } + + .col-lg-11, + .row.cols-lg-11 > * { + max-width: 91.6666666667%; + flex-basis: 91.6666666667%; } + + .col-lg-offset-10 { + margin-left: 83.3333333333%; } + + .col-lg-12, + .row.cols-lg-12 > * { + max-width: 100%; + flex-basis: 100%; } + + .col-lg-offset-11 { + margin-left: 91.6666666667%; } + + .col-lg-normal { + order: initial; } + + .col-lg-first { + order: -999; } + + .col-lg-last { + order: 999; } } +/* Card component CSS variable definitions */ +:root { + --card-back-color: #f8f8f8; + --card-fore-color: #111; + --card-border-color: #ddd; } + +.card { + display: flex; + flex-direction: column; + justify-content: space-between; + align-self: center; + position: relative; + width: 100%; + background: var(--card-back-color); + color: var(--card-fore-color); + border: 0.0625rem solid var(--card-border-color); + border-radius: var(--universal-border-radius); + margin: var(--universal-margin); + overflow: hidden; } + @media screen and (min-width: 320px) { + .card { + max-width: 320px; } } + .card > .sectione { + background: var(--card-back-color); + color: var(--card-fore-color); + box-sizing: border-box; + margin: 0; + border: 0; + border-radius: 0; + border-bottom: 0.0625rem solid var(--card-border-color); + padding: var(--universal-padding); + width: 100%; } + .card > .sectione.media { + height: 200px; + padding: 0; + -o-object-fit: cover; + object-fit: cover; } + .card > .sectione:last-child { + border-bottom: 0; } + +/* + Custom elements for card elements. +*/ +@media screen and (min-width: 240px) { + .card.small { + max-width: 240px; } } +@media screen and (min-width: 480px) { + .card.large { + max-width: 480px; } } +.card.fluid { + max-width: 100%; + width: auto; } + +.card.warning { +/* --card-back-color: #ffca28; */ + --card-back-color: #e5b8b7; + --card-border-color: #e8b825; } + +.card.error { + --card-back-color: #b71c1c; + --card-fore-color: #f8f8f8; + --card-border-color: #a71a1a; } + +.card > .sectione.dark { + --card-back-color: #e0e0e0; } + +.card > .sectione.double-padded { + padding: calc(1.5 * var(--universal-padding)); } + +/* + Definitions for forms and input elements. +*/ +/* Input_control module CSS variable definitions */ +:root { + --form-back-color: #f0f0f0; + --form-fore-color: #111; + --form-border-color: #ddd; + --input-back-color: #f8f8f8; + --input-fore-color: #111; + --input-border-color: #ddd; + --input-focus-color: #0288d1; + --input-invalid-color: #d32f2f; + --button-back-color: #e2e2e2; + --button-hover-back-color: #dcdcdc; + --button-fore-color: #212121; + --button-border-color: transparent; + --button-hover-border-color: transparent; + --button-group-border-color: rgba(124, 124, 124, 0.54); } + +form { + background: var(--form-back-color); + color: var(--form-fore-color); + border: 0.0625rem solid var(--form-border-color); + border-radius: var(--universal-border-radius); + margin: var(--universal-margin); + padding: calc(2 * var(--universal-padding)) var(--universal-padding); } + +fieldset { + border: 0.0625rem solid var(--form-border-color); + border-radius: var(--universal-border-radius); + margin: calc(var(--universal-margin) / 4); + padding: var(--universal-padding); } + +legend { + box-sizing: border-box; + display: table; + max-width: 100%; + white-space: normal; + font-weight: 700; + padding: calc(var(--universal-padding) / 2); } + +label { + padding: calc(var(--universal-padding) / 2) var(--universal-padding); } + +.input-group { + display: inline-block; } + .input-group.fluid { + display: flex; + align-items: center; + justify-content: center; } + .input-group.fluid > input { + max-width: 100%; + flex-grow: 1; + flex-basis: 0px; } + @media screen and (max-width: 499px) { + .input-group.fluid { + align-items: stretch; + flex-direction: column; } } + .input-group.vertical { + display: flex; + align-items: stretch; + flex-direction: column; } + .input-group.vertical > input { + max-width: 100%; + flex-grow: 1; + flex-basis: 0px; } + +[type="number"]::-webkit-inner-spin-button, [type="number"]::-webkit-outer-spin-button { + height: auto; } + +[type="search"] { + -webkit-appearance: textfield; + outline-offset: -2px; } + +[type="search"]::-webkit-search-cancel-button, +[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; } + +input:not([type]), [type="text"], [type="email"], [type="number"], [type="search"], +[type="password"], [type="url"], [type="tel"], [type="checkbox"], [type="radio"], textarea, select { + box-sizing: border-box; + background: var(--input-back-color); + color: var(--input-fore-color); + border: 0.0625rem solid var(--input-border-color); + border-radius: var(--universal-border-radius); + margin: calc(var(--universal-margin) / 2); + padding: var(--universal-padding) calc(1.5 * var(--universal-padding)); } + +input:not([type="button"]):not([type="submit"]):not([type="reset"]):hover, input:not([type="button"]):not([type="submit"]):not([type="reset"]):focus, textarea:hover, textarea:focus, select:hover, select:focus { + border-color: var(--input-focus-color); + box-shadow: none; } +input:not([type="button"]):not([type="submit"]):not([type="reset"]):invalid, input:not([type="button"]):not([type="submit"]):not([type="reset"]):focus:invalid, textarea:invalid, textarea:focus:invalid, select:invalid, select:focus:invalid { + border-color: var(--input-invalid-color); + box-shadow: none; } +input:not([type="button"]):not([type="submit"]):not([type="reset"])[readonly], textarea[readonly], select[readonly] { + background: var(--secondary-back-color); } + +select { + max-width: 100%; } + +option { + overflow: hidden; + text-overflow: ellipsis; } + +[type="checkbox"], [type="radio"] { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + position: relative; + height: calc(1rem + var(--universal-padding) / 2); + width: calc(1rem + var(--universal-padding) / 2); + vertical-align: text-bottom; + padding: 0; + flex-basis: calc(1rem + var(--universal-padding) / 2) !important; + flex-grow: 0 !important; } + [type="checkbox"]:checked:before, [type="radio"]:checked:before { + position: absolute; } + +[type="checkbox"]:checked:before { + content: '\2713'; + font-family: sans-serif; + font-size: calc(1rem + var(--universal-padding) / 2); + top: calc(0rem - var(--universal-padding)); + left: calc(var(--universal-padding) / 4); } + +[type="radio"] { + border-radius: 100%; } + [type="radio"]:checked:before { + border-radius: 100%; + content: ''; + top: calc(0.0625rem + var(--universal-padding) / 2); + left: calc(0.0625rem + var(--universal-padding) / 2); + background: var(--input-fore-color); + width: 0.5rem; + height: 0.5rem; } + +:placeholder-shown { + color: var(--input-fore-color); } + +::-ms-placeholder { + color: var(--input-fore-color); + opacity: 0.54; } + +button::-moz-focus-inner, [type="button"]::-moz-focus-inner, [type="reset"]::-moz-focus-inner, [type="submit"]::-moz-focus-inner { + border-style: none; + padding: 0; } + +button, html [type="button"], [type="reset"], [type="submit"] { + -webkit-appearance: button; } + +button { + overflow: visible; + text-transform: none; } + +button, [type="button"], [type="submit"], [type="reset"], +a.button, label.button, .button, +a[role="button"], label[role="button"], [role="button"] { + display: inline-block; + background: var(--button-back-color); + color: var(--button-fore-color); + border: 0.0625rem solid var(--button-border-color); + border-radius: var(--universal-border-radius); + padding: var(--universal-padding) calc(1.5 * var(--universal-padding)); + margin: var(--universal-margin); + text-decoration: none; + cursor: pointer; + transition: background 0.3s; } + button:hover, button:focus, [type="button"]:hover, [type="button"]:focus, [type="submit"]:hover, [type="submit"]:focus, [type="reset"]:hover, [type="reset"]:focus, + a.button:hover, + a.button:focus, label.button:hover, label.button:focus, .button:hover, .button:focus, + a[role="button"]:hover, + a[role="button"]:focus, label[role="button"]:hover, label[role="button"]:focus, [role="button"]:hover, [role="button"]:focus { + background: var(--button-hover-back-color); + border-color: var(--button-hover-border-color); } + +input:disabled, input[disabled], textarea:disabled, textarea[disabled], select:disabled, select[disabled], button:disabled, button[disabled], .button:disabled, .button[disabled], [role="button"]:disabled, [role="button"][disabled] { + cursor: not-allowed; + opacity: 0.75; } + +.button-group { + display: flex; + border: 0.0625rem solid var(--button-group-border-color); + border-radius: var(--universal-border-radius); + margin: var(--universal-margin); } + .button-group > button, .button-group [type="button"], .button-group > [type="submit"], .button-group > [type="reset"], .button-group > .button, .button-group > [role="button"] { + margin: 0; + max-width: 100%; + flex: 1 1 auto; + text-align: center; + border: 0; + border-radius: 0; + box-shadow: none; } + .button-group > :not(:first-child) { + border-left: 0.0625rem solid var(--button-group-border-color); } + @media screen and (max-width: 499px) { + .button-group { + flex-direction: column; } + .button-group > :not(:first-child) { + border: 0; + border-top: 0.0625rem solid var(--button-group-border-color); } } + +/* + Custom elements for forms and input elements. +*/ +button.primary, [type="button"].primary, [type="submit"].primary, [type="reset"].primary, .button.primary, [role="button"].primary { + --button-back-color: #1976d2; + --button-fore-color: #f8f8f8; } + button.primary:hover, button.primary:focus, [type="button"].primary:hover, [type="button"].primary:focus, [type="submit"].primary:hover, [type="submit"].primary:focus, [type="reset"].primary:hover, [type="reset"].primary:focus, .button.primary:hover, .button.primary:focus, [role="button"].primary:hover, [role="button"].primary:focus { + --button-hover-back-color: #1565c0; } + +button.secondary, [type="button"].secondary, [type="submit"].secondary, [type="reset"].secondary, .button.secondary, [role="button"].secondary { + --button-back-color: #d32f2f; + --button-fore-color: #f8f8f8; } + button.secondary:hover, button.secondary:focus, [type="button"].secondary:hover, [type="button"].secondary:focus, [type="submit"].secondary:hover, [type="submit"].secondary:focus, [type="reset"].secondary:hover, [type="reset"].secondary:focus, .button.secondary:hover, .button.secondary:focus, [role="button"].secondary:hover, [role="button"].secondary:focus { + --button-hover-back-color: #c62828; } + +button.tertiary, [type="button"].tertiary, [type="submit"].tertiary, [type="reset"].tertiary, .button.tertiary, [role="button"].tertiary { + --button-back-color: #308732; + --button-fore-color: #f8f8f8; } + button.tertiary:hover, button.tertiary:focus, [type="button"].tertiary:hover, [type="button"].tertiary:focus, [type="submit"].tertiary:hover, [type="submit"].tertiary:focus, [type="reset"].tertiary:hover, [type="reset"].tertiary:focus, .button.tertiary:hover, .button.tertiary:focus, [role="button"].tertiary:hover, [role="button"].tertiary:focus { + --button-hover-back-color: #277529; } + +button.inverse, [type="button"].inverse, [type="submit"].inverse, [type="reset"].inverse, .button.inverse, [role="button"].inverse { + --button-back-color: #212121; + --button-fore-color: #f8f8f8; } + button.inverse:hover, button.inverse:focus, [type="button"].inverse:hover, [type="button"].inverse:focus, [type="submit"].inverse:hover, [type="submit"].inverse:focus, [type="reset"].inverse:hover, [type="reset"].inverse:focus, .button.inverse:hover, .button.inverse:focus, [role="button"].inverse:hover, [role="button"].inverse:focus { + --button-hover-back-color: #111; } + +button.small, [type="button"].small, [type="submit"].small, [type="reset"].small, .button.small, [role="button"].small { + padding: calc(0.5 * var(--universal-padding)) calc(0.75 * var(--universal-padding)); + margin: var(--universal-margin); } + +button.large, [type="button"].large, [type="submit"].large, [type="reset"].large, .button.large, [role="button"].large { + padding: calc(1.5 * var(--universal-padding)) calc(2 * var(--universal-padding)); + margin: var(--universal-margin); } + +/* + Definitions for navigation elements. +*/ +/* Navigation module CSS variable definitions */ +:root { + --header-back-color: #f8f8f8; + --header-hover-back-color: #f0f0f0; + --header-fore-color: #444; + --header-border-color: #ddd; + --nav-back-color: #f8f8f8; + --nav-hover-back-color: #f0f0f0; + --nav-fore-color: #444; + --nav-border-color: #ddd; + --nav-link-color: #0277bd; + --footer-fore-color: #444; + --footer-back-color: #f8f8f8; + --footer-border-color: #ddd; + --footer-link-color: #0277bd; + --drawer-back-color: #f8f8f8; + --drawer-hover-back-color: #f0f0f0; + --drawer-border-color: #ddd; + --drawer-close-color: #444; } + +header { + height: 3.1875rem; + background: var(--header-back-color); + color: var(--header-fore-color); + border-bottom: 0.0625rem solid var(--header-border-color); + padding: calc(var(--universal-padding) / 4) 0; + white-space: nowrap; + overflow-x: auto; + overflow-y: hidden; } + header.row { + box-sizing: content-box; } + header .logo { + color: var(--header-fore-color); + font-size: 1.75rem; + padding: var(--universal-padding) calc(2 * var(--universal-padding)); + text-decoration: none; } + header button, header [type="button"], header .button, header [role="button"] { + box-sizing: border-box; + position: relative; + top: calc(0rem - var(--universal-padding) / 4); + height: calc(3.1875rem + var(--universal-padding) / 2); + background: var(--header-back-color); + line-height: calc(3.1875rem - var(--universal-padding) * 1.5); + text-align: center; + color: var(--header-fore-color); + border: 0; + border-radius: 0; + margin: 0; + text-transform: uppercase; } + header button:hover, header button:focus, header [type="button"]:hover, header [type="button"]:focus, header .button:hover, header .button:focus, header [role="button"]:hover, header [role="button"]:focus { + background: var(--header-hover-back-color); } + +nav { + background: var(--nav-back-color); + color: var(--nav-fore-color); + border: 0.0625rem solid var(--nav-border-color); + border-radius: var(--universal-border-radius); + margin: var(--universal-margin); } + nav * { + padding: var(--universal-padding) calc(1.5 * var(--universal-padding)); } + nav a, nav a:visited { + display: block; + color: var(--nav-link-color); + border-radius: var(--universal-border-radius); + transition: background 0.3s; } + nav a:hover, nav a:focus, nav a:visited:hover, nav a:visited:focus { + text-decoration: none; + background: var(--nav-hover-back-color); } + nav .sublink-1 { + position: relative; + margin-left: calc(2 * var(--universal-padding)); } + nav .sublink-1:before { + position: absolute; + left: calc(var(--universal-padding) - 1 * var(--universal-padding)); + top: -0.0625rem; + content: ''; + height: 100%; + border: 0.0625rem solid var(--nav-border-color); + border-left: 0; } + nav .sublink-2 { + position: relative; + margin-left: calc(4 * var(--universal-padding)); } + nav .sublink-2:before { + position: absolute; + left: calc(var(--universal-padding) - 3 * var(--universal-padding)); + top: -0.0625rem; + content: ''; + height: 100%; + border: 0.0625rem solid var(--nav-border-color); + border-left: 0; } + +footer { + background: var(--footer-back-color); + color: var(--footer-fore-color); + border-top: 0.0625rem solid var(--footer-border-color); + padding: calc(2 * var(--universal-padding)) var(--universal-padding); + font-size: 0.875rem; } + footer a, footer a:visited { + color: var(--footer-link-color); } + +header.sticky { + position: -webkit-sticky; + position: sticky; + z-index: 1101; + top: 0; } + +footer.sticky { + position: -webkit-sticky; + position: sticky; + z-index: 1101; + bottom: 0; } + +.drawer-toggle:before { + display: inline-block; + position: relative; + vertical-align: bottom; + content: '\00a0\2261\00a0'; + font-family: sans-serif; + font-size: 1.5em; } +@media screen and (min-width: 500px) { + .drawer-toggle:not(.persistent) { + display: none; } } + +[type="checkbox"].drawer { + height: 1px; + width: 1px; + margin: -1px; + overflow: hidden; + position: absolute; + clip: rect(0 0 0 0); + -webkit-clip-path: inset(100%); + clip-path: inset(100%); } + [type="checkbox"].drawer + * { + display: block; + box-sizing: border-box; + position: fixed; + top: 0; + width: 320px; + height: 100vh; + overflow-y: auto; + background: var(--drawer-back-color); + border: 0.0625rem solid var(--drawer-border-color); + border-radius: 0; + margin: 0; + z-index: 1110; + right: -320px; + transition: right 0.3s; } + [type="checkbox"].drawer + * .drawer-close { + position: absolute; + top: var(--universal-margin); + right: var(--universal-margin); + z-index: 1111; + width: 2rem; + height: 2rem; + border-radius: var(--universal-border-radius); + padding: var(--universal-padding); + margin: 0; + cursor: pointer; + transition: background 0.3s; } + [type="checkbox"].drawer + * .drawer-close:before { + display: block; + content: '\00D7'; + color: var(--drawer-close-color); + position: relative; + font-family: sans-serif; + font-size: 2rem; + line-height: 1; + text-align: center; } + [type="checkbox"].drawer + * .drawer-close:hover, [type="checkbox"].drawer + * .drawer-close:focus { + background: var(--drawer-hover-back-color); } + @media screen and (max-width: 320px) { + [type="checkbox"].drawer + * { + width: 100%; } } + [type="checkbox"].drawer:checked + * { + right: 0; } + @media screen and (min-width: 500px) { + [type="checkbox"].drawer:not(.persistent) + * { + position: static; + height: 100%; + z-index: 1100; } + [type="checkbox"].drawer:not(.persistent) + * .drawer-close { + display: none; } } + +/* + Definitions for the responsive table component. +*/ +/* Table module CSS variable definitions. */ +:root { + --table-border-color: #aaa; + --table-border-separator-color: #666; + --table-head-back-color: #e6e6e6; + --table-head-fore-color: #111; + --table-body-back-color: #f8f8f8; + --table-body-fore-color: #111; + --table-body-alt-back-color: #eee; } + +table { + border-collapse: separate; + border-spacing: 0; + : margin: calc(1.5 * var(--universal-margin)) var(--universal-margin); + display: flex; + flex: 0 1 auto; + flex-flow: row wrap; + padding: var(--universal-padding); + padding-top: 0; + margin: calc(1.5 * var(--universal-margin)) var(--universal-margin); } + table caption { + font-size: 1.25 * rem; + margin: calc(2 * var(--universal-margin)) 0; + max-width: 100%; + flex: 0 0 100%; + text-align: left;} + table thead, table tbody { + display: flex; + flex-flow: row wrap; + border: 0.0625rem solid var(--table-border-color); } + table thead { + z-index: 999; + border-radius: var(--universal-border-radius) var(--universal-border-radius) 0 0; + border-bottom: 0.0625rem solid var(--table-border-separator-color); } + table tbody { + border-top: 0; + margin-top: calc(0 - var(--universal-margin)); + border-radius: 0 0 var(--universal-border-radius) var(--universal-border-radius); } + table tr { + display: flex; + padding: 0; } + table th, table td { + padding: calc(0.5 * var(--universal-padding)); + font-size: 0.9rem; } + table th { + text-align: left; + background: var(--table-head-back-color); + color: var(--table-head-fore-color); } + table td { + background: var(--table-body-back-color); + color: var(--table-body-fore-color); + border-top: 0.0625rem solid var(--table-border-color); } + +table:not(.horizontal) { + overflow: auto; + max-height: 850px; } + table:not(.horizontal) thead, table:not(.horizontal) tbody { + max-width: 100%; + flex: 0 0 100%; } + table:not(.horizontal) tr { + flex-flow: row wrap; + flex: 0 0 100%; } + table:not(.horizontal) th, table:not(.horizontal) td { + flex: 1 0 0%; + overflow: hidden; + text-overflow: ellipsis; } + table:not(.horizontal) thead { + position: sticky; + top: 0; } + table:not(.horizontal) tbody tr:first-child td { + border-top: 0; } + +table.horizontal { + border: 0; } + table.horizontal thead, table.horizontal tbody { + border: 0; + flex-flow: row nowrap; } + table.horizontal tbody { + overflow: auto; + justify-content: space-between; + flex: 1 0 0; + margin-left: calc( 4 * var(--universal-margin)); + padding-bottom: calc(var(--universal-padding) / 4); } + table.horizontal tr { + flex-direction: column; + flex: 1 0 auto; } + table.horizontal th, table.horizontal td { + width: 100%; + border: 0; + border-bottom: 0.0625rem solid var(--table-border-color); } + table.horizontal th:not(:first-child), table.horizontal td:not(:first-child) { + border-top: 0; } + table.horizontal th { + text-align: right; + border-left: 0.0625rem solid var(--table-border-color); + border-right: 0.0625rem solid var(--table-border-separator-color); } + table.horizontal thead tr:first-child { + padding-left: 0; } + table.horizontal th:first-child, table.horizontal td:first-child { + border-top: 0.0625rem solid var(--table-border-color); } + table.horizontal tbody tr:last-child td { + border-right: 0.0625rem solid var(--table-border-color); } + table.horizontal tbody tr:last-child td:first-child { + border-top-right-radius: 0.25rem; } + table.horizontal tbody tr:last-child td:last-child { + border-bottom-right-radius: 0.25rem; } + table.horizontal thead tr:first-child th:first-child { + border-top-left-radius: 0.25rem; } + table.horizontal thead tr:first-child th:last-child { + border-bottom-left-radius: 0.25rem; } + +@media screen and (max-width: 499px) { + table, table.horizontal { + border-collapse: collapse; + border: 0; + width: 100%; + display: table; } + table thead, table th, table.horizontal thead, table.horizontal th { + border: 0; + height: 1px; + width: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + clip: rect(0 0 0 0); + -webkit-clip-path: inset(100%); + clip-path: inset(100%); } + table tbody, table.horizontal tbody { + border: 0; + display: table-row-group; } + table tr, table.horizontal tr { + display: block; + border: 0.0625rem solid var(--table-border-color); + border-radius: var(--universal-border-radius); + background: #fafafa; + padding: var(--universal-padding); + margin: var(--universal-margin); + margin-bottom: calc(2 * var(--universal-margin)); } + table th, table td, table.horizontal th, table.horizontal td { + width: auto; } + table td, table.horizontal td { + display: block; + border: 0; + text-align: right; } + table td:before, table.horizontal td:before { + content: attr(data-label); + float: left; + font-weight: 600; } + table th:first-child, table td:first-child, table.horizontal th:first-child, table.horizontal td:first-child { + border-top: 0; } + table tbody tr:last-child td, table.horizontal tbody tr:last-child td { + border-right: 0; } } +:root { + --table-body-alt-back-color: #eee; } + +table tr:nth-of-type(2n) > td { + background: var(--table-body-alt-back-color); } + +@media screen and (max-width: 500px) { + table tr:nth-of-type(2n) { + background: var(--table-body-alt-back-color); } } +:root { + --table-body-hover-back-color: #90caf9; } + +table.hoverable tr:hover, table.hoverable tr:hover > td, table.hoverable tr:focus, table.hoverable tr:focus > td { + background: var(--table-body-hover-back-color); } + +@media screen and (max-width: 500px) { + table.hoverable tr:hover, table.hoverable tr:hover > td, table.hoverable tr:focus, table.hoverable tr:focus > td { + background: var(--table-body-hover-back-color); } } +/* + Definitions for contextual background elements, toasts and tooltips. +*/ +/* Contextual module CSS variable definitions */ +:root { + --mark-back-color: #0277bd; + --mark-fore-color: #fafafa; } + +mark { + background: var(--mark-back-color); + color: var(--mark-fore-color); + font-size: 0.95em; + line-height: 1em; + border-radius: var(--universal-border-radius); + padding: calc(var(--universal-padding) / 4) calc(var(--universal-padding) / 2); } + mark.inline-block { + display: inline-block; + font-size: 1em; + line-height: 1.5; + padding: calc(var(--universal-padding) / 2) var(--universal-padding); } + +:root { + --toast-back-color: #424242; + --toast-fore-color: #fafafa; } + +.toast { + position: fixed; + bottom: calc(var(--universal-margin) * 3); + left: 50%; + transform: translate(-50%, -50%); + z-index: 1111; + color: var(--toast-fore-color); + background: var(--toast-back-color); + border-radius: calc(var(--universal-border-radius) * 16); + padding: var(--universal-padding) calc(var(--universal-padding) * 3); } + +:root { + --tooltip-back-color: #212121; + --tooltip-fore-color: #fafafa; } + +.tooltip { + position: relative; + display: inline-block; } + .tooltip:before, .tooltip:after { + position: absolute; + opacity: 0; + clip: rect(0 0 0 0); + -webkit-clip-path: inset(100%); + clip-path: inset(100%); + transition: all 0.3s; + z-index: 1010; + left: 50%; } + .tooltip:not(.bottom):before, .tooltip:not(.bottom):after { + bottom: 75%; } + .tooltip.bottom:before, .tooltip.bottom:after { + top: 75%; } + .tooltip:hover:before, .tooltip:hover:after, .tooltip:focus:before, .tooltip:focus:after { + opacity: 1; + clip: auto; + -webkit-clip-path: inset(0%); + clip-path: inset(0%); } + .tooltip:before { + content: ''; + background: transparent; + border: var(--universal-margin) solid transparent; + left: calc(50% - var(--universal-margin)); } + .tooltip:not(.bottom):before { + border-top-color: #212121; } + .tooltip.bottom:before { + border-bottom-color: #212121; } + .tooltip:after { + content: attr(aria-label); + color: var(--tooltip-fore-color); + background: var(--tooltip-back-color); + border-radius: var(--universal-border-radius); + padding: var(--universal-padding); + white-space: nowrap; + transform: translateX(-50%); } + .tooltip:not(.bottom):after { + margin-bottom: calc(2 * var(--universal-margin)); } + .tooltip.bottom:after { + margin-top: calc(2 * var(--universal-margin)); } + +:root { + --modal-overlay-color: rgba(0, 0, 0, 0.45); + --modal-close-color: #444; + --modal-close-hover-color: #f0f0f0; } + +[type="checkbox"].modal { + height: 1px; + width: 1px; + margin: -1px; + overflow: hidden; + position: absolute; + clip: rect(0 0 0 0); + -webkit-clip-path: inset(100%); + clip-path: inset(100%); } + [type="checkbox"].modal + div { + position: fixed; + top: 0; + left: 0; + display: none; + width: 100vw; + height: 100vh; + background: var(--modal-overlay-color); } + [type="checkbox"].modal + div .card { + margin: 0 auto; + max-height: 50vh; + overflow: auto; } + [type="checkbox"].modal + div .card .modal-close { + position: absolute; + top: 0; + right: 0; + width: 1.75rem; + height: 1.75rem; + border-radius: var(--universal-border-radius); + padding: var(--universal-padding); + margin: 0; + cursor: pointer; + transition: background 0.3s; } + [type="checkbox"].modal + div .card .modal-close:before { + display: block; + content: '\00D7'; + color: var(--modal-close-color); + position: relative; + font-family: sans-serif; + font-size: 1.75rem; + line-height: 1; + text-align: center; } + [type="checkbox"].modal + div .card .modal-close:hover, [type="checkbox"].modal + div .card .modal-close:focus { + background: var(--modal-close-hover-color); } + [type="checkbox"].modal:checked + div { + display: flex; + flex: 0 1 auto; + z-index: 1200; } + [type="checkbox"].modal:checked + div .card .modal-close { + z-index: 1211; } + +:root { + --collapse-label-back-color: #e8e8e8; + --collapse-label-fore-color: #212121; + --collapse-label-hover-back-color: #f0f0f0; + --collapse-selected-label-back-color: #ececec; + --collapse-border-color: #ddd; + --collapse-content-back-color: #fafafa; + --collapse-selected-label-border-color: #0277bd; } + +.collapse { + width: calc(100% - 2 * var(--universal-margin)); + opacity: 1; + display: flex; + flex-direction: column; + margin: var(--universal-margin); + border-radius: var(--universal-border-radius); } + .collapse > [type="radio"], .collapse > [type="checkbox"] { + height: 1px; + width: 1px; + margin: -1px; + overflow: hidden; + position: absolute; + clip: rect(0 0 0 0); + -webkit-clip-path: inset(100%); + clip-path: inset(100%); } + .collapse > label { + flex-grow: 1; + display: inline-block; + height: 1.5rem; + cursor: pointer; + transition: background 0.3s; + color: var(--collapse-label-fore-color); + background: var(--collapse-label-back-color); + border: 0.0625rem solid var(--collapse-border-color); + padding: calc(1.5 * var(--universal-padding)); } + .collapse > label:hover, .collapse > label:focus { + background: var(--collapse-label-hover-back-color); } + .collapse > label + div { + flex-basis: auto; + height: 1px; + width: 1px; + margin: -1px; + overflow: hidden; + position: absolute; + clip: rect(0 0 0 0); + -webkit-clip-path: inset(100%); + clip-path: inset(100%); + transition: max-height 0.3s; + max-height: 1px; } + .collapse > :checked + label { + background: var(--collapse-selected-label-back-color); + border-bottom-color: var(--collapse-selected-label-border-color); } + .collapse > :checked + label + div { + box-sizing: border-box; + position: relative; + width: 100%; + height: auto; + overflow: auto; + margin: 0; + background: var(--collapse-content-back-color); + border: 0.0625rem solid var(--collapse-border-color); + border-top: 0; + padding: var(--universal-padding); + clip: auto; + -webkit-clip-path: inset(0%); + clip-path: inset(0%); + max-height: 850px; } + .collapse > label:not(:first-of-type) { + border-top: 0; } + .collapse > label:first-of-type { + border-radius: var(--universal-border-radius) var(--universal-border-radius) 0 0; } + .collapse > label:last-of-type:not(:first-of-type) { + border-radius: 0 0 var(--universal-border-radius) var(--universal-border-radius); } + .collapse > label:last-of-type:first-of-type { + border-radius: var(--universal-border-radius); } + .collapse > :checked:last-of-type:not(:first-of-type) + label { + border-radius: 0; } + .collapse > :checked:last-of-type + label + div { + border-radius: 0 0 var(--universal-border-radius) var(--universal-border-radius); } + +/* + Custom elements for contextual background elements, toasts and tooltips. +*/ +mark.secondary { + --mark-back-color: #d32f2f; } + +mark.tertiary { + --mark-back-color: #308732; } + +mark.tag { + padding: calc(var(--universal-padding)/2) var(--universal-padding); + border-radius: 1em; } + +/* + Definitions for progress elements and spinners. +*/ +/* Progress module CSS variable definitions */ +:root { + --progress-back-color: #ddd; + --progress-fore-color: #555; } + +progress { + display: block; + vertical-align: baseline; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + height: 0.75rem; + width: calc(100% - 2 * var(--universal-margin)); + margin: var(--universal-margin); + border: 0; + border-radius: calc(2 * var(--universal-border-radius)); + background: var(--progress-back-color); + color: var(--progress-fore-color); } + progress::-webkit-progress-value { + background: var(--progress-fore-color); + border-top-left-radius: calc(2 * var(--universal-border-radius)); + border-bottom-left-radius: calc(2 * var(--universal-border-radius)); } + progress::-webkit-progress-bar { + background: var(--progress-back-color); } + progress::-moz-progress-bar { + background: var(--progress-fore-color); + border-top-left-radius: calc(2 * var(--universal-border-radius)); + border-bottom-left-radius: calc(2 * var(--universal-border-radius)); } + progress[value="1000"]::-webkit-progress-value { + border-radius: calc(2 * var(--universal-border-radius)); } + progress[value="1000"]::-moz-progress-bar { + border-radius: calc(2 * var(--universal-border-radius)); } + progress.inline { + display: inline-block; + vertical-align: middle; + width: 60%; } + +:root { + --spinner-back-color: #ddd; + --spinner-fore-color: #555; } + +@keyframes spinner-donut-anim { + 0% { + transform: rotate(0deg); } + 100% { + transform: rotate(360deg); } } +.spinner { + display: inline-block; + margin: var(--universal-margin); + border: 0.25rem solid var(--spinner-back-color); + border-left: 0.25rem solid var(--spinner-fore-color); + border-radius: 50%; + width: 1.25rem; + height: 1.25rem; + animation: spinner-donut-anim 1.2s linear infinite; } + +/* + Custom elements for progress bars and spinners. +*/ +progress.primary { + --progress-fore-color: #1976d2; } + +progress.secondary { + --progress-fore-color: #d32f2f; } + +progress.tertiary { + --progress-fore-color: #308732; } + +.spinner.primary { + --spinner-fore-color: #1976d2; } + +.spinner.secondary { + --spinner-fore-color: #d32f2f; } + +.spinner.tertiary { + --spinner-fore-color: #308732; } + +/* + Definitions for icons - powered by Feather (https://feathericons.com/). +*/ +span[class^='icon-'] { + display: inline-block; + height: 1em; + width: 1em; + vertical-align: -0.125em; + background-size: contain; + margin: 0 calc(var(--universal-margin) / 4); } + span[class^='icon-'].secondary { + -webkit-filter: invert(25%); + filter: invert(25%); } + span[class^='icon-'].inverse { + -webkit-filter: invert(100%); + filter: invert(100%); } + +span.icon-alert { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23111' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12' y2='16'%3E%3C/line%3E%3C/svg%3E"); } +span.icon-bookmark { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23111' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z'%3E%3C/path%3E%3C/svg%3E"); } +span.icon-calendar { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23111' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E"); } +span.icon-credit { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23111' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='1' y='4' width='22' height='16' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='1' y1='10' x2='23' y2='10'%3E%3C/line%3E%3C/svg%3E"); } +span.icon-edit { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23111' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 14.66V20a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h5.34'%3E%3C/path%3E%3Cpolygon points='18 2 22 6 12 16 8 16 8 12 18 2'%3E%3C/polygon%3E%3C/svg%3E"); } +span.icon-link { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23111' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6'%3E%3C/path%3E%3Cpolyline points='15 3 21 3 21 9'%3E%3C/polyline%3E%3Cline x1='10' y1='14' x2='21' y2='3'%3E%3C/line%3E%3C/svg%3E"); } +span.icon-help { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23111' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3'%3E%3C/path%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='17' x2='12' y2='17'%3E%3C/line%3E%3C/svg%3E"); } +span.icon-home { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23111' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z'%3E%3C/path%3E%3Cpolyline points='9 22 9 12 15 12 15 22'%3E%3C/polyline%3E%3C/svg%3E"); } +span.icon-info { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23111' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='16' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='8' x2='12' y2='8'%3E%3C/line%3E%3C/svg%3E"); } +span.icon-lock { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23111' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='11' width='18' height='11' rx='2' ry='2'%3E%3C/rect%3E%3Cpath d='M7 11V7a5 5 0 0 1 10 0v4'%3E%3C/path%3E%3C/svg%3E"); } +span.icon-mail { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23111' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z'%3E%3C/path%3E%3Cpolyline points='22,6 12,13 2,6'%3E%3C/polyline%3E%3C/svg%3E"); } +span.icon-location { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23111' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z'%3E%3C/path%3E%3Ccircle cx='12' cy='10' r='3'%3E%3C/circle%3E%3C/svg%3E"); } +span.icon-phone { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23111' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z'%3E%3C/path%3E%3C/svg%3E"); } +span.icon-rss { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23111' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 11a9 9 0 0 1 9 9'%3E%3C/path%3E%3Cpath d='M4 4a16 16 0 0 1 16 16'%3E%3C/path%3E%3Ccircle cx='5' cy='19' r='1'%3E%3C/circle%3E%3C/svg%3E"); } +span.icon-search { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23111' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E"); } +span.icon-settings { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23111' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='3'%3E%3C/circle%3E%3Cpath d='M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z'%3E%3C/path%3E%3C/svg%3E"); } +span.icon-share { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23111' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='18' cy='5' r='3'%3E%3C/circle%3E%3Ccircle cx='6' cy='12' r='3'%3E%3C/circle%3E%3Ccircle cx='18' cy='19' r='3'%3E%3C/circle%3E%3Cline x1='8.59' y1='13.51' x2='15.42' y2='17.49'%3E%3C/line%3E%3Cline x1='15.41' y1='6.51' x2='8.59' y2='10.49'%3E%3C/line%3E%3C/svg%3E"); } +span.icon-cart { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23111' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='9' cy='21' r='1'%3E%3C/circle%3E%3Ccircle cx='20' cy='21' r='1'%3E%3C/circle%3E%3Cpath d='M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6'%3E%3C/path%3E%3C/svg%3E"); } +span.icon-upload { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23111' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4'%3E%3C/path%3E%3Cpolyline points='17 8 12 3 7 8'%3E%3C/polyline%3E%3Cline x1='12' y1='3' x2='12' y2='15'%3E%3C/line%3E%3C/svg%3E"); } +span.icon-user { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23111' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2'%3E%3C/path%3E%3Ccircle cx='12' cy='7' r='4'%3E%3C/circle%3E%3C/svg%3E"); } + +/* + Definitions for utilities and helper classes. +*/ +/* Utility module CSS variable definitions */ +:root { + --generic-border-color: rgba(0, 0, 0, 0.3); + --generic-box-shadow: 0 0.25rem 0.25rem 0 rgba(0, 0, 0, 0.125), 0 0.125rem 0.125rem -0.125rem rgba(0, 0, 0, 0.25); } + +.hidden { + display: none !important; } + +.visually-hidden { + position: absolute !important; + width: 1px !important; + height: 1px !important; + margin: -1px !important; + border: 0 !important; + padding: 0 !important; + clip: rect(0 0 0 0) !important; + -webkit-clip-path: inset(100%) !important; + clip-path: inset(100%) !important; + overflow: hidden !important; } + +.bordered { + border: 0.0625rem solid var(--generic-border-color) !important; } + +.rounded { + border-radius: var(--universal-border-radius) !important; } + +.circular { + border-radius: 50% !important; } + +.shadowed { + box-shadow: var(--generic-box-shadow) !important; } + +.responsive-margin { + margin: calc(var(--universal-margin) / 4) !important; } + @media screen and (min-width: 500px) { + .responsive-margin { + margin: calc(var(--universal-margin) / 2) !important; } } + @media screen and (min-width: 1280px) { + .responsive-margin { + margin: var(--universal-margin) !important; } } + +.responsive-padding { + padding: calc(var(--universal-padding) / 4) !important; } + @media screen and (min-width: 500px) { + .responsive-padding { + padding: calc(var(--universal-padding) / 2) !important; } } + @media screen and (min-width: 1280px) { + .responsive-padding { + padding: var(--universal-padding) !important; } } + +@media screen and (max-width: 499px) { + .hidden-sm { + display: none !important; } } +@media screen and (min-width: 500px) and (max-width: 1279px) { + .hidden-md { + display: none !important; } } +@media screen and (min-width: 1280px) { + .hidden-lg { + display: none !important; } } +@media screen and (max-width: 499px) { + .visually-hidden-sm { + position: absolute !important; + width: 1px !important; + height: 1px !important; + margin: -1px !important; + border: 0 !important; + padding: 0 !important; + clip: rect(0 0 0 0) !important; + -webkit-clip-path: inset(100%) !important; + clip-path: inset(100%) !important; + overflow: hidden !important; } } +@media screen and (min-width: 500px) and (max-width: 1279px) { + .visually-hidden-md { + position: absolute !important; + width: 1px !important; + height: 1px !important; + margin: -1px !important; + border: 0 !important; + padding: 0 !important; + clip: rect(0 0 0 0) !important; + -webkit-clip-path: inset(100%) !important; + clip-path: inset(100%) !important; + overflow: hidden !important; } } +@media screen and (min-width: 1280px) { + .visually-hidden-lg { + position: absolute !important; + width: 1px !important; + height: 1px !important; + margin: -1px !important; + border: 0 !important; + padding: 0 !important; + clip: rect(0 0 0 0) !important; + -webkit-clip-path: inset(100%) !important; + clip-path: inset(100%) !important; + overflow: hidden !important; } } + +/*# sourceMappingURL=mini-default.css.map */ diff --git a/src/STM32CubeWL/Utilities/timer/_htmresc/st_logo.png b/src/STM32CubeWL/Utilities/timer/_htmresc/st_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..8b80057fd3a454a97de1c9d732b7fede82c83227 GIT binary patch literal 18616 zcmbTd^-~<*6D~X~?jgaQV8LAj0X_tm1Ydk1xVy{Z3GPmS;IP2r4oh%%cMl#Qcz~Pl zz5l>lZ`GVRHB&V|boY7A^z(F|Z=Y4=aIwg-006*MkpHOuZ?5<^0x;12-SsK9!v0Mt zmQpHG08kT${nrHb-!rC@ysj$%ki7ceKq56ESOEZeJ%x`_nqEey{^(v>eK${gL>pJ% zX8+KBAR_W-jhDrs{egi|sP<73DP`UFoa(>xj;8qknEx2bL~2@t%3k>}hnl@CWQrW@ zqfK>@e3$sL-m%ftg0YAkk!@=P!Ognuz(zhb|Tux{FeX<<7(5oLVU8=W*sUZ*$TqlSb6o1O0a zzeP#ZW!;?#>0N5v?0D|q?mzD8-<^@1V0FH{fY}2A9ooXbylcB6Y>PVo4nMxLi|AWA z8M(b#9`j|%0v7ktATOSzsh-T7%Wqa>t*x!29M*iDetE6#^`?iEoQW5F*w7rjcWYw>-UyKyDHetK@Im)qdu0o-zudq@gQN3)r z=(%XIh|%7(Y}2mODA6--)=u;7mi|lUCki50L@QOyZN@2N`Bwwn9et)BF?yQr9`Sn# ze!a;09%cuNiCJ+Hwx|5Sw&L`0rJvq<$7D5j#Y=O^YcW)1x!+MVRWRVHrXDj~g@40Q zBvp_niE6-dasJKX&t@%;X`7_R9QhT$w_Dv~zW73kCM;9WC z#^@^R#^^HZ#`rQ5ZjC*^uYUMgw=ae5*IV2JyEL@LlJ1k!yA8p=fmyQ={`Pjq&sK}Y>k9r>*Y-3njDRLc8z*D?su--n+y(fpV8FB zwS%vLw=L>F9>rMJzXaXgg5NRvaHPKO=qdV`%ecKE^q=CNs6^=Vl)5QG9h0>AKM-1F zvU-S)!Vnz~yg}XNmnaKSqm&}<1}#nOBCWZsLvn3_pkm8Z)~*KF8yv=yRk*!4rf$7T zT*ey^g`%>`O82HoVNPMCaM^5e_Eeop`^`Wsro=Q9SzJ-{LW5j1QdRH>Oq5bEX({TJ-TNGPvNBrk5{my=8FEQ%0fftv4 z)$FK)-usf%cyd|Y@=r@u!~HI3-5_Q=E%R!AkEqtv$Yv%Zit4K`i*n5tM!wdwLFM?% z@N0D&tLS9%TD>`41R~`%HzXtZS6pjo$}fsAA6cq`&Llq^TE@#ID4eU}(xZH$-0oa>g$RMe)N_S(=w@nXEL&?{|e zd%-=H@Ei^9kz3up?3!?QYr2O7^M9)q_E2E@^vESGQ&5WzDh<(QgQEd3BICrRm8O)S!fPO#z(h0}Vk) zolMw(Ecl!UD7xMUH0>?+9qzTMCMQxcM+Od*!L7F!tiwSSG>D@|J~*c~gu?`RewztA z1cO8*h9GGR{``zPp9t6vZJ81Ar<-bz38Jv-ro`wI#Mq&-k$*5tL<>Pk=)T1H_z8YhPJDWCuq5c#f&iDRo3$~XHhc-#T3{whJvB?;N^IKpX^H#=oYNa@u&^9He20t za7qlYKRH^S(Tj2{XC=lPI|MVMOVVX4V8cbx(9Ix%YK__iyN9E(k)118*aO-OzZNT# zbhE^f=Cze>bdhX>8xBFW70+=Tb@QnIyKKmQGt`}ZHXrVVWgxIT1k&eFDonM5iFh{^ z;FtT_qYo%x6$`ChDD~;i`c>h@T~X~pZ&-v==wrV4)ra@?=39Z}7c)OR&&9#@9uxU( z?hh)jyY_o}tH;1B>v%95XoGM@gDYB{I@;aJAn;N$2z~uDX|IL`uf-*Mm1ic21|E8c zQZWw`gvb==bz|iv=774j$zii$vlW@T4LDFEfea$Z+frqVA{<)qP_mhp2AbFqEE(0z zfCJgi{n&vKxpSY#-W)(E-Y3u@1KQGcnWN=qz;Nz2-6>bIL8wZk?oy8xe49zo9Evpm zI>QVA&&4C5*aCjxksX%9lfPpQNw|#TzMQ;YvC%Rx=uA#dmU{e@tzaW&rq}9N5VXBw z6Mff^1He^5U}j4TZD};Z7u2!LZ@OjGIPgR|MLZ*9%)E@0nE%K=W5s+NOT~n_{fBc9 z8DlU6un9om`MN~!FtpPXkJSq(+KPHqF&N23_vGeqphc*cEAF=okHGoFWHHWTm&R zAZXR)=q}Jv`jsvKCoL27h?ylNq0fz5xasR{P`5RW_7kzL^b_#T@e?r5nGKuMX?!lz zcEq|hYJscWj{YtO1of8Xi0jH z6s+!rS0;ag(Cml~|NKB+tNwwq9kl+8wc0!T$L$CFw95drNPiuZ3jOf4G_NXoM$sQj zZn*2v3^ISC(OoqO%W>m};%SHDOcD)D7%f&?jnrI9&1_u;6m(x2g#=wb zH$Cl!I6f#QI6iFo2i^nPy^8_Rt0g@Gzv3FoK629)r#wPie#!P^T*B)9JDi>Qta-Ee zyLS}t0#vL+3WcNfUo47o=g+h7Q(waq$0Fo`#^t+!ugP{n=lV`j6a9^vBl)I!L&VaI zK(10FWw?KM*=_ynJ3HIwyD^##=aKUk4u|yIYk$&C>^B?x{I5c+Il`m3RQ%_=Tq`!D zQw3HQ7dw%VR~rkqeqr+THi``YT){njI8j~%3VNWBl3EUyQ zx>y&BaDTkwjg$12&1?kD`IcCB_?j~8XMfHm4iQ(TCj7-)DOn-+%UzP)ab?nnNlfTA zh(FmGsK1tl`G8>eb=1j~9lDZPh<*?zhjW@Gx5%UjcH4 zbrrd<#%%JyFrW`_Loz= zP30^V%kIB;=&%K@{YbXT6@(|c>dXlNk~?15SVEmMX6`Mjv>+MN2M$^N?ju|1T-qoW zJQV;x5rIpTc>eCM*`;fq^U3U2uW>l1RVxe^4B$CEub2J}+bN)$=(gE92((ah@ar_) z+I|k<9;iL6@Dyhc+LX|pTR>r3{P!==s^guY!a#cZ5Ry6QtTzvk zUh~+ICB=TnC(!+~G1}X`=zKbJF=VNy60Le=gO@j5lEJet5>jc!PbM+D!ZlS$KuYx&pkm{S?k)BU1<65@ z({=ySGqzCiV-vc5qOJ z48y)rR(Ys{uWIjyQX*o`4?xK$K9nE1K!t$coI~(ku$IzWaVM`ocnY1)=&_o_R%I_2 zZ_{Cs>@7#7ktZS)0EENs++_HHh39c*#7z#Pyifk3+e!lsET`nm%a#Zp{hflp4Vw$+ zOju*)#0tN99xzE1;G}_c;Oj@<_%Z8;SCB3P74uOYE__wpp<3HB0g0wsxZ1toEwg)5 z23F}NQwRV%3UQi)GQQt^$a%zzV8w>aIl;CkQ!6h%=n!jXPZ;sfULBWNTi1QT%V~R| zdrjBQt+%&EcrjOO0&pO(SR|R1%nis?Q}KUl75Q=`bI5TGenEMls+QNXGp;Grr-EZVy`f(ovFSmI(u6D90n zU}rWOG+9F)ioe9yO)lx~AD<~|_xP=uVs4I z6w+kccIU+(Ltf0bDM$mvJrBdPzjnQ4w#L-qTZ+S6V5l=pqj|%(!m@K!R(Sm5G<;5V zXK~r#d34;M-;>*+VXbyWbw`4vdOanA^uK`Ag&w)G;7}_OpATxWe^GjFe%&*Ocx)w7 zwt4Bs4luF3C-9V+n~E!?(W3d6$CtEn7OZ{~I`6iW|1x;QzkF49GF&d=Wg#fC2^Vn?KLfW@n~pFc4gBpg!U$uFR0 z6`f||PCJat3glNlwW|z^j;^p%9oQc82S&N+!L>xWR*UT~JbFCj)0}2J6c-rV3iVO! z`IdFp zB0H{SvHRu;zx(EM(0%j9fA`HVZ|@5Oo0EGok@w*1K*{Sg3QERYynQ|7kzI{t_?~>T zQGQ|?TPR(EZYAFen;>d7>k zc`O4jwao>J?dp~fG@8l|SBHzOE5h7?Ba_OYs%93|;KP${8}j%VGb?LRi<;yffk06& zmc)TH`g@-+zt@fG!z|MO3057>Y}ppB{w8IS2o68)NnHSA-jKa+X$k+&Klw{5Ksly#ye_HBKV&h1zbIsIT-|0XRq)zWf_~s9{=n3BOfpPy7{f5RZzL^9tdzjj zr)R?-SV}4UX;&dWNKq={6q|g;FEbIjXC}?$K%uY_ur_MF+MkJ>-c@8l1|6F7^BR4N zf%t(1oJ!m zg^z<^ddW{6+A~!=F*1he)s`5=HR&3O@tjq)pn!{ zodn}X=d$=iUh-ibxQ>PQw|#fHTLppRwXG}*HyUkLKB?Vxf>#@2_z&V#B0Cjvmfka$ znI~k?Pp)A)OXy(kdOeH7nbmp9bNb|>|e%T7Dg>BKo&y=JzU)v zs{+P#O$)wko3MOQY!bv_78@Q%uABK!ZPIi<~iCxyQ>J*D53j_;0vks;+?UxqO^ z8)9k;>&t3F)oFofc_t(0cdCn(OIM;4fePgKSw+PKcigoQR9JV_C-y`&%By+|aMjTd z;$iN6>#`KNXtG+yNhfl+PYn(#cr;Nf>DZ1mRU`A-PFI}Scq~0EgRR31c4LZcz_w!3 zU&-x*oGPQoz`-m#bYEC;V<7tHiC(wn395M}YNU9p|6@2$$6(9N_DyMjuOwT6X&Cu> zXg1{_^+%NsBhDf;)3V~J5%bl|^XVjqRgu^moR2288%NOgcLoNBkN6t5F&l2`tPvao zfAbQy!&*Ln*uWc{tVDqwT1{Q>{s19S6+;c@2e$2eZd>zL~I~M}G^8w4Y2bnyq)>=S+L6j%|@%XWqbYm%+}R z%Jg=|X7Y&0*lujN6>tzy)?{CBuT|FT#I=sU+569+)8oyIH?8?{Y{Im(PMHAGs5_GI z>1wLl+yiE$+I28-c2!jx)_?k2nIm}7iH=O{X#yL$s@}hUPf^xece9Vi{DUPRKm%@= zI4q=C$Qla?I0{;1W!^-Bt)o=r>#KNZnZPW3piq_&q`~HLF~1_^MHlt66*62}BJqzu zM;g!LlycVJ?1ohPMvFHu3^-`<`sR(iyLG`EB|;bk%3GG!#?x`m5gx zWnZm7bb@UTrR9OXVs1t)?(5a%Yqq>?ivrob2S7W|CH$C|Kscw z=5hgFRsHTTA{lDQ(a0VW8vk$By+wL4Ao<5{Br)oU$x2pMfJKrlPqr@4P$Y9Nt_7R| zCx>hhMeHtjM0mJ|?T<(EIY{^^cAiA&R=2C=g&o@6vm!E&&86BrLOf18fr==x77OBH zdyOvB1fjqxDMa5;G9@=qu?tN_vB?)=#H^qB;g*jHrr^*ISGt+pLXyWcu+bAWNk&IG zl?zGxV&+)tmQ@d~T5Yypa4*^P5t*t6C($W-Y9zknsGLXPPDR^RF~`>QcV4iB%ltJg#%JgzSOl!L!d<7;Gfa5FAv zjVdBTD(TpZ3>zF8@VbIAM{aYtDv8fh>oAmOoV`*>G_abe#aOPM+6b%!IzPP2K{>A5U*>>2+^+79)a z;+jQ03qhGCNA7Yx7^lX9Ba9FuFHNen`s{buqNeEv)$x#QoePK6M~soRL17NVafu`4RB%F$`Pl z5~X9X{(zDkw(=x-=6pOllhfSrJCozywriAokKZ^VZ?epc?F2YfOmC=V98gW?oL=*# zC!4VJtdyAXwE6cHlNoijVy3KiZxeTrjL5AO4?|IT4#6gV63bUTC!(fd*MK@3^J@F! zOg&Y}^l`KyT>$RnH8O17_%?_PVh?o(+5L|_R7c|c+R_PRXb26L8QM&z+5MaH{wtOk zn}L=^TXs*WwrBLOJ6hDKim{LKAa3?WEiRefh;#TMZ3y1zA%QAUYh={Ux!GU!o~ zQNH$+pUp$BPoB27%q zF^6BflF{;t=SZSz+GrMJ3q~ti7gQ;5SbjS`5!DFxQB8KOt1OQ(G%_V;vcdj>K_dXjNxb}0M?HyjDs(afDCVx%>+I2GAO;jMfy0Iwh$=Utfm z5snMAm4|C3O1?MDEQ%I@RL1I{SrN67(Q)b*7k&Ip+-THJr%-;ILx=v!SaW75@EH3` zUhVOn4CYZ>iZ!iaGNBq9Be`Mcq5Opf?{HZfcJM-VDr$qSCy^3Lij|O&UW{&ffZ&!( zaA9$H9_5lFs;vRx6|mmn{Ic~u%y*(_t~*m12^>%iUOQ9Ap<@`U;!iRpBZ5y=p}@B6 zSP;R6QS{hs7)q75Mgj7814d~Bae=<{A1Z5>;LN66N?m?;5pl?`*_wW1l4a8IBb4tyR6@^@^BOm`{tD6YyAv};)Te2G+K}4;<~T9 ztiHbWTlGjD1=omQ_viT9PJOR7GjZ^{`7u?a_$hGpx54G9Z4Uj-NJ+>3SA0ZSx1vXw zLxYWusP2Sm*#o~_#B)vb&lTfmtsonTnPHIvx!#}HYvp=bPcZe zcHOCWuo0{MxR+#P#Pz1PSlaT$g-HbB!hTlHpV_F!Ay^U-vb1-6W)!xh?3imeOv*Z3 z=D=Ij-4e>!J=_Q#nqT5Fkomgv(@3uQo!?=8R9Sw(0)&ni z2jsV8*xm^OAO91C)$^*!X=%ZHvh_G35URQ9mZ|{A0)E?gJcL0T$H-NA92s6VF$CYW z9RHBse3R!V%B}9#+)P1_9L@j@2VcH-GZ=N2{$k05r?kj$KxpvthW zd7m|F4Ka%sEOHJC`oN z{Q9h2$S$VYkMHBEw7ybMx&7`nIaMLI5n~s)u5f7_tg^|2p4eFF&|6C45|-}T zY2bbCicJ7u0b>nvzMSvbBTOChoOAKvC$b5)Y}lT;{a-@oZBJ!oQNfsC36M4qtjvVR zX;Qkn$Pw56!sOMyw2f6>a4-#^ zy$1D*lt}-KofQ^atUig?;uYP;un=4nq7RPpS6+7^7eT`a+9Hs&(5Wu`IyLv0kJINP zH{2$kHb`Me^3C!975F7KG!qcJ%Ot-tp1f*bJffu1KR9B1lQ=XYBq15?hlJ33*QN-~ z25i$#OI}x{k+-P3EKo3v2XVk4?t;KE4nj1dk!Zo@w6D?!o#k^~T|3?;an*{_dc}rZ zWWWrKbdBu0k$7Zn5A%~0$lei$vU1P?CE&!L*!t%`ziuxu= z$+Xt=qUvFYn;a&JSK-D!mWnDWtF|5q!R|hT$Hv!*O-Hv$ zFMd5*W#~$3AJN-2|IVd@2bWN6TIfD_0uz(~vS50vn&4k2seimRF5`Q+1IS}!NNHN| zuWuQz50#5kO>f(wTSg+{VKXLrOZR$Gm~DhS1f%%-9{FGG$s*ZrqKZL|g5VaRU11N3WB;tGWJx5jj1rPZ1}$YE7~gsu zE25FmauDeN0tjmI!T8LA_@Jktp-r4gQRI3~pz@ext*^u56U%RNNACtB2^N&i&Zkq_ z`%gV|mr`$f?Rog-De|tRlA$9w&gIG-7Zqk}`K~S#ez0!r0TA4$*?1vW^S1eRHim+x~x!Fuo?ZZGGykdj`C(v!pIX!M7^#v%t*g zcznI+6jSi4g8knZOJ2XD^*-Nu8++1xNL67@Dpa}id>w3=oC<2l|TauHqSGbyr z9Lb=M3fe$ymZM2IcIy2$WhWPLfA8YEy!~$2XHICgk})!EbwTa@re-=DC1|8#7fNFq6gJ2K}GKAX`f_@q32jY5x4yTSxUH;`}j*L?c8b@JA9D(4X1n>r5 zmjA{5zUzqX9?77@2f4TGSC#Gv z>RXD%m8Sx#GLz`?10nyLA3f`rKtm)2mp8 z2WUMD#ZK*6rx@tHUO&Z&$15&*p$9S&RarVs7nI?jWCTx!i z0n`(39&^Y>ScN)8+_K-B#JBi}jEM2qqgbCqWKx*4*ll_rs)9n)b|4=f&23 zGJ5Ub{5j_`P?1;gHXtz{3VvNPjI4v63M z7VR-O|JQRM-E&ZagmZ6Y#+`oTU{Zdpg*T>rA?e2lXyimlx-MsB_vpS!^2jDQhm%@q z{n8XwoaYQc8y7Itb%2)$a=$~0tev`)%-s+AXZ8I@XV4DuPx#4Z3^R?1Q&1e*!{+@j zwy0-{m|^s)xqlSU>jQk{owo@5+inF)-p_24DlAw`pUe~G8ATB<-h>G97|FK_kfkQlN-!Xir7CB=dF)cJj`)++W>CeZ z0KpG5Ul%&-7q_N%mRtvtM37+jS>A#7p`RadxDFCIFsAEA)28 zRc#)^^3Z1>`W_P8_n+_5l5pGfayTk_=7^k}d#ir!c>8mR4k$J+> z7$;sN^3k#e1A<-CaO6F6V7^1u(puc4hVnfPK2u$wSE_XF>^Bp?OAv{2Y8)b{(a(2LFQfe!w)T1x>k{ZpuhTF(Y6rhpZbrH!ElxM! z5seXw{2(-vFEyNn8P2QzldxYgR;$=9Va+n>oR-HQXL;u7|E|m|OuX!t) z=Y4P{a-kdSJHXaCvpi=8=DW$Bomevgq&Ys4T71MX_~k_QpcOJ7j|>5e z8fKax8KCNY#00?1+;-F_`mYl6?wiA0M9-%AWH7g{~~uALu>r1q7;w|*!aJIeE{mR8WtR@KBhs8TcC2jA=CW|Xy-ycIi>d)c7Okmo?_;IS6kWJ z(`FLRj~hxiQw>hGi`}`RB+q+jpRWZ9z114q7dyj#>yMG?n=NfcSz}CGOi5Bt#D4u( zFREX`PCs3=cqxne=H=$udT;=|-YI7ij;hPlH)3oXm z`Zikh-OIS^*V9YKw;%r4iW?YA#ppM%LKP=jnMYQ)JEBqy1t4U@E<8VwMW2U*KvaS5 zNDwVyHjTg6hvcbS>{N7lJu=~^Ut)S#sq~v9%#hIV2H~>o^9=!kEGypac0E4e6TQIW zr~+Bn`Sb4k*0*Zts;f;Vq@fsZn1hLBQyIO8W(13u0211vHK)RMC5neH4xx7?6jMVOl3i-ENH1NU{ z-FW1hXwfmWi;TOg`k_dSL1ckNlukjE5IiKg=2DaEcWG#qTCd+ts`vavz;Wye>fPE6 zy5Y~H#6~R#r29XgZcKEUWF`#TkPjT0Tb$nr`$rM*rO!0=z{AwY-%*%Y>1iy07;xo= zlqRRR7Oc25bnNStf}IG@3`}b^k0oTD!zg(19YJjRnXs}9jracK>Fw6_hgpNk9M$d_ zY;%@p@*94vn6~^S;rS|c_SBN9%41Y5CNDz~xgJ>zs5bOlC^*0Hm`3d+UdEAQlhAJ~ z9rS!JpiEjf-g5TxWc*_}=Uu;kRBG#hg)R{HVt_KfnWZwXW)vK%qN^F`Uk1yRWlJX^%Xv zrk4pFBKoY0c4V8}-7;k5jeHn#no6bE=CpUiQ*YjAXr&^e4Ji=kd5l#`F`6lq$7V{v z3HxGM@4$C!_rCJ0-}}J#b+>i@#M5T@ zDq!my3QKfc?}%tQt*O2KZN233YvPN6nJ}^KNmAv>Z%4u&!~ecZRVXA}Vl6Juc1QC% z^+u0V1RbM%wwc6J;|v%G|8k{t}#XaV3b2aS>;{E0?a{QN?D zjap1}Foj*+4gOfLe03+j+-fGX6EVmh%q%{kCs18^=Y$ttM`Ru~Sih(@mxvo*(|OHJwq(zE2(ex%#gkzo*Y14gL&0 zb&R`Soa5K^wB%jo6cc>zQGL@J1IWOVy&G6nrZ5tClv8t|5cv^+Gb2^+T0kC3kdVb= zzt>d9Y8%qhJjVP{A;^*2E;@stxE=CCM8#hlN3jEzVQ}z~l*fFX-3jF?-%dnrKMp>* z+*ojsjy{>@Jvb5ZmHokSc4fmUNZRBEvkDd^(WV&AoGicLZM&xx+F?MzT8H=FtNK9| zS}XSejv}P(R*P5=IL)L^{d8bx{SC>9DDxXj4@z-n^Hya-p}k%LC>kvh2A}eK-{n8P z{ymeI^r5$}WuJ`hTT7y&m(wGugFoqC45jML$-|3L7JDo`mbG@4AeOa9^F5Xfc~AdJ z6z*HExRMYeE;qZsGE(eCPFCa$fMk$Uzn)5Lqpt$(K3(+J)whl&sJ0{&+hDO7rV zmH=Vx#~{t)BZI;GL9NP4eoCJAPi}V8s2_pM0^Qn!dLjeT+!j52$p%MSaS9-1=VIXE zZZI?CV3-Z~UNNk|?P_bEXiaFvcS$(=j(imNA_Txz*qk*3Zt> zNTsgN3vU6G(NEuWibkSSE-gZ&wr@}`tuvHEIJGFQY)vT7_Sn%Zf>;noCdR{II*9Uy zi1DPT!QZt9edc?XCO_%vF)Vha6tK-jiPV+wdZr2-8Z+moIE4fA9Um2wrmprd`ujDw zA4$!<#8*6C%(UP!wX!r@9XeCS{UX~rhBT6- z&m5@`REID~K)qRRLN40)>Fz=?P=C-jXZA1}lMo#Lic@|(zYtC?Sr$}gjz;wX-)dH; z>kQvsjFQ|FEvL5r4GE`Vi>HJ+qxMkQH`jx)M#C81t{fBmVaUEu2p_>}$^Lp*OiKYZg_C_ycw2+?0OT`)la$oyQwx zn_edD@HInp4-Gny;i{I~SnCp_RpFSS_!Eo_CI3DYHotlBCu`)~d17BV58M;K#oqAY zMpX+Xw9;xj#wpOozs(lT<+Th^5&14m(|Q*%;z`vKh4SNgAVBe}N~g2sLPrFC2|fE< zFpnnM-xp>{8@7DssTYKd@0S%KXilVkqrjiHGyiM<4X=4ToUoPe$O?bRyn$W!y*w+D z6&Dp2t9Ct*jrJO53Vv$UzniUP=-;pr=_NhmXKlFLRkmbSfW7QwHhvWb87Y|_ zx8ovSSXKm9h{zGnW$Hh-iI?ZMHSbjn*3Sh{-$#hX$;rQovTb9bL)q_$Wc zZmKiDhCM5p5vXSn($(MVPz`Tl^8Dq9O!MXzxdIh}Yi;I?zh>o(TXxwNlF}fbbJWC- z#GcWxTx796z)2UUjk&XWZFb3^oh-r)7Kkx{urkexT2D1!HLjPN~zvz2X#hz4#kSWLV*CW#DJu#do;exLU5E*Yb2H*HhXE&}5w)`L0O>xl{F?nRCT2 z*sv_q70&aZdR}eGSdA;#MccWyIlME%-v<$!Uv*^qnA&%(krwShZthK$iyit6H#l;> zK-^@!-w;mtEMfj7rnxx}?MKV=JHn^z-cHiGPN(d-mV0j(9hnwwg#l4%su_AWn&D=e zjR-cx9)55a@TwJcUi!8R@A2vD&T99g^diZcn-!n?8)u3269>8(cQRcMciiUGO^eip z5B)0E8kXbcz#sx*&|^TUl$Lb)lb&Ip>#TdtDfUcwzE~nzmuQ7EmTjAgdgUiGuSuNa zpCb6rE6(O5o(^pW-+RuE)g@nrZK=PFeQcL58r8o>9J$FQ<9+2A1d*DBdQ!b*dT;;4 z$Xo4EWN=S2^E$tAy9hSL=6Vn#bHD2g;0=sNhjJ6d)KUocZ)+A6o6_A*qTK}$*h#RS zyk#XkuOO@^1ht8v-%9N{Y9oewzu$e7L(scb^mXW2_TiW*-y)vNyH`OadIrI^Y>*Zd zp?=ROXFoq0Kk^tpwCFt$B)QKsZPM$&nJ*fs2;Xd)FtPd@FMUTnfVUp;sJHFaw;TuBTKR%BOW_}ClL_Bhz{A0l{Qgc%@tjIWj2ys8T z-56z(;=%E*LE!6!#2)6$>Eq4>1p;7`)Z_NSc1X=l%@0`gB7usIOR#p2{Cap%H#@u+ z`w+GL;VMer0DCjGMC|TGF_;&EgwZvSq=Q8@4}X7rF+n51h%CM@hl5WX$J z1a?I~km{+qh|RA-3+BNxgHjmg>KA!Bo!rA$QbB?cckI}KdkcLRox3JZd`fkXjx#A+ z_&En<1xc&Qmnoz0c*OV_guW?$J#uUHP(jS@beks0sZ#) z21ebzv6U?Wp@^S4Wn-$u_zmK3cE*C1Mlc5xAi|J_lu9>vY@H z+=VfBpk=&5g2V=pY;m2PHSN1`4hDAzs43VInEYm~-~S`AxRI%f?TU84wXtx z=s<1xk#OUIW)~ZG_2?E}ncAz?RlZ%Nu{wqJtc71aL~G>$Y^@Cl^I zh)|w&6EwGxERMm32{6|adN{lmCnO=?!|jUP3Ws1;e!SWGzjeq)Lvs!ZTTq&ie5vo- z`1p%Yqwt8KsRfc+Zbj`#L-1}(Bwi~Ax5qO&ZU@{ejQ+Hp4mt4VPoV_VeCr(6zF z9UR1ae&+2iX+s6E2V}Lxc6ZM+-8S6$a@?&Cn^C~=sPX~d#JLm;5Qw1n%IW*&PBV?q z09O(5{}gEc5xG_jOowcjF=x4y(&YamY5r}Y`?S#80Bh&J&-}>XgL{roRVEZo{x*i~ ziq&;TCj2%^Ju@%&4lTnyhe)5-5PDrQb*+9kAHW!EOaiu61g8cl_=CS1bA@HjhP}H5 zEBJUSKy2WF;ua_T{{-d-8TdvHidCA`BXq&j4cFtL z^yXVy20#nD1@%y@Y5U4sF1MvXa8K;F7B|Z;gH>tspveGY5S|}@U_A#|Imi?6GS1f%=ROP|BEkV#WqVG3b_;n2 z;H#;^adfh%ovD>w5Gs4>tI$7iJW3x%2mWus`fl%IFZf2qhN?JgWZYM_WBdsAyZ9Ln zRkEUt($@b`?c4fgl`7mn2lzu)}t zF)QPs=rMRr?Dp9+=yMv@`)?NKswHtVMS+34S>A@W)D9NFirDEhF)P8UhG0LzO-*O0 zw~iYtAHX;-bhAs~r#R<26~a<=Te-BB1z_}yavF7s_X>@Au~8kI-fv?*ch&2-MEDeRpn$| zQs#J6{sP}E#c@zKLH{=n*1NNgxp^;34)cyq+y$_nMaXHdPefdQB&ZYuaBF&F+#jI) z5iI(HZ*=0~V#^Xg^oqt{LGBS3`Mzzz-b6=qrl1#6B|u? z)MRjg9LIM9!?@uFajP;=#Ssg@2~wUs91pUhTWF1+X;!z;#!7zZ!HA3(S&VVh0-H-7)D5Ez?jhb5*13LRK%!y+ z0JbakM=Tfr@d$}P-7SM{#QqrU2pOeg#laPR_u*ECoxGxwD+5qp7mJFAC4KD`kx<@y z!H-TwF(`nXfja!2zxynS|Kfw?Nv{=+iYwx~iR_4 zsDFPJT72Tn&;L~mWIpqIHR?q6{H5=03xogjIQ00LT=Sm?Yu??dTo^X%GTU3y3 z5U%wt^lQ~lI;@oqpCR=JSG?o&&sGC)JkTBL$iPQn)gVhj=u1Ww=)nAbnfA|CTF1W} zHDFT%X57(fTIQ+HQ=ZLM-4b?z)=H^8gSHr jqXrx`;HZHtT?79Qd=?ufS>7*000000NkvXXu0mjfyH5ns literal 0 HcmV?d00001 diff --git a/src/STM32CubeWL/Utilities/timer/stm32_timer.c b/src/STM32CubeWL/Utilities/timer/stm32_timer.c new file mode 100644 index 0000000..d24fa56 --- /dev/null +++ b/src/STM32CubeWL/Utilities/timer/stm32_timer.c @@ -0,0 +1,542 @@ +/*! + * \file timer.c + * + * \brief Timer objects and scheduling management implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + */ + +/****************************************************************************** + * @file stm32_timer.c + * @author MCD Application Team + * @brief Time server utility + ****************************************************************************** + * @attention + * + *

© Copyright (c) 2019 STMicroelectronics. + * All rights reserved.

+ * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + +/* Includes ------------------------------------------------------------------*/ +#include "stm32_timer.h" + +/** @addtogroup TIMER_SERVER + * @{ + */ + +/* Private macro -----------------------------------------------------------*/ +/** + * @defgroup TIMER_SERVER_private_macro TIMER_SERVER private macros + * @{ + */ +/** + * @brief macro definition to initialize a critical section. + * + */ +#ifndef UTIL_TIMER_INIT_CRITICAL_SECTION + #define UTIL_TIMER_INIT_CRITICAL_SECTION( ) +#endif + +/** + * @brief macro definition to enter a critical section. + * + */ +#ifndef UTIL_TIMER_ENTER_CRITICAL_SECTION + #define UTIL_TIMER_ENTER_CRITICAL_SECTION( ) UTILS_ENTER_CRITICAL_SECTION( ) +#endif + +/** + * @brief macro definition to exit a critical section. + * + */ +#ifndef UTIL_TIMER_EXIT_CRITICAL_SECTION + #define UTIL_TIMER_EXIT_CRITICAL_SECTION( ) UTILS_EXIT_CRITICAL_SECTION( ) +#endif +/** + * @} + */ + +/* Private variables -----------------------------------------------------------*/ +/** + * @defgroup TIMER_SERVER_private_varaible TIMER_SERVER private variable + * @{ + */ + +/** + * @brief Timers list head pointer + * + */ +static UTIL_TIMER_Object_t *TimerListHead = NULL; + +/** + * @} + */ + +/** + * @defgroup TIMER_SERVER_private_function TIMER_SERVER private function + * @{ + */ + +void TimerInsertNewHeadTimer( UTIL_TIMER_Object_t *TimerObject ); +void TimerInsertTimer( UTIL_TIMER_Object_t *TimerObject ); +void TimerSetTimeout( UTIL_TIMER_Object_t *TimerObject ); +bool TimerExists( UTIL_TIMER_Object_t *TimerObject ); + +/** + * @} + */ + +/* Functions Definition ------------------------------------------------------*/ +/** + * @addtogroup TIMER_SERVER_exported_function + * @{ + */ + +UTIL_TIMER_Status_t UTIL_TIMER_Init(void) +{ + UTIL_TIMER_INIT_CRITICAL_SECTION(); + TimerListHead = NULL; + return UTIL_TimerDriver.InitTimer(); +} + +UTIL_TIMER_Status_t UTIL_TIMER_DeInit(void) +{ + return UTIL_TimerDriver.DeInitTimer(); +} + +UTIL_TIMER_Status_t UTIL_TIMER_Create( UTIL_TIMER_Object_t *TimerObject, uint32_t PeriodValue, UTIL_TIMER_Mode_t Mode, void ( *Callback )( void *), void *Argument) +{ + if((TimerObject != NULL) && (Callback != NULL)) + { + TimerObject->Timestamp = 0U; + TimerObject->ReloadValue = UTIL_TimerDriver.ms2Tick(PeriodValue); + TimerObject->IsPending = 0U; + TimerObject->IsRunning = 0U; + TimerObject->IsReloadStopped = 0U; + TimerObject->Callback = Callback; + TimerObject->argument = Argument; + TimerObject->Mode = Mode; + TimerObject->Next = NULL; + return UTIL_TIMER_OK; + } + else + { + return UTIL_TIMER_INVALID_PARAM; + } +} + +UTIL_TIMER_Status_t UTIL_TIMER_Start( UTIL_TIMER_Object_t *TimerObject) +{ + UTIL_TIMER_Status_t ret = UTIL_TIMER_OK; + uint32_t elapsedTime; + uint32_t minValue; + uint32_t ticks; + + if(( TimerObject != NULL ) && ( TimerExists( TimerObject ) == false ) && (TimerObject->IsRunning == 0U)) + { + UTIL_TIMER_ENTER_CRITICAL_SECTION(); + ticks = TimerObject->ReloadValue; + minValue = UTIL_TimerDriver.GetMinimumTimeout( ); + + if( ticks < minValue ) + { + ticks = minValue; + } + + TimerObject->Timestamp = ticks; + TimerObject->IsPending = 0U; + TimerObject->IsRunning = 1U; + TimerObject->IsReloadStopped = 0U; + if( TimerListHead == NULL ) + { + UTIL_TimerDriver.SetTimerContext(); + TimerInsertNewHeadTimer( TimerObject ); /* insert a timeout at now+obj->Timestamp */ + } + else + { + elapsedTime = UTIL_TimerDriver.GetTimerElapsedTime( ); + TimerObject->Timestamp += elapsedTime; + + if( TimerObject->Timestamp < TimerListHead->Timestamp ) + { + TimerInsertNewHeadTimer( TimerObject); + } + else + { + TimerInsertTimer( TimerObject); + } + } + UTIL_TIMER_EXIT_CRITICAL_SECTION(); + } + else + { + ret = UTIL_TIMER_INVALID_PARAM; + } + return ret; +} + +UTIL_TIMER_Status_t UTIL_TIMER_StartWithPeriod( UTIL_TIMER_Object_t *TimerObject, uint32_t PeriodValue) +{ + UTIL_TIMER_Status_t ret = UTIL_TIMER_OK; + + if(NULL == TimerObject) + { + ret = UTIL_TIMER_INVALID_PARAM; + } + else + { + TimerObject->ReloadValue = UTIL_TimerDriver.ms2Tick(PeriodValue); + if(TimerExists(TimerObject)) + { + (void)UTIL_TIMER_Stop(TimerObject); + } + ret = UTIL_TIMER_Start(TimerObject); + } + return ret; +} + +UTIL_TIMER_Status_t UTIL_TIMER_Stop( UTIL_TIMER_Object_t *TimerObject ) +{ + UTIL_TIMER_Status_t ret = UTIL_TIMER_OK; + + if (NULL != TimerObject) + { + UTIL_TIMER_ENTER_CRITICAL_SECTION(); + UTIL_TIMER_Object_t* prev = TimerListHead; + UTIL_TIMER_Object_t* cur = TimerListHead; + TimerObject->IsReloadStopped = 1U; + + /* List is empty or the Obj to stop does not exist */ + if(NULL != TimerListHead) + { + TimerObject->IsRunning = 0U; + + if( TimerListHead == TimerObject ) /* Stop the Head */ + { + TimerListHead->IsPending = 0; + if( TimerListHead->Next != NULL ) + { + TimerListHead = TimerListHead->Next; + TimerSetTimeout( TimerListHead ); + } + else + { + UTIL_TimerDriver.StopTimerEvt( ); + TimerListHead = NULL; + } + } + else /* Stop an object within the list */ + { + while( cur != NULL ) + { + if( cur == TimerObject ) + { + if( cur->Next != NULL ) + { + cur = cur->Next; + prev->Next = cur; + } + else + { + cur = NULL; + prev->Next = cur; + } + break; + } + else + { + prev = cur; + cur = cur->Next; + } + } + } + ret = UTIL_TIMER_OK; + } + UTIL_TIMER_EXIT_CRITICAL_SECTION(); + } + else + { + ret = UTIL_TIMER_INVALID_PARAM; + } + return ret; +} + +UTIL_TIMER_Status_t UTIL_TIMER_SetPeriod(UTIL_TIMER_Object_t *TimerObject, uint32_t NewPeriodValue) +{ + UTIL_TIMER_Status_t ret = UTIL_TIMER_OK; + + if(NULL == TimerObject) + { + ret = UTIL_TIMER_INVALID_PARAM; + } + else + { + TimerObject->ReloadValue = UTIL_TimerDriver.ms2Tick(NewPeriodValue); + if(TimerExists(TimerObject)) + { + (void)UTIL_TIMER_Stop(TimerObject); + ret = UTIL_TIMER_Start(TimerObject); + } + } + return ret; +} + +UTIL_TIMER_Status_t UTIL_TIMER_SetReloadMode(UTIL_TIMER_Object_t *TimerObject, UTIL_TIMER_Mode_t ReloadMode) +{ + UTIL_TIMER_Status_t ret = UTIL_TIMER_OK; + + if(NULL == TimerObject) + { + ret = UTIL_TIMER_INVALID_PARAM; + } + else + { + TimerObject->Mode = ReloadMode; + } + return ret; +} + +UTIL_TIMER_Status_t UTIL_TIMER_GetRemainingTime(UTIL_TIMER_Object_t *TimerObject, uint32_t *ElapsedTime) +{ + UTIL_TIMER_Status_t ret = UTIL_TIMER_OK; + if(TimerExists(TimerObject)) + { + uint32_t time = UTIL_TimerDriver.GetTimerElapsedTime(); + if (TimerObject->Timestamp < time ) + { + *ElapsedTime = 0; + } + else + { + *ElapsedTime = TimerObject->Timestamp - time; + } + } + else + { + ret = UTIL_TIMER_INVALID_PARAM; + } + return ret; +} + +uint32_t UTIL_TIMER_IsRunning( UTIL_TIMER_Object_t *TimerObject ) +{ + if( TimerObject != NULL ) + { + return TimerObject->IsRunning; + } + else + { + return 0; + } +} + +uint32_t UTIL_TIMER_GetFirstRemainingTime(void) +{ + uint32_t NextTimer = 0xFFFFFFFFU; + + if(TimerListHead != NULL) + { + (void)UTIL_TIMER_GetRemainingTime(TimerListHead, &NextTimer); + } + return NextTimer; +} + +void UTIL_TIMER_IRQ_Handler( void ) +{ + UTIL_TIMER_Object_t* cur; + uint32_t old, now, DeltaContext; + + UTIL_TIMER_ENTER_CRITICAL_SECTION(); + + old = UTIL_TimerDriver.GetTimerContext( ); + now = UTIL_TimerDriver.SetTimerContext( ); + + DeltaContext = now - old; /*intentional wrap around */ + + /* update timeStamp based upon new Time Reference*/ + /* because delta context should never exceed 2^32*/ + if ( TimerListHead != NULL ) + { + cur = TimerListHead; + do { + if (cur->Timestamp > DeltaContext) + { + cur->Timestamp -= DeltaContext; + } + else + { + cur->Timestamp = 0; + } + cur = cur->Next; + } while(cur != NULL); + } + + /* Execute expired timer and update the list */ + while ((TimerListHead != NULL) && ((TimerListHead->Timestamp == 0U) || (TimerListHead->Timestamp < UTIL_TimerDriver.GetTimerElapsedTime( )))) + { + cur = TimerListHead; + TimerListHead = TimerListHead->Next; + cur->IsPending = 0; + cur->IsRunning = 0; + cur->Callback(cur->argument); + if(( cur->Mode == UTIL_TIMER_PERIODIC) && (cur->IsReloadStopped == 0U)) + { + (void)UTIL_TIMER_Start(cur); + } + } + + /* start the next TimerListHead if it exists and it is not pending*/ + if(( TimerListHead != NULL ) && (TimerListHead->IsPending == 0U)) + { + TimerSetTimeout( TimerListHead ); + } + UTIL_TIMER_EXIT_CRITICAL_SECTION(); +} + +UTIL_TIMER_Time_t UTIL_TIMER_GetCurrentTime(void) +{ + uint32_t now = UTIL_TimerDriver.GetTimerValue( ); + return UTIL_TimerDriver.Tick2ms(now); +} + +UTIL_TIMER_Time_t UTIL_TIMER_GetElapsedTime(UTIL_TIMER_Time_t past ) +{ + uint32_t nowInTicks = UTIL_TimerDriver.GetTimerValue( ); + uint32_t pastInTicks = UTIL_TimerDriver.ms2Tick( past ); + /* intentional wrap around. Works Ok if tick duation below 1ms */ + return UTIL_TimerDriver.Tick2ms( nowInTicks- pastInTicks ); +} + +/** + * @} + */ + +/**************************** Private functions *******************************/ + +/** + * @addtogroup TIMER_SERVER_private_function + * + * @{ + */ +/** + * @brief Check if the Object to be added is not already in the list + * + * @param TimerObject Structure containing the timer object parameters + * @retval 1 (the object is already in the list) or 0 + */ +bool TimerExists( UTIL_TIMER_Object_t *TimerObject ) +{ + UTIL_TIMER_Object_t* cur = TimerListHead; + + while( cur != NULL ) + { + if( cur == TimerObject ) + { + return true; + } + cur = cur->Next; + } + return false; +} + +/** + * @brief Sets a timeout with the duration "timestamp" + * + * @param TimerObject Structure containing the timer object parameters + */ +void TimerSetTimeout( UTIL_TIMER_Object_t *TimerObject ) +{ + uint32_t minTicks= UTIL_TimerDriver.GetMinimumTimeout( ); + TimerObject->IsPending = 1; + + /* In case deadline too soon */ + if(TimerObject->Timestamp < (UTIL_TimerDriver.GetTimerElapsedTime( ) + minTicks) ) + { + TimerObject->Timestamp = UTIL_TimerDriver.GetTimerElapsedTime( ) + minTicks; + } + UTIL_TimerDriver.StartTimerEvt( TimerObject->Timestamp ); +} + +/** + * @brief Adds a timer to the list. + * + * @remark The list is automatically sorted. The list head always contains the + * next timer to expire. + * + * @param TimerObject Structure containing the timer object parameters + */ +void TimerInsertTimer( UTIL_TIMER_Object_t *TimerObject) +{ + UTIL_TIMER_Object_t* cur = TimerListHead; + UTIL_TIMER_Object_t* next = TimerListHead->Next; + + while (cur->Next != NULL ) + { + if( TimerObject->Timestamp > next->Timestamp ) + { + cur = next; + next = next->Next; + } + else + { + cur->Next = TimerObject; + TimerObject->Next = next; + return; + + } + } + cur->Next = TimerObject; + TimerObject->Next = NULL; +} + +/** + * @brief Adds or replace the head timer of the list. + * + * @param TimerObject Structure containing the timer object parameters + * + * @remark The list is automatically sorted. The list head always contains the + * next timer to expire. + */ +void TimerInsertNewHeadTimer( UTIL_TIMER_Object_t *TimerObject ) +{ + UTIL_TIMER_Object_t* cur = TimerListHead; + + if( cur != NULL ) + { + cur->IsPending = 0; + } + + TimerObject->Next = cur; + TimerListHead = TimerObject; + TimerSetTimeout( TimerListHead ); +} + +/** + * @} + */ + +/** + * @} + */ + diff --git a/src/STM32CubeWL/Utilities/timer/stm32_timer.h b/src/STM32CubeWL/Utilities/timer/stm32_timer.h new file mode 100644 index 0000000..d18e935 --- /dev/null +++ b/src/STM32CubeWL/Utilities/timer/stm32_timer.h @@ -0,0 +1,292 @@ +/*! + * \file timer.h + * + * \brief Timer objects and scheduling management implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + */ + +/****************************************************************************** + * @file stm32_timer.h + * @author MCD Application Team + * @brief This is the header of the timer server driver + ****************************************************************************** + * @attention + * + *

© Copyright (c) 2019 STMicroelectronics. + * All rights reserved.

+ * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef UTIL_TIME_SERVER_H__ +#define UTIL_TIME_SERVER_H__ + +#ifdef __cplusplus + + extern "C" { +#endif + + /** @defgroup TIMER_SERVER timer server + * @{ + */ + +/* Includes ------------------------------------------------------------------*/ +#include +#include +#include +#include +#include "../../../BSP/utilities_conf.h" + +/* Exported types ------------------------------------------------------------*/ +/** @defgroup TIMER_SERVER_exported_TypeDef TIMER_SERVER exported Typedef + * @{ + */ + +/** + * @brief Timer mode + */ +typedef enum { + UTIL_TIMER_ONESHOT = 0, /*! Date: Thu, 23 Jun 2022 23:45:54 +0200 Subject: [PATCH 06/73] Add timer driver based on RTC HAL The main part of this commit is the timer_if.c code, which implements UTIL_TimerDriver using the RTC and direct HAL calls. rtc.c/h handles RTC initialization. This commit uses a BSD-licensed version of these files, which was privately provided by ST. These files are identical to the files from STM32CMubeWL 1.2.0, except for the license header. STM32CubeWL/Projects/NUCLEO-WL55JC/Applications/LoRaWAN/LoRaWAN_End_Node/Core/*/timer_if.[ch] STM32CubeWL/Projects/NUCLEO-WL55JC/Applications/LoRaWAN/LoRaWAN_End_Node/Core/*/rtc.[ch] The files added in this commit have only been modified using fix-include.py, and by adding an alarm IRQ handler to rtc.c. main.h is a very much stripped down version of that file, containing just some RTC settings used by both other files. --- src/BSP/main.h | 50 +++++ src/BSP/rtc.c | 156 ++++++++++++++ src/BSP/rtc.h | 53 +++++ src/BSP/timer_if.c | 523 +++++++++++++++++++++++++++++++++++++++++++++ src/BSP/timer_if.h | 172 +++++++++++++++ 5 files changed, 954 insertions(+) create mode 100644 src/BSP/main.h create mode 100644 src/BSP/rtc.c create mode 100644 src/BSP/rtc.h create mode 100644 src/BSP/timer_if.c create mode 100644 src/BSP/timer_if.h diff --git a/src/BSP/main.h b/src/BSP/main.h new file mode 100644 index 0000000..cdf7dd0 --- /dev/null +++ b/src/BSP/main.h @@ -0,0 +1,50 @@ +#pragma once + +/** + ****************************************************************************** + * @file main.h + * @author Matthijs Kooijman + * @brief Header to supply RTC settings for timer_if.c and rtc.c. + * + * Originally supplied other things as well, but to allow using other + * files without changes, the filename (which is no longer really + * appropriate) is kept as-is. + * + ****************************************************************************** + * Copyright (c) 2022 STMicroelectronics. + * All rights reserved. + * + * Revised BSD License - https://spdx.org/licenses/BSD-3-Clause.html + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. + */ + +/* Includes ------------------------------------------------------------------*/ +#include + +/* Private defines -----------------------------------------------------------*/ +#define RTC_N_PREDIV_S 10 +#define RTC_PREDIV_S ((1<Instance==RTC) + { + /* USER CODE BEGIN RTC_MspInit 0 */ + + /* USER CODE END RTC_MspInit 0 */ + + /** Initializes the peripherals clocks + */ + PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC; + PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE; + + if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) + { + Error_Handler(); + } + + /* RTC clock enable */ + __HAL_RCC_RTC_ENABLE(); + __HAL_RCC_RTCAPB_CLK_ENABLE(); + + /* RTC interrupt Init */ + HAL_NVIC_SetPriority(TAMP_STAMP_LSECSS_SSRU_IRQn, 0, 0); + HAL_NVIC_EnableIRQ(TAMP_STAMP_LSECSS_SSRU_IRQn); + 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 */ + } +} + +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(); + __HAL_RCC_RTCAPB_CLK_DISABLE(); + + /* RTC interrupt Deinit */ + HAL_NVIC_DisableIRQ(TAMP_STAMP_LSECSS_SSRU_IRQn); + HAL_NVIC_DisableIRQ(RTC_Alarm_IRQn); + /* USER CODE BEGIN RTC_MspDeInit 1 */ + + /* USER CODE END RTC_MspDeInit 1 */ + } +} + +void RTC_Alarm_IRQHandler(void) +{ + /* USER CODE BEGIN RTC_Alarm_IRQn 0 */ + + /* USER CODE END RTC_Alarm_IRQn 0 */ + HAL_RTC_AlarmIRQHandler(&hrtc); + /* USER CODE BEGIN RTC_Alarm_IRQn 1 */ + + /* USER CODE END RTC_Alarm_IRQn 1 */ +} + +/* USER CODE BEGIN 1 */ + +/* USER CODE END 1 */ diff --git a/src/BSP/rtc.h b/src/BSP/rtc.h new file mode 100644 index 0000000..a3c5be7 --- /dev/null +++ b/src/BSP/rtc.h @@ -0,0 +1,53 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file rtc.h + * @brief This file contains all the function prototypes for + * the rtc.c file + ****************************************************************************** + * @attention + * + * Copyright (c) 2022 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 + * + ****************************************************************************** + */ +/* USER CODE END Header */ +/* Define to prevent recursive inclusion -------------------------------------*/ +#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; + +/* USER CODE BEGIN Private defines */ + +/* USER CODE END Private defines */ + +void MX_RTC_Init(void); + +/* USER CODE BEGIN Prototypes */ + +/* USER CODE END Prototypes */ + +#ifdef __cplusplus +} +#endif + +#endif /* __RTC_H__ */ + diff --git a/src/BSP/timer_if.c b/src/BSP/timer_if.c new file mode 100644 index 0000000..abbd9e4 --- /dev/null +++ b/src/BSP/timer_if.c @@ -0,0 +1,523 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file timer_if.c + * @author MCD Application Team + * @brief Configure RTC Alarm, Tick and Calendar manager + ****************************************************************************** + * @attention + * + * Copyright (c) 2022 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 + * + ****************************************************************************** + */ +/* USER CODE END Header */ + +/* Includes ------------------------------------------------------------------*/ +#include +#include "timer_if.h" +#include "main.h" /*for STM32CubeMX generated RTC_N_PREDIV_S and RTC_N_PREDIV_A*/ +#include "rtc.h" +// #include "stm32_lpm.h" +// #include "utilities_def.h" +#include "stm32wlxx_ll_rtc.h" + +/* USER CODE BEGIN Includes */ + +/* USER CODE END Includes */ + +/* External variables ---------------------------------------------------------*/ +/** + * @brief RTC handle + */ +extern RTC_HandleTypeDef hrtc; + +/** + * @brief Timer driver callbacks handler + */ +const UTIL_TIMER_Driver_s UTIL_TimerDriver = +{ + TIMER_IF_Init, + NULL, + + TIMER_IF_StartTimer, + TIMER_IF_StopTimer, + + TIMER_IF_SetTimerContext, + TIMER_IF_GetTimerContext, + + TIMER_IF_GetTimerElapsedTime, + TIMER_IF_GetTimerValue, + TIMER_IF_GetMinimumTimeout, + + TIMER_IF_Convert_ms2Tick, + TIMER_IF_Convert_Tick2ms, +}; + +/** + * @brief SysTime driver callbacks handler + */ +const UTIL_SYSTIM_Driver_s UTIL_SYSTIMDriver = +{ + TIMER_IF_BkUp_Write_Seconds, + TIMER_IF_BkUp_Read_Seconds, + TIMER_IF_BkUp_Write_SubSeconds, + TIMER_IF_BkUp_Read_SubSeconds, + TIMER_IF_GetTime, +}; + +/* USER CODE BEGIN EV */ + +/* USER CODE END EV */ + +/* Private typedef -----------------------------------------------------------*/ +/* USER CODE BEGIN PTD */ + +/* USER CODE END PTD */ + +/* Private define ------------------------------------------------------------*/ +/** + * @brief Minimum timeout delay of Alarm in ticks + */ +#define MIN_ALARM_DELAY 3 + +/** + * @brief Backup seconds register + */ +#define RTC_BKP_SECONDS RTC_BKP_DR0 + +/** + * @brief Backup subseconds register + */ +#define RTC_BKP_SUBSECONDS RTC_BKP_DR1 + +/** + * @brief Backup msbticks register + */ +#define RTC_BKP_MSBTICKS RTC_BKP_DR2 + +/* #define RTIF_DEBUG */ + +/** + * @brief Map UTIL_TIMER_IRQ can be overridden in utilities_conf.h to Map on Task rather then Isr + */ +#ifndef UTIL_TIMER_IRQ_MAP_INIT +#define UTIL_TIMER_IRQ_MAP_INIT() +#endif /* UTIL_TIMER_IRQ_MAP_INIT */ + +#ifndef UTIL_TIMER_IRQ_MAP_PROCESS +#define UTIL_TIMER_IRQ_MAP_PROCESS() UTIL_TIMER_IRQ_Handler() +#endif /* UTIL_TIMER_IRQ_MAP_PROCESS */ + +/* USER CODE BEGIN PD */ + +/* USER CODE END PD */ + +/* Private macro -------------------------------------------------------------*/ +#ifdef RTIF_DEBUG +#include "sys_app.h" /*for app_log*/ +/** + * @brief Post the RTC log string format to the circular queue for printing in using the polling mode + */ +#define TIMER_IF_DBG_PRINTF(...) do{ {UTIL_ADV_TRACE_COND_FSend(VLEVEL_ALWAYS, T_REG_OFF, TS_OFF, __VA_ARGS__);} }while(0); +#else +/** + * @brief not used + */ +#define TIMER_IF_DBG_PRINTF(...) +#endif /* RTIF_DEBUG */ + +/* USER CODE BEGIN PM */ + +/* USER CODE END PM */ + +/* Private variables ---------------------------------------------------------*/ +/** + * @brief Indicates if the RTC is already Initialized or not + */ +static bool RTC_Initialized = false; + +/** + * @brief RtcTimerContext + */ +static uint32_t RtcTimerContext = 0; + +/* USER CODE BEGIN PV */ + +/* USER CODE END PV */ + +/* Private function prototypes -----------------------------------------------*/ +/** + * @brief Get rtc timer Value in rtc tick + * @return val the rtc timer value (upcounting) + */ +static inline uint32_t GetTimerTicks(void); + +/** + * @brief Writes MSBticks to backup register + * Absolute RTC time in tick is (MSBticks)<<32 + (32bits binary counter) + * @note MSBticks incremented every time the 32bits RTC timer wraps around (~44days) + * @param[in] MSBticks + */ +static void TIMER_IF_BkUp_Write_MSBticks(uint32_t MSBticks); + +/** + * @brief Reads MSBticks from backup register + * Absolute RTC time in tick is (MSBticks)<<32 + (32bits binary counter) + * @note MSBticks incremented every time the 32bits RTC timer wraps around (~44days) + * @retval MSBticks + */ +static uint32_t TIMER_IF_BkUp_Read_MSBticks(void); + +/* USER CODE BEGIN PFP */ + +/* USER CODE END PFP */ + +/* Exported functions ---------------------------------------------------------*/ +UTIL_TIMER_Status_t TIMER_IF_Init(void) +{ + UTIL_TIMER_Status_t ret = UTIL_TIMER_OK; + /* USER CODE BEGIN TIMER_IF_Init */ + + /* USER CODE END TIMER_IF_Init */ + if (RTC_Initialized == false) + { + hrtc.IsEnabled.RtcFeatures = UINT32_MAX; + /*Init RTC*/ + MX_RTC_Init(); + /*Stop Timer */ + TIMER_IF_StopTimer(); + /** DeActivate the Alarm A enabled by STM32CubeMX during MX_RTC_Init() */ + HAL_RTC_DeactivateAlarm(&hrtc, RTC_ALARM_A); + /*overload RTC feature enable*/ + hrtc.IsEnabled.RtcFeatures = UINT32_MAX; + + /*Enable Direct Read of the calendar registers (not through Shadow) */ + HAL_RTCEx_EnableBypassShadow(&hrtc); + /*Initialize MSB ticks*/ + TIMER_IF_BkUp_Write_MSBticks(0); + + TIMER_IF_SetTimerContext(); + + /* Register a task to associate to UTIL_TIMER_Irq() interrupt */ + UTIL_TIMER_IRQ_MAP_INIT(); + + RTC_Initialized = true; + } + + /* USER CODE BEGIN TIMER_IF_Init_Last */ + + /* USER CODE END TIMER_IF_Init_Last */ + return ret; +} + +UTIL_TIMER_Status_t TIMER_IF_StartTimer(uint32_t timeout) +{ + UTIL_TIMER_Status_t ret = UTIL_TIMER_OK; + /* USER CODE BEGIN TIMER_IF_StartTimer */ + + /* USER CODE END TIMER_IF_StartTimer */ + RTC_AlarmTypeDef sAlarm = {0}; + /*Stop timer if one is already started*/ + TIMER_IF_StopTimer(); + timeout += RtcTimerContext; + + TIMER_IF_DBG_PRINTF("Start timer: time=%d, alarm=%d\n\r", GetTimerTicks(), timeout); + /* starts timer*/ + sAlarm.BinaryAutoClr = RTC_ALARMSUBSECONDBIN_AUTOCLR_NO; + sAlarm.AlarmTime.SubSeconds = UINT32_MAX - timeout; + sAlarm.AlarmMask = RTC_ALARMMASK_NONE; + sAlarm.AlarmSubSecondMask = RTC_ALARMSUBSECONDBINMASK_NONE; + sAlarm.Alarm = RTC_ALARM_A; + if (HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, RTC_FORMAT_BCD) != HAL_OK) + { + Error_Handler(); + } + /* USER CODE BEGIN TIMER_IF_StartTimer_Last */ + + /* USER CODE END TIMER_IF_StartTimer_Last */ + return ret; +} + +UTIL_TIMER_Status_t TIMER_IF_StopTimer(void) +{ + UTIL_TIMER_Status_t ret = UTIL_TIMER_OK; + /* USER CODE BEGIN TIMER_IF_StopTimer */ + + /* USER CODE END TIMER_IF_StopTimer */ + /* Clear RTC Alarm Flag */ + __HAL_RTC_ALARM_CLEAR_FLAG(&hrtc, RTC_FLAG_ALRAF); + /* Disable the Alarm A interrupt */ + HAL_RTC_DeactivateAlarm(&hrtc, RTC_ALARM_A); + /*overload RTC feature enable*/ + hrtc.IsEnabled.RtcFeatures = UINT32_MAX; + /* USER CODE BEGIN TIMER_IF_StopTimer_Last */ + + /* USER CODE END TIMER_IF_StopTimer_Last */ + return ret; +} + +uint32_t TIMER_IF_SetTimerContext(void) +{ + /*store time context*/ + RtcTimerContext = GetTimerTicks(); + + /* USER CODE BEGIN TIMER_IF_SetTimerContext */ + + /* USER CODE END TIMER_IF_SetTimerContext */ + + TIMER_IF_DBG_PRINTF("TIMER_IF_SetTimerContext=%d\n\r", RtcTimerContext); + /*return time context*/ + return RtcTimerContext; +} + +uint32_t TIMER_IF_GetTimerContext(void) +{ + /* USER CODE BEGIN TIMER_IF_GetTimerContext */ + + /* USER CODE END TIMER_IF_GetTimerContext */ + + TIMER_IF_DBG_PRINTF("TIMER_IF_GetTimerContext=%d\n\r", RtcTimerContext); + /*return time context*/ + return RtcTimerContext; +} + +uint32_t TIMER_IF_GetTimerElapsedTime(void) +{ + uint32_t ret = 0; + /* USER CODE BEGIN TIMER_IF_GetTimerElapsedTime */ + + /* USER CODE END TIMER_IF_GetTimerElapsedTime */ + ret = ((uint32_t)(GetTimerTicks() - RtcTimerContext)); + /* USER CODE BEGIN TIMER_IF_GetTimerElapsedTime_Last */ + + /* USER CODE END TIMER_IF_GetTimerElapsedTime_Last */ + return ret; +} + +uint32_t TIMER_IF_GetTimerValue(void) +{ + uint32_t ret = 0; + /* USER CODE BEGIN TIMER_IF_GetTimerValue */ + + /* USER CODE END TIMER_IF_GetTimerValue */ + if (RTC_Initialized == true) + { + ret = GetTimerTicks(); + } + /* USER CODE BEGIN TIMER_IF_GetTimerValue_Last */ + + /* USER CODE END TIMER_IF_GetTimerValue_Last */ + return ret; +} + +uint32_t TIMER_IF_GetMinimumTimeout(void) +{ + uint32_t ret = 0; + /* USER CODE BEGIN TIMER_IF_GetMinimumTimeout */ + + /* USER CODE END TIMER_IF_GetMinimumTimeout */ + ret = (MIN_ALARM_DELAY); + /* USER CODE BEGIN TIMER_IF_GetMinimumTimeout_Last */ + + /* USER CODE END TIMER_IF_GetMinimumTimeout_Last */ + return ret; +} + +uint32_t TIMER_IF_Convert_ms2Tick(uint32_t timeMilliSec) +{ + uint32_t ret = 0; + /* USER CODE BEGIN TIMER_IF_Convert_ms2Tick */ + + /* USER CODE END TIMER_IF_Convert_ms2Tick */ + ret = ((uint32_t)((((uint64_t) timeMilliSec) << RTC_N_PREDIV_S) / 1000)); + /* USER CODE BEGIN TIMER_IF_Convert_ms2Tick_Last */ + + /* USER CODE END TIMER_IF_Convert_ms2Tick_Last */ + return ret; +} + +uint32_t TIMER_IF_Convert_Tick2ms(uint32_t tick) +{ + uint32_t ret = 0; + /* USER CODE BEGIN TIMER_IF_Convert_Tick2ms */ + + /* USER CODE END TIMER_IF_Convert_Tick2ms */ + ret = ((uint32_t)((((uint64_t)(tick)) * 1000) >> RTC_N_PREDIV_S)); + /* USER CODE BEGIN TIMER_IF_Convert_Tick2ms_Last */ + + /* USER CODE END TIMER_IF_Convert_Tick2ms_Last */ + return ret; +} + +void TIMER_IF_DelayMs(uint32_t delay) +{ + /* USER CODE BEGIN TIMER_IF_DelayMs */ + + /* USER CODE END TIMER_IF_DelayMs */ + uint32_t delayTicks = TIMER_IF_Convert_ms2Tick(delay); + uint32_t timeout = GetTimerTicks(); + + /* Wait delay ms */ + while (((GetTimerTicks() - timeout)) < delayTicks) + { + __NOP(); + } + /* USER CODE BEGIN TIMER_IF_DelayMs_Last */ + + /* USER CODE END TIMER_IF_DelayMs_Last */ +} + +void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc) +{ + /* USER CODE BEGIN HAL_RTC_AlarmAEventCallback */ + + /* USER CODE END HAL_RTC_AlarmAEventCallback */ + UTIL_TIMER_IRQ_MAP_PROCESS(); + /* USER CODE BEGIN HAL_RTC_AlarmAEventCallback_Last */ + + /* USER CODE END HAL_RTC_AlarmAEventCallback_Last */ +} + +void HAL_RTCEx_SSRUEventCallback(RTC_HandleTypeDef *hrtc) +{ + /* USER CODE BEGIN HAL_RTCEx_SSRUEventCallback */ + + /* USER CODE END HAL_RTCEx_SSRUEventCallback */ + /*called every 48 days with 1024 ticks per seconds*/ + TIMER_IF_DBG_PRINTF(">>Handler SSRUnderflow at %d\n\r", GetTimerTicks()); + /*Increment MSBticks*/ + uint32_t MSB_ticks = TIMER_IF_BkUp_Read_MSBticks(); + TIMER_IF_BkUp_Write_MSBticks(MSB_ticks + 1); + /* USER CODE BEGIN HAL_RTCEx_SSRUEventCallback_Last */ + + /* USER CODE END HAL_RTCEx_SSRUEventCallback_Last */ +} + +uint32_t TIMER_IF_GetTime(uint16_t *mSeconds) +{ + uint32_t seconds = 0; + /* USER CODE BEGIN TIMER_IF_GetTime */ + + /* USER CODE END TIMER_IF_GetTime */ + uint64_t ticks; + uint32_t timerValueLsb = GetTimerTicks(); + uint32_t timerValueMSB = TIMER_IF_BkUp_Read_MSBticks(); + + ticks = (((uint64_t) timerValueMSB) << 32) + timerValueLsb; + + seconds = (uint32_t)(ticks >> RTC_N_PREDIV_S); + + ticks = (uint32_t) ticks & RTC_PREDIV_S; + + *mSeconds = TIMER_IF_Convert_Tick2ms(ticks); + + /* USER CODE BEGIN TIMER_IF_GetTime_Last */ + + /* USER CODE END TIMER_IF_GetTime_Last */ + return seconds; +} + +void TIMER_IF_BkUp_Write_Seconds(uint32_t Seconds) +{ + /* USER CODE BEGIN TIMER_IF_BkUp_Write_Seconds */ + + /* USER CODE END TIMER_IF_BkUp_Write_Seconds */ + HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_SECONDS, Seconds); + /* USER CODE BEGIN TIMER_IF_BkUp_Write_Seconds_Last */ + + /* USER CODE END TIMER_IF_BkUp_Write_Seconds_Last */ +} + +void TIMER_IF_BkUp_Write_SubSeconds(uint32_t SubSeconds) +{ + /* USER CODE BEGIN TIMER_IF_BkUp_Write_SubSeconds */ + + /* USER CODE END TIMER_IF_BkUp_Write_SubSeconds */ + HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_SUBSECONDS, SubSeconds); + /* USER CODE BEGIN TIMER_IF_BkUp_Write_SubSeconds_Last */ + + /* USER CODE END TIMER_IF_BkUp_Write_SubSeconds_Last */ +} + +uint32_t TIMER_IF_BkUp_Read_Seconds(void) +{ + uint32_t ret = 0; + /* USER CODE BEGIN TIMER_IF_BkUp_Read_Seconds */ + + /* USER CODE END TIMER_IF_BkUp_Read_Seconds */ + ret = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_SECONDS); + /* USER CODE BEGIN TIMER_IF_BkUp_Read_Seconds_Last */ + + /* USER CODE END TIMER_IF_BkUp_Read_Seconds_Last */ + return ret; +} + +uint32_t TIMER_IF_BkUp_Read_SubSeconds(void) +{ + uint32_t ret = 0; + /* USER CODE BEGIN TIMER_IF_BkUp_Read_SubSeconds */ + + /* USER CODE END TIMER_IF_BkUp_Read_SubSeconds */ + ret = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_SUBSECONDS); + /* USER CODE BEGIN TIMER_IF_BkUp_Read_SubSeconds_Last */ + + /* USER CODE END TIMER_IF_BkUp_Read_SubSeconds_Last */ + return ret; +} + +/* USER CODE BEGIN EF */ + +/* USER CODE END EF */ + +/* Private functions ---------------------------------------------------------*/ +static void TIMER_IF_BkUp_Write_MSBticks(uint32_t MSBticks) +{ + /* USER CODE BEGIN TIMER_IF_BkUp_Write_MSBticks */ + + /* USER CODE END TIMER_IF_BkUp_Write_MSBticks */ + HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_MSBTICKS, MSBticks); + /* USER CODE BEGIN TIMER_IF_BkUp_Write_MSBticks_Last */ + + /* USER CODE END TIMER_IF_BkUp_Write_MSBticks_Last */ +} + +static uint32_t TIMER_IF_BkUp_Read_MSBticks(void) +{ + /* USER CODE BEGIN TIMER_IF_BkUp_Read_MSBticks */ + + /* USER CODE END TIMER_IF_BkUp_Read_MSBticks */ + uint32_t MSBticks; + MSBticks = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_MSBTICKS); + return MSBticks; + /* USER CODE BEGIN TIMER_IF_BkUp_Read_MSBticks_Last */ + + /* USER CODE END TIMER_IF_BkUp_Read_MSBticks_Last */ +} + +static inline uint32_t GetTimerTicks(void) +{ + /* USER CODE BEGIN GetTimerTicks */ + + /* USER CODE END GetTimerTicks */ + uint32_t ssr = LL_RTC_TIME_GetSubSecond(RTC); + /* read twice to make sure value it valid*/ + while (ssr != LL_RTC_TIME_GetSubSecond(RTC)) + { + ssr = LL_RTC_TIME_GetSubSecond(RTC); + } + return UINT32_MAX - ssr; + /* USER CODE BEGIN GetTimerTicks_Last */ + + /* USER CODE END GetTimerTicks_Last */ +} + +/* USER CODE BEGIN PrFD */ + +/* USER CODE END PrFD */ diff --git a/src/BSP/timer_if.h b/src/BSP/timer_if.h new file mode 100644 index 0000000..a60ed36 --- /dev/null +++ b/src/BSP/timer_if.h @@ -0,0 +1,172 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file timer_if.h + * @author MCD Application Team + * @brief configuration of the timer_if.c instances + ****************************************************************************** + * @attention + * + * Copyright (c) 2022 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 + * + ****************************************************************************** + */ +/* USER CODE END Header */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __TIMER_IF_H__ +#define __TIMER_IF_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "../STM32CubeWL/Utilities/timer/stm32_timer.h" +#include "../STM32CubeWL/Utilities/misc/stm32_systime.h" + +/* USER CODE BEGIN Includes */ + +/* USER CODE END Includes */ + +/* Exported types ------------------------------------------------------------*/ +/* USER CODE BEGIN ET */ + +/* USER CODE END ET */ + +/* Exported constants --------------------------------------------------------*/ +/* USER CODE BEGIN EC */ + +/* USER CODE END EC */ + +/* External variables --------------------------------------------------------*/ +/* USER CODE BEGIN EV */ + +/* USER CODE END EV */ + +/* Exported macro ------------------------------------------------------------*/ +/* USER CODE BEGIN EM */ + +/* USER CODE END EM */ + +/* Exported functions prototypes ---------------------------------------------*/ +/** + * @brief Init RTC hardware + * @return Status based on @ref UTIL_TIMER_Status_t + */ +UTIL_TIMER_Status_t TIMER_IF_Init(void); + +/** + * @brief Set the alarm + * @note The alarm is set at timeout from timer Reference (TimerContext) + * @param timeout Duration of the Timer in ticks + * @return Status based on @ref UTIL_TIMER_Status_t + */ +UTIL_TIMER_Status_t TIMER_IF_StartTimer(uint32_t timeout); + +/** + * @brief Stop the Alarm + * @return Status based on @ref UTIL_TIMER_Status_t + */ +UTIL_TIMER_Status_t TIMER_IF_StopTimer(void); + +/** + * @brief set timer Reference (TimerContext) + * @return Timer Reference Value in Ticks + */ +uint32_t TIMER_IF_SetTimerContext(void); + +/** + * @brief Get the RTC timer Reference + * @return Timer Value in Ticks + */ +uint32_t TIMER_IF_GetTimerContext(void); + +/** + * @brief Get the timer elapsed time since timer Reference (TimerContext) was set + * @return RTC Elapsed time in ticks + */ +uint32_t TIMER_IF_GetTimerElapsedTime(void); + +/** + * @brief Get the timer value + * @return RTC Timer value in ticks + */ +uint32_t TIMER_IF_GetTimerValue(void); + +/** + * @brief Return the minimum timeout in ticks the RTC is able to handle + * @return minimum value for a timeout in ticks + */ +uint32_t TIMER_IF_GetMinimumTimeout(void); + +/** + * @brief a delay of delay ms by polling RTC + * @param delay in ms + */ +void TIMER_IF_DelayMs(uint32_t delay); + +/** + * @brief converts time in ms to time in ticks + * @param[in] timeMilliSec time in milliseconds + * @return time in timer ticks + */ +uint32_t TIMER_IF_Convert_ms2Tick(uint32_t timeMilliSec); + +/** + * @brief converts time in ticks to time in ms + * @param[in] tick time in timer ticks + * @return time in timer milliseconds + */ +uint32_t TIMER_IF_Convert_Tick2ms(uint32_t tick); + +/** + * @brief Get rtc time + * @param[out] subSeconds in ticks + * @return time seconds + */ +uint32_t TIMER_IF_GetTime(uint16_t *subSeconds); + +/** + * @brief write seconds in backUp register + * @note Used to store seconds difference between RTC time and Unix time + * @param[in] Seconds time in seconds + */ +void TIMER_IF_BkUp_Write_Seconds(uint32_t Seconds); + +/** + * @brief reads seconds from backUp register + * @note Used to store seconds difference between RTC time and Unix time + * @return Time in seconds + */ +uint32_t TIMER_IF_BkUp_Read_Seconds(void); + +/** + * @brief writes SubSeconds in backUp register + * @note Used to store SubSeconds difference between RTC time and Unix time + * @param[in] SubSeconds time in SubSeconds + */ +void TIMER_IF_BkUp_Write_SubSeconds(uint32_t SubSeconds); + +/** + * @brief reads SubSeconds from backUp register + * @note Used to store SubSeconds difference between RTC time and Unix time + * @return Time in SubSeconds + */ +uint32_t TIMER_IF_BkUp_Read_SubSeconds(void); + +/* USER CODE BEGIN EFP */ + +/* USER CODE END EFP */ + +#ifdef __cplusplus +} +#endif + +#endif /* __TIMER_IF_H__ */ From bad5c4ac23ce66d903aa85277640a6278517d9ea Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Fri, 24 Jun 2022 00:24:47 +0200 Subject: [PATCH 07/73] STM32LoRaWAN: Add main library class This class is the main entrypoint for the Arduino API, wrapping the STM32CubeWL/LoRaMAC-Node API. This adds an initial version that has roughly the right shape, but is still incomplete and does not properly mimic the MKRWAN API yet (in particular is non-blocking everywhere and does not implement Stream I/O yet). --- src/STM32LoRaWAN.cpp | 568 +++++++++++++++++++++++++++++++++++++++++++ src/STM32LoRaWAN.h | 188 ++++++++++++++ 2 files changed, 756 insertions(+) create mode 100644 src/STM32LoRaWAN.cpp create mode 100644 src/STM32LoRaWAN.h diff --git a/src/STM32LoRaWAN.cpp b/src/STM32LoRaWAN.cpp new file mode 100644 index 0000000..5f4363b --- /dev/null +++ b/src/STM32LoRaWAN.cpp @@ -0,0 +1,568 @@ +#include "STM32LoRaWAN.h" +#include + +bool STM32LoRaWAN::begin(_lora_band band) +{ + UTIL_TIMER_Init(); + + LoRaMacStatus_t res = LoRaMacInitialization(&LoRaMacPrimitives, &LoRaMacCallbacks, (LoRaMacRegion_t)band); + if (res != LORAMAC_STATUS_OK) { + return failure("LoRaMacInitialization failed: %s\r\n", toString(res)); + } + + res = LoRaMacStart(); + if (res != LORAMAC_STATUS_OK) + return failure("LoRaMacStart failed: %s\r\n", toString(res)); + + return true; +} + +void STM32LoRaWAN::poll() +{ + LoRaMacProcess( ); +} + +bool STM32LoRaWAN::join(bool otaa) +{ + // TODO rx.clear(); + if (otaa) { + MlmeReq_t mlmeReq; + + mlmeReq.Type = MLME_JOIN; + mlmeReq.Req.Join.Datarate = tx_dr; + mlmeReq.Req.Join.NetworkActivation = ACTIVATION_TYPE_OTAA; + + // Starts the OTAA join procedure + LoRaMacStatus_t res = LoRaMacMlmeRequest(&mlmeReq); + if (res != LORAMAC_STATUS_OK) + return failure("Join request failed: %s\r\n", toString(res)); + + // TODO: Block for join complete? Timeout? + // TODO: Retry joins + return true; + } else { + MibRequestConfirm_t mibReq; + mibReq.Param.NetworkActivation = ACTIVATION_TYPE_ABP; + if (!mibSet("MIB_NETWORK_ACTIVATION", MIB_NETWORK_ACTIVATION, mibReq)) + return false; + + return true; + } +} + +bool STM32LoRaWAN::dataRate(uint8_t dr) { + this->tx_dr = dr; + return true; +} + +int STM32LoRaWAN::getDataRate() { + // TODO: Check if MKRWAN returns actual datarate when ADR is in use and mimic that + return this->tx_dr; +} + +bool STM32LoRaWAN::send(const uint8_t *payload, size_t size, uint8_t port, bool confirmed) { + McpsReq_t mcpsReq; + mcpsReq.Type = confirmed ? MCPS_CONFIRMED : MCPS_UNCONFIRMED; + + if (confirmed) { + // TODO: ADR + mcpsReq.Req.Confirmed.Datarate = this->tx_dr; + mcpsReq.Req.Confirmed.fPort = port; + mcpsReq.Req.Confirmed.fBufferSize = size; + mcpsReq.Req.Confirmed.fBuffer = const_cast(payload); + #if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) + // Mimic the 1.0.4 behavior where the application manages retransmissions. + // See https://github.com/Lora-net/LoRaMac-node/discussions/1096 + mcpsReq.Req.Confirmed.NbTrials = 1; + #endif /* LORAMAC_VERSION */ + } else { + mcpsReq.Req.Unconfirmed.Datarate = this->tx_dr; + mcpsReq.Req.Unconfirmed.fPort = port; + mcpsReq.Req.Unconfirmed.fBufferSize = size; + mcpsReq.Req.Unconfirmed.fBuffer = const_cast(payload); + } + + LoRaMacTxInfo_t txInfo; + if( LoRaMacQueryTxPossible( size, &txInfo ) == LORAMAC_STATUS_LENGTH_ERROR ) { + if (size > txInfo.CurrentPossiblePayloadSize) + return failure("Packet too long, only %s bytes of payload supported at current datarate\r\n", txInfo.CurrentPossiblePayloadSize); + + // If the packet would fit, but is still rejected, this means there + // are pending MAC commands that do not fit in the header along with + // the payload, or do not fit in the header at all (i.e. require an + // empty uplink). Schedule an uplink to flush these commands. + mcpsReq.Type = MCPS_UNCONFIRMED; + mcpsReq.Req.Unconfirmed.fBuffer = NULL; + mcpsReq.Req.Unconfirmed.fBufferSize = 0; + LoRaMacMcpsRequest(&mcpsReq, /* allowDelayedTx */ true); + + return failure("Cannot send packet, wait for pending MAC commands to be sent\r\n"); + } + + LoRaMacStatus_t res = LoRaMacMcpsRequest(&mcpsReq, /* allowDelayedTx */ true); + if (res != LORAMAC_STATUS_OK) + return failure("Failed to send packet: %s\r\n", toString(res)); + + // TODO: Report mcpsReq.ReqReturn.DutyCycleWaitTime somewhere? + return true; +} + +// All these MIB get and set functions have quite a bit of boilerplate, +// but at least they make their callers a lot less verbose. +bool STM32LoRaWAN::mibGet(const char* name, Mib_t type, MibRequestConfirm_t& mibReq) { + mibReq.Type = type; + LoRaMacStatus_t res = LoRaMacMibGetRequestConfirm(&mibReq); + if (res != LORAMAC_STATUS_OK) + return failure("Failed to get %s: %s\r\n", name, toString(res)); + + return true; +} + +bool STM32LoRaWAN::mibSet(const char* name, Mib_t type, MibRequestConfirm_t& mibReq) { + mibReq.Type = type; + LoRaMacStatus_t res = LoRaMacMibSetRequestConfirm(&mibReq); + if (res != LORAMAC_STATUS_OK) + return failure("Failed to set %s: %s\r\n", name, toString(res)); + + return true; +} + +bool STM32LoRaWAN::mibGetBool(const char* name, Mib_t type, bool *value) { + MibRequestConfirm_t mibReq; + if (!mibGet(name, type, mibReq)) + return false; + + switch(type) { + case MIB_ADR: *value = mibReq.Param.AdrEnable; break; + case MIB_PUBLIC_NETWORK: *value = mibReq.Param.EnablePublicNetwork; break; + case MIB_REPEATER_SUPPORT: *value = mibReq.Param.EnableRepeaterSupport; break; + default: return failure("Internal error: Unknown MIB type: %s / %u\r\n", name, type); + } + return true; +} + +bool STM32LoRaWAN::mibSetBool(const char* name, Mib_t type, bool value) { + MibRequestConfirm_t mibReq; + switch(type) { + case MIB_ADR: mibReq.Param.AdrEnable = value; break; + case MIB_PUBLIC_NETWORK: mibReq.Param.EnablePublicNetwork = value; break; + case MIB_REPEATER_SUPPORT: mibReq.Param.EnableRepeaterSupport = value; break; + default: return failure("Internal error: Unknown MIB type: %s / %u\r\n", name, type); + } + + return mibSet(name, type, mibReq); +} + +bool STM32LoRaWAN::mibGetUint8(const char* name, Mib_t type, uint8_t *value) { + MibRequestConfirm_t mibReq; + if (!mibGet(name, type, mibReq)) + return false; + + switch(type) { + case MIB_CHANNELS_NB_TRANS: *value = mibReq.Param.ChannelsNbTrans; break; + case MIB_MIN_RX_SYMBOLS: *value = mibReq.Param.MinRxSymbols; break; + default: return failure("Internal error: Unknown MIB type: %s / %u\r\n", name, type); + } + return true; +} + +bool STM32LoRaWAN::mibSetUint8(const char* name, Mib_t type, uint8_t value) { + MibRequestConfirm_t mibReq; + switch(type) { + case MIB_CHANNELS_NB_TRANS: mibReq.Param.ChannelsNbTrans = value; break; + case MIB_MIN_RX_SYMBOLS: mibReq.Param.MinRxSymbols = value; break; + default: return failure("Internal error: Unknown MIB type: %s / %u\r\n", name, type); + } + + return mibSet(name, type, mibReq); +} + +bool STM32LoRaWAN::mibGetInt8(const char* name, Mib_t type, int8_t *value) { + MibRequestConfirm_t mibReq; + if (!mibGet(name, type, mibReq)) + return false; + + switch(type) { + #if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + case MIB_CHANNELS_MIN_TX_DATARATE: *value = mibReq.Param.ChannelsMinTxDatarate; break; + #endif /* LORAMAC_VERSION */ + case MIB_CHANNELS_DEFAULT_DATARATE: *value = mibReq.Param.ChannelsDefaultDatarate; break; + case MIB_CHANNELS_DATARATE: *value = mibReq.Param.ChannelsDatarate; break; + case MIB_CHANNELS_DEFAULT_TX_POWER: *value = mibReq.Param.ChannelsDefaultTxPower; break; + case MIB_CHANNELS_TX_POWER: *value = mibReq.Param.ChannelsTxPower; break; + case MIB_PING_SLOT_DATARATE: *value = mibReq.Param.PingSlotDatarate; break; + default: return failure("Internal error: Unknown MIB type: %s / %u\r\n", name, type); + } + return true; +} + +bool STM32LoRaWAN::mibSetInt8(const char* name, Mib_t type, int8_t value) { + MibRequestConfirm_t mibReq; + switch(type) { + #if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) + case MIB_CHANNELS_MIN_TX_DATARATE: mibReq.Param.ChannelsMinTxDatarate = value; break; + #endif /* LORAMAC_VERSION */ + case MIB_CHANNELS_DEFAULT_DATARATE: mibReq.Param.ChannelsDefaultDatarate = value; break; + case MIB_CHANNELS_DATARATE: mibReq.Param.ChannelsDatarate = value; break; + case MIB_CHANNELS_DEFAULT_TX_POWER: mibReq.Param.ChannelsDefaultTxPower = value; break; + case MIB_CHANNELS_TX_POWER: mibReq.Param.ChannelsTxPower = value; break; + case MIB_PING_SLOT_DATARATE: mibReq.Param.PingSlotDatarate = value; break; + default: return failure("Internal error: Unknown MIB type: %s / %u\r\n", name, type); + } + + return mibSet(name, type, mibReq); +} + +bool STM32LoRaWAN::mibGetUint32(const char* name, Mib_t type, uint32_t *value) { + MibRequestConfirm_t mibReq; + if (!mibGet(name, type, mibReq)) + return false; + + switch(type) { + case MIB_NET_ID: *value = mibReq.Param.NetID; break; + case MIB_DEV_ADDR: *value = mibReq.Param.DevAddr; break; + case MIB_MAX_RX_WINDOW_DURATION: *value = mibReq.Param.MaxRxWindow; break; + case MIB_RECEIVE_DELAY_1: *value = mibReq.Param.ReceiveDelay1; break; + case MIB_RECEIVE_DELAY_2: *value = mibReq.Param.ReceiveDelay2; break; + case MIB_JOIN_ACCEPT_DELAY_1: *value = mibReq.Param.JoinAcceptDelay1; break; + case MIB_JOIN_ACCEPT_DELAY_2: *value = mibReq.Param.JoinAcceptDelay2; break; + case MIB_SYSTEM_MAX_RX_ERROR: *value = mibReq.Param.SystemMaxRxError; break; + case MIB_BEACON_INTERVAL: *value = mibReq.Param.BeaconInterval; break; + case MIB_BEACON_RESERVED: *value = mibReq.Param.BeaconReserved; break; + case MIB_BEACON_GUARD: *value = mibReq.Param.BeaconGuard; break; + case MIB_BEACON_WINDOW: *value = mibReq.Param.BeaconWindow; break; + case MIB_BEACON_WINDOW_SLOTS: *value = mibReq.Param.BeaconWindowSlots; break; + case MIB_PING_SLOT_WINDOW: *value = mibReq.Param.PingSlotWindow; break; + case MIB_BEACON_SYMBOL_TO_DEFAULT: *value = mibReq.Param.BeaconSymbolToDefault; break; + case MIB_BEACON_SYMBOL_TO_EXPANSION_MAX: *value = mibReq.Param.BeaconSymbolToExpansionMax; break; + case MIB_PING_SLOT_SYMBOL_TO_EXPANSION_MAX: *value = mibReq.Param.PingSlotSymbolToExpansionMax; break; + case MIB_BEACON_SYMBOL_TO_EXPANSION_FACTOR: *value = mibReq.Param.BeaconSymbolToExpansionFactor; break; + case MIB_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR: *value = mibReq.Param.PingSlotSymbolToExpansionFactor; break; + case MIB_MAX_BEACON_LESS_PERIOD: *value = mibReq.Param.MaxBeaconLessPeriod; break; + case MIB_RXB_C_TIMEOUT: *value = mibReq.Param.RxBCTimeout; break; + default: return failure("Internal error: Unknown MIB type: %s / %u\r\n", name, type); + } + return true; +} + +bool STM32LoRaWAN::mibSetUint32(const char* name, Mib_t type, uint32_t value) { + MibRequestConfirm_t mibReq; + switch(type) { + case MIB_NET_ID: mibReq.Param.NetID = value; break; + case MIB_DEV_ADDR: mibReq.Param.DevAddr = value; break; + case MIB_MAX_RX_WINDOW_DURATION: mibReq.Param.MaxRxWindow = value; break; + case MIB_RECEIVE_DELAY_1: mibReq.Param.ReceiveDelay1 = value; break; + case MIB_RECEIVE_DELAY_2: mibReq.Param.ReceiveDelay2 = value; break; + case MIB_JOIN_ACCEPT_DELAY_1: mibReq.Param.JoinAcceptDelay1 = value; break; + case MIB_JOIN_ACCEPT_DELAY_2: mibReq.Param.JoinAcceptDelay2 = value; break; + case MIB_SYSTEM_MAX_RX_ERROR: mibReq.Param.SystemMaxRxError = value; break; + case MIB_BEACON_INTERVAL: mibReq.Param.BeaconInterval = value; break; + case MIB_BEACON_RESERVED: mibReq.Param.BeaconReserved = value; break; + case MIB_BEACON_GUARD: mibReq.Param.BeaconGuard = value; break; + case MIB_BEACON_WINDOW: mibReq.Param.BeaconWindow = value; break; + case MIB_BEACON_WINDOW_SLOTS: mibReq.Param.BeaconWindowSlots = value; break; + case MIB_PING_SLOT_WINDOW: mibReq.Param.PingSlotWindow = value; break; + case MIB_BEACON_SYMBOL_TO_DEFAULT: mibReq.Param.BeaconSymbolToDefault = value; break; + case MIB_BEACON_SYMBOL_TO_EXPANSION_MAX: mibReq.Param.BeaconSymbolToExpansionMax = value; break; + case MIB_PING_SLOT_SYMBOL_TO_EXPANSION_MAX: mibReq.Param.PingSlotSymbolToExpansionMax = value; break; + case MIB_BEACON_SYMBOL_TO_EXPANSION_FACTOR: mibReq.Param.BeaconSymbolToExpansionFactor = value; break; + case MIB_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR: mibReq.Param.PingSlotSymbolToExpansionFactor = value; break; + case MIB_MAX_BEACON_LESS_PERIOD: mibReq.Param.MaxBeaconLessPeriod = value; break; + case MIB_RXB_C_TIMEOUT: mibReq.Param.RxBCTimeout = value; break; + default: return failure("Internal error: Unknown MIB type: %s / %u\r\n", name, type); + } + + return mibSet(name, type, mibReq); +} + +bool STM32LoRaWAN::mibGetUint64(const char* name, Mib_t type, uint64_t *value) { + MibRequestConfirm_t mibReq; + if (!mibGet(name, type, mibReq)) + return false; + + const uint8_t *buf; + + switch(type) { + case MIB_DEV_EUI: buf = mibReq.Param.DevEui; break; + case MIB_JOIN_EUI: buf = mibReq.Param.JoinEui; break; + default: return failure("Internal error: Unknown MIB type: %s / %u\r\n", name, type); + } + + *value = (uint64_t)buf[0] << 7*8 | + (uint64_t)buf[1] << 6*8 | + (uint64_t)buf[2] << 5*8 | + (uint64_t)buf[3] << 4*8 | + (uint64_t)buf[4] << 3*8 | + (uint64_t)buf[5] << 2*8 | + (uint64_t)buf[6] << 1*8 | + (uint64_t)buf[7] << 0*8; + + return true; +} + +bool STM32LoRaWAN::mibSetUint64(const char* name, Mib_t type, uint64_t value) { + MibRequestConfirm_t mibReq; + uint8_t buf[8] = { + (uint8_t)(value >> 7*8), + (uint8_t)(value >> 6*8), + (uint8_t)(value >> 5*8), + (uint8_t)(value >> 4*8), + (uint8_t)(value >> 3*8), + (uint8_t)(value >> 2*8), + (uint8_t)(value >> 1*8), + (uint8_t)(value >> 0*8), + }; + + switch(type) { + case MIB_DEV_EUI: mibReq.Param.DevEui = buf; break; + case MIB_JOIN_EUI: mibReq.Param.JoinEui = buf; break; + default: return failure("Internal error: Unknown MIB type: %s / %u\r\n", name, type); + } + + return mibSet(name, type, mibReq); +} + +bool STM32LoRaWAN::mibSetHex(const char* name, Mib_t type, const char* value) { + // The buffer-passing API is a bit fragile, since the size of the + // buffer to be passed is implicit, and also not very well + // documented. So we need to derive the size here. + size_t size; + switch(type) { + case MIB_DEV_EUI: + case MIB_JOIN_EUI: + size = SE_EUI_SIZE; + break; + + case MIB_DEV_ADDR: + size = sizeof(MibRequestConfirm_t::Param.DevAddr); + break; + + case MIB_APP_KEY: + case MIB_NWK_KEY: + #if ( USE_LRWAN_1_1_X_CRYPTO == 1 ) + case MIB_J_S_INT_KEY: + case MIB_J_S_ENC_KEY: + case MIB_F_NWK_S_INT_KEY: + case MIB_S_NWK_S_INT_KEY: + case MIB_NWK_S_ENC_KEY: + #else /* USE_LRWAN_1_1_X_CRYPTO == 0 */ + case MIB_NWK_S_KEY: + #endif /* USE_LRWAN_1_1_X_CRYPTO */ + case MIB_APP_S_KEY: + case MIB_MC_KE_KEY: + #if ( LORAMAC_MAX_MC_CTX > 0 ) + case MIB_MC_KEY_0: + case MIB_MC_APP_S_KEY_0: + case MIB_MC_NWK_S_KEY_0: + #endif /* LORAMAC_MAX_MC_CTX > 0 */ + #if ( LORAMAC_MAX_MC_CTX > 1 ) + case MIB_MC_KEY_1: + case MIB_MC_APP_S_KEY_1: + case MIB_MC_NWK_S_KEY_1: + #endif /* LORAMAC_MAX_MC_CTX > 1 */ + #if ( LORAMAC_MAX_MC_CTX > 2 ) + case MIB_MC_KEY_2: + case MIB_MC_APP_S_KEY_2: + case MIB_MC_NWK_S_KEY_2: + #endif /* LORAMAC_MAX_MC_CTX > 2 */ + #if ( LORAMAC_MAX_MC_CTX > 3 ) + case MIB_MC_KEY_3: + case MIB_MC_APP_S_KEY_3: + case MIB_MC_NWK_S_KEY_3: + #endif /* LORAMAC_MAX_MC_CTX > 3 */ + size = SE_KEY_SIZE; + break; + + default: + return failure("Internal error: Unknown MIB type: %s / %u\r\n", name, type); + } + + uint8_t buf[size]; + if (!parseHex(buf, value, size)) + return false; + + MibRequestConfirm_t mibReq; + switch(type) { + case MIB_DEV_EUI: mibReq.Param.DevEui = buf; break; + case MIB_JOIN_EUI: mibReq.Param.JoinEui = buf; break; + // This assumes big endian, since that's the natural way to + // write down a a number in hex + case MIB_DEV_ADDR: mibReq.Param.DevAddr = makeUint32(buf[0], buf[1], buf[2], buf[3]); break; + case MIB_APP_KEY: mibReq.Param.AppKey = buf; break; + case MIB_NWK_KEY: mibReq.Param.NwkKey = buf; break; + #if ( USE_LRWAN_1_1_X_CRYPTO == 1 ) + case MIB_J_S_INT_KEY: mibReq.Param.JSIntKey = buf; break; + case MIB_J_S_ENC_KEY: mibReq.Param.JSEncKey = buf; break; + case MIB_F_NWK_S_INT_KEY: mibReq.Param.FNwkSIntKey = buf; break; + case MIB_S_NWK_S_INT_KEY: mibReq.Param.SNwkSIntKey = buf; break; + case MIB_NWK_S_ENC_KEY: mibReq.Param.NwkSEncKey = buf; break; + #else /* USE_LRWAN_1_1_X_CRYPTO == 0 */ + case MIB_NWK_S_KEY: mibReq.Param.NwkSKey = buf; break; + #endif /* USE_LRWAN_1_1_X_CRYPTO */ + case MIB_APP_S_KEY: mibReq.Param.AppSKey = buf; break; + case MIB_MC_KE_KEY: mibReq.Param.McKEKey = buf; break; + #if ( LORAMAC_MAX_MC_CTX > 0 ) + case MIB_MC_KEY_0: mibReq.Param.McKey0 = buf; break; + case MIB_MC_APP_S_KEY_0: mibReq.Param.McAppSKey0 = buf; break; + case MIB_MC_NWK_S_KEY_0: mibReq.Param.McNwkSKey0 = buf; break; + #endif /* LORAMAC_MAX_MC_CTX > 0 */ + #if ( LORAMAC_MAX_MC_CTX > 1 ) + case MIB_MC_KEY_1: mibReq.Param.McKey1 = buf; break; + case MIB_MC_APP_S_KEY_1: mibReq.Param.McAppSKey1 = buf; break; + case MIB_MC_NWK_S_KEY_1: mibReq.Param.McNwkSKey1 = buf; break; + #endif /* LORAMAC_MAX_MC_CTX > 1 */ + #if ( LORAMAC_MAX_MC_CTX > 2 ) + case MIB_MC_KEY_2: mibReq.Param.McKey2 = buf; break; + case MIB_MC_APP_S_KEY_2: mibReq.Param.McAppSKey2 = buf; break; + case MIB_MC_NWK_S_KEY_2: mibReq.Param.McNwkSKey2 = buf; break; + #endif /* LORAMAC_MAX_MC_CTX > 2 */ + #if ( LORAMAC_MAX_MC_CTX > 3 ) + case MIB_MC_KEY_3: mibReq.Param.McKey3 = buf; break; + case MIB_MC_APP_S_KEY_3: mibReq.Param.McAppSKey3 = buf; break; + case MIB_MC_NWK_S_KEY_3: mibReq.Param.McNwkSKey3 = buf; break; + #endif /* LORAMAC_MAX_MC_CTX > 3 */ + default: + return failure("Internal error: Unknown MIB type: %s / %u\r\n", name, type); + } + + return mibSet(name, type, mibReq); +} + +/* These MIB types do not have easy getter/setters defined for them yet + * (but since these were regexed together from LoRaMacInterfaces already, these + * are left here to maybe put into use in the future. + + case MIB_CHANNELS: mibReq.Param.ChannelList = value; break; + case MIB_RX2_CHANNEL: mibReq.Param.Rx2Channel = value; break; + case MIB_RX2_DEFAULT_CHANNEL: mibReq.Param.Rx2DefaultChannel = value; break; + case MIB_RXC_CHANNEL: mibReq.Param.RxCChannel = value; break; + case MIB_RXC_DEFAULT_CHANNEL: mibReq.Param.RxCDefaultChannel = value; break; + case MIB_CHANNELS_MASK: mibReq.Param.ChannelsMask = value; break; + case MIB_CHANNELS_DEFAULT_MASK: mibReq.Param.ChannelsDefaultMask = value; break; + case MIB_MULTICAST_CHANNEL: mibReq.Param.MulticastChannel = value; break; + case MIB_ANTENNA_GAIN: mibReq.Param.AntennaGain = value; break; + case MIB_DEFAULT_ANTENNA_GAIN: mibReq.Param.DefaultAntennaGain = value; break; + case MIB_NVM_CTXS: mibReq.Param.Contexts = value; break; + case MIB_NVM_CTXS: mibReq.Param.BackupContexts = value; break; + case MIB_ABP_LORAWAN_VERSION: mibReq.Param.AbpLrWanVersion = value; break; + case MIB_IS_CERT_FPORT_ON: mibReq.Param.IsCertPortOn = value; break; + case MIB_BEACON_STATE: mibReq.Param.BeaconState = value; break; + + case MIB_CHANNELS: *value = mibReq.Param.ChannelList; break; + case MIB_RX2_CHANNEL: *value = mibReq.Param.Rx2Channel; break; + case MIB_RX2_DEFAULT_CHANNEL: *value = mibReq.Param.Rx2DefaultChannel; break; + case MIB_RXC_CHANNEL: *value = mibReq.Param.RxCChannel; break; + case MIB_RXC_DEFAULT_CHANNEL: *value = mibReq.Param.RxCDefaultChannel; break; + case MIB_CHANNELS_MASK: *value = mibReq.Param.ChannelsMask; break; + case MIB_CHANNELS_DEFAULT_MASK: *value = mibReq.Param.ChannelsDefaultMask; break; + case MIB_MULTICAST_CHANNEL: *value = mibReq.Param.MulticastChannel; break; + case MIB_ANTENNA_GAIN: *value = mibReq.Param.AntennaGain; break; + case MIB_DEFAULT_ANTENNA_GAIN: *value = mibReq.Param.DefaultAntennaGain; break; + case MIB_NVM_CTXS: *value = mibReq.Param.Contexts; break; + case MIB_NVM_CTXS: *value = mibReq.Param.BackupContexts; break; + case MIB_ABP_LORAWAN_VERSION: *value = mibReq.Param.AbpLrWanVersion; break; + case MIB_IS_CERT_FPORT_ON: *value = mibReq.Param.IsCertPortOn; break; + case MIB_BEACON_STATE: *value = mibReq.Param.BeaconState; break; +*/ + +bool STM32LoRaWAN::connected() { + MibRequestConfirm_t mibReq; + return mibGet("MIB_NETWORK_ACTIVATION", MIB_NETWORK_ACTIVATION, mibReq) + && (mibReq.Param.NetworkActivation != ACTIVATION_TYPE_NONE); +} + +const char *STM32LoRaWAN::toString(LoRaMacStatus_t status) { + switch (status) { + case LORAMAC_STATUS_OK: + return "LORAMAC_STATUS_OK"; + case LORAMAC_STATUS_BUSY: + return "LORAMAC_STATUS_BUSY"; + case LORAMAC_STATUS_SERVICE_UNKNOWN: + return "LORAMAC_STATUS_SERVICE_UNKNOWN"; + case LORAMAC_STATUS_PARAMETER_INVALID: + return "LORAMAC_STATUS_PARAMETER_INVALID"; + case LORAMAC_STATUS_FREQUENCY_INVALID: + return "LORAMAC_STATUS_FREQUENCY_INVALID"; + case LORAMAC_STATUS_DATARATE_INVALID: + return "LORAMAC_STATUS_DATARATE_INVALID"; + case LORAMAC_STATUS_FREQ_AND_DR_INVALID: + return "LORAMAC_STATUS_FREQ_AND_DR_INVALID"; + case LORAMAC_STATUS_NO_NETWORK_JOINED: + return "LORAMAC_STATUS_NO_NETWORK_JOINED"; + case LORAMAC_STATUS_LENGTH_ERROR: + return "LORAMAC_STATUS_LENGTH_ERROR"; + case LORAMAC_STATUS_REGION_NOT_SUPPORTED: + return "LORAMAC_STATUS_REGION_NOT_SUPPORTED"; + case LORAMAC_STATUS_SKIPPED_APP_DATA: + return "LORAMAC_STATUS_SKIPPED_APP_DATA"; + case LORAMAC_STATUS_DUTYCYCLE_RESTRICTED: + return "LORAMAC_STATUS_DUTYCYCLE_RESTRICTED"; + case LORAMAC_STATUS_NO_CHANNEL_FOUND: + return "LORAMAC_STATUS_NO_CHANNEL_FOUND"; + case LORAMAC_STATUS_NO_FREE_CHANNEL_FOUND: + return "LORAMAC_STATUS_NO_FREE_CHANNEL_FOUND"; + case LORAMAC_STATUS_BUSY_BEACON_RESERVED_TIME: + return "LORAMAC_STATUS_BUSY_BEACON_RESERVED_TIME"; + case LORAMAC_STATUS_BUSY_PING_SLOT_WINDOW_TIME: + return "LORAMAC_STATUS_BUSY_PING_SLOT_WINDOW_TIME"; + case LORAMAC_STATUS_BUSY_UPLINK_COLLISION: + return "LORAMAC_STATUS_BUSY_UPLINK_COLLISION"; + case LORAMAC_STATUS_CRYPTO_ERROR: + return "LORAMAC_STATUS_CRYPTO_ERROR"; + case LORAMAC_STATUS_FCNT_HANDLER_ERROR: + return "LORAMAC_STATUS_FCNT_HANDLER_ERROR"; + case LORAMAC_STATUS_MAC_COMMAD_ERROR: + return "LORAMAC_STATUS_MAC_COMMAD_ERROR"; + case LORAMAC_STATUS_CLASS_B_ERROR: + return "LORAMAC_STATUS_CLASS_B_ERROR"; + case LORAMAC_STATUS_CONFIRM_QUEUE_ERROR: + return "LORAMAC_STATUS_CONFIRM_QUEUE_ERROR"; + case LORAMAC_STATUS_MC_GROUP_UNDEFINED: + return "LORAMAC_STATUS_MC_GROUP_UNDEFINED"; + case LORAMAC_STATUS_NVM_DATA_INCONSISTENT: + return "LORAMAC_STATUS_NVM_DATA_INCONSISTENT"; + case LORAMAC_STATUS_ERROR: + return "LORAMAC_STATUS_ERROR"; + default: + return "LORAMAC_STATUS_UKNOWN"; + } +} + +uint8_t STM32LoRaWAN::parseHex(char c) { + return (c >= 'A') ? (c >= 'a') ? (c - 'a' + 10) : (c - 'A' + 10) : (c - '0'); +} + + +bool STM32LoRaWAN::parseHex(uint8_t *dest, const char *hex, size_t dest_len) { + uint8_t *end = dest + dest_len;; + const char *next = hex; + while (dest != end) { + if (next[0] == '\0' || next[1] == '\0') { + MW_LOG(TS_ON, VLEVEL_M, "Hex string too short, needed %u bytes (%u digits), got: %s\r\n", dest_len, dest_len * 2, hex); + return false; + } + uint8_t high = parseHex(*next++); + uint8_t low = parseHex(*next++); + + if (high > 16 || low > 16) { + MW_LOG(TS_ON, VLEVEL_M, "Non-hex digit found in hex string %s\r\n", hex); + return false; + } + *dest++ = high << 4 | low; + } + + if (next[0] != '\0') { + MW_LOG(TS_ON, VLEVEL_M, "Hex string too long, needed %u bytes (%u digits), got: %s\r\n", dest_len, dest_len * 2, hex); + return false; + } + return true; +} + + +bool STM32LoRaWAN::failure(const char* fmt, ...) { + va_list ap; + va_start(ap, fmt); + vcore_debug(fmt, ap); + va_end(ap); + return false; +} diff --git a/src/STM32LoRaWAN.h b/src/STM32LoRaWAN.h new file mode 100644 index 0000000..c1c1237 --- /dev/null +++ b/src/STM32LoRaWAN.h @@ -0,0 +1,188 @@ +#pragma once + +#include "Arduino.h" +#include "STM32CubeWL/LoRaWAN/Mac/LoRaMac.h" +#include "STM32CubeWL/Utilities/timer/stm32_timer.h" +#include "BSP/mw_log_conf.h" + + +typedef enum { + AS923 = LORAMAC_REGION_AS923, + AU915 = LORAMAC_REGION_AU915, + CN470 = LORAMAC_REGION_CN470, + CN779 = LORAMAC_REGION_CN779, + EU433 = LORAMAC_REGION_EU433, + EU868 = LORAMAC_REGION_EU868, + KR920 = LORAMAC_REGION_KR920, + IN865 = LORAMAC_REGION_IN865, + US915 = LORAMAC_REGION_US915, + RU864 = LORAMAC_REGION_RU864, + // TODO: AU915_TTN = 0x80 | AU915, +} _lora_band; + +class STM32LoRaWAN { + public: + bool begin(_lora_band band); + + // TODO: See if this should be poll or maybe named otherwise + void poll(); + + bool join(bool otaa); + + bool dataRate(uint8_t dr); + int getDataRate(); + + bool joinOTAA(const char *appEui, const char *appKey, const char *devEui) { return setDevEui(devEui) && joinOTAA(appEui, appKey); } + bool joinOTAA(const char *appEui, const char *appKey) { return setAppEui(appEui) && setAppKey(appKey) && join(true); } + bool joinOTAA(String appEui, String appKey) { return joinOTAA(appEui.c_str(), appKey.c_str()); } + bool joinOTAA(String appEui, String appKey, String devEui) { return joinOTAA(appEui.c_str(), appKey.c_str(), devEui.c_str()); } + bool joinOTAA(uint64_t appEui, const char *appKey, uint64_t devEui) { return setDevEui(devEui) && joinOTAA(appEui, appKey); } + bool joinOTAA(uint64_t appEui, const char *appKey) { return setAppEui(appEui) && setAppKey(appKey) && join(true); } + bool joinOTAA(uint64_t appEui, String appKey, uint64_t devEui) { return joinOTAA(appEui, appKey.c_str(), devEui); } + bool joinOTAA(uint64_t appEui, String appKey) { return joinOTAA(appEui, appKey.c_str()); } + + bool joinABP(const char *devAddr, const char *nwkSKey, const char *appSKey) { return setDevAddr(devAddr) && setNwkSKey(nwkSKey) && setAppSKey(appSKey) && join(false); } + bool joinABP(String devAddr, String nwkSKey, String appSKey) { return joinABP(devAddr.c_str(), nwkSKey.c_str(), appSKey.c_str()); } + bool joinABP(uint32_t devAddr, String nwkSKey, String appSKey) { return setDevAddr(devAddr) && setNwkSKey(nwkSKey) && setAppSKey(appSKey) && join(false); } + + bool send(const uint8_t *payload, size_t size, uint8_t port, bool confirmed); + + /** + * @name Advanced MIB access + * + * These methods allow direct access to the MIB (Mac Information + * Base) layer of the underlying stack to set and query values. + * These are only intended for advanced usage, when the regular API + * does not provide sufficient access. + * + * @param name Parameter name, only used in error messages + */ + /// @{ + bool mibGet(const char *name, Mib_t type, MibRequestConfirm_t &mibReq); + bool mibGetBool(const char *name, Mib_t type, bool *value); + bool mibGetUint8(const char *name, Mib_t type, uint8_t *value); + bool mibGetInt8(const char *name, Mib_t type, int8_t *value); + bool mibGetUint32(const char *name, Mib_t type, uint32_t *value); + bool mibGetUint64(const char *name, Mib_t type, uint64_t *value); + bool mibSet(const char *name, Mib_t type, MibRequestConfirm_t &mibReq); + bool mibSetBool(const char *name, Mib_t type, bool value); + bool mibSetUint8(const char *name, Mib_t type, uint8_t value); + bool mibSetInt8(const char *name, Mib_t type, int8_t value); + bool mibSetUint32(const char *name, Mib_t type, uint32_t value); + bool mibSetUint64(const char *name, Mib_t type, uint64_t value); + bool mibSetHex(const char *name, Mib_t type, const char *value); + /// @} + + /** + * @name Setters for identifiers and keys + * + * These methods allow setting various identifiers and keys. + * + * You can pass a hex-encoded (MSB-first) string (`String` object or + * `const char*`), or a raw integer (32-bits for DevAddr and 64-bits + * for EUIs, keys are too long to be passed as an integer). + */ + bool setDevEui(const char *value) { return mibSetHex("DevEui", MIB_DEV_EUI, value); } + bool setDevEui(String value) { return setDevEui(value.c_str()); } + bool setDevEui(uint64_t value) { return mibSetUint64("DevEui", MIB_DEV_EUI, value); } + bool setAppEui(const char *value) { return mibSetHex("AppEui", MIB_JOIN_EUI, value); } + bool setAppEui(String value) { return setDevEui(value.c_str()); } + bool setAppEui(uint64_t value) { return mibSetUint64("AppEui", MIB_JOIN_EUI, value); } + bool setDevAddr(const char *value) { return mibSetHex("DevAddr", MIB_DEV_ADDR, value); } + bool setDevAddr(String value) { return setDevAddr(value.c_str()); } + bool setDevAddr(uint32_t value) { return mibSetUint32("DevAddr", MIB_DEV_ADDR, value); } + bool setAppKey(const char *value) + { + // In LoRaWAN 1.0, only the appKey was configured and all keys + // were derived from that. In 1.1, this was split into an appKey + // and nwkKey. However, when running the LoRaMac-Node in 1.0 mode, + // it actually seems to use nwkKey, not appKey. So to support + // sketches that only configure appKey for 1.0, this saves the + // appKey to nwkKey as well. But to also prepare for future + // support of 1.1 and sketches that configure both, only do this + // if no nwkKey was explicitly configured. + return mibSetHex("AppKey", MIB_APP_KEY, value) + && (this->nwkKeySet || mibSetHex("NwkKey", MIB_NWK_KEY, value)); + + } + bool setAppKey(String value) { return setAppKey(value.c_str()); } + bool setNwkKey(const char *value) + { + this->nwkKeySet = true; + return mibSetHex("NwkKey", MIB_NWK_KEY, value); + } + bool setNwkKey(String value) { return setNwkKey(value.c_str()); } + bool setAppSKey(const char *value) { return mibSetHex("AppSKey", MIB_APP_S_KEY, value); } + bool setAppSKey(String value) { return setAppSKey(value.c_str()); } + bool setNwkSKey(const char *value) + { +#if ( USE_LRWAN_1_1_X_CRYPTO == 1 ) + // When compiled for 1.1 crypto, three different keys are used. + // When the sketch only supplies a single key, just set all + // three keys to the same value. + return mibSetHex("NwkSEncKey", MIB_NWK_S_ENC_KEY, value) + && mibSetHex("FNwkSIntKey", MIB_F_NWK_S_INT_KEY, value) + && mibSetHex("SNwkSIntKey", MIB_S_NWK_S_INT_KEY, value); +#else /* USE_LRWAN_1_1_X_CRYPTO == 0 */ + return mibSetHex("NwkSKey", MIB_NWK_S_KEY, value); +#endif /* USE_LRWAN_1_1_X_CRYPTO */ + } + bool setNwkSKey(String value) { return setNwkSKey(value.c_str()); } + +#if ( USE_LRWAN_1_1_X_CRYPTO == 1 ) + bool setNwkSEncKey(const char *value) { return mibSetHex("NwkSEncKey", MIB_NWK_S_ENC_KEY, value); } + bool setNwkSEncKey(String value) { return setNwkSEncKey(value.c_str()); } + bool setFNwkSIntKey(const char *value) { return mibSetHex("FNwkSIntKey", MIB_F_NWK_S_INT_KEY, value); } + bool setFNwkSIntKey(String value) { return setFNwkSIntKey(value.c_str()); } + bool setSNwkSIntKey(const char *value) { return mibSetHex("SNwkSIntKey", MIB_S_NWK_S_INT_KEY, value); } + bool setSNwkSIntKey(String value) { return setSNwkSIntKey(value.c_str()); } +#endif /* USE_LRWAN_1_1_X_CRYPTO */ + + bool connected(); + + protected: + // TODO: Implement these + static void MacMcpsConfirm(McpsConfirm_t *McpsConfirm) { } + static void MacMcpsIndication(McpsIndication_t *McpsIndication, LoRaMacRxStatus_t *RxStatus) { } + static void MacMlmeConfirm(MlmeConfirm_t *MlmeConfirm) { } + static void MacMlmeIndication(MlmeIndication_t *MlmeIndication, LoRaMacRxStatus_t *RxStatus) { } + + LoRaMacPrimitives_t LoRaMacPrimitives = { + .MacMcpsConfirm = MacMcpsConfirm, + .MacMcpsIndication = MacMcpsIndication, + .MacMlmeConfirm = MacMlmeConfirm, + .MacMlmeIndication = MacMlmeIndication, + }; + + LoRaMacCallback_t LoRaMacCallbacks = { + // TODO: Do we need these? + .GetBatteryLevel = nullptr, + .GetTemperatureLevel = nullptr, + .GetUniqueId = nullptr, + .NvmDataChange = nullptr, + .MacProcessNotify = nullptr, + }; + + static const char *toString(LoRaMacStatus_t); + static uint8_t parseHex(char c); + static bool parseHex(uint8_t *dest, const char *hex, size_t dest_len); + static uint32_t makeUint32(uint8_t a, uint8_t b, uint8_t c, uint8_t d) { return (uint32_t)a << 24 | (uint32_t)b << 16 | (uint32_t)c << 8 | (uint32_t)d << 0; } + + /** + * Helper that prints an error and then always returns false, to + * allow for combining reporting and returning in a single line. + */ + static bool failure(const char *fmt, ...); + + // This is used for joining and transmission (until ADR changes + // it, if enabled). This defaults to DR 4 since that is the + // fastest/highest (least spectrum usage) DR that is supported + // by all regions. If this DR has insufficient range, the join + // process (and ADR) will fall back to lower datarates + // automaticall. + uint8_t tx_dr = DR_4; + + bool nwkKeySet = false; +}; +// For MKRWAN compatibility +using LoRaModem = STM32LoRaWAN; From 1de4cfff79f8931a44be211d21c3af124d5005ea Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Fri, 24 Jun 2022 00:28:39 +0200 Subject: [PATCH 08/73] First: Add initial example This is just minimal and incomplete example. --- examples/First/First.ino | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 examples/First/First.ino diff --git a/examples/First/First.ino b/examples/First/First.ino new file mode 100644 index 0000000..c7fc5e4 --- /dev/null +++ b/examples/First/First.ino @@ -0,0 +1,40 @@ +#include +#include + +STM32LoRaWAN modem; + +static const unsigned long TX_INTERVAL = 60000; /* ms */ +unsigned long last_tx = 0; + +void setup() +{ + Serial.begin(115200); + Serial.println("Start"); + modem.begin(); + modem.dataRate(0); // 0 == slowest == most range + + modem.joinOTAA(/* AppEui */ "0000000000000000", /* AppKey */ "00000000000000000000000000000000", /* DevEui */ "0000000000000000"); + //modem.joinABP(/* DevAddr */ "00000000", /* NwkSKey */ "00000000000000000000000000000000", /* AppSKey */ "00000000000000000000000000000000"); + + while (!modem.connected()) { + modem.poll(); + } + + Serial.println("Joined"); +} + +void send_packet() +{ + uint8_t payload[] = {0xde, 0xad, 0xbe, 0xef}; + modem.send(payload, sizeof(payload), /* port */ 10, /* confirmed */ false); +} + +void loop() +{ + modem.poll(); + + if (!last_tx || millis() - last_tx > TX_INTERVAL) { + send_packet(); + last_tx = millis(); + } +} From ad79b772bb0d1f3fb1a89b63e77d08cb7996d3ec Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Tue, 2 Aug 2022 17:28:22 +0200 Subject: [PATCH 09/73] Make timer_if debug printing work This can be enabled be defining `RTIF_DEBUG`. To make this work without modifying `timer_if.c`, this adds a tiny `sys_app.h` file that just defines a dummy UTIL_ADV_TRACE_COND_Fsend macro that forwards to the MW_LOG function. --- src/BSP/sys_app.h | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 src/BSP/sys_app.h diff --git a/src/BSP/sys_app.h b/src/BSP/sys_app.h new file mode 100644 index 0000000..dde8ef8 --- /dev/null +++ b/src/BSP/sys_app.h @@ -0,0 +1,46 @@ +/** + ****************************************************************************** + * @file sys_app.h + * @author Matthijs Kooijman + * @brief Provide debug output macro for timer_if + ****************************************************************************** + * Copyright (c) 2022 STMicroelectronics. + * + * Revised BSD License - https://spdx.org/licenses/BSD-3-Clause.html + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. + ****************************************************************************** + */ +#ifndef __MW_SYS_APP_H__ +#define __MW_SYS_APP_H__ + +#include "mw_log_conf.h" + +// In the original STM32CubeWL code this is the function behind +// MW_LOG, but it is called directly as well from timer_if.c, so +// just redirect it for that particular usecase. +#define UTIL_ADV_TRACE_COND_FSend(level, reg, ts, ...) MW_LOG(ts, level, __VA_ARGS__) + +#endif // __MW_SYS_APP_H__ From 126f539beb386fac7adb179489a8f890eb9b7952 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 11 Aug 2022 05:31:37 +0200 Subject: [PATCH 10/73] Doxyfile: Add default doxygen configuration This is just the default file generated by `doxygen -g` (version 1.9.1). --- Doxyfile | 2658 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2658 insertions(+) create mode 100644 Doxyfile diff --git a/Doxyfile b/Doxyfile new file mode 100644 index 0000000..9265c1a --- /dev/null +++ b/Doxyfile @@ -0,0 +1,2658 @@ +# Doxyfile 1.9.1 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the configuration +# file that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# https://www.gnu.org/software/libiconv/ for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = "My Project" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = + +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all generated output in the proper direction. +# Possible values are: None, LTR, RTL and Context. +# The default value is: None. + +OUTPUT_TEXT_DIRECTION = None + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = NO + +# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line +# such as +# /*************** +# as being the beginning of a Javadoc-style comment "banner". If set to NO, the +# Javadoc-style will behave just like regular comments and it will not be +# interpreted by doxygen. +# The default value is: NO. + +JAVADOC_BANNER = NO + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# By default Python docstrings are displayed as preformatted text and doxygen's +# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the +# doxygen's special commands can be used and the contents of the docstring +# documentation blocks is shown as doxygen documentation. +# The default value is: YES. + +PYTHON_DOCSTRING = YES + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines (in the resulting output). You can put ^^ in the value part of an +# alias to insert a newline as if a physical newline was in the original file. +# When you need a literal { or } or , in the value part of an alias you have to +# escape them by means of a backslash (\), this can lead to conflicts with the +# commands \{ and \} for these it is advised to use the version @{ and @} or use +# a double escape (\\{ and \\}) + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice +# sources only. Doxygen will then generate output that is more tailored for that +# language. For instance, namespaces will be presented as modules, types will be +# separated into more groups, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_SLICE = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, JavaScript, +# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL, +# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser +# tries to guess whether the code is fixed or free formatted code, this is the +# default for Fortran type files). For instance to make doxygen treat .inc files +# as Fortran files (default is PHP), and .f files as C (default is Fortran), +# use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. When specifying no_extension you should add +# * to the FILE_PATTERNS. +# +# Note see also the list of default file extension mappings. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See https://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 5. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 5 + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use +# during processing. When set to 0 doxygen will based this on the number of +# cores available in the system. You can set it explicitly to a value larger +# than 0 to get more control over the balance between CPU load and processing +# speed. At this moment only the input processing can be done using multiple +# threads. Since this is still an experimental feature the default is set to 1, +# which efficively disables parallel processing. Please report any issues you +# encounter. Generating dot graphs in parallel is controlled by the +# DOT_NUM_THREADS setting. +# Minimum value: 0, maximum value: 32, default value: 1. + +NUM_PROC_THREADS = 1 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual +# methods of a class will be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIV_VIRTUAL = NO + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If this flag is set to YES, the name of an unnamed parameter in a declaration +# will be determined by the corresponding definition. By default unnamed +# parameters remain unnamed in the output. +# The default value is: YES. + +RESOLVE_UNNAMED_PARAMS = YES + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# declarations. If set to NO, these declarations will be included in the +# documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# With the correct setting of option CASE_SENSE_NAMES doxygen will better be +# able to match the capabilities of the underlying filesystem. In case the +# filesystem is case sensitive (i.e. it supports files in the same directory +# whose names only differ in casing), the option must be set to YES to properly +# deal with such files in case they appear in the input. For filesystems that +# are not case sensitive the option should be be set to NO to properly deal with +# output files written for symbols that only differ in casing, such as for two +# classes, one named CLASS and the other named Class, and to also support +# references to files without having to specify the exact matching casing. On +# Windows (including Cygwin) and MacOS, users should typically set this option +# to NO, whereas on Linux or other Unix flavors it should typically be set to +# YES. +# The default value is: system dependent. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, doxygen will only warn about wrong or incomplete +# parameter documentation, but not about the absence of documentation. If +# EXTRACT_ALL is set to YES then this flag will automatically be disabled. +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS +# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but +# at the end of the doxygen process doxygen will return with a non-zero status. +# Possible values are: NO, YES and FAIL_ON_WARNINGS. +# The default value is: NO. + +WARN_AS_ERROR = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. + +INPUT = + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: +# https://www.gnu.org/software/libiconv/) for the list of possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# Note the list of default checked file patterns might differ from the list of +# default file extension mappings. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, +# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, +# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, +# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment), +# *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, *.vhdl, +# *.ucf, *.qsf and *.ice. + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.idl \ + *.ddl \ + *.odl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.cs \ + *.d \ + *.php \ + *.php4 \ + *.php5 \ + *.phtml \ + *.inc \ + *.m \ + *.markdown \ + *.md \ + *.mm \ + *.dox \ + *.py \ + *.pyw \ + *.f90 \ + *.f95 \ + *.f03 \ + *.f08 \ + *.f18 \ + *.f \ + *.for \ + *.vhd \ + *.vhdl \ + *.ucf \ + *.qsf \ + *.ice + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# entity all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see https://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the +# clang parser (see: +# http://clang.llvm.org/) for more accurate parsing at the cost of reduced +# performance. This can be particularly helpful with template rich C++ code for +# which doxygen's built-in parser lacks the necessary type information. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If clang assisted parsing is enabled and the CLANG_ADD_INC_PATHS tag is set to +# YES then doxygen will add the directory of each input to the include path. +# The default value is: YES. + +CLANG_ADD_INC_PATHS = YES + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +# If clang assisted parsing is enabled you can provide the clang parser with the +# path to the directory containing a file called compile_commands.json. This +# file is the compilation database (see: +# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the +# options used when the source files were built. This is equivalent to +# specifying the -p option to a clang tool, such as clang-check. These options +# will then be passed to the parser. Any options specified with CLANG_OPTIONS +# will be added as well. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. + +CLANG_DATABASE_PATH = + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# https://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to YES can help to show when doxygen was last run and thus if the +# documentation is up to date. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = NO + +# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML +# documentation will contain a main index with vertical navigation menus that +# are dynamically created via JavaScript. If disabled, the navigation index will +# consists of multiple levels of tabs that are statically embedded in every HTML +# page. Disable this option to support browsers that do not have JavaScript, +# like the Qt help browser. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_MENUS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: +# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To +# create a documentation set, doxygen will generate a Makefile in the HTML +# output directory. Running make will produce the docset in that directory and +# running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy +# genXcode/_index.html for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: +# https://www.microsoft.com/en-us/download/details.aspx?id=21138) on Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the main .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location (absolute path +# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to +# run qhelpgenerator on the generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg +# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see +# https://inkscape.org) to generate formulas as SVG images instead of PNGs for +# the HTML output. These images will generally look nicer at scaled resolutions. +# Possible values are: png (the default) and svg (looks nicer but requires the +# pdf2svg or inkscape tool). +# The default value is: png. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FORMULA_FORMAT = png + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANSPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands +# to create new LaTeX commands to be used in formulas as building blocks. See +# the section "Including formulas" for details. + +FORMULA_MACROFILE = + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# https://www.mathjax.org) which uses client side JavaScript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from https://www.mathjax.org before deployment. +# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = https://cdn.jsdelivr.net/npm/mathjax@2 + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /