From fe6803fa40b9629a45f9b795a9d7d81ccd404a63 Mon Sep 17 00:00:00 2001 From: mculover666 <2412828003@qq.com> Date: Mon, 8 Jun 2020 21:40:22 +0800 Subject: [PATCH] add lorawan project --- board/RHF0M0E5_STM32WLE5xx/BSP/Inc/adc.h | 59 + board/RHF0M0E5_STM32WLE5xx/BSP/Inc/adc_if.h | 81 + board/RHF0M0E5_STM32WLE5xx/BSP/Inc/dma.h | 63 + board/RHF0M0E5_STM32WLE5xx/BSP/Inc/main.h | 4 + board/RHF0M0E5_STM32WLE5xx/BSP/Inc/mcu_init.h | 2 + board/RHF0M0E5_STM32WLE5xx/BSP/Inc/platform.h | 56 + board/RHF0M0E5_STM32WLE5xx/BSP/Inc/rtc.h | 63 + board/RHF0M0E5_STM32WLE5xx/BSP/Inc/rtc_if.h | 156 + .../BSP/Inc/stm32_lpm_if.h | 81 + .../BSP/Inc/stm32wlxx_hal_conf.h | 46 +- board/RHF0M0E5_STM32WLE5xx/BSP/Inc/usart.h | 59 +- board/RHF0M0E5_STM32WLE5xx/BSP/Inc/usart_if.h | 70 + .../BSP/Middlewares/LoRaWAN/Crypto/aes.c | 936 ++++ .../BSP/Middlewares/LoRaWAN/Crypto/aes.h | 162 + .../BSP/Middlewares/LoRaWAN/Crypto/cmac.c | 153 + .../BSP/Middlewares/LoRaWAN/Crypto/cmac.h | 63 + .../BSP/Middlewares/LoRaWAN/Crypto/soft-se.c | 417 ++ .../BSP/Middlewares/LoRaWAN/LICENSE.txt | 25 + .../BSP/Middlewares/LoRaWAN/Mac/LoRaMac.c | 4754 +++++++++++++++++ .../BSP/Middlewares/LoRaWAN/Mac/LoRaMac.h | 2707 ++++++++++ .../BSP/Middlewares/LoRaWAN/Mac/LoRaMacAdr.c | 131 + .../BSP/Middlewares/LoRaWAN/Mac/LoRaMacAdr.h | 104 + .../Middlewares/LoRaWAN/Mac/LoRaMacClassB.c | 1707 ++++++ .../Middlewares/LoRaWAN/Mac/LoRaMacClassB.h | 670 +++ .../LoRaWAN/Mac/LoRaMacClassBConfig.h | 115 + .../Middlewares/LoRaWAN/Mac/LoRaMacCommands.c | 547 ++ .../Middlewares/LoRaWAN/Mac/LoRaMacCommands.h | 218 + .../LoRaWAN/Mac/LoRaMacConfirmQueue.c | 344 ++ .../LoRaWAN/Mac/LoRaMacConfirmQueue.h | 195 + .../Middlewares/LoRaWAN/Mac/LoRaMacCrypto.c | 1693 ++++++ .../Middlewares/LoRaWAN/Mac/LoRaMacCrypto.h | 320 ++ .../LoRaWAN/Mac/LoRaMacFCntHandler.h | 170 + .../LoRaWAN/Mac/LoRaMacHeaderTypes.h | 186 + .../LoRaWAN/Mac/LoRaMacMessageTypes.h | 327 ++ .../Middlewares/LoRaWAN/Mac/LoRaMacParser.c | 118 + .../Middlewares/LoRaWAN/Mac/LoRaMacParser.h | 86 + .../LoRaWAN/Mac/LoRaMacSerializer.c | 183 + .../LoRaWAN/Mac/LoRaMacSerializer.h | 101 + .../BSP/Middlewares/LoRaWAN/Mac/LoRaMacTest.h | 51 + .../Middlewares/LoRaWAN/Mac/LoRaMacTypes.h | 655 +++ .../LoRaWAN/Mac/lora_mac_version.h | 56 + .../Middlewares/LoRaWAN/Mac/region/Region.c | 1155 ++++ .../Middlewares/LoRaWAN/Mac/region/Region.h | 1687 ++++++ .../LoRaWAN/Mac/region/RegionAS923.c | 1110 ++++ .../LoRaWAN/Mac/region/RegionAS923.h | 546 ++ .../LoRaWAN/Mac/region/RegionAU915.c | 1037 ++++ .../LoRaWAN/Mac/region/RegionAU915.h | 540 ++ .../LoRaWAN/Mac/region/RegionCN470.c | 845 +++ .../LoRaWAN/Mac/region/RegionCN470.h | 494 ++ .../LoRaWAN/Mac/region/RegionCN470ALI.c | 895 ++++ .../LoRaWAN/Mac/region/RegionCN470ALI.h | 594 ++ .../LoRaWAN/Mac/region/RegionCN779.c | 1040 ++++ .../LoRaWAN/Mac/region/RegionCN779.h | 508 ++ .../LoRaWAN/Mac/region/RegionCommon.c | 387 ++ .../LoRaWAN/Mac/region/RegionCommon.h | 421 ++ .../LoRaWAN/Mac/region/RegionEU433.c | 1040 ++++ .../LoRaWAN/Mac/region/RegionEU433.h | 509 ++ .../LoRaWAN/Mac/region/RegionEU868.c | 1075 ++++ .../LoRaWAN/Mac/region/RegionEU868.h | 536 ++ .../LoRaWAN/Mac/region/RegionIN865.c | 1060 ++++ .../LoRaWAN/Mac/region/RegionIN865.h | 511 ++ .../LoRaWAN/Mac/region/RegionKR920.c | 1050 ++++ .../LoRaWAN/Mac/region/RegionKR920.h | 521 ++ .../LoRaWAN/Mac/region/RegionRU864.c | 1039 ++++ .../LoRaWAN/Mac/region/RegionRU864.h | 500 ++ .../LoRaWAN/Mac/region/RegionUS915.c | 1153 ++++ .../LoRaWAN/Mac/region/RegionUS915.h | 506 ++ .../Middlewares/LoRaWAN/Mac/secure-element.h | 207 + .../Patterns/Basic/app_conf_template.h | 56 + .../Patterns/Basic/app_system_template.h | 62 + .../LoRaWAN/Patterns/Basic/lora-test.c | 433 ++ .../LoRaWAN/Patterns/Basic/lora-test.h | 72 + .../Middlewares/LoRaWAN/Patterns/Basic/lora.c | 985 ++++ .../Middlewares/LoRaWAN/Patterns/Basic/lora.h | 407 ++ .../LoRaWAN/Patterns/Basic/lorawan_info.c | 96 + .../LoRaWAN/Patterns/Basic/lorawan_info.h | 67 + .../Patterns/Modem/app_conf_template.h | 56 + .../Patterns/Modem/app_system_template.h | 62 + .../LoRaWAN/Patterns/Modem/lora-test.c | 417 ++ .../LoRaWAN/Patterns/Modem/lora-test.h | 71 + .../Middlewares/LoRaWAN/Patterns/Modem/lora.c | 1016 ++++ .../Middlewares/LoRaWAN/Patterns/Modem/lora.h | 403 ++ .../LoRaWAN/Patterns/Modem/lorawan_info.c | 94 + .../LoRaWAN/Patterns/Modem/lorawan_info.h | 67 + .../Middlewares/LoRaWAN/Utilities/utilities.c | 94 + .../Middlewares/LoRaWAN/Utilities/utilities.h | 136 + .../BSP/Middlewares/LoRaWAN/readme.md | 771 +++ .../BSP/Middlewares/LoRaWAN/st_readme.txt | 212 + .../BSP/Middlewares/SubGHz_Phy/LICENSE.txt | 25 + .../BSP/Middlewares/SubGHz_Phy/radio.h | 418 ++ .../stm32_radio_driver/mw_log_conf_template.h | 61 + .../SubGHz_Phy/stm32_radio_driver/radio.c | 1531 ++++++ .../stm32_radio_driver/radio_conf_template.h | 51 + .../stm32_radio_driver/radio_driver.c | 1168 ++++ .../stm32_radio_driver/radio_driver.h | 1223 +++++ .../stm32_radio_driver/radio_mw_version.h | 56 + board/RHF0M0E5_STM32WLE5xx/BSP/Src/adc.c | 142 + board/RHF0M0E5_STM32WLE5xx/BSP/Src/adc_if.c | 179 + board/RHF0M0E5_STM32WLE5xx/BSP/Src/dma.c | 56 + board/RHF0M0E5_STM32WLE5xx/BSP/Src/mcu_init.c | 7 +- board/RHF0M0E5_STM32WLE5xx/BSP/Src/rtc.c | 183 + board/RHF0M0E5_STM32WLE5xx/BSP/Src/rtc_if.c | 541 ++ .../BSP/Src/stm32_lpm_if.c | 133 + .../BSP/Src/stm32wlxx_it.c | 30 +- board/RHF0M0E5_STM32WLE5xx/BSP/Src/usart.c | 260 +- board/RHF0M0E5_STM32WLE5xx/BSP/Src/usart_if.c | 83 + .../BSP/Utilities/lpm/tiny_lpm/stm32_lpm.c | 180 + .../BSP/Utilities/lpm/tiny_lpm/stm32_lpm.h | 126 + .../BSP/Utilities/misc/stm32_mem.c | 65 + .../BSP/Utilities/misc/stm32_mem.h | 76 + .../BSP/Utilities/misc/stm32_systime.c | 351 ++ .../BSP/Utilities/misc/stm32_systime.h | 175 + .../BSP/Utilities/misc/stm32_tiny_sscanf.c | 976 ++++ .../BSP/Utilities/misc/stm32_tiny_sscanf.h | 67 + .../BSP/Utilities/misc/stm32_tiny_vsnprintf.c | 669 +++ .../BSP/Utilities/misc/stm32_tiny_vsnprintf.h | 62 + .../KEIL/lorawan/App/Commissioning.h | 95 + .../KEIL/lorawan/App/app_conf.h | 109 + .../KEIL/lorawan/App/app_lorawan.c | 329 ++ .../KEIL/lorawan/App/app_lorawan.h | 57 + .../KEIL/lorawan/App/app_system.c | 128 + .../KEIL/lorawan/App/app_system.h | 71 + .../KEIL/lorawan/App/lorawan_version.h | 52 + .../KEIL/lorawan/Target/lorawan_conf.h | 91 + .../KEIL/lorawan/Target/mw_log_conf.h | 73 + .../KEIL/lorawan/Target/radio_conf.h | 64 + .../KEIL/lorawan/Target/systime.h | 41 + .../KEIL/lorawan/Target/timer.h | 72 + .../KEIL/lorawan/Target/timer_wrapper.c | 18 + .../KEIL/lorawan/Target/utilities_conf.h | 118 + .../KEIL/lorawan/TencentOS_Tiny.uvoptx | 1666 ++++++ .../KEIL/lorawan/TencentOS_Tiny.uvprojx | 1026 ++++ .../KEIL/lorawan/startup_stm32wle5xx.s | 359 ++ 133 files changed, 57442 insertions(+), 298 deletions(-) create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Inc/adc.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Inc/adc_if.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Inc/dma.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Inc/platform.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Inc/rtc.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Inc/rtc_if.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Inc/stm32_lpm_if.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Inc/usart_if.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Crypto/aes.c create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Crypto/aes.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Crypto/cmac.c create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Crypto/cmac.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Crypto/soft-se.c create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/LICENSE.txt create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMac.c create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMac.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacAdr.c create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacAdr.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacClassB.c create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacClassB.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacClassBConfig.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacCommands.c create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacCommands.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacConfirmQueue.c create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacConfirmQueue.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacCrypto.c create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacCrypto.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacFCntHandler.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacHeaderTypes.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacMessageTypes.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacParser.c create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacParser.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacSerializer.c create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacSerializer.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacTest.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacTypes.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/lora_mac_version.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/Region.c create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/Region.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionAS923.c create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionAS923.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionAU915.c create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionAU915.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCN470.c create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCN470.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCN470ALI.c create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCN470ALI.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCN779.c create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCN779.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCommon.c create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCommon.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionEU433.c create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionEU433.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionEU868.c create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionEU868.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionIN865.c create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionIN865.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionKR920.c create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionKR920.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionRU864.c create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionRU864.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionUS915.c create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionUS915.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/secure-element.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/app_conf_template.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/app_system_template.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/lora-test.c create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/lora-test.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/lora.c create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/lora.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/lorawan_info.c create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/lorawan_info.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/app_conf_template.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/app_system_template.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/lora-test.c create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/lora-test.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/lora.c create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/lora.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/lorawan_info.c create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/lorawan_info.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Utilities/utilities.c create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Utilities/utilities.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/readme.md create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/st_readme.txt create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/LICENSE.txt create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/radio.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/stm32_radio_driver/mw_log_conf_template.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/stm32_radio_driver/radio.c create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/stm32_radio_driver/radio_conf_template.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/stm32_radio_driver/radio_driver.c create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/stm32_radio_driver/radio_driver.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/stm32_radio_driver/radio_mw_version.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Src/adc.c create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Src/adc_if.c create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Src/dma.c create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Src/rtc.c create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Src/rtc_if.c create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Src/stm32_lpm_if.c create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Src/usart_if.c create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/lpm/tiny_lpm/stm32_lpm.c create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/lpm/tiny_lpm/stm32_lpm.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_mem.c create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_mem.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_systime.c create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_systime.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_tiny_sscanf.c create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_tiny_sscanf.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_tiny_vsnprintf.c create mode 100644 board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_tiny_vsnprintf.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/Commissioning.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/app_conf.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/app_lorawan.c create mode 100644 board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/app_lorawan.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/app_system.c create mode 100644 board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/app_system.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/lorawan_version.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/lorawan_conf.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/mw_log_conf.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/radio_conf.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/systime.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/timer.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/timer_wrapper.c create mode 100644 board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/utilities_conf.h create mode 100644 board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/TencentOS_Tiny.uvoptx create mode 100644 board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/TencentOS_Tiny.uvprojx create mode 100644 board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/startup_stm32wle5xx.s diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/adc.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/adc.h new file mode 100644 index 00000000..e3aa4be1 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/adc.h @@ -0,0 +1,59 @@ +/** + ****************************************************************************** + * @file adc.h + * @author MCD Application Team + * @brief configuration of the ADC instances + ****************************************************************************** + * @attention + * + *

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

+ * + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 + * + ****************************************************************************** + */ +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __ADC_H__ +#define __ADC_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "main.h" + +/* Exported types ------------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ +/* External variables --------------------------------------------------------*/ + +/* USER CODE BEGIN Includes */ + +/* USER CODE END Includes */ +#ifdef HAL_ADC_MODULE_ENABLED +extern ADC_HandleTypeDef hadc1; +#endif +/* USER CODE BEGIN Private defines */ + +/* USER CODE END Private defines */ + +/* Exported macros -----------------------------------------------------------*/ +/* Defines -------------------------------------------------------------------*/ +/* Exported functions ------------------------------------------------------- */ +void MX_ADC1_Init(void); + +/* USER CODE BEGIN Prototypes */ + +/* USER CODE END Prototypes */ + +#ifdef __cplusplus +} +#endif + +#endif /* __ADC_H__ */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/adc_if.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/adc_if.h new file mode 100644 index 00000000..1e273376 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/adc_if.h @@ -0,0 +1,81 @@ +/** + ****************************************************************************** + * @file adc_if.h + * @author MCD Application Team + * @brief API and configuration of the adc_if.c instances + ****************************************************************************** + * @attention + * + *

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

+ * + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __ADC_IF_H__ +#define __ADC_IF_H__ + +#ifdef __cplusplus +extern "C" { +#endif +/* Includes ------------------------------------------------------------------*/ +#include "main.h" +#include "platform.h" + +/* Includes ------------------------------------------------------------------*/ +/* Exported types ------------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ + +#define VDDA_VREFINT_CAL ((uint32_t) 3000) +#define BAT_CR2032 ((uint32_t) 3000) +#define VDD_BAT BAT_CR2032 +#define VDD_MIN 1800 + +/* External variables --------------------------------------------------------*/ +/* Exported macros -----------------------------------------------------------*/ +/* Exported functions ------------------------------------------------------- */ + +/*! + * @brief Initializes the ADC input + * @param none + * @retval none + */ +void SYS_InitMeasurement(void); + +/*! + * @brief DeInitializes the ADC + * @param none + * @retval none + */ +void SYS_DeInitMeasurement(void); + +/*! + * @brief Get the current temperature + * @param none + * @retval value temperature in degreeCelcius( q7.8 ) + */ +int16_t SYS_GetTemperatureLevel(void); + + +/*! + * @brief Get the current battery level + * @param none + * @retval value battery level in linear scale + */ +uint16_t SYS_GetBatteryLevel(void); + + +#ifdef __cplusplus +} +#endif + +#endif /* __ADC_IF_H__ */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ + diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/dma.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/dma.h new file mode 100644 index 00000000..637c1297 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/dma.h @@ -0,0 +1,63 @@ +/** + ****************************************************************************** + * @file dma.h + * @author MCD Application Team + * @brief Header for driver dma.c module + ****************************************************************************** + * @attention + * + *

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

+ * + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __DMA_H__ +#define __DMA_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "main.h" + +/* DMA memory to memory transfer handles -------------------------------------*/ + +/* USER CODE BEGIN Includes */ + +/* USER CODE END Includes */ + +/* USER CODE BEGIN Private defines */ + +/* USER CODE END Private defines */ + +/** + * @brief Enables DMA controller clock + * @param None + * @retval None + * + */ +void MX_DMA_Init(void); + +/* USER CODE BEGIN Prototypes */ + +/* USER CODE END Prototypes */ + +#ifdef __cplusplus +} +#endif + +#endif /* __DMA_H__ */ + +/** + * @} + */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/main.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/main.h index e0a799e9..a233ce49 100644 --- a/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/main.h +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/main.h @@ -50,6 +50,10 @@ extern "C" { /* USER CODE END EM */ +#define RTC_N_PREDIV_S 10 /* subsecond number of bits */ +#define RTC_PREDIV_S ((1<
© Copyright (c) 2019 STMicroelectronics. + * All rights reserved.
+ * + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __PLATFORM_H__ +#define __PLATFORM_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include +#include "stm32wlxx.h" +#include "stm32wlxx_ll_gpio.h" +#include "main.h" +#include "usart.h" +#include "rtc.h" + + +/* Exported types ------------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ +/* External variables --------------------------------------------------------*/ +/* Exported macros -----------------------------------------------------------*/ +/* Defines -------------------------------------------------------------------*/ + +/* #warning "DBG LCH: VREFINT to be defined from AN" */ + + +/* Nothing for L4 but other platform might need it */ +#define __HAL_RCC_DBGMCU_CLK_ENABLE() +#define __HAL_RCC_DBGMCU_CLK_DISABLE() + +#ifdef __cplusplus +} +#endif + +#endif /* __PLATFORM_H__ */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/rtc.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/rtc.h new file mode 100644 index 00000000..5057c3f9 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/rtc.h @@ -0,0 +1,63 @@ +/** + ****************************************************************************** + * @file rtc.h + * @author MCD Application Team + * @brief configuration of the RTC instances + ****************************************************************************** + * @attention + * + *

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

+ * + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 + * + ****************************************************************************** + */ + +#ifndef __RTC_H__ +#define __RTC_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "main.h" + +/* USER CODE BEGIN Includes */ + +/* USER CODE END Includes */ + +extern RTC_HandleTypeDef hrtc; +/* Defines ------------------------------------------------------------*/ + +/* USER CODE BEGIN Private defines */ + +/* USER CODE END Private defines */ + +/* Exported types ------------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ +/* External variables --------------------------------------------------------*/ +/* Exported macros -----------------------------------------------------------*/ +/* Exported functions ------------------------------------------------------- */ +/** + * @brief Initializes the RTC + * @param None + * @retval None + */ +void MX_RTC_Init(void); + +/* USER CODE BEGIN Prototypes */ + +/* USER CODE END Prototypes */ + +#ifdef __cplusplus +} +#endif + +#endif /* __RTC_H__ */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/rtc_if.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/rtc_if.h new file mode 100644 index 00000000..7e5c7fea --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/rtc_if.h @@ -0,0 +1,156 @@ +/** + ****************************************************************************** + * @file rtc_if.h + * @author MCD Application Team + * @brief configuration of the rtc_if.c instances + ****************************************************************************** + * @attention + * + *

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

+ * + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __RTC_IF_H__ +#define __RTC_IF_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +//#include "stm32_timer.h" +/* Exported types ------------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ +/* External variables --------------------------------------------------------*/ +/* Exported macros -----------------------------------------------------------*/ +/* Exported functions ------------------------------------------------------- */ +/*! + * @brief Initializes the RTC timer + * @note The timer is based on the RTC + * @param none + * @retval none + */ +void RTC_Init( void ); + + + + + +/*! + * @brief Return the minimum timeout in ticks the RTC is able to handle + * @param none + * @retval minimum value for a timeout in ticks + */ +uint32_t RTC_GetMinimumTimeout( void ); + +/*! + * @brief Get the RTC timer elapsed time since the last Alarm was set + * @param none + * @retval RTC Elapsed time in ticks + */ +uint32_t RTC_GetTimerElapsedTime(void); + +/*! + * @brief Get the RTC timer value + * @param none + * @retval RTC Timer value in ticks + */ +uint32_t RTC_GetTimerValue( void ); + +/*! + * @brief set Time Reference set also the RTC_DateStruct and RTC_TimeStruct + * @param none + * @retval Timer Reference Value in Ticks + */ +uint32_t RTC_SetTimerContext( void ); + +/*! + * @brief Get the RTC timer Reference + * @param none + * @retval Timer Value in Ticks + */ +uint32_t RTC_GetTimerContext( void ); + +/*! + * @brief RTC IRQ Handler on the RTC Alarm + * @param none + * @retval none + */ +void RTC_IrqHandler ( void ); + +/*! + * @brief a delay of delay ms by polling RTC + * @param delay in ms + * @retval none + */ +void RTC_DelayMs(uint32_t delay); + +/*! + * @brief calculates the wake up time between wake up and mcu start + * @note resolution in RTC_ALARM_TIME_BASE in timer ticks + * @param none + * @retval none + */ +void RTC_setMcuWakeUpTime(void); + +/*! + * @brief returns the wake up time in us + * @param none + * @retval wake up time in ticks + */ +int16_t RTC_getMcuWakeUpTime( void ); + +/*! + * @brief converts time in ms to time in ticks + * @param [IN] time in milliseconds + * @retval returns time in timer ticks + */ +uint32_t RTC_ms2Tick( uint32_t timeMicroSec ); + +/*! + * @brief converts time in ticks to time in ms + * @param [IN] time in timer ticks + * @retval returns time in timer milliseconds + */ +uint32_t RTC_Tick2ms( uint32_t tick ); + +/*! + * \brief Get system time + * \param [IN] subSeconds in ms + * + * \uint32_t seconds + */ +uint32_t HW_RTC_GetCalendarTime(uint16_t *subSeconds); + +/*! + * \brief Read from backup registers + * \param [IN] Data 0 + * \param [IN] Data 1 + * + */ +void HW_RTC_BKUPRead(uint32_t *Data0, uint32_t *Data1); + +/*! + * \brief Write in backup registers + * \param [IN] Data 0 + * \param [IN] Data 1 + * + */ + +void HW_RTC_BKUPWrite(uint32_t Data0, uint32_t Data1); + +#ifdef __cplusplus +} +#endif + +#endif /* __RTC_IF_H__ */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/stm32_lpm_if.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/stm32_lpm_if.h new file mode 100644 index 00000000..899404eb --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/stm32_lpm_if.h @@ -0,0 +1,81 @@ +/** +****************************************************************************** + * @file stm32_lpm_if.h + * @author MCD Application Team + * @brief Header for stm32_lpm_f.c module (deveice specfic LP management) + ****************************************************************************** + * @attention + * + *

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

+ * + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32_LPM_IF_H__ +#define __STM32_LPM_IF_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "stm32_lpm.h" + +/** + * @brief Enters Low Power Off Mode + * @param none + * @retval none + */ +void PWR_EnterOffMode(void); +/** + * @brief Exits Low Power Off Mode + * @param none + * @retval none + */ +void PWR_ExitOffMode(void); + +/** + * @brief Enters Low Power Stop Mode + * @note ARM exists the function when waking up + * @param none + * @retval none + */ +void PWR_EnterStopMode(void); +/** + * @brief Exits Low Power Stop Mode + * @note Enable the pll at 32MHz + * @param none + * @retval none + */ +void PWR_ExitStopMode(void); + +/** + * @brief Enters Low Power Sleep Mode + * @note ARM exits the function when waking up + * @param none + * @retval none + */ +void PWR_EnterSleepMode(void); + +/** + * @brief Enters Low Power Sleep Mode + * @note ARM exits the function when waking up + * @param none + * @retval none + */ +void PWR_ExitSleepMode(void); + +#ifdef __cplusplus +} +#endif + +#endif /*__STM32_LPM_IF_H__ */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/stm32wlxx_hal_conf.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/stm32wlxx_hal_conf.h index c2c804f2..96a25727 100644 --- a/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/stm32wlxx_hal_conf.h +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/stm32wlxx_hal_conf.h @@ -33,30 +33,30 @@ * @brief This is the list of modules to be used in the HAL driver */ #define HAL_MODULE_ENABLED -/* #define HAL_ADC_MODULE_ENABLED */ -/* #define HAL_COMP_MODULE_ENABLED */ -/* #define HAL_CRC_MODULE_ENABLED */ -/* #define HAL_CRYP_MODULE_ENABLED */ -/* #define HAL_DAC_MODULE_ENABLED */ -/* #define HAL_GTZC_MODULE_ENABLED */ -/* #define HAL_HSEM_MODULE_ENABLED */ -/* #define HAL_I2C_MODULE_ENABLED */ -/* #define HAL_I2S_MODULE_ENABLED */ -/* #define HAL_IPCC_MODULE_ENABLED */ -/* #define HAL_IRDA_MODULE_ENABLED */ -/* #define HAL_IWDG_MODULE_ENABLED */ -/* #define HAL_LPTIM_MODULE_ENABLED */ -/* #define HAL_PKA_MODULE_ENABLED */ -/* #define HAL_RNG_MODULE_ENABLED */ -/* #define HAL_RTC_MODULE_ENABLED */ -/* #define HAL_SMARTCARD_MODULE_ENABLED */ -/* #define HAL_SMBUS_MODULE_ENABLED */ -/* #define HAL_SPI_MODULE_ENABLED */ -/* #define HAL_SUBGHZ_MODULE_ENABLED */ -/* #define HAL_TIM_MODULE_ENABLED */ +#define HAL_ADC_MODULE_ENABLED +//#define HAL_COMP_MODULE_ENABLED +//#define HAL_CRC_MODULE_ENABLED +//#define HAL_CRYP_MODULE_ENABLED +//#define HAL_DAC_MODULE_ENABLED +//#define HAL_GTZC_MODULE_ENABLED +//#define HAL_HSEM_MODULE_ENABLED +//#define HAL_I2C_MODULE_ENABLED +//#define HAL_I2S_MODULE_ENABLED +//#define HAL_IPCC_MODULE_ENABLED +//#define HAL_IRDA_MODULE_ENABLED +//#define HAL_IWDG_MODULE_ENABLED +#define HAL_LPTIM_MODULE_ENABLED +//#define HAL_PKA_MODULE_ENABLED +//#define HAL_RNG_MODULE_ENABLED +#define HAL_RTC_MODULE_ENABLED +//#define HAL_SMARTCARD_MODULE_ENABLED +//#define HAL_SMBUS_MODULE_ENABLED +#define HAL_SPI_MODULE_ENABLED +#define HAL_SUBGHZ_MODULE_ENABLED +//#define HAL_TIM_MODULE_ENABLED #define HAL_UART_MODULE_ENABLED -/* #define HAL_USART_MODULE_ENABLED */ -/* #define HAL_WWDG_MODULE_ENABLED */ +#define HAL_USART_MODULE_ENABLED +//#define HAL_WWDG_MODULE_ENABLED #define HAL_EXTI_MODULE_ENABLED #define HAL_CORTEX_MODULE_ENABLED #define HAL_DMA_MODULE_ENABLED diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/usart.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/usart.h index e23715c2..b2380278 100644 --- a/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/usart.h +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/usart.h @@ -1,26 +1,27 @@ /** ****************************************************************************** - * File Name : USART.h - * Description : This file provides code for the configuration - * of the USART instances. + * @file usart.h + * @author MCD Application Team + * @brief configuration of the USART instances ****************************************************************************** * @attention * *

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

* - * This software component is licensed by ST under BSD 3-Clause license, - * the "License"; You may not use this file except in compliance with the - * License. You may obtain a copy of the License at: - * opensource.org/licenses/BSD-3-Clause + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 * ****************************************************************************** */ /* Define to prevent recursive inclusion -------------------------------------*/ -#ifndef __usart_H -#define __usart_H +#ifndef __USART_H__ +#define __USART_H__ + #ifdef __cplusplus - extern "C" { +extern "C" { #endif /* Includes ------------------------------------------------------------------*/ @@ -30,34 +31,32 @@ /* USER CODE END Includes */ -extern UART_HandleTypeDef hlpuart1; + +/* Pin mapping */ + +#define USARTx_TX_Pin GPIO_PIN_6 +#define USARTx_TX_GPIO_Port GPIOB + +#define USARTx_RX_Pin GPIO_PIN_7 +#define USARTx_RX_GPIO_Port GPIOB + +/* Not generated BY MX (?), was definied in usart_if.c */ +#define USARTx_RX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE() +#define USARTx_TX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE() +#define USARTx_CLK_ENABLE() __HAL_RCC_USART1_CLK_ENABLE() + +#define USARTx_TX_AF GPIO_AF7_USART1 +#define USARTx_RX_AF GPIO_AF7_USART1 + extern UART_HandleTypeDef huart1; -extern UART_HandleTypeDef huart2; -/* USER CODE BEGIN Private defines */ - -/* USER CODE END Private defines */ - -void MX_LPUART1_UART_Init(void); void MX_USART1_UART_Init(void); -void MX_USART2_UART_Init(void); -/* USER CODE BEGIN Prototypes */ - -/* USER CODE END Prototypes */ - #ifdef __cplusplus } #endif -#endif /*__ usart_H */ -/** - * @} - */ - -/** - * @} - */ +#endif /* __USART_H__*/ /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/usart_if.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/usart_if.h new file mode 100644 index 00000000..5c9aad60 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Inc/usart_if.h @@ -0,0 +1,70 @@ +/** + ****************************************************************************** + * @file usart_if.h + * @author MCD Application Team + * @brief Header file for the UART driver handling for hyperterminal communication + ****************************************************************************** + * @attention + * + *

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

+ * + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 + * + ****************************************************************************** + */ + +#include "usart.h" +#include "dma.h" + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __USART_IF_H__ +#define __USART_IF_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +/* Exported types ------------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ +/* External variables --------------------------------------------------------*/ +/* Exported functions ------------------------------------------------------- */ + +/** +* @brief Init the UART and associated DMA. +* @param TxCpltCallback +* @return none +*/ +uint8_t vcom_Init(void (*cb)(void*)); + + +/** +* @brief DeInit the UART and associated DMA. +* @param none +* @return none +*/ +uint8_t vcom_DeInit(void); + + +/** +* @brief send buffer @p_data of size size to vcom using DMA +* @param p_data data to be sent +* @param szie of buffer p_data to be sent +* @return none +*/ +uint8_t vcom_Trace(uint8_t *p_data, uint16_t size); + + + + +#ifdef __cplusplus +} +#endif + +#endif /* __USART_IF_H__ */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Crypto/aes.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Crypto/aes.c new file mode 100644 index 00000000..7f46ea37 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Crypto/aes.c @@ -0,0 +1,936 @@ +/* + --------------------------------------------------------------------------- + Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved. + + LICENSE TERMS + + The redistribution and use of this software (with or without changes) + is allowed without the payment of fees or royalties provided that: + + 1. source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + 2. binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation; + + 3. the name of the copyright holder is not used to endorse products + built using this software without specific written permission. + + DISCLAIMER + + This software is provided 'as is' with no explicit or implied warranties + in respect of its properties, including, but not limited to, correctness + and/or fitness for purpose. + --------------------------------------------------------------------------- + Issue 09/09/2006 + + This is an AES implementation that uses only 8-bit byte operations on the + cipher state (there are options to use 32-bit types if available). + + The combination of mix columns and byte substitution used here is based on + that developed by Karl Malbrain. His contribution is acknowledged. + */ + +/* define if you have a fast memcpy function on your system */ +#if 0 +# define HAVE_MEMCPY +# include +# if defined( _MSC_VER ) +# include +# pragma intrinsic( memcpy ) +# endif +#endif + + +#include +#include + +/* define if you have fast 32-bit types on your system */ +#if ( __CORTEX_M != 0 ) // if Cortex is different from M0/M0+ +# define HAVE_UINT_32T +#endif + +/* define if you don't want any tables */ +#if 1 +# define USE_TABLES +#endif + +/* On Intel Core 2 duo VERSION_1 is faster */ + +/* alternative versions (test for performance on your system) */ +#if 1 +# define VERSION_1 +#endif + +#include "aes.h" + +//#if defined( HAVE_UINT_32T ) +// typedef unsigned long uint32_t; +//#endif + +/* functions for finite field multiplication in the AES Galois field */ + +#define WPOLY 0x011b +#define BPOLY 0x1b +#define DPOLY 0x008d + +#define f1(x) (x) +#define f2(x) ((x << 1) ^ (((x >> 7) & 1) * WPOLY)) +#define f4(x) ((x << 2) ^ (((x >> 6) & 1) * WPOLY) ^ (((x >> 6) & 2) * WPOLY)) +#define f8(x) ((x << 3) ^ (((x >> 5) & 1) * WPOLY) ^ (((x >> 5) & 2) * WPOLY) \ + ^ (((x >> 5) & 4) * WPOLY)) +#define d2(x) (((x) >> 1) ^ ((x) & 1 ? DPOLY : 0)) + +#define f3(x) (f2(x) ^ x) +#define f9(x) (f8(x) ^ x) +#define fb(x) (f8(x) ^ f2(x) ^ x) +#define fd(x) (f8(x) ^ f4(x) ^ x) +#define fe(x) (f8(x) ^ f4(x) ^ f2(x)) + +#if defined( USE_TABLES ) + +#define sb_data(w) { /* S Box data values */ \ + w(0x63), w(0x7c), w(0x77), w(0x7b), w(0xf2), w(0x6b), w(0x6f), w(0xc5),\ + w(0x30), w(0x01), w(0x67), w(0x2b), w(0xfe), w(0xd7), w(0xab), w(0x76),\ + w(0xca), w(0x82), w(0xc9), w(0x7d), w(0xfa), w(0x59), w(0x47), w(0xf0),\ + w(0xad), w(0xd4), w(0xa2), w(0xaf), w(0x9c), w(0xa4), w(0x72), w(0xc0),\ + w(0xb7), w(0xfd), w(0x93), w(0x26), w(0x36), w(0x3f), w(0xf7), w(0xcc),\ + w(0x34), w(0xa5), w(0xe5), w(0xf1), w(0x71), w(0xd8), w(0x31), w(0x15),\ + w(0x04), w(0xc7), w(0x23), w(0xc3), w(0x18), w(0x96), w(0x05), w(0x9a),\ + w(0x07), w(0x12), w(0x80), w(0xe2), w(0xeb), w(0x27), w(0xb2), w(0x75),\ + w(0x09), w(0x83), w(0x2c), w(0x1a), w(0x1b), w(0x6e), w(0x5a), w(0xa0),\ + w(0x52), w(0x3b), w(0xd6), w(0xb3), w(0x29), w(0xe3), w(0x2f), w(0x84),\ + w(0x53), w(0xd1), w(0x00), w(0xed), w(0x20), w(0xfc), w(0xb1), w(0x5b),\ + w(0x6a), w(0xcb), w(0xbe), w(0x39), w(0x4a), w(0x4c), w(0x58), w(0xcf),\ + w(0xd0), w(0xef), w(0xaa), w(0xfb), w(0x43), w(0x4d), w(0x33), w(0x85),\ + w(0x45), w(0xf9), w(0x02), w(0x7f), w(0x50), w(0x3c), w(0x9f), w(0xa8),\ + w(0x51), w(0xa3), w(0x40), w(0x8f), w(0x92), w(0x9d), w(0x38), w(0xf5),\ + w(0xbc), w(0xb6), w(0xda), w(0x21), w(0x10), w(0xff), w(0xf3), w(0xd2),\ + w(0xcd), w(0x0c), w(0x13), w(0xec), w(0x5f), w(0x97), w(0x44), w(0x17),\ + w(0xc4), w(0xa7), w(0x7e), w(0x3d), w(0x64), w(0x5d), w(0x19), w(0x73),\ + w(0x60), w(0x81), w(0x4f), w(0xdc), w(0x22), w(0x2a), w(0x90), w(0x88),\ + w(0x46), w(0xee), w(0xb8), w(0x14), w(0xde), w(0x5e), w(0x0b), w(0xdb),\ + w(0xe0), w(0x32), w(0x3a), w(0x0a), w(0x49), w(0x06), w(0x24), w(0x5c),\ + w(0xc2), w(0xd3), w(0xac), w(0x62), w(0x91), w(0x95), w(0xe4), w(0x79),\ + w(0xe7), w(0xc8), w(0x37), w(0x6d), w(0x8d), w(0xd5), w(0x4e), w(0xa9),\ + w(0x6c), w(0x56), w(0xf4), w(0xea), w(0x65), w(0x7a), w(0xae), w(0x08),\ + w(0xba), w(0x78), w(0x25), w(0x2e), w(0x1c), w(0xa6), w(0xb4), w(0xc6),\ + w(0xe8), w(0xdd), w(0x74), w(0x1f), w(0x4b), w(0xbd), w(0x8b), w(0x8a),\ + w(0x70), w(0x3e), w(0xb5), w(0x66), w(0x48), w(0x03), w(0xf6), w(0x0e),\ + w(0x61), w(0x35), w(0x57), w(0xb9), w(0x86), w(0xc1), w(0x1d), w(0x9e),\ + w(0xe1), w(0xf8), w(0x98), w(0x11), w(0x69), w(0xd9), w(0x8e), w(0x94),\ + w(0x9b), w(0x1e), w(0x87), w(0xe9), w(0xce), w(0x55), w(0x28), w(0xdf),\ + w(0x8c), w(0xa1), w(0x89), w(0x0d), w(0xbf), w(0xe6), w(0x42), w(0x68),\ + w(0x41), w(0x99), w(0x2d), w(0x0f), w(0xb0), w(0x54), w(0xbb), w(0x16) } + +#define isb_data(w) { /* inverse S Box data values */ \ + w(0x52), w(0x09), w(0x6a), w(0xd5), w(0x30), w(0x36), w(0xa5), w(0x38),\ + w(0xbf), w(0x40), w(0xa3), w(0x9e), w(0x81), w(0xf3), w(0xd7), w(0xfb),\ + w(0x7c), w(0xe3), w(0x39), w(0x82), w(0x9b), w(0x2f), w(0xff), w(0x87),\ + w(0x34), w(0x8e), w(0x43), w(0x44), w(0xc4), w(0xde), w(0xe9), w(0xcb),\ + w(0x54), w(0x7b), w(0x94), w(0x32), w(0xa6), w(0xc2), w(0x23), w(0x3d),\ + w(0xee), w(0x4c), w(0x95), w(0x0b), w(0x42), w(0xfa), w(0xc3), w(0x4e),\ + w(0x08), w(0x2e), w(0xa1), w(0x66), w(0x28), w(0xd9), w(0x24), w(0xb2),\ + w(0x76), w(0x5b), w(0xa2), w(0x49), w(0x6d), w(0x8b), w(0xd1), w(0x25),\ + w(0x72), w(0xf8), w(0xf6), w(0x64), w(0x86), w(0x68), w(0x98), w(0x16),\ + w(0xd4), w(0xa4), w(0x5c), w(0xcc), w(0x5d), w(0x65), w(0xb6), w(0x92),\ + w(0x6c), w(0x70), w(0x48), w(0x50), w(0xfd), w(0xed), w(0xb9), w(0xda),\ + w(0x5e), w(0x15), w(0x46), w(0x57), w(0xa7), w(0x8d), w(0x9d), w(0x84),\ + w(0x90), w(0xd8), w(0xab), w(0x00), w(0x8c), w(0xbc), w(0xd3), w(0x0a),\ + w(0xf7), w(0xe4), w(0x58), w(0x05), w(0xb8), w(0xb3), w(0x45), w(0x06),\ + w(0xd0), w(0x2c), w(0x1e), w(0x8f), w(0xca), w(0x3f), w(0x0f), w(0x02),\ + w(0xc1), w(0xaf), w(0xbd), w(0x03), w(0x01), w(0x13), w(0x8a), w(0x6b),\ + w(0x3a), w(0x91), w(0x11), w(0x41), w(0x4f), w(0x67), w(0xdc), w(0xea),\ + w(0x97), w(0xf2), w(0xcf), w(0xce), w(0xf0), w(0xb4), w(0xe6), w(0x73),\ + w(0x96), w(0xac), w(0x74), w(0x22), w(0xe7), w(0xad), w(0x35), w(0x85),\ + w(0xe2), w(0xf9), w(0x37), w(0xe8), w(0x1c), w(0x75), w(0xdf), w(0x6e),\ + w(0x47), w(0xf1), w(0x1a), w(0x71), w(0x1d), w(0x29), w(0xc5), w(0x89),\ + w(0x6f), w(0xb7), w(0x62), w(0x0e), w(0xaa), w(0x18), w(0xbe), w(0x1b),\ + w(0xfc), w(0x56), w(0x3e), w(0x4b), w(0xc6), w(0xd2), w(0x79), w(0x20),\ + w(0x9a), w(0xdb), w(0xc0), w(0xfe), w(0x78), w(0xcd), w(0x5a), w(0xf4),\ + w(0x1f), w(0xdd), w(0xa8), w(0x33), w(0x88), w(0x07), w(0xc7), w(0x31),\ + w(0xb1), w(0x12), w(0x10), w(0x59), w(0x27), w(0x80), w(0xec), w(0x5f),\ + w(0x60), w(0x51), w(0x7f), w(0xa9), w(0x19), w(0xb5), w(0x4a), w(0x0d),\ + w(0x2d), w(0xe5), w(0x7a), w(0x9f), w(0x93), w(0xc9), w(0x9c), w(0xef),\ + w(0xa0), w(0xe0), w(0x3b), w(0x4d), w(0xae), w(0x2a), w(0xf5), w(0xb0),\ + w(0xc8), w(0xeb), w(0xbb), w(0x3c), w(0x83), w(0x53), w(0x99), w(0x61),\ + w(0x17), w(0x2b), w(0x04), w(0x7e), w(0xba), w(0x77), w(0xd6), w(0x26),\ + w(0xe1), w(0x69), w(0x14), w(0x63), w(0x55), w(0x21), w(0x0c), w(0x7d) } + +#define mm_data(w) { /* basic data for forming finite field tables */ \ + w(0x00), w(0x01), w(0x02), w(0x03), w(0x04), w(0x05), w(0x06), w(0x07),\ + w(0x08), w(0x09), w(0x0a), w(0x0b), w(0x0c), w(0x0d), w(0x0e), w(0x0f),\ + w(0x10), w(0x11), w(0x12), w(0x13), w(0x14), w(0x15), w(0x16), w(0x17),\ + w(0x18), w(0x19), w(0x1a), w(0x1b), w(0x1c), w(0x1d), w(0x1e), w(0x1f),\ + w(0x20), w(0x21), w(0x22), w(0x23), w(0x24), w(0x25), w(0x26), w(0x27),\ + w(0x28), w(0x29), w(0x2a), w(0x2b), w(0x2c), w(0x2d), w(0x2e), w(0x2f),\ + w(0x30), w(0x31), w(0x32), w(0x33), w(0x34), w(0x35), w(0x36), w(0x37),\ + w(0x38), w(0x39), w(0x3a), w(0x3b), w(0x3c), w(0x3d), w(0x3e), w(0x3f),\ + w(0x40), w(0x41), w(0x42), w(0x43), w(0x44), w(0x45), w(0x46), w(0x47),\ + w(0x48), w(0x49), w(0x4a), w(0x4b), w(0x4c), w(0x4d), w(0x4e), w(0x4f),\ + w(0x50), w(0x51), w(0x52), w(0x53), w(0x54), w(0x55), w(0x56), w(0x57),\ + w(0x58), w(0x59), w(0x5a), w(0x5b), w(0x5c), w(0x5d), w(0x5e), w(0x5f),\ + w(0x60), w(0x61), w(0x62), w(0x63), w(0x64), w(0x65), w(0x66), w(0x67),\ + w(0x68), w(0x69), w(0x6a), w(0x6b), w(0x6c), w(0x6d), w(0x6e), w(0x6f),\ + w(0x70), w(0x71), w(0x72), w(0x73), w(0x74), w(0x75), w(0x76), w(0x77),\ + w(0x78), w(0x79), w(0x7a), w(0x7b), w(0x7c), w(0x7d), w(0x7e), w(0x7f),\ + w(0x80), w(0x81), w(0x82), w(0x83), w(0x84), w(0x85), w(0x86), w(0x87),\ + w(0x88), w(0x89), w(0x8a), w(0x8b), w(0x8c), w(0x8d), w(0x8e), w(0x8f),\ + w(0x90), w(0x91), w(0x92), w(0x93), w(0x94), w(0x95), w(0x96), w(0x97),\ + w(0x98), w(0x99), w(0x9a), w(0x9b), w(0x9c), w(0x9d), w(0x9e), w(0x9f),\ + w(0xa0), w(0xa1), w(0xa2), w(0xa3), w(0xa4), w(0xa5), w(0xa6), w(0xa7),\ + w(0xa8), w(0xa9), w(0xaa), w(0xab), w(0xac), w(0xad), w(0xae), w(0xaf),\ + w(0xb0), w(0xb1), w(0xb2), w(0xb3), w(0xb4), w(0xb5), w(0xb6), w(0xb7),\ + w(0xb8), w(0xb9), w(0xba), w(0xbb), w(0xbc), w(0xbd), w(0xbe), w(0xbf),\ + w(0xc0), w(0xc1), w(0xc2), w(0xc3), w(0xc4), w(0xc5), w(0xc6), w(0xc7),\ + w(0xc8), w(0xc9), w(0xca), w(0xcb), w(0xcc), w(0xcd), w(0xce), w(0xcf),\ + w(0xd0), w(0xd1), w(0xd2), w(0xd3), w(0xd4), w(0xd5), w(0xd6), w(0xd7),\ + w(0xd8), w(0xd9), w(0xda), w(0xdb), w(0xdc), w(0xdd), w(0xde), w(0xdf),\ + w(0xe0), w(0xe1), w(0xe2), w(0xe3), w(0xe4), w(0xe5), w(0xe6), w(0xe7),\ + w(0xe8), w(0xe9), w(0xea), w(0xeb), w(0xec), w(0xed), w(0xee), w(0xef),\ + w(0xf0), w(0xf1), w(0xf2), w(0xf3), w(0xf4), w(0xf5), w(0xf6), w(0xf7),\ + w(0xf8), w(0xf9), w(0xfa), w(0xfb), w(0xfc), w(0xfd), w(0xfe), w(0xff) } + +static const uint8_t sbox[256] = sb_data(f1); + +#if defined( AES_DEC_PREKEYED ) +static const uint8_t isbox[256] = isb_data(f1); +#endif + +static const uint8_t gfm2_sbox[256] = sb_data(f2); +static const uint8_t gfm3_sbox[256] = sb_data(f3); + +#if defined( AES_DEC_PREKEYED ) +static const uint8_t gfmul_9[256] = mm_data(f9); +static const uint8_t gfmul_b[256] = mm_data(fb); +static const uint8_t gfmul_d[256] = mm_data(fd); +static const uint8_t gfmul_e[256] = mm_data(fe); +#endif + +#define s_box(x) sbox[(x)] +#if defined( AES_DEC_PREKEYED ) +#define is_box(x) isbox[(x)] +#endif +#define gfm2_sb(x) gfm2_sbox[(x)] +#define gfm3_sb(x) gfm3_sbox[(x)] +#if defined( AES_DEC_PREKEYED ) +#define gfm_9(x) gfmul_9[(x)] +#define gfm_b(x) gfmul_b[(x)] +#define gfm_d(x) gfmul_d[(x)] +#define gfm_e(x) gfmul_e[(x)] +#endif +#else + +/* this is the high bit of x right shifted by 1 */ +/* position. Since the starting polynomial has */ +/* 9 bits (0x11b), this right shift keeps the */ +/* values of all top bits within a byte */ + +static uint8_t hibit(const uint8_t x) +{ uint8_t r = (uint8_t)((x >> 1) | (x >> 2)); + + r |= (r >> 2); + r |= (r >> 4); + return (r + 1) >> 1; +} + +/* return the inverse of the finite field element x */ + +static uint8_t gf_inv(const uint8_t x) +{ uint8_t p1 = x, p2 = BPOLY, n1 = hibit(x), n2 = 0x80, v1 = 1, v2 = 0; + + if(x < 2) + return x; + + for( ; ; ) + { + if(n1) + while(n2 >= n1) /* divide polynomial p2 by p1 */ + { + n2 /= n1; /* shift smaller polynomial left */ + p2 ^= (p1 * n2) & 0xff; /* and remove from larger one */ + v2 ^= (v1 * n2); /* shift accumulated value and */ + n2 = hibit(p2); /* add into result */ + } + else + return v1; + + if(n2) /* repeat with values swapped */ + while(n1 >= n2) + { + n1 /= n2; + p1 ^= p2 * n1; + v1 ^= v2 * n1; + n1 = hibit(p1); + } + else + return v2; + } +} + +/* The forward and inverse affine transformations used in the S-box */ +uint8_t fwd_affine(const uint8_t x) +{ +#if defined( HAVE_UINT_32T ) + uint32_t w = x; + w ^= (w << 1) ^ (w << 2) ^ (w << 3) ^ (w << 4); + return 0x63 ^ ((w ^ (w >> 8)) & 0xff); +#else + return 0x63 ^ x ^ (x << 1) ^ (x << 2) ^ (x << 3) ^ (x << 4) + ^ (x >> 7) ^ (x >> 6) ^ (x >> 5) ^ (x >> 4); +#endif +} + +uint8_t inv_affine(const uint8_t x) +{ +#if defined( HAVE_UINT_32T ) + uint32_t w = x; + w = (w << 1) ^ (w << 3) ^ (w << 6); + return 0x05 ^ ((w ^ (w >> 8)) & 0xff); +#else + return 0x05 ^ (x << 1) ^ (x << 3) ^ (x << 6) + ^ (x >> 7) ^ (x >> 5) ^ (x >> 2); +#endif +} + +#define s_box(x) fwd_affine(gf_inv(x)) +#define is_box(x) gf_inv(inv_affine(x)) +#define gfm2_sb(x) f2(s_box(x)) +#define gfm3_sb(x) f3(s_box(x)) +#define gfm_9(x) f9(x) +#define gfm_b(x) fb(x) +#define gfm_d(x) fd(x) +#define gfm_e(x) fe(x) + +#endif + +#if defined( HAVE_MEMCPY ) +# define block_copy_nn(d, s, l) memcpy(d, s, l) +# define block_copy(d, s) memcpy(d, s, N_BLOCK) +#else +# define block_copy_nn(d, s, l) copy_block_nn(d, s, l) +# define block_copy(d, s) copy_block(d, s) +#endif + +static void copy_block( void *d, const void *s ) +{ +#if defined( HAVE_UINT_32T ) + ((uint32_t*)d)[ 0] = ((uint32_t*)s)[ 0]; + ((uint32_t*)d)[ 1] = ((uint32_t*)s)[ 1]; + ((uint32_t*)d)[ 2] = ((uint32_t*)s)[ 2]; + ((uint32_t*)d)[ 3] = ((uint32_t*)s)[ 3]; +#else + ((uint8_t*)d)[ 0] = ((uint8_t*)s)[ 0]; + ((uint8_t*)d)[ 1] = ((uint8_t*)s)[ 1]; + ((uint8_t*)d)[ 2] = ((uint8_t*)s)[ 2]; + ((uint8_t*)d)[ 3] = ((uint8_t*)s)[ 3]; + ((uint8_t*)d)[ 4] = ((uint8_t*)s)[ 4]; + ((uint8_t*)d)[ 5] = ((uint8_t*)s)[ 5]; + ((uint8_t*)d)[ 6] = ((uint8_t*)s)[ 6]; + ((uint8_t*)d)[ 7] = ((uint8_t*)s)[ 7]; + ((uint8_t*)d)[ 8] = ((uint8_t*)s)[ 8]; + ((uint8_t*)d)[ 9] = ((uint8_t*)s)[ 9]; + ((uint8_t*)d)[10] = ((uint8_t*)s)[10]; + ((uint8_t*)d)[11] = ((uint8_t*)s)[11]; + ((uint8_t*)d)[12] = ((uint8_t*)s)[12]; + ((uint8_t*)d)[13] = ((uint8_t*)s)[13]; + ((uint8_t*)d)[14] = ((uint8_t*)s)[14]; + ((uint8_t*)d)[15] = ((uint8_t*)s)[15]; +#endif +} + +static void copy_block_nn( uint8_t * d, const uint8_t *s, uint8_t nn ) +{ + while( nn-- ) + //*((uint8_t*)d)++ = *((uint8_t*)s)++; + *d++ = *s++; +} + +static void xor_block( void *d, const void *s ) +{ +#if defined( HAVE_UINT_32T ) + ((uint32_t*)d)[ 0] ^= ((uint32_t*)s)[ 0]; + ((uint32_t*)d)[ 1] ^= ((uint32_t*)s)[ 1]; + ((uint32_t*)d)[ 2] ^= ((uint32_t*)s)[ 2]; + ((uint32_t*)d)[ 3] ^= ((uint32_t*)s)[ 3]; +#else + ((uint8_t*)d)[ 0] ^= ((uint8_t*)s)[ 0]; + ((uint8_t*)d)[ 1] ^= ((uint8_t*)s)[ 1]; + ((uint8_t*)d)[ 2] ^= ((uint8_t*)s)[ 2]; + ((uint8_t*)d)[ 3] ^= ((uint8_t*)s)[ 3]; + ((uint8_t*)d)[ 4] ^= ((uint8_t*)s)[ 4]; + ((uint8_t*)d)[ 5] ^= ((uint8_t*)s)[ 5]; + ((uint8_t*)d)[ 6] ^= ((uint8_t*)s)[ 6]; + ((uint8_t*)d)[ 7] ^= ((uint8_t*)s)[ 7]; + ((uint8_t*)d)[ 8] ^= ((uint8_t*)s)[ 8]; + ((uint8_t*)d)[ 9] ^= ((uint8_t*)s)[ 9]; + ((uint8_t*)d)[10] ^= ((uint8_t*)s)[10]; + ((uint8_t*)d)[11] ^= ((uint8_t*)s)[11]; + ((uint8_t*)d)[12] ^= ((uint8_t*)s)[12]; + ((uint8_t*)d)[13] ^= ((uint8_t*)s)[13]; + ((uint8_t*)d)[14] ^= ((uint8_t*)s)[14]; + ((uint8_t*)d)[15] ^= ((uint8_t*)s)[15]; +#endif +} + +static void copy_and_key( void *d, const void *s, const void *k ) +{ +#if defined( HAVE_UINT_32T ) + ((uint32_t*)d)[ 0] = ((uint32_t*)s)[ 0] ^ ((uint32_t*)k)[ 0]; + ((uint32_t*)d)[ 1] = ((uint32_t*)s)[ 1] ^ ((uint32_t*)k)[ 1]; + ((uint32_t*)d)[ 2] = ((uint32_t*)s)[ 2] ^ ((uint32_t*)k)[ 2]; + ((uint32_t*)d)[ 3] = ((uint32_t*)s)[ 3] ^ ((uint32_t*)k)[ 3]; +#elif 1 + ((uint8_t*)d)[ 0] = ((uint8_t*)s)[ 0] ^ ((uint8_t*)k)[ 0]; + ((uint8_t*)d)[ 1] = ((uint8_t*)s)[ 1] ^ ((uint8_t*)k)[ 1]; + ((uint8_t*)d)[ 2] = ((uint8_t*)s)[ 2] ^ ((uint8_t*)k)[ 2]; + ((uint8_t*)d)[ 3] = ((uint8_t*)s)[ 3] ^ ((uint8_t*)k)[ 3]; + ((uint8_t*)d)[ 4] = ((uint8_t*)s)[ 4] ^ ((uint8_t*)k)[ 4]; + ((uint8_t*)d)[ 5] = ((uint8_t*)s)[ 5] ^ ((uint8_t*)k)[ 5]; + ((uint8_t*)d)[ 6] = ((uint8_t*)s)[ 6] ^ ((uint8_t*)k)[ 6]; + ((uint8_t*)d)[ 7] = ((uint8_t*)s)[ 7] ^ ((uint8_t*)k)[ 7]; + ((uint8_t*)d)[ 8] = ((uint8_t*)s)[ 8] ^ ((uint8_t*)k)[ 8]; + ((uint8_t*)d)[ 9] = ((uint8_t*)s)[ 9] ^ ((uint8_t*)k)[ 9]; + ((uint8_t*)d)[10] = ((uint8_t*)s)[10] ^ ((uint8_t*)k)[10]; + ((uint8_t*)d)[11] = ((uint8_t*)s)[11] ^ ((uint8_t*)k)[11]; + ((uint8_t*)d)[12] = ((uint8_t*)s)[12] ^ ((uint8_t*)k)[12]; + ((uint8_t*)d)[13] = ((uint8_t*)s)[13] ^ ((uint8_t*)k)[13]; + ((uint8_t*)d)[14] = ((uint8_t*)s)[14] ^ ((uint8_t*)k)[14]; + ((uint8_t*)d)[15] = ((uint8_t*)s)[15] ^ ((uint8_t*)k)[15]; +#else + block_copy(d, s); + xor_block(d, k); +#endif +} + +static void add_round_key( uint8_t d[N_BLOCK], const uint8_t k[N_BLOCK] ) +{ + xor_block(d, k); +} + +static void shift_sub_rows( uint8_t st[N_BLOCK] ) +{ uint8_t tt; + + st[ 0] = s_box(st[ 0]); st[ 4] = s_box(st[ 4]); + st[ 8] = s_box(st[ 8]); st[12] = s_box(st[12]); + + tt = st[1]; st[ 1] = s_box(st[ 5]); st[ 5] = s_box(st[ 9]); + st[ 9] = s_box(st[13]); st[13] = s_box( tt ); + + tt = st[2]; st[ 2] = s_box(st[10]); st[10] = s_box( tt ); + tt = st[6]; st[ 6] = s_box(st[14]); st[14] = s_box( tt ); + + tt = st[15]; st[15] = s_box(st[11]); st[11] = s_box(st[ 7]); + st[ 7] = s_box(st[ 3]); st[ 3] = s_box( tt ); +} + +#if defined( AES_DEC_PREKEYED ) + +static void inv_shift_sub_rows( uint8_t st[N_BLOCK] ) +{ uint8_t tt; + + st[ 0] = is_box(st[ 0]); st[ 4] = is_box(st[ 4]); + st[ 8] = is_box(st[ 8]); st[12] = is_box(st[12]); + + tt = st[13]; st[13] = is_box(st[9]); st[ 9] = is_box(st[5]); + st[ 5] = is_box(st[1]); st[ 1] = is_box( tt ); + + tt = st[2]; st[ 2] = is_box(st[10]); st[10] = is_box( tt ); + tt = st[6]; st[ 6] = is_box(st[14]); st[14] = is_box( tt ); + + tt = st[3]; st[ 3] = is_box(st[ 7]); st[ 7] = is_box(st[11]); + st[11] = is_box(st[15]); st[15] = is_box( tt ); +} + +#endif + +#if defined( VERSION_1 ) + static void mix_sub_columns( uint8_t dt[N_BLOCK] ) + { uint8_t st[N_BLOCK]; + block_copy(st, dt); +#else + static void mix_sub_columns( uint8_t dt[N_BLOCK], uint8_t st[N_BLOCK] ) + { +#endif + dt[ 0] = gfm2_sb(st[0]) ^ gfm3_sb(st[5]) ^ s_box(st[10]) ^ s_box(st[15]); + dt[ 1] = s_box(st[0]) ^ gfm2_sb(st[5]) ^ gfm3_sb(st[10]) ^ s_box(st[15]); + dt[ 2] = s_box(st[0]) ^ s_box(st[5]) ^ gfm2_sb(st[10]) ^ gfm3_sb(st[15]); + dt[ 3] = gfm3_sb(st[0]) ^ s_box(st[5]) ^ s_box(st[10]) ^ gfm2_sb(st[15]); + + dt[ 4] = gfm2_sb(st[4]) ^ gfm3_sb(st[9]) ^ s_box(st[14]) ^ s_box(st[3]); + dt[ 5] = s_box(st[4]) ^ gfm2_sb(st[9]) ^ gfm3_sb(st[14]) ^ s_box(st[3]); + dt[ 6] = s_box(st[4]) ^ s_box(st[9]) ^ gfm2_sb(st[14]) ^ gfm3_sb(st[3]); + dt[ 7] = gfm3_sb(st[4]) ^ s_box(st[9]) ^ s_box(st[14]) ^ gfm2_sb(st[3]); + + dt[ 8] = gfm2_sb(st[8]) ^ gfm3_sb(st[13]) ^ s_box(st[2]) ^ s_box(st[7]); + dt[ 9] = s_box(st[8]) ^ gfm2_sb(st[13]) ^ gfm3_sb(st[2]) ^ s_box(st[7]); + dt[10] = s_box(st[8]) ^ s_box(st[13]) ^ gfm2_sb(st[2]) ^ gfm3_sb(st[7]); + dt[11] = gfm3_sb(st[8]) ^ s_box(st[13]) ^ s_box(st[2]) ^ gfm2_sb(st[7]); + + dt[12] = gfm2_sb(st[12]) ^ gfm3_sb(st[1]) ^ s_box(st[6]) ^ s_box(st[11]); + dt[13] = s_box(st[12]) ^ gfm2_sb(st[1]) ^ gfm3_sb(st[6]) ^ s_box(st[11]); + dt[14] = s_box(st[12]) ^ s_box(st[1]) ^ gfm2_sb(st[6]) ^ gfm3_sb(st[11]); + dt[15] = gfm3_sb(st[12]) ^ s_box(st[1]) ^ s_box(st[6]) ^ gfm2_sb(st[11]); + } + +#if defined( AES_DEC_PREKEYED ) + +#if defined( VERSION_1 ) + static void inv_mix_sub_columns( uint8_t dt[N_BLOCK] ) + { uint8_t st[N_BLOCK]; + block_copy(st, dt); +#else + static void inv_mix_sub_columns( uint8_t dt[N_BLOCK], uint8_t st[N_BLOCK] ) + { +#endif + dt[ 0] = is_box(gfm_e(st[ 0]) ^ gfm_b(st[ 1]) ^ gfm_d(st[ 2]) ^ gfm_9(st[ 3])); + dt[ 5] = is_box(gfm_9(st[ 0]) ^ gfm_e(st[ 1]) ^ gfm_b(st[ 2]) ^ gfm_d(st[ 3])); + dt[10] = is_box(gfm_d(st[ 0]) ^ gfm_9(st[ 1]) ^ gfm_e(st[ 2]) ^ gfm_b(st[ 3])); + dt[15] = is_box(gfm_b(st[ 0]) ^ gfm_d(st[ 1]) ^ gfm_9(st[ 2]) ^ gfm_e(st[ 3])); + + dt[ 4] = is_box(gfm_e(st[ 4]) ^ gfm_b(st[ 5]) ^ gfm_d(st[ 6]) ^ gfm_9(st[ 7])); + dt[ 9] = is_box(gfm_9(st[ 4]) ^ gfm_e(st[ 5]) ^ gfm_b(st[ 6]) ^ gfm_d(st[ 7])); + dt[14] = is_box(gfm_d(st[ 4]) ^ gfm_9(st[ 5]) ^ gfm_e(st[ 6]) ^ gfm_b(st[ 7])); + dt[ 3] = is_box(gfm_b(st[ 4]) ^ gfm_d(st[ 5]) ^ gfm_9(st[ 6]) ^ gfm_e(st[ 7])); + + dt[ 8] = is_box(gfm_e(st[ 8]) ^ gfm_b(st[ 9]) ^ gfm_d(st[10]) ^ gfm_9(st[11])); + dt[13] = is_box(gfm_9(st[ 8]) ^ gfm_e(st[ 9]) ^ gfm_b(st[10]) ^ gfm_d(st[11])); + dt[ 2] = is_box(gfm_d(st[ 8]) ^ gfm_9(st[ 9]) ^ gfm_e(st[10]) ^ gfm_b(st[11])); + dt[ 7] = is_box(gfm_b(st[ 8]) ^ gfm_d(st[ 9]) ^ gfm_9(st[10]) ^ gfm_e(st[11])); + + dt[12] = is_box(gfm_e(st[12]) ^ gfm_b(st[13]) ^ gfm_d(st[14]) ^ gfm_9(st[15])); + dt[ 1] = is_box(gfm_9(st[12]) ^ gfm_e(st[13]) ^ gfm_b(st[14]) ^ gfm_d(st[15])); + dt[ 6] = is_box(gfm_d(st[12]) ^ gfm_9(st[13]) ^ gfm_e(st[14]) ^ gfm_b(st[15])); + dt[11] = is_box(gfm_b(st[12]) ^ gfm_d(st[13]) ^ gfm_9(st[14]) ^ gfm_e(st[15])); + } + +#endif + +#if defined( AES_ENC_PREKEYED ) || defined( AES_DEC_PREKEYED ) + +/* Set the cipher key for the pre-keyed version */ + +return_type aes_set_key( const uint8_t key[], length_type keylen, aes_context ctx[1] ) +{ + uint8_t cc, rc, hi; + + switch( keylen ) + { + case 16: + case 24: + case 32: + break; + default: + ctx->rnd = 0; + return ( uint8_t )-1; + } + block_copy_nn(ctx->ksch, key, keylen); + hi = (keylen + 28) << 2; + ctx->rnd = (hi >> 4) - 1; + for( cc = keylen, rc = 1; cc < hi; cc += 4 ) + { uint8_t tt, t0, t1, t2, t3; + + t0 = ctx->ksch[cc - 4]; + t1 = ctx->ksch[cc - 3]; + t2 = ctx->ksch[cc - 2]; + t3 = ctx->ksch[cc - 1]; + if( cc % keylen == 0 ) + { + tt = t0; + t0 = s_box(t1) ^ rc; + t1 = s_box(t2); + t2 = s_box(t3); + t3 = s_box(tt); + rc = f2(rc); + } + else if( keylen > 24 && cc % keylen == 16 ) + { + t0 = s_box(t0); + t1 = s_box(t1); + t2 = s_box(t2); + t3 = s_box(t3); + } + tt = cc - keylen; + ctx->ksch[cc + 0] = ctx->ksch[tt + 0] ^ t0; + ctx->ksch[cc + 1] = ctx->ksch[tt + 1] ^ t1; + ctx->ksch[cc + 2] = ctx->ksch[tt + 2] ^ t2; + ctx->ksch[cc + 3] = ctx->ksch[tt + 3] ^ t3; + } + return 0; +} + +#endif + +#if defined( AES_ENC_PREKEYED ) + +/* Encrypt a single block of 16 bytes */ + +return_type aes_encrypt( const uint8_t in[N_BLOCK], uint8_t out[N_BLOCK], const aes_context ctx[1] ) +{ + if( ctx->rnd ) + { + uint8_t s1[N_BLOCK], r; + copy_and_key( s1, in, ctx->ksch ); + + for( r = 1 ; r < ctx->rnd ; ++r ) +#if defined( VERSION_1 ) + { + mix_sub_columns( s1 ); + add_round_key( s1, ctx->ksch + r * N_BLOCK); + } +#else + { uint8_t s2[N_BLOCK]; + mix_sub_columns( s2, s1 ); + copy_and_key( s1, s2, ctx->ksch + r * N_BLOCK); + } +#endif + shift_sub_rows( s1 ); + copy_and_key( out, s1, ctx->ksch + r * N_BLOCK ); + } + else + return ( uint8_t )-1; + return 0; +} + +/* CBC encrypt a number of blocks (input and return an IV) */ + +return_type aes_cbc_encrypt( const uint8_t *in, uint8_t *out, + int32_t n_block, uint8_t iv[N_BLOCK], const aes_context ctx[1] ) +{ + + while(n_block--) + { + xor_block(iv, in); + if(aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) + return EXIT_FAILURE; + //memcpy(out, iv, N_BLOCK); + block_copy(out, iv); + in += N_BLOCK; + out += N_BLOCK; + } + return EXIT_SUCCESS; +} + +#endif + +#if defined( AES_DEC_PREKEYED ) + +/* Decrypt a single block of 16 bytes */ + +return_type aes_decrypt( const uint8_t in[N_BLOCK], uint8_t out[N_BLOCK], const aes_context ctx[1] ) +{ + if( ctx->rnd ) + { + uint8_t s1[N_BLOCK], r; + copy_and_key( s1, in, ctx->ksch + ctx->rnd * N_BLOCK ); + inv_shift_sub_rows( s1 ); + + for( r = ctx->rnd ; --r ; ) +#if defined( VERSION_1 ) + { + add_round_key( s1, ctx->ksch + r * N_BLOCK ); + inv_mix_sub_columns( s1 ); + } +#else + { uint8_t s2[N_BLOCK]; + copy_and_key( s2, s1, ctx->ksch + r * N_BLOCK ); + inv_mix_sub_columns( s1, s2 ); + } +#endif + copy_and_key( out, s1, ctx->ksch ); + } + else + return -1; + return 0; +} + +/* CBC decrypt a number of blocks (input and return an IV) */ + +return_type aes_cbc_decrypt( const uint8_t *in, uint8_t *out, + int32_t n_block, uint8_t iv[N_BLOCK], const aes_context ctx[1] ) +{ + while(n_block--) + { uint8_t tmp[N_BLOCK]; + + //memcpy(tmp, in, N_BLOCK); + block_copy(tmp, in); + if(aes_decrypt(in, out, ctx) != EXIT_SUCCESS) + return EXIT_FAILURE; + xor_block(out, iv); + //memcpy(iv, tmp, N_BLOCK); + block_copy(iv, tmp); + in += N_BLOCK; + out += N_BLOCK; + } + return EXIT_SUCCESS; +} + +#endif + +#if defined( AES_ENC_128_OTFK ) + +/* The 'on the fly' encryption key update for for 128 bit keys */ + +static void update_encrypt_key_128( uint8_t k[N_BLOCK], uint8_t *rc ) +{ uint8_t cc; + + k[0] ^= s_box(k[13]) ^ *rc; + k[1] ^= s_box(k[14]); + k[2] ^= s_box(k[15]); + k[3] ^= s_box(k[12]); + *rc = f2( *rc ); + + for(cc = 4; cc < 16; cc += 4 ) + { + k[cc + 0] ^= k[cc - 4]; + k[cc + 1] ^= k[cc - 3]; + k[cc + 2] ^= k[cc - 2]; + k[cc + 3] ^= k[cc - 1]; + } +} + +/* Encrypt a single block of 16 bytes with 'on the fly' 128 bit keying */ + +void aes_encrypt_128( const uint8_t in[N_BLOCK], uint8_t out[N_BLOCK], + const uint8_t key[N_BLOCK], uint8_t o_key[N_BLOCK] ) +{ uint8_t s1[N_BLOCK], r, rc = 1; + + if(o_key != key) + block_copy( o_key, key ); + copy_and_key( s1, in, o_key ); + + for( r = 1 ; r < 10 ; ++r ) +#if defined( VERSION_1 ) + { + mix_sub_columns( s1 ); + update_encrypt_key_128( o_key, &rc ); + add_round_key( s1, o_key ); + } +#else + { uint8_t s2[N_BLOCK]; + mix_sub_columns( s2, s1 ); + update_encrypt_key_128( o_key, &rc ); + copy_and_key( s1, s2, o_key ); + } +#endif + + shift_sub_rows( s1 ); + update_encrypt_key_128( o_key, &rc ); + copy_and_key( out, s1, o_key ); +} + +#endif + +#if defined( AES_DEC_128_OTFK ) + +/* The 'on the fly' decryption key update for for 128 bit keys */ + +static void update_decrypt_key_128( uint8_t k[N_BLOCK], uint8_t *rc ) +{ uint8_t cc; + + for( cc = 12; cc > 0; cc -= 4 ) + { + k[cc + 0] ^= k[cc - 4]; + k[cc + 1] ^= k[cc - 3]; + k[cc + 2] ^= k[cc - 2]; + k[cc + 3] ^= k[cc - 1]; + } + *rc = d2(*rc); + k[0] ^= s_box(k[13]) ^ *rc; + k[1] ^= s_box(k[14]); + k[2] ^= s_box(k[15]); + k[3] ^= s_box(k[12]); +} + +/* Decrypt a single block of 16 bytes with 'on the fly' 128 bit keying */ + +void aes_decrypt_128( const uint8_t in[N_BLOCK], uint8_t out[N_BLOCK], + const uint8_t key[N_BLOCK], uint8_t o_key[N_BLOCK] ) +{ + uint8_t s1[N_BLOCK], r, rc = 0x6c; + if(o_key != key) + block_copy( o_key, key ); + + copy_and_key( s1, in, o_key ); + inv_shift_sub_rows( s1 ); + + for( r = 10 ; --r ; ) +#if defined( VERSION_1 ) + { + update_decrypt_key_128( o_key, &rc ); + add_round_key( s1, o_key ); + inv_mix_sub_columns( s1 ); + } +#else + { uint8_t s2[N_BLOCK]; + update_decrypt_key_128( o_key, &rc ); + copy_and_key( s2, s1, o_key ); + inv_mix_sub_columns( s1, s2 ); + } +#endif + update_decrypt_key_128( o_key, &rc ); + copy_and_key( out, s1, o_key ); +} + +#endif + +#if defined( AES_ENC_256_OTFK ) + +/* The 'on the fly' encryption key update for for 256 bit keys */ + +static void update_encrypt_key_256( uint8_t k[2 * N_BLOCK], uint8_t *rc ) +{ uint8_t cc; + + k[0] ^= s_box(k[29]) ^ *rc; + k[1] ^= s_box(k[30]); + k[2] ^= s_box(k[31]); + k[3] ^= s_box(k[28]); + *rc = f2( *rc ); + + for(cc = 4; cc < 16; cc += 4) + { + k[cc + 0] ^= k[cc - 4]; + k[cc + 1] ^= k[cc - 3]; + k[cc + 2] ^= k[cc - 2]; + k[cc + 3] ^= k[cc - 1]; + } + + k[16] ^= s_box(k[12]); + k[17] ^= s_box(k[13]); + k[18] ^= s_box(k[14]); + k[19] ^= s_box(k[15]); + + for( cc = 20; cc < 32; cc += 4 ) + { + k[cc + 0] ^= k[cc - 4]; + k[cc + 1] ^= k[cc - 3]; + k[cc + 2] ^= k[cc - 2]; + k[cc + 3] ^= k[cc - 1]; + } +} + +/* Encrypt a single block of 16 bytes with 'on the fly' 256 bit keying */ + +void aes_encrypt_256( const uint8_t in[N_BLOCK], uint8_t out[N_BLOCK], + const uint8_t key[2 * N_BLOCK], uint8_t o_key[2 * N_BLOCK] ) +{ + uint8_t s1[N_BLOCK], r, rc = 1; + if(o_key != key) + { + block_copy( o_key, key ); + block_copy( o_key + 16, key + 16 ); + } + copy_and_key( s1, in, o_key ); + + for( r = 1 ; r < 14 ; ++r ) +#if defined( VERSION_1 ) + { + mix_sub_columns(s1); + if( r & 1 ) + add_round_key( s1, o_key + 16 ); + else + { + update_encrypt_key_256( o_key, &rc ); + add_round_key( s1, o_key ); + } + } +#else + { uint8_t s2[N_BLOCK]; + mix_sub_columns( s2, s1 ); + if( r & 1 ) + copy_and_key( s1, s2, o_key + 16 ); + else + { + update_encrypt_key_256( o_key, &rc ); + copy_and_key( s1, s2, o_key ); + } + } +#endif + + shift_sub_rows( s1 ); + update_encrypt_key_256( o_key, &rc ); + copy_and_key( out, s1, o_key ); +} + +#endif + +#if defined( AES_DEC_256_OTFK ) + +/* The 'on the fly' encryption key update for for 256 bit keys */ + +static void update_decrypt_key_256( uint8_t k[2 * N_BLOCK], uint8_t *rc ) +{ uint8_t cc; + + for(cc = 28; cc > 16; cc -= 4) + { + k[cc + 0] ^= k[cc - 4]; + k[cc + 1] ^= k[cc - 3]; + k[cc + 2] ^= k[cc - 2]; + k[cc + 3] ^= k[cc - 1]; + } + + k[16] ^= s_box(k[12]); + k[17] ^= s_box(k[13]); + k[18] ^= s_box(k[14]); + k[19] ^= s_box(k[15]); + + for(cc = 12; cc > 0; cc -= 4) + { + k[cc + 0] ^= k[cc - 4]; + k[cc + 1] ^= k[cc - 3]; + k[cc + 2] ^= k[cc - 2]; + k[cc + 3] ^= k[cc - 1]; + } + + *rc = d2(*rc); + k[0] ^= s_box(k[29]) ^ *rc; + k[1] ^= s_box(k[30]); + k[2] ^= s_box(k[31]); + k[3] ^= s_box(k[28]); +} + +/* Decrypt a single block of 16 bytes with 'on the fly' + 256 bit keying +*/ +void aes_decrypt_256( const uint8_t in[N_BLOCK], uint8_t out[N_BLOCK], + const uint8_t key[2 * N_BLOCK], uint8_t o_key[2 * N_BLOCK] ) +{ + uint8_t s1[N_BLOCK], r, rc = 0x80; + + if(o_key != key) + { + block_copy( o_key, key ); + block_copy( o_key + 16, key + 16 ); + } + + copy_and_key( s1, in, o_key ); + inv_shift_sub_rows( s1 ); + + for( r = 14 ; --r ; ) +#if defined( VERSION_1 ) + { + if( ( r & 1 ) ) + { + update_decrypt_key_256( o_key, &rc ); + add_round_key( s1, o_key + 16 ); + } + else + add_round_key( s1, o_key ); + inv_mix_sub_columns( s1 ); + } +#else + { uint8_t s2[N_BLOCK]; + if( ( r & 1 ) ) + { + update_decrypt_key_256( o_key, &rc ); + copy_and_key( s2, s1, o_key + 16 ); + } + else + copy_and_key( s2, s1, o_key ); + inv_mix_sub_columns( s1, s2 ); + } +#endif + copy_and_key( out, s1, o_key ); +} + +#endif diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Crypto/aes.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Crypto/aes.h new file mode 100644 index 00000000..2411c2fd --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Crypto/aes.h @@ -0,0 +1,162 @@ +/* + --------------------------------------------------------------------------- + Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved. + + LICENSE TERMS + + The redistribution and use of this software (with or without changes) + is allowed without the payment of fees or royalties provided that: + + 1. source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + 2. binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation; + + 3. the name of the copyright holder is not used to endorse products + built using this software without specific written permission. + + DISCLAIMER + + This software is provided 'as is' with no explicit or implied warranties + in respect of its properties, including, but not limited to, correctness + and/or fitness for purpose. + --------------------------------------------------------------------------- + Issue 09/09/2006 + + This is an AES implementation that uses only 8-bit byte operations on the + cipher state. + */ + +#ifndef AES_H +#define AES_H + +#include + +#if 1 +# define AES_ENC_PREKEYED /* AES encryption with a precomputed key schedule */ +#endif +#if 0 +# define AES_DEC_PREKEYED /* AES decryption with a precomputed key schedule */ +#endif +#if 0 +# define AES_ENC_128_OTFK /* AES encryption with 'on the fly' 128 bit keying */ +#endif +#if 0 +# define AES_DEC_128_OTFK /* AES decryption with 'on the fly' 128 bit keying */ +#endif +#if 0 +# define AES_ENC_256_OTFK /* AES encryption with 'on the fly' 256 bit keying */ +#endif +#if 0 +# define AES_DEC_256_OTFK /* AES decryption with 'on the fly' 256 bit keying */ +#endif + +#define N_ROW 4 +#define N_COL 4 +#define N_BLOCK (N_ROW * N_COL) +#define N_MAX_ROUNDS 14 + +typedef uint8_t return_type; + +/* Warning: The key length for 256 bit keys overflows a byte + (see comment below) +*/ + +typedef uint8_t length_type; + +typedef struct +{ uint8_t ksch[(N_MAX_ROUNDS + 1) * N_BLOCK]; + uint8_t rnd; +} aes_context; + +/* The following calls are for a precomputed key schedule + + NOTE: If the length_type used for the key length is an + unsigned 8-bit character, a key length of 256 bits must + be entered as a length in bytes (valid inputs are hence + 128, 192, 16, 24 and 32). +*/ + +#if defined( AES_ENC_PREKEYED ) || defined( AES_DEC_PREKEYED ) + +return_type aes_set_key( const uint8_t key[], + length_type keylen, + aes_context ctx[1] ); +#endif + +#if defined( AES_ENC_PREKEYED ) + +return_type aes_encrypt( const uint8_t in[N_BLOCK], + uint8_t out[N_BLOCK], + const aes_context ctx[1] ); + +return_type aes_cbc_encrypt( const uint8_t *in, + uint8_t *out, + int32_t n_block, + uint8_t iv[N_BLOCK], + const aes_context ctx[1] ); +#endif + +#if defined( AES_DEC_PREKEYED ) + +return_type aes_decrypt( const uint8_t in[N_BLOCK], + uint8_t out[N_BLOCK], + const aes_context ctx[1] ); + +return_type aes_cbc_decrypt( const uint8_t *in, + uint8_t *out, + int32_t n_block, + uint8_t iv[N_BLOCK], + const aes_context ctx[1] ); +#endif + +/* The following calls are for 'on the fly' keying. In this case the + encryption and decryption keys are different. + + The encryption subroutines take a key in an array of bytes in + key[L] where L is 16, 24 or 32 bytes for key lengths of 128, + 192, and 256 bits respectively. They then encrypts the input + data, in[] with this key and put the reult in the output array + out[]. In addition, the second key array, o_key[L], is used + to output the key that is needed by the decryption subroutine + to reverse the encryption operation. The two key arrays can + be the same array but in this case the original key will be + overwritten. + + In the same way, the decryption subroutines output keys that + can be used to reverse their effect when used for encryption. + + Only 128 and 256 bit keys are supported in these 'on the fly' + modes. +*/ + +#if defined( AES_ENC_128_OTFK ) +void aes_encrypt_128( const uint8_t in[N_BLOCK], + uint8_t out[N_BLOCK], + const uint8_t key[N_BLOCK], + uint8_t o_key[N_BLOCK] ); +#endif + +#if defined( AES_DEC_128_OTFK ) +void aes_decrypt_128( const uint8_t in[N_BLOCK], + uint8_t out[N_BLOCK], + const uint8_t key[N_BLOCK], + uint8_t o_key[N_BLOCK] ); +#endif + +#if defined( AES_ENC_256_OTFK ) +void aes_encrypt_256( const uint8_t in[N_BLOCK], + uint8_t out[N_BLOCK], + const uint8_t key[2 * N_BLOCK], + uint8_t o_key[2 * N_BLOCK] ); +#endif + +#if defined( AES_DEC_256_OTFK ) +void aes_decrypt_256( const uint8_t in[N_BLOCK], + uint8_t out[N_BLOCK], + const uint8_t key[2 * N_BLOCK], + uint8_t o_key[2 * N_BLOCK] ); +#endif + +#endif diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Crypto/cmac.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Crypto/cmac.c new file mode 100644 index 00000000..9ea1a42b --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Crypto/cmac.c @@ -0,0 +1,153 @@ +/************************************************************************** +Copyright (C) 2009 Lander Casado, Philippas Tsigas + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files +(the "Software"), to deal with the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimers. Redistributions in +binary form must reproduce the above copyright notice, this list of +conditions and the following disclaimers in the documentation and/or +other materials provided with the distribution. + +In no event shall the authors or copyright holders be liable for any special, +incidental, indirect or consequential damages of any kind, or any damages +whatsoever resulting from loss of use, data or profits, whether or not +advised of the possibility of damage, and on any theory of liability, +arising out of or in connection with the use or performance of this software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS WITH THE SOFTWARE + +*****************************************************************************/ +//#include +//#include +#include +#include "aes.h" +#include "cmac.h" +#include "utilities.h" + +#define LSHIFT(v, r) do { \ + int32_t i; \ + for (i = 0; i < 15; i++) \ + (r)[i] = (v)[i] << 1 | (v)[i + 1] >> 7; \ + (r)[15] = (v)[15] << 1; \ + } while (0) + +#define XOR(v, r) do { \ + int32_t i; \ + for (i = 0; i < 16; i++) \ + { \ + (r)[i] = (r)[i] ^ (v)[i]; \ + } \ + } while (0) \ + + +void AES_CMAC_Init(AES_CMAC_CTX *ctx) +{ + memset1(ctx->X, 0, sizeof ctx->X); + ctx->M_n = 0; + memset1(ctx->rijndael.ksch, '\0', 240); +} + +void AES_CMAC_SetKey(AES_CMAC_CTX *ctx, const uint8_t key[AES_CMAC_KEY_LENGTH]) +{ + //rijndael_set_key_enc_only(&ctx->rijndael, key, 128); + aes_set_key( key, AES_CMAC_KEY_LENGTH, &ctx->rijndael); +} + +void AES_CMAC_Update(AES_CMAC_CTX *ctx, const uint8_t *data, uint32_t len) +{ + uint32_t mlen; + uint8_t in[16]; + + if (ctx->M_n > 0) { + mlen = MIN(16 - ctx->M_n, len); + memcpy1(ctx->M_last + ctx->M_n, data, mlen); + ctx->M_n += mlen; + if (ctx->M_n < 16 || len == mlen) + return; + XOR(ctx->M_last, ctx->X); + //rijndael_encrypt(&ctx->rijndael, ctx->X, ctx->X); + aes_encrypt( ctx->X, ctx->X, &ctx->rijndael); + data += mlen; + len -= mlen; + } + while (len > 16) { /* not last block */ + + XOR(data, ctx->X); + //rijndael_encrypt(&ctx->rijndael, ctx->X, ctx->X); + + memcpy1(in, &ctx->X[0], 16); //Bestela ez du ondo iten + aes_encrypt( in, in, &ctx->rijndael); + memcpy1(&ctx->X[0], in, 16); + + data += 16; + len -= 16; + } + /* potential last block, save it */ + memcpy1(ctx->M_last, data, len); + ctx->M_n = len; +} + +void AES_CMAC_Final(uint8_t digest[AES_CMAC_DIGEST_LENGTH], AES_CMAC_CTX *ctx) +{ + uint8_t K[16]; + uint8_t in[16]; + /* generate subkey K1 */ + memset1(K, '\0', 16); + + //rijndael_encrypt(&ctx->rijndael, K, K); + + aes_encrypt( K, K, &ctx->rijndael); + + if (K[0] & 0x80) { + LSHIFT(K, K); + K[15] ^= 0x87; + } else + LSHIFT(K, K); + + + if (ctx->M_n == 16) { + /* last block was a complete block */ + XOR(K, ctx->M_last); + + } else { + /* generate subkey K2 */ + if (K[0] & 0x80) { + LSHIFT(K, K); + K[15] ^= 0x87; + } else + LSHIFT(K, K); + + /* padding(M_last) */ + ctx->M_last[ctx->M_n] = 0x80; + while (++ctx->M_n < 16) + ctx->M_last[ctx->M_n] = 0; + + XOR(K, ctx->M_last); + + + } + XOR(ctx->M_last, ctx->X); + + //rijndael_encrypt(&ctx->rijndael, ctx->X, digest); + + memcpy1(in, &ctx->X[0], 16); //Bestela ez du ondo iten + aes_encrypt(in, digest, &ctx->rijndael); + memset1(K, 0, sizeof K); + +} + diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Crypto/cmac.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Crypto/cmac.h new file mode 100644 index 00000000..af52f5ae --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Crypto/cmac.h @@ -0,0 +1,63 @@ +/************************************************************************** +Copyright (C) 2009 Lander Casado, Philippas Tsigas + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files +(the "Software"), to deal with the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimers. Redistributions in +binary form must reproduce the above copyright notice, this list of +conditions and the following disclaimers in the documentation and/or +other materials provided with the distribution. + +In no event shall the authors or copyright holders be liable for any special, +incidental, indirect or consequential damages of any kind, or any damages +whatsoever resulting from loss of use, data or profits, whether or not +advised of the possibility of damage, and on any theory of liability, +arising out of or in connection with the use or performance of this software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS WITH THE SOFTWARE + +*****************************************************************************/ + +#ifndef _CMAC_H_ +#define _CMAC_H_ + +#include "aes.h" + +#define AES_CMAC_KEY_LENGTH 16 +#define AES_CMAC_DIGEST_LENGTH 16 + +typedef struct _AES_CMAC_CTX { + aes_context rijndael; + uint8_t X[16]; + uint8_t M_last[16]; + uint32_t M_n; + } AES_CMAC_CTX; + +//#include + +//__BEGIN_DECLS +void AES_CMAC_Init(AES_CMAC_CTX * ctx); +void AES_CMAC_SetKey(AES_CMAC_CTX * ctx, const uint8_t key[AES_CMAC_KEY_LENGTH]); +void AES_CMAC_Update(AES_CMAC_CTX * ctx, const uint8_t * data, uint32_t len); + // __attribute__((__bounded__(__string__,2,3))); +void AES_CMAC_Final(uint8_t digest[AES_CMAC_DIGEST_LENGTH], AES_CMAC_CTX * ctx); + // __attribute__((__bounded__(__minbytes__,1,AES_CMAC_DIGEST_LENGTH))); +//__END_DECLS + +#endif /* _CMAC_H_ */ + diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Crypto/soft-se.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Crypto/soft-se.c new file mode 100644 index 00000000..068130b2 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Crypto/soft-se.c @@ -0,0 +1,417 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + ___ _____ _ ___ _ _____ ___ ___ ___ ___ +/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| +\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| +|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| +embedded.connectivity.solutions=============== + +Description: Secure Element software implementation + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ), + Daniel Jaeckle ( STACKFORCE ), Johannes Bruder ( STACKFORCE ) +*/ +#include "secure-element.h" + +#include +#include + +#include "LoRaMacCrypto.h" +#include "utilities.h" +#include "aes.h" +#include "cmac.h" +#include "radio.h" + +#define NUM_OF_KEYS 24 +#define KEY_SIZE 16 + +/*! + * Identifier value pair type for Keys + */ +typedef struct sKey +{ + /* + * Key identifier + */ + KeyIdentifier_t KeyID; + /* + * Key value + */ + uint8_t KeyValue[KEY_SIZE]; +} Key_t; + +/* + * Secure Element Non Volatile Context structure + */ +typedef struct sSecureElementNvCtx +{ + /* + * DevEUI storage + */ + uint8_t DevEui[SE_EUI_SIZE]; + /* + * Join EUI storage + */ + uint8_t JoinEui[SE_EUI_SIZE]; + /* + * AES computation context variable + */ + aes_context AesContext; + /* + * CMAC computation context variable + */ + AES_CMAC_CTX AesCmacCtx[1]; + /* + * Key List + */ + Key_t KeyList[NUM_OF_KEYS]; +}SecureElementNvCtx_t; + +/* + * Module context + */ +static SecureElementNvCtx_t SeNvmCtx; + +static SecureElementNvmEvent SeNvmCtxChanged; + +/* + * Local functions + */ + +/* + * Gets key item from key list. + * + * cmac = aes128_cmac(keyID, B0 | msg) + * + * \param[IN] keyID - Key identifier + * \param[OUT] keyItem - Key item reference + * \retval - Status of the operation + */ +static SecureElementStatus_t GetKeyByID( KeyIdentifier_t keyID, Key_t** keyItem ) +{ + for( uint8_t i = 0; i < NUM_OF_KEYS; i++ ) + { + if( SeNvmCtx.KeyList[i].KeyID == keyID ) + { + *keyItem = &( SeNvmCtx.KeyList[i] ); + return SECURE_ELEMENT_SUCCESS; + } + } + return SECURE_ELEMENT_ERROR_INVALID_KEY_ID; +} + +/* + * Dummy callback in case if the user provides NULL function pointer + */ +static void DummyCB( void ) +{ + return; +} + +/* + * Computes a CMAC of a message using provided initial Bx block + * + * cmac = aes128_cmac(keyID, blocks[i].Buffer) + * + * \param[IN] micBxBuffer - Buffer containing the initial Bx block + * \param[IN] buffer - Data buffer + * \param[IN] size - Data buffer size + * \param[IN] keyID - Key identifier to determine the AES key to be used + * \param[OUT] cmac - Computed cmac + * \retval - Status of the operation + */ +static SecureElementStatus_t ComputeCmac( uint8_t *micBxBuffer, uint8_t *buffer, uint16_t size, KeyIdentifier_t keyID, uint32_t* cmac ) +{ + if( ( buffer == NULL ) || ( cmac == NULL ) ) + { + return SECURE_ELEMENT_ERROR_NPE; + } + + uint8_t Cmac[16]; + + AES_CMAC_Init( SeNvmCtx.AesCmacCtx ); + + Key_t* keyItem; + SecureElementStatus_t retval = GetKeyByID( keyID, &keyItem ); + + if( retval == SECURE_ELEMENT_SUCCESS ) + { + AES_CMAC_SetKey( SeNvmCtx.AesCmacCtx, keyItem->KeyValue ); + + if( micBxBuffer != NULL ) + { + AES_CMAC_Update( SeNvmCtx.AesCmacCtx, micBxBuffer, 16 ); + } + + AES_CMAC_Update( SeNvmCtx.AesCmacCtx, buffer, size ); + + AES_CMAC_Final( Cmac, SeNvmCtx.AesCmacCtx ); + + // Bring into the required format + *cmac = ( uint32_t )( ( uint32_t ) Cmac[3] << 24 | ( uint32_t ) Cmac[2] << 16 | ( uint32_t ) Cmac[1] << 8 | ( uint32_t ) Cmac[0] ); + } + + return retval; +} + +/* + * API functions + */ + +SecureElementStatus_t SecureElementInit( SecureElementNvmEvent seNvmCtxChanged ) +{ + uint8_t itr = 0; + uint8_t zeroKey[16] = { 0 }; + + // Initialize with defaults + SeNvmCtx.KeyList[itr++].KeyID = APP_KEY; + SeNvmCtx.KeyList[itr++].KeyID = GEN_APP_KEY; + SeNvmCtx.KeyList[itr++].KeyID = NWK_KEY; + SeNvmCtx.KeyList[itr++].KeyID = J_S_INT_KEY; + SeNvmCtx.KeyList[itr++].KeyID = J_S_ENC_KEY; + SeNvmCtx.KeyList[itr++].KeyID = F_NWK_S_INT_KEY; + SeNvmCtx.KeyList[itr++].KeyID = S_NWK_S_INT_KEY; + SeNvmCtx.KeyList[itr++].KeyID = NWK_S_ENC_KEY; + SeNvmCtx.KeyList[itr++].KeyID = APP_S_KEY; + SeNvmCtx.KeyList[itr++].KeyID = MC_ROOT_KEY; + SeNvmCtx.KeyList[itr++].KeyID = MC_KE_KEY; + SeNvmCtx.KeyList[itr++].KeyID = MC_KEY_0; + SeNvmCtx.KeyList[itr++].KeyID = MC_APP_S_KEY_0; + SeNvmCtx.KeyList[itr++].KeyID = MC_NWK_S_KEY_0; + SeNvmCtx.KeyList[itr++].KeyID = MC_KEY_1; + SeNvmCtx.KeyList[itr++].KeyID = MC_APP_S_KEY_1; + SeNvmCtx.KeyList[itr++].KeyID = MC_NWK_S_KEY_1; + SeNvmCtx.KeyList[itr++].KeyID = MC_KEY_2; + SeNvmCtx.KeyList[itr++].KeyID = MC_APP_S_KEY_2; + SeNvmCtx.KeyList[itr++].KeyID = MC_NWK_S_KEY_2; + SeNvmCtx.KeyList[itr++].KeyID = MC_KEY_3; + SeNvmCtx.KeyList[itr++].KeyID = MC_APP_S_KEY_3; + SeNvmCtx.KeyList[itr++].KeyID = MC_NWK_S_KEY_3; + SeNvmCtx.KeyList[itr].KeyID = SLOT_RAND_ZERO_KEY; + + // Set standard keys + memcpy1( SeNvmCtx.KeyList[itr].KeyValue, zeroKey, KEY_SIZE ); + + memset1( SeNvmCtx.DevEui, 0, SE_EUI_SIZE ); + memset1( SeNvmCtx.JoinEui, 0, SE_EUI_SIZE ); + + // Assign callback + if( seNvmCtxChanged != 0 ) + { + SeNvmCtxChanged = seNvmCtxChanged; + } + else + { + SeNvmCtxChanged = DummyCB; + } + + return SECURE_ELEMENT_SUCCESS; +} + +SecureElementStatus_t SecureElementRestoreNvmCtx( void* seNvmCtx ) +{ + // Restore nvm context + if( seNvmCtx != 0 ) + { + memcpy1( ( uint8_t* ) &SeNvmCtx, ( uint8_t* ) seNvmCtx, sizeof( SeNvmCtx ) ); + return SECURE_ELEMENT_SUCCESS; + } + else + { + return SECURE_ELEMENT_ERROR_NPE; + } +} + +void* SecureElementGetNvmCtx( size_t* seNvmCtxSize ) +{ + *seNvmCtxSize = sizeof( SeNvmCtx ); + return &SeNvmCtx; +} + +SecureElementStatus_t SecureElementSetKey( KeyIdentifier_t keyID, uint8_t* key ) +{ + if( key == NULL ) + { + return SECURE_ELEMENT_ERROR_NPE; + } + + for( uint8_t i = 0; i < NUM_OF_KEYS; i++ ) + { + if( SeNvmCtx.KeyList[i].KeyID == keyID ) + { + if( ( keyID == MC_KEY_0 ) || ( keyID == MC_KEY_1 ) || ( keyID == MC_KEY_2 ) || ( keyID == MC_KEY_3 ) ) + { // Decrypt the key if its a Mckey + SecureElementStatus_t retval = SECURE_ELEMENT_ERROR; + uint8_t decryptedKey[16] = { 0 }; + + retval = SecureElementAesEncrypt( key, 16, MC_KE_KEY, decryptedKey ); + + memcpy1( SeNvmCtx.KeyList[i].KeyValue, decryptedKey, KEY_SIZE ); + SeNvmCtxChanged( ); + + return retval; + } + else + { + memcpy1( SeNvmCtx.KeyList[i].KeyValue, key, KEY_SIZE ); + SeNvmCtxChanged( ); + return SECURE_ELEMENT_SUCCESS; + } + } + } + + return SECURE_ELEMENT_ERROR_INVALID_KEY_ID; +} + +SecureElementStatus_t SecureElementComputeAesCmac( uint8_t *micBxBuffer, uint8_t *buffer, uint16_t size, KeyIdentifier_t keyID, uint32_t* cmac ) +{ + if( keyID >= LORAMAC_CRYPTO_MULTICAST_KEYS ) + { + //Never accept multicast key identifier for cmac computation + return SECURE_ELEMENT_ERROR_INVALID_KEY_ID; + } + + return ComputeCmac( micBxBuffer, buffer, size, keyID, cmac ); +} + +SecureElementStatus_t SecureElementVerifyAesCmac( uint8_t* buffer, uint16_t size, uint32_t expectedCmac, KeyIdentifier_t keyID ) +{ + if( buffer == NULL ) + { + return SECURE_ELEMENT_ERROR_NPE; + } + + SecureElementStatus_t retval = SECURE_ELEMENT_ERROR; + uint32_t compCmac = 0; + retval = ComputeCmac( NULL, buffer, size, keyID, &compCmac ); + if( retval != SECURE_ELEMENT_SUCCESS ) + { + return retval; + } + + if( expectedCmac != compCmac ) + { + retval = SECURE_ELEMENT_FAIL_CMAC; + } + + return retval; +} + +SecureElementStatus_t SecureElementAesEncrypt( uint8_t* buffer, uint16_t size, KeyIdentifier_t keyID, uint8_t* encBuffer ) +{ + if( buffer == NULL || encBuffer == NULL ) + { + return SECURE_ELEMENT_ERROR_NPE; + } + + // Check if the size is divisible by 16, + if( ( size % 16 ) != 0 ) + { + return SECURE_ELEMENT_ERROR_BUF_SIZE; + } + + memset1( SeNvmCtx.AesContext.ksch, '\0', 240 ); + + Key_t* pItem; + SecureElementStatus_t retval = GetKeyByID( keyID, &pItem ); + + if( retval == SECURE_ELEMENT_SUCCESS ) + { + aes_set_key( pItem->KeyValue, 16, &SeNvmCtx.AesContext ); + + uint8_t block = 0; + + while( size != 0 ) + { + aes_encrypt( &buffer[block], &encBuffer[block], &SeNvmCtx.AesContext ); + block = block + 16; + size = size - 16; + } + } + return retval; +} + +SecureElementStatus_t SecureElementDeriveAndStoreKey( Version_t version, uint8_t* input, KeyIdentifier_t rootKeyID, KeyIdentifier_t targetKeyID ) +{ + if( input == NULL ) + { + return SECURE_ELEMENT_ERROR_NPE; + } + + SecureElementStatus_t retval = SECURE_ELEMENT_ERROR; + uint8_t key[16] = { 0 }; + + // In case of MC_KE_KEY, prevent other keys than NwkKey or AppKey for LoRaWAN 1.1 or later + if( targetKeyID == MC_KE_KEY ) + { + if( ( ( rootKeyID == APP_KEY ) && ( version.Fields.Minor == 0 ) ) || ( rootKeyID == NWK_KEY ) ) + { + return SECURE_ELEMENT_ERROR_INVALID_KEY_ID; + } + } + + // Derive key + retval = SecureElementAesEncrypt( input, 16, rootKeyID, key ); + if( retval != SECURE_ELEMENT_SUCCESS ) + { + return retval; + } + + // Store key + retval = SecureElementSetKey( targetKeyID, key ); + if( retval != SECURE_ELEMENT_SUCCESS ) + { + return retval; + } + + return SECURE_ELEMENT_SUCCESS; +} + +SecureElementStatus_t SecureElementRandomNumber( uint32_t* randomNum ) +{ + if( randomNum == NULL ) + { + return SECURE_ELEMENT_ERROR_NPE; + } + *randomNum = Radio.Random( ); + return SECURE_ELEMENT_SUCCESS; +} + +SecureElementStatus_t SecureElementSetDevEui( uint8_t* devEui ) +{ + if( devEui == NULL ) + { + return SECURE_ELEMENT_ERROR_NPE; + } + memcpy1( SeNvmCtx.DevEui, devEui, SE_EUI_SIZE ); + SeNvmCtxChanged( ); + return SECURE_ELEMENT_SUCCESS; +} + +uint8_t* SecureElementGetDevEui( void ) +{ + return SeNvmCtx.DevEui; +} + +SecureElementStatus_t SecureElementSetJoinEui( uint8_t* joinEui ) +{ + if( joinEui == NULL ) + { + return SECURE_ELEMENT_ERROR_NPE; + } + memcpy1( SeNvmCtx.JoinEui, joinEui, SE_EUI_SIZE ); + SeNvmCtxChanged( ); + return SECURE_ELEMENT_SUCCESS; +} + +uint8_t* SecureElementGetJoinEui( void ) +{ + return SeNvmCtx.JoinEui; +} diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/LICENSE.txt b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/LICENSE.txt new file mode 100644 index 00000000..d6dfb1b8 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/LICENSE.txt @@ -0,0 +1,25 @@ +--- Revised BSD License --- +Copyright (c) 2013, SEMTECH S.A. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Semtech corporation nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL SEMTECH S.A. BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMac.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMac.c new file mode 100644 index 00000000..804e897e --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMac.c @@ -0,0 +1,4754 @@ +/*! + * \file LoRaMac.c + * + * \brief LoRa MAC layer implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + */ +#include "utilities.h" +#include "radio.h" +#include "region/Region.h" +#include "LoRaMacClassB.h" +#include "LoRaMacCrypto.h" +#include "secure-element.h" +#include "LoRaMacTest.h" +#include "LoRaMacTypes.h" +#include "LoRaMacConfirmQueue.h" +#include "LoRaMacHeaderTypes.h" +#include "LoRaMacMessageTypes.h" +#include "LoRaMacParser.h" +#include "LoRaMacCommands.h" +#include "LoRaMacAdr.h" + +#include "LoRaMac.h" +#include "lorawan_conf.h" +#include "mw_log_conf.h" + +/*! + * Maximum PHY layer payload size + */ +#define LORAMAC_PHY_MAXPAYLOAD 255 + +/*! + * Maximum MAC commands buffer size + */ +#define LORA_MAC_COMMAND_MAX_LENGTH 128 + +/*! + * Maximum length of the fOpts field + */ +#define LORA_MAC_COMMAND_MAX_FOPTS_LENGTH 15 + +/*! + * LoRaMac duty cycle for the back-off procedure during the first hour. + */ +#define BACKOFF_DC_1_HOUR 100 + +/*! + * LoRaMac duty cycle for the back-off procedure during the next 10 hours. + */ +#define BACKOFF_DC_10_HOURS 1000 + +/*! + * LoRaMac duty cycle for the back-off procedure during the next 24 hours. + */ +#define BACKOFF_DC_24_HOURS 10000 + +/*! + * LoRaMac internal states + */ +enum eLoRaMacState +{ + LORAMAC_IDLE = 0x00000000, + LORAMAC_STOPPED = 0x00000001, + LORAMAC_TX_RUNNING = 0x00000002, + LORAMAC_RX = 0x00000004, + LORAMAC_ACK_RETRY = 0x00000010, + LORAMAC_TX_DELAYED = 0x00000020, + LORAMAC_TX_CONFIG = 0x00000040, + LORAMAC_RX_ABORT = 0x00000080, +}; + +/* + * Request permission state + */ +typedef enum eLoRaMacRequestHandling +{ + LORAMAC_REQUEST_HANDLING_OFF = 0, + LORAMAC_REQUEST_HANDLING_ON = !LORAMAC_REQUEST_HANDLING_OFF +}LoRaMacRequestHandling_t; + +typedef struct sLoRaMacNvmCtx +{ + /* + * LoRaMac region. + */ + LoRaMacRegion_t Region; + /* + * LoRaMac default parameters + */ + LoRaMacParams_t MacParamsDefaults; + /* + * Network ID ( 3 bytes ) + */ + uint32_t NetID; + /* + * Mote Address + */ + uint32_t DevAddr; + /*! + * Multicast channel list + */ + MulticastCtx_t MulticastChannelList[LORAMAC_MAX_MC_CTX]; + /* + * Actual device class + */ + DeviceClass_t DeviceClass; + /* + * Indicates if the node is connected to + * a private or public network + */ + bool PublicNetwork; + /* + * LoRaMac ADR control status + */ + bool AdrCtrlOn; + /* + * Counts the number of missed ADR acknowledgements + */ + uint32_t AdrAckCounter; + + /* + * LoRaMac parameters + */ + LoRaMacParams_t MacParams; + /* + * Maximum duty cycle + * \remark Possibility to shutdown the device. + */ + uint8_t MaxDCycle; + /* + * Enables/Disables duty cycle management (Test only) + */ + bool DutyCycleOn; + /* + * Current channel index + */ + uint8_t LastTxChannel; + /* + * Holds the current rx window slot + */ + bool RepeaterSupport; + /* + * Buffer containing the MAC layer commands + */ + uint8_t MacCommandsBuffer[LORA_MAC_COMMAND_MAX_LENGTH]; + /* + * If the server has sent a FRAME_TYPE_DATA_CONFIRMED_DOWN this variable indicates + * if the ACK bit must be set for the next transmission + */ + bool SrvAckRequested; + /* + * Aggregated duty cycle management + */ + uint16_t AggregatedDCycle; + /* + * Aggregated duty cycle management + */ + TimerTime_t LastTxDoneTime; + TimerTime_t AggregatedTimeOff; + /* + * Stores the time at LoRaMac initialization. + * + * \remark Used for the BACKOFF_DC computation. + */ + TimerTime_t InitializationTime; + /* + * Current LoRaWAN Version + */ + Version_t Version; + /* + * End-Device network activation + */ + ActivationType_t NetworkActivation; + /*! + * Last received Message integrity Code (MIC) + */ + uint32_t LastRxMic; +}LoRaMacNvmCtx_t; + +typedef struct sLoRaMacCtx +{ + /* + * Length of packet in PktBuffer + */ + uint16_t PktBufferLen; + /* + * Buffer containing the data to be sent or received. + */ + uint8_t PktBuffer[LORAMAC_PHY_MAXPAYLOAD]; + /*! + * Current processed transmit message + */ + LoRaMacMessage_t TxMsg; + /*! + * Buffer containing the data received by the application. + */ + uint8_t AppData[LORAMAC_PHY_MAXPAYLOAD]; + /* + * Size of buffer containing the application data. + */ + uint8_t AppDataSize; + /* + * Buffer containing the upper layer data. + */ + uint8_t RxPayload[LORAMAC_PHY_MAXPAYLOAD]; + SysTime_t LastTxSysTime; + /* + * LoRaMac internal state + */ + uint32_t MacState; + /* + * LoRaMac upper layer event functions + */ + LoRaMacPrimitives_t* MacPrimitives; + /* + * LoRaMac upper layer callback functions + */ + LoRaMacCallback_t* MacCallbacks; + /* + * Radio events function pointer + */ + RadioEvents_t RadioEvents; + /* + * LoRaMac duty cycle delayed Tx timer + */ + TimerEvent_t TxDelayedTimer; + /* + * LoRaMac reception windows timers + */ + TimerEvent_t RxWindowTimer1; + TimerEvent_t RxWindowTimer2; + /* + * LoRaMac reception windows delay + * \remark normal frame: RxWindowXDelay = ReceiveDelayX - RADIO_WAKEUP_TIME + * join frame : RxWindowXDelay = JoinAcceptDelayX - RADIO_WAKEUP_TIME + */ + uint32_t RxWindow1Delay; + uint32_t RxWindow2Delay; + /* + * LoRaMac Rx windows configuration + */ + RxConfigParams_t RxWindow1Config; + RxConfigParams_t RxWindow2Config; + RxConfigParams_t RxWindowCConfig; + /* + * Limit of uplinks without any donwlink response before the ADRACKReq bit will be set. + */ + uint16_t AdrAckLimit; + /* + * Limit of uplinks without any donwlink response after a the first frame with set ADRACKReq bit + * before the trying to regain the connectivity. + */ + uint16_t AdrAckDelay; + /* + * Acknowledge timeout timer. Used for packet retransmissions. + */ + TimerEvent_t AckTimeoutTimer; + /* + * Uplink messages repetitions counter + */ + uint8_t ChannelsNbTransCounter; + /* + * Number of trials to get a frame acknowledged + */ + uint8_t AckTimeoutRetries; + /* + * Number of trials to get a frame acknowledged + */ + uint8_t AckTimeoutRetriesCounter; + /* + * Indicates if the AckTimeout timer has expired or not + */ + bool AckTimeoutRetry; + /* + * If the node has sent a FRAME_TYPE_DATA_CONFIRMED_UP this variable indicates + * if the nodes needs to manage the server acknowledgement. + */ + bool NodeAckRequested; + /* + * Current channel index + */ + uint8_t Channel; + /* + * Last transmission time on air + */ + TimerTime_t TxTimeOnAir; + /* + * Structure to hold an MCPS indication data. + */ + McpsIndication_t McpsIndication; + /* + * Structure to hold MCPS confirm data. + */ + McpsConfirm_t McpsConfirm; + /* + * Structure to hold MLME confirm data. + */ + MlmeConfirm_t MlmeConfirm; + /* + * Structure to hold MLME indication data. + */ + MlmeIndication_t MlmeIndication; + /* + * Holds the current rx window slot + */ + LoRaMacRxSlot_t RxSlot; + /* + * LoRaMac tx/rx operation state + */ + LoRaMacFlags_t MacFlags; + /* + * Data structure indicating if a request is allowed or not. + */ + LoRaMacRequestHandling_t AllowRequests; + /* + * Non-volatile module context structure + */ + LoRaMacNvmCtx_t* NvmCtx; +}LoRaMacCtx_t; + +/* + * Module context. + */ +static LoRaMacCtx_t MacCtx; + +/* + * Non-volatile module context. + */ +static LoRaMacNvmCtx_t NvmMacCtx; + + + +/* + * List of module contexts. + */ +LoRaMacCtxs_t Contexts; + +/*! + * Defines the LoRaMac radio events status + */ +typedef union uLoRaMacRadioEvents +{ + uint32_t Value; + struct sEvents + { + uint32_t RxTimeout : 1; + uint32_t RxError : 1; + uint32_t TxTimeout : 1; + uint32_t RxDone : 1; + uint32_t TxDone : 1; + }Events; +}LoRaMacRadioEvents_t; + +/*! + * LoRaMac radio events status + */ +LoRaMacRadioEvents_t LoRaMacRadioEvents = { .Value = 0 }; + +/*! + * \brief Function to be executed on Radio Tx Done event + */ +static void OnRadioTxDone( void ); + +/*! + * \brief This function prepares the MAC to abort the execution of function + * OnRadioRxDone in case of a reception error. + */ +static void PrepareRxDoneAbort( void ); + +/*! + * \brief Function to be executed on Radio Rx Done event + */ +static void OnRadioRxDone( uint8_t* payload, uint16_t size, int16_t rssi, int8_t snr ); + +/*! + * \brief Function executed on Radio Tx Timeout event + */ +static void OnRadioTxTimeout( void ); + +/*! + * \brief Function executed on Radio Rx error event + */ +static void OnRadioRxError( void ); + +/*! + * \brief Function executed on Radio Rx Timeout event + */ +static void OnRadioRxTimeout( void ); + +/*! + * \brief Function executed on duty cycle delayed Tx timer event + */ +static void OnTxDelayedTimerEvent( void* context ); + +/*! + * \brief Function executed on first Rx window timer event + */ +static void OnRxWindow1TimerEvent( void* context ); + +/*! + * \brief Function executed on second Rx window timer event + */ +static void OnRxWindow2TimerEvent( void* context ); + +/*! + * \brief Function executed on AckTimeout timer event + */ +static void OnAckTimeoutTimerEvent( void* context ); + +/*! + * \brief Configures the events to trigger an MLME-Indication with + * a MLME type of MLME_SCHEDULE_UPLINK. + */ +static void SetMlmeScheduleUplinkIndication( void ); + +/*! + * Computes next 32 bit downlink counter value and determines the frame counter ID. + * + * \param[IN] addrID - Address identifier + * \param[IN] fType - Frame type + * \param[IN] macMsg - Data message object, holding the current 16 bit transmitted frame counter + * \param[IN] lrWanVersion - LoRaWAN version + * \param[IN] maxFCntGap - Maximum allowed frame counter difference (only for 1.0.X necessary) + * \param[OUT] fCntID - Frame counter identifier + * \param[OUT] currentDown - Current downlink counter value + * + * \retval - Status of the operation + */ +static LoRaMacCryptoStatus_t GetFCntDown( AddressIdentifier_t addrID, FType_t fType, LoRaMacMessageData_t* macMsg, Version_t lrWanVersion, + uint16_t maxFCntGap, FCntIdentifier_t* fCntID, uint32_t* currentDown ); + +/*! + * \brief Switches the device class + * + * \param [IN] deviceClass Device class to switch to + */ +static LoRaMacStatus_t SwitchClass( DeviceClass_t deviceClass ); + +/*! + * \brief Gets the maximum application payload length in the absence of the optional FOpt field. + * + * \param [IN] datarate Current datarate + * + * \retval Max length + */ +static uint8_t GetMaxAppPayloadWithoutFOptsLength( int8_t datarate ); + +/*! + * \brief Validates if the payload fits into the frame, taking the datarate + * into account. + * + * \details Refer to chapter 4.3.2 of the LoRaWAN specification, v1.0 + * + * \param lenN Length of the application payload. The length depends on the + * datarate and is region specific + * + * \param datarate Current datarate + * + * \param fOptsLen Length of the fOpts field + * + * \retval [false: payload does not fit into the frame, true: payload fits into + * the frame] + */ +static bool ValidatePayloadLength( uint8_t lenN, int8_t datarate, uint8_t fOptsLen ); + +/*! + * \brief Decodes MAC commands in the fOpts field and in the payload + * + * \param [IN] payload A pointer to the payload + * \param [IN] macIndex The index of the payload where the MAC commands start + * \param [IN] commandsSize The size of the MAC commands + * \param [IN] snr The SNR value of the frame + * \param [IN] rxSlot The RX slot where the frame was received + */ +static void ProcessMacCommands( uint8_t* payload, uint8_t macIndex, uint8_t commandsSize, int8_t snr, LoRaMacRxSlot_t rxSlot ); + +/*! + * \brief LoRaMAC layer generic send frame + * + * \param [IN] macHdr MAC header field + * \param [IN] fPort MAC payload port + * \param [IN] fBuffer MAC data buffer to be sent + * \param [IN] fBufferSize MAC data buffer size + * \retval status Status of the operation. + */ +LoRaMacStatus_t Send( LoRaMacHeader_t* macHdr, uint8_t fPort, void* fBuffer, uint16_t fBufferSize ); + +/*! + * \brief LoRaMAC layer send join/rejoin request + * + * \param [IN] joinReqType Type of join-request or rejoin + * + * \retval status Status of the operation. + */ +LoRaMacStatus_t SendReJoinReq( JoinReqIdentifier_t joinReqType ); + +/*! + * \brief LoRaMAC layer frame buffer initialization + * + * \param [IN] macHdr MAC header field + * \param [IN] fCtrl MAC frame control field + * \param [IN] fOpts MAC commands buffer + * \param [IN] fPort MAC payload port + * \param [IN] fBuffer MAC data buffer to be sent + * \param [IN] fBufferSize MAC data buffer size + * \retval status Status of the operation. + */ +LoRaMacStatus_t PrepareFrame( LoRaMacHeader_t* macHdr, LoRaMacFrameCtrl_t* fCtrl, uint8_t fPort, void* fBuffer, uint16_t fBufferSize ); + +/* + * \brief Schedules the frame according to the duty cycle + * + * \param [IN] allowDelayedTx When set to true, the a frame will be delayed, + * the duty cycle restriction is active + * \retval Status of the operation + */ +static LoRaMacStatus_t ScheduleTx( bool allowDelayedTx ); + +/* + * \brief Secures the current processed frame ( TxMsg ) + * \param[IN] txDr Data rate used for the transmission + * \param[IN] txCh Index of the channel used for the transmission + * \retval status Status of the operation + */ +static LoRaMacStatus_t SecureFrame( uint8_t txDr, uint8_t txCh ); + +/* + * \brief Calculates the back-off time for the band of a channel. + * + * \param [IN] channel The last Tx channel index + */ +static void CalculateBackOff( uint8_t channel ); + +/* + * \brief Function to remove pending MAC commands + * + * \param [IN] rxSlot The RX slot on which the frame was received + * \param [IN] fCtrl The frame control field of the received frame + * \param [IN] request The request type + */ +static void RemoveMacCommands( LoRaMacRxSlot_t rxSlot, LoRaMacFrameCtrl_t fCtrl, Mcps_t request ); + +/*! + * \brief LoRaMAC layer prepared frame buffer transmission with channel specification + * + * \remark PrepareFrame must be called at least once before calling this + * function. + * + * \param [IN] channel Channel to transmit on + * \retval status Status of the operation. + */ +LoRaMacStatus_t SendFrameOnChannel( uint8_t channel ); + +/*! + * \brief Sets the radio in continuous transmission mode + * + * \remark Uses the radio parameters set on the previous transmission. + * + * \param [IN] timeout Time in seconds while the radio is kept in continuous wave mode + * \retval status Status of the operation. + */ +LoRaMacStatus_t SetTxContinuousWave( uint16_t timeout ); + +/*! + * \brief Sets the radio in continuous transmission mode + * + * \remark Uses the radio parameters set on the previous transmission. + * + * \param [IN] timeout Time in seconds while the radio is kept in continuous wave mode + * \param [IN] frequency RF frequency to be set. + * \param [IN] power RF output power to be set. + * \retval status Status of the operation. + */ +LoRaMacStatus_t SetTxContinuousWave1( uint16_t timeout, uint32_t frequency, uint8_t power ); + +/*! + * \brief Resets MAC specific parameters to default + */ +static void ResetMacParameters( void ); + +/*! + * \brief Initializes and opens the reception window + * + * \param [IN] rxTimer Window timer to be topped. + * \param [IN] rxConfig Window parameters to be setup + */ +static void RxWindowSetup( TimerEvent_t* rxTimer, RxConfigParams_t* rxConfig ); + +/*! + * \brief Opens up a continuous RX C window. This is used for + * class c devices. + */ +static void OpenContinuousRxCWindow( void ); + +/*! + * \brief Returns a pointer to the internal contexts structure. + * + * \retval void Points to a structure containing all contexts + */ +LoRaMacCtxs_t* GetCtxs( void ); + +/*! + * \brief Restoring of internal module contexts + * + * \details This function allows to restore module contexts by a given pointer. + * + * + * \retval LoRaMacStatus_t Status of the operation. Possible returns are: + * returns are: + * \ref LORAMAC_STATUS_OK, + * \ref LORAMAC_STATUS_PARAMETER_INVALID, + */ +LoRaMacStatus_t RestoreCtxs( LoRaMacCtxs_t* contexts ); + +/*! + * \brief Determines the frame type + * + * \param [IN] macMsg Data message object + * + * \param [OUT] fType Frame type + * + * \retval LoRaMacStatus_t Status of the operation. Possible returns are: + * returns are: + * \ref LORAMAC_STATUS_OK, + * \ref LORAMAC_STATUS_PARAMETER_INVALID, + */ +LoRaMacStatus_t DetermineFrameType( LoRaMacMessageData_t* macMsg, FType_t* fType ); + +/*! + * \brief Checks if the retransmission should be stopped in case of a unconfirmed uplink + * + * \retval Returns true if it should be stopped. + */ +static bool CheckRetransUnconfirmedUplink( void ); + +/*! + * \brief Checks if the retransmission should be stopped in case of a confirmed uplink + * + * \retval Returns true it should be stopped. + */ +static bool CheckRetransConfirmedUplink( void ); + +/*! + * \brief Stops the uplink retransmission + * + * \retval Returns true if successful. + */ +static bool StopRetransmission( void ); + +/*! + * \brief Handles the ACK retries algorithm. + * Increments the re-tries counter up until the specified number of + * trials or the allowed maximum. Decrease the uplink datarate every 2 + * trials. + */ +static void AckTimeoutRetriesProcess( void ); + +/*! + * \brief Finalizes the ACK retries algorithm. + * If no ACK is received restores the default channels + */ +static void AckTimeoutRetriesFinalize( void ); + +/*! + * \brief Calls the callback to indicate that a context changed + */ +static void CallNvmCtxCallback( LoRaMacNvmCtxModule_t module ); + +/*! + * \brief MAC NVM Context has been changed + */ +static void EventMacNvmCtxChanged( void ); + +/*! + * \brief Region NVM Context has been changed + */ +static void EventRegionNvmCtxChanged( void ); + +/*! + * \brief Crypto NVM Context has been changed + */ +static void EventCryptoNvmCtxChanged( void ); + +/*! + * \brief Secure Element NVM Context has been changed + */ +static void EventSecureElementNvmCtxChanged( void ); + +/*! + * \brief MAC commands module nvm context has been changed + */ +static void EventCommandsNvmCtxChanged( void ); + +/*! + * \brief Class B module nvm context has been changed + */ +static void EventClassBNvmCtxChanged( void ); + +/*! + * \brief Confirm Queue module nvm context has been changed + */ +static void EventConfirmQueueNvmCtxChanged( void ); + +/*! + * \brief Verifies if a request is pending currently + * + *\retval 1: Request pending, 0: request not pending + */ +static uint8_t IsRequestPending( void ); + +/*! + * \brief Enabled the possibility to perform requests + * + * \param [IN] requestState Request permission state + */ +static void LoRaMacEnableRequests( LoRaMacRequestHandling_t requestState ); + +/*! + * \brief This function verifies if a RX abort occurred + */ +static void LoRaMacCheckForRxAbort( void ); + +/*! + * \brief This function verifies if a beacon acquisition MLME + * request was pending + * + * \retval 1: Request pending, 0: no request pending + */ +static uint8_t LoRaMacCheckForBeaconAcquisition( void ); + +/*! + * \brief This function handles join request + */ +static void LoRaMacHandleMlmeRequest( void ); + +/*! + * \brief This function handles mcps request + */ +static void LoRaMacHandleMcpsRequest( void ); + +/*! + * \brief This function handles callback events for requests + */ +static void LoRaMacHandleRequestEvents( void ); + +/*! + * \brief This function handles callback events for indications + */ +static void LoRaMacHandleIndicationEvents( void ); + +/*! + * Structure used to store the radio Tx event data + */ +struct +{ + TimerTime_t CurTime; +}TxDoneParams; + +/*! + * Structure used to store the radio Rx event data + */ +struct +{ + TimerTime_t LastRxDone; + uint8_t *Payload; + uint16_t Size; + int16_t Rssi; + int8_t Snr; +}RxDoneParams; + +static void OnRadioTxDone( void ) +{ + TxDoneParams.CurTime = TimerGetCurrentTime( ); + MacCtx.LastTxSysTime = SysTimeGet( ); + + LoRaMacRadioEvents.Events.TxDone = 1; + + if( ( MacCtx.MacCallbacks != NULL ) && ( MacCtx.MacCallbacks->MacProcessNotify != NULL ) ) + { + MacCtx.MacCallbacks->MacProcessNotify( ); + } +#if !defined(NO_MAC_PRINTF) + MW_LOG( "MAC txDone\n\r" ); +#endif +} + +static void OnRadioRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ) +{ + RxDoneParams.LastRxDone = TimerGetCurrentTime( ); + RxDoneParams.Payload = payload; + RxDoneParams.Size = size; + RxDoneParams.Rssi = rssi; + RxDoneParams.Snr = snr; + + LoRaMacRadioEvents.Events.RxDone = 1; + + if( ( MacCtx.MacCallbacks != NULL ) && ( MacCtx.MacCallbacks->MacProcessNotify != NULL ) ) + { + MacCtx.MacCallbacks->MacProcessNotify( ); + } +#if !defined(NO_MAC_PRINTF) + MW_LOG( "MAC rxDone\n\r" ); +#endif +} + +static void OnRadioTxTimeout( void ) +{ + LoRaMacRadioEvents.Events.TxTimeout = 1; + + if( ( MacCtx.MacCallbacks != NULL ) && ( MacCtx.MacCallbacks->MacProcessNotify != NULL ) ) + { + MacCtx.MacCallbacks->MacProcessNotify( ); + } +#if !defined(NO_MAC_PRINTF) + MW_LOG( "MAC txTimeOut\n\r" ); +#endif +} + +static void OnRadioRxError( void ) +{ + LoRaMacRadioEvents.Events.RxError = 1; + + if( ( MacCtx.MacCallbacks != NULL ) && ( MacCtx.MacCallbacks->MacProcessNotify != NULL ) ) + { + MacCtx.MacCallbacks->MacProcessNotify( ); + } +} + +static void OnRadioRxTimeout( void ) +{ + LoRaMacRadioEvents.Events.RxTimeout = 1; + + if( ( MacCtx.MacCallbacks != NULL ) && ( MacCtx.MacCallbacks->MacProcessNotify != NULL ) ) + { + MacCtx.MacCallbacks->MacProcessNotify( ); + } +#if !defined(NO_MAC_PRINTF) + MW_LOG( "MAC rxTimeOut\n\r" ); +#endif +} + +static void UpdateRxSlotIdleState( void ) +{ + if( MacCtx.NvmCtx->DeviceClass != CLASS_C ) + { + MacCtx.RxSlot = RX_SLOT_NONE; + } + else + { + MacCtx.RxSlot = RX_SLOT_WIN_CLASS_C; + } +} + +static void ProcessRadioTxDone( void ) +{ + GetPhyParams_t getPhy; + PhyParam_t phyParam; + SetBandTxDoneParams_t txDone; + + if( MacCtx.NvmCtx->DeviceClass != CLASS_C ) + { + Radio.Sleep( ); + } + // Setup timers + TimerSetValue( &MacCtx.RxWindowTimer1, MacCtx.RxWindow1Delay ); + TimerStart( &MacCtx.RxWindowTimer1 ); + TimerSetValue( &MacCtx.RxWindowTimer2, MacCtx.RxWindow2Delay ); + TimerStart( &MacCtx.RxWindowTimer2 ); + + if( ( MacCtx.NvmCtx->DeviceClass == CLASS_C ) || ( MacCtx.NodeAckRequested == true ) ) + { + getPhy.Attribute = PHY_ACK_TIMEOUT; + phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy ); + TimerSetValue( &MacCtx.AckTimeoutTimer, MacCtx.RxWindow2Delay + phyParam.Value ); + TimerStart( &MacCtx.AckTimeoutTimer ); + } + + // Store last Tx channel + MacCtx.NvmCtx->LastTxChannel = MacCtx.Channel; + // Update last tx done time for the current channel + txDone.Channel = MacCtx.Channel; + if( MacCtx.NvmCtx->NetworkActivation == ACTIVATION_TYPE_NONE ) + { + txDone.Joined = false; + } + else + { + txDone.Joined = true; + } + txDone.LastTxDoneTime = TxDoneParams.CurTime; + RegionSetBandTxDone( MacCtx.NvmCtx->Region, &txDone ); + // Update Aggregated last tx done time + MacCtx.NvmCtx->LastTxDoneTime = TxDoneParams.CurTime; + + if( MacCtx.NodeAckRequested == false ) + { + MacCtx.McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_OK; + } +} + +static void PrepareRxDoneAbort( void ) +{ + MacCtx.MacState |= LORAMAC_RX_ABORT; + + if( MacCtx.NodeAckRequested == true ) + { + OnAckTimeoutTimerEvent( NULL ); + } + + MacCtx.MacFlags.Bits.McpsInd = 1; + MacCtx.MacFlags.Bits.MacDone = 1; + + UpdateRxSlotIdleState( ); +} + +#if (ACTIVE_REGION == LORAMAC_REGION_CN470ALI) + static uint8_t adr_on; +#endif +static void ProcessRadioRxDone( void ) +{ + LoRaMacHeader_t macHdr; + ApplyCFListParams_t applyCFList; + GetPhyParams_t getPhy; + PhyParam_t phyParam; + LoRaMacCryptoStatus_t macCryptoStatus = LORAMAC_CRYPTO_ERROR; + + LoRaMacMessageData_t macMsgData; + LoRaMacMessageJoinAccept_t macMsgJoinAccept; + uint8_t *payload = RxDoneParams.Payload; + uint16_t size = RxDoneParams.Size; + int16_t rssi = RxDoneParams.Rssi; + int8_t snr = RxDoneParams.Snr; + + uint8_t pktHeaderLen = 0; + + uint32_t downLinkCounter = 0; + uint32_t address = MacCtx.NvmCtx->DevAddr; + uint8_t multicast = 0; + AddressIdentifier_t addrID = UNICAST_DEV_ADDR; + FCntIdentifier_t fCntID; + + MacCtx.McpsConfirm.AckReceived = false; + MacCtx.McpsIndication.Rssi = rssi; + MacCtx.McpsIndication.Snr = snr; + MacCtx.McpsIndication.RxSlot = MacCtx.RxSlot; + MacCtx.McpsIndication.Port = 0; + MacCtx.McpsIndication.Multicast = 0; + MacCtx.McpsIndication.FramePending = 0; + MacCtx.McpsIndication.Buffer = NULL; + MacCtx.McpsIndication.BufferSize = 0; + MacCtx.McpsIndication.RxData = false; + MacCtx.McpsIndication.AckReceived = false; + MacCtx.McpsIndication.DownLinkCounter = 0; + MacCtx.McpsIndication.McpsIndication = MCPS_UNCONFIRMED; + MacCtx.McpsIndication.DevAddress = 0; + MacCtx.McpsIndication.DeviceTimeAnsReceived = false; + + Radio.Sleep( ); + TimerStop( &MacCtx.RxWindowTimer2 ); + + // This function must be called even if we are not in class b mode yet. + if( LoRaMacClassBRxBeacon( payload, size ) == true ) + { + MacCtx.MlmeIndication.BeaconInfo.Rssi = rssi; + MacCtx.MlmeIndication.BeaconInfo.Snr = snr; + return; + } + // Check if we expect a ping or a multicast slot. + if( MacCtx.NvmCtx->DeviceClass == CLASS_B ) + { + if( LoRaMacClassBIsPingExpected( ) == true ) + { + LoRaMacClassBSetPingSlotState( PINGSLOT_STATE_CALC_PING_OFFSET ); + LoRaMacClassBPingSlotTimerEvent( NULL ); + MacCtx.McpsIndication.RxSlot = RX_SLOT_WIN_CLASS_B_PING_SLOT; + } + else if( LoRaMacClassBIsMulticastExpected( ) == true ) + { + LoRaMacClassBSetMulticastSlotState( PINGSLOT_STATE_CALC_PING_OFFSET ); + LoRaMacClassBMulticastSlotTimerEvent( NULL ); + MacCtx.McpsIndication.RxSlot = RX_SLOT_WIN_CLASS_B_MULTICAST_SLOT; + } + } + + macHdr.Value = payload[pktHeaderLen++]; + + switch( macHdr.Bits.MType ) + { + case FRAME_TYPE_JOIN_ACCEPT: + macMsgJoinAccept.Buffer = payload; + macMsgJoinAccept.BufSize = size; + + // Abort in case if the device isn't joined yet and no rejoin request is ongoing. + if( MacCtx.NvmCtx->NetworkActivation != ACTIVATION_TYPE_NONE ) + { + MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; + PrepareRxDoneAbort( ); + return; + } + macCryptoStatus = LoRaMacCryptoHandleJoinAccept( JOIN_REQ, SecureElementGetJoinEui( ), &macMsgJoinAccept ); + + if( LORAMAC_CRYPTO_SUCCESS == macCryptoStatus ) + { + // Network ID + MacCtx.NvmCtx->NetID = ( uint32_t ) macMsgJoinAccept.NetID[0]; + MacCtx.NvmCtx->NetID |= ( ( uint32_t ) macMsgJoinAccept.NetID[1] << 8 ); + MacCtx.NvmCtx->NetID |= ( ( uint32_t ) macMsgJoinAccept.NetID[2] << 16 ); + + // Device Address + MacCtx.NvmCtx->DevAddr = macMsgJoinAccept.DevAddr; + + // DLSettings + MacCtx.NvmCtx->MacParams.Rx1DrOffset = macMsgJoinAccept.DLSettings.Bits.RX1DRoffset; + MacCtx.NvmCtx->MacParams.Rx2Channel.Datarate = macMsgJoinAccept.DLSettings.Bits.RX2DataRate; + MacCtx.NvmCtx->MacParams.RxCChannel.Datarate = macMsgJoinAccept.DLSettings.Bits.RX2DataRate; + + // RxDelay + MacCtx.NvmCtx->MacParams.ReceiveDelay1 = macMsgJoinAccept.RxDelay; + if( MacCtx.NvmCtx->MacParams.ReceiveDelay1 == 0 ) + { + MacCtx.NvmCtx->MacParams.ReceiveDelay1 = 1; + } + MacCtx.NvmCtx->MacParams.ReceiveDelay1 *= 1000; + MacCtx.NvmCtx->MacParams.ReceiveDelay2 = MacCtx.NvmCtx->MacParams.ReceiveDelay1 + 1000; + + MacCtx.NvmCtx->Version.Fields.Minor = 0; + + // Apply CF list + applyCFList.Payload = macMsgJoinAccept.CFList; + // Size of the regular payload is 12. Plus 1 byte MHDR and 4 bytes MIC + applyCFList.Size = size - 17; + + RegionApplyCFList( MacCtx.NvmCtx->Region, &applyCFList ); + + MacCtx.NvmCtx->NetworkActivation = ACTIVATION_TYPE_OTAA; + + // MLME handling + if( LoRaMacConfirmQueueIsCmdActive( MLME_JOIN ) == true ) + { + LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_OK, MLME_JOIN ); + } + } + else + { + // MLME handling + if( LoRaMacConfirmQueueIsCmdActive( MLME_JOIN ) == true ) + { + LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL, MLME_JOIN ); + } + } + break; + case FRAME_TYPE_DATA_CONFIRMED_DOWN: + MacCtx.McpsIndication.McpsIndication = MCPS_CONFIRMED; + // Intentional fall through + case FRAME_TYPE_DATA_UNCONFIRMED_DOWN: + // Check if the received payload size is valid + getPhy.UplinkDwellTime = MacCtx.NvmCtx->MacParams.DownlinkDwellTime; + getPhy.Datarate = MacCtx.McpsIndication.RxDatarate; + getPhy.Attribute = PHY_MAX_PAYLOAD; + + // Get the maximum payload length + if( MacCtx.NvmCtx->RepeaterSupport == true ) + { + getPhy.Attribute = PHY_MAX_PAYLOAD_REPEATER; + } + phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy ); + if( MAX( 0, ( int16_t )( ( int16_t ) size - ( int16_t ) LORA_MAC_FRMPAYLOAD_OVERHEAD ) ) > ( int16_t )phyParam.Value ) + { + MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; + PrepareRxDoneAbort( ); + return; + } + macMsgData.Buffer = payload; + macMsgData.BufSize = size; + macMsgData.FRMPayload = MacCtx.RxPayload; + macMsgData.FRMPayloadSize = LORAMAC_PHY_MAXPAYLOAD; + + if( LORAMAC_PARSER_SUCCESS != LoRaMacParserData( &macMsgData ) ) + { + MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; + PrepareRxDoneAbort( ); + return; + } + + // Store device address + MacCtx.McpsIndication.DevAddress = macMsgData.FHDR.DevAddr; + + FType_t fType; + if( LORAMAC_STATUS_OK != DetermineFrameType( &macMsgData, &fType ) ) + { + MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; + PrepareRxDoneAbort( ); + return; + } + + //Check if it is a multicast message + multicast = 0; + downLinkCounter = 0; + for( uint8_t i = 0; i < LORAMAC_MAX_MC_CTX; i++ ) + { + if( ( MacCtx.NvmCtx->MulticastChannelList[i].ChannelParams.Address == macMsgData.FHDR.DevAddr ) && + ( MacCtx.NvmCtx->MulticastChannelList[i].ChannelParams.IsEnabled == true ) ) + { + multicast = 1; + addrID = MacCtx.NvmCtx->MulticastChannelList[i].ChannelParams.GroupID; + downLinkCounter = *( MacCtx.NvmCtx->MulticastChannelList[i].DownLinkCounter ); + address = MacCtx.NvmCtx->MulticastChannelList[i].ChannelParams.Address; + if( MacCtx.NvmCtx->DeviceClass == CLASS_C ) + { + MacCtx.McpsIndication.RxSlot = RX_SLOT_WIN_CLASS_C_MULTICAST; + } + break; + } + } + + // Get maximum allowed counter difference + getPhy.Attribute = PHY_MAX_FCNT_GAP; + phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy ); + + // Get downlink frame counter value + macCryptoStatus = GetFCntDown( addrID, fType, &macMsgData, MacCtx.NvmCtx->Version, phyParam.Value, &fCntID, &downLinkCounter ); + if( macCryptoStatus != LORAMAC_CRYPTO_SUCCESS ) + { + if( macCryptoStatus == LORAMAC_CRYPTO_FAIL_FCNT_DUPLICATED ) + { + // Catch the case of repeated downlink frame counter + MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED; + if( ( MacCtx.NvmCtx->Version.Fields.Minor == 0 ) && ( macHdr.Bits.MType == FRAME_TYPE_DATA_CONFIRMED_DOWN ) && ( MacCtx.NvmCtx->LastRxMic == macMsgData.MIC ) ) + { + MacCtx.NvmCtx->SrvAckRequested = true; + } + } + else if( macCryptoStatus == LORAMAC_CRYPTO_FAIL_MAX_GAP_FCNT ) + { + // Lost too many frames + MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_DOWNLINK_TOO_MANY_FRAMES_LOSS; + } + else + { + // Other errors + MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; + } + MacCtx.McpsIndication.DownLinkCounter = downLinkCounter; + PrepareRxDoneAbort( ); + return; + } + + macCryptoStatus = LoRaMacCryptoUnsecureMessage( addrID, address, fCntID, downLinkCounter, &macMsgData ); + if( macCryptoStatus != LORAMAC_CRYPTO_SUCCESS ) + { + if( macCryptoStatus == LORAMAC_CRYPTO_FAIL_ADDRESS ) + { + // We are not the destination of this frame. + MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ADDRESS_FAIL; + } + else + { + // MIC calculation fail + MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_MIC_FAIL; + } + PrepareRxDoneAbort( ); + return; + } + + /*===========================================================*/ + #if (ACTIVE_REGION == LORAMAC_REGION_CN470ALI) + MacCtx.NvmCtx->AdrCtrlOn = macMsgData.FHDR.FCtrl.Bits.Adr; + #endif + + // Frame is valid + MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_OK; + MacCtx.McpsIndication.Multicast = multicast; + MacCtx.McpsIndication.FramePending = macMsgData.FHDR.FCtrl.Bits.FPending; + MacCtx.McpsIndication.Buffer = NULL; + MacCtx.McpsIndication.BufferSize = 0; + MacCtx.McpsIndication.DownLinkCounter = downLinkCounter; + MacCtx.McpsIndication.AckReceived = macMsgData.FHDR.FCtrl.Bits.Ack; + + MacCtx.McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_OK; + MacCtx.McpsConfirm.AckReceived = macMsgData.FHDR.FCtrl.Bits.Ack; + + // Reset ADR ACK Counter only, when RX1 or RX2 slot + if( ( MacCtx.McpsIndication.RxSlot == RX_SLOT_WIN_1 ) || + ( MacCtx.McpsIndication.RxSlot == RX_SLOT_WIN_2 ) ) + { + MacCtx.NvmCtx->AdrAckCounter = 0; + } + + // MCPS Indication and ack requested handling + if( multicast == 1 ) + { + MacCtx.McpsIndication.McpsIndication = MCPS_MULTICAST; + } + else + { + if( macHdr.Bits.MType == FRAME_TYPE_DATA_CONFIRMED_DOWN ) + { + MacCtx.NvmCtx->SrvAckRequested = true; + if( MacCtx.NvmCtx->Version.Fields.Minor == 0 ) + { + MacCtx.NvmCtx->LastRxMic = macMsgData.MIC; + } + MacCtx.McpsIndication.McpsIndication = MCPS_CONFIRMED; + } + else + { + MacCtx.NvmCtx->SrvAckRequested = false; + MacCtx.McpsIndication.McpsIndication = MCPS_UNCONFIRMED; + } + } + + RemoveMacCommands( MacCtx.McpsIndication.RxSlot, macMsgData.FHDR.FCtrl, MacCtx.McpsConfirm.McpsRequest ); + + switch( fType ) + { + case FRAME_TYPE_A: + { /* +----------+------+-------+--------------+ + * | FOptsLen | Fopt | FPort | FRMPayload | + * +----------+------+-------+--------------+ + * | > 0 | X | > 0 | X | + * +----------+------+-------+--------------+ + */ + + // Decode MAC commands in FOpts field + ProcessMacCommands( macMsgData.FHDR.FOpts, 0, macMsgData.FHDR.FCtrl.Bits.FOptsLen, snr, MacCtx.McpsIndication.RxSlot ); + MacCtx.McpsIndication.Port = macMsgData.FPort; + MacCtx.McpsIndication.Buffer = macMsgData.FRMPayload; + MacCtx.McpsIndication.BufferSize = macMsgData.FRMPayloadSize; + MacCtx.McpsIndication.RxData = true; + break; + } + case FRAME_TYPE_B: + { /* +----------+------+-------+--------------+ + * | FOptsLen | Fopt | FPort | FRMPayload | + * +----------+------+-------+--------------+ + * | > 0 | X | - | - | + * +----------+------+-------+--------------+ + */ + + // Decode MAC commands in FOpts field + ProcessMacCommands( macMsgData.FHDR.FOpts, 0, macMsgData.FHDR.FCtrl.Bits.FOptsLen, snr, MacCtx.McpsIndication.RxSlot ); + MacCtx.McpsIndication.Port = macMsgData.FPort; + break; + } + case FRAME_TYPE_C: + { /* +----------+------+-------+--------------+ + * | FOptsLen | Fopt | FPort | FRMPayload | + * +----------+------+-------+--------------+ + * | = 0 | - | = 0 | MAC commands | + * +----------+------+-------+--------------+ + */ + + // Decode MAC commands in FRMPayload + ProcessMacCommands( macMsgData.FRMPayload, 0, macMsgData.FRMPayloadSize, snr, MacCtx.McpsIndication.RxSlot ); + MacCtx.McpsIndication.Port = macMsgData.FPort; + break; + } + case FRAME_TYPE_D: + { /* +----------+------+-------+--------------+ + * | FOptsLen | Fopt | FPort | FRMPayload | + * +----------+------+-------+--------------+ + * | = 0 | - | > 0 | X | + * +----------+------+-------+--------------+ + */ + + // No MAC commands just application payload + MacCtx.McpsIndication.Port = macMsgData.FPort; + MacCtx.McpsIndication.Buffer = macMsgData.FRMPayload; + MacCtx.McpsIndication.BufferSize = macMsgData.FRMPayloadSize; + MacCtx.McpsIndication.RxData = true; + break; + } + default: + MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; + PrepareRxDoneAbort( ); + break; + } + + // Provide always an indication, skip the callback to the user application, + // in case of a confirmed downlink retransmission. + MacCtx.MacFlags.Bits.McpsInd = 1; + + break; + case FRAME_TYPE_PROPRIETARY: + memcpy1( MacCtx.RxPayload, &payload[pktHeaderLen], size - pktHeaderLen ); + + MacCtx.McpsIndication.McpsIndication = MCPS_PROPRIETARY; + MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_OK; + MacCtx.McpsIndication.Buffer = MacCtx.RxPayload; + MacCtx.McpsIndication.BufferSize = size - pktHeaderLen; + + MacCtx.MacFlags.Bits.McpsInd = 1; + break; + default: + MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; + PrepareRxDoneAbort( ); + break; + } + + // Verify if we need to disable the AckTimeoutTimer + if( MacCtx.NodeAckRequested == true ) + { + if( MacCtx.McpsConfirm.AckReceived == true ) + { + OnAckTimeoutTimerEvent( NULL ); + } + } + else + { + if( MacCtx.NvmCtx->DeviceClass == CLASS_C ) + { + OnAckTimeoutTimerEvent( NULL ); + } + } + MacCtx.MacFlags.Bits.MacDone = 1; + + UpdateRxSlotIdleState( ); +} + +static void ProcessRadioTxTimeout( void ) +{ + if( MacCtx.NvmCtx->DeviceClass != CLASS_C ) + { + Radio.Sleep( ); + } + UpdateRxSlotIdleState( ); + + MacCtx.McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT; + LoRaMacConfirmQueueSetStatusCmn( LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT ); + if( MacCtx.NodeAckRequested == true ) + { + MacCtx.AckTimeoutRetry = true; + } + MacCtx.MacFlags.Bits.MacDone = 1; +} + +static void HandleRadioRxErrorTimeout( LoRaMacEventInfoStatus_t rx1EventInfoStatus, LoRaMacEventInfoStatus_t rx2EventInfoStatus ) +{ + bool classBRx = false; + + if( MacCtx.NvmCtx->DeviceClass != CLASS_C ) + { + Radio.Sleep( ); + } + + if( LoRaMacClassBIsBeaconExpected( ) == true ) + { + LoRaMacClassBSetBeaconState( BEACON_STATE_TIMEOUT ); + LoRaMacClassBBeaconTimerEvent( NULL ); + classBRx = true; + } + if( MacCtx.NvmCtx->DeviceClass == CLASS_B ) + { + if( LoRaMacClassBIsPingExpected( ) == true ) + { + LoRaMacClassBSetPingSlotState( PINGSLOT_STATE_CALC_PING_OFFSET ); + LoRaMacClassBPingSlotTimerEvent( NULL ); + classBRx = true; + } + if( LoRaMacClassBIsMulticastExpected( ) == true ) + { + LoRaMacClassBSetMulticastSlotState( PINGSLOT_STATE_CALC_PING_OFFSET ); + LoRaMacClassBMulticastSlotTimerEvent( NULL ); + classBRx = true; + } + } + + if( classBRx == false ) + { + if( MacCtx.RxSlot == RX_SLOT_WIN_1 ) + { + if( MacCtx.NodeAckRequested == true ) + { + MacCtx.McpsConfirm.Status = rx1EventInfoStatus; + } + LoRaMacConfirmQueueSetStatusCmn( rx1EventInfoStatus ); + + if( TimerGetElapsedTime( MacCtx.NvmCtx->LastTxDoneTime ) >= MacCtx.RxWindow2Delay ) + { + TimerStop( &MacCtx.RxWindowTimer2 ); + MacCtx.MacFlags.Bits.MacDone = 1; + } + } + else + { + if( MacCtx.NodeAckRequested == true ) + { + MacCtx.McpsConfirm.Status = rx2EventInfoStatus; + } + LoRaMacConfirmQueueSetStatusCmn( rx2EventInfoStatus ); + + if( MacCtx.NvmCtx->DeviceClass != CLASS_C ) + { + MacCtx.MacFlags.Bits.MacDone = 1; + } + } + } + + UpdateRxSlotIdleState( ); +} + +static void ProcessRadioRxError( void ) +{ + HandleRadioRxErrorTimeout( LORAMAC_EVENT_INFO_STATUS_RX1_ERROR, LORAMAC_EVENT_INFO_STATUS_RX2_ERROR ); +} + +static void ProcessRadioRxTimeout( void ) +{ + HandleRadioRxErrorTimeout( LORAMAC_EVENT_INFO_STATUS_RX1_TIMEOUT, LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT ); +} + +static void LoRaMacHandleIrqEvents( void ) +{ + LoRaMacRadioEvents_t events; + + CRITICAL_SECTION_BEGIN( ); + events = LoRaMacRadioEvents; + LoRaMacRadioEvents.Value = 0; + CRITICAL_SECTION_END( ); + + if( events.Value != 0 ) + { + if( events.Events.TxDone == 1 ) + { + ProcessRadioTxDone( ); + } + if( events.Events.RxDone == 1 ) + { + ProcessRadioRxDone( ); + } + if( events.Events.TxTimeout == 1 ) + { + ProcessRadioTxTimeout( ); + } + if( events.Events.RxError == 1 ) + { + ProcessRadioRxError( ); + } + if( events.Events.RxTimeout == 1 ) + { + ProcessRadioRxTimeout( ); + } + } +} + +bool LoRaMacIsBusy( void ) +{ + if( ( MacCtx.MacState == LORAMAC_IDLE ) && + ( MacCtx.AllowRequests == LORAMAC_REQUEST_HANDLING_ON ) ) + { + return false; + } + return true; +} + + +static void LoRaMacEnableRequests( LoRaMacRequestHandling_t requestState ) +{ + MacCtx.AllowRequests = requestState; +} + +static void LoRaMacHandleRequestEvents( void ) +{ + // Handle events + LoRaMacFlags_t reqEvents = MacCtx.MacFlags; + + if( MacCtx.MacState == LORAMAC_IDLE ) + { + // Update event bits + if( MacCtx.MacFlags.Bits.McpsReq == 1 ) + { + MacCtx.MacFlags.Bits.McpsReq = 0; + } + + if( MacCtx.MacFlags.Bits.MlmeReq == 1 ) + { + MacCtx.MacFlags.Bits.MlmeReq = 0; + } + + // Allow requests again + LoRaMacEnableRequests( LORAMAC_REQUEST_HANDLING_ON ); + + // Handle callbacks + if( reqEvents.Bits.McpsReq == 1 ) + { + MacCtx.MacPrimitives->MacMcpsConfirm( &MacCtx.McpsConfirm ); + } + + if( reqEvents.Bits.MlmeReq == 1 ) + { + LoRaMacConfirmQueueHandleCb( &MacCtx.MlmeConfirm ); + if( LoRaMacConfirmQueueGetCnt( ) > 0 ) + { + MacCtx.MacFlags.Bits.MlmeReq = 1; + } + } + + // Start beaconing again + LoRaMacClassBResumeBeaconing( ); + + // Procedure done. Reset variables. + MacCtx.MacFlags.Bits.MacDone = 0; + } +} + +static void LoRaMacHandleScheduleUplinkEvent( void ) +{ + // Handle events + if( MacCtx.MacState == LORAMAC_IDLE ) + { + // Verify if sticky MAC commands are pending or not + bool isStickyMacCommandPending = false; + LoRaMacCommandsStickyCmdsPending( &isStickyMacCommandPending ); + if( isStickyMacCommandPending == true ) + {// Setup MLME indication + SetMlmeScheduleUplinkIndication( ); + } + } +} + +static void LoRaMacHandleIndicationEvents( void ) +{ + // Handle MLME indication + if( MacCtx.MacFlags.Bits.MlmeInd == 1 ) + { + MacCtx.MacFlags.Bits.MlmeInd = 0; + MacCtx.MacPrimitives->MacMlmeIndication( &MacCtx.MlmeIndication ); + } + + if( MacCtx.MacFlags.Bits.MlmeSchedUplinkInd == 1 ) + { + MlmeIndication_t schduleUplinkIndication; + schduleUplinkIndication.MlmeIndication = MLME_SCHEDULE_UPLINK; + schduleUplinkIndication.Status = LORAMAC_EVENT_INFO_STATUS_OK; + + MacCtx.MacPrimitives->MacMlmeIndication( &schduleUplinkIndication ); + MacCtx.MacFlags.Bits.MlmeSchedUplinkInd = 0; + } + + // Handle MCPS indication + if( MacCtx.MacFlags.Bits.McpsInd == 1 ) + { + MacCtx.MacFlags.Bits.McpsInd = 0; + MacCtx.MacPrimitives->MacMcpsIndication( &MacCtx.McpsIndication ); + } +} + +static void LoRaMacHandleMcpsRequest( void ) +{ + // Handle MCPS uplinks + if( MacCtx.MacFlags.Bits.McpsReq == 1 ) + { + bool stopRetransmission = false; + bool waitForRetransmission = false; + + if( ( MacCtx.McpsConfirm.McpsRequest == MCPS_UNCONFIRMED ) || + ( MacCtx.McpsConfirm.McpsRequest == MCPS_PROPRIETARY ) ) + { + stopRetransmission = CheckRetransUnconfirmedUplink( ); + } + else if( MacCtx.McpsConfirm.McpsRequest == MCPS_CONFIRMED ) + { + if( MacCtx.AckTimeoutRetry == true ) + { + stopRetransmission = CheckRetransConfirmedUplink( ); + + if( MacCtx.NvmCtx->Version.Fields.Minor == 0 ) + { + if( stopRetransmission == false ) + { + AckTimeoutRetriesProcess( ); + } + else + { + AckTimeoutRetriesFinalize( ); + } + } + } + else + { + waitForRetransmission = true; + } + } + + if( stopRetransmission == true ) + {// Stop retransmission + TimerStop( &MacCtx.TxDelayedTimer ); + MacCtx.MacState &= ~LORAMAC_TX_DELAYED; + StopRetransmission( ); + } + else if( waitForRetransmission == false ) + {// Arrange further retransmission + MacCtx.MacFlags.Bits.MacDone = 0; + // Reset the state of the AckTimeout + MacCtx.AckTimeoutRetry = false; + // Sends the same frame again + OnTxDelayedTimerEvent( NULL ); + } + } +} + +static void LoRaMacHandleMlmeRequest( void ) +{ + // Handle join request + if( MacCtx.MacFlags.Bits.MlmeReq == 1 ) + { + if( ( LoRaMacConfirmQueueIsCmdActive( MLME_JOIN ) == true ) ) + { + if( LoRaMacConfirmQueueGetStatus( MLME_JOIN ) == LORAMAC_EVENT_INFO_STATUS_OK ) + {// Node joined successfully + MacCtx.ChannelsNbTransCounter = 0; + } + MacCtx.MacState &= ~LORAMAC_TX_RUNNING; + } + else if( ( LoRaMacConfirmQueueIsCmdActive( MLME_TXCW ) == true ) || + ( LoRaMacConfirmQueueIsCmdActive( MLME_TXCW_1 ) == true ) ) + { + MacCtx.MacState &= ~LORAMAC_TX_RUNNING; + } + } +} + +static uint8_t LoRaMacCheckForBeaconAcquisition( void ) +{ + if( ( LoRaMacConfirmQueueIsCmdActive( MLME_BEACON_ACQUISITION ) == true ) && + ( MacCtx.MacFlags.Bits.McpsReq == 0 ) ) + { + if( MacCtx.MacFlags.Bits.MlmeReq == 1 ) + { + MacCtx.MacState &= ~LORAMAC_TX_RUNNING; + return 0x01; + } + } + return 0x00; +} + +static void LoRaMacCheckForRxAbort( void ) +{ + // A error occurs during receiving + if( ( MacCtx.MacState & LORAMAC_RX_ABORT ) == LORAMAC_RX_ABORT ) + { + MacCtx.MacState &= ~LORAMAC_RX_ABORT; + MacCtx.MacState &= ~LORAMAC_TX_RUNNING; + } +} + + +void LoRaMacProcess( void *args) +{ + uint8_t noTx = false; + + LoRaMacHandleIrqEvents( ); + LoRaMacClassBProcess( ); + + // MAC proceeded a state and is ready to check + if( MacCtx.MacFlags.Bits.MacDone == 1 ) + { + LoRaMacEnableRequests( LORAMAC_REQUEST_HANDLING_OFF ); + LoRaMacCheckForRxAbort( ); + + // An error occurs during transmitting + if( IsRequestPending( ) > 0 ) + { + noTx |= LoRaMacCheckForBeaconAcquisition( ); + } + + if( noTx == 0x00 ) + { + LoRaMacHandleMlmeRequest( ); + LoRaMacHandleMcpsRequest( ); + } + LoRaMacHandleRequestEvents( ); + LoRaMacHandleScheduleUplinkEvent( ); + LoRaMacEnableRequests( LORAMAC_REQUEST_HANDLING_ON ); + } + LoRaMacHandleIndicationEvents( ); + if( MacCtx.RxSlot == RX_SLOT_WIN_CLASS_C ) + { + OpenContinuousRxCWindow( ); + } +} + +static void OnTxDelayedTimerEvent( void* context ) +{ + TimerStop( &MacCtx.TxDelayedTimer ); + MacCtx.MacState &= ~LORAMAC_TX_DELAYED; + + // Schedule frame, allow delayed frame transmissions + switch( ScheduleTx( true ) ) + { + case LORAMAC_STATUS_OK: + case LORAMAC_STATUS_DUTYCYCLE_RESTRICTED: + { + break; + } + default: + { + // Stop retransmission attempt + MacCtx.McpsConfirm.Datarate = MacCtx.NvmCtx->MacParams.ChannelsDatarate; + MacCtx.McpsConfirm.NbRetries = MacCtx.AckTimeoutRetriesCounter; + MacCtx.McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_TX_DR_PAYLOAD_SIZE_ERROR; + LoRaMacConfirmQueueSetStatusCmn( LORAMAC_EVENT_INFO_STATUS_TX_DR_PAYLOAD_SIZE_ERROR ); + StopRetransmission( ); + break; + } + } +} + +static void OnRxWindow1TimerEvent( void* context ) +{ + MacCtx.RxWindow1Config.Channel = MacCtx.Channel; + MacCtx.RxWindow1Config.DrOffset = MacCtx.NvmCtx->MacParams.Rx1DrOffset; + MacCtx.RxWindow1Config.DownlinkDwellTime = MacCtx.NvmCtx->MacParams.DownlinkDwellTime; + MacCtx.RxWindow1Config.RepeaterSupport = MacCtx.NvmCtx->RepeaterSupport; + MacCtx.RxWindow1Config.RxContinuous = false; + MacCtx.RxWindow1Config.RxSlot = RX_SLOT_WIN_1; + + RxWindowSetup( &MacCtx.RxWindowTimer1, &MacCtx.RxWindow1Config ); +} + +static void OnRxWindow2TimerEvent( void* context ) +{ + // Check if we are processing Rx1 window. + // If yes, we don't setup the Rx2 window. + if( MacCtx.RxSlot == RX_SLOT_WIN_1 ) + { + return; + } + MacCtx.RxWindow2Config.Channel = MacCtx.Channel; + MacCtx.RxWindow2Config.Frequency = MacCtx.NvmCtx->MacParams.Rx2Channel.Frequency; + MacCtx.RxWindow2Config.DownlinkDwellTime = MacCtx.NvmCtx->MacParams.DownlinkDwellTime; + MacCtx.RxWindow2Config.RepeaterSupport = MacCtx.NvmCtx->RepeaterSupport; + MacCtx.RxWindow2Config.RxContinuous = false; + MacCtx.RxWindow2Config.RxSlot = RX_SLOT_WIN_2; + + RxWindowSetup( &MacCtx.RxWindowTimer2, &MacCtx.RxWindow2Config ); +} + +static void OnAckTimeoutTimerEvent( void* context ) +{ + TimerStop( &MacCtx.AckTimeoutTimer ); + + if( MacCtx.NodeAckRequested == true ) + { + MacCtx.AckTimeoutRetry = true; + } + if( MacCtx.NvmCtx->DeviceClass == CLASS_C ) + { + MacCtx.MacFlags.Bits.MacDone = 1; + } + if( ( MacCtx.MacCallbacks != NULL ) && ( MacCtx.MacCallbacks->MacProcessNotify != NULL ) ) + { + MacCtx.MacCallbacks->MacProcessNotify( ); + } +} + +static LoRaMacCryptoStatus_t GetFCntDown( AddressIdentifier_t addrID, FType_t fType, LoRaMacMessageData_t* macMsg, Version_t lrWanVersion, + uint16_t maxFCntGap, FCntIdentifier_t* fCntID, uint32_t* currentDown ) +{ + if( ( macMsg == NULL ) || ( fCntID == NULL ) || + ( currentDown == NULL ) ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + + // Determine the frame counter identifier and choose counter from FCntList + switch( addrID ) + { + case UNICAST_DEV_ADDR: + if( lrWanVersion.Fields.Minor == 1 ) + { + if( ( fType == FRAME_TYPE_A ) || ( fType == FRAME_TYPE_D ) ) + { + *fCntID = A_FCNT_DOWN; + } + else + { + *fCntID = N_FCNT_DOWN; + } + } + else + { // For LoRaWAN 1.0.X + *fCntID = FCNT_DOWN; + } + break; + case MULTICAST_0_ADDR: + *fCntID = MC_FCNT_DOWN_0; + break; + case MULTICAST_1_ADDR: + *fCntID = MC_FCNT_DOWN_1; + break; + case MULTICAST_2_ADDR: + *fCntID = MC_FCNT_DOWN_2; + break; + case MULTICAST_3_ADDR: + *fCntID = MC_FCNT_DOWN_3; + break; + default: + return LORAMAC_CRYPTO_FAIL_FCNT_ID; + } + + return LoRaMacCryptoGetFCntDown( *fCntID, maxFCntGap, macMsg->FHDR.FCnt, currentDown ); +} + +static LoRaMacStatus_t SwitchClass( DeviceClass_t deviceClass ) +{ + LoRaMacStatus_t status = LORAMAC_STATUS_PARAMETER_INVALID; + + switch( MacCtx.NvmCtx->DeviceClass ) + { + case CLASS_A: + { + if( deviceClass == CLASS_A ) + { + // Revert back RxC parameters + MacCtx.NvmCtx->MacParams.RxCChannel = MacCtx.NvmCtx->MacParams.Rx2Channel; + } + if( deviceClass == CLASS_B ) + { + status = LoRaMacClassBSwitchClass( deviceClass ); + if( status == LORAMAC_STATUS_OK ) + { + MacCtx.NvmCtx->DeviceClass = deviceClass; + } + } + + if( deviceClass == CLASS_C ) + { + MacCtx.NvmCtx->DeviceClass = deviceClass; + + MacCtx.RxWindowCConfig = MacCtx.RxWindow2Config; + MacCtx.RxWindowCConfig.RxSlot = RX_SLOT_WIN_CLASS_C; + + for( int8_t i = 0; i < LORAMAC_MAX_MC_CTX; i++ ) + { + if( MacCtx.NvmCtx->MulticastChannelList[i].ChannelParams.IsEnabled == true ) + // TODO: Check multicast channel device class. + { + MacCtx.NvmCtx->MacParams.RxCChannel.Frequency = MacCtx.NvmCtx->MulticastChannelList[i].ChannelParams.RxParams.ClassC.Frequency; + MacCtx.NvmCtx->MacParams.RxCChannel.Datarate = MacCtx.NvmCtx->MulticastChannelList[i].ChannelParams.RxParams.ClassC.Datarate; + + MacCtx.RxWindowCConfig.Channel = MacCtx.Channel; + MacCtx.RxWindowCConfig.Frequency = MacCtx.NvmCtx->MacParams.RxCChannel.Frequency; + MacCtx.RxWindowCConfig.DownlinkDwellTime = MacCtx.NvmCtx->MacParams.DownlinkDwellTime; + MacCtx.RxWindowCConfig.RepeaterSupport = MacCtx.NvmCtx->RepeaterSupport; + MacCtx.RxWindowCConfig.RxSlot = RX_SLOT_WIN_CLASS_C_MULTICAST; + MacCtx.RxWindowCConfig.RxContinuous = true; + break; + } + } + + // Set the NodeAckRequested indicator to default + MacCtx.NodeAckRequested = false; + // Set the radio into sleep mode in case we are still in RX mode + Radio.Sleep( ); + // Compute Rx2 windows parameters in case the RX2 datarate has changed + RegionComputeRxWindowParameters( MacCtx.NvmCtx->Region, + MacCtx.NvmCtx->MacParams.RxCChannel.Datarate, + MacCtx.NvmCtx->MacParams.MinRxSymbols, + MacCtx.NvmCtx->MacParams.SystemMaxRxError, + &MacCtx.RxWindowCConfig ); + OpenContinuousRxCWindow( ); + + status = LORAMAC_STATUS_OK; + } + break; + } + case CLASS_B: + { + status = LoRaMacClassBSwitchClass( deviceClass ); + if( status == LORAMAC_STATUS_OK ) + { + MacCtx.NvmCtx->DeviceClass = deviceClass; + } + break; + } + case CLASS_C: + { + if( deviceClass == CLASS_A ) + { + MacCtx.NvmCtx->DeviceClass = deviceClass; + + // Set the radio into sleep to setup a defined state + Radio.Sleep( ); + + status = LORAMAC_STATUS_OK; + } + break; + } + } + + return status; +} + +static uint8_t GetMaxAppPayloadWithoutFOptsLength( int8_t datarate ) +{ + GetPhyParams_t getPhy; + PhyParam_t phyParam; + + // Setup PHY request + getPhy.UplinkDwellTime = MacCtx.NvmCtx->MacParams.UplinkDwellTime; + getPhy.Datarate = datarate; + getPhy.Attribute = PHY_MAX_PAYLOAD; + + // Get the maximum payload length + if( MacCtx.NvmCtx->RepeaterSupport == true ) + { + getPhy.Attribute = PHY_MAX_PAYLOAD_REPEATER; + } + phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy ); + + return phyParam.Value; +} + +static bool ValidatePayloadLength( uint8_t lenN, int8_t datarate, uint8_t fOptsLen ) +{ + uint16_t maxN = 0; + uint16_t payloadSize = 0; + + maxN = GetMaxAppPayloadWithoutFOptsLength( datarate ); + + // Calculate the resulting payload size + payloadSize = ( lenN + fOptsLen ); + + // Validation of the application payload size + if( ( payloadSize <= maxN ) && ( payloadSize <= LORAMAC_PHY_MAXPAYLOAD ) ) + { + return true; + } + return false; +} + +static void SetMlmeScheduleUplinkIndication( void ) +{ + MacCtx.MacFlags.Bits.MlmeSchedUplinkInd = 1; +} + +static void ProcessMacCommands( uint8_t *payload, uint8_t macIndex, uint8_t commandsSize, int8_t snr, LoRaMacRxSlot_t rxSlot ) +{ + uint8_t status = 0; + bool adrBlockFound = false; + uint8_t macCmdPayload[2] = { 0x00, 0x00 }; + + while( macIndex < commandsSize ) + { + // Decode Frame MAC commands + switch( payload[macIndex++] ) + { + case SRV_MAC_LINK_CHECK_ANS: + { + if( LoRaMacConfirmQueueIsCmdActive( MLME_LINK_CHECK ) == true ) + { + LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_OK, MLME_LINK_CHECK ); + MacCtx.MlmeConfirm.DemodMargin = payload[macIndex++]; + MacCtx.MlmeConfirm.NbGateways = payload[macIndex++]; + } + break; + } + case SRV_MAC_LINK_ADR_REQ: + { + LinkAdrReqParams_t linkAdrReq; + int8_t linkAdrDatarate = DR_0; + int8_t linkAdrTxPower = TX_POWER_0; + uint8_t linkAdrNbRep = 0; + uint8_t linkAdrNbBytesParsed = 0; + + if( adrBlockFound == false ) + { + adrBlockFound = true; + + // Fill parameter structure + linkAdrReq.Payload = &payload[macIndex - 1]; + linkAdrReq.PayloadSize = commandsSize - ( macIndex - 1 ); + linkAdrReq.AdrEnabled = MacCtx.NvmCtx->AdrCtrlOn; + linkAdrReq.UplinkDwellTime = MacCtx.NvmCtx->MacParams.UplinkDwellTime; + linkAdrReq.CurrentDatarate = MacCtx.NvmCtx->MacParams.ChannelsDatarate; + linkAdrReq.CurrentTxPower = MacCtx.NvmCtx->MacParams.ChannelsTxPower; + linkAdrReq.CurrentNbRep = MacCtx.NvmCtx->MacParams.ChannelsNbTrans; + linkAdrReq.Version = MacCtx.NvmCtx->Version; + + // Process the ADR requests + status = RegionLinkAdrReq( MacCtx.NvmCtx->Region, &linkAdrReq, &linkAdrDatarate, + &linkAdrTxPower, &linkAdrNbRep, &linkAdrNbBytesParsed ); + + if( ( status & 0x07 ) == 0x07 ) + { + MacCtx.NvmCtx->MacParams.ChannelsDatarate = linkAdrDatarate; + MacCtx.NvmCtx->MacParams.ChannelsTxPower = linkAdrTxPower; + MacCtx.NvmCtx->MacParams.ChannelsNbTrans = linkAdrNbRep; + } + + // Add the answers to the buffer + for( uint8_t i = 0; i < ( linkAdrNbBytesParsed / 5 ); i++ ) + { + LoRaMacCommandsAddCmd( MOTE_MAC_LINK_ADR_ANS, &status, 1 ); + } + // Update MAC index + macIndex += linkAdrNbBytesParsed - 1; + } + break; + } + case SRV_MAC_DUTY_CYCLE_REQ: + { + MacCtx.NvmCtx->MaxDCycle = payload[macIndex++] & 0x0F; + MacCtx.NvmCtx->AggregatedDCycle = 1 << MacCtx.NvmCtx->MaxDCycle; + LoRaMacCommandsAddCmd( MOTE_MAC_DUTY_CYCLE_ANS, macCmdPayload, 0 ); + break; + } + case SRV_MAC_RX_PARAM_SETUP_REQ: + { + RxParamSetupReqParams_t rxParamSetupReq; + status = 0x07; + + rxParamSetupReq.DrOffset = ( payload[macIndex] >> 4 ) & 0x07; + rxParamSetupReq.Datarate = payload[macIndex] & 0x0F; + macIndex++; + + rxParamSetupReq.Frequency = ( uint32_t ) payload[macIndex++]; + rxParamSetupReq.Frequency |= ( uint32_t ) payload[macIndex++] << 8; + rxParamSetupReq.Frequency |= ( uint32_t ) payload[macIndex++] << 16; + rxParamSetupReq.Frequency *= 100; + + // Perform request on region + status = RegionRxParamSetupReq( MacCtx.NvmCtx->Region, &rxParamSetupReq ); + + if( ( status & 0x07 ) == 0x07 ) + { + MacCtx.NvmCtx->MacParams.Rx2Channel.Datarate = rxParamSetupReq.Datarate; + MacCtx.NvmCtx->MacParams.RxCChannel.Datarate = rxParamSetupReq.Datarate; + MacCtx.NvmCtx->MacParams.Rx2Channel.Frequency = rxParamSetupReq.Frequency; + MacCtx.NvmCtx->MacParams.RxCChannel.Frequency = rxParamSetupReq.Frequency; + MacCtx.NvmCtx->MacParams.Rx1DrOffset = rxParamSetupReq.DrOffset; + } + macCmdPayload[0] = status; + LoRaMacCommandsAddCmd( MOTE_MAC_RX_PARAM_SETUP_ANS, macCmdPayload, 1 ); + // Setup indication to inform the application + SetMlmeScheduleUplinkIndication( ); + break; + } + case SRV_MAC_DEV_STATUS_REQ: + { + uint8_t batteryLevel = BAT_LEVEL_NO_MEASURE; + if( ( MacCtx.MacCallbacks != NULL ) && ( MacCtx.MacCallbacks->GetBatteryLevel != NULL ) ) + { + batteryLevel = MacCtx.MacCallbacks->GetBatteryLevel( ); + } + macCmdPayload[0] = batteryLevel; + macCmdPayload[1] = ( uint8_t )( snr & 0x3F ); + LoRaMacCommandsAddCmd( MOTE_MAC_DEV_STATUS_ANS, macCmdPayload, 2 ); + break; + } + case SRV_MAC_NEW_CHANNEL_REQ: + { + NewChannelReqParams_t newChannelReq; + ChannelParams_t chParam; + status = 0x03; + + newChannelReq.ChannelId = payload[macIndex++]; + newChannelReq.NewChannel = &chParam; + + chParam.Frequency = ( uint32_t ) payload[macIndex++]; + chParam.Frequency |= ( uint32_t ) payload[macIndex++] << 8; + chParam.Frequency |= ( uint32_t ) payload[macIndex++] << 16; + chParam.Frequency *= 100; + chParam.Rx1Frequency = 0; + chParam.DrRange.Value = payload[macIndex++]; + + status = RegionNewChannelReq( MacCtx.NvmCtx->Region, &newChannelReq ); + + macCmdPayload[0] = status; + LoRaMacCommandsAddCmd( MOTE_MAC_NEW_CHANNEL_ANS, macCmdPayload, 1 ); + break; + } + case SRV_MAC_RX_TIMING_SETUP_REQ: + { + uint8_t delay = payload[macIndex++] & 0x0F; + + if( delay == 0 ) + { + delay++; + } + MacCtx.NvmCtx->MacParams.ReceiveDelay1 = delay * 1000; + MacCtx.NvmCtx->MacParams.ReceiveDelay2 = MacCtx.NvmCtx->MacParams.ReceiveDelay1 + 1000; + LoRaMacCommandsAddCmd( MOTE_MAC_RX_TIMING_SETUP_ANS, macCmdPayload, 0 ); + // Setup indication to inform the application + SetMlmeScheduleUplinkIndication( ); + break; + } + case SRV_MAC_TX_PARAM_SETUP_REQ: + { + TxParamSetupReqParams_t txParamSetupReq; + GetPhyParams_t getPhy; + PhyParam_t phyParam; + uint8_t eirpDwellTime = payload[macIndex++]; + + txParamSetupReq.UplinkDwellTime = 0; + txParamSetupReq.DownlinkDwellTime = 0; + + if( ( eirpDwellTime & 0x20 ) == 0x20 ) + { + txParamSetupReq.DownlinkDwellTime = 1; + } + if( ( eirpDwellTime & 0x10 ) == 0x10 ) + { + txParamSetupReq.UplinkDwellTime = 1; + } + txParamSetupReq.MaxEirp = eirpDwellTime & 0x0F; + + // Check the status for correctness + if( RegionTxParamSetupReq( MacCtx.NvmCtx->Region, &txParamSetupReq ) != -1 ) + { + // Accept command + MacCtx.NvmCtx->MacParams.UplinkDwellTime = txParamSetupReq.UplinkDwellTime; + MacCtx.NvmCtx->MacParams.DownlinkDwellTime = txParamSetupReq.DownlinkDwellTime; + MacCtx.NvmCtx->MacParams.MaxEirp = LoRaMacMaxEirpTable[txParamSetupReq.MaxEirp]; + // Update the datarate in case of the new configuration limits it + getPhy.Attribute = PHY_MIN_TX_DR; + getPhy.UplinkDwellTime = MacCtx.NvmCtx->MacParams.UplinkDwellTime; + phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy ); + MacCtx.NvmCtx->MacParams.ChannelsDatarate = MAX( MacCtx.NvmCtx->MacParams.ChannelsDatarate, ( int8_t )phyParam.Value ); + + // Add command response + LoRaMacCommandsAddCmd( MOTE_MAC_TX_PARAM_SETUP_ANS, macCmdPayload, 0 ); + } + break; + } + case SRV_MAC_DL_CHANNEL_REQ: + { + DlChannelReqParams_t dlChannelReq; + status = 0x03; + + dlChannelReq.ChannelId = payload[macIndex++]; + dlChannelReq.Rx1Frequency = ( uint32_t ) payload[macIndex++]; + dlChannelReq.Rx1Frequency |= ( uint32_t ) payload[macIndex++] << 8; + dlChannelReq.Rx1Frequency |= ( uint32_t ) payload[macIndex++] << 16; + dlChannelReq.Rx1Frequency *= 100; + + status = RegionDlChannelReq( MacCtx.NvmCtx->Region, &dlChannelReq ); + macCmdPayload[0] = status; + LoRaMacCommandsAddCmd( MOTE_MAC_DL_CHANNEL_ANS, macCmdPayload, 1 ); + // Setup indication to inform the application + SetMlmeScheduleUplinkIndication( ); + break; + } + case SRV_MAC_DEVICE_TIME_ANS: + { + SysTime_t gpsEpochTime = { 0 }; + SysTime_t sysTime = { 0 }; + SysTime_t sysTimeCurrent = { 0 }; + + gpsEpochTime.Seconds = ( uint32_t )payload[macIndex++]; + gpsEpochTime.Seconds |= ( uint32_t )payload[macIndex++] << 8; + gpsEpochTime.Seconds |= ( uint32_t )payload[macIndex++] << 16; + gpsEpochTime.Seconds |= ( uint32_t )payload[macIndex++] << 24; + gpsEpochTime.SubSeconds = payload[macIndex++]; + + // Convert the fractional second received in ms + // round( pow( 0.5, 8.0 ) * 1000 ) = 3.90625 + gpsEpochTime.SubSeconds = ( int16_t )( ( ( int32_t )gpsEpochTime.SubSeconds * 1000 ) >> 8 ); + + // Copy received GPS Epoch time into system time + sysTime = gpsEpochTime; + // Add Unix to Gps epcoh offset. The system time is based on Unix time. + sysTime.Seconds += UNIX_GPS_EPOCH_OFFSET; + + // Compensate time difference between Tx Done time and now + sysTimeCurrent = SysTimeGet( ); + sysTime = SysTimeAdd( sysTimeCurrent, SysTimeSub( sysTime, MacCtx.LastTxSysTime ) ); + + // Apply the new system time. + SysTimeSet( sysTime ); + LoRaMacClassBDeviceTimeAns( ); + MacCtx.McpsIndication.DeviceTimeAnsReceived = true; + break; + } + case SRV_MAC_PING_SLOT_INFO_ANS: + { + // According to the specification, it is not allowed to process this answer in + // a ping or multicast slot + if( ( MacCtx.RxSlot != RX_SLOT_WIN_CLASS_B_PING_SLOT ) && ( MacCtx.RxSlot != RX_SLOT_WIN_CLASS_B_MULTICAST_SLOT ) ) + { + LoRaMacClassBPingSlotInfoAns( ); + } + break; + } + case SRV_MAC_PING_SLOT_CHANNEL_REQ: + { + uint8_t status = 0x03; + uint32_t frequency = 0; + uint8_t datarate; + + frequency = ( uint32_t )payload[macIndex++]; + frequency |= ( uint32_t )payload[macIndex++] << 8; + frequency |= ( uint32_t )payload[macIndex++] << 16; + frequency *= 100; + datarate = payload[macIndex++] & 0x0F; + + status = LoRaMacClassBPingSlotChannelReq( datarate, frequency ); + macCmdPayload[0] = status; + LoRaMacCommandsAddCmd( MOTE_MAC_PING_SLOT_FREQ_ANS, macCmdPayload, 1 ); + break; + } + case SRV_MAC_BEACON_TIMING_ANS: + { + uint16_t beaconTimingDelay = 0; + uint8_t beaconTimingChannel = 0; + + beaconTimingDelay = ( uint16_t )payload[macIndex++]; + beaconTimingDelay |= ( uint16_t )payload[macIndex++] << 8; + beaconTimingChannel = payload[macIndex++]; + + LoRaMacClassBBeaconTimingAns( beaconTimingDelay, beaconTimingChannel, RxDoneParams.LastRxDone ); + break; + } + case SRV_MAC_BEACON_FREQ_REQ: + { + uint32_t frequency = 0; + + frequency = ( uint32_t )payload[macIndex++]; + frequency |= ( uint32_t )payload[macIndex++] << 8; + frequency |= ( uint32_t )payload[macIndex++] << 16; + frequency *= 100; + + if( LoRaMacClassBBeaconFreqReq( frequency ) == true ) + { + macCmdPayload[0] = 1; + } + else + { + macCmdPayload[0] = 0; + } + LoRaMacCommandsAddCmd( MOTE_MAC_BEACON_FREQ_ANS, macCmdPayload, 1 ); + } + break; + default: + // Unknown command. ABORT MAC commands processing + return; + } + } +} + +LoRaMacStatus_t Send( LoRaMacHeader_t* macHdr, uint8_t fPort, void* fBuffer, uint16_t fBufferSize ) +{ + LoRaMacFrameCtrl_t fCtrl; + LoRaMacStatus_t status = LORAMAC_STATUS_PARAMETER_INVALID; + int8_t datarate = MacCtx.NvmCtx->MacParams.ChannelsDatarate; + int8_t txPower = MacCtx.NvmCtx->MacParams.ChannelsTxPower; + uint32_t adrAckCounter = MacCtx.NvmCtx->AdrAckCounter; + CalcNextAdrParams_t adrNext; + + // Check if we are joined + if( MacCtx.NvmCtx->NetworkActivation == ACTIVATION_TYPE_NONE ) + { + return LORAMAC_STATUS_NO_NETWORK_JOINED; + } + if( MacCtx.NvmCtx->MaxDCycle == 0 ) + { + MacCtx.NvmCtx->AggregatedTimeOff = 0; + } + + fCtrl.Value = 0; + fCtrl.Bits.FOptsLen = 0; + fCtrl.Bits.Adr = MacCtx.NvmCtx->AdrCtrlOn; + + // Check class b + if( MacCtx.NvmCtx->DeviceClass == CLASS_B ) + { + fCtrl.Bits.FPending = 1; + } + else + { + fCtrl.Bits.FPending = 0; + } + + // Check server ack + if( MacCtx.NvmCtx->SrvAckRequested == true ) + { + fCtrl.Bits.Ack = 1; + } + + // ADR next request + adrNext.Version = MacCtx.NvmCtx->Version; + adrNext.UpdateChanMask = true; + adrNext.AdrEnabled = fCtrl.Bits.Adr; + adrNext.AdrAckCounter = MacCtx.NvmCtx->AdrAckCounter; + adrNext.AdrAckLimit = MacCtx.AdrAckLimit; + adrNext.AdrAckDelay = MacCtx.AdrAckDelay; + adrNext.Datarate = MacCtx.NvmCtx->MacParams.ChannelsDatarate; + adrNext.TxPower = MacCtx.NvmCtx->MacParams.ChannelsTxPower; + adrNext.UplinkDwellTime = MacCtx.NvmCtx->MacParams.UplinkDwellTime; + adrNext.Region = MacCtx.NvmCtx->Region; + + fCtrl.Bits.AdrAckReq = LoRaMacAdrCalcNext( &adrNext, &MacCtx.NvmCtx->MacParams.ChannelsDatarate, + &MacCtx.NvmCtx->MacParams.ChannelsTxPower, &adrAckCounter ); + + // Prepare the frame + status = PrepareFrame( macHdr, &fCtrl, fPort, fBuffer, fBufferSize ); + + // Validate status + if( ( status == LORAMAC_STATUS_OK ) || ( status == LORAMAC_STATUS_SKIPPED_APP_DATA ) ) + { + // Schedule frame, do not allow delayed transmissions + status = ScheduleTx( false ); + } + + // Post processing + if( status != LORAMAC_STATUS_OK ) + { + // Bad case - restore + // Store local variables + MacCtx.NvmCtx->MacParams.ChannelsDatarate = datarate; + MacCtx.NvmCtx->MacParams.ChannelsTxPower = txPower; + } + else + { + // Good case + MacCtx.NvmCtx->SrvAckRequested = false; + MacCtx.NvmCtx->AdrAckCounter = adrAckCounter; + // Remove all none sticky MAC commands + if( LoRaMacCommandsRemoveNoneStickyCmds( ) != LORAMAC_COMMANDS_SUCCESS ) + { + return LORAMAC_STATUS_MAC_COMMAD_ERROR; + } + } + return status; +} + +LoRaMacStatus_t SendReJoinReq( JoinReqIdentifier_t joinReqType ) +{ + LoRaMacStatus_t status = LORAMAC_STATUS_OK; + LoRaMacHeader_t macHdr; + macHdr.Value = 0; + bool allowDelayedTx = true; + + // Setup join/rejoin message + switch( joinReqType ) + { + case JOIN_REQ: + { + SwitchClass( CLASS_A ); + + MacCtx.TxMsg.Type = LORAMAC_MSG_TYPE_JOIN_REQUEST; + MacCtx.TxMsg.Message.JoinReq.Buffer = MacCtx.PktBuffer; + MacCtx.TxMsg.Message.JoinReq.BufSize = LORAMAC_PHY_MAXPAYLOAD; + + macHdr.Bits.MType = FRAME_TYPE_JOIN_REQ; + MacCtx.TxMsg.Message.JoinReq.MHDR.Value = macHdr.Value; + + memcpy1( MacCtx.TxMsg.Message.JoinReq.JoinEUI, SecureElementGetJoinEui( ), LORAMAC_JOIN_EUI_FIELD_SIZE ); + memcpy1( MacCtx.TxMsg.Message.JoinReq.DevEUI, SecureElementGetDevEui( ), LORAMAC_DEV_EUI_FIELD_SIZE ); + + allowDelayedTx = false; + + break; + } + default: + status = LORAMAC_STATUS_SERVICE_UNKNOWN; + break; + } + + // Schedule frame + status = ScheduleTx( allowDelayedTx ); + return status; +} + +static LoRaMacStatus_t CheckForClassBCollision( void ) +{ + if( LoRaMacClassBIsBeaconExpected( ) == true ) + { + return LORAMAC_STATUS_BUSY_BEACON_RESERVED_TIME; + } + + if( MacCtx.NvmCtx->DeviceClass == CLASS_B ) + { + if( LoRaMacClassBIsPingExpected( ) == true ) + { + return LORAMAC_STATUS_BUSY_PING_SLOT_WINDOW_TIME; + } + else if( LoRaMacClassBIsMulticastExpected( ) == true ) + { + return LORAMAC_STATUS_BUSY_PING_SLOT_WINDOW_TIME; + } + } + return LORAMAC_STATUS_OK; +} + +static LoRaMacStatus_t ScheduleTx( bool allowDelayedTx ) +{ + LoRaMacStatus_t status = LORAMAC_STATUS_PARAMETER_INVALID; + TimerTime_t dutyCycleTimeOff = 0; + NextChanParams_t nextChan; + size_t macCmdsSize = 0; + + // Check class b collisions + status = CheckForClassBCollision( ); + + if( status != LORAMAC_STATUS_OK ) + { + return status; + } + + // Update back-off + CalculateBackOff( MacCtx.NvmCtx->LastTxChannel ); + + nextChan.AggrTimeOff = MacCtx.NvmCtx->AggregatedTimeOff; + nextChan.Datarate = MacCtx.NvmCtx->MacParams.ChannelsDatarate; + nextChan.DutyCycleEnabled = MacCtx.NvmCtx->DutyCycleOn; + if( MacCtx.NvmCtx->NetworkActivation == ACTIVATION_TYPE_NONE ) + { + nextChan.Joined = false; + } + else + { + nextChan.Joined = true; + } + nextChan.LastAggrTx = MacCtx.NvmCtx->LastTxDoneTime; + + // Select channel + status = RegionNextChannel( MacCtx.NvmCtx->Region, &nextChan, &MacCtx.Channel, &dutyCycleTimeOff, &MacCtx.NvmCtx->AggregatedTimeOff ); + + if( status != LORAMAC_STATUS_OK ) + { + if( ( status == LORAMAC_STATUS_DUTYCYCLE_RESTRICTED ) && + ( allowDelayedTx == true ) ) + { + // Allow delayed transmissions. We have to allow it in case + // the MAC must retransmit a frame with the frame repetitions + if( dutyCycleTimeOff != 0 ) + {// Send later - prepare timer + MacCtx.MacState |= LORAMAC_TX_DELAYED; + TimerSetValue( &MacCtx.TxDelayedTimer, dutyCycleTimeOff ); + TimerStart( &MacCtx.TxDelayedTimer ); + } + return LORAMAC_STATUS_OK; + } + else + {// State where the MAC cannot send a frame + return status; + } + } + + // Compute Rx1 windows parameters + RegionComputeRxWindowParameters( MacCtx.NvmCtx->Region, + RegionApplyDrOffset( MacCtx.NvmCtx->Region, MacCtx.NvmCtx->MacParams.DownlinkDwellTime, MacCtx.NvmCtx->MacParams.ChannelsDatarate, MacCtx.NvmCtx->MacParams.Rx1DrOffset ), + MacCtx.NvmCtx->MacParams.MinRxSymbols, + MacCtx.NvmCtx->MacParams.SystemMaxRxError, + &MacCtx.RxWindow1Config ); + // Compute Rx2 windows parameters + RegionComputeRxWindowParameters( MacCtx.NvmCtx->Region, + MacCtx.NvmCtx->MacParams.Rx2Channel.Datarate, + MacCtx.NvmCtx->MacParams.MinRxSymbols, + MacCtx.NvmCtx->MacParams.SystemMaxRxError, + &MacCtx.RxWindow2Config ); + + if( MacCtx.NvmCtx->NetworkActivation == ACTIVATION_TYPE_NONE ) + { + MacCtx.RxWindow1Delay = MacCtx.NvmCtx->MacParams.JoinAcceptDelay1 + MacCtx.RxWindow1Config.WindowOffset; + MacCtx.RxWindow2Delay = MacCtx.NvmCtx->MacParams.JoinAcceptDelay2 + MacCtx.RxWindow2Config.WindowOffset; + } + else + { + if( LoRaMacCommandsGetSizeSerializedCmds( &macCmdsSize ) != LORAMAC_COMMANDS_SUCCESS ) + { + return LORAMAC_STATUS_MAC_COMMAD_ERROR; + } + + if( ValidatePayloadLength( MacCtx.AppDataSize, MacCtx.NvmCtx->MacParams.ChannelsDatarate, macCmdsSize ) == false ) + { + return LORAMAC_STATUS_LENGTH_ERROR; + } + MacCtx.RxWindow1Delay = MacCtx.NvmCtx->MacParams.ReceiveDelay1 + MacCtx.RxWindow1Config.WindowOffset; + MacCtx.RxWindow2Delay = MacCtx.NvmCtx->MacParams.ReceiveDelay2 + MacCtx.RxWindow2Config.WindowOffset; + } + + // Secure frame + LoRaMacStatus_t retval = SecureFrame( MacCtx.NvmCtx->MacParams.ChannelsDatarate, MacCtx.Channel ); + if( retval != LORAMAC_STATUS_OK ) + { + return retval; + } + + // Try to send now + return SendFrameOnChannel( MacCtx.Channel ); +} + +static LoRaMacStatus_t SecureFrame( uint8_t txDr, uint8_t txCh ) +{ + LoRaMacCryptoStatus_t macCryptoStatus = LORAMAC_CRYPTO_ERROR; + uint32_t fCntUp = 0; + + switch( MacCtx.TxMsg.Type ) + { + case LORAMAC_MSG_TYPE_JOIN_REQUEST: + macCryptoStatus = LoRaMacCryptoPrepareJoinRequest( &MacCtx.TxMsg.Message.JoinReq ); + if( LORAMAC_CRYPTO_SUCCESS != macCryptoStatus ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + MacCtx.PktBufferLen = MacCtx.TxMsg.Message.JoinReq.BufSize; + break; + case LORAMAC_MSG_TYPE_DATA: + + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoGetFCntUp( &fCntUp ) ) + { + return LORAMAC_STATUS_FCNT_HANDLER_ERROR; + } + + if( ( MacCtx.ChannelsNbTransCounter >= 1 ) || ( MacCtx.AckTimeoutRetriesCounter > 1 ) ) + { + fCntUp -= 1; + } + + macCryptoStatus = LoRaMacCryptoSecureMessage( fCntUp, txDr, txCh, &MacCtx.TxMsg.Message.Data ); + if( LORAMAC_CRYPTO_SUCCESS != macCryptoStatus ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + MacCtx.PktBufferLen = MacCtx.TxMsg.Message.Data.BufSize; + break; + case LORAMAC_MSG_TYPE_JOIN_ACCEPT: + case LORAMAC_MSG_TYPE_UNDEF: + default: + return LORAMAC_STATUS_PARAMETER_INVALID; + } + return LORAMAC_STATUS_OK; +} + +static void CalculateBackOff( uint8_t channel ) +{ + CalcBackOffParams_t calcBackOff; + + if( MacCtx.NvmCtx->NetworkActivation == ACTIVATION_TYPE_NONE ) + { + calcBackOff.Joined = false; + } + else + { + calcBackOff.Joined = true; + } + calcBackOff.DutyCycleEnabled = MacCtx.NvmCtx->DutyCycleOn; + calcBackOff.Channel = channel; + calcBackOff.ElapsedTime = TimerGetElapsedTime( MacCtx.NvmCtx->InitializationTime ); + calcBackOff.TxTimeOnAir = MacCtx.TxTimeOnAir; + calcBackOff.LastTxIsJoinRequest = false; + if( ( MacCtx.MacFlags.Bits.MlmeReq == 1 ) && ( LoRaMacConfirmQueueIsCmdActive( MLME_JOIN ) == true ) ) + { + calcBackOff.LastTxIsJoinRequest = true; + } + + // Update regional back-off + RegionCalcBackOff( MacCtx.NvmCtx->Region, &calcBackOff ); + + // Update aggregated time-off. This must be an assignment and no incremental + // update as we do only calculate the time-off based on the last transmission + MacCtx.NvmCtx->AggregatedTimeOff = ( MacCtx.TxTimeOnAir * MacCtx.NvmCtx->AggregatedDCycle - MacCtx.TxTimeOnAir ); +} + +static void RemoveMacCommands( LoRaMacRxSlot_t rxSlot, LoRaMacFrameCtrl_t fCtrl, Mcps_t request ) +{ + if( rxSlot == RX_SLOT_WIN_1 || rxSlot == RX_SLOT_WIN_2 ) + { + // Remove all sticky MAC commands answers since we can assume + // that they have been received by the server. + if( request == MCPS_CONFIRMED ) + { + if( fCtrl.Bits.Ack == 1 ) + { // For confirmed uplinks only if we have received an ACK. + LoRaMacCommandsRemoveStickyAnsCmds( ); + } + } + else + { + LoRaMacCommandsRemoveStickyAnsCmds( ); + } + } +} + + +static void ResetMacParameters( void ) +{ + MacCtx.NvmCtx->NetworkActivation = ACTIVATION_TYPE_NONE; + + // ADR counter + MacCtx.NvmCtx->AdrAckCounter = 0; + + MacCtx.ChannelsNbTransCounter = 0; + MacCtx.AckTimeoutRetries = 1; + MacCtx.AckTimeoutRetriesCounter = 1; + MacCtx.AckTimeoutRetry = false; + + MacCtx.NvmCtx->MaxDCycle = 0; + MacCtx.NvmCtx->AggregatedDCycle = 1; + + MacCtx.NvmCtx->MacParams.ChannelsTxPower = MacCtx.NvmCtx->MacParamsDefaults.ChannelsTxPower; + MacCtx.NvmCtx->MacParams.ChannelsDatarate = MacCtx.NvmCtx->MacParamsDefaults.ChannelsDatarate; + MacCtx.NvmCtx->MacParams.Rx1DrOffset = MacCtx.NvmCtx->MacParamsDefaults.Rx1DrOffset; + MacCtx.NvmCtx->MacParams.Rx2Channel = MacCtx.NvmCtx->MacParamsDefaults.Rx2Channel; + MacCtx.NvmCtx->MacParams.RxCChannel = MacCtx.NvmCtx->MacParamsDefaults.RxCChannel; + MacCtx.NvmCtx->MacParams.UplinkDwellTime = MacCtx.NvmCtx->MacParamsDefaults.UplinkDwellTime; + MacCtx.NvmCtx->MacParams.DownlinkDwellTime = MacCtx.NvmCtx->MacParamsDefaults.DownlinkDwellTime; + MacCtx.NvmCtx->MacParams.MaxEirp = MacCtx.NvmCtx->MacParamsDefaults.MaxEirp; + MacCtx.NvmCtx->MacParams.AntennaGain = MacCtx.NvmCtx->MacParamsDefaults.AntennaGain; + + MacCtx.NodeAckRequested = false; + MacCtx.NvmCtx->SrvAckRequested = false; + + // Reset to application defaults + InitDefaultsParams_t params; + params.Type = INIT_TYPE_RESTORE_DEFAULT_CHANNELS; + params.NvmCtx = NULL; + RegionInitDefaults( MacCtx.NvmCtx->Region, ¶ms ); + + // Initialize channel index. + MacCtx.Channel = 0; + MacCtx.NvmCtx->LastTxChannel = MacCtx.Channel; + + // Initialize Rx2 config parameters. + MacCtx.RxWindow2Config.Channel = MacCtx.Channel; + MacCtx.RxWindow2Config.Frequency = MacCtx.NvmCtx->MacParams.Rx2Channel.Frequency; + MacCtx.RxWindow2Config.DownlinkDwellTime = MacCtx.NvmCtx->MacParams.DownlinkDwellTime; + MacCtx.RxWindow2Config.RepeaterSupport = MacCtx.NvmCtx->RepeaterSupport; + MacCtx.RxWindow2Config.RxContinuous = false; + MacCtx.RxWindow2Config.RxSlot = RX_SLOT_WIN_2; +} + +/*! + * \brief Initializes and opens the reception window + * + * \param [IN] rxTimer Window timer to be topped. + * \param [IN] rxConfig Window parameters to be setup + */ +static void RxWindowSetup( TimerEvent_t* rxTimer, RxConfigParams_t* rxConfig ) +{ + TimerStop( rxTimer ); + + // Ensure the radio is Idle + Radio.Standby( ); + + if( RegionRxConfig( MacCtx.NvmCtx->Region, rxConfig, ( int8_t* )&MacCtx.McpsIndication.RxDatarate ) == true ) + { + Radio.Rx( MacCtx.NvmCtx->MacParams.MaxRxWindow ); + MacCtx.RxSlot = rxConfig->RxSlot; + } +} + +static void OpenContinuousRxCWindow( void ) +{ + MacCtx.RxWindowCConfig.RxSlot = RX_SLOT_WIN_CLASS_C; + // Setup continuous listening + MacCtx.RxWindowCConfig.RxContinuous = true; + + // At this point the Radio should be idle. + // Thus, there is no need to set the radio in standby mode. + if( RegionRxConfig( MacCtx.NvmCtx->Region, &MacCtx.RxWindowCConfig, ( int8_t* )&MacCtx.McpsIndication.RxDatarate ) == true ) + { + Radio.Rx( 0 ); // Continuous mode + MacCtx.RxSlot = MacCtx.RxWindowCConfig.RxSlot; + } +} + +LoRaMacStatus_t PrepareFrame( LoRaMacHeader_t* macHdr, LoRaMacFrameCtrl_t* fCtrl, uint8_t fPort, void* fBuffer, uint16_t fBufferSize ) +{ + MacCtx.PktBufferLen = 0; + MacCtx.NodeAckRequested = false; + uint32_t fCntUp = 0; + size_t macCmdsSize = 0; + uint8_t availableSize = 0; + + if( fBuffer == NULL ) + { + fBufferSize = 0; + } + + memcpy1( MacCtx.AppData, ( uint8_t* ) fBuffer, fBufferSize ); + MacCtx.AppDataSize = fBufferSize; + MacCtx.PktBuffer[0] = macHdr->Value; + + switch( macHdr->Bits.MType ) + { + case FRAME_TYPE_DATA_CONFIRMED_UP: + MacCtx.NodeAckRequested = true; + // Intentional fall through + case FRAME_TYPE_DATA_UNCONFIRMED_UP: + MacCtx.TxMsg.Type = LORAMAC_MSG_TYPE_DATA; + MacCtx.TxMsg.Message.Data.Buffer = MacCtx.PktBuffer; + MacCtx.TxMsg.Message.Data.BufSize = LORAMAC_PHY_MAXPAYLOAD; + MacCtx.TxMsg.Message.Data.MHDR.Value = macHdr->Value; + MacCtx.TxMsg.Message.Data.FPort = fPort; + MacCtx.TxMsg.Message.Data.FHDR.DevAddr = MacCtx.NvmCtx->DevAddr; + MacCtx.TxMsg.Message.Data.FHDR.FCtrl.Value = fCtrl->Value; + MacCtx.TxMsg.Message.Data.FRMPayloadSize = MacCtx.AppDataSize; + MacCtx.TxMsg.Message.Data.FRMPayload = MacCtx.AppData; + + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoGetFCntUp( &fCntUp ) ) + { + return LORAMAC_STATUS_FCNT_HANDLER_ERROR; + } + MacCtx.TxMsg.Message.Data.FHDR.FCnt = ( uint16_t )fCntUp; + + // Reset confirm parameters + MacCtx.McpsConfirm.NbRetries = 0; + MacCtx.McpsConfirm.AckReceived = false; + MacCtx.McpsConfirm.UpLinkCounter = fCntUp; + + // Handle the MAC commands if there are any available + if( LoRaMacCommandsGetSizeSerializedCmds( &macCmdsSize ) != LORAMAC_COMMANDS_SUCCESS ) + { + return LORAMAC_STATUS_MAC_COMMAD_ERROR; + } + + if( macCmdsSize > 0 ) + { + availableSize = GetMaxAppPayloadWithoutFOptsLength( MacCtx.NvmCtx->MacParams.ChannelsDatarate ); + + // There is application payload available and the MAC commands fit into FOpts field. + if( ( MacCtx.AppDataSize > 0 ) && ( macCmdsSize <= LORA_MAC_COMMAND_MAX_FOPTS_LENGTH ) ) + { + if( LoRaMacCommandsSerializeCmds( LORA_MAC_COMMAND_MAX_FOPTS_LENGTH, &macCmdsSize, MacCtx.TxMsg.Message.Data.FHDR.FOpts ) != LORAMAC_COMMANDS_SUCCESS ) + { + return LORAMAC_STATUS_MAC_COMMAD_ERROR; + } + fCtrl->Bits.FOptsLen = macCmdsSize; + // Update FCtrl field with new value of FOptionsLength + MacCtx.TxMsg.Message.Data.FHDR.FCtrl.Value = fCtrl->Value; + } + // There is application payload available but the MAC commands does NOT fit into FOpts field. + else if( ( MacCtx.AppDataSize > 0 ) && ( macCmdsSize > LORA_MAC_COMMAND_MAX_FOPTS_LENGTH ) ) + { + + if( LoRaMacCommandsSerializeCmds( availableSize, &macCmdsSize, MacCtx.NvmCtx->MacCommandsBuffer ) != LORAMAC_COMMANDS_SUCCESS ) + { + return LORAMAC_STATUS_MAC_COMMAD_ERROR; + } + return LORAMAC_STATUS_SKIPPED_APP_DATA; + } + // No application payload available therefore add all mac commands to the FRMPayload. + else + { + if( LoRaMacCommandsSerializeCmds( availableSize, &macCmdsSize, MacCtx.NvmCtx->MacCommandsBuffer ) != LORAMAC_COMMANDS_SUCCESS ) + { + return LORAMAC_STATUS_MAC_COMMAD_ERROR; + } + // Force FPort to be zero + MacCtx.TxMsg.Message.Data.FPort = 0; + + MacCtx.TxMsg.Message.Data.FRMPayload = MacCtx.NvmCtx->MacCommandsBuffer; + MacCtx.TxMsg.Message.Data.FRMPayloadSize = macCmdsSize; + } + } + + break; + case FRAME_TYPE_PROPRIETARY: + if( ( fBuffer != NULL ) && ( MacCtx.AppDataSize > 0 ) ) + { + memcpy1( MacCtx.PktBuffer + LORAMAC_MHDR_FIELD_SIZE, ( uint8_t* ) fBuffer, MacCtx.AppDataSize ); + MacCtx.PktBufferLen = LORAMAC_MHDR_FIELD_SIZE + MacCtx.AppDataSize; + } + break; + default: + return LORAMAC_STATUS_SERVICE_UNKNOWN; + } + + return LORAMAC_STATUS_OK; +} + +LoRaMacStatus_t SendFrameOnChannel( uint8_t channel ) +{ + TxConfigParams_t txConfig; + int8_t txPower = 0; + + txConfig.Channel = channel; + txConfig.Datarate = MacCtx.NvmCtx->MacParams.ChannelsDatarate; + txConfig.TxPower = MacCtx.NvmCtx->MacParams.ChannelsTxPower; + txConfig.MaxEirp = MacCtx.NvmCtx->MacParams.MaxEirp; + txConfig.AntennaGain = MacCtx.NvmCtx->MacParams.AntennaGain; + txConfig.PktLen = MacCtx.PktBufferLen; + + RegionTxConfig( MacCtx.NvmCtx->Region, &txConfig, &txPower, &MacCtx.TxTimeOnAir ); + + MacCtx.McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; + MacCtx.McpsConfirm.Datarate = MacCtx.NvmCtx->MacParams.ChannelsDatarate; + MacCtx.McpsConfirm.TxPower = txPower; + MacCtx.McpsConfirm.Channel = channel; + + // Store the time on air + MacCtx.McpsConfirm.TxTimeOnAir = MacCtx.TxTimeOnAir; + MacCtx.MlmeConfirm.TxTimeOnAir = MacCtx.TxTimeOnAir; + + if( LoRaMacClassBIsBeaconModeActive( ) == true ) + { + // Currently, the Time-On-Air can only be computed when the radio is configured with + // the TX configuration + TimerTime_t collisionTime = LoRaMacClassBIsUplinkCollision( MacCtx.TxTimeOnAir ); + + if( collisionTime > 0 ) + { + return LORAMAC_STATUS_BUSY_UPLINK_COLLISION; + } + } + + if( MacCtx.NvmCtx->DeviceClass == CLASS_B ) + { + // Stop slots for class b + LoRaMacClassBStopRxSlots( ); + } + + LoRaMacClassBHaltBeaconing( ); + + MacCtx.MacState |= LORAMAC_TX_RUNNING; + if( MacCtx.NodeAckRequested == false ) + { + MacCtx.ChannelsNbTransCounter++; + } + + // Send now + Radio.Send( MacCtx.PktBuffer, MacCtx.PktBufferLen ); + + return LORAMAC_STATUS_OK; +} + +LoRaMacStatus_t SetTxContinuousWave( uint16_t timeout ) +{ + ContinuousWaveParams_t continuousWave; + + continuousWave.Channel = MacCtx.Channel; + continuousWave.Datarate = MacCtx.NvmCtx->MacParams.ChannelsDatarate; + continuousWave.TxPower = MacCtx.NvmCtx->MacParams.ChannelsTxPower; + continuousWave.MaxEirp = MacCtx.NvmCtx->MacParams.MaxEirp; + continuousWave.AntennaGain = MacCtx.NvmCtx->MacParams.AntennaGain; + continuousWave.Timeout = timeout; + + RegionSetContinuousWave( MacCtx.NvmCtx->Region, &continuousWave ); + + MacCtx.MacState |= LORAMAC_TX_RUNNING; + + return LORAMAC_STATUS_OK; +} + +LoRaMacStatus_t SetTxContinuousWave1( uint16_t timeout, uint32_t frequency, uint8_t power ) +{ + Radio.SetTxContinuousWave( frequency, power, timeout ); + + MacCtx.MacState |= LORAMAC_TX_RUNNING; + + return LORAMAC_STATUS_OK; +} + +LoRaMacCtxs_t* GetCtxs( void ) +{ + Contexts.MacNvmCtx = &NvmMacCtx; + Contexts.MacNvmCtxSize = sizeof( NvmMacCtx ); + Contexts.CryptoNvmCtx = LoRaMacCryptoGetNvmCtx( &Contexts.CryptoNvmCtxSize ); + GetNvmCtxParams_t params ={ 0 }; + Contexts.RegionNvmCtx = RegionGetNvmCtx( MacCtx.NvmCtx->Region, ¶ms ); + Contexts.RegionNvmCtxSize = params.nvmCtxSize; + Contexts.SecureElementNvmCtx = SecureElementGetNvmCtx( &Contexts.SecureElementNvmCtxSize ); + Contexts.CommandsNvmCtx = LoRaMacCommandsGetNvmCtx( &Contexts.CommandsNvmCtxSize ); + Contexts.ClassBNvmCtx = LoRaMacClassBGetNvmCtx( &Contexts.ClassBNvmCtxSize ); + Contexts.ConfirmQueueNvmCtx = LoRaMacConfirmQueueGetNvmCtx( &Contexts.ConfirmQueueNvmCtxSize ); + return &Contexts; +} + +LoRaMacStatus_t RestoreCtxs( LoRaMacCtxs_t* contexts ) +{ + if( contexts == NULL ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + if( MacCtx.MacState != LORAMAC_STOPPED ) + { + return LORAMAC_STATUS_BUSY; + } + + if( contexts->MacNvmCtx != NULL ) + { + memcpy1( ( uint8_t* ) &NvmMacCtx, ( uint8_t* ) contexts->MacNvmCtx, contexts->MacNvmCtxSize ); + } + + InitDefaultsParams_t params; + params.Type = INIT_TYPE_RESTORE_CTX; + params.NvmCtx = contexts->RegionNvmCtx; + RegionInitDefaults( MacCtx.NvmCtx->Region, ¶ms ); + + if( SecureElementRestoreNvmCtx( contexts->SecureElementNvmCtx ) != SECURE_ELEMENT_SUCCESS ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + + if( LoRaMacCryptoRestoreNvmCtx( contexts->CryptoNvmCtx ) != LORAMAC_CRYPTO_SUCCESS ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + + if( LoRaMacCommandsRestoreNvmCtx( contexts->CommandsNvmCtx ) != LORAMAC_COMMANDS_SUCCESS ) + { + return LORAMAC_STATUS_MAC_COMMAD_ERROR; + } + + if( LoRaMacClassBRestoreNvmCtx( contexts->ClassBNvmCtx ) != true ) + { + return LORAMAC_STATUS_CLASS_B_ERROR; + } + + if( LoRaMacConfirmQueueRestoreNvmCtx( contexts->ConfirmQueueNvmCtx ) != true ) + { + return LORAMAC_STATUS_CONFIRM_QUEUE_ERROR; + } + + return LORAMAC_STATUS_OK; +} + +LoRaMacStatus_t DetermineFrameType( LoRaMacMessageData_t* macMsg, FType_t* fType ) +{ + if( ( macMsg == NULL ) || ( fType == NULL ) ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + /* The LoRaWAN specification allows several possible configurations how data up/down frames are built up. + * In sake of clearness the following naming is applied. Please keep in mind that this is + * implementation specific since there is no definition in the LoRaWAN specification included. + * + * X -> Field is available + * - -> Field is not available + * + * +-------+ +----------+------+-------+--------------+ + * | FType | | FOptsLen | Fopt | FPort | FRMPayload | + * +-------+ +----------+------+-------+--------------+ + * | A | | > 0 | X | > 0 | X | + * +-------+ +----------+------+-------+--------------+ + * | B | | >= 0 | X/- | - | - | + * +-------+ +----------+------+-------+--------------+ + * | C | | = 0 | - | = 0 | MAC commands | + * +-------+ +----------+------+-------+--------------+ + * | D | | = 0 | - | > 0 | X | + * +-------+ +----------+------+-------+--------------+ + */ + + if( ( macMsg->FHDR.FCtrl.Bits.FOptsLen > 0 ) && ( macMsg->FPort > 0 ) ) + { + *fType = FRAME_TYPE_A; + } + else if( macMsg->FRMPayloadSize == 0 ) + { + *fType = FRAME_TYPE_B; + } + else if( ( macMsg->FHDR.FCtrl.Bits.FOptsLen == 0 ) && ( macMsg->FPort == 0 ) ) + { + *fType = FRAME_TYPE_C; + } + else if( ( macMsg->FHDR.FCtrl.Bits.FOptsLen == 0 ) && ( macMsg->FPort > 0 ) ) + { + *fType = FRAME_TYPE_D; + } + else + { + // Should never happen. + return LORAMAC_STATUS_ERROR; + } + + return LORAMAC_STATUS_OK; +} + +static bool CheckRetransUnconfirmedUplink( void ) +{ + // Unconfirmed uplink, when all retransmissions are done. + if( MacCtx.ChannelsNbTransCounter >= + MacCtx.NvmCtx->MacParams.ChannelsNbTrans ) + { + return true; + } + else if( MacCtx.MacFlags.Bits.McpsInd == 1 ) + { + // For Class A stop in each case + if( MacCtx.NvmCtx->DeviceClass == CLASS_A ) + { + return true; + } + else + {// For Class B & C stop only if the frame was received in RX1 window + if( MacCtx.McpsIndication.RxSlot == RX_SLOT_WIN_1 ) + { + return true; + } + } + } + return false; +} + +static bool CheckRetransConfirmedUplink( void ) +{ + // Confirmed uplink, when all retransmissions ( tries to get a ack ) are done. + if( MacCtx.AckTimeoutRetriesCounter >= + MacCtx.AckTimeoutRetries ) + { + return true; + } + else if( MacCtx.MacFlags.Bits.McpsInd == 1 ) + { + if( MacCtx.McpsConfirm.AckReceived == true ) + { + return true; + } + } + return false; +} + +static bool StopRetransmission( void ) +{ + if( ( MacCtx.MacFlags.Bits.McpsInd == 0 ) || + ( ( MacCtx.McpsIndication.RxSlot != RX_SLOT_WIN_1 ) && + ( MacCtx.McpsIndication.RxSlot != RX_SLOT_WIN_2 ) ) ) + { // Maximum repetitions without downlink. Increase ADR Ack counter. + // Only process the case when the MAC did not receive a downlink. + if( MacCtx.NvmCtx->AdrCtrlOn == true ) + { + MacCtx.NvmCtx->AdrAckCounter++; + } + } + + MacCtx.ChannelsNbTransCounter = 0; + MacCtx.NodeAckRequested = false; + MacCtx.AckTimeoutRetry = false; + MacCtx.MacState &= ~LORAMAC_TX_RUNNING; + + return true; +} + +static void AckTimeoutRetriesProcess( void ) +{ + if( MacCtx.AckTimeoutRetriesCounter < MacCtx.AckTimeoutRetries ) + { + MacCtx.AckTimeoutRetriesCounter++; + if( ( MacCtx.AckTimeoutRetriesCounter % 2 ) == 1 ) + { + GetPhyParams_t getPhy; + PhyParam_t phyParam; + + getPhy.Attribute = PHY_NEXT_LOWER_TX_DR; + getPhy.UplinkDwellTime = MacCtx.NvmCtx->MacParams.UplinkDwellTime; + getPhy.Datarate = MacCtx.NvmCtx->MacParams.ChannelsDatarate; + phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy ); + + MacCtx.NvmCtx->MacParams.ChannelsDatarate = phyParam.Value; + #if (LORAWAN_DEFAULT_ACTIVE_REGION == LORAMAC_REGION_CN470ALI) + if(adr_on && MacCtx.NvmCtx->MacParams.ChannelsDatarate <2) + { + MacCtx.NvmCtx->MacParams.ChannelsDatarate =2; + } + #endif + } + } +} + +static void AckTimeoutRetriesFinalize( void ) +{ + if( MacCtx.McpsConfirm.AckReceived == false ) + { + InitDefaultsParams_t params; + params.Type = INIT_TYPE_RESTORE_DEFAULT_CHANNELS; + params.NvmCtx = Contexts.RegionNvmCtx; + RegionInitDefaults( MacCtx.NvmCtx->Region, ¶ms ); + + MacCtx.NodeAckRequested = false; + MacCtx.McpsConfirm.AckReceived = false; + } + MacCtx.McpsConfirm.NbRetries = MacCtx.AckTimeoutRetriesCounter; +} + +static void CallNvmCtxCallback( LoRaMacNvmCtxModule_t module ) +{ + if( ( MacCtx.MacCallbacks != NULL ) && ( MacCtx.MacCallbacks->NvmContextChange != NULL ) ) + { + MacCtx.MacCallbacks->NvmContextChange( module ); + } +} + +static void EventMacNvmCtxChanged( void ) +{ + CallNvmCtxCallback( LORAMAC_NVMCTXMODULE_MAC ); +} + +static void EventRegionNvmCtxChanged( void ) +{ + CallNvmCtxCallback( LORAMAC_NVMCTXMODULE_REGION ); +} + +static void EventCryptoNvmCtxChanged( void ) +{ + CallNvmCtxCallback( LORAMAC_NVMCTXMODULE_CRYPTO ); +} + +static void EventSecureElementNvmCtxChanged( void ) +{ + CallNvmCtxCallback( LORAMAC_NVMCTXMODULE_SECURE_ELEMENT ); +} + +static void EventCommandsNvmCtxChanged( void ) +{ + CallNvmCtxCallback( LORAMAC_NVMCTXMODULE_COMMANDS ); +} + +static void EventClassBNvmCtxChanged( void ) +{ + CallNvmCtxCallback( LORAMAC_NVMCTXMODULE_CLASS_B ); +} + +static void EventConfirmQueueNvmCtxChanged( void ) +{ + CallNvmCtxCallback( LORAMAC_NVMCTXMODULE_CONFIRM_QUEUE ); +} + +static uint8_t IsRequestPending( void ) +{ + if( ( MacCtx.MacFlags.Bits.MlmeReq == 1 ) || + ( MacCtx.MacFlags.Bits.McpsReq == 1 ) ) + { + return 1; + } + return 0; +} + + +LoRaMacStatus_t LoRaMacInitialization( LoRaMacPrimitives_t* primitives, LoRaMacCallback_t* callbacks, LoRaMacRegion_t region ) +{ + GetPhyParams_t getPhy; + PhyParam_t phyParam; + LoRaMacClassBCallback_t classBCallbacks; + LoRaMacClassBParams_t classBParams; + + if( ( primitives == NULL ) || + ( callbacks == NULL ) ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + if( ( primitives->MacMcpsConfirm == NULL ) || + ( primitives->MacMcpsIndication == NULL ) || + ( primitives->MacMlmeConfirm == NULL ) || + ( primitives->MacMlmeIndication == NULL ) ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + // Verify if the region is supported + if( RegionIsActive( region ) == false ) + { + return LORAMAC_STATUS_REGION_NOT_SUPPORTED; + } + + // Confirm queue reset + LoRaMacConfirmQueueInit( primitives, EventConfirmQueueNvmCtxChanged ); + + // Initialize the module context with zeros + memset1( ( uint8_t* ) &NvmMacCtx, 0x00, sizeof( LoRaMacNvmCtx_t ) ); + memset1( ( uint8_t* ) &MacCtx, 0x00, sizeof( LoRaMacCtx_t ) ); + MacCtx.NvmCtx = &NvmMacCtx; + + // Set non zero variables to its default value + MacCtx.AckTimeoutRetriesCounter = 1; + MacCtx.AckTimeoutRetries = 1; + MacCtx.NvmCtx->Region = region; + MacCtx.NvmCtx->DeviceClass = CLASS_A; + MacCtx.NvmCtx->RepeaterSupport = false; + + Version_t lrWanVersion; + lrWanVersion.Fields.Major = 1; + lrWanVersion.Fields.Minor = 0; + lrWanVersion.Fields.Revision = 3; + lrWanVersion.Fields.Rfu = 0; + MacCtx.NvmCtx->Version = lrWanVersion; + + // Reset to defaults + getPhy.Attribute = PHY_DUTY_CYCLE; + phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy ); + MacCtx.NvmCtx->DutyCycleOn = ( bool ) phyParam.Value; + + getPhy.Attribute = PHY_DEF_TX_POWER; + phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy ); + MacCtx.NvmCtx->MacParamsDefaults.ChannelsTxPower = phyParam.Value; + + getPhy.Attribute = PHY_DEF_TX_DR; + phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy ); + MacCtx.NvmCtx->MacParamsDefaults.ChannelsDatarate = phyParam.Value; + + getPhy.Attribute = PHY_MAX_RX_WINDOW; + phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy ); + MacCtx.NvmCtx->MacParamsDefaults.MaxRxWindow = phyParam.Value; + + getPhy.Attribute = PHY_RECEIVE_DELAY1; + phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy ); + MacCtx.NvmCtx->MacParamsDefaults.ReceiveDelay1 = phyParam.Value; + + getPhy.Attribute = PHY_RECEIVE_DELAY2; + phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy ); + MacCtx.NvmCtx->MacParamsDefaults.ReceiveDelay2 = phyParam.Value; + + getPhy.Attribute = PHY_JOIN_ACCEPT_DELAY1; + phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy ); + MacCtx.NvmCtx->MacParamsDefaults.JoinAcceptDelay1 = phyParam.Value; + + getPhy.Attribute = PHY_JOIN_ACCEPT_DELAY2; + phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy ); + MacCtx.NvmCtx->MacParamsDefaults.JoinAcceptDelay2 = phyParam.Value; + + getPhy.Attribute = PHY_DEF_DR1_OFFSET; + phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy ); + MacCtx.NvmCtx->MacParamsDefaults.Rx1DrOffset = phyParam.Value; + + getPhy.Attribute = PHY_DEF_RX2_FREQUENCY; + phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy ); + MacCtx.NvmCtx->MacParamsDefaults.Rx2Channel.Frequency = phyParam.Value; + MacCtx.NvmCtx->MacParamsDefaults.RxCChannel.Frequency = phyParam.Value; + + getPhy.Attribute = PHY_DEF_RX2_DR; + phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy ); + MacCtx.NvmCtx->MacParamsDefaults.Rx2Channel.Datarate = phyParam.Value; + MacCtx.NvmCtx->MacParamsDefaults.RxCChannel.Datarate = phyParam.Value; + + getPhy.Attribute = PHY_DEF_UPLINK_DWELL_TIME; + phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy ); + MacCtx.NvmCtx->MacParamsDefaults.UplinkDwellTime = phyParam.Value; + + getPhy.Attribute = PHY_DEF_DOWNLINK_DWELL_TIME; + phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy ); + MacCtx.NvmCtx->MacParamsDefaults.DownlinkDwellTime = phyParam.Value; + + getPhy.Attribute = PHY_DEF_MAX_EIRP; + phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy ); + MacCtx.NvmCtx->MacParamsDefaults.MaxEirp = phyParam.fValue; + + getPhy.Attribute = PHY_DEF_ANTENNA_GAIN; + phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy ); + MacCtx.NvmCtx->MacParamsDefaults.AntennaGain = phyParam.fValue; + + getPhy.Attribute = PHY_DEF_ADR_ACK_LIMIT; + phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy ); + MacCtx.AdrAckLimit = phyParam.Value; + + getPhy.Attribute = PHY_DEF_ADR_ACK_DELAY; + phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy ); + MacCtx.AdrAckDelay = phyParam.Value; + + // Init parameters which are not set in function ResetMacParameters + MacCtx.NvmCtx->MacParamsDefaults.ChannelsNbTrans = 1; + MacCtx.NvmCtx->MacParamsDefaults.SystemMaxRxError = 20; + MacCtx.NvmCtx->MacParamsDefaults.MinRxSymbols = 6; + + MacCtx.NvmCtx->MacParams.SystemMaxRxError = MacCtx.NvmCtx->MacParamsDefaults.SystemMaxRxError; + MacCtx.NvmCtx->MacParams.MinRxSymbols = MacCtx.NvmCtx->MacParamsDefaults.MinRxSymbols; + MacCtx.NvmCtx->MacParams.MaxRxWindow = MacCtx.NvmCtx->MacParamsDefaults.MaxRxWindow; + MacCtx.NvmCtx->MacParams.ReceiveDelay1 = MacCtx.NvmCtx->MacParamsDefaults.ReceiveDelay1; + MacCtx.NvmCtx->MacParams.ReceiveDelay2 = MacCtx.NvmCtx->MacParamsDefaults.ReceiveDelay2; + MacCtx.NvmCtx->MacParams.JoinAcceptDelay1 = MacCtx.NvmCtx->MacParamsDefaults.JoinAcceptDelay1; + MacCtx.NvmCtx->MacParams.JoinAcceptDelay2 = MacCtx.NvmCtx->MacParamsDefaults.JoinAcceptDelay2; + MacCtx.NvmCtx->MacParams.ChannelsNbTrans = MacCtx.NvmCtx->MacParamsDefaults.ChannelsNbTrans; + + ResetMacParameters( ); + + MacCtx.NvmCtx->PublicNetwork = true; + + MacCtx.MacPrimitives = primitives; + MacCtx.MacCallbacks = callbacks; + MacCtx.MacFlags.Value = 0; + MacCtx.MacState = LORAMAC_STOPPED; + + // Reset duty cycle times + MacCtx.NvmCtx->LastTxDoneTime = 0; + MacCtx.NvmCtx->AggregatedTimeOff = 0; + + // Initialize timers + TimerInit( &MacCtx.TxDelayedTimer, OnTxDelayedTimerEvent ); + TimerInit( &MacCtx.RxWindowTimer1, OnRxWindow1TimerEvent ); + TimerInit( &MacCtx.RxWindowTimer2, OnRxWindow2TimerEvent ); + TimerInit( &MacCtx.AckTimeoutTimer, OnAckTimeoutTimerEvent ); + + // Store the current initialization time + MacCtx.NvmCtx->InitializationTime = TimerGetCurrentTime( ); + + // Initialize Radio driver + MacCtx.RadioEvents.TxDone = OnRadioTxDone; + MacCtx.RadioEvents.RxDone = OnRadioRxDone; + MacCtx.RadioEvents.RxError = OnRadioRxError; + MacCtx.RadioEvents.TxTimeout = OnRadioTxTimeout; + MacCtx.RadioEvents.RxTimeout = OnRadioRxTimeout; + Radio.Init( &MacCtx.RadioEvents ); + + InitDefaultsParams_t params; + params.Type = INIT_TYPE_INIT; + params.NvmCtx = NULL; + RegionInitDefaults( MacCtx.NvmCtx->Region, ¶ms ); + + // Initialize the Secure Element driver + if( SecureElementInit( EventSecureElementNvmCtxChanged ) != SECURE_ELEMENT_SUCCESS ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + + // Initialize Crypto module + if( LoRaMacCryptoInit( EventCryptoNvmCtxChanged ) != LORAMAC_CRYPTO_SUCCESS ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + + // Initialize MAC commands module + if( LoRaMacCommandsInit( EventCommandsNvmCtxChanged ) != LORAMAC_COMMANDS_SUCCESS ) + { + return LORAMAC_STATUS_MAC_COMMAD_ERROR; + } + + // Set multicast downlink counter reference + if( LoRaMacCryptoSetMulticastReference( MacCtx.NvmCtx->MulticastChannelList ) != LORAMAC_CRYPTO_SUCCESS ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + + // Random seed initialization + srand1( Radio.Random( ) ); + + Radio.SetPublicNetwork( MacCtx.NvmCtx->PublicNetwork ); + Radio.Sleep( ); + + // Initialize class b + // Apply callback + classBCallbacks.GetTemperatureLevel = NULL; + classBCallbacks.MacProcessNotify = NULL; + if( callbacks != NULL ) + { + classBCallbacks.GetTemperatureLevel = callbacks->GetTemperatureLevel; + classBCallbacks.MacProcessNotify = callbacks->MacProcessNotify; + } + + // Must all be static. Don't use local references. + classBParams.MlmeIndication = &MacCtx.MlmeIndication; + classBParams.McpsIndication = &MacCtx.McpsIndication; + classBParams.MlmeConfirm = &MacCtx.MlmeConfirm; + classBParams.LoRaMacFlags = &MacCtx.MacFlags; + classBParams.LoRaMacDevAddr = &MacCtx.NvmCtx->DevAddr; + classBParams.LoRaMacRegion = &MacCtx.NvmCtx->Region; + classBParams.LoRaMacParams = &MacCtx.NvmCtx->MacParams; + classBParams.MulticastChannels = &MacCtx.NvmCtx->MulticastChannelList[0]; + + LoRaMacClassBInit( &classBParams, &classBCallbacks, &EventClassBNvmCtxChanged ); + + LoRaMacEnableRequests( LORAMAC_REQUEST_HANDLING_ON ); + + return LORAMAC_STATUS_OK; +} + +LoRaMacStatus_t LoRaMacStart( void ) +{ + MacCtx.MacState = LORAMAC_IDLE; + return LORAMAC_STATUS_OK; +} + +LoRaMacStatus_t LoRaMacStop( void ) +{ + if( LoRaMacIsBusy( ) == false ) + { + MacCtx.MacState = LORAMAC_STOPPED; + return LORAMAC_STATUS_OK; + } + else if( MacCtx.MacState == LORAMAC_STOPPED ) + { + return LORAMAC_STATUS_OK; + } + return LORAMAC_STATUS_BUSY; +} + +LoRaMacStatus_t LoRaMacQueryTxPossible( uint8_t size, LoRaMacTxInfo_t* txInfo ) +{ + CalcNextAdrParams_t adrNext; + uint32_t adrAckCounter = MacCtx.NvmCtx->AdrAckCounter; + int8_t datarate = MacCtx.NvmCtx->MacParamsDefaults.ChannelsDatarate; + int8_t txPower = MacCtx.NvmCtx->MacParamsDefaults.ChannelsTxPower; + size_t macCmdsSize = 0; + + if( txInfo == NULL ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + // Setup ADR request + adrNext.Version = MacCtx.NvmCtx->Version; + adrNext.UpdateChanMask = false; + adrNext.AdrEnabled = MacCtx.NvmCtx->AdrCtrlOn; + adrNext.AdrAckCounter = MacCtx.NvmCtx->AdrAckCounter; + adrNext.AdrAckLimit = MacCtx.AdrAckLimit; + adrNext.AdrAckDelay = MacCtx.AdrAckDelay; + adrNext.Datarate = MacCtx.NvmCtx->MacParams.ChannelsDatarate; + adrNext.TxPower = MacCtx.NvmCtx->MacParams.ChannelsTxPower; + adrNext.UplinkDwellTime = MacCtx.NvmCtx->MacParams.UplinkDwellTime; + adrNext.Region = MacCtx.NvmCtx->Region; + + // We call the function for information purposes only. We don't want to + // apply the datarate, the tx power and the ADR ack counter. + LoRaMacAdrCalcNext( &adrNext, &datarate, &txPower, &adrAckCounter ); + + txInfo->CurrentPossiblePayloadSize = GetMaxAppPayloadWithoutFOptsLength( datarate ); + + if( LoRaMacCommandsGetSizeSerializedCmds( &macCmdsSize ) != LORAMAC_COMMANDS_SUCCESS ) + { + return LORAMAC_STATUS_MAC_COMMAD_ERROR; + } + + // Verify if the MAC commands fit into the FOpts and into the maximum payload. + if( ( LORA_MAC_COMMAND_MAX_FOPTS_LENGTH >= macCmdsSize ) && ( txInfo->CurrentPossiblePayloadSize >= macCmdsSize ) ) + { + txInfo->MaxPossibleApplicationDataSize = txInfo->CurrentPossiblePayloadSize - macCmdsSize; + + // Verify if the application data together with MAC command fit into the maximum payload. + if( txInfo->CurrentPossiblePayloadSize >= ( macCmdsSize + size ) ) + { + return LORAMAC_STATUS_OK; + } + else + { + return LORAMAC_STATUS_LENGTH_ERROR; + } + } + else + { + txInfo->MaxPossibleApplicationDataSize = 0; + return LORAMAC_STATUS_LENGTH_ERROR; + } +} + +LoRaMacStatus_t LoRaMacMibGetRequestConfirm( MibRequestConfirm_t* mibGet ) +{ + LoRaMacStatus_t status = LORAMAC_STATUS_OK; + GetPhyParams_t getPhy; + PhyParam_t phyParam; + + if( mibGet == NULL ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + switch( mibGet->Type ) + { + case MIB_DEVICE_CLASS: + { + mibGet->Param.Class = MacCtx.NvmCtx->DeviceClass; + break; + } + case MIB_NETWORK_ACTIVATION: + { + mibGet->Param.NetworkActivation = MacCtx.NvmCtx->NetworkActivation; + break; + } + case MIB_DEV_EUI: + { + mibGet->Param.DevEui = SecureElementGetDevEui( ); + break; + } + case MIB_JOIN_EUI: + { + mibGet->Param.JoinEui = SecureElementGetJoinEui( ); + break; + } + case MIB_ADR: + { + mibGet->Param.AdrEnable = MacCtx.NvmCtx->AdrCtrlOn; + break; + } + case MIB_NET_ID: + { + mibGet->Param.NetID = MacCtx.NvmCtx->NetID; + break; + } + case MIB_DEV_ADDR: + { + mibGet->Param.DevAddr = MacCtx.NvmCtx->DevAddr; + break; + } + case MIB_PUBLIC_NETWORK: + { + mibGet->Param.EnablePublicNetwork = MacCtx.NvmCtx->PublicNetwork; + break; + } + case MIB_REPEATER_SUPPORT: + { + mibGet->Param.EnableRepeaterSupport = MacCtx.NvmCtx->RepeaterSupport; + break; + } + case MIB_CHANNELS: + { + getPhy.Attribute = PHY_CHANNELS; + phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy ); + + mibGet->Param.ChannelList = phyParam.Channels; + break; + } + case MIB_RX2_CHANNEL: + { + mibGet->Param.Rx2Channel = MacCtx.NvmCtx->MacParams.Rx2Channel; + break; + } + case MIB_RX2_DEFAULT_CHANNEL: + { + mibGet->Param.Rx2Channel = MacCtx.NvmCtx->MacParamsDefaults.Rx2Channel; + break; + } + case MIB_RXC_CHANNEL: + { + mibGet->Param.RxCChannel = MacCtx.NvmCtx->MacParams.RxCChannel; + break; + } + case MIB_RXC_DEFAULT_CHANNEL: + { + mibGet->Param.RxCChannel = MacCtx.NvmCtx->MacParamsDefaults.RxCChannel; + break; + } + case MIB_CHANNELS_DEFAULT_MASK: + { + getPhy.Attribute = PHY_CHANNELS_DEFAULT_MASK; + phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy ); + + mibGet->Param.ChannelsDefaultMask = phyParam.ChannelsMask; + break; + } + case MIB_CHANNELS_MASK: + { + getPhy.Attribute = PHY_CHANNELS_MASK; + phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy ); + + mibGet->Param.ChannelsMask = phyParam.ChannelsMask; + break; + } + case MIB_CHANNELS_NB_TRANS: + { + mibGet->Param.ChannelsNbTrans = MacCtx.NvmCtx->MacParams.ChannelsNbTrans; + break; + } + case MIB_MAX_RX_WINDOW_DURATION: + { + mibGet->Param.MaxRxWindow = MacCtx.NvmCtx->MacParams.MaxRxWindow; + break; + } + case MIB_RECEIVE_DELAY_1: + { + mibGet->Param.ReceiveDelay1 = MacCtx.NvmCtx->MacParams.ReceiveDelay1; + break; + } + case MIB_RECEIVE_DELAY_2: + { + mibGet->Param.ReceiveDelay2 = MacCtx.NvmCtx->MacParams.ReceiveDelay2; + break; + } + case MIB_JOIN_ACCEPT_DELAY_1: + { + mibGet->Param.JoinAcceptDelay1 = MacCtx.NvmCtx->MacParams.JoinAcceptDelay1; + break; + } + case MIB_JOIN_ACCEPT_DELAY_2: + { + mibGet->Param.JoinAcceptDelay2 = MacCtx.NvmCtx->MacParams.JoinAcceptDelay2; + break; + } + case MIB_CHANNELS_DEFAULT_DATARATE: + { + mibGet->Param.ChannelsDefaultDatarate = MacCtx.NvmCtx->MacParamsDefaults.ChannelsDatarate; + break; + } + case MIB_CHANNELS_DATARATE: + { + mibGet->Param.ChannelsDatarate = MacCtx.NvmCtx->MacParams.ChannelsDatarate; + break; + } + case MIB_CHANNELS_DEFAULT_TX_POWER: + { + mibGet->Param.ChannelsDefaultTxPower = MacCtx.NvmCtx->MacParamsDefaults.ChannelsTxPower; + break; + } + case MIB_CHANNELS_TX_POWER: + { + mibGet->Param.ChannelsTxPower = MacCtx.NvmCtx->MacParams.ChannelsTxPower; + break; + } + case MIB_SYSTEM_MAX_RX_ERROR: + { + mibGet->Param.SystemMaxRxError = MacCtx.NvmCtx->MacParams.SystemMaxRxError; + break; + } + case MIB_MIN_RX_SYMBOLS: + { + mibGet->Param.MinRxSymbols = MacCtx.NvmCtx->MacParams.MinRxSymbols; + break; + } + case MIB_ANTENNA_GAIN: + { + mibGet->Param.AntennaGain = MacCtx.NvmCtx->MacParams.AntennaGain; + break; + } + case MIB_NVM_CTXS: + { + mibGet->Param.Contexts = GetCtxs( ); + break; + } + case MIB_DEFAULT_ANTENNA_GAIN: + { + mibGet->Param.DefaultAntennaGain = MacCtx.NvmCtx->MacParamsDefaults.AntennaGain; + break; + } + default: + { + status = LoRaMacClassBMibGetRequestConfirm( mibGet ); + break; + } + } + return status; +} + +LoRaMacStatus_t LoRaMacMibSetRequestConfirm( MibRequestConfirm_t* mibSet ) +{ + LoRaMacStatus_t status = LORAMAC_STATUS_OK; + ChanMaskSetParams_t chanMaskSet; + VerifyParams_t verify; + + if( mibSet == NULL ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + if( ( MacCtx.MacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING ) + { + return LORAMAC_STATUS_BUSY; + } + + switch( mibSet->Type ) + { + case MIB_DEVICE_CLASS: + { + status = SwitchClass( mibSet->Param.Class ); + break; + } + case MIB_NETWORK_ACTIVATION: + { + if( mibSet->Param.NetworkActivation != ACTIVATION_TYPE_OTAA ) + { + MacCtx.NvmCtx->NetworkActivation = mibSet->Param.NetworkActivation; + } + else + { // Do not allow to set ACTIVATION_TYPE_OTAA since the MAC will set it automatically after a successful join process. + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_DEV_EUI: + { + if( SecureElementSetDevEui( mibSet->Param.DevEui ) != SECURE_ELEMENT_SUCCESS ) + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_JOIN_EUI: + { + if( SecureElementSetJoinEui( mibSet->Param.JoinEui ) != SECURE_ELEMENT_SUCCESS ) + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_ADR: + { + MacCtx.NvmCtx->AdrCtrlOn = mibSet->Param.AdrEnable; + break; + } + case MIB_NET_ID: + { + MacCtx.NvmCtx->NetID = mibSet->Param.NetID; + break; + } + case MIB_DEV_ADDR: + { + MacCtx.NvmCtx->DevAddr = mibSet->Param.DevAddr; + break; + } + case MIB_GEN_APP_KEY: + { + if( mibSet->Param.GenAppKey != NULL ) + { + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( GEN_APP_KEY, mibSet->Param.GenAppKey ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_APP_KEY: + { + if( mibSet->Param.AppKey != NULL ) + { + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( APP_KEY, mibSet->Param.AppKey ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_NWK_KEY: + { + if( mibSet->Param.NwkKey != NULL ) + { + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( NWK_KEY, mibSet->Param.NwkKey ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_J_S_INT_KEY: + { + if( mibSet->Param.JSIntKey != NULL ) + { + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( J_S_INT_KEY, mibSet->Param.JSIntKey ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_J_S_ENC_KEY: + { + if( mibSet->Param.JSEncKey != NULL ) + { + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( J_S_ENC_KEY, mibSet->Param.JSEncKey ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_F_NWK_S_INT_KEY: + { + if( mibSet->Param.FNwkSIntKey != NULL ) + { + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( F_NWK_S_INT_KEY, mibSet->Param.FNwkSIntKey ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_S_NWK_S_INT_KEY: + { + if( mibSet->Param.SNwkSIntKey != NULL ) + { + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( S_NWK_S_INT_KEY, mibSet->Param.SNwkSIntKey ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_NWK_S_ENC_KEY: + { + if( mibSet->Param.NwkSEncKey != NULL ) + { + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( NWK_S_ENC_KEY, mibSet->Param.NwkSEncKey ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_APP_S_KEY: + { + if( mibSet->Param.AppSKey != NULL ) + { + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( APP_S_KEY, mibSet->Param.AppSKey ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_MC_KE_KEY: + { + if( mibSet->Param.McKEKey != NULL ) + { + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_KE_KEY, mibSet->Param.McKEKey ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_MC_KEY_0: + { + if( mibSet->Param.McKey0 != NULL ) + { + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_KEY_0, mibSet->Param.McKey0 ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_MC_APP_S_KEY_0: + { + if( mibSet->Param.McAppSKey0 != NULL ) + { + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_APP_S_KEY_0, mibSet->Param.McAppSKey0 ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_MC_NWK_S_KEY_0: + { + if( mibSet->Param.McNwkSKey0 != NULL ) + { + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_NWK_S_KEY_0, mibSet->Param.McNwkSKey0 ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_MC_KEY_1: + { + if( mibSet->Param.McKey1 != NULL ) + { + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_KEY_1, mibSet->Param.McKey1 ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_MC_APP_S_KEY_1: + { + if( mibSet->Param.McAppSKey1 != NULL ) + { + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_APP_S_KEY_1, mibSet->Param.McAppSKey1 ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_MC_NWK_S_KEY_1: + { + if( mibSet->Param.McNwkSKey1 != NULL ) + { + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_NWK_S_KEY_1, mibSet->Param.McNwkSKey1 ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_MC_KEY_2: + { + if( mibSet->Param.McKey2 != NULL ) + { + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_KEY_2, mibSet->Param.McKey2 ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_MC_APP_S_KEY_2: + { + if( mibSet->Param.McAppSKey2 != NULL ) + { + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_APP_S_KEY_2, mibSet->Param.McAppSKey2 ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_MC_NWK_S_KEY_2: + { + if( mibSet->Param.McNwkSKey2 != NULL ) + { + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_NWK_S_KEY_2, mibSet->Param.McNwkSKey2 ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_MC_KEY_3: + { + if( mibSet->Param.McKey3 != NULL ) + { + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_KEY_3, mibSet->Param.McKey3 ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_MC_APP_S_KEY_3: + { + if( mibSet->Param.McAppSKey3 != NULL ) + { + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_APP_S_KEY_3, mibSet->Param.McAppSKey3 ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_MC_NWK_S_KEY_3: + { + if( mibSet->Param.McNwkSKey3 != NULL ) + { + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_NWK_S_KEY_3, mibSet->Param.McNwkSKey3 ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_PUBLIC_NETWORK: + { + MacCtx.NvmCtx->PublicNetwork = mibSet->Param.EnablePublicNetwork; + Radio.SetPublicNetwork( MacCtx.NvmCtx->PublicNetwork ); + break; + } + case MIB_REPEATER_SUPPORT: + { + MacCtx.NvmCtx->RepeaterSupport = mibSet->Param.EnableRepeaterSupport; + break; + } + case MIB_RX2_CHANNEL: + { + verify.DatarateParams.Datarate = mibSet->Param.Rx2Channel.Datarate; + verify.DatarateParams.DownlinkDwellTime = MacCtx.NvmCtx->MacParams.DownlinkDwellTime; + + if( RegionVerify( MacCtx.NvmCtx->Region, &verify, PHY_RX_DR ) == true ) + { + MacCtx.NvmCtx->MacParams.Rx2Channel = mibSet->Param.Rx2Channel; + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_RX2_DEFAULT_CHANNEL: + { + verify.DatarateParams.Datarate = mibSet->Param.Rx2Channel.Datarate; + verify.DatarateParams.DownlinkDwellTime = MacCtx.NvmCtx->MacParams.DownlinkDwellTime; + + if( RegionVerify( MacCtx.NvmCtx->Region, &verify, PHY_RX_DR ) == true ) + { + MacCtx.NvmCtx->MacParamsDefaults.Rx2Channel = mibSet->Param.Rx2DefaultChannel; + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_RXC_CHANNEL: + { + verify.DatarateParams.Datarate = mibSet->Param.RxCChannel.Datarate; + verify.DatarateParams.DownlinkDwellTime = MacCtx.NvmCtx->MacParams.DownlinkDwellTime; + + if( RegionVerify( MacCtx.NvmCtx->Region, &verify, PHY_RX_DR ) == true ) + { + MacCtx.NvmCtx->MacParams.RxCChannel = mibSet->Param.RxCChannel; + + if( ( MacCtx.NvmCtx->DeviceClass == CLASS_C ) && ( MacCtx.NvmCtx->NetworkActivation != ACTIVATION_TYPE_NONE ) ) + { + // We can only compute the RX window parameters directly, if we are already + // in class c mode and joined. We cannot setup an RX window in case of any other + // class type. + // Set the radio into sleep mode in case we are still in RX mode + Radio.Sleep( ); + // Compute RxC windows parameters + RegionComputeRxWindowParameters( MacCtx.NvmCtx->Region, + MacCtx.NvmCtx->MacParams.RxCChannel.Datarate, + MacCtx.NvmCtx->MacParams.MinRxSymbols, + MacCtx.NvmCtx->MacParams.SystemMaxRxError, + &MacCtx.RxWindowCConfig ); + OpenContinuousRxCWindow( ); + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_RXC_DEFAULT_CHANNEL: + { + verify.DatarateParams.Datarate = mibSet->Param.RxCChannel.Datarate; + verify.DatarateParams.DownlinkDwellTime = MacCtx.NvmCtx->MacParams.DownlinkDwellTime; + + if( RegionVerify( MacCtx.NvmCtx->Region, &verify, PHY_RX_DR ) == true ) + { + MacCtx.NvmCtx->MacParamsDefaults.RxCChannel = mibSet->Param.RxCDefaultChannel; + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_CHANNELS_DEFAULT_MASK: + { + chanMaskSet.ChannelsMaskIn = mibSet->Param.ChannelsDefaultMask; + chanMaskSet.ChannelsMaskType = CHANNELS_DEFAULT_MASK; + + if( RegionChanMaskSet( MacCtx.NvmCtx->Region, &chanMaskSet ) == false ) + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_CHANNELS_MASK: + { + chanMaskSet.ChannelsMaskIn = mibSet->Param.ChannelsMask; + chanMaskSet.ChannelsMaskType = CHANNELS_MASK; + + if( RegionChanMaskSet( MacCtx.NvmCtx->Region, &chanMaskSet ) == false ) + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_CHANNELS_NB_TRANS: + { + if( ( mibSet->Param.ChannelsNbTrans >= 1 ) && + ( mibSet->Param.ChannelsNbTrans <= 15 ) ) + { + MacCtx.NvmCtx->MacParams.ChannelsNbTrans = mibSet->Param.ChannelsNbTrans; + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_MAX_RX_WINDOW_DURATION: + { + MacCtx.NvmCtx->MacParams.MaxRxWindow = mibSet->Param.MaxRxWindow; + break; + } + case MIB_RECEIVE_DELAY_1: + { + MacCtx.NvmCtx->MacParams.ReceiveDelay1 = mibSet->Param.ReceiveDelay1; + break; + } + case MIB_RECEIVE_DELAY_2: + { + MacCtx.NvmCtx->MacParams.ReceiveDelay2 = mibSet->Param.ReceiveDelay2; + break; + } + case MIB_JOIN_ACCEPT_DELAY_1: + { + MacCtx.NvmCtx->MacParams.JoinAcceptDelay1 = mibSet->Param.JoinAcceptDelay1; + break; + } + case MIB_JOIN_ACCEPT_DELAY_2: + { + MacCtx.NvmCtx->MacParams.JoinAcceptDelay2 = mibSet->Param.JoinAcceptDelay2; + break; + } + case MIB_CHANNELS_DEFAULT_DATARATE: + { + verify.DatarateParams.Datarate = mibSet->Param.ChannelsDefaultDatarate; + + if( RegionVerify( MacCtx.NvmCtx->Region, &verify, PHY_DEF_TX_DR ) == true ) + { + MacCtx.NvmCtx->MacParamsDefaults.ChannelsDatarate = verify.DatarateParams.Datarate; + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_CHANNELS_DATARATE: + { + verify.DatarateParams.Datarate = mibSet->Param.ChannelsDatarate; + verify.DatarateParams.UplinkDwellTime = MacCtx.NvmCtx->MacParams.UplinkDwellTime; + + if( RegionVerify( MacCtx.NvmCtx->Region, &verify, PHY_TX_DR ) == true ) + { + MacCtx.NvmCtx->MacParams.ChannelsDatarate = verify.DatarateParams.Datarate; + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_CHANNELS_DEFAULT_TX_POWER: + { + verify.TxPower = mibSet->Param.ChannelsDefaultTxPower; + + if( RegionVerify( MacCtx.NvmCtx->Region, &verify, PHY_DEF_TX_POWER ) == true ) + { + MacCtx.NvmCtx->MacParamsDefaults.ChannelsTxPower = verify.TxPower; + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_CHANNELS_TX_POWER: + { + verify.TxPower = mibSet->Param.ChannelsTxPower; + + if( RegionVerify( MacCtx.NvmCtx->Region, &verify, PHY_TX_POWER ) == true ) + { + MacCtx.NvmCtx->MacParams.ChannelsTxPower = verify.TxPower; + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_SYSTEM_MAX_RX_ERROR: + { + MacCtx.NvmCtx->MacParams.SystemMaxRxError = MacCtx.NvmCtx->MacParamsDefaults.SystemMaxRxError = mibSet->Param.SystemMaxRxError; + break; + } + case MIB_MIN_RX_SYMBOLS: + { + MacCtx.NvmCtx->MacParams.MinRxSymbols = MacCtx.NvmCtx->MacParamsDefaults.MinRxSymbols = mibSet->Param.MinRxSymbols; + break; + } + case MIB_ANTENNA_GAIN: + { + MacCtx.NvmCtx->MacParams.AntennaGain = mibSet->Param.AntennaGain; + break; + } + case MIB_DEFAULT_ANTENNA_GAIN: + { + MacCtx.NvmCtx->MacParamsDefaults.AntennaGain = mibSet->Param.DefaultAntennaGain; + break; + } + case MIB_NVM_CTXS: + { + if( mibSet->Param.Contexts != 0 ) + { + status = RestoreCtxs( mibSet->Param.Contexts ); + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_ABP_LORAWAN_VERSION: + { + if( mibSet->Param.AbpLrWanVersion.Fields.Minor <= 1 ) + { + MacCtx.NvmCtx->Version = mibSet->Param.AbpLrWanVersion; + + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetLrWanVersion( mibSet->Param.AbpLrWanVersion ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + default: + { + status = LoRaMacMibClassBSetRequestConfirm( mibSet ); + break; + } + } + EventRegionNvmCtxChanged( ); + EventMacNvmCtxChanged( ); + return status; +} + +LoRaMacStatus_t LoRaMacChannelAdd( uint8_t id, ChannelParams_t params ) +{ + ChannelAddParams_t channelAdd; + + // Validate if the MAC is in a correct state + if( ( MacCtx.MacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING ) + { + if( ( MacCtx.MacState & LORAMAC_TX_CONFIG ) != LORAMAC_TX_CONFIG ) + { + return LORAMAC_STATUS_BUSY; + } + } + + channelAdd.NewChannel = ¶ms; + channelAdd.ChannelId = id; + + EventRegionNvmCtxChanged( ); + return RegionChannelAdd( MacCtx.NvmCtx->Region, &channelAdd ); +} + +LoRaMacStatus_t LoRaMacChannelRemove( uint8_t id ) +{ + ChannelRemoveParams_t channelRemove; + + if( ( MacCtx.MacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING ) + { + if( ( MacCtx.MacState & LORAMAC_TX_CONFIG ) != LORAMAC_TX_CONFIG ) + { + return LORAMAC_STATUS_BUSY; + } + } + + channelRemove.ChannelId = id; + + if( RegionChannelsRemove( MacCtx.NvmCtx->Region, &channelRemove ) == false ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + EventRegionNvmCtxChanged( ); + return LORAMAC_STATUS_OK; +} + +LoRaMacStatus_t LoRaMacMcChannelSetup( McChannelParams_t *channel ) +{ + if( ( MacCtx.MacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING ) + { + return LORAMAC_STATUS_BUSY; + } + + if( channel->GroupID >= LORAMAC_MAX_MC_CTX ) + { + return LORAMAC_STATUS_MC_GROUP_UNDEFINED; + } + + MacCtx.NvmCtx->MulticastChannelList[channel->GroupID].ChannelParams = *channel; + + const KeyIdentifier_t mcKeys[LORAMAC_MAX_MC_CTX] = { MC_KEY_0, MC_KEY_1, MC_KEY_2, MC_KEY_3 }; + if( LoRaMacCryptoSetKey( mcKeys[channel->GroupID], channel->McKeyE ) != LORAMAC_CRYPTO_SUCCESS ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + + if( LoRaMacCryptoDeriveMcSessionKeyPair( channel->GroupID, channel->Address ) != LORAMAC_CRYPTO_SUCCESS ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + + if( channel->Class == CLASS_B ) + { + // Calculate class b parameters + LoRaMacClassBSetMulticastPeriodicity( &MacCtx.NvmCtx->MulticastChannelList[channel->GroupID] ); + } + + // Reset multicast channel downlink counter to initial value. + *MacCtx.NvmCtx->MulticastChannelList[channel->GroupID].DownLinkCounter = FCNT_DOWN_INITAL_VALUE; + + EventMacNvmCtxChanged( ); + EventRegionNvmCtxChanged( ); + return LORAMAC_STATUS_OK; +} + +LoRaMacStatus_t LoRaMacMcChannelDelete( AddressIdentifier_t groupID ) +{ + if( ( MacCtx.MacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING ) + { + return LORAMAC_STATUS_BUSY; + } + + if( ( groupID >= LORAMAC_MAX_MC_CTX ) || + ( MacCtx.NvmCtx->MulticastChannelList[groupID].ChannelParams.IsEnabled == false ) ) + { + return LORAMAC_STATUS_MC_GROUP_UNDEFINED; + } + + McChannelParams_t channel; + + // Set all channel fields with 0 + memset1( ( uint8_t* )&channel, 0, sizeof( McChannelParams_t ) ); + + MacCtx.NvmCtx->MulticastChannelList[groupID].ChannelParams = channel; + + EventMacNvmCtxChanged( ); + EventRegionNvmCtxChanged( ); + return LORAMAC_STATUS_OK; +} + +uint8_t LoRaMacMcChannelGetGroupId( uint32_t mcAddress ) +{ + for( uint8_t i = 0; i < LORAMAC_MAX_MC_CTX; i++ ) + { + if( mcAddress == MacCtx.NvmCtx->MulticastChannelList[i].ChannelParams.Address ) + { + return i; + } + } + return 0xFF; +} + +LoRaMacStatus_t LoRaMacMcChannelSetupRxParams( AddressIdentifier_t groupID, McRxParams_t *rxParams, uint8_t *status ) +{ + *status = 0x1C + ( groupID & 0x03 ); + + if( ( MacCtx.MacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING ) + { + return LORAMAC_STATUS_BUSY; + } + + DeviceClass_t devClass = MacCtx.NvmCtx->MulticastChannelList[groupID].ChannelParams.Class; + if( ( devClass == CLASS_A ) || ( devClass > CLASS_C ) ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + if( ( groupID >= LORAMAC_MAX_MC_CTX ) || + ( MacCtx.NvmCtx->MulticastChannelList[groupID].ChannelParams.IsEnabled == false ) ) + { + return LORAMAC_STATUS_MC_GROUP_UNDEFINED; + } + *status &= 0x0F; // groupID OK + + VerifyParams_t verify; + // Check datarate + if( devClass == CLASS_B ) + { + verify.DatarateParams.Datarate = rxParams->ClassB.Datarate; + } + else + { + verify.DatarateParams.Datarate = rxParams->ClassC.Datarate; + } + verify.DatarateParams.DownlinkDwellTime = MacCtx.NvmCtx->MacParams.DownlinkDwellTime; + + if( RegionVerify( MacCtx.NvmCtx->Region, &verify, PHY_RX_DR ) == true ) + { + *status &= 0xFB; // datarate OK + } + + // Check frequency + if( devClass == CLASS_B ) + { + verify.Frequency = rxParams->ClassB.Frequency; + } + else + { + verify.Frequency = rxParams->ClassC.Frequency; + } + if( RegionVerify( MacCtx.NvmCtx->Region, &verify, PHY_FREQUENCY ) == true ) + { + *status &= 0xF7; // frequency OK + } + + if( *status == ( groupID & 0x03 ) ) + { + // Apply parameters + MacCtx.NvmCtx->MulticastChannelList[groupID].ChannelParams.RxParams = *rxParams; + } + + EventMacNvmCtxChanged( ); + EventRegionNvmCtxChanged( ); + return LORAMAC_STATUS_OK; +} + +LoRaMacStatus_t LoRaMacMlmeRequest( MlmeReq_t* mlmeRequest ) +{ + LoRaMacStatus_t status = LORAMAC_STATUS_SERVICE_UNKNOWN; + MlmeConfirmQueue_t queueElement; + uint8_t macCmdPayload[2] = { 0x00, 0x00 }; + + if( mlmeRequest == NULL ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + if( LoRaMacIsBusy( ) == true ) + { + return LORAMAC_STATUS_BUSY; + } + if( LoRaMacConfirmQueueIsFull( ) == true ) + { + return LORAMAC_STATUS_BUSY; + } + + if( LoRaMacConfirmQueueGetCnt( ) == 0 ) + { + memset1( ( uint8_t* ) &MacCtx.MlmeConfirm, 0, sizeof( MacCtx.MlmeConfirm ) ); + } + MacCtx.MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; + + MacCtx.MacFlags.Bits.MlmeReq = 1; + queueElement.Request = mlmeRequest->Type; + queueElement.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; + queueElement.RestrictCommonReadyToHandle = false; + + switch( mlmeRequest->Type ) + { + case MLME_JOIN: + { + if( ( MacCtx.MacState & LORAMAC_TX_DELAYED ) == LORAMAC_TX_DELAYED ) + { + return LORAMAC_STATUS_BUSY; + } + + ResetMacParameters( ); + + MacCtx.NvmCtx->MacParams.ChannelsDatarate = RegionAlternateDr( MacCtx.NvmCtx->Region, mlmeRequest->Req.Join.Datarate, ALTERNATE_DR ); + + queueElement.Status = LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL; + MW_LOG("Join DR = DR_%d\r\n" ,MacCtx.NvmCtx->MacParams.ChannelsDatarate); + status = SendReJoinReq( JOIN_REQ ); + + if( status != LORAMAC_STATUS_OK ) + { + // Revert back the previous datarate ( mainly used for US915 like regions ) + MacCtx.NvmCtx->MacParams.ChannelsDatarate = RegionAlternateDr( MacCtx.NvmCtx->Region, mlmeRequest->Req.Join.Datarate, ALTERNATE_DR_RESTORE ); + } + + + break; + } + case MLME_LINK_CHECK: + { + // LoRaMac will send this command piggy-pack + status = LORAMAC_STATUS_OK; + if( LoRaMacCommandsAddCmd( MOTE_MAC_LINK_CHECK_REQ, macCmdPayload, 0 ) != LORAMAC_COMMANDS_SUCCESS ) + { + status = LORAMAC_STATUS_MAC_COMMAD_ERROR; + } + break; + } + case MLME_TXCW: + { + status = SetTxContinuousWave( mlmeRequest->Req.TxCw.Timeout ); + break; + } + case MLME_TXCW_1: + { + + status = SetTxContinuousWave1( mlmeRequest->Req.TxCw.Timeout, mlmeRequest->Req.TxCw.Frequency, mlmeRequest->Req.TxCw.Power ); + break; + } + case MLME_DEVICE_TIME: + { + // LoRaMac will send this command piggy-pack + status = LORAMAC_STATUS_OK; + if( LoRaMacCommandsAddCmd( MOTE_MAC_DEVICE_TIME_REQ, macCmdPayload, 0 ) != LORAMAC_COMMANDS_SUCCESS ) + { + status = LORAMAC_STATUS_MAC_COMMAD_ERROR; + } + break; + } + case MLME_PING_SLOT_INFO: + { + if( MacCtx.NvmCtx->DeviceClass == CLASS_A ) + { + uint8_t value = mlmeRequest->Req.PingSlotInfo.PingSlot.Value; + + // LoRaMac will send this command piggy-pack + LoRaMacClassBSetPingSlotInfo( mlmeRequest->Req.PingSlotInfo.PingSlot.Fields.Periodicity ); + macCmdPayload[0] = value; + status = LORAMAC_STATUS_OK; + if( LoRaMacCommandsAddCmd( MOTE_MAC_PING_SLOT_INFO_REQ, macCmdPayload, 1 ) != LORAMAC_COMMANDS_SUCCESS ) + { + status = LORAMAC_STATUS_MAC_COMMAD_ERROR; + } + } + break; + } + case MLME_BEACON_TIMING: + { + // LoRaMac will send this command piggy-pack + status = LORAMAC_STATUS_OK; + if( LoRaMacCommandsAddCmd( MOTE_MAC_BEACON_TIMING_REQ, macCmdPayload, 0 ) != LORAMAC_COMMANDS_SUCCESS ) + { + status = LORAMAC_STATUS_MAC_COMMAD_ERROR; + } + break; + } + case MLME_BEACON_ACQUISITION: + { + // Apply the request + queueElement.RestrictCommonReadyToHandle = true; + + if( LoRaMacClassBIsAcquisitionInProgress( ) == false ) + { + // Start class B algorithm + LoRaMacClassBSetBeaconState( BEACON_STATE_ACQUISITION ); + LoRaMacClassBBeaconTimerEvent( NULL ); + + status = LORAMAC_STATUS_OK; + } + else + { + status = LORAMAC_STATUS_BUSY; + } + break; + } + default: + break; + } + + if( status != LORAMAC_STATUS_OK ) + { + if( LoRaMacConfirmQueueGetCnt( ) == 0 ) + { + MacCtx.NodeAckRequested = false; + MacCtx.MacFlags.Bits.MlmeReq = 0; + } + } + else + { + LoRaMacConfirmQueueAdd( &queueElement ); + EventMacNvmCtxChanged( ); + } + return status; +} + +LoRaMacStatus_t LoRaMacMcpsRequest( McpsReq_t* mcpsRequest ) +{ + GetPhyParams_t getPhy; + PhyParam_t phyParam; + LoRaMacStatus_t status = LORAMAC_STATUS_SERVICE_UNKNOWN; + LoRaMacHeader_t macHdr; + VerifyParams_t verify; + uint8_t fPort = 0; + void* fBuffer; + uint16_t fBufferSize; + int8_t datarate = DR_0; + bool readyToSend = false; + + if( mcpsRequest == NULL ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + if( LoRaMacIsBusy( ) == true ) + { + return LORAMAC_STATUS_BUSY; + } + + macHdr.Value = 0; + memset1( ( uint8_t* ) &MacCtx.McpsConfirm, 0, sizeof( MacCtx.McpsConfirm ) ); + MacCtx.McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; + + // AckTimeoutRetriesCounter must be reset every time a new request (unconfirmed or confirmed) is performed. + MacCtx.AckTimeoutRetriesCounter = 1; + + switch( mcpsRequest->Type ) + { + case MCPS_UNCONFIRMED: + { + readyToSend = true; + MacCtx.AckTimeoutRetries = 1; + + macHdr.Bits.MType = FRAME_TYPE_DATA_UNCONFIRMED_UP; + fPort = mcpsRequest->Req.Unconfirmed.fPort; + fBuffer = mcpsRequest->Req.Unconfirmed.fBuffer; + fBufferSize = mcpsRequest->Req.Unconfirmed.fBufferSize; + datarate = mcpsRequest->Req.Unconfirmed.Datarate; + break; + } + case MCPS_CONFIRMED: + { + readyToSend = true; + MacCtx.AckTimeoutRetries = MIN( mcpsRequest->Req.Confirmed.NbTrials, MAX_ACK_RETRIES ); + + macHdr.Bits.MType = FRAME_TYPE_DATA_CONFIRMED_UP; + fPort = mcpsRequest->Req.Confirmed.fPort; + fBuffer = mcpsRequest->Req.Confirmed.fBuffer; + fBufferSize = mcpsRequest->Req.Confirmed.fBufferSize; + datarate = mcpsRequest->Req.Confirmed.Datarate; + break; + } + case MCPS_PROPRIETARY: + { + readyToSend = true; + MacCtx.AckTimeoutRetries = 1; + + macHdr.Bits.MType = FRAME_TYPE_PROPRIETARY; + fBuffer = mcpsRequest->Req.Proprietary.fBuffer; + fBufferSize = mcpsRequest->Req.Proprietary.fBufferSize; + datarate = mcpsRequest->Req.Proprietary.Datarate; + break; + } + default: + break; + } + + // Get the minimum possible datarate + getPhy.Attribute = PHY_MIN_TX_DR; + getPhy.UplinkDwellTime = MacCtx.NvmCtx->MacParams.UplinkDwellTime; + phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy ); + // Apply the minimum possible datarate. + // Some regions have limitations for the minimum datarate. + datarate = MAX( datarate, ( int8_t )phyParam.Value ); + + if( readyToSend == true ) + { + if( MacCtx.NvmCtx->AdrCtrlOn == false ) + { + verify.DatarateParams.Datarate = datarate; + verify.DatarateParams.UplinkDwellTime = MacCtx.NvmCtx->MacParams.UplinkDwellTime; + + if( RegionVerify( MacCtx.NvmCtx->Region, &verify, PHY_TX_DR ) == true ) + { + MacCtx.NvmCtx->MacParams.ChannelsDatarate = verify.DatarateParams.Datarate; + } + else + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + } + + status = Send( &macHdr, fPort, fBuffer, fBufferSize ); + if( status == LORAMAC_STATUS_OK ) + { + MacCtx.McpsConfirm.McpsRequest = mcpsRequest->Type; + MacCtx.MacFlags.Bits.McpsReq = 1; + } + else + { + MacCtx.NodeAckRequested = false; + } + } + + EventMacNvmCtxChanged( ); + return status; +} + +void LoRaMacTestSetDutyCycleOn( bool enable ) +{ + VerifyParams_t verify; + + verify.DutyCycle = enable; + + if( RegionVerify( MacCtx.NvmCtx->Region, &verify, PHY_DUTY_CYCLE ) == true ) + { + MacCtx.NvmCtx->DutyCycleOn = enable; + } +} diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMac.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMac.h new file mode 100644 index 00000000..65d4291e --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMac.h @@ -0,0 +1,2707 @@ +/*! + * \file LoRaMac.h + * + * \brief LoRa MAC layer implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + * + * \defgroup LORAMAC LoRa MAC layer implementation + * This module specifies the API implementation of the LoRaMAC layer. + * This is a placeholder for a detailed description of the LoRaMac + * layer and the supported features. + * \{ + * + * \example classA/B-L072Z-LRWAN1/main.c + * LoRaWAN class A application example for the B-L072Z-LRWAN1. + * + * \example classB/B-L072Z-LRWAN1/main.c + * LoRaWAN class B application example for the B-L072Z-LRWAN1. + * + * \example classC/B-L072Z-LRWAN1/main.c + * LoRaWAN class C application example for the B-L072Z-LRWAN1. + * + * \example classA/NAMote72/main.c + * LoRaWAN class A application example for the NAMote72. + * + * \example classB/NAMote72/main.c + * LoRaWAN class B application example for the NAMote72. + * + * \example classC/NAMote72/main.c + * LoRaWAN class C application example for the NAMote72. + * + * \example classA/NucleoL073/main.c + * LoRaWAN class A application example for the NucleoL073. + * + * \example classB/NucleoL073/main.c + * LoRaWAN class B application example for the NucleoL073. + * + * \example classC/NucleoL073/main.c + * LoRaWAN class C application example for the NucleoL073. + * + * \example classA/NucleoL152/main.c + * LoRaWAN class A application example for the NucleoL152. + * + * \example classB/NucleoL152/main.c + * LoRaWAN class B application example for the NucleoL152. + * + * \example classC/NucleoL152/main.c + * LoRaWAN class C application example for the NucleoL152. + * + * \example classA/NucleoL476/main.c + * LoRaWAN class A application example for the NucleoL476. + * + * \example classB/NucleoL476/main.c + * LoRaWAN class B application example for the NucleoL476. + * + * \example classC/NucleoL476/main.c + * LoRaWAN class C application example for the NucleoL476. + * + * \example classA/SAML21/main.c + * LoRaWAN class A application example for the SAML21. + * + * \example classB/SAML21/main.c + * LoRaWAN class B application example for the SAML21. + * + * \example classC/SAML21/main.c + * LoRaWAN class C application example for the SAML21. + * + * \example classA/SKiM880B/main.c + * LoRaWAN class A application example for the SKiM880B. + * + * \example classB/SKiM880B/main.c + * LoRaWAN class B application example for the SKiM880B. + * + * \example classC/SKiM880B/main.c + * LoRaWAN class C application example for the SKiM880B. + * + * \example classA/SKiM881AXL/main.c + * LoRaWAN class A application example for the SKiM881AXL. + * + * \example classB/SKiM881AXL/main.c + * LoRaWAN class B application example for the SKiM881AXL. + * + * \example classC/SKiM881AXL/main.c + * LoRaWAN class C application example for the SKiM881AXL. + * + * \example classA/SKiM980A/main.c + * LoRaWAN class A application example for the SKiM980A. + * + * \example classB/SKiM980A/main.c + * LoRaWAN class B application example for the SKiM980A. + * + * \example classC/SKiM980A/main.c + * LoRaWAN class C application example for the SKiM980A. + * + */ +#ifndef __LORAMAC_H__ +#define __LORAMAC_H__ + +#include +#include +#include "LoRaMacTypes.h" +#include "lorawan_conf.h" + +/*! + * Maximum number of times the MAC layer tries to get an acknowledge. + */ +#define MAX_ACK_RETRIES 8 + +/*! + * Frame direction definition for up-link communications + */ +#define UP_LINK 0 + +/*! + * Frame direction definition for down-link communications + */ +#define DOWN_LINK 1 + +/*! + * Sets the length of the LoRaMAC footer field. + * Mainly indicates the MIC field length + */ +#define LORAMAC_MFR_LEN 4 + +/*! + * LoRaMac MLME-Confirm queue length + */ +#define LORA_MAC_MLME_CONFIRM_QUEUE_LEN 5 + +/*! + * FRMPayload overhead to be used when setting the Radio.SetMaxPayloadLength + * in RxWindowSetup function. + * Maximum PHYPayload = MaxPayloadOfDatarate/MaxPayloadOfDatarateRepeater + LORA_MAC_FRMPAYLOAD_OVERHEAD + */ +#define LORA_MAC_FRMPAYLOAD_OVERHEAD 13 // MHDR(1) + FHDR(7) + Port(1) + MIC(4) + +/*! + * Maximum number of multicast context + */ +#define LORAMAC_MAX_MC_CTX 4 + +/*! + * Start value for multicast keys enumeration + */ +#define LORAMAC_CRYPTO_MULTICAST_KEYS 127 + +/*! + * End-Device activation type + */ +typedef enum eActivationType +{ + /*! + * None + */ + ACTIVATION_TYPE_NONE = 0, + /*! + * Activation By Personalization (ACTIVATION_TYPE_ABP) + */ + ACTIVATION_TYPE_ABP = 1, + /*! + * Over-The-Air Activation (ACTIVATION_TYPE_OTAA) + */ + ACTIVATION_TYPE_OTAA = 2, +}ActivationType_t; + +/*! + * LoRaMAC channels parameters definition + */ +typedef union uDrRange +{ + /*! + * Byte-access to the bits + */ + int8_t Value; + /*! + * Structure to store the minimum and the maximum datarate + */ + struct sFields + { + /*! + * Minimum data rate + * + * LoRaWAN Regional Parameters V1.0.2rB + * + * The allowed ranges are region specific. Please refer to \ref DR_0 to \ref DR_15 for details. + */ + int8_t Min : 4; + /*! + * Maximum data rate + * + * LoRaWAN Regional Parameters V1.0.2rB + * + * The allowed ranges are region specific. Please refer to \ref DR_0 to \ref DR_15 for details. + */ + int8_t Max : 4; + }Fields; +}DrRange_t; + +/*! + * LoRaMAC channel definition + */ +typedef struct sChannelParams +{ + /*! + * Frequency in Hz + */ + uint32_t Frequency; + /*! + * Alternative frequency for RX window 1 + */ + uint32_t Rx1Frequency; + /*! + * Data rate definition + */ + DrRange_t DrRange; + /*! + * Band index + */ + uint8_t Band; +}ChannelParams_t; + +/*! + * LoRaMAC receive window channel parameters + */ +typedef struct sRxChannelParams +{ + /*! + * Frequency in Hz + */ + uint32_t Frequency; + /*! + * Data rate + * + * LoRaWAN Regional Parameters V1.0.2rB + * + * The allowed ranges are region specific. Please refer to \ref DR_0 to \ref DR_15 for details. + */ + uint8_t Datarate; +}RxChannelParams_t; + +/*! + * LoRaMAC receive window enumeration + */ +typedef enum eLoRaMacRxSlot +{ + /*! + * LoRaMAC receive window 1 + */ + RX_SLOT_WIN_1, + /*! + * LoRaMAC receive window 2 + */ + RX_SLOT_WIN_2, + /*! + * LoRaMAC receive window 2 for class c - continuous listening + */ + RX_SLOT_WIN_CLASS_C, + /*! + * LoRaMAC class c multicast downlink + */ + RX_SLOT_WIN_CLASS_C_MULTICAST, + /*! + * LoRaMAC class b ping slot window + */ + RX_SLOT_WIN_CLASS_B_PING_SLOT, + /*! + * LoRaMAC class b multicast slot window + */ + RX_SLOT_WIN_CLASS_B_MULTICAST_SLOT, + /*! + * LoRaMAC no active receive window + */ + RX_SLOT_NONE, +}LoRaMacRxSlot_t; + +/*! + * LoRaMAC structure to hold internal context pointers and its lengths + */ +typedef struct sLoRaMacCtxs +{ + /*! + * \brief Pointer to Mac context + */ + void* MacNvmCtx; + /*! + * \brief Size of Mac context + */ + size_t MacNvmCtxSize; + /*! + * \brief Pointer to region context + */ + void* RegionNvmCtx; + /*! + * \brief Size of region context + */ + size_t RegionNvmCtxSize; + /*! + * \brief Pointer to crypto module context + */ + void* CryptoNvmCtx; + /*! + * \brief Size of crypto module context + */ + size_t CryptoNvmCtxSize; + /*! + * \brief Pointer to secure element driver context + */ + void* SecureElementNvmCtx; + /*! + * \brief Size of secure element driver context + */ + size_t SecureElementNvmCtxSize; + /*! + * \brief Pointer to MAC commands module context + */ + void* CommandsNvmCtx; + /*! + * \brief Size of MAC commands module context + */ + size_t CommandsNvmCtxSize; + /*! + * \brief Pointer to Class B module context + */ + void* ClassBNvmCtx; + /*! + * \brief Size of MAC Class B module context + */ + size_t ClassBNvmCtxSize; + /*! + * \brief Pointer to MLME Confirm queue module context + */ + void* ConfirmQueueNvmCtx; + /*! + * \brief Size of MLME Confirm queue module context + */ + size_t ConfirmQueueNvmCtxSize; +}LoRaMacCtxs_t; + +/*! + * Global MAC layer parameters + */ +typedef struct sLoRaMacParams +{ + /*! + * Channels TX power + */ + int8_t ChannelsTxPower; + /*! + * Channels data rate + */ + int8_t ChannelsDatarate; + /*! + * System overall timing error in milliseconds. + * [-SystemMaxRxError : +SystemMaxRxError] + * Default: +/-10 ms + */ + uint32_t SystemMaxRxError; + /*! + * Minimum required number of symbols to detect an Rx frame + * Default: 6 symbols + */ + uint8_t MinRxSymbols; + /*! + * LoRaMac maximum time a reception window stays open + */ + uint32_t MaxRxWindow; + /*! + * Receive delay 1 + */ + uint32_t ReceiveDelay1; + /*! + * Receive delay 2 + */ + uint32_t ReceiveDelay2; + /*! + * Join accept delay 1 + */ + uint32_t JoinAcceptDelay1; + /*! + * Join accept delay 1 + */ + uint32_t JoinAcceptDelay2; + /*! + * Number of uplink messages repetitions [1:15] (unconfirmed messages only) + */ + uint8_t ChannelsNbTrans; + /*! + * Datarate offset between uplink and downlink on first window + */ + uint8_t Rx1DrOffset; + /*! + * LoRaMAC 2nd reception window settings + */ + RxChannelParams_t Rx2Channel; + /*! + * LoRaMAC continuous reception window settings + */ + RxChannelParams_t RxCChannel; + /*! + * Uplink dwell time configuration. 0: No limit, 1: 400ms + */ + uint8_t UplinkDwellTime; + /*! + * Downlink dwell time configuration. 0: No limit, 1: 400ms + */ + uint8_t DownlinkDwellTime; + /*! + * Maximum possible EIRP + */ + float MaxEirp; + /*! + * Antenna gain of the node + */ + float AntennaGain; + /*! + * Indicates if the node supports repeaters + */ + bool RepeaterSupport; +}LoRaMacParams_t; + +/*! + * LoRaMAC data structure for a PingSlotInfoReq \ref MLME_PING_SLOT_INFO + * + * LoRaWAN Specification + */ +typedef union uPingSlotInfo +{ + /*! + * Parameter for byte access + */ + uint8_t Value; + /*! + * Structure containing the parameters for the PingSlotInfoReq + */ + struct sInfoFields + { + /*! + * Periodicity = 0: ping slot every second + * Periodicity = 7: ping slot every 128 seconds + */ + uint8_t Periodicity : 3; + /*! + * RFU + */ + uint8_t RFU : 5; + }Fields; +}PingSlotInfo_t; + +/*! + * LoRaMAC data structure for the \ref MLME_BEACON MLME-Indication + * + * LoRaWAN Specification + */ +typedef struct sBeaconInfo +{ + /*! + * Timestamp in seconds since 00:00:00, Sunday 6th of January 1980 + * (start of the GPS epoch) modulo 2^32 + */ + SysTime_t Time; + /*! + * Frequency + */ + uint32_t Frequency; + /*! + * Datarate + */ + uint8_t Datarate; + /*! + * RSSI + */ + int16_t Rssi; + /*! + * SNR + */ + int8_t Snr; + /*! + * Data structure for the gateway specific part. The + * content of the values may differ for each gateway + */ + struct sGwSpecific + { + /*! + * Info descriptor - can differ for each gateway + */ + uint8_t InfoDesc; + /*! + * Info - can differ for each gateway + */ + uint8_t Info[6]; + }GwSpecific; +}BeaconInfo_t; + +/*! + * Enumeration containing the status of the operation of a MAC service + */ +typedef enum eLoRaMacEventInfoStatus +{ + /*! + * Service performed successfully + */ + LORAMAC_EVENT_INFO_STATUS_OK = 0, + /*! + * An error occurred during the execution of the service + */ + LORAMAC_EVENT_INFO_STATUS_ERROR, + /*! + * A Tx timeout occurred + */ + LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT, + /*! + * An Rx timeout occurred on receive window 1 + */ + LORAMAC_EVENT_INFO_STATUS_RX1_TIMEOUT, + /*! + * An Rx timeout occurred on receive window 2 + */ + LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT, + /*! + * An Rx error occurred on receive window 1 + */ + LORAMAC_EVENT_INFO_STATUS_RX1_ERROR, + /*! + * An Rx error occurred on receive window 2 + */ + LORAMAC_EVENT_INFO_STATUS_RX2_ERROR, + /*! + * An error occurred in the join procedure + */ + LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL, + /*! + * A frame with an invalid downlink counter was received. The + * downlink counter of the frame was equal to the local copy + * of the downlink counter of the node. + */ + LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED, + /*! + * The MAC could not retransmit a frame since the MAC decreased the datarate. The + * payload size is not applicable for the datarate. + */ + LORAMAC_EVENT_INFO_STATUS_TX_DR_PAYLOAD_SIZE_ERROR, + /*! + * The node has lost MAX_FCNT_GAP or more frames. + */ + LORAMAC_EVENT_INFO_STATUS_DOWNLINK_TOO_MANY_FRAMES_LOSS, + /*! + * An address error occurred + */ + LORAMAC_EVENT_INFO_STATUS_ADDRESS_FAIL, + /*! + * Message integrity check failure + */ + LORAMAC_EVENT_INFO_STATUS_MIC_FAIL, + /*! + * ToDo + */ + LORAMAC_EVENT_INFO_STATUS_MULTICAST_FAIL, + /*! + * ToDo + */ + LORAMAC_EVENT_INFO_STATUS_BEACON_LOCKED, + /*! + * ToDo + */ + LORAMAC_EVENT_INFO_STATUS_BEACON_LOST, + /*! + * ToDo + */ + LORAMAC_EVENT_INFO_STATUS_BEACON_NOT_FOUND, +}LoRaMacEventInfoStatus_t; + +/*! + * LoRaMac tx/rx operation state + */ +typedef union eLoRaMacFlags_t +{ + /*! + * Byte-access to the bits + */ + uint8_t Value; + /*! + * Structure containing single access to bits + */ + struct sMacFlagBits + { + /*! + * MCPS-Req pending + */ + uint8_t McpsReq : 1; + /*! + * MCPS-Ind pending + */ + uint8_t McpsInd : 1; + /*! + * MLME-Req pending + */ + uint8_t MlmeReq : 1; + /*! + * MLME-Ind pending + */ + uint8_t MlmeInd : 1; + /*! + * MLME-Ind to schedule an uplink pending + */ + uint8_t MlmeSchedUplinkInd : 1; + /*! + * MAC cycle done + */ + uint8_t MacDone : 1; + }Bits; +}LoRaMacFlags_t; + +/*! + * + * \brief LoRaMAC data services + * + * \details The following table list the primitives which are supported by the + * specific MAC data service: + * + * Name | Request | Indication | Response | Confirm + * --------------------- | :-----: | :--------: | :------: | :-----: + * \ref MCPS_UNCONFIRMED | YES | YES | NO | YES + * \ref MCPS_CONFIRMED | YES | YES | NO | YES + * \ref MCPS_MULTICAST | NO | YES | NO | NO + * \ref MCPS_PROPRIETARY | YES | YES | NO | YES + * + * The following table provides links to the function implementations of the + * related MCPS primitives: + * + * Primitive | Function + * ---------------- | :---------------------: + * MCPS-Request | \ref LoRaMacMlmeRequest + * MCPS-Confirm | MacMcpsConfirm in \ref LoRaMacPrimitives_t + * MCPS-Indication | MacMcpsIndication in \ref LoRaMacPrimitives_t + */ +typedef enum eMcps +{ + /*! + * Unconfirmed LoRaMAC frame + */ + MCPS_UNCONFIRMED, + /*! + * Confirmed LoRaMAC frame + */ + MCPS_CONFIRMED, + /*! + * Multicast LoRaMAC frame + */ + MCPS_MULTICAST, + /*! + * Proprietary frame + */ + MCPS_PROPRIETARY, +}Mcps_t; + +/*! + * LoRaMAC MCPS-Request for an unconfirmed frame + */ +typedef struct sMcpsReqUnconfirmed +{ + /*! + * Frame port field. Must be set if the payload is not empty. Use the + * application specific frame port values: [1...223] + * + * LoRaWAN Specification V1.0.2, chapter 4.3.2 + */ + uint8_t fPort; + /*! + * Pointer to the buffer of the frame payload + */ + void* fBuffer; + /*! + * Size of the frame payload + */ + uint16_t fBufferSize; + /*! + * Uplink datarate, if ADR is off + */ + int8_t Datarate; +}McpsReqUnconfirmed_t; + +/*! + * LoRaMAC MCPS-Request for a confirmed frame + */ +typedef struct sMcpsReqConfirmed +{ + /*! + * Frame port field. Must be set if the payload is not empty. Use the + * application specific frame port values: [1...223] + * + * LoRaWAN Specification V1.0.2, chapter 4.3.2 + */ + uint8_t fPort; + /*! + * Pointer to the buffer of the frame payload + */ + void* fBuffer; + /*! + * Size of the frame payload + */ + uint16_t fBufferSize; + /*! + * Uplink datarate, if ADR is off + */ + int8_t Datarate; + /*! + * Number of trials to transmit the frame, if the LoRaMAC layer did not + * receive an acknowledgment. The MAC performs a datarate adaptation, + * according to the LoRaWAN Specification V1.0.2, chapter 18.4, according + * to the following table: + * + * Transmission nb | Data Rate + * ----------------|----------- + * 1 (first) | DR + * 2 | DR + * 3 | max(DR-1,0) + * 4 | max(DR-1,0) + * 5 | max(DR-2,0) + * 6 | max(DR-2,0) + * 7 | max(DR-3,0) + * 8 | max(DR-3,0) + * + * Note, that if NbTrials is set to 1 or 2, the MAC will not decrease + * the datarate, in case the LoRaMAC layer did not receive an acknowledgment + */ + uint8_t NbTrials; +}McpsReqConfirmed_t; + +/*! + * LoRaMAC MCPS-Request for a proprietary frame + */ +typedef struct sMcpsReqProprietary +{ + /*! + * Pointer to the buffer of the frame payload + */ + void* fBuffer; + /*! + * Size of the frame payload + */ + uint16_t fBufferSize; + /*! + * Uplink datarate, if ADR is off + */ + int8_t Datarate; +}McpsReqProprietary_t; + +/*! + * LoRaMAC MCPS-Request structure + */ +typedef struct sMcpsReq +{ + /*! + * MCPS-Request type + */ + Mcps_t Type; + + /*! + * MCPS-Request parameters + */ + union uMcpsParam + { + /*! + * MCPS-Request parameters for an unconfirmed frame + */ + McpsReqUnconfirmed_t Unconfirmed; + /*! + * MCPS-Request parameters for a confirmed frame + */ + McpsReqConfirmed_t Confirmed; + /*! + * MCPS-Request parameters for a proprietary frame + */ + McpsReqProprietary_t Proprietary; + }Req; +}McpsReq_t; + +/*! + * LoRaMAC MCPS-Confirm + */ +typedef struct sMcpsConfirm +{ + /*! + * Holds the previously performed MCPS-Request + */ + Mcps_t McpsRequest; + /*! + * Status of the operation + */ + LoRaMacEventInfoStatus_t Status; + /*! + * Uplink datarate + */ + uint8_t Datarate; + /*! + * Transmission power + */ + int8_t TxPower; + /*! + * Set if an acknowledgement was received + */ + bool AckReceived; + /*! + * Provides the number of retransmissions + */ + uint8_t NbRetries; + /*! + * The transmission time on air of the frame + */ + TimerTime_t TxTimeOnAir; + /*! + * The uplink counter value related to the frame + */ + uint32_t UpLinkCounter; + /*! + * The uplink channel related to the frame + */ + uint32_t Channel; +}McpsConfirm_t; + +/*! + * LoRaMAC MCPS-Indication primitive + */ +typedef struct sMcpsIndication +{ + /*! + * MCPS-Indication type + */ + Mcps_t McpsIndication; + /*! + * Status of the operation + */ + LoRaMacEventInfoStatus_t Status; + /*! + * Multicast + */ + uint8_t Multicast; + /*! + * Application port + */ + uint8_t Port; + /*! + * Downlink datarate + */ + uint8_t RxDatarate; + /*! + * Frame pending status + */ + uint8_t FramePending; + /*! + * Pointer to the received data stream + */ + uint8_t* Buffer; + /*! + * Size of the received data stream + */ + uint8_t BufferSize; + /*! + * Indicates, if data is available + */ + bool RxData; + /*! + * Rssi of the received packet + */ + int16_t Rssi; + /*! + * Snr of the received packet + */ + int8_t Snr; + /*! + * Receive window + */ + LoRaMacRxSlot_t RxSlot; + /*! + * Set if an acknowledgement was received + */ + bool AckReceived; + /*! + * The downlink counter value for the received frame + */ + uint32_t DownLinkCounter; + /*! + * The device address of the frame + */ + uint32_t DevAddress; + /*! + * Set if a DeviceTimeAns MAC command was received. + */ + bool DeviceTimeAnsReceived; +}McpsIndication_t; + +/*! + * \brief LoRaMAC management services + * + * \details The following table list the primitives which are supported by the + * specific MAC management service: + * + * Name | Request | Indication | Response | Confirm + * ---------------------------- | :-----: | :--------: | :------: | :-----: + * \ref MLME_JOIN | YES | NO | NO | YES + * \ref MLME_LINK_CHECK | YES | NO | NO | YES + * \ref MLME_TXCW | YES | NO | NO | YES + * \ref MLME_SCHEDULE_UPLINK | NO | YES | NO | NO + * \ref MLME_DERIVE_MC_KE_KEY | YES | NO | NO | YES + * \ref MLME_DERIVE_MC_KEY_PAIR | YES | NO | NO | YES + * + * The following table provides links to the function implementations of the + * related MLME primitives. + * + * Primitive | Function + * ---------------- | :---------------------: + * MLME-Request | \ref LoRaMacMlmeRequest + * MLME-Confirm | MacMlmeConfirm in \ref LoRaMacPrimitives_t + * MLME-Indication | MacMlmeIndication in \ref LoRaMacPrimitives_t + */ +typedef enum eMlme +{ + /*! + * Initiates the Over-the-Air activation + * + * LoRaWAN Specification V1.0.2, chapter 6.2 + */ + MLME_JOIN, + /*! + * Initiates sending a ReJoin-request type 0 + * + * LoRaWAN Specification V1.1.0, chapter 6.2.4.1 + */ + MLME_REJOIN_0, + /*! + * Initiates sending a ReJoin-request type 1 + * + * LoRaWAN Specification V1.1.0, chapter 6.2.4.2 + */ + MLME_REJOIN_1, + /*! + * LinkCheckReq - Connectivity validation + * + * LoRaWAN Specification V1.0.2, chapter 5, table 4 + */ + MLME_LINK_CHECK, + /*! + * Sets Tx continuous wave mode + * + * LoRaWAN end-device certification + */ + MLME_TXCW, + /*! + * Sets Tx continuous wave mode (new LoRa-Alliance CC definition) + * + * LoRaWAN end-device certification + */ + MLME_TXCW_1, + /*! + * Indicates that the application shall perform an uplink as + * soon as possible. + */ + MLME_SCHEDULE_UPLINK, + /*! + * Derives the McKEKey from the AppKey or NwkKey. + */ + MLME_DERIVE_MC_KE_KEY, + /*! + * Derives a Multicast group key pair ( McAppSKey, McNwkSKey ) from McKey + */ + MLME_DERIVE_MC_KEY_PAIR, + /*! + * Initiates a DeviceTimeReq + * + * LoRaWAN end-device certification + */ + MLME_DEVICE_TIME, + /*! + * The MAC uses this MLME primitive to indicate a beacon reception + * status. + * + * LoRaWAN end-device certification + */ + MLME_BEACON, + /*! + * Initiate a beacon acquisition. The MAC will search for a beacon. + * It will search for XX_BEACON_INTERVAL milliseconds. + * + * LoRaWAN end-device certification + */ + MLME_BEACON_ACQUISITION, + /*! + * Initiates a PingSlotInfoReq + * + * LoRaWAN end-device certification + */ + MLME_PING_SLOT_INFO, + /*! + * Initiates a BeaconTimingReq + * + * LoRaWAN end-device certification + */ + MLME_BEACON_TIMING, + /*! + * Primitive which indicates that the beacon has been lost + * + * \remark The upper layer is required to switch the device class to ClassA + * + * LoRaWAN end-device certification + */ + MLME_BEACON_LOST, +}Mlme_t; + +/*! + * LoRaMAC MLME-Request for the join service + */ +typedef struct sMlmeReqJoin +{ + /*! + * Datarate used for join request. + */ + uint8_t Datarate; +}MlmeReqJoin_t; + +/*! + * LoRaMAC MLME-Request for Tx continuous wave mode + */ +typedef struct sMlmeReqTxCw +{ + /*! + * Time in seconds while the radio is kept in continuous wave mode + */ + uint16_t Timeout; + /*! + * RF frequency to set (Only used with new way) + */ + uint32_t Frequency; + /*! + * RF output power to set (Only used with new way) + */ + uint8_t Power; +}MlmeReqTxCw_t; + +/*! + * LoRaMAC MLME-Request for the ping slot info service + */ +typedef struct sMlmeReqPingSlotInfo +{ + PingSlotInfo_t PingSlot; +}MlmeReqPingSlotInfo_t; + +/*! + * LoRaMAC MLME-Request to derive the McKEKey from the AppKey or NwkKey + */ +typedef struct sMlmeReqDeriveMcKEKey +{ + /*! + * Key identifier of the root key to use to perform the derivation ( NwkKey or AppKey ) + */ + KeyIdentifier_t KeyID; + /*! + * Nonce value ( nonce <= 15) + */ + uint16_t Nonce; + /*! + * DevEUI Value + */ + uint8_t* DevEUI; +}MlmeReqDeriveMcKEKey_t; + +/*! + * LoRaMAC MLME-Request to derive a Multicast group key pair ( McAppSKey, McNwkSKey ) from McKey + */ +typedef struct sMlmeReqDeriveMcSessionKeyPair +{ + /*! + * Address identifier to select the multicast group + */ + AddressIdentifier_t GroupID; +}MlmeReqDeriveMcSessionKeyPair_t; + +/*! + * LoRaMAC MLME-Request structure + */ +typedef struct sMlmeReq +{ + /*! + * MLME-Request type + */ + Mlme_t Type; + + /*! + * MLME-Request parameters + */ + union uMlmeParam + { + /*! + * MLME-Request parameters for a join request + */ + MlmeReqJoin_t Join; + /*! + * MLME-Request parameters for Tx continuous mode request + */ + MlmeReqTxCw_t TxCw; + /*! + * MLME-Request parameters for a ping slot info request + */ + MlmeReqPingSlotInfo_t PingSlotInfo; + /*! + * MLME-Request to derive the McKEKey from the AppKey or NwkKey + */ + MlmeReqDeriveMcKEKey_t DeriveMcKEKey; + /*! + * MLME-Request to derive a Multicast group key pair ( McAppSKey, McNwkSKey ) from McKey + */ + MlmeReqDeriveMcSessionKeyPair_t DeriveMcSessionKeyPair; + }Req; +}MlmeReq_t; + +/*! + * LoRaMAC MLME-Confirm primitive + */ +typedef struct sMlmeConfirm +{ + /*! + * Holds the previously performed MLME-Request + */ + Mlme_t MlmeRequest; + /*! + * Status of the operation + */ + LoRaMacEventInfoStatus_t Status; + /*! + * The transmission time on air of the frame + */ + TimerTime_t TxTimeOnAir; + /*! + * Demodulation margin. Contains the link margin [dB] of the last + * successfully received LinkCheckReq + */ + uint8_t DemodMargin; + /*! + * Number of gateways which received the last LinkCheckReq + */ + uint8_t NbGateways; + /*! + * Provides the number of retransmissions + */ + uint8_t NbRetries; + /*! + * The delay which we have received through the + * BeaconTimingAns + */ + TimerTime_t BeaconTimingDelay; + /*! + * The channel of the next beacon + */ + uint8_t BeaconTimingChannel; +}MlmeConfirm_t; + +/*! + * LoRaMAC MLME-Indication primitive + */ +typedef struct sMlmeIndication +{ + /*! + * MLME-Indication type + */ + Mlme_t MlmeIndication; + /*! + * Status of the operation + */ + LoRaMacEventInfoStatus_t Status; + /*! + * Beacon information. Only valid for \ref MLME_BEACON, + * status \ref LORAMAC_EVENT_INFO_STATUS_BEACON_LOCKED + */ + BeaconInfo_t BeaconInfo; +}MlmeIndication_t; + +/*! + * LoRa Mac Information Base (MIB) + * + * The following table lists the MIB parameters and the related attributes: + * + * Attribute | Get | Set + * ----------------------------------------------| :-: | :-: + * \ref MIB_DEVICE_CLASS | YES | YES + * \ref MIB_NETWORK_ACTIVATION | YES | YES + * \ref MIB_DEV_EUI | YES | YES + * \ref MIB_JOIN_EUI | YES | YES + * \ref MIB_ADR | YES | YES + * \ref MIB_NET_ID | YES | YES + * \ref MIB_DEV_ADDR | YES | YES + * \ref MIB_GEN_APP_KEY | NO | YES + * \ref MIB_APP_KEY | NO | YES + * \ref MIB_NWK_KEY | NO | YES + * \ref MIB_J_S_INT_KEY | NO | YES + * \ref MIB_J_S_ENC_KEY | NO | YES + * \ref MIB_F_NWK_S_INT_KEY | NO | YES + * \ref MIB_S_NWK_S_INT_KEY | NO | YES + * \ref MIB_NWK_S_ENC_KEY | NO | YES + * \ref MIB_APP_S_KEY | NO | YES + * \ref MIB_MC_KE_KEY | NO | YES + * \ref MIB_MC_KEY_0 | NO | YES + * \ref MIB_MC_APP_S_KEY_0 | NO | YES + * \ref MIB_MC_NWK_S_KEY_0 | NO | YES + * \ref MIB_MC_KEY_1 | NO | YES + * \ref MIB_MC_APP_S_KEY_1 | NO | YES + * \ref MIB_MC_NWK_S_KEY_1 | NO | YES + * \ref MIB_MC_KEY_2 | NO | YES + * \ref MIB_MC_APP_S_KEY_2 | NO | YES + * \ref MIB_MC_NWK_S_KEY_2 | NO | YES + * \ref MIB_MC_KEY_3 | NO | YES + * \ref MIB_MC_APP_S_KEY_3 | NO | YES + * \ref MIB_MC_NWK_S_KEY_3 | NO | YES + * \ref MIB_PUBLIC_NETWORK | YES | YES + * \ref MIB_REPEATER_SUPPORT | YES | YES + * \ref MIB_CHANNELS | YES | NO + * \ref MIB_RX2_CHANNEL | YES | YES + * \ref MIB_RX2_DFAULT_CHANNEL | YES | YES + * \ref MIB_RXC_CHANNEL | YES | YES + * \ref MIB_RXC_DFAULT_CHANNEL | YES | YES + * \ref MIB_CHANNELS_MASK | YES | YES + * \ref MIB_CHANNELS_DEFAULT_MASK | YES | YES + * \ref MIB_CHANNELS_NB_TRANS | YES | YES + * \ref MIB_MAX_RX_WINDOW_DURATION | YES | YES + * \ref MIB_RECEIVE_DELAY_1 | YES | YES + * \ref MIB_RECEIVE_DELAY_2 | YES | YES + * \ref MIB_JOIN_ACCEPT_DELAY_1 | YES | YES + * \ref MIB_JOIN_ACCEPT_DELAY_2 | YES | YES + * \ref MIB_CHANNELS_DATARATE | YES | YES + * \ref MIB_CHANNELS_DEFAULT_DATARATE | YES | YES + * \ref MIB_CHANNELS_TX_POWER | YES | YES + * \ref MIB_CHANNELS_DEFAULT_TX_POWER | YES | YES + * \ref MIB_SYSTEM_MAX_RX_ERROR | YES | YES + * \ref MIB_MIN_RX_SYMBOLS | YES | YES + * \ref MIB_BEACON_INTERVAL | YES | YES + * \ref MIB_BEACON_RESERVED | YES | YES + * \ref MIB_BEACON_GUARD | YES | YES + * \ref MIB_BEACON_WINDOW | YES | YES + * \ref MIB_BEACON_WINDOW_SLOTS | YES | YES + * \ref MIB_PING_SLOT_WINDOW | YES | YES + * \ref MIB_BEACON_SYMBOL_TO_DEFAULT | YES | YES + * \ref MIB_BEACON_SYMBOL_TO_EXPANSION_MAX | YES | YES + * \ref MIB_PING_SLOT_SYMBOL_TO_EXPANSION_MAX | YES | YES + * \ref MIB_BEACON_SYMBOL_TO_EXPANSION_FACTOR | YES | YES + * \ref MIB_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR | YES | YES + * \ref MIB_MAX_BEACON_LESS_PERIOD | YES | YES + * \ref MIB_ANTENNA_GAIN | YES | YES + * \ref MIB_DEFAULT_ANTENNA_GAIN | YES | YES + * \ref MIB_NVM_CTXS | YES | YES + * \ref MIB_ABP_LORAWAN_VERSION | YES | YES + * + * The following table provides links to the function implementations of the + * related MIB primitives: + * + * Primitive | Function + * ---------------- | :---------------------: + * MIB-Set | \ref LoRaMacMibSetRequestConfirm + * MIB-Get | \ref LoRaMacMibGetRequestConfirm + */ +typedef enum eMib +{ + /*! + * LoRaWAN device class + * + * LoRaWAN Specification V1.0.2 + */ + MIB_DEVICE_CLASS, + /*! + * LoRaWAN Network End-Device Activation + * + * LoRaWAN Specification V1.0.2 + */ + MIB_NETWORK_ACTIVATION, + /*! + * LoRaWAN device EUI + * + * LoRaWAN Specification V1.0.2 + */ + MIB_DEV_EUI, + /*! + * LoRaWAN join EUI + * + * LoRaWAN Specification V1.0.2 + */ + MIB_JOIN_EUI, + /*! + * Adaptive data rate + * + * LoRaWAN Specification V1.0.2, chapter 4.3.1.1 + * + * [true: ADR enabled, false: ADR disabled] + */ + MIB_ADR, + /*! + * Network identifier + * + * LoRaWAN Specification V1.0.2, chapter 6.1.1 + */ + MIB_NET_ID, + /*! + * End-device address + * + * LoRaWAN Specification V1.0.2, chapter 6.1.1 + */ + MIB_DEV_ADDR, + /*! + * Application root key - 1.0.x devices only. + * + * LoRaWAN Remote Multicast Setup v1.0.0 Specification, chapter 4.3 + */ + MIB_GEN_APP_KEY, + /*! + * Application root key + * + * LoRaWAN Specification V1.1.0, chapter 6.1.1.3 + */ + MIB_APP_KEY, + /*! + * Network root key + * + * LoRaWAN Specification V1.1.0, chapter 6.1.1.3 + */ + MIB_NWK_KEY, + /*! + * Join session integrity key + * + * LoRaWAN Specification V1.1.0, chapter 6.1.1.4 + */ + MIB_J_S_INT_KEY, + /*! + * Join session encryption key + * + * LoRaWAN Specification V1.1.0, chapter 6.1.1.4 + */ + MIB_J_S_ENC_KEY, + /*! + * Forwarding Network session integrity key + * + * LoRaWAN Specification V1.1.0, chapter 6.1.2.2 + */ + MIB_F_NWK_S_INT_KEY, + /*! + * Serving Network session integrity key + * + * LoRaWAN Specification V1.1.0, chapter 6.1.2.3 + */ + MIB_S_NWK_S_INT_KEY, + /*! + * Network session encryption key + * + * LoRaWAN Specification V1.1.0, chapter 6.1.2.4 + */ + MIB_NWK_S_ENC_KEY, + /*! + * Application session key + * + * LoRaWAN Specification V1.1.0, chapter 6.1.1.3 + */ + MIB_APP_S_KEY, + /*! + * Multicast key encryption key + * + * LoRaWAN - Secure element specification v1 + */ + MIB_MC_KE_KEY, + /*! + * Multicast root key index 0 + * + * LoRaWAN - Secure element specification v1 + */ + MIB_MC_KEY_0, + /*! + * Multicast Application session key index 0 + * + * LoRaWAN - Secure element specification v1 + */ + MIB_MC_APP_S_KEY_0, + /*! + * Multicast Network session key index 0 + * + * LoRaWAN - Secure element specification v1 + */ + MIB_MC_NWK_S_KEY_0, + /*! + * Multicast root key index 1 + * + * LoRaWAN - Secure element specification v1 + */ + MIB_MC_KEY_1, + /*! + * Multicast Application session key index 1 + * + * LoRaWAN - Secure element specification v1 + */ + MIB_MC_APP_S_KEY_1, + /*! + * Multicast Network session key index 1 + * + * LoRaWAN - Secure element specification v1 + */ + MIB_MC_NWK_S_KEY_1, + /*! + * Multicast root key index 2 + * + * LoRaWAN - Secure element specification v1 + */ + MIB_MC_KEY_2, + /*! + * Multicast Application session key index 2 + * + * LoRaWAN - Secure element specification v1 + */ + MIB_MC_APP_S_KEY_2, + /*! + * Multicast Network session key index 2 + * + * LoRaWAN - Secure element specification v1 + */ + MIB_MC_NWK_S_KEY_2, + /*! + * Multicast root key index 3 + * + * LoRaWAN - Secure element specification v1 + */ + MIB_MC_KEY_3, + /*! + * Multicast Application session key index 3 + * + * LoRaWAN - Secure element specification v1 + */ + MIB_MC_APP_S_KEY_3, + /*! + * Multicast Network session key index 3 + * + * LoRaWAN - Secure element specification v1 + */ + MIB_MC_NWK_S_KEY_3, + /*! + * Set the network type to public or private + * + * LoRaWAN Regional Parameters V1.0.2rB + * + * [true: public network, false: private network] + */ + MIB_PUBLIC_NETWORK, + /*! + * Support the operation with repeaters + * + * LoRaWAN Regional Parameters V1.0.2rB + * + * [true: repeater support enabled, false: repeater support disabled] + */ + MIB_REPEATER_SUPPORT, + /*! + * Communication channels. A get request will return a + * pointer which references the first entry of the channel list. The + * list is of size LORA_MAX_NB_CHANNELS + * + * LoRaWAN Regional Parameters V1.0.2rB + */ + MIB_CHANNELS, + /*! + * Set receive window 2 channel + * + * LoRaWAN Specification V1.0.2, chapter 3.3.1 + */ + MIB_RX2_CHANNEL, + /*! + * Set receive window 2 channel + * + * LoRaWAN Specification V1.0.2, chapter 3.3.2 + */ + MIB_RX2_DEFAULT_CHANNEL, + /*! + * Set receive window C channel + * + * LoRaWAN Specification V1.0.2, chapter 3.3.1 + */ + MIB_RXC_CHANNEL, + /*! + * Set receive window C channel + * + * LoRaWAN Specification V1.0.2, chapter 3.3.2 + */ + MIB_RXC_DEFAULT_CHANNEL, + /*! + * LoRaWAN channels mask + * + * LoRaWAN Regional Parameters V1.0.2rB + */ + MIB_CHANNELS_MASK, + /*! + * LoRaWAN default channels mask + * + * LoRaWAN Regional Parameters V1.0.2rB + */ + MIB_CHANNELS_DEFAULT_MASK, + /*! + * Set the number of repetitions on a channel + * + * LoRaWAN Specification V1.0.2, chapter 5.2 + */ + MIB_CHANNELS_NB_TRANS, + /*! + * Maximum receive window duration in [ms] + * + * LoRaWAN Specification V1.0.2, chapter 3.3.3 + */ + MIB_MAX_RX_WINDOW_DURATION, + /*! + * Receive delay 1 in [ms] + * + * LoRaWAN Regional Parameters V1.0.2rB + */ + MIB_RECEIVE_DELAY_1, + /*! + * Receive delay 2 in [ms] + * + * LoRaWAN Regional Parameters V1.0.2rB + */ + MIB_RECEIVE_DELAY_2, + /*! + * Join accept delay 1 in [ms] + * + * LoRaWAN Regional Parameters V1.0.2rB + */ + MIB_JOIN_ACCEPT_DELAY_1, + /*! + * Join accept delay 2 in [ms] + * + * LoRaWAN Regional Parameters V1.0.2rB + */ + MIB_JOIN_ACCEPT_DELAY_2, + /*! + * Default Data rate of a channel + * + * LoRaWAN Regional Parameters V1.0.2rB + * + * The allowed ranges are region specific. Please refer to \ref DR_0 to \ref DR_15 for details. + */ + MIB_CHANNELS_DEFAULT_DATARATE, + /*! + * Data rate of a channel + * + * LoRaWAN Regional Parameters V1.0.2rB + * + * The allowed ranges are region specific. Please refer to \ref DR_0 to \ref DR_15 for details. + */ + MIB_CHANNELS_DATARATE, + /*! + * Transmission power of a channel + * + * LoRaWAN Regional Parameters V1.0.2rB + * + * The allowed ranges are region specific. Please refer to \ref TX_POWER_0 to \ref TX_POWER_15 for details. + */ + MIB_CHANNELS_TX_POWER, + /*! + * Transmission power of a channel + * + * LoRaWAN Regional Parameters V1.0.2rB + * + * The allowed ranges are region specific. Please refer to \ref TX_POWER_0 to \ref TX_POWER_15 for details. + */ + MIB_CHANNELS_DEFAULT_TX_POWER, + /*! + * System overall timing error in milliseconds. + * [-SystemMaxRxError : +SystemMaxRxError] + * Default: +/-10 ms + */ + MIB_SYSTEM_MAX_RX_ERROR, + /*! + * Minimum required number of symbols to detect an Rx frame + * Default: 6 symbols + */ + MIB_MIN_RX_SYMBOLS, + /*! + * Antenna gain of the node. Default value is region specific. + * The antenna gain is used to calculate the TX power of the node. + * The formula is: + * radioTxPower = ( int8_t )floor( maxEirp - antennaGain ) + * + * \remark The antenna gain value is referenced to the isotropic antenna. + * The value is in dBi. + * MIB_ANTENNA_GAIN[dBi] = measuredAntennaGain[dBd] + 2.15 + */ + MIB_ANTENNA_GAIN, + /*! + * Default antenna gain of the node. Default value is region specific. + * The antenna gain is used to calculate the TX power of the node. + * The formula is: + * radioTxPower = ( int8_t )floor( maxEirp - antennaGain ) + * + * \remark The antenna gain value is referenced to the isotropic antenna. + * The value is in dBi. + * MIB_DEFAULT_ANTENNA_GAIN[dBi] = measuredAntennaGain[dBd] + 2.15 + */ + MIB_DEFAULT_ANTENNA_GAIN, + /*! + * Structure holding pointers to internal contexts and its size + */ + MIB_NVM_CTXS, + /*! + * LoRaWAN MAC layer operating version when activated by ABP. + */ + MIB_ABP_LORAWAN_VERSION, + /*! + * Beacon interval in ms + */ + MIB_BEACON_INTERVAL, + /*! + * Beacon reserved time in ms + */ + MIB_BEACON_RESERVED, + /*! + * Beacon guard time in ms + */ + MIB_BEACON_GUARD, + /*! + * Beacon window time in ms + */ + MIB_BEACON_WINDOW, + /*! + * Beacon window time in number of slots + */ + MIB_BEACON_WINDOW_SLOTS, + /*! + * Ping slot length time in ms + */ + MIB_PING_SLOT_WINDOW, + /*! + * Default symbol timeout for beacons and ping slot windows + */ + MIB_BEACON_SYMBOL_TO_DEFAULT, + /*! + * Maximum symbol timeout for beacons + */ + MIB_BEACON_SYMBOL_TO_EXPANSION_MAX, + /*! + * Maximum symbol timeout for ping slots + */ + MIB_PING_SLOT_SYMBOL_TO_EXPANSION_MAX, + /*! + * Symbol expansion value for beacon windows in case of beacon + * loss in symbols + */ + MIB_BEACON_SYMBOL_TO_EXPANSION_FACTOR, + /*! + * Symbol expansion value for ping slot windows in case of beacon + * loss in symbols + */ + MIB_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR, + /*! + * Maximum allowed beacon less time in ms + */ + MIB_MAX_BEACON_LESS_PERIOD, + /*! + * Ping slot data rate + * + * LoRaWAN Regional Parameters V1.0.2rB + * + * The allowed ranges are region specific. Please refer to \ref DR_0 to \ref DR_15 for details. + */ + MIB_PING_SLOT_DATARATE, +}Mib_t; + +/*! + * LoRaMAC MIB parameters + */ +typedef union uMibParam +{ + /*! + * LoRaWAN device class + * + * Related MIB type: \ref MIB_DEVICE_CLASS + */ + DeviceClass_t Class; + /*! + * LoRaWAN Network End-Device Activation ( ACTIVATION_TYPE_NONE, ACTIVATION_TYPE_ABP or OTTA ) + * + * Related MIB type: \ref MIB_NETWORK_ACTIVATION + */ + ActivationType_t NetworkActivation; + /*! + * LoRaWAN device class + * + * Related MIB type: \ref MIB_DEV_EUI + */ + uint8_t* DevEui; + /*! + * LoRaWAN device class + * + * Related MIB type: \ref MIB_JOIN_EUI + */ + uint8_t* JoinEui; + /*! + * Activation state of ADR + * + * Related MIB type: \ref MIB_ADR + */ + bool AdrEnable; + /*! + * Network identifier + * + * Related MIB type: \ref MIB_NET_ID + */ + uint32_t NetID; + /*! + * End-device address + * + * Related MIB type: \ref MIB_DEV_ADDR + */ + uint32_t DevAddr; + /*! + * Application root key - 1.0.x device only + * + * Related MIB type: \ref MIB_GEN_APP_KEY + */ + uint8_t* GenAppKey; + /*! + * Application root key + * + * Related MIB type: \ref MIB_APP_KEY + */ + uint8_t* AppKey; + /*! + * Network root key + * + * Related MIB type: \ref MIB_NWK_KEY + */ + uint8_t* NwkKey; + /*! + * Join session integrity key + * + * Related MIB type: \ref MIB_J_S_INT_KEY + */ + uint8_t* JSIntKey; + /*! + * Join session encryption key + * + * Related MIB type: \ref MIB_J_S_ENC_KEY + */ + uint8_t* JSEncKey; + /*! + * Forwarding Network session integrity key + * + * Related MIB type: \ref MIB_F_NWK_S_INT_KEY + */ + uint8_t* FNwkSIntKey; + /*! + * Serving Network session integrity key + * + * Related MIB type: \ref MIB_S_NWK_S_INT_KEY + */ + uint8_t* SNwkSIntKey; + /*! + * Network session encryption key + * + * Related MIB type: \ref MIB_NWK_S_ENC_KEY + */ + uint8_t* NwkSEncKey; + /*! + * Application session key + * + * Related MIB type: \ref MIB_APP_S_KEY + */ + uint8_t* AppSKey; + /*! + * Multicast key encryption key + * + * Related MIB type: \ref MIB_MC_KE_KEY + */ + uint8_t* McKEKey; + /*! + * Multicast root key index 0 + * + * Related MIB type: \ref MIB_MC_KEY_0 + */ + uint8_t* McKey0; + /*! + * Multicast Application session key index 0 + * + * Related MIB type: \ref MIB_MC_APP_S_KEY_0 + */ + uint8_t* McAppSKey0; + /*! + * Multicast Network session key index 0 + * + * Related MIB type: \ref MIB_MC_NWK_S_KEY_0 + */ + uint8_t* McNwkSKey0; + /*! + * Multicast root key index 0 + * + * Related MIB type: \ref MIB_MC_KEY_0 + */ + uint8_t* McKey1; + /*! + * Multicast Application session key index 1 + * + * Related MIB type: \ref MIB_MC_APP_S_KEY_1 + */ + uint8_t* McAppSKey1; + /*! + * Multicast Network session key index 1 + * + * Related MIB type: \ref MIB_MC_NWK_S_KEY_1 + */ + uint8_t* McNwkSKey1; + /*! + * Multicast root key index 2 + * + * Related MIB type: \ref MIB_MC_KEY_2 + */ + uint8_t* McKey2; + /*! + * Multicast Application session key index 2 + * + * Related MIB type: \ref MIB_MC_APP_S_KEY_2 + */ + uint8_t* McAppSKey2; + /*! + * Multicast Network session key index 2 + * + * Related MIB type: \ref MIB_MC_NWK_S_KEY_2 + */ + uint8_t* McNwkSKey2; + /*! + * Multicast root key index 2 + * + * Related MIB type: \ref MIB_MC_KEY_2 + */ + uint8_t* McKey3; + /*! + * Multicast Application session key index 2 + * + * Related MIB type: \ref MIB_MC_APP_S_KEY_2 + */ + uint8_t* McAppSKey3; + /*! + * Multicast Network session key index 2 + * + * Related MIB type: \ref MIB_MC_NWK_S_KEY_2 + */ + uint8_t* McNwkSKey3; + /*! + * Enable or disable a public network + * + * Related MIB type: \ref MIB_PUBLIC_NETWORK + */ + bool EnablePublicNetwork; + /*! + * Enable or disable repeater support + * + * Related MIB type: \ref MIB_REPEATER_SUPPORT + */ + bool EnableRepeaterSupport; + /*! + * LoRaWAN Channel + * + * Related MIB type: \ref MIB_CHANNELS + */ + ChannelParams_t* ChannelList; + /*! + * Channel for the receive window 2 + * + * Related MIB type: \ref MIB_RX2_CHANNEL + */ + RxChannelParams_t Rx2Channel; + /*! + * Channel for the receive window 2 + * + * Related MIB type: \ref MIB_RX2_DEFAULT_CHANNEL + */ + RxChannelParams_t Rx2DefaultChannel; + /*! + * Channel for the receive window C + * + * Related MIB type: \ref MIB_RXC_CHANNEL + */ + RxChannelParams_t RxCChannel; + /*! + * Channel for the receive window C + * + * Related MIB type: \ref MIB_RXC_DEFAULT_CHANNEL + */ + RxChannelParams_t RxCDefaultChannel; + /*! + * Channel mask + * + * Related MIB type: \ref MIB_CHANNELS_MASK + */ + uint16_t* ChannelsMask; + /*! + * Default channel mask + * + * Related MIB type: \ref MIB_CHANNELS_DEFAULT_MASK + */ + uint16_t* ChannelsDefaultMask; + /*! + * Number of frame repetitions + * + * Related MIB type: \ref MIB_CHANNELS_NB_TRANS + */ + uint8_t ChannelsNbTrans; + /*! + * Maximum receive window duration + * + * Related MIB type: \ref MIB_MAX_RX_WINDOW_DURATION + */ + uint32_t MaxRxWindow; + /*! + * Receive delay 1 + * + * Related MIB type: \ref MIB_RECEIVE_DELAY_1 + */ + uint32_t ReceiveDelay1; + /*! + * Receive delay 2 + * + * Related MIB type: \ref MIB_RECEIVE_DELAY_2 + */ + uint32_t ReceiveDelay2; + /*! + * Join accept delay 1 + * + * Related MIB type: \ref MIB_JOIN_ACCEPT_DELAY_1 + */ + uint32_t JoinAcceptDelay1; + /*! + * Join accept delay 2 + * + * Related MIB type: \ref MIB_JOIN_ACCEPT_DELAY_2 + */ + uint32_t JoinAcceptDelay2; + /*! + * Channels data rate + * + * Related MIB type: \ref MIB_CHANNELS_DEFAULT_DATARATE + */ + int8_t ChannelsDefaultDatarate; + /*! + * Channels data rate + * + * Related MIB type: \ref MIB_CHANNELS_DATARATE + */ + int8_t ChannelsDatarate; + /*! + * Channels TX power + * + * Related MIB type: \ref MIB_CHANNELS_DEFAULT_TX_POWER + */ + int8_t ChannelsDefaultTxPower; + /*! + * Channels TX power + * + * Related MIB type: \ref MIB_CHANNELS_TX_POWER + */ + int8_t ChannelsTxPower; + /*! + * Multicast channels + * + * Related MIB type: \ref MIB_MULTICAST_CHANNEL + */ + McChannelParams_t MulticastChannel; + /*! + * System overall timing error in milliseconds. + * + * Related MIB type: \ref MIB_SYSTEM_MAX_RX_ERROR + */ + uint32_t SystemMaxRxError; + /*! + * Minimum required number of symbols to detect an Rx frame + * + * Related MIB type: \ref MIB_MIN_RX_SYMBOLS + */ + uint8_t MinRxSymbols; + /*! + * Antenna gain + * + * Related MIB type: \ref MIB_ANTENNA_GAIN + */ + float AntennaGain; + /*! + * Default antenna gain + * + * Related MIB type: \ref MIB_DEFAULT_ANTENNA_GAIN + */ + float DefaultAntennaGain; + /*! + * Structure holding pointers to internal non-volatile contexts and its lengths. + * + * Related MIB type: \ref MIB_NVM_CTXS + */ + LoRaMacCtxs_t* Contexts; + /* + * LoRaWAN MAC layer operating version when activated by ABP. + * + * Related MIB type: \ref MIB_ABP_LORAWAN_VERSION + */ + Version_t AbpLrWanVersion; + /*! + * Beacon interval in ms + * + * Related MIB type: \ref MIB_BEACON_INTERVAL + */ + uint32_t BeaconInterval; + /*! + * Beacon reserved time in ms + * + * Related MIB type: \ref MIB_BEACON_RESERVED + */ + uint32_t BeaconReserved; + /*! + * Beacon guard time in ms + * + * Related MIB type: \ref MIB_BEACON_GUARD + */ + uint32_t BeaconGuard; + /*! + * Beacon window time in ms + * + * Related MIB type: \ref MIB_BEACON_WINDOW + */ + uint32_t BeaconWindow; + /*! + * Beacon window time in number of slots + * + * Related MIB type: \ref MIB_BEACON_WINDOW_SLOTS + */ + uint32_t BeaconWindowSlots; + /*! + * Ping slot length time in ms + * + * Related MIB type: \ref MIB_PING_SLOT_WINDOW + */ + uint32_t PingSlotWindow; + /*! + * Default symbol timeout for beacons and ping slot windows + * + * Related MIB type: \ref MIB_BEACON_SYMBOL_TO_DEFAULT + */ + uint32_t BeaconSymbolToDefault; + /*! + * Maximum symbol timeout for beacons + * + * Related MIB type: \ref MIB_BEACON_SYMBOL_TO_EXPANSION_MAX + */ + uint32_t BeaconSymbolToExpansionMax; + /*! + * Maximum symbol timeout for ping slots + * + * Related MIB type: \ref MIB_PING_SLOT_SYMBOL_TO_EXPANSION_MAX + */ + uint32_t PingSlotSymbolToExpansionMax; + /*! + * Symbol expansion value for beacon windows in case of beacon + * loss in symbols + * + * Related MIB type: \ref MIB_BEACON_SYMBOL_TO_EXPANSION_FACTOR + */ + uint32_t BeaconSymbolToExpansionFactor; + /*! + * Symbol expansion value for ping slot windows in case of beacon + * loss in symbols + * + * Related MIB type: \ref MIB_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR + */ + uint32_t PingSlotSymbolToExpansionFactor; + /*! + * Maximum allowed beacon less time in ms + * + * Related MIB type: \ref MIB_MAX_BEACON_LESS_PERIOD + */ + uint32_t MaxBeaconLessPeriod; + /*! + * Ping slots data rate + * + * Related MIB type: \ref MIB_PING_SLOT_DATARATE + */ + int8_t PingSlotDatarate; +}MibParam_t; + +/*! + * LoRaMAC MIB-RequestConfirm structure + */ +typedef struct eMibRequestConfirm +{ + /*! + * MIB-Request type + */ + Mib_t Type; + + /*! + * MLME-RequestConfirm parameters + */ + MibParam_t Param; +}MibRequestConfirm_t; + +/*! + * LoRaMAC tx information + */ +typedef struct sLoRaMacTxInfo +{ + /*! + * Size of the application data payload which can be transmitted. + */ + uint8_t MaxPossibleApplicationDataSize; + /*! + * The current maximum possible payload size without MAC commands + * which is dependent on the current datarate. + */ + uint8_t CurrentPossiblePayloadSize; +}LoRaMacTxInfo_t; + +/*! + * LoRaMAC Status + */ +typedef enum eLoRaMacStatus +{ + /*! + * Service started successfully + */ + LORAMAC_STATUS_OK, + /*! + * Service not started - LoRaMAC is busy + */ + LORAMAC_STATUS_BUSY, + /*! + * Service unknown + */ + LORAMAC_STATUS_SERVICE_UNKNOWN, + /*! + * Service not started - invalid parameter + */ + LORAMAC_STATUS_PARAMETER_INVALID, + /*! + * Service not started - invalid frequency + */ + LORAMAC_STATUS_FREQUENCY_INVALID, + /*! + * Service not started - invalid datarate + */ + LORAMAC_STATUS_DATARATE_INVALID, + /*! + * Service not started - invalid frequency and datarate + */ + LORAMAC_STATUS_FREQ_AND_DR_INVALID, + /*! + * Service not started - the device is not in a LoRaWAN + */ + LORAMAC_STATUS_NO_NETWORK_JOINED, + /*! + * Service not started - payload length error + */ + LORAMAC_STATUS_LENGTH_ERROR, + /*! + * Service not started - the specified region is not supported + * or not activated with preprocessor definitions. + */ + LORAMAC_STATUS_REGION_NOT_SUPPORTED, + /*! + * The application data was not transmitted + * because prioritized pending MAC commands had to be sent. + */ + LORAMAC_STATUS_SKIPPED_APP_DATA, + /*! + * ToDo + */ + LORAMAC_STATUS_DUTYCYCLE_RESTRICTED, + /*! + * + */ + LORAMAC_STATUS_NO_CHANNEL_FOUND, + /*! + * + */ + LORAMAC_STATUS_NO_FREE_CHANNEL_FOUND, + /*! + * ToDo + */ + LORAMAC_STATUS_BUSY_BEACON_RESERVED_TIME, + /*! + * ToDo + */ + LORAMAC_STATUS_BUSY_PING_SLOT_WINDOW_TIME, + /*! + * ToDo + */ + LORAMAC_STATUS_BUSY_UPLINK_COLLISION, + /*! + * An error in the cryptographic module is occurred + */ + LORAMAC_STATUS_CRYPTO_ERROR, + /*! + * An error in the frame counter handler module is occurred + */ + LORAMAC_STATUS_FCNT_HANDLER_ERROR, + /*! + * An error in the MAC command module is occurred + */ + LORAMAC_STATUS_MAC_COMMAD_ERROR, + /*! + * An error in the Class B module is occurred + */ + LORAMAC_STATUS_CLASS_B_ERROR, + /*! + * An error in the Confirm Queue module is occurred + */ + LORAMAC_STATUS_CONFIRM_QUEUE_ERROR, + /*! + * The multicast group doesn't exist + */ + LORAMAC_STATUS_MC_GROUP_UNDEFINED, + /*! + * Undefined error occurred + */ + LORAMAC_STATUS_ERROR +}LoRaMacStatus_t; + +/*! + * LoRaMAC region enumeration + */ +typedef enum eLoRaMacRegion_t +{ + /*! + * AS band on 923MHz + */ + LORAMAC_REGION_AS923, + /*! + * Australian band on 915MHz + */ + LORAMAC_REGION_AU915, + /*! + * Chinese band on 470MHz + */ + LORAMAC_REGION_CN470, + + LORAMAC_REGION_CN470ALI, + /*! + * Chinese band on 779MHz + */ + LORAMAC_REGION_CN779, + /*! + * European band on 433MHz + */ + LORAMAC_REGION_EU433, + /*! + * European band on 868MHz + */ + LORAMAC_REGION_EU868, + /*! + * South korean band on 920MHz + */ + LORAMAC_REGION_KR920, + /*! + * India band on 865MHz + */ + LORAMAC_REGION_IN865, + /*! + * North american band on 915MHz + */ + LORAMAC_REGION_US915, + /*! + * Russia band on 864MHz + */ + LORAMAC_REGION_RU864, +}LoRaMacRegion_t; + +/*! + * Enumeration of modules which have a context + */ +typedef enum LoRaMacNvmCtxModule_e +{ + /*! + * Context for the MAC + */ + LORAMAC_NVMCTXMODULE_MAC, + /*! + * Context for the regions + */ + LORAMAC_NVMCTXMODULE_REGION, + /*! + * Context for the crypto module + */ + LORAMAC_NVMCTXMODULE_CRYPTO, + /*! + * Context for the secure element + */ + LORAMAC_NVMCTXMODULE_SECURE_ELEMENT, + /*! + * Context for the command queue + */ + LORAMAC_NVMCTXMODULE_COMMANDS, + /*! + * Context for class b + */ + LORAMAC_NVMCTXMODULE_CLASS_B, + /*! + * Context for the confirm queue + */ + LORAMAC_NVMCTXMODULE_CONFIRM_QUEUE, +}LoRaMacNvmCtxModule_t; + + +/*! + * LoRaMAC events structure + * Used to notify upper layers of MAC events + */ +typedef struct sLoRaMacPrimitives +{ + /*! + * \brief MCPS-Confirm primitive + * + * \param [OUT] MCPS-Confirm parameters + */ + void ( *MacMcpsConfirm )( McpsConfirm_t* McpsConfirm ); + /*! + * \brief MCPS-Indication primitive + * + * \param [OUT] MCPS-Indication parameters + */ + void ( *MacMcpsIndication )( McpsIndication_t* McpsIndication ); + /*! + * \brief MLME-Confirm primitive + * + * \param [OUT] MLME-Confirm parameters + */ + void ( *MacMlmeConfirm )( MlmeConfirm_t* MlmeConfirm ); + /*! + * \brief MLME-Indication primitive + * + * \param [OUT] MLME-Indication parameters + */ + void ( *MacMlmeIndication )( MlmeIndication_t* MlmeIndication ); +}LoRaMacPrimitives_t; + +/*! + * LoRaMAC callback structure + */ +typedef struct sLoRaMacCallback +{ + /*! + * \brief Measures the battery level + * + * \retval Battery level [0: node is connected to an external + * power source, 1..254: battery level, where 1 is the minimum + * and 254 is the maximum value, 255: the node was not able + * to measure the battery level] + */ + uint8_t ( *GetBatteryLevel )( void ); + /*! + * \brief Measures the temperature level + * + * \retval Temperature level + */ + uint16_t ( *GetTemperatureLevel )( void ); + /*! + * \brief Will be called when an attribute has changed in one of the context. + * + * \param Context that changed + */ + void ( *NvmContextChange )( LoRaMacNvmCtxModule_t module ); + /*! + *\brief Will be called each time a Radio IRQ is handled by the MAC + * layer. + * + *\warning Runs in a IRQ context. Should only change variables state. + */ + void ( *MacProcessNotify )( void ); +}LoRaMacCallback_t; + + +/*! + * LoRaMAC Max EIRP (dBm) table + */ +static const uint8_t LoRaMacMaxEirpTable[] = { 8, 10, 12, 13, 14, 16, 18, 20, 21, 24, 26, 27, 29, 30, 33, 36 }; + +/*! + * \brief LoRaMAC layer initialization + * + * \details In addition to the initialization of the LoRaMAC layer, this + * function initializes the callback primitives of the MCPS and + * MLME services. Every data field of \ref LoRaMacPrimitives_t must be + * set to a valid callback function. + * + * \param [IN] primitives - Pointer to a structure defining the LoRaMAC + * event functions. Refer to \ref LoRaMacPrimitives_t. + * + * \param [IN] callbacks - Pointer to a structure defining the LoRaMAC + * callback functions. Refer to \ref LoRaMacCallback_t. + * + * \param [IN] region - The region to start. + * + * \retval LoRaMacStatus_t Status of the operation. Possible returns are: + * returns are: + * \ref LORAMAC_STATUS_OK, + * \ref LORAMAC_STATUS_PARAMETER_INVALID, + * \ref LORAMAC_STATUS_REGION_NOT_SUPPORTED. + */ +LoRaMacStatus_t LoRaMacInitialization( LoRaMacPrimitives_t* primitives, LoRaMacCallback_t* callbacks, LoRaMacRegion_t region ); + +/*! + * \brief Starts LoRaMAC layer + * + * \retval LoRaMacStatus_t Status of the operation. Possible returns are: + * returns are: + * \ref LORAMAC_STATUS_OK, + */ +LoRaMacStatus_t LoRaMacStart( void ); + +/*! + * \brief Stops LoRaMAC layer + * + * \retval LoRaMacStatus_t Status of the operation. Possible returns are: + * returns are: + * \ref LORAMAC_STATUS_OK, + */ +LoRaMacStatus_t LoRaMacStop( void ); + +/*! + * \brief Returns a value indicating if the MAC layer is busy or not. + * + * \retval isBusy Mac layer is busy. + */ +bool LoRaMacIsBusy( void ); + +/*! + * Processes the LoRaMac events. + * + * \remark This function must be called in the main loop. + */ +void LoRaMacProcess( void *args); + +/*! + * \brief Queries the LoRaMAC if it is possible to send the next frame with + * a given application data payload size. The LoRaMAC takes scheduled + * MAC commands into account and reports, when the frame can be send or not. + * + * \param [IN] size - Size of application data payload to be send next + * + * \param [OUT] txInfo - The structure \ref LoRaMacTxInfo_t contains + * information about the actual maximum payload possible + * ( according to the configured datarate or the next + * datarate according to ADR ), and the maximum frame + * size, taking the scheduled MAC commands into account. + * + * \retval LoRaMacStatus_t Status of the operation. When the parameters are + * not valid, the function returns \ref LORAMAC_STATUS_PARAMETER_INVALID. + * In case of a length error caused by the application data payload in combination + * with the MAC commands, the function returns \ref LORAMAC_STATUS_LENGTH_ERROR. + * In this case its recommended to send a frame without application data to flush + * the MAC commands. Otherwise the LoRaMAC will prioritize the MAC commands and + * if needed it will skip the application data. Please note that if MAC commands do + * not fit at all into the payload size on the related datarate, the LoRaMAC will + * automatically clip the MAC commands. + * In case the query is valid, and the LoRaMAC is able to send the frame, + * the function returns \ref LORAMAC_STATUS_OK. + */ +LoRaMacStatus_t LoRaMacQueryTxPossible( uint8_t size, LoRaMacTxInfo_t* txInfo ); + +/*! + * \brief LoRaMAC channel add service + * + * \details Adds a new channel to the channel list and activates the id in + * the channel mask. Please note that this functionality is not available + * on all regions. Information about allowed ranges are available at the LoRaWAN Regional Parameters V1.0.2rB + * + * \param [IN] id - Id of the channel. + * + * \param [IN] params - Channel parameters to set. + * + * \retval LoRaMacStatus_t Status of the operation. Possible returns are: + * \ref LORAMAC_STATUS_OK, + * \ref LORAMAC_STATUS_BUSY, + * \ref LORAMAC_STATUS_PARAMETER_INVALID. + */ +LoRaMacStatus_t LoRaMacChannelAdd( uint8_t id, ChannelParams_t params ); + +/*! + * \brief LoRaMAC channel remove service + * + * \details Deactivates the id in the channel mask. + * + * \param [IN] id - Id of the channel. + * + * \retval LoRaMacStatus_t Status of the operation. Possible returns are: + * \ref LORAMAC_STATUS_OK, + * \ref LORAMAC_STATUS_BUSY, + * \ref LORAMAC_STATUS_PARAMETER_INVALID. + */ +LoRaMacStatus_t LoRaMacChannelRemove( uint8_t id ); + +/*! + * \brief LoRaMAC multicast channel setup service + * + * \details Sets up a multicast channel. + * + * \param [IN] channel - Multicast channel to set. + * + * \retval LoRaMacStatus_t Status of the operation. Possible returns are: + * \ref LORAMAC_STATUS_OK, + * \ref LORAMAC_STATUS_BUSY, + * \ref LORAMAC_STATUS_PARAMETER_INVALID, + * \ref LORAMAC_STATUS_MC_GROUP_UNDEFINED. + */ +LoRaMacStatus_t LoRaMacMcChannelSetup( McChannelParams_t *channel ); + +/*! + * \brief LoRaMAC multicast channel removal service + * + * \details Removes/Disables a multicast channel. + * + * \param [IN] groupID - Multicast channel ID to be removed/disabled + * + * \retval LoRaMacStatus_t Status of the operation. Possible returns are: + * \ref LORAMAC_STATUS_OK, + * \ref LORAMAC_STATUS_BUSY, + * \ref LORAMAC_STATUS_MC_GROUP_UNDEFINED. + */ +LoRaMacStatus_t LoRaMacMcChannelDelete( AddressIdentifier_t groupID ); + +/*! + * \brief LoRaMAC multicast channel get groupId from MC address. + * + * \param [IN] mcAddress - Multicast address to be checked + * + * \retval groupID Multicast channel ID associated to the address. + * Returns 0xFF if the address isn't found. + */ +uint8_t LoRaMacMcChannelGetGroupId( uint32_t mcAddress ); + +/*! + * \brief LoRaMAC multicast channel Rx parameters setup service + * + * \details Sets up a multicast channel reception parameters. + * + * \param [IN] groupID - Multicast channel ID + * \param [IN] rxParams - Reception parameters + * \param [OUT] status - Status mask [UNDEF_ID | FREQ_ERR | DR_ERR | GROUP_ID] + * + * \retval LoRaMacStatus_t Status of the operation. Possible returns are: + * \ref LORAMAC_STATUS_OK, + * \ref LORAMAC_STATUS_BUSY, + * \ref LORAMAC_STATUS_PARAMETER_INVALID, + * \ref LORAMAC_STATUS_MC_GROUP_UNDEFINED. + */ +LoRaMacStatus_t LoRaMacMcChannelSetupRxParams( AddressIdentifier_t groupID, McRxParams_t *rxParams, uint8_t *status ); + +/*! + * \brief LoRaMAC MIB-Get + * + * \details The mac information base service to get attributes of the LoRaMac + * layer. + * + * The following code-snippet shows how to use the API to get the + * parameter AdrEnable, defined by the enumeration type + * \ref MIB_ADR. + * \code + * MibRequestConfirm_t mibReq; + * mibReq.Type = MIB_ADR; + * + * if( LoRaMacMibGetRequestConfirm( &mibReq ) == LORAMAC_STATUS_OK ) + * { + * // LoRaMAC updated the parameter mibParam.AdrEnable + * } + * \endcode + * + * \param [IN] mibRequest - MIB-GET-Request to perform. Refer to \ref MibRequestConfirm_t. + * + * \retval LoRaMacStatus_t Status of the operation. Possible returns are: + * \ref LORAMAC_STATUS_OK, + * \ref LORAMAC_STATUS_SERVICE_UNKNOWN, + * \ref LORAMAC_STATUS_PARAMETER_INVALID. + */ +LoRaMacStatus_t LoRaMacMibGetRequestConfirm( MibRequestConfirm_t* mibGet ); + +/*! + * \brief LoRaMAC MIB-Set + * + * \details The mac information base service to set attributes of the LoRaMac + * layer. + * + * The following code-snippet shows how to use the API to set the + * parameter AdrEnable, defined by the enumeration type + * \ref MIB_ADR. + * + * \code + * MibRequestConfirm_t mibReq; + * mibReq.Type = MIB_ADR; + * mibReq.Param.AdrEnable = true; + * + * if( LoRaMacMibGetRequestConfirm( &mibReq ) == LORAMAC_STATUS_OK ) + * { + * // LoRaMAC updated the parameter + * } + * \endcode + * + * \param [IN] mibRequest - MIB-SET-Request to perform. Refer to \ref MibRequestConfirm_t. + * + * \retval LoRaMacStatus_t Status of the operation. Possible returns are: + * \ref LORAMAC_STATUS_OK, + * \ref LORAMAC_STATUS_BUSY, + * \ref LORAMAC_STATUS_SERVICE_UNKNOWN, + * \ref LORAMAC_STATUS_PARAMETER_INVALID. + */ +LoRaMacStatus_t LoRaMacMibSetRequestConfirm( MibRequestConfirm_t* mibSet ); + +/*! + * \brief LoRaMAC MLME-Request + * + * \details The Mac layer management entity handles management services. The + * following code-snippet shows how to use the API to perform a + * network join request. Please note that for a join request, the + * DevEUI and the JoinEUI must be set previously via the MIB. Please + * also refer to the sample implementations. + * + * \code + * + * MlmeReq_t mlmeReq; + * mlmeReq.Type = MLME_JOIN; + * mlmeReq.Req.Join.Datarate = LORAWAN_DEFAULT_DATARATE; + * + * if( LoRaMacMlmeRequest( &mlmeReq ) == LORAMAC_STATUS_OK ) + * { + * // Service started successfully. Waiting for the Mlme-Confirm event + * } + * \endcode + * + * \param [IN] mlmeRequest - MLME-Request to perform. Refer to \ref MlmeReq_t. + * + * \retval LoRaMacStatus_t Status of the operation. Possible returns are: + * \ref LORAMAC_STATUS_OK, + * \ref LORAMAC_STATUS_BUSY, + * \ref LORAMAC_STATUS_SERVICE_UNKNOWN, + * \ref LORAMAC_STATUS_PARAMETER_INVALID, + * \ref LORAMAC_STATUS_NO_NETWORK_JOINED, + * \ref LORAMAC_STATUS_LENGTH_ERROR, + */ +LoRaMacStatus_t LoRaMacMlmeRequest( MlmeReq_t* mlmeRequest ); + +/*! + * \brief LoRaMAC MCPS-Request + * + * \details The Mac Common Part Sublayer handles data services. The following + * code-snippet shows how to use the API to send an unconfirmed + * LoRaMAC frame. + * + * \code + * uint8_t myBuffer[] = { 1, 2, 3 }; + * + * McpsReq_t mcpsReq; + * mcpsReq.Type = MCPS_UNCONFIRMED; + * mcpsReq.Req.Unconfirmed.fPort = 1; + * mcpsReq.Req.Unconfirmed.fBuffer = myBuffer; + * mcpsReq.Req.Unconfirmed.fBufferSize = sizeof( myBuffer ); + * + * if( LoRaMacMcpsRequest( &mcpsReq ) == LORAMAC_STATUS_OK ) + * { + * // Service started successfully. Waiting for the MCPS-Confirm event + * } + * \endcode + * + * \param [IN] mcpsRequest - MCPS-Request to perform. Refer to \ref McpsReq_t. + * + * \retval LoRaMacStatus_t Status of the operation. Possible returns are: + * \ref LORAMAC_STATUS_OK, + * \ref LORAMAC_STATUS_BUSY, + * \ref LORAMAC_STATUS_SERVICE_UNKNOWN, + * \ref LORAMAC_STATUS_PARAMETER_INVALID, + * \ref LORAMAC_STATUS_NO_NETWORK_JOINED, + * \ref LORAMAC_STATUS_LENGTH_ERROR, + */ +LoRaMacStatus_t LoRaMacMcpsRequest( McpsReq_t* mcpsRequest ); + +/*! + * Automatically add the Region.h file at the end of LoRaMac.h file. + * This is required because Region.h uses definitions from LoRaMac.h + */ +#include "region/Region.h" + +/*! \} defgroup LORAMAC */ + +#endif // __LORAMAC_H__ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacAdr.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacAdr.c new file mode 100644 index 00000000..d80c10ed --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacAdr.c @@ -0,0 +1,131 @@ +/*! + * \file LoRaMacAdr.c + * + * \brief LoRa MAC ADR implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + */ + +#include "utilities.h" +#include "region/Region.h" +#include "LoRaMacAdr.h" + +static bool CalcNextV10X( CalcNextAdrParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter ) +{ + bool adrAckReq = false; + int8_t datarate = adrNext->Datarate; + int8_t txPower = adrNext->TxPower; + int8_t minTxDatarate; + GetPhyParams_t getPhy; + PhyParam_t phyParam; + + // Report back the adr ack counter + *adrAckCounter = adrNext->AdrAckCounter; + + if( adrNext->AdrEnabled == true ) + { + // Query minimum TX Datarate + getPhy.Attribute = PHY_MIN_TX_DR; + getPhy.UplinkDwellTime = adrNext->UplinkDwellTime; + phyParam = RegionGetPhyParam( adrNext->Region, &getPhy ); + minTxDatarate = phyParam.Value; + datarate = MAX( datarate, minTxDatarate ); + + if( datarate == minTxDatarate ) + { + *adrAckCounter = 0; + adrAckReq = false; + } + else + { + if( adrNext->AdrAckCounter >= adrNext->AdrAckLimit ) + { + adrAckReq = true; + } + else + { + adrAckReq = false; + } + if( adrNext->AdrAckCounter >= ( adrNext->AdrAckLimit + adrNext->AdrAckDelay ) ) + { + // Set TX Power to maximum + getPhy.Attribute = PHY_MAX_TX_POWER; + phyParam = RegionGetPhyParam( adrNext->Region, &getPhy ); + txPower = phyParam.Value; + + if( ( adrNext->AdrAckCounter % adrNext->AdrAckDelay ) == 1 ) + { + // Decrease the datarate + getPhy.Attribute = PHY_NEXT_LOWER_TX_DR; + getPhy.Datarate = datarate; + getPhy.UplinkDwellTime = adrNext->UplinkDwellTime; + phyParam = RegionGetPhyParam( adrNext->Region, &getPhy ); + datarate = phyParam.Value; + + if( datarate == minTxDatarate ) + { + // We must set adrAckReq to false as soon as we reach the lowest datarate + adrAckReq = false; + if( adrNext->UpdateChanMask == true ) + { + InitDefaultsParams_t params; + params.Type = INIT_TYPE_RESTORE_DEFAULT_CHANNELS; + RegionInitDefaults( adrNext->Region, ¶ms ); + } + } + } + } + } + } + + *drOut = datarate; + *txPowOut = txPower; + return adrAckReq; +} + +/*! + * \brief Calculates the next datarate to set, when ADR is on or off. + * + * \param [IN] adrNext Pointer to the function parameters. + * + * \param [OUT] drOut The calculated datarate for the next TX. + * + * \param [OUT] txPowOut The TX power for the next TX. + * + * \param [OUT] adrAckCounter The calculated ADR acknowledgement counter. + * + * \retval Returns true, if an ADR request should be performed. + */ +bool LoRaMacAdrCalcNext( CalcNextAdrParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter ) +{ + if( adrNext->Version.Fields.Minor == 0 ) + { + return CalcNextV10X( adrNext, drOut, txPowOut, adrAckCounter ); + } + return false; +} diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacAdr.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacAdr.h new file mode 100644 index 00000000..228df763 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacAdr.h @@ -0,0 +1,104 @@ +/*! + * \file LoRaMacAdr.h + * + * \brief LoRa MAC ADR implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + * + * \defgroup LORAMACADR LoRa MAC ADR implementation + * Implementation of the ADR algorithm for LoRa. + * \{ + */ +#ifndef __LORAMACADR_H__ +#define __LORAMACADR_H__ + +/*! \} defgroup LORAMACADR */ + +/* + * Parameter structure for the function CalcNextAdr. + */ +typedef struct sCalcNextAdrParams +{ + /*! + * LoRaWAN Minor Version 1.X + */ + Version_t Version; + /*! + * Set to true, if the function should update the channels mask. + */ + bool UpdateChanMask; + /*! + * Set to true, if ADR is enabled. + */ + bool AdrEnabled; + /*! + * ADR ack counter. + */ + uint32_t AdrAckCounter; + /*! + * ADR Ack limit + */ + uint16_t AdrAckLimit; + /*! + * ADR Ack delay + */ + uint16_t AdrAckDelay; + /*! + * Datarate used currently. + */ + int8_t Datarate; + /*! + * TX power used currently. + */ + int8_t TxPower; + /*! + * UplinkDwellTime + */ + uint8_t UplinkDwellTime; + /*! + * Region + */ + LoRaMacRegion_t Region; +}CalcNextAdrParams_t; + +/*! + * \brief Calculates the next datarate to set, when ADR is on or off. + * + * \param [IN] adrNext Pointer to the function parameters. + * + * \param [OUT] drOut The calculated datarate for the next TX. + * + * \param [OUT] txPowOut The TX power for the next TX. + * + * \param [OUT] adrAckCounter The calculated ADR acknowledgement counter. + * + * \retval Returns true, if an ADR request should be performed. + */ +bool LoRaMacAdrCalcNext( CalcNextAdrParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter ); + +#endif // __LORAMACADR_H__ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacClassB.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacClassB.c new file mode 100644 index 00000000..5a6108b2 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacClassB.c @@ -0,0 +1,1707 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + ___ _____ _ ___ _ _____ ___ ___ ___ ___ +/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| +\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| +|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| +embedded.connectivity.solutions=============== + +Description: LoRa MAC Class B layer implementation + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE ) +*/ +#include +#include "utilities.h" +#include "lorawan_conf.h" /* Don't remove: needed for LORAMAC_CLASSB_ENABLED */ +#include "radio.h" +#include "secure-element.h" +#include "LoRaMac.h" +#include "LoRaMacClassB.h" +#include "LoRaMacClassBConfig.h" +#include "LoRaMacCrypto.h" +#include "LoRaMacConfirmQueue.h" + + +#ifdef LORAMAC_CLASSB_ENABLED + + +/*! + * Defines the LoRaMac radio events status + */ +typedef union uLoRaMacClassBEvents +{ + uint32_t Value; + struct sEvents + { + uint32_t Beacon : 1; + uint32_t PingSlot : 1; + uint32_t MulticastSlot : 1; + }Events; +}LoRaMacClassBEvents_t; + +LoRaMacClassBEvents_t LoRaMacClassBEvents = { .Value = 0 }; + +/* + * Non-volatile module context. + */ +static LoRaMacClassBNvmCtx_t NvmCtx; + +/* + * Module context. + */ +static LoRaMacClassBCtx_t Ctx; + + +/*! + * \brief Computes the temperature compensation for a period of time on a + * specific temperature. + * + * \param [IN] period Time period to compensate + * \param [IN] temperature Current temperature + * + * \retval Compensated time period + */ +static TimerTime_t TimerTempCompensation( TimerTime_t period, float temperature ) +{ + float k = RTC_TEMP_COEFFICIENT; + float kDev = RTC_TEMP_DEV_COEFFICIENT; + float t = RTC_TEMP_TURNOVER; + float tDev = RTC_TEMP_DEV_TURNOVER; + float interim = 0.0; + float ppm = 0.0; + + if (k < 0.0f) + { + ppm = (k - kDev); + } + else + { + ppm = (k + kDev); + } + interim = (temperature - (t - tDev)); + ppm *= interim * interim; + + // Calculate the drift in time + interim = ((float) period * ppm) / 1000000; + // Calculate the resulting time period + interim += period; + interim = floor(interim); + + if (interim < 0.0f) + { + interim = (float)period; + } + + // Calculate the resulting period + return ( UTIL_TIMER_Time_t ) interim; +} + +/*! + * Computes the Ping Offset + * + * \param [IN] beaconTime - Time of the recent received beacon + * \param [IN] address - Frame address + * \param [IN] pingPeriod - Ping period of the node + * \param [OUT] pingOffset - Pseudo random ping offset + */ +static void ComputePingOffset( uint64_t beaconTime, uint32_t address, uint16_t pingPeriod, uint16_t *pingOffset ) +{ + uint8_t buffer[16]; + uint8_t cipher[16]; + uint32_t result = 0; + /* Refer to chapter 15.2 of the LoRaWAN specification v1.1. The beacon time + * GPS time in seconds modulo 2^32 + */ + uint32_t time = ( beaconTime % ( ( ( uint64_t ) 1 ) << 32 ) ); + + memset1( buffer, 0, 16 ); + memset1( cipher, 0, 16 ); + + buffer[0] = ( time ) & 0xFF; + buffer[1] = ( time >> 8 ) & 0xFF; + buffer[2] = ( time >> 16 ) & 0xFF; + buffer[3] = ( time >> 24 ) & 0xFF; + + buffer[4] = ( address ) & 0xFF; + buffer[5] = ( address >> 8 ) & 0xFF; + buffer[6] = ( address >> 16 ) & 0xFF; + buffer[7] = ( address >> 24 ) & 0xFF; + + SecureElementAesEncrypt( buffer, 16, SLOT_RAND_ZERO_KEY, cipher ); + + result = ( ( ( uint32_t ) cipher[0] ) + ( ( ( uint32_t ) cipher[1] ) * 256 ) ); + + *pingOffset = ( uint16_t )( result % pingPeriod ); +} + +/*! + * \brief Calculates the downlink frequency for a given channel. + * + * \param [IN] channel The channel according to the channel plan. + * + * \retval The downlink frequency + */ +static uint32_t CalcDownlinkFrequency( uint8_t channel ) +{ + GetPhyParams_t getPhy; + PhyParam_t phyParam; + uint32_t frequency = 0; + uint32_t stepwidth = 0; + + getPhy.Attribute = PHY_BEACON_CHANNEL_FREQ; + phyParam = RegionGetPhyParam( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &getPhy ); + frequency = phyParam.Value; + + getPhy.Attribute = PHY_BEACON_CHANNEL_STEPWIDTH; + phyParam = RegionGetPhyParam( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &getPhy ); + stepwidth = phyParam.Value; + + // Calculate the frequency + return frequency + ( channel * stepwidth ); +} + +/*! + * \brief Calculates the downlink channel for the beacon and for + * ping slot downlinks. + * + * \param [IN] devAddr The address of the device + * + * \param [IN] beaconTime The beacon time of the beacon. + * + * \param [IN] beaconInterval The beacon interval + * + * \retval The downlink channel + */ +static uint32_t CalcDownlinkChannelAndFrequency( uint32_t devAddr, TimerTime_t beaconTime, TimerTime_t beaconInterval ) +{ + GetPhyParams_t getPhy; + PhyParam_t phyParam; + uint32_t channel = 0; + uint8_t nbChannels = 0; + uint32_t frequency = 0; + + getPhy.Attribute = PHY_BEACON_NB_CHANNELS; + phyParam = RegionGetPhyParam( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &getPhy ); + nbChannels = (uint8_t) phyParam.Value; + + if( nbChannels > 1 ) + { + // Calculate the channel for the next downlink + channel = devAddr + ( beaconTime / ( beaconInterval / 1000 ) ); + channel = channel % nbChannels; + } + + // Calculate the frequency for the next downlink + frequency = CalcDownlinkFrequency( channel ); + + // Calculate the frequency for the next downlink + return frequency; +} + +/*! + * \brief Calculates the correct frequency and opens up the beacon reception window. + * + * \param [IN] rxTime The reception time which should be setup + * + * \param [IN] activateDefaultChannel Set to true, if the function shall setup the default channel + */ +static void RxBeaconSetup( TimerTime_t rxTime, bool activateDefaultChannel ) +{ + RxBeaconSetup_t rxBeaconSetup; + uint32_t frequency = 0; + RxConfigParams_t beaconRxConfig; + GetPhyParams_t getPhy; + PhyParam_t phyParam; + uint16_t windowTimeout = Ctx.BeaconCtx.SymbolTimeout; + + if( activateDefaultChannel == true ) + { + // This is the default frequency in case we don't know when the next + // beacon will be transmitted. We select channel 0 as default. + frequency = CalcDownlinkFrequency( 0 ); + } + else + { + // This is the frequency according to the channel plan + frequency = CalcDownlinkChannelAndFrequency( 0, Ctx.BeaconCtx.BeaconTime.Seconds + ( CLASSB_BEACON_INTERVAL / 1000 ), + CLASSB_BEACON_INTERVAL ); + } + + if( Ctx.NvmCtx->BeaconCtx.Ctrl.CustomFreq == 1 ) + { + // Set the frequency from the BeaconFreqReq + frequency = Ctx.NvmCtx->BeaconCtx.Frequency; + } + + if( Ctx.BeaconCtx.Ctrl.BeaconChannelSet == 1 ) + { + // Set the frequency which was provided by BeaconTimingAns MAC command + Ctx.BeaconCtx.Ctrl.BeaconChannelSet = 0; + frequency = CalcDownlinkFrequency( Ctx.BeaconCtx.BeaconTimingChannel ); + } + + if( ( Ctx.BeaconCtx.Ctrl.BeaconAcquired == 1 ) || ( Ctx.BeaconCtx.Ctrl.AcquisitionPending == 1 ) ) + { + // Apply the symbol timeout only if we have acquired the beacon + // Otherwise, take the window enlargement into account + // Read beacon datarate + getPhy.Attribute = PHY_BEACON_CHANNEL_DR; + phyParam = RegionGetPhyParam( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &getPhy ); + + // Calculate downlink symbols + RegionComputeRxWindowParameters( *Ctx.LoRaMacClassBParams.LoRaMacRegion, + ( int8_t )phyParam.Value, // datarate + Ctx.LoRaMacClassBParams.LoRaMacParams->MinRxSymbols, + Ctx.LoRaMacClassBParams.LoRaMacParams->SystemMaxRxError, + &beaconRxConfig ); + windowTimeout = beaconRxConfig.WindowTimeout; + } + + rxBeaconSetup.SymbolTimeout = windowTimeout; + rxBeaconSetup.RxTime = rxTime; + rxBeaconSetup.Frequency = frequency; + + RegionRxBeaconSetup( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &rxBeaconSetup, &Ctx.LoRaMacClassBParams.McpsIndication->RxDatarate ); + + Ctx.LoRaMacClassBParams.MlmeIndication->BeaconInfo.Frequency = frequency; + Ctx.LoRaMacClassBParams.MlmeIndication->BeaconInfo.Datarate = Ctx.LoRaMacClassBParams.McpsIndication->RxDatarate; +} + +/*! + * \brief Calculates the next ping slot time. + * + * \param [IN] slotOffset The ping slot offset + * \param [IN] pingPeriod The ping period + * \param [OUT] timeOffset Time offset of the next slot, based on current time + * + * \retval [true: ping slot found, false: no ping slot found] + */ +static bool CalcNextSlotTime( uint16_t slotOffset, uint16_t pingPeriod, uint16_t pingNb, TimerTime_t* timeOffset ) +{ + uint8_t currentPingSlot = 0; + TimerTime_t slotTime = 0; + TimerTime_t currentTime = TimerGetCurrentTime( ); + + // Calculate the point in time of the last beacon even if we missed it + slotTime = ( ( currentTime - SysTimeToMs( Ctx.BeaconCtx.LastBeaconRx ) ) % CLASSB_BEACON_INTERVAL ); + slotTime = currentTime - slotTime; + + // Add the reserved time and the ping offset + slotTime += CLASSB_BEACON_RESERVED; + slotTime += slotOffset * CLASSB_PING_SLOT_WINDOW; + + if( slotTime < currentTime ) + { + currentPingSlot = ( ( currentTime - slotTime ) / + ( pingPeriod * CLASSB_PING_SLOT_WINDOW ) ) + 1; + slotTime += ( ( TimerTime_t )( currentPingSlot * pingPeriod ) * + CLASSB_PING_SLOT_WINDOW ); + } + + if( currentPingSlot < pingNb ) + { + if( slotTime <= ( SysTimeToMs( Ctx.BeaconCtx.NextBeaconRx ) - CLASSB_BEACON_GUARD - CLASSB_PING_SLOT_WINDOW ) ) + { + // Calculate the relative ping slot time + slotTime -= currentTime; + slotTime -= Radio.GetWakeupTime( ); + slotTime = TimerTempCompensation( slotTime, Ctx.BeaconCtx.Temperature ); + *timeOffset = slotTime; + return true; + } + } + return false; +} + +/*! + * \brief Calculates CRC's of the beacon frame + * + * \param [IN] buffer Pointer to the data + * \param [IN] length Length of the data + * + * \retval CRC + */ +static uint16_t BeaconCrc( uint8_t *buffer, uint16_t length ) +{ + // The CRC calculation follows CCITT + const uint16_t polynom = 0x1021; + // CRC initial value + uint16_t crc = 0x0000; + + if( buffer == NULL ) + { + return 0; + } + + for( uint16_t i = 0; i < length; ++i ) + { + crc ^= ( uint16_t ) buffer[i] << 8; + for( uint16_t j = 0; j < 8; ++j ) + { + crc = ( crc & 0x8000 ) ? ( crc << 1 ) ^ polynom : ( crc << 1 ); + } + } + + return crc; +} + +static void GetTemperatureLevel( LoRaMacClassBCallback_t *callbacks, BeaconContext_t *beaconCtx ) +{ + // Measure temperature, if available + if( ( callbacks != NULL ) && ( callbacks->GetTemperatureLevel != NULL ) ) + { + beaconCtx->Temperature = (float) callbacks->GetTemperatureLevel( ); + } +} + +static void InitClassB( void ) +{ + GetPhyParams_t getPhy; + PhyParam_t phyParam; + + // Init events + LoRaMacClassBEvents.Value = 0; + + // Init variables to default + memset1( ( uint8_t* ) &NvmCtx, 0, sizeof( LoRaMacClassBNvmCtx_t ) ); + memset1( ( uint8_t* ) &Ctx.PingSlotCtx, 0, sizeof( PingSlotContext_t ) ); + memset1( ( uint8_t* ) &Ctx.BeaconCtx, 0, sizeof( BeaconContext_t ) ); + + // Setup default temperature + Ctx.BeaconCtx.Temperature = 25.0; + + //YBO removed this useless line from Semthec stack which gives a problem in DualCore + //GetTemperatureLevel( &Ctx.LoRaMacClassBCallbacks, &Ctx.BeaconCtx ); + + // Setup default ping slot datarate + getPhy.Attribute = PHY_PING_SLOT_CHANNEL_DR; + phyParam = RegionGetPhyParam( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &getPhy ); + Ctx.NvmCtx->PingSlotCtx.Datarate = (int8_t)( phyParam.Value ); + + // Setup default beacon freq + getPhy.Attribute = PHY_BEACON_CHANNEL_FREQ; + phyParam = RegionGetPhyParam( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &getPhy ); + Ctx.NvmCtx->BeaconCtx.Frequency = phyParam.Value; + + // Setup default states + Ctx.BeaconState = BEACON_STATE_ACQUISITION; + Ctx.PingSlotState = PINGSLOT_STATE_CALC_PING_OFFSET; + Ctx.MulticastSlotState = PINGSLOT_STATE_CALC_PING_OFFSET; + Ctx.NvmCtx->BeaconCtx.BeaconState = BEACON_STATE_ACQUISITION; +} + +static void InitClassBDefaults( void ) +{ + // This function shall reset the Class B settings to default, + // but should keep important configurations + LoRaMacClassBBeaconNvmCtx_t beaconCtx = Ctx.NvmCtx->BeaconCtx; + LoRaMacClassBPingSlotNvmCtx_t pingSlotCtx = Ctx.NvmCtx->PingSlotCtx; + + InitClassB( ); + + // Parameters from BeaconFreqReq + Ctx.NvmCtx->BeaconCtx.Frequency = beaconCtx.Frequency; + Ctx.NvmCtx->BeaconCtx.Ctrl.CustomFreq = beaconCtx.Ctrl.CustomFreq; + + // Parameters from PingSlotChannelReq + Ctx.NvmCtx->PingSlotCtx.Ctrl.CustomFreq = pingSlotCtx.Ctrl.CustomFreq; + Ctx.NvmCtx->PingSlotCtx.Frequency = pingSlotCtx.Frequency; + Ctx.NvmCtx->PingSlotCtx.Datarate = pingSlotCtx.Datarate; +} + +static void EnlargeWindowTimeout( void ) +{ + // Update beacon movement + Ctx.BeaconCtx.BeaconWindowMovement *= CLASSB_WINDOW_MOVE_EXPANSION_FACTOR; + if( Ctx.BeaconCtx.BeaconWindowMovement > CLASSB_WINDOW_MOVE_EXPANSION_MAX ) + { + Ctx.BeaconCtx.BeaconWindowMovement = CLASSB_WINDOW_MOVE_EXPANSION_MAX; + } + // Update symbol timeout + Ctx.BeaconCtx.SymbolTimeout *= CLASSB_BEACON_SYMBOL_TO_EXPANSION_FACTOR; + if( Ctx.BeaconCtx.SymbolTimeout > CLASSB_BEACON_SYMBOL_TO_EXPANSION_MAX ) + { + Ctx.BeaconCtx.SymbolTimeout = CLASSB_BEACON_SYMBOL_TO_EXPANSION_MAX; + } + Ctx.PingSlotCtx.SymbolTimeout *= CLASSB_BEACON_SYMBOL_TO_EXPANSION_FACTOR; + if( Ctx.PingSlotCtx.SymbolTimeout > CLASSB_PING_SLOT_SYMBOL_TO_EXPANSION_MAX ) + { + Ctx.PingSlotCtx.SymbolTimeout = CLASSB_PING_SLOT_SYMBOL_TO_EXPANSION_MAX; + } +} + +static void ResetWindowTimeout( void ) +{ + Ctx.BeaconCtx.SymbolTimeout = CLASSB_BEACON_SYMBOL_TO_DEFAULT; + Ctx.PingSlotCtx.SymbolTimeout = CLASSB_BEACON_SYMBOL_TO_DEFAULT; + Ctx.BeaconCtx.BeaconWindowMovement = CLASSB_WINDOW_MOVE_DEFAULT; +} + +static TimerTime_t CalcDelayForNextBeacon( TimerTime_t currentTime, TimerTime_t lastBeaconRx ) +{ + TimerTime_t nextBeaconRxTime = 0; + + // Calculate the point in time of the next beacon + nextBeaconRxTime = ( ( currentTime - lastBeaconRx ) % CLASSB_BEACON_INTERVAL ); + return ( CLASSB_BEACON_INTERVAL - nextBeaconRxTime ); +} + +static void IndicateBeaconStatus( LoRaMacEventInfoStatus_t status ) +{ + if( Ctx.BeaconCtx.Ctrl.ResumeBeaconing == 0 ) + { + Ctx.LoRaMacClassBParams.MlmeIndication->MlmeIndication = MLME_BEACON; + Ctx.LoRaMacClassBParams.MlmeIndication->Status = status; + Ctx.LoRaMacClassBParams.LoRaMacFlags->Bits.MlmeInd = 1; + + Ctx.LoRaMacClassBParams.LoRaMacFlags->Bits.MacDone = 1; + } + Ctx.BeaconCtx.Ctrl.ResumeBeaconing = 0; +} + +static TimerTime_t ApplyGuardTime( TimerTime_t beaconEventTime ) +{ + TimerTime_t timeGuard = beaconEventTime; + + if( timeGuard > CLASSB_BEACON_GUARD ) + { + timeGuard -= CLASSB_BEACON_GUARD; + } + return timeGuard; +} + +static TimerTime_t UpdateBeaconState( LoRaMacEventInfoStatus_t status, + TimerTime_t windowMovement, TimerTime_t currentTime ) + +{ + TimerTime_t beaconEventTime = 0; + + // Calculate the next beacon RX time + beaconEventTime = CalcDelayForNextBeacon( currentTime, SysTimeToMs( Ctx.BeaconCtx.LastBeaconRx ) ); + Ctx.BeaconCtx.NextBeaconRx = SysTimeFromMs( currentTime + beaconEventTime ); + + // Take temperature compensation into account + beaconEventTime = TimerTempCompensation( beaconEventTime, Ctx.BeaconCtx.Temperature ); + + // Move the window + if( beaconEventTime > windowMovement ) + { + beaconEventTime -= windowMovement; + } + Ctx.BeaconCtx.NextBeaconRxAdjusted = currentTime + beaconEventTime; + + // Start the RX slot state machine for ping and multicast slots + LoRaMacClassBStartRxSlots( ); + + // Setup an MLME_BEACON indication to inform the upper layer + IndicateBeaconStatus( status ); + + // Apply guard time + return ApplyGuardTime( beaconEventTime ); +} + +static uint8_t CalcPingNb( uint16_t periodicity ) +{ + return 128 / ( 1 << periodicity ); +} + +static uint16_t CalcPingPeriod( uint8_t pingNb ) +{ + return CLASSB_BEACON_WINDOW_SLOTS / pingNb; +} + +/* + * Dummy callback in case if the user provides NULL function pointer + */ +static void NvmContextChange( void ) +{ + if( Ctx.LoRaMacClassBNvmEvent != NULL ) + { + Ctx.LoRaMacClassBNvmEvent( ); + } +} + +#endif // LORAMAC_CLASSB_ENABLED + +void LoRaMacClassBInit( LoRaMacClassBParams_t *classBParams, LoRaMacClassBCallback_t *callbacks, LoRaMacClassBNvmEvent classBNvmCtxChanged ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + // Store callbacks + Ctx.LoRaMacClassBCallbacks = *callbacks; + + // Store parameter pointers + Ctx.LoRaMacClassBParams = *classBParams; + + // Assign non-volatile context + Ctx.NvmCtx = &NvmCtx; + + // Assign callback + Ctx.LoRaMacClassBNvmEvent = classBNvmCtxChanged; + + // Initialize timers + TimerInit( &Ctx.BeaconTimer, LoRaMacClassBBeaconTimerEvent ); + TimerInit( &Ctx.PingSlotTimer, LoRaMacClassBPingSlotTimerEvent ); + TimerInit( &Ctx.MulticastSlotTimer, LoRaMacClassBMulticastSlotTimerEvent ); + + InitClassB( ); +#endif // LORAMAC_CLASSB_ENABLED +} + +bool LoRaMacClassBRestoreNvmCtx( void* classBNvmCtx ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + // Restore module context + if( classBNvmCtx != NULL ) + { + memcpy1( ( uint8_t* ) &NvmCtx, ( uint8_t* ) classBNvmCtx, sizeof( NvmCtx ) ); + return true; + } + else + { + return false; + } +#else + return true; +#endif // LORAMAC_CLASSB_ENABLED +} + +void* LoRaMacClassBGetNvmCtx( size_t* classBNvmCtxSize ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + *classBNvmCtxSize = sizeof( NvmCtx ); + return &NvmCtx; +#else + *classBNvmCtxSize = 0; + return NULL; +#endif // LORAMAC_CLASSB_ENABLED +} + +void LoRaMacClassBSetBeaconState( BeaconState_t beaconState ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + if( beaconState == BEACON_STATE_ACQUISITION ) + { + // If the MAC has received a time reference for the beacon, + // apply the state BEACON_STATE_ACQUISITION_BY_TIME. + if( ( Ctx.BeaconCtx.Ctrl.BeaconDelaySet == 1 ) && + ( LoRaMacClassBIsAcquisitionPending( ) == false ) ) + { + Ctx.BeaconState = BEACON_STATE_ACQUISITION_BY_TIME; + } + else + { + Ctx.BeaconState = beaconState; + } + } + else + { + if( ( Ctx.BeaconState != BEACON_STATE_ACQUISITION ) && + ( Ctx.BeaconState != BEACON_STATE_ACQUISITION_BY_TIME ) ) + { + Ctx.BeaconState = beaconState; + } + } + Ctx.NvmCtx->BeaconCtx.BeaconState = Ctx.BeaconState; +#endif // LORAMAC_CLASSB_ENABLED +} + +void LoRaMacClassBSetPingSlotState( PingSlotState_t pingSlotState ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + Ctx.PingSlotState = pingSlotState; +#endif // LORAMAC_CLASSB_ENABLED +} + +void LoRaMacClassBSetMulticastSlotState( PingSlotState_t multicastSlotState ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + Ctx.MulticastSlotState = multicastSlotState; +#endif // LORAMAC_CLASSB_ENABLED +} + +bool LoRaMacClassBIsAcquisitionInProgress( void ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + if( Ctx.BeaconState == BEACON_STATE_ACQUISITION_BY_TIME ) + { + // In this case the acquisition is in progress, as the MAC has + // a time reference for the next beacon RX. + return true; + } + if( LoRaMacClassBIsAcquisitionPending( ) == true ) + { + // In this case the acquisition is in progress, as the MAC + // searches for a beacon. + return true; + } + return false; +#else + return false; +#endif // LORAMAC_CLASSB_ENABLED +} + +void LoRaMacClassBBeaconTimerEvent( void* context ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + Ctx.BeaconCtx.TimeStamp = TimerGetCurrentTime( ); + TimerStop( &Ctx.BeaconTimer ); + LoRaMacClassBEvents.Events.Beacon = 1; + + if( Ctx.LoRaMacClassBCallbacks.MacProcessNotify != NULL ) + { + Ctx.LoRaMacClassBCallbacks.MacProcessNotify( ); + } +#endif // LORAMAC_CLASSB_ENABLED +} + +#ifdef LORAMAC_CLASSB_ENABLED +static void LoRaMacClassBProcessBeacon( void ) +{ + bool activateTimer = false; + TimerTime_t beaconEventTime = 1; + TimerTime_t currentTime = Ctx.BeaconCtx.TimeStamp; + + // Beacon state machine + switch( Ctx.BeaconState ) + { + case BEACON_STATE_ACQUISITION_BY_TIME: + { + activateTimer = true; + + if( Ctx.BeaconCtx.Ctrl.AcquisitionPending == 1 ) + { + Radio.Sleep(); + Ctx.BeaconState = BEACON_STATE_LOST; + } + else + { + // Default symbol timeouts + ResetWindowTimeout( ); + + if( Ctx.BeaconCtx.Ctrl.BeaconDelaySet == 1 ) + { + if( Ctx.BeaconCtx.BeaconTimingDelay > 0 ) + { + if( SysTimeToMs( Ctx.BeaconCtx.NextBeaconRx ) > currentTime ) + { + beaconEventTime = TimerTempCompensation( SysTimeToMs( Ctx.BeaconCtx.NextBeaconRx ) - currentTime, Ctx.BeaconCtx.Temperature ); + } + else + { + // Reset status provides by BeaconTimingAns + Ctx.BeaconCtx.Ctrl.BeaconDelaySet = 0; + Ctx.BeaconCtx.Ctrl.BeaconChannelSet = 0; + Ctx.BeaconState = BEACON_STATE_ACQUISITION; + } + Ctx.BeaconCtx.BeaconTimingDelay = 0; + } + else + { + activateTimer = false; + + // Reset status provides by BeaconTimingAns + Ctx.BeaconCtx.Ctrl.BeaconDelaySet = 0; + // Set the node into acquisition mode + Ctx.BeaconCtx.Ctrl.AcquisitionPending = 1; + + // Don't use the default channel. We know on which + // channel the next beacon will be transmitted + RxBeaconSetup( CLASSB_BEACON_RESERVED, false ); + } + } + else + { + Ctx.BeaconCtx.NextBeaconRx.Seconds = 0; + Ctx.BeaconCtx.NextBeaconRx.SubSeconds = 0; + Ctx.BeaconCtx.BeaconTimingDelay = 0; + + Ctx.BeaconState = BEACON_STATE_ACQUISITION; + } + } + break; + } + case BEACON_STATE_ACQUISITION: + { + activateTimer = true; + + if( Ctx.BeaconCtx.Ctrl.AcquisitionPending == 1 ) + { + Radio.Sleep(); + Ctx.BeaconState = BEACON_STATE_LOST; + } + else + { + // Default symbol timeouts + ResetWindowTimeout( ); + + Ctx.BeaconCtx.Ctrl.AcquisitionPending = 1; + beaconEventTime = CLASSB_BEACON_INTERVAL; + + // Start the beacon acquisition. When the MAC has received a beacon in function + // RxBeacon successfully, the next state is BEACON_STATE_LOCKED. If the MAC does not + // find a beacon, the state machine will stay in state BEACON_STATE_ACQUISITION. + // This state detects that a acquisition was pending previously and will change the next + // state to BEACON_STATE_LOST. + RxBeaconSetup( 0, true ); + } + break; + } + case BEACON_STATE_TIMEOUT: + { + // We have to update the beacon time, since we missed a beacon + Ctx.BeaconCtx.BeaconTime.Seconds += ( CLASSB_BEACON_INTERVAL / 1000 ); + Ctx.BeaconCtx.BeaconTime.SubSeconds = 0; + + // Enlarge window timeouts to increase the chance to receive the next beacon + EnlargeWindowTimeout( ); + + // Setup next state + Ctx.BeaconState = BEACON_STATE_REACQUISITION; + } + // Intentional fall through + case BEACON_STATE_REACQUISITION: + { + activateTimer = true; + + // The beacon is no longer acquired + Ctx.BeaconCtx.Ctrl.BeaconAcquired = 0; + + // Verify if the maximum beacon less period has been elapsed + if( ( currentTime - SysTimeToMs( Ctx.BeaconCtx.LastBeaconRx ) ) > CLASSB_MAX_BEACON_LESS_PERIOD ) + { + Ctx.BeaconState = BEACON_STATE_LOST; + } + else + { + // Handle beacon miss + beaconEventTime = UpdateBeaconState( LORAMAC_EVENT_INFO_STATUS_BEACON_LOST, + Ctx.BeaconCtx.BeaconWindowMovement, currentTime ); + + // Setup next state + Ctx.BeaconState = BEACON_STATE_IDLE; + } + break; + } + case BEACON_STATE_LOCKED: + { + activateTimer = true; + + // We have received a beacon. Acquisition is no longer pending. + Ctx.BeaconCtx.Ctrl.AcquisitionPending = 0; + + // Handle beacon reception + beaconEventTime = UpdateBeaconState( LORAMAC_EVENT_INFO_STATUS_BEACON_LOCKED, + 0, currentTime ); + + // Setup the MLME confirm for the MLME_BEACON_ACQUISITION + if( Ctx.LoRaMacClassBParams.LoRaMacFlags->Bits.MlmeReq == 1 ) + { + if( LoRaMacConfirmQueueIsCmdActive( MLME_BEACON_ACQUISITION ) == true ) + { + LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_OK, MLME_BEACON_ACQUISITION ); + Ctx.LoRaMacClassBParams.MlmeConfirm->TxTimeOnAir = 0; + } + } + + // Setup next state + Ctx.BeaconState = BEACON_STATE_IDLE; + break; + } + case BEACON_STATE_IDLE: + { + activateTimer = true; + GetTemperatureLevel( &Ctx.LoRaMacClassBCallbacks, &Ctx.BeaconCtx ); + beaconEventTime = Ctx.BeaconCtx.NextBeaconRxAdjusted - Radio.GetWakeupTime( ); + currentTime = TimerGetCurrentTime( ); + + if( beaconEventTime > currentTime ) + { + Ctx.BeaconState = BEACON_STATE_GUARD; + beaconEventTime -= currentTime; + beaconEventTime = TimerTempCompensation( beaconEventTime, Ctx.BeaconCtx.Temperature ); + } + else + { + Ctx.BeaconState = BEACON_STATE_REACQUISITION; + beaconEventTime = 1; + } + break; + } + case BEACON_STATE_GUARD: + { + Ctx.BeaconState = BEACON_STATE_RX; + + // Stop slot timers + LoRaMacClassBStopRxSlots( ); + + // Don't use the default channel. We know on which + // channel the next beacon will be transmitted + RxBeaconSetup( CLASSB_BEACON_RESERVED, false ); + break; + } + case BEACON_STATE_LOST: + { + // Handle events + if( Ctx.LoRaMacClassBParams.LoRaMacFlags->Bits.MlmeReq == 1 ) + { + if( LoRaMacConfirmQueueIsCmdActive( MLME_BEACON_ACQUISITION ) == true ) + { + LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_BEACON_NOT_FOUND, MLME_BEACON_ACQUISITION ); + } + } + else + { + Ctx.LoRaMacClassBParams.MlmeIndication->MlmeIndication = MLME_BEACON_LOST; + Ctx.LoRaMacClassBParams.MlmeIndication->Status = LORAMAC_EVENT_INFO_STATUS_OK; + Ctx.LoRaMacClassBParams.LoRaMacFlags->Bits.MlmeInd = 1; + } + + // Stop slot timers + LoRaMacClassBStopRxSlots( ); + + // Initialize default state for class b + InitClassBDefaults( ); + + Ctx.LoRaMacClassBParams.LoRaMacFlags->Bits.MacDone = 1; + + break; + } + default: + { + Ctx.BeaconState = BEACON_STATE_ACQUISITION; + break; + } + } + + if( activateTimer == true ) + { + TimerSetValue( &Ctx.BeaconTimer, beaconEventTime ); + TimerStart( &Ctx.BeaconTimer ); + } +} +#endif // LORAMAC_CLASSB_ENABLED + +void LoRaMacClassBPingSlotTimerEvent( void* context ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + LoRaMacClassBEvents.Events.PingSlot = 1; + + if( Ctx.LoRaMacClassBCallbacks.MacProcessNotify != NULL ) + { + Ctx.LoRaMacClassBCallbacks.MacProcessNotify( ); + } +#endif // LORAMAC_CLASSB_ENABLED +} + +#ifdef LORAMAC_CLASSB_ENABLED +static void LoRaMacClassBProcessPingSlot( void ) +{ + static RxConfigParams_t pingSlotRxConfig; + TimerTime_t pingSlotTime = 0; + + switch( Ctx.PingSlotState ) + { + case PINGSLOT_STATE_CALC_PING_OFFSET: + { + ComputePingOffset( Ctx.BeaconCtx.BeaconTime.Seconds, + *Ctx.LoRaMacClassBParams.LoRaMacDevAddr, + Ctx.NvmCtx->PingSlotCtx.PingPeriod, + &( Ctx.PingSlotCtx.PingOffset ) ); + Ctx.PingSlotState = PINGSLOT_STATE_SET_TIMER; + } + // Intentional fall through + case PINGSLOT_STATE_SET_TIMER: + { + if( CalcNextSlotTime( Ctx.PingSlotCtx.PingOffset, Ctx.NvmCtx->PingSlotCtx.PingPeriod, Ctx.NvmCtx->PingSlotCtx.PingNb, &pingSlotTime ) == true ) + { + if( Ctx.BeaconCtx.Ctrl.BeaconAcquired == 1 ) + { + // Compute the symbol timeout. Apply it only, if the beacon is acquired + // Otherwise, take the enlargement of the symbols into account. + RegionComputeRxWindowParameters( *Ctx.LoRaMacClassBParams.LoRaMacRegion, + Ctx.NvmCtx->PingSlotCtx.Datarate, + Ctx.LoRaMacClassBParams.LoRaMacParams->MinRxSymbols, + Ctx.LoRaMacClassBParams.LoRaMacParams->SystemMaxRxError, + &pingSlotRxConfig ); + Ctx.PingSlotCtx.SymbolTimeout = pingSlotRxConfig.WindowTimeout; + + if( ( int32_t )pingSlotTime > pingSlotRxConfig.WindowOffset ) + {// Apply the window offset + pingSlotTime += pingSlotRxConfig.WindowOffset; + } + } + + // Start the timer if the ping slot time is in range + Ctx.PingSlotState = PINGSLOT_STATE_IDLE; + TimerSetValue( &Ctx.PingSlotTimer, pingSlotTime ); + TimerStart( &Ctx.PingSlotTimer ); + } + break; + } + case PINGSLOT_STATE_IDLE: + { + uint32_t frequency = Ctx.NvmCtx->PingSlotCtx.Frequency; + + // Apply a custom frequency if the following bit is set + if( Ctx.NvmCtx->PingSlotCtx.Ctrl.CustomFreq == 0 ) + { + // Restore floor plan + frequency = CalcDownlinkChannelAndFrequency( *Ctx.LoRaMacClassBParams.LoRaMacDevAddr, Ctx.BeaconCtx.BeaconTime.Seconds, CLASSB_BEACON_INTERVAL ); + } + + // Open the ping slot window only, if there is no multicast ping slot + // open. Multicast ping slots have always priority + if( Ctx.MulticastSlotState != PINGSLOT_STATE_RX ) + { + Ctx.PingSlotState = PINGSLOT_STATE_RX; + + pingSlotRxConfig.Datarate = Ctx.NvmCtx->PingSlotCtx.Datarate; + pingSlotRxConfig.DownlinkDwellTime = Ctx.LoRaMacClassBParams.LoRaMacParams->DownlinkDwellTime; + pingSlotRxConfig.RepeaterSupport = Ctx.LoRaMacClassBParams.LoRaMacParams->RepeaterSupport; + pingSlotRxConfig.Frequency = frequency; + pingSlotRxConfig.RxContinuous = false; + pingSlotRxConfig.RxSlot = RX_SLOT_WIN_CLASS_B_PING_SLOT; + + RegionRxConfig( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &pingSlotRxConfig, ( int8_t* )&Ctx.LoRaMacClassBParams.McpsIndication->RxDatarate ); + + if( pingSlotRxConfig.RxContinuous == false ) + { + Radio.Rx( Ctx.LoRaMacClassBParams.LoRaMacParams->MaxRxWindow ); + } + else + { + Radio.Rx( 0 ); // Continuous mode + } + } + else + { + // Multicast slots have priority. Skip Rx + Ctx.PingSlotState = PINGSLOT_STATE_CALC_PING_OFFSET; + TimerSetValue( &Ctx.PingSlotTimer, CLASSB_PING_SLOT_WINDOW ); + TimerStart( &Ctx.PingSlotTimer ); + } + break; + } + default: + { + Ctx.PingSlotState = PINGSLOT_STATE_CALC_PING_OFFSET; + break; + } + } +} +#endif // LORAMAC_CLASSB_ENABLED + +void LoRaMacClassBMulticastSlotTimerEvent( void* context ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + LoRaMacClassBEvents.Events.MulticastSlot = 1; + + if( Ctx.LoRaMacClassBCallbacks.MacProcessNotify != NULL ) + { + Ctx.LoRaMacClassBCallbacks.MacProcessNotify( ); + } +#endif // LORAMAC_CLASSB_ENABLED +} + +#ifdef LORAMAC_CLASSB_ENABLED +static void LoRaMacClassBProcessMulticastSlot( void ) +{ + static RxConfigParams_t multicastSlotRxConfig; + TimerTime_t multicastSlotTime = 0; + TimerTime_t slotTime = 0; + MulticastCtx_t *cur = Ctx.LoRaMacClassBParams.MulticastChannels; + + + if( cur == NULL ) + { + return; + } + + if( Ctx.MulticastSlotState == PINGSLOT_STATE_RX ) + { + // A multicast slot is already open + return; + } + + switch( Ctx.MulticastSlotState ) + { + case PINGSLOT_STATE_CALC_PING_OFFSET: + { + // Compute all offsets for every multicast slots + for( uint8_t i = 0; i < 4; i++ ) + { + ComputePingOffset( Ctx.BeaconCtx.BeaconTime.Seconds, + cur->ChannelParams.Address, + cur->PingPeriod, + &( cur->PingOffset ) ); + cur++; + } + Ctx.MulticastSlotState = PINGSLOT_STATE_SET_TIMER; + } + // Intentional fall through + case PINGSLOT_STATE_SET_TIMER: + { + cur = Ctx.LoRaMacClassBParams.MulticastChannels; + Ctx.PingSlotCtx.NextMulticastChannel = NULL; + + for( uint8_t i = 0; i < 4; i++ ) + { + // Calculate the next slot time for every multicast slot + if( CalcNextSlotTime( cur->PingOffset, cur->PingPeriod, cur->PingNb, &slotTime ) == true ) + { + if( ( multicastSlotTime == 0 ) || ( multicastSlotTime > slotTime ) ) + { + // Update the slot time and the next multicast channel + multicastSlotTime = slotTime; + Ctx.PingSlotCtx.NextMulticastChannel = cur; + } + } + cur++; + } + + // Schedule the next multicast slot + if( Ctx.PingSlotCtx.NextMulticastChannel != NULL ) + { + if( Ctx.BeaconCtx.Ctrl.BeaconAcquired == 1 ) + { + RegionComputeRxWindowParameters( *Ctx.LoRaMacClassBParams.LoRaMacRegion, + Ctx.NvmCtx->PingSlotCtx.Datarate, + Ctx.LoRaMacClassBParams.LoRaMacParams->MinRxSymbols, + Ctx.LoRaMacClassBParams.LoRaMacParams->SystemMaxRxError, + &multicastSlotRxConfig ); + Ctx.PingSlotCtx.SymbolTimeout = multicastSlotRxConfig.WindowTimeout; + } + + if( ( int32_t )multicastSlotTime > multicastSlotRxConfig.WindowOffset ) + {// Apply the window offset + multicastSlotTime += multicastSlotRxConfig.WindowOffset; + } + + // Start the timer if the ping slot time is in range + Ctx.MulticastSlotState = PINGSLOT_STATE_IDLE; + TimerSetValue( &Ctx.MulticastSlotTimer, multicastSlotTime ); + TimerStart( &Ctx.MulticastSlotTimer ); + } + break; + } + case PINGSLOT_STATE_IDLE: + { + uint32_t frequency = 0; + + // Verify if the multicast channel is valid + if( Ctx.PingSlotCtx.NextMulticastChannel == NULL ) + { + Ctx.MulticastSlotState = PINGSLOT_STATE_CALC_PING_OFFSET; + TimerSetValue( &Ctx.MulticastSlotTimer, 1 ); + TimerStart( &Ctx.MulticastSlotTimer ); + break; + } + + // Apply frequency + frequency = Ctx.PingSlotCtx.NextMulticastChannel->ChannelParams.RxParams.ClassB.Frequency; + + // Restore the floor plan frequency if there is no individual frequency assigned + if( frequency == 0 ) + { + // Restore floor plan + frequency = CalcDownlinkChannelAndFrequency( Ctx.PingSlotCtx.NextMulticastChannel->ChannelParams.Address, Ctx.BeaconCtx.BeaconTime.Seconds, CLASSB_BEACON_INTERVAL ); + } + + Ctx.MulticastSlotState = PINGSLOT_STATE_RX; + + multicastSlotRxConfig.Datarate = Ctx.PingSlotCtx.NextMulticastChannel->ChannelParams.RxParams.ClassB.Datarate; + multicastSlotRxConfig.DownlinkDwellTime = Ctx.LoRaMacClassBParams.LoRaMacParams->DownlinkDwellTime; + multicastSlotRxConfig.RepeaterSupport = Ctx.LoRaMacClassBParams.LoRaMacParams->RepeaterSupport; + multicastSlotRxConfig.Frequency = frequency; + multicastSlotRxConfig.RxContinuous = false; + multicastSlotRxConfig.RxSlot = RX_SLOT_WIN_CLASS_B_MULTICAST_SLOT; + + RegionRxConfig( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &multicastSlotRxConfig, ( int8_t* )&Ctx.LoRaMacClassBParams.McpsIndication->RxDatarate ); + + if( Ctx.PingSlotState == PINGSLOT_STATE_RX ) + { + // Close ping slot window, if necessary. Multicast slots have priority + Radio.Standby( ); + Ctx.PingSlotState = PINGSLOT_STATE_CALC_PING_OFFSET; + TimerSetValue( &Ctx.PingSlotTimer, CLASSB_PING_SLOT_WINDOW ); + TimerStart( &Ctx.PingSlotTimer ); + } + + if( multicastSlotRxConfig.RxContinuous == false ) + { + Radio.Rx( Ctx.LoRaMacClassBParams.LoRaMacParams->MaxRxWindow ); + } + else + { + Radio.Rx( 0 ); // Continuous mode + } + break; + } + default: + { + Ctx.MulticastSlotState = PINGSLOT_STATE_CALC_PING_OFFSET; + break; + } + } +} +#endif // LORAMAC_CLASSB_ENABLED + +bool LoRaMacClassBRxBeacon( uint8_t *payload, uint16_t size ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + GetPhyParams_t getPhy; + PhyParam_t phyParam; + bool beaconProcessed = false; + uint16_t crc0 = 0; + uint16_t crc1 = 0; + uint16_t beaconCrc0 = 0; + uint16_t beaconCrc1 = 0; + + getPhy.Attribute = PHY_BEACON_FORMAT; + phyParam = RegionGetPhyParam( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &getPhy ); + + // Verify if we are in the state where we expect a beacon + if( ( Ctx.BeaconState == BEACON_STATE_RX ) || ( Ctx.BeaconCtx.Ctrl.AcquisitionPending == 1 ) ) + { + if( size == phyParam.BeaconFormat.BeaconSize ) + { + // A beacon frame is defined as: + // Bytes: | x | 4 | 2 | 7 | y | 2 | + // |------|------|------|------------|------|------| + // Field: | RFU1 | Time | CRC1 | GwSpecific | RFU2 | CRC2 | + // + // Field RFU1 and RFU2 have variable sizes. It depends on the region specific implementation + + // Read CRC1 field from the frame + beaconCrc0 = ( ( uint16_t )payload[phyParam.BeaconFormat.Rfu1Size + 4] ) & 0x00FF; + beaconCrc0 |= ( ( uint16_t )payload[phyParam.BeaconFormat.Rfu1Size + 4 + 1] << 8 ) & 0xFF00; + crc0 = BeaconCrc( payload, phyParam.BeaconFormat.Rfu1Size + 4 ); + + // Validate the first crc of the beacon frame + if( crc0 == beaconCrc0 ) + { + // Read Time field from the frame + Ctx.BeaconCtx.BeaconTime.Seconds = ( ( uint32_t )payload[phyParam.BeaconFormat.Rfu1Size] ) & 0x000000FF; + Ctx.BeaconCtx.BeaconTime.Seconds |= ( ( uint32_t )( payload[phyParam.BeaconFormat.Rfu1Size + 1] << 8 ) ) & 0x0000FF00; + Ctx.BeaconCtx.BeaconTime.Seconds |= ( ( uint32_t )( payload[phyParam.BeaconFormat.Rfu1Size + 2] << 16 ) ) & 0x00FF0000; + Ctx.BeaconCtx.BeaconTime.Seconds |= ( ( uint32_t )( payload[phyParam.BeaconFormat.Rfu1Size + 3] << 24 ) ) & 0xFF000000; + Ctx.BeaconCtx.BeaconTime.SubSeconds = 0; + Ctx.LoRaMacClassBParams.MlmeIndication->BeaconInfo.Time = Ctx.BeaconCtx.BeaconTime; + beaconProcessed = true; + } + + // Read CRC2 field from the frame + beaconCrc1 = ( ( uint16_t )payload[phyParam.BeaconFormat.Rfu1Size + 4 + 2 + 7 + phyParam.BeaconFormat.Rfu2Size] ) & 0x00FF; + beaconCrc1 |= ( ( uint16_t )payload[phyParam.BeaconFormat.Rfu1Size + 4 + 2 + 7 + phyParam.BeaconFormat.Rfu2Size + 1] << 8 ) & 0xFF00; + crc1 = BeaconCrc( &payload[phyParam.BeaconFormat.Rfu1Size + 4 + 2], 7 + phyParam.BeaconFormat.Rfu2Size ); + + // Validate the second crc of the beacon frame + if( crc1 == beaconCrc1 ) + { + // Read GwSpecific field from the frame + // The GwSpecific field contains 1 byte InfoDesc and 6 bytes Info + Ctx.LoRaMacClassBParams.MlmeIndication->BeaconInfo.GwSpecific.InfoDesc = payload[phyParam.BeaconFormat.Rfu1Size + 4 + 2]; + memcpy1( Ctx.LoRaMacClassBParams.MlmeIndication->BeaconInfo.GwSpecific.Info, &payload[phyParam.BeaconFormat.Rfu1Size + 4 + 2 + 1], 6 ); + + Ctx.NvmCtx->BeaconCtx.GwSpecific.InfoDesc = Ctx.LoRaMacClassBParams.MlmeIndication->BeaconInfo.GwSpecific.InfoDesc; + memcpy1( Ctx.NvmCtx->BeaconCtx.GwSpecific.Info, Ctx.LoRaMacClassBParams.MlmeIndication->BeaconInfo.GwSpecific.Info, 6 ); + } + + // Reset beacon variables, if one of the crc is valid + if( beaconProcessed == true ) + { + TimerTime_t time = Radio.TimeOnAir( MODEM_LORA, size ); + SysTime_t timeOnAir; + timeOnAir.Seconds = time / 1000; + timeOnAir.SubSeconds = time - timeOnAir.Seconds * 1000; + + Ctx.BeaconCtx.LastBeaconRx = Ctx.BeaconCtx.BeaconTime; + Ctx.BeaconCtx.LastBeaconRx.Seconds += UNIX_GPS_EPOCH_OFFSET; + + // Update system time. + SysTimeSet( SysTimeAdd( Ctx.BeaconCtx.LastBeaconRx, timeOnAir ) ); + + Ctx.NvmCtx->BeaconCtx.LastBeaconRx = Ctx.BeaconCtx.LastBeaconRx; + + Ctx.BeaconCtx.Ctrl.BeaconAcquired = 1; + Ctx.BeaconCtx.Ctrl.BeaconMode = 1; + ResetWindowTimeout( ); + Ctx.BeaconState = BEACON_STATE_LOCKED; + Ctx.NvmCtx->BeaconCtx.BeaconState = Ctx.BeaconState; + LoRaMacClassBBeaconTimerEvent( NULL ); + } + } + + if( Ctx.BeaconState == BEACON_STATE_RX ) + { + Ctx.BeaconState = BEACON_STATE_TIMEOUT; + Ctx.NvmCtx->BeaconCtx.BeaconState = Ctx.BeaconState; + LoRaMacClassBBeaconTimerEvent( NULL ); + } + // When the MAC listens for a beacon, it is not allowed to process any other + // downlink except the beacon frame itself. The reason for this is that no valid downlink window is open. + // If it receives a frame which is + // 1. not a beacon or + // 2. a beacon with a crc fail + // the MAC shall ignore the frame completely. Thus, the function must always return true, even if no + // valid beacon has been received. + beaconProcessed = true; + } + return beaconProcessed; +#else + return false; +#endif // LORAMAC_CLASSB_ENABLED +} + +bool LoRaMacClassBIsBeaconExpected( void ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + if( ( Ctx.BeaconCtx.Ctrl.AcquisitionPending == 1 ) || + ( Ctx.BeaconState == BEACON_STATE_RX ) ) + { + return true; + } + return false; +#else + return false; +#endif // LORAMAC_CLASSB_ENABLED +} + +bool LoRaMacClassBIsPingExpected( void ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + if( Ctx.PingSlotState == PINGSLOT_STATE_RX ) + { + return true; + } + return false; +#else + return false; +#endif // LORAMAC_CLASSB_ENABLED +} + +bool LoRaMacClassBIsMulticastExpected( void ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + if( Ctx.MulticastSlotState == PINGSLOT_STATE_RX ) + { + return true; + } + return false; +#else + return false; +#endif // LORAMAC_CLASSB_ENABLED +} + +bool LoRaMacClassBIsAcquisitionPending( void ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + if( Ctx.BeaconCtx.Ctrl.AcquisitionPending == 1 ) + { + return true; + } + return false; +#else + return false; +#endif // LORAMAC_CLASSB_ENABLED +} + +bool LoRaMacClassBIsBeaconModeActive( void ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + if( ( Ctx.BeaconCtx.Ctrl.BeaconMode == 1 ) || + ( Ctx.BeaconState == BEACON_STATE_ACQUISITION_BY_TIME ) ) + { + return true; + } + return false; +#else + return false; +#endif // LORAMAC_CLASSB_ENABLED +} + +void LoRaMacClassBSetPingSlotInfo( uint8_t periodicity ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + Ctx.NvmCtx->PingSlotCtx.PingNb = CalcPingNb( periodicity ); + Ctx.NvmCtx->PingSlotCtx.PingPeriod = CalcPingPeriod( Ctx.NvmCtx->PingSlotCtx.PingNb ); + NvmContextChange( ); +#endif // LORAMAC_CLASSB_ENABLED +} + +void LoRaMacClassBHaltBeaconing( void ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + if( Ctx.BeaconCtx.Ctrl.BeaconMode == 1 ) + { + if( ( Ctx.BeaconState == BEACON_STATE_TIMEOUT ) || + ( Ctx.BeaconState == BEACON_STATE_LOST ) ) + { + // Update the state machine before halt + LoRaMacClassBBeaconTimerEvent( NULL ); + } + + CRITICAL_SECTION_BEGIN( ); + LoRaMacClassBEvents.Events.Beacon = 0; + CRITICAL_SECTION_END( ); + + // Halt ping slot state machine + TimerStop( &Ctx.BeaconTimer ); + + // Halt beacon state machine + Ctx.BeaconState = BEACON_STATE_HALT; + Ctx.NvmCtx->BeaconCtx.BeaconState = Ctx.BeaconState; + + // Halt ping and multicast slot state machines + LoRaMacClassBStopRxSlots( ); + } +#endif // LORAMAC_CLASSB_ENABLED +} + +void LoRaMacClassBResumeBeaconing( void ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + if( Ctx.BeaconState == BEACON_STATE_HALT ) + { + Ctx.BeaconCtx.Ctrl.ResumeBeaconing = 1; + + // Set default state + Ctx.BeaconState = BEACON_STATE_LOCKED; + + if( Ctx.BeaconCtx.Ctrl.BeaconAcquired == 0 ) + { + // Set the default state for beacon less operation + Ctx.BeaconState = BEACON_STATE_REACQUISITION; + } + + Ctx.NvmCtx->BeaconCtx.BeaconState = Ctx.BeaconState; + LoRaMacClassBBeaconTimerEvent( NULL ); + } +#endif // LORAMAC_CLASSB_ENABLED +} + +LoRaMacStatus_t LoRaMacClassBSwitchClass( DeviceClass_t nextClass ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + if( nextClass == CLASS_B ) + {// Switch to from class a to class b + if( ( Ctx.BeaconCtx.Ctrl.BeaconMode == 1 ) && ( Ctx.NvmCtx->PingSlotCtx.Ctrl.Assigned == 1 ) ) + { + return LORAMAC_STATUS_OK; + } + } + if( nextClass == CLASS_A ) + {// Switch from class b to class a + LoRaMacClassBHaltBeaconing( ); + + // Initialize default state for class b + InitClassBDefaults( ); + + return LORAMAC_STATUS_OK; + } + return LORAMAC_STATUS_SERVICE_UNKNOWN; +#else + return LORAMAC_STATUS_SERVICE_UNKNOWN; +#endif // LORAMAC_CLASSB_ENABLED +} + +LoRaMacStatus_t LoRaMacClassBMibGetRequestConfirm( MibRequestConfirm_t *mibGet ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + LoRaMacStatus_t status; + + switch( mibGet->Type ) + { + case MIB_PING_SLOT_DATARATE: + { + mibGet->Param.PingSlotDatarate = Ctx.NvmCtx->PingSlotCtx.Datarate; + break; + } + default: + { + status = LORAMAC_STATUS_SERVICE_UNKNOWN; + break; + } + } + return status; +#else + return LORAMAC_STATUS_SERVICE_UNKNOWN; +#endif // LORAMAC_CLASSB_ENABLED +} + +LoRaMacStatus_t LoRaMacMibClassBSetRequestConfirm( MibRequestConfirm_t *mibSet ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + LoRaMacStatus_t status; + + switch( mibSet->Type ) + { + case MIB_PING_SLOT_DATARATE: + { + Ctx.NvmCtx->PingSlotCtx.Datarate = mibSet->Param.PingSlotDatarate; + NvmContextChange( ); + break; + } + default: + { + status = LORAMAC_STATUS_SERVICE_UNKNOWN; + break; + } + } + return status; +#else + return LORAMAC_STATUS_SERVICE_UNKNOWN; +#endif // LORAMAC_CLASSB_ENABLED +} + +void LoRaMacClassBPingSlotInfoAns( void ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + if( LoRaMacConfirmQueueIsCmdActive( MLME_PING_SLOT_INFO ) == true ) + { + LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_OK, MLME_PING_SLOT_INFO ); + Ctx.NvmCtx->PingSlotCtx.Ctrl.Assigned = 1; + NvmContextChange( ); + } +#endif // LORAMAC_CLASSB_ENABLED +} + +uint8_t LoRaMacClassBPingSlotChannelReq( uint8_t datarate, uint32_t frequency ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + uint8_t status = 0x03; + VerifyParams_t verify; + bool isCustomFreq = false; + + if( frequency != 0 ) + { + isCustomFreq = true; + verify.Frequency = frequency; + if( RegionVerify( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &verify, PHY_FREQUENCY ) == false ) + { + status &= 0xFE; // Channel frequency KO + } + } + + verify.DatarateParams.Datarate = datarate; + verify.DatarateParams.DownlinkDwellTime = Ctx.LoRaMacClassBParams.LoRaMacParams->DownlinkDwellTime; + + if( RegionVerify( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &verify, PHY_RX_DR ) == false ) + { + status &= 0xFD; // Datarate range KO + } + + if( status == 0x03 ) + { + if( isCustomFreq == true ) + { + Ctx.NvmCtx->PingSlotCtx.Ctrl.CustomFreq = 1; + Ctx.NvmCtx->PingSlotCtx.Frequency = frequency; + } + else + { + Ctx.NvmCtx->PingSlotCtx.Ctrl.CustomFreq = 0; + Ctx.NvmCtx->PingSlotCtx.Frequency = 0; + } + Ctx.NvmCtx->PingSlotCtx.Datarate = datarate; + NvmContextChange( ); + } + + return status; +#else + return 0; +#endif // LORAMAC_CLASSB_ENABLED +} + +void LoRaMacClassBBeaconTimingAns( uint16_t beaconTimingDelay, uint8_t beaconTimingChannel, TimerTime_t lastRxDone ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + Ctx.BeaconCtx.BeaconTimingDelay = ( CLASSB_BEACON_DELAY_BEACON_TIMING_ANS * beaconTimingDelay ); + Ctx.BeaconCtx.BeaconTimingChannel = beaconTimingChannel; + + if( LoRaMacConfirmQueueIsCmdActive( MLME_BEACON_TIMING ) == true ) + { + if( Ctx.BeaconCtx.BeaconTimingDelay > CLASSB_BEACON_INTERVAL ) + { + // We missed the beacon already + Ctx.BeaconCtx.BeaconTimingDelay = 0; + Ctx.BeaconCtx.BeaconTimingChannel = 0; + LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_BEACON_NOT_FOUND, MLME_BEACON_TIMING ); + } + else + { + Ctx.BeaconCtx.Ctrl.BeaconDelaySet = 1; + Ctx.BeaconCtx.Ctrl.BeaconChannelSet = 1; + Ctx.BeaconCtx.NextBeaconRx = SysTimeFromMs( lastRxDone + Ctx.BeaconCtx.BeaconTimingDelay ); + LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_OK, MLME_BEACON_TIMING ); + } + + Ctx.LoRaMacClassBParams.MlmeConfirm->BeaconTimingDelay = Ctx.BeaconCtx.BeaconTimingDelay; + Ctx.LoRaMacClassBParams.MlmeConfirm->BeaconTimingChannel = Ctx.BeaconCtx.BeaconTimingChannel; + } +#endif // LORAMAC_CLASSB_ENABLED +} + +void LoRaMacClassBDeviceTimeAns( void ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + + SysTime_t nextBeacon = SysTimeGet( ); + uint32_t currentTimeMs = SysTimeToMs( nextBeacon ); + + nextBeacon.Seconds = nextBeacon.Seconds + ( 128 - ( nextBeacon.Seconds % 128 ) ); + nextBeacon.SubSeconds = 0; + + Ctx.BeaconCtx.NextBeaconRx = nextBeacon; + Ctx.BeaconCtx.LastBeaconRx = SysTimeSub( Ctx.BeaconCtx.NextBeaconRx, ( SysTime_t ){ .Seconds = CLASSB_BEACON_INTERVAL / 1000, .SubSeconds = 0 } ); + + if( LoRaMacConfirmQueueIsCmdActive( MLME_DEVICE_TIME ) == true ) + { + if( currentTimeMs > SysTimeToMs( Ctx.BeaconCtx.NextBeaconRx ) ) + { + // We missed the beacon already + Ctx.BeaconCtx.LastBeaconRx.Seconds = 0; + Ctx.BeaconCtx.LastBeaconRx.SubSeconds = 0; + Ctx.BeaconCtx.NextBeaconRx.Seconds = 0; + Ctx.BeaconCtx.NextBeaconRx.SubSeconds = 0; + LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_BEACON_NOT_FOUND, MLME_DEVICE_TIME ); + } + else + { + Ctx.BeaconCtx.Ctrl.BeaconDelaySet = 1; + Ctx.BeaconCtx.BeaconTimingDelay = SysTimeToMs( Ctx.BeaconCtx.NextBeaconRx ) - currentTimeMs; + Ctx.BeaconCtx.BeaconTime.Seconds = nextBeacon.Seconds - UNIX_GPS_EPOCH_OFFSET - 128; + Ctx.BeaconCtx.BeaconTime.SubSeconds = 0; + LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_OK, MLME_DEVICE_TIME ); + } + } +#endif // LORAMAC_CLASSB_ENABLED +} + +bool LoRaMacClassBBeaconFreqReq( uint32_t frequency ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + VerifyParams_t verify; + + if( frequency != 0 ) + { + verify.Frequency = frequency; + + if( RegionVerify( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &verify, PHY_FREQUENCY ) == true ) + { + Ctx.NvmCtx->BeaconCtx.Ctrl.CustomFreq = 1; + Ctx.NvmCtx->BeaconCtx.Frequency = frequency; + NvmContextChange( ); + return true; + } + } + else + { + Ctx.NvmCtx->BeaconCtx.Ctrl.CustomFreq = 0; + NvmContextChange( ); + return true; + } + return false; +#else + return false; +#endif // LORAMAC_CLASSB_ENABLED +} + +TimerTime_t LoRaMacClassBIsUplinkCollision( TimerTime_t txTimeOnAir ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + TimerTime_t currentTime = TimerGetCurrentTime( ); + TimerTime_t beaconReserved = 0; + TimerTime_t nextBeacon = SysTimeToMs( Ctx.BeaconCtx.NextBeaconRx ); + + beaconReserved = nextBeacon - + CLASSB_BEACON_GUARD - + Ctx.LoRaMacClassBParams.LoRaMacParams->ReceiveDelay1 - + Ctx.LoRaMacClassBParams.LoRaMacParams->ReceiveDelay2 - + txTimeOnAir; + + // Check if the next beacon will be received during the next uplink. + if( ( currentTime >= beaconReserved ) && ( currentTime < ( nextBeacon + CLASSB_BEACON_RESERVED ) ) ) + {// Next beacon will be sent during the next uplink. + return CLASSB_BEACON_RESERVED; + } + return 0; +#else + return 0; +#endif // LORAMAC_CLASSB_ENABLED +} + +void LoRaMacClassBStopRxSlots( void ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + TimerStop( &Ctx.PingSlotTimer ); + TimerStop( &Ctx.MulticastSlotTimer ); + + CRITICAL_SECTION_BEGIN( ); + LoRaMacClassBEvents.Events.PingSlot = 0; + LoRaMacClassBEvents.Events.MulticastSlot = 0; + CRITICAL_SECTION_END( ); +#endif // LORAMAC_CLASSB_ENABLED +} + +void LoRaMacClassBStartRxSlots( void ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + if( Ctx.NvmCtx->PingSlotCtx.Ctrl.Assigned == 1 ) + { + Ctx.PingSlotState = PINGSLOT_STATE_CALC_PING_OFFSET; + TimerSetValue( &Ctx.PingSlotTimer, 1 ); + TimerStart( &Ctx.PingSlotTimer ); + + Ctx.MulticastSlotState = PINGSLOT_STATE_CALC_PING_OFFSET; + TimerSetValue( &Ctx.MulticastSlotTimer, 1 ); + TimerStart( &Ctx.MulticastSlotTimer ); + } +#endif // LORAMAC_CLASSB_ENABLED +} + +void LoRaMacClassBSetMulticastPeriodicity( MulticastCtx_t* multicastChannel ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + if( multicastChannel != NULL ) + { + multicastChannel->PingNb = CalcPingNb( multicastChannel->ChannelParams.RxParams.ClassB.Periodicity ); + multicastChannel->PingPeriod = CalcPingPeriod( multicastChannel->PingNb ); + } +#endif // LORAMAC_CLASSB_ENABLED +} + +void LoRaMacClassBProcess( void ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + LoRaMacClassBEvents_t events; + + CRITICAL_SECTION_BEGIN( ); + events = LoRaMacClassBEvents; + LoRaMacClassBEvents.Value = 0; + CRITICAL_SECTION_END( ); + + if( events.Value != 0 ) + { + if( events.Events.Beacon == 1 ) + { + LoRaMacClassBProcessBeacon( ); + } + if( events.Events.PingSlot == 1 ) + { + LoRaMacClassBProcessPingSlot( ); + } + if( events.Events.MulticastSlot == 1 ) + { + LoRaMacClassBProcessMulticastSlot( ); + } + } +#endif // LORAMAC_CLASSB_ENABLED +} diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacClassB.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacClassB.h new file mode 100644 index 00000000..81b92b12 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacClassB.h @@ -0,0 +1,670 @@ +/*! + * \file LoRaMacClassB.h + * + * \brief LoRa MAC Class B layer implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \defgroup LORAMACCLASSB LoRa MAC Class B layer implementation + * This module specifies the API implementation of the LoRaMAC Class B layer. + * This is a placeholder for a detailed description of the LoRaMac + * layer and the supported features. + * \{ + */ +#ifndef __LORAMACCLASSB_H__ +#define __LORAMACCLASSB_H__ + +#include "systime.h" +#include "LoRaMacTypes.h" + +/*! + * States of the class B beacon acquisition and tracking + */ +typedef enum eBeaconState +{ + /*! + * Initial state to acquire the beacon + */ + BEACON_STATE_ACQUISITION, + /*! + * Beacon acquisition state when a time reference is available + */ + BEACON_STATE_ACQUISITION_BY_TIME, + /*! + * Handles the state when the beacon reception fails + */ + BEACON_STATE_TIMEOUT, + /*! + * Handles the state when the beacon was missed due to an uplink + */ + BEACON_STATE_BEACON_MISSED, + /*! + * Reacquisition state which applies the algorithm to enlarge the reception + * windows + */ + BEACON_STATE_REACQUISITION, + /*! + * The node has locked a beacon successfully + */ + BEACON_STATE_LOCKED, + /*! + * The beacon state machine is stopped due to operations with higher priority + */ + BEACON_STATE_HALT, + /*! + * The node currently operates in the beacon window and is idle. In this + * state, the temperature measurement takes place + */ + BEACON_STATE_IDLE, + /*! + * The node operates in the guard time of class B + */ + BEACON_STATE_GUARD, + /*! + * The node is in receive mode to lock a beacon + */ + BEACON_STATE_RX, + /*! + * The nodes switches the device class + */ + BEACON_STATE_LOST, +}BeaconState_t; + +/*! + * States of the class B ping slot mechanism + */ +typedef enum ePingSlotState +{ + /*! + * Calculation of the ping slot offset + */ + PINGSLOT_STATE_CALC_PING_OFFSET, + /*! + * State to set the timer to open the next ping slot + */ + PINGSLOT_STATE_SET_TIMER, + /*! + * The node is in idle state + */ + PINGSLOT_STATE_IDLE, + /*! + * The node opens up a ping slot window + */ + PINGSLOT_STATE_RX, +}PingSlotState_t; + +/*! + * Class B ping slot context structure + */ +typedef struct sPingSlotContext +{ + + /*! + * Ping slot length time in ms + */ + uint32_t PingSlotWindow; + /*! + * Ping offset + */ + uint16_t PingOffset; + /*! + * Current symbol timeout. The node enlarges this variable in case of beacon + * loss. + */ + uint16_t SymbolTimeout; + /*! + * The multicast channel which will be enabled next. + */ + MulticastCtx_t *NextMulticastChannel; +}PingSlotContext_t; + + +/*! + * Class B beacon context structure + */ +typedef struct sBeaconContext +{ + struct sBeaconCtrl + { + /*! + * Set if the node receives beacons + */ + uint8_t BeaconMode : 1; + /*! + * Set if the node has acquired the beacon + */ + uint8_t BeaconAcquired : 1; + /*! + * Set if a beacon delay was set for the beacon acquisition + */ + uint8_t BeaconDelaySet : 1; + /*! + * Set if a beacon channel was set for the beacon acquisition + */ + uint8_t BeaconChannelSet : 1; + /*! + * Set if beacon acquisition is pending + */ + uint8_t AcquisitionPending : 1; + /*! + * Set if the beacon state machine will be resumed + */ + uint8_t ResumeBeaconing : 1; + }Ctrl; + + /*! + * Current temperature + */ + float Temperature; + /*! + * Beacon time received with the beacon frame + */ + SysTime_t BeaconTime; + /*! + * Time when the last beacon was received + */ + SysTime_t LastBeaconRx; + /*! + * Time when the next beacon will be received + */ + SysTime_t NextBeaconRx; + /*! + * This is the time where the RX window will be opened. + * Its base is NextBeaconRx with temperature compensations + * and RX window movement. + */ + TimerTime_t NextBeaconRxAdjusted; + /*! + * Current symbol timeout. The node enlarges this variable in case of beacon + * loss. + */ + uint16_t SymbolTimeout; + /*! + * Specifies how much time the beacon window will be moved. + */ + TimerTime_t BeaconWindowMovement; + /*! + * Beacon timing channel for next beacon + */ + uint8_t BeaconTimingChannel; + /*! + * Delay for next beacon in ms + */ + TimerTime_t BeaconTimingDelay; + TimerTime_t TimeStamp; +}BeaconContext_t; + +/*! + * Data structure which contains the callbacks + */ +typedef struct sLoRaMacClassBCallback +{ + /*! + * \brief Measures the temperature level + * + * \retval Temperature level + */ + uint16_t ( *GetTemperatureLevel )( void ); + /*! + *\brief Will be called each time a Radio IRQ is handled by the MAC + * layer. + * + *\warning Runs in a IRQ context. Should only change variables state. + */ + void ( *MacProcessNotify )( void ); +}LoRaMacClassBCallback_t; + +/*! + * Data structure which pointers to the properties LoRaMAC + */ +typedef struct sLoRaMacClassBParams +{ + /*! + * Pointer to the MlmeIndication structure + */ + MlmeIndication_t *MlmeIndication; + /*! + * Pointer to the McpsIndication structure + */ + McpsIndication_t *McpsIndication; + /*! + * Pointer to the MlmeConfirm structure + */ + MlmeConfirm_t *MlmeConfirm; + /*! + * Pointer to the LoRaMacFlags structure + */ + LoRaMacFlags_t *LoRaMacFlags; + /*! + * Pointer to the LoRaMac device address + */ + uint32_t *LoRaMacDevAddr; + /*! + * Pointer to the LoRaMac region definition + */ + LoRaMacRegion_t *LoRaMacRegion; + /*! + * Pointer to the LoRaMacParams structure + */ + LoRaMacParams_t *LoRaMacParams; + /*! + * Pointer to the multicast channel list + */ + MulticastCtx_t *MulticastChannels; +}LoRaMacClassBParams_t; + + +/*! + * Signature of callback function to be called by this module when the + * non-volatile needs to be saved. + */ +typedef void ( *LoRaMacClassBNvmEvent )( void ); + +/*! + * \brief Initialize LoRaWAN Class B + * + * \param [IN] classBParams Information and feedback parameter + * \param [IN] callbacks Contains the callback which the Class B implementation needs + * \param [IN] callback function which will be called when the non-volatile context needs to be saved. + */ +void LoRaMacClassBInit( LoRaMacClassBParams_t *classBParams, LoRaMacClassBCallback_t *callbacks, LoRaMacClassBNvmEvent classBNvmCtxChanged ); + +/*! + * Restores the internal non-volatile context from passed pointer. + * + * \param [IN] classBNvmCtx - Pointer to non-volatile class B module context to be restored. + * + * \retval - Status of the operation + */ +bool LoRaMacClassBRestoreNvmCtx( void* classBNvmCtx ); + +/*! + * Returns a pointer to the internal non-volatile context. + * + * \param [IN] classBNvmCtxSize - Size of the module non-volatile context + * + * \retval - Points to a structure where the module store its non-volatile context + */ +void* LoRaMacClassBGetNvmCtx( size_t* classBNvmCtxSize ); + +/*! + * \brief Set the state of the beacon state machine + * + * \param [IN] beaconState Beacon state. + */ +void LoRaMacClassBSetBeaconState( BeaconState_t beaconState ); + +/*! + * \brief Set the state of the ping slot state machine + * + * \param [IN] pingSlotState Ping slot state. + */ +void LoRaMacClassBSetPingSlotState( PingSlotState_t pingSlotState ); + +/*! + * \brief Set the state of the multicast slot state machine + * + * \param [IN] pingSlotState multicast slot state. + */ +void LoRaMacClassBSetMulticastSlotState( PingSlotState_t multicastSlotState ); + +/*! + * \brief Verifies if an acquisition procedure is in progress + * + * \retval [true, if the acquisition is in progress; false, if not] + */ +bool LoRaMacClassBIsAcquisitionInProgress( void ); + +/*! + * \brief State machine of the Class B for beaconing + */ +void LoRaMacClassBBeaconTimerEvent( void* context ); + +/*! + * \brief State machine of the Class B for ping slots + */ +void LoRaMacClassBPingSlotTimerEvent( void* context ); + +/*! + * \brief State machine of the Class B for multicast slots + */ +void LoRaMacClassBMulticastSlotTimerEvent( void* context ); + +/*! + * \brief Receives and decodes the beacon frame + * + * \param [IN] payload Pointer to the payload + * \param [IN] size Size of the payload + * \retval [true, if the node has received a beacon; false, if not] + */ +bool LoRaMacClassBRxBeacon( uint8_t *payload, uint16_t size ); + +/*! + * \brief The function validates, if the node expects a beacon + * at the current time. + * + * \retval [true, if the node expects a beacon; false, if not] + */ +bool LoRaMacClassBIsBeaconExpected( void ); + +/*! + * \brief The function validates, if the node expects a ping slot + * at the current time. + * + * \retval [true, if the node expects a ping slot; false, if not] + */ +bool LoRaMacClassBIsPingExpected( void ); + +/*! + * \brief The function validates, if the node expects a multicast slot + * at the current time. + * + * \retval [true, if the node expects a multicast slot; false, if not] + */ +bool LoRaMacClassBIsMulticastExpected( void ); + +/*! + * \brief Verifies if the acquisition pending bit is set + * + * \retval [true, if the bit is set; false, if not] + */ +bool LoRaMacClassBIsAcquisitionPending( void ); + +/*! + * \brief Verifies if the beacon mode active bit is set + * + * \retval [true, if the bit is set; false, if not] + */ +bool LoRaMacClassBIsBeaconModeActive( void ); + +/*! + * \brief Stops the beacon and ping slot operation + */ +void LoRaMacClassBHaltBeaconing( void ); + +/*! + * \brief Resumes the beacon and ping slot operation + */ +void LoRaMacClassBResumeBeaconing( void ); + +/*! + * \brief Sets the periodicity of the ping slots + * + * \param [IN] periodicity Periodicity + */ +void LoRaMacClassBSetPingSlotInfo( uint8_t periodicity ); + +/*! + * \brief Switches the device class + * + * \param [IN] nextClass Device class to switch to + * + * \retval LoRaMacStatus_t Status of the operation. + */ +LoRaMacStatus_t LoRaMacClassBSwitchClass( DeviceClass_t nextClass ); + +/*! + * \brief LoRaMAC ClassB MIB-Get + * + * \details The mac information base service to get attributes of the LoRaMac + * Class B layer. + * + * \param [IN] mibRequest - MIB-GET-Request to perform. Refer to \ref MibRequestConfirm_t. + * + * \retval LoRaMacStatus_t Status of the operation. Possible returns are: + * \ref LORAMAC_STATUS_OK, + * \ref LORAMAC_STATUS_SERVICE_UNKNOWN, + * \ref LORAMAC_STATUS_PARAMETER_INVALID. + */ +LoRaMacStatus_t LoRaMacClassBMibGetRequestConfirm( MibRequestConfirm_t *mibGet ); + +/*! + * \brief LoRaMAC Class B MIB-Set + * + * \details The mac information base service to set attributes of the LoRaMac + * Class B layer. + * + * \param [IN] mibRequest - MIB-SET-Request to perform. Refer to \ref MibRequestConfirm_t. + * + * \retval LoRaMacStatus_t Status of the operation. Possible returns are: + * \ref LORAMAC_STATUS_OK, + * \ref LORAMAC_STATUS_BUSY, + * \ref LORAMAC_STATUS_SERVICE_UNKNOWN, + * \ref LORAMAC_STATUS_PARAMETER_INVALID. + */ +LoRaMacStatus_t LoRaMacMibClassBSetRequestConfirm( MibRequestConfirm_t *mibSet ); + +/*! + * \brief This function handles the PING_SLOT_FREQ_ANS + */ +void LoRaMacClassBPingSlotInfoAns( void ); + +/*! + * \brief This function handles the PING_SLOT_CHANNEL_REQ + * + * \param [IN] datarate Device class to switch to + * \param [IN] frequency Device class to switch to + * + * \retval Status for the MAC answer. + */ +uint8_t LoRaMacClassBPingSlotChannelReq( uint8_t datarate, uint32_t frequency ); + +/*! + * \brief This function handles the BEACON_TIMING_ANS + * + * \param [IN] beaconTimingDelay The beacon timing delay + * \param [IN] beaconTimingChannel The beacon timing channel + * \param [IN] lastRxDone The time of the last frame reception + */ +void LoRaMacClassBBeaconTimingAns( uint16_t beaconTimingDelay, uint8_t beaconTimingChannel, TimerTime_t lastRxDone ); + +/*! + * \brief This function handles the ClassB DEVICE_TIME_ANS + */ +void LoRaMacClassBDeviceTimeAns( void ); + +/*! + * \brief This function handles the BEACON_FREQ_REQ + * + * \param [IN] frequency Frequency to set + * + * \retval [true, if MAC shall send an answer; false, if not] + */ +bool LoRaMacClassBBeaconFreqReq( uint32_t frequency ); + +/*! + * \brief Queries the ping slot window time + * + * \param [IN] txTimeOnAir TX time on air for the next uplink + * + * \retval Returns the time the uplink should be delayed + */ +TimerTime_t LoRaMacClassBIsUplinkCollision( TimerTime_t txTimeOnAir ); + +/*! + * \brief Stops the timers for the RX slots. This includes the + * timers for ping and multicast slots. + */ +void LoRaMacClassBStopRxSlots( void ); + +/*! + * \brief Starts the timers for the RX slots. This includes the + * timers for ping and multicast slots. + */ +void LoRaMacClassBStartRxSlots( void ); + +/*! + * \brief Starts the timers for the RX slots. This includes the + * timers for ping and multicast slots. + * + * \param [IN] periodicity Downlink periodicity + * + * \param [IN] multicastChannel Related multicast channel + */ +void LoRaMacClassBSetMulticastPeriodicity( MulticastCtx_t* multicastChannel ); + +void LoRaMacClassBProcess( void ); + + +/* + * LoRaMac Class B Context structure for NVM parameters + * related to ping slots + */ +typedef struct sLoRaMacClassBPingSlotNvmCtx +{ + struct sPingSlotCtrlNvm + { + /*! + * Set when the server assigned a ping slot to the node + */ + uint8_t Assigned : 1; + /*! + * Set when a custom frequency is used + */ + uint8_t CustomFreq : 1; + }Ctrl; + /*! + * Number of ping slots + */ + uint8_t PingNb; + /*! + * Period of the ping slots + */ + uint16_t PingPeriod; + /*! + * Reception frequency of the ping slot windows + */ + uint32_t Frequency; + /*! + * Datarate of the ping slot + */ + int8_t Datarate; +} LoRaMacClassBPingSlotNvmCtx_t; + +/* + * LoRaMac Class B Context structure for NVM parameters + * related to beaconing + */ +typedef struct sLoRaMacClassBBeaconNvmCtx +{ + struct sBeaconCtrlNvm + { + /*! + * Set if the node has a custom frequency for beaconing and ping slots + */ + uint8_t CustomFreq : 1; + }Ctrl; + /*! + * Beacon reception frequency + */ + uint32_t Frequency; + /*! + * State of the beaconing mechanism + */ + BeaconState_t BeaconState; + /*! + * Time when the last beacon was received + */ + SysTime_t LastBeaconRx; + /*! + * Data structure for the gateway specific part. The + * content of the values may differ for each gateway + */ + struct sGwSpecific GwSpecific; +} LoRaMacClassBBeaconNvmCtx_t; + +/* + * LoRaMac Class B Context structure + */ +typedef struct sLoRaMacClassBNvmCtx +{ + /*! + * Class B ping slot context + */ + LoRaMacClassBPingSlotNvmCtx_t PingSlotCtx; + /*! + * Class B beacon context + */ + LoRaMacClassBBeaconNvmCtx_t BeaconCtx; +} LoRaMacClassBNvmCtx_t; + +/* + * LoRaMac Class B Context structure + */ +typedef struct sLoRaMacClassBCtx +{ + /*! + * Class B ping slot context + */ + PingSlotContext_t PingSlotCtx; + /*! + * Class B beacon context + */ + BeaconContext_t BeaconCtx; + /*! + * State of the beaconing mechanism + */ + BeaconState_t BeaconState; + /*! + * State of the ping slot mechanism + */ + PingSlotState_t PingSlotState; + /*! + * State of the multicast slot mechanism + */ + PingSlotState_t MulticastSlotState; + /*! + * Timer for CLASS B beacon acquisition and tracking. + */ + TimerEvent_t BeaconTimer; + /*! + * Timer for CLASS B ping slot timer. + */ + TimerEvent_t PingSlotTimer; + /*! + * Timer for CLASS B multicast ping slot timer. + */ + TimerEvent_t MulticastSlotTimer; + /*! + * Container for the callbacks related to class b. + */ + LoRaMacClassBCallback_t LoRaMacClassBCallbacks; + /*! + * Data structure which holds the parameters which needs to be set + * in class b operation. + */ + LoRaMacClassBParams_t LoRaMacClassBParams; + /* + * Callback function to notify the upper layer about context change + */ + LoRaMacClassBNvmEvent LoRaMacClassBNvmEvent; + /*! + * Non-volatile module context. + */ + LoRaMacClassBNvmCtx_t* NvmCtx; +} LoRaMacClassBCtx_t; + +#endif // __LORAMACCLASSB_H__ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacClassBConfig.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacClassBConfig.h new file mode 100644 index 00000000..b1b089c6 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacClassBConfig.h @@ -0,0 +1,115 @@ +/*! + * \file LoRaMacClassBConfig.h + * + * \brief LoRa MAC Class B configuration + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \defgroup LORAMACCLASSB LoRa MAC Class B configuration + * This header file contains parameters to configure the class b operation. + * By default, all parameters are set according to the specification. + * \{ + */ +#ifndef __LORAMACCLASSBCONFIG_H__ +#define __LORAMACCLASSBCONFIG_H__ + +/*! + * Defines the beacon interval in ms + */ +#define CLASSB_BEACON_INTERVAL 128000 + +/*! + * Beacon reserved time in ms + */ +#define CLASSB_BEACON_RESERVED 2120 + +/*! + * Beacon guard time in ms + */ +#define CLASSB_BEACON_GUARD 3000 + +/*! + * Beacon window time in ms + */ +#define CLASSB_BEACON_WINDOW 122880 + +/*! + * Beacon window time in numer of slots + */ +#define CLASSB_BEACON_WINDOW_SLOTS 4096 + +/*! + * Ping slot length time in ms + */ +#define CLASSB_PING_SLOT_WINDOW 30 + +/*! + * Maximum allowed beacon less time in ms + */ +#define CLASSB_MAX_BEACON_LESS_PERIOD 7200000 + +/*! + * Delay time for the BeaconTimingAns in ms + */ +#define CLASSB_BEACON_DELAY_BEACON_TIMING_ANS 30 + +/*! + * Default symbol timeout for beacons and ping slot windows + */ +#define CLASSB_BEACON_SYMBOL_TO_DEFAULT 8 + +/*! + * Maximum symbol timeout for beacons + */ +#define CLASSB_BEACON_SYMBOL_TO_EXPANSION_MAX 255 + +/*! + * Maximum symbol timeout for ping slots + */ +#define CLASSB_PING_SLOT_SYMBOL_TO_EXPANSION_MAX 30 + +/*! + * Symbol expansion value for beacon windows in case of beacon + * loss in symbols + */ +#define CLASSB_BEACON_SYMBOL_TO_EXPANSION_FACTOR 2 + +/*! + * Defines the default window movement time + */ +#define CLASSB_WINDOW_MOVE_DEFAULT 2 + +/*! + * Defines the maximum time for the beacon movement + */ +#define CLASSB_WINDOW_MOVE_EXPANSION_MAX 256 + +/*! + * Defines the expansion factor for the beacon movement + */ +#define CLASSB_WINDOW_MOVE_EXPANSION_FACTOR 2 + +#endif // __LORAMACCLASSBCONFIG_H__ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacCommands.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacCommands.c new file mode 100644 index 00000000..b330e714 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacCommands.c @@ -0,0 +1,547 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + ___ _____ _ ___ _ _____ ___ ___ ___ ___ +/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| +\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| +|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| +embedded.connectivity.solutions=============== + +Description: LoRa MAC commands + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis ( Semtech ), Daniel Jaeckle ( STACKFORCE ), Johannes Bruder ( STACKFORCE ) +*/ + +#include "utilities.h" +#include "LoRaMacCommands.h" +#include "LoRaMacConfirmQueue.h" + +/*! + * Number of MAC Command slots + */ +#define NUM_OF_MAC_COMMANDS 15 + +/*! + * Size of the CID field of MAC commands + */ +#define CID_FIELD_SIZE 1 + +/*! + * Mac Commands list structure + */ +typedef struct sMacCommandsList +{ + /* + * First element of MAC command list. + */ + MacCommand_t* First; + /* + * Last element of MAC command list. + */ + MacCommand_t* Last; +} MacCommandsList_t; + +/*! + * LoRaMac Commands Context structure + */ +typedef struct sLoRaMacCommandsCtx +{ + /* + * List of MAC command elements + */ + MacCommandsList_t MacCommandList; + /* + * Buffer to store MAC command elements + */ + MacCommand_t MacCommandSlots[NUM_OF_MAC_COMMANDS]; + /* + * Size of all MAC commands serialized as buffer + */ + size_t SerializedCmdsSize; +} LoRaMacCommandsCtx_t; + +/*! + * Callback function to notify the upper layer about context change + */ +static LoRaMacCommandsNvmEvent CommandsNvmCtxChanged; + +/*! + * Non-volatile module context. + */ +static LoRaMacCommandsCtx_t NvmCtx; + +/* Memory management functions */ + +/*! + * \brief Determines if a MAC command slot is free + * + * \param[IN] slot - Slot to check + * \retval - Status of the operation + */ +static bool IsSlotFree( const MacCommand_t* slot ) +{ + uint8_t* mem = ( uint8_t* )slot; + + for( uint16_t size = 0; size < sizeof( MacCommand_t ); size++ ) + { + if( mem[size] != 0x00 ) + { + return false; + } + } + return true; +} + +/*! + * \brief Allocates a new MAC command memory slot + * + * \retval - Pointer to slot + */ +static MacCommand_t* MallocNewMacCommandSlot( void ) +{ + uint8_t itr = 0; + + while( IsSlotFree( ( const MacCommand_t* )&NvmCtx.MacCommandSlots[itr] ) == false ) + { + itr++; + if( itr == NUM_OF_MAC_COMMANDS ) + { + return 0; + } + } + + return &NvmCtx.MacCommandSlots[itr]; +} + +/*! + * \brief Free memory slot + * + * \param[IN] slot - Slot to free + * + * \retval - Status of the operation + */ +static bool FreeMacCommandSlot( MacCommand_t* slot ) +{ + if( slot == 0 ) + { + return false; + } + + memset1( ( uint8_t* )slot, 0x00, sizeof( MacCommand_t ) ); + + return true; +} + +/* Linked list functions */ + +/*! + * \brief Initialize list + * + * \param[IN] list - List that shall be initialized + * \retval - Status of the operation + */ +static bool LinkedListInit( MacCommandsList_t* list ) +{ + if( list == 0 ) + { + return false; + } + + list->First = 0; + list->Last = 0; + + return true; +} + +/*! + * \brief Add an element to the list + * + * \param[IN] list - List where the element shall be added. + * \param[IN] element - Element to add + * \retval - Status of the operation + */ +static bool LinkedListAdd( MacCommandsList_t* list, MacCommand_t* element ) +{ + if( ( list == 0 ) && ( element == 0 ) ) + { + return false; + } + + // Check if this is the first entry to enter the list. + if( list->First == 0 ) + { + list->First = element; + } + + // Check if the last entry exists and update its next point. + if( list->Last ) + { + list->Last->Next = element; + } + + // Update the next point of this entry. + element->Next = 0; + + // Update the last entry of the list. + list->Last = element; + + return true; +} + +/*! + * \brief Return the previous element in the list. + * + * \param[IN] list - List + * \param[IN] element - Element where the previous element shall be searched + * \retval - Status of the operation + */ +static MacCommand_t* LinkedListGetPrevious( MacCommandsList_t* list, MacCommand_t* element ) +{ + if( ( list == 0 ) && ( element == 0 ) ) + { + return NULL; + } + + MacCommand_t* curElement; + + // Start at the head of the list + curElement = list->First; + + // When current element is the first of the list, there's no previous element so we can return NULL immediately. + if( element != curElement ) + { + // Loop through all elements until the end is reached or the next of current is the current element. + while( ( curElement != NULL ) && ( curElement->Next != element ) ) + { + curElement = curElement->Next; + } + } + else + { + curElement = NULL; + } + + return curElement; +} + +/*! + * \brief Remove an element from the list + * + * \param[IN] list - List where the element shall be removed from. + * \param[IN] element - Element to remove + * \retval - Status of the operation + */ +static bool LinkedListRemove( MacCommandsList_t* list, MacCommand_t* element ) +{ + if( ( list == 0 ) && ( element == 0 ) ) + { + return false; + } + + MacCommand_t* PrevElement = LinkedListGetPrevious( list, element ); + + if( list->First == element ) + { + list->First = element->Next; + } + + if( list->Last == element ) + { + list->Last = PrevElement; + } + + if( PrevElement != NULL ) + { + PrevElement->Next = element->Next; + } + + element->Next = NULL; + + return true; +} + +/* + * \brief Determines if a MAC command is sticky or not + * + * \param[IN] cid - MAC command identifier + * + * \retval - Status of the operation + */ +static bool IsSticky( uint8_t cid ) +{ + switch( cid ) + { + case MOTE_MAC_DL_CHANNEL_ANS: + case MOTE_MAC_RX_PARAM_SETUP_ANS: + case MOTE_MAC_RX_TIMING_SETUP_ANS: + return true; + default: + return false; + } +} + +/* + * \brief Wrapper function for the NvmCtx + */ +static void NvmCtxCallback( void ) +{ + if( CommandsNvmCtxChanged != NULL ) + { + CommandsNvmCtxChanged( ); + } +} + +LoRaMacCommandStatus_t LoRaMacCommandsInit( LoRaMacCommandsNvmEvent commandsNvmCtxChanged ) +{ + // Initialize with default + memset1( ( uint8_t* )&NvmCtx, 0, sizeof( NvmCtx ) ); + + LinkedListInit( &NvmCtx.MacCommandList ); + + // Assign callback + CommandsNvmCtxChanged = commandsNvmCtxChanged; + + return LORAMAC_COMMANDS_SUCCESS; +} + +LoRaMacCommandStatus_t LoRaMacCommandsRestoreNvmCtx( void* commandsNvmCtx ) +{ + // Restore module context + if( commandsNvmCtx != NULL ) + { + memcpy1( ( uint8_t* )&NvmCtx, ( uint8_t* )commandsNvmCtx, sizeof( NvmCtx ) ); + return LORAMAC_COMMANDS_SUCCESS; + } + else + { + return LORAMAC_COMMANDS_ERROR_NPE; + } +} + +void* LoRaMacCommandsGetNvmCtx( size_t* commandsNvmCtxSize ) +{ + *commandsNvmCtxSize = sizeof( NvmCtx ); + return &NvmCtx; +} + +LoRaMacCommandStatus_t LoRaMacCommandsAddCmd( uint8_t cid, uint8_t* payload, size_t payloadSize ) +{ + if( payload == 0 ) + { + return LORAMAC_COMMANDS_ERROR_NPE; + } + MacCommand_t* newCmd; + + // Allocate a memory slot + newCmd = MallocNewMacCommandSlot( ); + + if( newCmd == 0 ) + { + return LORAMAC_COMMANDS_ERROR_MEMORY; + } + + // Add it to the list of Mac commands + if( LinkedListAdd( &NvmCtx.MacCommandList, newCmd ) == false ) + { + return LORAMAC_COMMANDS_ERROR; + } + + // Set Values + newCmd->CID = cid; + newCmd->PayloadSize = payloadSize; + memcpy1( ( uint8_t* )newCmd->Payload, payload, payloadSize ); + newCmd->IsSticky = IsSticky( cid ); + + NvmCtx.SerializedCmdsSize += ( CID_FIELD_SIZE + payloadSize ); + + NvmCtxCallback( ); + + return LORAMAC_COMMANDS_SUCCESS; +} + +LoRaMacCommandStatus_t LoRaMacCommandsRemoveCmd( MacCommand_t* macCmd ) +{ + if( macCmd == NULL ) + { + return LORAMAC_COMMANDS_ERROR_NPE; + } + + // Remove the Mac command element from MacCommandList + if( LinkedListRemove( &NvmCtx.MacCommandList, macCmd ) == false ) + { + return LORAMAC_COMMANDS_ERROR_CMD_NOT_FOUND; + } + + NvmCtx.SerializedCmdsSize -= ( CID_FIELD_SIZE + macCmd->PayloadSize ); + + // Free the MacCommand Slot + if( FreeMacCommandSlot( macCmd ) == false ) + { + return LORAMAC_COMMANDS_ERROR; + } + + NvmCtxCallback( ); + + return LORAMAC_COMMANDS_SUCCESS; +} + +LoRaMacCommandStatus_t LoRaMacCommandsGetCmd( uint8_t cid, MacCommand_t** macCmd ) +{ + MacCommand_t* curElement; + + // Start at the head of the list + curElement = NvmCtx.MacCommandList.First; + + // Loop through all elements until we find the element with the given CID + while( ( curElement != NULL ) && ( curElement->CID != cid ) ) + { + curElement = curElement->Next; + } + + // Update the pointer anyway + *macCmd = curElement; + + // Handle error in case if we reached the end without finding it. + if( curElement == NULL ) + { + return LORAMAC_COMMANDS_ERROR_CMD_NOT_FOUND; + } + return LORAMAC_COMMANDS_SUCCESS; +} + +LoRaMacCommandStatus_t LoRaMacCommandsRemoveNoneStickyCmds( void ) +{ + MacCommand_t* curElement; + MacCommand_t* nexElement; + + // Start at the head of the list + curElement = NvmCtx.MacCommandList.First; + + // Loop through all elements + while( curElement != NULL ) + { + if( curElement->IsSticky == false ) + { + nexElement = curElement->Next; + LoRaMacCommandsRemoveCmd( curElement ); + curElement = nexElement; + } + else + { + curElement = curElement->Next; + } + } + + NvmCtxCallback( ); + + return LORAMAC_COMMANDS_SUCCESS; +} + +LoRaMacCommandStatus_t LoRaMacCommandsRemoveStickyAnsCmds( void ) +{ + MacCommand_t* curElement; + MacCommand_t* nexElement; + + // Start at the head of the list + curElement = NvmCtx.MacCommandList.First; + + // Loop through all elements + while( curElement != NULL ) + { + nexElement = curElement->Next; + if( IsSticky( curElement->CID ) == true ) + { + LoRaMacCommandsRemoveCmd( curElement ); + } + curElement = nexElement; + } + + NvmCtxCallback( ); + + return LORAMAC_COMMANDS_SUCCESS; +} + +LoRaMacCommandStatus_t LoRaMacCommandsGetSizeSerializedCmds( size_t* size ) +{ + if( size == NULL ) + { + return LORAMAC_COMMANDS_ERROR_NPE; + } + *size = NvmCtx.SerializedCmdsSize; + return LORAMAC_COMMANDS_SUCCESS; +} + +LoRaMacCommandStatus_t LoRaMacCommandsSerializeCmds( size_t availableSize, size_t* effectiveSize, uint8_t* buffer ) +{ + MacCommand_t* curElement = NvmCtx.MacCommandList.First; + MacCommand_t* nextElement; + uint8_t itr = 0; + + if( ( buffer == NULL ) || ( effectiveSize == NULL ) ) + { + return LORAMAC_COMMANDS_ERROR_NPE; + } + + // Loop through all elements which fits into the buffer + while( curElement != NULL ) + { + // If the next MAC command still fits into the buffer, add it. + if( ( availableSize - itr ) >= ( CID_FIELD_SIZE + curElement->PayloadSize ) ) + { + buffer[itr++] = curElement->CID; + memcpy1( &buffer[itr], curElement->Payload, curElement->PayloadSize ); + itr += curElement->PayloadSize; + } + else + { + break; + } + curElement = curElement->Next; + } + + // Remove all commands which do not fit into the buffer + while( curElement != NULL ) + { + // Store the next element before removing the current one + nextElement = curElement->Next; + LoRaMacCommandsRemoveCmd( curElement ); + curElement = nextElement; + } + + // Fetch the effective size of the mac commands + LoRaMacCommandsGetSizeSerializedCmds( effectiveSize ); + + return LORAMAC_COMMANDS_SUCCESS; +} + +LoRaMacCommandStatus_t LoRaMacCommandsStickyCmdsPending( bool* cmdsPending ) +{ + if( cmdsPending == NULL ) + { + return LORAMAC_COMMANDS_ERROR_NPE; + } + MacCommand_t* curElement; + curElement = NvmCtx.MacCommandList.First; + + *cmdsPending = false; + + // Loop through all elements + while( curElement != NULL ) + { + if( curElement->IsSticky == true ) + { + // Found one sticky MAC command + *cmdsPending = true; + return LORAMAC_COMMANDS_SUCCESS; + } + curElement = curElement->Next; + } + + return LORAMAC_COMMANDS_SUCCESS; +} diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacCommands.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacCommands.h new file mode 100644 index 00000000..6e411ba7 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacCommands.h @@ -0,0 +1,218 @@ +/*! + * \file LoRaMacCommands.h + * + * \brief LoRa MAC commands + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + * + * addtogroup LORAMAC + * \{ + * + */ +#ifndef __LORAMAC_COMMANDS_H__ +#define __LORAMAC_COMMANDS_H__ + +#include +#include +#include "LoRaMacTypes.h" + + +/* + * Number of MAC Command slots + */ +#define LORAMAC_COMMADS_MAX_NUM_OF_PARAMS 2 + +/*! + * LoRaWAN MAC Command element + */ +typedef struct sMacCommand MacCommand_t; + +struct sMacCommand +{ + /*! + * The pointer to the next MAC Command element in the list + */ + MacCommand_t* Next; + /*! + * MAC command identifier + */ + uint8_t CID; + /*! + * MAC command payload + */ + uint8_t Payload[LORAMAC_COMMADS_MAX_NUM_OF_PARAMS]; + /*! + * Size of MAC command payload + */ + size_t PayloadSize; + /*! + * Indicates if it's a sticky MAC command + */ + bool IsSticky; +}; + +/*! + * LoRaMac Commands Status + */ +typedef enum eLoRaMacCommandsStatus +{ + /*! + * No error occurred + */ + LORAMAC_COMMANDS_SUCCESS = 0, + /*! + * Null pointer exception + */ + LORAMAC_COMMANDS_ERROR_NPE, + /*! + * There is no memory left to add a further MAC command + */ + LORAMAC_COMMANDS_ERROR_MEMORY, + /*! + * MAC command not found. + */ + LORAMAC_COMMANDS_ERROR_CMD_NOT_FOUND, + /*! + * Unknown or corrupted command error occurred. + */ + LORAMAC_COMMANDS_ERROR_UNKNOWN_CMD, + /*! + * Undefined Error occurred + */ + LORAMAC_COMMANDS_ERROR, +}LoRaMacCommandStatus_t; + +/*! + * Signature of callback function to be called by this module when the + * non-volatile needs to be saved. + */ +typedef void ( *LoRaMacCommandsNvmEvent )( void ); + +/*! + * \brief Initialization of LoRaMac MAC commands module + * + * \param[IN] commandsNvmCtxChanged - Callback function which will be called when the + * non-volatile context needs to be saved. + * + * \retval - Status of the operation + */ +LoRaMacCommandStatus_t LoRaMacCommandsInit( LoRaMacCommandsNvmEvent commandsNvmCtxChanged ); + +/*! + * Restores the internal non-volatile context from passed pointer. + * + * \param[IN] commandsNvmCtx - Pointer to non-volatile MAC commands module context to be restored. + * + * \retval - Status of the operation + */ +LoRaMacCommandStatus_t LoRaMacCommandsRestoreNvmCtx( void* commandsNvmCtx ); + +/*! + * Returns a pointer to the internal non-volatile context. + * + * \param[IN] commandsNvmCtxSize - Size of the module non-volatile context + * + * \retval - Points to a structure where the module store its non-volatile context + */ +void* LoRaMacCommandsGetNvmCtx( size_t* commandsNvmCtxSize ); + +/*! + * \brief Adds a new MAC command to be sent. + * + * \param[IN] cid - MAC command identifier + * \param[IN] payload - MAC command payload containing parameters + * \param[IN] payloadSize - Size of MAC command payload + * + * \retval - Status of the operation + */ +LoRaMacCommandStatus_t LoRaMacCommandsAddCmd( uint8_t cid, uint8_t* payload, size_t payloadSize ); + +/*! + * \brief Remove a MAC command. + * + * \param[OUT] cmd - MAC command + * + * \retval - Status of the operation + */ +LoRaMacCommandStatus_t LoRaMacCommandsRemoveCmd( MacCommand_t* macCmd ); + +/*! + * \brief Get the MAC command with corresponding CID. + * + * \param[IN] cid - MAC command identifier + * \param[OUT] cmd - MAC command + * + * \retval - Status of the operation + */ +LoRaMacCommandStatus_t LoRaMacCommandsGetCmd( uint8_t cid, MacCommand_t** macCmd ); + +/*! + * \brief Remove all none sticky MAC commands. + * + * \retval - Status of the operation + */ +LoRaMacCommandStatus_t LoRaMacCommandsRemoveNoneStickyCmds( void ); + +/*! + * \brief Remove all sticky answer MAC commands. + * + * \retval - Status of the operation + */ +LoRaMacCommandStatus_t LoRaMacCommandsRemoveStickyAnsCmds( void ); + +/*! + * \brief Get size of all MAC commands serialized as buffer + * + * \param[out] size - Available size of memory for MAC commands + * + * \retval - Status of the operation + */ +LoRaMacCommandStatus_t LoRaMacCommandsGetSizeSerializedCmds( size_t* size ); + +/*! + * \brief Get as many as possible MAC commands serialized + * + * \param[IN] availableSize - Available size of memory for MAC commands + * \param[out] effectiveSize - Size of memory which was effectively used for serializing. + * \param[out] buffer - Destination data buffer + * + * \retval - Status of the operation + */ +LoRaMacCommandStatus_t LoRaMacCommandsSerializeCmds( size_t availableSize, size_t* effectiveSize, uint8_t* buffer ); + +/*! + * \brief Determines if there are sticky MAC commands pending. + * + * \param[IN] cmdsPending - Indicates if there are sticky MAC commands in the queue. + * + * \retval - Status of the operation + */ +LoRaMacCommandStatus_t LoRaMacCommandsStickyCmdsPending( bool* cmdsPending ); + +/*! \} addtogroup LORAMAC */ + +#endif // __LORAMAC_COMMANDS_H__ + diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacConfirmQueue.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacConfirmQueue.c new file mode 100644 index 00000000..11698448 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacConfirmQueue.c @@ -0,0 +1,344 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + ___ _____ _ ___ _ _____ ___ ___ ___ ___ +/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| +\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| +|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| +embedded.connectivity.solutions=============== + +Description: LoRa MAC confirm queue implementation + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE ) +*/ +#include +#include +#include + +#include "timer.h" +#include "utilities.h" +#include "LoRaMac.h" +#include "LoRaMacConfirmQueue.h" + + +/* + * LoRaMac Confirm Queue Context NVM structure + */ +typedef struct sLoRaMacConfirmQueueNvmCtx +{ + /*! + * MlmeConfirm queue data structure + */ + MlmeConfirmQueue_t MlmeConfirmQueue[LORA_MAC_MLME_CONFIRM_QUEUE_LEN]; + /*! + * Counts the number of MlmeConfirms to process + */ + uint8_t MlmeConfirmQueueCnt; + /*! + * Variable which holds a common status + */ + LoRaMacEventInfoStatus_t CommonStatus; +} LoRaMacConfirmQueueNvmCtx_t; + +/* + * LoRaMac Confirm Queue Context structure + */ +typedef struct sLoRaMacConfirmQueueCtx +{ + /*! + * LoRaMac callback function primitives + */ + LoRaMacPrimitives_t* Primitives; + /*! + * Pointer to the first element of the ring buffer + */ + MlmeConfirmQueue_t* BufferStart; + /*! + * Pointer to the last element of the ring buffer + */ + MlmeConfirmQueue_t* BufferEnd; + /* + * Callback function to notify the upper layer about context change + */ + LoRaMacConfirmQueueNvmEvent LoRaMacConfirmQueueNvmEvent; + /*! + * Non-volatile module context. + */ + LoRaMacConfirmQueueNvmCtx_t* ConfirmQueueNvmCtx; +} LoRaMacConfirmQueueCtx_t; + +/* + * Non-volatile module context. + */ +static LoRaMacConfirmQueueNvmCtx_t ConfirmQueueNvmCtx; + +/* + * Module context. + */ +static LoRaMacConfirmQueueCtx_t ConfirmQueueCtx; + +static MlmeConfirmQueue_t* IncreaseBufferPointer( MlmeConfirmQueue_t* bufferPointer ) +{ + if( bufferPointer == &ConfirmQueueCtx.ConfirmQueueNvmCtx->MlmeConfirmQueue[LORA_MAC_MLME_CONFIRM_QUEUE_LEN - 1] ) + { + // Reset to the first element + bufferPointer = ConfirmQueueCtx.ConfirmQueueNvmCtx->MlmeConfirmQueue; + } + else + { + // Increase + bufferPointer++; + } + return bufferPointer; +} + +static MlmeConfirmQueue_t* DecreaseBufferPointer( MlmeConfirmQueue_t* bufferPointer ) +{ + if( bufferPointer == ConfirmQueueCtx.ConfirmQueueNvmCtx->MlmeConfirmQueue ) + { + // Reset to the last element + bufferPointer = &ConfirmQueueCtx.ConfirmQueueNvmCtx->MlmeConfirmQueue[LORA_MAC_MLME_CONFIRM_QUEUE_LEN - 1]; + } + else + { + bufferPointer--; + } + return bufferPointer; +} + +static MlmeConfirmQueue_t* GetElement( Mlme_t request, MlmeConfirmQueue_t* bufferStart, MlmeConfirmQueue_t* bufferEnd ) +{ + MlmeConfirmQueue_t* element = bufferStart; + + while( element != bufferEnd ) + { + if( element->Request == request ) + { + // We have found the element + return element; + } + else + { + element = IncreaseBufferPointer( element ); + } + } + return NULL; +} + +void LoRaMacConfirmQueueInit( LoRaMacPrimitives_t* primitives, LoRaMacConfirmQueueNvmEvent confirmQueueNvmCtxChanged ) +{ + ConfirmQueueCtx.Primitives = primitives; + + // Assign nvm context + ConfirmQueueCtx.ConfirmQueueNvmCtx = &ConfirmQueueNvmCtx; + + // Init counter + ConfirmQueueCtx.ConfirmQueueNvmCtx->MlmeConfirmQueueCnt = 0; + + // Init buffer + ConfirmQueueCtx.BufferStart = ConfirmQueueCtx.ConfirmQueueNvmCtx->MlmeConfirmQueue; + ConfirmQueueCtx.BufferEnd = ConfirmQueueCtx.ConfirmQueueNvmCtx->MlmeConfirmQueue; + + memset1( ( uint8_t* )ConfirmQueueCtx.ConfirmQueueNvmCtx->MlmeConfirmQueue, 0xFF, sizeof( ConfirmQueueCtx.ConfirmQueueNvmCtx->MlmeConfirmQueue ) ); + + // Common status + ConfirmQueueCtx.ConfirmQueueNvmCtx->CommonStatus = LORAMAC_EVENT_INFO_STATUS_ERROR; + + // Assign callback + ConfirmQueueCtx.LoRaMacConfirmQueueNvmEvent = confirmQueueNvmCtxChanged; +} + +bool LoRaMacConfirmQueueRestoreNvmCtx( void* confirmQueueNvmCtx ) +{ + // Restore module context + if( confirmQueueNvmCtx != NULL ) + { + memcpy1( ( uint8_t* )&ConfirmQueueNvmCtx, ( uint8_t* ) confirmQueueNvmCtx, sizeof( ConfirmQueueNvmCtx ) ); + return true; + } + else + { + return false; + } +} + +void* LoRaMacConfirmQueueGetNvmCtx( size_t* confirmQueueNvmCtxSize ) +{ + *confirmQueueNvmCtxSize = sizeof( ConfirmQueueNvmCtx ); + return &ConfirmQueueNvmCtx; +} + +bool LoRaMacConfirmQueueAdd( MlmeConfirmQueue_t* mlmeConfirm ) +{ + if( ConfirmQueueCtx.ConfirmQueueNvmCtx->MlmeConfirmQueueCnt >= LORA_MAC_MLME_CONFIRM_QUEUE_LEN ) + { + // Protect the buffer against overwrites + return false; + } + + // Add the element to the ring buffer + ConfirmQueueCtx.BufferEnd->Request = mlmeConfirm->Request; + ConfirmQueueCtx.BufferEnd->Status = mlmeConfirm->Status; + ConfirmQueueCtx.BufferEnd->RestrictCommonReadyToHandle = mlmeConfirm->RestrictCommonReadyToHandle; + ConfirmQueueCtx.BufferEnd->ReadyToHandle = false; + // Increase counter + ConfirmQueueCtx.ConfirmQueueNvmCtx->MlmeConfirmQueueCnt++; + // Update end pointer + ConfirmQueueCtx.BufferEnd = IncreaseBufferPointer( ConfirmQueueCtx.BufferEnd ); + + return true; +} + +bool LoRaMacConfirmQueueRemoveLast( void ) +{ + if( ConfirmQueueCtx.ConfirmQueueNvmCtx->MlmeConfirmQueueCnt == 0 ) + { + return false; + } + + // Increase counter + ConfirmQueueCtx.ConfirmQueueNvmCtx->MlmeConfirmQueueCnt--; + // Update start pointer + ConfirmQueueCtx.BufferEnd = DecreaseBufferPointer( ConfirmQueueCtx.BufferEnd ); + + return true; +} + +bool LoRaMacConfirmQueueRemoveFirst( void ) +{ + if( ConfirmQueueCtx.ConfirmQueueNvmCtx->MlmeConfirmQueueCnt == 0 ) + { + return false; + } + + // Increase counter + ConfirmQueueCtx.ConfirmQueueNvmCtx->MlmeConfirmQueueCnt--; + // Update start pointer + ConfirmQueueCtx.BufferStart = IncreaseBufferPointer( ConfirmQueueCtx.BufferStart ); + + return true; +} + +void LoRaMacConfirmQueueSetStatus( LoRaMacEventInfoStatus_t status, Mlme_t request ) +{ + MlmeConfirmQueue_t* element = NULL; + + if( ConfirmQueueCtx.ConfirmQueueNvmCtx->MlmeConfirmQueueCnt > 0 ) + { + element = GetElement( request, ConfirmQueueCtx.BufferStart, ConfirmQueueCtx.BufferEnd ); + if( element != NULL ) + { + element->Status = status; + element->ReadyToHandle = true; + } + } +} + +LoRaMacEventInfoStatus_t LoRaMacConfirmQueueGetStatus( Mlme_t request ) +{ + MlmeConfirmQueue_t* element = NULL; + + if( ConfirmQueueCtx.ConfirmQueueNvmCtx->MlmeConfirmQueueCnt > 0 ) + { + element = GetElement( request, ConfirmQueueCtx.BufferStart, ConfirmQueueCtx.BufferEnd ); + if( element != NULL ) + { + return element->Status; + } + } + return LORAMAC_EVENT_INFO_STATUS_ERROR; +} + +void LoRaMacConfirmQueueSetStatusCmn( LoRaMacEventInfoStatus_t status ) +{ + MlmeConfirmQueue_t* element = ConfirmQueueCtx.BufferStart; + + ConfirmQueueCtx.ConfirmQueueNvmCtx->CommonStatus = status; + + if( ConfirmQueueCtx.ConfirmQueueNvmCtx->MlmeConfirmQueueCnt > 0 ) + { + do + { + element->Status = status; + // Set the status if it is allowed to set it with a call to + // LoRaMacConfirmQueueSetStatusCmn. + if( element->RestrictCommonReadyToHandle == false ) + { + element->ReadyToHandle = true; + } + element = IncreaseBufferPointer( element ); + }while( element != ConfirmQueueCtx.BufferEnd ); + } +} + +LoRaMacEventInfoStatus_t LoRaMacConfirmQueueGetStatusCmn( void ) +{ + return ConfirmQueueCtx.ConfirmQueueNvmCtx->CommonStatus; +} + +bool LoRaMacConfirmQueueIsCmdActive( Mlme_t request ) +{ + if( GetElement( request, ConfirmQueueCtx.BufferStart, ConfirmQueueCtx.BufferEnd ) != NULL ) + { + return true; + } + return false; +} + +void LoRaMacConfirmQueueHandleCb( MlmeConfirm_t* mlmeConfirm ) +{ + uint8_t nbElements = ConfirmQueueCtx.ConfirmQueueNvmCtx->MlmeConfirmQueueCnt; + bool readyToHandle = false; + MlmeConfirmQueue_t mlmeConfirmToStore; + + for( uint8_t i = 0; i < nbElements; i++ ) + { + mlmeConfirm->MlmeRequest = ConfirmQueueCtx.BufferStart->Request; + mlmeConfirm->Status = ConfirmQueueCtx.BufferStart->Status; + readyToHandle = ConfirmQueueCtx.BufferStart->ReadyToHandle; + + if( readyToHandle == true ) + { + ConfirmQueueCtx.Primitives->MacMlmeConfirm( mlmeConfirm ); + } + else + { + // The request is not processed yet. Store the state. + mlmeConfirmToStore.Request = ConfirmQueueCtx.BufferStart->Request; + mlmeConfirmToStore.Status = ConfirmQueueCtx.BufferStart->Status; + mlmeConfirmToStore.RestrictCommonReadyToHandle = ConfirmQueueCtx.BufferStart->RestrictCommonReadyToHandle; + } + + // Increase the pointer afterwards to prevent overwrites + LoRaMacConfirmQueueRemoveFirst( ); + + if( readyToHandle == false ) + { + // Add a request which has not been finished again to the queue + LoRaMacConfirmQueueAdd( &mlmeConfirmToStore ); + } + } +} + +uint8_t LoRaMacConfirmQueueGetCnt( void ) +{ + return ConfirmQueueCtx.ConfirmQueueNvmCtx->MlmeConfirmQueueCnt; +} + +bool LoRaMacConfirmQueueIsFull( void ) +{ + if( ConfirmQueueCtx.ConfirmQueueNvmCtx->MlmeConfirmQueueCnt >= LORA_MAC_MLME_CONFIRM_QUEUE_LEN ) + { + return true; + } + else + { + return false; + } +} diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacConfirmQueue.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacConfirmQueue.h new file mode 100644 index 00000000..2dc88ad0 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacConfirmQueue.h @@ -0,0 +1,195 @@ +/*! + * \file LoRaMacConfirmQueue.h + * + * \brief LoRa MAC confirm queue implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \defgroup LORAMACCONFIRMQUEUE LoRa MAC confirm queue implementation + * This module specifies the API implementation of the LoRaMAC confirm queue. + * The confirm queue is implemented with as a ring buffer. The number of + * elements can be defined with \ref LORA_MAC_MLME_CONFIRM_QUEUE_LEN. The + * current implementation does not support multiple elements of the same + * Mlme_t type. + * \{ + */ +#ifndef __LORAMAC_CONFIRMQUEUE_H__ +#define __LORAMAC_CONFIRMQUEUE_H__ + +#include +#include + +#include "LoRaMac.h" + +/*! + * LoRaMac MLME-Confirm queue length + */ +#define LORA_MAC_MLME_CONFIRM_QUEUE_LEN 5 + +/*! + * Structure to hold multiple MLME request confirm data + */ +typedef struct sMlmeConfirmQueue +{ + /*! + * Holds the previously performed MLME-Request + */ + Mlme_t Request; + /*! + * Status of the operation + */ + LoRaMacEventInfoStatus_t Status; + /*! + * Set to true, if the request is ready to be handled + */ + bool ReadyToHandle; + /*! + * Set to true, if it is not permitted to set the ReadyToHandle variable + * with a function call to LoRaMacConfirmQueueSetStatusCmn. + */ + bool RestrictCommonReadyToHandle; +}MlmeConfirmQueue_t; + +/*! + * Signature of callback function to be called by this module when the + * non-volatile needs to be saved. + */ +typedef void ( *LoRaMacConfirmQueueNvmEvent )( void ); + +/*! + * \brief Initializes the confirm queue + * + * \param [IN] primitives - Pointer to the LoRaMac primitives. + * + * \param [IN] confirmQueueNvmCtxChanged - Callback function which will be called when the + * non-volatile context needs to be saved. + */ +void LoRaMacConfirmQueueInit( LoRaMacPrimitives_t* primitives, LoRaMacConfirmQueueNvmEvent confirmQueueNvmCtxChanged ); + +/*! + * Restores the internal non-volatile context from passed pointer. + * + * \param [IN] confirmQueueNvmCtx - Pointer to non-volatile class B module context to be restored. + * + * \retval [true - operation was successful, false - operation failed] + */ +bool LoRaMacConfirmQueueRestoreNvmCtx( void* confirmQueueNvmCtx ); + +/*! + * Returns a pointer to the internal non-volatile context. + * + * \param [IN] confirmQueueNvmCtxSize - Size of the module non-volatile context + * + * \retval - Points to a structure where the module store its non-volatile context + */ +void* LoRaMacConfirmQueueGetNvmCtx( size_t* confirmQueueNvmCtxSize ); + +/*! + * \brief Adds an element to the confirm queue. + * + * \param [IN] mlmeConfirm - Pointer to the element to add. + * + * \retval [true - operation was successful, false - operation failed] + */ +bool LoRaMacConfirmQueueAdd( MlmeConfirmQueue_t* mlmeConfirm ); + +/*! + * \brief Removes the last element which was added into the queue. + * + * \retval [true - operation was successful, false - operation failed] + */ +bool LoRaMacConfirmQueueRemoveLast( void ); + +/*! + * \brief Removes the first element which was added to the confirm queue. + * + * \retval [true - operation was successful, false - operation failed] + */ +bool LoRaMacConfirmQueueRemoveFirst( void ); + +/*! + * \brief Sets the status of an element. + * + * \param [IN] status - The status to set. + * + * \param [IN] request - The related request to set the status. + */ +void LoRaMacConfirmQueueSetStatus( LoRaMacEventInfoStatus_t status, Mlme_t request ); + +/*! + * \brief Gets the status of an element. + * + * \param [IN] request - The request to query the status. + * + * \retval The status of the related MlmeRequest. + */ +LoRaMacEventInfoStatus_t LoRaMacConfirmQueueGetStatus( Mlme_t request ); + +/*! + * \brief Sets a common status for all elements in the queue. + * + * \param [IN] status - The status to set. + */ +void LoRaMacConfirmQueueSetStatusCmn( LoRaMacEventInfoStatus_t status ); + +/*! + * \brief Gets the common status of all elements. + * + * \retval The common status of all elements. + */ +LoRaMacEventInfoStatus_t LoRaMacConfirmQueueGetStatusCmn( void ); + +/*! + * \brief Verifies if a request is in the queue and active. + * + * \param [IN] request - The request to verify. + * + * \retval [true - element is in the queue, false - element is not in the queue]. + */ +bool LoRaMacConfirmQueueIsCmdActive( Mlme_t request ); + +/*! + * \brief Handles all callbacks of active requests + * + * \param [IN] mlmeConfirm - Pointer to the generic mlmeConfirm structure. + */ +void LoRaMacConfirmQueueHandleCb( MlmeConfirm_t* mlmeConfirm ); + +/*! + * \brief Query number of elements in the queue. + * + * \retval Number of elements. + */ +uint8_t LoRaMacConfirmQueueGetCnt( void ); + +/*! + * \brief Verify if the confirm queue is full. + * + * \retval [true - queue is full, false - queue is not full]. + */ +bool LoRaMacConfirmQueueIsFull( void ); + +#endif // __LORAMAC_CONFIRMQUEUE_H__ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacCrypto.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacCrypto.c new file mode 100644 index 00000000..2e2d22c6 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacCrypto.c @@ -0,0 +1,1693 @@ +/*! + * \file LoRaMacCrypto.c + * + * \brief LoRa MAC layer cryptography implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + */ +#include +#include +#include + +#include "utilities.h" +#include "secure-element.h" + +#include "LoRaMacParser.h" +#include "LoRaMacSerializer.h" +#include "LoRaMacCrypto.h" + +/*! + * Indicates if LoRaWAN 1.1.x crypto scheme is enabled + */ +#define USE_LRWAN_1_1_X_CRYPTO 0 + +/*! + * Indicates if a random devnonce must be used or not + */ +#define USE_RANDOM_DEV_NONCE 1 + +/* + * Frame direction definition for uplink communications + */ +#define UPLINK 0 + +/* + * Frame direction definition for downlink communications + */ +#define DOWNLINK 1 + +/* + * CMAC/AES Message Integrity Code (MIC) Block B0 size + */ +#define MIC_BLOCK_BX_SIZE 16 + +/* + * Size of JoinReqType is field for integrity check + */ +#define JOIN_REQ_TYPE_SIZE 1 + +/* + * Size of DevNonce is field for integrity check + */ +#define DEV_NONCE_SIZE 2 + +/* + * Number of security context entries + */ +#define NUM_OF_SEC_CTX 5 + +/* + * Size of the module context + */ +#define CRYPTO_CTX_SIZE sizeof( LoRaMacCryptoCtx_t ) + +/* + * Size of the module non volatile context + */ +#define CRYPTO_NVM_CTX_SIZE sizeof( LoRaMacCryptoNvmCtx_t ) + +/* + * Maximum size of the message that can be handled by the crypto operations + */ +#define CRYPTO_MAXMESSAGE_SIZE 256 + +/* + * Maximum size of the buffer for crypto operations + */ +#define CRYPTO_BUFFER_SIZE CRYPTO_MAXMESSAGE_SIZE + MIC_BLOCK_BX_SIZE + +/* + * MIC computaion offset + */ +#define CRYPTO_MIC_COMPUTATION_OFFSET JOIN_REQ_TYPE_SIZE + LORAMAC_JOIN_EUI_FIELD_SIZE + DEV_NONCE_SIZE + LORAMAC_MHDR_FIELD_SIZE + +/*! + * LoRaWAN Frame counter list. + */ +typedef struct sFCntList +{ + /*! + * Uplink frame counter which is incremented with each uplink. + */ + uint32_t FCntUp; + /*! + * Network downlink frame counter which is incremented with each downlink on FPort 0 + * or when the FPort field is missing. + */ + uint32_t NFCntDown; + /*! + * Application downlink frame counter which is incremented with each downlink + * on a port different than 0. + */ + uint32_t AFCntDown; + /*! + * In case if the device is connected to a LoRaWAN 1.0 Server, + * this counter is used for every kind of downlink frame. + */ + uint32_t FCntDown; + /*! + * Multicast downlink counter for index 0 + */ + uint32_t McFCntDown0; + /*! + * Multicast downlink counter for index 1 + */ + uint32_t McFCntDown1; + /*! + * Multicast downlink counter for index 2 + */ + uint32_t McFCntDown2; + /*! + * Multicast downlink counter for index 3 + */ + uint32_t McFCntDown3; +}FCntList_t; + +/* + * LoRaMac Crypto Non Volatile Context structure + */ +typedef struct sLoRaMacCryptoNvmCtx +{ + /* + * Stores the information if the device is connected to a LoRaWAN network + * server with prior to 1.1.0 implementation. + */ + Version_t LrWanVersion; + /* + * Device nonce is a counter starting at 0 when the device is initially + * powered up and incremented with every JoinRequest. + */ + uint16_t DevNonce; + /* + * JoinNonce is a device specific counter value (that never repeats itself) + * provided by the join server and incremented with every JoinAccept message. + */ + uint32_t JoinNonce; + /* + * Frame counter list + */ + FCntList_t FCntList; + /* + * RJcount1 is a counter incremented with every Rejoin request Type 1 frame transmitted. + */ + uint16_t RJcount1; + /* + * LastDownFCnt stores the information which frame counter was used to unsecure the last frame. + * This information is needed to compute ConfFCnt in B1 block for the MIC. + */ + uint32_t* LastDownFCnt; +}LoRaMacCryptoNvmCtx_t; + +/* + * LoRaMac Crypto Context structure + */ +typedef struct sLoRaMacCryptoCtx +{ + /* + * RJcount0 is a counter incremented with every Type 0 or 2 Rejoin frame transmitted. + */ + uint16_t RJcount0; + /* + * Non volatile module context structure + */ + LoRaMacCryptoNvmCtx_t* NvmCtx; + /* + * Callback function to notify the upper layer about context change + */ + LoRaMacCryptoNvmEvent EventCryptoNvmCtxChanged; +}LoRaMacCryptoCtx_t; + +/* + * Key-Address item + */ +typedef struct sKeyAddr +{ + /* + * Address identifier + */ + AddressIdentifier_t AddrID; + /* + * Application session key + */ + KeyIdentifier_t AppSkey; + /* + * Network session key + */ + KeyIdentifier_t NwkSkey; + /* + * Rootkey (Multicast only) + */ + KeyIdentifier_t RootKey; +}KeyAddr_t; + +/* + *Crypto module context. + */ +static LoRaMacCryptoCtx_t CryptoCtx; + +/* + * Non volatile module context. + */ +static LoRaMacCryptoNvmCtx_t NvmCryptoCtx; + +/* + * Key-Address list + */ +static KeyAddr_t KeyAddrList[NUM_OF_SEC_CTX] = + { + { MULTICAST_0_ADDR, MC_APP_S_KEY_0, MC_NWK_S_KEY_0, MC_KEY_0 }, + { MULTICAST_1_ADDR, MC_APP_S_KEY_1, MC_NWK_S_KEY_1, MC_KEY_1 }, + { MULTICAST_2_ADDR, MC_APP_S_KEY_2, MC_NWK_S_KEY_2, MC_KEY_2 }, + { MULTICAST_3_ADDR, MC_APP_S_KEY_3, MC_NWK_S_KEY_3, MC_KEY_3 }, + { UNICAST_DEV_ADDR, APP_S_KEY, S_NWK_S_INT_KEY, NO_KEY } + }; + +/* + * Local functions + */ + +/* + * Encrypts the payload + * + * \param[IN] keyID - Key identifier + * \param[IN] address - Address + * \param[IN] dir - Frame direction ( Uplink or Downlink ) + * \param[IN] frameCounter - Frame counter + * \param[IN] size - Size of data + * \param[IN/OUT] buffer - Data buffer + * \retval - Status of the operation + */ +static LoRaMacCryptoStatus_t PayloadEncrypt( uint8_t* buffer, int16_t size, KeyIdentifier_t keyID, uint32_t address, uint8_t dir, uint32_t frameCounter ) +{ + if( buffer == 0 ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + + uint8_t bufferIndex = 0; + uint16_t ctr = 1; + uint8_t sBlock[16] = { 0 }; + uint8_t aBlock[16] = { 0 }; + + aBlock[0] = 0x01; + + aBlock[5] = dir; + + aBlock[6] = address & 0xFF; + aBlock[7] = ( address >> 8 ) & 0xFF; + aBlock[8] = ( address >> 16 ) & 0xFF; + aBlock[9] = ( address >> 24 ) & 0xFF; + + aBlock[10] = frameCounter & 0xFF; + aBlock[11] = ( frameCounter >> 8 ) & 0xFF; + aBlock[12] = ( frameCounter >> 16 ) & 0xFF; + aBlock[13] = ( frameCounter >> 24 ) & 0xFF; + + while( size > 0 ) + { + aBlock[15] = ctr & 0xFF; + ctr++; + if( SecureElementAesEncrypt( aBlock, 16, keyID, sBlock ) != SECURE_ELEMENT_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC; + } + + for( uint8_t i = 0; i < ( ( size > 16 ) ? 16 : size ); i++ ) + { + buffer[bufferIndex + i] = buffer[bufferIndex + i] ^ sBlock[i]; + } + size -= 16; + bufferIndex += 16; + } + + return LORAMAC_CRYPTO_SUCCESS; +} + +#if( USE_LRWAN_1_1_X_CRYPTO == 1 ) +/* + * Encrypts the FOpts + * + * \param[IN] address - Address + * \param[IN] dir - Frame direction ( Uplink or Downlink ) + * \param[IN] fCntID - Frame counter identifier + * \param[IN] frameCounter - Frame counter + * \param[IN] size - Size of data + * \param[IN/OUT] buffer - Data buffer + * \retval - Status of the operation + */ +static LoRaMacCryptoStatus_t FOptsEncrypt( uint16_t size, uint32_t address, uint8_t dir, FCntIdentifier_t fCntID, uint32_t frameCounter, uint8_t* buffer ) +{ + if( buffer == 0 ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + + uint8_t bufferIndex = 0; + uint8_t sBlock[16] = { 0 }; + uint8_t aBlock[16] = { 0 }; + + aBlock[0] = 0x01; + + if( CryptoCtx.NvmCtx->LrWanVersion.Value > 0x01010000 ) + { + // Introduced in LoRaWAN 1.1.1 specification + switch( fCntID ) + { + case FCNT_UP: + { + aBlock[4] = 0x01; + break; + } + case N_FCNT_DOWN: + { + aBlock[4] = 0x01; + break; + } + case A_FCNT_DOWN: + { + aBlock[4] = 0x02; + break; + } + default: + return LORAMAC_CRYPTO_FAIL_PARAM; + } + } + + aBlock[5] = dir; + + aBlock[6] = address & 0xFF; + aBlock[7] = ( address >> 8 ) & 0xFF; + aBlock[8] = ( address >> 16 ) & 0xFF; + aBlock[9] = ( address >> 24 ) & 0xFF; + + aBlock[10] = frameCounter & 0xFF; + aBlock[11] = ( frameCounter >> 8 ) & 0xFF; + aBlock[12] = ( frameCounter >> 16 ) & 0xFF; + aBlock[13] = ( frameCounter >> 24 ) & 0xFF; + + if( CryptoCtx.NvmCtx->LrWanVersion.Value > 0x01010000 ) + { + // Introduced in LoRaWAN 1.1.1 specification + aBlock[15] = 0x01; + } + + if( size > 0 ) + { + if( SecureElementAesEncrypt( aBlock, 16, NWK_S_ENC_KEY, sBlock ) != SECURE_ELEMENT_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC; + } + for( uint8_t i = 0; i < size; i++ ) + { + buffer[bufferIndex + i] = buffer[bufferIndex + i] ^ sBlock[i]; + } + } + + return LORAMAC_CRYPTO_SUCCESS; +} +#endif + +/* + * Prepares B0 block for cmac computation. + * + * \param[IN] msgLen - Length of message + * \param[IN] keyID - Key identifier + * \param[IN] isAck - True if it is a acknowledge frame ( Sets ConfFCnt in B0 block ) + * \param[IN] devAddr - Device address + * \param[IN] dir - Frame direction ( Uplink:0, Downlink:1 ) + * \param[IN] fCnt - Frame counter + * \param[IN/OUT] b0 - B0 block + * \retval - Status of the operation + */ +static LoRaMacCryptoStatus_t PrepareB0( uint16_t msgLen, KeyIdentifier_t keyID, bool isAck, uint8_t dir, uint32_t devAddr, uint32_t fCnt, uint8_t* b0 ) +{ + if( b0 == 0 ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + + b0[0] = 0x49; + + if( ( isAck == true ) && ( dir == DOWNLINK ) ) + { + // confFCnt contains the frame counter value modulo 2^16 of the "confirmed" uplink or downlink frame that is being acknowledged + uint16_t confFCnt = 0; + + confFCnt = ( uint16_t )( CryptoCtx.NvmCtx->FCntList.FCntUp % 65536 ); + + b0[1] = confFCnt & 0xFF; + b0[2] = ( confFCnt >> 8 ) & 0xFF; + } + else + { + b0[1] = 0x00; + b0[2] = 0x00; + } + + b0[3] = 0x00; + b0[4] = 0x00; + + b0[5] = dir; + + b0[6] = devAddr & 0xFF; + b0[7] = ( devAddr >> 8 ) & 0xFF; + b0[8] = ( devAddr >> 16 ) & 0xFF; + b0[9] = ( devAddr >> 24 ) & 0xFF; + + b0[10] = fCnt & 0xFF; + b0[11] = ( fCnt >> 8 ) & 0xFF; + b0[12] = ( fCnt >> 16 ) & 0xFF; + b0[13] = ( fCnt >> 24 ) & 0xFF; + + b0[14] = 0x00; + + b0[15] = msgLen & 0xFF; + + return LORAMAC_CRYPTO_SUCCESS; +} + +/* + * Computes cmac with adding B0 block in front. + * + * cmac = aes128_cmac(keyID, B0 | msg) + * + * \param[IN] msg - Message to compute the integrity code + * \param[IN] len - Length of message + * \param[IN] keyID - Key identifier + * \param[IN] isAck - True if it is a acknowledge frame ( Sets ConfFCnt in B0 block ) + * \param[IN] devAddr - Device address + * \param[IN] dir - Frame direction ( Uplink:0, Downlink:1 ) + * \param[IN] fCnt - Frame counter + * \param[OUT] cmac - Computed cmac + * \retval - Status of the operation + */ +static LoRaMacCryptoStatus_t ComputeCmacB0( uint8_t* msg, uint16_t len, KeyIdentifier_t keyID, bool isAck, uint8_t dir, uint32_t devAddr, uint32_t fCnt, uint32_t* cmac ) +{ + if( ( msg == 0 ) || ( cmac == 0 ) ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + if( len > CRYPTO_MAXMESSAGE_SIZE ) + { + return LORAMAC_CRYPTO_ERROR_BUF_SIZE; + } + + uint8_t micBuff[MIC_BLOCK_BX_SIZE]; + + // Initialize the first Block + PrepareB0( len, keyID, isAck, dir, devAddr, fCnt, micBuff ); + + if( SecureElementComputeAesCmac( micBuff, msg, len, keyID, cmac ) != SECURE_ELEMENT_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC; + } + return LORAMAC_CRYPTO_SUCCESS; +} + +/*! + * Verifies cmac with adding B0 block in front. + * + * \param[IN] msg - Message to compute the integrity code + * \param[IN] len - Length of message + * \param[IN] keyID - Key identifier + * \param[IN] isAck - True if it is a acknowledge frame ( Sets ConfFCnt in B0 block ) + * \param[IN] devAddr - Device address + * \param[IN] dir - Frame direction ( Uplink:0, Downlink:1 ) + * \param[IN] fCnt - Frame counter + * \param[in] expectedCmac - Expected cmac + * \retval - Status of the operation + */ +static LoRaMacCryptoStatus_t VerifyCmacB0( uint8_t* msg, uint16_t len, KeyIdentifier_t keyID, bool isAck, uint8_t dir, uint32_t devAddr, uint32_t fCnt, uint32_t expectedCmac ) +{ + if( msg == 0 ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + if( len > CRYPTO_MAXMESSAGE_SIZE ) + { + return LORAMAC_CRYPTO_ERROR_BUF_SIZE; + } + + uint8_t micBuff[CRYPTO_BUFFER_SIZE]; + memset1( micBuff, 0, CRYPTO_BUFFER_SIZE ); + + // Initialize the first Block + PrepareB0( len, keyID, isAck, dir, devAddr, fCnt, micBuff ); + + // Copy the given data to the mic computation buffer + memcpy1( ( micBuff + MIC_BLOCK_BX_SIZE ), msg, len ); + + SecureElementStatus_t retval = SECURE_ELEMENT_ERROR; + retval = SecureElementVerifyAesCmac( micBuff, ( len + MIC_BLOCK_BX_SIZE ), expectedCmac, keyID ); + + if( retval == SECURE_ELEMENT_SUCCESS ) + { + return LORAMAC_CRYPTO_SUCCESS; + } + else if( retval == SECURE_ELEMENT_FAIL_CMAC ) + { + return LORAMAC_CRYPTO_FAIL_MIC; + } + + return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC; +} + +#if( USE_LRWAN_1_1_X_CRYPTO == 1 ) +/* + * Prpares B1 block for cmac computation. + * + * \param[IN] msgLen - Length of message + * \param[IN] keyID - Key identifier + * \param[IN] isAck - True if it is a acknowledge frame ( Sets ConfFCnt in B0 block ) + * \param[IN] txDr - Data rate used for the transmission + * \param[IN] txCh - Index of the channel used for the transmission + * \param[IN] devAddr - Device address + * \param[IN] fCntUp - Frame counter + * \param[IN/OUT] b0 - B0 block + * \retval - Status of the operation + */ +static LoRaMacCryptoStatus_t PrepareB1( uint16_t msgLen, KeyIdentifier_t keyID, bool isAck, uint8_t txDr, uint8_t txCh, uint32_t devAddr, uint32_t fCntUp, uint8_t* b1 ) +{ + if( b1 == 0 ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + + b1[0] = 0x49; + + if( isAck == true ) + { + // confFCnt contains the frame counter value modulo 2^16 of the "confirmed" uplink frame that is being acknowledged + uint16_t confFCnt = ( uint16_t )( *CryptoCtx.NvmCtx->LastDownFCnt % 65536 ); + b1[1] = confFCnt & 0xFF; + b1[2] = ( confFCnt >> 8 ) & 0xFF; + } + else + { + b1[1] = 0x00; + b1[2] = 0x00; + } + + b1[3] = txDr; + b1[4] = txCh; + b1[5] = UPLINK; // dir = Uplink + + b1[6] = devAddr & 0xFF; + b1[7] = ( devAddr >> 8 ) & 0xFF; + b1[8] = ( devAddr >> 16 ) & 0xFF; + b1[9] = ( devAddr >> 24 ) & 0xFF; + + b1[10] = fCntUp & 0xFF; + b1[11] = ( fCntUp >> 8 ) & 0xFF; + b1[12] = ( fCntUp >> 16 ) & 0xFF; + b1[13] = ( fCntUp >> 24 ) & 0xFF; + + b1[14] = 0x00; + + b1[15] = msgLen & 0xFF; + + return LORAMAC_CRYPTO_SUCCESS; +} + +/* + * Computes cmac with adding B1 block in front ( only for Uplink frames LoRaWAN 1.1 ) + * + * cmac = aes128_cmac(keyID, B1 | msg) + * + * \param[IN] msg - Message to calculate the Integrity code + * \param[IN] len - Length of message + * \param[IN] keyID - Key identifier + * \param[IN] isAck - True if it is a acknowledge frame ( Sets ConfFCnt in B0 block ) + * \param[IN] txDr - Data rate used for the transmission + * \param[IN] txCh - Index of the channel used for the transmission + * \param[IN] devAddr - Device address + * \param[IN] fCntUp - Uplink Frame counter + * \param[OUT] cmac - Computed cmac + * \retval - Status of the operation + */ +static LoRaMacCryptoStatus_t ComputeCmacB1( uint8_t* msg, uint16_t len, KeyIdentifier_t keyID, bool isAck, uint8_t txDr, uint8_t txCh, uint32_t devAddr, uint32_t fCntUp, uint32_t* cmac ) +{ + if( ( msg == 0 ) || ( cmac == 0 ) ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + if( len > CRYPTO_MAXMESSAGE_SIZE ) + { + return LORAMAC_CRYPTO_ERROR_BUF_SIZE; + } + + uint8_t micBuff[MIC_BLOCK_BX_SIZE]; + + // Initialize the first Block + PrepareB1( len, keyID, isAck, txDr, txCh, devAddr, fCntUp, micBuff ); + + if( SecureElementComputeAesCmac( micBuff, msg, len, keyID, cmac ) != SECURE_ELEMENT_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC; + } + return LORAMAC_CRYPTO_SUCCESS; +} +#endif + +/* + * Gets security item from list. + * + * \param[IN] addrID - Address identifier + * \param[OUT] keyItem - Key item reference + * \retval - Status of the operation + */ +static LoRaMacCryptoStatus_t GetKeyAddrItem( AddressIdentifier_t addrID, KeyAddr_t** item ) +{ + for( uint8_t i = 0; i < NUM_OF_SEC_CTX; i++ ) + { + if( KeyAddrList[i].AddrID == addrID ) + { + *item = &( KeyAddrList[i] ); + return LORAMAC_CRYPTO_SUCCESS; + } + } + return LORAMAC_CRYPTO_ERROR_INVALID_ADDR_ID; +} + +/* + * Derives a session key as of LoRaWAN versions prior to 1.1.0 + * + * \param[IN] keyID - Key Identifier for the key to be calculated + * \param[IN] joinNonce - Sever nonce + * \param[IN] netID - Network Identifier + * \param[IN] deviceNonce - Device nonce + * \retval - Status of the operation + */ +static LoRaMacCryptoStatus_t DeriveSessionKey10x( KeyIdentifier_t keyID, uint8_t* joinNonce, uint8_t* netID, uint8_t* devNonce ) +{ + if( ( joinNonce == 0 ) || ( netID == 0 ) || ( devNonce == 0 ) ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + + uint8_t compBase[16] = { 0 }; + + switch( keyID ) + { + case F_NWK_S_INT_KEY: + case S_NWK_S_INT_KEY: + case NWK_S_ENC_KEY: + compBase[0] = 0x01; + break; + case APP_S_KEY: + compBase[0] = 0x02; + break; + default: + return LORAMAC_CRYPTO_ERROR_INVALID_KEY_ID; + } + + memcpy1( compBase + 1, joinNonce, 3 ); + memcpy1( compBase + 4, netID, 3 ); + memcpy1( compBase + 7, devNonce, 2 ); + + if( SecureElementDeriveAndStoreKey( CryptoCtx.NvmCtx->LrWanVersion, compBase, NWK_KEY, keyID ) != SECURE_ELEMENT_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC; + } + + return LORAMAC_CRYPTO_SUCCESS; +} + +#if( USE_LRWAN_1_1_X_CRYPTO == 1 ) +/* + * Derives a session key as of LoRaWAN 1.1.0 + * + * \param[IN] keyID - Key Identifier for the key to be calculated + * \param[IN] joinNonce - Sever nonce + * \param[IN] joinEUI - Join Server EUI + * \param[IN] deviceNonce - Device nonce + * \retval - Status of the operation + */ +static LoRaMacCryptoStatus_t DeriveSessionKey11x( KeyIdentifier_t keyID, uint8_t* joinNonce, uint8_t* joinEUI, uint8_t* devNonce ) +{ + if( ( joinNonce == 0 ) || ( joinEUI == 0 ) || ( devNonce == 0 ) ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + + uint8_t compBase[16] = { 0 }; + KeyIdentifier_t rootKeyId = NWK_KEY; + + switch( keyID ) + { + case F_NWK_S_INT_KEY: + compBase[0] = 0x01; + break; + case S_NWK_S_INT_KEY: + compBase[0] = 0x03; + break; + case NWK_S_ENC_KEY: + compBase[0] = 0x04; + break; + case APP_S_KEY: + rootKeyId = APP_KEY; + compBase[0] = 0x02; + break; + default: + return LORAMAC_CRYPTO_ERROR_INVALID_KEY_ID; + } + + memcpy1( compBase + 1, joinNonce, 3 ); + memcpyr( compBase + 4, joinEUI, 8 ); + memcpy1( compBase + 12, devNonce, 2 ); + + if( SecureElementDeriveAndStoreKey( CryptoCtx.NvmCtx->LrWanVersion, compBase, rootKeyId, keyID ) != SECURE_ELEMENT_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC; + } + + return LORAMAC_CRYPTO_SUCCESS; +} + +/* + * Derives a life time session key (JSIntKey or JSEncKey) as of LoRaWAN 1.1.0 + * + * \param[IN] keyID - Key Identifier for the key to be calculated + * \param[IN] devEUI - Device EUI + * \retval - Status of the operation + */ +static LoRaMacCryptoStatus_t DeriveLifeTimeSessionKey( KeyIdentifier_t keyID, uint8_t* devEUI ) +{ + if( devEUI == 0 ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + + uint8_t compBase[16] = { 0 }; + + switch( keyID ) + { + case J_S_INT_KEY: + compBase[0] = 0x06; + break; + case J_S_ENC_KEY: + compBase[0] = 0x05; + break; + default: + return LORAMAC_CRYPTO_ERROR_INVALID_KEY_ID; + } + + memcpyr( compBase + 1, devEUI, 8 ); + + if( SecureElementDeriveAndStoreKey( CryptoCtx.NvmCtx->LrWanVersion, compBase, NWK_KEY, keyID ) != SECURE_ELEMENT_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC; + } + + return LORAMAC_CRYPTO_SUCCESS; +} +#endif + +/* + * Gets the last received frame counter + * + * \param[IN] fCntID - Frame counter identifier + * \param[IN] lastDown - Last downlink counter value + * + * \retval - Status of the operation + */ +static LoRaMacCryptoStatus_t GetLastFcntDown( FCntIdentifier_t fCntID, uint32_t* lastDown ) +{ + if( lastDown == NULL ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + switch( fCntID ) + { + case N_FCNT_DOWN: + *lastDown = CryptoCtx.NvmCtx->FCntList.NFCntDown; + CryptoCtx.NvmCtx->LastDownFCnt = &CryptoCtx.NvmCtx->FCntList.NFCntDown; + break; + case A_FCNT_DOWN: + *lastDown = CryptoCtx.NvmCtx->FCntList.AFCntDown; + CryptoCtx.NvmCtx->LastDownFCnt = &CryptoCtx.NvmCtx->FCntList.AFCntDown; + break; + case FCNT_DOWN: + *lastDown = CryptoCtx.NvmCtx->FCntList.FCntDown; + CryptoCtx.NvmCtx->LastDownFCnt = &CryptoCtx.NvmCtx->FCntList.FCntDown; + break; + case MC_FCNT_DOWN_0: + *lastDown = CryptoCtx.NvmCtx->FCntList.McFCntDown0; + break; + case MC_FCNT_DOWN_1: + *lastDown = CryptoCtx.NvmCtx->FCntList.McFCntDown1; + break; + case MC_FCNT_DOWN_2: + *lastDown = CryptoCtx.NvmCtx->FCntList.McFCntDown2; + break; + case MC_FCNT_DOWN_3: + *lastDown = CryptoCtx.NvmCtx->FCntList.McFCntDown3; + break; + default: + return LORAMAC_CRYPTO_FAIL_FCNT_ID; + } + return LORAMAC_CRYPTO_SUCCESS; +} + +/* + * Checks the downlink counter value + * + * \param[IN] fCntID - Frame counter identifier + * \param[IN] currentDown - Current downlink counter value + * + * \retval - Status of the operation + */ +static bool CheckFCntDown( FCntIdentifier_t fCntID, uint32_t currentDown ) +{ + uint32_t lastDown = 0; + if( GetLastFcntDown( fCntID, &lastDown ) != LORAMAC_CRYPTO_SUCCESS ) + { + return false; + } + if( ( currentDown > lastDown ) || + // For LoRaWAN 1.0.X only. Allow downlink frames of 0 + ( lastDown == FCNT_DOWN_INITAL_VALUE ) ) + { + return true; + } + else + { + return false; + } +} + +/*! + * Updates the reference downlink counter + * + * \param[IN] fCntID - Frame counter identifier + * \param[IN] currentDown - Current downlink counter value + * + * \retval - Status of the operation + */ +static void UpdateFCntDown( FCntIdentifier_t fCntID, uint32_t currentDown ) +{ + switch( fCntID ) + { + case N_FCNT_DOWN: + CryptoCtx.NvmCtx->FCntList.NFCntDown = currentDown; + break; + case A_FCNT_DOWN: + CryptoCtx.NvmCtx->FCntList.AFCntDown = currentDown; + break; + case FCNT_DOWN: + CryptoCtx.NvmCtx->FCntList.FCntDown = currentDown; + break; + case MC_FCNT_DOWN_0: + CryptoCtx.NvmCtx->FCntList.McFCntDown0 = currentDown; + break; + case MC_FCNT_DOWN_1: + CryptoCtx.NvmCtx->FCntList.McFCntDown1 = currentDown; + break; + case MC_FCNT_DOWN_2: + CryptoCtx.NvmCtx->FCntList.McFCntDown2 = currentDown; + break; + case MC_FCNT_DOWN_3: + CryptoCtx.NvmCtx->FCntList.McFCntDown3 = currentDown; + break; + default: + break; + } + CryptoCtx.EventCryptoNvmCtxChanged( ); +} + +/*! + * Resets the frame counters + */ +static void ResetFCnts( void ) +{ + + CryptoCtx.NvmCtx->FCntList.FCntUp = 0; + CryptoCtx.NvmCtx->FCntList.NFCntDown = FCNT_DOWN_INITAL_VALUE; + CryptoCtx.NvmCtx->FCntList.AFCntDown = FCNT_DOWN_INITAL_VALUE; + CryptoCtx.NvmCtx->FCntList.FCntDown = FCNT_DOWN_INITAL_VALUE; + CryptoCtx.NvmCtx->LastDownFCnt = &CryptoCtx.NvmCtx->FCntList.FCntDown; + + CryptoCtx.NvmCtx->FCntList.McFCntDown0 = FCNT_DOWN_INITAL_VALUE; + CryptoCtx.NvmCtx->FCntList.McFCntDown1 = FCNT_DOWN_INITAL_VALUE; + CryptoCtx.NvmCtx->FCntList.McFCntDown2 = FCNT_DOWN_INITAL_VALUE; + CryptoCtx.NvmCtx->FCntList.McFCntDown3 = FCNT_DOWN_INITAL_VALUE; + + CryptoCtx.EventCryptoNvmCtxChanged( ); +} + +/* + * Dummy callback in case if the user provides NULL function pointer + */ +static void DummyCB( void ) +{ + return; +} + +/* + * API functions + */ + +LoRaMacCryptoStatus_t LoRaMacCryptoInit( LoRaMacCryptoNvmEvent cryptoNvmCtxChanged ) +{ + // Assign non volatile context + CryptoCtx.NvmCtx = &NvmCryptoCtx; + + // Assign callback + if( cryptoNvmCtxChanged != 0 ) + { + CryptoCtx.EventCryptoNvmCtxChanged = cryptoNvmCtxChanged; + } + else + { + CryptoCtx.EventCryptoNvmCtxChanged = DummyCB; + } + + // Initialize with default + memset1( (uint8_t*) CryptoCtx.NvmCtx, 0, sizeof( LoRaMacCryptoNvmCtx_t ) ); + + // Set default LoRaWAN version + CryptoCtx.NvmCtx->LrWanVersion.Fields.Major = 1; + CryptoCtx.NvmCtx->LrWanVersion.Fields.Minor = 1; + CryptoCtx.NvmCtx->LrWanVersion.Fields.Revision = 1; + CryptoCtx.NvmCtx->LrWanVersion.Fields.Rfu = 0; + + // Reset frame counters + ResetFCnts( ); + + return LORAMAC_CRYPTO_SUCCESS; +} + +LoRaMacCryptoStatus_t LoRaMacCryptoSetLrWanVersion( Version_t version ) +{ + CryptoCtx.NvmCtx->LrWanVersion = version; + return LORAMAC_CRYPTO_SUCCESS; +} + +LoRaMacCryptoStatus_t LoRaMacCryptoRestoreNvmCtx( void* cryptoNvmCtx ) +{ + // Restore module context + if( cryptoNvmCtx != 0 ) + { + memcpy1( ( uint8_t* ) &NvmCryptoCtx, ( uint8_t* ) cryptoNvmCtx, CRYPTO_NVM_CTX_SIZE ); + return LORAMAC_CRYPTO_SUCCESS; + } + else + { + return LORAMAC_CRYPTO_ERROR_NPE; + } +} + +void* LoRaMacCryptoGetNvmCtx( size_t* cryptoNvmCtxSize ) +{ + *cryptoNvmCtxSize = CRYPTO_NVM_CTX_SIZE; + return &NvmCryptoCtx; +} + +LoRaMacCryptoStatus_t LoRaMacCryptoGetFCntUp( uint32_t* currentUp ) +{ + if( currentUp == NULL ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + + *currentUp = CryptoCtx.NvmCtx->FCntList.FCntUp + 1; + + return LORAMAC_CRYPTO_SUCCESS; +} + +LoRaMacCryptoStatus_t LoRaMacCryptoGetFCntDown( FCntIdentifier_t fCntID, uint16_t maxFCntGap, uint32_t frameFcnt, uint32_t* currentDown ) +{ + uint32_t lastDown = 0; + int32_t fCntDiff = 0; + LoRaMacCryptoStatus_t cryptoStatus = LORAMAC_CRYPTO_ERROR; + + if( currentDown == NULL ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + + cryptoStatus = GetLastFcntDown( fCntID, &lastDown ); + if( cryptoStatus != LORAMAC_CRYPTO_SUCCESS ) + { + return cryptoStatus; + } + + // For LoRaWAN 1.0.X only, allow downlink frames of 0 + if( lastDown == FCNT_DOWN_INITAL_VALUE ) + { + *currentDown = frameFcnt; + } + else + { + // Add difference, consider roll-over + fCntDiff = ( int32_t )( ( int64_t )frameFcnt - ( int64_t )( lastDown & 0x0000FFFF ) ); + + if( fCntDiff > 0 ) + { // Positive difference + *currentDown = lastDown + fCntDiff; + } + else if( fCntDiff == 0 ) + { // Duplicate FCnt value, keep the current value. + *currentDown = lastDown; + return LORAMAC_CRYPTO_FAIL_FCNT_DUPLICATED; + } + else + { // Negative difference, assume a roll-over of one uint16_t + *currentDown = ( lastDown & 0xFFFF0000 ) + 0x10000 + frameFcnt; + } + } + + + // For LoRaWAN 1.0.X only, check maxFCntGap + if( CryptoCtx.NvmCtx->LrWanVersion.Fields.Minor == 0 ) + { + if( ( ( int64_t )*currentDown - ( int64_t )lastDown ) >= maxFCntGap ) + { + return LORAMAC_CRYPTO_FAIL_MAX_GAP_FCNT; + } + } + + return LORAMAC_CRYPTO_SUCCESS; +} + +LoRaMacCryptoStatus_t LoRaMacCryptoSetMulticastReference( MulticastCtx_t* multicastList ) +{ + if( multicastList == NULL ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + + multicastList[0].DownLinkCounter = &CryptoCtx.NvmCtx->FCntList.McFCntDown0; + multicastList[1].DownLinkCounter = &CryptoCtx.NvmCtx->FCntList.McFCntDown1; + multicastList[2].DownLinkCounter = &CryptoCtx.NvmCtx->FCntList.McFCntDown2; + multicastList[3].DownLinkCounter = &CryptoCtx.NvmCtx->FCntList.McFCntDown3; + + return LORAMAC_CRYPTO_SUCCESS; +} + +LoRaMacCryptoStatus_t LoRaMacCryptoSetKey( KeyIdentifier_t keyID, uint8_t* key ) +{ + if( SecureElementSetKey( keyID, key ) != SECURE_ELEMENT_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC; + } + if( ( keyID == GEN_APP_KEY ) || ( keyID == APP_KEY ) ) + { + // Derive lifetime keys + if( LoRaMacCryptoDeriveMcRootKey( keyID ) != LORAMAC_CRYPTO_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC; + } + if( LoRaMacCryptoDeriveMcKEKey( MC_ROOT_KEY ) != LORAMAC_CRYPTO_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC; + } + } + return LORAMAC_CRYPTO_SUCCESS; +} + +LoRaMacCryptoStatus_t LoRaMacCryptoPrepareJoinRequest( LoRaMacMessageJoinRequest_t* macMsg ) +{ + if( macMsg == 0 ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + KeyIdentifier_t micComputationKeyID = NWK_KEY; + + // Add device nonce +#if ( USE_RANDOM_DEV_NONCE == 1 ) + uint32_t devNonce = 0; + SecureElementRandomNumber( &devNonce ); + CryptoCtx.NvmCtx->DevNonce = devNonce; +#else + CryptoCtx.NvmCtx->DevNonce++; +#endif + CryptoCtx.EventCryptoNvmCtxChanged( ); + macMsg->DevNonce = CryptoCtx.NvmCtx->DevNonce; + +#if( USE_LRWAN_1_1_X_CRYPTO == 1 ) + // Derive lifetime session keys + if( DeriveLifeTimeSessionKey( J_S_INT_KEY, macMsg->DevEUI ) != LORAMAC_CRYPTO_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR; + } + if( DeriveLifeTimeSessionKey( J_S_ENC_KEY, macMsg->DevEUI ) != LORAMAC_CRYPTO_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR; + } +#endif + + // Serialize message + if( LoRaMacSerializerJoinRequest( macMsg ) != LORAMAC_SERIALIZER_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SERIALIZER; + } + + // Compute mic + if( SecureElementComputeAesCmac( NULL, macMsg->Buffer, ( LORAMAC_JOIN_REQ_MSG_SIZE - LORAMAC_MIC_FIELD_SIZE ), micComputationKeyID, &macMsg->MIC ) != SECURE_ELEMENT_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC; + } + + // Reserialize message to add the MIC + if( LoRaMacSerializerJoinRequest( macMsg ) != LORAMAC_SERIALIZER_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SERIALIZER; + } + + return LORAMAC_CRYPTO_SUCCESS; +} + +LoRaMacCryptoStatus_t LoRaMacCryptoPrepareReJoinType1( LoRaMacMessageReJoinType1_t* macMsg ) +{ + if( macMsg == 0 ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + + // Check for RJcount1 overflow + if( CryptoCtx.NvmCtx->RJcount1 == 65535 ) + { + return LORAMAC_CRYPTO_ERROR_RJCOUNT1_OVERFLOW; + } + + // Serialize message + if( LoRaMacSerializerReJoinType1( macMsg ) != LORAMAC_SERIALIZER_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SERIALIZER; + } + + // Compute mic + // cmac = aes128_cmac(JSIntKey, MHDR | RejoinType | JoinEUI| DevEUI | RJcount1) + if( SecureElementComputeAesCmac( NULL, macMsg->Buffer, ( LORAMAC_RE_JOIN_1_MSG_SIZE - LORAMAC_MIC_FIELD_SIZE ), J_S_INT_KEY, &macMsg->MIC ) != SECURE_ELEMENT_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC; + } + + // Reserialize message to add the MIC + if( LoRaMacSerializerReJoinType1( macMsg ) != LORAMAC_SERIALIZER_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SERIALIZER; + } + + // Increment RJcount1 + CryptoCtx.NvmCtx->RJcount1++; + CryptoCtx.EventCryptoNvmCtxChanged( ); + + return LORAMAC_CRYPTO_SUCCESS; +} + +LoRaMacCryptoStatus_t LoRaMacCryptoPrepareReJoinType0or2( LoRaMacMessageReJoinType0or2_t* macMsg ) +{ + if( macMsg == 0 ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + + // Check for RJcount0 overflow + if( CryptoCtx.RJcount0 == 65535 ) + { + return LORAMAC_CRYPTO_FAIL_RJCOUNT0_OVERFLOW; + } + + // Serialize message + if( LoRaMacSerializerReJoinType0or2( macMsg ) != LORAMAC_SERIALIZER_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SERIALIZER; + } + + // Compute mic + // cmac = aes128_cmac(SNwkSIntKey, MHDR | Rejoin Type | NetID | DevEUI | RJcount0) + if( SecureElementComputeAesCmac( NULL, macMsg->Buffer, ( LORAMAC_RE_JOIN_0_2_MSG_SIZE - LORAMAC_MIC_FIELD_SIZE ), S_NWK_S_INT_KEY, &macMsg->MIC ) != SECURE_ELEMENT_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC; + } + + // Reserialize message to add the MIC + if( LoRaMacSerializerReJoinType0or2( macMsg ) != LORAMAC_SERIALIZER_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SERIALIZER; + } + + // Increment RJcount0 + CryptoCtx.RJcount0++; + + return LORAMAC_CRYPTO_SUCCESS; +} + +LoRaMacCryptoStatus_t LoRaMacCryptoHandleJoinAccept( JoinReqIdentifier_t joinReqType, uint8_t* joinEUI, LoRaMacMessageJoinAccept_t* macMsg ) +{ + if( ( macMsg == 0 ) || ( joinEUI == 0 ) ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + + LoRaMacCryptoStatus_t retval = LORAMAC_CRYPTO_ERROR; + KeyIdentifier_t micComputationKeyID; + KeyIdentifier_t encryptionKeyID; + uint8_t micComputationOffset = 0; +#if( USE_LRWAN_1_1_X_CRYPTO == 1 ) + uint8_t* devNonceForKeyDerivation = ( uint8_t* ) &CryptoCtx.NvmCtx->DevNonce; +#endif + + // Determine decryption key and DevNonce for key derivation + if( joinReqType == JOIN_REQ ) + { + encryptionKeyID = NWK_KEY; + micComputationOffset = CRYPTO_MIC_COMPUTATION_OFFSET; + } +#if( USE_LRWAN_1_1_X_CRYPTO == 1 ) + else + { + encryptionKeyID = J_S_ENC_KEY; + + // If Join-accept is a reply to a rejoin, the RJcount(0 or 1) replaces DevNonce in the key derivation process. + if( ( joinReqType == REJOIN_REQ_0 ) || ( joinReqType == REJOIN_REQ_2 ) ) + { + devNonceForKeyDerivation = ( uint8_t* ) &CryptoCtx.RJcount0; + } + else + { + devNonceForKeyDerivation = ( uint8_t* ) &CryptoCtx.NvmCtx->RJcount1; + } + } +#endif + // Decrypt header, skip MHDR + uint8_t procBuffer[CRYPTO_MAXMESSAGE_SIZE + CRYPTO_MIC_COMPUTATION_OFFSET]; + memset1( procBuffer, 0, ( macMsg->BufSize + micComputationOffset ) ); + + if( SecureElementAesEncrypt( macMsg->Buffer + LORAMAC_MHDR_FIELD_SIZE, ( macMsg->BufSize - LORAMAC_MHDR_FIELD_SIZE ), encryptionKeyID, ( procBuffer + micComputationOffset ) ) != SECURE_ELEMENT_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC; + } + // Copy the result to an offset location to keep space for additional information which have to be added in case of 1.1 and later + memcpy1( macMsg->Buffer + LORAMAC_MHDR_FIELD_SIZE, ( procBuffer + micComputationOffset ), ( macMsg->BufSize - LORAMAC_MHDR_FIELD_SIZE ) ); + + // Parse the message + if( LoRaMacParserJoinAccept( macMsg ) != LORAMAC_PARSER_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_PARSER; + } + + // Is it a LoRaWAN 1.1.0 or later ? + if( macMsg->DLSettings.Bits.OptNeg == 1 ) + { + CryptoCtx.NvmCtx->LrWanVersion.Fields.Minor = 1; + micComputationKeyID = J_S_INT_KEY; + } + else + { + CryptoCtx.NvmCtx->LrWanVersion.Fields.Minor = 0; + micComputationKeyID = NWK_KEY; + } + + // Verify mic + if( CryptoCtx.NvmCtx->LrWanVersion.Fields.Minor == 0 ) + { + // For legacy mode : + // cmac = aes128_cmac(NwkKey, MHDR | JoinNonce | NetID | DevAddr | DLSettings | RxDelay | CFList | CFListType) + if( SecureElementVerifyAesCmac( macMsg->Buffer, ( macMsg->BufSize - LORAMAC_MIC_FIELD_SIZE ), macMsg->MIC, micComputationKeyID ) != SECURE_ELEMENT_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC; + } + } + else + { + // For 1.1 and later: + // cmac = aes128_cmac(JSIntKey, JoinReqType | JoinEUI | DevNonce | MHDR | JoinNonce | NetID | DevAddr | DLSettings | RxDelay | CFList | CFListType) + + // Prepare the msg for integrity check (adding JoinReqType, JoinEUI and DevNonce) + uint16_t bufItr = 0; + procBuffer[bufItr++] = ( uint8_t ) joinReqType; + + memcpyr( &procBuffer[bufItr], joinEUI, LORAMAC_JOIN_EUI_FIELD_SIZE ); + bufItr += LORAMAC_JOIN_EUI_FIELD_SIZE; + + procBuffer[bufItr++] = CryptoCtx.NvmCtx->DevNonce & 0xFF; + procBuffer[bufItr++] = ( CryptoCtx.NvmCtx->DevNonce >> 8 ) & 0xFF; + + procBuffer[bufItr++] = macMsg->MHDR.Value; + + if( SecureElementVerifyAesCmac( procBuffer, ( macMsg->BufSize + micComputationOffset - LORAMAC_MHDR_FIELD_SIZE - LORAMAC_MIC_FIELD_SIZE ), macMsg->MIC, micComputationKeyID ) != SECURE_ELEMENT_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC; + } + + // Check if the JoinNonce is greater as the previous one + uint32_t currentJoinNonce = 0; + currentJoinNonce = ( uint32_t ) macMsg->JoinNonce[0]; + currentJoinNonce |= ( ( uint32_t ) macMsg->JoinNonce[1] << 8 ); + currentJoinNonce |= ( ( uint32_t ) macMsg->JoinNonce[2] << 16 ); + + if( currentJoinNonce > CryptoCtx.NvmCtx->JoinNonce ) + { + CryptoCtx.NvmCtx->JoinNonce = currentJoinNonce; + CryptoCtx.EventCryptoNvmCtxChanged( ); + } + else + { + return LORAMAC_CRYPTO_FAIL_JOIN_NONCE; + } + } + + // Derive session keys +#if( USE_LRWAN_1_1_X_CRYPTO == 1 ) + if( CryptoCtx.NvmCtx->LrWanVersion.Fields.Minor == 1 ) + { + // Derive lifetime keys + retval = LoRaMacCryptoDeriveMcRootKey( APP_KEY ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + + retval = LoRaMacCryptoDeriveMcKEKey( MC_ROOT_KEY ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + + retval = DeriveSessionKey11x( F_NWK_S_INT_KEY, macMsg->JoinNonce, joinEUI, devNonceForKeyDerivation ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + + retval = DeriveSessionKey11x( S_NWK_S_INT_KEY, macMsg->JoinNonce, joinEUI, devNonceForKeyDerivation ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + + retval = DeriveSessionKey11x( NWK_S_ENC_KEY, macMsg->JoinNonce, joinEUI, devNonceForKeyDerivation ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + + retval = DeriveSessionKey11x( APP_S_KEY, macMsg->JoinNonce, joinEUI, devNonceForKeyDerivation ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + } + else +#endif + { + // prior LoRaWAN 1.1.0 + retval = LoRaMacCryptoDeriveMcRootKey( GEN_APP_KEY ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + + retval = LoRaMacCryptoDeriveMcKEKey( MC_ROOT_KEY ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + + retval = DeriveSessionKey10x( APP_S_KEY, macMsg->JoinNonce, macMsg->NetID, ( uint8_t* ) &CryptoCtx.NvmCtx->DevNonce ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + + retval = DeriveSessionKey10x( NWK_S_ENC_KEY, macMsg->JoinNonce, macMsg->NetID, ( uint8_t* ) &CryptoCtx.NvmCtx->DevNonce ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + + retval = DeriveSessionKey10x( F_NWK_S_INT_KEY, macMsg->JoinNonce, macMsg->NetID, ( uint8_t* ) &CryptoCtx.NvmCtx->DevNonce ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + + retval = DeriveSessionKey10x( S_NWK_S_INT_KEY, macMsg->JoinNonce, macMsg->NetID, ( uint8_t* ) &CryptoCtx.NvmCtx->DevNonce ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + } + + // Join-Accept is successfully processed, reset frame counters + CryptoCtx.RJcount0 = 0; + CryptoCtx.NvmCtx->FCntList.FCntUp = 0; + CryptoCtx.NvmCtx->FCntList.FCntDown = FCNT_DOWN_INITAL_VALUE; + CryptoCtx.NvmCtx->FCntList.NFCntDown = FCNT_DOWN_INITAL_VALUE; + CryptoCtx.NvmCtx->FCntList.AFCntDown = FCNT_DOWN_INITAL_VALUE; + CryptoCtx.EventCryptoNvmCtxChanged( ); + + return LORAMAC_CRYPTO_SUCCESS; +} + +LoRaMacCryptoStatus_t LoRaMacCryptoSecureMessage( uint32_t fCntUp, uint8_t txDr, uint8_t txCh, LoRaMacMessageData_t* macMsg ) +{ + LoRaMacCryptoStatus_t retval = LORAMAC_CRYPTO_ERROR; + KeyIdentifier_t payloadDecryptionKeyID = APP_S_KEY; + + if( macMsg == NULL ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + + if( fCntUp < CryptoCtx.NvmCtx->FCntList.FCntUp ) + { + return LORAMAC_CRYPTO_FAIL_FCNT_SMALLER; + } + + // Encrypt payload + if( macMsg->FPort == 0 ) + { + // Use network session key + payloadDecryptionKeyID = NWK_S_ENC_KEY; + } + + if( fCntUp > CryptoCtx.NvmCtx->FCntList.FCntUp ) + { + retval = PayloadEncrypt( macMsg->FRMPayload, macMsg->FRMPayloadSize, payloadDecryptionKeyID, macMsg->FHDR.DevAddr, UPLINK, fCntUp ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + +#if( USE_LRWAN_1_1_X_CRYPTO == 1 ) + if( CryptoCtx.NvmCtx->LrWanVersion.Fields.Minor == 1 ) + { + // Encrypt FOpts + retval = FOptsEncrypt( macMsg->FHDR.FCtrl.Bits.FOptsLen, macMsg->FHDR.DevAddr, UPLINK, FCNT_UP, fCntUp, macMsg->FHDR.FOpts ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + } +#endif + } + CryptoCtx.NvmCtx->FCntList.FCntUp = fCntUp; + CryptoCtx.EventCryptoNvmCtxChanged( ); + + // Serialize message + if( LoRaMacSerializerData( macMsg ) != LORAMAC_SERIALIZER_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SERIALIZER; + } + + // Compute mic +#if( USE_LRWAN_1_1_X_CRYPTO == 1 ) + if( CryptoCtx.NvmCtx->LrWanVersion.Fields.Minor == 1 ) + { + uint32_t cmacS = 0; + uint32_t cmacF = 0; + + // cmacS = aes128_cmac(SNwkSIntKey, B1 | msg) + retval = ComputeCmacB1( macMsg->Buffer, ( macMsg->BufSize - LORAMAC_MIC_FIELD_SIZE ), S_NWK_S_INT_KEY, macMsg->FHDR.FCtrl.Bits.Ack, txDr, txCh, macMsg->FHDR.DevAddr, fCntUp, &cmacS ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + //cmacF = aes128_cmac(FNwkSIntKey, B0 | msg) + retval = ComputeCmacB0( macMsg->Buffer, ( macMsg->BufSize - LORAMAC_MIC_FIELD_SIZE ), F_NWK_S_INT_KEY, macMsg->FHDR.FCtrl.Bits.Ack, UPLINK, macMsg->FHDR.DevAddr, fCntUp, &cmacF ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + // MIC = cmacS[0..1] | cmacF[0..1] + macMsg->MIC = ( ( cmacF << 16 ) & 0xFFFF0000 ) | ( cmacS & 0x0000FFFF ); + } + else +#endif + { + // MIC = cmacF[0..3] + // The IsAck parameter is every time false since the ConfFCnt field is not used in legacy mode. + retval = ComputeCmacB0( macMsg->Buffer, ( macMsg->BufSize - LORAMAC_MIC_FIELD_SIZE ), NWK_S_ENC_KEY, false, UPLINK, macMsg->FHDR.DevAddr, fCntUp, &macMsg->MIC ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + } + + // Re-serialize message to add the MIC + if( LoRaMacSerializerData( macMsg ) != LORAMAC_SERIALIZER_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SERIALIZER; + } + + return LORAMAC_CRYPTO_SUCCESS; +} + +LoRaMacCryptoStatus_t LoRaMacCryptoUnsecureMessage( AddressIdentifier_t addrID, uint32_t address, FCntIdentifier_t fCntID, uint32_t fCntDown, LoRaMacMessageData_t* macMsg ) +{ + if( macMsg == 0 ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + + if( CheckFCntDown( fCntID, fCntDown ) == false ) + { + return LORAMAC_CRYPTO_FAIL_FCNT_SMALLER; + } + + LoRaMacCryptoStatus_t retval = LORAMAC_CRYPTO_ERROR; + KeyIdentifier_t payloadDecryptionKeyID = APP_S_KEY; + KeyIdentifier_t micComputationKeyID = S_NWK_S_INT_KEY; + KeyAddr_t* curItem; + + // Parse the message + if( LoRaMacParserData( macMsg ) != LORAMAC_PARSER_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_PARSER; + } + + // Determine current security context + retval = GetKeyAddrItem( addrID, &curItem ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + + payloadDecryptionKeyID = curItem->AppSkey; + micComputationKeyID = curItem->NwkSkey; + + // Check if it is our address + if( address != macMsg->FHDR.DevAddr ) + { + return LORAMAC_CRYPTO_FAIL_ADDRESS; + } + + // Compute mic + bool isAck = macMsg->FHDR.FCtrl.Bits.Ack; + if( CryptoCtx.NvmCtx->LrWanVersion.Fields.Minor == 0 ) + { + // In legacy mode the IsAck parameter is forced to be false since the ConfFCnt field is not used. + isAck = false; + } + + // Verify mic + retval = VerifyCmacB0( macMsg->Buffer, ( macMsg->BufSize - LORAMAC_MIC_FIELD_SIZE ), micComputationKeyID, isAck, DOWNLINK, address, fCntDown, macMsg->MIC ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + + // Decrypt payload + if( macMsg->FPort == 0 ) + { + // Use network session encryption key + payloadDecryptionKeyID = NWK_S_ENC_KEY; + } + retval = PayloadEncrypt( macMsg->FRMPayload, macMsg->FRMPayloadSize, payloadDecryptionKeyID, address, DOWNLINK, fCntDown ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + +#if( USE_LRWAN_1_1_X_CRYPTO == 1 ) + if( CryptoCtx.NvmCtx->LrWanVersion.Fields.Minor == 1 ) + { + // Decrypt FOpts + retval = FOptsEncrypt( macMsg->FHDR.FCtrl.Bits.FOptsLen, address, DOWNLINK, fCntID, fCntDown, macMsg->FHDR.FOpts ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + } +#endif + + UpdateFCntDown( fCntID, fCntDown ); + + return LORAMAC_CRYPTO_SUCCESS; +} + +LoRaMacCryptoStatus_t LoRaMacCryptoDeriveMcRootKey( KeyIdentifier_t keyID ) +{ + // Prevent other keys than GenAppKey for LoRaWAN 1.0.x or AppKey for LoRaWAN 1.1 or later + if( ( ( keyID == APP_KEY ) && ( CryptoCtx.NvmCtx->LrWanVersion.Fields.Minor == 0 ) ) || + ( ( keyID == GEN_APP_KEY ) && ( CryptoCtx.NvmCtx->LrWanVersion.Fields.Minor == 1 ) ) ) + { + return LORAMAC_CRYPTO_ERROR_INVALID_KEY_ID; + } + uint8_t compBase[16] = { 0 }; + + if( CryptoCtx.NvmCtx->LrWanVersion.Fields.Minor == 1 ) + { + compBase[0] = 0x20; + } + if( SecureElementDeriveAndStoreKey( CryptoCtx.NvmCtx->LrWanVersion, compBase, keyID, MC_ROOT_KEY ) != SECURE_ELEMENT_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC; + } + + return LORAMAC_CRYPTO_SUCCESS; +} + +LoRaMacCryptoStatus_t LoRaMacCryptoDeriveMcKEKey( KeyIdentifier_t keyID ) +{ + // Prevent other keys than McRootKey + if( keyID != MC_ROOT_KEY ) + { + return LORAMAC_CRYPTO_ERROR_INVALID_KEY_ID; + } + uint8_t compBase[16] = { 0 }; + + if( SecureElementDeriveAndStoreKey( CryptoCtx.NvmCtx->LrWanVersion, compBase, keyID, MC_KE_KEY ) != SECURE_ELEMENT_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC; + } + + return LORAMAC_CRYPTO_SUCCESS; +} + +LoRaMacCryptoStatus_t LoRaMacCryptoDeriveMcSessionKeyPair( AddressIdentifier_t addrID, uint32_t mcAddr ) +{ + if( mcAddr == 0 ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + + LoRaMacCryptoStatus_t retval = LORAMAC_CRYPTO_ERROR; + + // Determine current security context + KeyAddr_t* curItem; + retval = GetKeyAddrItem( addrID, &curItem ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + + //McAppSKey = aes128_encrypt(McKey, 0x01 | McAddr | pad16) + //McNwkSKey = aes128_encrypt(McKey, 0x02 | McAddr | pad16) + + uint8_t compBaseAppS[16] = { 0 }; + uint8_t compBaseNwkS[16] = { 0 }; + + compBaseAppS[0] = 0x01; + compBaseAppS[1] = mcAddr & 0xFF; + compBaseAppS[2] = ( mcAddr >> 8 ) & 0xFF; + compBaseAppS[3] = ( mcAddr >> 16 ) & 0xFF; + compBaseAppS[4] = ( mcAddr >> 24 ) & 0xFF; + + compBaseNwkS[0] = 0x02; + compBaseNwkS[1] = mcAddr & 0xFF; + compBaseNwkS[2] = ( mcAddr >> 8 ) & 0xFF; + compBaseNwkS[3] = ( mcAddr >> 16 ) & 0xFF; + compBaseNwkS[4] = ( mcAddr >> 24 ) & 0xFF; + + if( SecureElementDeriveAndStoreKey( CryptoCtx.NvmCtx->LrWanVersion, compBaseAppS, curItem->RootKey, curItem->AppSkey ) != SECURE_ELEMENT_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC; + } + + if( SecureElementDeriveAndStoreKey( CryptoCtx.NvmCtx->LrWanVersion, compBaseNwkS, curItem->RootKey, curItem->NwkSkey ) != SECURE_ELEMENT_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC; + } + + return LORAMAC_CRYPTO_SUCCESS; +} diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacCrypto.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacCrypto.h new file mode 100644 index 00000000..613740b7 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacCrypto.h @@ -0,0 +1,320 @@ +/*! + * \file LoRaMacCrypto.h + * + * \brief LoRa MAC layer cryptographic functionality implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + * + * addtogroup LORAMAC + * \{ + * + */ +#ifndef __LORAMAC_CRYPTO_H__ +#define __LORAMAC_CRYPTO_H__ + +#include +#include +#include +#include "utilities.h" +#include "LoRaMacTypes.h" +#include "LoRaMacMessageTypes.h" + +/* + * Initial value of the frame counters + */ +#define FCNT_DOWN_INITAL_VALUE 0xFFFFFFFF + +/*! + * LoRaMac Cryto Status + */ +typedef enum eLoRaMacCryptoStatus +{ + /*! + * No error occurred + */ + LORAMAC_CRYPTO_SUCCESS = 0, + /*! + * MIC does not match + */ + LORAMAC_CRYPTO_FAIL_MIC, + /*! + * Address does not match + */ + LORAMAC_CRYPTO_FAIL_ADDRESS, + /*! + * JoinNonce was not greater than previous one. + */ + LORAMAC_CRYPTO_FAIL_JOIN_NONCE, + /*! + * RJcount0 reached 2^16-1 + */ + LORAMAC_CRYPTO_FAIL_RJCOUNT0_OVERFLOW, + /*! + * FCNT_ID is not supported + */ + LORAMAC_CRYPTO_FAIL_FCNT_ID, + /*! + * FCntUp/Down check failed (new FCnt is smaller than previous one) + */ + LORAMAC_CRYPTO_FAIL_FCNT_SMALLER, + /*! + * FCntUp/Down check failed (duplicated) + */ + LORAMAC_CRYPTO_FAIL_FCNT_DUPLICATED, + /*! + * MAX_GAP_FCNT check failed + */ + LORAMAC_CRYPTO_FAIL_MAX_GAP_FCNT, + /*! + * Not allowed parameter value + */ + LORAMAC_CRYPTO_FAIL_PARAM, + /*! + * Null pointer exception + */ + LORAMAC_CRYPTO_ERROR_NPE, + /*! + * Invalid key identifier exception + */ + LORAMAC_CRYPTO_ERROR_INVALID_KEY_ID, + /*! + * Invalid address identifier exception + */ + LORAMAC_CRYPTO_ERROR_INVALID_ADDR_ID, + /*! + * Invalid LoRaWAN specification version + */ + LORAMAC_CRYPTO_ERROR_INVALID_VERSION, + /*! + * Incompatible buffer size + */ + LORAMAC_CRYPTO_ERROR_BUF_SIZE, + /*! + * The secure element reports an error + */ + LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC, + /*! + * Error from parser reported + */ + LORAMAC_CRYPTO_ERROR_PARSER, + /*! + * Error from serializer reported + */ + LORAMAC_CRYPTO_ERROR_SERIALIZER, + /*! + * RJcount1 reached 2^16-1 which should never happen + */ + LORAMAC_CRYPTO_ERROR_RJCOUNT1_OVERFLOW, + /*! + * Undefined Error occurred + */ + LORAMAC_CRYPTO_ERROR, +}LoRaMacCryptoStatus_t; + +/*! + * Signature of callback function to be called by the LoRaMac Crypto module when the + * non-volatile context have to be stored. It is also possible to save the entire + * crypto module context. + * + */ +typedef void ( *LoRaMacCryptoNvmEvent )( void ); + +/*! + * Initialization of LoRaMac Crypto module + * It sets initial values of volatile variables and assigns the non-volatile context. + * + * \param[IN] cryptoNvmCtxChanged - Callback function which will be called when the + * non-volatile context have to be stored. + * \retval - Status of the operation + */ +LoRaMacCryptoStatus_t LoRaMacCryptoInit( LoRaMacCryptoNvmEvent cryptoNvmCtxChanged ); + +/*! + * Sets the LoRaWAN specification version to be used. + * + * \warning This function should be used for ABP only. In case of OTA the version will be set automatically. + * + * \param[IN] version - LoRaWAN specification version to be used. + * + * \retval - Status of the operation + */ +LoRaMacCryptoStatus_t LoRaMacCryptoSetLrWanVersion( Version_t version ); + +/*! + * Restores the internal nvm context from passed pointer. + * + * \param[IN] cryptoNmvCtx - Pointer to non-volatile crypto module context to be restored. + * \retval - Status of the operation + */ +LoRaMacCryptoStatus_t LoRaMacCryptoRestoreNvmCtx( void* cryptoNvmCtx ); + +/*! + * Returns a pointer to the internal non-volatile context. + * + * \param[IN] cryptoNvmCtxSize - Size of the module non-volatile context + * \retval - Points to a structure where the module store its non-volatile context + */ +void* LoRaMacCryptoGetNvmCtx( size_t* cryptoNvmCtxSize ); + +/*! + * Returns updated fCntID downlink counter value. + * + * \param[IN] fCntID - Frame counter identifier + * \param[IN] maxFcntGap - Maximum allowed frame counter difference (only necessary for L2 LW1.0.x) + * \param[IN] frameFcnt - Received frame counter (used to update current counter value) + * \param[OUT] currentDown - Current downlink counter value + * \retval - Status of the operation + */ +LoRaMacCryptoStatus_t LoRaMacCryptoGetFCntDown( FCntIdentifier_t fCntID, uint16_t maxFCntGap, uint32_t frameFcnt, uint32_t* currentDown ); + +/*! + * Returns updated fCntUp uplink counter value. + * + * \param[IN] currentUp - Uplink counter value + * \retval - Status of the operation + */ +LoRaMacCryptoStatus_t LoRaMacCryptoGetFCntUp( uint32_t* currentUp ); + +/*! + * Provides multicast context. + * + * \param[IN] multicastList - Pointer to the multicast context list + * + * \retval - Status of the operation + */ +LoRaMacCryptoStatus_t LoRaMacCryptoSetMulticastReference( MulticastCtx_t* multicastList ); + +/*! + * Sets a key + * + * \param[IN] keyID - Key identifier + * \param[IN] key - Key value (16 byte), if its a multicast key it must be encrypted with McKEKey + * \retval - Status of the operation + */ +LoRaMacCryptoStatus_t LoRaMacCryptoSetKey( KeyIdentifier_t keyID, uint8_t* key ); + +/*! + * Prepares the join-request message. + * It computes the mic and add it to the message. + * + * \param[IN/OUT] macMsg - Join-request message object + * \retval - Status of the operation + */ +LoRaMacCryptoStatus_t LoRaMacCryptoPrepareJoinRequest( LoRaMacMessageJoinRequest_t* macMsg ); + +/*! + * Prepares a rejoin-request type 1 message. + * It computes the mic and add it to the message. + * + * \param[IN/OUT] macMsg - Rejoin message object + * \retval - Status of the operation + */ +LoRaMacCryptoStatus_t LoRaMacCryptoPrepareReJoinType1( LoRaMacMessageReJoinType1_t* macMsg ); + +/*! + * Prepares a rejoin-request type 0 or 2 message. + * It computes the mic and add it to the message. + * + * \param[IN/OUT] macMsg - Rejoin message object + * \retval - Status of the operation + */ +LoRaMacCryptoStatus_t LoRaMacCryptoPrepareReJoinType0or2( LoRaMacMessageReJoinType0or2_t* macMsg ); + +/*! + * Handles the join-accept message. + * It decrypts the message, verifies the MIC and if successful derives the session keys. + * + * \param[IN] joinReqType - Type of last join-request or rejoin which triggered the join-accept response + * \param[IN] joinEUI - Join server EUI (8 byte) + * \param[IN/OUT] macMsg - Join-accept message object + * \retval - Status of the operation + */ +LoRaMacCryptoStatus_t LoRaMacCryptoHandleJoinAccept( JoinReqIdentifier_t joinReqType, uint8_t* joinEUI, LoRaMacMessageJoinAccept_t* macMsg ); + +/*! + * Secures a message (encryption + integrity). + * + * \param[IN] fCntUp - Uplink sequence counter + * \param[IN] txDr - Data rate used for the transmission + * \param[IN] txCh - Index of the channel used for the transmission + * \param[IN/OUT] macMsg - Data message object + * \retval - Status of the operation + */ +LoRaMacCryptoStatus_t LoRaMacCryptoSecureMessage( uint32_t fCntUp, uint8_t txDr, uint8_t txCh, LoRaMacMessageData_t* macMsg ); + +/*! + * Unsecures a message (decryption + integrity verification). + * + * \param[IN] addrID - Address identifier + * \param[IN] address - Address + * \param[IN] fCntID - Frame counter identifier + * \param[IN] fCntDown - Downlink sequence counter + * \param[IN/OUT] macMsg - Data message object + * \retval - Status of the operation + */ +LoRaMacCryptoStatus_t LoRaMacCryptoUnsecureMessage( AddressIdentifier_t addrID, uint32_t address, FCntIdentifier_t fCntID, uint32_t fCntDown, LoRaMacMessageData_t* macMsg ); + +/*! + * Derives the McRootKey from the GenAppKey or AppKey. + * + * 1.0.x + * McRootKey = aes128_encrypt(GenAppKey , 0x00 | pad16) + * + * 1.1.x + * McRootKey = aes128_encrypt(AppKey , 0x20 | pad16) + * + * \param[IN] keyID - Key identifier of the root key to use to perform the derivation ( GenAppKey or AppKey ) + * \retval - Status of the operation + */ +LoRaMacCryptoStatus_t LoRaMacCryptoDeriveMcRootKey( KeyIdentifier_t keyID ); + +/*! + * Derives the McKEKey from the McRootKey. + * + * McKEKey = aes128_encrypt(McRootKey , 0x00 | pad16) + * + * \param[IN] keyID - Key identifier of the root key to use to perform the derivation ( McRootKey ) + * \retval - Status of the operation + */ +LoRaMacCryptoStatus_t LoRaMacCryptoDeriveMcKEKey( KeyIdentifier_t keyID ); + +/*! + * Derives a Multicast group key pair ( McAppSKey, McNwkSKey ) from McKey + * + * McAppSKey = aes128_encrypt(McKey, 0x01 | McAddr | pad16) + * McNwkSKey = aes128_encrypt(McKey, 0x02 | McAddr | pad16) + * + * \param[IN] addrID - Address identifier to select the multicast group + * \param[IN] mcAddr - Multicast group address (4 bytes) + * \retval - Status of the operation + */ +LoRaMacCryptoStatus_t LoRaMacCryptoDeriveMcSessionKeyPair( AddressIdentifier_t addrID, uint32_t mcAddr ); + +/*! \} addtogroup LORAMAC */ + +#endif // __LORAMAC_CRYPTO_H__ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacFCntHandler.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacFCntHandler.h new file mode 100644 index 00000000..df766251 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacFCntHandler.h @@ -0,0 +1,170 @@ +/*! + * \file LoRaMacFCntHandler.h + * + * \brief LoRa MAC layer frame counter handling + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + * + * addtogroup LORAMAC + * \{ + * + */ +#ifndef __LORAMAC_FCNT_HANDLER_H__ +#define __LORAMAC_FCNT_HANDLER_H__ + +#include +#include "LoRaMacTypes.h" +#include "LoRaMacMessageTypes.h" + +/*! + * LoRaMac FCnt Handler Status + */ +typedef enum eLoRaMacFCntHandlerStatus +{ + /*! + * No error occurred + */ + LORAMAC_FCNT_HANDLER_SUCCESS = 0, + /*! + * FCntUp/Down check failed + */ + LORAMAC_FCNT_HANDLER_CHECK_FAIL, + /*! + * The status idicates that the node has lost MAX_FCNT_GAP or more frames + */ + LORAMAC_FCNT_HANDLER_MAX_GAP_FAIL, + /*! + * Invalid frame counter identifier exception + */ + LORAMAC_FCNT_HANDLER_ERROR_INVALID_FCNT_ID, + /*! + * Null pointer exception + */ + LORAMAC_FCNT_HANDLER_ERROR_NPE, + /*! + * Undefined Error occurred + */ + LORAMAC_FCNT_HANDLER_ERROR, +}LoRaMacFCntHandlerStatus_t; + +/*! + * Signature of callback function to be called by this module when the + * non-volatile needs to be saved. + */ +typedef void ( *EventNvmCtxChanged )( void ); + +/*! + * \brief Initialization of LoRaMac FCnt Handler module + * + * \param[IN] fCntHandlerNvmCtxChanged - Callback function which will be called when the + * non-volatile context needs to be saved. + * + * \retval - Status of the operation + */ +LoRaMacFCntHandlerStatus_t LoRaMacFCntHandlerInit( EventNvmCtxChanged fCntHandlerNvmCtxChanged ); + +/*! + * Restores the internal non-volatile context from passed pointer. + * + * \param[IN] fCntHandlerNvmCtx - Pointer to non-volatile FCnt handler module context to be restored. + * + * \retval - Status of the operation + */ +LoRaMacFCntHandlerStatus_t LoRaMacFCntHandlerRestoreNvmCtx( void* fCntHandlerNvmCtx ); + +/*! + * Returns a pointer to the internal non-volatile context. + * + * \param[IN] fCntHandlerNvmCtxSize - Size of the module non-volatile context + * + * \retval - Points to a structure where the module store its non-volatile context + */ +void* LoRaMacFCntHandlerGetNvmCtx( size_t* fCntHandlerNvmCtxSize ); + +/*! + * Computes next 32 bit downlink counter value and determines the frame counter ID. + * + * \param[IN] addrID - Address identifier + * \param[IN] fType - Frame type + * \param[IN] macMsg - Data message object, holding the current 16 bit transmitted frame counter + * \param[IN] lrWanVersion - LoRaWAN version + * \param[IN] maxFCntGap - Maximum allowed frame counter difference (only for 1.0.X necessary) + * \param[OUT] fCntID - Frame counter identifier + * \param[OUT] currentDown - Current downlink counter value + * + * \retval - Status of the operation + */ +LoRaMacFCntHandlerStatus_t LoRaMacGetFCntDown( AddressIdentifier_t addrID, FType_t fType, LoRaMacMessageData_t* macMsg, Version_t lrWanVersion, + uint16_t maxFCntGap, FCntIdentifier_t* fCntID, uint32_t* currentDown ); + +/*! + * Sets the downlink counter value according to the counter ID. + * + * \param[IN] fCntID - Frame counter identifier + * \param[IN] currentDown - Current downlink counter value + * + * \retval - Status of the operation + */ +LoRaMacFCntHandlerStatus_t LoRaMacSetFCntDown( FCntIdentifier_t fCntID, uint32_t currentDown ); + +/*! + * Computes next uplink counter value. + * + * \param[OUT] currentUp - Current uplink counter value + * + * \retval - Status of the operation + */ +LoRaMacFCntHandlerStatus_t LoRaMacGetFCntUp( uint32_t* currentUp ); + +/*! + * Sets the uplink counter value according to the counter ID. + * + * \param[IN] currentUp - Current uplink counter value + * + * \retval - Status of the operation + */ +LoRaMacFCntHandlerStatus_t LoRaMacSetFCntUp( uint32_t currentUp ); + +/*! + * Resets the value of FCntUp, NFCntDown, AFCntDown and FCntDown to zero + * + * \retval - Status of the operation + */ +LoRaMacFCntHandlerStatus_t LoRaMacResetFCnts( void ); + +/*! + * Sets the reference to the multicast downlink counter + * + * \param[IN] multicastList Pointer to the multicast list + * + * \retval - Status of the operation + */ +LoRaMacFCntHandlerStatus_t LoRaMacFCntHandlerSetMulticastReference( MulticastCtx_t* multicastList ); + +/*! \} addtogroup LORAMAC */ + +#endif // __LORAMAC_FCNT_HANDLER_H__ + diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacHeaderTypes.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacHeaderTypes.h new file mode 100644 index 00000000..61c43c06 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacHeaderTypes.h @@ -0,0 +1,186 @@ +/*! + * \file LoRaMacHeaderTypes.h + * + * \brief LoRa MAC layer header type definitions + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + * + * addtogroup LORAMAC + * \{ + * + */ +#ifndef __LORAMAC_HEADER_TYPES_H__ +#define __LORAMAC_HEADER_TYPES_H__ + +#include + +/*! Frame header (FHDR) maximum field size */ +#define LORAMAC_FHDR_MAX_FIELD_SIZE 22 + +/*! FHDR Device address field size */ +#define LORAMAC_FHDR_DEV_ADD_FIELD_SIZE 4 + +/*! FHDR Frame control field size */ +#define LORAMAC_FHDR_F_CTRL_FIELD_SIZE 1 + +/*! FHDR Frame control field size */ +#define LORAMAC_FHDR_F_CNT_FIELD_SIZE 2 + +/*! FOpts maximum field size */ +#define LORAMAC_FHDR_F_OPTS_MAX_FIELD_SIZE 15 + + +/*! + * LoRaMAC field definition of DLSettings + * + * LoRaWAN Specification V1.0.2, chapter 5.4 + */ +typedef union uLoRaMacDLSettings +{ + /*! + * Byte-access to the bits + */ + uint8_t Value; + /*! + * Structure containing single access to header bits + */ + struct sDLSettingsBits + { + /*! + * Data rate of a downlink using the second receive window + */ + uint8_t RX2DataRate : 4; + /*! + * Offset between up and downlink datarate of first reception slot + */ + uint8_t RX1DRoffset : 3; + /*! + * Indicates network server LoRaWAN implementation version 1.1 or later. + */ + uint8_t OptNeg : 1; + }Bits; +}LoRaMacDLSettings_t; + +/*! + * LoRaMAC header field definition (MHDR field) + * + * LoRaWAN Specification V1.0.2, chapter 4.2 + */ +typedef union uLoRaMacHeader +{ + /*! + * Byte-access to the bits + */ + uint8_t Value; + /*! + * Structure containing single access to header bits + */ + struct sMacHeaderBits + { + /*! + * Major version + */ + uint8_t Major : 2; + /*! + * RFU + */ + uint8_t RFU : 3; + /*! + * Message type + */ + uint8_t MType : 3; + }Bits; +}LoRaMacHeader_t; + +/*! + * LoRaMAC frame control field definition (FCtrl) + * + * LoRaWAN Specification V1.0.2, chapter 4.3.1 + */ +typedef union uLoRaMacFrameCtrl +{ + /*! + * Byte-access to the bits + */ + uint8_t Value; + /*! + * Structure containing single access to bits + */ + struct sCtrlBits + { + /*! + * Frame options length + */ + uint8_t FOptsLen : 4; + /*! + * Frame pending bit + */ + uint8_t FPending : 1; + /*! + * Message acknowledge bit + */ + uint8_t Ack : 1; + /*! + * ADR acknowledgment request bit + */ + uint8_t AdrAckReq : 1; + /*! + * ADR control in frame header + */ + uint8_t Adr : 1; + }Bits; +}LoRaMacFrameCtrl_t; + +/*! + * LoRaMac Frame header (FHDR) + * + * LoRaWAN Specification V1.0.2, chapter 4.3.1 + */ +typedef struct sLoRaMacFrameHeader +{ + /*! + * Device address + */ + uint32_t DevAddr; + /*! + * Frame control field + */ + LoRaMacFrameCtrl_t FCtrl; + /*! + * Frame counter + */ + uint16_t FCnt; + /*! + * FOpts field may transport MAC commands (opt. 0-15 Bytes) + */ + uint8_t FOpts[LORAMAC_FHDR_F_OPTS_MAX_FIELD_SIZE]; +}LoRaMacFrameHeader_t; + +/*! \} addtogroup LORAMAC */ + +#endif // __LORAMAC_HEADER_TYPES_H__ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacMessageTypes.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacMessageTypes.h new file mode 100644 index 00000000..de53f52a --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacMessageTypes.h @@ -0,0 +1,327 @@ +/*! + * \file LoRaMacMessageTypes.h + * + * \brief LoRa MAC layer message type definitions + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + * + * addtogroup LORAMAC + * \{ + * + */ +#ifndef __LORAMAC_MESSAGE_TYPES_H__ +#define __LORAMAC_MESSAGE_TYPES_H__ + +#include +#include "LoRaMacHeaderTypes.h" + + +/*! MAC header field size */ +#define LORAMAC_MHDR_FIELD_SIZE 1 + +/*! Join EUI field size */ +#define LORAMAC_JOIN_EUI_FIELD_SIZE 8 + +/*! Device EUI field size */ +#define LORAMAC_DEV_EUI_FIELD_SIZE 8 + +/*! Join-server nonce field size */ +#define LORAMAC_JOIN_NONCE_FIELD_SIZE 3 + +/*! Network ID field size */ +#define LORAMAC_NET_ID_FIELD_SIZE 3 + +/*! Port field size */ +#define LORAMAC_F_PORT_FIELD_SIZE 1 + +/*! CFList field size */ +#define LORAMAC_C_FLIST_FIELD_SIZE 16 + +/*! MIC field size */ +#define LORAMAC_MIC_FIELD_SIZE 4 + +/*! Join-request message size */ +#define LORAMAC_JOIN_REQ_MSG_SIZE 23 + +/*! ReJoin-request type 1 message size */ +#define LORAMAC_RE_JOIN_1_MSG_SIZE 24 + +/*! ReJoin-request type 0 or 2 message size */ +#define LORAMAC_RE_JOIN_0_2_MSG_SIZE 19 + +/*! + * LoRaMac type for Join-request message + */ +typedef struct sLoRaMacMessageJoinRequest +{ + /*! + * Serialized message buffer + */ + uint8_t* Buffer; + /*! + * Size of serialized message buffer + */ + uint8_t BufSize; + /*! + * MAC header + */ + LoRaMacHeader_t MHDR; + /*! + * Join EUI + */ + uint8_t JoinEUI[LORAMAC_JOIN_EUI_FIELD_SIZE]; + /*! + * Device EUI + */ + uint8_t DevEUI[LORAMAC_DEV_EUI_FIELD_SIZE]; + /*! + * Device Nonce + */ + uint16_t DevNonce; + /*! + * Message integrity code (MIC) + */ + uint32_t MIC; +}LoRaMacMessageJoinRequest_t; + +/*! + * LoRaMac type for rejoin-request type 1 message + */ +typedef struct sLoRaMacMessageReJoinType1 +{ + /*! + * Serialized message buffer + */ + uint8_t* Buffer; + /*! + * Size of serialized message buffer + */ + uint8_t BufSize; + /*! + * MAC header + */ + LoRaMacHeader_t MHDR; + /*! + * Rejoin-request type ( 1 ) + */ + uint8_t ReJoinType; + /*! + * Join EUI + */ + uint8_t JoinEUI[LORAMAC_JOIN_EUI_FIELD_SIZE]; + /*! + * Device EUI + */ + uint8_t DevEUI[LORAMAC_DEV_EUI_FIELD_SIZE]; + /*! + * ReJoin Type 1 counter + */ + uint16_t RJcount1; + /*! + * Message integrity code (MIC) + */ + uint32_t MIC; +}LoRaMacMessageReJoinType1_t; + +/*! + * LoRaMac type for rejoin-request type 0 or 2 message + */ +typedef struct sLoRaMacMessageReJoinType0or2 +{ + /*! + * Serialized message buffer + */ + uint8_t* Buffer; + /*! + * Size of serialized message buffer + */ + uint8_t BufSize; + /*! + * MAC header + */ + LoRaMacHeader_t MHDR; + /*! + * Rejoin-request type ( 0 or 2 ) + */ + uint8_t ReJoinType; + /*! + * Network ID ( 3 bytes ) + */ + uint8_t NetID[LORAMAC_NET_ID_FIELD_SIZE]; + /*! + * Device EUI + */ + uint8_t DevEUI[LORAMAC_DEV_EUI_FIELD_SIZE]; + /*! + * ReJoin Type 0 and 2 frame counter + */ + uint16_t RJcount0; + /*! + * Message integrity code (MIC) + */ + uint32_t MIC; +}LoRaMacMessageReJoinType0or2_t; + +/*! + * LoRaMac type for Join-accept message + */ +typedef struct sLoRaMacMessageJoinAccept +{ + /*! + * Serialized message buffer + */ + uint8_t* Buffer; + /*! + * Size of serialized message buffer + */ + uint8_t BufSize; + /*! + * MAC header + */ + LoRaMacHeader_t MHDR; + /*! + * Server Nonce ( 3 bytes ) + */ + uint8_t JoinNonce[LORAMAC_JOIN_NONCE_FIELD_SIZE]; + /*! + * Network ID ( 3 bytes ) + */ + uint8_t NetID[LORAMAC_NET_ID_FIELD_SIZE]; + /*! + * Device address + */ + uint32_t DevAddr; + /*! + * Device address + */ + LoRaMacDLSettings_t DLSettings; + /*! + * Delay between TX and RX + */ + uint8_t RxDelay; + /*! + * List of channel frequencies (opt.) + */ + uint8_t CFList[16]; + /*! + * Message integrity code (MIC) + */ + uint32_t MIC; +}LoRaMacMessageJoinAccept_t; + + +/*! + * LoRaMac type for Data MAC messages + * (Unconfirmed Data Up, Confirmed Data Up, Unconfirmed Data Down, Confirmed Data Down) + */ +typedef struct sLoRaMacMessageData +{ + /*! + * Serialized message buffer + */ + uint8_t* Buffer; + /*! + * Size of serialized message buffer + */ + uint8_t BufSize; + /*! + * MAC header + */ + LoRaMacHeader_t MHDR; + /*! + * Frame header (FHDR) + */ + LoRaMacFrameHeader_t FHDR; + /*! + * Port field (opt.) + */ + uint8_t FPort; + /*! + * Frame payload may contain MAC commands or data (opt.) + */ + uint8_t* FRMPayload; + /*! + * Size of frame payload (not included in LoRaMac messages) + */ + uint8_t FRMPayloadSize; + /*! + * Message integrity code (MIC) + */ + uint32_t MIC; +}LoRaMacMessageData_t; + +/*! + * LoRaMac message type enumerator + */ +typedef enum eLoRaMacMessageType +{ + /*! + * Join-request message + */ + LORAMAC_MSG_TYPE_JOIN_REQUEST, + /*! + * Rejoin-request type 1 message + */ + LORAMAC_MSG_TYPE_RE_JOIN_1, + /*! + * Rejoin-request type 1 message + */ + LORAMAC_MSG_TYPE_RE_JOIN_0_2, + /*! + * Join-accept message + */ + LORAMAC_MSG_TYPE_JOIN_ACCEPT, + /*! + * Data MAC messages + */ + LORAMAC_MSG_TYPE_DATA, + /*! + * Undefined message type + */ + LORAMAC_MSG_TYPE_UNDEF, +}LoRaMacMessageType_t; + +/*! + * LoRaMac general message type + */ +typedef struct sLoRaMacMessage +{ + LoRaMacMessageType_t Type; + union uMessage + { + LoRaMacMessageJoinRequest_t JoinReq; + LoRaMacMessageReJoinType1_t ReJoin1; + LoRaMacMessageReJoinType0or2_t ReJoin0or2; + LoRaMacMessageJoinAccept_t JoinAccept; + LoRaMacMessageData_t Data; + }Message; +}LoRaMacMessage_t; + +/*! \} addtogroup LORAMAC */ + +#endif // __LORAMAC_MESSAGE_TYPES_H__ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacParser.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacParser.c new file mode 100644 index 00000000..6f29d06d --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacParser.c @@ -0,0 +1,118 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + ___ _____ _ ___ _ _____ ___ ___ ___ ___ +/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| +\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| +|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| +embedded.connectivity.solutions=============== + +Description: LoRa MAC layer message parser functionality implementation + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ), + Daniel Jaeckle ( STACKFORCE ), Johannes Bruder ( STACKFORCE ) +*/ +#include "LoRaMacParser.h" +#include "utilities.h" + +LoRaMacParserStatus_t LoRaMacParserJoinAccept( LoRaMacMessageJoinAccept_t* macMsg ) +{ + if( ( macMsg == 0 ) || ( macMsg->Buffer == 0 ) ) + { + return LORAMAC_PARSER_ERROR_NPE; + } + + uint16_t bufItr = 0; + + macMsg->MHDR.Value = macMsg->Buffer[bufItr++]; + + memcpy1( macMsg->JoinNonce, &macMsg->Buffer[bufItr], 3 ); + bufItr = bufItr + 3; + + memcpy1( macMsg->NetID, &macMsg->Buffer[bufItr], 3 ); + bufItr = bufItr + 3; + + macMsg->DevAddr = ( uint32_t ) macMsg->Buffer[bufItr++]; + macMsg->DevAddr |= ( ( uint32_t ) macMsg->Buffer[bufItr++] << 8 ); + macMsg->DevAddr |= ( ( uint32_t ) macMsg->Buffer[bufItr++] << 16 ); + macMsg->DevAddr |= ( ( uint32_t ) macMsg->Buffer[bufItr++] << 24 ); + + macMsg->DLSettings.Value = macMsg->Buffer[bufItr++]; + + macMsg->RxDelay = macMsg->Buffer[bufItr++]; + + if( ( macMsg->BufSize - LORAMAC_MIC_FIELD_SIZE - bufItr ) == LORAMAC_C_FLIST_FIELD_SIZE ) + { + memcpy1( macMsg->CFList, &macMsg->Buffer[bufItr], LORAMAC_C_FLIST_FIELD_SIZE ); + bufItr = bufItr + LORAMAC_C_FLIST_FIELD_SIZE; + } + else if( ( macMsg->BufSize - LORAMAC_MIC_FIELD_SIZE - bufItr ) > 0 ) + { + return LORAMAC_PARSER_FAIL; + } + + macMsg->MIC = ( uint32_t ) macMsg->Buffer[bufItr++]; + macMsg->MIC |= ( ( uint32_t ) macMsg->Buffer[bufItr++] << 8 ); + macMsg->MIC |= ( ( uint32_t ) macMsg->Buffer[bufItr++] << 16 ); + macMsg->MIC |= ( ( uint32_t ) macMsg->Buffer[bufItr++] << 24 ); + + return LORAMAC_PARSER_SUCCESS; +} + +LoRaMacParserStatus_t LoRaMacParserData( LoRaMacMessageData_t* macMsg ) +{ + if( ( macMsg == 0 ) || ( macMsg->Buffer == 0 ) ) + { + return LORAMAC_PARSER_ERROR_NPE; + } + + uint16_t bufItr = 0; + + macMsg->MHDR.Value = macMsg->Buffer[bufItr++]; + + macMsg->FHDR.DevAddr = macMsg->Buffer[bufItr++]; + macMsg->FHDR.DevAddr |= ( ( uint32_t ) macMsg->Buffer[bufItr++] << 8 ); + macMsg->FHDR.DevAddr |= ( ( uint32_t ) macMsg->Buffer[bufItr++] << 16 ); + macMsg->FHDR.DevAddr |= ( ( uint32_t ) macMsg->Buffer[bufItr++] << 24 ); + + macMsg->FHDR.FCtrl.Value = macMsg->Buffer[bufItr++]; + + macMsg->FHDR.FCnt = macMsg->Buffer[bufItr++]; + macMsg->FHDR.FCnt |= macMsg->Buffer[bufItr++] << 8; + + if( macMsg->FHDR.FCtrl.Bits.FOptsLen <= 15 ) + { + memcpy1( macMsg->FHDR.FOpts, &macMsg->Buffer[bufItr], macMsg->FHDR.FCtrl.Bits.FOptsLen ); + bufItr = bufItr + macMsg->FHDR.FCtrl.Bits.FOptsLen; + } + else + { + return LORAMAC_PARSER_FAIL; + } + + // Initialize anyway with zero. + macMsg->FPort = 0; + macMsg->FRMPayloadSize = 0; + + if( ( macMsg->BufSize - bufItr - LORAMAC_MIC_FIELD_SIZE ) > 0 ) + { + macMsg->FPort = macMsg->Buffer[bufItr++]; + + macMsg->FRMPayloadSize = ( macMsg->BufSize - bufItr - LORAMAC_MIC_FIELD_SIZE ); + memcpy1( macMsg->FRMPayload, &macMsg->Buffer[bufItr], macMsg->FRMPayloadSize ); + bufItr = bufItr + macMsg->FRMPayloadSize; + } + + macMsg->MIC = ( uint32_t ) macMsg->Buffer[( macMsg->BufSize - LORAMAC_MIC_FIELD_SIZE )]; + macMsg->MIC |= ( ( uint32_t ) macMsg->Buffer[( macMsg->BufSize - LORAMAC_MIC_FIELD_SIZE ) + 1] << 8 ); + macMsg->MIC |= ( ( uint32_t ) macMsg->Buffer[( macMsg->BufSize - LORAMAC_MIC_FIELD_SIZE ) + 2] << 16 ); + macMsg->MIC |= ( ( uint32_t ) macMsg->Buffer[( macMsg->BufSize - LORAMAC_MIC_FIELD_SIZE ) + 3] << 24 ); + + return LORAMAC_PARSER_SUCCESS; +} diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacParser.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacParser.h new file mode 100644 index 00000000..3446ae19 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacParser.h @@ -0,0 +1,86 @@ +/*! + * \file LoRaMacParser.h + * + * \brief LoRa MAC layer message parser functionality implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + * + * addtogroup LORAMAC + * \{ + * + */ +#ifndef __LORAMAC_PARSER_H__ +#define __LORAMAC_PARSER_H__ + +#include +#include "LoRaMacMessageTypes.h" + +/*! + * LoRaMac Parser Status + */ +typedef enum eLoRaMacParserStatus +{ + /*! + * No error occurred + */ + LORAMAC_PARSER_SUCCESS = 0, + /*! + * Failure during parsing occurred + */ + LORAMAC_PARSER_FAIL, + /*! + * Null pointer exception + */ + LORAMAC_PARSER_ERROR_NPE, + /*! + * Undefined Error occurred + */ + LORAMAC_PARSER_ERROR, +}LoRaMacParserStatus_t; + + +/*! + * Parse a serialized join-accept message and fills the structured object. + * + * \param[IN/OUT] macMsg - Join-accept message object + * \retval - Status of the operation + */ +LoRaMacParserStatus_t LoRaMacParserJoinAccept( LoRaMacMessageJoinAccept_t *macMsg ); + +/*! + * Parse a serialized data message and fills the structured object. + * + * \param[IN/OUT] macMsg - Data message object + * \retval - Status of the operation + */ +LoRaMacParserStatus_t LoRaMacParserData( LoRaMacMessageData_t *macMsg ); + +/*! \} addtogroup LORAMAC */ + +#endif // __LORAMAC_PARSER_H__ + diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacSerializer.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacSerializer.c new file mode 100644 index 00000000..261cfb2f --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacSerializer.c @@ -0,0 +1,183 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + ___ _____ _ ___ _ _____ ___ ___ ___ ___ +/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| +\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| +|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| +embedded.connectivity.solutions=============== + +Description: LoRa MAC layer message serializer functionality implementation + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ), + Daniel Jaeckle ( STACKFORCE ), Johannes Bruder ( STACKFORCE ) +*/ +#include "LoRaMacSerializer.h" +#include "utilities.h" + +LoRaMacSerializerStatus_t LoRaMacSerializerJoinRequest( LoRaMacMessageJoinRequest_t* macMsg ) +{ + if( ( macMsg == 0 ) || ( macMsg->Buffer == 0 ) ) + { + return LORAMAC_SERIALIZER_ERROR_NPE; + } + + uint16_t bufItr = 0; + + // Check macMsg->BufSize + if( macMsg->BufSize < LORAMAC_JOIN_REQ_MSG_SIZE ) + { + return LORAMAC_SERIALIZER_ERROR_BUF_SIZE; + } + + macMsg->Buffer[bufItr++] = macMsg->MHDR.Value; + + memcpyr( &macMsg->Buffer[bufItr], macMsg->JoinEUI, LORAMAC_JOIN_EUI_FIELD_SIZE ); + bufItr += LORAMAC_JOIN_EUI_FIELD_SIZE; + + memcpyr( &macMsg->Buffer[bufItr], macMsg->DevEUI, LORAMAC_DEV_EUI_FIELD_SIZE ); + bufItr += LORAMAC_DEV_EUI_FIELD_SIZE; + + macMsg->Buffer[bufItr++] = macMsg->DevNonce & 0xFF; + macMsg->Buffer[bufItr++] = ( macMsg->DevNonce >> 8 ) & 0xFF; + + macMsg->Buffer[bufItr++] = macMsg->MIC & 0xFF; + macMsg->Buffer[bufItr++] = ( macMsg->MIC >> 8 ) & 0xFF; + macMsg->Buffer[bufItr++] = ( macMsg->MIC >> 16 ) & 0xFF; + macMsg->Buffer[bufItr++] = ( macMsg->MIC >> 24 ) & 0xFF; + + macMsg->BufSize = bufItr; + + return LORAMAC_SERIALIZER_SUCCESS; +} + +LoRaMacSerializerStatus_t LoRaMacSerializerReJoinType1( LoRaMacMessageReJoinType1_t* macMsg ) +{ + if( ( macMsg == 0 ) || ( macMsg->Buffer == 0 ) ) + { + return LORAMAC_SERIALIZER_ERROR_NPE; + } + + uint16_t bufItr = 0; + + // Check macMsg->BufSize + if( macMsg->BufSize < LORAMAC_RE_JOIN_1_MSG_SIZE ) + { + return LORAMAC_SERIALIZER_ERROR_BUF_SIZE; + } + + macMsg->Buffer[bufItr++] = macMsg->MHDR.Value; + + macMsg->Buffer[bufItr++] = macMsg->ReJoinType; + + memcpyr( &macMsg->Buffer[bufItr], macMsg->JoinEUI, LORAMAC_JOIN_EUI_FIELD_SIZE ); + bufItr += LORAMAC_JOIN_EUI_FIELD_SIZE; + + memcpyr( &macMsg->Buffer[bufItr], macMsg->DevEUI, LORAMAC_DEV_EUI_FIELD_SIZE ); + bufItr += LORAMAC_DEV_EUI_FIELD_SIZE; + + macMsg->Buffer[bufItr++] = macMsg->RJcount1 & 0xFF; + macMsg->Buffer[bufItr++] = ( macMsg->RJcount1 >> 8 ) & 0xFF; + + return LORAMAC_SERIALIZER_SUCCESS; +} + +LoRaMacSerializerStatus_t LoRaMacSerializerReJoinType0or2( LoRaMacMessageReJoinType0or2_t* macMsg ) +{ + if( ( macMsg == 0 ) || ( macMsg->Buffer == 0 ) ) + { + return LORAMAC_SERIALIZER_ERROR_NPE; + } + + uint16_t bufItr = 0; + + // Check macMsg->BufSize + if( macMsg->BufSize < LORAMAC_RE_JOIN_0_2_MSG_SIZE ) + { + return LORAMAC_SERIALIZER_ERROR_BUF_SIZE; + } + + macMsg->Buffer[bufItr++] = macMsg->MHDR.Value; + + macMsg->Buffer[bufItr++] = macMsg->ReJoinType; + + memcpy1( &macMsg->Buffer[bufItr], macMsg->NetID, LORAMAC_NET_ID_FIELD_SIZE ); + bufItr += LORAMAC_NET_ID_FIELD_SIZE; + + memcpyr( &macMsg->Buffer[bufItr], macMsg->DevEUI, LORAMAC_DEV_EUI_FIELD_SIZE ); + bufItr += LORAMAC_DEV_EUI_FIELD_SIZE; + + macMsg->Buffer[bufItr++] = macMsg->RJcount0 & 0xFF; + macMsg->Buffer[bufItr++] = ( macMsg->RJcount0 >> 8 ) & 0xFF; + + return LORAMAC_SERIALIZER_SUCCESS; +} + +LoRaMacSerializerStatus_t LoRaMacSerializerData( LoRaMacMessageData_t* macMsg ) +{ + if( ( macMsg == 0 ) || ( macMsg->Buffer == 0 ) ) + { + return LORAMAC_SERIALIZER_ERROR_NPE; + } + + uint16_t bufItr = 0; + + // Check macMsg->BufSize + uint16_t computedBufSize = LORAMAC_MHDR_FIELD_SIZE + + LORAMAC_FHDR_DEV_ADD_FIELD_SIZE + + LORAMAC_FHDR_F_CTRL_FIELD_SIZE + + LORAMAC_FHDR_F_CNT_FIELD_SIZE; + + computedBufSize += macMsg->FHDR.FCtrl.Bits.FOptsLen; + + if( macMsg->FRMPayloadSize > 0 ) + { + computedBufSize += LORAMAC_F_PORT_FIELD_SIZE; + } + + computedBufSize += macMsg->FRMPayloadSize; + computedBufSize += LORAMAC_MIC_FIELD_SIZE; + + if( macMsg->BufSize < computedBufSize ) + { + return LORAMAC_SERIALIZER_ERROR_BUF_SIZE; + } + + macMsg->Buffer[bufItr++] = macMsg->MHDR.Value; + + macMsg->Buffer[bufItr++] = ( macMsg->FHDR.DevAddr ) & 0xFF; + macMsg->Buffer[bufItr++] = ( macMsg->FHDR.DevAddr >> 8 ) & 0xFF; + macMsg->Buffer[bufItr++] = ( macMsg->FHDR.DevAddr >> 16 ) & 0xFF; + macMsg->Buffer[bufItr++] = ( macMsg->FHDR.DevAddr >> 24 ) & 0xFF; + + macMsg->Buffer[bufItr++] = macMsg->FHDR.FCtrl.Value; + + macMsg->Buffer[bufItr++] = macMsg->FHDR.FCnt & 0xFF; + macMsg->Buffer[bufItr++] = ( macMsg->FHDR.FCnt >> 8 ) & 0xFF; + + memcpy1( &macMsg->Buffer[bufItr], macMsg->FHDR.FOpts, macMsg->FHDR.FCtrl.Bits.FOptsLen ); + bufItr = bufItr + macMsg->FHDR.FCtrl.Bits.FOptsLen; + + if( macMsg->FRMPayloadSize > 0 ) + { + macMsg->Buffer[bufItr++] = macMsg->FPort; + } + + memcpy1( &macMsg->Buffer[bufItr], macMsg->FRMPayload, macMsg->FRMPayloadSize ); + bufItr = bufItr + macMsg->FRMPayloadSize; + + macMsg->Buffer[bufItr++] = macMsg->MIC & 0xFF; + macMsg->Buffer[bufItr++] = ( macMsg->MIC >> 8 ) & 0xFF; + macMsg->Buffer[bufItr++] = ( macMsg->MIC >> 16 ) & 0xFF; + macMsg->Buffer[bufItr++] = ( macMsg->MIC >> 24 ) & 0xFF; + + macMsg->BufSize = bufItr; + + return LORAMAC_SERIALIZER_SUCCESS; +} diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacSerializer.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacSerializer.h new file mode 100644 index 00000000..0236abee --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacSerializer.h @@ -0,0 +1,101 @@ +/*! + * \file LoRaMacSerializer.h + * + * \brief LoRa MAC layer message serializer functionality implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + * + * addtogroup LORAMAC + * \{ + * + */ +#ifndef __LORAMAC_SERIALIZER_H__ +#define __LORAMAC_SERIALIZER_H__ +#include +#include "LoRaMacMessageTypes.h" + + +/*! + * LoRaMac Serializer Status + */ +typedef enum eLoRaMacSerializerStatus +{ + /*! + * No error occurred + */ + LORAMAC_SERIALIZER_SUCCESS = 0, + /*! + * Null pointer exception + */ + LORAMAC_SERIALIZER_ERROR_NPE, + /*! + * Incompatible buffer size + */ + LORAMAC_SERIALIZER_ERROR_BUF_SIZE, + /*! + * Undefined Error occurred + */ + LORAMAC_SERIALIZER_ERROR, +}LoRaMacSerializerStatus_t; + +/*! + * Creates serialized MAC message of structured object. + * + * \param[IN/OUT] macMsg - Join-request message object + * \retval - Status of the operation + */ +LoRaMacSerializerStatus_t LoRaMacSerializerJoinRequest( LoRaMacMessageJoinRequest_t* macMsg ); + +/*! + * Creates serialized MAC message of structured object. + * + * \param[IN/OUT] macMsg - Join-request message object + * \retval - Status of the operation + */ +LoRaMacSerializerStatus_t LoRaMacSerializerReJoinType1( LoRaMacMessageReJoinType1_t* macMsg ); + +/*! + * Creates serialized MAC message of structured object. + * + * \param[IN/OUT] macMsg - Join-request message object + * \retval - Status of the operation + */ +LoRaMacSerializerStatus_t LoRaMacSerializerReJoinType0or2( LoRaMacMessageReJoinType0or2_t* macMsg ); + +/*! + * Creates serialized MAC message of structured object. + * + * \param[IN/OUT] macMsg - Data message object + * \retval - Status of the operation + */ +LoRaMacSerializerStatus_t LoRaMacSerializerData( LoRaMacMessageData_t* macMsg ); + +/*! \} addtogroup LORAMAC */ + +#endif // __LORAMAC_SERIALIZER_H__ + diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacTest.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacTest.h new file mode 100644 index 00000000..0c348602 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacTest.h @@ -0,0 +1,51 @@ +/*! + * \file LoRaMacTest.h + * + * \brief LoRa MAC layer test function implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \defgroup LORAMACTEST LoRa MAC layer test function implementation + * This module specifies the API implementation of test function of the LoRaMAC layer. + * The functions in this file are only for testing purposes only. + * \{ + */ +#ifndef __LORAMACTEST_H__ +#define __LORAMACTEST_H__ + +/*! + * \brief Enabled or disables the duty cycle + * + * \details This is a test function. It shall be used for testing purposes only. + * Changing this attribute may lead to a non-conformance LoRaMac operation. + * + * \param [IN] enable - Enabled or disables the duty cycle + */ +void LoRaMacTestSetDutyCycleOn( bool enable ); + +/*! \} defgroup LORAMACTEST */ + +#endif // __LORAMACTEST_H__ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacTypes.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacTypes.h new file mode 100644 index 00000000..291b5cd0 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/LoRaMacTypes.h @@ -0,0 +1,655 @@ +/*! + * \file LoRaMacTypes.h + * + * \brief LoRa MAC layer internal types definition. Please do not include in application sources. + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + * + * addtogroup LORAMAC + * \{ + * + */ +#ifndef __LORAMAC_TYPES_H__ +#define __LORAMAC_TYPES_H__ + +#include +#include +#include "timer.h" + +/*! + * Start value for unicast keys enumeration + */ +#define LORAMAC_CRYPTO_UNICAST_KEYS 0 + +/*! + * Start value for multicast keys enumeration + */ +#define LORAMAC_CRYPTO_MULTICAST_KEYS 127 + +typedef union Version_u +{ + struct Version_s + { + uint8_t Rfu; + uint8_t Revision; + uint8_t Minor; + uint8_t Major; + }Fields; + uint32_t Value; +}Version_t; + +/*! + * LoRaWAN devices classes definition + * + * LoRaWAN Specification V1.0.2, chapter 2.1 + */ +typedef enum DeviceClass_e +{ + /*! + * LoRaWAN device class A + * + * LoRaWAN Specification V1.0.2, chapter 3 + */ + CLASS_A = 0x00, + /*! + * LoRaWAN device class B + * + * LoRaWAN Specification V1.0.2, chapter 8 + */ + CLASS_B = 0x01, + /*! + * LoRaWAN device class C + * + * LoRaWAN Specification V1.0.2, chapter 17 + */ + CLASS_C = 0x02, +}DeviceClass_t; + +/*! + * LoRaWAN Frame type enumeration to differ between the possible data up/down frame configurations. + * + * Note: The naming is implementation specific since there is no definition + * in the LoRaWAN specification included. + */ +typedef enum eFType +{ + /*! + * Frame type A + * + * FOptsLen > 0, Fopt present, FPort > 0, FRMPayload present + */ + FRAME_TYPE_A, + /*! + * Frame type B + * + * FOptsLen > 0, Fopt present, FPort not present, FRMPayload not present + */ + FRAME_TYPE_B, + /*! + * Frame type C + * + * FOptsLen = 0, Fopt not present, FPort = 0 , FRMPayload containing MAC commands + */ + FRAME_TYPE_C, + /*! + * Frame type D + * + * FOptsLen = 0, Fopt not present, FPort > 0 , FRMPayload present + */ + FRAME_TYPE_D, +}FType_t; + +/*! + * LoRaWAN Frame counter identifier. + */ +typedef enum eFCntIdentifier +{ + /*! + * Uplink frame counter which is incremented with each uplink. + */ + FCNT_UP = 0, + /*! + * Network downlink frame counter which is incremented with each downlink on FPort 0 + * or when the FPort field is missing. + */ + N_FCNT_DOWN, + /*! + * Application downlink frame counter which is incremented with each downlink + * on a port different than 0. + */ + A_FCNT_DOWN, + /*! + * In case if the device is connected to a LoRaWAN 1.0 Server, + * this counter is used for every kind of downlink frame. + */ + FCNT_DOWN, + /*! + * Multicast downlink counter for index 0 + */ + MC_FCNT_DOWN_0, + /*! + * Multicast downlink counter for index 1 + */ + MC_FCNT_DOWN_1, + /*! + * Multicast downlink counter for index 2 + */ + MC_FCNT_DOWN_2, + /*! + * Multicast downlink counter for index 3 + */ + MC_FCNT_DOWN_3, +}FCntIdentifier_t; + +/*! + * LoRaMac Key identifier + */ +typedef enum eKeyIdentifier +{ + /*! + * Application root key + */ + APP_KEY = 0, + /*! + * Application root key + * Used to derive McRootKey for 1.0.x devices + */ + GEN_APP_KEY, + /*! + * Network root key + */ + NWK_KEY, + /*! + * Join session integrity key + */ + J_S_INT_KEY, + /*! + * Join session encryption key + */ + J_S_ENC_KEY, + /*! + * Forwarding Network session integrity key + */ + F_NWK_S_INT_KEY, + /*! + * Serving Network session integrity key + */ + S_NWK_S_INT_KEY, + /*! + * Network session encryption key + */ + NWK_S_ENC_KEY, + /*! + * Application session key + */ + APP_S_KEY, + /*! + * Multicast root key + */ + MC_ROOT_KEY, + /*! + * Multicast key encryption key + */ + MC_KE_KEY = LORAMAC_CRYPTO_MULTICAST_KEYS, + /*! + * Multicast root key index 0 + */ + MC_KEY_0, + /*! + * Multicast Application session key index 0 + */ + MC_APP_S_KEY_0, + /*! + * Multicast Network session key index 0 + */ + MC_NWK_S_KEY_0, + /*! + * Multicast root key index 1 + */ + MC_KEY_1, + /*! + * Multicast Application session key index 1 + */ + MC_APP_S_KEY_1, + /*! + * Multicast Network session key index 1 + */ + MC_NWK_S_KEY_1, + /*! + * Multicast root key index 2 + */ + MC_KEY_2, + /*! + * Multicast Application session key index 2 + */ + MC_APP_S_KEY_2, + /*! + * Multicast Network session key index 2 + */ + MC_NWK_S_KEY_2, + /*! + * Multicast root key index 3 + */ + MC_KEY_3, + /*! + * Multicast Application session key index 3 + */ + MC_APP_S_KEY_3, + /*! + * Multicast Network session key index 3 + */ + MC_NWK_S_KEY_3, + /*! + * Zero key for slot randomization in class B + */ + SLOT_RAND_ZERO_KEY, + /*! + * No Key + */ + NO_KEY, +}KeyIdentifier_t; + +/*! + * LoRaMac Crypto address identifier + */ +typedef enum eAddressIdentifier +{ + /*! + * Multicast Address for index 0 + */ + MULTICAST_0_ADDR = 0, + /*! + * Multicast Address for index 1 + */ + MULTICAST_1_ADDR = 1, + /*! + * Multicast Address for index 2 + */ + MULTICAST_2_ADDR = 2, + /*! + * Multicast Address for index 3 + */ + MULTICAST_3_ADDR = 3, + /*! + * Unicast End-device address + */ + UNICAST_DEV_ADDR = 4, +}AddressIdentifier_t; + +/* + * Multicast Rx window parameters + */ +typedef union uMcRxParams +{ + struct + { + /*! + * Reception frequency of the ping slot windows + */ + uint32_t Frequency; + /*! + * Datarate of the ping slot + */ + int8_t Datarate; + /*! + * This parameter is necessary for class B operation. It defines the + * periodicity of the multicast downlink slots + */ + uint16_t Periodicity; + }ClassB; + struct + { + /*! + * Reception frequency of the ping slot windows + */ + uint32_t Frequency; + /*! + * Datarate of the ping slot + */ + int8_t Datarate; + }ClassC; +}McRxParams_t; + +/*! + * Multicast channel + */ +typedef struct sMcChannelParams +{ + /*! + * Multicats channel LoRaWAN class B or C + */ + DeviceClass_t Class; + /*! + * True if the entry is active + */ + bool IsEnabled; + /* + * Address identifier + */ + AddressIdentifier_t GroupID; + /*! + * Address + */ + uint32_t Address; + /*! + * Encrypted multicast key + */ + uint8_t *McKeyE; + /*! + * Minimum multicast frame counter value + */ + uint32_t FCountMin; + /*! + * Maximum multicast frame counter value + */ + uint32_t FCountMax; + /*! + * Multicast reception parameters + */ + McRxParams_t RxParams; +}McChannelParams_t; + +/*! + * Multicast context + */ +typedef struct sMulticastCtx +{ + /*! + * Multicast channel parameters + */ + McChannelParams_t ChannelParams; + /*! + * Downlink counter + */ + uint32_t* DownLinkCounter; + /* + * Following parameters are only used for ClassB multicast channels + */ + /*! + * Number of multicast slots. The variable can be + * calculated as follows: + * PingNb = 128 / ( 1 << periodicity ), where + * 0 <= periodicity <= 7 + */ + uint8_t PingNb; + /*! + * Period of the multicast slots. The variable can be + * calculated as follows: + * PingPeriod = 4096 / PingNb + */ + uint16_t PingPeriod; + /*! + * Ping offset of the multicast channel for Class B + */ + uint16_t PingOffset; +}MulticastCtx_t; + +/*! + * LoRaMac join-request / rejoin type identifier + */ +typedef enum eJoinReqIdentifier +{ + /*! + * Rejoin type 0 + */ + REJOIN_REQ_0 = 0x00, + /*! + * Rejoin type 1 + */ + REJOIN_REQ_1 = 0x01, + /*! + * Rejoin type 2 + */ + REJOIN_REQ_2 = 0x02, + /*! + * Join-request + */ + JOIN_REQ = 0xFF, +}JoinReqIdentifier_t; + +/*! + * LoRaMAC mote MAC commands + * + * LoRaWAN Specification V1.1.0, chapter 5, table 4 + */ +typedef enum eLoRaMacMoteCmd +{ + /*! + * LinkCheckReq + */ + MOTE_MAC_LINK_CHECK_REQ = 0x02, + /*! + * LinkADRAns + */ + MOTE_MAC_LINK_ADR_ANS = 0x03, + /*! + * DutyCycleAns + */ + MOTE_MAC_DUTY_CYCLE_ANS = 0x04, + /*! + * RXParamSetupAns + */ + MOTE_MAC_RX_PARAM_SETUP_ANS = 0x05, + /*! + * DevStatusAns + */ + MOTE_MAC_DEV_STATUS_ANS = 0x06, + /*! + * NewChannelAns + */ + MOTE_MAC_NEW_CHANNEL_ANS = 0x07, + /*! + * RXTimingSetupAns + */ + MOTE_MAC_RX_TIMING_SETUP_ANS = 0x08, + /*! + * TXParamSetupAns + */ + MOTE_MAC_TX_PARAM_SETUP_ANS = 0x09, + /*! + * DlChannelAns + */ + MOTE_MAC_DL_CHANNEL_ANS = 0x0A, + /*! + * DeviceTimeReq + */ + MOTE_MAC_DEVICE_TIME_REQ = 0x0D, + /*! + * PingSlotInfoReq + */ + MOTE_MAC_PING_SLOT_INFO_REQ = 0x10, + /*! + * PingSlotFreqAns + */ + MOTE_MAC_PING_SLOT_FREQ_ANS = 0x11, + /*! + * BeaconTimingReq + */ + MOTE_MAC_BEACON_TIMING_REQ = 0x12, + /*! + * BeaconFreqAns + */ + MOTE_MAC_BEACON_FREQ_ANS = 0x13, +}LoRaMacMoteCmd_t; + +/*! + * LoRaMAC server MAC commands + * + * LoRaWAN Specification V1.1.0 chapter 5, table 4 + */ +typedef enum eLoRaMacSrvCmd +{ + /*! + * ResetInd + */ + SRV_MAC_RESET_CONF = 0x01, + /*! + * LinkCheckAns + */ + SRV_MAC_LINK_CHECK_ANS = 0x02, + /*! + * LinkADRReq + */ + SRV_MAC_LINK_ADR_REQ = 0x03, + /*! + * DutyCycleReq + */ + SRV_MAC_DUTY_CYCLE_REQ = 0x04, + /*! + * RXParamSetupReq + */ + SRV_MAC_RX_PARAM_SETUP_REQ = 0x05, + /*! + * DevStatusReq + */ + SRV_MAC_DEV_STATUS_REQ = 0x06, + /*! + * NewChannelReq + */ + SRV_MAC_NEW_CHANNEL_REQ = 0x07, + /*! + * RXTimingSetupReq + */ + SRV_MAC_RX_TIMING_SETUP_REQ = 0x08, + /*! + * NewChannelReq + */ + SRV_MAC_TX_PARAM_SETUP_REQ = 0x09, + /*! + * DlChannelReq + */ + SRV_MAC_DL_CHANNEL_REQ = 0x0A, + /*! + * DeviceTimeAns + */ + SRV_MAC_DEVICE_TIME_ANS = 0x0D, + /*! + * PingSlotInfoAns + */ + SRV_MAC_PING_SLOT_INFO_ANS = 0x10, + /*! + * PingSlotChannelReq + */ + SRV_MAC_PING_SLOT_CHANNEL_REQ = 0x11, + /*! + * BeaconTimingAns + */ + SRV_MAC_BEACON_TIMING_ANS = 0x12, + /*! + * BeaconFreqReq + */ + SRV_MAC_BEACON_FREQ_REQ = 0x13, +}LoRaMacSrvCmd_t; + +/*! + * LoRaMAC band parameters definition + */ +typedef struct sBand +{ + /*! + * Duty cycle + */ + uint16_t DCycle; + /*! + * Maximum Tx power + */ + int8_t TxMaxPower; + /*! + * Time stamp of the last JoinReq Tx frame. + */ + TimerTime_t LastJoinTxDoneTime; + /*! + * Time stamp of the last Tx frame + */ + TimerTime_t LastTxDoneTime; + /*! + * Holds the time where the device is off + */ + TimerTime_t TimeOff; +}Band_t; + +/*! + * LoRaMAC frame types + * + * LoRaWAN Specification V1.0.2, chapter 4.2.1, table 1 + */ +typedef enum eLoRaMacFrameType +{ + /*! + * LoRaMAC join request frame + */ + FRAME_TYPE_JOIN_REQ = 0x00, + /*! + * LoRaMAC join accept frame + */ + FRAME_TYPE_JOIN_ACCEPT = 0x01, + /*! + * LoRaMAC unconfirmed up-link frame + */ + FRAME_TYPE_DATA_UNCONFIRMED_UP = 0x02, + /*! + * LoRaMAC unconfirmed down-link frame + */ + FRAME_TYPE_DATA_UNCONFIRMED_DOWN = 0x03, + /*! + * LoRaMAC confirmed up-link frame + */ + FRAME_TYPE_DATA_CONFIRMED_UP = 0x04, + /*! + * LoRaMAC confirmed down-link frame + */ + FRAME_TYPE_DATA_CONFIRMED_DOWN = 0x05, + /*! + * LoRaMAC proprietary frame + */ + FRAME_TYPE_PROPRIETARY = 0x07, +}LoRaMacFrameType_t; + +/*! + * LoRaMAC Battery level indicator + */ +typedef enum eLoRaMacBatteryLevel +{ + /*! + * External power source + */ + BAT_LEVEL_EXT_SRC = 0x00, + /*! + * Battery level empty + */ + BAT_LEVEL_EMPTY = 0x01, + /*! + * Battery level full + */ + BAT_LEVEL_FULL = 0xFE, + /*! + * Battery level - no measurement available + */ + BAT_LEVEL_NO_MEASURE = 0xFF, +}LoRaMacBatteryLevel_t; + +#endif // __LORAMAC_TYPES_H__ + diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/lora_mac_version.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/lora_mac_version.h new file mode 100644 index 00000000..2c67edb1 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/lora_mac_version.h @@ -0,0 +1,56 @@ +/** + ****************************************************************************** + * @file lora_mac_version.h + * @author MCD Application Team + * @brief Identifies the version of loramac on github + ****************************************************************************** + * @attention + * + *

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

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ + +#ifndef __LORA_MAC_VERSION_H__ +#define __LORA_MAC_VERSION_H__ + +#ifdef __cplusplus + extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ + +/*the 4 MSBs define the version based on Github version */ +/*https://github.com/Lora-net/LoRaMac-node/wiki/LoRaMAC-node-Wiki*/ +/* version 4.4.2-rc.5 from develop branch */ +/*#define LORA_MAC_VERSION (uint32_t) 0x44250000*/ +#define __LORA_MAC_VERSION_MAIN (0x04U) /*!< [31:24] main version */ +#define __LORA_MAC_VERSION_SUB1 (0x04U) /*!< [23:16] sub1 version */ +#define __LORA_MAC_VERSION_SUB2 (0x02U) /*!< [15:8] sub2 version */ +#define __LORA_MAC_VERSION_RC (0x00U) /*!< [7:0] release candidate */ +#define __LORA_MAC_VERSION ((__LORA_MAC_VERSION_MAIN <<24)\ + |(__LORA_MAC_VERSION_SUB1 << 16)\ + |(__LORA_MAC_VERSION_SUB2 << 8 )\ + |(__LORA_MAC_VERSION_RC)) + +/* Exported types ------------------------------------------------------------*/ +/* External variables --------------------------------------------------------*/ +/* Exported macros -----------------------------------------------------------*/ +/* Exported functions ------------------------------------------------------- */ + +#ifdef __cplusplus +} +#endif + +#endif /*__LORA_MAC_VERSION_H__*/ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/Region.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/Region.c new file mode 100644 index 00000000..c83f0088 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/Region.c @@ -0,0 +1,1155 @@ +/*! + * \file Region.c + * + * \brief Region implementation. + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + */ +#include "LoRaMac.h" + +// Setup regions +#ifdef REGION_AS923 +#include "RegionAS923.h" +#define AS923_CASE case LORAMAC_REGION_AS923: +#define AS923_IS_ACTIVE( ) AS923_CASE { return true; } +#define AS923_GET_PHY_PARAM( ) AS923_CASE { return RegionAS923GetPhyParam( getPhy ); } +#define AS923_SET_BAND_TX_DONE( ) AS923_CASE { RegionAS923SetBandTxDone( txDone ); break; } +#define AS923_INIT_DEFAULTS( ) AS923_CASE { RegionAS923InitDefaults( params ); break; } +#define AS923_GET_NVM_CTX( ) AS923_CASE { return RegionAS923GetNvmCtx( params ); } +#define AS923_VERIFY( ) AS923_CASE { return RegionAS923Verify( verify, phyAttribute ); } +#define AS923_APPLY_CF_LIST( ) AS923_CASE { RegionAS923ApplyCFList( applyCFList ); break; } +#define AS923_CHAN_MASK_SET( ) AS923_CASE { return RegionAS923ChanMaskSet( chanMaskSet ); } +#define AS923_COMPUTE_RX_WINDOW_PARAMETERS( ) AS923_CASE { RegionAS923ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; } +#define AS923_RX_CONFIG( ) AS923_CASE { return RegionAS923RxConfig( rxConfig, datarate ); } +#define AS923_TX_CONFIG( ) AS923_CASE { return RegionAS923TxConfig( txConfig, txPower, txTimeOnAir ); } +#define AS923_LINK_ADR_REQ( ) AS923_CASE { return RegionAS923LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); } +#define AS923_RX_PARAM_SETUP_REQ( ) AS923_CASE { return RegionAS923RxParamSetupReq( rxParamSetupReq ); } +#define AS923_NEW_CHANNEL_REQ( ) AS923_CASE { return RegionAS923NewChannelReq( newChannelReq ); } +#define AS923_TX_PARAM_SETUP_REQ( ) AS923_CASE { return RegionAS923TxParamSetupReq( txParamSetupReq ); } +#define AS923_DL_CHANNEL_REQ( ) AS923_CASE { return RegionAS923DlChannelReq( dlChannelReq ); } +#define AS923_ALTERNATE_DR( ) AS923_CASE { return RegionAS923AlternateDr( currentDr, type ); } +#define AS923_CALC_BACKOFF( ) AS923_CASE { RegionAS923CalcBackOff( calcBackOff ); break; } +#define AS923_NEXT_CHANNEL( ) AS923_CASE { return RegionAS923NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); } +#define AS923_CHANNEL_ADD( ) AS923_CASE { return RegionAS923ChannelAdd( channelAdd ); } +#define AS923_CHANNEL_REMOVE( ) AS923_CASE { return RegionAS923ChannelsRemove( channelRemove ); } +#define AS923_SET_CONTINUOUS_WAVE( ) AS923_CASE { RegionAS923SetContinuousWave( continuousWave ); break; } +#define AS923_APPLY_DR_OFFSET( ) AS923_CASE { return RegionAS923ApplyDrOffset( downlinkDwellTime, dr, drOffset ); } +#define AS923_RX_BEACON_SETUP( ) AS923_CASE { RegionAS923RxBeaconSetup( rxBeaconSetup, outDr ); break; } +#else +#define AS923_IS_ACTIVE( ) +#define AS923_GET_PHY_PARAM( ) +#define AS923_SET_BAND_TX_DONE( ) +#define AS923_INIT_DEFAULTS( ) +#define AS923_GET_NVM_CTX( ) +#define AS923_VERIFY( ) +#define AS923_APPLY_CF_LIST( ) +#define AS923_CHAN_MASK_SET( ) +#define AS923_COMPUTE_RX_WINDOW_PARAMETERS( ) +#define AS923_RX_CONFIG( ) +#define AS923_TX_CONFIG( ) +#define AS923_LINK_ADR_REQ( ) +#define AS923_RX_PARAM_SETUP_REQ( ) +#define AS923_NEW_CHANNEL_REQ( ) +#define AS923_TX_PARAM_SETUP_REQ( ) +#define AS923_DL_CHANNEL_REQ( ) +#define AS923_ALTERNATE_DR( ) +#define AS923_CALC_BACKOFF( ) +#define AS923_NEXT_CHANNEL( ) +#define AS923_CHANNEL_ADD( ) +#define AS923_CHANNEL_REMOVE( ) +#define AS923_SET_CONTINUOUS_WAVE( ) +#define AS923_APPLY_DR_OFFSET( ) +#define AS923_RX_BEACON_SETUP( ) +#endif + +#ifdef REGION_AU915 +#include "RegionAU915.h" +#define AU915_CASE case LORAMAC_REGION_AU915: +#define AU915_IS_ACTIVE( ) AU915_CASE { return true; } +#define AU915_GET_PHY_PARAM( ) AU915_CASE { return RegionAU915GetPhyParam( getPhy ); } +#define AU915_SET_BAND_TX_DONE( ) AU915_CASE { RegionAU915SetBandTxDone( txDone ); break; } +#define AU915_INIT_DEFAULTS( ) AU915_CASE { RegionAU915InitDefaults( params ); break; } +#define AU915_GET_NVM_CTX( ) AU915_CASE { return RegionAU915GetNvmCtx( params ); } +#define AU915_VERIFY( ) AU915_CASE { return RegionAU915Verify( verify, phyAttribute ); } +#define AU915_APPLY_CF_LIST( ) AU915_CASE { RegionAU915ApplyCFList( applyCFList ); break; } +#define AU915_CHAN_MASK_SET( ) AU915_CASE { return RegionAU915ChanMaskSet( chanMaskSet ); } +#define AU915_COMPUTE_RX_WINDOW_PARAMETERS( ) AU915_CASE { RegionAU915ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; } +#define AU915_RX_CONFIG( ) AU915_CASE { return RegionAU915RxConfig( rxConfig, datarate ); } +#define AU915_TX_CONFIG( ) AU915_CASE { return RegionAU915TxConfig( txConfig, txPower, txTimeOnAir ); } +#define AU915_LINK_ADR_REQ( ) AU915_CASE { return RegionAU915LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); } +#define AU915_RX_PARAM_SETUP_REQ( ) AU915_CASE { return RegionAU915RxParamSetupReq( rxParamSetupReq ); } +#define AU915_NEW_CHANNEL_REQ( ) AU915_CASE { return RegionAU915NewChannelReq( newChannelReq ); } +#define AU915_TX_PARAM_SETUP_REQ( ) AU915_CASE { return RegionAU915TxParamSetupReq( txParamSetupReq ); } +#define AU915_DL_CHANNEL_REQ( ) AU915_CASE { return RegionAU915DlChannelReq( dlChannelReq ); } +#define AU915_ALTERNATE_DR( ) AU915_CASE { return RegionAU915AlternateDr( currentDr, type ); } +#define AU915_CALC_BACKOFF( ) AU915_CASE { RegionAU915CalcBackOff( calcBackOff ); break; } +#define AU915_NEXT_CHANNEL( ) AU915_CASE { return RegionAU915NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); } +#define AU915_CHANNEL_ADD( ) AU915_CASE { return RegionAU915ChannelAdd( channelAdd ); } +#define AU915_CHANNEL_REMOVE( ) AU915_CASE { return RegionAU915ChannelsRemove( channelRemove ); } +#define AU915_SET_CONTINUOUS_WAVE( ) AU915_CASE { RegionAU915SetContinuousWave( continuousWave ); break; } +#define AU915_APPLY_DR_OFFSET( ) AU915_CASE { return RegionAU915ApplyDrOffset( downlinkDwellTime, dr, drOffset ); } +#define AU915_RX_BEACON_SETUP( ) AU915_CASE { RegionAU915RxBeaconSetup( rxBeaconSetup, outDr ); break; } +#else +#define AU915_IS_ACTIVE( ) +#define AU915_GET_PHY_PARAM( ) +#define AU915_SET_BAND_TX_DONE( ) +#define AU915_INIT_DEFAULTS( ) +#define AU915_GET_NVM_CTX( ) +#define AU915_VERIFY( ) +#define AU915_APPLY_CF_LIST( ) +#define AU915_CHAN_MASK_SET( ) +#define AU915_COMPUTE_RX_WINDOW_PARAMETERS( ) +#define AU915_RX_CONFIG( ) +#define AU915_TX_CONFIG( ) +#define AU915_LINK_ADR_REQ( ) +#define AU915_RX_PARAM_SETUP_REQ( ) +#define AU915_NEW_CHANNEL_REQ( ) +#define AU915_TX_PARAM_SETUP_REQ( ) +#define AU915_DL_CHANNEL_REQ( ) +#define AU915_ALTERNATE_DR( ) +#define AU915_CALC_BACKOFF( ) +#define AU915_NEXT_CHANNEL( ) +#define AU915_CHANNEL_ADD( ) +#define AU915_CHANNEL_REMOVE( ) +#define AU915_SET_CONTINUOUS_WAVE( ) +#define AU915_APPLY_DR_OFFSET( ) +#define AU915_RX_BEACON_SETUP( ) +#endif + +#ifdef REGION_CN470 +#include "RegionCN470.h" +#define CN470_CASE case LORAMAC_REGION_CN470: +#define CN470_IS_ACTIVE( ) CN470_CASE { return true; } +#define CN470_GET_PHY_PARAM( ) CN470_CASE { return RegionCN470GetPhyParam( getPhy ); } +#define CN470_SET_BAND_TX_DONE( ) CN470_CASE { RegionCN470SetBandTxDone( txDone ); break; } +#define CN470_INIT_DEFAULTS( ) CN470_CASE { RegionCN470InitDefaults( params ); break; } +#define CN470_GET_NVM_CTX( ) CN470_CASE { return RegionCN470GetNvmCtx( params ); } +#define CN470_VERIFY( ) CN470_CASE { return RegionCN470Verify( verify, phyAttribute ); } +#define CN470_APPLY_CF_LIST( ) CN470_CASE { RegionCN470ApplyCFList( applyCFList ); break; } +#define CN470_CHAN_MASK_SET( ) CN470_CASE { return RegionCN470ChanMaskSet( chanMaskSet ); } +#define CN470_COMPUTE_RX_WINDOW_PARAMETERS( ) CN470_CASE { RegionCN470ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; } +#define CN470_RX_CONFIG( ) CN470_CASE { return RegionCN470RxConfig( rxConfig, datarate ); } +#define CN470_TX_CONFIG( ) CN470_CASE { return RegionCN470TxConfig( txConfig, txPower, txTimeOnAir ); } +#define CN470_LINK_ADR_REQ( ) CN470_CASE { return RegionCN470LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); } +#define CN470_RX_PARAM_SETUP_REQ( ) CN470_CASE { return RegionCN470RxParamSetupReq( rxParamSetupReq ); } +#define CN470_NEW_CHANNEL_REQ( ) CN470_CASE { return RegionCN470NewChannelReq( newChannelReq ); } +#define CN470_TX_PARAM_SETUP_REQ( ) CN470_CASE { return RegionCN470TxParamSetupReq( txParamSetupReq ); } +#define CN470_DL_CHANNEL_REQ( ) CN470_CASE { return RegionCN470DlChannelReq( dlChannelReq ); } +#define CN470_ALTERNATE_DR( ) CN470_CASE { return RegionCN470AlternateDr( currentDr, type ); } +#define CN470_CALC_BACKOFF( ) CN470_CASE { RegionCN470CalcBackOff( calcBackOff ); break; } +#define CN470_NEXT_CHANNEL( ) CN470_CASE { return RegionCN470NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); } +#define CN470_CHANNEL_ADD( ) CN470_CASE { return RegionCN470ChannelAdd( channelAdd ); } +#define CN470_CHANNEL_REMOVE( ) CN470_CASE { return RegionCN470ChannelsRemove( channelRemove ); } +#define CN470_SET_CONTINUOUS_WAVE( ) CN470_CASE { RegionCN470SetContinuousWave( continuousWave ); break; } +#define CN470_APPLY_DR_OFFSET( ) CN470_CASE { return RegionCN470ApplyDrOffset( downlinkDwellTime, dr, drOffset ); } +#define CN470_RX_BEACON_SETUP( ) CN470_CASE { RegionCN470RxBeaconSetup( rxBeaconSetup, outDr ); break; } +#else +#define CN470_IS_ACTIVE( ) +#define CN470_GET_PHY_PARAM( ) +#define CN470_SET_BAND_TX_DONE( ) +#define CN470_INIT_DEFAULTS( ) +#define CN470_GET_NVM_CTX( ) +#define CN470_VERIFY( ) +#define CN470_APPLY_CF_LIST( ) +#define CN470_CHAN_MASK_SET( ) +#define CN470_COMPUTE_RX_WINDOW_PARAMETERS( ) +#define CN470_RX_CONFIG( ) +#define CN470_TX_CONFIG( ) +#define CN470_LINK_ADR_REQ( ) +#define CN470_RX_PARAM_SETUP_REQ( ) +#define CN470_NEW_CHANNEL_REQ( ) +#define CN470_TX_PARAM_SETUP_REQ( ) +#define CN470_DL_CHANNEL_REQ( ) +#define CN470_ALTERNATE_DR( ) +#define CN470_CALC_BACKOFF( ) +#define CN470_NEXT_CHANNEL( ) +#define CN470_CHANNEL_ADD( ) +#define CN470_CHANNEL_REMOVE( ) +#define CN470_SET_CONTINUOUS_WAVE( ) +#define CN470_APPLY_DR_OFFSET( ) +#define CN470_RX_BEACON_SETUP( ) +#endif + +#ifdef REGION_CN470ALI +#include "RegionCN470ALI.h" +#define CN470ALI_CASE case LORAMAC_REGION_CN470ALI: +#define CN470ALI_IS_ACTIVE( ) CN470ALI_CASE { return true; } +#define CN470ALI_GET_PHY_PARAM( ) CN470ALI_CASE { return RegionCN470ALIGetPhyParam( getPhy ); } +#define CN470ALI_SET_BAND_TX_DONE( ) CN470ALI_CASE { RegionCN470ALISetBandTxDone( txDone ); break; } +#define CN470ALI_INIT_DEFAULTS( ) CN470ALI_CASE { RegionCN470ALIInitDefaults( params ); break; } +#define CN470ALI_GET_NVM_CTX( ) CN470ALI_CASE { return RegionCN470ALIGetNvmCtx( params ); } +#define CN470ALI_VERIFY( ) CN470ALI_CASE { return RegionCN470ALIVerify( verify, phyAttribute ); } +#define CN470ALI_APPLY_CF_LIST( ) CN470ALI_CASE { RegionCN470ALIApplyCFList( applyCFList ); break; } +#define CN470ALI_CHAN_MASK_SET( ) CN470ALI_CASE { return RegionCN470ALIChanMaskSet( chanMaskSet ); } +#define CN470ALI_COMPUTE_RX_WINDOW_PARAMETERS( ) CN470ALI_CASE { RegionCN470ALIComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; } +#define CN470ALI_RX_CONFIG( ) CN470ALI_CASE { return RegionCN470ALIRxConfig( rxConfig, datarate ); } +#define CN470ALI_TX_CONFIG( ) CN470ALI_CASE { return RegionCN470ALITxConfig( txConfig, txPower, txTimeOnAir ); } +#define CN470ALI_LINK_ADR_REQ( ) CN470ALI_CASE { return RegionCN470ALILinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); } +#define CN470ALI_RX_PARAM_SETUP_REQ( ) CN470ALI_CASE { return RegionCN470ALIRxParamSetupReq( rxParamSetupReq ); } +#define CN470ALI_NEW_CHANNEL_REQ( ) CN470ALI_CASE { return RegionCN470ALINewChannelReq( newChannelReq ); } +#define CN470ALI_TX_PARAM_SETUP_REQ( ) CN470ALI_CASE { return RegionCN470ALITxParamSetupReq( txParamSetupReq ); } +#define CN470ALI_DL_CHANNEL_REQ( ) CN470ALI_CASE { return RegionCN470ALIDlChannelReq( dlChannelReq ); } +#define CN470ALI_ALTERNATE_DR( ) CN470ALI_CASE { return RegionCN470ALIAlternateDr( currentDr, type ); } +#define CN470ALI_CALC_BACKOFF( ) CN470ALI_CASE { RegionCN470ALICalcBackOff( calcBackOff ); break; } +#define CN470ALI_NEXT_CHANNEL( ) CN470ALI_CASE { return RegionCN470ALINextChannel( nextChanParams, channel, time, aggregatedTimeOff ); } +#define CN470ALI_CHANNEL_ADD( ) CN470ALI_CASE { return RegionCN470ALIChannelAdd( channelAdd ); } +#define CN470ALI_CHANNEL_REMOVE( ) CN470ALI_CASE { return RegionCN470ALIChannelsRemove( channelRemove ); } +#define CN470ALI_SET_CONTINUOUS_WAVE( ) CN470ALI_CASE { RegionCN470ALISetContinuousWave( continuousWave ); break; } +#define CN470ALI_APPLY_DR_OFFSET( ) CN470ALI_CASE { return RegionCN470ALIApplyDrOffset( downlinkDwellTime, dr, drOffset ); } +#define CN470ALI_RX_BEACON_SETUP( ) CN470ALI_CASE { RegionCN470ALIRxBeaconSetup( rxBeaconSetup, outDr ); break; } +#else +#define CN470ALI_IS_ACTIVE( ) +#define CN470ALI_GET_PHY_PARAM( ) +#define CN470ALI_SET_BAND_TX_DONE( ) +#define CN470ALI_INIT_DEFAULTS( ) +#define CN470ALI_GET_NVM_CTX( ) +#define CN470ALI_VERIFY( ) +#define CN470ALI_APPLY_CF_LIST( ) +#define CN470ALI_CHAN_MASK_SET( ) +#define CN470ALI_COMPUTE_RX_WINDOW_PARAMETERS( ) +#define CN470ALI_RX_CONFIG( ) +#define CN470ALI_TX_CONFIG( ) +#define CN470ALI_LINK_ADR_REQ( ) +#define CN470ALI_RX_PARAM_SETUP_REQ( ) +#define CN470ALI_NEW_CHANNEL_REQ( ) +#define CN470ALI_TX_PARAM_SETUP_REQ( ) +#define CN470ALI_DL_CHANNEL_REQ( ) +#define CN470ALI_ALTERNATE_DR( ) +#define CN470ALI_CALC_BACKOFF( ) +#define CN470ALI_NEXT_CHANNEL( ) +#define CN470ALI_CHANNEL_ADD( ) +#define CN470ALI_CHANNEL_REMOVE( ) +#define CN470ALI_SET_CONTINUOUS_WAVE( ) +#define CN470ALI_APPLY_DR_OFFSET( ) +#define CN470ALI_RX_BEACON_SETUP( ) +#endif + +#ifdef REGION_CN779 +#include "RegionCN779.h" +#define CN779_CASE case LORAMAC_REGION_CN779: +#define CN779_IS_ACTIVE( ) CN779_CASE { return true; } +#define CN779_GET_PHY_PARAM( ) CN779_CASE { return RegionCN779GetPhyParam( getPhy ); } +#define CN779_SET_BAND_TX_DONE( ) CN779_CASE { RegionCN779SetBandTxDone( txDone ); break; } +#define CN779_INIT_DEFAULTS( ) CN779_CASE { RegionCN779InitDefaults( params ); break; } +#define CN779_GET_NVM_CTX( ) CN779_CASE { return RegionCN779GetNvmCtx( params ); } +#define CN779_VERIFY( ) CN779_CASE { return RegionCN779Verify( verify, phyAttribute ); } +#define CN779_APPLY_CF_LIST( ) CN779_CASE { RegionCN779ApplyCFList( applyCFList ); break; } +#define CN779_CHAN_MASK_SET( ) CN779_CASE { return RegionCN779ChanMaskSet( chanMaskSet ); } +#define CN779_COMPUTE_RX_WINDOW_PARAMETERS( ) CN779_CASE { RegionCN779ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; } +#define CN779_RX_CONFIG( ) CN779_CASE { return RegionCN779RxConfig( rxConfig, datarate ); } +#define CN779_TX_CONFIG( ) CN779_CASE { return RegionCN779TxConfig( txConfig, txPower, txTimeOnAir ); } +#define CN779_LINK_ADR_REQ( ) CN779_CASE { return RegionCN779LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); } +#define CN779_RX_PARAM_SETUP_REQ( ) CN779_CASE { return RegionCN779RxParamSetupReq( rxParamSetupReq ); } +#define CN779_NEW_CHANNEL_REQ( ) CN779_CASE { return RegionCN779NewChannelReq( newChannelReq ); } +#define CN779_TX_PARAM_SETUP_REQ( ) CN779_CASE { return RegionCN779TxParamSetupReq( txParamSetupReq ); } +#define CN779_DL_CHANNEL_REQ( ) CN779_CASE { return RegionCN779DlChannelReq( dlChannelReq ); } +#define CN779_ALTERNATE_DR( ) CN779_CASE { return RegionCN779AlternateDr( currentDr, type ); } +#define CN779_CALC_BACKOFF( ) CN779_CASE { RegionCN779CalcBackOff( calcBackOff ); break; } +#define CN779_NEXT_CHANNEL( ) CN779_CASE { return RegionCN779NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); } +#define CN779_CHANNEL_ADD( ) CN779_CASE { return RegionCN779ChannelAdd( channelAdd ); } +#define CN779_CHANNEL_REMOVE( ) CN779_CASE { return RegionCN779ChannelsRemove( channelRemove ); } +#define CN779_SET_CONTINUOUS_WAVE( ) CN779_CASE { RegionCN779SetContinuousWave( continuousWave ); break; } +#define CN779_APPLY_DR_OFFSET( ) CN779_CASE { return RegionCN779ApplyDrOffset( downlinkDwellTime, dr, drOffset ); } +#define CN779_RX_BEACON_SETUP( ) CN779_CASE { RegionCN779RxBeaconSetup( rxBeaconSetup, outDr ); break; } +#else +#define CN779_IS_ACTIVE( ) +#define CN779_GET_PHY_PARAM( ) +#define CN779_SET_BAND_TX_DONE( ) +#define CN779_INIT_DEFAULTS( ) +#define CN779_GET_NVM_CTX( ) +#define CN779_VERIFY( ) +#define CN779_APPLY_CF_LIST( ) +#define CN779_CHAN_MASK_SET( ) +#define CN779_COMPUTE_RX_WINDOW_PARAMETERS( ) +#define CN779_RX_CONFIG( ) +#define CN779_TX_CONFIG( ) +#define CN779_LINK_ADR_REQ( ) +#define CN779_RX_PARAM_SETUP_REQ( ) +#define CN779_NEW_CHANNEL_REQ( ) +#define CN779_TX_PARAM_SETUP_REQ( ) +#define CN779_DL_CHANNEL_REQ( ) +#define CN779_ALTERNATE_DR( ) +#define CN779_CALC_BACKOFF( ) +#define CN779_NEXT_CHANNEL( ) +#define CN779_CHANNEL_ADD( ) +#define CN779_CHANNEL_REMOVE( ) +#define CN779_SET_CONTINUOUS_WAVE( ) +#define CN779_APPLY_DR_OFFSET( ) +#define CN779_RX_BEACON_SETUP( ) +#endif + +#ifdef REGION_EU433 +#include "RegionEU433.h" +#define EU433_CASE case LORAMAC_REGION_EU433: +#define EU433_IS_ACTIVE( ) EU433_CASE { return true; } +#define EU433_GET_PHY_PARAM( ) EU433_CASE { return RegionEU433GetPhyParam( getPhy ); } +#define EU433_SET_BAND_TX_DONE( ) EU433_CASE { RegionEU433SetBandTxDone( txDone ); break; } +#define EU433_INIT_DEFAULTS( ) EU433_CASE { RegionEU433InitDefaults( params ); break; } +#define EU433_GET_NVM_CTX( ) EU433_CASE { return RegionEU433GetNvmCtx( params ); } +#define EU433_VERIFY( ) EU433_CASE { return RegionEU433Verify( verify, phyAttribute ); } +#define EU433_APPLY_CF_LIST( ) EU433_CASE { RegionEU433ApplyCFList( applyCFList ); break; } +#define EU433_CHAN_MASK_SET( ) EU433_CASE { return RegionEU433ChanMaskSet( chanMaskSet ); } +#define EU433_COMPUTE_RX_WINDOW_PARAMETERS( ) EU433_CASE { RegionEU433ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; } +#define EU433_RX_CONFIG( ) EU433_CASE { return RegionEU433RxConfig( rxConfig, datarate ); } +#define EU433_TX_CONFIG( ) EU433_CASE { return RegionEU433TxConfig( txConfig, txPower, txTimeOnAir ); } +#define EU433_LINK_ADR_REQ( ) EU433_CASE { return RegionEU433LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); } +#define EU433_RX_PARAM_SETUP_REQ( ) EU433_CASE { return RegionEU433RxParamSetupReq( rxParamSetupReq ); } +#define EU433_NEW_CHANNEL_REQ( ) EU433_CASE { return RegionEU433NewChannelReq( newChannelReq ); } +#define EU433_TX_PARAM_SETUP_REQ( ) EU433_CASE { return RegionEU433TxParamSetupReq( txParamSetupReq ); } +#define EU433_DL_CHANNEL_REQ( ) EU433_CASE { return RegionEU433DlChannelReq( dlChannelReq ); } +#define EU433_ALTERNATE_DR( ) EU433_CASE { return RegionEU433AlternateDr( currentDr, type ); } +#define EU433_CALC_BACKOFF( ) EU433_CASE { RegionEU433CalcBackOff( calcBackOff ); break; } +#define EU433_NEXT_CHANNEL( ) EU433_CASE { return RegionEU433NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); } +#define EU433_CHANNEL_ADD( ) EU433_CASE { return RegionEU433ChannelAdd( channelAdd ); } +#define EU433_CHANNEL_REMOVE( ) EU433_CASE { return RegionEU433ChannelsRemove( channelRemove ); } +#define EU433_SET_CONTINUOUS_WAVE( ) EU433_CASE { RegionEU433SetContinuousWave( continuousWave ); break; } +#define EU433_APPLY_DR_OFFSET( ) EU433_CASE { return RegionEU433ApplyDrOffset( downlinkDwellTime, dr, drOffset ); } +#define EU433_RX_BEACON_SETUP( ) EU433_CASE { RegionEU433RxBeaconSetup( rxBeaconSetup, outDr ); break; } +#else +#define EU433_IS_ACTIVE( ) +#define EU433_GET_PHY_PARAM( ) +#define EU433_SET_BAND_TX_DONE( ) +#define EU433_INIT_DEFAULTS( ) +#define EU433_GET_NVM_CTX( ) +#define EU433_VERIFY( ) +#define EU433_APPLY_CF_LIST( ) +#define EU433_CHAN_MASK_SET( ) +#define EU433_COMPUTE_RX_WINDOW_PARAMETERS( ) +#define EU433_RX_CONFIG( ) +#define EU433_TX_CONFIG( ) +#define EU433_LINK_ADR_REQ( ) +#define EU433_RX_PARAM_SETUP_REQ( ) +#define EU433_NEW_CHANNEL_REQ( ) +#define EU433_TX_PARAM_SETUP_REQ( ) +#define EU433_DL_CHANNEL_REQ( ) +#define EU433_ALTERNATE_DR( ) +#define EU433_CALC_BACKOFF( ) +#define EU433_NEXT_CHANNEL( ) +#define EU433_CHANNEL_ADD( ) +#define EU433_CHANNEL_REMOVE( ) +#define EU433_SET_CONTINUOUS_WAVE( ) +#define EU433_APPLY_DR_OFFSET( ) +#define EU433_RX_BEACON_SETUP( ) +#endif + +#ifdef REGION_EU868 +#include "RegionEU868.h" +#define EU868_CASE case LORAMAC_REGION_EU868: +#define EU868_IS_ACTIVE( ) EU868_CASE { return true; } +#define EU868_GET_PHY_PARAM( ) EU868_CASE { return RegionEU868GetPhyParam( getPhy ); } +#define EU868_SET_BAND_TX_DONE( ) EU868_CASE { RegionEU868SetBandTxDone( txDone ); break; } +#define EU868_INIT_DEFAULTS( ) EU868_CASE { RegionEU868InitDefaults( params ); break; } +#define EU868_GET_NVM_CTX( ) EU868_CASE { return RegionEU868GetNvmCtx( params ); } +#define EU868_VERIFY( ) EU868_CASE { return RegionEU868Verify( verify, phyAttribute ); } +#define EU868_APPLY_CF_LIST( ) EU868_CASE { RegionEU868ApplyCFList( applyCFList ); break; } +#define EU868_CHAN_MASK_SET( ) EU868_CASE { return RegionEU868ChanMaskSet( chanMaskSet ); } +#define EU868_COMPUTE_RX_WINDOW_PARAMETERS( ) EU868_CASE { RegionEU868ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; } +#define EU868_RX_CONFIG( ) EU868_CASE { return RegionEU868RxConfig( rxConfig, datarate ); } +#define EU868_TX_CONFIG( ) EU868_CASE { return RegionEU868TxConfig( txConfig, txPower, txTimeOnAir ); } +#define EU868_LINK_ADR_REQ( ) EU868_CASE { return RegionEU868LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); } +#define EU868_RX_PARAM_SETUP_REQ( ) EU868_CASE { return RegionEU868RxParamSetupReq( rxParamSetupReq ); } +#define EU868_NEW_CHANNEL_REQ( ) EU868_CASE { return RegionEU868NewChannelReq( newChannelReq ); } +#define EU868_TX_PARAM_SETUP_REQ( ) EU868_CASE { return RegionEU868TxParamSetupReq( txParamSetupReq ); } +#define EU868_DL_CHANNEL_REQ( ) EU868_CASE { return RegionEU868DlChannelReq( dlChannelReq ); } +#define EU868_ALTERNATE_DR( ) EU868_CASE { return RegionEU868AlternateDr( currentDr, type ); } +#define EU868_CALC_BACKOFF( ) EU868_CASE { RegionEU868CalcBackOff( calcBackOff ); break; } +#define EU868_NEXT_CHANNEL( ) EU868_CASE { return RegionEU868NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); } +#define EU868_CHANNEL_ADD( ) EU868_CASE { return RegionEU868ChannelAdd( channelAdd ); } +#define EU868_CHANNEL_REMOVE( ) EU868_CASE { return RegionEU868ChannelsRemove( channelRemove ); } +#define EU868_SET_CONTINUOUS_WAVE( ) EU868_CASE { RegionEU868SetContinuousWave( continuousWave ); break; } +#define EU868_APPLY_DR_OFFSET( ) EU868_CASE { return RegionEU868ApplyDrOffset( downlinkDwellTime, dr, drOffset ); } +#define EU868_RX_BEACON_SETUP( ) EU868_CASE { RegionEU868RxBeaconSetup( rxBeaconSetup, outDr ); break; } +#else +#define EU868_IS_ACTIVE( ) +#define EU868_GET_PHY_PARAM( ) +#define EU868_SET_BAND_TX_DONE( ) +#define EU868_INIT_DEFAULTS( ) +#define EU868_GET_NVM_CTX( ) +#define EU868_VERIFY( ) +#define EU868_APPLY_CF_LIST( ) +#define EU868_CHAN_MASK_SET( ) +#define EU868_COMPUTE_RX_WINDOW_PARAMETERS( ) +#define EU868_RX_CONFIG( ) +#define EU868_TX_CONFIG( ) +#define EU868_LINK_ADR_REQ( ) +#define EU868_RX_PARAM_SETUP_REQ( ) +#define EU868_NEW_CHANNEL_REQ( ) +#define EU868_TX_PARAM_SETUP_REQ( ) +#define EU868_DL_CHANNEL_REQ( ) +#define EU868_ALTERNATE_DR( ) +#define EU868_CALC_BACKOFF( ) +#define EU868_NEXT_CHANNEL( ) +#define EU868_CHANNEL_ADD( ) +#define EU868_CHANNEL_REMOVE( ) +#define EU868_SET_CONTINUOUS_WAVE( ) +#define EU868_APPLY_DR_OFFSET( ) +#define EU868_RX_BEACON_SETUP( ) +#endif + +#ifdef REGION_KR920 +#include "RegionKR920.h" +#define KR920_CASE case LORAMAC_REGION_KR920: +#define KR920_IS_ACTIVE( ) KR920_CASE { return true; } +#define KR920_GET_PHY_PARAM( ) KR920_CASE { return RegionKR920GetPhyParam( getPhy ); } +#define KR920_SET_BAND_TX_DONE( ) KR920_CASE { RegionKR920SetBandTxDone( txDone ); break; } +#define KR920_INIT_DEFAULTS( ) KR920_CASE { RegionKR920InitDefaults( params ); break; } +#define KR920_GET_NVM_CTX( ) KR920_CASE { return RegionKR920GetNvmCtx( params ); } +#define KR920_VERIFY( ) KR920_CASE { return RegionKR920Verify( verify, phyAttribute ); } +#define KR920_APPLY_CF_LIST( ) KR920_CASE { RegionKR920ApplyCFList( applyCFList ); break; } +#define KR920_CHAN_MASK_SET( ) KR920_CASE { return RegionKR920ChanMaskSet( chanMaskSet ); } +#define KR920_COMPUTE_RX_WINDOW_PARAMETERS( ) KR920_CASE { RegionKR920ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; } +#define KR920_RX_CONFIG( ) KR920_CASE { return RegionKR920RxConfig( rxConfig, datarate ); } +#define KR920_TX_CONFIG( ) KR920_CASE { return RegionKR920TxConfig( txConfig, txPower, txTimeOnAir ); } +#define KR920_LINK_ADR_REQ( ) KR920_CASE { return RegionKR920LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); } +#define KR920_RX_PARAM_SETUP_REQ( ) KR920_CASE { return RegionKR920RxParamSetupReq( rxParamSetupReq ); } +#define KR920_NEW_CHANNEL_REQ( ) KR920_CASE { return RegionKR920NewChannelReq( newChannelReq ); } +#define KR920_TX_PARAM_SETUP_REQ( ) KR920_CASE { return RegionKR920TxParamSetupReq( txParamSetupReq ); } +#define KR920_DL_CHANNEL_REQ( ) KR920_CASE { return RegionKR920DlChannelReq( dlChannelReq ); } +#define KR920_ALTERNATE_DR( ) KR920_CASE { return RegionKR920AlternateDr( currentDr, type ); } +#define KR920_CALC_BACKOFF( ) KR920_CASE { RegionKR920CalcBackOff( calcBackOff ); break; } +#define KR920_NEXT_CHANNEL( ) KR920_CASE { return RegionKR920NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); } +#define KR920_CHANNEL_ADD( ) KR920_CASE { return RegionKR920ChannelAdd( channelAdd ); } +#define KR920_CHANNEL_REMOVE( ) KR920_CASE { return RegionKR920ChannelsRemove( channelRemove ); } +#define KR920_SET_CONTINUOUS_WAVE( ) KR920_CASE { RegionKR920SetContinuousWave( continuousWave ); break; } +#define KR920_APPLY_DR_OFFSET( ) KR920_CASE { return RegionKR920ApplyDrOffset( downlinkDwellTime, dr, drOffset ); } +#define KR920_RX_BEACON_SETUP( ) KR920_CASE { RegionKR920RxBeaconSetup( rxBeaconSetup, outDr ); break; } +#else +#define KR920_IS_ACTIVE( ) +#define KR920_GET_PHY_PARAM( ) +#define KR920_SET_BAND_TX_DONE( ) +#define KR920_INIT_DEFAULTS( ) +#define KR920_GET_NVM_CTX( ) +#define KR920_VERIFY( ) +#define KR920_APPLY_CF_LIST( ) +#define KR920_CHAN_MASK_SET( ) +#define KR920_COMPUTE_RX_WINDOW_PARAMETERS( ) +#define KR920_RX_CONFIG( ) +#define KR920_TX_CONFIG( ) +#define KR920_LINK_ADR_REQ( ) +#define KR920_RX_PARAM_SETUP_REQ( ) +#define KR920_NEW_CHANNEL_REQ( ) +#define KR920_TX_PARAM_SETUP_REQ( ) +#define KR920_DL_CHANNEL_REQ( ) +#define KR920_ALTERNATE_DR( ) +#define KR920_CALC_BACKOFF( ) +#define KR920_NEXT_CHANNEL( ) +#define KR920_CHANNEL_ADD( ) +#define KR920_CHANNEL_REMOVE( ) +#define KR920_SET_CONTINUOUS_WAVE( ) +#define KR920_APPLY_DR_OFFSET( ) +#define KR920_RX_BEACON_SETUP( ) +#endif + +#ifdef REGION_IN865 +#include "RegionIN865.h" +#define IN865_CASE case LORAMAC_REGION_IN865: +#define IN865_IS_ACTIVE( ) IN865_CASE { return true; } +#define IN865_GET_PHY_PARAM( ) IN865_CASE { return RegionIN865GetPhyParam( getPhy ); } +#define IN865_SET_BAND_TX_DONE( ) IN865_CASE { RegionIN865SetBandTxDone( txDone ); break; } +#define IN865_INIT_DEFAULTS( ) IN865_CASE { RegionIN865InitDefaults( params ); break; } +#define IN865_GET_NVM_CTX( ) IN865_CASE { return RegionIN865GetNvmCtx( params ); } +#define IN865_VERIFY( ) IN865_CASE { return RegionIN865Verify( verify, phyAttribute ); } +#define IN865_APPLY_CF_LIST( ) IN865_CASE { RegionIN865ApplyCFList( applyCFList ); break; } +#define IN865_CHAN_MASK_SET( ) IN865_CASE { return RegionIN865ChanMaskSet( chanMaskSet ); } +#define IN865_COMPUTE_RX_WINDOW_PARAMETERS( ) IN865_CASE { RegionIN865ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; } +#define IN865_RX_CONFIG( ) IN865_CASE { return RegionIN865RxConfig( rxConfig, datarate ); } +#define IN865_TX_CONFIG( ) IN865_CASE { return RegionIN865TxConfig( txConfig, txPower, txTimeOnAir ); } +#define IN865_LINK_ADR_REQ( ) IN865_CASE { return RegionIN865LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); } +#define IN865_RX_PARAM_SETUP_REQ( ) IN865_CASE { return RegionIN865RxParamSetupReq( rxParamSetupReq ); } +#define IN865_NEW_CHANNEL_REQ( ) IN865_CASE { return RegionIN865NewChannelReq( newChannelReq ); } +#define IN865_TX_PARAM_SETUP_REQ( ) IN865_CASE { return RegionIN865TxParamSetupReq( txParamSetupReq ); } +#define IN865_DL_CHANNEL_REQ( ) IN865_CASE { return RegionIN865DlChannelReq( dlChannelReq ); } +#define IN865_ALTERNATE_DR( ) IN865_CASE { return RegionIN865AlternateDr( currentDr, type ); } +#define IN865_CALC_BACKOFF( ) IN865_CASE { RegionIN865CalcBackOff( calcBackOff ); break; } +#define IN865_NEXT_CHANNEL( ) IN865_CASE { return RegionIN865NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); } +#define IN865_CHANNEL_ADD( ) IN865_CASE { return RegionIN865ChannelAdd( channelAdd ); } +#define IN865_CHANNEL_REMOVE( ) IN865_CASE { return RegionIN865ChannelsRemove( channelRemove ); } +#define IN865_SET_CONTINUOUS_WAVE( ) IN865_CASE { RegionIN865SetContinuousWave( continuousWave ); break; } +#define IN865_APPLY_DR_OFFSET( ) IN865_CASE { return RegionIN865ApplyDrOffset( downlinkDwellTime, dr, drOffset ); } +#define IN865_RX_BEACON_SETUP( ) IN865_CASE { RegionIN865RxBeaconSetup( rxBeaconSetup, outDr ); break; } +#else +#define IN865_IS_ACTIVE( ) +#define IN865_GET_PHY_PARAM( ) +#define IN865_SET_BAND_TX_DONE( ) +#define IN865_INIT_DEFAULTS( ) +#define IN865_GET_NVM_CTX( ) +#define IN865_VERIFY( ) +#define IN865_APPLY_CF_LIST( ) +#define IN865_CHAN_MASK_SET( ) +#define IN865_COMPUTE_RX_WINDOW_PARAMETERS( ) +#define IN865_RX_CONFIG( ) +#define IN865_TX_CONFIG( ) +#define IN865_LINK_ADR_REQ( ) +#define IN865_RX_PARAM_SETUP_REQ( ) +#define IN865_NEW_CHANNEL_REQ( ) +#define IN865_TX_PARAM_SETUP_REQ( ) +#define IN865_DL_CHANNEL_REQ( ) +#define IN865_ALTERNATE_DR( ) +#define IN865_CALC_BACKOFF( ) +#define IN865_NEXT_CHANNEL( ) +#define IN865_CHANNEL_ADD( ) +#define IN865_CHANNEL_REMOVE( ) +#define IN865_SET_CONTINUOUS_WAVE( ) +#define IN865_APPLY_DR_OFFSET( ) +#define IN865_RX_BEACON_SETUP( ) +#endif + +#ifdef REGION_US915 +#include "RegionUS915.h" +#define US915_CASE case LORAMAC_REGION_US915: +#define US915_IS_ACTIVE( ) US915_CASE { return true; } +#define US915_GET_PHY_PARAM( ) US915_CASE { return RegionUS915GetPhyParam( getPhy ); } +#define US915_SET_BAND_TX_DONE( ) US915_CASE { RegionUS915SetBandTxDone( txDone ); break; } +#define US915_INIT_DEFAULTS( ) US915_CASE { RegionUS915InitDefaults( params ); break; } +#define US915_GET_NVM_CTX( ) US915_CASE { return RegionUS915GetNvmCtx( params ); } +#define US915_VERIFY( ) US915_CASE { return RegionUS915Verify( verify, phyAttribute ); } +#define US915_APPLY_CF_LIST( ) US915_CASE { RegionUS915ApplyCFList( applyCFList ); break; } +#define US915_CHAN_MASK_SET( ) US915_CASE { return RegionUS915ChanMaskSet( chanMaskSet ); } +#define US915_COMPUTE_RX_WINDOW_PARAMETERS( ) US915_CASE { RegionUS915ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; } +#define US915_RX_CONFIG( ) US915_CASE { return RegionUS915RxConfig( rxConfig, datarate ); } +#define US915_TX_CONFIG( ) US915_CASE { return RegionUS915TxConfig( txConfig, txPower, txTimeOnAir ); } +#define US915_LINK_ADR_REQ( ) US915_CASE { return RegionUS915LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); } +#define US915_RX_PARAM_SETUP_REQ( ) US915_CASE { return RegionUS915RxParamSetupReq( rxParamSetupReq ); } +#define US915_NEW_CHANNEL_REQ( ) US915_CASE { return RegionUS915NewChannelReq( newChannelReq ); } +#define US915_TX_PARAM_SETUP_REQ( ) US915_CASE { return RegionUS915TxParamSetupReq( txParamSetupReq ); } +#define US915_DL_CHANNEL_REQ( ) US915_CASE { return RegionUS915DlChannelReq( dlChannelReq ); } +#define US915_ALTERNATE_DR( ) US915_CASE { return RegionUS915AlternateDr( currentDr, type ); } +#define US915_CALC_BACKOFF( ) US915_CASE { RegionUS915CalcBackOff( calcBackOff ); break; } +#define US915_NEXT_CHANNEL( ) US915_CASE { return RegionUS915NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); } +#define US915_CHANNEL_ADD( ) US915_CASE { return RegionUS915ChannelAdd( channelAdd ); } +#define US915_CHANNEL_REMOVE( ) US915_CASE { return RegionUS915ChannelsRemove( channelRemove ); } +#define US915_SET_CONTINUOUS_WAVE( ) US915_CASE { RegionUS915SetContinuousWave( continuousWave ); break; } +#define US915_APPLY_DR_OFFSET( ) US915_CASE { return RegionUS915ApplyDrOffset( downlinkDwellTime, dr, drOffset ); } +#define US915_RX_BEACON_SETUP( ) US915_CASE { RegionUS915RxBeaconSetup( rxBeaconSetup, outDr ); break; } +#else +#define US915_IS_ACTIVE( ) +#define US915_GET_PHY_PARAM( ) +#define US915_SET_BAND_TX_DONE( ) +#define US915_INIT_DEFAULTS( ) +#define US915_GET_NVM_CTX( ) +#define US915_VERIFY( ) +#define US915_APPLY_CF_LIST( ) +#define US915_CHAN_MASK_SET( ) +#define US915_COMPUTE_RX_WINDOW_PARAMETERS( ) +#define US915_RX_CONFIG( ) +#define US915_TX_CONFIG( ) +#define US915_LINK_ADR_REQ( ) +#define US915_RX_PARAM_SETUP_REQ( ) +#define US915_NEW_CHANNEL_REQ( ) +#define US915_TX_PARAM_SETUP_REQ( ) +#define US915_DL_CHANNEL_REQ( ) +#define US915_ALTERNATE_DR( ) +#define US915_CALC_BACKOFF( ) +#define US915_NEXT_CHANNEL( ) +#define US915_CHANNEL_ADD( ) +#define US915_CHANNEL_REMOVE( ) +#define US915_SET_CONTINUOUS_WAVE( ) +#define US915_APPLY_DR_OFFSET( ) +#define US915_RX_BEACON_SETUP( ) +#endif + +#ifdef REGION_RU864 +#include "RegionRU864.h" +#define RU864_CASE case LORAMAC_REGION_RU864: +#define RU864_IS_ACTIVE( ) RU864_CASE { return true; } +#define RU864_GET_PHY_PARAM( ) RU864_CASE { return RegionRU864GetPhyParam( getPhy ); } +#define RU864_SET_BAND_TX_DONE( ) RU864_CASE { RegionRU864SetBandTxDone( txDone ); break; } +#define RU864_INIT_DEFAULTS( ) RU864_CASE { RegionRU864InitDefaults( params ); break; } +#define RU864_GET_NVM_CTX( ) RU864_CASE { return RegionRU864GetNvmCtx( params ); } +#define RU864_VERIFY( ) RU864_CASE { return RegionRU864Verify( verify, phyAttribute ); } +#define RU864_APPLY_CF_LIST( ) RU864_CASE { RegionRU864ApplyCFList( applyCFList ); break; } +#define RU864_CHAN_MASK_SET( ) RU864_CASE { return RegionRU864ChanMaskSet( chanMaskSet ); } +#define RU864_COMPUTE_RX_WINDOW_PARAMETERS( ) RU864_CASE { RegionRU864ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; } +#define RU864_RX_CONFIG( ) RU864_CASE { return RegionRU864RxConfig( rxConfig, datarate ); } +#define RU864_TX_CONFIG( ) RU864_CASE { return RegionRU864TxConfig( txConfig, txPower, txTimeOnAir ); } +#define RU864_LINK_ADR_REQ( ) RU864_CASE { return RegionRU864LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); } +#define RU864_RX_PARAM_SETUP_REQ( ) RU864_CASE { return RegionRU864RxParamSetupReq( rxParamSetupReq ); } +#define RU864_NEW_CHANNEL_REQ( ) RU864_CASE { return RegionRU864NewChannelReq( newChannelReq ); } +#define RU864_TX_PARAM_SETUP_REQ( ) RU864_CASE { return RegionRU864TxParamSetupReq( txParamSetupReq ); } +#define RU864_DL_CHANNEL_REQ( ) RU864_CASE { return RegionRU864DlChannelReq( dlChannelReq ); } +#define RU864_ALTERNATE_DR( ) RU864_CASE { return RegionRU864AlternateDr( currentDr, type ); } +#define RU864_CALC_BACKOFF( ) RU864_CASE { RegionRU864CalcBackOff( calcBackOff ); break; } +#define RU864_NEXT_CHANNEL( ) RU864_CASE { return RegionRU864NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); } +#define RU864_CHANNEL_ADD( ) RU864_CASE { return RegionRU864ChannelAdd( channelAdd ); } +#define RU864_CHANNEL_REMOVE( ) RU864_CASE { return RegionRU864ChannelsRemove( channelRemove ); } +#define RU864_SET_CONTINUOUS_WAVE( ) RU864_CASE { RegionRU864SetContinuousWave( continuousWave ); break; } +#define RU864_APPLY_DR_OFFSET( ) RU864_CASE { return RegionRU864ApplyDrOffset( downlinkDwellTime, dr, drOffset ); } +#define RU864_RX_BEACON_SETUP( ) RU864_CASE { RegionRU864RxBeaconSetup( rxBeaconSetup, outDr ); break; } +#else +#define RU864_IS_ACTIVE( ) +#define RU864_GET_PHY_PARAM( ) +#define RU864_SET_BAND_TX_DONE( ) +#define RU864_INIT_DEFAULTS( ) +#define RU864_GET_NVM_CTX( ) +#define RU864_VERIFY( ) +#define RU864_APPLY_CF_LIST( ) +#define RU864_CHAN_MASK_SET( ) +#define RU864_COMPUTE_RX_WINDOW_PARAMETERS( ) +#define RU864_RX_CONFIG( ) +#define RU864_TX_CONFIG( ) +#define RU864_LINK_ADR_REQ( ) +#define RU864_RX_PARAM_SETUP_REQ( ) +#define RU864_NEW_CHANNEL_REQ( ) +#define RU864_TX_PARAM_SETUP_REQ( ) +#define RU864_DL_CHANNEL_REQ( ) +#define RU864_ALTERNATE_DR( ) +#define RU864_CALC_BACKOFF( ) +#define RU864_NEXT_CHANNEL( ) +#define RU864_CHANNEL_ADD( ) +#define RU864_CHANNEL_REMOVE( ) +#define RU864_SET_CONTINUOUS_WAVE( ) +#define RU864_APPLY_DR_OFFSET( ) +#define RU864_RX_BEACON_SETUP( ) +#endif + +bool RegionIsActive( LoRaMacRegion_t region ) +{ + switch( region ) + { + AS923_IS_ACTIVE( ); + AU915_IS_ACTIVE( ); + CN470_IS_ACTIVE( ); + CN470ALI_IS_ACTIVE( ); + CN779_IS_ACTIVE( ); + EU433_IS_ACTIVE( ); + EU868_IS_ACTIVE( ); + KR920_IS_ACTIVE( ); + IN865_IS_ACTIVE( ); + US915_IS_ACTIVE( ); + RU864_IS_ACTIVE( ); + default: + { + return false; + } + } +} + +PhyParam_t RegionGetPhyParam( LoRaMacRegion_t region, GetPhyParams_t* getPhy ) +{ + PhyParam_t phyParam = { 0 }; + switch( region ) + { + AS923_GET_PHY_PARAM( ); + AU915_GET_PHY_PARAM( ); + CN470_GET_PHY_PARAM( ); + CN470ALI_GET_PHY_PARAM( ); + CN779_GET_PHY_PARAM( ); + EU433_GET_PHY_PARAM( ); + EU868_GET_PHY_PARAM( ); + KR920_GET_PHY_PARAM( ); + IN865_GET_PHY_PARAM( ); + US915_GET_PHY_PARAM( ); + RU864_GET_PHY_PARAM( ); + default: + { + return phyParam; + } + } +} + +void RegionSetBandTxDone( LoRaMacRegion_t region, SetBandTxDoneParams_t* txDone ) +{ + switch( region ) + { + AS923_SET_BAND_TX_DONE( ); + AU915_SET_BAND_TX_DONE( ); + CN470_SET_BAND_TX_DONE( ); + CN470ALI_SET_BAND_TX_DONE( ); + CN779_SET_BAND_TX_DONE( ); + EU433_SET_BAND_TX_DONE( ); + EU868_SET_BAND_TX_DONE( ); + KR920_SET_BAND_TX_DONE( ); + IN865_SET_BAND_TX_DONE( ); + US915_SET_BAND_TX_DONE( ); + RU864_SET_BAND_TX_DONE( ); + default: + { + return; + } + } +} + +void RegionInitDefaults( LoRaMacRegion_t region, InitDefaultsParams_t* params ) +{ + switch( region ) + { + AS923_INIT_DEFAULTS( ); + AU915_INIT_DEFAULTS( ); + CN470_INIT_DEFAULTS( ); + CN470ALI_INIT_DEFAULTS( ); + CN779_INIT_DEFAULTS( ); + EU433_INIT_DEFAULTS( ); + EU868_INIT_DEFAULTS( ); + KR920_INIT_DEFAULTS( ); + IN865_INIT_DEFAULTS( ); + US915_INIT_DEFAULTS( ); + RU864_INIT_DEFAULTS( ); + default: + { + break; + } + } +} + +void* RegionGetNvmCtx( LoRaMacRegion_t region, GetNvmCtxParams_t* params ) +{ + switch( region ) + { + AS923_GET_NVM_CTX( ); + AU915_GET_NVM_CTX( ); + CN470_GET_NVM_CTX( ); + CN470ALI_GET_NVM_CTX( ); + CN779_GET_NVM_CTX( ); + EU433_GET_NVM_CTX( ); + EU868_GET_NVM_CTX( ); + KR920_GET_NVM_CTX( ); + IN865_GET_NVM_CTX( ); + US915_GET_NVM_CTX( ); + RU864_GET_NVM_CTX( ); + default: + { + return 0; + } + } +} + +bool RegionVerify( LoRaMacRegion_t region, VerifyParams_t* verify, PhyAttribute_t phyAttribute ) +{ + switch( region ) + { + AS923_VERIFY( ); + AU915_VERIFY( ); + CN470_VERIFY( ); + CN470ALI_VERIFY( ); + CN779_VERIFY( ); + EU433_VERIFY( ); + EU868_VERIFY( ); + KR920_VERIFY( ); + IN865_VERIFY( ); + US915_VERIFY( ); + RU864_VERIFY( ); + default: + { + return false; + } + } +} + +void RegionApplyCFList( LoRaMacRegion_t region, ApplyCFListParams_t* applyCFList ) +{ + switch( region ) + { + AS923_APPLY_CF_LIST( ); + AU915_APPLY_CF_LIST( ); + CN470_APPLY_CF_LIST( ); + CN470ALI_APPLY_CF_LIST( ); + CN779_APPLY_CF_LIST( ); + EU433_APPLY_CF_LIST( ); + EU868_APPLY_CF_LIST( ); + KR920_APPLY_CF_LIST( ); + IN865_APPLY_CF_LIST( ); + US915_APPLY_CF_LIST( ); + RU864_APPLY_CF_LIST( ); + default: + { + break; + } + } +} + +bool RegionChanMaskSet( LoRaMacRegion_t region, ChanMaskSetParams_t* chanMaskSet ) +{ + switch( region ) + { + AS923_CHAN_MASK_SET( ); + AU915_CHAN_MASK_SET( ); + CN470_CHAN_MASK_SET( ); + CN470ALI_CHAN_MASK_SET( ); + CN779_CHAN_MASK_SET( ); + EU433_CHAN_MASK_SET( ); + EU868_CHAN_MASK_SET( ); + KR920_CHAN_MASK_SET( ); + IN865_CHAN_MASK_SET( ); + US915_CHAN_MASK_SET( ); + RU864_CHAN_MASK_SET( ); + default: + { + return false; + } + } +} + +void RegionComputeRxWindowParameters( LoRaMacRegion_t region, int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ) +{ + switch( region ) + { + AS923_COMPUTE_RX_WINDOW_PARAMETERS( ); + AU915_COMPUTE_RX_WINDOW_PARAMETERS( ); + CN470_COMPUTE_RX_WINDOW_PARAMETERS( ); + CN470ALI_COMPUTE_RX_WINDOW_PARAMETERS( ); + CN779_COMPUTE_RX_WINDOW_PARAMETERS( ); + EU433_COMPUTE_RX_WINDOW_PARAMETERS( ); + EU868_COMPUTE_RX_WINDOW_PARAMETERS( ); + KR920_COMPUTE_RX_WINDOW_PARAMETERS( ); + IN865_COMPUTE_RX_WINDOW_PARAMETERS( ); + US915_COMPUTE_RX_WINDOW_PARAMETERS( ); + RU864_COMPUTE_RX_WINDOW_PARAMETERS( ); + default: + { + break; + } + } +} + +bool RegionRxConfig( LoRaMacRegion_t region, RxConfigParams_t* rxConfig, int8_t* datarate ) +{ + switch( region ) + { + AS923_RX_CONFIG( ); + AU915_RX_CONFIG( ); + CN470_RX_CONFIG( ); + CN470ALI_RX_CONFIG( ); + CN779_RX_CONFIG( ); + EU433_RX_CONFIG( ); + EU868_RX_CONFIG( ); + KR920_RX_CONFIG( ); + IN865_RX_CONFIG( ); + US915_RX_CONFIG( ); + RU864_RX_CONFIG( ); + default: + { + return false; + } + } +} + +bool RegionTxConfig( LoRaMacRegion_t region, TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ) +{ + switch( region ) + { + AS923_TX_CONFIG( ); + AU915_TX_CONFIG( ); + CN470_TX_CONFIG( ); + CN470ALI_TX_CONFIG( ); + CN779_TX_CONFIG( ); + EU433_TX_CONFIG( ); + EU868_TX_CONFIG( ); + KR920_TX_CONFIG( ); + IN865_TX_CONFIG( ); + US915_TX_CONFIG( ); + RU864_TX_CONFIG( ); + default: + { + return false; + } + } +} + +uint8_t RegionLinkAdrReq( LoRaMacRegion_t region, LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ) +{ + switch( region ) + { + AS923_LINK_ADR_REQ( ); + AU915_LINK_ADR_REQ( ); + CN470_LINK_ADR_REQ( ); + CN470ALI_LINK_ADR_REQ( ); + CN779_LINK_ADR_REQ( ); + EU433_LINK_ADR_REQ( ); + EU868_LINK_ADR_REQ( ); + KR920_LINK_ADR_REQ( ); + IN865_LINK_ADR_REQ( ); + US915_LINK_ADR_REQ( ); + RU864_LINK_ADR_REQ( ); + default: + { + return 0; + } + } +} + +uint8_t RegionRxParamSetupReq( LoRaMacRegion_t region, RxParamSetupReqParams_t* rxParamSetupReq ) +{ + switch( region ) + { + AS923_RX_PARAM_SETUP_REQ( ); + AU915_RX_PARAM_SETUP_REQ( ); + CN470_RX_PARAM_SETUP_REQ( ); + CN470ALI_RX_PARAM_SETUP_REQ( ); + CN779_RX_PARAM_SETUP_REQ( ); + EU433_RX_PARAM_SETUP_REQ( ); + EU868_RX_PARAM_SETUP_REQ( ); + KR920_RX_PARAM_SETUP_REQ( ); + IN865_RX_PARAM_SETUP_REQ( ); + US915_RX_PARAM_SETUP_REQ( ); + RU864_RX_PARAM_SETUP_REQ( ); + default: + { + return 0; + } + } +} + +uint8_t RegionNewChannelReq( LoRaMacRegion_t region, NewChannelReqParams_t* newChannelReq ) +{ + switch( region ) + { + AS923_NEW_CHANNEL_REQ( ); + AU915_NEW_CHANNEL_REQ( ); + CN470_NEW_CHANNEL_REQ( ); + CN470ALI_NEW_CHANNEL_REQ( ); + CN779_NEW_CHANNEL_REQ( ); + EU433_NEW_CHANNEL_REQ( ); + EU868_NEW_CHANNEL_REQ( ); + KR920_NEW_CHANNEL_REQ( ); + IN865_NEW_CHANNEL_REQ( ); + US915_NEW_CHANNEL_REQ( ); + RU864_NEW_CHANNEL_REQ( ); + default: + { + return 0; + } + } +} + +int8_t RegionTxParamSetupReq( LoRaMacRegion_t region, TxParamSetupReqParams_t* txParamSetupReq ) +{ + switch( region ) + { + AS923_TX_PARAM_SETUP_REQ( ); + AU915_TX_PARAM_SETUP_REQ( ); + CN470_TX_PARAM_SETUP_REQ( ); + CN470ALI_TX_PARAM_SETUP_REQ( ); + CN779_TX_PARAM_SETUP_REQ( ); + EU433_TX_PARAM_SETUP_REQ( ); + EU868_TX_PARAM_SETUP_REQ( ); + KR920_TX_PARAM_SETUP_REQ( ); + IN865_TX_PARAM_SETUP_REQ( ); + US915_TX_PARAM_SETUP_REQ( ); + RU864_TX_PARAM_SETUP_REQ( ); + default: + { + return 0; + } + } +} + +uint8_t RegionDlChannelReq( LoRaMacRegion_t region, DlChannelReqParams_t* dlChannelReq ) +{ + switch( region ) + { + AS923_DL_CHANNEL_REQ( ); + AU915_DL_CHANNEL_REQ( ); + CN470_DL_CHANNEL_REQ( ); + CN470ALI_DL_CHANNEL_REQ( ); + CN779_DL_CHANNEL_REQ( ); + EU433_DL_CHANNEL_REQ( ); + EU868_DL_CHANNEL_REQ( ); + KR920_DL_CHANNEL_REQ( ); + IN865_DL_CHANNEL_REQ( ); + US915_DL_CHANNEL_REQ( ); + RU864_DL_CHANNEL_REQ( ); + default: + { + return 0; + } + } +} + +int8_t RegionAlternateDr( LoRaMacRegion_t region, int8_t currentDr, AlternateDrType_t type ) +{ + switch( region ) + { + AS923_ALTERNATE_DR( ); + AU915_ALTERNATE_DR( ); + CN470_ALTERNATE_DR( ); + CN470ALI_ALTERNATE_DR( ); + CN779_ALTERNATE_DR( ); + EU433_ALTERNATE_DR( ); + EU868_ALTERNATE_DR( ); + KR920_ALTERNATE_DR( ); + IN865_ALTERNATE_DR( ); + US915_ALTERNATE_DR( ); + RU864_ALTERNATE_DR( ); + default: + { + return 0; + } + } +} + +void RegionCalcBackOff( LoRaMacRegion_t region, CalcBackOffParams_t* calcBackOff ) +{ + switch( region ) + { + AS923_CALC_BACKOFF( ); + AU915_CALC_BACKOFF( ); + CN470_CALC_BACKOFF( ); + CN470ALI_CALC_BACKOFF( ); + CN779_CALC_BACKOFF( ); + EU433_CALC_BACKOFF( ); + EU868_CALC_BACKOFF( ); + KR920_CALC_BACKOFF( ); + IN865_CALC_BACKOFF( ); + US915_CALC_BACKOFF( ); + RU864_CALC_BACKOFF( ); + default: + { + break; + } + } +} + +LoRaMacStatus_t RegionNextChannel( LoRaMacRegion_t region, NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ) +{ + switch( region ) + { + AS923_NEXT_CHANNEL( ); + AU915_NEXT_CHANNEL( ); + CN470_NEXT_CHANNEL( ); + CN470ALI_NEXT_CHANNEL( ); + CN779_NEXT_CHANNEL( ); + EU433_NEXT_CHANNEL( ); + EU868_NEXT_CHANNEL( ); + KR920_NEXT_CHANNEL( ); + IN865_NEXT_CHANNEL( ); + US915_NEXT_CHANNEL( ); + RU864_NEXT_CHANNEL( ); + default: + { + return LORAMAC_STATUS_REGION_NOT_SUPPORTED; + } + } +} + +LoRaMacStatus_t RegionChannelAdd( LoRaMacRegion_t region, ChannelAddParams_t* channelAdd ) +{ + switch( region ) + { + AS923_CHANNEL_ADD( ); + AU915_CHANNEL_ADD( ); + CN470_CHANNEL_ADD( ); + CN470ALI_CHANNEL_ADD( ); + CN779_CHANNEL_ADD( ); + EU433_CHANNEL_ADD( ); + EU868_CHANNEL_ADD( ); + KR920_CHANNEL_ADD( ); + IN865_CHANNEL_ADD( ); + US915_CHANNEL_ADD( ); + RU864_CHANNEL_ADD( ); + default: + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + } +} + +bool RegionChannelsRemove( LoRaMacRegion_t region, ChannelRemoveParams_t* channelRemove ) +{ + switch( region ) + { + AS923_CHANNEL_REMOVE( ); + AU915_CHANNEL_REMOVE( ); + CN470_CHANNEL_REMOVE( ); + CN470ALI_CHANNEL_REMOVE( ); + CN779_CHANNEL_REMOVE( ); + EU433_CHANNEL_REMOVE( ); + EU868_CHANNEL_REMOVE( ); + KR920_CHANNEL_REMOVE( ); + IN865_CHANNEL_REMOVE( ); + US915_CHANNEL_REMOVE( ); + RU864_CHANNEL_REMOVE( ); + default: + { + return false; + } + } +} + +void RegionSetContinuousWave( LoRaMacRegion_t region, ContinuousWaveParams_t* continuousWave ) +{ + switch( region ) + { + AS923_SET_CONTINUOUS_WAVE( ); + AU915_SET_CONTINUOUS_WAVE( ); + CN470_SET_CONTINUOUS_WAVE( ); + CN470ALI_SET_CONTINUOUS_WAVE( ); + CN779_SET_CONTINUOUS_WAVE( ); + EU433_SET_CONTINUOUS_WAVE( ); + EU868_SET_CONTINUOUS_WAVE( ); + KR920_SET_CONTINUOUS_WAVE( ); + IN865_SET_CONTINUOUS_WAVE( ); + US915_SET_CONTINUOUS_WAVE( ); + RU864_SET_CONTINUOUS_WAVE( ); + default: + { + break; + } + } +} + +uint8_t RegionApplyDrOffset( LoRaMacRegion_t region, uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) +{ + switch( region ) + { + AS923_APPLY_DR_OFFSET( ); + AU915_APPLY_DR_OFFSET( ); + CN470_APPLY_DR_OFFSET( ); + CN470ALI_APPLY_DR_OFFSET( ); + CN779_APPLY_DR_OFFSET( ); + EU433_APPLY_DR_OFFSET( ); + EU868_APPLY_DR_OFFSET( ); + KR920_APPLY_DR_OFFSET( ); + IN865_APPLY_DR_OFFSET( ); + US915_APPLY_DR_OFFSET( ); + RU864_APPLY_DR_OFFSET( ); + default: + { + return dr; + } + } +} + +void RegionRxBeaconSetup( LoRaMacRegion_t region, RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ) +{ + switch( region ) + { + AS923_RX_BEACON_SETUP( ); + AU915_RX_BEACON_SETUP( ); + CN470_RX_BEACON_SETUP( ); + CN470ALI_RX_BEACON_SETUP( ); + CN779_RX_BEACON_SETUP( ); + EU433_RX_BEACON_SETUP( ); + EU868_RX_BEACON_SETUP( ); + KR920_RX_BEACON_SETUP( ); + IN865_RX_BEACON_SETUP( ); + US915_RX_BEACON_SETUP( ); + RU864_RX_BEACON_SETUP( ); + default: + { + break; + } + } +} diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/Region.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/Region.h new file mode 100644 index 00000000..6b5fd45e --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/Region.h @@ -0,0 +1,1687 @@ +/*! + * \file Region.h + * + * \brief Region implementation. + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \defgroup REGION Region implementation + * This is the common API to access the specific + * regional implementations. + * + * Preprocessor options: + * - LoRaWAN regions can be activated by defining the related preprocessor + * definition. It is possible to define more than one region. + * The following regions are supported: + * - #define REGION_AS923 + * - #define REGION_AU915 + * - #define REGION_CN470 + * - #define REGION_CN779 + * - #define REGION_EU433 + * - #define REGION_EU868 + * - #define REGION_KR920 + * - #define REGION_IN865 + * - #define REGION_US915 + * - #define REGION_RU864 + * + * \{ + */ +#ifndef __REGION_H__ +#define __REGION_H__ + +#include +#include +#include "LoRaMac.h" +#include "timer.h" + +/*! + * Macro to compute bit of a channel index. + */ +#define LC( channelIndex ) ( uint16_t )( 1 << ( channelIndex - 1 ) ) + +/*! + * Region | SF + * ------------ | :-----: + * AS923 | SF12 - BW125 + * AU915 | SF10 - BW125 + * CN470 | SF12 - BW125 + * CN779 | SF12 - BW125 + * EU433 | SF12 - BW125 + * EU868 | SF12 - BW125 + * IN865 | SF12 - BW125 + * KR920 | SF12 - BW125 + * US915 | SF10 - BW125 + * RU864 | SF12 - BW125 + */ +#define DR_0 0 + +/*! + * Region | SF + * ------------ | :-----: + * AS923 | SF11 - BW125 + * AU915 | SF9 - BW125 + * CN470 | SF11 - BW125 + * CN779 | SF11 - BW125 + * EU433 | SF11 - BW125 + * EU868 | SF11 - BW125 + * IN865 | SF11 - BW125 + * KR920 | SF11 - BW125 + * US915 | SF9 - BW125 + * RU864 | SF11 - BW125 + */ +#define DR_1 1 + +/*! + * Region | SF + * ------------ | :-----: + * AS923 | SF10 - BW125 + * AU915 | SF8 - BW125 + * CN470 | SF10 - BW125 + * CN779 | SF10 - BW125 + * EU433 | SF10 - BW125 + * EU868 | SF10 - BW125 + * IN865 | SF10 - BW125 + * KR920 | SF10 - BW125 + * US915 | SF8 - BW125 + * RU864 | SF10 - BW125 + */ +#define DR_2 2 + +/*! + * Region | SF + * ------------ | :-----: + * AS923 | SF9 - BW125 + * AU915 | SF7 - BW125 + * CN470 | SF9 - BW125 + * CN779 | SF9 - BW125 + * EU433 | SF9 - BW125 + * EU868 | SF9 - BW125 + * IN865 | SF9 - BW125 + * KR920 | SF9 - BW125 + * US915 | SF7 - BW125 + * RU864 | SF9 - BW125 + */ +#define DR_3 3 + +/*! + * Region | SF + * ------------ | :-----: + * AS923 | SF8 - BW125 + * AU915 | SF8 - BW500 + * CN470 | SF8 - BW125 + * CN779 | SF8 - BW125 + * EU433 | SF8 - BW125 + * EU868 | SF8 - BW125 + * IN865 | SF8 - BW125 + * KR920 | SF8 - BW125 + * US915 | SF8 - BW500 + * RU864 | SF8 - BW125 + */ +#define DR_4 4 + +/*! + * Region | SF + * ------------ | :-----: + * AS923 | SF7 - BW125 + * AU915 | RFU + * CN470 | SF7 - BW125 + * CN779 | SF7 - BW125 + * EU433 | SF7 - BW125 + * EU868 | SF7 - BW125 + * IN865 | SF7 - BW125 + * KR920 | SF7 - BW125 + * US915 | RFU + * RU864 | SF7 - BW125 + */ +#define DR_5 5 + +/*! + * Region | SF + * ------------ | :-----: + * AS923 | SF7 - BW250 + * AU915 | RFU + * CN470 | SF12 - BW125 + * CN779 | SF7 - BW250 + * EU433 | SF7 - BW250 + * EU868 | SF7 - BW250 + * IN865 | SF7 - BW250 + * KR920 | RFU + * US915 | RFU + * RU864 | SF7 - BW250 + */ +#define DR_6 6 + +/*! + * Region | SF + * ------------ | :-----: + * AS923 | FSK + * AU915 | RFU + * CN470 | SF12 - BW125 + * CN779 | FSK + * EU433 | FSK + * EU868 | FSK + * IN865 | FSK + * KR920 | RFU + * US915 | RFU + * RU864 | FSK + */ +#define DR_7 7 + +/*! + * Region | SF + * ------------ | :-----: + * AS923 | RFU + * AU915 | SF12 - BW500 + * CN470 | RFU + * CN779 | RFU + * EU433 | RFU + * EU868 | RFU + * IN865 | RFU + * KR920 | RFU + * US915 | SF12 - BW500 + * RU864 | RFU + */ +#define DR_8 8 + +/*! + * Region | SF + * ------------ | :-----: + * AS923 | RFU + * AU915 | SF11 - BW500 + * CN470 | RFU + * CN779 | RFU + * EU433 | RFU + * EU868 | RFU + * IN865 | RFU + * KR920 | RFU + * US915 | SF11 - BW500 + * RU864 | RFU + */ +#define DR_9 9 + +/*! + * Region | SF + * ------------ | :-----: + * AS923 | RFU + * AU915 | SF10 - BW500 + * CN470 | RFU + * CN779 | RFU + * EU433 | RFU + * EU868 | RFU + * IN865 | RFU + * KR920 | RFU + * US915 | SF10 - BW500 + * RU864 | RFU + */ +#define DR_10 10 + +/*! + * Region | SF + * ------------ | :-----: + * AS923 | RFU + * AU915 | SF9 - BW500 + * CN470 | RFU + * CN779 | RFU + * EU433 | RFU + * EU868 | RFU + * IN865 | RFU + * KR920 | RFU + * US915 | SF9 - BW500 + * RU864 | RFU + */ +#define DR_11 11 + +/*! + * Region | SF + * ------------ | :-----: + * AS923 | RFU + * AU915 | SF8 - BW500 + * CN470 | RFU + * CN779 | RFU + * EU433 | RFU + * EU868 | RFU + * IN865 | RFU + * KR920 | RFU + * US915 | SF8 - BW500 + * RU864 | RFU + */ +#define DR_12 12 + +/*! + * Region | SF + * ------------ | :-----: + * AS923 | RFU + * AU915 | SF7 - BW500 + * CN470 | RFU + * CN779 | RFU + * EU433 | RFU + * EU868 | RFU + * IN865 | RFU + * KR920 | RFU + * US915 | SF7 - BW500 + * RU864 | RFU + */ +#define DR_13 13 + +/*! + * Region | SF + * ------------ | :-----: + * AS923 | RFU + * AU915 | RFU + * CN470 | RFU + * CN779 | RFU + * EU433 | RFU + * EU868 | RFU + * IN865 | RFU + * KR920 | RFU + * US915 | RFU + * RU864 | RFU + */ +#define DR_14 14 + +/*! + * Region | SF + * ------------ | :-----: + * AS923 | RFU + * AU915 | RFU + * CN470 | RFU + * CN779 | RFU + * EU433 | RFU + * EU868 | RFU + * IN865 | RFU + * KR920 | RFU + * US915 | RFU + * RU864 | RFU + */ +#define DR_15 15 + + + +/*! + * Region | dBM + * ------------ | :-----: + * AS923 | Max EIRP + * AU915 | Max EIRP + * CN470 | Max EIRP + * CN779 | Max EIRP + * EU433 | Max EIRP + * EU868 | Max EIRP + * IN865 | Max EIRP + * KR920 | Max EIRP + * US915 | Max ERP + * RU864 | Max EIRP + */ +#define TX_POWER_0 0 + +/*! + * Region | dBM + * ------------ | :-----: + * AS923 | Max EIRP - 2 + * AU915 | Max EIRP - 2 + * CN470 | Max EIRP - 2 + * CN779 | Max EIRP - 2 + * EU433 | Max EIRP - 2 + * EU868 | Max EIRP - 2 + * IN865 | Max EIRP - 2 + * KR920 | Max EIRP - 2 + * US915 | Max ERP - 2 + * RU864 | Max EIRP - 2 + */ +#define TX_POWER_1 1 + +/*! + * Region | dBM + * ------------ | :-----: + * AS923 | Max EIRP - 4 + * AU915 | Max EIRP - 4 + * CN470 | Max EIRP - 4 + * CN779 | Max EIRP - 4 + * EU433 | Max EIRP - 4 + * EU868 | Max EIRP - 4 + * IN865 | Max EIRP - 4 + * KR920 | Max EIRP - 4 + * US915 | Max ERP - 4 + * RU864 | Max EIRP - 4 + */ +#define TX_POWER_2 2 + +/*! + * Region | dBM + * ------------ | :-----: + * AS923 | Max EIRP - 6 + * AU915 | Max EIRP - 6 + * CN470 | Max EIRP - 6 + * CN779 | Max EIRP - 6 + * EU433 | Max EIRP - 6 + * EU868 | Max EIRP - 6 + * IN865 | Max EIRP - 6 + * KR920 | Max EIRP - 6 + * US915 | Max ERP - 6 + * RU864 | Max EIRP - 6 + */ +#define TX_POWER_3 3 + +/*! + * Region | dBM + * ------------ | :-----: + * AS923 | Max EIRP - 8 + * AU915 | Max EIRP - 8 + * CN470 | Max EIRP - 8 + * CN779 | Max EIRP - 8 + * EU433 | Max EIRP - 8 + * EU868 | Max EIRP - 8 + * IN865 | Max EIRP - 8 + * KR920 | Max EIRP - 8 + * US915 | Max ERP - 8 + * RU864 | Max EIRP - 8 + */ +#define TX_POWER_4 4 + +/*! + * Region | dBM + * ------------ | :-----: + * AS923 | Max EIRP - 10 + * AU915 | Max EIRP - 10 + * CN470 | Max EIRP - 10 + * CN779 | Max EIRP - 10 + * EU433 | Max EIRP - 10 + * EU868 | Max EIRP - 10 + * IN865 | Max EIRP - 10 + * KR920 | Max EIRP - 10 + * US915 | Max ERP - 10 + * RU864 | Max EIRP - 10 + */ +#define TX_POWER_5 5 + +/*! + * Region | dBM + * ------------ | :-----: + * AS923 | Max EIRP - 12 + * AU915 | Max EIRP - 12 + * CN470 | Max EIRP - 12 + * CN779 | - + * EU433 | - + * EU868 | Max EIRP - 12 + * IN865 | Max EIRP - 12 + * KR920 | Max EIRP - 12 + * US915 | Max ERP - 12 + * RU864 | Max EIRP - 12 + */ +#define TX_POWER_6 6 + +/*! + * Region | dBM + * ------------ | :-----: + * AS923 | Max EIRP - 14 + * AU915 | Max EIRP - 14 + * CN470 | Max EIRP - 14 + * CN779 | - + * EU433 | - + * EU868 | Max EIRP - 14 + * IN865 | Max EIRP - 14 + * KR920 | Max EIRP - 14 + * US915 | Max ERP - 14 + * RU864 | Max EIRP - 14 + */ +#define TX_POWER_7 7 + +/*! + * Region | dBM + * ------------ | :-----: + * AS923 | - + * AU915 | Max EIRP - 16 + * CN470 | - + * CN779 | - + * EU433 | - + * EU868 | - + * IN865 | Max EIRP - 16 + * KR920 | - + * US915 | Max ERP - 16 + * RU864 | - + */ +#define TX_POWER_8 8 + +/*! + * Region | dBM + * ------------ | :-----: + * AS923 | - + * AU915 | Max EIRP - 18 + * CN470 | - + * CN779 | - + * EU433 | - + * EU868 | - + * IN865 | Max EIRP - 18 + * KR920 | - + * US915 | Max ERP - 18 + * RU864 | - + */ +#define TX_POWER_9 9 + +/*! + * Region | dBM + * ------------ | :-----: + * AS923 | - + * AU915 | Max EIRP - 20 + * CN470 | - + * CN779 | - + * EU433 | - + * EU868 | - + * IN865 | Max EIRP - 20 + * KR920 | - + * US915 | Max ERP - 20 + * RU864 | - + */ +#define TX_POWER_10 10 + +/*! + * Region | dBM + * ------------ | :-----: + * AS923 | - + * AU915 | Max EIRP - 22 + * CN470 | - + * CN779 | - + * EU433 | - + * EU868 | - + * IN865 | - + * KR920 | - + * US915 | Max ERP - 22 + * RU864 | - + */ +#define TX_POWER_11 11 + +/*! + * Region | dBM + * ------------ | :-----: + * AS923 | - + * AU915 | Max EIRP - 24 + * CN470 | - + * CN779 | - + * EU433 | - + * EU868 | - + * IN865 | - + * KR920 | - + * US915 | Max ERP - 24 + * RU864 | - + */ +#define TX_POWER_12 12 + +/*! + * Region | dBM + * ------------ | :-----: + * AS923 | - + * AU915 | Max EIRP - 26 + * CN470 | - + * CN779 | - + * EU433 | - + * EU868 | - + * IN865 | - + * KR920 | - + * US915 | Max ERP - 26 + * RU864 | - + */ +#define TX_POWER_13 13 + +/*! + * Region | dBM + * ------------ | :-----: + * AS923 | - + * AU915 | Max EIRP - 28 + * CN470 | - + * CN779 | - + * EU433 | - + * EU868 | - + * IN865 | - + * KR920 | - + * US915 | Max ERP - 28 + * RU864 | - + */ +#define TX_POWER_14 14 + +/*! + * RFU + */ +#define TX_POWER_15 15 + + + +/*! + * Enumeration of phy attributes. + */ +typedef enum ePhyAttribute +{ + /*! + * Frequency. It is available + * to perform a verification with RegionVerify(). + */ + PHY_FREQUENCY, + /*! + * Minimum RX datarate. + */ + PHY_MIN_RX_DR, + /*! + * Minimum TX datarate. + */ + PHY_MIN_TX_DR, + /*! + * Maximum RX datarate. + */ + PHY_MAX_RX_DR, + /*! + * Maximum TX datarate. + */ + PHY_MAX_TX_DR, + /*! + * TX datarate. + * This is a parameter which can't be queried. It is available + * to perform a verification with RegionVerify(). + */ + PHY_TX_DR, + /*! + * Default TX datarate. + */ + PHY_DEF_TX_DR, + /*! + * RX datarate. It is available + * to perform a verification with RegionVerify(). + */ + PHY_RX_DR, + /*! + * Maximum TX power. + */ + PHY_MAX_TX_POWER, + /*! + * TX power. It is available + * to perform a verification with RegionVerify(). + */ + PHY_TX_POWER, + /*! + * Default TX power. + */ + PHY_DEF_TX_POWER, + /*! + * Default ADR_ACK_LIMIT value. + */ + PHY_DEF_ADR_ACK_LIMIT, + /*! + * Default ADR_ACK_DELAY value. + */ + PHY_DEF_ADR_ACK_DELAY, + /*! + * Maximum payload possible. + */ + PHY_MAX_PAYLOAD, + /*! + * Maximum payload possible when repeater support is enabled. + */ + PHY_MAX_PAYLOAD_REPEATER, + /*! + * Duty cycle. + */ + PHY_DUTY_CYCLE, + /*! + * Maximum receive window duration. + */ + PHY_MAX_RX_WINDOW, + /*! + * Receive delay for window 1. + */ + PHY_RECEIVE_DELAY1, + /*! + * Receive delay for window 2. + */ + PHY_RECEIVE_DELAY2, + /*! + * Join accept delay for window 1. + */ + PHY_JOIN_ACCEPT_DELAY1, + /*! + * Join accept delay for window 2. + */ + PHY_JOIN_ACCEPT_DELAY2, + /*! + * Maximum frame counter gap. + */ + PHY_MAX_FCNT_GAP, + /*! + * Acknowledgement time out. + */ + PHY_ACK_TIMEOUT, + /*! + * Default datarate offset for window 1. + */ + PHY_DEF_DR1_OFFSET, + /*! + * Default receive window 2 frequency. + */ + PHY_DEF_RX2_FREQUENCY, + /*! + * Default receive window 2 datarate. + */ + PHY_DEF_RX2_DR, + /*! + * Channels mask. + */ + PHY_CHANNELS_MASK, + /*! + * Channels default mask. + */ + PHY_CHANNELS_DEFAULT_MASK, + /*! + * Maximum number of supported channels + */ + PHY_MAX_NB_CHANNELS, + /*! + * Channels. + */ + PHY_CHANNELS, + /*! + * Default value of the uplink dwell time. + */ + PHY_DEF_UPLINK_DWELL_TIME, + /*! + * Default value of the downlink dwell time. + */ + PHY_DEF_DOWNLINK_DWELL_TIME, + /*! + * Default value of the MaxEIRP. + */ + PHY_DEF_MAX_EIRP, + /*! + * Default value of the antenna gain. + */ + PHY_DEF_ANTENNA_GAIN, + /*! + * Next lower datarate. + */ + PHY_NEXT_LOWER_TX_DR, + /*! + * Beacon interval in ms. + */ + PHY_BEACON_INTERVAL, + /*! + * Beacon reserved time in ms. + */ + PHY_BEACON_RESERVED, + /*! + * Beacon guard time in ms. + */ + PHY_BEACON_GUARD, + /*! + * Beacon window time in ms. + */ + PHY_BEACON_WINDOW, + /*! + * Beacon window time in numer of slots. + */ + PHY_BEACON_WINDOW_SLOTS, + /*! + * Ping slot length time in ms. + */ + PHY_PING_SLOT_WINDOW, + /*! + * Default symbol timeout for beacons and ping slot windows. + */ + PHY_BEACON_SYMBOL_TO_DEFAULT, + /*! + * Maximum symbol timeout for beacons. + */ + PHY_BEACON_SYMBOL_TO_EXPANSION_MAX, + /*! + * Maximum symbol timeout for ping slots. + */ + PHY_PING_SLOT_SYMBOL_TO_EXPANSION_MAX, + /*! + * Symbol expansion value for beacon windows in case of beacon + * loss in symbols. + */ + PHY_BEACON_SYMBOL_TO_EXPANSION_FACTOR, + /*! + * Symbol expansion value for ping slot windows in case of beacon + * loss in symbols. + */ + PHY_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR, + /*! + * Maximum allowed beacon less time in ms. + */ + PHY_MAX_BEACON_LESS_PERIOD, + /*! + * Delay time for the BeaconTimingAns in ms. + */ + PHY_BEACON_DELAY_BEACON_TIMING_ANS, + /*! + * Beacon channel frequency. + */ + PHY_BEACON_CHANNEL_FREQ, + /*! + * The format of the beacon. + */ + PHY_BEACON_FORMAT, + /*! + * The beacon channel datarate. + */ + PHY_BEACON_CHANNEL_DR, + /*! + * The frequency stepwidth between the beacon channels. + */ + PHY_BEACON_CHANNEL_STEPWIDTH, + /*! + * The number of channels for the beacon reception. + */ + PHY_BEACON_NB_CHANNELS, + /*! + * The datarate of a ping slot channel. + */ + PHY_PING_SLOT_CHANNEL_DR +}PhyAttribute_t; + +/*! + * Enumeration of initialization types. + */ +typedef enum eInitType +{ + /*! + * Initializes the region specific data to defaults, according to the + * LoRaWAN specification. + */ + INIT_TYPE_INIT, + /*! + * Restores default channels defined by the LoRaWAN specification only. + */ + INIT_TYPE_RESTORE_DEFAULT_CHANNELS, + /*! + * Restores internal context from passed pointer. + */ + INIT_TYPE_RESTORE_CTX +}InitType_t; + +typedef enum eChannelsMask +{ + /*! + * The channels mask. + */ + CHANNELS_MASK, + /*! + * The channels default mask. + */ + CHANNELS_DEFAULT_MASK +}ChannelsMask_t; + +/*! + * Structure containing the beacon format + */ +typedef struct sBeaconFormat +{ + /*! + * Size of the beacon + */ + uint8_t BeaconSize; + /*! + * Size of the RFU 1 data field + */ + uint8_t Rfu1Size; + /*! + * Size of the RFU 2 data field + */ + uint8_t Rfu2Size; +}BeaconFormat_t; + +/*! + * Union for the structure uGetPhyParams + */ +typedef union uPhyParam +{ + /*! + * A parameter value. + */ + uint32_t Value; + /*! + * A floating point value. + */ + float fValue; + /*! + * Pointer to the channels mask. + */ + uint16_t* ChannelsMask; + /*! + * Pointer to the channels. + */ + ChannelParams_t* Channels; + /*! + * Beacon format + */ + BeaconFormat_t BeaconFormat; +}PhyParam_t; + +/*! + * Parameter structure for the function RegionGetPhyParam. + */ +typedef struct sGetPhyParams +{ + /*! + * Setup the parameter to get. + */ + PhyAttribute_t Attribute; + /*! + * Datarate. + * The parameter is needed for the following queries: + * PHY_MAX_PAYLOAD, PHY_MAX_PAYLOAD_REPEATER, PHY_NEXT_LOWER_TX_DR. + */ + int8_t Datarate; + /*! + * Uplink dwell time. This parameter must be set to query: + * PHY_MAX_PAYLOAD, PHY_MAX_PAYLOAD_REPEATER, PHY_MIN_TX_DR. + * The parameter is needed for the following queries: + * PHY_MIN_TX_DR, PHY_MAX_PAYLOAD, PHY_MAX_PAYLOAD_REPEATER, PHY_NEXT_LOWER_TX_DR. + */ + uint8_t UplinkDwellTime; + /*! + * Downlink dwell time. This parameter must be set to query: + * PHY_MAX_PAYLOAD, PHY_MAX_PAYLOAD_REPEATER, PHY_MIN_RX_DR. + * The parameter is needed for the following queries: + * PHY_MIN_RX_DR, PHY_MAX_PAYLOAD, PHY_MAX_PAYLOAD_REPEATER. + */ + uint8_t DownlinkDwellTime; +}GetPhyParams_t; + +/*! + * Parameter structure for the function RegionSetBandTxDone. + */ +typedef struct sSetBandTxDoneParams +{ + /*! + * Channel to update. + */ + uint8_t Channel; + /*! + * Joined Set to true, if the node has joined the network + */ + bool Joined; + /*! + * Last TX done time. + */ + TimerTime_t LastTxDoneTime; +}SetBandTxDoneParams_t; + +/*! + * Parameter structure for the function RegionInitDefaults. + */ +typedef struct sInitDefaultsParams +{ + /*! + * Pointer to region module context to be restored. + */ + void* NvmCtx; + /*! + * Sets the initialization type. + */ + InitType_t Type; +}InitDefaultsParams_t; + +/*! + * Parameter structure for the function RegionGetNvmCtx. + */ +typedef struct sGetNvmCtxParams +{ + /*! + * Size of module context. + */ + size_t nvmCtxSize; +}GetNvmCtxParams_t; + + +/*! + * Parameter structure for the function RegionVerify. + */ +typedef union uVerifyParams +{ + /*! + * Channel frequency to verify + */ + uint32_t Frequency; + /*! + * TX power to verify. + */ + int8_t TxPower; + /*! + * Set to true, if the duty cycle is enabled, otherwise false. + */ + bool DutyCycle; + /*! + * Datarate to verify. + */ + struct sDatarateParams + { + /*! + * Datarate to verify. + */ + int8_t Datarate; + /*! + * The downlink dwell time. + */ + uint8_t DownlinkDwellTime; + /*! + * The up link dwell time. + */ + uint8_t UplinkDwellTime; + }DatarateParams; +}VerifyParams_t; + +/*! + * Parameter structure for the function RegionApplyCFList. + */ +typedef struct sApplyCFListParams +{ + /*! + * Payload which contains the CF list. + */ + uint8_t* Payload; + /*! + * Size of the payload. + */ + uint8_t Size; +}ApplyCFListParams_t; + +/*! + * Parameter structure for the function RegionChanMaskSet. + */ +typedef struct sChanMaskSetParams +{ + /*! + * Pointer to the channels mask which should be set. + */ + uint16_t* ChannelsMaskIn; + /*! + * Pointer to the channels mask which should be set. + */ + ChannelsMask_t ChannelsMaskType; +}ChanMaskSetParams_t; + +/*! + * Parameter structure for the function RegionRxConfig. + */ +typedef struct sRxConfigParams +{ + /*! + * The RX channel. + */ + uint8_t Channel; + /*! + * RX datarate. + */ + int8_t Datarate; + /*! + * RX bandwidth. + */ + uint8_t Bandwidth; + /*! + * RX datarate offset. + */ + int8_t DrOffset; + /*! + * RX frequency. + */ + uint32_t Frequency; + /*! + * RX window timeout + */ + uint32_t WindowTimeout; + /*! + * RX window offset + */ + int32_t WindowOffset; + /*! + * Downlink dwell time. + */ + uint8_t DownlinkDwellTime; + /*! + * Set to true, if a repeater is supported. + */ + bool RepeaterSupport; + /*! + * Set to true, if RX should be continuous. + */ + bool RxContinuous; + /*! + * Sets the RX window. + */ + LoRaMacRxSlot_t RxSlot; +}RxConfigParams_t; + +/*! + * Parameter structure for the function RegionTxConfig. + */ +typedef struct sTxConfigParams +{ + /*! + * The TX channel. + */ + uint8_t Channel; + /*! + * The TX datarate. + */ + int8_t Datarate; + /*! + * The TX power. + */ + int8_t TxPower; + /*! + * The Max EIRP, if applicable. + */ + float MaxEirp; + /*! + * The antenna gain, if applicable. + */ + float AntennaGain; + /*! + * Frame length to setup. + */ + uint16_t PktLen; +}TxConfigParams_t; + +/*! + * Parameter structure for the function RegionLinkAdrReq. + */ +typedef struct sLinkAdrReqParams +{ + /*! + * Current LoRaWAN Version + */ + Version_t Version; + /*! + * Pointer to the payload which contains the MAC commands. + */ + uint8_t* Payload; + /*! + * Size of the payload. + */ + uint8_t PayloadSize; + /*! + * Uplink dwell time. + */ + uint8_t UplinkDwellTime; + /*! + * Set to true, if ADR is enabled. + */ + bool AdrEnabled; + /*! + * The current datarate. + */ + int8_t CurrentDatarate; + /*! + * The current TX power. + */ + int8_t CurrentTxPower; + /*! + * The current number of repetitions. + */ + uint8_t CurrentNbRep; +}LinkAdrReqParams_t; + +/*! + * Parameter structure for the function RegionRxParamSetupReq. + */ +typedef struct sRxParamSetupReqParams +{ + /*! + * The datarate to setup. + */ + int8_t Datarate; + /*! + * Datarate offset. + */ + int8_t DrOffset; + /*! + * The frequency to setup. + */ + uint32_t Frequency; +}RxParamSetupReqParams_t; + +/*! + * Parameter structure for the function RegionNewChannelReq. + */ +typedef struct sNewChannelReqParams +{ + /*! + * Pointer to the new channels. + */ + ChannelParams_t* NewChannel; + /*! + * Channel id. + */ + int8_t ChannelId; +}NewChannelReqParams_t; + +/*! + * Parameter structure for the function RegionTxParamSetupReq. + */ +typedef struct sTxParamSetupReqParams +{ + /*! + * Uplink dwell time. + */ + uint8_t UplinkDwellTime; + /*! + * Downlink dwell time. + */ + uint8_t DownlinkDwellTime; + /*! + * Max EIRP. + */ + uint8_t MaxEirp; +}TxParamSetupReqParams_t; + +/*! + * Parameter structure for the function RegionDlChannelReq. + */ +typedef struct sDlChannelReqParams +{ + /*! + * Channel Id to add the frequency. + */ + uint8_t ChannelId; + /*! + * Alternative frequency for the Rx1 window. + */ + uint32_t Rx1Frequency; +}DlChannelReqParams_t; + +/*! + * Enumeration of alternation type + */ +typedef enum eAlternateDrType +{ + /*! + * Type to use for an alternation + */ + ALTERNATE_DR, + /*! + * Type to use to restore one alternation + */ + ALTERNATE_DR_RESTORE +}AlternateDrType_t; + +/*! + * Parameter structure for the function RegionCalcBackOff. + */ +typedef struct sCalcBackOffParams +{ + /*! + * Set to true, if the node has already joined a network, otherwise false. + */ + bool Joined; + /*! + * Joined Set to true, if the last uplink was a join request + */ + bool LastTxIsJoinRequest; + /*! + * Set to true, if the duty cycle is enabled, otherwise false. + */ + bool DutyCycleEnabled; + /*! + * Current channel index. + */ + uint8_t Channel; + /*! + * Elapsed time since the start of the node. + */ + TimerTime_t ElapsedTime; + /*! + * Time-on-air of the last transmission. + */ + TimerTime_t TxTimeOnAir; +}CalcBackOffParams_t; + +/*! + * Parameter structure for the function RegionNextChannel. + */ +typedef struct sNextChanParams +{ + /*! + * Aggregated time-off time. + */ + TimerTime_t AggrTimeOff; + /*! + * Time of the last aggregated TX. + */ + TimerTime_t LastAggrTx; + /*! + * Current datarate. + */ + int8_t Datarate; + /*! + * Set to true, if the node has already joined a network, otherwise false. + */ + bool Joined; + /*! + * Set to true, if the duty cycle is enabled, otherwise false. + */ + bool DutyCycleEnabled; +}NextChanParams_t; + +/*! + * Parameter structure for the function RegionChannelsAdd. + */ +typedef struct sChannelAddParams +{ + /*! + * Pointer to the new channel to add. + */ + ChannelParams_t* NewChannel; + /*! + * Channel id to add. + */ + uint8_t ChannelId; +}ChannelAddParams_t; + +/*! + * Parameter structure for the function RegionChannelsRemove. + */ +typedef struct sChannelRemoveParams +{ + /*! + * Channel id to remove. + */ + uint8_t ChannelId; +}ChannelRemoveParams_t; + +/*! + * Parameter structure for the function RegionContinuousWave. + */ +typedef struct sContinuousWaveParams +{ + /*! + * Current channel index. + */ + uint8_t Channel; + /*! + * Datarate. Used to limit the TX power. + */ + int8_t Datarate; + /*! + * The TX power to setup. + */ + int8_t TxPower; + /*! + * Max EIRP, if applicable. + */ + float MaxEirp; + /*! + * The antenna gain, if applicable. + */ + float AntennaGain; + /*! + * Specifies the time the radio will stay in CW mode. + */ + uint16_t Timeout; +}ContinuousWaveParams_t; + +/*! + * Parameter structure for the function RegionRxBeaconSetup + */ +typedef struct sRxBeaconSetupParams +{ + /*! + * Symbol timeout. + */ + uint16_t SymbolTimeout; + /*! + * Receive time. + */ + uint32_t RxTime; + /*! + * The frequency to setup. + */ + uint32_t Frequency; +}RxBeaconSetup_t; + + + +/*! + * \brief The function verifies if a region is active or not. If a region + * is not active, it cannot be used. + * + * \param [IN] region LoRaWAN region. + * + * \retval Return true, if the region is supported. + */ +bool RegionIsActive( LoRaMacRegion_t region ); + +/*! + * \brief The function gets a value of a specific phy attribute. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] getPhy Pointer to the function parameters. + * + * \retval Returns a structure containing the PHY parameter. + */ +PhyParam_t RegionGetPhyParam( LoRaMacRegion_t region, GetPhyParams_t* getPhy ); + +/*! + * \brief Updates the last TX done parameters of the current channel. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] txDone Pointer to the function parameters. + */ +void RegionSetBandTxDone( LoRaMacRegion_t region, SetBandTxDoneParams_t* txDone ); + +/*! + * \brief Initializes the channels masks and the channels. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] params Pointer to the function parameters. + */ +void RegionInitDefaults( LoRaMacRegion_t region, InitDefaultsParams_t* params ); + +/*! + * \brief Returns a pointer to the internal context and its size. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] params Pointer to the function parameters. + * + * \retval Points to a structure where the module store its non-volatile context. + */ +void* RegionGetNvmCtx( LoRaMacRegion_t region, GetNvmCtxParams_t* params ); + +/*! + * \brief Verifies a parameter. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] verify Pointer to the function parameters. + * + * \param [IN] type Sets the initialization type. + * + * \retval Returns true, if the parameter is valid. + */ +bool RegionVerify( LoRaMacRegion_t region, VerifyParams_t* verify, PhyAttribute_t phyAttribute ); + +/*! + * \brief The function parses the input buffer and sets up the channels of the + * CF list. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] applyCFList Pointer to the function parameters. + */ +void RegionApplyCFList( LoRaMacRegion_t region, ApplyCFListParams_t* applyCFList ); + +/*! + * \brief Sets a channels mask. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] chanMaskSet Pointer to the function parameters. + * + * \retval Returns true, if the channels mask could be set. + */ +bool RegionChanMaskSet( LoRaMacRegion_t region, ChanMaskSetParams_t* chanMaskSet ); + +/*! + * \brief Configuration of the RX windows. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] rxConfig Pointer to the function parameters. + * + * \param [OUT] datarate The datarate index which was set. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionRxConfig( LoRaMacRegion_t region, RxConfigParams_t* rxConfig, int8_t* datarate ); + +/* + * Rx window precise timing + * + * For more details please consult the following document, chapter 3.1.2. + * https://www.semtech.com/uploads/documents/SX1272_settings_for_LoRaWAN_v2.0.pdf + * or + * https://www.semtech.com/uploads/documents/SX1276_settings_for_LoRaWAN_v2.0.pdf + * + * Downlink start: T = Tx + 1s (+/- 20 us) + * | + * TRxEarly | TRxLate + * | | | + * | | +---+---+---+---+---+---+---+---+ + * | | | Latest Rx window | + * | | +---+---+---+---+---+---+---+---+ + * | | | + * +---+---+---+---+---+---+---+---+ + * | Earliest Rx window | + * +---+---+---+---+---+---+---+---+ + * | + * +---+---+---+---+---+---+---+---+ + *Downlink preamble 8 symbols | | | | | | | | | + * +---+---+---+---+---+---+---+---+ + * + * Worst case Rx window timings + * + * TRxLate = DEFAULT_MIN_RX_SYMBOLS * tSymbol - RADIO_WAKEUP_TIME + * TRxEarly = 8 - DEFAULT_MIN_RX_SYMBOLS * tSymbol - RxWindowTimeout - RADIO_WAKEUP_TIME + * + * TRxLate - TRxEarly = 2 * DEFAULT_SYSTEM_MAX_RX_ERROR + * + * RxOffset = ( TRxLate + TRxEarly ) / 2 + * + * RxWindowTimeout = ( 2 * DEFAULT_MIN_RX_SYMBOLS - 8 ) * tSymbol + 2 * DEFAULT_SYSTEM_MAX_RX_ERROR + * RxOffset = 4 * tSymbol - RxWindowTimeout / 2 - RADIO_WAKE_UP_TIME + * + * Minimal value of RxWindowTimeout must be 5 symbols which implies that the system always tolerates at least an error of 1.5 * tSymbol + */ +/*! + * Computes the Rx window timeout and offset. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] datarate Rx window datarate index to be used + * + * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame. + * + * \param [IN] rxError System maximum timing error of the receiver. In milliseconds + * The receiver will turn on in a [-rxError : +rxError] ms + * interval around RxOffset + * + * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields. + */ +void RegionComputeRxWindowParameters( LoRaMacRegion_t region, int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ); + +/*! + * \brief TX configuration. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] txConfig Pointer to the function parameters. + * + * \param [OUT] txPower The tx power index which was set. + * + * \param [OUT] txTimeOnAir The time-on-air of the frame. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionTxConfig( LoRaMacRegion_t region, TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ); + +/*! + * \brief The function processes a Link ADR Request. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] linkAdrReq Pointer to the function parameters. + * + * \param [OUT] drOut The datarate which was applied. + * + * \param [OUT] txPowOut The TX power which was applied. + * + * \param [OUT] nbRepOut The number of repetitions to apply. + * + * \param [OUT] nbBytesParsed The number bytes which were parsed. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionLinkAdrReq( LoRaMacRegion_t region, LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ); + +/*! + * \brief The function processes a RX Parameter Setup Request. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] rxParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionRxParamSetupReq( LoRaMacRegion_t region, RxParamSetupReqParams_t* rxParamSetupReq ); + +/*! + * \brief The function processes a New Channel Request. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] newChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionNewChannelReq( LoRaMacRegion_t region, NewChannelReqParams_t* newChannelReq ); + +/*! + * \brief The function processes a TX ParamSetup Request. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] txParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + * Returns -1, if the functionality is not implemented. In this case, the end node + * shall ignore the command. + */ +int8_t RegionTxParamSetupReq( LoRaMacRegion_t region, TxParamSetupReqParams_t* txParamSetupReq ); + +/*! + * \brief The function processes a DlChannel Request. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] dlChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionDlChannelReq( LoRaMacRegion_t region, DlChannelReqParams_t* dlChannelReq ); + +/*! + * \brief Alternates the datarate of the channel for the join request. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] currentDr Current datarate. + * + * \param [IN] type Alternation type. + * + * \retval Datarate to apply. + */ +int8_t RegionAlternateDr( LoRaMacRegion_t region, int8_t currentDr, AlternateDrType_t type ); + +/*! + * \brief Calculates the back-off time. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] calcBackOff Pointer to the function parameters. + */ +void RegionCalcBackOff( LoRaMacRegion_t region, CalcBackOffParams_t* calcBackOff ); + +/*! + * \brief Searches and set the next random available channel + * + * \param [IN] region LoRaWAN region. + * + * \param [OUT] channel Next channel to use for TX. + * + * \param [OUT] time Time to wait for the next transmission according to the duty + * cycle. + * + * \param [OUT] aggregatedTimeOff Updates the aggregated time off. + * + * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate]. + */ +LoRaMacStatus_t RegionNextChannel( LoRaMacRegion_t region, NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ); + +/*! + * \brief Adds a channel. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] channelAdd Pointer to the function parameters. + * + * \retval Status of the operation. + */ +LoRaMacStatus_t RegionChannelAdd( LoRaMacRegion_t region, ChannelAddParams_t* channelAdd ); + +/*! + * \brief Removes a channel. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] channelRemove Pointer to the function parameters. + * + * \retval Returns true, if the channel was removed successfully. + */ +bool RegionChannelsRemove( LoRaMacRegion_t region, ChannelRemoveParams_t* channelRemove ); + +/*! + * \brief Sets the radio into continuous wave mode. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] continuousWave Pointer to the function parameters. + */ +void RegionSetContinuousWave( LoRaMacRegion_t region, ContinuousWaveParams_t* continuousWave ); + +/*! + * \brief Computes new datarate according to the given offset + * + * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms + * + * \param [IN] dr Current datarate + * + * \param [IN] drOffset Offset to be applied + * + * \retval newDr Computed datarate. + */ +uint8_t RegionApplyDrOffset( LoRaMacRegion_t region, uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ); + +/*! + * \brief Sets the radio into beacon reception mode + * + * \param [IN] rxBeaconSetup Pointer to the function parameters + * + * \param [out] outDr Datarate used to receive the beacon + */ +void RegionRxBeaconSetup( LoRaMacRegion_t region, RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ); + +/*! \} defgroup REGION */ + +#endif // __REGION_H__ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionAS923.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionAS923.c new file mode 100644 index 00000000..9ee6122b --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionAS923.c @@ -0,0 +1,1110 @@ +/*! + * \file RegionAS923.c + * + * \brief Region implementation for AS923 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) +*/ +#include "utilities.h" + +#include "RegionCommon.h" +#include "RegionAS923.h" +#include "lorawan_conf.h" +#include "mw_log_conf.h" + +// Definitions +#define CHANNELS_MASK_SIZE 1 + +/*! + * Region specific context + */ +typedef struct sRegionAS923NvmCtx +{ + /*! + * LoRaMAC channels + */ + ChannelParams_t Channels[ AS923_MAX_NB_CHANNELS ]; + /*! + * LoRaMac bands + */ + Band_t Bands[ AS923_MAX_NB_BANDS ]; + /*! + * LoRaMac channels mask + */ + uint16_t ChannelsMask[ CHANNELS_MASK_SIZE ]; + /*! + * LoRaMac channels default mask + */ + uint16_t ChannelsDefaultMask[ CHANNELS_MASK_SIZE ]; +}RegionAS923NvmCtx_t; + +/* + * Non-volatile module context. + */ +static RegionAS923NvmCtx_t NvmCtx; + +// Static functions +static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr ) +{ + uint8_t nextLowerDr = 0; + + if( dr == minDr ) + { + nextLowerDr = minDr; + } + else + { + nextLowerDr = dr - 1; + } + return nextLowerDr; +} + +static uint32_t GetBandwidth( uint32_t drIndex ) +{ + switch( BandwidthsAS923[drIndex] ) + { + default: + case 125000: + return 0; + case 250000: + return 1; + case 500000: + return 2; + } +} + +static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask ) +{ + int8_t txPowerResult = txPower; + + // Limit tx power to the band max + txPowerResult = MAX( txPower, maxBandTxPower ); + + return txPowerResult; +} + +static bool VerifyRfFreq( uint32_t freq ) +{ + // Check radio driver support + if( Radio.CheckRfFrequency( freq ) == false ) + { + return false; + } + + if( ( freq < 915000000 ) || ( freq > 928000000 ) ) + { + return false; + } + return true; +} + +static uint8_t CountNbOfEnabledChannels( bool joined, uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t delayTransmission = 0; + + for( uint8_t i = 0, k = 0; i < AS923_MAX_NB_CHANNELS; i += 16, k++ ) + { + for( uint8_t j = 0; j < 16; j++ ) + { + if( ( channelsMask[k] & ( 1 << j ) ) != 0 ) + { + if( channels[i + j].Frequency == 0 ) + { // Check if the channel is enabled + continue; + } + if( joined == false ) + { + if( ( AS923_JOIN_CHANNELS & ( 1 << j ) ) == 0 ) + { + continue; + } + } + if( RegionCommonValueInRange( datarate, channels[i + j].DrRange.Fields.Min, + channels[i + j].DrRange.Fields.Max ) == false ) + { // Check if the current channel selection supports the given datarate + continue; + } + if( bands[channels[i + j].Band].TimeOff > 0 ) + { // Check if the band is available for transmission + delayTransmission++; + continue; + } + enabledChannels[nbEnabledChannels++] = i + j; + } + } + } + + *delayTx = delayTransmission; + return nbEnabledChannels; +} + +PhyParam_t RegionAS923GetPhyParam( GetPhyParams_t* getPhy ) +{ + PhyParam_t phyParam = { 0 }; + + switch( getPhy->Attribute ) + { + case PHY_MIN_RX_DR: + { + if( getPhy->DownlinkDwellTime == 0 ) + { + phyParam.Value = AS923_RX_MIN_DATARATE; + } + else + { + phyParam.Value = AS923_DWELL_LIMIT_DATARATE; + } + break; + } + case PHY_MIN_TX_DR: + { + if( getPhy->UplinkDwellTime == 0 ) + { + phyParam.Value = AS923_TX_MIN_DATARATE; + } + else + { + phyParam.Value = AS923_DWELL_LIMIT_DATARATE; + } + break; + } + case PHY_DEF_TX_DR: + { + phyParam.Value = AS923_DEFAULT_DATARATE; + break; + } + case PHY_NEXT_LOWER_TX_DR: + { + if( getPhy->UplinkDwellTime == 0 ) + { + phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, AS923_TX_MIN_DATARATE ); + } + else + { + phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, AS923_DWELL_LIMIT_DATARATE ); + } + break; + } + case PHY_MAX_TX_POWER: + { + phyParam.Value = AS923_MAX_TX_POWER; + break; + } + case PHY_DEF_TX_POWER: + { + phyParam.Value = AS923_DEFAULT_TX_POWER; + break; + } + case PHY_DEF_ADR_ACK_LIMIT: + { + phyParam.Value = AS923_ADR_ACK_LIMIT; + break; + } + case PHY_DEF_ADR_ACK_DELAY: + { + phyParam.Value = AS923_ADR_ACK_DELAY; + break; + } + case PHY_MAX_PAYLOAD: + { + if( getPhy->UplinkDwellTime == 0 ) + { + phyParam.Value = MaxPayloadOfDatarateDwell0AS923[getPhy->Datarate]; + } + else + { + phyParam.Value = MaxPayloadOfDatarateDwell1UpAS923[getPhy->Datarate]; + } + break; + } + case PHY_MAX_PAYLOAD_REPEATER: + { + if( getPhy->UplinkDwellTime == 0 ) + { + phyParam.Value = MaxPayloadOfDatarateRepeaterDwell0AS923[getPhy->Datarate]; + } + else + { + phyParam.Value = MaxPayloadOfDatarateDwell1UpAS923[getPhy->Datarate]; + } + break; + } + case PHY_DUTY_CYCLE: + { + phyParam.Value = AS923_DUTY_CYCLE_ENABLED; + break; + } + case PHY_MAX_RX_WINDOW: + { + phyParam.Value = AS923_MAX_RX_WINDOW; + break; + } + case PHY_RECEIVE_DELAY1: + { + phyParam.Value = AS923_RECEIVE_DELAY1; + break; + } + case PHY_RECEIVE_DELAY2: + { + phyParam.Value = AS923_RECEIVE_DELAY2; + break; + } + case PHY_JOIN_ACCEPT_DELAY1: + { + phyParam.Value = AS923_JOIN_ACCEPT_DELAY1; + break; + } + case PHY_JOIN_ACCEPT_DELAY2: + { + phyParam.Value = AS923_JOIN_ACCEPT_DELAY2; + break; + } + case PHY_MAX_FCNT_GAP: + { + phyParam.Value = AS923_MAX_FCNT_GAP; + break; + } + case PHY_ACK_TIMEOUT: + { + phyParam.Value = ( AS923_ACKTIMEOUT + randr( -AS923_ACK_TIMEOUT_RND, AS923_ACK_TIMEOUT_RND ) ); + break; + } + case PHY_DEF_DR1_OFFSET: + { + phyParam.Value = AS923_DEFAULT_RX1_DR_OFFSET; + break; + } + case PHY_DEF_RX2_FREQUENCY: + { + phyParam.Value = AS923_RX_WND_2_FREQ; + break; + } + case PHY_DEF_RX2_DR: + { + phyParam.Value = AS923_RX_WND_2_DR; + break; + } + case PHY_CHANNELS_MASK: + { + phyParam.ChannelsMask = NvmCtx.ChannelsMask; + break; + } + case PHY_CHANNELS_DEFAULT_MASK: + { + phyParam.ChannelsMask = NvmCtx.ChannelsDefaultMask; + break; + } + case PHY_MAX_NB_CHANNELS: + { + phyParam.Value = AS923_MAX_NB_CHANNELS; + break; + } + case PHY_CHANNELS: + { + phyParam.Channels = NvmCtx.Channels; + break; + } + case PHY_DEF_UPLINK_DWELL_TIME: + { + phyParam.Value = AS923_DEFAULT_UPLINK_DWELL_TIME; + break; + } + case PHY_DEF_DOWNLINK_DWELL_TIME: + { + phyParam.Value = AS923_DEFAULT_DOWNLINK_DWELL_TIME; + break; + } + case PHY_DEF_MAX_EIRP: + { + phyParam.fValue = AS923_DEFAULT_MAX_EIRP; + break; + } + case PHY_DEF_ANTENNA_GAIN: + { + phyParam.fValue = AS923_DEFAULT_ANTENNA_GAIN; + break; + } + case PHY_BEACON_CHANNEL_FREQ: + { + phyParam.Value = AS923_BEACON_CHANNEL_FREQ; + break; + } + case PHY_BEACON_FORMAT: + { + phyParam.BeaconFormat.BeaconSize = AS923_BEACON_SIZE; + phyParam.BeaconFormat.Rfu1Size = AS923_RFU1_SIZE; + phyParam.BeaconFormat.Rfu2Size = AS923_RFU2_SIZE; + break; + } + case PHY_BEACON_CHANNEL_DR: + { + phyParam.Value = AS923_BEACON_CHANNEL_DR; + break; + } + case PHY_PING_SLOT_CHANNEL_DR: + { + phyParam.Value = AS923_PING_SLOT_CHANNEL_DR; + break; + } + default: + { + break; + } + } + + return phyParam; +} + +void RegionAS923SetBandTxDone( SetBandTxDoneParams_t* txDone ) +{ + RegionCommonSetBandTxDone( txDone->Joined, &NvmCtx.Bands[NvmCtx.Channels[txDone->Channel].Band], txDone->LastTxDoneTime ); +} + +void RegionAS923InitDefaults( InitDefaultsParams_t* params ) +{ + Band_t bands[AS923_MAX_NB_BANDS] = + { + AS923_BAND0 + }; + + switch( params->Type ) + { + case INIT_TYPE_INIT: + { + // Initialize bands + memcpy1( ( uint8_t* )NvmCtx.Bands, ( uint8_t* )bands, sizeof( Band_t ) * AS923_MAX_NB_BANDS ); + + // Channels + NvmCtx.Channels[0] = ( ChannelParams_t ) AS923_LC1; + NvmCtx.Channels[1] = ( ChannelParams_t ) AS923_LC2; + + // Initialize the channels default mask + NvmCtx.ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ); + // Update the channels mask + RegionCommonChanMaskCopy( NvmCtx.ChannelsMask, NvmCtx.ChannelsDefaultMask, 1 ); + break; + } + case INIT_TYPE_RESTORE_CTX: + { + if( params->NvmCtx != 0 ) + { + memcpy1( (uint8_t*) &NvmCtx, (uint8_t*) params->NvmCtx, sizeof( NvmCtx ) ); + } + break; + } + case INIT_TYPE_RESTORE_DEFAULT_CHANNELS: + { + // Restore channels default mask + NvmCtx.ChannelsMask[0] |= NvmCtx.ChannelsDefaultMask[0]; + + // Channels + NvmCtx.Channels[0] = ( ChannelParams_t ) AS923_LC1; + NvmCtx.Channels[1] = ( ChannelParams_t ) AS923_LC2; + break; + } + default: + { + break; + } + } +} + +void* RegionAS923GetNvmCtx( GetNvmCtxParams_t* params ) +{ + params->nvmCtxSize = sizeof( RegionAS923NvmCtx_t ); + return &NvmCtx; +} + +bool RegionAS923Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ) +{ + switch( phyAttribute ) + { + case PHY_FREQUENCY: + { + return VerifyRfFreq( verify->Frequency ); + } + case PHY_TX_DR: + { + if( verify->DatarateParams.UplinkDwellTime == 0 ) + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, AS923_TX_MIN_DATARATE, AS923_TX_MAX_DATARATE ); + } + else + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, AS923_DWELL_LIMIT_DATARATE, AS923_TX_MAX_DATARATE ); + } + } + case PHY_DEF_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, DR_0, DR_5 ); + } + case PHY_RX_DR: + { + if( verify->DatarateParams.DownlinkDwellTime == 0 ) + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, AS923_RX_MIN_DATARATE, AS923_RX_MAX_DATARATE ); + } + else + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, AS923_DWELL_LIMIT_DATARATE, AS923_RX_MAX_DATARATE ); + } + } + case PHY_DEF_TX_POWER: + case PHY_TX_POWER: + { + // Remark: switched min and max! + return RegionCommonValueInRange( verify->TxPower, AS923_MAX_TX_POWER, AS923_MIN_TX_POWER ); + } + case PHY_DUTY_CYCLE: + { + return AS923_DUTY_CYCLE_ENABLED; + } + default: + return false; + } +} + +void RegionAS923ApplyCFList( ApplyCFListParams_t* applyCFList ) +{ + ChannelParams_t newChannel; + ChannelAddParams_t channelAdd; + ChannelRemoveParams_t channelRemove; + + // Setup default datarate range + newChannel.DrRange.Value = ( DR_5 << 4 ) | DR_0; + + // Size of the optional CF list + if( applyCFList->Size != 16 ) + { + return; + } + + // Last byte CFListType must be 0 to indicate the CFList contains a list of frequencies + if( applyCFList->Payload[15] != 0 ) + { + return; + } + + // Last byte is RFU, don't take it into account + for( uint8_t i = 0, chanIdx = AS923_NUMB_DEFAULT_CHANNELS; chanIdx < AS923_MAX_NB_CHANNELS; i+=3, chanIdx++ ) + { + if( chanIdx < ( AS923_NUMB_CHANNELS_CF_LIST + AS923_NUMB_DEFAULT_CHANNELS ) ) + { + // Channel frequency + newChannel.Frequency = (uint32_t) applyCFList->Payload[i]; + newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 1] << 8 ); + newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 2] << 16 ); + newChannel.Frequency *= 100; + + // Initialize alternative frequency to 0 + newChannel.Rx1Frequency = 0; + } + else + { + newChannel.Frequency = 0; + newChannel.DrRange.Value = 0; + newChannel.Rx1Frequency = 0; + } + + if( newChannel.Frequency != 0 ) + { + channelAdd.NewChannel = &newChannel; + channelAdd.ChannelId = chanIdx; + + // Try to add all channels + RegionAS923ChannelAdd( &channelAdd ); + } + else + { + channelRemove.ChannelId = chanIdx; + + RegionAS923ChannelsRemove( &channelRemove ); + } + } +} + +bool RegionAS923ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ) +{ + switch( chanMaskSet->ChannelsMaskType ) + { + case CHANNELS_MASK: + { + RegionCommonChanMaskCopy( NvmCtx.ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + case CHANNELS_DEFAULT_MASK: + { + RegionCommonChanMaskCopy( NvmCtx.ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + default: + return false; + } + return true; +} + +void RegionAS923ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ) +{ + double tSymbol = 0.0; + + // Get the datarate, perform a boundary check + rxConfigParams->Datarate = MIN( datarate, AS923_RX_MAX_DATARATE ); + rxConfigParams->Bandwidth = GetBandwidth( rxConfigParams->Datarate ); + + if( rxConfigParams->Datarate == DR_7 ) + { // FSK + tSymbol = RegionCommonComputeSymbolTimeFsk( DataratesAS923[rxConfigParams->Datarate] ); + } + else + { // LoRa + tSymbol = RegionCommonComputeSymbolTimeLoRa( DataratesAS923[rxConfigParams->Datarate], BandwidthsAS923[rxConfigParams->Datarate] ); + } + + RegionCommonComputeRxWindowParameters( tSymbol, minRxSymbols, rxError, Radio.GetWakeupTime( ), &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); +} + +bool RegionAS923RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) +{ + RadioModems_t modem; + int8_t dr = rxConfig->Datarate; + uint8_t maxPayload = 0; + int8_t phyDr = 0; + uint32_t frequency = rxConfig->Frequency; + const char *slotStrings[] = { "1", "2", "C", "Multi_C", "P", "Multi_P" }; + + if( Radio.GetStatus( ) != RF_IDLE ) + { + return false; + } + + if( rxConfig->RxSlot == RX_SLOT_WIN_1 ) + { + // Apply window 1 frequency + frequency = NvmCtx.Channels[rxConfig->Channel].Frequency; + // Apply the alternative RX 1 window frequency, if it is available + if( NvmCtx.Channels[rxConfig->Channel].Rx1Frequency != 0 ) + { + frequency = NvmCtx.Channels[rxConfig->Channel].Rx1Frequency; + } + } + + // Read the physical datarate from the datarates table + phyDr = DataratesAS923[dr]; + + Radio.SetChannel( frequency ); + + // Radio configuration + if( dr == DR_7 ) + { + modem = MODEM_FSK; + Radio.SetRxConfig( modem, 50000, phyDr * 1000, 0, 83333, 5, rxConfig->WindowTimeout, false, 0, true, 0, 0, false, rxConfig->RxContinuous ); + } + else + { + modem = MODEM_LORA; + Radio.SetRxConfig( modem, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous ); + } + + // Check for repeater support + if( rxConfig->RepeaterSupport == true ) + { + maxPayload = MaxPayloadOfDatarateRepeaterDwell0AS923[dr]; + } + else + { + maxPayload = MaxPayloadOfDatarateDwell0AS923[dr]; + } + + Radio.SetMaxPayloadLength( modem, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD ); + if ( rxConfig->RxSlot < RX_SLOT_NONE ) + { + MW_LOG( "RX_%s on freq %d Hz at DR %d\n\r", slotStrings[rxConfig->RxSlot], frequency, dr ); + } + else + { + MW_LOG( "RX on freq %d Hz at DR %d\n\r", frequency, dr ); + } + + *datarate = (uint8_t) dr; + return true; +} + +bool RegionAS923TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ) +{ + RadioModems_t modem; + int8_t phyDr = DataratesAS923[txConfig->Datarate]; + int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, NvmCtx.Bands[NvmCtx.Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, NvmCtx.ChannelsMask ); + uint32_t bandwidth = GetBandwidth( txConfig->Datarate ); + int8_t phyTxPower = 0; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain ); + + // Setup the radio frequency + Radio.SetChannel( NvmCtx.Channels[txConfig->Channel].Frequency ); + + if( txConfig->Datarate == DR_7 ) + { // High Speed FSK channel + modem = MODEM_FSK; + Radio.SetTxConfig( modem, phyTxPower, 25000, bandwidth, phyDr * 1000, 0, 5, false, true, 0, 0, false, 4000 ); + } + else + { + modem = MODEM_LORA; + Radio.SetTxConfig( modem, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 4000 ); + } + MW_LOG( "TX on freq %d Hz at DR %d\n\r", NvmCtx.Channels[txConfig->Channel].Frequency, txConfig->Datarate ); + // Setup maximum payload lenght of the radio driver + Radio.SetMaxPayloadLength( modem, txConfig->PktLen ); + // Get the time-on-air of the next tx frame + *txTimeOnAir = Radio.TimeOnAir( modem, txConfig->PktLen ); + + *txPower = txPowerLimited; + return true; +} + +uint8_t RegionAS923LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ) +{ + uint8_t status = 0x07; + RegionCommonLinkAdrParams_t linkAdrParams; + uint8_t nextIndex = 0; + uint8_t bytesProcessed = 0; + uint16_t chMask = 0; + GetPhyParams_t getPhy; + PhyParam_t phyParam; + RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams; + + while( bytesProcessed < linkAdrReq->PayloadSize ) + { + // Get ADR request parameters + nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams ); + + if( nextIndex == 0 ) + break; // break loop, since no more request has been found + + // Update bytes processed + bytesProcessed += nextIndex; + + // Revert status, as we only check the last ADR request for the channel mask KO + status = 0x07; + + // Setup temporary channels mask + chMask = linkAdrParams.ChMask; + + // Verify channels mask + if( ( linkAdrParams.ChMaskCtrl == 0 ) && ( chMask == 0 ) ) + { + status &= 0xFE; // Channel mask KO + } + else if( ( ( linkAdrParams.ChMaskCtrl >= 1 ) && ( linkAdrParams.ChMaskCtrl <= 5 )) || + ( linkAdrParams.ChMaskCtrl >= 7 ) ) + { + // RFU + status &= 0xFE; // Channel mask KO + } + else + { + for( uint8_t i = 0; i < AS923_MAX_NB_CHANNELS; i++ ) + { + if( linkAdrParams.ChMaskCtrl == 6 ) + { + if( NvmCtx.Channels[i].Frequency != 0 ) + { + chMask |= 1 << i; + } + } + else + { + if( ( ( chMask & ( 1 << i ) ) != 0 ) && + ( NvmCtx.Channels[i].Frequency == 0 ) ) + {// Trying to enable an undefined channel + status &= 0xFE; // Channel mask KO + } + } + } + } + } + + // Get the minimum possible datarate + getPhy.Attribute = PHY_MIN_TX_DR; + getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime; + phyParam = RegionAS923GetPhyParam( &getPhy ); + + linkAdrVerifyParams.Status = status; + linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled; + linkAdrVerifyParams.Datarate = linkAdrParams.Datarate; + linkAdrVerifyParams.TxPower = linkAdrParams.TxPower; + linkAdrVerifyParams.NbRep = linkAdrParams.NbRep; + linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate; + linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower; + linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep; + linkAdrVerifyParams.NbChannels = AS923_MAX_NB_CHANNELS; + linkAdrVerifyParams.ChannelsMask = &chMask; + linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value; + linkAdrVerifyParams.MaxDatarate = AS923_TX_MAX_DATARATE; + linkAdrVerifyParams.Channels = NvmCtx.Channels; + linkAdrVerifyParams.MinTxPower = AS923_MIN_TX_POWER; + linkAdrVerifyParams.MaxTxPower = AS923_MAX_TX_POWER; + linkAdrVerifyParams.Version = linkAdrReq->Version; + + // Verify the parameters and update, if necessary + status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep ); + + // Update channelsMask if everything is correct + if( status == 0x07 ) + { + // Set the channels mask to a default value + memset1( ( uint8_t* ) NvmCtx.ChannelsMask, 0, sizeof( NvmCtx.ChannelsMask ) ); + // Update the channels mask + NvmCtx.ChannelsMask[0] = chMask; + } + + // Update status variables + *drOut = linkAdrParams.Datarate; + *txPowOut = linkAdrParams.TxPower; + *nbRepOut = linkAdrParams.NbRep; + *nbBytesParsed = bytesProcessed; + + return status; +} + +uint8_t RegionAS923RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ) +{ + uint8_t status = 0x07; + + // Verify radio frequency + if( VerifyRfFreq( rxParamSetupReq->Frequency ) == false ) + { + status &= 0xFE; // Channel frequency KO + } + + // Verify datarate + if( RegionCommonValueInRange( rxParamSetupReq->Datarate, AS923_RX_MIN_DATARATE, AS923_RX_MAX_DATARATE ) == false ) + { + status &= 0xFD; // Datarate KO + } + + // Verify datarate offset + if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, AS923_MIN_RX1_DR_OFFSET, AS923_MAX_RX1_DR_OFFSET ) == false ) + { + status &= 0xFB; // Rx1DrOffset range KO + } + + return status; +} + +uint8_t RegionAS923NewChannelReq( NewChannelReqParams_t* newChannelReq ) +{ + uint8_t status = 0x03; + ChannelAddParams_t channelAdd; + ChannelRemoveParams_t channelRemove; + + if( newChannelReq->NewChannel->Frequency == 0 ) + { + channelRemove.ChannelId = newChannelReq->ChannelId; + + // Remove + if( RegionAS923ChannelsRemove( &channelRemove ) == false ) + { + status &= 0xFC; + } + } + else + { + channelAdd.NewChannel = newChannelReq->NewChannel; + channelAdd.ChannelId = newChannelReq->ChannelId; + + switch( RegionAS923ChannelAdd( &channelAdd ) ) + { + case LORAMAC_STATUS_OK: + { + break; + } + case LORAMAC_STATUS_FREQUENCY_INVALID: + { + status &= 0xFE; + break; + } + case LORAMAC_STATUS_DATARATE_INVALID: + { + status &= 0xFD; + break; + } + case LORAMAC_STATUS_FREQ_AND_DR_INVALID: + { + status &= 0xFC; + break; + } + default: + { + status &= 0xFC; + break; + } + } + } + + return status; +} + +int8_t RegionAS923TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ) +{ + // Accept the request + return 0; +} + +uint8_t RegionAS923DlChannelReq( DlChannelReqParams_t* dlChannelReq ) +{ + uint8_t status = 0x03; + + // Verify if the frequency is supported + if( VerifyRfFreq( dlChannelReq->Rx1Frequency ) == false ) + { + status &= 0xFE; + } + + // Verify if an uplink frequency exists + if( NvmCtx.Channels[dlChannelReq->ChannelId].Frequency == 0 ) + { + status &= 0xFD; + } + + // Apply Rx1 frequency, if the status is OK + if( status == 0x03 ) + { + NvmCtx.Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency; + } + + return status; +} + +int8_t RegionAS923AlternateDr( int8_t currentDr, AlternateDrType_t type ) +{ + // Only AS923_DWELL_LIMIT_DATARATE is supported + return AS923_DWELL_LIMIT_DATARATE; +} + +void RegionAS923CalcBackOff( CalcBackOffParams_t* calcBackOff ) +{ + RegionCommonCalcBackOffParams_t calcBackOffParams; + + calcBackOffParams.Channels = NvmCtx.Channels; + calcBackOffParams.Bands = NvmCtx.Bands; + calcBackOffParams.LastTxIsJoinRequest = calcBackOff->LastTxIsJoinRequest; + calcBackOffParams.Joined = calcBackOff->Joined; + calcBackOffParams.DutyCycleEnabled = calcBackOff->DutyCycleEnabled; + calcBackOffParams.Channel = calcBackOff->Channel; + calcBackOffParams.ElapsedTime = calcBackOff->ElapsedTime; + calcBackOffParams.TxTimeOnAir = calcBackOff->TxTimeOnAir; + + RegionCommonCalcBackOff( &calcBackOffParams ); +} + +LoRaMacStatus_t RegionAS923NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ) +{ + uint8_t channelNext = 0; + uint8_t nbEnabledChannels = 0; + uint8_t delayTx = 0; + uint8_t enabledChannels[AS923_MAX_NB_CHANNELS] = { 0 }; + TimerTime_t nextTxDelay = 0; + + if( RegionCommonCountChannels( NvmCtx.ChannelsMask, 0, 1 ) == 0 ) + { // Reactivate default channels + NvmCtx.ChannelsMask[0] |= LC( 1 ) + LC( 2 ); + } + + TimerTime_t elapsed = TimerGetElapsedTime( nextChanParams->LastAggrTx ); + if( ( nextChanParams->LastAggrTx == 0 ) || ( nextChanParams->AggrTimeOff <= elapsed ) ) + { + // Reset Aggregated time off + *aggregatedTimeOff = 0; + + // Update bands Time OFF + nextTxDelay = RegionCommonUpdateBandTimeOff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, NvmCtx.Bands, AS923_MAX_NB_BANDS ); + + // Search how many channels are enabled + nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Joined, nextChanParams->Datarate, + NvmCtx.ChannelsMask, NvmCtx.Channels, + NvmCtx.Bands, enabledChannels, &delayTx ); + } + else + { + delayTx++; + nextTxDelay = nextChanParams->AggrTimeOff - elapsed; + } + + if( nbEnabledChannels > 0 ) + { + for( uint8_t i = 0, j = randr( 0, nbEnabledChannels - 1 ); i < AS923_MAX_NB_CHANNELS; i++ ) + { + channelNext = enabledChannels[j]; + j = ( j + 1 ) % nbEnabledChannels; + + // Perform carrier sense for AS923_CARRIER_SENSE_TIME + // If the channel is free, we can stop the LBT mechanism + if( Radio.IsChannelFree( MODEM_LORA, NvmCtx.Channels[channelNext].Frequency, AS923_RSSI_FREE_TH, AS923_CARRIER_SENSE_TIME ) == true ) + { + // Free channel found + *channel = channelNext; + *time = 0; + return LORAMAC_STATUS_OK; + } + } + return LORAMAC_STATUS_NO_FREE_CHANNEL_FOUND; + } + else + { + if( delayTx > 0 ) + { + // Delay transmission due to AggregatedTimeOff or to a band time off + *time = nextTxDelay; + return LORAMAC_STATUS_DUTYCYCLE_RESTRICTED; + } + // Datarate not supported by any channel, restore defaults + NvmCtx.ChannelsMask[0] |= LC( 1 ) + LC( 2 ); + *time = 0; + return LORAMAC_STATUS_NO_CHANNEL_FOUND; + } +} + +LoRaMacStatus_t RegionAS923ChannelAdd( ChannelAddParams_t* channelAdd ) +{ + bool drInvalid = false; + bool freqInvalid = false; + uint8_t id = channelAdd->ChannelId; + + if( id < AS923_NUMB_DEFAULT_CHANNELS ) + { + return LORAMAC_STATUS_FREQ_AND_DR_INVALID; + } + + if( id >= AS923_MAX_NB_CHANNELS ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + // Validate the datarate range + if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Min, AS923_TX_MIN_DATARATE, AS923_TX_MAX_DATARATE ) == false ) + { + drInvalid = true; + } + if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, AS923_TX_MIN_DATARATE, AS923_TX_MAX_DATARATE ) == false ) + { + drInvalid = true; + } + if( channelAdd->NewChannel->DrRange.Fields.Min > channelAdd->NewChannel->DrRange.Fields.Max ) + { + drInvalid = true; + } + + // Check frequency + if( freqInvalid == false ) + { + if( VerifyRfFreq( channelAdd->NewChannel->Frequency ) == false ) + { + freqInvalid = true; + } + } + + // Check status + if( ( drInvalid == true ) && ( freqInvalid == true ) ) + { + return LORAMAC_STATUS_FREQ_AND_DR_INVALID; + } + if( drInvalid == true ) + { + return LORAMAC_STATUS_DATARATE_INVALID; + } + if( freqInvalid == true ) + { + return LORAMAC_STATUS_FREQUENCY_INVALID; + } + + memcpy1( ( uint8_t* ) &(NvmCtx.Channels[id]), ( uint8_t* ) channelAdd->NewChannel, sizeof( NvmCtx.Channels[id] ) ); + NvmCtx.Channels[id].Band = 0; + NvmCtx.ChannelsMask[0] |= ( 1 << id ); + return LORAMAC_STATUS_OK; +} + +bool RegionAS923ChannelsRemove( ChannelRemoveParams_t* channelRemove ) +{ + uint8_t id = channelRemove->ChannelId; + + if( id < AS923_NUMB_DEFAULT_CHANNELS ) + { + return false; + } + + // Remove the channel from the list of channels + NvmCtx.Channels[id] = ( ChannelParams_t ){ 0, 0, { 0 }, 0 }; + + return RegionCommonChanDisable( NvmCtx.ChannelsMask, id, AS923_MAX_NB_CHANNELS ); +} + +void RegionAS923SetContinuousWave( ContinuousWaveParams_t* continuousWave ) +{ + int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, NvmCtx.Bands[NvmCtx.Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, NvmCtx.ChannelsMask ); + int8_t phyTxPower = 0; + uint32_t frequency = NvmCtx.Channels[continuousWave->Channel].Frequency; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, continuousWave->MaxEirp, continuousWave->AntennaGain ); + + Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout ); +} + +uint8_t RegionAS923ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) +{ + // Initialize minDr for a downlink dwell time configuration of 0 + int8_t minDr = DR_0; + + // Update the minDR for a downlink dwell time configuration of 1 + if( downlinkDwellTime == 1 ) + { + minDr = AS923_DWELL_LIMIT_DATARATE; + } + + // Apply offset formula + return MIN( DR_5, MAX( minDr, dr - EffectiveRx1DrOffsetAS923[drOffset] ) ); +} + +void RegionAS923RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ) +{ + RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup; + + regionCommonRxBeaconSetup.Datarates = DataratesAS923; + regionCommonRxBeaconSetup.Frequency = rxBeaconSetup->Frequency; + regionCommonRxBeaconSetup.BeaconSize = AS923_BEACON_SIZE; + regionCommonRxBeaconSetup.BeaconDatarate = AS923_BEACON_CHANNEL_DR; + regionCommonRxBeaconSetup.BeaconChannelBW = AS923_BEACON_CHANNEL_BW; + regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime; + regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout; + + RegionCommonRxBeaconSetup( ®ionCommonRxBeaconSetup ); + + // Store downlink datarate + *outDr = AS923_BEACON_CHANNEL_DR; +} diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionAS923.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionAS923.h new file mode 100644 index 00000000..ab0a3d33 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionAS923.h @@ -0,0 +1,546 @@ +/*! + * \file RegionAS923.h + * + * \brief Region definition for AS923 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + * + * \defgroup REGIONAS923 Region AS923 + * Implementation according to LoRaWAN Specification v1.0.2. + * \{ + */ +#ifndef __REGION_AS923_H__ +#define __REGION_AS923_H__ + +#include "region/Region.h" + +/*! + * LoRaMac maximum number of channels + */ +#define AS923_MAX_NB_CHANNELS 16 + +/*! + * Number of default channels + */ +#define AS923_NUMB_DEFAULT_CHANNELS 2 + +/*! + * Number of channels to apply for the CF list + */ +#define AS923_NUMB_CHANNELS_CF_LIST 5 + +/*! + * Minimal datarate that can be used by the node + */ +#define AS923_TX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define AS923_TX_MAX_DATARATE DR_7 + +/*! + * Minimal datarate that can be used by the node + */ +#define AS923_RX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define AS923_RX_MAX_DATARATE DR_7 + +/*! + * Default datarate used by the node + */ +#define AS923_DEFAULT_DATARATE DR_2 + +/*! + * The minimum datarate which is used when the + * dwell time is limited. + */ +#define AS923_DWELL_LIMIT_DATARATE DR_2 + +/*! + * Minimal Rx1 receive datarate offset + */ +#define AS923_MIN_RX1_DR_OFFSET 0 + +/*! + * Maximal Rx1 receive datarate offset + */ +#define AS923_MAX_RX1_DR_OFFSET 7 + +/*! + * Default Rx1 receive datarate offset + */ +#define AS923_DEFAULT_RX1_DR_OFFSET 0 + +/*! + * Minimal Tx output power that can be used by the node + */ +#define AS923_MIN_TX_POWER TX_POWER_7 + +/*! + * Maximal Tx output power that can be used by the node + */ +#define AS923_MAX_TX_POWER TX_POWER_0 + +/*! + * Default Tx output power used by the node + */ +#define AS923_DEFAULT_TX_POWER TX_POWER_0 + +/*! + * Default uplink dwell time configuration + */ +#define AS923_DEFAULT_UPLINK_DWELL_TIME 1 + +/*! + * Default downlink dwell time configuration + */ +#define AS923_DEFAULT_DOWNLINK_DWELL_TIME 1 + +/*! + * Default Max EIRP + */ +#define AS923_DEFAULT_MAX_EIRP 16.0f + +/*! + * Default antenna gain + */ +#define AS923_DEFAULT_ANTENNA_GAIN 2.15f + +/*! + * ADR Ack limit + */ +#define AS923_ADR_ACK_LIMIT 64 + +/*! + * ADR Ack delay + */ +#define AS923_ADR_ACK_DELAY 32 + +/*! + * Enabled or disabled the duty cycle + */ +#define AS923_DUTY_CYCLE_ENABLED 0 + +/*! + * Maximum RX window duration + */ +#define AS923_MAX_RX_WINDOW 3000 + +/*! + * Receive delay 1 + */ +#define AS923_RECEIVE_DELAY1 1000 + +/*! + * Receive delay 2 + */ +#define AS923_RECEIVE_DELAY2 2000 + +/*! + * Join accept delay 1 + */ +#define AS923_JOIN_ACCEPT_DELAY1 5000 + +/*! + * Join accept delay 2 + */ +#define AS923_JOIN_ACCEPT_DELAY2 6000 + +/*! + * Maximum frame counter gap + */ +#define AS923_MAX_FCNT_GAP 16384 + +/*! + * Ack timeout + */ +#define AS923_ACKTIMEOUT 2000 + +/*! + * Random ack timeout limits + */ +#define AS923_ACK_TIMEOUT_RND 1000 + +#if ( AS923_DEFAULT_DATARATE > DR_5 ) +#error "A default DR higher than DR_5 may lead to connectivity loss." +#endif + +/*! + * Second reception window channel frequency definition. + */ +#define AS923_RX_WND_2_FREQ 923200000 + +/*! + * Second reception window channel datarate definition. + */ +#define AS923_RX_WND_2_DR DR_2 + +/* + * CLASS B + */ +/*! + * Beacon frequency + */ +#define AS923_BEACON_CHANNEL_FREQ 923400000 + +/*! + * Payload size of a beacon frame + */ +#define AS923_BEACON_SIZE 17 + +/*! + * Size of RFU 1 field + */ +#define AS923_RFU1_SIZE 2 + +/*! + * Size of RFU 2 field + */ +#define AS923_RFU2_SIZE 0 + +/*! + * Datarate of the beacon channel + */ +#define AS923_BEACON_CHANNEL_DR DR_3 + +/*! + * Bandwith of the beacon channel + */ +#define AS923_BEACON_CHANNEL_BW 0 + +/*! + * Ping slot channel datarate + */ +#define AS923_PING_SLOT_CHANNEL_DR DR_3 + +/*! + * Maximum number of bands + */ +#define AS923_MAX_NB_BANDS 1 + +/*! + * Band 0 definition + * { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff } + */ +#define AS923_BAND0 { 100, AS923_MAX_TX_POWER, 0, 0, 0 } // 1.0 % + +/*! + * LoRaMac default channel 1 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define AS923_LC1 { 923200000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } + +/*! + * LoRaMac default channel 2 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define AS923_LC2 { 923400000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } + +/*! + * LoRaMac channels which are allowed for the join procedure + */ +#define AS923_JOIN_CHANNELS ( uint16_t )( LC( 1 ) | LC( 2 ) ) + +/*! + * RSSI threshold for a free channel [dBm] + */ +#define AS923_RSSI_FREE_TH -85 + +/*! + * Specifies the time the node performs a carrier sense + */ +#define AS923_CARRIER_SENSE_TIME 6 + +/*! + * Data rates table definition + */ +static const uint8_t DataratesAS923[] = { 12, 11, 10, 9, 8, 7, 7, 50 }; + +/*! + * Bandwidths table definition in Hz + */ +static const uint32_t BandwidthsAS923[] = { 125000, 125000, 125000, 125000, 125000, 125000, 250000, 0 }; + +/*! + * Maximum payload with respect to the datarate index. Cannot operate with repeater. + * The table is valid for the dwell time configuration of 0 for uplinks and downlinks. + */ +static const uint8_t MaxPayloadOfDatarateDwell0AS923[] = { 51, 51, 51, 115, 242, 242, 242, 242 }; + +/*! + * Maximum payload with respect to the datarate index. Can operate with repeater. + * The table is valid for the dwell time configuration of 0 for uplinks and downlinks. The table provides + * repeater support. + */ +static const uint8_t MaxPayloadOfDatarateRepeaterDwell0AS923[] = { 51, 51, 51, 115, 222, 222, 222, 222 }; + +/*! + * Maximum payload with respect to the datarate index. Can operate with and without repeater. + * The table proides repeater support. The table is only valid for uplinks. + */ +static const uint8_t MaxPayloadOfDatarateDwell1UpAS923[] = { 0, 0, 11, 53, 125, 242, 242, 242 }; + +/*! + * Maximum payload with respect to the datarate index. Can operate with and without repeater. + * The table proides repeater support. The table is only valid for downlinks. + */ +static const uint8_t MaxPayloadOfDatarateDwell1DownAS923[] = { 0, 0, 11, 53, 126, 242, 242, 242 }; + +/*! + * Effective datarate offsets for receive window 1. + */ +static const int8_t EffectiveRx1DrOffsetAS923[] = { 0, 1, 2, 3, 4, 5, -1, -2 }; + +/*! + * \brief The function gets a value of a specific phy attribute. + * + * \param [IN] getPhy Pointer to the function parameters. + * + * \retval Returns a structure containing the PHY parameter. + */ +PhyParam_t RegionAS923GetPhyParam( GetPhyParams_t* getPhy ); + +/*! + * \brief Updates the last TX done parameters of the current channel. + * + * \param [IN] txDone Pointer to the function parameters. + */ +void RegionAS923SetBandTxDone( SetBandTxDoneParams_t* txDone ); + +/*! + * \brief Initializes the channels masks and the channels. + * + * \param [IN] type Sets the initialization type. + */ +void RegionAS923InitDefaults( InitDefaultsParams_t* params ); + +/*! + * \brief Returns a pointer to the internal context and its size. + * + * \param [OUT] params Pointer to the function parameters. + * + * \retval Points to a structure where the module store its non-volatile context. + */ +void* RegionAS923GetNvmCtx( GetNvmCtxParams_t* params ); + +/*! + * \brief Verifies a parameter. + * + * \param [IN] verify Pointer to the function parameters. + * + * \param [IN] type Sets the initialization type. + * + * \retval Returns true, if the parameter is valid. + */ +bool RegionAS923Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ); + +/*! + * \brief The function parses the input buffer and sets up the channels of the + * CF list. + * + * \param [IN] applyCFList Pointer to the function parameters. + */ +void RegionAS923ApplyCFList( ApplyCFListParams_t* applyCFList ); + +/*! + * \brief Sets a channels mask. + * + * \param [IN] chanMaskSet Pointer to the function parameters. + * + * \retval Returns true, if the channels mask could be set. + */ +bool RegionAS923ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ); + +/*! + * Computes the Rx window timeout and offset. + * + * \param [IN] datarate Rx window datarate index to be used + * + * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame. + * + * \param [IN] rxError System maximum timing error of the receiver. In milliseconds + * The receiver will turn on in a [-rxError : +rxError] ms + * interval around RxOffset + * + * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields. + */ +void RegionAS923ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ); + +/*! + * \brief Configuration of the RX windows. + * + * \param [IN] rxConfig Pointer to the function parameters. + * + * \param [OUT] datarate The datarate index which was set. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionAS923RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ); + +/*! + * \brief TX configuration. + * + * \param [IN] txConfig Pointer to the function parameters. + * + * \param [OUT] txPower The tx power index which was set. + * + * \param [OUT] txTimeOnAir The time-on-air of the frame. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionAS923TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ); + +/*! + * \brief The function processes a Link ADR Request. + * + * \param [IN] linkAdrReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionAS923LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ); + +/*! + * \brief The function processes a RX Parameter Setup Request. + * + * \param [IN] rxParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionAS923RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ); + +/*! + * \brief The function processes a Channel Request. + * + * \param [IN] newChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionAS923NewChannelReq( NewChannelReqParams_t* newChannelReq ); + +/*! + * \brief The function processes a TX ParamSetup Request. + * + * \param [IN] txParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + * Returns -1, if the functionality is not implemented. In this case, the end node + * shall not process the command. + */ +int8_t RegionAS923TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ); + +/*! + * \brief The function processes a DlChannel Request. + * + * \param [IN] dlChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionAS923DlChannelReq( DlChannelReqParams_t* dlChannelReq ); + +/*! + * \brief Alternates the datarate of the channel for the join request. + * + * \param [IN] currentDr Current datarate. + * + * \retval Datarate to apply. + */ +int8_t RegionAS923AlternateDr( int8_t currentDr, AlternateDrType_t type ); + +/*! + * \brief Calculates the back-off time. + * + * \param [IN] calcBackOff Pointer to the function parameters. + */ +void RegionAS923CalcBackOff( CalcBackOffParams_t* calcBackOff ); + +/*! + * \brief Searches and set the next random available channel + * + * \param [OUT] channel Next channel to use for TX. + * + * \param [OUT] time Time to wait for the next transmission according to the duty + * cycle. + * + * \param [OUT] aggregatedTimeOff Updates the aggregated time off. + * + * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate] + */ +LoRaMacStatus_t RegionAS923NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ); + +/*! + * \brief Adds a channel. + * + * \param [IN] channelAdd Pointer to the function parameters. + * + * \retval Status of the operation. + */ +LoRaMacStatus_t RegionAS923ChannelAdd( ChannelAddParams_t* channelAdd ); + +/*! + * \brief Removes a channel. + * + * \param [IN] channelRemove Pointer to the function parameters. + * + * \retval Returns true, if the channel was removed successfully. + */ +bool RegionAS923ChannelsRemove( ChannelRemoveParams_t* channelRemove ); + +/*! + * \brief Sets the radio into continuous wave mode. + * + * \param [IN] continuousWave Pointer to the function parameters. + */ +void RegionAS923SetContinuousWave( ContinuousWaveParams_t* continuousWave ); + +/*! + * \brief Computes new datarate according to the given offset + * + * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms + * + * \param [IN] dr Current datarate + * + * \param [IN] drOffset Offset to be applied + * + * \retval newDr Computed datarate. + */ +uint8_t RegionAS923ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ); + +/*! + * \brief Sets the radio into beacon reception mode + * + * \param [IN] rxBeaconSetup Pointer to the function parameters + */ + void RegionAS923RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ); + +/*! \} defgroup REGIONAS923 */ + +#endif // __REGION_AS923_H__ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionAU915.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionAU915.c new file mode 100644 index 00000000..30b89860 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionAU915.c @@ -0,0 +1,1037 @@ +/*! + * \file RegionAU915.c + * + * \brief Region implementation for AU915 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) +*/ +#include "utilities.h" + +#include "RegionCommon.h" +#include "RegionAU915.h" +#include "lorawan_conf.h" +#include "mw_log_conf.h" + +// Definitions +#define CHANNELS_MASK_SIZE 6 + +// A mask to select only valid 500KHz channels +#define CHANNELS_MASK_500KHZ_MASK 0x00FF + +/*! + * Region specific context + */ +typedef struct sRegionAU915NvmCtx +{ + /*! + * LoRaMAC channels + */ + ChannelParams_t Channels[ AU915_MAX_NB_CHANNELS ]; + /*! + * LoRaMac bands + */ + Band_t Bands[ AU915_MAX_NB_BANDS ]; + /*! + * LoRaMac channels mask + */ + uint16_t ChannelsMask[ CHANNELS_MASK_SIZE ]; + /*! + * LoRaMac channels remaining + */ + uint16_t ChannelsMaskRemaining[CHANNELS_MASK_SIZE]; + /*! + * LoRaMac channels default mask + */ + uint16_t ChannelsDefaultMask[ CHANNELS_MASK_SIZE ]; +}RegionAU915NvmCtx_t; + +/* + * Non-volatile module context. + */ +static RegionAU915NvmCtx_t NvmCtx; + +// Static functions +static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr ) +{ + uint8_t nextLowerDr = 0; + + if( dr == minDr ) + { + nextLowerDr = minDr; + } + else if( dr == DR_8 ) + { // DR_7 is not allowed + nextLowerDr = DR_6; + } + else + { + nextLowerDr = dr - 1; + } + return nextLowerDr; +} + +static uint32_t GetBandwidth( uint32_t drIndex ) +{ + switch( BandwidthsAU915[drIndex] ) + { + default: + case 125000: + return 0; + case 250000: + return 1; + case 500000: + return 2; + } +} + +static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask ) +{ + int8_t txPowerResult = txPower; + + // Limit tx power to the band max + txPowerResult = MAX( txPower, maxBandTxPower ); + + return txPowerResult; +} + +static bool VerifyRfFreq( uint32_t freq ) +{ + // Check radio driver support + if( Radio.CheckRfFrequency( freq ) == false ) + { + return false; + } + + // Rx frequencies + if( ( freq < AU915_FIRST_RX1_CHANNEL ) || + ( freq > AU915_LAST_RX1_CHANNEL ) || + ( ( ( freq - ( uint32_t ) AU915_FIRST_RX1_CHANNEL ) % ( uint32_t ) AU915_STEPWIDTH_RX1_CHANNEL ) != 0 ) ) + { + return false; + } + + // Tx frequencies for 125kHz + // Also includes the range for 500kHz channels + if( ( freq < 915200000 ) || ( freq > 927800000 ) ) + { + return false; + } + return true; +} + +static uint8_t CountNbOfEnabledChannels( uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t delayTransmission = 0; + + for( uint8_t i = 0, k = 0; i < AU915_MAX_NB_CHANNELS; i += 16, k++ ) + { + for( uint8_t j = 0; j < 16; j++ ) + { + if( ( channelsMask[k] & ( 1 << j ) ) != 0 ) + { + if( channels[i + j].Frequency == 0 ) + { // Check if the channel is enabled + continue; + } + if( RegionCommonValueInRange( datarate, channels[i + j].DrRange.Fields.Min, + channels[i + j].DrRange.Fields.Max ) == false ) + { // Check if the current channel selection supports the given datarate + continue; + } + if( bands[channels[i + j].Band].TimeOff > 0 ) + { // Check if the band is available for transmission + delayTransmission++; + continue; + } + enabledChannels[nbEnabledChannels++] = i + j; + } + } + } + + *delayTx = delayTransmission; + return nbEnabledChannels; +} + +PhyParam_t RegionAU915GetPhyParam( GetPhyParams_t* getPhy ) +{ + PhyParam_t phyParam = { 0 }; + + switch( getPhy->Attribute ) + { + case PHY_MIN_RX_DR: + { + if( getPhy->DownlinkDwellTime == 0) + { + phyParam.Value = AU915_RX_MIN_DATARATE; + } + else + { + phyParam.Value = AU915_DWELL_LIMIT_DATARATE; + } + break; + } + case PHY_MIN_TX_DR: + { + if( getPhy->UplinkDwellTime == 0) + { + phyParam.Value = AU915_TX_MIN_DATARATE; + } + else + { + phyParam.Value = AU915_DWELL_LIMIT_DATARATE; + } + break; + } + case PHY_DEF_TX_DR: + { + phyParam.Value = AU915_DEFAULT_DATARATE; + break; + } + case PHY_NEXT_LOWER_TX_DR: + { + if( getPhy->UplinkDwellTime == 0) + { + phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, AU915_TX_MIN_DATARATE ); + } + else + { + phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, AU915_DWELL_LIMIT_DATARATE ); + } + break; + } + case PHY_MAX_TX_POWER: + { + phyParam.Value = AU915_MAX_TX_POWER; + break; + } + case PHY_DEF_TX_POWER: + { + phyParam.Value = AU915_DEFAULT_TX_POWER; + break; + } + case PHY_DEF_ADR_ACK_LIMIT: + { + phyParam.Value = AU915_ADR_ACK_LIMIT; + break; + } + case PHY_DEF_ADR_ACK_DELAY: + { + phyParam.Value = AU915_ADR_ACK_DELAY; + break; + } + case PHY_MAX_PAYLOAD: + { + if( getPhy->UplinkDwellTime == 0 ) + { + phyParam.Value = MaxPayloadOfDatarateDwell0AU915[getPhy->Datarate]; + } + else + { + phyParam.Value = MaxPayloadOfDatarateDwell1AU915[getPhy->Datarate]; + } + break; + } + case PHY_MAX_PAYLOAD_REPEATER: + { + if( getPhy->UplinkDwellTime == 0) + { + phyParam.Value = MaxPayloadOfDatarateRepeaterDwell0AU915[getPhy->Datarate]; + } + else + { + phyParam.Value = MaxPayloadOfDatarateRepeaterDwell1AU915[getPhy->Datarate]; + } + break; + } + case PHY_DUTY_CYCLE: + { + phyParam.Value = AU915_DUTY_CYCLE_ENABLED; + break; + } + case PHY_MAX_RX_WINDOW: + { + phyParam.Value = AU915_MAX_RX_WINDOW; + break; + } + case PHY_RECEIVE_DELAY1: + { + phyParam.Value = AU915_RECEIVE_DELAY1; + break; + } + case PHY_RECEIVE_DELAY2: + { + phyParam.Value = AU915_RECEIVE_DELAY2; + break; + } + case PHY_JOIN_ACCEPT_DELAY1: + { + phyParam.Value = AU915_JOIN_ACCEPT_DELAY1; + break; + } + case PHY_JOIN_ACCEPT_DELAY2: + { + phyParam.Value = AU915_JOIN_ACCEPT_DELAY2; + break; + } + case PHY_MAX_FCNT_GAP: + { + phyParam.Value = AU915_MAX_FCNT_GAP; + break; + } + case PHY_ACK_TIMEOUT: + { + phyParam.Value = ( AU915_ACKTIMEOUT + randr( -AU915_ACK_TIMEOUT_RND, AU915_ACK_TIMEOUT_RND ) ); + break; + } + case PHY_DEF_DR1_OFFSET: + { + phyParam.Value = AU915_DEFAULT_RX1_DR_OFFSET; + break; + } + case PHY_DEF_RX2_FREQUENCY: + { + phyParam.Value = AU915_RX_WND_2_FREQ; + break; + } + case PHY_DEF_RX2_DR: + { + phyParam.Value = AU915_RX_WND_2_DR; + break; + } + case PHY_CHANNELS_MASK: + { + phyParam.ChannelsMask = NvmCtx.ChannelsMask; + break; + } + case PHY_CHANNELS_DEFAULT_MASK: + { + phyParam.ChannelsMask = NvmCtx.ChannelsDefaultMask; + break; + } + case PHY_MAX_NB_CHANNELS: + { + phyParam.Value = AU915_MAX_NB_CHANNELS; + break; + } + case PHY_CHANNELS: + { + phyParam.Channels = NvmCtx.Channels; + break; + } + case PHY_DEF_UPLINK_DWELL_TIME: + { + phyParam.Value = AU915_DEFAULT_UPLINK_DWELL_TIME; + break; + } + case PHY_DEF_DOWNLINK_DWELL_TIME: + { + phyParam.Value = AU915_DEFAULT_DOWNLINK_DWELL_TIME; + break; + } + case PHY_DEF_MAX_EIRP: + { + phyParam.fValue = AU915_DEFAULT_MAX_EIRP; + break; + } + case PHY_DEF_ANTENNA_GAIN: + { + phyParam.fValue = AU915_DEFAULT_ANTENNA_GAIN; + break; + } + case PHY_BEACON_FORMAT: + { + phyParam.BeaconFormat.BeaconSize = AU915_BEACON_SIZE; + phyParam.BeaconFormat.Rfu1Size = AU915_RFU1_SIZE; + phyParam.BeaconFormat.Rfu2Size = AU915_RFU2_SIZE; + break; + } + case PHY_BEACON_CHANNEL_DR: + { + phyParam.Value = AU915_BEACON_CHANNEL_DR; + break; + } + case PHY_BEACON_CHANNEL_STEPWIDTH: + { + phyParam.Value = AU915_BEACON_CHANNEL_STEPWIDTH; + break; + } + case PHY_BEACON_NB_CHANNELS: + { + phyParam.Value = AU915_BEACON_NB_CHANNELS; + break; + } + case PHY_PING_SLOT_CHANNEL_DR: + { + phyParam.Value = AU915_PING_SLOT_CHANNEL_DR; + break; + } + default: + { + break; + } + } + + return phyParam; +} + +void RegionAU915SetBandTxDone( SetBandTxDoneParams_t* txDone ) +{ + RegionCommonSetBandTxDone( txDone->Joined, &NvmCtx.Bands[NvmCtx.Channels[txDone->Channel].Band], txDone->LastTxDoneTime ); +} + +void RegionAU915InitDefaults( InitDefaultsParams_t* params ) +{ + Band_t bands[AU915_MAX_NB_BANDS] = + { + AU915_BAND0 + }; + + switch( params->Type ) + { + case INIT_TYPE_INIT: + { + // Initialize bands + memcpy1( ( uint8_t* )NvmCtx.Bands, ( uint8_t* )bands, sizeof( Band_t ) * AU915_MAX_NB_BANDS ); + + // Channels + // 125 kHz channels + for( uint8_t i = 0; i < AU915_MAX_NB_CHANNELS - 8; i++ ) + { + NvmCtx.Channels[i].Frequency = 915200000 + i * 200000; + NvmCtx.Channels[i].DrRange.Value = ( DR_5 << 4 ) | DR_0; + NvmCtx.Channels[i].Band = 0; + } + // 500 kHz channels + for( uint8_t i = AU915_MAX_NB_CHANNELS - 8; i < AU915_MAX_NB_CHANNELS; i++ ) + { + NvmCtx.Channels[i].Frequency = 915900000 + ( i - ( AU915_MAX_NB_CHANNELS - 8 ) ) * 1600000; + NvmCtx.Channels[i].DrRange.Value = ( DR_6 << 4 ) | DR_6; + NvmCtx.Channels[i].Band = 0; + } + + // Initialize channels default mask + NvmCtx.ChannelsDefaultMask[0] = 0xFFFF; + NvmCtx.ChannelsDefaultMask[1] = 0xFFFF; + NvmCtx.ChannelsDefaultMask[2] = 0xFFFF; + NvmCtx.ChannelsDefaultMask[3] = 0xFFFF; + NvmCtx.ChannelsDefaultMask[4] = 0x00FF; + NvmCtx.ChannelsDefaultMask[5] = 0x0000; + + // Copy channels default mask + RegionCommonChanMaskCopy( NvmCtx.ChannelsMask, NvmCtx.ChannelsDefaultMask, 6 ); + + // Copy into channels mask remaining + RegionCommonChanMaskCopy( NvmCtx.ChannelsMaskRemaining, NvmCtx.ChannelsMask, 6 ); + break; + } + case INIT_TYPE_RESTORE_CTX: + { + if( params->NvmCtx != 0 ) + { + memcpy1( (uint8_t*) &NvmCtx, (uint8_t*) params->NvmCtx, sizeof( NvmCtx ) ); + } + break; + } + case INIT_TYPE_RESTORE_DEFAULT_CHANNELS: + { + // Copy channels default mask + RegionCommonChanMaskCopy( NvmCtx.ChannelsMask, NvmCtx.ChannelsDefaultMask, 6 ); + + for( uint8_t i = 0; i < 6; i++ ) + { // Copy-And the channels mask + NvmCtx.ChannelsMaskRemaining[i] &= NvmCtx.ChannelsMask[i]; + } + break; + } + default: + { + break; + } + } +} + +void* RegionAU915GetNvmCtx( GetNvmCtxParams_t* params ) +{ + params->nvmCtxSize = sizeof( RegionAU915NvmCtx_t ); + return &NvmCtx; +} + +bool RegionAU915Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ) +{ + switch( phyAttribute ) + { + case PHY_FREQUENCY: + { + return VerifyRfFreq( verify->Frequency ); + } + case PHY_TX_DR: + case PHY_DEF_TX_DR: + { + if( verify->DatarateParams.UplinkDwellTime == 0 ) + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, AU915_TX_MIN_DATARATE, AU915_TX_MAX_DATARATE ); + } + else + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, AU915_DWELL_LIMIT_DATARATE, AU915_TX_MAX_DATARATE ); + } + } + case PHY_RX_DR: + { + if( verify->DatarateParams.UplinkDwellTime == 0 ) + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, AU915_RX_MIN_DATARATE, AU915_RX_MAX_DATARATE ); + } + else + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, AU915_DWELL_LIMIT_DATARATE, AU915_RX_MAX_DATARATE ); + } + } + case PHY_DEF_TX_POWER: + case PHY_TX_POWER: + { + // Remark: switched min and max! + return RegionCommonValueInRange( verify->TxPower, AU915_MAX_TX_POWER, AU915_MIN_TX_POWER ); + } + case PHY_DUTY_CYCLE: + { + return AU915_DUTY_CYCLE_ENABLED; + } + default: + return false; + } +} + +void RegionAU915ApplyCFList( ApplyCFListParams_t* applyCFList ) +{ + // Size of the optional CF list must be 16 byte + if( applyCFList->Size != 16 ) + { + return; + } + + // Last byte CFListType must be 0x01 to indicate the CFList contains a series of ChMask fields + if( applyCFList->Payload[15] != 0x01 ) + { + return; + } + + // ChMask0 - ChMask4 must be set (every ChMask has 16 bit) + for( uint8_t chMaskItr = 0, cntPayload = 0; chMaskItr <= 4; chMaskItr++, cntPayload+=2 ) + { + NvmCtx.ChannelsMask[chMaskItr] = (uint16_t) (0x00FF & applyCFList->Payload[cntPayload]); + NvmCtx.ChannelsMask[chMaskItr] |= (uint16_t) (applyCFList->Payload[cntPayload+1] << 8); + if( chMaskItr == 4 ) + { + NvmCtx.ChannelsMask[chMaskItr] = NvmCtx.ChannelsMask[chMaskItr] & CHANNELS_MASK_500KHZ_MASK; + } + // Set the channel mask to the remaining + NvmCtx.ChannelsMaskRemaining[chMaskItr] &= NvmCtx.ChannelsMask[chMaskItr]; + } +} + +bool RegionAU915ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ) +{ + switch( chanMaskSet->ChannelsMaskType ) + { + case CHANNELS_MASK: + { + RegionCommonChanMaskCopy( NvmCtx.ChannelsMask, chanMaskSet->ChannelsMaskIn, 6 ); + + NvmCtx.ChannelsDefaultMask[4] = NvmCtx.ChannelsDefaultMask[4] & CHANNELS_MASK_500KHZ_MASK; + NvmCtx.ChannelsDefaultMask[5] = 0x0000; + + for( uint8_t i = 0; i < 6; i++ ) + { // Copy-And the channels mask + NvmCtx.ChannelsMaskRemaining[i] &= NvmCtx.ChannelsMask[i]; + } + break; + } + case CHANNELS_DEFAULT_MASK: + { + RegionCommonChanMaskCopy( NvmCtx.ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 6 ); + break; + } + default: + return false; + } + return true; +} + +void RegionAU915ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ) +{ + double tSymbol = 0.0; + + // Get the datarate, perform a boundary check + rxConfigParams->Datarate = MIN( datarate, AU915_RX_MAX_DATARATE ); + rxConfigParams->Bandwidth = GetBandwidth( rxConfigParams->Datarate ); + + tSymbol = RegionCommonComputeSymbolTimeLoRa( DataratesAU915[rxConfigParams->Datarate], BandwidthsAU915[rxConfigParams->Datarate] ); + + RegionCommonComputeRxWindowParameters( tSymbol, minRxSymbols, rxError, Radio.GetWakeupTime( ), &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); +} + +bool RegionAU915RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) +{ + int8_t dr = rxConfig->Datarate; + uint8_t maxPayload = 0; + int8_t phyDr = 0; + uint32_t frequency = rxConfig->Frequency; + const char *slotStrings[] = { "1", "2", "C", "Multi_C", "P", "Multi_P" }; + + if( Radio.GetStatus( ) != RF_IDLE ) + { + return false; + } + + if( rxConfig->RxSlot == RX_SLOT_WIN_1 ) + { + // Apply window 1 frequency + frequency = AU915_FIRST_RX1_CHANNEL + ( rxConfig->Channel % 8 ) * AU915_STEPWIDTH_RX1_CHANNEL; + } + + // Read the physical datarate from the datarates table + phyDr = DataratesAU915[dr]; + + Radio.SetChannel( frequency ); + + // Radio configuration + Radio.SetRxConfig( MODEM_LORA, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous ); + + if( rxConfig->RepeaterSupport == true ) + { + maxPayload = MaxPayloadOfDatarateRepeaterDwell0AU915[dr]; + } + else + { + maxPayload = MaxPayloadOfDatarateDwell0AU915[dr]; + } + Radio.SetMaxPayloadLength( MODEM_LORA, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD ); + MW_LOG( "RX on freq %d Hz at DR %d\n\r", frequency, dr ); + if ( rxConfig->RxSlot < RX_SLOT_NONE ) + { + MW_LOG( "RX_%s on freq %d Hz at DR %d\n\r", slotStrings[rxConfig->RxSlot], frequency, dr ); + } + else + { + MW_LOG( "RX on freq %d Hz at DR %d\n\r", frequency, dr ); + } + + *datarate = (uint8_t) dr; + return true; +} + +bool RegionAU915TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ) +{ + int8_t phyDr = DataratesAU915[txConfig->Datarate]; + int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, NvmCtx.Bands[NvmCtx.Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, NvmCtx.ChannelsMask ); + uint32_t bandwidth = GetBandwidth( txConfig->Datarate ); + int8_t phyTxPower = 0; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain ); + + // Setup the radio frequency + Radio.SetChannel( NvmCtx.Channels[txConfig->Channel].Frequency ); + + Radio.SetTxConfig( MODEM_LORA, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 3000 ); + MW_LOG( "TX on freq %d Hz at DR %d\n\r", NvmCtx.Channels[txConfig->Channel].Frequency, txConfig->Datarate ); + + // Setup maximum payload lenght of the radio driver + Radio.SetMaxPayloadLength( MODEM_LORA, txConfig->PktLen ); + + *txTimeOnAir = Radio.TimeOnAir( MODEM_LORA, txConfig->PktLen ); + *txPower = txPowerLimited; + + return true; +} + +uint8_t RegionAU915LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ) +{ + uint8_t status = 0x07; + RegionCommonLinkAdrParams_t linkAdrParams; + uint8_t nextIndex = 0; + uint8_t bytesProcessed = 0; + uint16_t channelsMask[6] = { 0, 0, 0, 0, 0, 0 }; + GetPhyParams_t getPhy; + PhyParam_t phyParam; + RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams; + + // Initialize local copy of channels mask + RegionCommonChanMaskCopy( channelsMask, NvmCtx.ChannelsMask, 6 ); + + while( bytesProcessed < linkAdrReq->PayloadSize ) + { + nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams ); + + if( nextIndex == 0 ) + break; // break loop, since no more request has been found + + // Update bytes processed + bytesProcessed += nextIndex; + + // Revert status, as we only check the last ADR request for the channel mask KO + status = 0x07; + + if( linkAdrParams.ChMaskCtrl == 6 ) + { + // Enable all 125 kHz channels + channelsMask[0] = 0xFFFF; + channelsMask[1] = 0xFFFF; + channelsMask[2] = 0xFFFF; + channelsMask[3] = 0xFFFF; + // Apply chMask to channels 64 to 71 + channelsMask[4] = linkAdrParams.ChMask & CHANNELS_MASK_500KHZ_MASK; + } + else if( linkAdrParams.ChMaskCtrl == 7 ) + { + // Disable all 125 kHz channels + channelsMask[0] = 0x0000; + channelsMask[1] = 0x0000; + channelsMask[2] = 0x0000; + channelsMask[3] = 0x0000; + // Apply chMask to channels 64 to 71 + channelsMask[4] = linkAdrParams.ChMask & CHANNELS_MASK_500KHZ_MASK; + } + else if( linkAdrParams.ChMaskCtrl == 5 ) + { + // Start value for comparision + uint8_t bitMask = 1; + + // cntChannelMask for channelsMask[0] until channelsMask[3] + uint8_t cntChannelMask = 0; + + // i will be 1, 2, 3, ..., 7 + for( uint8_t i = 0; i <= 7; i++ ) + { + // 8 MSBs of ChMask are RFU + // Checking if the ChMask is set, then true + if( ( ( linkAdrParams.ChMask & 0x00FF ) & ( bitMask << i ) ) != 0 ) + { + if( ( i % 2 ) == 0 ) + { + // Enable a bank of 8 125kHz channels, 8 LSBs + channelsMask[cntChannelMask] |= 0x00FF; + // Enable the corresponding 500kHz channel + channelsMask[4] |= ( bitMask << i ); + } + else + { + // Enable a bank of 8 125kHz channels, 8 MSBs + channelsMask[cntChannelMask] |= 0xFF00; + // Enable the corresponding 500kHz channel + channelsMask[4] |= ( bitMask << i ); + // cntChannelMask increment for uneven i + cntChannelMask++; + } + } + // ChMask is not set + else + { + if( ( i % 2 ) == 0 ) + { + // Disable a bank of 8 125kHz channels, 8 LSBs + channelsMask[cntChannelMask] &= 0xFF00; + // Disable the corresponding 500kHz channel + channelsMask[4] &= ~( bitMask << i ); + } + else + { + // Enable a bank of 8 125kHz channels, 8 MSBs + channelsMask[cntChannelMask] &= 0x00FF; + // Disable the corresponding 500kHz channel + channelsMask[4] &= ~( bitMask << i ); + // cntChannelMask increment for uneven i + cntChannelMask++; + } + } + } + } + else + { + channelsMask[linkAdrParams.ChMaskCtrl] = linkAdrParams.ChMask; + } + } + + // FCC 15.247 paragraph F mandates to hop on at least 2 125 kHz channels + if( ( linkAdrParams.Datarate < DR_6 ) && ( RegionCommonCountChannels( channelsMask, 0, 4 ) < 2 ) ) + { + status &= 0xFE; // Channel mask KO + } + + // Get the minimum possible datarate + getPhy.Attribute = PHY_MIN_TX_DR; + getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime; + phyParam = RegionAU915GetPhyParam( &getPhy ); + + linkAdrVerifyParams.Status = status; + linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled; + linkAdrVerifyParams.Datarate = linkAdrParams.Datarate; + linkAdrVerifyParams.TxPower = linkAdrParams.TxPower; + linkAdrVerifyParams.NbRep = linkAdrParams.NbRep; + linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate; + linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower; + linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep; + linkAdrVerifyParams.NbChannels = AU915_MAX_NB_CHANNELS; + linkAdrVerifyParams.ChannelsMask = channelsMask; + linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value; + linkAdrVerifyParams.MaxDatarate = AU915_TX_MAX_DATARATE; + linkAdrVerifyParams.Channels = NvmCtx.Channels; + linkAdrVerifyParams.MinTxPower = AU915_MIN_TX_POWER; + linkAdrVerifyParams.MaxTxPower = AU915_MAX_TX_POWER; + linkAdrVerifyParams.Version = linkAdrReq->Version; + + // Verify the parameters and update, if necessary + status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep ); + + // Update channelsMask if everything is correct + if( status == 0x07 ) + { + // Copy Mask + RegionCommonChanMaskCopy( NvmCtx.ChannelsMask, channelsMask, 6 ); + + NvmCtx.ChannelsMaskRemaining[0] &= NvmCtx.ChannelsMask[0]; + NvmCtx.ChannelsMaskRemaining[1] &= NvmCtx.ChannelsMask[1]; + NvmCtx.ChannelsMaskRemaining[2] &= NvmCtx.ChannelsMask[2]; + NvmCtx.ChannelsMaskRemaining[3] &= NvmCtx.ChannelsMask[3]; + NvmCtx.ChannelsMaskRemaining[4] = NvmCtx.ChannelsMask[4]; + NvmCtx.ChannelsMaskRemaining[5] = NvmCtx.ChannelsMask[5]; + } + + // Update status variables + *drOut = linkAdrParams.Datarate; + *txPowOut = linkAdrParams.TxPower; + *nbRepOut = linkAdrParams.NbRep; + *nbBytesParsed = bytesProcessed; + + return status; +} + +uint8_t RegionAU915RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ) +{ + uint8_t status = 0x07; + + // Verify radio frequency + if( VerifyRfFreq( rxParamSetupReq->Frequency ) == false ) + { + status &= 0xFE; // Channel frequency KO + } + + // Verify datarate + if( RegionCommonValueInRange( rxParamSetupReq->Datarate, AU915_RX_MIN_DATARATE, AU915_RX_MAX_DATARATE ) == false ) + { + status &= 0xFD; // Datarate KO + } + if( ( rxParamSetupReq->Datarate == DR_7 ) || + ( rxParamSetupReq->Datarate > DR_13 ) ) + { + status &= 0xFD; // Datarate KO + } + + // Verify datarate offset + if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, AU915_MIN_RX1_DR_OFFSET, AU915_MAX_RX1_DR_OFFSET ) == false ) + { + status &= 0xFB; // Rx1DrOffset range KO + } + + return status; +} + +uint8_t RegionAU915NewChannelReq( NewChannelReqParams_t* newChannelReq ) +{ + // Datarate and frequency KO + return 0; +} + +int8_t RegionAU915TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ) +{ + // Accept the request + return 0; +} + +uint8_t RegionAU915DlChannelReq( DlChannelReqParams_t* dlChannelReq ) +{ + return 0; +} + +int8_t RegionAU915AlternateDr( int8_t currentDr, AlternateDrType_t type ) +{ + static int8_t trialsCount = 0; + + // Re-enable 500 kHz default channels + NvmCtx.ChannelsMask[4] = CHANNELS_MASK_500KHZ_MASK; + + if( ( trialsCount & 0x01 ) == 0x01 ) + { + currentDr = DR_6; + } + else + { + currentDr = DR_2; + } + trialsCount++; + return currentDr; +} + +void RegionAU915CalcBackOff( CalcBackOffParams_t* calcBackOff ) +{ + RegionCommonCalcBackOffParams_t calcBackOffParams; + + calcBackOffParams.Channels = NvmCtx.Channels; + calcBackOffParams.Bands = NvmCtx.Bands; + calcBackOffParams.LastTxIsJoinRequest = calcBackOff->LastTxIsJoinRequest; + calcBackOffParams.Joined = calcBackOff->Joined; + calcBackOffParams.DutyCycleEnabled = calcBackOff->DutyCycleEnabled; + calcBackOffParams.Channel = calcBackOff->Channel; + calcBackOffParams.ElapsedTime = calcBackOff->ElapsedTime; + calcBackOffParams.TxTimeOnAir = calcBackOff->TxTimeOnAir; + + RegionCommonCalcBackOff( &calcBackOffParams ); +} + +LoRaMacStatus_t RegionAU915NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t delayTx = 0; + uint8_t enabledChannels[AU915_MAX_NB_CHANNELS] = { 0 }; + TimerTime_t nextTxDelay = 0; + + // Count 125kHz channels + if( RegionCommonCountChannels( NvmCtx.ChannelsMaskRemaining, 0, 4 ) == 0 ) + { // Reactivate default channels + RegionCommonChanMaskCopy( NvmCtx.ChannelsMaskRemaining, NvmCtx.ChannelsMask, 4 ); + } + // Check other channels + if( nextChanParams->Datarate >= DR_6 ) + { + if( ( NvmCtx.ChannelsMaskRemaining[4] & CHANNELS_MASK_500KHZ_MASK ) == 0 ) + { + NvmCtx.ChannelsMaskRemaining[4] = NvmCtx.ChannelsMask[4]; + } + } + + TimerTime_t elapsed = TimerGetElapsedTime( nextChanParams->LastAggrTx ); + if( ( nextChanParams->LastAggrTx == 0 ) || ( nextChanParams->AggrTimeOff <= elapsed ) ) + { + // Reset Aggregated time off + *aggregatedTimeOff = 0; + + // Update bands Time OFF + nextTxDelay = RegionCommonUpdateBandTimeOff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, NvmCtx.Bands, AU915_MAX_NB_BANDS ); + + // Search how many channels are enabled + nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Datarate, + NvmCtx.ChannelsMaskRemaining, NvmCtx.Channels, + NvmCtx.Bands, enabledChannels, &delayTx ); + } + else + { + delayTx++; + nextTxDelay = nextChanParams->AggrTimeOff - elapsed; + } + + if( nbEnabledChannels > 0 ) + { + // We found a valid channel + *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )]; + // Disable the channel in the mask + RegionCommonChanDisable( NvmCtx.ChannelsMaskRemaining, *channel, AU915_MAX_NB_CHANNELS - 8 ); + + *time = 0; + return LORAMAC_STATUS_OK; + } + else + { + if( delayTx > 0 ) + { + // Delay transmission due to AggregatedTimeOff or to a band time off + *time = nextTxDelay; + return LORAMAC_STATUS_DUTYCYCLE_RESTRICTED; + } + // Datarate not supported by any channel + *time = 0; + return LORAMAC_STATUS_NO_CHANNEL_FOUND; + } +} + +LoRaMacStatus_t RegionAU915ChannelAdd( ChannelAddParams_t* channelAdd ) +{ + return LORAMAC_STATUS_PARAMETER_INVALID; +} + +bool RegionAU915ChannelsRemove( ChannelRemoveParams_t* channelRemove ) +{ + return LORAMAC_STATUS_PARAMETER_INVALID; +} + +void RegionAU915SetContinuousWave( ContinuousWaveParams_t* continuousWave ) +{ + int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, NvmCtx.Bands[NvmCtx.Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, NvmCtx.ChannelsMask ); + int8_t phyTxPower = 0; + uint32_t frequency = NvmCtx.Channels[continuousWave->Channel].Frequency; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, continuousWave->MaxEirp, continuousWave->AntennaGain ); + + Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout ); +} + +uint8_t RegionAU915ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) +{ + int8_t datarate = DatarateOffsetsAU915[dr][drOffset]; + + if( datarate < 0 ) + { + if( downlinkDwellTime == 0 ) + { + datarate = AU915_TX_MIN_DATARATE; + } + else + { + datarate = AU915_DWELL_LIMIT_DATARATE; + } + } + return datarate; +} + +void RegionAU915RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ) +{ + RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup; + + regionCommonRxBeaconSetup.Datarates = DataratesAU915; + regionCommonRxBeaconSetup.Frequency = rxBeaconSetup->Frequency; + regionCommonRxBeaconSetup.BeaconSize = AU915_BEACON_SIZE; + regionCommonRxBeaconSetup.BeaconDatarate = AU915_BEACON_CHANNEL_DR; + regionCommonRxBeaconSetup.BeaconChannelBW = AU915_BEACON_CHANNEL_BW; + regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime; + regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout; + + RegionCommonRxBeaconSetup( ®ionCommonRxBeaconSetup ); + + // Store downlink datarate + *outDr = AU915_BEACON_CHANNEL_DR; +} diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionAU915.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionAU915.h new file mode 100644 index 00000000..1af8c789 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionAU915.h @@ -0,0 +1,540 @@ +/*! + * \file RegionAU915.h + * + * \brief Region definition for AU915 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + * + * \defgroup REGIONAU915 Region AU915 + * Implementation according to LoRaWAN Specification v1.0.2. + * \{ + */ +#ifndef __REGION_AU915_H__ +#define __REGION_AU915_H__ + +#include "region/Region.h" + +/*! + * LoRaMac maximum number of channels + */ +#define AU915_MAX_NB_CHANNELS 72 + +/*! + * Minimal datarate that can be used by the node + */ +#define AU915_TX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define AU915_TX_MAX_DATARATE DR_13 + +/*! + * Minimal datarate that can be used by the node + */ +#define AU915_RX_MIN_DATARATE DR_8 + +/*! + * Maximal datarate that can be used by the node + */ +#define AU915_RX_MAX_DATARATE DR_13 + +/*! + * Default datarate used by the node + */ +#define AU915_DEFAULT_DATARATE DR_2 + +/*! + * The minimum datarate which is used when the + * dwell time is limited. + */ +#define AU915_DWELL_LIMIT_DATARATE DR_2 + +/*! + * Minimal Rx1 receive datarate offset + */ +#define AU915_MIN_RX1_DR_OFFSET 0 + +/*! + * Maximal Rx1 receive datarate offset + */ +#define AU915_MAX_RX1_DR_OFFSET 6 + +/*! + * Default Rx1 receive datarate offset + */ +#define AU915_DEFAULT_RX1_DR_OFFSET 0 + +/*! + * Minimal Tx output power that can be used by the node + */ +#define AU915_MIN_TX_POWER TX_POWER_14 + +/*! + * Maximal Tx output power that can be used by the node + */ +#define AU915_MAX_TX_POWER TX_POWER_0 + +/*! + * Default Tx output power used by the node + */ +#define AU915_DEFAULT_TX_POWER TX_POWER_0 + +/*! + * Default uplink dwell time configuration + */ +#define AU915_DEFAULT_UPLINK_DWELL_TIME 1 + +/*! + * Default downlink dwell time configuration + */ +#define AU915_DEFAULT_DOWNLINK_DWELL_TIME 0 + +/*! + * Default Max EIRP + */ +#define AU915_DEFAULT_MAX_EIRP 30.0f + +/*! + * Default antenna gain + */ +#define AU915_DEFAULT_ANTENNA_GAIN 2.15f + +/*! + * ADR Ack limit + */ +#define AU915_ADR_ACK_LIMIT 64 + +/*! + * ADR Ack delay + */ +#define AU915_ADR_ACK_DELAY 32 + +/*! + * Enabled or disabled the duty cycle + */ +#define AU915_DUTY_CYCLE_ENABLED 0 + +/*! + * Maximum RX window duration + */ +#define AU915_MAX_RX_WINDOW 3000 + +/*! + * Receive delay 1 + */ +#define AU915_RECEIVE_DELAY1 1000 + +/*! + * Receive delay 2 + */ +#define AU915_RECEIVE_DELAY2 2000 + +/*! + * Join accept delay 1 + */ +#define AU915_JOIN_ACCEPT_DELAY1 5000 + +/*! + * Join accept delay 2 + */ +#define AU915_JOIN_ACCEPT_DELAY2 6000 + +/*! + * Maximum frame counter gap + */ +#define AU915_MAX_FCNT_GAP 16384 + +/*! + * Ack timeout + */ +#define AU915_ACKTIMEOUT 2000 + +/*! + * Random ack timeout limits + */ +#define AU915_ACK_TIMEOUT_RND 1000 + +/*! + * Second reception window channel frequency definition. + */ +#define AU915_RX_WND_2_FREQ 923300000 + +/*! + * Second reception window channel datarate definition. + */ +#define AU915_RX_WND_2_DR DR_8 + +/* + * CLASS B + */ +/*! + * Beacon frequency + */ +#define AU915_BEACON_CHANNEL_FREQ 923300000 + +/*! + * Beacon frequency channel stepwidth + */ +#define AU915_BEACON_CHANNEL_STEPWIDTH 600000 + +/*! + * Number of possible beacon channels + */ +#define AU915_BEACON_NB_CHANNELS 8 + +/*! + * Payload size of a beacon frame + */ +#define AU915_BEACON_SIZE 19 + +/*! + * Size of RFU 1 field + */ +#define AU915_RFU1_SIZE 3 + +/*! + * Size of RFU 2 field + */ +#define AU915_RFU2_SIZE 1 + +/*! + * Datarate of the beacon channel + */ +#define AU915_BEACON_CHANNEL_DR DR_10 + +/*! + * Bandwith of the beacon channel + */ +#define AU915_BEACON_CHANNEL_BW 2 + +/*! + * Ping slot channel datarate + */ +#define AU915_PING_SLOT_CHANNEL_DR DR_10 + +/*! + * LoRaMac maximum number of bands + */ +#define AU915_MAX_NB_BANDS 1 + +/*! + * Band 0 definition + * { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff } + */ +#define AU915_BAND0 { 1, AU915_MAX_TX_POWER, 0, 0, 0 } // 100.0 % + +/*! + * Defines the first channel for RX window 1 for US band + */ +#define AU915_FIRST_RX1_CHANNEL ( (uint32_t) 923300000 ) + +/*! + * Defines the last channel for RX window 1 for US band + */ +#define AU915_LAST_RX1_CHANNEL ( (uint32_t) 927500000 ) + +/*! + * Defines the step width of the channels for RX window 1 + */ +#define AU915_STEPWIDTH_RX1_CHANNEL ( (uint32_t) 600000 ) + +/*! + * Data rates table definition + */ +static const uint8_t DataratesAU915[] = { 12, 11, 10, 9, 8, 7, 8, 0, 12, 11, 10, 9, 8, 7, 0, 0 }; + +/*! + * Bandwidths table definition in Hz + */ +static const uint32_t BandwidthsAU915[] = { 125000, 125000, 125000, 125000, 125000, 125000, 500000, 0, 500000, 500000, 500000, 500000, 500000, 500000, 0, 0 }; + +/*! + * Up/Down link data rates offset definition + */ +static const int8_t DatarateOffsetsAU915[7][6] = +{ + { DR_8 , DR_8 , DR_8 , DR_8 , DR_8 , DR_8 }, // DR_0 + { DR_9 , DR_8 , DR_8 , DR_8 , DR_8 , DR_8 }, // DR_1 + { DR_10, DR_9 , DR_8 , DR_8 , DR_8 , DR_8 }, // DR_2 + { DR_11, DR_10, DR_9 , DR_8 , DR_8 , DR_8 }, // DR_3 + { DR_12, DR_11, DR_10, DR_9 , DR_8 , DR_8 }, // DR_4 + { DR_13, DR_12, DR_11, DR_10, DR_9 , DR_8 }, // DR_5 + { DR_13, DR_13, DR_12, DR_11, DR_10, DR_9 }, // DR_6 +}; + +/*! + * Maximum payload with respect to the datarate index. Cannot operate with repeater. + * The table is valid for the dwell time configuration of 0 for uplinks. + */ +static const uint8_t MaxPayloadOfDatarateDwell0AU915[] = { 51, 51, 51, 115, 242, 242, 242, 0, 53, 129, 242, 242, 242, 242 }; + +/*! + * Maximum payload with respect to the datarate index. Can operate with repeater. + * The table is valid for the dwell time configuration of 0 for uplinks. The table provides + * repeater support. + */ +static const uint8_t MaxPayloadOfDatarateRepeaterDwell0AU915[] = { 51, 51, 51, 115, 222, 222, 222, 0, 33, 109, 222, 222, 222, 222 }; + +/*! + * Maximum payload with respect to the datarate index. Cannot operate with repeater. + * The table is valid for the dwell time configuration of 1 for uplinks. + */ +static const uint8_t MaxPayloadOfDatarateDwell1AU915[] = { 0, 0, 11, 53, 125, 242, 242, 0, 53, 129, 242, 242, 242, 242 }; + +/*! + * Maximum payload with respect to the datarate index. Can operate with repeater. + * The table is valid for the dwell time configuration of 1 for uplinks. The table provides + * repeater support. + */ +static const uint8_t MaxPayloadOfDatarateRepeaterDwell1AU915[] = { 0, 0, 11, 53, 125, 242, 242, 0, 33, 109, 222, 222, 222, 222 }; + +/*! + * \brief The function gets a value of a specific phy attribute. + * + * \param [IN] getPhy Pointer to the function parameters. + * + * \retval Returns a structure containing the PHY parameter. + */ +PhyParam_t RegionAU915GetPhyParam( GetPhyParams_t* getPhy ); + +/*! + * \brief Updates the last TX done parameters of the current channel. + * + * \param [IN] txDone Pointer to the function parameters. + */ +void RegionAU915SetBandTxDone( SetBandTxDoneParams_t* txDone ); + +/*! + * \brief Initializes the channels masks and the channels. + * + * \param [IN] type Sets the initialization type. + */ +void RegionAU915InitDefaults( InitDefaultsParams_t* params ); + +/*! + * \brief Returns a pointer to the internal context and its size. + * + * \param [OUT] params Pointer to the function parameters. + * + * \retval Points to a structure where the module store its non-volatile context. + */ +void* RegionAU915GetNvmCtx( GetNvmCtxParams_t* params ); + +/*! + * \brief Verifies a parameter. + * + * \param [IN] verify Pointer to the function parameters. + * + * \param [IN] type Sets the initialization type. + * + * \retval Returns true, if the parameter is valid. + */ +bool RegionAU915Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ); + +/*! + * \brief The function parses the input buffer and sets up the channels of the + * CF list. + * + * \param [IN] applyCFList Pointer to the function parameters. + */ +void RegionAU915ApplyCFList( ApplyCFListParams_t* applyCFList ); + +/*! + * \brief Sets a channels mask. + * + * \param [IN] chanMaskSet Pointer to the function parameters. + * + * \retval Returns true, if the channels mask could be set. + */ +bool RegionAU915ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ); + +/*! + * Computes the Rx window timeout and offset. + * + * \param [IN] datarate Rx window datarate index to be used + * + * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame. + * + * \param [IN] rxError System maximum timing error of the receiver. In milliseconds + * The receiver will turn on in a [-rxError : +rxError] ms + * interval around RxOffset + * + * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields. + */ +void RegionAU915ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ); + +/*! + * \brief Configuration of the RX windows. + * + * \param [IN] rxConfig Pointer to the function parameters. + * + * \param [OUT] datarate The datarate index which was set. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionAU915RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ); + +/*! + * \brief TX configuration. + * + * \param [IN] txConfig Pointer to the function parameters. + * + * \param [OUT] txPower The tx power index which was set. + * + * \param [OUT] txTimeOnAir The time-on-air of the frame. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionAU915TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ); + +/*! + * \brief The function processes a Link ADR Request. + * + * \param [IN] linkAdrReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionAU915LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ); + +/*! + * \brief The function processes a RX Parameter Setup Request. + * + * \param [IN] rxParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionAU915RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ); + +/*! + * \brief The function processes a Channel Request. + * + * \param [IN] newChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionAU915NewChannelReq( NewChannelReqParams_t* newChannelReq ); + +/*! + * \brief The function processes a TX ParamSetup Request. + * + * \param [IN] txParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + * Returns -1, if the functionality is not implemented. In this case, the end node + * shall not process the command. + */ +int8_t RegionAU915TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ); + +/*! + * \brief The function processes a DlChannel Request. + * + * \param [IN] dlChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionAU915DlChannelReq( DlChannelReqParams_t* dlChannelReq ); + +/*! + * \brief Alternates the datarate of the channel for the join request. + * + * \param [IN] currentDr Current datarate. + * + * \retval Datarate to apply. + */ +int8_t RegionAU915AlternateDr( int8_t currentDr, AlternateDrType_t type ); + +/*! + * \brief Calculates the back-off time. + * + * \param [IN] calcBackOff Pointer to the function parameters. + */ +void RegionAU915CalcBackOff( CalcBackOffParams_t* calcBackOff ); + +/*! + * \brief Searches and set the next random available channel + * + * \param [OUT] channel Next channel to use for TX. + * + * \param [OUT] time Time to wait for the next transmission according to the duty + * cycle. + * + * \param [OUT] aggregatedTimeOff Updates the aggregated time off. + * + * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate] + */ +LoRaMacStatus_t RegionAU915NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ); + +/*! + * \brief Adds a channel. + * + * \param [IN] channelAdd Pointer to the function parameters. + * + * \retval Status of the operation. + */ +LoRaMacStatus_t RegionAU915ChannelAdd( ChannelAddParams_t* channelAdd ); + +/*! + * \brief Removes a channel. + * + * \param [IN] channelRemove Pointer to the function parameters. + * + * \retval Returns true, if the channel was removed successfully. + */ +bool RegionAU915ChannelsRemove( ChannelRemoveParams_t* channelRemove ); + +/*! + * \brief Sets the radio into continuous wave mode. + * + * \param [IN] continuousWave Pointer to the function parameters. + */ +void RegionAU915SetContinuousWave( ContinuousWaveParams_t* continuousWave ); + +/*! + * \brief Computes new datarate according to the given offset + * + * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms + * + * \param [IN] dr Current datarate + * + * \param [IN] drOffset Offset to be applied + * + * \retval newDr Computed datarate. + */ +uint8_t RegionAU915ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ); + +/*! + * \brief Sets the radio into beacon reception mode + * + * \param [IN] rxBeaconSetup Pointer to the function parameters + */ + void RegionAU915RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ); + +/*! \} defgroup REGIONAU915 */ + +#endif // __REGION_AU915_H__ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCN470.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCN470.c new file mode 100644 index 00000000..975508eb --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCN470.c @@ -0,0 +1,845 @@ +/*! + * \file RegionCN470.c + * + * \brief Region implementation for CN470 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) +*/ +#include "utilities.h" + +#include "RegionCommon.h" +#include "RegionCN470.h" +#include "lorawan_conf.h" +#include "mw_log_conf.h" + +// Definitions +#define CHANNELS_MASK_SIZE 6 + +/*! + * Region specific context + */ +typedef struct sRegionCN470NvmCtx +{ + /*! + * LoRaMAC channels + */ + ChannelParams_t Channels[ CN470_MAX_NB_CHANNELS ]; + /*! + * LoRaMac bands + */ + Band_t Bands[ CN470_MAX_NB_BANDS ]; + /*! + * LoRaMac channels mask + */ + uint16_t ChannelsMask[ CHANNELS_MASK_SIZE ]; + /*! + * LoRaMac channels default mask + */ + uint16_t ChannelsDefaultMask[ CHANNELS_MASK_SIZE ]; +}RegionCN470NvmCtx_t; + +/* + * Non-volatile module context. + */ +static RegionCN470NvmCtx_t NvmCtx; + +// Static functions +static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr ) +{ + uint8_t nextLowerDr = 0; + + if( dr == minDr ) + { + nextLowerDr = minDr; + } + else + { + nextLowerDr = dr - 1; + } + return nextLowerDr; +} + +static uint32_t GetBandwidth( uint32_t drIndex ) +{ + switch( BandwidthsCN470[drIndex] ) + { + default: + case 125000: + return 0; + case 250000: + return 1; + case 500000: + return 2; + } +} + +static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask ) +{ + int8_t txPowerResult = txPower; + + // Limit tx power to the band max + txPowerResult = MAX( txPower, maxBandTxPower ); + + return txPowerResult; +} + +static bool VerifyRfFreq( uint32_t freq ) +{ + // Check radio driver support + if( Radio.CheckRfFrequency( freq ) == false ) + { + return false; + } + + // Rx frequencies + if( ( freq < CN470_FIRST_RX1_CHANNEL ) || + ( freq > CN470_LAST_RX1_CHANNEL ) || + ( ( ( freq - ( uint32_t ) CN470_FIRST_RX1_CHANNEL ) % ( uint32_t ) CN470_STEPWIDTH_RX1_CHANNEL ) != 0 ) ) + { + return false; + } + + // Test for frequency range - take RX and TX freqencies into account + if( ( freq < 470300000 ) || ( freq > 509700000 ) ) + { + return false; + } + return true; +} + +static uint8_t CountNbOfEnabledChannels( uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t delayTransmission = 0; + + for( uint8_t i = 0, k = 0; i < CN470_MAX_NB_CHANNELS; i += 16, k++ ) + { + for( uint8_t j = 0; j < 16; j++ ) + { + if( ( channelsMask[k] & ( 1 << j ) ) != 0 ) + { + if( channels[i + j].Frequency == 0 ) + { // Check if the channel is enabled + continue; + } + if( RegionCommonValueInRange( datarate, channels[i + j].DrRange.Fields.Min, + channels[i + j].DrRange.Fields.Max ) == false ) + { // Check if the current channel selection supports the given datarate + continue; + } + if( bands[channels[i + j].Band].TimeOff > 0 ) + { // Check if the band is available for transmission + delayTransmission++; + continue; + } + enabledChannels[nbEnabledChannels++] = i + j; + } + } + } + + *delayTx = delayTransmission; + return nbEnabledChannels; +} + +PhyParam_t RegionCN470GetPhyParam( GetPhyParams_t* getPhy ) +{ + PhyParam_t phyParam = { 0 }; + + switch( getPhy->Attribute ) + { + case PHY_MIN_RX_DR: + { + phyParam.Value = CN470_RX_MIN_DATARATE; + break; + } + case PHY_MIN_TX_DR: + { + phyParam.Value = CN470_TX_MIN_DATARATE; + break; + } + case PHY_DEF_TX_DR: + { + phyParam.Value = CN470_DEFAULT_DATARATE; + break; + } + case PHY_NEXT_LOWER_TX_DR: + { + phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, CN470_TX_MIN_DATARATE ); + break; + } + case PHY_MAX_TX_POWER: + { + phyParam.Value = CN470_MAX_TX_POWER; + break; + } + case PHY_DEF_TX_POWER: + { + phyParam.Value = CN470_DEFAULT_TX_POWER; + break; + } + case PHY_DEF_ADR_ACK_LIMIT: + { + phyParam.Value = CN470_ADR_ACK_LIMIT; + break; + } + case PHY_DEF_ADR_ACK_DELAY: + { + phyParam.Value = CN470_ADR_ACK_DELAY; + break; + } + case PHY_MAX_PAYLOAD: + { + phyParam.Value = MaxPayloadOfDatarateCN470[getPhy->Datarate]; + break; + } + case PHY_MAX_PAYLOAD_REPEATER: + { + phyParam.Value = MaxPayloadOfDatarateRepeaterCN470[getPhy->Datarate]; + break; + } + case PHY_DUTY_CYCLE: + { + phyParam.Value = CN470_DUTY_CYCLE_ENABLED; + break; + } + case PHY_MAX_RX_WINDOW: + { + phyParam.Value = CN470_MAX_RX_WINDOW; + break; + } + case PHY_RECEIVE_DELAY1: + { + phyParam.Value = CN470_RECEIVE_DELAY1; + break; + } + case PHY_RECEIVE_DELAY2: + { + phyParam.Value = CN470_RECEIVE_DELAY2; + break; + } + case PHY_JOIN_ACCEPT_DELAY1: + { + phyParam.Value = CN470_JOIN_ACCEPT_DELAY1; + break; + } + case PHY_JOIN_ACCEPT_DELAY2: + { + phyParam.Value = CN470_JOIN_ACCEPT_DELAY2; + break; + } + case PHY_MAX_FCNT_GAP: + { + phyParam.Value = CN470_MAX_FCNT_GAP; + break; + } + case PHY_ACK_TIMEOUT: + { + phyParam.Value = ( CN470_ACKTIMEOUT + randr( -CN470_ACK_TIMEOUT_RND, CN470_ACK_TIMEOUT_RND ) ); + break; + } + case PHY_DEF_DR1_OFFSET: + { + phyParam.Value = CN470_DEFAULT_RX1_DR_OFFSET; + break; + } + case PHY_DEF_RX2_FREQUENCY: + { + phyParam.Value = CN470_RX_WND_2_FREQ; + break; + } + case PHY_DEF_RX2_DR: + { + phyParam.Value = CN470_RX_WND_2_DR; + break; + } + case PHY_CHANNELS_MASK: + { + phyParam.ChannelsMask = NvmCtx.ChannelsMask; + break; + } + case PHY_CHANNELS_DEFAULT_MASK: + { + phyParam.ChannelsMask = NvmCtx.ChannelsDefaultMask; + break; + } + case PHY_MAX_NB_CHANNELS: + { + phyParam.Value = CN470_MAX_NB_CHANNELS; + break; + } + case PHY_CHANNELS: + { + phyParam.Channels = NvmCtx.Channels; + break; + } + case PHY_DEF_UPLINK_DWELL_TIME: + case PHY_DEF_DOWNLINK_DWELL_TIME: + { + phyParam.Value = 0; + break; + } + case PHY_DEF_MAX_EIRP: + { + phyParam.fValue = CN470_DEFAULT_MAX_EIRP; + break; + } + case PHY_DEF_ANTENNA_GAIN: + { + phyParam.fValue = CN470_DEFAULT_ANTENNA_GAIN; + break; + } + case PHY_BEACON_FORMAT: + { + phyParam.BeaconFormat.BeaconSize = CN470_BEACON_SIZE; + phyParam.BeaconFormat.Rfu1Size = CN470_RFU1_SIZE; + phyParam.BeaconFormat.Rfu2Size = CN470_RFU2_SIZE; + break; + } + case PHY_BEACON_CHANNEL_DR: + { + phyParam.Value = CN470_BEACON_CHANNEL_DR; + break; + } + case PHY_BEACON_CHANNEL_STEPWIDTH: + { + phyParam.Value = CN470_BEACON_CHANNEL_STEPWIDTH; + break; + } + case PHY_BEACON_NB_CHANNELS: + { + phyParam.Value = CN470_BEACON_NB_CHANNELS; + break; + } + case PHY_PING_SLOT_CHANNEL_DR: + { + phyParam.Value = CN470_PING_SLOT_CHANNEL_DR; + break; + } + default: + { + break; + } + } + + return phyParam; +} + +void RegionCN470SetBandTxDone( SetBandTxDoneParams_t* txDone ) +{ + RegionCommonSetBandTxDone( txDone->Joined, &NvmCtx.Bands[NvmCtx.Channels[txDone->Channel].Band], txDone->LastTxDoneTime ); +} + +void RegionCN470InitDefaults( InitDefaultsParams_t* params ) +{ + Band_t bands[CN470_MAX_NB_BANDS] = + { + CN470_BAND0 + }; + + switch( params->Type ) + { + case INIT_TYPE_INIT: + { + // Initialize bands + memcpy1( ( uint8_t* )NvmCtx.Bands, ( uint8_t* )bands, sizeof( Band_t ) * CN470_MAX_NB_BANDS ); + + // Channels + // 125 kHz channels + for( uint8_t i = 0; i < CN470_MAX_NB_CHANNELS; i++ ) + { + NvmCtx.Channels[i].Frequency = 470300000 + i * 200000; + NvmCtx.Channels[i].DrRange.Value = ( DR_5 << 4 ) | DR_0; + NvmCtx.Channels[i].Band = 0; + } + + // Initialize the channels default mask + NvmCtx.ChannelsDefaultMask[0] = 0xFFFF; + NvmCtx.ChannelsDefaultMask[1] = 0xFFFF; + NvmCtx.ChannelsDefaultMask[2] = 0xFFFF; + NvmCtx.ChannelsDefaultMask[3] = 0xFFFF; + NvmCtx.ChannelsDefaultMask[4] = 0xFFFF; + NvmCtx.ChannelsDefaultMask[5] = 0xFFFF; + + // Update the channels mask + RegionCommonChanMaskCopy( NvmCtx.ChannelsMask, NvmCtx.ChannelsDefaultMask, 6 ); + break; + } + case INIT_TYPE_RESTORE_CTX: + { + if( params->NvmCtx != 0 ) + { + memcpy1( (uint8_t*) &NvmCtx, (uint8_t*) params->NvmCtx, sizeof( NvmCtx ) ); + } + break; + } + case INIT_TYPE_RESTORE_DEFAULT_CHANNELS: + { + // Restore channels default mask + RegionCommonChanMaskCopy( NvmCtx.ChannelsMask, NvmCtx.ChannelsDefaultMask, 6 ); + break; + } + default: + { + break; + } + } +} + +void* RegionCN470GetNvmCtx( GetNvmCtxParams_t* params ) +{ + params->nvmCtxSize = sizeof( RegionCN470NvmCtx_t ); + return &NvmCtx; +} + +bool RegionCN470Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ) +{ + switch( phyAttribute ) + { + case PHY_FREQUENCY: + { + return VerifyRfFreq( verify->Frequency ); + } + case PHY_TX_DR: + case PHY_DEF_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, CN470_TX_MIN_DATARATE, CN470_TX_MAX_DATARATE ); + } + case PHY_RX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, CN470_RX_MIN_DATARATE, CN470_RX_MAX_DATARATE ); + } + case PHY_DEF_TX_POWER: + case PHY_TX_POWER: + { + // Remark: switched min and max! + return RegionCommonValueInRange( verify->TxPower, CN470_MAX_TX_POWER, CN470_MIN_TX_POWER ); + } + case PHY_DUTY_CYCLE: + { + return CN470_DUTY_CYCLE_ENABLED; + } + default: + return false; + } +} + +void RegionCN470ApplyCFList( ApplyCFListParams_t* applyCFList ) +{ + // Size of the optional CF list must be 16 byte + if( applyCFList->Size != 16 ) + { + return; + } + + // Last byte CFListType must be 0x01 to indicate the CFList contains a series of ChMask fields + if( applyCFList->Payload[15] != 0x01 ) + { + return; + } + + // ChMask0 - ChMask5 must be set (every ChMask has 16 bit) + for( uint8_t chMaskItr = 0, cntPayload = 0; chMaskItr <= 5; chMaskItr++, cntPayload+=2 ) + { + NvmCtx.ChannelsMask[chMaskItr] = (uint16_t) (0x00FF & applyCFList->Payload[cntPayload]); + NvmCtx.ChannelsMask[chMaskItr] |= (uint16_t) (applyCFList->Payload[cntPayload+1] << 8); + } +} + +bool RegionCN470ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ) +{ + switch( chanMaskSet->ChannelsMaskType ) + { + case CHANNELS_MASK: + { + RegionCommonChanMaskCopy( NvmCtx.ChannelsMask, chanMaskSet->ChannelsMaskIn, 6 ); + break; + } + case CHANNELS_DEFAULT_MASK: + { + RegionCommonChanMaskCopy( NvmCtx.ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 6 ); + break; + } + default: + return false; + } + return true; +} + +void RegionCN470ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ) +{ + double tSymbol = 0.0; + + // Get the datarate, perform a boundary check + rxConfigParams->Datarate = MIN( datarate, CN470_RX_MAX_DATARATE ); + rxConfigParams->Bandwidth = GetBandwidth( rxConfigParams->Datarate ); + + tSymbol = RegionCommonComputeSymbolTimeLoRa( DataratesCN470[rxConfigParams->Datarate], BandwidthsCN470[rxConfigParams->Datarate] ); + + RegionCommonComputeRxWindowParameters( tSymbol, minRxSymbols, rxError, Radio.GetWakeupTime( ), &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); +} + +bool RegionCN470RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) +{ + int8_t dr = rxConfig->Datarate; + uint8_t maxPayload = 0; + int8_t phyDr = 0; + uint32_t frequency = rxConfig->Frequency; + const char *slotStrings[] = { "1", "2", "C", "Multi_C", "P", "Multi_P" }; + + if( Radio.GetStatus( ) != RF_IDLE ) + { + return false; + } + + if( rxConfig->RxSlot == RX_SLOT_WIN_1 ) + { + // Apply window 1 frequency + frequency = CN470_FIRST_RX1_CHANNEL + ( rxConfig->Channel % 48 ) * CN470_STEPWIDTH_RX1_CHANNEL; + } + + // Read the physical datarate from the datarates table + phyDr = DataratesCN470[dr]; + + Radio.SetChannel( frequency ); + + // Radio configuration + Radio.SetRxConfig( MODEM_LORA, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous ); + + if( rxConfig->RepeaterSupport == true ) + { + maxPayload = MaxPayloadOfDatarateRepeaterCN470[dr]; + } + else + { + maxPayload = MaxPayloadOfDatarateCN470[dr]; + } + Radio.SetMaxPayloadLength( MODEM_LORA, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD ); + if ( rxConfig->RxSlot < RX_SLOT_NONE ) + { + MW_LOG( "RX_%s on freq %d Hz at DR %d\n\r", slotStrings[rxConfig->RxSlot], frequency, dr ); + } + else + { + MW_LOG( "RX on freq %d Hz at DR %d\n\r", frequency, dr ); + } + + *datarate = (uint8_t) dr; + return true; +} + +bool RegionCN470TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ) +{ + int8_t phyDr = DataratesCN470[txConfig->Datarate]; + int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, NvmCtx.Bands[NvmCtx.Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, NvmCtx.ChannelsMask ); + int8_t phyTxPower = 0; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain ); + + // Setup the radio frequency + Radio.SetChannel( NvmCtx.Channels[txConfig->Channel].Frequency ); + + Radio.SetTxConfig( MODEM_LORA, phyTxPower, 0, 0, phyDr, 1, 8, false, true, 0, 0, false, 4000 ); + MW_LOG( "TX on freq %d Hz at DR %d\n\r", NvmCtx.Channels[txConfig->Channel].Frequency, txConfig->Datarate ); + + // Setup maximum payload lenght of the radio driver + Radio.SetMaxPayloadLength( MODEM_LORA, txConfig->PktLen ); + // Get the time-on-air of the next tx frame + *txTimeOnAir = Radio.TimeOnAir( MODEM_LORA, txConfig->PktLen ); + *txPower = txPowerLimited; + + return true; +} + +uint8_t RegionCN470LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ) +{ + uint8_t status = 0x07; + RegionCommonLinkAdrParams_t linkAdrParams; + uint8_t nextIndex = 0; + uint8_t bytesProcessed = 0; + uint16_t channelsMask[6] = { 0, 0, 0, 0, 0, 0 }; + GetPhyParams_t getPhy; + PhyParam_t phyParam; + RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams; + + // Initialize local copy of channels mask + RegionCommonChanMaskCopy( channelsMask, NvmCtx.ChannelsMask, 6 ); + + while( bytesProcessed < linkAdrReq->PayloadSize ) + { + // Get ADR request parameters + nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams ); + + if( nextIndex == 0 ) + break; // break loop, since no more request has been found + + // Update bytes processed + bytesProcessed += nextIndex; + + // Revert status, as we only check the last ADR request for the channel mask KO + status = 0x07; + + if( linkAdrParams.ChMaskCtrl == 6 ) + { + // Enable all 125 kHz channels + channelsMask[0] = 0xFFFF; + channelsMask[1] = 0xFFFF; + channelsMask[2] = 0xFFFF; + channelsMask[3] = 0xFFFF; + channelsMask[4] = 0xFFFF; + channelsMask[5] = 0xFFFF; + } + else if( linkAdrParams.ChMaskCtrl == 7 ) + { + status &= 0xFE; // Channel mask KO + } + else + { + for( uint8_t i = 0; i < 16; i++ ) + { + if( ( ( linkAdrParams.ChMask & ( 1 << i ) ) != 0 ) && + ( NvmCtx.Channels[linkAdrParams.ChMaskCtrl * 16 + i].Frequency == 0 ) ) + {// Trying to enable an undefined channel + status &= 0xFE; // Channel mask KO + } + } + channelsMask[linkAdrParams.ChMaskCtrl] = linkAdrParams.ChMask; + } + } + + // Get the minimum possible datarate + getPhy.Attribute = PHY_MIN_TX_DR; + getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime; + phyParam = RegionCN470GetPhyParam( &getPhy ); + + linkAdrVerifyParams.Status = status; + linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled; + linkAdrVerifyParams.Datarate = linkAdrParams.Datarate; + linkAdrVerifyParams.TxPower = linkAdrParams.TxPower; + linkAdrVerifyParams.NbRep = linkAdrParams.NbRep; + linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate; + linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower; + linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep; + linkAdrVerifyParams.NbChannels = CN470_MAX_NB_CHANNELS; + linkAdrVerifyParams.ChannelsMask = channelsMask; + linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value; + linkAdrVerifyParams.MaxDatarate = CN470_TX_MAX_DATARATE; + linkAdrVerifyParams.Channels = NvmCtx.Channels; + linkAdrVerifyParams.MinTxPower = CN470_MIN_TX_POWER; + linkAdrVerifyParams.MaxTxPower = CN470_MAX_TX_POWER; + linkAdrVerifyParams.Version = linkAdrReq->Version; + + // Verify the parameters and update, if necessary + status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep ); + + // Update channelsMask if everything is correct + if( status == 0x07 ) + { + // Copy Mask + RegionCommonChanMaskCopy( NvmCtx.ChannelsMask, channelsMask, 6 ); + } + + // Update status variables + *drOut = linkAdrParams.Datarate; + *txPowOut = linkAdrParams.TxPower; + *nbRepOut = linkAdrParams.NbRep; + *nbBytesParsed = bytesProcessed; + + return status; +} + +uint8_t RegionCN470RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ) +{ + uint8_t status = 0x07; + + // Verify radio frequency + if( VerifyRfFreq( rxParamSetupReq->Frequency ) == false ) + { + status &= 0xFE; // Channel frequency KO + } + + // Verify datarate + if( RegionCommonValueInRange( rxParamSetupReq->Datarate, CN470_RX_MIN_DATARATE, CN470_RX_MAX_DATARATE ) == false ) + { + status &= 0xFD; // Datarate KO + } + + // Verify datarate offset + if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, CN470_MIN_RX1_DR_OFFSET, CN470_MAX_RX1_DR_OFFSET ) == false ) + { + status &= 0xFB; // Rx1DrOffset range KO + } + + return status; +} + +uint8_t RegionCN470NewChannelReq( NewChannelReqParams_t* newChannelReq ) +{ + // Datarate and frequency KO + return 0; +} + +int8_t RegionCN470TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ) +{ + return -1; +} + +uint8_t RegionCN470DlChannelReq( DlChannelReqParams_t* dlChannelReq ) +{ + return 0; +} + +int8_t RegionCN470AlternateDr( int8_t currentDr, AlternateDrType_t type ) +{ + return currentDr; +} + +void RegionCN470CalcBackOff( CalcBackOffParams_t* calcBackOff ) +{ + RegionCommonCalcBackOffParams_t calcBackOffParams; + + calcBackOffParams.Channels = NvmCtx.Channels; + calcBackOffParams.Bands = NvmCtx.Bands; + calcBackOffParams.LastTxIsJoinRequest = calcBackOff->LastTxIsJoinRequest; + calcBackOffParams.Joined = calcBackOff->Joined; + calcBackOffParams.DutyCycleEnabled = calcBackOff->DutyCycleEnabled; + calcBackOffParams.Channel = calcBackOff->Channel; + calcBackOffParams.ElapsedTime = calcBackOff->ElapsedTime; + calcBackOffParams.TxTimeOnAir = calcBackOff->TxTimeOnAir; + + RegionCommonCalcBackOff( &calcBackOffParams ); +} + +LoRaMacStatus_t RegionCN470NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t delayTx = 0; + uint8_t enabledChannels[CN470_MAX_NB_CHANNELS] = { 0 }; + TimerTime_t nextTxDelay = 0; + + // Count 125kHz channels + if( RegionCommonCountChannels( NvmCtx.ChannelsMask, 0, 6 ) == 0 ) + { // Reactivate default channels + NvmCtx.ChannelsMask[0] = 0xFFFF; + NvmCtx.ChannelsMask[1] = 0xFFFF; + NvmCtx.ChannelsMask[2] = 0xFFFF; + NvmCtx.ChannelsMask[3] = 0xFFFF; + NvmCtx.ChannelsMask[4] = 0xFFFF; + NvmCtx.ChannelsMask[5] = 0xFFFF; + } + + TimerTime_t elapsed = TimerGetElapsedTime( nextChanParams->LastAggrTx ); + if( ( nextChanParams->LastAggrTx == 0 ) || ( nextChanParams->AggrTimeOff <= elapsed ) ) + { + // Reset Aggregated time off + *aggregatedTimeOff = 0; + + // Update bands Time OFF + nextTxDelay = RegionCommonUpdateBandTimeOff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, NvmCtx.Bands, CN470_MAX_NB_BANDS ); + + // Search how many channels are enabled + nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Datarate, + NvmCtx.ChannelsMask, NvmCtx.Channels, + NvmCtx.Bands, enabledChannels, &delayTx ); + } + else + { + delayTx++; + nextTxDelay = nextChanParams->AggrTimeOff - elapsed; + } + + if( nbEnabledChannels > 0 ) + { + // We found a valid channel + *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )]; + + *time = 0; + return LORAMAC_STATUS_OK; + } + else + { + if( delayTx > 0 ) + { + // Delay transmission due to AggregatedTimeOff or to a band time off + *time = nextTxDelay; + return LORAMAC_STATUS_DUTYCYCLE_RESTRICTED; + } + // Datarate not supported by any channel + *time = 0; + return LORAMAC_STATUS_NO_CHANNEL_FOUND; + } +} + +LoRaMacStatus_t RegionCN470ChannelAdd( ChannelAddParams_t* channelAdd ) +{ + return LORAMAC_STATUS_PARAMETER_INVALID; +} + +bool RegionCN470ChannelsRemove( ChannelRemoveParams_t* channelRemove ) +{ + return LORAMAC_STATUS_PARAMETER_INVALID; +} + +void RegionCN470SetContinuousWave( ContinuousWaveParams_t* continuousWave ) +{ + int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, NvmCtx.Bands[NvmCtx.Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, NvmCtx.ChannelsMask ); + int8_t phyTxPower = 0; + uint32_t frequency = NvmCtx.Channels[continuousWave->Channel].Frequency; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, continuousWave->MaxEirp, continuousWave->AntennaGain ); + + Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout ); +} + +uint8_t RegionCN470ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) +{ + int8_t datarate = dr - drOffset; + + if( datarate < 0 ) + { + datarate = DR_0; + } + return datarate; +} + +void RegionCN470RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ) +{ + RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup; + + regionCommonRxBeaconSetup.Datarates = DataratesCN470; + regionCommonRxBeaconSetup.Frequency = rxBeaconSetup->Frequency; + regionCommonRxBeaconSetup.BeaconSize = CN470_BEACON_SIZE; + regionCommonRxBeaconSetup.BeaconDatarate = CN470_BEACON_CHANNEL_DR; + regionCommonRxBeaconSetup.BeaconChannelBW = CN470_BEACON_CHANNEL_BW; + regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime; + regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout; + + RegionCommonRxBeaconSetup( ®ionCommonRxBeaconSetup ); + + // Store downlink datarate + *outDr = CN470_BEACON_CHANNEL_DR; +} diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCN470.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCN470.h new file mode 100644 index 00000000..0be4e736 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCN470.h @@ -0,0 +1,494 @@ +/*! + * \file RegionCN470.h + * + * \brief Region definition for CN470 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + * + * \defgroup REGIONCN470 Region CN470 + * Implementation according to LoRaWAN Specification v1.0.2. + * \{ + */ +#ifndef __REGION_CN470_H__ +#define __REGION_CN470_H__ + +#include "region/Region.h" + +/*! + * LoRaMac maximum number of channels + */ +#define CN470_MAX_NB_CHANNELS 96 + +/*! + * Minimal datarate that can be used by the node + */ +#define CN470_TX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define CN470_TX_MAX_DATARATE DR_5 + +/*! + * Minimal datarate that can be used by the node + */ +#define CN470_RX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define CN470_RX_MAX_DATARATE DR_5 + +/*! + * Default datarate used by the node + */ +#define CN470_DEFAULT_DATARATE DR_0 + +/*! + * Minimal Rx1 receive datarate offset + */ +#define CN470_MIN_RX1_DR_OFFSET 0 + +/*! + * Maximal Rx1 receive datarate offset + */ +#define CN470_MAX_RX1_DR_OFFSET 3 + +/*! + * Default Rx1 receive datarate offset + */ +#define CN470_DEFAULT_RX1_DR_OFFSET 0 + +/*! + * Minimal Tx output power that can be used by the node + */ +#define CN470_MIN_TX_POWER TX_POWER_7 + +/*! + * Maximal Tx output power that can be used by the node + */ +#define CN470_MAX_TX_POWER TX_POWER_0 + +/*! + * Default Tx output power used by the node + */ +#define CN470_DEFAULT_TX_POWER TX_POWER_0 + +/*! + * Default Max EIRP + */ +#define CN470_DEFAULT_MAX_EIRP 19.15f + +/*! + * Default antenna gain + */ +#define CN470_DEFAULT_ANTENNA_GAIN 2.15f + +/*! + * ADR Ack limit + */ +#define CN470_ADR_ACK_LIMIT 64 + +/*! + * ADR Ack delay + */ +#define CN470_ADR_ACK_DELAY 32 + +/*! + * Enabled or disabled the duty cycle + */ +#define CN470_DUTY_CYCLE_ENABLED 0 + +/*! + * Maximum RX window duration + */ +#define CN470_MAX_RX_WINDOW 3000 + +/*! + * Receive delay 1 + */ +#define CN470_RECEIVE_DELAY1 1000 + +/*! + * Receive delay 2 + */ +#define CN470_RECEIVE_DELAY2 2000 + +/*! + * Join accept delay 1 + */ +#define CN470_JOIN_ACCEPT_DELAY1 5000 + +/*! + * Join accept delay 2 + */ +#define CN470_JOIN_ACCEPT_DELAY2 6000 + +/*! + * Maximum frame counter gap + */ +#define CN470_MAX_FCNT_GAP 16384 + +/*! + * Ack timeout + */ +#define CN470_ACKTIMEOUT 2000 + +/*! + * Random ack timeout limits + */ +#define CN470_ACK_TIMEOUT_RND 1000 + +/*! + * Second reception window channel frequency definition. + */ +#define CN470_RX_WND_2_FREQ 505300000 + +/*! + * Second reception window channel datarate definition. + */ +#define CN470_RX_WND_2_DR DR_0 + +/* + * CLASS B + */ +/*! + * Beacon frequency + */ +#define CN470_BEACON_CHANNEL_FREQ 508300000 + +/*! + * Beacon frequency channel stepwidth + */ +#define CN470_BEACON_CHANNEL_STEPWIDTH 200000 + +/*! + * Number of possible beacon channels + */ +#define CN470_BEACON_NB_CHANNELS 8 + +/*! + * Payload size of a beacon frame + */ +#define CN470_BEACON_SIZE 19 + +/*! + * Size of RFU 1 field + */ +#define CN470_RFU1_SIZE 3 + +/*! + * Size of RFU 2 field + */ +#define CN470_RFU2_SIZE 1 + +/*! + * Datarate of the beacon channel + */ +#define CN470_BEACON_CHANNEL_DR DR_2 + +/*! + * Bandwith of the beacon channel + */ +#define CN470_BEACON_CHANNEL_BW 0 + +/*! + * Ping slot channel datarate + */ +#define CN470_PING_SLOT_CHANNEL_DR DR_2 + +/*! + * LoRaMac maximum number of bands + */ +#define CN470_MAX_NB_BANDS 1 + +/*! + * Band 0 definition + * { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff } + */ +#define CN470_BAND0 { 1, CN470_MAX_TX_POWER, 0, 0, 0 } // 100.0 % + +/*! + * Defines the first channel for RX window 1 for CN470 band + */ +#define CN470_FIRST_RX1_CHANNEL ( (uint32_t) 500300000 ) + +/*! + * Defines the last channel for RX window 1 for CN470 band + */ +#define CN470_LAST_RX1_CHANNEL ( (uint32_t) 509700000 ) + +/*! + * Defines the step width of the channels for RX window 1 + */ +#define CN470_STEPWIDTH_RX1_CHANNEL ( (uint32_t) 200000 ) + +/*! + * Data rates table definition + */ +static const uint8_t DataratesCN470[] = { 12, 11, 10, 9, 8, 7 }; + +/*! + * Bandwidths table definition in Hz + */ +static const uint32_t BandwidthsCN470[] = { 125000, 125000, 125000, 125000, 125000, 125000 }; + +/*! + * Maximum payload with respect to the datarate index. Cannot operate with repeater. + */ +static const uint8_t MaxPayloadOfDatarateCN470[] = { 51, 51, 51, 115, 242, 242 }; + +/*! + * Maximum payload with respect to the datarate index. Can operate with repeater. + */ +static const uint8_t MaxPayloadOfDatarateRepeaterCN470[] = { 51, 51, 51, 115, 222, 222 }; + +/*! + * \brief The function gets a value of a specific phy attribute. + * + * \param [IN] getPhy Pointer to the function parameters. + * + * \retval Returns a structure containing the PHY parameter. + */ +PhyParam_t RegionCN470GetPhyParam( GetPhyParams_t* getPhy ); + +/*! + * \brief Updates the last TX done parameters of the current channel. + * + * \param [IN] txDone Pointer to the function parameters. + */ +void RegionCN470SetBandTxDone( SetBandTxDoneParams_t* txDone ); + +/*! + * \brief Initializes the channels masks and the channels. + * + * \param [IN] type Sets the initialization type. + */ +void RegionCN470InitDefaults( InitDefaultsParams_t* params ); + +/*! + * \brief Returns a pointer to the internal context and its size. + * + * \param [OUT] params Pointer to the function parameters. + * + * \retval Points to a structure where the module store its non-volatile context. + */ +void* RegionCN470GetNvmCtx( GetNvmCtxParams_t* params ); + +/*! + * \brief Verifies a parameter. + * + * \param [IN] verify Pointer to the function parameters. + * + * \param [IN] type Sets the initialization type. + * + * \retval Returns true, if the parameter is valid. + */ +bool RegionCN470Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ); + +/*! + * \brief The function parses the input buffer and sets up the channels of the + * CF list. + * + * \param [IN] applyCFList Pointer to the function parameters. + */ +void RegionCN470ApplyCFList( ApplyCFListParams_t* applyCFList ); + +/*! + * \brief Sets a channels mask. + * + * \param [IN] chanMaskSet Pointer to the function parameters. + * + * \retval Returns true, if the channels mask could be set. + */ +bool RegionCN470ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ); + +/*! + * Computes the Rx window timeout and offset. + * + * \param [IN] datarate Rx window datarate index to be used + * + * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame. + * + * \param [IN] rxError System maximum timing error of the receiver. In milliseconds + * The receiver will turn on in a [-rxError : +rxError] ms + * interval around RxOffset + * + * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields. + */ +void RegionCN470ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ); + +/*! + * \brief Configuration of the RX windows. + * + * \param [IN] rxConfig Pointer to the function parameters. + * + * \param [OUT] datarate The datarate index which was set. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionCN470RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ); + +/*! + * \brief TX configuration. + * + * \param [IN] txConfig Pointer to the function parameters. + * + * \param [OUT] txPower The tx power index which was set. + * + * \param [OUT] txTimeOnAir The time-on-air of the frame. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionCN470TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ); + +/*! + * \brief The function processes a Link ADR Request. + * + * \param [IN] linkAdrReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionCN470LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ); + +/*! + * \brief The function processes a RX Parameter Setup Request. + * + * \param [IN] rxParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionCN470RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ); + +/*! + * \brief The function processes a Channel Request. + * + * \param [IN] newChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionCN470NewChannelReq( NewChannelReqParams_t* newChannelReq ); + +/*! + * \brief The function processes a TX ParamSetup Request. + * + * \param [IN] txParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + * Returns -1, if the functionality is not implemented. In this case, the end node + * shall not process the command. + */ +int8_t RegionCN470TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ); + +/*! + * \brief The function processes a DlChannel Request. + * + * \param [IN] dlChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionCN470DlChannelReq( DlChannelReqParams_t* dlChannelReq ); + +/*! + * \brief Alternates the datarate of the channel for the join request. + * + * \param [IN] currentDr Current datarate. + * + * \retval Datarate to apply. + */ +int8_t RegionCN470AlternateDr( int8_t currentDr, AlternateDrType_t type ); + +/*! + * \brief Calculates the back-off time. + * + * \param [IN] calcBackOff Pointer to the function parameters. + */ +void RegionCN470CalcBackOff( CalcBackOffParams_t* calcBackOff ); + +/*! + * \brief Searches and set the next random available channel + * + * \param [OUT] channel Next channel to use for TX. + * + * \param [OUT] time Time to wait for the next transmission according to the duty + * cycle. + * + * \param [OUT] aggregatedTimeOff Updates the aggregated time off. + * + * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate] + */ +LoRaMacStatus_t RegionCN470NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ); + +/*! + * \brief Adds a channel. + * + * \param [IN] channelAdd Pointer to the function parameters. + * + * \retval Status of the operation. + */ +LoRaMacStatus_t RegionCN470ChannelAdd( ChannelAddParams_t* channelAdd ); + +/*! + * \brief Removes a channel. + * + * \param [IN] channelRemove Pointer to the function parameters. + * + * \retval Returns true, if the channel was removed successfully. + */ +bool RegionCN470ChannelsRemove( ChannelRemoveParams_t* channelRemove ); + +/*! + * \brief Sets the radio into continuous wave mode. + * + * \param [IN] continuousWave Pointer to the function parameters. + */ +void RegionCN470SetContinuousWave( ContinuousWaveParams_t* continuousWave ); + +/*! + * \brief Computes new datarate according to the given offset + * + * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms + * + * \param [IN] dr Current datarate + * + * \param [IN] drOffset Offset to be applied + * + * \retval newDr Computed datarate. + */ +uint8_t RegionCN470ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ); + +/*! + * \brief Sets the radio into beacon reception mode + * + * \param [IN] rxBeaconSetup Pointer to the function parameters + */ + void RegionCN470RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ); + +/*! \} defgroup REGIONCN470 */ + +#endif // __REGION_CN470_H__ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCN470ALI.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCN470ALI.c new file mode 100644 index 00000000..51b84776 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCN470ALI.c @@ -0,0 +1,895 @@ +/*! + * \file RegionCN470ALIALI.c + * + * \brief Region implementation for CN470ALIALI + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) +*/ +#include "utilities.h" + +#include "RegionCommon.h" +#include "RegionCN470ALI.h" +#include "lorawan_conf.h" +#include "mw_log_conf.h" + +// Definitions +#define CHANNELS_MASK_SIZE 6 + +/*! + * Region specific context + */ +typedef struct sRegionCN470ALINvmCtx +{ + /*! + * LoRaMAC channels + */ + ChannelParams_t Channels[ CN470ALI_MAX_NB_CHANNELS ]; + /*! + * LoRaMac bands + */ + Band_t Bands[ CN470ALI_MAX_NB_BANDS ]; + /*! + * LoRaMac channels mask + */ + uint16_t ChannelsMask[ CHANNELS_MASK_SIZE ]; + /*! + * LoRaMac channels default mask + */ + uint16_t ChannelsDefaultMask[ CHANNELS_MASK_SIZE ]; +}RegionCN470ALINvmCtx_t; + +/* + * Non-volatile module context. + */ +static RegionCN470ALINvmCtx_t NvmCtx; + +// Static functions +static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr ) +{ + uint8_t nextLowerDr = 0; + + if( dr == minDr ) + { + nextLowerDr = minDr; + } + else + { + nextLowerDr = dr - 1; + } + return nextLowerDr; +} + +static uint32_t GetBandwidth( uint32_t drIndex ) +{ + switch( BandwidthsCN470ALI[drIndex] ) + { + default: + case 125000: + return 0; + case 250000: + return 1; + case 500000: + return 2; + } +} + +static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask ) +{ + int8_t txPowerResult = txPower; + + // Limit tx power to the band max + txPowerResult = MAX( txPower, maxBandTxPower ); + + return txPowerResult; +} + +static bool VerifyRfFreq( uint32_t freq ) +{ + // Check radio driver support + if( Radio.CheckRfFrequency( freq ) == false ) + { + return false; + } + +#if ((ALI_DOWNLINK_FREQ == ALI_FREQ470_S) || (ALI_DOWNLINK_FREQ == ALI_FREQ480_D)) + // Rx frequencies + if((freq < 503500000) && (freq >476500000)) + { + return false; + } +#endif + + // Rx frequencies + if( ( freq < CN470ALI_FIRST_RX1_CHANNEL ) || + ( freq > CN470ALI_LAST_RX1_CHANNEL ) || + ( ( ( freq - ( uint32_t ) CN470ALI_FIRST_RX1_CHANNEL ) % ( uint32_t ) CN470ALI_STEPWIDTH_RX1_CHANNEL ) != 0 ) ) + { + return false; + } + + // Test for frequency range - take RX and TX freqencies into account + if( ( freq < 470300000 ) || ( freq > 509700000 ) ) + { + return false; + } + return true; +} + +static uint8_t CountNbOfEnabledChannels( uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t delayTransmission = 0; + + for( uint8_t i = 0, k = 0; i < CN470ALI_MAX_NB_CHANNELS; i += 16, k++ ) + { + for( uint8_t j = 0; j < 16; j++ ) + { + if( ( channelsMask[k] & ( 1 << j ) ) != 0 ) + { + if( channels[i + j].Frequency == 0 ) + { // Check if the channel is enabled + continue; + } + if( RegionCommonValueInRange( datarate, channels[i + j].DrRange.Fields.Min, + channels[i + j].DrRange.Fields.Max ) == false ) + { // Check if the current channel selection supports the given datarate + continue; + } + if( bands[channels[i + j].Band].TimeOff > 0 ) + { // Check if the band is available for transmission + delayTransmission++; + continue; + } + enabledChannels[nbEnabledChannels++] = i + j; + } + } + } + + *delayTx = delayTransmission; + return nbEnabledChannels; +} + +PhyParam_t RegionCN470ALIGetPhyParam( GetPhyParams_t* getPhy ) +{ + PhyParam_t phyParam = { 0 }; + + switch( getPhy->Attribute ) + { + case PHY_MIN_RX_DR: + { + phyParam.Value = CN470ALI_RX_MIN_DATARATE; + break; + } + case PHY_MIN_TX_DR: + { + phyParam.Value = CN470ALI_TX_MIN_DATARATE; + break; + } + case PHY_DEF_TX_DR: + { + phyParam.Value = CN470ALI_DEFAULT_DATARATE; + break; + } + case PHY_NEXT_LOWER_TX_DR: + { + /*next low speed*/ + + phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, DR_0); + break; + } + case PHY_MAX_TX_POWER: + { + phyParam.Value = CN470ALI_MAX_TX_POWER; + break; + } + case PHY_DEF_TX_POWER: + { + phyParam.Value = CN470ALI_DEFAULT_TX_POWER; + break; + } + case PHY_DEF_ADR_ACK_LIMIT: + { + phyParam.Value = CN470ALI_ADR_ACK_LIMIT; + break; + } + case PHY_DEF_ADR_ACK_DELAY: + { + phyParam.Value = CN470ALI_ADR_ACK_DELAY; + break; + } + case PHY_MAX_PAYLOAD: + { + phyParam.Value = MaxPayloadOfDatarateCN470ALI[getPhy->Datarate]; + break; + } + case PHY_MAX_PAYLOAD_REPEATER: + { + phyParam.Value = MaxPayloadOfDatarateRepeaterCN470ALI[getPhy->Datarate]; + break; + } + case PHY_DUTY_CYCLE: + { + phyParam.Value = CN470ALI_DUTY_CYCLE_ENABLED; + break; + } + case PHY_MAX_RX_WINDOW: + { + phyParam.Value = CN470ALI_MAX_RX_WINDOW; + break; + } + case PHY_RECEIVE_DELAY1: + { + phyParam.Value = CN470ALI_RECEIVE_DELAY1; + break; + } + case PHY_RECEIVE_DELAY2: + { + phyParam.Value = CN470ALI_RECEIVE_DELAY2; + break; + } + case PHY_JOIN_ACCEPT_DELAY1: + { + phyParam.Value = CN470ALI_JOIN_ACCEPT_DELAY1; + break; + } + case PHY_JOIN_ACCEPT_DELAY2: + { + phyParam.Value = CN470ALI_JOIN_ACCEPT_DELAY2; + break; + } + case PHY_MAX_FCNT_GAP: + { + phyParam.Value = CN470ALI_MAX_FCNT_GAP; + break; + } + case PHY_ACK_TIMEOUT: + { +/*===============================ALI_Region==============================================*/ + phyParam.Value = ( CN470ALI_ACKTIMEOUT)+ randr( -CN470ALI_ACK_TIMEOUT_RND, CN470ALI_ACK_TIMEOUT_RND ); +/*=======================================================================================*/ + break; + } + case PHY_DEF_DR1_OFFSET: + { + phyParam.Value = CN470ALI_DEFAULT_RX1_DR_OFFSET; + break; + } + case PHY_DEF_RX2_FREQUENCY: + { + phyParam.Value = CN470ALI_RX_WND_2_FREQ; + break; + } + case PHY_DEF_RX2_DR: + { + phyParam.Value = CN470ALI_RX_WND_2_DR; + break; + } + case PHY_CHANNELS_MASK: + { + phyParam.ChannelsMask = NvmCtx.ChannelsMask; + break; + } + case PHY_CHANNELS_DEFAULT_MASK: + { + phyParam.ChannelsMask = NvmCtx.ChannelsDefaultMask; + break; + } + case PHY_MAX_NB_CHANNELS: + { + phyParam.Value = CN470ALI_MAX_NB_CHANNELS; + break; + } + case PHY_CHANNELS: + { + phyParam.Channels = NvmCtx.Channels; + break; + } + case PHY_DEF_UPLINK_DWELL_TIME: + case PHY_DEF_DOWNLINK_DWELL_TIME: + { + phyParam.Value = 0; + break; + } + case PHY_DEF_MAX_EIRP: + { + phyParam.fValue = CN470ALI_DEFAULT_MAX_EIRP; + break; + } + case PHY_DEF_ANTENNA_GAIN: + { + phyParam.fValue = CN470ALI_DEFAULT_ANTENNA_GAIN; + break; + } + case PHY_BEACON_FORMAT: + { + phyParam.BeaconFormat.BeaconSize = CN470ALI_BEACON_SIZE; + phyParam.BeaconFormat.Rfu1Size = CN470ALI_RFU1_SIZE; + phyParam.BeaconFormat.Rfu2Size = CN470ALI_RFU2_SIZE; + break; + } + case PHY_BEACON_CHANNEL_DR: + { + phyParam.Value = CN470ALI_BEACON_CHANNEL_DR; + break; + } + case PHY_BEACON_CHANNEL_STEPWIDTH: + { + phyParam.Value = CN470ALI_BEACON_CHANNEL_STEPWIDTH; + break; + } + case PHY_BEACON_NB_CHANNELS: + { + phyParam.Value = CN470ALI_BEACON_NB_CHANNELS; + break; + } + case PHY_PING_SLOT_CHANNEL_DR: + { + phyParam.Value = CN470ALI_PING_SLOT_CHANNEL_DR; + break; + } + default: + { + break; + } + } + + return phyParam; +} + +void RegionCN470ALISetBandTxDone( SetBandTxDoneParams_t* txDone ) +{ + RegionCommonSetBandTxDone( txDone->Joined, &NvmCtx.Bands[NvmCtx.Channels[txDone->Channel].Band], txDone->LastTxDoneTime ); +} + +void RegionCN470ALIInitDefaults( InitDefaultsParams_t* params ) +{ + Band_t bands[CN470ALI_MAX_NB_BANDS] = + { + CN470ALI_BAND0 + }; + + switch( params->Type ) + { + case INIT_TYPE_INIT: + { + // Initialize bands + memcpy1( ( uint8_t* )NvmCtx.Bands, ( uint8_t* )bands, sizeof( Band_t ) * CN470ALI_MAX_NB_BANDS ); +/*===============================ALI_Region==============================================*/ + + // Channels + // 125 kHz channels + + #if (ALI_DOWNLINK_FREQ == ALI_FREQ470_S)||(ALI_DOWNLINK_FREQ == ALI_FREQ470_D) + + for( uint8_t i = 0; i < 32; i++ ) + { + NvmCtx.Channels[i].Frequency = 470300000 + i * 200000; + NvmCtx.Channels[i].DrRange.Value = ( DR_5 << 4 ) | DR_0; + NvmCtx.Channels[i].Band = 0; + } + for( uint8_t i = 32; i < CN470ALI_MAX_NB_CHANNELS; i++ ) + { + NvmCtx.Channels[i].Frequency = 470300000 + (i + (166-32)) * 200000; + NvmCtx.Channels[i].DrRange.Value = ( DR_5 << 4 ) | DR_0; + NvmCtx.Channels[i].Band = 0; + } + #else + for(i=0; iNvmCtx != 0 ) + { + memcpy1( (uint8_t*) &NvmCtx, (uint8_t*) params->NvmCtx, sizeof( NvmCtx ) ); + } + break; + } + case INIT_TYPE_RESTORE_DEFAULT_CHANNELS: + { + // Restore channels default mask + RegionCommonChanMaskCopy( NvmCtx.ChannelsMask, NvmCtx.ChannelsDefaultMask, 6 ); + break; + } + default: + { + break; + } + } +} + +void* RegionCN470ALIGetNvmCtx( GetNvmCtxParams_t* params ) +{ + params->nvmCtxSize = sizeof( RegionCN470ALINvmCtx_t ); + return &NvmCtx; +} + +bool RegionCN470ALIVerify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ) +{ + switch( phyAttribute ) + { + case PHY_FREQUENCY: + { + return VerifyRfFreq( verify->Frequency ); + } + case PHY_TX_DR: + case PHY_DEF_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, CN470ALI_TX_MIN_DATARATE, CN470ALI_TX_MAX_DATARATE ); + } + case PHY_RX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, CN470ALI_RX_MIN_DATARATE, CN470ALI_RX_MAX_DATARATE ); + } + case PHY_DEF_TX_POWER: + case PHY_TX_POWER: + { + // Remark: switched min and max! + return RegionCommonValueInRange( verify->TxPower, CN470ALI_MAX_TX_POWER, CN470ALI_MIN_TX_POWER ); + } + case PHY_DUTY_CYCLE: + { + return CN470ALI_DUTY_CYCLE_ENABLED; + } + default: + return false; + } +} + +void RegionCN470ALIApplyCFList( ApplyCFListParams_t* applyCFList ) +{ + // Size of the optional CF list must be 16 byte + if( applyCFList->Size != 16 ) + { + return; + } + + // Last byte CFListType must be 0x01 to indicate the CFList contains a series of ChMask fields + if( applyCFList->Payload[15] != 0x01 ) + { + return; + } + + // ChMask0 - ChMask5 must be set (every ChMask has 16 bit) + for( uint8_t chMaskItr = 0, cntPayload = 0; chMaskItr <= 5; chMaskItr++, cntPayload+=2 ) + { + NvmCtx.ChannelsMask[chMaskItr] = (uint16_t) (0x00FF & applyCFList->Payload[cntPayload]); + NvmCtx.ChannelsMask[chMaskItr] |= (uint16_t) (applyCFList->Payload[cntPayload+1] << 8); + } +} + +bool RegionCN470ALIChanMaskSet( ChanMaskSetParams_t* chanMaskSet ) +{ + switch( chanMaskSet->ChannelsMaskType ) + { + case CHANNELS_MASK: + { + RegionCommonChanMaskCopy( NvmCtx.ChannelsMask, chanMaskSet->ChannelsMaskIn, 6 ); + break; + } + case CHANNELS_DEFAULT_MASK: + { + RegionCommonChanMaskCopy( NvmCtx.ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 6 ); + break; + } + default: + return false; + } + return true; +} + +void RegionCN470ALIComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ) +{ + double tSymbol = 0.0; + + // Get the datarate, perform a boundary check + rxConfigParams->Datarate = MIN( datarate, CN470ALI_RX_MAX_DATARATE ); + rxConfigParams->Bandwidth = GetBandwidth( rxConfigParams->Datarate ); + + tSymbol = RegionCommonComputeSymbolTimeLoRa( DataratesCN470ALI[rxConfigParams->Datarate], BandwidthsCN470ALI[rxConfigParams->Datarate] ); + + RegionCommonComputeRxWindowParameters( tSymbol, minRxSymbols, rxError, Radio.GetWakeupTime( ), &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); +} + +bool RegionCN470ALIRxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) +{ + int8_t dr = rxConfig->Datarate; + uint8_t maxPayload = 0; + int8_t phyDr = 0; + uint32_t frequency = rxConfig->Frequency; + const char *slotStrings[] = { "1", "2", "C", "Multi_C", "P", "Multi_P" }; + + if( Radio.GetStatus( ) != RF_IDLE ) + { + return false; + } + + if( rxConfig->RxSlot == RX_SLOT_WIN_1 ) + { + #if (ALI_DOWNLINK_FREQ == ALI_FREQ470_S || ALI_DOWNLINK_FREQ == ALI_FREQ480_D) + if(rxConfig->Channel <32) + frequency = CN470ALI_FIRST_RX1_CHANNEL + ( rxConfig->Channel % 64 ) * CN470ALI_STEPWIDTH_RX1_CHANNEL; + else + frequency = (uint32_t)503500000 + ((rxConfig->Channel-32) % 64 ) * CN470ALI_STEPWIDTH_RX1_CHANNEL; + + #else + // Apply window 1 frequency + frequency = CN470ALI_FIRST_RX1_CHANNEL + ( rxConfig->Channel % 64 ) * CN470ALI_STEPWIDTH_RX1_CHANNEL; + #endif + } + + // Read the physical datarate from the datarates table + phyDr = DataratesCN470ALI[dr]; + + Radio.SetChannel( frequency ); + + // Radio configuration + Radio.SetRxConfig( MODEM_LORA, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous ); + + if( rxConfig->RepeaterSupport == true ) + { + maxPayload = MaxPayloadOfDatarateRepeaterCN470ALI[dr]; + } + else + { + maxPayload = MaxPayloadOfDatarateCN470ALI[dr]; + } + Radio.SetMaxPayloadLength( MODEM_LORA, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD ); + if ( rxConfig->RxSlot < RX_SLOT_NONE ) + { + MW_LOG( "RX_%s on freq %d Hz at DR %d\n\r", slotStrings[rxConfig->RxSlot], frequency, dr ); + } + else + { + MW_LOG( "RX on freq %d Hz at DR %d\n\r", frequency, dr ); + } + + *datarate = (uint8_t) dr; + return true; +} + +bool RegionCN470ALITxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ) +{ + int8_t phyDr = DataratesCN470ALI[txConfig->Datarate]; + int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, NvmCtx.Bands[NvmCtx.Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, NvmCtx.ChannelsMask ); + int8_t phyTxPower = 0; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain ); + + // Setup the radio frequency + Radio.SetChannel( NvmCtx.Channels[txConfig->Channel].Frequency ); + + Radio.SetTxConfig( MODEM_LORA, phyTxPower, 0, 0, phyDr, 1, 8, false, true, 0, 0, false, 4000 ); + MW_LOG( "TX on freq %d Hz at DR %d,TxPower %d\n\r", NvmCtx.Channels[txConfig->Channel].Frequency, txConfig->Datarate ,phyTxPower); + + // Setup maximum payload lenght of the radio driver + Radio.SetMaxPayloadLength( MODEM_LORA, txConfig->PktLen ); + // Get the time-on-air of the next tx frame + *txTimeOnAir = Radio.TimeOnAir( MODEM_LORA, txConfig->PktLen ); + *txPower = txPowerLimited; + + return true; +} + +uint8_t RegionCN470ALILinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ) +{ + uint8_t status = 0x07; + RegionCommonLinkAdrParams_t linkAdrParams; + uint8_t nextIndex = 0; + uint8_t bytesProcessed = 0; + uint16_t channelsMask[6] = { 0, 0, 0, 0, 0, 0 }; + GetPhyParams_t getPhy; + PhyParam_t phyParam; + RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams; + + // Initialize local copy of channels mask + RegionCommonChanMaskCopy( channelsMask, NvmCtx.ChannelsMask, 6 ); + + while( bytesProcessed < linkAdrReq->PayloadSize ) + { + // Get ADR request parameters + nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams ); + + if( nextIndex == 0 ) + break; // break loop, since no more request has been found + + // Update bytes processed + bytesProcessed += nextIndex; + + // Revert status, as we only check the last ADR request for the channel mask KO + status = 0x07; + +/*===============================ALI_Region==============================================*/ + if( linkAdrParams.ChMaskCtrl >4 ) + { + status &= 0xFE; // Channel mask KO + } + else if( linkAdrParams.ChMaskCtrl == 4) + { + // Enable all 125 kHz channels + channelsMask[0] = 0xFFFF; + channelsMask[1] = 0xFFFF; + channelsMask[2] = 0xFFFF; + channelsMask[3] = 0xFFFF; + channelsMask[4] = 0; + channelsMask[5] = 0; + } + else + { + for( uint8_t i = 0; i < 16; i++ ) + { + if( ( ( linkAdrParams.ChMask & ( 1 << i ) ) != 0 ) && + ( NvmCtx.Channels[linkAdrParams.ChMaskCtrl * 16 + i].Frequency == 0 ) ) + {// Trying to enable an undefined channel + status &= 0xFE; // Channel mask KO + } + } + channelsMask[linkAdrParams.ChMaskCtrl] = linkAdrParams.ChMask; + } + } +/*========================================================================================*/ + + // Get the minimum possible datarate + getPhy.Attribute = PHY_MIN_TX_DR; + getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime; + phyParam = RegionCN470ALIGetPhyParam( &getPhy ); + + linkAdrVerifyParams.Status = status; + linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled; + linkAdrVerifyParams.Datarate = linkAdrParams.Datarate; + linkAdrVerifyParams.TxPower = linkAdrParams.TxPower; + linkAdrVerifyParams.NbRep = linkAdrParams.NbRep; + linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate; + linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower; + linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep; + linkAdrVerifyParams.NbChannels = CN470ALI_MAX_NB_CHANNELS; + linkAdrVerifyParams.ChannelsMask = channelsMask; + linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value; + linkAdrVerifyParams.MaxDatarate = CN470ALI_TX_MAX_DATARATE; + linkAdrVerifyParams.Channels = NvmCtx.Channels; + linkAdrVerifyParams.MinTxPower = CN470ALI_MIN_TX_POWER; + linkAdrVerifyParams.MaxTxPower = CN470ALI_MAX_TX_POWER; + linkAdrVerifyParams.Version = linkAdrReq->Version; + + // Verify the parameters and update, if necessary + status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep ); + + // Update channelsMask if everything is correct + if( status == 0x07 ) + { + // Copy Mask + RegionCommonChanMaskCopy( NvmCtx.ChannelsMask, channelsMask, 6 ); + } + + // Update status variables + *drOut = linkAdrParams.Datarate; + *txPowOut = linkAdrParams.TxPower; + *nbRepOut = linkAdrParams.NbRep; + *nbBytesParsed = bytesProcessed; + + return status; +} + +uint8_t RegionCN470ALIRxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ) +{ + uint8_t status = 0x07; + + // Verify radio frequency + if( VerifyRfFreq( rxParamSetupReq->Frequency ) == false ) + { + status &= 0xFE; // Channel frequency KO + } + + // Verify datarate + if( RegionCommonValueInRange( rxParamSetupReq->Datarate, CN470ALI_RX_MIN_DATARATE, CN470ALI_RX_MAX_DATARATE ) == false ) + { + status &= 0xFD; // Datarate KO + } + + // Verify datarate offset + if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, CN470ALI_MIN_RX1_DR_OFFSET, CN470ALI_MAX_RX1_DR_OFFSET ) == false ) + { + status &= 0xFB; // Rx1DrOffset range KO + } + + return status; +} + +uint8_t RegionCN470ALINewChannelReq( NewChannelReqParams_t* newChannelReq ) +{ + // Datarate and frequency KO + return 0; +} + +int8_t RegionCN470ALITxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ) +{ + return -1; +} + +uint8_t RegionCN470ALIDlChannelReq( DlChannelReqParams_t* dlChannelReq ) +{ + return 0; +} + +/*===============================ALI_Region==============================================*/ +int8_t RegionCN470ALIAlternateDr( int8_t currentDr, AlternateDrType_t type ) +{ + static uint16_t JoinRequestTrials; + if(ALTERNATE_DR == type) + { + ++JoinRequestTrials; + return ((JoinRequestTrials%2) ? DR_2 : DR_3); + } + return currentDr; +} +/*=======================================================================================*/ + +void RegionCN470ALICalcBackOff( CalcBackOffParams_t* calcBackOff ) +{ + RegionCommonCalcBackOffParams_t calcBackOffParams; + + calcBackOffParams.Channels = NvmCtx.Channels; + calcBackOffParams.Bands = NvmCtx.Bands; + calcBackOffParams.LastTxIsJoinRequest = calcBackOff->LastTxIsJoinRequest; + calcBackOffParams.Joined = calcBackOff->Joined; + calcBackOffParams.DutyCycleEnabled = calcBackOff->DutyCycleEnabled; + calcBackOffParams.Channel = calcBackOff->Channel; + calcBackOffParams.ElapsedTime = calcBackOff->ElapsedTime; + calcBackOffParams.TxTimeOnAir = calcBackOff->TxTimeOnAir; + + RegionCommonCalcBackOff( &calcBackOffParams ); +} + +LoRaMacStatus_t RegionCN470ALINextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t delayTx = 0; + uint8_t enabledChannels[CN470ALI_MAX_NB_CHANNELS] = { 0 }; + TimerTime_t nextTxDelay = 0; + + // Count 125kHz channels + if( RegionCommonCountChannels( NvmCtx.ChannelsMask, 0, 6 ) == 0 ) + { // Reactivate default channels + NvmCtx.ChannelsMask[0] = 0xFFFF; + NvmCtx.ChannelsMask[1] = 0xFFFF; + NvmCtx.ChannelsMask[2] = 0xFFFF; + NvmCtx.ChannelsMask[3] = 0xFFFF; + NvmCtx.ChannelsMask[4] = 0; + NvmCtx.ChannelsMask[5] = 0; + } + + TimerTime_t elapsed = TimerGetElapsedTime( nextChanParams->LastAggrTx ); + if( ( nextChanParams->LastAggrTx == 0 ) || ( nextChanParams->AggrTimeOff <= elapsed ) ) + { + // Reset Aggregated time off + *aggregatedTimeOff = 0; + + // Update bands Time OFF + nextTxDelay = RegionCommonUpdateBandTimeOff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, NvmCtx.Bands, CN470ALI_MAX_NB_BANDS ); + + // Search how many channels are enabled + nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Datarate, + NvmCtx.ChannelsMask, NvmCtx.Channels, + NvmCtx.Bands, enabledChannels, &delayTx ); + } + else + { + delayTx++; + nextTxDelay = nextChanParams->AggrTimeOff - elapsed; + } + + if( nbEnabledChannels > 0 ) + { + // We found a valid channel + *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )]; + + *time = 0; + return LORAMAC_STATUS_OK; + } + else + { + if( delayTx > 0 ) + { + // Delay transmission due to AggregatedTimeOff or to a band time off + *time = nextTxDelay; + return LORAMAC_STATUS_DUTYCYCLE_RESTRICTED; + } + // Datarate not supported by any channel + *time = 0; + return LORAMAC_STATUS_NO_CHANNEL_FOUND; + } +} + +LoRaMacStatus_t RegionCN470ALIChannelAdd( ChannelAddParams_t* channelAdd ) +{ + return LORAMAC_STATUS_PARAMETER_INVALID; +} + +bool RegionCN470ALIChannelsRemove( ChannelRemoveParams_t* channelRemove ) +{ + return LORAMAC_STATUS_PARAMETER_INVALID; +} + +void RegionCN470ALISetContinuousWave( ContinuousWaveParams_t* continuousWave ) +{ + int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, NvmCtx.Bands[NvmCtx.Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, NvmCtx.ChannelsMask ); + int8_t phyTxPower = 0; + uint32_t frequency = NvmCtx.Channels[continuousWave->Channel].Frequency; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, continuousWave->MaxEirp, continuousWave->AntennaGain ); + + Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout ); +} + +uint8_t RegionCN470ALIApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) +{ + int8_t datarate = dr - drOffset; + + if( datarate < 0 ) + { + datarate = DR_0; + } + return datarate; +} + +void RegionCN470ALIRxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ) +{ + RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup; + + regionCommonRxBeaconSetup.Datarates = DataratesCN470ALI; + regionCommonRxBeaconSetup.Frequency = rxBeaconSetup->Frequency; + regionCommonRxBeaconSetup.BeaconSize = CN470ALI_BEACON_SIZE; + regionCommonRxBeaconSetup.BeaconDatarate = CN470ALI_BEACON_CHANNEL_DR; + regionCommonRxBeaconSetup.BeaconChannelBW = CN470ALI_BEACON_CHANNEL_BW; + regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime; + regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout; + + RegionCommonRxBeaconSetup( ®ionCommonRxBeaconSetup ); + + // Store downlink datarate + *outDr = CN470ALI_BEACON_CHANNEL_DR; +} diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCN470ALI.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCN470ALI.h new file mode 100644 index 00000000..ec4110c2 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCN470ALI.h @@ -0,0 +1,594 @@ +/*! + * \file RegionCN470ALI.h + * + * \brief Region definition for CN470ALI + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + * + * \defgroup REGIONCN470ALI Region CN470ALI + * Implementation according to LoRaWAN Specification v1.0.2. + * \{ + */ +#ifndef __REGION_CN470ALI_H__ +#define __REGION_CN470ALI_H__ + +#include "region/Region.h" + +#define ALI_FREQ470_S (0) +#define ALI_FREQ470_D (1) + +#define ALI_FREQ480_S (3) +#define ALI_FREQ480_D (4) + +/*table[1] for CN470ALID and CN480ALIS*/ +#define ALI_RX_WND_2_FREQ_1A1D_1B1S 485300000 +#define ALI_RX_WND_2_FREQ_1A2D_1B2S 486900000 +#define ALI_RX_WND_2_FREQ_2A1D_2B1S 488500000 +#define ALI_RX_WND_2_FREQ_2A2D_2B2S 490100000 +#define ALI_RX_WND_2_FREQ_3B1D_3A1S 491700000 +#define ALI_RX_WND_2_FREQ_3B2D_3A2S 493300000 +#define ALI_RX_WND_2_FREQ_4B1D_4A1S 494900000 +#define ALI_RX_WND_2_FREQ_4B2D_4A2S 496500000 + +/*table[2] for CN470ALIS and CN480ALID*/ +#define ALI_RX_WND_2_FREQ_1A1S_1B1D 471700000 +#define ALI_RX_WND_2_FREQ_1A2S_1B2D 473300000 +#define ALI_RX_WND_2_FREQ_2A1S_2B1D 474900000 +#define ALI_RX_WND_2_FREQ_2A2S_2B2D 476500000 +#define ALI_RX_WND_2_FREQ_3B1S_3A1D 504900000 +#define ALI_RX_WND_2_FREQ_3B2S_3A2D 506500000 +#define ALI_RX_WND_2_FREQ_4B1S_4A1D 508100000 +#define ALI_RX_WND_2_FREQ_4B2S_4A2D 509700000 + + + +/*TODO: add beacon freq*/ + + + +#define ALI_DOWNLINK_FREQ ALI_FREQ470_S +#define ALI_RX2WIN_FREQ ALI_RX_WND_2_FREQ_1A2S_1B2D +#define ALI_BEACON_FREQ + + +/*error msg*/ +#if ((ALI_DOWNLINK_FREQ == ALI_FREQ470_D)||(ALI_DOWNLINK_FREQ == ALI_FREQ480_S)) + #if (ALI_RX2WIN_FREQ < ALI_RX_WND_2_FREQ_1A1D_1B1S) ||((ALI_RX2WIN_FREQ > ALI_RX_WND_2_FREQ_4B2D_4A2S)) + #error ======>Incorrect ALI_RX2WIN_FREQ , you should use the value in table[1] + #endif +#else + #if ((ALI_RX2WIN_FREQ < ALI_RX_WND_2_FREQ_1A1S_1B1D)||(ALI_RX2WIN_FREQ > ALI_RX_WND_2_FREQ_4B2S_4A2D)) + #error ======>Incorrect ALI_RX2WIN_FREQ , you should use the value in table[2] + #endif + #if ((ALI_RX2WIN_FREQ > ALI_RX_WND_2_FREQ_2A2S_2B2D) && (ALI_RX2WIN_FREQ < ALI_RX_WND_2_FREQ_3B1S_3A1D)) + ##error ======>Incorrect ALI_RX2WIN_FREQ , you should use the value in table[2] + #endif +#endif + +#ifndef ALI_DOWNLINK_FREQ + #error ALI_DOWNLINK_FREQ not defined +#endif +#ifndef ALI_RX2WIN_FREQ + #erorr ALI_RX2WIN_FREQ not defined +#endif + +/*! + * LoRaMac maximum number of channels + */ +#define CN470ALI_MAX_NB_CHANNELS 64 + +/*! + * Minimal datarate that can be used by the node + */ +#define CN470ALI_TX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define CN470ALI_TX_MAX_DATARATE DR_5 + +/*! + * Minimal datarate that can be used by the node + */ +#define CN470ALI_RX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define CN470ALI_RX_MAX_DATARATE DR_5 + +/*! + * Default datarate used by the node + */ + +/*===============================ALI_Region==============================================*/ +#define CN470ALI_DEFAULT_DATARATE DR_2//primitive_cn470 DR_0 +/*=======================================================================================*/ + +/*! + * Minimal Rx1 receive datarate offset + */ +#define CN470ALI_MIN_RX1_DR_OFFSET 0 + +/*! + * Maximal Rx1 receive datarate offset + */ +#define CN470ALI_MAX_RX1_DR_OFFSET 3 + +/*! + * Default Rx1 receive datarate offset + */ +#define CN470ALI_DEFAULT_RX1_DR_OFFSET 0 + +/*! + * Minimal Tx output power that can be used by the node + */ +#define CN470ALI_MIN_TX_POWER TX_POWER_7 + +/*! + * Maximal Tx output power that can be used by the node + */ +#define CN470ALI_MAX_TX_POWER TX_POWER_0 + +/*! + * Default Tx output power used by the node + */ +#define CN470ALI_DEFAULT_TX_POWER TX_POWER_0 + +/*! + * Default Max EIRP + */ +#define CN470ALI_DEFAULT_MAX_EIRP 19.15f + +/*! + * Default antenna gain + */ +#define CN470ALI_DEFAULT_ANTENNA_GAIN 2.15f + +/*! + * ADR Ack limit + */ +#define CN470ALI_ADR_ACK_LIMIT 64 + +/*! + * ADR Ack delay + */ +#define CN470ALI_ADR_ACK_DELAY 32 + +/*! + * Enabled or disabled the duty cycle + */ +#define CN470ALI_DUTY_CYCLE_ENABLED 0 + +/*! + * Maximum RX window duration + */ +#define CN470ALI_MAX_RX_WINDOW 3000 + +/*! + * Receive delay 1 + */ +#define CN470ALI_RECEIVE_DELAY1 1000 + +/*! + * Receive delay 2 + */ +#define CN470ALI_RECEIVE_DELAY2 2000 + +/*! + * Join accept delay 1 + */ +#define CN470ALI_JOIN_ACCEPT_DELAY1 5000 + +/*! + * Join accept delay 2 + */ +#define CN470ALI_JOIN_ACCEPT_DELAY2 6000 + +/*! + * Maximum frame counter gap + */ +#define CN470ALI_MAX_FCNT_GAP 16384 + +/*! + * Ack timeout + */ +#define CN470ALI_ACKTIMEOUT 5500 + +/*! + * Random ack timeout limits + */ +#define CN470ALI_ACK_TIMEOUT_RND 500 + + +/*! + * Second reception window channel datarate definition. + */ +#define CN470ALI_RX_WND_2_DR DR_0 + +/* + * CLASS B + */ +/*! + * Beacon frequency + */ +#define CN470ALI_BEACON_CHANNEL_FREQ 485500000 + +/*! + * Beacon frequency channel stepwidth + */ +#define CN470ALI_BEACON_CHANNEL_STEPWIDTH 200000 + +/*! + * Number of possible beacon channels + */ +#define CN470ALI_BEACON_NB_CHANNELS 8 + +/*! + * Payload size of a beacon frame + */ +#define CN470ALI_BEACON_SIZE 19 + +/*! + * Size of RFU 1 field + */ +#define CN470ALI_RFU1_SIZE 3 + +/*! + * Size of RFU 2 field + */ +#define CN470ALI_RFU2_SIZE 1 + +/*! + * Datarate of the beacon channel + */ +#define CN470ALI_BEACON_CHANNEL_DR DR_2 + +/*! + * Bandwith of the beacon channel + */ +#define CN470ALI_BEACON_CHANNEL_BW 0 + +/*! + * Ping slot channel datarate + */ +#define CN470ALI_PING_SLOT_CHANNEL_DR DR_2 + +/*! + * LoRaMac maximum number of bands + */ +#define CN470ALI_MAX_NB_BANDS 1 + +/*! + * Band 0 definition + * { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff } + */ +#define CN470ALI_BAND0 { 1, CN470ALI_MAX_TX_POWER, 0, 0, 0 } // 100.0 % + +#ifdef ALI_DOWNLINK_FREQ + + #if (ALI_DOWNLINK_FREQ == ALI_FREQ470_S || ALI_DOWNLINK_FREQ == ALI_FREQ480_D) + /*! + * Defines the first channel for RX window 1 for CN470ALI band + */ + #define CN470ALI_FIRST_RX1_CHANNEL ( (uint32_t) 470300000 ) + + /*! + * Defines the last channel for RX window 1 for CN470ALI band + */ + #define CN470ALI_LAST_RX1_CHANNEL ( (uint32_t) 509700000 ) + + /*! + * Defines the step width of the channels for RX window 1 + */ + #define CN470ALI_STEPWIDTH_RX1_CHANNEL ( (uint32_t) 200000 ) + + + /*! + * Second reception window channel frequency definition. + */ + #define CN470ALI_RX_WND_2_FREQ ((uint32_t)ALI_RX2WIN_FREQ) + + + #elif (ALI_DOWNLINK_FREQ == ALI_FREQ470_D || ALI_DOWNLINK_FREQ == ALI_FREQ480_S) + + /*! + * Defines the first channel for RX window 1 for CN470ALI band + */ + #define CN470ALI_FIRST_RX1_CHANNEL ( (uint32_t) 483900000 ) + + /*! + * Defines the last channel for RX window 1 for CN470ALI band + */ + #define CN470ALI_LAST_RX1_CHANNEL ( (uint32_t) 496500000 ) + + /*! + * Defines the step width of the channels for RX window 1 + */ + #define CN470ALI_STEPWIDTH_RX1_CHANNEL ( (uint32_t) 200000 ) + + /*! + * Second reception window channel frequency definition. + */ + #define CN470ALI_RX_WND_2_FREQ ((uint32_t)ALI_RX2WIN_FREQ) + + + #else + #error ALI_DOWNLINK_FREQ not existed + + + #endif //===============================================================================> +#else + #error ALI_DOWNLINK_FREQ not define +#endif + +/*! + * Data rates table definition + */ +static const uint8_t DataratesCN470ALI[] = { 12, 11, 10, 9, 8, 7 }; + +/*! + * Bandwidths table definition in Hz + */ +static const uint32_t BandwidthsCN470ALI[] = { 125000, 125000, 125000, 125000, 125000, 125000 }; +/*! + * Maximum payload with respect to the datarate index. Cannot operate with repeater. + + + */ +static const uint8_t MaxPayloadOfDatarateCN470ALI[] = { 51+1, 51+1, 51+1, 115+1, 242+1, 242+1 }; + +/*! + * Maximum payload with respect to the datarate index. Can operate with repeater. + */ +static const uint8_t MaxPayloadOfDatarateRepeaterCN470ALI[] = { 51+1, 51+1, 51+1, 115+1, 222+1, 222+1 }; + +/*! + * \brief The function gets a value of a specific phy attribute. + * + * \param [IN] getPhy Pointer to the function parameters. + * + * \retval Returns a structure containing the PHY parameter. + */ +PhyParam_t RegionCN470ALIGetPhyParam( GetPhyParams_t* getPhy ); + +/*! + * \brief Updates the last TX done parameters of the current channel. + * + * \param [IN] txDone Pointer to the function parameters. + */ +void RegionCN470ALISetBandTxDone( SetBandTxDoneParams_t* txDone ); + +/*! + * \brief Initializes the channels masks and the channels. + * + * \param [IN] type Sets the initialization type. + */ +void RegionCN470ALIInitDefaults( InitDefaultsParams_t* params ); + +/*! + * \brief Returns a pointer to the internal context and its size. + * + * \param [OUT] params Pointer to the function parameters. + * + * \retval Points to a structure where the module store its non-volatile context. + */ +void* RegionCN470ALIGetNvmCtx( GetNvmCtxParams_t* params ); + +/*! + * \brief Verifies a parameter. + * + * \param [IN] verify Pointer to the function parameters. + * + * \param [IN] type Sets the initialization type. + * + * \retval Returns true, if the parameter is valid. + */ +bool RegionCN470ALIVerify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ); + +/*! + * \brief The function parses the input buffer and sets up the channels of the + * CF list. + * + * \param [IN] applyCFList Pointer to the function parameters. + */ +void RegionCN470ALIApplyCFList( ApplyCFListParams_t* applyCFList ); + +/*! + * \brief Sets a channels mask. + * + * \param [IN] chanMaskSet Pointer to the function parameters. + * + * \retval Returns true, if the channels mask could be set. + */ +bool RegionCN470ALIChanMaskSet( ChanMaskSetParams_t* chanMaskSet ); + +/*! + * Computes the Rx window timeout and offset. + * + * \param [IN] datarate Rx window datarate index to be used + * + * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame. + * + * \param [IN] rxError System maximum timing error of the receiver. In milliseconds + * The receiver will turn on in a [-rxError : +rxError] ms + * interval around RxOffset + * + * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields. + */ +void RegionCN470ALIComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ); + +/*! + * \brief Configuration of the RX windows. + * + * \param [IN] rxConfig Pointer to the function parameters. + * + * \param [OUT] datarate The datarate index which was set. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionCN470ALIRxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ); + +/*! + * \brief TX configuration. + * + * \param [IN] txConfig Pointer to the function parameters. + * + * \param [OUT] txPower The tx power index which was set. + * + * \param [OUT] txTimeOnAir The time-on-air of the frame. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionCN470ALITxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ); + +/*! + * \brief The function processes a Link ADR Request. + * + * \param [IN] linkAdrReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionCN470ALILinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ); + +/*! + * \brief The function processes a RX Parameter Setup Request. + * + * \param [IN] rxParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionCN470ALIRxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ); + +/*! + * \brief The function processes a Channel Request. + * + * \param [IN] newChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionCN470ALINewChannelReq( NewChannelReqParams_t* newChannelReq ); + +/*! + * \brief The function processes a TX ParamSetup Request. + * + * \param [IN] txParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + * Returns -1, if the functionality is not implemented. In this case, the end node + * shall not process the command. + */ +int8_t RegionCN470ALITxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ); + +/*! + * \brief The function processes a DlChannel Request. + * + * \param [IN] dlChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionCN470ALIDlChannelReq( DlChannelReqParams_t* dlChannelReq ); + +/*! + * \brief Alternates the datarate of the channel for the join request. + * + * \param [IN] currentDr Current datarate. + * + * \retval Datarate to apply. + */ +int8_t RegionCN470ALIAlternateDr( int8_t currentDr, AlternateDrType_t type ); + +/*! + * \brief Calculates the back-off time. + * + * \param [IN] calcBackOff Pointer to the function parameters. + */ +void RegionCN470ALICalcBackOff( CalcBackOffParams_t* calcBackOff ); + +/*! + * \brief Searches and set the next random available channel + * + * \param [OUT] channel Next channel to use for TX. + * + * \param [OUT] time Time to wait for the next transmission according to the duty + * cycle. + * + * \param [OUT] aggregatedTimeOff Updates the aggregated time off. + * + * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate] + */ +LoRaMacStatus_t RegionCN470ALINextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ); + +/*! + * \brief Adds a channel. + * + * \param [IN] channelAdd Pointer to the function parameters. + * + * \retval Status of the operation. + */ +LoRaMacStatus_t RegionCN470ALIChannelAdd( ChannelAddParams_t* channelAdd ); + +/*! + * \brief Removes a channel. + * + * \param [IN] channelRemove Pointer to the function parameters. + * + * \retval Returns true, if the channel was removed successfully. + */ +bool RegionCN470ALIChannelsRemove( ChannelRemoveParams_t* channelRemove ); + +/*! + * \brief Sets the radio into continuous wave mode. + * + * \param [IN] continuousWave Pointer to the function parameters. + */ +void RegionCN470ALISetContinuousWave( ContinuousWaveParams_t* continuousWave ); + +/*! + * \brief Computes new datarate according to the given offset + * + * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms + * + * \param [IN] dr Current datarate + * + * \param [IN] drOffset Offset to be applied + * + * \retval newDr Computed datarate. + */ +uint8_t RegionCN470ALIApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ); + +/*! + * \brief Sets the radio into beacon reception mode + * + * \param [IN] rxBeaconSetup Pointer to the function parameters + */ + void RegionCN470ALIRxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ); + +/*! \} defgroup REGIONCN470ALI */ + +#endif // __REGION_CN470ALI_H__ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCN779.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCN779.c new file mode 100644 index 00000000..27cb86d3 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCN779.c @@ -0,0 +1,1040 @@ +/*! + * \file RegionCN779.c + * + * \brief Region implementation for CN779 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) +*/ +#include "utilities.h" + +#include "RegionCommon.h" +#include "RegionCN779.h" +#include "lorawan_conf.h" +#include "mw_log_conf.h" + +// Definitions +#define CHANNELS_MASK_SIZE 1 + +/*! + * Region specific context + */ +typedef struct sRegionCN779NvmCtx +{ + /*! + * LoRaMAC channels + */ + ChannelParams_t Channels[ CN779_MAX_NB_CHANNELS ]; + /*! + * LoRaMac bands + */ + Band_t Bands[ CN779_MAX_NB_BANDS ]; + /*! + * LoRaMac channels mask + */ + uint16_t ChannelsMask[ CHANNELS_MASK_SIZE ]; + /*! + * LoRaMac channels default mask + */ + uint16_t ChannelsDefaultMask[ CHANNELS_MASK_SIZE ]; +}RegionCN779NvmCtx_t; + +/* + * Non-volatile module context. + */ +static RegionCN779NvmCtx_t NvmCtx; + +// Static functions +static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr ) +{ + uint8_t nextLowerDr = 0; + + if( dr == minDr ) + { + nextLowerDr = minDr; + } + else + { + nextLowerDr = dr - 1; + } + return nextLowerDr; +} + +static uint32_t GetBandwidth( uint32_t drIndex ) +{ + switch( BandwidthsCN779[drIndex] ) + { + default: + case 125000: + return 0; + case 250000: + return 1; + case 500000: + return 2; + } +} + +static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask ) +{ + int8_t txPowerResult = txPower; + + // Limit tx power to the band max + txPowerResult = MAX( txPower, maxBandTxPower ); + + return txPowerResult; +} + +static bool VerifyRfFreq( uint32_t freq ) +{ + // Check radio driver support + if( Radio.CheckRfFrequency( freq ) == false ) + { + return false; + } + + if( ( freq < 779500000 ) || ( freq > 786500000 ) ) + { + return false; + } + return true; +} + +static uint8_t CountNbOfEnabledChannels( bool joined, uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t delayTransmission = 0; + + for( uint8_t i = 0, k = 0; i < CN779_MAX_NB_CHANNELS; i += 16, k++ ) + { + for( uint8_t j = 0; j < 16; j++ ) + { + if( ( channelsMask[k] & ( 1 << j ) ) != 0 ) + { + if( channels[i + j].Frequency == 0 ) + { // Check if the channel is enabled + continue; + } + if( joined == false ) + { + if( ( CN779_JOIN_CHANNELS & ( 1 << j ) ) == 0 ) + { + continue; + } + } + if( RegionCommonValueInRange( datarate, channels[i + j].DrRange.Fields.Min, + channels[i + j].DrRange.Fields.Max ) == false ) + { // Check if the current channel selection supports the given datarate + continue; + } + if( bands[channels[i + j].Band].TimeOff > 0 ) + { // Check if the band is available for transmission + delayTransmission++; + continue; + } + enabledChannels[nbEnabledChannels++] = i + j; + } + } + } + + *delayTx = delayTransmission; + return nbEnabledChannels; +} + +PhyParam_t RegionCN779GetPhyParam( GetPhyParams_t* getPhy ) +{ + PhyParam_t phyParam = { 0 }; + + switch( getPhy->Attribute ) + { + case PHY_MIN_RX_DR: + { + phyParam.Value = CN779_RX_MIN_DATARATE; + break; + } + case PHY_MIN_TX_DR: + { + phyParam.Value = CN779_TX_MIN_DATARATE; + break; + } + case PHY_DEF_TX_DR: + { + phyParam.Value = CN779_DEFAULT_DATARATE; + break; + } + case PHY_NEXT_LOWER_TX_DR: + { + phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, CN779_TX_MIN_DATARATE ); + break; + } + case PHY_MAX_TX_POWER: + { + phyParam.Value = CN779_MAX_TX_POWER; + break; + } + case PHY_DEF_TX_POWER: + { + phyParam.Value = CN779_DEFAULT_TX_POWER; + break; + } + case PHY_DEF_ADR_ACK_LIMIT: + { + phyParam.Value = CN779_ADR_ACK_LIMIT; + break; + } + case PHY_DEF_ADR_ACK_DELAY: + { + phyParam.Value = CN779_ADR_ACK_DELAY; + break; + } + case PHY_MAX_PAYLOAD: + { + phyParam.Value = MaxPayloadOfDatarateCN779[getPhy->Datarate]; + break; + } + case PHY_MAX_PAYLOAD_REPEATER: + { + phyParam.Value = MaxPayloadOfDatarateRepeaterCN779[getPhy->Datarate]; + break; + } + case PHY_DUTY_CYCLE: + { + phyParam.Value = CN779_DUTY_CYCLE_ENABLED; + break; + } + case PHY_MAX_RX_WINDOW: + { + phyParam.Value = CN779_MAX_RX_WINDOW; + break; + } + case PHY_RECEIVE_DELAY1: + { + phyParam.Value = CN779_RECEIVE_DELAY1; + break; + } + case PHY_RECEIVE_DELAY2: + { + phyParam.Value = CN779_RECEIVE_DELAY2; + break; + } + case PHY_JOIN_ACCEPT_DELAY1: + { + phyParam.Value = CN779_JOIN_ACCEPT_DELAY1; + break; + } + case PHY_JOIN_ACCEPT_DELAY2: + { + phyParam.Value = CN779_JOIN_ACCEPT_DELAY2; + break; + } + case PHY_MAX_FCNT_GAP: + { + phyParam.Value = CN779_MAX_FCNT_GAP; + break; + } + case PHY_ACK_TIMEOUT: + { + phyParam.Value = ( CN779_ACKTIMEOUT + randr( -CN779_ACK_TIMEOUT_RND, CN779_ACK_TIMEOUT_RND ) ); + break; + } + case PHY_DEF_DR1_OFFSET: + { + phyParam.Value = CN779_DEFAULT_RX1_DR_OFFSET; + break; + } + case PHY_DEF_RX2_FREQUENCY: + { + phyParam.Value = CN779_RX_WND_2_FREQ; + break; + } + case PHY_DEF_RX2_DR: + { + phyParam.Value = CN779_RX_WND_2_DR; + break; + } + case PHY_CHANNELS_MASK: + { + phyParam.ChannelsMask = NvmCtx.ChannelsMask; + break; + } + case PHY_CHANNELS_DEFAULT_MASK: + { + phyParam.ChannelsMask = NvmCtx.ChannelsDefaultMask; + break; + } + case PHY_MAX_NB_CHANNELS: + { + phyParam.Value = CN779_MAX_NB_CHANNELS; + break; + } + case PHY_CHANNELS: + { + phyParam.Channels = NvmCtx.Channels; + break; + } + case PHY_DEF_UPLINK_DWELL_TIME: + case PHY_DEF_DOWNLINK_DWELL_TIME: + { + phyParam.Value = 0; + break; + } + case PHY_DEF_MAX_EIRP: + { + phyParam.fValue = CN779_DEFAULT_MAX_EIRP; + break; + } + case PHY_DEF_ANTENNA_GAIN: + { + phyParam.fValue = CN779_DEFAULT_ANTENNA_GAIN; + break; + } + case PHY_BEACON_CHANNEL_FREQ: + { + phyParam.Value = CN779_BEACON_CHANNEL_FREQ; + break; + } + case PHY_BEACON_FORMAT: + { + phyParam.BeaconFormat.BeaconSize = CN779_BEACON_SIZE; + phyParam.BeaconFormat.Rfu1Size = CN779_RFU1_SIZE; + phyParam.BeaconFormat.Rfu2Size = CN779_RFU2_SIZE; + break; + } + case PHY_BEACON_CHANNEL_DR: + { + phyParam.Value = CN779_BEACON_CHANNEL_DR; + break; + } + case PHY_PING_SLOT_CHANNEL_DR: + { + phyParam.Value = CN779_PING_SLOT_CHANNEL_DR; + break; + } + default: + { + break; + } + } + + return phyParam; +} + +void RegionCN779SetBandTxDone( SetBandTxDoneParams_t* txDone ) +{ + RegionCommonSetBandTxDone( txDone->Joined, &NvmCtx.Bands[NvmCtx.Channels[txDone->Channel].Band], txDone->LastTxDoneTime ); +} + +void RegionCN779InitDefaults( InitDefaultsParams_t* params ) +{ + Band_t bands[CN779_MAX_NB_BANDS] = + { + CN779_BAND0 + }; + + switch( params->Type ) + { + case INIT_TYPE_INIT: + { + // Initialize bands + memcpy1( ( uint8_t* )NvmCtx.Bands, ( uint8_t* )bands, sizeof( Band_t ) * CN779_MAX_NB_BANDS ); + + // Channels + NvmCtx.Channels[0] = ( ChannelParams_t ) CN779_LC1; + NvmCtx.Channels[1] = ( ChannelParams_t ) CN779_LC2; + NvmCtx.Channels[2] = ( ChannelParams_t ) CN779_LC3; + + // Initialize the channels default mask + NvmCtx.ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 ); + // Update the channels mask + RegionCommonChanMaskCopy( NvmCtx.ChannelsMask, NvmCtx.ChannelsDefaultMask, 1 ); + break; + } + case INIT_TYPE_RESTORE_CTX: + { + if( params->NvmCtx != 0 ) + { + memcpy1( (uint8_t*) &NvmCtx, (uint8_t*) params->NvmCtx, sizeof( NvmCtx ) ); + } + break; + } + case INIT_TYPE_RESTORE_DEFAULT_CHANNELS: + { + // Restore channels default mask + NvmCtx.ChannelsMask[0] |= NvmCtx.ChannelsDefaultMask[0]; + + // Channels + NvmCtx.Channels[0] = ( ChannelParams_t ) CN779_LC1; + NvmCtx.Channels[1] = ( ChannelParams_t ) CN779_LC2; + NvmCtx.Channels[2] = ( ChannelParams_t ) CN779_LC3; + break; + } + default: + { + break; + } + } +} + +void* RegionCN779GetNvmCtx( GetNvmCtxParams_t* params ) +{ + params->nvmCtxSize = sizeof( RegionCN779NvmCtx_t ); + return &NvmCtx; +} + +bool RegionCN779Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ) +{ + switch( phyAttribute ) + { + case PHY_FREQUENCY: + { + return VerifyRfFreq( verify->Frequency ); + } + case PHY_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, CN779_TX_MIN_DATARATE, CN779_TX_MAX_DATARATE ); + } + case PHY_DEF_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, DR_0, DR_5 ); + } + case PHY_RX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, CN779_RX_MIN_DATARATE, CN779_RX_MAX_DATARATE ); + } + case PHY_DEF_TX_POWER: + case PHY_TX_POWER: + { + // Remark: switched min and max! + return RegionCommonValueInRange( verify->TxPower, CN779_MAX_TX_POWER, CN779_MIN_TX_POWER ); + } + case PHY_DUTY_CYCLE: + { + return CN779_DUTY_CYCLE_ENABLED; + } + default: + return false; + } +} + +void RegionCN779ApplyCFList( ApplyCFListParams_t* applyCFList ) +{ + ChannelParams_t newChannel; + ChannelAddParams_t channelAdd; + ChannelRemoveParams_t channelRemove; + + // Setup default datarate range + newChannel.DrRange.Value = ( DR_5 << 4 ) | DR_0; + + // Size of the optional CF list + if( applyCFList->Size != 16 ) + { + return; + } + + // Last byte CFListType must be 0 to indicate the CFList contains a list of frequencies + if( applyCFList->Payload[15] != 0 ) + { + return; + } + + // Last byte is RFU, don't take it into account + for( uint8_t i = 0, chanIdx = CN779_NUMB_DEFAULT_CHANNELS; chanIdx < CN779_MAX_NB_CHANNELS; i+=3, chanIdx++ ) + { + if( chanIdx < ( CN779_NUMB_CHANNELS_CF_LIST + CN779_NUMB_DEFAULT_CHANNELS ) ) + { + // Channel frequency + newChannel.Frequency = (uint32_t) applyCFList->Payload[i]; + newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 1] << 8 ); + newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 2] << 16 ); + newChannel.Frequency *= 100; + + // Initialize alternative frequency to 0 + newChannel.Rx1Frequency = 0; + } + else + { + newChannel.Frequency = 0; + newChannel.DrRange.Value = 0; + newChannel.Rx1Frequency = 0; + } + + if( newChannel.Frequency != 0 ) + { + channelAdd.NewChannel = &newChannel; + channelAdd.ChannelId = chanIdx; + + // Try to add all channels + RegionCN779ChannelAdd( &channelAdd ); + } + else + { + channelRemove.ChannelId = chanIdx; + + RegionCN779ChannelsRemove( &channelRemove ); + } + } +} + +bool RegionCN779ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ) +{ + switch( chanMaskSet->ChannelsMaskType ) + { + case CHANNELS_MASK: + { + RegionCommonChanMaskCopy( NvmCtx.ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + case CHANNELS_DEFAULT_MASK: + { + RegionCommonChanMaskCopy( NvmCtx.ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + default: + return false; + } + return true; +} + +void RegionCN779ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ) +{ + double tSymbol = 0.0; + + // Get the datarate, perform a boundary check + rxConfigParams->Datarate = MIN( datarate, CN779_RX_MAX_DATARATE ); + rxConfigParams->Bandwidth = GetBandwidth( rxConfigParams->Datarate ); + + if( rxConfigParams->Datarate == DR_7 ) + { // FSK + tSymbol = RegionCommonComputeSymbolTimeFsk( DataratesCN779[rxConfigParams->Datarate] ); + } + else + { // LoRa + tSymbol = RegionCommonComputeSymbolTimeLoRa( DataratesCN779[rxConfigParams->Datarate], BandwidthsCN779[rxConfigParams->Datarate] ); + } + + RegionCommonComputeRxWindowParameters( tSymbol, minRxSymbols, rxError, Radio.GetWakeupTime( ), &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); +} + +bool RegionCN779RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) +{ + RadioModems_t modem; + int8_t dr = rxConfig->Datarate; + uint8_t maxPayload = 0; + int8_t phyDr = 0; + uint32_t frequency = rxConfig->Frequency; + const char *slotStrings[] = { "1", "2", "C", "Multi_C", "P", "Multi_P" }; + + if( Radio.GetStatus( ) != RF_IDLE ) + { + return false; + } + + if( rxConfig->RxSlot == RX_SLOT_WIN_1 ) + { + // Apply window 1 frequency + frequency = NvmCtx.Channels[rxConfig->Channel].Frequency; + // Apply the alternative RX 1 window frequency, if it is available + if( NvmCtx.Channels[rxConfig->Channel].Rx1Frequency != 0 ) + { + frequency = NvmCtx.Channels[rxConfig->Channel].Rx1Frequency; + } + } + + // Read the physical datarate from the datarates table + phyDr = DataratesCN779[dr]; + + Radio.SetChannel( frequency ); + + // Radio configuration + if( dr == DR_7 ) + { + modem = MODEM_FSK; + Radio.SetRxConfig( modem, 50000, phyDr * 1000, 0, 83333, 5, rxConfig->WindowTimeout, false, 0, true, 0, 0, false, rxConfig->RxContinuous ); + } + else + { + modem = MODEM_LORA; + Radio.SetRxConfig( modem, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous ); + } + + if( rxConfig->RepeaterSupport == true ) + { + maxPayload = MaxPayloadOfDatarateRepeaterCN779[dr]; + } + else + { + maxPayload = MaxPayloadOfDatarateCN779[dr]; + } + Radio.SetMaxPayloadLength( modem, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD ); + if ( rxConfig->RxSlot < RX_SLOT_NONE ) + { + MW_LOG( "RX_%s on freq %d Hz at DR %d\n\r", slotStrings[rxConfig->RxSlot], frequency, dr ); + } + else + { + MW_LOG( "RX on freq %d Hz at DR %d\n\r", frequency, dr ); + } + + *datarate = (uint8_t) dr; + return true; +} + +bool RegionCN779TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ) +{ + RadioModems_t modem; + int8_t phyDr = DataratesCN779[txConfig->Datarate]; + int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, NvmCtx.Bands[NvmCtx.Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, NvmCtx.ChannelsMask ); + uint32_t bandwidth = GetBandwidth( txConfig->Datarate ); + int8_t phyTxPower = 0; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain ); + + // Setup the radio frequency + Radio.SetChannel( NvmCtx.Channels[txConfig->Channel].Frequency ); + + if( txConfig->Datarate == DR_7 ) + { // High Speed FSK channel + modem = MODEM_FSK; + Radio.SetTxConfig( modem, phyTxPower, 25000, bandwidth, phyDr * 1000, 0, 5, false, true, 0, 0, false, 4000 ); + } + else + { + modem = MODEM_LORA; + Radio.SetTxConfig( modem, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 4000 ); + } + MW_LOG( "TX on freq %d Hz at DR %d\n\r", NvmCtx.Channels[txConfig->Channel].Frequency, txConfig->Datarate ); + + // Setup maximum payload lenght of the radio driver + Radio.SetMaxPayloadLength( modem, txConfig->PktLen ); + // Get the time-on-air of the next tx frame + *txTimeOnAir = Radio.TimeOnAir( modem, txConfig->PktLen ); + + *txPower = txPowerLimited; + return true; +} + +uint8_t RegionCN779LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ) +{ + uint8_t status = 0x07; + RegionCommonLinkAdrParams_t linkAdrParams; + uint8_t nextIndex = 0; + uint8_t bytesProcessed = 0; + uint16_t chMask = 0; + GetPhyParams_t getPhy; + PhyParam_t phyParam; + RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams; + + while( bytesProcessed < linkAdrReq->PayloadSize ) + { + // Get ADR request parameters + nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams ); + + if( nextIndex == 0 ) + break; // break loop, since no more request has been found + + // Update bytes processed + bytesProcessed += nextIndex; + + // Revert status, as we only check the last ADR request for the channel mask KO + status = 0x07; + + // Setup temporary channels mask + chMask = linkAdrParams.ChMask; + + // Verify channels mask + if( ( linkAdrParams.ChMaskCtrl == 0 ) && ( chMask == 0 ) ) + { + status &= 0xFE; // Channel mask KO + } + else if( ( ( linkAdrParams.ChMaskCtrl >= 1 ) && ( linkAdrParams.ChMaskCtrl <= 5 )) || + ( linkAdrParams.ChMaskCtrl >= 7 ) ) + { + // RFU + status &= 0xFE; // Channel mask KO + } + else + { + for( uint8_t i = 0; i < CN779_MAX_NB_CHANNELS; i++ ) + { + if( linkAdrParams.ChMaskCtrl == 6 ) + { + if( NvmCtx.Channels[i].Frequency != 0 ) + { + chMask |= 1 << i; + } + } + else + { + if( ( ( chMask & ( 1 << i ) ) != 0 ) && + ( NvmCtx.Channels[i].Frequency == 0 ) ) + {// Trying to enable an undefined channel + status &= 0xFE; // Channel mask KO + } + } + } + } + } + + // Get the minimum possible datarate + getPhy.Attribute = PHY_MIN_TX_DR; + getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime; + phyParam = RegionCN779GetPhyParam( &getPhy ); + + linkAdrVerifyParams.Status = status; + linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled; + linkAdrVerifyParams.Datarate = linkAdrParams.Datarate; + linkAdrVerifyParams.TxPower = linkAdrParams.TxPower; + linkAdrVerifyParams.NbRep = linkAdrParams.NbRep; + linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate; + linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower; + linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep; + linkAdrVerifyParams.NbChannels = CN779_MAX_NB_CHANNELS; + linkAdrVerifyParams.ChannelsMask = &chMask; + linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value; + linkAdrVerifyParams.MaxDatarate = CN779_TX_MAX_DATARATE; + linkAdrVerifyParams.Channels = NvmCtx.Channels; + linkAdrVerifyParams.MinTxPower = CN779_MIN_TX_POWER; + linkAdrVerifyParams.MaxTxPower = CN779_MAX_TX_POWER; + linkAdrVerifyParams.Version = linkAdrReq->Version; + + // Verify the parameters and update, if necessary + status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep ); + + // Update channelsMask if everything is correct + if( status == 0x07 ) + { + // Set the channels mask to a default value + memset1( ( uint8_t* ) NvmCtx.ChannelsMask, 0, sizeof( NvmCtx.ChannelsMask ) ); + // Update the channels mask + NvmCtx.ChannelsMask[0] = chMask; + } + + // Update status variables + *drOut = linkAdrParams.Datarate; + *txPowOut = linkAdrParams.TxPower; + *nbRepOut = linkAdrParams.NbRep; + *nbBytesParsed = bytesProcessed; + + return status; +} + +uint8_t RegionCN779RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ) +{ + uint8_t status = 0x07; + + // Verify radio frequency + if( VerifyRfFreq( rxParamSetupReq->Frequency ) == false ) + { + status &= 0xFE; // Channel frequency KO + } + + // Verify datarate + if( RegionCommonValueInRange( rxParamSetupReq->Datarate, CN779_RX_MIN_DATARATE, CN779_RX_MAX_DATARATE ) == false ) + { + status &= 0xFD; // Datarate KO + } + + // Verify datarate offset + if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, CN779_MIN_RX1_DR_OFFSET, CN779_MAX_RX1_DR_OFFSET ) == false ) + { + status &= 0xFB; // Rx1DrOffset range KO + } + + return status; +} + +uint8_t RegionCN779NewChannelReq( NewChannelReqParams_t* newChannelReq ) +{ + uint8_t status = 0x03; + ChannelAddParams_t channelAdd; + ChannelRemoveParams_t channelRemove; + + if( newChannelReq->NewChannel->Frequency == 0 ) + { + channelRemove.ChannelId = newChannelReq->ChannelId; + + // Remove + if( RegionCN779ChannelsRemove( &channelRemove ) == false ) + { + status &= 0xFC; + } + } + else + { + channelAdd.NewChannel = newChannelReq->NewChannel; + channelAdd.ChannelId = newChannelReq->ChannelId; + + switch( RegionCN779ChannelAdd( &channelAdd ) ) + { + case LORAMAC_STATUS_OK: + { + break; + } + case LORAMAC_STATUS_FREQUENCY_INVALID: + { + status &= 0xFE; + break; + } + case LORAMAC_STATUS_DATARATE_INVALID: + { + status &= 0xFD; + break; + } + case LORAMAC_STATUS_FREQ_AND_DR_INVALID: + { + status &= 0xFC; + break; + } + default: + { + status &= 0xFC; + break; + } + } + } + + return status; +} + +int8_t RegionCN779TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ) +{ + return -1; +} + +uint8_t RegionCN779DlChannelReq( DlChannelReqParams_t* dlChannelReq ) +{ + uint8_t status = 0x03; + + // Verify if the frequency is supported + if( VerifyRfFreq( dlChannelReq->Rx1Frequency ) == false ) + { + status &= 0xFE; + } + + // Verify if an uplink frequency exists + if( NvmCtx.Channels[dlChannelReq->ChannelId].Frequency == 0 ) + { + status &= 0xFD; + } + + // Apply Rx1 frequency, if the status is OK + if( status == 0x03 ) + { + NvmCtx.Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency; + } + + return status; +} + +int8_t RegionCN779AlternateDr( int8_t currentDr, AlternateDrType_t type ) +{ + return currentDr; +} + +void RegionCN779CalcBackOff( CalcBackOffParams_t* calcBackOff ) +{ + RegionCommonCalcBackOffParams_t calcBackOffParams; + + calcBackOffParams.Channels = NvmCtx.Channels; + calcBackOffParams.Bands = NvmCtx.Bands; + calcBackOffParams.LastTxIsJoinRequest = calcBackOff->LastTxIsJoinRequest; + calcBackOffParams.Joined = calcBackOff->Joined; + calcBackOffParams.DutyCycleEnabled = calcBackOff->DutyCycleEnabled; + calcBackOffParams.Channel = calcBackOff->Channel; + calcBackOffParams.ElapsedTime = calcBackOff->ElapsedTime; + calcBackOffParams.TxTimeOnAir = calcBackOff->TxTimeOnAir; + + RegionCommonCalcBackOff( &calcBackOffParams ); +} + +LoRaMacStatus_t RegionCN779NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t delayTx = 0; + uint8_t enabledChannels[CN779_MAX_NB_CHANNELS] = { 0 }; + TimerTime_t nextTxDelay = 0; + + if( RegionCommonCountChannels( NvmCtx.ChannelsMask, 0, 1 ) == 0 ) + { // Reactivate default channels + NvmCtx.ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); + } + + TimerTime_t elapsed = TimerGetElapsedTime( nextChanParams->LastAggrTx ); + if( ( nextChanParams->LastAggrTx == 0 ) || ( nextChanParams->AggrTimeOff <= elapsed ) ) + { + // Reset Aggregated time off + *aggregatedTimeOff = 0; + + // Update bands Time OFF + nextTxDelay = RegionCommonUpdateBandTimeOff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, NvmCtx.Bands, CN779_MAX_NB_BANDS ); + + // Search how many channels are enabled + nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Joined, nextChanParams->Datarate, + NvmCtx.ChannelsMask, NvmCtx.Channels, + NvmCtx.Bands, enabledChannels, &delayTx ); + } + else + { + delayTx++; + nextTxDelay = nextChanParams->AggrTimeOff - elapsed; + } + + if( nbEnabledChannels > 0 ) + { + // We found a valid channel + *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )]; + + *time = 0; + return LORAMAC_STATUS_OK; + } + else + { + if( delayTx > 0 ) + { + // Delay transmission due to AggregatedTimeOff or to a band time off + *time = nextTxDelay; + return LORAMAC_STATUS_DUTYCYCLE_RESTRICTED; + } + // Datarate not supported by any channel, restore defaults + NvmCtx.ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); + *time = 0; + return LORAMAC_STATUS_NO_CHANNEL_FOUND; + } +} + +LoRaMacStatus_t RegionCN779ChannelAdd( ChannelAddParams_t* channelAdd ) +{ + bool drInvalid = false; + bool freqInvalid = false; + uint8_t id = channelAdd->ChannelId; + + if( id < CN779_NUMB_DEFAULT_CHANNELS ) + { + return LORAMAC_STATUS_FREQ_AND_DR_INVALID; + } + + if( id >= CN779_MAX_NB_CHANNELS ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + // Validate the datarate range + if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Min, CN779_TX_MIN_DATARATE, CN779_TX_MAX_DATARATE ) == false ) + { + drInvalid = true; + } + if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, CN779_TX_MIN_DATARATE, CN779_TX_MAX_DATARATE ) == false ) + { + drInvalid = true; + } + if( channelAdd->NewChannel->DrRange.Fields.Min > channelAdd->NewChannel->DrRange.Fields.Max ) + { + drInvalid = true; + } + + // Check frequency + if( freqInvalid == false ) + { + if( VerifyRfFreq( channelAdd->NewChannel->Frequency ) == false ) + { + freqInvalid = true; + } + } + + // Check status + if( ( drInvalid == true ) && ( freqInvalid == true ) ) + { + return LORAMAC_STATUS_FREQ_AND_DR_INVALID; + } + if( drInvalid == true ) + { + return LORAMAC_STATUS_DATARATE_INVALID; + } + if( freqInvalid == true ) + { + return LORAMAC_STATUS_FREQUENCY_INVALID; + } + + memcpy1( ( uint8_t* ) &(NvmCtx.Channels[id]), ( uint8_t* ) channelAdd->NewChannel, sizeof( NvmCtx.Channels[id] ) ); + NvmCtx.Channels[id].Band = 0; + NvmCtx.ChannelsMask[0] |= ( 1 << id ); + return LORAMAC_STATUS_OK; +} + +bool RegionCN779ChannelsRemove( ChannelRemoveParams_t* channelRemove ) +{ + uint8_t id = channelRemove->ChannelId; + + if( id < CN779_NUMB_DEFAULT_CHANNELS ) + { + return false; + } + + // Remove the channel from the list of channels + NvmCtx.Channels[id] = ( ChannelParams_t ){ 0, 0, { 0 }, 0 }; + + return RegionCommonChanDisable( NvmCtx.ChannelsMask, id, CN779_MAX_NB_CHANNELS ); +} + +void RegionCN779SetContinuousWave( ContinuousWaveParams_t* continuousWave ) +{ + int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, NvmCtx.Bands[NvmCtx.Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, NvmCtx.ChannelsMask ); + int8_t phyTxPower = 0; + uint32_t frequency = NvmCtx.Channels[continuousWave->Channel].Frequency; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, continuousWave->MaxEirp, continuousWave->AntennaGain ); + + Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout ); +} + +uint8_t RegionCN779ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) +{ + int8_t datarate = dr - drOffset; + + if( datarate < 0 ) + { + datarate = DR_0; + } + return datarate; +} + +void RegionCN779RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ) +{ + RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup; + + regionCommonRxBeaconSetup.Datarates = DataratesCN779; + regionCommonRxBeaconSetup.Frequency = rxBeaconSetup->Frequency; + regionCommonRxBeaconSetup.BeaconSize = CN779_BEACON_SIZE; + regionCommonRxBeaconSetup.BeaconDatarate = CN779_BEACON_CHANNEL_DR; + regionCommonRxBeaconSetup.BeaconChannelBW = CN779_BEACON_CHANNEL_BW; + regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime; + regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout; + + RegionCommonRxBeaconSetup( ®ionCommonRxBeaconSetup ); + + // Store downlink datarate + *outDr = CN779_BEACON_CHANNEL_DR; +} diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCN779.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCN779.h new file mode 100644 index 00000000..3c404dd8 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCN779.h @@ -0,0 +1,508 @@ +/*! + * \file RegionCN779.h + * + * \brief Region definition for CN779 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + * + * \defgroup REGIONCN779 Region CN779 + * Implementation according to LoRaWAN Specification v1.0.2. + * \{ + */ +#ifndef __REGION_CN779_H__ +#define __REGION_CN779_H__ + +#include "region/Region.h" + +/*! + * LoRaMac maximum number of channels + */ +#define CN779_MAX_NB_CHANNELS 16 + +/*! + * Number of default channels + */ +#define CN779_NUMB_DEFAULT_CHANNELS 3 + +/*! + * Number of channels to apply for the CF list + */ +#define CN779_NUMB_CHANNELS_CF_LIST 5 + +/*! + * Minimal datarate that can be used by the node + */ +#define CN779_TX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define CN779_TX_MAX_DATARATE DR_7 + +/*! + * Minimal datarate that can be used by the node + */ +#define CN779_RX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define CN779_RX_MAX_DATARATE DR_7 + +/*! + * Default datarate used by the node + */ +#define CN779_DEFAULT_DATARATE DR_0 + +/*! + * Minimal Rx1 receive datarate offset + */ +#define CN779_MIN_RX1_DR_OFFSET 0 + +/*! + * Maximal Rx1 receive datarate offset + */ +#define CN779_MAX_RX1_DR_OFFSET 5 + +/*! + * Default Rx1 receive datarate offset + */ +#define CN779_DEFAULT_RX1_DR_OFFSET 0 + +/*! + * Minimal Tx output power that can be used by the node + */ +#define CN779_MIN_TX_POWER TX_POWER_5 + +/*! + * Maximal Tx output power that can be used by the node + */ +#define CN779_MAX_TX_POWER TX_POWER_0 + +/*! + * Default Tx output power used by the node + */ +#define CN779_DEFAULT_TX_POWER TX_POWER_0 + +/*! + * Default Max EIRP + */ +#define CN779_DEFAULT_MAX_EIRP 12.15f + +/*! + * Default antenna gain + */ +#define CN779_DEFAULT_ANTENNA_GAIN 2.15f + +/*! + * ADR Ack limit + */ +#define CN779_ADR_ACK_LIMIT 64 + +/*! + * ADR Ack delay + */ +#define CN779_ADR_ACK_DELAY 32 + +/*! + * Enabled or disabled the duty cycle + */ +#define CN779_DUTY_CYCLE_ENABLED 1 + +/*! + * Maximum RX window duration + */ +#define CN779_MAX_RX_WINDOW 3000 + +/*! + * Receive delay 1 + */ +#define CN779_RECEIVE_DELAY1 1000 + +/*! + * Receive delay 2 + */ +#define CN779_RECEIVE_DELAY2 2000 + +/*! + * Join accept delay 1 + */ +#define CN779_JOIN_ACCEPT_DELAY1 5000 + +/*! + * Join accept delay 2 + */ +#define CN779_JOIN_ACCEPT_DELAY2 6000 + +/*! + * Maximum frame counter gap + */ +#define CN779_MAX_FCNT_GAP 16384 + +/*! + * Ack timeout + */ +#define CN779_ACKTIMEOUT 2000 + +/*! + * Random ack timeout limits + */ +#define CN779_ACK_TIMEOUT_RND 1000 + +/*! + * Verification of default datarate + */ +#if ( CN779_DEFAULT_DATARATE > DR_5 ) +#error "A default DR higher than DR_5 may lead to connectivity loss." +#endif + +/*! + * Second reception window channel frequency definition. + */ +#define CN779_RX_WND_2_FREQ 786000000 + +/*! + * Second reception window channel datarate definition. + */ +#define CN779_RX_WND_2_DR DR_0 + +/* + * CLASS B + */ +/*! + * Beacon frequency + */ +#define CN779_BEACON_CHANNEL_FREQ 785000000 + +/*! + * Payload size of a beacon frame + */ +#define CN779_BEACON_SIZE 17 + +/*! + * Size of RFU 1 field + */ +#define CN779_RFU1_SIZE 2 + +/*! + * Size of RFU 2 field + */ +#define CN779_RFU2_SIZE 0 + +/*! + * Datarate of the beacon channel + */ +#define CN779_BEACON_CHANNEL_DR DR_3 + +/*! + * Bandwith of the beacon channel + */ +#define CN779_BEACON_CHANNEL_BW 0 + +/*! + * Ping slot channel datarate + */ +#define CN779_PING_SLOT_CHANNEL_DR DR_3 + +/*! + * LoRaMac maximum number of bands + */ +#define CN779_MAX_NB_BANDS 1 + +/*! + * Band 0 definition + * { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff } + */ +#define CN779_BAND0 { 100, CN779_MAX_TX_POWER, 0, 0, 0 } // 1.0 % + +/*! + * LoRaMac default channel 1 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define CN779_LC1 { 779500000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } +/*! + * LoRaMac default channel 2 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define CN779_LC2 { 779700000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } + +/*! + * LoRaMac default channel 3 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define CN779_LC3 { 779900000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } + +/*! + * LoRaMac channels which are allowed for the join procedure + */ +#define CN779_JOIN_CHANNELS ( uint16_t )( LC( 1 ) | LC( 2 ) | LC( 3 ) ) + +/*! + * Data rates table definition + */ +static const uint8_t DataratesCN779[] = { 12, 11, 10, 9, 8, 7, 7, 50 }; + +/*! + * Bandwidths table definition in Hz + */ +static const uint32_t BandwidthsCN779[] = { 125000, 125000, 125000, 125000, 125000, 125000, 250000, 0 }; + +/*! + * Maximum payload with respect to the datarate index. Cannot operate with repeater. + */ +static const uint8_t MaxPayloadOfDatarateCN779[] = { 51, 51, 51, 115, 242, 242, 242, 242 }; + +/*! + * Maximum payload with respect to the datarate index. Can operate with repeater. + */ +static const uint8_t MaxPayloadOfDatarateRepeaterCN779[] = { 51, 51, 51, 115, 222, 222, 222, 222 }; + +/*! + * \brief The function gets a value of a specific phy attribute. + * + * \param [IN] getPhy Pointer to the function parameters. + * + * \retval Returns a structure containing the PHY parameter. + */ +PhyParam_t RegionCN779GetPhyParam( GetPhyParams_t* getPhy ); + +/*! + * \brief Updates the last TX done parameters of the current channel. + * + * \param [IN] txDone Pointer to the function parameters. + */ +void RegionCN779SetBandTxDone( SetBandTxDoneParams_t* txDone ); + +/*! + * \brief Initializes the channels masks and the channels. + * + * \param [IN] type Sets the initialization type. + */ +void RegionCN779InitDefaults( InitDefaultsParams_t* params ); + +/*! + * \brief Returns a pointer to the internal context and its size. + * + * \param [OUT] params Pointer to the function parameters. + * + * \retval Points to a structure where the module store its non-volatile context. + */ +void* RegionCN779GetNvmCtx( GetNvmCtxParams_t* params ); + +/*! + * \brief Verifies a parameter. + * + * \param [IN] verify Pointer to the function parameters. + * + * \param [IN] type Sets the initialization type. + * + * \retval Returns true, if the parameter is valid. + */ +bool RegionCN779Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ); + +/*! + * \brief The function parses the input buffer and sets up the channels of the + * CF list. + * + * \param [IN] applyCFList Pointer to the function parameters. + */ +void RegionCN779ApplyCFList( ApplyCFListParams_t* applyCFList ); + +/*! + * \brief Sets a channels mask. + * + * \param [IN] chanMaskSet Pointer to the function parameters. + * + * \retval Returns true, if the channels mask could be set. + */ +bool RegionCN779ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ); + +/*! + * Computes the Rx window timeout and offset. + * + * \param [IN] datarate Rx window datarate index to be used + * + * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame. + * + * \param [IN] rxError System maximum timing error of the receiver. In milliseconds + * The receiver will turn on in a [-rxError : +rxError] ms + * interval around RxOffset + * + * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields. + */ +void RegionCN779ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ); + +/*! + * \brief Configuration of the RX windows. + * + * \param [IN] rxConfig Pointer to the function parameters. + * + * \param [OUT] datarate The datarate index which was set. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionCN779RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ); + +/*! + * \brief TX configuration. + * + * \param [IN] txConfig Pointer to the function parameters. + * + * \param [OUT] txPower The tx power index which was set. + * + * \param [OUT] txTimeOnAir The time-on-air of the frame. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionCN779TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ); + +/*! + * \brief The function processes a Link ADR Request. + * + * \param [IN] linkAdrReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionCN779LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ); + +/*! + * \brief The function processes a RX Parameter Setup Request. + * + * \param [IN] rxParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionCN779RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ); + +/*! + * \brief The function processes a Channel Request. + * + * \param [IN] newChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionCN779NewChannelReq( NewChannelReqParams_t* newChannelReq ); + +/*! + * \brief The function processes a TX ParamSetup Request. + * + * \param [IN] txParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + * Returns -1, if the functionality is not implemented. In this case, the end node + * shall not process the command. + */ +int8_t RegionCN779TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ); + +/*! + * \brief The function processes a DlChannel Request. + * + * \param [IN] dlChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionCN779DlChannelReq( DlChannelReqParams_t* dlChannelReq ); + +/*! + * \brief Alternates the datarate of the channel for the join request. + * + * \param [IN] currentDr Current datarate. + * + * \retval Datarate to apply. + */ +int8_t RegionCN779AlternateDr( int8_t currentDr, AlternateDrType_t type ); + +/*! + * \brief Calculates the back-off time. + * + * \param [IN] calcBackOff Pointer to the function parameters. + */ +void RegionCN779CalcBackOff( CalcBackOffParams_t* calcBackOff ); + +/*! + * \brief Searches and set the next random available channel + * + * \param [OUT] channel Next channel to use for TX. + * + * \param [OUT] time Time to wait for the next transmission according to the duty + * cycle. + * + * \param [OUT] aggregatedTimeOff Updates the aggregated time off. + * + * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate] + */ +LoRaMacStatus_t RegionCN779NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ); + +/*! + * \brief Adds a channel. + * + * \param [IN] channelAdd Pointer to the function parameters. + * + * \retval Status of the operation. + */ +LoRaMacStatus_t RegionCN779ChannelAdd( ChannelAddParams_t* channelAdd ); + +/*! + * \brief Removes a channel. + * + * \param [IN] channelRemove Pointer to the function parameters. + * + * \retval Returns true, if the channel was removed successfully. + */ +bool RegionCN779ChannelsRemove( ChannelRemoveParams_t* channelRemove ); + +/*! + * \brief Sets the radio into continuous wave mode. + * + * \param [IN] continuousWave Pointer to the function parameters. + */ +void RegionCN779SetContinuousWave( ContinuousWaveParams_t* continuousWave ); + +/*! + * \brief Computes new datarate according to the given offset + * + * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms + * + * \param [IN] dr Current datarate + * + * \param [IN] drOffset Offset to be applied + * + * \retval newDr Computed datarate. + */ +uint8_t RegionCN779ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ); + +/*! + * \brief Sets the radio into beacon reception mode + * + * \param [IN] rxBeaconSetup Pointer to the function parameters + */ + void RegionCN779RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ); + +/*! \} defgroup REGIONCN779 */ + +#endif // __REGION_CN779_H__ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCommon.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCommon.c new file mode 100644 index 00000000..33a90c2a --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCommon.c @@ -0,0 +1,387 @@ +/*! + * \file RegionCommon.c + * + * \brief LoRa MAC common region implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + */ +#include +#include "radio.h" +#include "utilities.h" +#include "RegionCommon.h" + +#define BACKOFF_DC_1_HOUR 100 +#define BACKOFF_DC_10_HOURS 1000 +#define BACKOFF_DC_24_HOURS 10000 + +static uint8_t CountChannels( uint16_t mask, uint8_t nbBits ) +{ + uint8_t nbActiveBits = 0; + + for( uint8_t j = 0; j < nbBits; j++ ) + { + if( ( mask & ( 1 << j ) ) == ( 1 << j ) ) + { + nbActiveBits++; + } + } + return nbActiveBits; +} + +uint16_t RegionCommonGetJoinDc( TimerTime_t elapsedTime ) +{ + uint16_t dutyCycle = 0; + + if( elapsedTime < 3600000 ) + { + dutyCycle = BACKOFF_DC_1_HOUR; + } + else if( elapsedTime < ( 3600000 + 36000000 ) ) + { + dutyCycle = BACKOFF_DC_10_HOURS; + } + else + { + dutyCycle = BACKOFF_DC_24_HOURS; + } + return dutyCycle; +} + +bool RegionCommonChanVerifyDr( uint8_t nbChannels, uint16_t* channelsMask, int8_t dr, int8_t minDr, int8_t maxDr, ChannelParams_t* channels ) +{ + if( RegionCommonValueInRange( dr, minDr, maxDr ) == 0 ) + { + return false; + } + + for( uint8_t i = 0, k = 0; i < nbChannels; i += 16, k++ ) + { + for( uint8_t j = 0; j < 16; j++ ) + { + if( ( ( channelsMask[k] & ( 1 << j ) ) != 0 ) ) + {// Check datarate validity for enabled channels + if( RegionCommonValueInRange( dr, ( channels[i + j].DrRange.Fields.Min & 0x0F ), + ( channels[i + j].DrRange.Fields.Max & 0x0F ) ) == 1 ) + { + // At least 1 channel has been found we can return OK. + return true; + } + } + } + } + return false; +} + +uint8_t RegionCommonValueInRange( int8_t value, int8_t min, int8_t max ) +{ + if( ( value >= min ) && ( value <= max ) ) + { + return 1; + } + return 0; +} + +bool RegionCommonChanDisable( uint16_t* channelsMask, uint8_t id, uint8_t maxChannels ) +{ + uint8_t index = id / 16; + + if( ( index > ( maxChannels / 16 ) ) || ( id >= maxChannels ) ) + { + return false; + } + + // Deactivate channel + channelsMask[index] &= ~( 1 << ( id % 16 ) ); + + return true; +} + +uint8_t RegionCommonCountChannels( uint16_t* channelsMask, uint8_t startIdx, uint8_t stopIdx ) +{ + uint8_t nbChannels = 0; + + if( channelsMask == NULL ) + { + return 0; + } + + for( uint8_t i = startIdx; i < stopIdx; i++ ) + { + nbChannels += CountChannels( channelsMask[i], 16 ); + } + + return nbChannels; +} + +void RegionCommonChanMaskCopy( uint16_t* channelsMaskDest, uint16_t* channelsMaskSrc, uint8_t len ) +{ + if( ( channelsMaskDest != NULL ) && ( channelsMaskSrc != NULL ) ) + { + for( uint8_t i = 0; i < len; i++ ) + { + channelsMaskDest[i] = channelsMaskSrc[i]; + } + } +} + +void RegionCommonSetBandTxDone( bool joined, Band_t* band, TimerTime_t lastTxDone ) +{ + if( joined == true ) + { + band->LastTxDoneTime = lastTxDone; + } + else + { + band->LastTxDoneTime = lastTxDone; + band->LastJoinTxDoneTime = lastTxDone; + } +} + +TimerTime_t RegionCommonUpdateBandTimeOff( bool joined, bool dutyCycle, Band_t* bands, uint8_t nbBands ) +{ + TimerTime_t nextTxDelay = TIMERTIME_T_MAX; + + // Update bands Time OFF + for( uint8_t i = 0; i < nbBands; i++ ) + { + if( joined == false ) + { + TimerTime_t elapsedJoin = TimerGetElapsedTime( bands[i].LastJoinTxDoneTime ); + TimerTime_t elapsedTx = TimerGetElapsedTime( bands[i].LastTxDoneTime ); + TimerTime_t txDoneTime = MAX( elapsedJoin, + ( dutyCycle == true ) ? elapsedTx : 0 ); + + if( bands[i].TimeOff <= txDoneTime ) + { + bands[i].TimeOff = 0; + } + if( bands[i].TimeOff != 0 ) + { + nextTxDelay = MIN( bands[i].TimeOff - txDoneTime, nextTxDelay ); + } + } + else + { + if( dutyCycle == true ) + { + TimerTime_t elapsed = TimerGetElapsedTime( bands[i].LastTxDoneTime ); + if( bands[i].TimeOff <= elapsed ) + { + bands[i].TimeOff = 0; + } + if( bands[i].TimeOff != 0 ) + { + nextTxDelay = MIN( bands[i].TimeOff - elapsed, nextTxDelay ); + } + } + else + { + nextTxDelay = 0; + bands[i].TimeOff = 0; + } + } + } + + return ( nextTxDelay == TIMERTIME_T_MAX ) ? 0 : nextTxDelay; +} + +uint8_t RegionCommonParseLinkAdrReq( uint8_t* payload, RegionCommonLinkAdrParams_t* linkAdrParams ) +{ + uint8_t retIndex = 0; + + if( payload[0] == SRV_MAC_LINK_ADR_REQ ) + { + // Parse datarate and tx power + linkAdrParams->Datarate = payload[1]; + linkAdrParams->TxPower = linkAdrParams->Datarate & 0x0F; + linkAdrParams->Datarate = ( linkAdrParams->Datarate >> 4 ) & 0x0F; + // Parse ChMask + linkAdrParams->ChMask = ( uint16_t )payload[2]; + linkAdrParams->ChMask |= ( uint16_t )payload[3] << 8; + // Parse ChMaskCtrl and nbRep + linkAdrParams->NbRep = payload[4]; + linkAdrParams->ChMaskCtrl = ( linkAdrParams->NbRep >> 4 ) & 0x07; + linkAdrParams->NbRep &= 0x0F; + + // LinkAdrReq has 4 bytes length + 1 byte CMD + retIndex = 5; + } + return retIndex; +} + +uint8_t RegionCommonLinkAdrReqVerifyParams( RegionCommonLinkAdrReqVerifyParams_t* verifyParams, int8_t* dr, int8_t* txPow, uint8_t* nbRep ) +{ + uint8_t status = verifyParams->Status; + int8_t datarate = verifyParams->Datarate; + int8_t txPower = verifyParams->TxPower; + int8_t nbRepetitions = verifyParams->NbRep; + + // Handle the case when ADR is off. + if( verifyParams->AdrEnabled == false ) + { + // When ADR is off, we are allowed to change the channels mask + nbRepetitions = verifyParams->CurrentNbRep; + datarate = verifyParams->CurrentDatarate; + txPower = verifyParams->CurrentTxPower; + } + + if( status != 0 ) + { + // Verify datarate. The variable phyParam. Value contains the minimum allowed datarate. + if( RegionCommonChanVerifyDr( verifyParams->NbChannels, verifyParams->ChannelsMask, datarate, + verifyParams->MinDatarate, verifyParams->MaxDatarate, verifyParams->Channels ) == false ) + { + status &= 0xFD; // Datarate KO + } + + // Verify tx power + if( RegionCommonValueInRange( txPower, verifyParams->MaxTxPower, verifyParams->MinTxPower ) == 0 ) + { + // Verify if the maximum TX power is exceeded + if( verifyParams->MaxTxPower > txPower ) + { // Apply maximum TX power. Accept TX power. + txPower = verifyParams->MaxTxPower; + } + else + { + status &= 0xFB; // TxPower KO + } + } + } + + // If the status is ok, verify the NbRep + if( status == 0x07 ) + { + if( nbRepetitions == 0 ) + { // Restore the default value according to the LoRaWAN specification + nbRepetitions = 1; + } + } + + // Apply changes + *dr = datarate; + *txPow = txPower; + *nbRep = nbRepetitions; + + return status; +} + +double RegionCommonComputeSymbolTimeLoRa( uint8_t phyDr, uint32_t bandwidth ) +{ + return ( ( double )( 1 << phyDr ) / ( double )bandwidth ) * 1000; +} + +double RegionCommonComputeSymbolTimeFsk( uint8_t phyDr ) +{ + return ( 8.0 / ( double )phyDr ); // 1 symbol equals 1 byte +} + +void RegionCommonComputeRxWindowParameters( double tSymbol, uint8_t minRxSymbols, uint32_t rxError, uint32_t wakeUpTime, uint32_t* windowTimeout, int32_t* windowOffset ) +{ + *windowTimeout = MAX( ( uint32_t )ceil( ( ( 2 * minRxSymbols - 8 ) * tSymbol + 2 * rxError ) / tSymbol ), minRxSymbols ); // Computed number of symbols + *windowOffset = ( int32_t )ceil( ( 4.0 * tSymbol ) - ( ( *windowTimeout * tSymbol ) / 2.0 ) - wakeUpTime ); +} + +int8_t RegionCommonComputeTxPower( int8_t txPowerIndex, float maxEirp, float antennaGain ) +{ + int8_t phyTxPower = 0; + + phyTxPower = ( int8_t )floor( ( maxEirp - ( txPowerIndex * 2U ) ) - antennaGain ); + + return phyTxPower; +} + +void RegionCommonCalcBackOff( RegionCommonCalcBackOffParams_t* calcBackOffParams ) +{ + uint8_t bandIdx = calcBackOffParams->Channels[calcBackOffParams->Channel].Band; + uint16_t dutyCycle = calcBackOffParams->Bands[bandIdx].DCycle; + uint16_t joinDutyCycle = 0; + + // Reset time-off to initial value. + calcBackOffParams->Bands[bandIdx].TimeOff = 0; + + if( calcBackOffParams->Joined == false ) + { + // Get the join duty cycle + joinDutyCycle = RegionCommonGetJoinDc( calcBackOffParams->ElapsedTime ); + // Apply the most restricting duty cycle + dutyCycle = MAX( dutyCycle, joinDutyCycle ); + // Reset the timeoff if the last frame was not a join request and when the duty cycle is not enabled + if( ( calcBackOffParams->DutyCycleEnabled == false ) && ( calcBackOffParams->LastTxIsJoinRequest == false ) ) + { + // This is the case when the duty cycle is off and the last uplink frame was not a join. + // This could happen in case of a rejoin, e.g. in compliance test mode. + // In this special case we have to set the time off to 0, since the join duty cycle shall only + // be applied after the first join request. + calcBackOffParams->Bands[bandIdx].TimeOff = 0; + } + else + { + // Apply band time-off. + calcBackOffParams->Bands[bandIdx].TimeOff = calcBackOffParams->TxTimeOnAir * dutyCycle - calcBackOffParams->TxTimeOnAir; + } + } + else + { + if( calcBackOffParams->DutyCycleEnabled == true ) + { + calcBackOffParams->Bands[bandIdx].TimeOff = calcBackOffParams->TxTimeOnAir * dutyCycle - calcBackOffParams->TxTimeOnAir; + } + else + { + calcBackOffParams->Bands[bandIdx].TimeOff = 0; + } + } +} + + +void RegionCommonRxBeaconSetup( RegionCommonRxBeaconSetupParams_t* rxBeaconSetupParams ) +{ + bool rxContinuous = true; + uint8_t datarate; + + // Set the radio into sleep mode + Radio.Sleep( ); + + // Setup frequency and payload length + Radio.SetChannel( rxBeaconSetupParams->Frequency ); + Radio.SetMaxPayloadLength( MODEM_LORA, rxBeaconSetupParams->BeaconSize ); + + // Check the RX continuous mode + if( rxBeaconSetupParams->RxTime != 0 ) + { + rxContinuous = false; + } + + // Get region specific datarate + datarate = rxBeaconSetupParams->Datarates[rxBeaconSetupParams->BeaconDatarate]; + + // Setup radio + Radio.SetRxConfig( MODEM_LORA, rxBeaconSetupParams->BeaconChannelBW, datarate, + 1, 0, 10, rxBeaconSetupParams->SymbolTimeout, true, rxBeaconSetupParams->BeaconSize, false, 0, 0, false, rxContinuous ); + + Radio.Rx( rxBeaconSetupParams->RxTime ); +} diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCommon.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCommon.h new file mode 100644 index 00000000..79fafeae --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionCommon.h @@ -0,0 +1,421 @@ +/*! + * \file RegionCommon.h + * + * \brief Region independent implementations which are common to all regions. + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + * + * \defgroup REGIONCOMMON Common region implementation + * Region independent implementations which are common to all regions. + * \{ + */ +#ifndef __REGIONCOMMON_H__ +#define __REGIONCOMMON_H__ + +#include "utilities.h" +#include "radio.h" +#include "LoRaMacTypes.h" +#include "region/Region.h" + +typedef struct sRegionCommonLinkAdrParams +{ + /*! + * Number of repetitions. + */ + uint8_t NbRep; + /*! + * Datarate. + */ + int8_t Datarate; + /*! + * Tx power. + */ + int8_t TxPower; + /*! + * Channels mask control field. + */ + uint8_t ChMaskCtrl; + /*! + * Channels mask field. + */ + uint16_t ChMask; +}RegionCommonLinkAdrParams_t; + +typedef struct sRegionCommonLinkAdrReqVerifyParams +{ + /*! + * LoRaWAN specification Version + */ + Version_t Version; + /*! + * The current status of the AdrLinkRequest. + */ + uint8_t Status; + /*! + * Set to true, if ADR is enabled. + */ + bool AdrEnabled; + /*! + * The datarate the AdrLinkRequest wants to set. + */ + int8_t Datarate; + /*! + * The TX power the AdrLinkRequest wants to set. + */ + int8_t TxPower; + /*! + * The number of repetitions the AdrLinkRequest wants to set. + */ + uint8_t NbRep; + /*! + * The current datarate the node is using. + */ + int8_t CurrentDatarate; + /*! + * The current TX power the node is using. + */ + int8_t CurrentTxPower; + /*! + * The current number of repetitions the node is using. + */ + int8_t CurrentNbRep; + /*! + * The number of channels. + */ + uint8_t NbChannels; + /*! + * Pointer to the first element of the channels mask. + */ + uint16_t* ChannelsMask; + /*! + * The minimum possible datarate. + */ + int8_t MinDatarate; + /*! + * The maximum possible datarate. + */ + int8_t MaxDatarate; + /*! + * Pointer to the channels. + */ + ChannelParams_t* Channels; + /*! + * The minimum possible TX power. + */ + int8_t MinTxPower; + /*! + * The maximum possible TX power. + */ + int8_t MaxTxPower; +}RegionCommonLinkAdrReqVerifyParams_t; + +typedef struct sRegionCommonCalcBackOffParams +{ + /*! + * A pointer to region specific channels. + */ + ChannelParams_t* Channels; + /*! + * A pointer to region specific bands. + */ + Band_t* Bands; + /*! + * Set to true, if the last uplink was a join request. + */ + bool LastTxIsJoinRequest; + /*! + * Set to true, if the node is joined. + */ + bool Joined; + /*! + * Set to true, if the duty cycle is enabled. + */ + bool DutyCycleEnabled; + /*! + * The current channel. + */ + uint8_t Channel; + /*! + * The elapsed time since initialization. + */ + TimerTime_t ElapsedTime; + /*! + * The time on air of the last Tx frame. + */ + TimerTime_t TxTimeOnAir; +}RegionCommonCalcBackOffParams_t; + +typedef struct sRegionCommonRxBeaconSetupParams +{ + /*! + * A pointer to the available datarates. + */ + const uint8_t* Datarates; + /*! + * Frequency + */ + uint32_t Frequency; + /*! + * The size of the beacon frame. + */ + uint8_t BeaconSize; + /*! + * The datarate of the beacon. + */ + uint8_t BeaconDatarate; + /*! + * The channel bandwidth of the beacon. + */ + uint8_t BeaconChannelBW; + /*! + * The RX time. + */ + uint32_t RxTime; + /*! + * The symbol timeout of the RX procedure. + */ + uint16_t SymbolTimeout; +}RegionCommonRxBeaconSetupParams_t; + +/*! + * \brief Calculates the join duty cycle. + * This is a generic function and valid for all regions. + * + * \param [IN] elapsedTime Elapsed time since the start of the device. + * + * \retval Duty cycle restriction. + */ +uint16_t RegionCommonGetJoinDc( TimerTime_t elapsedTime ); + +/*! + * \brief Verifies, if a value is in a given range. + * This is a generic function and valid for all regions. + * + * \param [IN] value Value to verify, if it is in range. + * + * \param [IN] min Minimum possible value. + * + * \param [IN] max Maximum possible value. + * + * \retval Returns 1 if the value is in range, otherwise 0. + */ +uint8_t RegionCommonValueInRange( int8_t value, int8_t min, int8_t max ); + +/*! + * \brief Verifies, if a datarate is available on an active channel. + * This is a generic function and valid for all regions. + * + * \param [IN] nbChannels Number of channels. + * + * \param [IN] channelsMask The channels mask of the region. + * + * \param [IN] dr The datarate to verify. + * + * \param [IN] minDr Minimum datarate. + * + * \param [IN] maxDr Maximum datarate. + * + * \param [IN] channels The channels of the region. + * + * \retval Returns true if the datarate is supported, false if not. + */ +bool RegionCommonChanVerifyDr( uint8_t nbChannels, uint16_t* channelsMask, int8_t dr, + int8_t minDr, int8_t maxDr, ChannelParams_t* channels ); + +/*! + * \brief Disables a channel in a given channels mask. + * This is a generic function and valid for all regions. + * + * \param [IN] channelsMask The channels mask of the region. + * + * \param [IN] id The id of the channels mask to disable. + * + * \param [IN] maxChannels Maximum number of channels. + * + * \retval Returns true if the channel could be disabled, false if not. + */ +bool RegionCommonChanDisable( uint16_t* channelsMask, uint8_t id, uint8_t maxChannels ); + +/*! + * \brief Counts the number of active channels in a given channels mask. + * This is a generic function and valid for all regions. + * + * \param [IN] channelsMask The channels mask of the region. + * + * \param [IN] startIdx Start index. + * + * \param [IN] stopIdx Stop index ( the channels of this index will not be counted ). + * + * \retval Returns the number of active channels. + */ +uint8_t RegionCommonCountChannels( uint16_t* channelsMask, uint8_t startIdx, uint8_t stopIdx ); + +/*! + * \brief Copy a channels mask. + * This is a generic function and valid for all regions. + * + * \param [IN] channelsMaskDest The destination channels mask. + * + * \param [IN] channelsMaskSrc The source channels mask. + * + * \param [IN] len The index length to copy. + */ +void RegionCommonChanMaskCopy( uint16_t* channelsMaskDest, uint16_t* channelsMaskSrc, uint8_t len ); + +/*! + * \brief Sets the last tx done property. + * This is a generic function and valid for all regions. + * + * \param [IN] joined Set to true, if the node has joined the network + * + * \param [IN] band The band to be updated. + * + * \param [IN] lastTxDone The time of the last TX done. + */ +void RegionCommonSetBandTxDone( bool joined, Band_t* band, TimerTime_t lastTxDone ); + +/*! + * \brief Updates the time-offs of the bands. + * This is a generic function and valid for all regions. + * + * \param [IN] joined Set to true, if the node has joined the network + * + * \param [IN] dutyCycle Set to true, if the duty cycle is enabled. + * + * \param [IN] bands A pointer to the bands. + * + * \param [IN] nbBands The number of bands available. + * + * \retval Returns the time which must be waited to perform the next uplink. + */ +TimerTime_t RegionCommonUpdateBandTimeOff( bool joined, bool dutyCycle, Band_t* bands, uint8_t nbBands ); + +/*! + * \brief Parses the parameter of an LinkAdrRequest. + * This is a generic function and valid for all regions. + * + * \param [IN] payload Pointer to the payload containing the MAC commands. The payload + * must contain the CMD identifier, following by the parameters. + * + * \param [OUT] parseLinkAdr The function fills the structure with the ADR parameters. + * + * \retval Returns the length of the ADR request, if a request was found. Otherwise, the + * function returns 0. + */ +uint8_t RegionCommonParseLinkAdrReq( uint8_t* payload, RegionCommonLinkAdrParams_t* parseLinkAdr ); + +/*! + * \brief Verifies and updates the datarate, the TX power and the number of repetitions + * of a LinkAdrRequest. This depends on the configuration of ADR also. + * + * \param [IN] verifyParams Pointer to a structure containing input parameters. + * + * \param [OUT] dr The updated datarate. + * + * \param [OUT] txPow The updated TX power. + * + * \param [OUT] nbRep The updated number of repetitions. + * + * \retval Returns the status according to the LinkAdrRequest definition. + */ +uint8_t RegionCommonLinkAdrReqVerifyParams( RegionCommonLinkAdrReqVerifyParams_t* verifyParams, int8_t* dr, int8_t* txPow, uint8_t* nbRep ); + +/*! + * \brief Computes the symbol time for LoRa modulation. + * + * \param [IN] phyDr Physical datarate to use. + * + * \param [IN] bandwidth Bandwidth to use. + * + * \retval Returns the symbol time. + */ +double RegionCommonComputeSymbolTimeLoRa( uint8_t phyDr, uint32_t bandwidth ); + +/*! + * \brief Computes the symbol time for FSK modulation. + * + * \param [IN] phyDr Physical datarate to use. + * + * \param [IN] bandwidth Bandwidth to use. + * + * \retval Returns the symbol time. + */ +double RegionCommonComputeSymbolTimeFsk( uint8_t phyDr ); + +/*! + * \brief Computes the RX window timeout and the RX window offset. + * + * \param [IN] tSymbol Symbol timeout. + * + * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame. + * + * \param [IN] rxError System maximum timing error of the receiver. In milliseconds + * The receiver will turn on in a [-rxError : +rxError] ms interval around RxOffset. + * + * \param [IN] wakeUpTime Wakeup time of the system. + * + * \param [OUT] windowTimeout RX window timeout. + * + * \param [OUT] windowOffset RX window time offset to be applied to the RX delay. + */ +void RegionCommonComputeRxWindowParameters( double tSymbol, uint8_t minRxSymbols, uint32_t rxError, uint32_t wakeUpTime, uint32_t* windowTimeout, int32_t* windowOffset ); + +/*! + * \brief Computes the txPower, based on the max EIRP and the antenna gain. + * + * \remark US915 region uses a conducted power as input value for maxEirp. + * Thus, the antennaGain parameter must be set to 0. + * + * \param [IN] txPower TX power index. + * + * \param [IN] maxEirp Maximum EIRP. + * + * \param [IN] antennaGain Antenna gain. Referenced to the isotropic antenna. + * Value is in dBi. ( antennaGain[dBi] = measuredAntennaGain[dBd] + 2.15 ) + * + * \retval Returns the physical TX power. + */ +int8_t RegionCommonComputeTxPower( int8_t txPowerIndex, float maxEirp, float antennaGain ); + +/*! + * \brief Calculates the duty cycle for the current band. + * + * \param [IN] calcBackOffParams A pointer to the input parameters. + */ +void RegionCommonCalcBackOff( RegionCommonCalcBackOffParams_t* calcBackOffParams ); + +/*! + * \brief Sets up the radio into RX beacon mode. + * + * \param [IN] rxBeaconSetupParams A pointer to the input parameters. + */ +void RegionCommonRxBeaconSetup( RegionCommonRxBeaconSetupParams_t* rxBeaconSetupParams ); + +/*! \} defgroup REGIONCOMMON */ + +#endif // __REGIONCOMMON_H__ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionEU433.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionEU433.c new file mode 100644 index 00000000..f87de7aa --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionEU433.c @@ -0,0 +1,1040 @@ +/*! + * \file RegionEU433.c + * + * \brief Region implementation for EU433 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) +*/ +#include "utilities.h" + +#include "RegionCommon.h" +#include "RegionEU433.h" +#include "lorawan_conf.h" +#include "mw_log_conf.h" + +// Definitions +#define CHANNELS_MASK_SIZE 1 + +/*! + * Region specific context + */ +typedef struct sRegionEU433NvmCtx +{ + /*! + * LoRaMAC channels + */ + ChannelParams_t Channels[ EU433_MAX_NB_CHANNELS ]; + /*! + * LoRaMac bands + */ + Band_t Bands[ EU433_MAX_NB_BANDS ]; + /*! + * LoRaMac channels mask + */ + uint16_t ChannelsMask[ CHANNELS_MASK_SIZE ]; + /*! + * LoRaMac channels default mask + */ + uint16_t ChannelsDefaultMask[ CHANNELS_MASK_SIZE ]; +}RegionEU433NvmCtx_t; + +/* + * Non-volatile module context. + */ +static RegionEU433NvmCtx_t NvmCtx; + +// Static functions +static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr ) +{ + uint8_t nextLowerDr = 0; + + if( dr == minDr ) + { + nextLowerDr = minDr; + } + else + { + nextLowerDr = dr - 1; + } + return nextLowerDr; +} + +static uint32_t GetBandwidth( uint32_t drIndex ) +{ + switch( BandwidthsEU433[drIndex] ) + { + default: + case 125000: + return 0; + case 250000: + return 1; + case 500000: + return 2; + } +} + +static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask ) +{ + int8_t txPowerResult = txPower; + + // Limit tx power to the band max + txPowerResult = MAX( txPower, maxBandTxPower ); + + return txPowerResult; +} + +static bool VerifyRfFreq( uint32_t freq ) +{ + // Check radio driver support + if( Radio.CheckRfFrequency( freq ) == false ) + { + return false; + } + + if( ( freq < 433175000 ) || ( freq > 434665000 ) ) + { + return false; + } + return true; +} + +static uint8_t CountNbOfEnabledChannels( bool joined, uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t delayTransmission = 0; + + for( uint8_t i = 0, k = 0; i < EU433_MAX_NB_CHANNELS; i += 16, k++ ) + { + for( uint8_t j = 0; j < 16; j++ ) + { + if( ( channelsMask[k] & ( 1 << j ) ) != 0 ) + { + if( channels[i + j].Frequency == 0 ) + { // Check if the channel is enabled + continue; + } + if( joined == false ) + { + if( ( EU433_JOIN_CHANNELS & ( 1 << j ) ) == 0 ) + { + continue; + } + } + if( RegionCommonValueInRange( datarate, channels[i + j].DrRange.Fields.Min, + channels[i + j].DrRange.Fields.Max ) == false ) + { // Check if the current channel selection supports the given datarate + continue; + } + if( bands[channels[i + j].Band].TimeOff > 0 ) + { // Check if the band is available for transmission + delayTransmission++; + continue; + } + enabledChannels[nbEnabledChannels++] = i + j; + } + } + } + + *delayTx = delayTransmission; + return nbEnabledChannels; +} + +PhyParam_t RegionEU433GetPhyParam( GetPhyParams_t* getPhy ) +{ + PhyParam_t phyParam = { 0 }; + + switch( getPhy->Attribute ) + { + case PHY_MIN_RX_DR: + { + phyParam.Value = EU433_RX_MIN_DATARATE; + break; + } + case PHY_MIN_TX_DR: + { + phyParam.Value = EU433_TX_MIN_DATARATE; + break; + } + case PHY_DEF_TX_DR: + { + phyParam.Value = EU433_DEFAULT_DATARATE; + break; + } + case PHY_NEXT_LOWER_TX_DR: + { + phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, EU433_TX_MIN_DATARATE ); + break; + } + case PHY_MAX_TX_POWER: + { + phyParam.Value = EU433_MAX_TX_POWER; + break; + } + case PHY_DEF_TX_POWER: + { + phyParam.Value = EU433_DEFAULT_TX_POWER; + break; + } + case PHY_DEF_ADR_ACK_LIMIT: + { + phyParam.Value = EU433_ADR_ACK_LIMIT; + break; + } + case PHY_DEF_ADR_ACK_DELAY: + { + phyParam.Value = EU433_ADR_ACK_DELAY; + break; + } + case PHY_MAX_PAYLOAD: + { + phyParam.Value = MaxPayloadOfDatarateEU433[getPhy->Datarate]; + break; + } + case PHY_MAX_PAYLOAD_REPEATER: + { + phyParam.Value = MaxPayloadOfDatarateRepeaterEU433[getPhy->Datarate]; + break; + } + case PHY_DUTY_CYCLE: + { + phyParam.Value = EU433_DUTY_CYCLE_ENABLED; + break; + } + case PHY_MAX_RX_WINDOW: + { + phyParam.Value = EU433_MAX_RX_WINDOW; + break; + } + case PHY_RECEIVE_DELAY1: + { + phyParam.Value = EU433_RECEIVE_DELAY1; + break; + } + case PHY_RECEIVE_DELAY2: + { + phyParam.Value = EU433_RECEIVE_DELAY2; + break; + } + case PHY_JOIN_ACCEPT_DELAY1: + { + phyParam.Value = EU433_JOIN_ACCEPT_DELAY1; + break; + } + case PHY_JOIN_ACCEPT_DELAY2: + { + phyParam.Value = EU433_JOIN_ACCEPT_DELAY2; + break; + } + case PHY_MAX_FCNT_GAP: + { + phyParam.Value = EU433_MAX_FCNT_GAP; + break; + } + case PHY_ACK_TIMEOUT: + { + phyParam.Value = ( EU433_ACKTIMEOUT + randr( -EU433_ACK_TIMEOUT_RND, EU433_ACK_TIMEOUT_RND ) ); + break; + } + case PHY_DEF_DR1_OFFSET: + { + phyParam.Value = EU433_DEFAULT_RX1_DR_OFFSET; + break; + } + case PHY_DEF_RX2_FREQUENCY: + { + phyParam.Value = EU433_RX_WND_2_FREQ; + break; + } + case PHY_DEF_RX2_DR: + { + phyParam.Value = EU433_RX_WND_2_DR; + break; + } + case PHY_CHANNELS_MASK: + { + phyParam.ChannelsMask = NvmCtx.ChannelsMask; + break; + } + case PHY_CHANNELS_DEFAULT_MASK: + { + phyParam.ChannelsMask = NvmCtx.ChannelsDefaultMask; + break; + } + case PHY_MAX_NB_CHANNELS: + { + phyParam.Value = EU433_MAX_NB_CHANNELS; + break; + } + case PHY_CHANNELS: + { + phyParam.Channels = NvmCtx.Channels; + break; + } + case PHY_DEF_UPLINK_DWELL_TIME: + case PHY_DEF_DOWNLINK_DWELL_TIME: + { + phyParam.Value = 0; + break; + } + case PHY_DEF_MAX_EIRP: + { + phyParam.fValue = EU433_DEFAULT_MAX_EIRP; + break; + } + case PHY_DEF_ANTENNA_GAIN: + { + phyParam.fValue = EU433_DEFAULT_ANTENNA_GAIN; + break; + } + case PHY_BEACON_CHANNEL_FREQ: + { + phyParam.Value = EU433_BEACON_CHANNEL_FREQ; + break; + } + case PHY_BEACON_FORMAT: + { + phyParam.BeaconFormat.BeaconSize = EU433_BEACON_SIZE; + phyParam.BeaconFormat.Rfu1Size = EU433_RFU1_SIZE; + phyParam.BeaconFormat.Rfu2Size = EU433_RFU2_SIZE; + break; + } + case PHY_BEACON_CHANNEL_DR: + { + phyParam.Value = EU433_BEACON_CHANNEL_DR; + break; + } + case PHY_PING_SLOT_CHANNEL_DR: + { + phyParam.Value = EU433_PING_SLOT_CHANNEL_DR; + break; + } + default: + { + break; + } + } + + return phyParam; +} + +void RegionEU433SetBandTxDone( SetBandTxDoneParams_t* txDone ) +{ + RegionCommonSetBandTxDone( txDone->Joined, &NvmCtx.Bands[NvmCtx.Channels[txDone->Channel].Band], txDone->LastTxDoneTime ); +} + +void RegionEU433InitDefaults( InitDefaultsParams_t* params ) +{ + Band_t bands[EU433_MAX_NB_BANDS] = + { + EU433_BAND0 + }; + + switch( params->Type ) + { + case INIT_TYPE_INIT: + { + // Initialize bands + memcpy1( ( uint8_t* )NvmCtx.Bands, ( uint8_t* )bands, sizeof( Band_t ) * EU433_MAX_NB_BANDS ); + + // Channels + NvmCtx.Channels[0] = ( ChannelParams_t ) EU433_LC1; + NvmCtx.Channels[1] = ( ChannelParams_t ) EU433_LC2; + NvmCtx.Channels[2] = ( ChannelParams_t ) EU433_LC3; + + // Initialize the channels default mask + NvmCtx.ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 ); + // Update the channels mask + RegionCommonChanMaskCopy( NvmCtx.ChannelsMask, NvmCtx.ChannelsDefaultMask, 1 ); + break; + } + case INIT_TYPE_RESTORE_CTX: + { + if( params->NvmCtx != 0 ) + { + memcpy1( (uint8_t*) &NvmCtx, (uint8_t*) params->NvmCtx, sizeof( NvmCtx ) ); + } + break; + } + case INIT_TYPE_RESTORE_DEFAULT_CHANNELS: + { + // Restore channels default mask + NvmCtx.ChannelsMask[0] |= NvmCtx.ChannelsDefaultMask[0]; + + // Channels + NvmCtx.Channels[0] = ( ChannelParams_t ) EU433_LC1; + NvmCtx.Channels[1] = ( ChannelParams_t ) EU433_LC2; + NvmCtx.Channels[2] = ( ChannelParams_t ) EU433_LC3; + break; + } + default: + { + break; + } + } +} + +void* RegionEU433GetNvmCtx( GetNvmCtxParams_t* params ) +{ + params->nvmCtxSize = sizeof( RegionEU433NvmCtx_t ); + return &NvmCtx; +} + +bool RegionEU433Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ) +{ + switch( phyAttribute ) + { + case PHY_FREQUENCY: + { + return VerifyRfFreq( verify->Frequency ); + } + case PHY_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, EU433_TX_MIN_DATARATE, EU433_TX_MAX_DATARATE ); + } + case PHY_DEF_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, DR_0, DR_5 ); + } + case PHY_RX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, EU433_RX_MIN_DATARATE, EU433_RX_MAX_DATARATE ); + } + case PHY_DEF_TX_POWER: + case PHY_TX_POWER: + { + // Remark: switched min and max! + return RegionCommonValueInRange( verify->TxPower, EU433_MAX_TX_POWER, EU433_MIN_TX_POWER ); + } + case PHY_DUTY_CYCLE: + { + return EU433_DUTY_CYCLE_ENABLED; + } + default: + return false; + } +} + +void RegionEU433ApplyCFList( ApplyCFListParams_t* applyCFList ) +{ + ChannelParams_t newChannel; + ChannelAddParams_t channelAdd; + ChannelRemoveParams_t channelRemove; + + // Setup default datarate range + newChannel.DrRange.Value = ( DR_5 << 4 ) | DR_0; + + // Size of the optional CF list + if( applyCFList->Size != 16 ) + { + return; + } + + // Last byte CFListType must be 0 to indicate the CFList contains a list of frequencies + if( applyCFList->Payload[15] != 0 ) + { + return; + } + + // Last byte is RFU, don't take it into account + for( uint8_t i = 0, chanIdx = EU433_NUMB_DEFAULT_CHANNELS; chanIdx < EU433_MAX_NB_CHANNELS; i+=3, chanIdx++ ) + { + if( chanIdx < ( EU433_NUMB_CHANNELS_CF_LIST + EU433_NUMB_DEFAULT_CHANNELS ) ) + { + // Channel frequency + newChannel.Frequency = (uint32_t) applyCFList->Payload[i]; + newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 1] << 8 ); + newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 2] << 16 ); + newChannel.Frequency *= 100; + + // Initialize alternative frequency to 0 + newChannel.Rx1Frequency = 0; + } + else + { + newChannel.Frequency = 0; + newChannel.DrRange.Value = 0; + newChannel.Rx1Frequency = 0; + } + + if( newChannel.Frequency != 0 ) + { + channelAdd.NewChannel = &newChannel; + channelAdd.ChannelId = chanIdx; + + // Try to add all channels + RegionEU433ChannelAdd( &channelAdd ); + } + else + { + channelRemove.ChannelId = chanIdx; + + RegionEU433ChannelsRemove( &channelRemove ); + } + } +} + +bool RegionEU433ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ) +{ + switch( chanMaskSet->ChannelsMaskType ) + { + case CHANNELS_MASK: + { + RegionCommonChanMaskCopy( NvmCtx.ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + case CHANNELS_DEFAULT_MASK: + { + RegionCommonChanMaskCopy( NvmCtx.ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + default: + return false; + } + return true; +} + +void RegionEU433ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ) +{ + double tSymbol = 0.0; + + // Get the datarate, perform a boundary check + rxConfigParams->Datarate = MIN( datarate, EU433_RX_MAX_DATARATE ); + rxConfigParams->Bandwidth = GetBandwidth( rxConfigParams->Datarate ); + + if( rxConfigParams->Datarate == DR_7 ) + { // FSK + tSymbol = RegionCommonComputeSymbolTimeFsk( DataratesEU433[rxConfigParams->Datarate] ); + } + else + { // LoRa + tSymbol = RegionCommonComputeSymbolTimeLoRa( DataratesEU433[rxConfigParams->Datarate], BandwidthsEU433[rxConfigParams->Datarate] ); + } + + RegionCommonComputeRxWindowParameters( tSymbol, minRxSymbols, rxError, Radio.GetWakeupTime( ), &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); +} + +bool RegionEU433RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) +{ + RadioModems_t modem; + int8_t dr = rxConfig->Datarate; + uint8_t maxPayload = 0; + int8_t phyDr = 0; + uint32_t frequency = rxConfig->Frequency; + const char *slotStrings[] = { "1", "2", "C", "Multi_C", "P", "Multi_P" }; + + if( Radio.GetStatus( ) != RF_IDLE ) + { + return false; + } + + if( rxConfig->RxSlot == RX_SLOT_WIN_1 ) + { + // Apply window 1 frequency + frequency = NvmCtx.Channels[rxConfig->Channel].Frequency; + // Apply the alternative RX 1 window frequency, if it is available + if( NvmCtx.Channels[rxConfig->Channel].Rx1Frequency != 0 ) + { + frequency = NvmCtx.Channels[rxConfig->Channel].Rx1Frequency; + } + } + + // Read the physical datarate from the datarates table + phyDr = DataratesEU433[dr]; + + Radio.SetChannel( frequency ); + + // Radio configuration + if( dr == DR_7 ) + { + modem = MODEM_FSK; + Radio.SetRxConfig( modem, 50000, phyDr * 1000, 0, 83333, 5, rxConfig->WindowTimeout, false, 0, true, 0, 0, false, rxConfig->RxContinuous ); + } + else + { + modem = MODEM_LORA; + Radio.SetRxConfig( modem, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous ); + } + + if( rxConfig->RepeaterSupport == true ) + { + maxPayload = MaxPayloadOfDatarateRepeaterEU433[dr]; + } + else + { + maxPayload = MaxPayloadOfDatarateEU433[dr]; + } + Radio.SetMaxPayloadLength( modem, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD ); + if ( rxConfig->RxSlot < RX_SLOT_NONE ) + { + MW_LOG( "RX_%s on freq %d Hz at DR %d\n\r", slotStrings[rxConfig->RxSlot], frequency, dr ); + } + else + { + MW_LOG( "RX on freq %d Hz at DR %d\n\r", frequency, dr ); + } + + *datarate = (uint8_t) dr; + return true; +} + +bool RegionEU433TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ) +{ + RadioModems_t modem; + int8_t phyDr = DataratesEU433[txConfig->Datarate]; + int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, NvmCtx.Bands[NvmCtx.Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, NvmCtx.ChannelsMask ); + uint32_t bandwidth = GetBandwidth( txConfig->Datarate ); + int8_t phyTxPower = 0; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain ); + + // Setup the radio frequency + Radio.SetChannel( NvmCtx.Channels[txConfig->Channel].Frequency ); + + if( txConfig->Datarate == DR_7 ) + { // High Speed FSK channel + modem = MODEM_FSK; + Radio.SetTxConfig( modem, phyTxPower, 25000, bandwidth, phyDr * 1000, 0, 5, false, true, 0, 0, false, 4000 ); + } + else + { + modem = MODEM_LORA; + Radio.SetTxConfig( modem, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 4000 ); + } + MW_LOG( "TX on freq %d Hz at DR %d\n\r", NvmCtx.Channels[txConfig->Channel].Frequency, txConfig->Datarate ); + + // Setup maximum payload lenght of the radio driver + Radio.SetMaxPayloadLength( modem, txConfig->PktLen ); + // Get the time-on-air of the next tx frame + *txTimeOnAir = Radio.TimeOnAir( modem, txConfig->PktLen ); + + *txPower = txPowerLimited; + return true; +} + +uint8_t RegionEU433LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ) +{ + uint8_t status = 0x07; + RegionCommonLinkAdrParams_t linkAdrParams; + uint8_t nextIndex = 0; + uint8_t bytesProcessed = 0; + uint16_t chMask = 0; + GetPhyParams_t getPhy; + PhyParam_t phyParam; + RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams; + + while( bytesProcessed < linkAdrReq->PayloadSize ) + { + // Get ADR request parameters + nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams ); + + if( nextIndex == 0 ) + break; // break loop, since no more request has been found + + // Update bytes processed + bytesProcessed += nextIndex; + + // Revert status, as we only check the last ADR request for the channel mask KO + status = 0x07; + + // Setup temporary channels mask + chMask = linkAdrParams.ChMask; + + // Verify channels mask + if( ( linkAdrParams.ChMaskCtrl == 0 ) && ( chMask == 0 ) ) + { + status &= 0xFE; // Channel mask KO + } + else if( ( ( linkAdrParams.ChMaskCtrl >= 1 ) && ( linkAdrParams.ChMaskCtrl <= 5 )) || + ( linkAdrParams.ChMaskCtrl >= 7 ) ) + { + // RFU + status &= 0xFE; // Channel mask KO + } + else + { + for( uint8_t i = 0; i < EU433_MAX_NB_CHANNELS; i++ ) + { + if( linkAdrParams.ChMaskCtrl == 6 ) + { + if( NvmCtx.Channels[i].Frequency != 0 ) + { + chMask |= 1 << i; + } + } + else + { + if( ( ( chMask & ( 1 << i ) ) != 0 ) && + ( NvmCtx.Channels[i].Frequency == 0 ) ) + {// Trying to enable an undefined channel + status &= 0xFE; // Channel mask KO + } + } + } + } + } + + // Get the minimum possible datarate + getPhy.Attribute = PHY_MIN_TX_DR; + getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime; + phyParam = RegionEU433GetPhyParam( &getPhy ); + + linkAdrVerifyParams.Status = status; + linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled; + linkAdrVerifyParams.Datarate = linkAdrParams.Datarate; + linkAdrVerifyParams.TxPower = linkAdrParams.TxPower; + linkAdrVerifyParams.NbRep = linkAdrParams.NbRep; + linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate; + linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower; + linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep; + linkAdrVerifyParams.NbChannels = EU433_MAX_NB_CHANNELS; + linkAdrVerifyParams.ChannelsMask = &chMask; + linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value; + linkAdrVerifyParams.MaxDatarate = EU433_TX_MAX_DATARATE; + linkAdrVerifyParams.Channels = NvmCtx.Channels; + linkAdrVerifyParams.MinTxPower = EU433_MIN_TX_POWER; + linkAdrVerifyParams.MaxTxPower = EU433_MAX_TX_POWER; + linkAdrVerifyParams.Version = linkAdrReq->Version; + + // Verify the parameters and update, if necessary + status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep ); + + // Update channelsMask if everything is correct + if( status == 0x07 ) + { + // Set the channels mask to a default value + memset1( ( uint8_t* ) NvmCtx.ChannelsMask, 0, sizeof( NvmCtx.ChannelsMask ) ); + // Update the channels mask + NvmCtx.ChannelsMask[0] = chMask; + } + + // Update status variables + *drOut = linkAdrParams.Datarate; + *txPowOut = linkAdrParams.TxPower; + *nbRepOut = linkAdrParams.NbRep; + *nbBytesParsed = bytesProcessed; + + return status; +} + +uint8_t RegionEU433RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ) +{ + uint8_t status = 0x07; + + // Verify radio frequency + if( VerifyRfFreq( rxParamSetupReq->Frequency ) == false ) + { + status &= 0xFE; // Channel frequency KO + } + + // Verify datarate + if( RegionCommonValueInRange( rxParamSetupReq->Datarate, EU433_RX_MIN_DATARATE, EU433_RX_MAX_DATARATE ) == false ) + { + status &= 0xFD; // Datarate KO + } + + // Verify datarate offset + if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, EU433_MIN_RX1_DR_OFFSET, EU433_MAX_RX1_DR_OFFSET ) == false ) + { + status &= 0xFB; // Rx1DrOffset range KO + } + + return status; +} + +uint8_t RegionEU433NewChannelReq( NewChannelReqParams_t* newChannelReq ) +{ + uint8_t status = 0x03; + ChannelAddParams_t channelAdd; + ChannelRemoveParams_t channelRemove; + + if( newChannelReq->NewChannel->Frequency == 0 ) + { + channelRemove.ChannelId = newChannelReq->ChannelId; + + // Remove + if( RegionEU433ChannelsRemove( &channelRemove ) == false ) + { + status &= 0xFC; + } + } + else + { + channelAdd.NewChannel = newChannelReq->NewChannel; + channelAdd.ChannelId = newChannelReq->ChannelId; + + switch( RegionEU433ChannelAdd( &channelAdd ) ) + { + case LORAMAC_STATUS_OK: + { + break; + } + case LORAMAC_STATUS_FREQUENCY_INVALID: + { + status &= 0xFE; + break; + } + case LORAMAC_STATUS_DATARATE_INVALID: + { + status &= 0xFD; + break; + } + case LORAMAC_STATUS_FREQ_AND_DR_INVALID: + { + status &= 0xFC; + break; + } + default: + { + status &= 0xFC; + break; + } + } + } + + return status; +} + +int8_t RegionEU433TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ) +{ + return -1; +} + +uint8_t RegionEU433DlChannelReq( DlChannelReqParams_t* dlChannelReq ) +{ + uint8_t status = 0x03; + + // Verify if the frequency is supported + if( VerifyRfFreq( dlChannelReq->Rx1Frequency ) == false ) + { + status &= 0xFE; + } + + // Verify if an uplink frequency exists + if( NvmCtx.Channels[dlChannelReq->ChannelId].Frequency == 0 ) + { + status &= 0xFD; + } + + // Apply Rx1 frequency, if the status is OK + if( status == 0x03 ) + { + NvmCtx.Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency; + } + + return status; +} + +int8_t RegionEU433AlternateDr( int8_t currentDr, AlternateDrType_t type ) +{ + return currentDr; +} + +void RegionEU433CalcBackOff( CalcBackOffParams_t* calcBackOff ) +{ + RegionCommonCalcBackOffParams_t calcBackOffParams; + + calcBackOffParams.Channels = NvmCtx.Channels; + calcBackOffParams.Bands = NvmCtx.Bands; + calcBackOffParams.LastTxIsJoinRequest = calcBackOff->LastTxIsJoinRequest; + calcBackOffParams.Joined = calcBackOff->Joined; + calcBackOffParams.DutyCycleEnabled = calcBackOff->DutyCycleEnabled; + calcBackOffParams.Channel = calcBackOff->Channel; + calcBackOffParams.ElapsedTime = calcBackOff->ElapsedTime; + calcBackOffParams.TxTimeOnAir = calcBackOff->TxTimeOnAir; + + RegionCommonCalcBackOff( &calcBackOffParams ); +} + +LoRaMacStatus_t RegionEU433NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t delayTx = 0; + uint8_t enabledChannels[EU433_MAX_NB_CHANNELS] = { 0 }; + TimerTime_t nextTxDelay = 0; + + if( RegionCommonCountChannels( NvmCtx.ChannelsMask, 0, 1 ) == 0 ) + { // Reactivate default channels + NvmCtx.ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); + } + + TimerTime_t elapsed = TimerGetElapsedTime( nextChanParams->LastAggrTx ); + if( ( nextChanParams->LastAggrTx == 0 ) || ( nextChanParams->AggrTimeOff <= elapsed ) ) + { + // Reset Aggregated time off + *aggregatedTimeOff = 0; + + // Update bands Time OFF + nextTxDelay = RegionCommonUpdateBandTimeOff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, NvmCtx.Bands, EU433_MAX_NB_BANDS ); + + // Search how many channels are enabled + nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Joined, nextChanParams->Datarate, + NvmCtx.ChannelsMask, NvmCtx.Channels, + NvmCtx.Bands, enabledChannels, &delayTx ); + } + else + { + delayTx++; + nextTxDelay = nextChanParams->AggrTimeOff - elapsed; + } + + if( nbEnabledChannels > 0 ) + { + // We found a valid channel + *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )]; + + *time = 0; + return LORAMAC_STATUS_OK; + } + else + { + if( delayTx > 0 ) + { + // Delay transmission due to AggregatedTimeOff or to a band time off + *time = nextTxDelay; + return LORAMAC_STATUS_DUTYCYCLE_RESTRICTED; + } + // Datarate not supported by any channel, restore defaults + NvmCtx.ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); + *time = 0; + return LORAMAC_STATUS_NO_CHANNEL_FOUND; + } +} + +LoRaMacStatus_t RegionEU433ChannelAdd( ChannelAddParams_t* channelAdd ) +{ + bool drInvalid = false; + bool freqInvalid = false; + uint8_t id = channelAdd->ChannelId; + + if( id < EU433_NUMB_DEFAULT_CHANNELS ) + { + return LORAMAC_STATUS_FREQ_AND_DR_INVALID; + } + + if( id >= EU433_MAX_NB_CHANNELS ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + // Validate the datarate range + if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Min, EU433_TX_MIN_DATARATE, EU433_TX_MAX_DATARATE ) == false ) + { + drInvalid = true; + } + if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, EU433_TX_MIN_DATARATE, EU433_TX_MAX_DATARATE ) == false ) + { + drInvalid = true; + } + if( channelAdd->NewChannel->DrRange.Fields.Min > channelAdd->NewChannel->DrRange.Fields.Max ) + { + drInvalid = true; + } + + // Check frequency + if( freqInvalid == false ) + { + if( VerifyRfFreq( channelAdd->NewChannel->Frequency ) == false ) + { + freqInvalid = true; + } + } + + // Check status + if( ( drInvalid == true ) && ( freqInvalid == true ) ) + { + return LORAMAC_STATUS_FREQ_AND_DR_INVALID; + } + if( drInvalid == true ) + { + return LORAMAC_STATUS_DATARATE_INVALID; + } + if( freqInvalid == true ) + { + return LORAMAC_STATUS_FREQUENCY_INVALID; + } + + memcpy1( ( uint8_t* ) &(NvmCtx.Channels[id]), ( uint8_t* ) channelAdd->NewChannel, sizeof( NvmCtx.Channels[id] ) ); + NvmCtx.Channels[id].Band = 0; + NvmCtx.ChannelsMask[0] |= ( 1 << id ); + return LORAMAC_STATUS_OK; +} + +bool RegionEU433ChannelsRemove( ChannelRemoveParams_t* channelRemove ) +{ + uint8_t id = channelRemove->ChannelId; + + if( id < EU433_NUMB_DEFAULT_CHANNELS ) + { + return false; + } + + // Remove the channel from the list of channels + NvmCtx.Channels[id] = ( ChannelParams_t ){ 0, 0, { 0 }, 0 }; + + return RegionCommonChanDisable( NvmCtx.ChannelsMask, id, EU433_MAX_NB_CHANNELS ); +} + +void RegionEU433SetContinuousWave( ContinuousWaveParams_t* continuousWave ) +{ + int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, NvmCtx.Bands[NvmCtx.Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, NvmCtx.ChannelsMask ); + int8_t phyTxPower = 0; + uint32_t frequency = NvmCtx.Channels[continuousWave->Channel].Frequency; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, continuousWave->MaxEirp, continuousWave->AntennaGain ); + + Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout ); +} + +uint8_t RegionEU433ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) +{ + int8_t datarate = dr - drOffset; + + if( datarate < 0 ) + { + datarate = DR_0; + } + return datarate; +} + +void RegionEU433RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ) +{ + RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup; + + regionCommonRxBeaconSetup.Datarates = DataratesEU433; + regionCommonRxBeaconSetup.Frequency = rxBeaconSetup->Frequency; + regionCommonRxBeaconSetup.BeaconSize = EU433_BEACON_SIZE; + regionCommonRxBeaconSetup.BeaconDatarate = EU433_BEACON_CHANNEL_DR; + regionCommonRxBeaconSetup.BeaconChannelBW = EU433_BEACON_CHANNEL_BW; + regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime; + regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout; + + RegionCommonRxBeaconSetup( ®ionCommonRxBeaconSetup ); + + // Store downlink datarate + *outDr = EU433_BEACON_CHANNEL_DR; +} diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionEU433.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionEU433.h new file mode 100644 index 00000000..6f8b38d3 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionEU433.h @@ -0,0 +1,509 @@ +/*! + * \file RegionEU433.h + * + * \brief Region definition for EU433 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + * + * \defgroup REGIONEU433 Region EU433 + * Implementation according to LoRaWAN Specification v1.0.2. + * \{ + */ +#ifndef __REGION_EU433_H__ +#define __REGION_EU433_H__ + +#include "region/Region.h" + +/*! + * LoRaMac maximum number of channels + */ +#define EU433_MAX_NB_CHANNELS 16 + +/*! + * Number of default channels + */ +#define EU433_NUMB_DEFAULT_CHANNELS 3 + +/*! + * Number of channels to apply for the CF list + */ +#define EU433_NUMB_CHANNELS_CF_LIST 5 + +/*! + * Minimal datarate that can be used by the node + */ +#define EU433_TX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define EU433_TX_MAX_DATARATE DR_7 + +/*! + * Minimal datarate that can be used by the node + */ +#define EU433_RX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define EU433_RX_MAX_DATARATE DR_7 + +/*! + * Default datarate used by the node + */ +#define EU433_DEFAULT_DATARATE DR_0 + +/*! + * Minimal Rx1 receive datarate offset + */ +#define EU433_MIN_RX1_DR_OFFSET 0 + +/*! + * Maximal Rx1 receive datarate offset + */ +#define EU433_MAX_RX1_DR_OFFSET 5 + +/*! + * Default Rx1 receive datarate offset + */ +#define EU433_DEFAULT_RX1_DR_OFFSET 0 + +/*! + * Minimal Tx output power that can be used by the node + */ +#define EU433_MIN_TX_POWER TX_POWER_5 + +/*! + * Maximal Tx output power that can be used by the node + */ +#define EU433_MAX_TX_POWER TX_POWER_0 + +/*! + * Default Tx output power used by the node + */ +#define EU433_DEFAULT_TX_POWER TX_POWER_0 + +/*! + * Default Max EIRP + */ +#define EU433_DEFAULT_MAX_EIRP 12.15f + +/*! + * Default antenna gain + */ +#define EU433_DEFAULT_ANTENNA_GAIN 2.15f + +/*! + * ADR Ack limit + */ +#define EU433_ADR_ACK_LIMIT 64 + +/*! + * ADR Ack delay + */ +#define EU433_ADR_ACK_DELAY 32 + +/*! + * Enabled or disabled the duty cycle + */ +#define EU433_DUTY_CYCLE_ENABLED 1 + +/*! + * Maximum RX window duration + */ +#define EU433_MAX_RX_WINDOW 3000 + +/*! + * Receive delay 1 + */ +#define EU433_RECEIVE_DELAY1 1000 + +/*! + * Receive delay 2 + */ +#define EU433_RECEIVE_DELAY2 2000 + +/*! + * Join accept delay 1 + */ +#define EU433_JOIN_ACCEPT_DELAY1 5000 + +/*! + * Join accept delay 2 + */ +#define EU433_JOIN_ACCEPT_DELAY2 6000 + +/*! + * Maximum frame counter gap + */ +#define EU433_MAX_FCNT_GAP 16384 + +/*! + * Ack timeout + */ +#define EU433_ACKTIMEOUT 2000 + +/*! + * Random ack timeout limits + */ +#define EU433_ACK_TIMEOUT_RND 1000 + +/*! + * Verification of default datarate + */ +#if ( EU433_DEFAULT_DATARATE > DR_5 ) +#error "A default DR higher than DR_5 may lead to connectivity loss." +#endif + +/*! + * Second reception window channel frequency definition. + */ +#define EU433_RX_WND_2_FREQ 434665000 + +/*! + * Second reception window channel datarate definition. + */ +#define EU433_RX_WND_2_DR DR_0 + +/*! + * LoRaMac maximum number of bands + */ +#define EU433_MAX_NB_BANDS 1 + +/* + * CLASS B + */ +/*! + * Beacon frequency + */ +#define EU433_BEACON_CHANNEL_FREQ 434665000 + +/*! + * Payload size of a beacon frame + */ +#define EU433_BEACON_SIZE 17 + +/*! + * Size of RFU 1 field + */ +#define EU433_RFU1_SIZE 2 + +/*! + * Size of RFU 2 field + */ +#define EU433_RFU2_SIZE 0 + +/*! + * Datarate of the beacon channel + */ +#define EU433_BEACON_CHANNEL_DR DR_3 + +/*! + * Bandwith of the beacon channel + */ +#define EU433_BEACON_CHANNEL_BW 0 + +/*! + * Ping slot channel datarate + */ +#define EU433_PING_SLOT_CHANNEL_DR DR_3 + +/*! + * Band 0 definition + * { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff } + */ +#define EU433_BAND0 { 100, EU433_MAX_TX_POWER, 0, 0, 0 } // 1.0 % + +/*! + * LoRaMac default channel 1 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define EU433_LC1 { 433175000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } + +/*! + * LoRaMac default channel 2 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define EU433_LC2 { 433375000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } + +/*! + * LoRaMac default channel 3 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define EU433_LC3 { 433575000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } + +/*! + * LoRaMac channels which are allowed for the join procedure + */ +#define EU433_JOIN_CHANNELS ( uint16_t )( LC( 1 ) | LC( 2 ) | LC( 3 ) ) + +/*! + * Data rates table definition + */ +static const uint8_t DataratesEU433[] = { 12, 11, 10, 9, 8, 7, 7, 50 }; + +/*! + * Bandwidths table definition in Hz + */ +static const uint32_t BandwidthsEU433[] = { 125000, 125000, 125000, 125000, 125000, 125000, 250000, 0 }; + +/*! + * Maximum payload with respect to the datarate index. Cannot operate with repeater. + */ +static const uint8_t MaxPayloadOfDatarateEU433[] = { 51, 51, 51, 115, 242, 242, 242, 242 }; + +/*! + * Maximum payload with respect to the datarate index. Can operate with repeater. + */ +static const uint8_t MaxPayloadOfDatarateRepeaterEU433[] = { 51, 51, 51, 115, 222, 222, 222, 222 }; + +/*! + * \brief The function gets a value of a specific phy attribute. + * + * \param [IN] getPhy Pointer to the function parameters. + * + * \retval Returns a structure containing the PHY parameter. + */ +PhyParam_t RegionEU433GetPhyParam( GetPhyParams_t* getPhy ); + +/*! + * \brief Updates the last TX done parameters of the current channel. + * + * \param [IN] txDone Pointer to the function parameters. + */ +void RegionEU433SetBandTxDone( SetBandTxDoneParams_t* txDone ); + +/*! + * \brief Initializes the channels masks and the channels. + * + * \param [IN] type Sets the initialization type. + */ +void RegionEU433InitDefaults( InitDefaultsParams_t* params ); + +/*! + * \brief Returns a pointer to the internal context and its size. + * + * \param [OUT] params Pointer to the function parameters. + * + * \retval Points to a structure where the module store its non-volatile context. + */ +void* RegionEU433GetNvmCtx( GetNvmCtxParams_t* params ); + +/*! + * \brief Verifies a parameter. + * + * \param [IN] verify Pointer to the function parameters. + * + * \param [IN] type Sets the initialization type. + * + * \retval Returns true, if the parameter is valid. + */ +bool RegionEU433Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ); + +/*! + * \brief The function parses the input buffer and sets up the channels of the + * CF list. + * + * \param [IN] applyCFList Pointer to the function parameters. + */ +void RegionEU433ApplyCFList( ApplyCFListParams_t* applyCFList ); + +/*! + * \brief Sets a channels mask. + * + * \param [IN] chanMaskSet Pointer to the function parameters. + * + * \retval Returns true, if the channels mask could be set. + */ +bool RegionEU433ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ); + +/*! + * Computes the Rx window timeout and offset. + * + * \param [IN] datarate Rx window datarate index to be used + * + * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame. + * + * \param [IN] rxError System maximum timing error of the receiver. In milliseconds + * The receiver will turn on in a [-rxError : +rxError] ms + * interval around RxOffset + * + * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields. + */ +void RegionEU433ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ); + +/*! + * \brief Configuration of the RX windows. + * + * \param [IN] rxConfig Pointer to the function parameters. + * + * \param [OUT] datarate The datarate index which was set. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionEU433RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ); + +/*! + * \brief TX configuration. + * + * \param [IN] txConfig Pointer to the function parameters. + * + * \param [OUT] txPower The tx power index which was set. + * + * \param [OUT] txTimeOnAir The time-on-air of the frame. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionEU433TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ); + +/*! + * \brief The function processes a Link ADR Request. + * + * \param [IN] linkAdrReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionEU433LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ); + +/*! + * \brief The function processes a RX Parameter Setup Request. + * + * \param [IN] rxParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionEU433RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ); + +/*! + * \brief The function processes a Channel Request. + * + * \param [IN] newChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionEU433NewChannelReq( NewChannelReqParams_t* newChannelReq ); + +/*! + * \brief The function processes a TX ParamSetup Request. + * + * \param [IN] txParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + * Returns -1, if the functionality is not implemented. In this case, the end node + * shall not process the command. + */ +int8_t RegionEU433TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ); + +/*! + * \brief The function processes a DlChannel Request. + * + * \param [IN] dlChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionEU433DlChannelReq( DlChannelReqParams_t* dlChannelReq ); + +/*! + * \brief Alternates the datarate of the channel for the join request. + * + * \param [IN] currentDr Current datarate. + * + * \retval Datarate to apply. + */ +int8_t RegionEU433AlternateDr( int8_t currentDr, AlternateDrType_t type ); + +/*! + * \brief Calculates the back-off time. + * + * \param [IN] calcBackOff Pointer to the function parameters. + */ +void RegionEU433CalcBackOff( CalcBackOffParams_t* calcBackOff ); + +/*! + * \brief Searches and set the next random available channel + * + * \param [OUT] channel Next channel to use for TX. + * + * \param [OUT] time Time to wait for the next transmission according to the duty + * cycle. + * + * \param [OUT] aggregatedTimeOff Updates the aggregated time off. + * + * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate] + */ +LoRaMacStatus_t RegionEU433NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ); + +/*! + * \brief Adds a channel. + * + * \param [IN] channelAdd Pointer to the function parameters. + * + * \retval Status of the operation. + */ +LoRaMacStatus_t RegionEU433ChannelAdd( ChannelAddParams_t* channelAdd ); + +/*! + * \brief Removes a channel. + * + * \param [IN] channelRemove Pointer to the function parameters. + * + * \retval Returns true, if the channel was removed successfully. + */ +bool RegionEU433ChannelsRemove( ChannelRemoveParams_t* channelRemove ); + +/*! + * \brief Sets the radio into continuous wave mode. + * + * \param [IN] continuousWave Pointer to the function parameters. + */ +void RegionEU433SetContinuousWave( ContinuousWaveParams_t* continuousWave ); + +/*! + * \brief Computes new datarate according to the given offset + * + * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms + * + * \param [IN] dr Current datarate + * + * \param [IN] drOffset Offset to be applied + * + * \retval newDr Computed datarate. + */ +uint8_t RegionEU433ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ); + +/*! + * \brief Sets the radio into beacon reception mode + * + * \param [IN] rxBeaconSetup Pointer to the function parameters + */ + void RegionEU433RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ); + +/*! \} defgroup REGIONEU433 */ + +#endif // __REGION_EU433_H__ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionEU868.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionEU868.c new file mode 100644 index 00000000..a7e78c4c --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionEU868.c @@ -0,0 +1,1075 @@ +/*! + * \file RegionEU868.c + * + * \brief Region implementation for EU868 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) +*/ +#include "utilities.h" + +#include "RegionCommon.h" +#include "RegionEU868.h" +#include "lorawan_conf.h" +#include "mw_log_conf.h" + +// Definitions +#define CHANNELS_MASK_SIZE 1 + +/*! + * Region specific context + */ +typedef struct sRegionEU868NvmCtx +{ + /*! + * LoRaMAC channels + */ + ChannelParams_t Channels[ EU868_MAX_NB_CHANNELS ]; + /*! + * LoRaMac bands + */ + Band_t Bands[ EU868_MAX_NB_BANDS ]; + /*! + * LoRaMac channels mask + */ + uint16_t ChannelsMask[ CHANNELS_MASK_SIZE ]; + /*! + * LoRaMac channels default mask + */ + uint16_t ChannelsDefaultMask[ CHANNELS_MASK_SIZE ]; +}RegionEU868NvmCtx_t; + +/* + * Non-volatile module context. + */ +static RegionEU868NvmCtx_t NvmCtx; + +// Static functions +static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr ) +{ + uint8_t nextLowerDr = 0; + + if( dr == minDr ) + { + nextLowerDr = minDr; + } + else + { + nextLowerDr = dr - 1; + } + return nextLowerDr; +} + +static uint32_t GetBandwidth( uint32_t drIndex ) +{ + switch( BandwidthsEU868[drIndex] ) + { + default: + case 125000: + return 0; + case 250000: + return 1; + case 500000: + return 2; + } +} + +static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask ) +{ + int8_t txPowerResult = txPower; + + // Limit tx power to the band max + txPowerResult = MAX( txPower, maxBandTxPower ); + + return txPowerResult; +} + +static bool VerifyRfFreq( uint32_t freq, uint8_t *band ) +{ + // Check radio driver support + if( Radio.CheckRfFrequency( freq ) == false ) + { + return false; + } + + // Check frequency bands + if( ( freq >= 863000000 ) && ( freq < 865000000 ) ) + { + *band = 2; + } + else if( ( freq >= 865000000 ) && ( freq <= 868000000 ) ) + { + *band = 0; + } + else if( ( freq > 868000000 ) && ( freq <= 868600000 ) ) + { + *band = 1; + } + else if( ( freq >= 868700000 ) && ( freq <= 869200000 ) ) + { + *band = 5; + } + else if( ( freq >= 869400000 ) && ( freq <= 869650000 ) ) + { + *band = 3; + } + else if( ( freq >= 869700000 ) && ( freq <= 870000000 ) ) + { + *band = 4; + } + else + { + return false; + } + return true; +} + +static uint8_t CountNbOfEnabledChannels( bool joined, uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t delayTransmission = 0; + + for( uint8_t i = 0, k = 0; i < EU868_MAX_NB_CHANNELS; i += 16, k++ ) + { + for( uint8_t j = 0; j < 16; j++ ) + { + if( ( channelsMask[k] & ( 1 << j ) ) != 0 ) + { + if( channels[i + j].Frequency == 0 ) + { // Check if the channel is enabled + continue; + } + if( joined == false ) + { + if( ( EU868_JOIN_CHANNELS & ( 1 << j ) ) == 0 ) + { + continue; + } + } + if( RegionCommonValueInRange( datarate, channels[i + j].DrRange.Fields.Min, + channels[i + j].DrRange.Fields.Max ) == false ) + { // Check if the current channel selection supports the given datarate + continue; + } + if( bands[channels[i + j].Band].TimeOff > 0 ) + { // Check if the band is available for transmission + delayTransmission++; + continue; + } + enabledChannels[nbEnabledChannels++] = i + j; + } + } + } + + *delayTx = delayTransmission; + return nbEnabledChannels; +} + +PhyParam_t RegionEU868GetPhyParam( GetPhyParams_t* getPhy ) +{ + PhyParam_t phyParam = { 0 }; + + switch( getPhy->Attribute ) + { + case PHY_MIN_RX_DR: + { + phyParam.Value = EU868_RX_MIN_DATARATE; + break; + } + case PHY_MIN_TX_DR: + { + phyParam.Value = EU868_TX_MIN_DATARATE; + break; + } + case PHY_DEF_TX_DR: + { + phyParam.Value = EU868_DEFAULT_DATARATE; + break; + } + case PHY_NEXT_LOWER_TX_DR: + { + phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, EU868_TX_MIN_DATARATE ); + break; + } + case PHY_MAX_TX_POWER: + { + phyParam.Value = EU868_MAX_TX_POWER; + break; + } + case PHY_DEF_TX_POWER: + { + phyParam.Value = EU868_DEFAULT_TX_POWER; + break; + } + case PHY_DEF_ADR_ACK_LIMIT: + { + phyParam.Value = EU868_ADR_ACK_LIMIT; + break; + } + case PHY_DEF_ADR_ACK_DELAY: + { + phyParam.Value = EU868_ADR_ACK_DELAY; + break; + } + case PHY_MAX_PAYLOAD: + { + phyParam.Value = MaxPayloadOfDatarateEU868[getPhy->Datarate]; + break; + } + case PHY_MAX_PAYLOAD_REPEATER: + { + phyParam.Value = MaxPayloadOfDatarateRepeaterEU868[getPhy->Datarate]; + break; + } + case PHY_DUTY_CYCLE: + { + phyParam.Value = EU868_DUTY_CYCLE_ENABLED; + break; + } + case PHY_MAX_RX_WINDOW: + { + phyParam.Value = EU868_MAX_RX_WINDOW; + break; + } + case PHY_RECEIVE_DELAY1: + { + phyParam.Value = EU868_RECEIVE_DELAY1; + break; + } + case PHY_RECEIVE_DELAY2: + { + phyParam.Value = EU868_RECEIVE_DELAY2; + break; + } + case PHY_JOIN_ACCEPT_DELAY1: + { + phyParam.Value = EU868_JOIN_ACCEPT_DELAY1; + break; + } + case PHY_JOIN_ACCEPT_DELAY2: + { + phyParam.Value = EU868_JOIN_ACCEPT_DELAY2; + break; + } + case PHY_MAX_FCNT_GAP: + { + phyParam.Value = EU868_MAX_FCNT_GAP; + break; + } + case PHY_ACK_TIMEOUT: + { + phyParam.Value = ( EU868_ACKTIMEOUT + randr( -EU868_ACK_TIMEOUT_RND, EU868_ACK_TIMEOUT_RND ) ); + break; + } + case PHY_DEF_DR1_OFFSET: + { + phyParam.Value = EU868_DEFAULT_RX1_DR_OFFSET; + break; + } + case PHY_DEF_RX2_FREQUENCY: + { + phyParam.Value = EU868_RX_WND_2_FREQ; + break; + } + case PHY_DEF_RX2_DR: + { + phyParam.Value = EU868_RX_WND_2_DR; + break; + } + case PHY_CHANNELS_MASK: + { + phyParam.ChannelsMask = NvmCtx.ChannelsMask; + break; + } + case PHY_CHANNELS_DEFAULT_MASK: + { + phyParam.ChannelsMask = NvmCtx.ChannelsDefaultMask; + break; + } + case PHY_MAX_NB_CHANNELS: + { + phyParam.Value = EU868_MAX_NB_CHANNELS; + break; + } + case PHY_CHANNELS: + { + phyParam.Channels = NvmCtx.Channels; + break; + } + case PHY_DEF_UPLINK_DWELL_TIME: + case PHY_DEF_DOWNLINK_DWELL_TIME: + { + phyParam.Value = 0; + break; + } + case PHY_DEF_MAX_EIRP: + { + phyParam.fValue = EU868_DEFAULT_MAX_EIRP; + break; + } + case PHY_DEF_ANTENNA_GAIN: + { + phyParam.fValue = EU868_DEFAULT_ANTENNA_GAIN; + break; + } + case PHY_BEACON_CHANNEL_FREQ: + { + phyParam.Value = EU868_BEACON_CHANNEL_FREQ; + break; + } + case PHY_BEACON_FORMAT: + { + phyParam.BeaconFormat.BeaconSize = EU868_BEACON_SIZE; + phyParam.BeaconFormat.Rfu1Size = EU868_RFU1_SIZE; + phyParam.BeaconFormat.Rfu2Size = EU868_RFU2_SIZE; + break; + } + case PHY_BEACON_CHANNEL_DR: + { + phyParam.Value = EU868_BEACON_CHANNEL_DR; + break; + } + case PHY_PING_SLOT_CHANNEL_DR: + { + phyParam.Value = EU868_PING_SLOT_CHANNEL_DR; + break; + } + default: + { + break; + } + } + + return phyParam; +} + +void RegionEU868SetBandTxDone( SetBandTxDoneParams_t* txDone ) +{ + RegionCommonSetBandTxDone( txDone->Joined, &NvmCtx.Bands[NvmCtx.Channels[txDone->Channel].Band], txDone->LastTxDoneTime ); +} + +void RegionEU868InitDefaults( InitDefaultsParams_t* params ) +{ + Band_t bands[EU868_MAX_NB_BANDS] = + { + EU868_BAND0, + EU868_BAND1, + EU868_BAND2, + EU868_BAND3, + EU868_BAND4, + EU868_BAND5, + }; + + switch( params->Type ) + { + case INIT_TYPE_INIT: + { + // Initialize bands + memcpy1( ( uint8_t* )NvmCtx.Bands, ( uint8_t* )bands, sizeof( Band_t ) * EU868_MAX_NB_BANDS ); + + // Channels + NvmCtx.Channels[0] = ( ChannelParams_t ) EU868_LC1; + NvmCtx.Channels[1] = ( ChannelParams_t ) EU868_LC2; + NvmCtx.Channels[2] = ( ChannelParams_t ) EU868_LC3; + + // Initialize the channels default mask + NvmCtx.ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 ); + // Update the channels mask + RegionCommonChanMaskCopy( NvmCtx.ChannelsMask, NvmCtx.ChannelsDefaultMask, 1 ); + break; + } + case INIT_TYPE_RESTORE_CTX: + { + if( params->NvmCtx != 0 ) + { + memcpy1( (uint8_t*) &NvmCtx, (uint8_t*) params->NvmCtx, sizeof( NvmCtx ) ); + } + break; + } + case INIT_TYPE_RESTORE_DEFAULT_CHANNELS: + { + // Restore channels default mask + NvmCtx.ChannelsMask[0] |= NvmCtx.ChannelsDefaultMask[0]; + + // Channels + NvmCtx.Channels[0] = ( ChannelParams_t ) EU868_LC1; + NvmCtx.Channels[1] = ( ChannelParams_t ) EU868_LC2; + NvmCtx.Channels[2] = ( ChannelParams_t ) EU868_LC3; + break; + } + default: + { + break; + } + } +} + +void* RegionEU868GetNvmCtx( GetNvmCtxParams_t* params ) +{ + params->nvmCtxSize = sizeof( RegionEU868NvmCtx_t ); + return &NvmCtx; +} + +bool RegionEU868Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ) +{ + switch( phyAttribute ) + { + case PHY_FREQUENCY: + { + uint8_t band = 0; + return VerifyRfFreq( verify->Frequency, &band ); + } + case PHY_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, EU868_TX_MIN_DATARATE, EU868_TX_MAX_DATARATE ); + } + case PHY_DEF_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, DR_0, DR_5 ); + } + case PHY_RX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, EU868_RX_MIN_DATARATE, EU868_RX_MAX_DATARATE ); + } + case PHY_DEF_TX_POWER: + case PHY_TX_POWER: + { + // Remark: switched min and max! + return RegionCommonValueInRange( verify->TxPower, EU868_MAX_TX_POWER, EU868_MIN_TX_POWER ); + } + case PHY_DUTY_CYCLE: + { + return EU868_DUTY_CYCLE_ENABLED; + } + default: + return false; + } +} + +void RegionEU868ApplyCFList( ApplyCFListParams_t* applyCFList ) +{ + ChannelParams_t newChannel; + ChannelAddParams_t channelAdd; + ChannelRemoveParams_t channelRemove; + + // Setup default datarate range + newChannel.DrRange.Value = ( DR_5 << 4 ) | DR_0; + + // Size of the optional CF list + if( applyCFList->Size != 16 ) + { + return; + } + + // Last byte CFListType must be 0 to indicate the CFList contains a list of frequencies + if( applyCFList->Payload[15] != 0 ) + { + return; + } + + // Last byte is RFU, don't take it into account + for( uint8_t i = 0, chanIdx = EU868_NUMB_DEFAULT_CHANNELS; chanIdx < EU868_MAX_NB_CHANNELS; i+=3, chanIdx++ ) + { + if( chanIdx < ( EU868_NUMB_CHANNELS_CF_LIST + EU868_NUMB_DEFAULT_CHANNELS ) ) + { + // Channel frequency + newChannel.Frequency = (uint32_t) applyCFList->Payload[i]; + newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 1] << 8 ); + newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 2] << 16 ); + newChannel.Frequency *= 100; + + // Initialize alternative frequency to 0 + newChannel.Rx1Frequency = 0; + } + else + { + newChannel.Frequency = 0; + newChannel.DrRange.Value = 0; + newChannel.Rx1Frequency = 0; + } + + if( newChannel.Frequency != 0 ) + { + channelAdd.NewChannel = &newChannel; + channelAdd.ChannelId = chanIdx; + + // Try to add all channels + RegionEU868ChannelAdd( &channelAdd ); + } + else + { + channelRemove.ChannelId = chanIdx; + + RegionEU868ChannelsRemove( &channelRemove ); + } + } +} + +bool RegionEU868ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ) +{ + switch( chanMaskSet->ChannelsMaskType ) + { + case CHANNELS_MASK: + { + RegionCommonChanMaskCopy( NvmCtx.ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + case CHANNELS_DEFAULT_MASK: + { + RegionCommonChanMaskCopy( NvmCtx.ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + default: + return false; + } + return true; +} + +void RegionEU868ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ) +{ + double tSymbol = 0.0; + + // Get the datarate, perform a boundary check + rxConfigParams->Datarate = MIN( datarate, EU868_RX_MAX_DATARATE ); + rxConfigParams->Bandwidth = GetBandwidth( rxConfigParams->Datarate ); + + if( rxConfigParams->Datarate == DR_7 ) + { // FSK + tSymbol = RegionCommonComputeSymbolTimeFsk( DataratesEU868[rxConfigParams->Datarate] ); + } + else + { // LoRa + tSymbol = RegionCommonComputeSymbolTimeLoRa( DataratesEU868[rxConfigParams->Datarate], BandwidthsEU868[rxConfigParams->Datarate] ); + } + + RegionCommonComputeRxWindowParameters( tSymbol, minRxSymbols, rxError, Radio.GetWakeupTime( ), &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); +} + +bool RegionEU868RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) +{ + RadioModems_t modem; + int8_t dr = rxConfig->Datarate; + uint8_t maxPayload = 0; + int8_t phyDr = 0; + uint32_t frequency = rxConfig->Frequency; + const char *slotStrings[] = { "1", "2", "C", "Multi_C", "P", "Multi_P" }; + + if( Radio.GetStatus( ) != RF_IDLE ) + { + return false; + } + + if( rxConfig->RxSlot == RX_SLOT_WIN_1 ) + { + // Apply window 1 frequency + frequency = NvmCtx.Channels[rxConfig->Channel].Frequency; + // Apply the alternative RX 1 window frequency, if it is available + if( NvmCtx.Channels[rxConfig->Channel].Rx1Frequency != 0 ) + { + frequency = NvmCtx.Channels[rxConfig->Channel].Rx1Frequency; + } + } + + // Read the physical datarate from the datarates table + phyDr = DataratesEU868[dr]; + + Radio.SetChannel( frequency ); + + // Radio configuration + if( dr == DR_7 ) + { + modem = MODEM_FSK; + Radio.SetRxConfig( modem, 50000, phyDr * 1000, 0, 83333, 5, rxConfig->WindowTimeout, false, 0, true, 0, 0, false, rxConfig->RxContinuous ); + } + else + { + modem = MODEM_LORA; + Radio.SetRxConfig( modem, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous ); + } + + if( rxConfig->RepeaterSupport == true ) + { + maxPayload = MaxPayloadOfDatarateRepeaterEU868[dr]; + } + else + { + maxPayload = MaxPayloadOfDatarateEU868[dr]; + } + + Radio.SetMaxPayloadLength( modem, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD ); + if ( rxConfig->RxSlot < RX_SLOT_NONE ) + { + MW_LOG( "RX_%s on freq %d Hz at DR %d\n\r", slotStrings[rxConfig->RxSlot], frequency, dr ); + } + else + { + MW_LOG( "RX on freq %d Hz at DR %d\n\r", frequency, dr ); + } + + *datarate = (uint8_t) dr; + return true; +} + +bool RegionEU868TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ) +{ + RadioModems_t modem; + int8_t phyDr = DataratesEU868[txConfig->Datarate]; + int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, NvmCtx.Bands[NvmCtx.Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, NvmCtx.ChannelsMask ); + uint32_t bandwidth = GetBandwidth( txConfig->Datarate ); + int8_t phyTxPower = 0; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain ); + + // Setup the radio frequency + Radio.SetChannel( NvmCtx.Channels[txConfig->Channel].Frequency ); + + if( txConfig->Datarate == DR_7 ) + { // High Speed FSK channel + modem = MODEM_FSK; + Radio.SetTxConfig( modem, phyTxPower, 25000, bandwidth, phyDr * 1000, 0, 5, false, true, 0, 0, false, 4000 ); + } + else + { + modem = MODEM_LORA; + Radio.SetTxConfig( modem, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 4000 ); + } + MW_LOG( "TX on freq %d Hz at DR %d\n\r", NvmCtx.Channels[txConfig->Channel].Frequency, txConfig->Datarate ); + + // Setup maximum payload lenght of the radio driver + Radio.SetMaxPayloadLength( modem, txConfig->PktLen ); + // Get the time-on-air of the next tx frame + *txTimeOnAir = Radio.TimeOnAir( modem, txConfig->PktLen ); + + *txPower = txPowerLimited; + return true; +} + +uint8_t RegionEU868LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ) +{ + uint8_t status = 0x07; + RegionCommonLinkAdrParams_t linkAdrParams; + uint8_t nextIndex = 0; + uint8_t bytesProcessed = 0; + uint16_t chMask = 0; + GetPhyParams_t getPhy; + PhyParam_t phyParam; + RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams; + + while( bytesProcessed < linkAdrReq->PayloadSize ) + { + // Get ADR request parameters + nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams ); + + if( nextIndex == 0 ) + break; // break loop, since no more request has been found + + // Update bytes processed + bytesProcessed += nextIndex; + + // Revert status, as we only check the last ADR request for the channel mask KO + status = 0x07; + + // Setup temporary channels mask + chMask = linkAdrParams.ChMask; + + // Verify channels mask + if( ( linkAdrParams.ChMaskCtrl == 0 ) && ( chMask == 0 ) ) + { + status &= 0xFE; // Channel mask KO + } + else if( ( ( linkAdrParams.ChMaskCtrl >= 1 ) && ( linkAdrParams.ChMaskCtrl <= 5 )) || + ( linkAdrParams.ChMaskCtrl >= 7 ) ) + { + // RFU + status &= 0xFE; // Channel mask KO + } + else + { + for( uint8_t i = 0; i < EU868_MAX_NB_CHANNELS; i++ ) + { + if( linkAdrParams.ChMaskCtrl == 6 ) + { + if( NvmCtx.Channels[i].Frequency != 0 ) + { + chMask |= 1 << i; + } + } + else + { + if( ( ( chMask & ( 1 << i ) ) != 0 ) && + ( NvmCtx.Channels[i].Frequency == 0 ) ) + {// Trying to enable an undefined channel + status &= 0xFE; // Channel mask KO + } + } + } + } + } + + // Get the minimum possible datarate + getPhy.Attribute = PHY_MIN_TX_DR; + getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime; + phyParam = RegionEU868GetPhyParam( &getPhy ); + + linkAdrVerifyParams.Status = status; + linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled; + linkAdrVerifyParams.Datarate = linkAdrParams.Datarate; + linkAdrVerifyParams.TxPower = linkAdrParams.TxPower; + linkAdrVerifyParams.NbRep = linkAdrParams.NbRep; + linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate; + linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower; + linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep; + linkAdrVerifyParams.NbChannels = EU868_MAX_NB_CHANNELS; + linkAdrVerifyParams.ChannelsMask = &chMask; + linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value; + linkAdrVerifyParams.MaxDatarate = EU868_TX_MAX_DATARATE; + linkAdrVerifyParams.Channels = NvmCtx.Channels; + linkAdrVerifyParams.MinTxPower = EU868_MIN_TX_POWER; + linkAdrVerifyParams.MaxTxPower = EU868_MAX_TX_POWER; + linkAdrVerifyParams.Version = linkAdrReq->Version; + + // Verify the parameters and update, if necessary + status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep ); + + // Update channelsMask if everything is correct + if( status == 0x07 ) + { + // Set the channels mask to a default value + memset1( ( uint8_t* ) NvmCtx.ChannelsMask, 0, sizeof( NvmCtx.ChannelsMask ) ); + // Update the channels mask + NvmCtx.ChannelsMask[0] = chMask; + } + + // Update status variables + *drOut = linkAdrParams.Datarate; + *txPowOut = linkAdrParams.TxPower; + *nbRepOut = linkAdrParams.NbRep; + *nbBytesParsed = bytesProcessed; + + return status; +} + +uint8_t RegionEU868RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ) +{ + uint8_t status = 0x07; + uint8_t band = 0; + + // Verify radio frequency + if( VerifyRfFreq( rxParamSetupReq->Frequency, &band ) == false ) + { + status &= 0xFE; // Channel frequency KO + } + + // Verify datarate + if( RegionCommonValueInRange( rxParamSetupReq->Datarate, EU868_RX_MIN_DATARATE, EU868_RX_MAX_DATARATE ) == false ) + { + status &= 0xFD; // Datarate KO + } + + // Verify datarate offset + if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, EU868_MIN_RX1_DR_OFFSET, EU868_MAX_RX1_DR_OFFSET ) == false ) + { + status &= 0xFB; // Rx1DrOffset range KO + } + + return status; +} + +uint8_t RegionEU868NewChannelReq( NewChannelReqParams_t* newChannelReq ) +{ + uint8_t status = 0x03; + ChannelAddParams_t channelAdd; + ChannelRemoveParams_t channelRemove; + + if( newChannelReq->NewChannel->Frequency == 0 ) + { + channelRemove.ChannelId = newChannelReq->ChannelId; + + // Remove + if( RegionEU868ChannelsRemove( &channelRemove ) == false ) + { + status &= 0xFC; + } + } + else + { + channelAdd.NewChannel = newChannelReq->NewChannel; + channelAdd.ChannelId = newChannelReq->ChannelId; + + switch( RegionEU868ChannelAdd( &channelAdd ) ) + { + case LORAMAC_STATUS_OK: + { + break; + } + case LORAMAC_STATUS_FREQUENCY_INVALID: + { + status &= 0xFE; + break; + } + case LORAMAC_STATUS_DATARATE_INVALID: + { + status &= 0xFD; + break; + } + case LORAMAC_STATUS_FREQ_AND_DR_INVALID: + { + status &= 0xFC; + break; + } + default: + { + status &= 0xFC; + break; + } + } + } + + return status; +} + +int8_t RegionEU868TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ) +{ + return -1; +} + +uint8_t RegionEU868DlChannelReq( DlChannelReqParams_t* dlChannelReq ) +{ + uint8_t status = 0x03; + uint8_t band = 0; + + // Verify if the frequency is supported + if( VerifyRfFreq( dlChannelReq->Rx1Frequency, &band ) == false ) + { + status &= 0xFE; + } + + // Verify if an uplink frequency exists + if( NvmCtx.Channels[dlChannelReq->ChannelId].Frequency == 0 ) + { + status &= 0xFD; + } + + // Apply Rx1 frequency, if the status is OK + if( status == 0x03 ) + { + NvmCtx.Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency; + } + + return status; +} + +int8_t RegionEU868AlternateDr( int8_t currentDr, AlternateDrType_t type ) +{ + return currentDr; +} + +void RegionEU868CalcBackOff( CalcBackOffParams_t* calcBackOff ) +{ + RegionCommonCalcBackOffParams_t calcBackOffParams; + + calcBackOffParams.Channels = NvmCtx.Channels; + calcBackOffParams.Bands = NvmCtx.Bands; + calcBackOffParams.LastTxIsJoinRequest = calcBackOff->LastTxIsJoinRequest; + calcBackOffParams.Joined = calcBackOff->Joined; + calcBackOffParams.DutyCycleEnabled = calcBackOff->DutyCycleEnabled; + calcBackOffParams.Channel = calcBackOff->Channel; + calcBackOffParams.ElapsedTime = calcBackOff->ElapsedTime; + calcBackOffParams.TxTimeOnAir = calcBackOff->TxTimeOnAir; + + RegionCommonCalcBackOff( &calcBackOffParams ); +} + +LoRaMacStatus_t RegionEU868NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t delayTx = 0; + uint8_t enabledChannels[EU868_MAX_NB_CHANNELS] = { 0 }; + TimerTime_t nextTxDelay = 0; + + if( RegionCommonCountChannels( NvmCtx.ChannelsMask, 0, 1 ) == 0 ) + { // Reactivate default channels + NvmCtx.ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); + } + + TimerTime_t elapsed = TimerGetElapsedTime( nextChanParams->LastAggrTx ); + if( ( nextChanParams->LastAggrTx == 0 ) || ( nextChanParams->AggrTimeOff <= elapsed ) ) + { + // Reset Aggregated time off + *aggregatedTimeOff = 0; + + // Update bands Time OFF + nextTxDelay = RegionCommonUpdateBandTimeOff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, NvmCtx.Bands, EU868_MAX_NB_BANDS ); + + // Search how many channels are enabled + nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Joined, nextChanParams->Datarate, + NvmCtx.ChannelsMask, NvmCtx.Channels, + NvmCtx.Bands, enabledChannels, &delayTx ); + } + else + { + delayTx++; + nextTxDelay = nextChanParams->AggrTimeOff - elapsed; + } + + if( nbEnabledChannels > 0 ) + { + // We found a valid channel + *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )]; + + *time = 0; + return LORAMAC_STATUS_OK; + } + else + { + if( delayTx > 0 ) + { + // Delay transmission due to AggregatedTimeOff or to a band time off + *time = nextTxDelay; + return LORAMAC_STATUS_DUTYCYCLE_RESTRICTED; + } + // Datarate not supported by any channel, restore defaults + NvmCtx.ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); + *time = 0; + return LORAMAC_STATUS_NO_CHANNEL_FOUND; + } +} + +LoRaMacStatus_t RegionEU868ChannelAdd( ChannelAddParams_t* channelAdd ) +{ + uint8_t band = 0; + bool drInvalid = false; + bool freqInvalid = false; + uint8_t id = channelAdd->ChannelId; + + if( id < EU868_NUMB_DEFAULT_CHANNELS ) + { + return LORAMAC_STATUS_FREQ_AND_DR_INVALID; + } + + if( id >= EU868_MAX_NB_CHANNELS ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + // Validate the datarate range + if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Min, EU868_TX_MIN_DATARATE, EU868_TX_MAX_DATARATE ) == false ) + { + drInvalid = true; + } + if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, EU868_TX_MIN_DATARATE, EU868_TX_MAX_DATARATE ) == false ) + { + drInvalid = true; + } + if( channelAdd->NewChannel->DrRange.Fields.Min > channelAdd->NewChannel->DrRange.Fields.Max ) + { + drInvalid = true; + } + + // Check frequency + if( freqInvalid == false ) + { + if( VerifyRfFreq( channelAdd->NewChannel->Frequency, &band ) == false ) + { + freqInvalid = true; + } + } + + // Check status + if( ( drInvalid == true ) && ( freqInvalid == true ) ) + { + return LORAMAC_STATUS_FREQ_AND_DR_INVALID; + } + if( drInvalid == true ) + { + return LORAMAC_STATUS_DATARATE_INVALID; + } + if( freqInvalid == true ) + { + return LORAMAC_STATUS_FREQUENCY_INVALID; + } + + memcpy1( ( uint8_t* ) &(NvmCtx.Channels[id]), ( uint8_t* ) channelAdd->NewChannel, sizeof( NvmCtx.Channels[id] ) ); + NvmCtx.Channels[id].Band = band; + NvmCtx.ChannelsMask[0] |= ( 1 << id ); + return LORAMAC_STATUS_OK; +} + +bool RegionEU868ChannelsRemove( ChannelRemoveParams_t* channelRemove ) +{ + uint8_t id = channelRemove->ChannelId; + + if( id < EU868_NUMB_DEFAULT_CHANNELS ) + { + return false; + } + + // Remove the channel from the list of channels + NvmCtx.Channels[id] = ( ChannelParams_t ){ 0, 0, { 0 }, 0 }; + + return RegionCommonChanDisable( NvmCtx.ChannelsMask, id, EU868_MAX_NB_CHANNELS ); +} + +void RegionEU868SetContinuousWave( ContinuousWaveParams_t* continuousWave ) +{ + int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, NvmCtx.Bands[NvmCtx.Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, NvmCtx.ChannelsMask ); + int8_t phyTxPower = 0; + uint32_t frequency = NvmCtx.Channels[continuousWave->Channel].Frequency; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, continuousWave->MaxEirp, continuousWave->AntennaGain ); + + Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout ); +} + +uint8_t RegionEU868ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) +{ + int8_t datarate = dr - drOffset; + + if( datarate < 0 ) + { + datarate = DR_0; + } + return datarate; +} + +void RegionEU868RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ) +{ + RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup; + + regionCommonRxBeaconSetup.Datarates = DataratesEU868; + regionCommonRxBeaconSetup.Frequency = rxBeaconSetup->Frequency; + regionCommonRxBeaconSetup.BeaconSize = EU868_BEACON_SIZE; + regionCommonRxBeaconSetup.BeaconDatarate = EU868_BEACON_CHANNEL_DR; + regionCommonRxBeaconSetup.BeaconChannelBW = EU868_BEACON_CHANNEL_BW; + regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime; + regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout; + + RegionCommonRxBeaconSetup( ®ionCommonRxBeaconSetup ); + + // Store downlink datarate + *outDr = EU868_BEACON_CHANNEL_DR; +} diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionEU868.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionEU868.h new file mode 100644 index 00000000..3c1d4ed5 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionEU868.h @@ -0,0 +1,536 @@ +/*! + * \file RegionEU868.h + * + * \brief Region definition for EU868 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + * + * \defgroup REGIONEU868 Region EU868 + * Implementation according to LoRaWAN Specification v1.0.2. + * \{ + */ +#ifndef __REGION_EU868_H__ +#define __REGION_EU868_H__ + +#include "region/Region.h" + +/*! + * LoRaMac maximum number of channels + */ +#define EU868_MAX_NB_CHANNELS 16 + +/*! + * Number of default channels + */ +#define EU868_NUMB_DEFAULT_CHANNELS 3 + +/*! + * Number of channels to apply for the CF list + */ +#define EU868_NUMB_CHANNELS_CF_LIST 5 + +/*! + * Minimal datarate that can be used by the node + */ +#define EU868_TX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define EU868_TX_MAX_DATARATE DR_7 + +/*! + * Minimal datarate that can be used by the node + */ +#define EU868_RX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define EU868_RX_MAX_DATARATE DR_7 + +/*! + * Default datarate used by the node + */ +#define EU868_DEFAULT_DATARATE DR_0 + +/*! + * Minimal Rx1 receive datarate offset + */ +#define EU868_MIN_RX1_DR_OFFSET 0 + +/*! + * Maximal Rx1 receive datarate offset + */ +#define EU868_MAX_RX1_DR_OFFSET 5 + +/*! + * Default Rx1 receive datarate offset + */ +#define EU868_DEFAULT_RX1_DR_OFFSET 0 + +/*! + * Minimal Tx output power that can be used by the node + */ +#define EU868_MIN_TX_POWER TX_POWER_7 + +/*! + * Maximal Tx output power that can be used by the node + */ +#define EU868_MAX_TX_POWER TX_POWER_0 + +/*! + * Default Tx output power used by the node + */ +#define EU868_DEFAULT_TX_POWER TX_POWER_0 + +/*! + * Default Max EIRP + */ +#define EU868_DEFAULT_MAX_EIRP 16.0f + +/*! + * Default antenna gain + */ +#define EU868_DEFAULT_ANTENNA_GAIN 2.15f + +/*! + * ADR Ack limit + */ +#define EU868_ADR_ACK_LIMIT 64 + +/*! + * ADR Ack delay + */ +#define EU868_ADR_ACK_DELAY 32 + +/*! + * Enabled or disabled the duty cycle + */ +#define EU868_DUTY_CYCLE_ENABLED 1 + +/*! + * Maximum RX window duration + */ +#define EU868_MAX_RX_WINDOW 3000 + +/*! + * Receive delay 1 + */ +#define EU868_RECEIVE_DELAY1 1000 + +/*! + * Receive delay 2 + */ +#define EU868_RECEIVE_DELAY2 2000 + +/*! + * Join accept delay 1 + */ +#define EU868_JOIN_ACCEPT_DELAY1 5000 + +/*! + * Join accept delay 2 + */ +#define EU868_JOIN_ACCEPT_DELAY2 6000 + +/*! + * Maximum frame counter gap + */ +#define EU868_MAX_FCNT_GAP 16384 + +/*! + * Ack timeout + */ +#define EU868_ACKTIMEOUT 2000 + +/*! + * Random ack timeout limits + */ +#define EU868_ACK_TIMEOUT_RND 1000 + +#if ( EU868_DEFAULT_DATARATE > DR_5 ) +#error "A default DR higher than DR_5 may lead to connectivity loss." +#endif + +/*! + * Second reception window channel frequency definition. + */ +#define EU868_RX_WND_2_FREQ 869525000 + +/*! + * Second reception window channel datarate definition. + */ +#define EU868_RX_WND_2_DR DR_0 + +/* + * CLASS B + */ +/*! + * Beacon frequency + */ +#define EU868_BEACON_CHANNEL_FREQ 869525000 + +/*! + * Payload size of a beacon frame + */ +#define EU868_BEACON_SIZE 17 + +/*! + * Size of RFU 1 field + */ +#define EU868_RFU1_SIZE 2 + +/*! + * Size of RFU 2 field + */ +#define EU868_RFU2_SIZE 0 + +/*! + * Datarate of the beacon channel + */ +#define EU868_BEACON_CHANNEL_DR DR_3 + +/*! + * Bandwith of the beacon channel + */ +#define EU868_BEACON_CHANNEL_BW 0 + +/*! + * Ping slot channel datarate + */ +#define EU868_PING_SLOT_CHANNEL_DR DR_3 + +/*! + * Maximum number of bands + */ +#define EU868_MAX_NB_BANDS 6 + +/*! + * Band 0 definition + * { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff } + */ +#define EU868_BAND0 { 100 , EU868_MAX_TX_POWER, 0, 0, 0 } // 1.0 % + +/*! + * Band 1 definition + * { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff } + */ +#define EU868_BAND1 { 100 , EU868_MAX_TX_POWER, 0, 0, 0 } // 1.0 % + +/*! + * Band 2 definition + * Band = { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff } + */ +#define EU868_BAND2 { 1000, EU868_MAX_TX_POWER, 0, 0, 0 } // 0.1 % + +/*! + * Band 3 definition + * Band = { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff } + */ +#define EU868_BAND3 { 10 , EU868_MAX_TX_POWER, 0, 0, 0 } // 10.0 % + +/*! + * Band 4 definition + * Band = { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff } + */ +#define EU868_BAND4 { 100 , EU868_MAX_TX_POWER, 0, 0, 0 } // 1.0 % + +/*! + * Band 5 definition + * Band = { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff } + */ +#define EU868_BAND5 { 1000, EU868_MAX_TX_POWER, 0, 0, 0 } // 0.1 % + +/*! + * LoRaMac default channel 1 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define EU868_LC1 { 868100000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 1 } + +/*! + * LoRaMac default channel 2 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define EU868_LC2 { 868300000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 1 } + +/*! + * LoRaMac default channel 3 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define EU868_LC3 { 868500000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 1 } + +/*! + * LoRaMac channels which are allowed for the join procedure + */ +#define EU868_JOIN_CHANNELS ( uint16_t )( LC( 1 ) | LC( 2 ) | LC( 3 ) ) + +/*! + * Data rates table definition + */ +static const uint8_t DataratesEU868[] = { 12, 11, 10, 9, 8, 7, 7, 50 }; + +/*! + * Bandwidths table definition in Hz + */ +static const uint32_t BandwidthsEU868[] = { 125000, 125000, 125000, 125000, 125000, 125000, 250000, 0 }; + +/*! + * Maximum payload with respect to the datarate index. Cannot operate with repeater. + */ +static const uint8_t MaxPayloadOfDatarateEU868[] = { 51, 51, 51, 115, 242, 242, 242, 242 }; + +/*! + * Maximum payload with respect to the datarate index. Can operate with repeater. + */ +static const uint8_t MaxPayloadOfDatarateRepeaterEU868[] = { 51, 51, 51, 115, 222, 222, 222, 222 }; + +/*! + * \brief The function gets a value of a specific phy attribute. + * + * \param [IN] getPhy Pointer to the function parameters. + * + * \retval Returns a structure containing the PHY parameter. + */ +PhyParam_t RegionEU868GetPhyParam( GetPhyParams_t* getPhy ); + +/*! + * \brief Updates the last TX done parameters of the current channel. + * + * \param [IN] txDone Pointer to the function parameters. + */ +void RegionEU868SetBandTxDone( SetBandTxDoneParams_t* txDone ); + +/*! + * \brief Initializes the channels masks and the channels. + * + * \param [IN] type Sets the initialization type. + */ +void RegionEU868InitDefaults( InitDefaultsParams_t* params ); + +/*! + * \brief Returns a pointer to the internal context and its size. + * + * \param [OUT] params Pointer to the function parameters. + * + * \retval Points to a structure where the module store its non-volatile context. + */ +void* RegionEU868GetNvmCtx( GetNvmCtxParams_t* params ); + +/*! + * \brief Verifies a parameter. + * + * \param [IN] verify Pointer to the function parameters. + * + * \param [IN] type Sets the initialization type. + * + * \retval Returns true, if the parameter is valid. + */ +bool RegionEU868Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ); + +/*! + * \brief The function parses the input buffer and sets up the channels of the + * CF list. + * + * \param [IN] applyCFList Pointer to the function parameters. + */ +void RegionEU868ApplyCFList( ApplyCFListParams_t* applyCFList ); + +/*! + * \brief Sets a channels mask. + * + * \param [IN] chanMaskSet Pointer to the function parameters. + * + * \retval Returns true, if the channels mask could be set. + */ +bool RegionEU868ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ); + +/*! + * Computes the Rx window timeout and offset. + * + * \param [IN] datarate Rx window datarate index to be used + * + * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame. + * + * \param [IN] rxError System maximum timing error of the receiver. In milliseconds + * The receiver will turn on in a [-rxError : +rxError] ms + * interval around RxOffset + * + * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields. + */ +void RegionEU868ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ); + +/*! + * \brief Configuration of the RX windows. + * + * \param [IN] rxConfig Pointer to the function parameters. + * + * \param [OUT] datarate The datarate index which was set. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionEU868RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ); + +/*! + * \brief TX configuration. + * + * \param [IN] txConfig Pointer to the function parameters. + * + * \param [OUT] txPower The tx power index which was set. + * + * \param [OUT] txTimeOnAir The time-on-air of the frame. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionEU868TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ); + +/*! + * \brief The function processes a Link ADR Request. + * + * \param [IN] linkAdrReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionEU868LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ); + +/*! + * \brief The function processes a RX Parameter Setup Request. + * + * \param [IN] rxParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionEU868RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ); + +/*! + * \brief The function processes a Channel Request. + * + * \param [IN] newChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionEU868NewChannelReq( NewChannelReqParams_t* newChannelReq ); + +/*! + * \brief The function processes a TX ParamSetup Request. + * + * \param [IN] txParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + * Returns -1, if the functionality is not implemented. In this case, the end node + * shall not process the command. + */ +int8_t RegionEU868TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ); + +/*! + * \brief The function processes a DlChannel Request. + * + * \param [IN] dlChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionEU868DlChannelReq( DlChannelReqParams_t* dlChannelReq ); + +/*! + * \brief Alternates the datarate of the channel for the join request. + * + * \param [IN] currentDr Current datarate. + * + * \retval Datarate to apply. + */ +int8_t RegionEU868AlternateDr( int8_t currentDr, AlternateDrType_t type ); + +/*! + * \brief Calculates the back-off time. + * + * \param [IN] calcBackOff Pointer to the function parameters. + */ +void RegionEU868CalcBackOff( CalcBackOffParams_t* calcBackOff ); + +/*! + * \brief Searches and set the next random available channel + * + * \param [OUT] channel Next channel to use for TX. + * + * \param [OUT] time Time to wait for the next transmission according to the duty + * cycle. + * + * \param [OUT] aggregatedTimeOff Updates the aggregated time off. + * + * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate] + */ +LoRaMacStatus_t RegionEU868NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ); + +/*! + * \brief Adds a channel. + * + * \param [IN] channelAdd Pointer to the function parameters. + * + * \retval Status of the operation. + */ +LoRaMacStatus_t RegionEU868ChannelAdd( ChannelAddParams_t* channelAdd ); + +/*! + * \brief Removes a channel. + * + * \param [IN] channelRemove Pointer to the function parameters. + * + * \retval Returns true, if the channel was removed successfully. + */ +bool RegionEU868ChannelsRemove( ChannelRemoveParams_t* channelRemove ); + +/*! + * \brief Sets the radio into continuous wave mode. + * + * \param [IN] continuousWave Pointer to the function parameters. + */ +void RegionEU868SetContinuousWave( ContinuousWaveParams_t* continuousWave ); + +/*! + * \brief Computes new datarate according to the given offset + * + * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms + * + * \param [IN] dr Current datarate + * + * \param [IN] drOffset Offset to be applied + * + * \retval newDr Computed datarate. + */ +uint8_t RegionEU868ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ); + +/*! + * \brief Sets the radio into beacon reception mode + * + * \param [IN] rxBeaconSetup Pointer to the function parameters + */ +void RegionEU868RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ); + +/*! \} defgroup REGIONEU868 */ + +#endif // __REGION_EU868_H__ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionIN865.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionIN865.c new file mode 100644 index 00000000..4a4cbfbe --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionIN865.c @@ -0,0 +1,1060 @@ +/*! + * \file RegionIN865.c + * + * \brief Region implementation for IN865 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) +*/ +#include "utilities.h" + +#include "RegionCommon.h" +#include "RegionIN865.h" +#include "lorawan_conf.h" +#include "mw_log_conf.h" + +// Definitions +#define CHANNELS_MASK_SIZE 1 + +/*! + * Region specific context + */ +typedef struct sRegionIN865NvmCtx +{ + /*! + * LoRaMAC channels + */ + ChannelParams_t Channels[ IN865_MAX_NB_CHANNELS ]; + /*! + * LoRaMac bands + */ + Band_t Bands[ IN865_MAX_NB_BANDS ]; + /*! + * LoRaMac channels mask + */ + uint16_t ChannelsMask[ CHANNELS_MASK_SIZE ]; + /*! + * LoRaMac channels default mask + */ + uint16_t ChannelsDefaultMask[ CHANNELS_MASK_SIZE ]; +}RegionIN865NvmCtx_t; + +/* + * Non-volatile module context. + */ +static RegionIN865NvmCtx_t NvmCtx; + +// Static functions +static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr ) +{ + uint8_t nextLowerDr = 0; + + if( dr == minDr ) + { + nextLowerDr = minDr; + } + else if( dr == DR_7 ) + { + nextLowerDr = DR_5; + } + else + { + nextLowerDr = dr - 1; + } + return nextLowerDr; +} + +static uint32_t GetBandwidth( uint32_t drIndex ) +{ + switch( BandwidthsIN865[drIndex] ) + { + default: + case 125000: + return 0; + case 250000: + return 1; + case 500000: + return 2; + } +} + +static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask ) +{ + int8_t txPowerResult = txPower; + + // Limit tx power to the band max + txPowerResult = MAX( txPower, maxBandTxPower ); + + return txPowerResult; +} + +static bool VerifyRfFreq( uint32_t freq ) +{ + // Check radio driver support + if( Radio.CheckRfFrequency( freq ) == false ) + { + return false; + } + + if( ( freq < 865000000 ) || ( freq > 867000000 ) ) + { + return false; + } + return true; +} + +static uint8_t CountNbOfEnabledChannels( bool joined, uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t delayTransmission = 0; + + for( uint8_t i = 0, k = 0; i < IN865_MAX_NB_CHANNELS; i += 16, k++ ) + { + for( uint8_t j = 0; j < 16; j++ ) + { + if( ( channelsMask[k] & ( 1 << j ) ) != 0 ) + { + if( channels[i + j].Frequency == 0 ) + { // Check if the channel is enabled + continue; + } + if( joined == false ) + { + if( ( IN865_JOIN_CHANNELS & ( 1 << j ) ) == 0 ) + { + continue; + } + } + if( RegionCommonValueInRange( datarate, channels[i + j].DrRange.Fields.Min, + channels[i + j].DrRange.Fields.Max ) == false ) + { // Check if the current channel selection supports the given datarate + continue; + } + if( bands[channels[i + j].Band].TimeOff > 0 ) + { // Check if the band is available for transmission + delayTransmission++; + continue; + } + enabledChannels[nbEnabledChannels++] = i + j; + } + } + } + + *delayTx = delayTransmission; + return nbEnabledChannels; +} + +PhyParam_t RegionIN865GetPhyParam( GetPhyParams_t* getPhy ) +{ + PhyParam_t phyParam = { 0 }; + + switch( getPhy->Attribute ) + { + case PHY_MIN_RX_DR: + { + phyParam.Value = IN865_RX_MIN_DATARATE; + break; + } + case PHY_MIN_TX_DR: + { + phyParam.Value = IN865_TX_MIN_DATARATE; + break; + } + case PHY_DEF_TX_DR: + { + phyParam.Value = IN865_DEFAULT_DATARATE; + break; + } + case PHY_NEXT_LOWER_TX_DR: + { + phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, IN865_TX_MIN_DATARATE ); + break; + } + case PHY_MAX_TX_POWER: + { + phyParam.Value = IN865_MAX_TX_POWER; + break; + } + case PHY_DEF_TX_POWER: + { + phyParam.Value = IN865_DEFAULT_TX_POWER; + break; + } + case PHY_DEF_ADR_ACK_LIMIT: + { + phyParam.Value = IN865_ADR_ACK_LIMIT; + break; + } + case PHY_DEF_ADR_ACK_DELAY: + { + phyParam.Value = IN865_ADR_ACK_DELAY; + break; + } + case PHY_MAX_PAYLOAD: + { + phyParam.Value = MaxPayloadOfDatarateIN865[getPhy->Datarate]; + break; + } + case PHY_MAX_PAYLOAD_REPEATER: + { + phyParam.Value = MaxPayloadOfDatarateRepeaterIN865[getPhy->Datarate]; + break; + } + case PHY_DUTY_CYCLE: + { + phyParam.Value = IN865_DUTY_CYCLE_ENABLED; + break; + } + case PHY_MAX_RX_WINDOW: + { + phyParam.Value = IN865_MAX_RX_WINDOW; + break; + } + case PHY_RECEIVE_DELAY1: + { + phyParam.Value = IN865_RECEIVE_DELAY1; + break; + } + case PHY_RECEIVE_DELAY2: + { + phyParam.Value = IN865_RECEIVE_DELAY2; + break; + } + case PHY_JOIN_ACCEPT_DELAY1: + { + phyParam.Value = IN865_JOIN_ACCEPT_DELAY1; + break; + } + case PHY_JOIN_ACCEPT_DELAY2: + { + phyParam.Value = IN865_JOIN_ACCEPT_DELAY2; + break; + } + case PHY_MAX_FCNT_GAP: + { + phyParam.Value = IN865_MAX_FCNT_GAP; + break; + } + case PHY_ACK_TIMEOUT: + { + phyParam.Value = ( IN865_ACKTIMEOUT + randr( -IN865_ACK_TIMEOUT_RND, IN865_ACK_TIMEOUT_RND ) ); + break; + } + case PHY_DEF_DR1_OFFSET: + { + phyParam.Value = IN865_DEFAULT_RX1_DR_OFFSET; + break; + } + case PHY_DEF_RX2_FREQUENCY: + { + phyParam.Value = IN865_RX_WND_2_FREQ; + break; + } + case PHY_DEF_RX2_DR: + { + phyParam.Value = IN865_RX_WND_2_DR; + break; + } + case PHY_CHANNELS_MASK: + { + phyParam.ChannelsMask = NvmCtx.ChannelsMask; + break; + } + case PHY_CHANNELS_DEFAULT_MASK: + { + phyParam.ChannelsMask = NvmCtx.ChannelsDefaultMask; + break; + } + case PHY_MAX_NB_CHANNELS: + { + phyParam.Value = IN865_MAX_NB_CHANNELS; + break; + } + case PHY_CHANNELS: + { + phyParam.Channels = NvmCtx.Channels; + break; + } + case PHY_DEF_UPLINK_DWELL_TIME: + case PHY_DEF_DOWNLINK_DWELL_TIME: + { + phyParam.Value = 0; + break; + } + case PHY_DEF_MAX_EIRP: + { + phyParam.fValue = IN865_DEFAULT_MAX_EIRP; + break; + } + case PHY_DEF_ANTENNA_GAIN: + { + phyParam.fValue = IN865_DEFAULT_ANTENNA_GAIN; + break; + } + case PHY_BEACON_CHANNEL_FREQ: + { + phyParam.Value = IN865_BEACON_CHANNEL_FREQ; + break; + } + case PHY_BEACON_FORMAT: + { + phyParam.BeaconFormat.BeaconSize = IN865_BEACON_SIZE; + phyParam.BeaconFormat.Rfu1Size = IN865_RFU1_SIZE; + phyParam.BeaconFormat.Rfu2Size = IN865_RFU2_SIZE; + break; + } + case PHY_BEACON_CHANNEL_DR: + { + phyParam.Value = IN865_BEACON_CHANNEL_DR; + break; + } + case PHY_PING_SLOT_CHANNEL_DR: + { + phyParam.Value = IN865_PING_SLOT_CHANNEL_DR; + break; + } + default: + { + break; + } + } + + return phyParam; +} + +void RegionIN865SetBandTxDone( SetBandTxDoneParams_t* txDone ) +{ + RegionCommonSetBandTxDone( txDone->Joined, &NvmCtx.Bands[NvmCtx.Channels[txDone->Channel].Band], txDone->LastTxDoneTime ); +} + +void RegionIN865InitDefaults( InitDefaultsParams_t* params ) +{ + Band_t bands[IN865_MAX_NB_BANDS] = + { + IN865_BAND0 + }; + + switch( params->Type ) + { + case INIT_TYPE_INIT: + { + // Initialize bands + memcpy1( ( uint8_t* )NvmCtx.Bands, ( uint8_t* )bands, sizeof( Band_t ) * IN865_MAX_NB_BANDS ); + + // Channels + NvmCtx.Channels[0] = ( ChannelParams_t ) IN865_LC1; + NvmCtx.Channels[1] = ( ChannelParams_t ) IN865_LC2; + NvmCtx.Channels[2] = ( ChannelParams_t ) IN865_LC3; + + // Initialize the channels default mask + NvmCtx.ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 ); + // Update the channels mask + RegionCommonChanMaskCopy( NvmCtx.ChannelsMask, NvmCtx.ChannelsDefaultMask, 1 ); + break; + } + case INIT_TYPE_RESTORE_CTX: + { + if( params->NvmCtx != 0 ) + { + memcpy1( (uint8_t*) &NvmCtx, (uint8_t*) params->NvmCtx, sizeof( NvmCtx ) ); + } + break; + } + case INIT_TYPE_RESTORE_DEFAULT_CHANNELS: + { + // Restore channels default mask + NvmCtx.ChannelsMask[0] |= NvmCtx.ChannelsDefaultMask[0]; + + // Channels + NvmCtx.Channels[0] = ( ChannelParams_t ) IN865_LC1; + NvmCtx.Channels[1] = ( ChannelParams_t ) IN865_LC2; + NvmCtx.Channels[2] = ( ChannelParams_t ) IN865_LC3; + break; + } + default: + { + break; + } + } +} + +void* RegionIN865GetNvmCtx( GetNvmCtxParams_t* params ) +{ + params->nvmCtxSize = sizeof( RegionIN865NvmCtx_t ); + return &NvmCtx; +} + +bool RegionIN865Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ) +{ + switch( phyAttribute ) + { + case PHY_FREQUENCY: + { + return VerifyRfFreq( verify->Frequency ); + } + case PHY_TX_DR: + { + if( verify->DatarateParams.Datarate == DR_6 ) + {// DR_6 is not supported by this region + return false; + } + else + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, IN865_TX_MIN_DATARATE, IN865_TX_MAX_DATARATE ); + } + } + case PHY_DEF_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, DR_0, DR_5 ); + } + case PHY_RX_DR: + { + if( verify->DatarateParams.Datarate == DR_6 ) + {// DR_6 is not supported by this region + return false; + } + else + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, IN865_RX_MIN_DATARATE, IN865_RX_MAX_DATARATE ); + } + } + case PHY_DEF_TX_POWER: + case PHY_TX_POWER: + { + // Remark: switched min and max! + return RegionCommonValueInRange( verify->TxPower, IN865_MAX_TX_POWER, IN865_MIN_TX_POWER ); + } + case PHY_DUTY_CYCLE: + { + return IN865_DUTY_CYCLE_ENABLED; + } + default: + return false; + } +} + +void RegionIN865ApplyCFList( ApplyCFListParams_t* applyCFList ) +{ + ChannelParams_t newChannel; + ChannelAddParams_t channelAdd; + ChannelRemoveParams_t channelRemove; + + // Setup default datarate range + newChannel.DrRange.Value = ( DR_5 << 4 ) | DR_0; + + // Size of the optional CF list + if( applyCFList->Size != 16 ) + { + return; + } + + // Last byte CFListType must be 0 to indicate the CFList contains a list of frequencies + if( applyCFList->Payload[15] != 0 ) + { + return; + } + + // Last byte is RFU, don't take it into account + for( uint8_t i = 0, chanIdx = IN865_NUMB_DEFAULT_CHANNELS; chanIdx < IN865_MAX_NB_CHANNELS; i+=3, chanIdx++ ) + { + if( chanIdx < ( IN865_NUMB_CHANNELS_CF_LIST + IN865_NUMB_DEFAULT_CHANNELS ) ) + { + // Channel frequency + newChannel.Frequency = (uint32_t) applyCFList->Payload[i]; + newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 1] << 8 ); + newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 2] << 16 ); + newChannel.Frequency *= 100; + + // Initialize alternative frequency to 0 + newChannel.Rx1Frequency = 0; + } + else + { + newChannel.Frequency = 0; + newChannel.DrRange.Value = 0; + newChannel.Rx1Frequency = 0; + } + + if( newChannel.Frequency != 0 ) + { + channelAdd.NewChannel = &newChannel; + channelAdd.ChannelId = chanIdx; + + // Try to add all channels + RegionIN865ChannelAdd( &channelAdd ); + } + else + { + channelRemove.ChannelId = chanIdx; + + RegionIN865ChannelsRemove( &channelRemove ); + } + } +} + +bool RegionIN865ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ) +{ + switch( chanMaskSet->ChannelsMaskType ) + { + case CHANNELS_MASK: + { + RegionCommonChanMaskCopy( NvmCtx.ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + case CHANNELS_DEFAULT_MASK: + { + RegionCommonChanMaskCopy( NvmCtx.ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + default: + return false; + } + return true; +} + +void RegionIN865ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ) +{ + double tSymbol = 0.0; + + // Get the datarate, perform a boundary check + rxConfigParams->Datarate = MIN( datarate, IN865_RX_MAX_DATARATE ); + rxConfigParams->Bandwidth = GetBandwidth( rxConfigParams->Datarate ); + + if( rxConfigParams->Datarate == DR_7 ) + { // FSK + tSymbol = RegionCommonComputeSymbolTimeFsk( DataratesIN865[rxConfigParams->Datarate] ); + } + else + { // LoRa + tSymbol = RegionCommonComputeSymbolTimeLoRa( DataratesIN865[rxConfigParams->Datarate], BandwidthsIN865[rxConfigParams->Datarate] ); + } + + RegionCommonComputeRxWindowParameters( tSymbol, minRxSymbols, rxError, Radio.GetWakeupTime( ), &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); +} + +bool RegionIN865RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) +{ + RadioModems_t modem; + int8_t dr = rxConfig->Datarate; + uint8_t maxPayload = 0; + int8_t phyDr = 0; + uint32_t frequency = rxConfig->Frequency; + const char *slotStrings[] = { "1", "2", "C", "Multi_C", "P", "Multi_P" }; + + if( Radio.GetStatus( ) != RF_IDLE ) + { + return false; + } + + if( rxConfig->RxSlot == RX_SLOT_WIN_1 ) + { + // Apply window 1 frequency + frequency = NvmCtx.Channels[rxConfig->Channel].Frequency; + // Apply the alternative RX 1 window frequency, if it is available + if( NvmCtx.Channels[rxConfig->Channel].Rx1Frequency != 0 ) + { + frequency = NvmCtx.Channels[rxConfig->Channel].Rx1Frequency; + } + } + + // Read the physical datarate from the datarates table + phyDr = DataratesIN865[dr]; + + Radio.SetChannel( frequency ); + + // Radio configuration + if( dr == DR_7 ) + { + modem = MODEM_FSK; + Radio.SetRxConfig( modem, 50000, phyDr * 1000, 0, 83333, 5, rxConfig->WindowTimeout, false, 0, true, 0, 0, false, rxConfig->RxContinuous ); + } + else + { + modem = MODEM_LORA; + Radio.SetRxConfig( modem, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous ); + } + + if( rxConfig->RepeaterSupport == true ) + { + maxPayload = MaxPayloadOfDatarateRepeaterIN865[dr]; + } + else + { + maxPayload = MaxPayloadOfDatarateIN865[dr]; + } + Radio.SetMaxPayloadLength( modem, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD ); + if ( rxConfig->RxSlot < RX_SLOT_NONE ) + { + MW_LOG( "RX_%s on freq %d Hz at DR %d\n\r", slotStrings[rxConfig->RxSlot], frequency, dr ); + } + else + { + MW_LOG( "RX on freq %d Hz at DR %d\n\r", frequency, dr ); + } + + *datarate = (uint8_t) dr; + return true; +} + +bool RegionIN865TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ) +{ + RadioModems_t modem; + int8_t phyDr = DataratesIN865[txConfig->Datarate]; + int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, NvmCtx.Bands[NvmCtx.Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, NvmCtx.ChannelsMask ); + uint32_t bandwidth = GetBandwidth( txConfig->Datarate ); + int8_t phyTxPower = 0; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain ); + + // Setup the radio frequency + Radio.SetChannel( NvmCtx.Channels[txConfig->Channel].Frequency ); + + if( txConfig->Datarate == DR_7 ) + { // High Speed FSK channel + modem = MODEM_FSK; + Radio.SetTxConfig( modem, phyTxPower, 25000, bandwidth, phyDr * 1000, 0, 5, false, true, 0, 0, false, 4000 ); + } + else + { + modem = MODEM_LORA; + Radio.SetTxConfig( modem, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 4000 ); + } + MW_LOG( "TX on freq %d Hz at DR %d\n\r", NvmCtx.Channels[txConfig->Channel].Frequency, txConfig->Datarate ); + + // Setup maximum payload lenght of the radio driver + Radio.SetMaxPayloadLength( modem, txConfig->PktLen ); + // Get the time-on-air of the next tx frame + *txTimeOnAir = Radio.TimeOnAir( modem, txConfig->PktLen ); + + *txPower = txPowerLimited; + return true; +} + +uint8_t RegionIN865LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ) +{ + uint8_t status = 0x07; + RegionCommonLinkAdrParams_t linkAdrParams; + uint8_t nextIndex = 0; + uint8_t bytesProcessed = 0; + uint16_t chMask = 0; + GetPhyParams_t getPhy; + PhyParam_t phyParam; + RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams; + + while( bytesProcessed < linkAdrReq->PayloadSize ) + { + // Get ADR request parameters + nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams ); + + if( nextIndex == 0 ) + break; // break loop, since no more request has been found + + // Update bytes processed + bytesProcessed += nextIndex; + + // Revert status, as we only check the last ADR request for the channel mask KO + status = 0x07; + + // Setup temporary channels mask + chMask = linkAdrParams.ChMask; + + // Verify channels mask + if( ( linkAdrParams.ChMaskCtrl == 0 ) && ( chMask == 0 ) ) + { + status &= 0xFE; // Channel mask KO + } + else if( ( ( linkAdrParams.ChMaskCtrl >= 1 ) && ( linkAdrParams.ChMaskCtrl <= 5 )) || + ( linkAdrParams.ChMaskCtrl >= 7 ) ) + { + // RFU + status &= 0xFE; // Channel mask KO + } + else + { + for( uint8_t i = 0; i < IN865_MAX_NB_CHANNELS; i++ ) + { + if( linkAdrParams.ChMaskCtrl == 6 ) + { + if( NvmCtx.Channels[i].Frequency != 0 ) + { + chMask |= 1 << i; + } + } + else + { + if( ( ( chMask & ( 1 << i ) ) != 0 ) && + ( NvmCtx.Channels[i].Frequency == 0 ) ) + {// Trying to enable an undefined channel + status &= 0xFE; // Channel mask KO + } + } + } + } + } + + if( linkAdrParams.Datarate != DR_6 ) + { + // Get the minimum possible datarate + getPhy.Attribute = PHY_MIN_TX_DR; + getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime; + phyParam = RegionIN865GetPhyParam( &getPhy ); + + linkAdrVerifyParams.Status = status; + linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled; + linkAdrVerifyParams.Datarate = linkAdrParams.Datarate; + linkAdrVerifyParams.TxPower = linkAdrParams.TxPower; + linkAdrVerifyParams.NbRep = linkAdrParams.NbRep; + linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate; + linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower; + linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep; + linkAdrVerifyParams.NbChannels = IN865_MAX_NB_CHANNELS; + linkAdrVerifyParams.ChannelsMask = &chMask; + linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value; + linkAdrVerifyParams.MaxDatarate = IN865_TX_MAX_DATARATE; + linkAdrVerifyParams.Channels = NvmCtx.Channels; + linkAdrVerifyParams.MinTxPower = IN865_MIN_TX_POWER; + linkAdrVerifyParams.MaxTxPower = IN865_MAX_TX_POWER; + linkAdrVerifyParams.Version = linkAdrReq->Version; + + // Verify the parameters and update, if necessary + status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep ); + } + else + {// DR_6 is not supported by this region + status &= 0xFD; // Datarate KO + } + + // Update channelsMask if everything is correct + if( status == 0x07 ) + { + // Set the channels mask to a default value + memset1( ( uint8_t* ) NvmCtx.ChannelsMask, 0, sizeof( NvmCtx.ChannelsMask ) ); + // Update the channels mask + NvmCtx.ChannelsMask[0] = chMask; + } + + // Update status variables + *drOut = linkAdrParams.Datarate; + *txPowOut = linkAdrParams.TxPower; + *nbRepOut = linkAdrParams.NbRep; + *nbBytesParsed = bytesProcessed; + + return status; +} + +uint8_t RegionIN865RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ) +{ + uint8_t status = 0x07; + + // Verify radio frequency + if( VerifyRfFreq( rxParamSetupReq->Frequency ) == false ) + { + status &= 0xFE; // Channel frequency KO + } + + // Verify datarate + if( RegionCommonValueInRange( rxParamSetupReq->Datarate, IN865_RX_MIN_DATARATE, IN865_RX_MAX_DATARATE ) == false ) + { + status &= 0xFD; // Datarate KO + } + + // Verify datarate offset + if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, IN865_MIN_RX1_DR_OFFSET, IN865_MAX_RX1_DR_OFFSET ) == false ) + { + status &= 0xFB; // Rx1DrOffset range KO + } + + return status; +} + +uint8_t RegionIN865NewChannelReq( NewChannelReqParams_t* newChannelReq ) +{ + uint8_t status = 0x03; + ChannelAddParams_t channelAdd; + ChannelRemoveParams_t channelRemove; + + if( newChannelReq->NewChannel->Frequency == 0 ) + { + channelRemove.ChannelId = newChannelReq->ChannelId; + + // Remove + if( RegionIN865ChannelsRemove( &channelRemove ) == false ) + { + status &= 0xFC; + } + } + else + { + channelAdd.NewChannel = newChannelReq->NewChannel; + channelAdd.ChannelId = newChannelReq->ChannelId; + + switch( RegionIN865ChannelAdd( &channelAdd ) ) + { + case LORAMAC_STATUS_OK: + { + break; + } + case LORAMAC_STATUS_FREQUENCY_INVALID: + { + status &= 0xFE; + break; + } + case LORAMAC_STATUS_DATARATE_INVALID: + { + status &= 0xFD; + break; + } + case LORAMAC_STATUS_FREQ_AND_DR_INVALID: + { + status &= 0xFC; + break; + } + default: + { + status &= 0xFC; + break; + } + } + } + + return status; +} + +int8_t RegionIN865TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ) +{ + return -1; +} + +uint8_t RegionIN865DlChannelReq( DlChannelReqParams_t* dlChannelReq ) +{ + uint8_t status = 0x03; + + // Verify if the frequency is supported + if( VerifyRfFreq( dlChannelReq->Rx1Frequency ) == false ) + { + status &= 0xFE; + } + + // Verify if an uplink frequency exists + if( NvmCtx.Channels[dlChannelReq->ChannelId].Frequency == 0 ) + { + status &= 0xFD; + } + + // Apply Rx1 frequency, if the status is OK + if( status == 0x03 ) + { + NvmCtx.Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency; + } + + return status; +} + +int8_t RegionIN865AlternateDr( int8_t currentDr, AlternateDrType_t type ) +{ + return currentDr; +} + +void RegionIN865CalcBackOff( CalcBackOffParams_t* calcBackOff ) +{ + RegionCommonCalcBackOffParams_t calcBackOffParams; + + calcBackOffParams.Channels = NvmCtx.Channels; + calcBackOffParams.Bands = NvmCtx.Bands; + calcBackOffParams.LastTxIsJoinRequest = calcBackOff->LastTxIsJoinRequest; + calcBackOffParams.Joined = calcBackOff->Joined; + calcBackOffParams.DutyCycleEnabled = calcBackOff->DutyCycleEnabled; + calcBackOffParams.Channel = calcBackOff->Channel; + calcBackOffParams.ElapsedTime = calcBackOff->ElapsedTime; + calcBackOffParams.TxTimeOnAir = calcBackOff->TxTimeOnAir; + + RegionCommonCalcBackOff( &calcBackOffParams ); +} + +LoRaMacStatus_t RegionIN865NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t delayTx = 0; + uint8_t enabledChannels[IN865_MAX_NB_CHANNELS] = { 0 }; + TimerTime_t nextTxDelay = 0; + + if( RegionCommonCountChannels( NvmCtx.ChannelsMask, 0, 1 ) == 0 ) + { // Reactivate default channels + NvmCtx.ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); + } + + TimerTime_t elapsed = TimerGetElapsedTime( nextChanParams->LastAggrTx ); + if( ( nextChanParams->LastAggrTx == 0 ) || ( nextChanParams->AggrTimeOff <= elapsed ) ) + { + // Reset Aggregated time off + *aggregatedTimeOff = 0; + + // Update bands Time OFF + nextTxDelay = RegionCommonUpdateBandTimeOff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, NvmCtx.Bands, IN865_MAX_NB_BANDS ); + + // Search how many channels are enabled + nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Joined, nextChanParams->Datarate, + NvmCtx.ChannelsMask, NvmCtx.Channels, + NvmCtx.Bands, enabledChannels, &delayTx ); + } + else + { + delayTx++; + nextTxDelay = nextChanParams->AggrTimeOff - elapsed; + } + + if( nbEnabledChannels > 0 ) + { + // We found a valid channel + *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )]; + + *time = 0; + return LORAMAC_STATUS_OK; + } + else + { + if( delayTx > 0 ) + { + // Delay transmission due to AggregatedTimeOff or to a band time off + *time = nextTxDelay; + return LORAMAC_STATUS_DUTYCYCLE_RESTRICTED; + } + // Datarate not supported by any channel, restore defaults + NvmCtx.ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); + *time = 0; + return LORAMAC_STATUS_NO_CHANNEL_FOUND; + } +} + +LoRaMacStatus_t RegionIN865ChannelAdd( ChannelAddParams_t* channelAdd ) +{ + bool drInvalid = false; + bool freqInvalid = false; + uint8_t id = channelAdd->ChannelId; + + if( id < IN865_NUMB_DEFAULT_CHANNELS ) + { + return LORAMAC_STATUS_FREQ_AND_DR_INVALID; + } + + if( id >= IN865_MAX_NB_CHANNELS ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + // Validate the datarate range + if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Min, IN865_TX_MIN_DATARATE, IN865_TX_MAX_DATARATE ) == false ) + { + drInvalid = true; + } + if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, IN865_TX_MIN_DATARATE, IN865_TX_MAX_DATARATE ) == false ) + { + drInvalid = true; + } + if( channelAdd->NewChannel->DrRange.Fields.Min > channelAdd->NewChannel->DrRange.Fields.Max ) + { + drInvalid = true; + } + + // Check frequency + if( freqInvalid == false ) + { + if( VerifyRfFreq( channelAdd->NewChannel->Frequency ) == false ) + { + freqInvalid = true; + } + } + + // Check status + if( ( drInvalid == true ) && ( freqInvalid == true ) ) + { + return LORAMAC_STATUS_FREQ_AND_DR_INVALID; + } + if( drInvalid == true ) + { + return LORAMAC_STATUS_DATARATE_INVALID; + } + if( freqInvalid == true ) + { + return LORAMAC_STATUS_FREQUENCY_INVALID; + } + + memcpy1( ( uint8_t* ) &(NvmCtx.Channels[id]), ( uint8_t* ) channelAdd->NewChannel, sizeof( NvmCtx.Channels[id] ) ); + NvmCtx.Channels[id].Band = 0; + NvmCtx.ChannelsMask[0] |= ( 1 << id ); + return LORAMAC_STATUS_OK; +} + +bool RegionIN865ChannelsRemove( ChannelRemoveParams_t* channelRemove ) +{ + uint8_t id = channelRemove->ChannelId; + + if( id < IN865_NUMB_DEFAULT_CHANNELS ) + { + return false; + } + + // Remove the channel from the list of channels + NvmCtx.Channels[id] = ( ChannelParams_t ){ 0, 0, { 0 }, 0 }; + + return RegionCommonChanDisable( NvmCtx.ChannelsMask, id, IN865_MAX_NB_CHANNELS ); +} + +void RegionIN865SetContinuousWave( ContinuousWaveParams_t* continuousWave ) +{ + int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, NvmCtx.Bands[NvmCtx.Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, NvmCtx.ChannelsMask ); + int8_t phyTxPower = 0; + uint32_t frequency = NvmCtx.Channels[continuousWave->Channel].Frequency; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, continuousWave->MaxEirp, continuousWave->AntennaGain ); + + Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout ); +} + +uint8_t RegionIN865ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) +{ + // Apply offset formula + return MIN( DR_5, MAX( DR_0, dr - EffectiveRx1DrOffsetIN865[drOffset] ) ); +} + +void RegionIN865RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ) +{ + RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup; + + regionCommonRxBeaconSetup.Datarates = DataratesIN865; + regionCommonRxBeaconSetup.Frequency = rxBeaconSetup->Frequency; + regionCommonRxBeaconSetup.BeaconSize = IN865_BEACON_SIZE; + regionCommonRxBeaconSetup.BeaconDatarate = IN865_BEACON_CHANNEL_DR; + regionCommonRxBeaconSetup.BeaconChannelBW = IN865_BEACON_CHANNEL_BW; + regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime; + regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout; + + RegionCommonRxBeaconSetup( ®ionCommonRxBeaconSetup ); + + // Store downlink datarate + *outDr = IN865_BEACON_CHANNEL_DR; +} diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionIN865.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionIN865.h new file mode 100644 index 00000000..60c871e6 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionIN865.h @@ -0,0 +1,511 @@ +/*! + * \file RegionIN865.h + * + * \brief Region definition for IN865 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + * + * \defgroup REGIONIN865 Region IN865 + * Implementation according to LoRaWAN Specification v1.0.2. + * \{ + */ +#ifndef __REGION_IN865_H__ +#define __REGION_IN865_H__ + +#include "region/Region.h" + +/*! + * LoRaMac maximum number of channels + */ +#define IN865_MAX_NB_CHANNELS 16 + +/*! + * Number of default channels + */ +#define IN865_NUMB_DEFAULT_CHANNELS 3 + +/*! + * Number of channels to apply for the CF list + */ +#define IN865_NUMB_CHANNELS_CF_LIST 5 + +/*! + * Minimal datarate that can be used by the node + */ +#define IN865_TX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define IN865_TX_MAX_DATARATE DR_7 + +/*! + * Minimal datarate that can be used by the node + */ +#define IN865_RX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define IN865_RX_MAX_DATARATE DR_7 + +/*! + * Default datarate used by the node + */ +#define IN865_DEFAULT_DATARATE DR_0 + +/*! + * Minimal Rx1 receive datarate offset + */ +#define IN865_MIN_RX1_DR_OFFSET 0 + +/*! + * Maximal Rx1 receive datarate offset + */ +#define IN865_MAX_RX1_DR_OFFSET 7 + +/*! + * Default Rx1 receive datarate offset + */ +#define IN865_DEFAULT_RX1_DR_OFFSET 0 + +/*! + * Minimal Tx output power that can be used by the node + */ +#define IN865_MIN_TX_POWER TX_POWER_10 + +/*! + * Maximal Tx output power that can be used by the node + */ +#define IN865_MAX_TX_POWER TX_POWER_0 + +/*! + * Default Tx output power used by the node + */ +#define IN865_DEFAULT_TX_POWER TX_POWER_0 + +/*! + * Default Max EIRP + */ +#define IN865_DEFAULT_MAX_EIRP 30.0f + +/*! + * Default antenna gain + */ +#define IN865_DEFAULT_ANTENNA_GAIN 2.15f + +/*! + * ADR Ack limit + */ +#define IN865_ADR_ACK_LIMIT 64 + +/*! + * ADR Ack delay + */ +#define IN865_ADR_ACK_DELAY 32 + +/*! + * Enabled or disabled the duty cycle + */ +#define IN865_DUTY_CYCLE_ENABLED 1 + +/*! + * Maximum RX window duration + */ +#define IN865_MAX_RX_WINDOW 3000 + +/*! + * Receive delay 1 + */ +#define IN865_RECEIVE_DELAY1 1000 + +/*! + * Receive delay 2 + */ +#define IN865_RECEIVE_DELAY2 2000 + +/*! + * Join accept delay 1 + */ +#define IN865_JOIN_ACCEPT_DELAY1 5000 + +/*! + * Join accept delay 2 + */ +#define IN865_JOIN_ACCEPT_DELAY2 6000 + +/*! + * Maximum frame counter gap + */ +#define IN865_MAX_FCNT_GAP 16384 + +/*! + * Ack timeout + */ +#define IN865_ACKTIMEOUT 2000 + +/*! + * Random ack timeout limits + */ +#define IN865_ACK_TIMEOUT_RND 1000 + +#if ( IN865_DEFAULT_DATARATE > DR_5 ) +#error "A default DR higher than DR_5 may lead to connectivity loss." +#endif + +/*! + * Second reception window channel frequency definition. + */ +#define IN865_RX_WND_2_FREQ 866550000 + +/*! + * Second reception window channel datarate definition. + */ +#define IN865_RX_WND_2_DR DR_2 + +/* + * CLASS B + */ +/*! + * Beacon frequency + */ +#define IN865_BEACON_CHANNEL_FREQ 866550000 + +/*! + * Payload size of a beacon frame + */ +#define IN865_BEACON_SIZE 19 + +/*! + * Size of RFU 1 field + */ +#define IN865_RFU1_SIZE 1 + +/*! + * Size of RFU 2 field + */ +#define IN865_RFU2_SIZE 3 + +/*! + * Datarate of the beacon channel + */ +#define IN865_BEACON_CHANNEL_DR DR_4 + +/*! + * Bandwith of the beacon channel + */ +#define IN865_BEACON_CHANNEL_BW 0 + +/*! + * Ping slot channel datarate + */ +#define IN865_PING_SLOT_CHANNEL_DR DR_4 + +/*! + * Maximum number of bands + */ +#define IN865_MAX_NB_BANDS 1 + +/*! + * Band 0 definition + * { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff } + */ +#define IN865_BAND0 { 1 , IN865_MAX_TX_POWER, 0, 0, 0 } // 100.0 % + +/*! + * LoRaMac default channel 1 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define IN865_LC1 { 865062500, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } + +/*! + * LoRaMac default channel 2 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define IN865_LC2 { 865402500, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } + +/*! + * LoRaMac default channel 3 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define IN865_LC3 { 865985000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } + +/*! + * LoRaMac channels which are allowed for the join procedure + */ +#define IN865_JOIN_CHANNELS ( uint16_t )( LC( 1 ) | LC( 2 ) | LC( 3 ) ) + +/*! + * Data rates table definition + */ +static const uint8_t DataratesIN865[] = { 12, 11, 10, 9, 8, 7, 7, 50 }; + +/*! + * Bandwidths table definition in Hz + */ +static const uint32_t BandwidthsIN865[] = { 125000, 125000, 125000, 125000, 125000, 125000, 250000, 0 }; + +/*! + * Maximum payload with respect to the datarate index. Cannot operate with repeater. + */ +static const uint8_t MaxPayloadOfDatarateIN865[] = { 51, 51, 51, 115, 242, 242, 242, 242 }; + +/*! + * Maximum payload with respect to the datarate index. Can operate with repeater. + */ +static const uint8_t MaxPayloadOfDatarateRepeaterIN865[] = { 51, 51, 51, 115, 222, 222, 222, 222 }; + +/*! + * Effective datarate offsets for receive window 1. + */ +static const int8_t EffectiveRx1DrOffsetIN865[] = { 0, 1, 2, 3, 4, 5, -1, -2 }; + +/*! + * \brief The function gets a value of a specific phy attribute. + * + * \param [IN] getPhy Pointer to the function parameters. + * + * \retval Returns a structure containing the PHY parameter. + */ +PhyParam_t RegionIN865GetPhyParam( GetPhyParams_t* getPhy ); + +/*! + * \brief Updates the last TX done parameters of the current channel. + * + * \param [IN] txDone Pointer to the function parameters. + */ +void RegionIN865SetBandTxDone( SetBandTxDoneParams_t* txDone ); + +/*! + * \brief Initializes the channels masks and the channels. + * + * \param [IN] type Sets the initialization type. + */ +void RegionIN865InitDefaults( InitDefaultsParams_t* params ); + +/*! + * \brief Returns a pointer to the internal context and its size. + * + * \param [OUT] params Pointer to the function parameters. + * + * \retval Points to a structure where the module store its non-volatile context. + */ +void* RegionIN865GetNvmCtx( GetNvmCtxParams_t* params ); + +/*! + * \brief Verifies a parameter. + * + * \param [IN] verify Pointer to the function parameters. + * + * \param [IN] type Sets the initialization type. + * + * \retval Returns true, if the parameter is valid. + */ +bool RegionIN865Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ); + +/*! + * \brief The function parses the input buffer and sets up the channels of the + * CF list. + * + * \param [IN] applyCFList Pointer to the function parameters. + */ +void RegionIN865ApplyCFList( ApplyCFListParams_t* applyCFList ); + +/*! + * \brief Sets a channels mask. + * + * \param [IN] chanMaskSet Pointer to the function parameters. + * + * \retval Returns true, if the channels mask could be set. + */ +bool RegionIN865ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ); + +/*! + * Computes the Rx window timeout and offset. + * + * \param [IN] datarate Rx window datarate index to be used + * + * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame. + * + * \param [IN] rxError System maximum timing error of the receiver. In milliseconds + * The receiver will turn on in a [-rxError : +rxError] ms + * interval around RxOffset + * + * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields. + */ +void RegionIN865ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ); + +/*! + * \brief Configuration of the RX windows. + * + * \param [IN] rxConfig Pointer to the function parameters. + * + * \param [OUT] datarate The datarate index which was set. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionIN865RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ); + +/*! + * \brief TX configuration. + * + * \param [IN] txConfig Pointer to the function parameters. + * + * \param [OUT] txPower The tx power index which was set. + * + * \param [OUT] txTimeOnAir The time-on-air of the frame. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionIN865TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ); + +/*! + * \brief The function processes a Link ADR Request. + * + * \param [IN] linkAdrReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionIN865LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ); + +/*! + * \brief The function processes a RX Parameter Setup Request. + * + * \param [IN] rxParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionIN865RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ); + +/*! + * \brief The function processes a Channel Request. + * + * \param [IN] newChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionIN865NewChannelReq( NewChannelReqParams_t* newChannelReq ); + +/*! + * \brief The function processes a TX ParamSetup Request. + * + * \param [IN] txParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + * Returns -1, if the functionality is not implemented. In this case, the end node + * shall not process the command. + */ +int8_t RegionIN865TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ); + +/*! + * \brief The function processes a DlChannel Request. + * + * \param [IN] dlChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionIN865DlChannelReq( DlChannelReqParams_t* dlChannelReq ); + +/*! + * \brief Alternates the datarate of the channel for the join request. + * + * \param [IN] currentDr Current datarate. + * + * \retval Datarate to apply. + */ +int8_t RegionIN865AlternateDr( int8_t currentDr, AlternateDrType_t type ); + +/*! + * \brief Calculates the back-off time. + * + * \param [IN] calcBackOff Pointer to the function parameters. + */ +void RegionIN865CalcBackOff( CalcBackOffParams_t* calcBackOff ); + +/*! + * \brief Searches and set the next random available channel + * + * \param [OUT] channel Next channel to use for TX. + * + * \param [OUT] time Time to wait for the next transmission according to the duty + * cycle. + * + * \param [OUT] aggregatedTimeOff Updates the aggregated time off. + * + * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate] + */ +LoRaMacStatus_t RegionIN865NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ); + +/*! + * \brief Adds a channel. + * + * \param [IN] channelAdd Pointer to the function parameters. + * + * \retval Status of the operation. + */ +LoRaMacStatus_t RegionIN865ChannelAdd( ChannelAddParams_t* channelAdd ); + +/*! + * \brief Removes a channel. + * + * \param [IN] channelRemove Pointer to the function parameters. + * + * \retval Returns true, if the channel was removed successfully. + */ +bool RegionIN865ChannelsRemove( ChannelRemoveParams_t* channelRemove ); + +/*! + * \brief Sets the radio into continuous wave mode. + * + * \param [IN] continuousWave Pointer to the function parameters. + */ +void RegionIN865SetContinuousWave( ContinuousWaveParams_t* continuousWave ); + +/*! + * \brief Computes new datarate according to the given offset + * + * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms + * + * \param [IN] dr Current datarate + * + * \param [IN] drOffset Offset to be applied + * + * \retval newDr Computed datarate. + */ +uint8_t RegionIN865ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ); + +/*! + * \brief Sets the radio into beacon reception mode + * + * \param [IN] rxBeaconSetup Pointer to the function parameters + */ + void RegionIN865RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ); + +/*! \} defgroup REGIONIN865 */ + +#endif // __REGION_IN865_H__ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionKR920.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionKR920.c new file mode 100644 index 00000000..4a2647de --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionKR920.c @@ -0,0 +1,1050 @@ +/*! + * \file RegionKR920.c + * + * \brief Region implementation for KR920 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) +*/ +#include "utilities.h" + +#include "RegionCommon.h" +#include "RegionKR920.h" +#include "lorawan_conf.h" +#include "mw_log_conf.h" + +// Definitions +#define CHANNELS_MASK_SIZE 1 + +/*! + * Region specific context + */ +typedef struct sRegionKR920NvmCtx +{ + /*! + * LoRaMAC channels + */ + ChannelParams_t Channels[ KR920_MAX_NB_CHANNELS ]; + /*! + * LoRaMac bands + */ + Band_t Bands[ KR920_MAX_NB_BANDS ]; + /*! + * LoRaMac channels mask + */ + uint16_t ChannelsMask[ CHANNELS_MASK_SIZE ]; + /*! + * LoRaMac channels default mask + */ + uint16_t ChannelsDefaultMask[ CHANNELS_MASK_SIZE ]; +}RegionKR920NvmCtx_t; + +/* + * Non-volatile module context. + */ +static RegionKR920NvmCtx_t NvmCtx; + +// Static functions +static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr ) +{ + uint8_t nextLowerDr = 0; + + if( dr == minDr ) + { + nextLowerDr = minDr; + } + else + { + nextLowerDr = dr - 1; + } + return nextLowerDr; +} + +static int8_t GetMaxEIRP( uint32_t freq ) +{ + if( freq >= 922100000 ) + {// Limit to 14dBm + return KR920_DEFAULT_MAX_EIRP_HIGH; + } + // Limit to 10dBm + return KR920_DEFAULT_MAX_EIRP_LOW; +} + +static uint32_t GetBandwidth( uint32_t drIndex ) +{ + switch( BandwidthsKR920[drIndex] ) + { + default: + case 125000: + return 0; + case 250000: + return 1; + case 500000: + return 2; + } +} + +static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask ) +{ + int8_t txPowerResult = txPower; + + // Limit tx power to the band max + txPowerResult = MAX( txPower, maxBandTxPower ); + + return txPowerResult; +} + +static bool VerifyRfFreq( uint32_t freq ) +{ + uint32_t tmpFreq = freq; + + // Check radio driver support + if( Radio.CheckRfFrequency( tmpFreq ) == false ) + { + return false; + } + + // Verify if the frequency is valid. The frequency must be in a specified + // range and can be set to specific values. + if( ( tmpFreq >= 920900000 ) && ( tmpFreq <=923300000 ) ) + { + // Range ok, check for specific value + tmpFreq -= 920900000; + if( ( tmpFreq % 200000 ) == 0 ) + { + return true; + } + } + return false; +} + +static uint8_t CountNbOfEnabledChannels( bool joined, uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t delayTransmission = 0; + + for( uint8_t i = 0, k = 0; i < KR920_MAX_NB_CHANNELS; i += 16, k++ ) + { + for( uint8_t j = 0; j < 16; j++ ) + { + if( ( channelsMask[k] & ( 1 << j ) ) != 0 ) + { + if( channels[i + j].Frequency == 0 ) + { // Check if the channel is enabled + continue; + } + if( joined == false ) + { + if( ( KR920_JOIN_CHANNELS & ( 1 << j ) ) == 0 ) + { + continue; + } + } + if( RegionCommonValueInRange( datarate, channels[i + j].DrRange.Fields.Min, + channels[i + j].DrRange.Fields.Max ) == false ) + { // Check if the current channel selection supports the given datarate + continue; + } + if( bands[channels[i + j].Band].TimeOff > 0 ) + { // Check if the band is available for transmission + delayTransmission++; + continue; + } + enabledChannels[nbEnabledChannels++] = i + j; + } + } + } + + *delayTx = delayTransmission; + return nbEnabledChannels; +} + +PhyParam_t RegionKR920GetPhyParam( GetPhyParams_t* getPhy ) +{ + PhyParam_t phyParam = { 0 }; + + switch( getPhy->Attribute ) + { + case PHY_MIN_RX_DR: + { + phyParam.Value = KR920_RX_MIN_DATARATE; + break; + } + case PHY_MIN_TX_DR: + { + phyParam.Value = KR920_TX_MIN_DATARATE; + break; + } + case PHY_DEF_TX_DR: + { + phyParam.Value = KR920_DEFAULT_DATARATE; + break; + } + case PHY_NEXT_LOWER_TX_DR: + { + phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, KR920_TX_MIN_DATARATE ); + break; + } + case PHY_MAX_TX_POWER: + { + phyParam.Value = KR920_MAX_TX_POWER; + break; + } + case PHY_DEF_TX_POWER: + { + phyParam.Value = KR920_DEFAULT_TX_POWER; + break; + } + case PHY_DEF_ADR_ACK_LIMIT: + { + phyParam.Value = KR920_ADR_ACK_LIMIT; + break; + } + case PHY_DEF_ADR_ACK_DELAY: + { + phyParam.Value = KR920_ADR_ACK_DELAY; + break; + } + case PHY_MAX_PAYLOAD: + { + phyParam.Value = MaxPayloadOfDatarateKR920[getPhy->Datarate]; + break; + } + case PHY_MAX_PAYLOAD_REPEATER: + { + phyParam.Value = MaxPayloadOfDatarateRepeaterKR920[getPhy->Datarate]; + break; + } + case PHY_DUTY_CYCLE: + { + phyParam.Value = KR920_DUTY_CYCLE_ENABLED; + break; + } + case PHY_MAX_RX_WINDOW: + { + phyParam.Value = KR920_MAX_RX_WINDOW; + break; + } + case PHY_RECEIVE_DELAY1: + { + phyParam.Value = KR920_RECEIVE_DELAY1; + break; + } + case PHY_RECEIVE_DELAY2: + { + phyParam.Value = KR920_RECEIVE_DELAY2; + break; + } + case PHY_JOIN_ACCEPT_DELAY1: + { + phyParam.Value = KR920_JOIN_ACCEPT_DELAY1; + break; + } + case PHY_JOIN_ACCEPT_DELAY2: + { + phyParam.Value = KR920_JOIN_ACCEPT_DELAY2; + break; + } + case PHY_MAX_FCNT_GAP: + { + phyParam.Value = KR920_MAX_FCNT_GAP; + break; + } + case PHY_ACK_TIMEOUT: + { + phyParam.Value = ( KR920_ACKTIMEOUT + randr( -KR920_ACK_TIMEOUT_RND, KR920_ACK_TIMEOUT_RND ) ); + break; + } + case PHY_DEF_DR1_OFFSET: + { + phyParam.Value = KR920_DEFAULT_RX1_DR_OFFSET; + break; + } + case PHY_DEF_RX2_FREQUENCY: + { + phyParam.Value = KR920_RX_WND_2_FREQ; + break; + } + case PHY_DEF_RX2_DR: + { + phyParam.Value = KR920_RX_WND_2_DR; + break; + } + case PHY_CHANNELS_MASK: + { + phyParam.ChannelsMask = NvmCtx.ChannelsMask; + break; + } + case PHY_CHANNELS_DEFAULT_MASK: + { + phyParam.ChannelsMask = NvmCtx.ChannelsDefaultMask; + break; + } + case PHY_MAX_NB_CHANNELS: + { + phyParam.Value = KR920_MAX_NB_CHANNELS; + break; + } + case PHY_CHANNELS: + { + phyParam.Channels = NvmCtx.Channels; + break; + } + case PHY_DEF_UPLINK_DWELL_TIME: + case PHY_DEF_DOWNLINK_DWELL_TIME: + { + phyParam.Value = 0; + break; + } + case PHY_DEF_MAX_EIRP: + { + // We set the higher maximum EIRP as default value. + // The reason for this is, that the frequency may + // change during a channel selection for the next uplink. + // The value has to be recalculated in the TX configuration. + phyParam.fValue = KR920_DEFAULT_MAX_EIRP_HIGH; + break; + } + case PHY_DEF_ANTENNA_GAIN: + { + phyParam.fValue = KR920_DEFAULT_ANTENNA_GAIN; + break; + } + case PHY_BEACON_CHANNEL_FREQ: + { + phyParam.Value = KR920_BEACON_CHANNEL_FREQ; + break; + } + case PHY_BEACON_FORMAT: + { + phyParam.BeaconFormat.BeaconSize = KR920_BEACON_SIZE; + phyParam.BeaconFormat.Rfu1Size = KR920_RFU1_SIZE; + phyParam.BeaconFormat.Rfu2Size = KR920_RFU2_SIZE; + break; + } + case PHY_BEACON_CHANNEL_DR: + { + phyParam.Value = KR920_BEACON_CHANNEL_DR; + break; + } + case PHY_PING_SLOT_CHANNEL_DR: + { + phyParam.Value = KR920_PING_SLOT_CHANNEL_DR; + break; + } + default: + { + break; + } + } + + return phyParam; +} + +void RegionKR920SetBandTxDone( SetBandTxDoneParams_t* txDone ) +{ + RegionCommonSetBandTxDone( txDone->Joined, &NvmCtx.Bands[NvmCtx.Channels[txDone->Channel].Band], txDone->LastTxDoneTime ); +} + +void RegionKR920InitDefaults( InitDefaultsParams_t* params ) +{ + Band_t bands[KR920_MAX_NB_BANDS] = + { + KR920_BAND0 + }; + + switch( params->Type ) + { + case INIT_TYPE_INIT: + { + // Initialize bands + memcpy1( ( uint8_t* )NvmCtx.Bands, ( uint8_t* )bands, sizeof( Band_t ) * KR920_MAX_NB_BANDS ); + + // Channels + NvmCtx.Channels[0] = ( ChannelParams_t ) KR920_LC1; + NvmCtx.Channels[1] = ( ChannelParams_t ) KR920_LC2; + NvmCtx.Channels[2] = ( ChannelParams_t ) KR920_LC3; + + // Initialize the channels default mask + NvmCtx.ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 ); + // Update the channels mask + RegionCommonChanMaskCopy( NvmCtx.ChannelsMask, NvmCtx.ChannelsDefaultMask, 1 ); + break; + } + case INIT_TYPE_RESTORE_CTX: + { + if( params->NvmCtx != 0 ) + { + memcpy1( (uint8_t*) &NvmCtx, (uint8_t*) params->NvmCtx, sizeof( NvmCtx ) ); + } + break; + } + case INIT_TYPE_RESTORE_DEFAULT_CHANNELS: + { + // Restore channels default mask + NvmCtx.ChannelsMask[0] |= NvmCtx.ChannelsDefaultMask[0]; + + // Channels + NvmCtx.Channels[0] = ( ChannelParams_t ) KR920_LC1; + NvmCtx.Channels[1] = ( ChannelParams_t ) KR920_LC2; + NvmCtx.Channels[2] = ( ChannelParams_t ) KR920_LC3; + break; + } + default: + { + break; + } + } +} + +void* RegionKR920GetNvmCtx( GetNvmCtxParams_t* params ) +{ + params->nvmCtxSize = sizeof( RegionKR920NvmCtx_t ); + return &NvmCtx; +} + +bool RegionKR920Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ) +{ + switch( phyAttribute ) + { + case PHY_FREQUENCY: + { + return VerifyRfFreq( verify->Frequency ); + } + case PHY_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, KR920_TX_MIN_DATARATE, KR920_TX_MAX_DATARATE ); + } + case PHY_DEF_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, DR_0, DR_5 ); + } + case PHY_RX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, KR920_RX_MIN_DATARATE, KR920_RX_MAX_DATARATE ); + } + case PHY_DEF_TX_POWER: + case PHY_TX_POWER: + { + // Remark: switched min and max! + return RegionCommonValueInRange( verify->TxPower, KR920_MAX_TX_POWER, KR920_MIN_TX_POWER ); + } + case PHY_DUTY_CYCLE: + { + return KR920_DUTY_CYCLE_ENABLED; + } + default: + return false; + } +} + +void RegionKR920ApplyCFList( ApplyCFListParams_t* applyCFList ) +{ + ChannelParams_t newChannel; + ChannelAddParams_t channelAdd; + ChannelRemoveParams_t channelRemove; + + // Setup default datarate range + newChannel.DrRange.Value = ( DR_5 << 4 ) | DR_0; + + // Size of the optional CF list + if( applyCFList->Size != 16 ) + { + return; + } + + // Last byte CFListType must be 0 to indicate the CFList contains a list of frequencies + if( applyCFList->Payload[15] != 0 ) + { + return; + } + + // Last byte is RFU, don't take it into account + for( uint8_t i = 0, chanIdx = KR920_NUMB_DEFAULT_CHANNELS; chanIdx < KR920_MAX_NB_CHANNELS; i+=3, chanIdx++ ) + { + if( chanIdx < ( KR920_NUMB_CHANNELS_CF_LIST + KR920_NUMB_DEFAULT_CHANNELS ) ) + { + // Channel frequency + newChannel.Frequency = (uint32_t) applyCFList->Payload[i]; + newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 1] << 8 ); + newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 2] << 16 ); + newChannel.Frequency *= 100; + + // Initialize alternative frequency to 0 + newChannel.Rx1Frequency = 0; + } + else + { + newChannel.Frequency = 0; + newChannel.DrRange.Value = 0; + newChannel.Rx1Frequency = 0; + } + + if( newChannel.Frequency != 0 ) + { + channelAdd.NewChannel = &newChannel; + channelAdd.ChannelId = chanIdx; + + // Try to add all channels + RegionKR920ChannelAdd( &channelAdd ); + } + else + { + channelRemove.ChannelId = chanIdx; + + RegionKR920ChannelsRemove( &channelRemove ); + } + } +} + +bool RegionKR920ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ) +{ + switch( chanMaskSet->ChannelsMaskType ) + { + case CHANNELS_MASK: + { + RegionCommonChanMaskCopy( NvmCtx.ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + case CHANNELS_DEFAULT_MASK: + { + RegionCommonChanMaskCopy( NvmCtx.ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + default: + return false; + } + return true; +} + +void RegionKR920ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ) +{ + double tSymbol = 0.0; + + // Get the datarate, perform a boundary check + rxConfigParams->Datarate = MIN( datarate, KR920_RX_MAX_DATARATE ); + rxConfigParams->Bandwidth = GetBandwidth( rxConfigParams->Datarate ); + + tSymbol = RegionCommonComputeSymbolTimeLoRa( DataratesKR920[rxConfigParams->Datarate], BandwidthsKR920[rxConfigParams->Datarate] ); + + RegionCommonComputeRxWindowParameters( tSymbol, minRxSymbols, rxError, Radio.GetWakeupTime( ), &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); +} + +bool RegionKR920RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) +{ + int8_t dr = rxConfig->Datarate; + uint8_t maxPayload = 0; + int8_t phyDr = 0; + uint32_t frequency = rxConfig->Frequency; + const char *slotStrings[] = { "1", "2", "C", "Multi_C", "P", "Multi_P" }; + + if( Radio.GetStatus( ) != RF_IDLE ) + { + return false; + } + + if( rxConfig->RxSlot == RX_SLOT_WIN_1 ) + { + // Apply window 1 frequency + frequency = NvmCtx.Channels[rxConfig->Channel].Frequency; + // Apply the alternative RX 1 window frequency, if it is available + if( NvmCtx.Channels[rxConfig->Channel].Rx1Frequency != 0 ) + { + frequency = NvmCtx.Channels[rxConfig->Channel].Rx1Frequency; + } + } + + // Read the physical datarate from the datarates table + phyDr = DataratesKR920[dr]; + + Radio.SetChannel( frequency ); + + // Radio configuration + Radio.SetRxConfig( MODEM_LORA, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous ); + maxPayload = MaxPayloadOfDatarateKR920[dr]; + Radio.SetMaxPayloadLength( MODEM_LORA, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD ); + if ( rxConfig->RxSlot < RX_SLOT_NONE ) + { + MW_LOG( "RX_%s on freq %d Hz at DR %d\n\r", slotStrings[rxConfig->RxSlot], frequency, dr ); + } + else + { + MW_LOG( "RX on freq %d Hz at DR %d\n\r", frequency, dr ); + } + + *datarate = (uint8_t) dr; + return true; +} + +bool RegionKR920TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ) +{ + int8_t phyDr = DataratesKR920[txConfig->Datarate]; + int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, NvmCtx.Bands[NvmCtx.Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, NvmCtx.ChannelsMask ); + uint32_t bandwidth = GetBandwidth( txConfig->Datarate ); + float maxEIRP = GetMaxEIRP( NvmCtx.Channels[txConfig->Channel].Frequency ); + int8_t phyTxPower = 0; + + // Take the minimum between the maxEIRP and txConfig->MaxEirp. + // The value of txConfig->MaxEirp could have changed during runtime, e.g. due to a MAC command. + maxEIRP = MIN( txConfig->MaxEirp, maxEIRP ); + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, maxEIRP, txConfig->AntennaGain ); + + // Setup the radio frequency + Radio.SetChannel( NvmCtx.Channels[txConfig->Channel].Frequency ); + + Radio.SetTxConfig( MODEM_LORA, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 4000 ); + MW_LOG( "TX on freq %d Hz at DR %d\n\r", NvmCtx.Channels[txConfig->Channel].Frequency, txConfig->Datarate ); + + // Setup maximum payload lenght of the radio driver + Radio.SetMaxPayloadLength( MODEM_LORA, txConfig->PktLen ); + // Get the time-on-air of the next tx frame + *txTimeOnAir = Radio.TimeOnAir( MODEM_LORA, txConfig->PktLen ); + + *txPower = txPowerLimited; + return true; +} + +uint8_t RegionKR920LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ) +{ + uint8_t status = 0x07; + RegionCommonLinkAdrParams_t linkAdrParams; + uint8_t nextIndex = 0; + uint8_t bytesProcessed = 0; + uint16_t chMask = 0; + GetPhyParams_t getPhy; + PhyParam_t phyParam; + RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams; + + while( bytesProcessed < linkAdrReq->PayloadSize ) + { + // Get ADR request parameters + nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams ); + + if( nextIndex == 0 ) + break; // break loop, since no more request has been found + + // Update bytes processed + bytesProcessed += nextIndex; + + // Revert status, as we only check the last ADR request for the channel mask KO + status = 0x07; + + // Setup temporary channels mask + chMask = linkAdrParams.ChMask; + + // Verify channels mask + if( ( linkAdrParams.ChMaskCtrl == 0 ) && ( chMask == 0 ) ) + { + status &= 0xFE; // Channel mask KO + } + else if( ( ( linkAdrParams.ChMaskCtrl >= 1 ) && ( linkAdrParams.ChMaskCtrl <= 5 )) || + ( linkAdrParams.ChMaskCtrl >= 7 ) ) + { + // RFU + status &= 0xFE; // Channel mask KO + } + else + { + for( uint8_t i = 0; i < KR920_MAX_NB_CHANNELS; i++ ) + { + if( linkAdrParams.ChMaskCtrl == 6 ) + { + if( NvmCtx.Channels[i].Frequency != 0 ) + { + chMask |= 1 << i; + } + } + else + { + if( ( ( chMask & ( 1 << i ) ) != 0 ) && + ( NvmCtx.Channels[i].Frequency == 0 ) ) + {// Trying to enable an undefined channel + status &= 0xFE; // Channel mask KO + } + } + } + } + } + + // Get the minimum possible datarate + getPhy.Attribute = PHY_MIN_TX_DR; + getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime; + phyParam = RegionKR920GetPhyParam( &getPhy ); + + linkAdrVerifyParams.Status = status; + linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled; + linkAdrVerifyParams.Datarate = linkAdrParams.Datarate; + linkAdrVerifyParams.TxPower = linkAdrParams.TxPower; + linkAdrVerifyParams.NbRep = linkAdrParams.NbRep; + linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate; + linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower; + linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep; + linkAdrVerifyParams.NbChannels = KR920_MAX_NB_CHANNELS; + linkAdrVerifyParams.ChannelsMask = &chMask; + linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value; + linkAdrVerifyParams.MaxDatarate = KR920_TX_MAX_DATARATE; + linkAdrVerifyParams.Channels = NvmCtx.Channels; + linkAdrVerifyParams.MinTxPower = KR920_MIN_TX_POWER; + linkAdrVerifyParams.MaxTxPower = KR920_MAX_TX_POWER; + linkAdrVerifyParams.Version = linkAdrReq->Version; + + // Verify the parameters and update, if necessary + status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep ); + + // Update channelsMask if everything is correct + if( status == 0x07 ) + { + // Set the channels mask to a default value + memset1( ( uint8_t* ) NvmCtx.ChannelsMask, 0, sizeof( NvmCtx.ChannelsMask ) ); + // Update the channels mask + NvmCtx.ChannelsMask[0] = chMask; + } + + // Update status variables + *drOut = linkAdrParams.Datarate; + *txPowOut = linkAdrParams.TxPower; + *nbRepOut = linkAdrParams.NbRep; + *nbBytesParsed = bytesProcessed; + + return status; +} + +uint8_t RegionKR920RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ) +{ + uint8_t status = 0x07; + + // Verify radio frequency + if( VerifyRfFreq( rxParamSetupReq->Frequency ) == false ) + { + status &= 0xFE; // Channel frequency KO + } + + // Verify datarate + if( RegionCommonValueInRange( rxParamSetupReq->Datarate, KR920_RX_MIN_DATARATE, KR920_RX_MAX_DATARATE ) == false ) + { + status &= 0xFD; // Datarate KO + } + + // Verify datarate offset + if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, KR920_MIN_RX1_DR_OFFSET, KR920_MAX_RX1_DR_OFFSET ) == false ) + { + status &= 0xFB; // Rx1DrOffset range KO + } + + return status; +} + +uint8_t RegionKR920NewChannelReq( NewChannelReqParams_t* newChannelReq ) +{ + uint8_t status = 0x03; + ChannelAddParams_t channelAdd; + ChannelRemoveParams_t channelRemove; + + if( newChannelReq->NewChannel->Frequency == 0 ) + { + channelRemove.ChannelId = newChannelReq->ChannelId; + + // Remove + if( RegionKR920ChannelsRemove( &channelRemove ) == false ) + { + status &= 0xFC; + } + } + else + { + channelAdd.NewChannel = newChannelReq->NewChannel; + channelAdd.ChannelId = newChannelReq->ChannelId; + + switch( RegionKR920ChannelAdd( &channelAdd ) ) + { + case LORAMAC_STATUS_OK: + { + break; + } + case LORAMAC_STATUS_FREQUENCY_INVALID: + { + status &= 0xFE; + break; + } + case LORAMAC_STATUS_DATARATE_INVALID: + { + status &= 0xFD; + break; + } + case LORAMAC_STATUS_FREQ_AND_DR_INVALID: + { + status &= 0xFC; + break; + } + default: + { + status &= 0xFC; + break; + } + } + } + + return status; +} + +int8_t RegionKR920TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ) +{ + return -1; +} + +uint8_t RegionKR920DlChannelReq( DlChannelReqParams_t* dlChannelReq ) +{ + uint8_t status = 0x03; + + // Verify if the frequency is supported + if( VerifyRfFreq( dlChannelReq->Rx1Frequency ) == false ) + { + status &= 0xFE; + } + + // Verify if an uplink frequency exists + if( NvmCtx.Channels[dlChannelReq->ChannelId].Frequency == 0 ) + { + status &= 0xFD; + } + + // Apply Rx1 frequency, if the status is OK + if( status == 0x03 ) + { + NvmCtx.Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency; + } + + return status; +} + +int8_t RegionKR920AlternateDr( int8_t currentDr, AlternateDrType_t type ) +{ + return currentDr; +} + +void RegionKR920CalcBackOff( CalcBackOffParams_t* calcBackOff ) +{ + RegionCommonCalcBackOffParams_t calcBackOffParams; + + calcBackOffParams.Channels = NvmCtx.Channels; + calcBackOffParams.Bands = NvmCtx.Bands; + calcBackOffParams.LastTxIsJoinRequest = calcBackOff->LastTxIsJoinRequest; + calcBackOffParams.Joined = calcBackOff->Joined; + calcBackOffParams.DutyCycleEnabled = calcBackOff->DutyCycleEnabled; + calcBackOffParams.Channel = calcBackOff->Channel; + calcBackOffParams.ElapsedTime = calcBackOff->ElapsedTime; + calcBackOffParams.TxTimeOnAir = calcBackOff->TxTimeOnAir; + + RegionCommonCalcBackOff( &calcBackOffParams ); +} + +LoRaMacStatus_t RegionKR920NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ) +{ + uint8_t channelNext = 0; + uint8_t nbEnabledChannels = 0; + uint8_t delayTx = 0; + uint8_t enabledChannels[KR920_MAX_NB_CHANNELS] = { 0 }; + TimerTime_t nextTxDelay = 0; + + if( RegionCommonCountChannels( NvmCtx.ChannelsMask, 0, 1 ) == 0 ) + { // Reactivate default channels + NvmCtx.ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); + } + + TimerTime_t elapsed = TimerGetElapsedTime( nextChanParams->LastAggrTx ); + if( ( nextChanParams->LastAggrTx == 0 ) || ( nextChanParams->AggrTimeOff <= elapsed ) ) + { + // Reset Aggregated time off + *aggregatedTimeOff = 0; + + // Update bands Time OFF + nextTxDelay = RegionCommonUpdateBandTimeOff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, NvmCtx.Bands, KR920_MAX_NB_BANDS ); + + // Search how many channels are enabled + nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Joined, nextChanParams->Datarate, + NvmCtx.ChannelsMask, NvmCtx.Channels, + NvmCtx.Bands, enabledChannels, &delayTx ); + } + else + { + delayTx++; + nextTxDelay = nextChanParams->AggrTimeOff - elapsed; + } + + if( nbEnabledChannels > 0 ) + { + for( uint8_t i = 0, j = randr( 0, nbEnabledChannels - 1 ); i < KR920_MAX_NB_CHANNELS; i++ ) + { + channelNext = enabledChannels[j]; + j = ( j + 1 ) % nbEnabledChannels; + + // Perform carrier sense for KR920_CARRIER_SENSE_TIME + // If the channel is free, we can stop the LBT mechanism + if( Radio.IsChannelFree( MODEM_LORA, NvmCtx.Channels[channelNext].Frequency, KR920_RSSI_FREE_TH, KR920_CARRIER_SENSE_TIME ) == true ) + { + // Free channel found + *channel = channelNext; + *time = 0; + return LORAMAC_STATUS_OK; + } + } + return LORAMAC_STATUS_NO_FREE_CHANNEL_FOUND; + } + else + { + if( delayTx > 0 ) + { + // Delay transmission due to AggregatedTimeOff or to a band time off + *time = nextTxDelay; + return LORAMAC_STATUS_DUTYCYCLE_RESTRICTED; + } + // Datarate not supported by any channel, restore defaults + NvmCtx.ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); + *time = 0; + return LORAMAC_STATUS_NO_CHANNEL_FOUND; + } +} + +LoRaMacStatus_t RegionKR920ChannelAdd( ChannelAddParams_t* channelAdd ) +{ + bool drInvalid = false; + bool freqInvalid = false; + uint8_t id = channelAdd->ChannelId; + + if( id < KR920_NUMB_DEFAULT_CHANNELS ) + { + return LORAMAC_STATUS_FREQ_AND_DR_INVALID; + } + + if( id >= KR920_MAX_NB_CHANNELS ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + // Validate the datarate range + if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Min, KR920_TX_MIN_DATARATE, KR920_TX_MAX_DATARATE ) == false ) + { + drInvalid = true; + } + if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, KR920_TX_MIN_DATARATE, KR920_TX_MAX_DATARATE ) == false ) + { + drInvalid = true; + } + if( channelAdd->NewChannel->DrRange.Fields.Min > channelAdd->NewChannel->DrRange.Fields.Max ) + { + drInvalid = true; + } + + // Check frequency + if( freqInvalid == false ) + { + if( VerifyRfFreq( channelAdd->NewChannel->Frequency ) == false ) + { + freqInvalid = true; + } + } + + // Check status + if( ( drInvalid == true ) && ( freqInvalid == true ) ) + { + return LORAMAC_STATUS_FREQ_AND_DR_INVALID; + } + if( drInvalid == true ) + { + return LORAMAC_STATUS_DATARATE_INVALID; + } + if( freqInvalid == true ) + { + return LORAMAC_STATUS_FREQUENCY_INVALID; + } + + memcpy1( ( uint8_t* ) &(NvmCtx.Channels[id]), ( uint8_t* ) channelAdd->NewChannel, sizeof( NvmCtx.Channels[id] ) ); + NvmCtx.Channels[id].Band = 0; + NvmCtx.ChannelsMask[0] |= ( 1 << id ); + return LORAMAC_STATUS_OK; +} + +bool RegionKR920ChannelsRemove( ChannelRemoveParams_t* channelRemove ) +{ + uint8_t id = channelRemove->ChannelId; + + if( id < KR920_NUMB_DEFAULT_CHANNELS ) + { + return false; + } + + // Remove the channel from the list of channels + NvmCtx.Channels[id] = ( ChannelParams_t ){ 0, 0, { 0 }, 0 }; + + return RegionCommonChanDisable( NvmCtx.ChannelsMask, id, KR920_MAX_NB_CHANNELS ); +} + +void RegionKR920SetContinuousWave( ContinuousWaveParams_t* continuousWave ) +{ + int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, NvmCtx.Bands[NvmCtx.Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, NvmCtx.ChannelsMask ); + float maxEIRP = GetMaxEIRP( NvmCtx.Channels[continuousWave->Channel].Frequency ); + int8_t phyTxPower = 0; + uint32_t frequency = NvmCtx.Channels[continuousWave->Channel].Frequency; + + // Take the minimum between the maxEIRP and continuousWave->MaxEirp. + // The value of continuousWave->MaxEirp could have changed during runtime, e.g. due to a MAC command. + maxEIRP = MIN( continuousWave->MaxEirp, maxEIRP ); + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, maxEIRP, continuousWave->AntennaGain ); + + Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout ); +} + +uint8_t RegionKR920ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) +{ + int8_t datarate = dr - drOffset; + + if( datarate < 0 ) + { + datarate = DR_0; + } + return datarate; +} + +void RegionKR920RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ) +{ + RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup; + + regionCommonRxBeaconSetup.Datarates = DataratesKR920; + regionCommonRxBeaconSetup.Frequency = rxBeaconSetup->Frequency; + regionCommonRxBeaconSetup.BeaconSize = KR920_BEACON_SIZE; + regionCommonRxBeaconSetup.BeaconDatarate = KR920_BEACON_CHANNEL_DR; + regionCommonRxBeaconSetup.BeaconChannelBW = KR920_BEACON_CHANNEL_BW; + regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime; + regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout; + + RegionCommonRxBeaconSetup( ®ionCommonRxBeaconSetup ); + + // Store downlink datarate + *outDr = KR920_BEACON_CHANNEL_DR; +} diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionKR920.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionKR920.h new file mode 100644 index 00000000..7f5052c2 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionKR920.h @@ -0,0 +1,521 @@ +/*! + * \file RegionKR920.h + * + * \brief Region definition for KR920 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + * + * \defgroup REGIONKR920 Region KR920 + * Implementation according to LoRaWAN Specification v1.0.2. + * \{ + */ +#ifndef __REGION_KR920_H__ +#define __REGION_KR920_H__ + +#include "region/Region.h" + +/*! + * LoRaMac maximum number of channels + */ +#define KR920_MAX_NB_CHANNELS 16 + +/*! + * Number of default channels + */ +#define KR920_NUMB_DEFAULT_CHANNELS 3 + +/*! + * Number of channels to apply for the CF list + */ +#define KR920_NUMB_CHANNELS_CF_LIST 5 + +/*! + * Minimal datarate that can be used by the node + */ +#define KR920_TX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define KR920_TX_MAX_DATARATE DR_5 + +/*! + * Minimal datarate that can be used by the node + */ +#define KR920_RX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define KR920_RX_MAX_DATARATE DR_5 + +/*! + * Default datarate used by the node + */ +#define KR920_DEFAULT_DATARATE DR_0 + +/*! + * Minimal Rx1 receive datarate offset + */ +#define KR920_MIN_RX1_DR_OFFSET 0 + +/*! + * Maximal Rx1 receive datarate offset + */ +#define KR920_MAX_RX1_DR_OFFSET 5 + +/*! + * Default Rx1 receive datarate offset + */ +#define KR920_DEFAULT_RX1_DR_OFFSET 0 + +/*! + * Minimal Tx output power that can be used by the node + */ +#define KR920_MIN_TX_POWER TX_POWER_7 + +/*! + * Maximal Tx output power that can be used by the node + */ +#define KR920_MAX_TX_POWER TX_POWER_0 + +/*! + * Default Tx output power used by the node + */ +#define KR920_DEFAULT_TX_POWER TX_POWER_0 + +/*! + * Default Max EIRP for frequency 920.9 MHz - 921.9 MHz + */ +#define KR920_DEFAULT_MAX_EIRP_LOW 10.0f + +/*! + * Default Max EIRP for frequency 922.1 MHz - 923.3 MHz + */ +#define KR920_DEFAULT_MAX_EIRP_HIGH 14.0f + +/*! + * Default antenna gain + */ +#define KR920_DEFAULT_ANTENNA_GAIN 2.15f + +/*! + * ADR Ack limit + */ +#define KR920_ADR_ACK_LIMIT 64 + +/*! + * ADR Ack delay + */ +#define KR920_ADR_ACK_DELAY 32 + +/*! + * Enabled or disabled the duty cycle + */ +#define KR920_DUTY_CYCLE_ENABLED 0 + +/*! + * Maximum RX window duration + */ +#define KR920_MAX_RX_WINDOW 4000 + +/*! + * Receive delay 1 + */ +#define KR920_RECEIVE_DELAY1 1000 + +/*! + * Receive delay 2 + */ +#define KR920_RECEIVE_DELAY2 2000 + +/*! + * Join accept delay 1 + */ +#define KR920_JOIN_ACCEPT_DELAY1 5000 + +/*! + * Join accept delay 2 + */ +#define KR920_JOIN_ACCEPT_DELAY2 6000 + +/*! + * Maximum frame counter gap + */ +#define KR920_MAX_FCNT_GAP 16384 + +/*! + * Ack timeout + */ +#define KR920_ACKTIMEOUT 2000 + +/*! + * Random ack timeout limits + */ +#define KR920_ACK_TIMEOUT_RND 1000 + +#if ( KR920_DEFAULT_DATARATE > DR_5 ) +#error "A default DR higher than DR_5 may lead to connectivity loss." +#endif + +/*! + * Second reception window channel frequency definition. + */ +#define KR920_RX_WND_2_FREQ 921900000 + +/*! + * Second reception window channel datarate definition. + */ +#define KR920_RX_WND_2_DR DR_0 + +/* + * CLASS B + */ +/*! + * Beacon frequency + */ +#define KR920_BEACON_CHANNEL_FREQ 923100000 + +/*! + * Payload size of a beacon frame + */ +#define KR920_BEACON_SIZE 17 + +/*! + * Size of RFU 1 field + */ +#define KR920_RFU1_SIZE 2 + +/*! + * Size of RFU 2 field + */ +#define KR920_RFU2_SIZE 0 + +/*! + * Datarate of the beacon channel + */ +#define KR920_BEACON_CHANNEL_DR DR_3 + +/*! + * Bandwith of the beacon channel + */ +#define KR920_BEACON_CHANNEL_BW 0 + +/*! + * Ping slot channel datarate + */ +#define KR920_PING_SLOT_CHANNEL_DR DR_3 + +/*! + * Maximum number of bands + */ +#define KR920_MAX_NB_BANDS 1 + +/*! + * Band 0 definition + * { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff } + */ +#define KR920_BAND0 { 1 , KR920_MAX_TX_POWER, 0, 0, 0 } // 100.0 % + +/*! + * LoRaMac default channel 1 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define KR920_LC1 { 922100000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } + +/*! + * LoRaMac default channel 2 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define KR920_LC2 { 922300000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } + +/*! + * LoRaMac default channel 3 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define KR920_LC3 { 922500000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } + +/*! + * LoRaMac channels which are allowed for the join procedure + */ +#define KR920_JOIN_CHANNELS ( uint16_t )( LC( 1 ) | LC( 2 ) | LC( 3 ) ) + +/*! + * RSSI threshold for a free channel [dBm] + */ +#define KR920_RSSI_FREE_TH -65 + +/*! + * Specifies the time the node performs a carrier sense + */ +#define KR920_CARRIER_SENSE_TIME 6 + +/*! + * Data rates table definition + */ +static const uint8_t DataratesKR920[] = { 12, 11, 10, 9, 8, 7 }; + +/*! + * Bandwidths table definition in Hz + */ +static const uint32_t BandwidthsKR920[] = { 125000, 125000, 125000, 125000, 125000, 125000 }; + +/*! + * Maximum payload with respect to the datarate index. Can operate with and without a repeater. + */ +static const uint8_t MaxPayloadOfDatarateKR920[] = { 51, 51, 51, 115, 242, 242 }; + +/*! + * Maximum payload with respect to the datarate index. Can operate with repeater. + */ +static const uint8_t MaxPayloadOfDatarateRepeaterKR920[] = { 51, 51, 51, 115, 222, 222 }; + +/*! + * \brief The function gets a value of a specific phy attribute. + * + * \param [IN] getPhy Pointer to the function parameters. + * + * \retval Returns a structure containing the PHY parameter. + */ +PhyParam_t RegionKR920GetPhyParam( GetPhyParams_t* getPhy ); + +/*! + * \brief Updates the last TX done parameters of the current channel. + * + * \param [IN] txDone Pointer to the function parameters. + */ +void RegionKR920SetBandTxDone( SetBandTxDoneParams_t* txDone ); + +/*! + * \brief Initializes the channels masks and the channels. + * + * \param [IN] type Sets the initialization type. + */ +void RegionKR920InitDefaults( InitDefaultsParams_t* params ); + +/*! + * \brief Returns a pointer to the internal context and its size. + * + * \param [OUT] params Pointer to the function parameters. + * + * \retval Points to a structure where the module store its non-volatile context. + */ +void* RegionKR920GetNvmCtx( GetNvmCtxParams_t* params ); + +/*! + * \brief Verifies a parameter. + * + * \param [IN] verify Pointer to the function parameters. + * + * \param [IN] type Sets the initialization type. + * + * \retval Returns true, if the parameter is valid. + */ +bool RegionKR920Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ); + +/*! + * \brief The function parses the input buffer and sets up the channels of the + * CF list. + * + * \param [IN] applyCFList Pointer to the function parameters. + */ +void RegionKR920ApplyCFList( ApplyCFListParams_t* applyCFList ); + +/*! + * \brief Sets a channels mask. + * + * \param [IN] chanMaskSet Pointer to the function parameters. + * + * \retval Returns true, if the channels mask could be set. + */ +bool RegionKR920ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ); + +/*! + * Computes the Rx window timeout and offset. + * + * \param [IN] datarate Rx window datarate index to be used + * + * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame. + * + * \param [IN] rxError System maximum timing error of the receiver. In milliseconds + * The receiver will turn on in a [-rxError : +rxError] ms + * interval around RxOffset + * + * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields. + */ +void RegionKR920ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ); + +/*! + * \brief Configuration of the RX windows. + * + * \param [IN] rxConfig Pointer to the function parameters. + * + * \param [OUT] datarate The datarate index which was set. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionKR920RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ); + +/*! + * \brief TX configuration. + * + * \param [IN] txConfig Pointer to the function parameters. + * + * \param [OUT] txPower The tx power index which was set. + * + * \param [OUT] txTimeOnAir The time-on-air of the frame. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionKR920TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ); + +/*! + * \brief The function processes a Link ADR Request. + * + * \param [IN] linkAdrReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionKR920LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ); + +/*! + * \brief The function processes a RX Parameter Setup Request. + * + * \param [IN] rxParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionKR920RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ); + +/*! + * \brief The function processes a Channel Request. + * + * \param [IN] newChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionKR920NewChannelReq( NewChannelReqParams_t* newChannelReq ); + +/*! + * \brief The function processes a TX ParamSetup Request. + * + * \param [IN] txParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + * Returns -1, if the functionality is not implemented. In this case, the end node + * shall not process the command. + */ +int8_t RegionKR920TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ); + +/*! + * \brief The function processes a DlChannel Request. + * + * \param [IN] dlChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionKR920DlChannelReq( DlChannelReqParams_t* dlChannelReq ); + +/*! + * \brief Alternates the datarate of the channel for the join request. + * + * \param [IN] currentDr current datarate. + * + * \retval Datarate to apply. + */ +int8_t RegionKR920AlternateDr( int8_t currentDr, AlternateDrType_t type ); + +/*! + * \brief Calculates the back-off time. + * + * \param [IN] calcBackOff Pointer to the function parameters. + */ +void RegionKR920CalcBackOff( CalcBackOffParams_t* calcBackOff ); + +/*! + * \brief Searches and set the next random available channel + * + * \param [OUT] channel Next channel to use for TX. + * + * \param [OUT] time Time to wait for the next transmission according to the duty + * cycle. + * + * \param [OUT] aggregatedTimeOff Updates the aggregated time off. + * + * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate] + */ +LoRaMacStatus_t RegionKR920NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ); + +/*! + * \brief Adds a channel. + * + * \param [IN] channelAdd Pointer to the function parameters. + * + * \retval Status of the operation. + */ +LoRaMacStatus_t RegionKR920ChannelAdd( ChannelAddParams_t* channelAdd ); + +/*! + * \brief Removes a channel. + * + * \param [IN] channelRemove Pointer to the function parameters. + * + * \retval Returns true, if the channel was removed successfully. + */ +bool RegionKR920ChannelsRemove( ChannelRemoveParams_t* channelRemove ); + +/*! + * \brief Sets the radio into continuous wave mode. + * + * \param [IN] continuousWave Pointer to the function parameters. + */ +void RegionKR920SetContinuousWave( ContinuousWaveParams_t* continuousWave ); + +/*! + * \brief Computes new datarate according to the given offset + * + * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms + * + * \param [IN] dr Current datarate + * + * \param [IN] drOffset Offset to be applied + * + * \retval newDr Computed datarate. + */ +uint8_t RegionKR920ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ); + +/*! + * \brief Sets the radio into beacon reception mode + * + * \param [IN] rxBeaconSetup Pointer to the function parameters + */ + void RegionKR920RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ); + +/*! \} defgroup REGIONKR920 */ + +#endif // __REGION_KR920_H__ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionRU864.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionRU864.c new file mode 100644 index 00000000..4ae5d494 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionRU864.c @@ -0,0 +1,1039 @@ +/*! + * \file RegionRU864.c + * + * \brief Region implementation for RU864 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) +*/ +#include "utilities.h" + +#include "RegionCommon.h" +#include "RegionRU864.h" +#include "lorawan_conf.h" +#include "mw_log_conf.h" + +// Definitions +#define CHANNELS_MASK_SIZE 1 + +/*! + * Region specific context + */ +typedef struct sRegionRU864NvmCtx +{ + /*! + * LoRaMAC channels + */ + ChannelParams_t Channels[ RU864_MAX_NB_CHANNELS ]; + /*! + * LoRaMac bands + */ + Band_t Bands[ RU864_MAX_NB_BANDS ]; + /*! + * LoRaMac channels mask + */ + uint16_t ChannelsMask[ CHANNELS_MASK_SIZE ]; + /*! + * LoRaMac channels default mask + */ + uint16_t ChannelsDefaultMask[ CHANNELS_MASK_SIZE ]; +}RegionRU864NvmCtx_t; + +/* + * Non-volatile module context. + */ +static RegionRU864NvmCtx_t NvmCtx; + +// Static functions +static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr ) +{ + uint8_t nextLowerDr = 0; + + if( dr == minDr ) + { + nextLowerDr = minDr; + } + else + { + nextLowerDr = dr - 1; + } + return nextLowerDr; +} + +static uint32_t GetBandwidth( uint32_t drIndex ) +{ + switch( BandwidthsRU864[drIndex] ) + { + default: + case 125000: + return 0; + case 250000: + return 1; + case 500000: + return 2; + } +} + +static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask ) +{ + int8_t txPowerResult = txPower; + + // Limit tx power to the band max + txPowerResult = MAX( txPower, maxBandTxPower ); + + return txPowerResult; +} + +static bool VerifyRfFreq( uint32_t freq ) +{ + // Check radio driver support + if( Radio.CheckRfFrequency( freq ) == false ) + { + return false; + } + + // Check frequency bands + if( ( freq < 864000000 ) || ( freq > 870000000 ) ) + { + return false; + } + return true; +} + +static uint8_t CountNbOfEnabledChannels( bool joined, uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t delayTransmission = 0; + + for( uint8_t i = 0, k = 0; i < RU864_MAX_NB_CHANNELS; i += 16, k++ ) + { + for( uint8_t j = 0; j < MIN( RU864_MAX_NB_CHANNELS, 16 ); j++ ) + { + if( ( channelsMask[k] & ( 1 << j ) ) != 0 ) + { + if( channels[i + j].Frequency == 0 ) + { // Check if the channel is enabled + continue; + } + if( joined == false ) + { + if( ( RU864_JOIN_CHANNELS & ( 1 << j ) ) == 0 ) + { + continue; + } + } + if( RegionCommonValueInRange( datarate, channels[i + j].DrRange.Fields.Min, + channels[i + j].DrRange.Fields.Max ) == false ) + { // Check if the current channel selection supports the given datarate + continue; + } + if( bands[channels[i + j].Band].TimeOff > 0 ) + { // Check if the band is available for transmission + delayTransmission++; + continue; + } + enabledChannels[nbEnabledChannels++] = i + j; + } + } + } + + *delayTx = delayTransmission; + return nbEnabledChannels; +} + +PhyParam_t RegionRU864GetPhyParam( GetPhyParams_t* getPhy ) +{ + PhyParam_t phyParam = { 0 }; + + switch( getPhy->Attribute ) + { + case PHY_MIN_RX_DR: + { + phyParam.Value = RU864_RX_MIN_DATARATE; + break; + } + case PHY_MIN_TX_DR: + { + phyParam.Value = RU864_TX_MIN_DATARATE; + break; + } + case PHY_DEF_TX_DR: + { + phyParam.Value = RU864_DEFAULT_DATARATE; + break; + } + case PHY_NEXT_LOWER_TX_DR: + { + phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, RU864_TX_MIN_DATARATE ); + break; + } + case PHY_MAX_TX_POWER: + { + phyParam.Value = RU864_MAX_TX_POWER; + break; + } + case PHY_DEF_TX_POWER: + { + phyParam.Value = RU864_DEFAULT_TX_POWER; + break; + } + case PHY_DEF_ADR_ACK_LIMIT: + { + phyParam.Value = RU864_ADR_ACK_LIMIT; + break; + } + case PHY_DEF_ADR_ACK_DELAY: + { + phyParam.Value = RU864_ADR_ACK_DELAY; + break; + } + case PHY_MAX_PAYLOAD: + { + phyParam.Value = MaxPayloadOfDatarateRU864[getPhy->Datarate]; + break; + } + case PHY_MAX_PAYLOAD_REPEATER: + { + phyParam.Value = MaxPayloadOfDatarateRepeaterRU864[getPhy->Datarate]; + break; + } + case PHY_DUTY_CYCLE: + { + phyParam.Value = RU864_DUTY_CYCLE_ENABLED; + break; + } + case PHY_MAX_RX_WINDOW: + { + phyParam.Value = RU864_MAX_RX_WINDOW; + break; + } + case PHY_RECEIVE_DELAY1: + { + phyParam.Value = RU864_RECEIVE_DELAY1; + break; + } + case PHY_RECEIVE_DELAY2: + { + phyParam.Value = RU864_RECEIVE_DELAY2; + break; + } + case PHY_JOIN_ACCEPT_DELAY1: + { + phyParam.Value = RU864_JOIN_ACCEPT_DELAY1; + break; + } + case PHY_JOIN_ACCEPT_DELAY2: + { + phyParam.Value = RU864_JOIN_ACCEPT_DELAY2; + break; + } + case PHY_MAX_FCNT_GAP: + { + phyParam.Value = RU864_MAX_FCNT_GAP; + break; + } + case PHY_ACK_TIMEOUT: + { + phyParam.Value = ( RU864_ACKTIMEOUT + randr( -RU864_ACK_TIMEOUT_RND, RU864_ACK_TIMEOUT_RND ) ); + break; + } + case PHY_DEF_DR1_OFFSET: + { + phyParam.Value = RU864_DEFAULT_RX1_DR_OFFSET; + break; + } + case PHY_DEF_RX2_FREQUENCY: + { + phyParam.Value = RU864_RX_WND_2_FREQ; + break; + } + case PHY_DEF_RX2_DR: + { + phyParam.Value = RU864_RX_WND_2_DR; + break; + } + case PHY_CHANNELS_MASK: + { + phyParam.ChannelsMask = NvmCtx.ChannelsMask; + break; + } + case PHY_CHANNELS_DEFAULT_MASK: + { + phyParam.ChannelsMask = NvmCtx.ChannelsDefaultMask; + break; + } + case PHY_MAX_NB_CHANNELS: + { + phyParam.Value = RU864_MAX_NB_CHANNELS; + break; + } + case PHY_CHANNELS: + { + phyParam.Channels = NvmCtx.Channels; + break; + } + case PHY_DEF_UPLINK_DWELL_TIME: + case PHY_DEF_DOWNLINK_DWELL_TIME: + { + phyParam.Value = 0; + break; + } + case PHY_DEF_MAX_EIRP: + { + phyParam.fValue = RU864_DEFAULT_MAX_EIRP; + break; + } + case PHY_DEF_ANTENNA_GAIN: + { + phyParam.fValue = RU864_DEFAULT_ANTENNA_GAIN; + break; + } + case PHY_BEACON_CHANNEL_FREQ: + { + phyParam.Value = RU864_BEACON_CHANNEL_FREQ; + break; + } + case PHY_BEACON_FORMAT: + { + phyParam.BeaconFormat.BeaconSize = RU864_BEACON_SIZE; + phyParam.BeaconFormat.Rfu1Size = RU864_RFU1_SIZE; + phyParam.BeaconFormat.Rfu2Size = RU864_RFU2_SIZE; + break; + } + case PHY_BEACON_CHANNEL_DR: + { + phyParam.Value = RU864_BEACON_CHANNEL_DR; + break; + } + case PHY_PING_SLOT_CHANNEL_DR: + { + phyParam.Value = RU864_PING_SLOT_CHANNEL_DR; + break; + } + default: + { + break; + } + } + + return phyParam; +} + +void RegionRU864SetBandTxDone( SetBandTxDoneParams_t* txDone ) +{ + RegionCommonSetBandTxDone( txDone->Joined, &NvmCtx.Bands[NvmCtx.Channels[txDone->Channel].Band], txDone->LastTxDoneTime ); +} + +void RegionRU864InitDefaults( InitDefaultsParams_t* params ) +{ + Band_t bands[RU864_MAX_NB_BANDS] = + { + RU864_BAND0 + }; + + switch( params->Type ) + { + case INIT_TYPE_INIT: + { + // Initialize bands + memcpy1( ( uint8_t* )NvmCtx.Bands, ( uint8_t* )bands, sizeof( Band_t ) * RU864_MAX_NB_BANDS ); + + // Channels + NvmCtx.Channels[0] = ( ChannelParams_t ) RU864_LC1; + NvmCtx.Channels[1] = ( ChannelParams_t ) RU864_LC2; + + // Initialize the channels default mask + NvmCtx.ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ); + // Update the channels mask + RegionCommonChanMaskCopy( NvmCtx.ChannelsMask, NvmCtx.ChannelsDefaultMask, 1 ); + break; + } + case INIT_TYPE_RESTORE_CTX: + { + if( params->NvmCtx != 0 ) + { + memcpy1( (uint8_t*) &NvmCtx, (uint8_t*) params->NvmCtx, sizeof( NvmCtx ) ); + } + break; + } + case INIT_TYPE_RESTORE_DEFAULT_CHANNELS: + { + // Restore channels default mask + NvmCtx.ChannelsMask[0] |= NvmCtx.ChannelsDefaultMask[0]; + + // Channels + NvmCtx.Channels[0] = ( ChannelParams_t ) RU864_LC1; + NvmCtx.Channels[1] = ( ChannelParams_t ) RU864_LC2; + break; + } + default: + { + break; + } + } +} + +void* RegionRU864GetNvmCtx( GetNvmCtxParams_t* params ) +{ + params->nvmCtxSize = sizeof( RegionRU864NvmCtx_t ); + return &NvmCtx; +} + +bool RegionRU864Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ) +{ + switch( phyAttribute ) + { + case PHY_FREQUENCY: + { + return VerifyRfFreq( verify->Frequency ); + } + case PHY_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, RU864_TX_MIN_DATARATE, RU864_TX_MAX_DATARATE ); + } + case PHY_DEF_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, DR_0, DR_5 ); + } + case PHY_RX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, RU864_RX_MIN_DATARATE, RU864_RX_MAX_DATARATE ); + } + case PHY_DEF_TX_POWER: + case PHY_TX_POWER: + { + // Remark: switched min and max! + return RegionCommonValueInRange( verify->TxPower, RU864_MAX_TX_POWER, RU864_MIN_TX_POWER ); + } + case PHY_DUTY_CYCLE: + { + return RU864_DUTY_CYCLE_ENABLED; + } + default: + return false; + } +} + +void RegionRU864ApplyCFList( ApplyCFListParams_t* applyCFList ) +{ + ChannelParams_t newChannel; + ChannelAddParams_t channelAdd; + ChannelRemoveParams_t channelRemove; + + // Setup default datarate range + newChannel.DrRange.Value = ( DR_5 << 4 ) | DR_0; + + // Size of the optional CF list + if( applyCFList->Size != 16 ) + { + return; + } + + // Last byte CFListType must be 0 to indicate the CFList contains a list of frequencies + if( applyCFList->Payload[15] != 0 ) + { + return; + } + + // Last byte is RFU, don't take it into account + for( uint8_t i = 0, chanIdx = RU864_NUMB_DEFAULT_CHANNELS; chanIdx < RU864_MAX_NB_CHANNELS; i+=3, chanIdx++ ) + { + if( chanIdx < ( RU864_NUMB_CHANNELS_CF_LIST + RU864_NUMB_DEFAULT_CHANNELS ) ) + { + // Channel frequency + newChannel.Frequency = (uint32_t) applyCFList->Payload[i]; + newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 1] << 8 ); + newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 2] << 16 ); + newChannel.Frequency *= 100; + + // Initialize alternative frequency to 0 + newChannel.Rx1Frequency = 0; + } + else + { + newChannel.Frequency = 0; + newChannel.DrRange.Value = 0; + newChannel.Rx1Frequency = 0; + } + + if( newChannel.Frequency != 0 ) + { + channelAdd.NewChannel = &newChannel; + channelAdd.ChannelId = chanIdx; + + // Try to add all channels + RegionRU864ChannelAdd( &channelAdd ); + } + else + { + channelRemove.ChannelId = chanIdx; + + RegionRU864ChannelsRemove( &channelRemove ); + } + } +} + +bool RegionRU864ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ) +{ + switch( chanMaskSet->ChannelsMaskType ) + { + case CHANNELS_MASK: + { + RegionCommonChanMaskCopy( NvmCtx.ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + case CHANNELS_DEFAULT_MASK: + { + RegionCommonChanMaskCopy( NvmCtx.ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + default: + return false; + } + return true; +} + +void RegionRU864ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ) +{ + double tSymbol = 0.0; + + // Get the datarate, perform a boundary check + rxConfigParams->Datarate = MIN( datarate, RU864_RX_MAX_DATARATE ); + rxConfigParams->Bandwidth = GetBandwidth( rxConfigParams->Datarate ); + + if( rxConfigParams->Datarate == DR_7 ) + { // FSK + tSymbol = RegionCommonComputeSymbolTimeFsk( DataratesRU864[rxConfigParams->Datarate] ); + } + else + { // LoRa + tSymbol = RegionCommonComputeSymbolTimeLoRa( DataratesRU864[rxConfigParams->Datarate], BandwidthsRU864[rxConfigParams->Datarate] ); + } + + RegionCommonComputeRxWindowParameters( tSymbol, minRxSymbols, rxError, Radio.GetWakeupTime( ), &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); +} + +bool RegionRU864RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) +{ + RadioModems_t modem; + int8_t dr = rxConfig->Datarate; + uint8_t maxPayload = 0; + int8_t phyDr = 0; + uint32_t frequency = rxConfig->Frequency; + const char *slotStrings[] = { "1", "2", "C", "Multi_C", "P", "Multi_P" }; + + if( Radio.GetStatus( ) != RF_IDLE ) + { + return false; + } + + if( rxConfig->RxSlot == RX_SLOT_WIN_1 ) + { + // Apply window 1 frequency + frequency = NvmCtx.Channels[rxConfig->Channel].Frequency; + // Apply the alternative RX 1 window frequency, if it is available + if( NvmCtx.Channels[rxConfig->Channel].Rx1Frequency != 0 ) + { + frequency = NvmCtx.Channels[rxConfig->Channel].Rx1Frequency; + } + } + + // Read the physical datarate from the datarates table + phyDr = DataratesRU864[dr]; + + Radio.SetChannel( frequency ); + + // Radio configuration + if( dr == DR_7 ) + { + modem = MODEM_FSK; + Radio.SetRxConfig( modem, 50000, phyDr * 1000, 0, 83333, 5, rxConfig->WindowTimeout, false, 0, true, 0, 0, false, rxConfig->RxContinuous ); + } + else + { + modem = MODEM_LORA; + Radio.SetRxConfig( modem, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous ); + } + + if( rxConfig->RepeaterSupport == true ) + { + maxPayload = MaxPayloadOfDatarateRepeaterRU864[dr]; + } + else + { + maxPayload = MaxPayloadOfDatarateRU864[dr]; + } + + Radio.SetMaxPayloadLength( modem, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD ); + if ( rxConfig->RxSlot < RX_SLOT_NONE ) + { + MW_LOG( "RX_%s on freq %d Hz at DR %d\n\r", slotStrings[rxConfig->RxSlot], frequency, dr ); + } + else + { + MW_LOG( "RX on freq %d Hz at DR %d\n\r", frequency, dr ); + } + + *datarate = (uint8_t) dr; + return true; +} + +bool RegionRU864TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ) +{ + RadioModems_t modem; + int8_t phyDr = DataratesRU864[txConfig->Datarate]; + int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, NvmCtx.Bands[NvmCtx.Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, NvmCtx.ChannelsMask ); + uint32_t bandwidth = GetBandwidth( txConfig->Datarate ); + int8_t phyTxPower = 0; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain ); + + // Setup the radio frequency + Radio.SetChannel( NvmCtx.Channels[txConfig->Channel].Frequency ); + + if( txConfig->Datarate == DR_7 ) + { // High Speed FSK channel + modem = MODEM_FSK; + Radio.SetTxConfig( modem, phyTxPower, 25000, bandwidth, phyDr * 1000, 0, 5, false, true, 0, 0, false, 4000 ); + } + else + { + modem = MODEM_LORA; + Radio.SetTxConfig( modem, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 4000 ); + } + MW_LOG( "TX on freq %d Hz at DR %d\n\r", NvmCtx.Channels[txConfig->Channel].Frequency, txConfig->Datarate ); + // Setup maximum payload lenght of the radio driver + Radio.SetMaxPayloadLength( modem, txConfig->PktLen ); + // Get the time-on-air of the next tx frame + *txTimeOnAir = Radio.TimeOnAir( modem, txConfig->PktLen ); + + *txPower = txPowerLimited; + return true; +} + +uint8_t RegionRU864LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ) +{ + uint8_t status = 0x07; + RegionCommonLinkAdrParams_t linkAdrParams; + uint8_t nextIndex = 0; + uint8_t bytesProcessed = 0; + uint16_t chMask = 0; + GetPhyParams_t getPhy; + PhyParam_t phyParam; + RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams; + + while( bytesProcessed < linkAdrReq->PayloadSize ) + { + // Get ADR request parameters + nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams ); + + if( nextIndex == 0 ) + break; // break loop, since no more request has been found + + // Update bytes processed + bytesProcessed += nextIndex; + + // Revert status, as we only check the last ADR request for the channel mask KO + status = 0x07; + + // Setup temporary channels mask + chMask = linkAdrParams.ChMask; + + // Verify channels mask + if( ( linkAdrParams.ChMaskCtrl == 0 ) && ( chMask == 0 ) ) + { + status &= 0xFE; // Channel mask KO + } + else if( ( ( linkAdrParams.ChMaskCtrl >= 1 ) && ( linkAdrParams.ChMaskCtrl <= 5 )) || + ( linkAdrParams.ChMaskCtrl >= 7 ) ) + { + // RFU + status &= 0xFE; // Channel mask KO + } + else + { + for( uint8_t i = 0; i < RU864_MAX_NB_CHANNELS; i++ ) + { + if( linkAdrParams.ChMaskCtrl == 6 ) + { + if( NvmCtx.Channels[i].Frequency != 0 ) + { + chMask |= 1 << i; + } + } + else + { + if( ( ( chMask & ( 1 << i ) ) != 0 ) && + ( NvmCtx.Channels[i].Frequency == 0 ) ) + {// Trying to enable an undefined channel + status &= 0xFE; // Channel mask KO + } + } + } + } + } + + // Get the minimum possible datarate + getPhy.Attribute = PHY_MIN_TX_DR; + getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime; + phyParam = RegionRU864GetPhyParam( &getPhy ); + + linkAdrVerifyParams.Status = status; + linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled; + linkAdrVerifyParams.Datarate = linkAdrParams.Datarate; + linkAdrVerifyParams.TxPower = linkAdrParams.TxPower; + linkAdrVerifyParams.NbRep = linkAdrParams.NbRep; + linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate; + linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower; + linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep; + linkAdrVerifyParams.NbChannels = RU864_MAX_NB_CHANNELS; + linkAdrVerifyParams.ChannelsMask = &chMask; + linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value; + linkAdrVerifyParams.MaxDatarate = RU864_TX_MAX_DATARATE; + linkAdrVerifyParams.Channels = NvmCtx.Channels; + linkAdrVerifyParams.MinTxPower = RU864_MIN_TX_POWER; + linkAdrVerifyParams.MaxTxPower = RU864_MAX_TX_POWER; + linkAdrVerifyParams.Version = linkAdrReq->Version; + + // Verify the parameters and update, if necessary + status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep ); + + // Update channelsMask if everything is correct + if( status == 0x07 ) + { + // Set the channels mask to a default value + memset1( ( uint8_t* ) NvmCtx.ChannelsMask, 0, sizeof( NvmCtx.ChannelsMask ) ); + // Update the channels mask + NvmCtx.ChannelsMask[0] = chMask; + } + + // Update status variables + *drOut = linkAdrParams.Datarate; + *txPowOut = linkAdrParams.TxPower; + *nbRepOut = linkAdrParams.NbRep; + *nbBytesParsed = bytesProcessed; + + return status; +} + +uint8_t RegionRU864RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ) +{ + uint8_t status = 0x07; + + // Verify radio frequency + if( VerifyRfFreq( rxParamSetupReq->Frequency ) == false ) + { + status &= 0xFE; // Channel frequency KO + } + + // Verify datarate + if( RegionCommonValueInRange( rxParamSetupReq->Datarate, RU864_RX_MIN_DATARATE, RU864_RX_MAX_DATARATE ) == false ) + { + status &= 0xFD; // Datarate KO + } + + // Verify datarate offset + if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, RU864_MIN_RX1_DR_OFFSET, RU864_MAX_RX1_DR_OFFSET ) == false ) + { + status &= 0xFB; // Rx1DrOffset range KO + } + + return status; +} + +uint8_t RegionRU864NewChannelReq( NewChannelReqParams_t* newChannelReq ) +{ + uint8_t status = 0x03; + ChannelAddParams_t channelAdd; + ChannelRemoveParams_t channelRemove; + + if( newChannelReq->NewChannel->Frequency == 0 ) + { + channelRemove.ChannelId = newChannelReq->ChannelId; + + // Remove + if( RegionRU864ChannelsRemove( &channelRemove ) == false ) + { + status &= 0xFC; + } + } + else + { + channelAdd.NewChannel = newChannelReq->NewChannel; + channelAdd.ChannelId = newChannelReq->ChannelId; + + switch( RegionRU864ChannelAdd( &channelAdd ) ) + { + case LORAMAC_STATUS_OK: + { + break; + } + case LORAMAC_STATUS_FREQUENCY_INVALID: + { + status &= 0xFE; + break; + } + case LORAMAC_STATUS_DATARATE_INVALID: + { + status &= 0xFD; + break; + } + case LORAMAC_STATUS_FREQ_AND_DR_INVALID: + { + status &= 0xFC; + break; + } + default: + { + status &= 0xFC; + break; + } + } + } + + return status; +} + +int8_t RegionRU864TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ) +{ + return -1; +} + +uint8_t RegionRU864DlChannelReq( DlChannelReqParams_t* dlChannelReq ) +{ + uint8_t status = 0x03; + + // Verify if the frequency is supported + if( VerifyRfFreq( dlChannelReq->Rx1Frequency ) == false ) + { + status &= 0xFE; + } + + // Verify if an uplink frequency exists + if( NvmCtx.Channels[dlChannelReq->ChannelId].Frequency == 0 ) + { + status &= 0xFD; + } + + // Apply Rx1 frequency, if the status is OK + if( status == 0x03 ) + { + NvmCtx.Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency; + } + + return status; +} + +int8_t RegionRU864AlternateDr( int8_t currentDr, AlternateDrType_t type ) +{ + return currentDr; +} + +void RegionRU864CalcBackOff( CalcBackOffParams_t* calcBackOff ) +{ + RegionCommonCalcBackOffParams_t calcBackOffParams; + + calcBackOffParams.Channels = NvmCtx.Channels; + calcBackOffParams.Bands = NvmCtx.Bands; + calcBackOffParams.LastTxIsJoinRequest = calcBackOff->LastTxIsJoinRequest; + calcBackOffParams.Joined = calcBackOff->Joined; + calcBackOffParams.DutyCycleEnabled = calcBackOff->DutyCycleEnabled; + calcBackOffParams.Channel = calcBackOff->Channel; + calcBackOffParams.ElapsedTime = calcBackOff->ElapsedTime; + calcBackOffParams.TxTimeOnAir = calcBackOff->TxTimeOnAir; + + RegionCommonCalcBackOff( &calcBackOffParams ); +} + +LoRaMacStatus_t RegionRU864NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t delayTx = 0; + uint8_t enabledChannels[RU864_MAX_NB_CHANNELS] = { 0 }; + TimerTime_t nextTxDelay = 0; + + if( RegionCommonCountChannels( NvmCtx.ChannelsMask, 0, 1 ) == 0 ) + { // Reactivate default channels + NvmCtx.ChannelsMask[0] |= LC( 1 ) + LC( 2 ); + } + + TimerTime_t elapsed = TimerGetElapsedTime( nextChanParams->LastAggrTx ); + if( ( nextChanParams->LastAggrTx == 0 ) || ( nextChanParams->AggrTimeOff <= elapsed ) ) + { + // Reset Aggregated time off + *aggregatedTimeOff = 0; + + // Update bands Time OFF + nextTxDelay = RegionCommonUpdateBandTimeOff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, NvmCtx.Bands, RU864_MAX_NB_BANDS ); + + // Search how many channels are enabled + nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Joined, nextChanParams->Datarate, + NvmCtx.ChannelsMask, NvmCtx.Channels, + NvmCtx.Bands, enabledChannels, &delayTx ); + } + else + { + delayTx++; + nextTxDelay = nextChanParams->AggrTimeOff - elapsed; + } + + if( nbEnabledChannels > 0 ) + { + // We found a valid channel + *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )]; + + *time = 0; + return LORAMAC_STATUS_OK; + } + else + { + if( delayTx > 0 ) + { + // Delay transmission due to AggregatedTimeOff or to a band time off + *time = nextTxDelay; + return LORAMAC_STATUS_DUTYCYCLE_RESTRICTED; + } + // Datarate not supported by any channel, restore defaults + NvmCtx.ChannelsMask[0] |= LC( 1 ) + LC( 2 ); + *time = 0; + return LORAMAC_STATUS_NO_CHANNEL_FOUND; + } +} + +LoRaMacStatus_t RegionRU864ChannelAdd( ChannelAddParams_t* channelAdd ) +{ + bool drInvalid = false; + bool freqInvalid = false; + uint8_t id = channelAdd->ChannelId; + + if( id < RU864_NUMB_DEFAULT_CHANNELS ) + { + return LORAMAC_STATUS_FREQ_AND_DR_INVALID; + } + + if( id >= RU864_MAX_NB_CHANNELS ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + // Validate the datarate range + if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Min, RU864_TX_MIN_DATARATE, RU864_TX_MAX_DATARATE ) == false ) + { + drInvalid = true; + } + if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, RU864_TX_MIN_DATARATE, RU864_TX_MAX_DATARATE ) == false ) + { + drInvalid = true; + } + if( channelAdd->NewChannel->DrRange.Fields.Min > channelAdd->NewChannel->DrRange.Fields.Max ) + { + drInvalid = true; + } + + // Check frequency + if( freqInvalid == false ) + { + if( VerifyRfFreq( channelAdd->NewChannel->Frequency ) == false ) + { + freqInvalid = true; + } + } + + // Check status + if( ( drInvalid == true ) && ( freqInvalid == true ) ) + { + return LORAMAC_STATUS_FREQ_AND_DR_INVALID; + } + if( drInvalid == true ) + { + return LORAMAC_STATUS_DATARATE_INVALID; + } + if( freqInvalid == true ) + { + return LORAMAC_STATUS_FREQUENCY_INVALID; + } + + memcpy1( ( uint8_t* ) &(NvmCtx.Channels[id]), ( uint8_t* ) channelAdd->NewChannel, sizeof( NvmCtx.Channels[id] ) ); + NvmCtx.Channels[id].Band = 0; + NvmCtx.ChannelsMask[0] |= ( 1 << id ); + return LORAMAC_STATUS_OK; +} + +bool RegionRU864ChannelsRemove( ChannelRemoveParams_t* channelRemove ) +{ + uint8_t id = channelRemove->ChannelId; + + if( id < RU864_NUMB_DEFAULT_CHANNELS ) + { + return false; + } + + // Remove the channel from the list of channels + NvmCtx.Channels[id] = ( ChannelParams_t ){ 0, 0, { 0 }, 0 }; + + return RegionCommonChanDisable( NvmCtx.ChannelsMask, id, RU864_MAX_NB_CHANNELS ); +} + +void RegionRU864SetContinuousWave( ContinuousWaveParams_t* continuousWave ) +{ + int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, NvmCtx.Bands[NvmCtx.Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, NvmCtx.ChannelsMask ); + int8_t phyTxPower = 0; + uint32_t frequency = NvmCtx.Channels[continuousWave->Channel].Frequency; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, continuousWave->MaxEirp, continuousWave->AntennaGain ); + + Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout ); +} + +uint8_t RegionRU864ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) +{ + int8_t datarate = dr - drOffset; + + if( datarate < 0 ) + { + datarate = DR_0; + } + return datarate; +} + +void RegionRU864RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ) +{ + RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup; + + regionCommonRxBeaconSetup.Datarates = DataratesRU864; + regionCommonRxBeaconSetup.Frequency = rxBeaconSetup->Frequency; + regionCommonRxBeaconSetup.BeaconSize = RU864_BEACON_SIZE; + regionCommonRxBeaconSetup.BeaconDatarate = RU864_BEACON_CHANNEL_DR; + regionCommonRxBeaconSetup.BeaconChannelBW = RU864_BEACON_CHANNEL_BW; + regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime; + regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout; + + RegionCommonRxBeaconSetup( ®ionCommonRxBeaconSetup ); + + // Store downlink datarate + *outDr = RU864_BEACON_CHANNEL_DR; +} diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionRU864.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionRU864.h new file mode 100644 index 00000000..bfd6fe3f --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionRU864.h @@ -0,0 +1,500 @@ +/*! + * \file RegionRU864.h + * + * \brief Region definition for RU864 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \defgroup REGIONRU864 Region RU864 + * Implementation according to LoRaWAN Specification v1.0.2. + * \{ + */ +#ifndef __REGION_RU864_H__ +#define __REGION_RU864_H__ + +#include "LoRaMac.h" + +/*! + * LoRaMac maximum number of channels + */ +#define RU864_MAX_NB_CHANNELS 8 + +/*! + * Number of default channels + */ +#define RU864_NUMB_DEFAULT_CHANNELS 2 + +/*! + * Number of channels to apply for the CF list + */ +#define RU864_NUMB_CHANNELS_CF_LIST 5 + +/*! + * Minimal datarate that can be used by the node + */ +#define RU864_TX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define RU864_TX_MAX_DATARATE DR_7 + +/*! + * Minimal datarate that can be used by the node + */ +#define RU864_RX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define RU864_RX_MAX_DATARATE DR_7 + +/*! + * Default datarate used by the node + */ +#define RU864_DEFAULT_DATARATE DR_0 + +/*! + * Minimal Rx1 receive datarate offset + */ +#define RU864_MIN_RX1_DR_OFFSET 0 + +/*! + * Maximal Rx1 receive datarate offset + */ +#define RU864_MAX_RX1_DR_OFFSET 5 + +/*! + * Default Rx1 receive datarate offset + */ +#define RU864_DEFAULT_RX1_DR_OFFSET 0 + +/*! + * Minimal Tx output power that can be used by the node + */ +#define RU864_MIN_TX_POWER TX_POWER_7 + +/*! + * Maximal Tx output power that can be used by the node + */ +#define RU864_MAX_TX_POWER TX_POWER_0 + +/*! + * Default Tx output power used by the node + */ +#define RU864_DEFAULT_TX_POWER TX_POWER_0 + +/*! + * Default Max EIRP + */ +#define RU864_DEFAULT_MAX_EIRP 16.0f + +/*! + * Default antenna gain + */ +#define RU864_DEFAULT_ANTENNA_GAIN 2.15f + +/*! + * ADR Ack limit + */ +#define RU864_ADR_ACK_LIMIT 64 + +/*! + * ADR Ack delay + */ +#define RU864_ADR_ACK_DELAY 32 + +/*! + * Enabled or disabled the duty cycle + */ +#define RU864_DUTY_CYCLE_ENABLED 1 + +/*! + * Maximum RX window duration + */ +#define RU864_MAX_RX_WINDOW 3000 //TODO + +/*! + * Receive delay 1 + */ +#define RU864_RECEIVE_DELAY1 1000 + +/*! + * Receive delay 2 + */ +#define RU864_RECEIVE_DELAY2 2000 + +/*! + * Join accept delay 1 + */ +#define RU864_JOIN_ACCEPT_DELAY1 5000 + +/*! + * Join accept delay 2 + */ +#define RU864_JOIN_ACCEPT_DELAY2 6000 + +/*! + * Maximum frame counter gap + */ +#define RU864_MAX_FCNT_GAP 16384 + +/*! + * Ack timeout + */ +#define RU864_ACKTIMEOUT 2000 + +/*! + * Random ack timeout limits + */ +#define RU864_ACK_TIMEOUT_RND 1000 + +#if ( RU864_DEFAULT_DATARATE > DR_5 ) +#error "A default DR higher than DR_5 may lead to connectivity loss." +#endif + +/*! + * Second reception window channel frequency definition. + */ +#define RU864_RX_WND_2_FREQ 869100000 + +/*! + * Second reception window channel datarate definition. + */ +#define RU864_RX_WND_2_DR DR_0 + +/* + * CLASS B + */ +/*! + * Beacon frequency + */ +#define RU864_BEACON_CHANNEL_FREQ 869100000 + +/*! + * Payload size of a beacon frame + */ +#define RU864_BEACON_SIZE 17 + +/*! + * Size of RFU 1 field + */ +#define RU864_RFU1_SIZE 2 + +/*! + * Size of RFU 2 field + */ +#define RU864_RFU2_SIZE 0 + +/*! + * Datarate of the beacon channel + */ +#define RU864_BEACON_CHANNEL_DR DR_3 + +/*! + * Bandwith of the beacon channel (Index of BandwidthsRU864[]) + */ +#define RU864_BEACON_CHANNEL_BW 0 + +/*! + * Ping slot channel datarate + */ +#define RU864_PING_SLOT_CHANNEL_DR DR_3 + + +/*! + * Maximum number of bands + */ +#define RU864_MAX_NB_BANDS 1 + +/*! + * Band 0 definition + * { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff } + */ +#define RU864_BAND0 { 100 , RU864_MAX_TX_POWER, 0, 0, 0 } // 1.0 % + +/*! + * LoRaMac default channel 1 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define RU864_LC1 { 868900000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } + +/*! + * LoRaMac default channel 2 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define RU864_LC2 { 869100000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } + + +/*! + * LoRaMac channels which are allowed for the join procedure + */ +#define RU864_JOIN_CHANNELS ( uint16_t )( LC( 1 ) | LC( 2 ) ) + +/*! + * Data rates table definition + */ +static const uint8_t DataratesRU864[] = { 12, 11, 10, 9, 8, 7, 7, 50 }; + +/*! + * Bandwidths table definition in Hz + */ +static const uint32_t BandwidthsRU864[] = { 125000, 125000, 125000, 125000, 125000, 125000, 250000, 0 }; + +/*! + * Maximum payload with respect to the datarate index. Cannot operate with repeater. + */ +static const uint8_t MaxPayloadOfDatarateRU864[] = { 51, 51, 51, 115, 242, 242, 242, 242 }; + +/*! + * Maximum payload with respect to the datarate index. Can operate with repeater. + */ +static const uint8_t MaxPayloadOfDatarateRepeaterRU864[] = { 51, 51, 51, 115, 222, 222, 222, 222 }; + +/*! + * \brief The function gets a value of a specific phy attribute. + * + * \param [IN] getPhy Pointer to the function parameters. + * + * \retval Returns a structure containing the PHY parameter. + */ +PhyParam_t RegionRU864GetPhyParam( GetPhyParams_t* getPhy ); + +/*! + * \brief Updates the last TX done parameters of the current channel. + * + * \param [IN] txDone Pointer to the function parameters. + */ +void RegionRU864SetBandTxDone( SetBandTxDoneParams_t* txDone ); + +/*! + * \brief Initializes the channels masks and the channels. + * + * \param [IN] type Sets the initialization type. + */ +void RegionRU864InitDefaults( InitDefaultsParams_t* params ); + +/*! + * \brief Returns a pointer to the internal context and its size. + * + * \param [OUT] params Pointer to the function parameters. + * + * \retval Points to a structure where the module store its non-volatile context. + */ +void* RegionRU864GetNvmCtx( GetNvmCtxParams_t* params ); + +/*! + * \brief Verifies a parameter. + * + * \param [IN] verify Pointer to the function parameters. + * + * \param [IN] type Sets the initialization type. + * + * \retval Returns true, if the parameter is valid. + */ +bool RegionRU864Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ); + +/*! + * \brief The function parses the input buffer and sets up the channels of the + * CF list. + * + * \param [IN] applyCFList Pointer to the function parameters. + */ +void RegionRU864ApplyCFList( ApplyCFListParams_t* applyCFList ); + +/*! + * \brief Sets a channels mask. + * + * \param [IN] chanMaskSet Pointer to the function parameters. + * + * \retval Returns true, if the channels mask could be set. + */ +bool RegionRU864ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ); + +/*! + * Computes the Rx window timeout and offset. + * + * \param [IN] datarate Rx window datarate index to be used + * + * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame. + * + * \param [IN] rxError System maximum timing error of the receiver. In milliseconds + * The receiver will turn on in a [-rxError : +rxError] ms + * interval around RxOffset + * + * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields. + */ +void RegionRU864ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ); + +/*! + * \brief Configuration of the RX windows. + * + * \param [IN] rxConfig Pointer to the function parameters. + * + * \param [OUT] datarate The datarate index which was set. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionRU864RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ); + +/*! + * \brief TX configuration. + * + * \param [IN] txConfig Pointer to the function parameters. + * + * \param [OUT] txPower The tx power index which was set. + * + * \param [OUT] txTimeOnAir The time-on-air of the frame. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionRU864TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ); + +/*! + * \brief The function processes a Link ADR Request. + * + * \param [IN] linkAdrReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionRU864LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ); + +/*! + * \brief The function processes a RX Parameter Setup Request. + * + * \param [IN] rxParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionRU864RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ); + +/*! + * \brief The function processes a Channel Request. + * + * \param [IN] newChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionRU864NewChannelReq( NewChannelReqParams_t* newChannelReq ); + +/*! + * \brief The function processes a TX ParamSetup Request. + * + * \param [IN] txParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + * Returns -1, if the functionality is not implemented. In this case, the end node + * shall not process the command. + */ +int8_t RegionRU864TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ); + +/*! + * \brief The function processes a DlChannel Request. + * + * \param [IN] dlChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionRU864DlChannelReq( DlChannelReqParams_t* dlChannelReq ); + +/*! + * \brief Alternates the datarate of the channel for the join request. + * + * \param [IN] currentDr Current datarate. + * + * \retval Datarate to apply. + */ +int8_t RegionRU864AlternateDr( int8_t currentDr, AlternateDrType_t type ); + +/*! + * \brief Calculates the back-off time. + * + * \param [IN] calcBackOff Pointer to the function parameters. + */ +void RegionRU864CalcBackOff( CalcBackOffParams_t* calcBackOff ); + +/*! + * \brief Searches and set the next random available channel + * + * \param [OUT] channel Next channel to use for TX. + * + * \param [OUT] time Time to wait for the next transmission according to the duty + * cycle. + * + * \param [OUT] aggregatedTimeOff Updates the aggregated time off. + * + * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate] + */ +LoRaMacStatus_t RegionRU864NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ); + +/*! + * \brief Adds a channel. + * + * \param [IN] channelAdd Pointer to the function parameters. + * + * \retval Status of the operation. + */ +LoRaMacStatus_t RegionRU864ChannelAdd( ChannelAddParams_t* channelAdd ); + +/*! + * \brief Removes a channel. + * + * \param [IN] channelRemove Pointer to the function parameters. + * + * \retval Returns true, if the channel was removed successfully. + */ +bool RegionRU864ChannelsRemove( ChannelRemoveParams_t* channelRemove ); + +/*! + * \brief Sets the radio into continuous wave mode. + * + * \param [IN] continuousWave Pointer to the function parameters. + */ +void RegionRU864SetContinuousWave( ContinuousWaveParams_t* continuousWave ); + +/*! + * \brief Computes new datarate according to the given offset + * + * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms + * + * \param [IN] dr Current datarate + * + * \param [IN] drOffset Offset to be applied + * + * \retval newDr Computed datarate. + */ +uint8_t RegionRU864ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ); + +/*! + * \brief Sets the radio into beacon reception mode + * + * \param [IN] rxBeaconSetup Pointer to the function parameters + */ +void RegionRU864RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ); + +/*! \} defgroup REGIONRU864 */ + +#endif // __REGION_RU864_H__ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionUS915.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionUS915.c new file mode 100644 index 00000000..688d5b53 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionUS915.c @@ -0,0 +1,1153 @@ +/*! + * \file RegionUS915.c + * + * \brief Region implementation for US915 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) +*/ +#include "utilities.h" + +#include "RegionCommon.h" +#include "RegionUS915.h" +#include "lorawan_conf.h" +#include "mw_log_conf.h" + +// Definitions +#define CHANNELS_MASK_SIZE 6 + +// A mask to select only valid 500KHz channels +#define CHANNELS_MASK_500KHZ_MASK 0x00FF + +/*! + * Region specific context + */ +typedef struct sRegionUS915NvmCtx +{ + /*! + * LoRaMAC channels + */ + ChannelParams_t Channels[ US915_MAX_NB_CHANNELS ]; + /*! + * LoRaMac bands + */ + Band_t Bands[ US915_MAX_NB_BANDS ]; + /*! + * LoRaMac channels mask + */ + uint16_t ChannelsMask[ CHANNELS_MASK_SIZE ]; + /*! + * LoRaMac channels remaining + */ + uint16_t ChannelsMaskRemaining[CHANNELS_MASK_SIZE]; + /*! + * LoRaMac channels default mask + */ + uint16_t ChannelsDefaultMask[ CHANNELS_MASK_SIZE ]; + /*! + * Index of current in use 8 bit group (0: bit 0 - 7, 1: bit 8 - 15, ..., 7: bit 56 - 63) + */ + uint8_t JoinChannelGroupsCurrentIndex; + /*! + * Counter of join trials needed to alternate between DR0 and DR4, see \ref RegionUS915AlternateDr + */ + uint8_t JoinTrialsCounter; +}RegionUS915NvmCtx_t; + +/* + * Non-volatile module context. + */ +static RegionUS915NvmCtx_t NvmCtx; + +// Static functions +static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr ) +{ + uint8_t nextLowerDr = 0; + + if( dr == minDr ) + { + nextLowerDr = minDr; + } + else + { + nextLowerDr = dr - 1; + } + return nextLowerDr; +} + +/*! + * \brief Searches for available 125 kHz channels in the given channel mask. + * + * \param [IN] channelMaskRemaining The remaining channel mask. + * + * \param [OUT] findAvailableChannelsIndex List containing the indexes of all available 125 kHz channels. + * + * \param [OUT] availableChannels Number of available 125 kHz channels. + * + * \retval Status + */ +static LoRaMacStatus_t FindAvailable125kHzChannels( uint8_t* findAvailableChannelsIndex, uint16_t channelMaskRemaining, uint8_t* availableChannels ) +{ + // Nullpointer check + if( findAvailableChannelsIndex == NULL || availableChannels == NULL ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + // Initialize counter + *availableChannels = 0; + for( uint8_t i = 0; i < 8; i++ ) + { + // Find available channels + if( ( channelMaskRemaining & ( 1 << i ) ) != 0 ) + { + // Save available channel index + findAvailableChannelsIndex[*availableChannels] = i; + // Increment counter of available channels if the current channel is available + ( *availableChannels )++; + } + } + + return LORAMAC_STATUS_OK; +} + +/*! + * \brief Computes the next 125kHz channel used for join requests. + * + * \param [OUT] newChannelIndex Index of available channel. + * + * \retval Status + */ +static LoRaMacStatus_t ComputeNext125kHzJoinChannel( uint8_t* newChannelIndex ) +{ + uint8_t currentChannelsMaskRemainingIndex; + uint16_t channelMaskRemaining; + uint8_t findAvailableChannelsIndex[8] = { 0 }; + uint8_t availableChannels = 0; + uint8_t startIndex = NvmCtx.JoinChannelGroupsCurrentIndex; + + // Null pointer check + if( newChannelIndex == NULL ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + do { + // Current ChannelMaskRemaining, two groups per channel mask. For example Group 0 and 1 (8 bit) are ChannelMaskRemaining 0 (16 bit), etc. + currentChannelsMaskRemainingIndex = (uint8_t) startIndex / 2; + + // For even numbers we need the 8 LSBs and for uneven the 8 MSBs + if( ( startIndex % 2 ) == 0 ) + { + channelMaskRemaining = ( NvmCtx.ChannelsMaskRemaining[currentChannelsMaskRemainingIndex] & 0x00FF ); + } + else + { + channelMaskRemaining = ( ( NvmCtx.ChannelsMaskRemaining[currentChannelsMaskRemainingIndex] >> 8 ) & 0x00FF ); + } + + + if( FindAvailable125kHzChannels( findAvailableChannelsIndex, channelMaskRemaining, &availableChannels ) == LORAMAC_STATUS_PARAMETER_INVALID ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + if ( availableChannels > 0 ) + { + // Choose randomly a free channel 125kHz + *newChannelIndex = ( startIndex * 8 ) + findAvailableChannelsIndex[randr( 0, ( availableChannels - 1 ) )]; + } + + // Increment start index + startIndex++; + if ( startIndex > 7 ) + { + startIndex = 0; + } + } while( ( availableChannels == 0 ) && ( startIndex != NvmCtx.JoinChannelGroupsCurrentIndex ) ); + + if ( availableChannels > 0 ) + { + NvmCtx.JoinChannelGroupsCurrentIndex = startIndex; + return LORAMAC_STATUS_OK; + } + + return LORAMAC_STATUS_PARAMETER_INVALID; +} + +static uint32_t GetBandwidth( uint32_t drIndex ) +{ + switch( BandwidthsUS915[drIndex] ) + { + default: + case 125000: + return 0; + case 250000: + return 1; + case 500000: + return 2; + } +} + +static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask ) +{ + int8_t txPowerResult = txPower; + + // Limit tx power to the band max + txPowerResult = MAX( txPower, maxBandTxPower ); + + if( datarate == DR_4 ) + {// Limit tx power to max 26dBm + txPowerResult = MAX( txPower, TX_POWER_2 ); + } + else + { + if( RegionCommonCountChannels( channelsMask, 0, 4 ) < 50 ) + {// Limit tx power to max 21dBm + txPowerResult = MAX( txPower, TX_POWER_5 ); + } + } + return txPowerResult; +} + +static bool VerifyRfFreq( uint32_t freq ) +{ + // Check radio driver support + if( Radio.CheckRfFrequency( freq ) == false ) + { + return false; + } + + // Rx frequencies + if( ( freq < US915_FIRST_RX1_CHANNEL ) || + ( freq > US915_LAST_RX1_CHANNEL ) || + ( ( ( freq - ( uint32_t ) US915_FIRST_RX1_CHANNEL ) % ( uint32_t ) US915_STEPWIDTH_RX1_CHANNEL ) != 0 ) ) + { + return false; + } + + // Test for frequency range - take RX and TX freqencies into account + if( ( freq < 902300000 ) || ( freq > 927500000 ) ) + { + return false; + } + return true; +} + +static uint8_t CountNbOfEnabledChannels( uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t delayTransmission = 0; + + for( uint8_t i = 0, k = 0; i < US915_MAX_NB_CHANNELS; i += 16, k++ ) + { + for( uint8_t j = 0; j < 16; j++ ) + { + if( ( channelsMask[k] & ( 1 << j ) ) != 0 ) + { + if( channels[i + j].Frequency == 0 ) + { // Check if the channel is enabled + continue; + } + if( RegionCommonValueInRange( datarate, channels[i + j].DrRange.Fields.Min, + channels[i + j].DrRange.Fields.Max ) == false ) + { // Check if the current channel selection supports the given datarate + continue; + } + if( bands[channels[i + j].Band].TimeOff > 0 ) + { // Check if the band is available for transmission + delayTransmission++; + continue; + } + enabledChannels[nbEnabledChannels++] = i + j; + } + } + } + + *delayTx = delayTransmission; + return nbEnabledChannels; +} + +PhyParam_t RegionUS915GetPhyParam( GetPhyParams_t* getPhy ) +{ + PhyParam_t phyParam = { 0 }; + + switch( getPhy->Attribute ) + { + case PHY_MIN_RX_DR: + { + phyParam.Value = US915_RX_MIN_DATARATE; + break; + } + case PHY_MIN_TX_DR: + { + phyParam.Value = US915_TX_MIN_DATARATE; + break; + } + case PHY_DEF_TX_DR: + { + phyParam.Value = US915_DEFAULT_DATARATE; + break; + } + case PHY_NEXT_LOWER_TX_DR: + { + phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, US915_TX_MIN_DATARATE ); + break; + } + case PHY_MAX_TX_POWER: + { + phyParam.Value = US915_MAX_TX_POWER; + break; + } + case PHY_DEF_TX_POWER: + { + phyParam.Value = US915_DEFAULT_TX_POWER; + break; + } + case PHY_DEF_ADR_ACK_LIMIT: + { + phyParam.Value = US915_ADR_ACK_LIMIT; + break; + } + case PHY_DEF_ADR_ACK_DELAY: + { + phyParam.Value = US915_ADR_ACK_DELAY; + break; + } + case PHY_MAX_PAYLOAD: + { + phyParam.Value = MaxPayloadOfDatarateUS915[getPhy->Datarate]; + break; + } + case PHY_MAX_PAYLOAD_REPEATER: + { + phyParam.Value = MaxPayloadOfDatarateRepeaterUS915[getPhy->Datarate]; + break; + } + case PHY_DUTY_CYCLE: + { + phyParam.Value = US915_DUTY_CYCLE_ENABLED; + break; + } + case PHY_MAX_RX_WINDOW: + { + phyParam.Value = US915_MAX_RX_WINDOW; + break; + } + case PHY_RECEIVE_DELAY1: + { + phyParam.Value = US915_RECEIVE_DELAY1; + break; + } + case PHY_RECEIVE_DELAY2: + { + phyParam.Value = US915_RECEIVE_DELAY2; + break; + } + case PHY_JOIN_ACCEPT_DELAY1: + { + phyParam.Value = US915_JOIN_ACCEPT_DELAY1; + break; + } + case PHY_JOIN_ACCEPT_DELAY2: + { + phyParam.Value = US915_JOIN_ACCEPT_DELAY2; + break; + } + case PHY_MAX_FCNT_GAP: + { + phyParam.Value = US915_MAX_FCNT_GAP; + break; + } + case PHY_ACK_TIMEOUT: + { + phyParam.Value = ( US915_ACKTIMEOUT + randr( -US915_ACK_TIMEOUT_RND, US915_ACK_TIMEOUT_RND ) ); + break; + } + case PHY_DEF_DR1_OFFSET: + { + phyParam.Value = US915_DEFAULT_RX1_DR_OFFSET; + break; + } + case PHY_DEF_RX2_FREQUENCY: + { + phyParam.Value = US915_RX_WND_2_FREQ; + break; + } + case PHY_DEF_RX2_DR: + { + phyParam.Value = US915_RX_WND_2_DR; + break; + } + case PHY_CHANNELS_MASK: + { + phyParam.ChannelsMask = NvmCtx.ChannelsMask; + break; + } + case PHY_CHANNELS_DEFAULT_MASK: + { + phyParam.ChannelsMask = NvmCtx.ChannelsDefaultMask; + break; + } + case PHY_MAX_NB_CHANNELS: + { + phyParam.Value = US915_MAX_NB_CHANNELS; + break; + } + case PHY_CHANNELS: + { + phyParam.Channels = NvmCtx.Channels; + break; + } + case PHY_DEF_UPLINK_DWELL_TIME: + case PHY_DEF_DOWNLINK_DWELL_TIME: + { + phyParam.Value = 0; + break; + } + case PHY_DEF_MAX_EIRP: + { + phyParam.fValue = US915_DEFAULT_MAX_ERP + (float) 2.15; + break; + } + case PHY_DEF_ANTENNA_GAIN: + { + phyParam.fValue = 0; + break; + } + case PHY_BEACON_CHANNEL_FREQ: + { + phyParam.Value = US915_BEACON_CHANNEL_FREQ; + break; + } + case PHY_BEACON_FORMAT: + { + phyParam.BeaconFormat.BeaconSize = US915_BEACON_SIZE; + phyParam.BeaconFormat.Rfu1Size = US915_RFU1_SIZE; + phyParam.BeaconFormat.Rfu2Size = US915_RFU2_SIZE; + break; + } + case PHY_BEACON_CHANNEL_DR: + { + phyParam.Value = US915_BEACON_CHANNEL_DR; + break; + } + case PHY_BEACON_CHANNEL_STEPWIDTH: + { + phyParam.Value = US915_BEACON_CHANNEL_STEPWIDTH; + break; + } + case PHY_BEACON_NB_CHANNELS: + { + phyParam.Value = US915_BEACON_NB_CHANNELS; + break; + } + case PHY_PING_SLOT_CHANNEL_DR: + { + phyParam.Value = US915_PING_SLOT_CHANNEL_DR; + break; + } + default: + { + break; + } + } + + return phyParam; +} + +void RegionUS915SetBandTxDone( SetBandTxDoneParams_t* txDone ) +{ + RegionCommonSetBandTxDone( txDone->Joined, &NvmCtx.Bands[NvmCtx.Channels[txDone->Channel].Band], txDone->LastTxDoneTime ); +} + +void RegionUS915InitDefaults( InitDefaultsParams_t* params ) +{ + Band_t bands[US915_MAX_NB_BANDS] = + { + US915_BAND0 + }; + + switch( params->Type ) + { + case INIT_TYPE_INIT: + { + // Initialize bands + memcpy1( ( uint8_t* )NvmCtx.Bands, ( uint8_t* )bands, sizeof( Band_t ) * US915_MAX_NB_BANDS ); + + // Initialize 8 bit channel groups index + NvmCtx.JoinChannelGroupsCurrentIndex = 0; + + // Initialize the join trials counter + NvmCtx.JoinTrialsCounter = 0; + + // Channels + // 125 kHz channels + for( uint8_t i = 0; i < US915_MAX_NB_CHANNELS - 8; i++ ) + { + NvmCtx.Channels[i].Frequency = 902300000 + i * 200000; + NvmCtx.Channels[i].DrRange.Value = ( DR_3 << 4 ) | DR_0; + NvmCtx.Channels[i].Band = 0; + } + // 500 kHz channels + for( uint8_t i = US915_MAX_NB_CHANNELS - 8; i < US915_MAX_NB_CHANNELS; i++ ) + { + NvmCtx.Channels[i].Frequency = 903000000 + ( i - ( US915_MAX_NB_CHANNELS - 8 ) ) * 1600000; + NvmCtx.Channels[i].DrRange.Value = ( DR_4 << 4 ) | DR_4; + NvmCtx.Channels[i].Band = 0; + } + + // ChannelsMask + NvmCtx.ChannelsDefaultMask[0] = 0xFFFF; + NvmCtx.ChannelsDefaultMask[1] = 0xFFFF; + NvmCtx.ChannelsDefaultMask[2] = 0xFFFF; + NvmCtx.ChannelsDefaultMask[3] = 0xFFFF; + NvmCtx.ChannelsDefaultMask[4] = 0x00FF; + NvmCtx.ChannelsDefaultMask[5] = 0x0000; + + // Copy channels default mask + RegionCommonChanMaskCopy( NvmCtx.ChannelsMask, NvmCtx.ChannelsDefaultMask, 6 ); + + // Copy into channels mask remaining + RegionCommonChanMaskCopy( NvmCtx.ChannelsMaskRemaining, NvmCtx.ChannelsMask, 6 ); + break; + } + case INIT_TYPE_RESTORE_CTX: + { + if( params->NvmCtx != 0 ) + { + memcpy1( (uint8_t*) &NvmCtx, (uint8_t*) params->NvmCtx, sizeof( NvmCtx ) ); + } + break; + } + case INIT_TYPE_RESTORE_DEFAULT_CHANNELS: + { + // Copy channels default mask + RegionCommonChanMaskCopy( NvmCtx.ChannelsMask, NvmCtx.ChannelsDefaultMask, 6 ); + + for( uint8_t i = 0; i < 6; i++ ) + { // Copy-And the channels mask + NvmCtx.ChannelsMaskRemaining[i] &= NvmCtx.ChannelsMask[i]; + } + break; + } + default: + { + break; + } + } +} + +void* RegionUS915GetNvmCtx( GetNvmCtxParams_t* params ) +{ + params->nvmCtxSize = sizeof( RegionUS915NvmCtx_t ); + return &NvmCtx; +} + +bool RegionUS915Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ) +{ + switch( phyAttribute ) + { + case PHY_FREQUENCY: + { + return VerifyRfFreq( verify->Frequency ); + } + case PHY_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, US915_TX_MIN_DATARATE, US915_TX_MAX_DATARATE ); + } + case PHY_DEF_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, DR_0, DR_5 ); + } + case PHY_RX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, US915_RX_MIN_DATARATE, US915_RX_MAX_DATARATE ); + } + case PHY_DEF_TX_POWER: + case PHY_TX_POWER: + { + // Remark: switched min and max! + return RegionCommonValueInRange( verify->TxPower, US915_MAX_TX_POWER, US915_MIN_TX_POWER ); + } + case PHY_DUTY_CYCLE: + { + return US915_DUTY_CYCLE_ENABLED; + } + default: + return false; + } +} + +void RegionUS915ApplyCFList( ApplyCFListParams_t* applyCFList ) +{ + // Size of the optional CF list must be 16 byte + if( applyCFList->Size != 16 ) + { + return; + } + + // Last byte CFListType must be 0x01 to indicate the CFList contains a series of ChMask fields + if( applyCFList->Payload[15] != 0x01 ) + { + return; + } + + // ChMask0 - ChMask4 must be set (every ChMask has 16 bit) + for( uint8_t chMaskItr = 0, cntPayload = 0; chMaskItr <= 4; chMaskItr++, cntPayload+=2 ) + { + NvmCtx.ChannelsMask[chMaskItr] = (uint16_t) (0x00FF & applyCFList->Payload[cntPayload]); + NvmCtx.ChannelsMask[chMaskItr] |= (uint16_t) (applyCFList->Payload[cntPayload+1] << 8); + if( chMaskItr == 4 ) + { + NvmCtx.ChannelsMask[chMaskItr] = NvmCtx.ChannelsMask[chMaskItr] & CHANNELS_MASK_500KHZ_MASK; + } + // Set the channel mask to the remaining + NvmCtx.ChannelsMaskRemaining[chMaskItr] &= NvmCtx.ChannelsMask[chMaskItr]; + } +} + +bool RegionUS915ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ) +{ + uint8_t nbChannels = RegionCommonCountChannels( chanMaskSet->ChannelsMaskIn, 0, 4 ); + + // Check the number of active channels + if( ( nbChannels < 2 ) && + ( nbChannels > 0 ) ) + { + return false; + } + + switch( chanMaskSet->ChannelsMaskType ) + { + case CHANNELS_MASK: + { + RegionCommonChanMaskCopy( NvmCtx.ChannelsMask, chanMaskSet->ChannelsMaskIn, CHANNELS_MASK_SIZE ); + + NvmCtx.ChannelsDefaultMask[4] = NvmCtx.ChannelsDefaultMask[4] & CHANNELS_MASK_500KHZ_MASK; + NvmCtx.ChannelsDefaultMask[5] = 0x0000; + + for( uint8_t i = 0; i < CHANNELS_MASK_SIZE; i++ ) + { // Copy-And the channels mask + NvmCtx.ChannelsMaskRemaining[i] &= NvmCtx.ChannelsMask[i]; + } + break; + } + case CHANNELS_DEFAULT_MASK: + { + RegionCommonChanMaskCopy( NvmCtx.ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, CHANNELS_MASK_SIZE ); + break; + } + default: + return false; + } + return true; +} + +void RegionUS915ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ) +{ + double tSymbol = 0.0; + + // Get the datarate, perform a boundary check + rxConfigParams->Datarate = MIN( datarate, US915_RX_MAX_DATARATE ); + rxConfigParams->Bandwidth = GetBandwidth( rxConfigParams->Datarate ); + + tSymbol = RegionCommonComputeSymbolTimeLoRa( DataratesUS915[rxConfigParams->Datarate], BandwidthsUS915[rxConfigParams->Datarate] ); + + RegionCommonComputeRxWindowParameters( tSymbol, minRxSymbols, rxError, Radio.GetWakeupTime( ), &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); +} + +bool RegionUS915RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) +{ + int8_t dr = rxConfig->Datarate; + uint8_t maxPayload = 0; + int8_t phyDr = 0; + uint32_t frequency = rxConfig->Frequency; + const char *slotStrings[] = { "1", "2", "C", "Multi_C", "P", "Multi_P" }; + + if( Radio.GetStatus( ) != RF_IDLE ) + { + return false; + } + + if( rxConfig->RxSlot == RX_SLOT_WIN_1 ) + { + // Apply window 1 frequency + frequency = US915_FIRST_RX1_CHANNEL + ( rxConfig->Channel % 8 ) * US915_STEPWIDTH_RX1_CHANNEL; + } + + // Read the physical datarate from the datarates table + phyDr = DataratesUS915[dr]; + + Radio.SetChannel( frequency ); + + // Radio configuration + Radio.SetRxConfig( MODEM_LORA, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous ); + + if( rxConfig->RepeaterSupport == true ) + { + maxPayload = MaxPayloadOfDatarateRepeaterUS915[dr]; + } + else + { + maxPayload = MaxPayloadOfDatarateUS915[dr]; + } + Radio.SetMaxPayloadLength( MODEM_LORA, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD ); + if ( rxConfig->RxSlot < RX_SLOT_NONE ) + { + MW_LOG( "RX_%s on freq %d Hz at DR %d\n\r", slotStrings[rxConfig->RxSlot], frequency, dr ); + } + else + { + MW_LOG( "RX on freq %d Hz at DR %d\n\r", frequency, dr ); + } + + *datarate = (uint8_t) dr; + return true; +} + +bool RegionUS915TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ) +{ + int8_t phyDr = DataratesUS915[txConfig->Datarate]; + int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, NvmCtx.Bands[NvmCtx.Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, NvmCtx.ChannelsMask ); + uint32_t bandwidth = GetBandwidth( txConfig->Datarate ); + int8_t phyTxPower = 0; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, US915_DEFAULT_MAX_ERP, 0 ); + + // Setup the radio frequency + Radio.SetChannel( NvmCtx.Channels[txConfig->Channel].Frequency ); + + Radio.SetTxConfig( MODEM_LORA, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 4000 ); + MW_LOG( "TX on freq %d Hz at DR %d\n\r", NvmCtx.Channels[txConfig->Channel].Frequency, txConfig->Datarate ); + + // Setup maximum payload lenght of the radio driver + Radio.SetMaxPayloadLength( MODEM_LORA, txConfig->PktLen ); + // Get the time-on-air of the next tx frame + *txTimeOnAir = Radio.TimeOnAir( MODEM_LORA, txConfig->PktLen ); + *txPower = txPowerLimited; + + return true; +} + +uint8_t RegionUS915LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ) +{ + uint8_t status = 0x07; + RegionCommonLinkAdrParams_t linkAdrParams; + uint8_t nextIndex = 0; + uint8_t bytesProcessed = 0; + uint16_t channelsMask[6] = { 0, 0, 0, 0, 0, 0 }; + GetPhyParams_t getPhy; + PhyParam_t phyParam; + RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams; + + // Initialize local copy of channels mask + RegionCommonChanMaskCopy( channelsMask, NvmCtx.ChannelsMask, 6 ); + + while( bytesProcessed < linkAdrReq->PayloadSize ) + { + nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams ); + + if( nextIndex == 0 ) + break; // break loop, since no more request has been found + + // Update bytes processed + bytesProcessed += nextIndex; + + // Revert status, as we only check the last ADR request for the channel mask KO + status = 0x07; + + if( linkAdrParams.ChMaskCtrl == 6 ) + { + // Enable all 125 kHz channels + channelsMask[0] = 0xFFFF; + channelsMask[1] = 0xFFFF; + channelsMask[2] = 0xFFFF; + channelsMask[3] = 0xFFFF; + // Apply chMask to channels 64 to 71 + channelsMask[4] = linkAdrParams.ChMask & CHANNELS_MASK_500KHZ_MASK; + } + else if( linkAdrParams.ChMaskCtrl == 7 ) + { + // Disable all 125 kHz channels + channelsMask[0] = 0x0000; + channelsMask[1] = 0x0000; + channelsMask[2] = 0x0000; + channelsMask[3] = 0x0000; + // Apply chMask to channels 64 to 71 + channelsMask[4] = linkAdrParams.ChMask & CHANNELS_MASK_500KHZ_MASK; + } + else if( linkAdrParams.ChMaskCtrl == 5 ) + { + // Start value for comparision + uint8_t bitMask = 1; + + // cntChannelMask for channelsMask[0] until channelsMask[3] + uint8_t cntChannelMask = 0; + + // i will be 1, 2, 3, ..., 7 + for( uint8_t i = 0; i <= 7; i++ ) + { + // 8 MSBs of ChMask are RFU + // Checking if the ChMask is set, then true + if( ( ( linkAdrParams.ChMask & 0x00FF ) & ( bitMask << i ) ) != 0 ) + { + if( ( i % 2 ) == 0 ) + { + // Enable a bank of 8 125kHz channels, 8 LSBs + channelsMask[cntChannelMask] |= 0x00FF; + // Enable the corresponding 500kHz channel + channelsMask[4] |= ( bitMask << i ); + } + else + { + // Enable a bank of 8 125kHz channels, 8 MSBs + channelsMask[cntChannelMask] |= 0xFF00; + // Enable the corresponding 500kHz channel + channelsMask[4] |= ( bitMask << i ); + // cntChannelMask increment for uneven i + cntChannelMask++; + } + } + // ChMask is not set + else + { + if( ( i % 2 ) == 0 ) + { + // Disable a bank of 8 125kHz channels, 8 LSBs + channelsMask[cntChannelMask] &= 0xFF00; + // Disable the corresponding 500kHz channel + channelsMask[4] &= ~( bitMask << i ); + } + else + { + // Enable a bank of 8 125kHz channels, 8 MSBs + channelsMask[cntChannelMask] &= 0x00FF; + // Disable the corresponding 500kHz channel + channelsMask[4] &= ~( bitMask << i ); + // cntChannelMask increment for uneven i + cntChannelMask++; + } + } + } + } + else + { + channelsMask[linkAdrParams.ChMaskCtrl] = linkAdrParams.ChMask; + } + } + + // FCC 15.247 paragraph F mandates to hop on at least 2 125 kHz channels + if( ( linkAdrParams.Datarate < DR_4 ) && ( RegionCommonCountChannels( channelsMask, 0, 4 ) < 2 ) ) + { + status &= 0xFE; // Channel mask KO + } + + // Get the minimum possible datarate + getPhy.Attribute = PHY_MIN_TX_DR; + getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime; + phyParam = RegionUS915GetPhyParam( &getPhy ); + + linkAdrVerifyParams.Status = status; + linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled; + linkAdrVerifyParams.Datarate = linkAdrParams.Datarate; + linkAdrVerifyParams.TxPower = linkAdrParams.TxPower; + linkAdrVerifyParams.NbRep = linkAdrParams.NbRep; + linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate; + linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower; + linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep; + linkAdrVerifyParams.NbChannels = US915_MAX_NB_CHANNELS; + linkAdrVerifyParams.ChannelsMask = channelsMask; + linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value; + linkAdrVerifyParams.MaxDatarate = US915_TX_MAX_DATARATE; + linkAdrVerifyParams.Channels = NvmCtx.Channels; + linkAdrVerifyParams.MinTxPower = US915_MIN_TX_POWER; + linkAdrVerifyParams.MaxTxPower = US915_MAX_TX_POWER; + linkAdrVerifyParams.Version = linkAdrReq->Version; + + // Verify the parameters and update, if necessary + status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep ); + + // Update channelsMask if everything is correct + if( status == 0x07 ) + { + // Copy Mask + RegionCommonChanMaskCopy( NvmCtx.ChannelsMask, channelsMask, 6 ); + + NvmCtx.ChannelsMaskRemaining[0] &= NvmCtx.ChannelsMask[0]; + NvmCtx.ChannelsMaskRemaining[1] &= NvmCtx.ChannelsMask[1]; + NvmCtx.ChannelsMaskRemaining[2] &= NvmCtx.ChannelsMask[2]; + NvmCtx.ChannelsMaskRemaining[3] &= NvmCtx.ChannelsMask[3]; + NvmCtx.ChannelsMaskRemaining[4] = NvmCtx.ChannelsMask[4]; + NvmCtx.ChannelsMaskRemaining[5] = NvmCtx.ChannelsMask[5]; + } + + // Update status variables + *drOut = linkAdrParams.Datarate; + *txPowOut = linkAdrParams.TxPower; + *nbRepOut = linkAdrParams.NbRep; + *nbBytesParsed = bytesProcessed; + + return status; +} + +uint8_t RegionUS915RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ) +{ + uint8_t status = 0x07; + + // Verify radio frequency + if( VerifyRfFreq( rxParamSetupReq->Frequency ) == false ) + { + status &= 0xFE; // Channel frequency KO + } + + // Verify datarate + if( RegionCommonValueInRange( rxParamSetupReq->Datarate, US915_RX_MIN_DATARATE, US915_RX_MAX_DATARATE ) == false ) + { + status &= 0xFD; // Datarate KO + } + if( ( RegionCommonValueInRange( rxParamSetupReq->Datarate, DR_5, DR_7 ) == true ) || + ( rxParamSetupReq->Datarate > DR_13 ) ) + { + status &= 0xFD; // Datarate KO + } + + // Verify datarate offset + if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, US915_MIN_RX1_DR_OFFSET, US915_MAX_RX1_DR_OFFSET ) == false ) + { + status &= 0xFB; // Rx1DrOffset range KO + } + + return status; +} + +uint8_t RegionUS915NewChannelReq( NewChannelReqParams_t* newChannelReq ) +{ + // Datarate and frequency KO + return 0; +} + +int8_t RegionUS915TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ) +{ + return -1; +} + +uint8_t RegionUS915DlChannelReq( DlChannelReqParams_t* dlChannelReq ) +{ + return 0; +} + +int8_t RegionUS915AlternateDr( int8_t currentDr, AlternateDrType_t type ) +{ + // Alternates the data rate according to the channel sequence: + // Eight times a 125kHz DR_0 and then one 500kHz DR_4 channel + if( type == ALTERNATE_DR ) + { + NvmCtx.JoinTrialsCounter++; + } + else + { + NvmCtx.JoinTrialsCounter--; + } + + if( NvmCtx.JoinTrialsCounter % 9 == 0 ) + { + // Use DR_4 every 9th times. + currentDr = DR_4; + } + else + { + currentDr = DR_0; + } + return currentDr; +} + +void RegionUS915CalcBackOff( CalcBackOffParams_t* calcBackOff ) +{ + RegionCommonCalcBackOffParams_t calcBackOffParams; + + calcBackOffParams.Channels = NvmCtx.Channels; + calcBackOffParams.Bands = NvmCtx.Bands; + calcBackOffParams.LastTxIsJoinRequest = calcBackOff->LastTxIsJoinRequest; + calcBackOffParams.Joined = calcBackOff->Joined; + calcBackOffParams.DutyCycleEnabled = calcBackOff->DutyCycleEnabled; + calcBackOffParams.Channel = calcBackOff->Channel; + calcBackOffParams.ElapsedTime = calcBackOff->ElapsedTime; + calcBackOffParams.TxTimeOnAir = calcBackOff->TxTimeOnAir; + + RegionCommonCalcBackOff( &calcBackOffParams ); +} + +LoRaMacStatus_t RegionUS915NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t delayTx = 0; + uint8_t enabledChannels[US915_MAX_NB_CHANNELS] = { 0 }; + TimerTime_t nextTxDelay = 0; + uint8_t newChannelIndex; + + // Count 125kHz channels + if( RegionCommonCountChannels( NvmCtx.ChannelsMaskRemaining, 0, 4 ) == 0 ) + { // Reactivate default channels + RegionCommonChanMaskCopy( NvmCtx.ChannelsMaskRemaining, NvmCtx.ChannelsMask, 4 ); + + NvmCtx.JoinChannelGroupsCurrentIndex = 0; + } + // Check other channels + if( nextChanParams->Datarate >= DR_4 ) + { + if( ( NvmCtx.ChannelsMaskRemaining[4] & CHANNELS_MASK_500KHZ_MASK ) == 0 ) + { + NvmCtx.ChannelsMaskRemaining[4] = NvmCtx.ChannelsMask[4]; + } + } + + TimerTime_t elapsed = TimerGetElapsedTime( nextChanParams->LastAggrTx ); + if( ( nextChanParams->LastAggrTx == 0 ) || ( nextChanParams->AggrTimeOff <= elapsed ) ) + { + // Reset Aggregated time off + *aggregatedTimeOff = 0; + + // Update bands Time OFF + nextTxDelay = RegionCommonUpdateBandTimeOff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, NvmCtx.Bands, US915_MAX_NB_BANDS ); + + // Search how many channels are enabled + nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Datarate, + NvmCtx.ChannelsMaskRemaining, NvmCtx.Channels, + NvmCtx.Bands, enabledChannels, &delayTx ); + } + else + { + delayTx++; + nextTxDelay = nextChanParams->AggrTimeOff - elapsed; + } + + if( nbEnabledChannels > 0 ) + { + if( nextChanParams->Joined == true ) + { + // Choose randomly on of the remaining channels + *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )]; + } + else + { + // For rapid network acquisition in mixed gateway channel plan environments, the device + // follow a random channel selection sequence. It probes alternating one out of a + // group of eight 125 kHz channels followed by probing one 500 kHz channel each pass. + // Each time a 125 kHz channel will be selected from another group. + + // 125kHz Channels (0 - 63) DR0 + if( nextChanParams->Datarate == DR_0 ) + { + if( ComputeNext125kHzJoinChannel( &newChannelIndex ) == LORAMAC_STATUS_PARAMETER_INVALID ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + *channel = newChannelIndex; + } + // 500kHz Channels (64 - 71) DR4 + else + { + // Choose the next available channel + uint8_t i = 0; + while( ( ( NvmCtx.ChannelsMaskRemaining[4] & CHANNELS_MASK_500KHZ_MASK ) & ( 1 << i ) ) == 0 ) + { + i++; + } + *channel = 64 + i; + } + } + + // Disable the channel in the mask + RegionCommonChanDisable( NvmCtx.ChannelsMaskRemaining, *channel, US915_MAX_NB_CHANNELS ); + + *time = 0; + return LORAMAC_STATUS_OK; + } + else + { + if( delayTx > 0 ) + { + // Delay transmission due to AggregatedTimeOff or to a band time off + *time = nextTxDelay; + return LORAMAC_STATUS_DUTYCYCLE_RESTRICTED; + } + // Datarate not supported by any channel + *time = 0; + return LORAMAC_STATUS_NO_CHANNEL_FOUND; + } +} + +LoRaMacStatus_t RegionUS915ChannelAdd( ChannelAddParams_t* channelAdd ) +{ + return LORAMAC_STATUS_PARAMETER_INVALID; +} + +bool RegionUS915ChannelsRemove( ChannelRemoveParams_t* channelRemove ) +{ + return LORAMAC_STATUS_PARAMETER_INVALID; +} + +void RegionUS915SetContinuousWave( ContinuousWaveParams_t* continuousWave ) +{ + int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, NvmCtx.Bands[NvmCtx.Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, NvmCtx.ChannelsMask ); + int8_t phyTxPower = 0; + uint32_t frequency = NvmCtx.Channels[continuousWave->Channel].Frequency; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, US915_DEFAULT_MAX_ERP, 0 ); + + Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout ); +} + +uint8_t RegionUS915ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) +{ + int8_t datarate = DatarateOffsetsUS915[dr][drOffset]; + + if( datarate < 0 ) + { + datarate = DR_0; + } + return datarate; +} + +void RegionUS915RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ) +{ + RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup; + + regionCommonRxBeaconSetup.Datarates = DataratesUS915; + regionCommonRxBeaconSetup.Frequency = rxBeaconSetup->Frequency; + regionCommonRxBeaconSetup.BeaconSize = US915_BEACON_SIZE; + regionCommonRxBeaconSetup.BeaconDatarate = US915_BEACON_CHANNEL_DR; + regionCommonRxBeaconSetup.BeaconChannelBW = US915_BEACON_CHANNEL_BW; + regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime; + regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout; + + RegionCommonRxBeaconSetup( ®ionCommonRxBeaconSetup ); + + // Store downlink datarate + *outDr = US915_BEACON_CHANNEL_DR; +} diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionUS915.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionUS915.h new file mode 100644 index 00000000..37c08b17 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/region/RegionUS915.h @@ -0,0 +1,506 @@ +/*! + * \file RegionUS915.h + * + * \brief Region definition for US915 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + * + * \defgroup REGIONUS915 Region US915 + * Implementation according to LoRaWAN Specification v1.0.2. + * \{ + */ +#ifndef __REGION_US915_H__ +#define __REGION_US915_H__ + +#include "region/Region.h" + +/*! + * LoRaMac maximum number of channels + */ +#define US915_MAX_NB_CHANNELS 72 + +/*! + * Minimal datarate that can be used by the node + */ +#define US915_TX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define US915_TX_MAX_DATARATE DR_4 + +/*! + * Minimal datarate that can be used by the node + */ +#define US915_RX_MIN_DATARATE DR_8 + +/*! + * Maximal datarate that can be used by the node + */ +#define US915_RX_MAX_DATARATE DR_13 + +/*! + * Default datarate used by the node + */ +#define US915_DEFAULT_DATARATE DR_0 + +/*! + * Minimal Rx1 receive datarate offset + */ +#define US915_MIN_RX1_DR_OFFSET 0 + +/*! + * Maximal Rx1 receive datarate offset + */ +#define US915_MAX_RX1_DR_OFFSET 3 + +/*! + * Default Rx1 receive datarate offset + */ +#define US915_DEFAULT_RX1_DR_OFFSET 0 + +/*! + * Minimal Tx output power that can be used by the node + */ +#if (defined(CERTIF_LORAWAN_VERSION) &&(CERTIF_LORAWAN_VERSION == 103)) +#define US915_MIN_TX_POWER TX_POWER_14 +#else +#define US915_MIN_TX_POWER TX_POWER_10 +#endif +/*! + * Maximal Tx output power that can be used by the node + */ +#define US915_MAX_TX_POWER TX_POWER_0 + +/*! + * Default Tx output power used by the node + */ +#define US915_DEFAULT_TX_POWER TX_POWER_0 + +/*! + * Default Max ERP + */ +#define US915_DEFAULT_MAX_ERP 30.0f + +/*! + * ADR Ack limit + */ +#define US915_ADR_ACK_LIMIT 64 + +/*! + * ADR Ack delay + */ +#define US915_ADR_ACK_DELAY 32 + +/*! + * Enabled or disabled the duty cycle + */ +#define US915_DUTY_CYCLE_ENABLED 0 + +/*! + * Maximum RX window duration + */ +#define US915_MAX_RX_WINDOW 3000 + +/*! + * Receive delay 1 + */ +#define US915_RECEIVE_DELAY1 1000 + +/*! + * Receive delay 2 + */ +#define US915_RECEIVE_DELAY2 2000 + +/*! + * Join accept delay 1 + */ +#define US915_JOIN_ACCEPT_DELAY1 5000 + +/*! + * Join accept delay 2 + */ +#define US915_JOIN_ACCEPT_DELAY2 6000 + +/*! + * Maximum frame counter gap + */ +#define US915_MAX_FCNT_GAP 16384 + +/*! + * Ack timeout + */ +#define US915_ACKTIMEOUT 2000 + +/*! + * Random ack timeout limits + */ +#define US915_ACK_TIMEOUT_RND 1000 + +/*! + * Second reception window channel frequency definition. + */ +#define US915_RX_WND_2_FREQ 923300000 + +/*! + * Second reception window channel datarate definition. + */ +#define US915_RX_WND_2_DR DR_8 + +/* + * CLASS B + */ +/*! + * Beacon frequency + */ +#define US915_BEACON_CHANNEL_FREQ 923300000 + +/*! + * Beacon frequency channel stepwidth + */ +#define US915_BEACON_CHANNEL_STEPWIDTH 600000 + +/*! + * Number of possible beacon channels + */ +#define US915_BEACON_NB_CHANNELS 8 + +/*! + * Payload size of a beacon frame + */ +#define US915_BEACON_SIZE 23 + +/*! + * Size of RFU 1 field + */ +#define US915_RFU1_SIZE 5 + +/*! + * Size of RFU 2 field + */ +#define US915_RFU2_SIZE 3 + +/*! + * Datarate of the beacon channel + */ +#define US915_BEACON_CHANNEL_DR DR_8 + +/*! + * Bandwith of the beacon channel + */ +#define US915_BEACON_CHANNEL_BW 2 + +/*! + * Ping slot channel datarate + */ +#define US915_PING_SLOT_CHANNEL_DR DR_8 + +/*! + * LoRaMac maximum number of bands + */ +#define US915_MAX_NB_BANDS 1 + +/*! + * Band 0 definition + * { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff } + */ +#define US915_BAND0 { 1, US915_MAX_TX_POWER, 0, 0, 0 } // 100.0 % + +/*! + * Defines the first channel for RX window 1 for US band + */ +#define US915_FIRST_RX1_CHANNEL ( (uint32_t) 923300000 ) + +/*! + * Defines the last channel for RX window 1 for US band + */ +#define US915_LAST_RX1_CHANNEL ( (uint32_t) 927500000 ) + +/*! + * Defines the step width of the channels for RX window 1 + */ +#define US915_STEPWIDTH_RX1_CHANNEL ( (uint32_t) 600000 ) + +/*! + * Data rates table definition + */ +static const uint8_t DataratesUS915[] = { 10, 9, 8, 7, 8, 0, 0, 0, 12, 11, 10, 9, 8, 7, 0, 0 }; + +/*! + * Bandwidths table definition in Hz + */ +static const uint32_t BandwidthsUS915[] = { 125000, 125000, 125000, 125000, 500000, 0, 0, 0, 500000, 500000, 500000, 500000, 500000, 500000, 0, 0 }; + +/*! + * Up/Down link data rates offset definition + */ +static const int8_t DatarateOffsetsUS915[5][4] = +{ + { DR_10, DR_9 , DR_8 , DR_8 }, // DR_0 + { DR_11, DR_10, DR_9 , DR_8 }, // DR_1 + { DR_12, DR_11, DR_10, DR_9 }, // DR_2 + { DR_13, DR_12, DR_11, DR_10 }, // DR_3 + { DR_13, DR_13, DR_12, DR_11 }, // DR_4 +}; + +/*! + * Maximum payload with respect to the datarate index. Cannot operate with repeater. + */ +static const uint8_t MaxPayloadOfDatarateUS915[] = { 11, 53, 125, 242, 242, 0, 0, 0, 53, 129, 242, 242, 242, 242, 0, 0 }; + +/*! + * Maximum payload with respect to the datarate index. Can operate with repeater. + */ +static const uint8_t MaxPayloadOfDatarateRepeaterUS915[] = { 11, 53, 125, 242, 242, 0, 0, 0, 33, 109, 222, 222, 222, 222, 0, 0 }; + +/*! + * \brief The function gets a value of a specific phy attribute. + * + * \param [IN] getPhy Pointer to the function parameters. + * + * \retval Returns a structure containing the PHY parameter. + */ +PhyParam_t RegionUS915GetPhyParam( GetPhyParams_t* getPhy ); + +/*! + * \brief Updates the last TX done parameters of the current channel. + * + * \param [IN] txDone Pointer to the function parameters. + */ +void RegionUS915SetBandTxDone( SetBandTxDoneParams_t* txDone ); + +/*! + * \brief Initializes the channels masks and the channels. + * + * \param [IN] type Sets the initialization type. + */ +void RegionUS915InitDefaults( InitDefaultsParams_t* params ); + +/*! + * \brief Returns a pointer to the internal context and its size. + * + * \param [OUT] params Pointer to the function parameters. + * + * \retval Points to a structure where the module store its non-volatile context. + */ +void* RegionUS915GetNvmCtx( GetNvmCtxParams_t* params ); + +/*! + * \brief Verifies a parameter. + * + * \param [IN] verify Pointer to the function parameters. + * + * \param [IN] type Sets the initialization type. + * + * \retval Returns true, if the parameter is valid. + */ +bool RegionUS915Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ); + +/*! + * \brief The function parses the input buffer and sets up the channels of the + * CF list. + * + * \param [IN] applyCFList Pointer to the function parameters. + */ +void RegionUS915ApplyCFList( ApplyCFListParams_t* applyCFList ); + +/*! + * \brief Sets a channels mask. + * + * \param [IN] chanMaskSet Pointer to the function parameters. + * + * \retval Returns true, if the channels mask could be set. + */ +bool RegionUS915ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ); + +/*! + * Computes the Rx window timeout and offset. + * + * \param [IN] datarate Rx window datarate index to be used + * + * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame. + * + * \param [IN] rxError System maximum timing error of the receiver. In milliseconds + * The receiver will turn on in a [-rxError : +rxError] ms + * interval around RxOffset + * + * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields. + */ +void RegionUS915ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ); + +/*! + * \brief Configuration of the RX windows. + * + * \param [IN] rxConfig Pointer to the function parameters. + * + * \param [OUT] datarate The datarate index which was set. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionUS915RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ); + +/*! + * \brief TX configuration. + * + * \param [IN] txConfig Pointer to the function parameters. + * + * \param [OUT] txPower The tx power index which was set. + * + * \param [OUT] txTimeOnAir The time-on-air of the frame. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionUS915TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ); + +/*! + * \brief The function processes a Link ADR Request. + * + * \param [IN] linkAdrReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionUS915LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ); + +/*! + * \brief The function processes a RX Parameter Setup Request. + * + * \param [IN] rxParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionUS915RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ); + +/*! + * \brief The function processes a Channel Request. + * + * \param [IN] newChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionUS915NewChannelReq( NewChannelReqParams_t* newChannelReq ); + +/*! + * \brief The function processes a TX ParamSetup Request. + * + * \param [IN] txParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + * Returns -1, if the functionality is not implemented. In this case, the end node + * shall not process the command. + */ +int8_t RegionUS915TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ); + +/*! + * \brief The function processes a DlChannel Request. + * + * \param [IN] dlChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionUS915DlChannelReq( DlChannelReqParams_t* dlChannelReq ); + +/*! + * \brief Alternates the datarate of the channel for the join request. + * + * \param [IN] currentDr Current datarate. + * + * \param [IN] type Alternation type. + * + * \retval Datarate to apply. + */ +int8_t RegionUS915AlternateDr( int8_t currentDr, AlternateDrType_t type ); + +/*! + * \brief Calculates the back-off time. + * + * \param [IN] calcBackOff Pointer to the function parameters. + */ +void RegionUS915CalcBackOff( CalcBackOffParams_t* calcBackOff ); + +/*! + * \brief Searches and set the next random available channel + * + * \param [OUT] channel Next channel to use for TX. + * + * \param [OUT] time Time to wait for the next transmission according to the duty + * cycle. + * + * \param [OUT] aggregatedTimeOff Updates the aggregated time off. + * + * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate] + */ +LoRaMacStatus_t RegionUS915NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ); + +/*! + * \brief Adds a channel. + * + * \param [IN] channelAdd Pointer to the function parameters. + * + * \retval Status of the operation. + */ +LoRaMacStatus_t RegionUS915ChannelAdd( ChannelAddParams_t* channelAdd ); + +/*! + * \brief Removes a channel. + * + * \param [IN] channelRemove Pointer to the function parameters. + * + * \retval Returns true, if the channel was removed successfully. + */ +bool RegionUS915ChannelsRemove( ChannelRemoveParams_t* channelRemove ); + +/*! + * \brief Sets the radio into continuous wave mode. + * + * \param [IN] continuousWave Pointer to the function parameters. + */ +void RegionUS915SetContinuousWave( ContinuousWaveParams_t* continuousWave ); + +/*! + * \brief Computes new datarate according to the given offset + * + * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms + * + * \param [IN] dr Current datarate + * + * \param [IN] drOffset Offset to be applied + * + * \retval newDr Computed datarate. + */ +uint8_t RegionUS915ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ); + +/*! + * \brief Sets the radio into beacon reception mode + * + * \param [IN] rxBeaconSetup Pointer to the function parameters + */ + void RegionUS915RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ); + +/*! \} defgroup REGIONUS915 */ + +#endif // __REGION_US915_H__ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/secure-element.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/secure-element.h new file mode 100644 index 00000000..aee98ed9 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Mac/secure-element.h @@ -0,0 +1,207 @@ +/*! + * \file secure-element.h + * + * \brief Secure Element driver API + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + * + * \defgroup SECUREELEMENT Secure Element API Definition + * + * \{ + * + */ +#ifndef __SECURE_ELEMENT_H__ +#define __SECURE_ELEMENT_H__ + +#include +#include "LoRaMacCrypto.h" + +#define SE_EUI_SIZE 16 + +/*! + * Return values. + */ +typedef enum eSecureElementStatus +{ + /*! + * No error occurred + */ + SECURE_ELEMENT_SUCCESS = 0, + /*! + * CMAC does not match + */ + SECURE_ELEMENT_FAIL_CMAC, + /*! + * Null pointer exception + */ + SECURE_ELEMENT_ERROR_NPE, + /*! + * Invalid key identifier exception + */ + SECURE_ELEMENT_ERROR_INVALID_KEY_ID, + /*! + * Invalid LoRaWAN specification version + */ + SECURE_ELEMENT_ERROR_INVALID_LORAWAM_SPEC_VERSION, + /*! + * Incompatible buffer size + */ + SECURE_ELEMENT_ERROR_BUF_SIZE, + /*! + * Undefined Error occurred + */ + SECURE_ELEMENT_ERROR, +}SecureElementStatus_t; + +/*! + * Signature of callback function to be called by the Secure Element driver when the + * non volatile context have to be stored. + * + */ +typedef void ( *SecureElementNvmEvent )( void ); + +/*! + * Initialization of Secure Element driver + * + * \param[IN] seNvmCtxChanged - Callback function which will be called when the + * non-volatile context have to be stored. + * \retval - Status of the operation + */ +SecureElementStatus_t SecureElementInit( SecureElementNvmEvent seNvmCtxChanged ); + +/*! + * Restores the internal nvm context from passed pointer. + * + * \param[IN] seNvmCtx - Pointer to non-volatile module context to be restored. + * \retval - Status of the operation + */ +SecureElementStatus_t SecureElementRestoreNvmCtx( void* seNvmCtx ); + +/*! + * Returns a pointer to the internal non-volatile context. + * + * \param[IN] seNvmCtxSize - Size of the module non volatile context + * \retval - Points to a structure where the module store its non volatile context + */ +void* SecureElementGetNvmCtx( size_t* seNvmCtxSize ); + +/*! + * Sets a key + * + * \param[IN] keyID - Key identifier + * \param[IN] key - Key value + * \retval - Status of the operation + */ +SecureElementStatus_t SecureElementSetKey( KeyIdentifier_t keyID, uint8_t* key ); + +/*! + * Computes a CMAC of a message using provided initial Bx block + * + * \param[IN] micBxBuffer - Buffer containing the initial Bx block + * \param[IN] buffer - Data buffer + * \param[IN] size - Data buffer size + * \param[IN] keyID - Key identifier to determine the AES key to be used + * \param[OUT] cmac - Computed cmac + * \retval - Status of the operation + */ +SecureElementStatus_t SecureElementComputeAesCmac( uint8_t* micBxBuffer, uint8_t* buffer, uint16_t size, KeyIdentifier_t keyID, uint32_t* cmac ); + +/*! + * Verifies a CMAC (computes and compare with expected cmac) + * + * \param[IN] buffer - Data buffer + * \param[IN] size - Data buffer size + * \param[in] expectedCmac - Expected cmac + * \param[IN] keyID - Key identifier to determine the AES key to be used + * \retval - Status of the operation + */ +SecureElementStatus_t SecureElementVerifyAesCmac( uint8_t* buffer, uint16_t size, uint32_t expectedCmac, KeyIdentifier_t keyID ); + +/*! + * Encrypt a buffer + * + * \param[IN] buffer - Data buffer + * \param[IN] size - Data buffer size + * \param[IN] keyID - Key identifier to determine the AES key to be used + * \param[OUT] encBuffer - Encrypted buffer + * \retval - Status of the operation + */ +SecureElementStatus_t SecureElementAesEncrypt( uint8_t* buffer, uint16_t size, KeyIdentifier_t keyID, uint8_t* encBuffer ); + +/*! + * Derives and store a key + * + * \param[IN] version - LoRaWAN specification version currently in use. + * \param[IN] input - Input data from which the key is derived ( 16 byte ) + * \param[IN] rootKeyID - Key identifier of the root key to use to perform the derivation + * \param[IN] targetKeyID - Key identifier of the key which will be derived + * \retval - Status of the operation + */ +SecureElementStatus_t SecureElementDeriveAndStoreKey( Version_t version, uint8_t* input, KeyIdentifier_t rootKeyID, KeyIdentifier_t targetKeyID ); + +/*! + * Generates a random number + * + * \param[OUT] randomNum - 32 bit random number + * \retval - Status of the operation + */ +SecureElementStatus_t SecureElementRandomNumber( uint32_t* randomNum ); + +/*! + * Sets the DevEUI + * + * \param[IN] devEui - Pointer to the 16-byte devEUI + * \retval - Status of the operation + */ +SecureElementStatus_t SecureElementSetDevEui( uint8_t* devEui ); + +/*! + * Gets the DevEUI + * + * \retval - Pointer to the 16-byte devEUI + */ +uint8_t* SecureElementGetDevEui( void ); + +/*! + * Sets the JoinEUI + * + * \param[IN] joinEui - Pointer to the 16-byte joinEui + * \retval - Status of the operation + */ +SecureElementStatus_t SecureElementSetJoinEui( uint8_t* joinEui ); + +/*! + * Gets the DevEUI + * + * \retval - Pointer to the 16-byte joinEui + */ +uint8_t* SecureElementGetJoinEui( void ); + +/*! \} defgroup SECUREELEMENT */ + +#endif // __SECURE_ELEMENT_H__ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/app_conf_template.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/app_conf_template.h new file mode 100644 index 00000000..66077eef --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/app_conf_template.h @@ -0,0 +1,56 @@ +/** + ****************************************************************************** + * @file app_conf_template.h + * @author MCD Application Team + * @brief applic configuration, e.g. : debug, trace, low power, sensors + * @note this is a template: the app_conf.h shall be defined in the Project/... + ****************************************************************************** + * @attention + * + *

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

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __APP_CONF_H__ +#define __APP_CONF_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +/* Exported types ------------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ + +/* LoraWAN application configuration (Mw is configured by lorawan_conf.h) */ +#define ACTIVE_REGION LORAMAC_REGION_EU868 +#define HYBRID_ENABLED 0 + +#define LORAMAC_CLASSB_ACTIVE 0 /* 0 if not active, 1 if active */ + +/* Enable trace logs. Note verbose level can be selected in app_system.h */ +#define APP_LOG_ENABLED 1 + +#define DEBUGGER_ON 0 /* if ON (=1) it enables the debbugger plus 4 dgb pins */ + /* if OFF (=0) the debugger is OFF (lower consumption) */ + +/* External variables --------------------------------------------------------*/ +/* Exported macros -----------------------------------------------------------*/ +/* Defines -------------------------------------------------------------------*/ +/* Exported functions ------------------------------------------------------- */ + +#ifdef __cplusplus +} +#endif + +#endif /* __APP_CONF_H__ */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/app_system_template.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/app_system_template.h new file mode 100644 index 00000000..378f1515 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/app_system_template.h @@ -0,0 +1,62 @@ +/** + ****************************************************************************** + * @file app_system_template.h + * @author MCD Application Team + * @brief function prototypes for app_system.c file + * @note this is a template: the app_conf.h shall be defined in the Project/... + ****************************************************************************** + * @attention + * + *

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

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __APP_SYSTEM_H__ +#define __APP_SYSTEM_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include +#include "app_conf.h" +#include "trace.h" + +/* Exported types ------------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ +#define VERBOSE_LEVEL VLEVEL_L + +/* External variables --------------------------------------------------------*/ +/* Exported macros -----------------------------------------------------------*/ +#define APP_PPRINTF(...) do{ } while( 0!= TraceSend(0, 0, __VA_ARGS__) ) //Polling Mode +#define APP_TPRINTF(...) do{ {TraceSend(1, 0, __VA_ARGS__);} }while(0); //with timestamp +#define APP_PRINTF(...) do{ {TraceSend(0, 0, __VA_ARGS__);} }while(0); + + +#if defined (APP_LOG_ENABLED) && (APP_LOG_ENABLED == 1) +#define APP_LOG(TS,VL,...) do{ {TraceSend(TS, VL, __VA_ARGS__);} }while(0); +#elif defined (APP_LOG_ENABLED) && (APP_LOG_ENABLED == 0) /* APP_LOG disabled */ +#define APP_LOG(TS,VL,...) +#else +#error "APP_LOG_ENABLED not defined or out of range <0,1>" +#endif + +/* Defines -------------------------------------------------------------------*/ +/* Exported functions ------------------------------------------------------- */ + + +#ifdef __cplusplus +} +#endif + +#endif /* __APP_SYSTEM_H__ */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/lora-test.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/lora-test.c new file mode 100644 index 00000000..d0b33d56 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/lora-test.c @@ -0,0 +1,433 @@ +/****************************************************************************** + * @file lora-test.c + * @author MCD Application Team + * @brief lora API to drive the lora state Machine + ****************************************************************************** + * @attention + * + *

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

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ + + + +/* Includes ------------------------------------------------------------------*/ +#include "stddef.h" +//#include "lorawan_conf.h" /* Don't remove: needed for LORAMAC_CLASSB_ENABLED */ +//#include "mw_log_conf.h" +#include "LoRaMac.h" +#include "LoRaMacTest.h" +#include "lora.h" + + +/* Private typedef -----------------------------------------------------------*/ + +typedef struct ComplianceTest_s +{ + bool Running; + uint8_t State; + LoraConfirm_t IsTxConfirmed; + uint8_t DataBufferSize; + uint8_t DataBuffer[242]; /* max possible payload */ + uint16_t DownLinkCounter; + bool LinkCheck; + uint8_t DemodMargin; + uint8_t NbGateways; +} ComplianceTest_t; + +typedef enum +{ + CERTIF_TX_PROCESS = 0, + CERTIF_RX_PROCESS +} requestProcess_t; + +/* Private define ------------------------------------------------------------*/ +#define TEST_TX_DUTYCYCLE 5000 +/* Private variables ---------------------------------------------------------*/ + + +static requestProcess_t requested_process_id; + +/*! + * Timer to handle the application data transmission duty cycle + */ +static TimerEvent_t CertifTxNextPacketTimer; + +static ComplianceTest_t certifParam; +static LoraConfirm_t IsTxConfirmed; +static bool AdrEnableInit; +static McpsIndication_t *CertifMcpsIndication; +static MlmeReqJoin_t *CertifJoinParameters; + +/* Private functions ---------------------------------------------------------*/ +static bool CERTIF_running(void); + +static void CERTIF_DownLinkIncrement(void); + +static void CERTIF_linkCheck(MlmeConfirm_t *mlmeConfirm); + +static void CERTIF_rx_request(McpsIndication_t *mcpsIndication, MlmeReqJoin_t *JoinParameters); + +static void CERTIF_rx(McpsIndication_t *mcpsIndication, MlmeReqJoin_t *JoinParameters); + +static bool CERTIF_tx(void); + +static void OnCertifTxNextPacketTimerEvent(void *context); + +static void (*CertifProcessNotify)(void); + +static LoRaCertifCallback_t certifCb = +{ + CERTIF_running, + CERTIF_DownLinkIncrement, + CERTIF_linkCheck, + CERTIF_rx_request, +}; + +/* Exported functions definition---------------------------------------------------------*/ + +void CERTIF_Init(void (*CERTIF_ProcessNotify)(void)) +{ + /*initialises Callback into the Lora.c layer*/ + LORA_TestInit(&certifCb); + /*Register TestProcessNotify callback*/ + CertifProcessNotify = CERTIF_ProcessNotify; +} + +void CERTIF_Process(void) +{ + switch (requested_process_id) + { + case CERTIF_TX_PROCESS: + CERTIF_tx(); + break; + case CERTIF_RX_PROCESS: + CERTIF_rx(CertifMcpsIndication, CertifJoinParameters); + break; + default: + break; + } +} +static bool CERTIF_running(void) +{ + return certifParam.Running; +} + +static void CERTIF_DownLinkIncrement(void) +{ + certifParam.DownLinkCounter++; +} + +static void CERTIF_linkCheck(MlmeConfirm_t *mlmeConfirm) +{ + certifParam.LinkCheck = true; + certifParam.DemodMargin = mlmeConfirm->DemodMargin; + certifParam.NbGateways = mlmeConfirm->NbGateways; +} + +static bool CERTIF_tx(void) +{ + McpsReq_t mcpsReq; + LoRaMacTxInfo_t txInfo; +#if (ACTIVE_REGION == LORAMAC_REGION_US915) + bool abortTx = false; +#endif /* ACTIVE_REGION == LORAMAC_REGION_US915 */ + + if (certifParam.LinkCheck == true) + { + certifParam.LinkCheck = false; + certifParam.DataBufferSize = 3; + certifParam.DataBuffer[0] = 5; + certifParam.DataBuffer[1] = certifParam.DemodMargin; + certifParam.DataBuffer[2] = certifParam.NbGateways; + certifParam.State = 1; + } + else + { + switch (certifParam.State) + { + case 4: + certifParam.State = 1; + break; + case 1: + certifParam.DataBufferSize = 2; + certifParam.DataBuffer[0] = certifParam.DownLinkCounter >> 8; + certifParam.DataBuffer[1] = certifParam.DownLinkCounter; + break; + } + } + + if (LoRaMacQueryTxPossible(certifParam.DataBufferSize, &txInfo) != LORAMAC_STATUS_OK) + { +#if (ACTIVE_REGION == LORAMAC_REGION_US915) + abortTx = true; /* Do not send empty frame as some testers do not support it */ +#else + // Send empty frame in order to flush MAC commands + mcpsReq.Type = MCPS_UNCONFIRMED; + mcpsReq.Req.Unconfirmed.fBuffer = NULL; + mcpsReq.Req.Unconfirmed.fBufferSize = 0; + mcpsReq.Req.Unconfirmed.Datarate = DR_0; +#endif /* ACTIVE_REGION == LORAMAC_REGION_US915 */ + } + else + { + if (IsTxConfirmed == LORAWAN_UNCONFIRMED_MSG) + { + mcpsReq.Type = MCPS_UNCONFIRMED; + mcpsReq.Req.Unconfirmed.fPort = CERTIF_PORT; + mcpsReq.Req.Unconfirmed.fBufferSize = certifParam.DataBufferSize; + mcpsReq.Req.Unconfirmed.fBuffer = &(certifParam.DataBuffer); + mcpsReq.Req.Unconfirmed.Datarate = DR_0; + } + else + { + mcpsReq.Type = MCPS_CONFIRMED; + mcpsReq.Req.Confirmed.fPort = CERTIF_PORT; + mcpsReq.Req.Confirmed.fBufferSize = certifParam.DataBufferSize; + mcpsReq.Req.Confirmed.fBuffer = &(certifParam.DataBuffer); + mcpsReq.Req.Confirmed.NbTrials = 8; + mcpsReq.Req.Confirmed.Datarate = DR_0; + } + } + + /*cerification test on-going*/ + TimerStart(&CertifTxNextPacketTimer); + +#if (ACTIVE_REGION == LORAMAC_REGION_US915) + if (abortTx == true) + { + return true; + } +#endif /* ACTIVE_REGION == LORAMAC_REGION_US915 */ + + if (LoRaMacMcpsRequest(&mcpsReq) == LORAMAC_STATUS_OK) + { + return false; + } + return true; +} + +static void CERTIF_rx_request(McpsIndication_t *mcpsIndication, MlmeReqJoin_t *JoinParameters) +{ + CertifMcpsIndication = mcpsIndication; + CertifJoinParameters = JoinParameters; + + requested_process_id = CERTIF_RX_PROCESS; + + CertifProcessNotify(); +} + +static void CERTIF_rx(McpsIndication_t *mcpsIndication, MlmeReqJoin_t *JoinParameters) +{ + if (certifParam.Running == false) + { + // Check compliance test enable command (i) + if ((mcpsIndication->BufferSize == 4) && + (mcpsIndication->Buffer[0] == 0x01) && + (mcpsIndication->Buffer[1] == 0x01) && + (mcpsIndication->Buffer[2] == 0x01) && + (mcpsIndication->Buffer[3] == 0x01)) + { + MibRequestConfirm_t mibReq; + IsTxConfirmed = LORAWAN_UNCONFIRMED_MSG; + certifParam.DataBufferSize = 2; + certifParam.DownLinkCounter = 0; + certifParam.LinkCheck = false; + certifParam.DemodMargin = 0; + certifParam.NbGateways = 0; + certifParam.Running = true; + certifParam.State = 1; + + mibReq.Type = MIB_ADR; + + LoRaMacMibGetRequestConfirm(&mibReq); + AdrEnableInit = mibReq.Param.AdrEnable; + + mibReq.Type = MIB_ADR; + mibReq.Param.AdrEnable = true; + LoRaMacMibSetRequestConfirm(&mibReq); + + if ( (ACTIVE_REGION == LORAMAC_REGION_EU868) + || (ACTIVE_REGION == LORAMAC_REGION_RU864) + || (ACTIVE_REGION == LORAMAC_REGION_CN779) + || (ACTIVE_REGION == LORAMAC_REGION_EU433)) + { + LoRaMacTestSetDutyCycleOn(false); + } + + TimerInit(&CertifTxNextPacketTimer, OnCertifTxNextPacketTimerEvent); + TimerSetValue(&CertifTxNextPacketTimer, TEST_TX_DUTYCYCLE); + + /*confirm test mode activation */ + CERTIF_tx(); + } + } + + else + { + certifParam.State = mcpsIndication->Buffer[0]; + switch (certifParam.State) + { + case 0: // Check compliance test disable command (ii) + { + + certifParam.DownLinkCounter = 0; + certifParam.Running = false; + + MibRequestConfirm_t mibReq; + mibReq.Type = MIB_ADR; + mibReq.Param.AdrEnable = AdrEnableInit; + LoRaMacMibSetRequestConfirm(&mibReq); + + if ( (ACTIVE_REGION == LORAMAC_REGION_EU868) + || (ACTIVE_REGION == LORAMAC_REGION_RU864) + || (ACTIVE_REGION == LORAMAC_REGION_CN779) + || (ACTIVE_REGION == LORAMAC_REGION_EU433)) + { + LoRaMacTestSetDutyCycleOn(true); + } + + break; + } + case 1: // (iii, iv) + certifParam.DataBufferSize = 2; + break; + case 2: // Enable confirmed messages (v) + IsTxConfirmed = LORAWAN_CONFIRMED_MSG; + certifParam.State = 1; + break; + case 3: // Disable confirmed messages (vi) + IsTxConfirmed = LORAWAN_UNCONFIRMED_MSG; + certifParam.State = 1; + break; + case 4: // (vii) + certifParam.DataBufferSize = mcpsIndication->BufferSize; + + certifParam.DataBuffer[0] = 4; + for (uint8_t i = 1; i < certifParam.DataBufferSize; i++) + { + certifParam.DataBuffer[i] = mcpsIndication->Buffer[i] + 1; + } + break; + case 5: // (viii) + { + MlmeReq_t mlmeReq; + mlmeReq.Type = MLME_LINK_CHECK; + LoRaMacMlmeRequest(&mlmeReq); + break; + } + case 6: // (ix) + { + MlmeReq_t mlmeReq; + + // Disable TestMode and revert back to normal operation + + certifParam.DownLinkCounter = 0; + certifParam.Running = false; + + MibRequestConfirm_t mibReq; + mibReq.Type = MIB_ADR; + mibReq.Param.AdrEnable = AdrEnableInit; + LoRaMacMibSetRequestConfirm(&mibReq); + + mlmeReq.Type = MLME_JOIN; + mlmeReq.Req.Join = *JoinParameters; + + LoRaMacMlmeRequest(&mlmeReq); + break; + } + + case 7: // (x) + { + if (mcpsIndication->BufferSize == 3) + { + MlmeReq_t mlmeReq; + mlmeReq.Type = MLME_TXCW; + mlmeReq.Req.TxCw.Timeout = (uint16_t)((mcpsIndication->Buffer[1] << 8) | mcpsIndication->Buffer[2]); + LoRaMacMlmeRequest(&mlmeReq); + } + else if (mcpsIndication->BufferSize == 7) + { + MlmeReq_t mlmeReq; + mlmeReq.Type = MLME_TXCW_1; + mlmeReq.Req.TxCw.Timeout = (uint16_t)((mcpsIndication->Buffer[1] << 8) | mcpsIndication->Buffer[2]); + mlmeReq.Req.TxCw.Frequency = (uint32_t)((mcpsIndication->Buffer[3] << 16) | (mcpsIndication->Buffer[4] << 8) | mcpsIndication->Buffer[5]) * 100; + mlmeReq.Req.TxCw.Power = mcpsIndication->Buffer[6]; + LoRaMacMlmeRequest(&mlmeReq); + } + certifParam.State = 1; + break; + } + + case 8: // Send DeviceTimeReq + { + MlmeReq_t mlmeReq; + + mlmeReq.Type = MLME_DEVICE_TIME; + + LoRaMacMlmeRequest(&mlmeReq); + break; + } +#if defined (LORAMAC_CLASSB_ACTIVE) && ( LORAMAC_CLASSB_ACTIVE == 1 ) + case 9: // Switch end device Class + { + MibRequestConfirm_t mibReq; + + mibReq.Type = MIB_DEVICE_CLASS; + // CLASS_A = 0, CLASS_B = 1, CLASS_C = 2 + mibReq.Param.Class = (DeviceClass_t)mcpsIndication->Buffer[1]; + LoRaMacMibSetRequestConfirm(&mibReq); + break; + } + case 10: // Send PingSlotInfoReq + { + MlmeReq_t mlmeReq; + + mlmeReq.Type = MLME_PING_SLOT_INFO; + + mlmeReq.Req.PingSlotInfo.PingSlot.Value = mcpsIndication->Buffer[1]; + + LoRaMacMlmeRequest(&mlmeReq); + break; + } + case 11: // Send BeaconTimingReq + { + MlmeReq_t mlmeReq; + + mlmeReq.Type = MLME_BEACON_TIMING; + + LoRaMacMlmeRequest(&mlmeReq); + break; + } +#elif !defined (LORAMAC_CLASSB_ACTIVE) + #error LORAMAC_CLASSB_ACTIVE not defined +#endif + default: + break; + } + } + + if (certifParam.Running == false) + { + /*certification test stops*/ + TimerStop(&CertifTxNextPacketTimer); + } +} + +/*! + * \brief Function executed on TxNextPacket Timeout event + */ +static void OnCertifTxNextPacketTimerEvent(void *context) +{ + requested_process_id = CERTIF_TX_PROCESS; + CertifProcessNotify(); +} +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ + diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/lora-test.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/lora-test.h new file mode 100644 index 00000000..40bc4fff --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/lora-test.h @@ -0,0 +1,72 @@ +/****************************************************************************** + * @file lora_test.h + * @author MCD Application Team + * @brief lora API to drive the lora state Machine + ****************************************************************************** + * @attention + * + *

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

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ + + +/* Define to prevent recursive inclusion -------------------------------------*/ + +#ifndef __LORA_TEST_H__ +#define __LORA_TEST_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ + +#define CERTIF_PORT 224 +/* Exported types ------------------------------------------------------------*/ + + +/* External variables --------------------------------------------------------*/ +/* Exported macros -----------------------------------------------------------*/ +/* Exported functions ------------------------------------------------------- */ + +/** + * @brief Certification Initialisation + * @param [IN] Notification call back to the appl that a CERTIF_Process must be called + * @retval none + */ +void CERTIF_Init(void (*CERTIF_ProcessNotify)(void)); +/** + * @brief CERTIF_Process to be called when a CERTIF_ProcessNotify callback is generated + * @param [IN] none + * @retval none + */ +void CERTIF_Process(void); + +/* Lora Test callbacks*/ +typedef struct sLoRaCertifCallback +{ + bool (*running)(void); + + void (*DownLinkIncrement)(void); + + void (*linkCheck)(MlmeConfirm_t *mlmeConfirm); + + void (*rx)(McpsIndication_t *mcpsIndication, MlmeReqJoin_t *JoinParameters); + +} LoRaCertifCallback_t; + +#ifdef __cplusplus +} +#endif + +#endif /*__LORA_TEST_H__*/ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/lora.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/lora.c new file mode 100644 index 00000000..d313d87f --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/lora.c @@ -0,0 +1,985 @@ +/* + _/_/_/ _/_/_/ _/_/_/ _/_/_/ _/ _/ _/_/_/ _/ _/ _/_/_/_/ + _/ _/ _/ _/ _/ _/_/ _/ _/ _/ _/ _/ + _/_/_/ _/ _/_/ _/ _/ _/ _/ _/ _/_/ _/_/_/_/ _/_/_/ + _/ _/ _/ _/ _/ _/ _/_/ _/ _/ _/ _/ _/ +_/ _/ _/_/_/ _/_/_/ _/_/_/ _/ _/ _/_/_/ _/ _/ _/ + (C)2020/5/20 RisingHF, all rights reserved. + Lorawan API +*/ + +#include "timer.h" +#include "LoRaMac.h" +#include "LoRaMacTest.h" +#include "lora.h" +#include "app_system.h" +#include "lorawan_info.h" +#include "stm32_mem.h" + + +typedef struct +{ + lw_state_t otaa; /*< ENABLE if over the air activation, DISABLE otherwise */ + lw_state_t duty_cycle; /*< ENABLE if dutycyle is on, DISABLE otherwise */ + uint8_t DevEui[8]; /*< Device EUI */ + uint8_t JoinEui[8]; /*< Join Eui */ + uint8_t AppKey[16]; /*< Application Key */ + uint8_t NwkKey[16]; /*< Application Key */ + uint8_t NwkSEncKey[16]; /*< Network Session Key */ + uint8_t AppSKey[16]; /*< Application Session Key */ + uint8_t FNwkSIntKey[16]; /*< Application Session Key */ + uint8_t SNwkSIntKey[16]; /*< Application Session Key */ + int16_t Rssi; /*< Rssi of the received packet */ + int8_t Snr; /*< Snr of the received packet */ + uint8_t application_port; /*< Application port we will receive to */ + LoraConfirm_t ReqAck; /*< ENABLE if acknowledge is requested */ + int8_t TxDatarate; +}lorawan_configuration_t; + +/*! + * LoRa State Machine states + */ +typedef struct slorawan_param +{ + bool adr_enable; /*Activation state of adaptativeDatarate*/ + bool enable_public_network; /*Enable or disable a public network*/ + int8_t tx_datarate; /*Uplink datarate, if AdrEnable is off*/ + uint8_t confirm_retry; /*the uplink confirm frame retry times*/ + LoRaMacRegion_t active_region; /*region to open*/ + uint32_t dev_addr; +} lw_param_t; + +static lorawan_configuration_t lw_config = +{ + .otaa = LORA_ENABLE, + .duty_cycle = LORA_ENABLE, + .DevEui = LORAWAN_DEVICE_EUI, + .JoinEui = LORAWAN_JOIN_EUI, + .AppKey = LORAWAN_APP_KEY, + .NwkKey = LORAWAN_NWK_KEY, + .NwkSEncKey = LORAWAN_NWK_S_ENC_KEY, + .AppSKey = LORAWAN_APP_S_KEY, + .FNwkSIntKey = LORAWAN_F_NWK_S_INT_KEY, + .SNwkSIntKey = LORAWAN_S_NWK_S_INT_KEY, + .Rssi = 0, + .Snr = 0, + .ReqAck = LORAWAN_UNCONFIRMED_MSG, + .TxDatarate = 0 +}; + + +static lw_param_t lw_param_init = +{ + .adr_enable = 0, + .tx_datarate =DR_0, + .confirm_retry = 2, + .enable_public_network = LORAWAN_PUBLIC_NETWORK, + .active_region = LORAWAN_DEFAULT_ACTIVE_REGION, + .dev_addr = LORAWAN_DEVICE_ADDRESS +}; + +/*! + * Select either Device_Time_req or Beacon_Time_Req following LoRaWAN version + * - Device_Time_req Available for V1.0.3 or later + * - Beacon_time_Req Available for V1.0.2 and before + */ +#define USE_DEVICE_TIMING + + +#include "LoRaMacTest.h" + +/*for classB=============================================================*/ +#ifndef LORAMAC_CLASSB_ACTIVE + #error LORAMAC_CLASSB_ACTIVE not defined ( shall be <0 or 1> ) +#endif /* LORAMAC_CLASSB_ACTIVE */ + +#if ( LORAMAC_CLASSB_ACTIVE == 1 ) +/*! + * Default ping slots periodicity + * + * \remark periodicity is equal to 2^LORAWAN_DEFAULT_PING_SLOT_PERIODICITY seconds + * example: 2^3 = 8 seconds. The end-device will open an Rx slot every 8 seconds. + */ +#define LORAWAN_DEFAULT_PING_SLOT_PERIODICITY 0 +static uint8_t DefaultPingSlotPeriodicity; + +#endif /* LORAMAC_CLASSB_ACTIVE */ +/*=============================================================*/ + +#define HEX16(X) X[0],X[1], X[2],X[3], X[4],X[5], X[6],X[7],X[8],X[9], X[10],X[11], X[12],X[13], X[14],X[15] +#define HEX8(X) X[0],X[1], X[2],X[3], X[4],X[5], X[6],X[7] + +static MlmeReqJoin_t JoinParameters; + + + + +/* Dummy data sent periodically to let the tester respond with start test command*/ +static TimerEvent_t TxcertifTimer; + +/* Private macro -------------------------------------------------------------*/ +/* Private variables ---------------------------------------------------------*/ +/* Private function prototypes -----------------------------------------------*/ +/*for classB=============================================================*/ +#if ( LORAMAC_CLASSB_ACTIVE == 1 ) + static LoraErrorStatus lw_beacon_req(void); + static LoraErrorStatus lw_pingslot_req(void); + + #if defined( USE_DEVICE_TIMING ) + static LoraErrorStatus lw_device_time_req(void); + #else + static LoraErrorStatus lw_beacon_time_req(void); + #endif /* USE_DEVICE_TIMING */ +#endif /* LORAMAC_CLASSB_ACTIVE */ +/*======================================================================*/ + + + +static LoRaMacPrimitives_t LoRaMacPrimitives; +static LoRaMacCallback_t LoRaMacCallbacks; +static MibRequestConfirm_t mibReq; + + +static LoRaMainCallback_t *LoRaMainCallbacks; + + +/* Private user code ---------------------------------------------------------*/ +/*! + * \brief MCPS-Confirm event function + * + * \param [IN] McpsConfirm - Pointer to the confirm structure, + * containing confirm attributes. + */ +static void McpsConfirm(McpsConfirm_t *mcpsConfirm) +{ + lw_app_data_t app_data = {0}; + + if (mcpsConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK) + { + switch (mcpsConfirm->McpsRequest) + { + case MCPS_UNCONFIRMED: + case MCPS_CONFIRMED: + { + // Check Datarate + // Check TxPower + // Check AckReceived + + // Check NbTrials + break; + } + case MCPS_PROPRIETARY: + { + break; + } + default: + break; + + + } + } + app_data.ack = mcpsConfirm->AckReceived; + LoRaMainCallbacks->app_events(APP_TX_DONE_EVENT,&app_data); + +} + +/*! + * \brief MCPS-Indication event function + * + * \param [IN] mcpsIndication - Pointer to the indication structure, + * containing indication attributes. + */ +static void McpsIndication(McpsIndication_t *mcpsIndication) +{ + lw_app_data_t app_data ={0}; + + if(mcpsIndication->Status != LORAMAC_EVENT_INFO_STATUS_OK) + { + return; + } + + switch (mcpsIndication->McpsIndication) + { + case MCPS_UNCONFIRMED: + { + break; + } + case MCPS_CONFIRMED: + { + break; + } + case MCPS_PROPRIETARY: + { + break; + } + case MCPS_MULTICAST: + { + break; + } + default: + break; + } + + // Check Buffer + // Check BufferSize + // Check Rssi + // Check Snr + // Check RxSlot + if(mcpsIndication->RxData == true) + { + app_data.port = mcpsIndication->Port; + app_data.size = mcpsIndication->BufferSize; + app_data.buff = mcpsIndication->Buffer; + app_data.fpending = mcpsIndication->FramePending; + + lw_config.Rssi = mcpsIndication->Rssi; + lw_config.Snr = mcpsIndication->Snr; + LoRaMainCallbacks->app_events(APP_RX_EVENT,&app_data); + } +} + +/*! + * \brief MLME-Confirm event function + * + * \param [IN] MlmeConfirm - Pointer to the confirm structure, + * containing confirm attributes. + */ +static void MlmeConfirm(MlmeConfirm_t *mlmeConfirm) +{ +#if (LORAMAC_CLASSB_ACTIVE == 1) + MibRequestConfirm_t mibReq; +#endif /* LORAMAC_CLASSB_ACTIVE */ + + switch (mlmeConfirm->MlmeRequest) + { + case MLME_JOIN: + { + if(mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK) + { + // Status is OK, node has joined the network + LoRaMainCallbacks->app_events(APP_JOINED_EVENT,NULL); + #if ( LORAMAC_CLASSB_ACTIVE == 1 ) + #if defined( USE_DEVICE_TIMING ) + lw_device_time_req(); + #else + lw_beacon_time_req(); + #endif /* USE_DEVICE_TIMING */ + #endif /* LORAMAC_CLASSB_ACTIVE */ + } + else + { + // Join was not successful. Try to join again + lw_join(); + } + break; + } + case MLME_LINK_CHECK: + { + if (mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK) + { + + } + break; + } +#if ( LORAMAC_CLASSB_ACTIVE == 1 ) + case MLME_BEACON_ACQUISITION: + { + if(mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK) + { + /* Beacon has been acquired */ + /* REquest Server for Ping Slot */ + lw_pingslot_req(); + } + else + { + /* Beacon not acquired */ + /* Search again */ + /* we can check if the MAC has received a time reference for the beacon*/ + /* in this case do either a Device_Time_Req or a Beacon_Timing_req*/ + lw_beacon_req(); + } + break; + } + case MLME_PING_SLOT_INFO: + { + if(mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK) + { + /* class B is now ativated*/ + mibReq.Type = MIB_DEVICE_CLASS; + mibReq.Param.Class = CLASS_B; + LoRaMacMibSetRequestConfirm(&mibReq); + + if((lw_param_init.active_region == LORAMAC_REGION_AU915) || (lw_param_init.active_region == LORAMAC_REGION_US915)) + { + mibReq.Type = MIB_PING_SLOT_DATARATE; + mibReq.Param.PingSlotDatarate = DR_8; + LoRaMacMibSetRequestConfirm(&mibReq); + } + /*notify upper layer*/ + /*ClassB*/ + DeviceClass_t cur_class =CLASS_B; + LoRaMainCallbacks->app_events(APP_CONFIRM_ClASS_EVENT,&cur_class); + } + else + { + lw_pingslot_req(); + } + break; + } +#if defined( USE_DEVICE_TIMING ) + case MLME_DEVICE_TIME: + { + if (mlmeConfirm->Status != LORAMAC_EVENT_INFO_STATUS_OK) + { + lw_device_time_req(); + } + } +#endif /* USE_DEVICE_TIMING */ +#endif /* LORAMAC_CLASSB_ACTIVE */ + default: + break; + } +} + +/*! + * \brief MLME-Indication event function + * + * \param [IN] MlmeIndication - Pointer to the indication structure. + */ +static void MlmeIndication(MlmeIndication_t *MlmeIndication) +{ + lw_app_data_t app_data ={0}; + +#if ( LORAMAC_CLASSB_ACTIVE == 1 ) + MibRequestConfirm_t mibReq; +#endif /* LORAMAC_CLASSB_ACTIVE */ + + switch (MlmeIndication->MlmeIndication) + { + case MLME_SCHEDULE_UPLINK: + { + app_data.fpending = 1; + // The MAC signals that we shall provide an uplink as soon as possible + LoRaMainCallbacks->app_events(APP_RX_EVENT,&app_data); + break; + } + #if ( LORAMAC_CLASSB_ACTIVE == 1 ) + case MLME_BEACON_LOST: + { + // Switch to class A again + mibReq.Type = MIB_DEVICE_CLASS; + mibReq.Param.Class = CLASS_A; + LoRaMacMibSetRequestConfirm(&mibReq); + lw_beacon_req(); + break; + } + case MLME_BEACON: + { + if(MlmeIndication->Status == LORAMAC_EVENT_INFO_STATUS_BEACON_LOCKED) + { + APP_LOG("BEACON RECEIVED\n\r"); + } + else + { + APP_LOG("BEACON NOT RECEIVED\n\r"); + } + break; + } + #endif /* LORAMAC_CLASSB_ACTIVE */ + default: + break; + } +} +/** + * lorawan init + */ +void lw_init(LoRaMainCallback_t *callbacks) +{ + LoRaMacInfo_t *LoRaMacRegionInfo; + + LoraMacInfo_Init(); + LoRaMacRegionInfo = LoraMacInfo_GetPtr(); + if(LoRaMacRegionInfo->Region == 0) + { + APP_PRINTF("error: At leats one region shall be defined in the MW: check lorawan_conf.h \n\r"); + while(1) {} /* At leats one region shall be defined in MW */ + } + + #if ( LORAMAC_CLASSB_ACTIVE == 1 ) + if(LoRaMacRegionInfo->ClassB != 1) + { + APP_PRINTF("error: ClassB is not defined in the MW: set lorawan_conf.h accordingly\n\r"); + while(1) {} /* error: ClassB is not defined in the MW*/ + } + #elif ( LORAMAC_CLASSB_ACTIVE == 0 ) + if(LoRaMacRegionInfo->ClassB != 0) + { + APP_PRINTF("error: missmatch between applic and MW CLASSB defines: set lorawan_conf.h according to app_conf.h\n\r"); + while(1) {} /* error: ClassB should not be defined in the MW*/ + } + #endif /* LORAMAC_CLASSB_ACTIVE */ + + /* init the main call backs*/ + LoRaMainCallbacks = callbacks; + + #if (STATIC_DEVICE_EUI != 1) + LoRaMainCallbacks->BoardGetUniqueId(lw_config.DevEui); + #endif + + #if (STATIC_DEVICE_ADDRESS != 1) + // Choose a "pseudo-random" device address + lw_param_init.dev_addr = LoRaMainCallbacks->BoardGetRandomSeed(); + #endif + + LoRaMacPrimitives.MacMcpsConfirm = McpsConfirm; + LoRaMacPrimitives.MacMcpsIndication = McpsIndication; + LoRaMacPrimitives.MacMlmeConfirm = MlmeConfirm; + LoRaMacPrimitives.MacMlmeIndication = MlmeIndication; + LoRaMacCallbacks.GetBatteryLevel = LoRaMainCallbacks->BoardGetBatteryLevel; + LoRaMacCallbacks.GetTemperatureLevel = LoRaMainCallbacks->BoardGetTemperatureLevel; + LoRaMacCallbacks.MacProcessNotify = LoRaMainCallbacks->MacProcessNotify; + + if(0U != ((1<<(lw_param_init.active_region))& (LoRaMacRegionInfo->Region))) + { + LoRaMacInitialization(&LoRaMacPrimitives, &LoRaMacCallbacks, lw_param_init.active_region); + } + else + { + APP_PRINTF("error: Region is not defined in the MW: set lorawan_conf.h accordingly\n\r"); + while(1) {} /* error: Region is not defined in the MW */ + } + if((lw_param_init.active_region == LORAMAC_REGION_CN470)||(lw_param_init.active_region == LORAMAC_REGION_CN470ALI)) + { + uint16_t channelMask[] = {0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}; + if(lw_param_init.active_region == LORAMAC_REGION_CN470){ + channelMask[0] = 0x00FF; + } + else{ + channelMask[0] = 0xFF00; + } + mibReq.Type = MIB_CHANNELS_MASK; + mibReq.Param.ChannelsMask = channelMask; + LoRaMacMibSetRequestConfirm(&mibReq); + mibReq.Type = MIB_CHANNELS_DEFAULT_MASK; + mibReq.Param.ChannelsDefaultMask = channelMask; + LoRaMacMibSetRequestConfirm(&mibReq); + } + + lw_config_otaa_set(lw_config.otaa); + + mibReq.Type = MIB_DEV_EUI; + mibReq.Param.DevEui = lw_config.DevEui; + LoRaMacMibSetRequestConfirm(&mibReq); + + mibReq.Type = MIB_JOIN_EUI; + mibReq.Param.JoinEui = lw_config.JoinEui; + LoRaMacMibSetRequestConfirm(&mibReq); + + mibReq.Type = MIB_ADR; + mibReq.Param.AdrEnable = lw_param_init.adr_enable; + LoRaMacMibSetRequestConfirm(&mibReq); + + mibReq.Type = MIB_PUBLIC_NETWORK; + mibReq.Param.EnablePublicNetwork = lw_param_init.enable_public_network; + LoRaMacMibSetRequestConfirm(&mibReq); + + mibReq.Type = MIB_APP_KEY; + mibReq.Param.AppKey = lw_config.AppKey; + LoRaMacMibSetRequestConfirm(&mibReq); + + mibReq.Type = MIB_NWK_KEY; + mibReq.Param.NwkKey = lw_config.NwkKey; + LoRaMacMibSetRequestConfirm(&mibReq); + + mibReq.Type = MIB_DEVICE_CLASS; + mibReq.Param.Class = CLASS_A; + LoRaMacMibSetRequestConfirm(&mibReq); + + if((lw_param_init.active_region == LORAMAC_REGION_EU868) + || (lw_param_init.active_region == LORAMAC_REGION_RU864) + || (lw_param_init.active_region == LORAMAC_REGION_CN779) + || (lw_param_init.active_region == LORAMAC_REGION_EU433)) + { + LoRaMacTestSetDutyCycleOn(true); + lw_config.duty_cycle = LORA_ENABLE; + } + else + { + lw_config.duty_cycle = LORA_DISABLE; + } + + mibReq.Type = MIB_SYSTEM_MAX_RX_ERROR; + mibReq.Param.SystemMaxRxError = 20; + LoRaMacMibSetRequestConfirm(&mibReq); + + #if ( LORAMAC_CLASSB_ACTIVE == 1 ) + if (LoRaMacRegionInfo->ClassB == 1) + { + DefaultPingSlotPeriodicity = LORAWAN_DEFAULT_PING_SLOT_PERIODICITY; + } + #endif /* LORAMAC_CLASSB_ACTIVE */ + + /*set Mac statein Idle*/ + LoRaMacStart(); +} + +LoRaMacStatus_t lw_join(void) +{ + LoRaMacStatus_t status; + + if (lw_config.otaa == LORA_ENABLE) + { + MlmeReq_t mlmeReq; + + mlmeReq.Type = MLME_JOIN; + mlmeReq.Req.Join.Datarate = lw_param_init.tx_datarate; + + JoinParameters = mlmeReq.Req.Join; + status = LoRaMacMlmeRequest(&mlmeReq); + } + else + { + /*no Join in abp*/ + status = LORAMAC_STATUS_BUSY; + } + + return status; +} + +LoraFlagStatus lw_join_status(void) +{ + MibRequestConfirm_t mibReq; + + mibReq.Type = MIB_NETWORK_ACTIVATION; + + LoRaMacMibGetRequestConfirm(&mibReq); + + if(mibReq.Param.NetworkActivation == ACTIVATION_TYPE_NONE) + { + return LORA_RESET; + } + else + { + return LORA_SET; + } +} + +LoRaMacStatus_t lw_current_chmask_get(LWChannel_mask_t* ch) +{ + mibReq.Type = MIB_CHANNELS_MASK; + mibReq.Param.ChannelsMask = (uint16_t*)ch; + LoRaMacStatus_t sta = LoRaMacMibGetRequestConfirm(&mibReq); + + if(LORAMAC_STATUS_OK == sta) + { + for(uint8_t i=0;i<6;i++) + { + ch->mask[i] = mibReq.Param.ChannelsMask[i]; + } + } + return sta; +} +LoRaMacStatus_t lw_current_chmask_set(const LWChannel_mask_t* ch) +{ + static bool only; + + if(only == false) + { + only = true; + mibReq.Type = MIB_CHANNELS_DEFAULT_MASK; + mibReq.Param.ChannelsDefaultMask = (uint16_t*)ch; + LoRaMacMibSetRequestConfirm(&mibReq); + } + + mibReq.Type = MIB_CHANNELS_MASK; + mibReq.Param.ChannelsMask = (uint16_t*)ch; + return LoRaMacMibSetRequestConfirm(&mibReq); +} +LoRaMacStatus_t lw_rxwin2_ch_set(const RxChannelParams_t* rx2_ch) +{ + LoRaMacStatus_t sta; + + mibReq.Type = MIB_RX2_CHANNEL; + mibReq.Param.Rx2Channel.Frequency = rx2_ch->Frequency; + mibReq.Param.Rx2Channel.Datarate = rx2_ch->Datarate; + + sta = LoRaMacMibSetRequestConfirm(&mibReq); + return sta; +} + +LoRaMacStatus_t lw_rxwin2_ch_get(RxChannelParams_t* rx2_ch) +{ + LoRaMacStatus_t sta; + mibReq.Type = MIB_RX2_CHANNEL; + + sta = LoRaMacMibGetRequestConfirm(&mibReq); + + if(LORAMAC_STATUS_OK == sta) + { + *rx2_ch = mibReq.Param.Rx2Channel; + } + return sta; + } + + +LoRaMacStatus_t lw_adr_set(lw_state_t sta) +{ + LoRaMacStatus_t n_sta=0; + + mibReq.Type = MIB_ADR; + mibReq.Param.AdrEnable = sta ; + n_sta = LoRaMacMibSetRequestConfirm(&mibReq); + if(LORAMAC_STATUS_OK == n_sta){ + lw_param_init.adr_enable = sta; + } + return n_sta; +} + + +LoRaMacStatus_t lw_send(lw_app_data_t *app_data, LoraConfirm_t IsTxConfirmed) +{ + McpsReq_t mcpsReq; + LoRaMacTxInfo_t txInfo; + + if(LoRaMacQueryTxPossible(app_data->size, &txInfo) != LORAMAC_STATUS_OK) + { + // Send empty frame in order to flush MAC commands + mcpsReq.Type = MCPS_UNCONFIRMED; + mcpsReq.Req.Unconfirmed.fBuffer = NULL; + mcpsReq.Req.Unconfirmed.fBufferSize = 0; + mcpsReq.Req.Unconfirmed.Datarate = lw_config_tx_datarate_get() ; + } + else + { + if (IsTxConfirmed == LORAWAN_UNCONFIRMED_MSG) + { + mcpsReq.Type = MCPS_UNCONFIRMED; + mcpsReq.Req.Unconfirmed.fPort = app_data->port; + mcpsReq.Req.Unconfirmed.fBufferSize = app_data->size; + mcpsReq.Req.Unconfirmed.fBuffer = app_data->buff; + mcpsReq.Req.Unconfirmed.Datarate = lw_config_tx_datarate_get() ; + } + else + { + mcpsReq.Type = MCPS_CONFIRMED; + mcpsReq.Req.Confirmed.fPort = app_data->port; + mcpsReq.Req.Confirmed.fBufferSize = app_data->size; + mcpsReq.Req.Confirmed.fBuffer = app_data->buff; + mcpsReq.Req.Confirmed.NbTrials = lw_confirm_retry_get(); + mcpsReq.Req.Confirmed.Datarate = lw_config_tx_datarate_get() ; + } + } + return LoRaMacMcpsRequest(&mcpsReq); + +} + +#if ( LORAMAC_CLASSB_ACTIVE == 1 ) + #if defined( USE_DEVICE_TIMING ) + static LoRaMacStatus_t lw_device_time_req(void) + { + MlmeReq_t mlmeReq; + + mlmeReq.Type = MLME_DEVICE_TIME; + + return LoRaMacMlmeRequest(&mlmeReq); + } + #else + static LoRaMacStatus_t lw_beacon_time_req(void) + { + MlmeReq_t mlmeReq; + + mlmeReq.Type = MLME_BEACON_TIMING; + + return LoRaMacMlmeRequest(&mlmeReq); + } + #endif /* USE_DEVICE_TIMING */ + + static LoRaMacStatus_t lw_beacon_req(void) + { + MlmeReq_t mlmeReq; + + mlmeReq.Type = MLME_BEACON_ACQUISITION; + + return LoRaMacMlmeRequest(&mlmeReq); + } + + static LoRaMacStatus_t lw_pingslot_req(void) + { + MlmeReq_t mlmeReq; + + mlmeReq.Type = MLME_LINK_CHECK; + LoRaMacMlmeRequest(&mlmeReq); + + mlmeReq.Type = MLME_PING_SLOT_INFO; + mlmeReq.Req.PingSlotInfo.PingSlot.Fields.Periodicity = DefaultPingSlotPeriodicity; + mlmeReq.Req.PingSlotInfo.PingSlot.Fields.RFU = 0; + + return LoRaMacMlmeRequest(&mlmeReq); + } +#endif /* LORAMAC_CLASSB_ACTIVE */ + +LoRaMacStatus_t lw_request_class(DeviceClass_t newClass) +{ + LoRaMacStatus_t Errorstatus = LORAMAC_STATUS_OK; + + MibRequestConfirm_t mibReq; + DeviceClass_t currentClass; + + mibReq.Type = MIB_DEVICE_CLASS; + LoRaMacMibGetRequestConfirm(&mibReq); + + currentClass = mibReq.Param.Class; + /*attempt to swicth only if class update*/ + if(currentClass != newClass) + { + switch(newClass) + { + case CLASS_A: + { + mibReq.Param.Class = CLASS_A; + + Errorstatus = LoRaMacMibSetRequestConfirm(&mibReq); + if(Errorstatus == LORAMAC_STATUS_OK) + { + DeviceClass_t cur_class =CLASS_A; + LoRaMainCallbacks->app_events(APP_CONFIRM_ClASS_EVENT,&cur_class); + } + break; + } + case CLASS_B: + { + #if (LORAMAC_CLASSB_ACTIVE == 1) + if(currentClass != CLASS_A) + { + Errorstatus = LORAMAC_STATUS_ERROR; + } + /*switch is not instantanuous*/ + Errorstatus = lw_beacon_req(); + #else + APP_PRINTF("warning: LORAMAC_CLASSB_ACTIVE has not been defined at compilation\n\r"); + #endif /* LORAMAC_CLASSB_ACTIVE */ + break; + } + case CLASS_C: + { + if (currentClass != CLASS_A) + { + Errorstatus = LORAMAC_STATUS_ERROR; + } + /*switch is instantanuous*/ + mibReq.Param.Class = CLASS_C; + + Errorstatus = LoRaMacMibSetRequestConfirm(&mibReq); + + if(Errorstatus == LORAMAC_STATUS_OK) + { + DeviceClass_t cur_class =CLASS_C; + LoRaMainCallbacks->app_events(APP_CONFIRM_ClASS_EVENT,&cur_class); + } + break; + } + default: + break; + } + } + return Errorstatus; +} + +DeviceClass_t lw_get_current_class(void) +{ + MibRequestConfirm_t mibReq; + + mibReq.Type = MIB_DEVICE_CLASS; + LoRaMacMibGetRequestConfirm(&mibReq); + + return mibReq.Param.Class; +} + +void lw_config_otaa_set(lw_state_t otaa) +{ + lw_config.otaa = otaa; + + if (lw_config.otaa == LORA_ENABLE) + { + APP_PPRINTF("OTAA Mode enabled\n\r"); + APP_PPRINTF("DevEui= %02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X\n\r", HEX8(lw_config.DevEui)); + APP_PPRINTF("JoinEui= %02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X\n\r", HEX8(lw_config.JoinEui)); + APP_PPRINTF("AppKey= %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n\r", HEX16(lw_config.AppKey)); + + mibReq.Type = MIB_NETWORK_ACTIVATION; + mibReq.Param.NetworkActivation = ACTIVATION_TYPE_NONE; + LoRaMacMibSetRequestConfirm(&mibReq); + } + else + { + APP_PPRINTF("ABP Mode enabled\n\r"); + APP_PPRINTF("DevEui= %02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X\n\r", HEX8(lw_config.DevEui)); + APP_PPRINTF("DevAdd= %08X\n\r", lw_param_init.dev_addr) ; + APP_PPRINTF("NwkSKey= %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n\r", HEX16(lw_config.NwkSEncKey)); + APP_PPRINTF("AppSKey= %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n\r", HEX16(lw_config.AppSKey)); + + mibReq.Type = MIB_NET_ID; + mibReq.Param.NetID = LORAWAN_NETWORK_ID; + LoRaMacMibSetRequestConfirm(&mibReq); + + mibReq.Type = MIB_DEV_ADDR; + mibReq.Param.DevAddr = lw_param_init.dev_addr; + LoRaMacMibSetRequestConfirm(&mibReq); + + mibReq.Type = MIB_F_NWK_S_INT_KEY; + mibReq.Param.FNwkSIntKey = lw_config.FNwkSIntKey; + LoRaMacMibSetRequestConfirm(&mibReq); + + mibReq.Type = MIB_S_NWK_S_INT_KEY; + mibReq.Param.SNwkSIntKey = lw_config.SNwkSIntKey; + LoRaMacMibSetRequestConfirm(&mibReq); + + mibReq.Type = MIB_NWK_S_ENC_KEY; + mibReq.Param.NwkSEncKey = lw_config.NwkSEncKey; + LoRaMacMibSetRequestConfirm(&mibReq); + + mibReq.Type = MIB_APP_S_KEY; + mibReq.Param.AppSKey = lw_config.AppSKey; + LoRaMacMibSetRequestConfirm(&mibReq); + + mibReq.Type = MIB_NETWORK_ACTIVATION; + mibReq.Param.NetworkActivation = ACTIVATION_TYPE_ABP; + LoRaMacMibSetRequestConfirm(&mibReq); + + // Enable legacy mode to operate according to LoRaWAN Spec. 1.0.3 + Version_t abpLrWanVersion; + + abpLrWanVersion.Fields.Major = 1; + abpLrWanVersion.Fields.Minor = 0; + abpLrWanVersion.Fields.Revision = 3; + abpLrWanVersion.Fields.Rfu = 0; + + mibReq.Type = MIB_ABP_LORAWAN_VERSION; + mibReq.Param.AbpLrWanVersion = abpLrWanVersion; + LoRaMacMibSetRequestConfirm(&mibReq); + } +} + +lw_state_t lw_config_otaa_get(void) +{ + return lw_config.otaa; +} + + +void lw_confirm_retry_set(uint8_t retry) +{ + lw_param_init.confirm_retry = retry; +} + + +uint8_t lw_confirm_retry_get(void) +{ + return lw_param_init.confirm_retry; +} + + +void lw_config_duty_cycle_set(lw_state_t duty_cycle) +{ + lw_config.duty_cycle = duty_cycle; + LoRaMacTestSetDutyCycleOn((duty_cycle == LORA_ENABLE) ? 1 : 0); +} + +lw_state_t lw_config_duty_cycle_get(void) +{ + return lw_config.duty_cycle; +} + +uint8_t *lw_config_deveui_get(void) +{ + return lw_config.DevEui; +} + +uint8_t *lw_config_joineui_get(void) +{ + return lw_config.JoinEui; +} + +void lw_config_joineui_set(uint8_t joineui[8]) +{ + UTIL_MEM_cpy_8(lw_config.JoinEui, joineui, sizeof(lw_config.JoinEui)); + + mibReq.Type = MIB_JOIN_EUI; + mibReq.Param.JoinEui = lw_config.JoinEui; + LoRaMacMibSetRequestConfirm(&mibReq); +} + +uint8_t *lw_config_appkey_get(void) +{ + return lw_config.AppKey; +} + +void lw_config_appkey_set(uint8_t appkey[16]) +{ + UTIL_MEM_cpy_8(lw_config.AppKey, appkey, sizeof(lw_config.AppKey)); + UTIL_MEM_cpy_8(lw_config.NwkKey, appkey, sizeof(lw_config.NwkKey)); + + mibReq.Type = MIB_APP_KEY; + mibReq.Param.AppKey = lw_config.AppKey; + LoRaMacMibSetRequestConfirm(&mibReq); + + mibReq.Type = MIB_NWK_KEY; + mibReq.Param.NwkKey = lw_config.NwkKey; + LoRaMacMibSetRequestConfirm(&mibReq); +} + +void lw_config_reqack_set(LoraConfirm_t reqack) +{ + lw_config.ReqAck = reqack; +} + +LoraConfirm_t lw_config_reqack_get(void) +{ + return lw_config.ReqAck; +} + +int8_t lw_config_snr_get(void) +{ + return lw_config.Snr; +} + +int16_t lw_config_rssi_get(void) +{ + return lw_config.Rssi; +} + +void lw_config_tx_datarate_set(int8_t TxDataRate) +{ + lw_config.TxDatarate = TxDataRate; +} + +int8_t lw_config_tx_datarate_get(void) +{ + return lw_config.TxDatarate; +} + + +LoRaMacRegion_t lora_region_get(void) +{ + return lw_param_init.active_region; +} +#if ( LORAMAC_CLASSB_ACTIVE == 1 ) + LoRaMacClassBNvmCtx_t* lw_get_classBCtx(void) + { + mibReq.Type = MIB_NVM_CTXS; + LoRaMacMibGetRequestConfirm( &mibReq ); + return ( LoRaMacClassBNvmCtx_t*) mibReq.Param.Contexts->ClassBNvmCtx; + } + + uint8_t lw_get_pingslot_periodicity(void) + { + return DefaultPingSlotPeriodicity; + } + + void lw_set_pingslot_periodicity(uint8_t PingSlotPeriodicity) + { + DefaultPingSlotPeriodicity= PingSlotPeriodicity; + } +#endif /* LORAMAC_CLASSB_ACTIVE */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ + diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/lora.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/lora.h new file mode 100644 index 00000000..41fc7fee --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/lora.h @@ -0,0 +1,407 @@ +/** + ****************************************************************************** + * @file lora.h + * @author MCD Application Team + * @brief lora API to drive the lora state Machine + ****************************************************************************** + * @attention + * + *

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

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ + +#ifndef __LORA_H__ +#define __LORA_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "Commissioning.h" +#include "LoRaMac.h" +#include "app_conf.h" +#if defined (LORAMAC_CLASSB_ACTIVE) && ( LORAMAC_CLASSB_ACTIVE == 1 ) +#include "LoRaMacClassB.h" +#elif !defined (LORAMAC_CLASSB_ACTIVE) + #error LORAMAC_CLASSB_ACTIVE not defined +#endif /* LORAMAC_CLASSB_ACTIVE */ + +#include "region/Region.h" + + +/* Exported constants --------------------------------------------------------*/ +/*! +* LoRaWAN confirmed messages +*/ + +#define LORAWAN_ADR_ON 1 +#define LORAWAN_ADR_OFF 0 +/* Exported types ------------------------------------------------------------*/ + +/*! + * Application Data structure + */ +typedef struct +{ + /*point to the LoRa App data buffer*/ + uint8_t *buff; + /*LoRa App data buffer size*/ + uint8_t size; + /*Port on which the LoRa App is data is sent/ received*/ + uint8_t port; + + uint8_t ack; + + uint8_t fpending; + +} lw_app_data_t; + + +typedef enum +{ + LORA_RESET = 0, + LORA_SET = !LORA_RESET +} LoraFlagStatus; + +typedef enum +{ + LORA_DISABLE = 0, + LORA_ENABLE = !LORA_DISABLE +} lw_state_t; + +typedef enum +{ + LORA_ERROR = -1, + LORA_SUCCESS = 0 +} LoraErrorStatus; + +typedef enum +{ + LORAWAN_UNCONFIRMED_MSG = 0, + LORAWAN_CONFIRMED_MSG = !LORAWAN_UNCONFIRMED_MSG +} LoraConfirm_t; + +typedef enum +{ + LORA_TRUE = 0, + LORA_FALSE = !LORA_TRUE +} LoraBool_t; + + +typedef enum +{ + APP_RX_EVENT, + APP_JOINED_EVENT, + APP_CONFIRM_ClASS_EVENT, + APP_TX_DONE_EVENT, +}APP_EVENT_t; + + +/* Lora Main callbacks*/ +typedef struct sLoRaMainCallback +{ + /*! + * @brief mac event callback + * + * @retval null + */ + void (*app_events)(APP_EVENT_t evt,const void* param); + /*! + *\brief Will be called each time a Radio IRQ is handled by the MAC + * layer. + * + *\warning Runs in a IRQ context. Should only change variables state. + */ + void (*MacProcessNotify)(void); + /*! + * @brief Get the current battery level + * + * @retval value battery level ( 0: very low, 254: fully charged ) + */ + uint8_t (*BoardGetBatteryLevel)(void); + /*! + * \brief Get the current temperature + * + * \retval value temperature in degreeCelcius( q7.8 ) + */ + uint16_t (*BoardGetTemperatureLevel)(void); + /*! + * @brief Gets the board 64 bits unique ID + * + * @param [IN] id Pointer to an array that will contain the Unique ID + */ + void (*BoardGetUniqueId)(uint8_t *id); + /*! + * Returns a pseudo random seed generated using the MCU Unique ID + * + * @retval seed Generated pseudo random seed + */ + uint32_t (*BoardGetRandomSeed)(void); + +} LoRaMainCallback_t; + + + +typedef struct +{ + uint16_t mask[6]; +}LWChannel_mask_t; + + +/* External variables --------------------------------------------------------*/ +/* Exported macros -----------------------------------------------------------*/ +/* Exported functions ------------------------------------------------------- */ +/** + * @brief Lora Initialisation + * @param [IN] LoRaMainCallback_t + * @param [IN] application parmaters + * @retval none + */ +void lw_init(LoRaMainCallback_t *callbacks); + +/** + * @brief run Lora classA state Machine + * @param [IN] none + * @retval none + */ +LoRaMacStatus_t lw_send(lw_app_data_t *AppData, LoraConfirm_t IsTxConfirmed); + + + +/** + * @brief Join a Lora Network in classA + * @Note if the device is ABP, this is a pass through functon + * @param [IN] none + * @retval none + */ +LoRaMacStatus_t lw_join(void); + +/** + * @brief Check whether the Device is joined to the network + * @param [IN] none + * @retval returns LORA_SET if joined + */ +LoraFlagStatus lw_join_status(void); + +/*===================================================================== +if you want to use the Api below , you must call them after LORA_Init + +because it has been made Initialization configuration + +======================================================================*/ + +/** + * @brief Enable or Disable ADR + * @param [IN] lw_state_t + * @retval returns req state + */ +LoRaMacStatus_t lw_adr_set(lw_state_t sta); + + +/** + * @brief uplink confirm retry times setting + * @param [IN] times + * @retval returns req state + */ +void lw_confirm_retry_set(uint8_t retry); + +/** + * @brief return uplink confirm retry times + * @param [IN] null + * @retval times + */ +uint8_t lw_confirm_retry_get(void); + + +/** + * @brief set the currnt chmask + * @param [IN] LWChannel_mask_t + * @retval status + */ +LoRaMacStatus_t lw_current_chmask_set(const LWChannel_mask_t* ch); + +/** + * @brief return the currnt chmask + * @param [IN] LWChannel_mask_t + * @retval status + */ +LoRaMacStatus_t lw_current_chmask_get(LWChannel_mask_t* ch); + +/** + * @brief set Rx win2 datarate and freq , there are default configuration in region parameter + * @param [IN] RxChannelParams_t + * @retval status + */ +LoRaMacStatus_t lw_rxwin2_ch_set(const RxChannelParams_t* rx2_ch); + +/** + * @brief get Rx win2 datarate and freq + * @param [IN] RxChannelParams_t + * @retval status + */ + +LoRaMacStatus_t lw_rxwin2_ch_get(RxChannelParams_t* rx2_ch); + +/** + * @brief change Lora Class + * @Note callback LORA_ConfirmClass informs upper layer that the change has occured + * @Note Only switch from class A to class B/C OR from class B/C to class A is allowed + * @Attention can be calld only in LORA_ClassSwitchSlot or LORA_RxData callbacks + * @param [IN] DeviceClass_t NewClass + * @retval LoraErrorStatus + */ +LoRaMacStatus_t lw_request_class(DeviceClass_t newClass); + +/** + * @brief get the current Lora Class + * @param [IN] DeviceClass_t NewClass + * @retval None + */ +DeviceClass_t lw_get_current_class(void); + +/** + * @brief Set join activation process: OTAA vs ABP + * @param Over The Air Activation status to set: enable or disable + * @retval None + */ +void lw_config_otaa_set(lw_state_t otaa); + +/** + * @brief Get join activation process: OTAA vs ABP + * @param None + * @retval ENABLE if OTAA is used, DISABLE if ABP is used + */ +lw_state_t lw_config_otaa_get(void); + +/** + * @brief Set duty cycle: ENABLE or DISABLE + * @param Duty cycle to set: enable or disable + * @retval None + */ +void lw_config_duty_cycle_set(lw_state_t duty_cycle); + +/** + * @brief Get Duty cycle: OTAA vs ABP + * @param None + * @retval ENABLE / DISABLE + */ +lw_state_t lw_config_duty_cycle_get(void); + +/** + * @brief Get Device EUI + * @param None + * @retval DevEUI + */ +uint8_t *lw_config_deveui_get(void); + +/** + * @brief Get Join Eui + * @param None + * @retval JoinEUI + */ +uint8_t *lw_config_joineui_get(void); + +/** + * @brief Set Join Eui + * @param JoinEUI + * @retval Nonoe + */ +void lw_config_joineui_set(uint8_t joineui[8]); + +/** + * @brief Get Application Key + * @param None + * @retval AppKey + */ +uint8_t *lw_config_appkey_get(void); + +/** + * @brief Set Application Key + * @param AppKey + * @retval None + */ +void lw_config_appkey_set(uint8_t appkey[16]); + +/** + * @brief Set whether or not acknowledgement is required + * @param ENABLE or DISABLE + * @retval None + */ +void lw_config_reqack_set(LoraConfirm_t reqack); + +/** + * @brief Get whether or not acknowledgement is required + * @param None + * @retval ENABLE or DISABLE + */ +LoraConfirm_t lw_config_reqack_get(void); + +/** + * @brief Get the SNR of the last received data + * @param None + * @retval SNR + */ +int8_t lw_config_snr_get(void); + +/** + * @brief Get the RSSI of the last received data + * @param None + * @retval RSSI + */ +int16_t lw_config_rssi_get(void); + +/** + * @brief set tx datarate + * @param None + * @retval The application port + */ +void lw_config_tx_datarate_set(int8_t TxDataRate); + +/** + * @brief get tx datarate + * @param None + * @retval tx datarate + */ +int8_t lw_config_tx_datarate_get(void); + +/** + * @brief get LoRaMac region + * @param None + * @retval LoRaMac region + */ +LoRaMacRegion_t lw_region_get(void); + +#if defined (LORAMAC_CLASSB_ACTIVE) && ( LORAMAC_CLASSB_ACTIVE == 1 ) + + LoRaMacClassBNvmCtx_t* LORA_Get_ClassBCtx(void); + uint8_t LORA_Get_PingSlotPeriodicity(void); + void LORA_Set_PingSlotPeriodicity(uint8_t PingSlotPeriodicity); + +#elif !defined (LORAMAC_CLASSB_ACTIVE) + #error LORAMAC_CLASSB_ACTIVE not defined +#endif + +#ifdef __cplusplus + } +#endif + + + + +#ifdef __cplusplus +} +#endif + +#endif /*__LORA_H__*/ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/lorawan_info.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/lorawan_info.c new file mode 100644 index 00000000..d9a25552 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Basic/lorawan_info.c @@ -0,0 +1,96 @@ +/** + ****************************************************************************** + * @file lorawan_info.c + * @author MCD Application Team + * @brief To give info to the application about LoraWAN configuration + ****************************************************************************** + * @attention + * + *

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

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ + +/* Includes ------------------------------------------------------------------*/ +#include "lorawan_info.h" +#include "LoRaMac.h" + +/* Private typedef -----------------------------------------------------------*/ +/* Private define ------------------------------------------------------------*/ +/* Private macro -------------------------------------------------------------*/ +/* Private variables ---------------------------------------------------------*/ +/* Private function prototypes -----------------------------------------------*/ +/* Exported variables --------------------------------------------------------*/ +LoRaMacInfo_t LoRaMacInfoTable; + +/* Exported functions --------------------------------------------------------*/ +/** + * @brief initialises the LoraMacInfo capabilities table + * @param none + * @retval none + */ +void LoraMacInfo_Init(void) +{ + LoRaMacInfoTable.Region = 0; +#ifdef REGION_AS923 + LoRaMacInfoTable.Region |= (1<
© Copyright (c) 2019 STMicroelectronics. + * All rights reserved.
+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ +#ifndef __LORAWAN_INFO_H__ +#define __LORAWAN_INFO_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include + +/* Exported constants --------------------------------------------------------*/ +/* Exported types ------------------------------------------------------------*/ + +/*! + * To give info to the application about LoraWAN capability + * it can depend how it has been compiled (e.g. compiled regions ...) + * Params should be better uint32_t foe easier alignment with info_table concept + */ +typedef struct +{ + uint32_t Region; /*!< Combination of regions compiled on MW */ + uint32_t ClassB; /*!< 0: not compiled in Mw, 1 : compiled in MW */ +}LoRaMacInfo_t; + + +/* External variables --------------------------------------------------------*/ +/* Exported macros -----------------------------------------------------------*/ +/* Exported functions ------------------------------------------------------- */ + +/** + * @brief initialises the LoraMacInfo table + * @param none + * @retval none + */ +void LoraMacInfo_Init(void); + +/** + * @brief returns the pointer to the LoraMacInfo table + * @param none + * @retval LoRaMacInfoTable pointer + */ +LoRaMacInfo_t* LoraMacInfo_GetPtr(void); + +#ifdef __cplusplus +} +#endif + +#endif // __LORAWAN_INFO_H__ + diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/app_conf_template.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/app_conf_template.h new file mode 100644 index 00000000..66077eef --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/app_conf_template.h @@ -0,0 +1,56 @@ +/** + ****************************************************************************** + * @file app_conf_template.h + * @author MCD Application Team + * @brief applic configuration, e.g. : debug, trace, low power, sensors + * @note this is a template: the app_conf.h shall be defined in the Project/... + ****************************************************************************** + * @attention + * + *

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

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __APP_CONF_H__ +#define __APP_CONF_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +/* Exported types ------------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ + +/* LoraWAN application configuration (Mw is configured by lorawan_conf.h) */ +#define ACTIVE_REGION LORAMAC_REGION_EU868 +#define HYBRID_ENABLED 0 + +#define LORAMAC_CLASSB_ACTIVE 0 /* 0 if not active, 1 if active */ + +/* Enable trace logs. Note verbose level can be selected in app_system.h */ +#define APP_LOG_ENABLED 1 + +#define DEBUGGER_ON 0 /* if ON (=1) it enables the debbugger plus 4 dgb pins */ + /* if OFF (=0) the debugger is OFF (lower consumption) */ + +/* External variables --------------------------------------------------------*/ +/* Exported macros -----------------------------------------------------------*/ +/* Defines -------------------------------------------------------------------*/ +/* Exported functions ------------------------------------------------------- */ + +#ifdef __cplusplus +} +#endif + +#endif /* __APP_CONF_H__ */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/app_system_template.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/app_system_template.h new file mode 100644 index 00000000..378f1515 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/app_system_template.h @@ -0,0 +1,62 @@ +/** + ****************************************************************************** + * @file app_system_template.h + * @author MCD Application Team + * @brief function prototypes for app_system.c file + * @note this is a template: the app_conf.h shall be defined in the Project/... + ****************************************************************************** + * @attention + * + *

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

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __APP_SYSTEM_H__ +#define __APP_SYSTEM_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include +#include "app_conf.h" +#include "trace.h" + +/* Exported types ------------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ +#define VERBOSE_LEVEL VLEVEL_L + +/* External variables --------------------------------------------------------*/ +/* Exported macros -----------------------------------------------------------*/ +#define APP_PPRINTF(...) do{ } while( 0!= TraceSend(0, 0, __VA_ARGS__) ) //Polling Mode +#define APP_TPRINTF(...) do{ {TraceSend(1, 0, __VA_ARGS__);} }while(0); //with timestamp +#define APP_PRINTF(...) do{ {TraceSend(0, 0, __VA_ARGS__);} }while(0); + + +#if defined (APP_LOG_ENABLED) && (APP_LOG_ENABLED == 1) +#define APP_LOG(TS,VL,...) do{ {TraceSend(TS, VL, __VA_ARGS__);} }while(0); +#elif defined (APP_LOG_ENABLED) && (APP_LOG_ENABLED == 0) /* APP_LOG disabled */ +#define APP_LOG(TS,VL,...) +#else +#error "APP_LOG_ENABLED not defined or out of range <0,1>" +#endif + +/* Defines -------------------------------------------------------------------*/ +/* Exported functions ------------------------------------------------------- */ + + +#ifdef __cplusplus +} +#endif + +#endif /* __APP_SYSTEM_H__ */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/lora-test.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/lora-test.c new file mode 100644 index 00000000..7d05d67f --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/lora-test.c @@ -0,0 +1,417 @@ +/****************************************************************************** + * @file lora-test.c + * @author MCD Application Team + * @brief lora API to drive the lora state Machine + ****************************************************************************** + * @attention + * + *

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

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ + + +/* Includes ------------------------------------------------------------------*/ +#include "stddef.h" +//#include "lorawan_conf.h" /* Don't remove: needed for LORAMAC_CLASSB_ENABLED */ +//#include "mw_log_conf.h" +#include "LoRaMac.h" +#include "LoRaMacTest.h" +#include "lora.h" + +/* Private typedef -----------------------------------------------------------*/ + +typedef struct ComplianceTest_s +{ + bool Running; + uint8_t State; + LoraConfirm_t IsTxConfirmed; + uint8_t DataBufferSize; + uint8_t DataBuffer[242]; /* max possible payload */ + uint16_t DownLinkCounter; + bool LinkCheck; + uint8_t DemodMargin; + uint8_t NbGateways; +} ComplianceTest_t; + +typedef enum +{ + CERTIF_TX_PROCESS = 0, + CERTIF_RX_PROCESS +} requestProcess_t; + +/* Private define ------------------------------------------------------------*/ +#define TEST_TX_DUTYCYCLE 5000 +/* Private variables ---------------------------------------------------------*/ + + +static requestProcess_t requested_process_id; + +/*! + * Timer to handle the application data transmission duty cycle + */ +static TimerEvent_t CertifTxNextPacketTimer; + +static ComplianceTest_t certifParam; +static LoraConfirm_t IsTxConfirmed; +static bool AdrEnableInit; +static McpsIndication_t *CertifMcpsIndication; +static MlmeReqJoin_t *CertifJoinParameters; + +/* Private functions ---------------------------------------------------------*/ +static bool CERTIF_running(void); + +static void CERTIF_DownLinkIncrement(void); + +static void CERTIF_linkCheck(MlmeConfirm_t *mlmeConfirm); + +static void CERTIF_rx_request(McpsIndication_t *mcpsIndication, MlmeReqJoin_t *JoinParameters); + +static void CERTIF_rx(McpsIndication_t *mcpsIndication, MlmeReqJoin_t *JoinParameters); + +static bool CERTIF_tx(void); + +static void OnCertifTxNextPacketTimerEvent(void *context); + +static void (*CertifProcessNotify)(void); + +static LoRaCertifCallback_t certifCb = +{ + CERTIF_running, + CERTIF_DownLinkIncrement, + CERTIF_linkCheck, + CERTIF_rx_request, +}; + +/* Exported functions definition---------------------------------------------------------*/ + +void CERTIF_Init(void (*CERTIF_ProcessNotify)(void)) +{ + /*initialises Callback into the Lora.c layer*/ + LORA_TestInit(&certifCb); + /*Register TestProcessNotify callback*/ + CertifProcessNotify = CERTIF_ProcessNotify; +} + +void CERTIF_Process(void) +{ + switch (requested_process_id) + { + case CERTIF_TX_PROCESS: + CERTIF_tx(); + break; + case CERTIF_RX_PROCESS: + CERTIF_rx(CertifMcpsIndication, CertifJoinParameters); + break; + default: + break; + } +} +static bool CERTIF_running(void) +{ + return certifParam.Running; +} + +static void CERTIF_DownLinkIncrement(void) +{ + certifParam.DownLinkCounter++; +} + +static void CERTIF_linkCheck(MlmeConfirm_t *mlmeConfirm) +{ + certifParam.LinkCheck = true; + certifParam.DemodMargin = mlmeConfirm->DemodMargin; + certifParam.NbGateways = mlmeConfirm->NbGateways; +} + +static bool CERTIF_tx(void) +{ + McpsReq_t mcpsReq; + LoRaMacTxInfo_t txInfo; + + if (certifParam.LinkCheck == true) + { + certifParam.LinkCheck = false; + certifParam.DataBufferSize = 3; + certifParam.DataBuffer[0] = 5; + certifParam.DataBuffer[1] = certifParam.DemodMargin; + certifParam.DataBuffer[2] = certifParam.NbGateways; + certifParam.State = 1; + } + else + { + switch (certifParam.State) + { + case 4: + certifParam.State = 1; + break; + case 1: + certifParam.DataBufferSize = 2; + certifParam.DataBuffer[0] = certifParam.DownLinkCounter >> 8; + certifParam.DataBuffer[1] = certifParam.DownLinkCounter; + break; + } + } + + if (LoRaMacQueryTxPossible(certifParam.DataBufferSize, &txInfo) != LORAMAC_STATUS_OK) + { + // Send empty frame in order to flush MAC commands + mcpsReq.Type = MCPS_UNCONFIRMED; + mcpsReq.Req.Unconfirmed.fBuffer = NULL; + mcpsReq.Req.Unconfirmed.fBufferSize = 0; + mcpsReq.Req.Unconfirmed.Datarate = DR_0; + } + else + { + if (IsTxConfirmed == LORAWAN_UNCONFIRMED_MSG) + { + mcpsReq.Type = MCPS_UNCONFIRMED; + mcpsReq.Req.Unconfirmed.fPort = CERTIF_PORT; + mcpsReq.Req.Unconfirmed.fBufferSize = certifParam.DataBufferSize; + mcpsReq.Req.Unconfirmed.fBuffer = &(certifParam.DataBuffer); + mcpsReq.Req.Unconfirmed.Datarate = DR_0; + } + else + { + mcpsReq.Type = MCPS_CONFIRMED; + mcpsReq.Req.Confirmed.fPort = CERTIF_PORT; + mcpsReq.Req.Confirmed.fBufferSize = certifParam.DataBufferSize; + mcpsReq.Req.Confirmed.fBuffer = &(certifParam.DataBuffer); + mcpsReq.Req.Confirmed.NbTrials = 8; + mcpsReq.Req.Confirmed.Datarate = DR_0; + } + } + + /*cerification test on-going*/ + TimerStart(&CertifTxNextPacketTimer); + + if (LoRaMacMcpsRequest(&mcpsReq) == LORAMAC_STATUS_OK) + { + return false; + } + return true; +} + +static void CERTIF_rx_request(McpsIndication_t *mcpsIndication, MlmeReqJoin_t *JoinParameters) +{ + CertifMcpsIndication = mcpsIndication; + CertifJoinParameters = JoinParameters; + + requested_process_id = CERTIF_RX_PROCESS; + + CertifProcessNotify(); +} + +static void CERTIF_rx(McpsIndication_t *mcpsIndication, MlmeReqJoin_t *JoinParameters) +{ + if (certifParam.Running == false) + { + // Check compliance test enable command (i) + if ((mcpsIndication->BufferSize == 4) && + (mcpsIndication->Buffer[0] == 0x01) && + (mcpsIndication->Buffer[1] == 0x01) && + (mcpsIndication->Buffer[2] == 0x01) && + (mcpsIndication->Buffer[3] == 0x01)) + { + MibRequestConfirm_t mibReq; + IsTxConfirmed = LORAWAN_UNCONFIRMED_MSG; + certifParam.DataBufferSize = 2; + certifParam.DownLinkCounter = 0; + certifParam.LinkCheck = false; + certifParam.DemodMargin = 0; + certifParam.NbGateways = 0; + certifParam.Running = true; + certifParam.State = 1; + + mibReq.Type = MIB_ADR; + + LoRaMacMibGetRequestConfirm(&mibReq); + AdrEnableInit = mibReq.Param.AdrEnable; + + mibReq.Type = MIB_ADR; + mibReq.Param.AdrEnable = true; + LoRaMacMibSetRequestConfirm(&mibReq); + + if ( (ACTIVE_REGION == LORAMAC_REGION_EU868) + || (ACTIVE_REGION == LORAMAC_REGION_RU864) + || (ACTIVE_REGION == LORAMAC_REGION_CN779) + || (ACTIVE_REGION == LORAMAC_REGION_EU433)) + { + LoRaMacTestSetDutyCycleOn(false); + } + + TimerInit(&CertifTxNextPacketTimer, OnCertifTxNextPacketTimerEvent); + TimerSetValue(&CertifTxNextPacketTimer, TEST_TX_DUTYCYCLE); + + /*confirm test mode activation */ + CERTIF_tx(); + } + } + + else + { + certifParam.State = mcpsIndication->Buffer[0]; + switch (certifParam.State) + { + case 0: // Check compliance test disable command (ii) + { + + certifParam.DownLinkCounter = 0; + certifParam.Running = false; + + MibRequestConfirm_t mibReq; + mibReq.Type = MIB_ADR; + mibReq.Param.AdrEnable = AdrEnableInit; + LoRaMacMibSetRequestConfirm(&mibReq); + + if ( (ACTIVE_REGION == LORAMAC_REGION_EU868) + || (ACTIVE_REGION == LORAMAC_REGION_RU864) + || (ACTIVE_REGION == LORAMAC_REGION_CN779) + || (ACTIVE_REGION == LORAMAC_REGION_EU433)) + { + LoRaMacTestSetDutyCycleOn(true); + } + + break; + } + case 1: // (iii, iv) + certifParam.DataBufferSize = 2; + break; + case 2: // Enable confirmed messages (v) + IsTxConfirmed = LORAWAN_CONFIRMED_MSG; + certifParam.State = 1; + break; + case 3: // Disable confirmed messages (vi) + IsTxConfirmed = LORAWAN_UNCONFIRMED_MSG; + certifParam.State = 1; + break; + case 4: // (vii) + certifParam.DataBufferSize = mcpsIndication->BufferSize; + + certifParam.DataBuffer[0] = 4; + for (uint8_t i = 1; i < certifParam.DataBufferSize; i++) + { + certifParam.DataBuffer[i] = mcpsIndication->Buffer[i] + 1; + } + break; + case 5: // (viii) + { + MlmeReq_t mlmeReq; + mlmeReq.Type = MLME_LINK_CHECK; + LoRaMacMlmeRequest(&mlmeReq); + break; + } + case 6: // (ix) + { + MlmeReq_t mlmeReq; + + // Disable TestMode and revert back to normal operation + + certifParam.DownLinkCounter = 0; + certifParam.Running = false; + + MibRequestConfirm_t mibReq; + mibReq.Type = MIB_ADR; + mibReq.Param.AdrEnable = AdrEnableInit; + LoRaMacMibSetRequestConfirm(&mibReq); + + mlmeReq.Type = MLME_JOIN; + mlmeReq.Req.Join = *JoinParameters; + + LoRaMacMlmeRequest(&mlmeReq); + break; + } + + case 7: // (x) + { + if (mcpsIndication->BufferSize == 3) + { + MlmeReq_t mlmeReq; + mlmeReq.Type = MLME_TXCW; + mlmeReq.Req.TxCw.Timeout = (uint16_t)((mcpsIndication->Buffer[1] << 8) | mcpsIndication->Buffer[2]); + LoRaMacMlmeRequest(&mlmeReq); + } + else if (mcpsIndication->BufferSize == 7) + { + MlmeReq_t mlmeReq; + mlmeReq.Type = MLME_TXCW_1; + mlmeReq.Req.TxCw.Timeout = (uint16_t)((mcpsIndication->Buffer[1] << 8) | mcpsIndication->Buffer[2]); + mlmeReq.Req.TxCw.Frequency = (uint32_t)((mcpsIndication->Buffer[3] << 16) | (mcpsIndication->Buffer[4] << 8) | mcpsIndication->Buffer[5]) * 100; + mlmeReq.Req.TxCw.Power = mcpsIndication->Buffer[6]; + LoRaMacMlmeRequest(&mlmeReq); + } + certifParam.State = 1; + break; + } + + case 8: // Send DeviceTimeReq + { + MlmeReq_t mlmeReq; + + mlmeReq.Type = MLME_DEVICE_TIME; + + LoRaMacMlmeRequest(&mlmeReq); + break; + } +#if defined (LORAMAC_CLASSB_ACTIVE) && ( LORAMAC_CLASSB_ACTIVE == 1 ) + case 9: // Switch end device Class + { + MibRequestConfirm_t mibReq; + + mibReq.Type = MIB_DEVICE_CLASS; + // CLASS_A = 0, CLASS_B = 1, CLASS_C = 2 + mibReq.Param.Class = (DeviceClass_t)mcpsIndication->Buffer[1]; + LoRaMacMibSetRequestConfirm(&mibReq); + break; + } + case 10: // Send PingSlotInfoReq + { + MlmeReq_t mlmeReq; + + mlmeReq.Type = MLME_PING_SLOT_INFO; + + mlmeReq.Req.PingSlotInfo.PingSlot.Value = mcpsIndication->Buffer[1]; + + LoRaMacMlmeRequest(&mlmeReq); + break; + } + case 11: // Send BeaconTimingReq + { + MlmeReq_t mlmeReq; + + mlmeReq.Type = MLME_BEACON_TIMING; + + LoRaMacMlmeRequest(&mlmeReq); + break; + } +#elif !defined (LORAMAC_CLASSB_ACTIVE) + #error LORAMAC_CLASSB_ACTIVE not defined +#endif + default: + break; + } + } + + if (certifParam.Running == false) + { + /*certification test stops*/ + TimerStop(&CertifTxNextPacketTimer); + } +} + +/*! + * \brief Function executed on TxNextPacket Timeout event + */ +static void OnCertifTxNextPacketTimerEvent(void *context) +{ + requested_process_id = CERTIF_TX_PROCESS; + CertifProcessNotify(); +} +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ + diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/lora-test.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/lora-test.h new file mode 100644 index 00000000..1e8056e0 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/lora-test.h @@ -0,0 +1,71 @@ +/****************************************************************************** + * @file lora_test.h + * @author MCD Application Team + * @brief lora API to drive the lora state Machine + ****************************************************************************** + * @attention + * + *

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

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ + +#ifndef __LORA_TEST_H__ +#define __LORA_TEST_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ + +#define CERTIF_PORT 224 +/* Exported types ------------------------------------------------------------*/ + + +/* External variables --------------------------------------------------------*/ +/* Exported macros -----------------------------------------------------------*/ +/* Exported functions ------------------------------------------------------- */ + +/** + * @brief Certification Initialisation + * @param [IN] Notification call back to the appl that a CERTIF_Process must be called + * @retval none + */ +void CERTIF_Init(void (*CERTIF_ProcessNotify)(void)); +/** + * @brief CERTIF_Process to be called when a CERTIF_ProcessNotify callback is generated + * @param [IN] none + * @retval none + */ +void CERTIF_Process(void); + +/* Lora Test callbacks*/ +typedef struct sLoRaCertifCallback +{ + bool (*running)(void); + + void (*DownLinkIncrement)(void); + + void (*linkCheck)(MlmeConfirm_t *mlmeConfirm); + + void (*rx)(McpsIndication_t *mcpsIndication, MlmeReqJoin_t *JoinParameters); + +} LoRaCertifCallback_t; + +#ifdef __cplusplus +} +#endif + +#endif /*__LORA_TEST_H__*/ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/lora.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/lora.c new file mode 100644 index 00000000..6459d781 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/lora.c @@ -0,0 +1,1016 @@ +/** + ****************************************************************************** + * @file lora.c + * @author MCD Application Team + * @brief lora API to drive the lora state Machine + ****************************************************************************** + * @attention + * + *

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

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ + +/* Includes ------------------------------------------------------------------*/ +#include "timer.h" +#include "LoRaMac.h" +#include "lora.h" +#include "stm32_tiny_sscanf.h" +#include "app_system.h" +#include "stm32_seq.h" +#include "lorawan_info.h" + +/* External variables --------------------------------------------------------*/ +/* Private typedef -----------------------------------------------------------*/ + +/** + * Lora Configuration + */ +typedef struct +{ + LoraState_t otaa; /*< ENABLE if over the air activation, DISABLE otherwise */ + LoraState_t duty_cycle; /*< ENABLE if dutycyle is on, DISABLE otherwise */ + uint8_t DevEui[8]; /*< Device EUI */ + uint8_t JoinEui[8]; /*< Join Eui */ + uint8_t AppKey[16]; /*< Application Key */ + uint8_t NwkKey[16]; /*< Application Key */ + uint8_t NwkSEncKey[16]; /*< Network Session Key */ + uint8_t AppSKey[16]; /*< Application Session Key */ + uint8_t FNwkSIntKey[16]; /*< Application Session Key */ + uint8_t SNwkSIntKey[16]; /*< Application Session Key */ + int16_t Rssi; /*< Rssi of the received packet */ + int8_t Snr; /*< Snr of the received packet */ + uint8_t application_port; /*< Application port we will receive to */ + LoraConfirm_t ReqAck; /*< ENABLE if acknowledge is requested */ + int8_t TxDatarate; +} lora_configuration_t; + + +static lora_configuration_t lora_config = +{ + .otaa = LORA_ENABLE, + .duty_cycle = LORA_ENABLE, + .DevEui = LORAWAN_DEVICE_EUI, + .JoinEui = LORAWAN_JOIN_EUI, + .AppKey = LORAWAN_APP_KEY, + .NwkKey = LORAWAN_NWK_KEY, + .NwkSEncKey = LORAWAN_NWK_S_ENC_KEY, + .AppSKey = LORAWAN_APP_S_KEY, + .FNwkSIntKey = LORAWAN_F_NWK_S_INT_KEY, + .SNwkSIntKey = LORAWAN_S_NWK_S_INT_KEY, + .Rssi = 0, + .Snr = 0, + .ReqAck = LORAWAN_UNCONFIRMED_MSG, + .TxDatarate = 0 +}; + +/*! + * Select either Device_Time_req or Beacon_Time_Req following LoRaWAN version + * - Device_Time_req Available for V1.0.3 or later + * - Beacon_time_Req Available for V1.0.2 and before + */ +#define USE_DEVICE_TIMING + +/*! + * Join requests trials duty cycle. + */ +#define OVER_THE_AIR_ACTIVATION_DUTYCYCLE 10000 // 10 [s] value in ms + +#include "LoRaMacTest.h" + +#ifndef LORAMAC_CLASSB_ACTIVE + #error LORAMAC_CLASSB_ACTIVE not defined ( shall be <0 or 1> ) +#endif /* LORAMAC_CLASSB_ACTIVE */ + +#if ( LORAMAC_CLASSB_ACTIVE == 1 ) +/*! + * Default ping slots periodicity + * + * \remark periodicity is equal to 2^LORAWAN_DEFAULT_PING_SLOT_PERIODICITY seconds + * example: 2^3 = 8 seconds. The end-device will open an Rx slot every 8 seconds. + */ +#define LORAWAN_DEFAULT_PING_SLOT_PERIODICITY 0 +static uint8_t DefaultPingSlotPeriodicity; + +#endif /* LORAMAC_CLASSB_ACTIVE */ + + +#define HEX16(X) X[0],X[1], X[2],X[3], X[4],X[5], X[6],X[7],X[8],X[9], X[10],X[11], X[12],X[13], X[14],X[15] +#define HEX8(X) X[0],X[1], X[2],X[3], X[4],X[5], X[6],X[7] + +static MlmeReqJoin_t JoinParameters; + + +static uint32_t DevAddr = LORAWAN_DEVICE_ADDRESS; + +/* Dummy data sent periodically to let the tester respond with start test command*/ +static TimerEvent_t TxcertifTimer; + +/* Private macro -------------------------------------------------------------*/ +/* Private variables ---------------------------------------------------------*/ +/* Private function prototypes -----------------------------------------------*/ + +#if ( LORAMAC_CLASSB_ACTIVE == 1 ) + static LoraErrorStatus LORA_BeaconReq(void); + static LoraErrorStatus LORA_PingSlotReq(void); + + #if defined( USE_DEVICE_TIMING ) + static LoraErrorStatus LORA_DeviceTimeReq(void); + #else + static LoraErrorStatus LORA_BeaconTimeReq(void); + #endif /* USE_DEVICE_TIMING */ +#endif /* LORAMAC_CLASSB_ACTIVE */ + +static void OnCertifTimer(void *); + +static void CertifSend(void); +/*! + * Defines the LoRa parameters at Init + */ +static LoRaParam_t *LoRaParamInit; +static LoRaMacPrimitives_t LoRaMacPrimitives; +static LoRaMacCallback_t LoRaMacCallbacks; +static MibRequestConfirm_t mibReq; +static LoRaMacRegion_t LoRaRegion; + +static LoRaMainCallback_t *LoRaMainCallbacks; + +static LoRaCertifCallback_t *certif; + +/* Private user code ---------------------------------------------------------*/ +/*! + * \brief MCPS-Confirm event function + * + * \param [IN] McpsConfirm - Pointer to the confirm structure, + * containing confirm attributes. + */ +static void McpsConfirm(McpsConfirm_t *mcpsConfirm) +{ + if (mcpsConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK) + { + switch (mcpsConfirm->McpsRequest) + { + case MCPS_UNCONFIRMED: + { + // Check Datarate + // Check TxPower + break; + } + case MCPS_CONFIRMED: + { + // Check Datarate + // Check TxPower + // Check AckReceived + if (mcpsConfirm->AckReceived) + { + LoRaMainCallbacks->LORA_McpsDataConfirm(); + } + // Check NbTrials + break; + } + case MCPS_PROPRIETARY: + { + break; + } + default: + break; + } + } +} + +/*! + * \brief MCPS-Indication event function + * + * \param [IN] mcpsIndication - Pointer to the indication structure, + * containing indication attributes. + */ +static void McpsIndication(McpsIndication_t *mcpsIndication) +{ + lora_AppData_t AppData; + + if (mcpsIndication->Status != LORAMAC_EVENT_INFO_STATUS_OK) + { + return; + } + + switch (mcpsIndication->McpsIndication) + { + case MCPS_UNCONFIRMED: + { + break; + } + case MCPS_CONFIRMED: + { + break; + } + case MCPS_PROPRIETARY: + { + break; + } + case MCPS_MULTICAST: + { + break; + } + default: + break; + } + + // Check Multicast + // Check Port + // Check Datarate + // Check FramePending + if (mcpsIndication->FramePending == true) + { + // The server signals that it has pending data to be sent. + // We schedule an uplink as soon as possible to flush the server. + LoRaMainCallbacks->LORA_TxNeeded(); + } + // Check Buffer + // Check BufferSize + // Check Rssi + // Check Snr + // Check RxSlot + /*if (certif->running() == true) + { + certif->DownLinkIncrement(); + }*/ + + if (mcpsIndication->RxData == true) + { + switch (mcpsIndication->Port) + { + /*case CERTIF_PORT: + certif->rx(mcpsIndication, &JoinParameters); + break; + */ + default: + + AppData.Port = mcpsIndication->Port; + AppData.BuffSize = mcpsIndication->BufferSize; + AppData.Buff = mcpsIndication->Buffer; + lora_config.Rssi = mcpsIndication->Rssi; + lora_config.Snr = mcpsIndication->Snr; + LoRaMainCallbacks->LORA_RxData(&AppData); + break; + } + } +} + +/*! + * \brief MLME-Confirm event function + * + * \param [IN] MlmeConfirm - Pointer to the confirm structure, + * containing confirm attributes. + */ +static void MlmeConfirm(MlmeConfirm_t *mlmeConfirm) +{ +#if ( LORAMAC_CLASSB_ACTIVE == 1 ) + MibRequestConfirm_t mibReq; +#endif /* LORAMAC_CLASSB_ACTIVE */ + + switch (mlmeConfirm->MlmeRequest) + { + case MLME_JOIN: + { + if (mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK) + { + // Status is OK, node has joined the network + LoRaMainCallbacks->LORA_HasJoined(); +#if ( LORAMAC_CLASSB_ACTIVE == 1 ) +#if defined( USE_DEVICE_TIMING ) + LORA_DeviceTimeReq(); +#else + LORA_BeaconTimeReq(); +#endif /* USE_DEVICE_TIMING */ + +#endif /* LORAMAC_CLASSB_ACTIVE */ + } + else + { + // Join was not successful. Try to join again + LORA_Join(); + } + break; + } + case MLME_LINK_CHECK: + { + if (mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK) + { + // Check DemodMargin + // Check NbGateways + /*if (certif->running() == true) + { + certif->linkCheck(mlmeConfirm); + }*/ + } + break; + } +#if ( LORAMAC_CLASSB_ACTIVE == 1 ) + case MLME_BEACON_ACQUISITION: + { + if (mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK) + { + /* Beacon has been acquired */ + /* REquest Server for Ping Slot */ + LORA_PingSlotReq(); + } + else + { + /* Beacon not acquired */ + /* Search again */ + /* we can check if the MAC has received a time reference for the beacon*/ + /* in this case do either a Device_Time_Req or a Beacon_Timing_req*/ + LORA_BeaconReq(); + } + break; + } + case MLME_PING_SLOT_INFO: + { + if (mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK) + { + /* class B is now ativated*/ + mibReq.Type = MIB_DEVICE_CLASS; + mibReq.Param.Class = CLASS_B; + LoRaMacMibSetRequestConfirm(&mibReq); + + if ((LoRaParamInit->ActiveRegion == LORAMAC_REGION_AU915) || (LoRaParamInit->ActiveRegion == LORAMAC_REGION_US915)) + { + mibReq.Type = MIB_PING_SLOT_DATARATE; + mibReq.Param.PingSlotDatarate = DR_8; + LoRaMacMibSetRequestConfirm(&mibReq); + } + + /*notify upper layer*/ + LoRaMainCallbacks->LORA_ConfirmClass(CLASS_B); + } + else + { + LORA_PingSlotReq(); + } + break; + } +#if defined( USE_DEVICE_TIMING ) + case MLME_DEVICE_TIME: + { + if (mlmeConfirm->Status != LORAMAC_EVENT_INFO_STATUS_OK) + { + LORA_DeviceTimeReq(); + } + } +#endif /* USE_DEVICE_TIMING */ +#endif /* LORAMAC_CLASSB_ACTIVE */ + default: + break; + } +} + +/*! + * \brief MLME-Indication event function + * + * \param [IN] MlmeIndication - Pointer to the indication structure. + */ +static void MlmeIndication(MlmeIndication_t *MlmeIndication) +{ +#if ( LORAMAC_CLASSB_ACTIVE == 1 ) + MibRequestConfirm_t mibReq; +#endif /* LORAMAC_CLASSB_ACTIVE */ + + + switch (MlmeIndication->MlmeIndication) + { + case MLME_SCHEDULE_UPLINK: + { + // The MAC signals that we shall provide an uplink as soon as possible + LoRaMainCallbacks->LORA_TxNeeded(); + break; + } +#if ( LORAMAC_CLASSB_ACTIVE == 1 ) + case MLME_BEACON_LOST: + { + // Switch to class A again + mibReq.Type = MIB_DEVICE_CLASS; + mibReq.Param.Class = CLASS_A; + LoRaMacMibSetRequestConfirm(&mibReq); + + LORA_BeaconReq(); + break; + } + case MLME_BEACON: + { + if (MlmeIndication->Status == LORAMAC_EVENT_INFO_STATUS_BEACON_LOCKED) + { + APP_LOG(TS_ON, VLEVEL_H, "BEACON RECEIVED\n\r"); + } + else + { + APP_LOG(TS_ON, VLEVEL_H, "BEACON NOT RECEIVED\n\r"); + } + break; + + } +#endif /* LORAMAC_CLASSB_ACTIVE */ + default: + break; + } +} +/** + * lora Init + */ +void LORA_Init(LoRaMainCallback_t *callbacks, LoRaParam_t *LoRaParam) +{ + LoRaMacInfo_t *LoRaMacRegionInfo; + + LoraMacInfo_Init(); + LoRaMacRegionInfo = LoraMacInfo_GetPtr(); + if (LoRaMacRegionInfo->Region == 0) + { + APP_PRINTF("error: At leats one region shall be defined in the MW: check lorawan_conf.h \n\r"); + while(1) {} /* At leats one region shall be defined in MW */ + } + +#if ( LORAMAC_CLASSB_ACTIVE == 1 ) + if (LoRaMacRegionInfo->ClassB != 1) + { + APP_PRINTF("error: ClassB is not defined in the MW: set lorawan_conf.h accordingly\n\r"); + while(1) {} /* error: ClassB is not defined in the MW*/ + } +#elif ( LORAMAC_CLASSB_ACTIVE == 0 ) + if (LoRaMacRegionInfo->ClassB != 0) + { + APP_PRINTF("error: missmatch between applic and MW CLASSB defines: set lorawan_conf.h according to app_conf.h\n\r"); + while(1) {} /* error: ClassB should not be defined in the MW*/ + } +#endif /* LORAMAC_CLASSB_ACTIVE */ + + /* init the Tx Duty Cycle*/ + LoRaParamInit = LoRaParam; + + /* init the main call backs*/ + LoRaMainCallbacks = callbacks; + +#if (STATIC_DEVICE_EUI != 1) + LoRaMainCallbacks->BoardGetUniqueId(lora_config.DevEui); +#endif + +#if (STATIC_DEVICE_ADDRESS != 1) + // Choose a "pseudo-random" device address + DevAddr = LoRaMainCallbacks->BoardGetRandomSeed(); +#endif + + LoRaMacPrimitives.MacMcpsConfirm = McpsConfirm; + LoRaMacPrimitives.MacMcpsIndication = McpsIndication; + LoRaMacPrimitives.MacMlmeConfirm = MlmeConfirm; + LoRaMacPrimitives.MacMlmeIndication = MlmeIndication; + LoRaMacCallbacks.GetBatteryLevel = LoRaMainCallbacks->BoardGetBatteryLevel; + LoRaMacCallbacks.GetTemperatureLevel = LoRaMainCallbacks->BoardGetTemperatureLevel; + LoRaMacCallbacks.MacProcessNotify = LoRaMainCallbacks->MacProcessNotify; + + if( 0U != ((1<<(LoRaParam->ActiveRegion))& (LoRaMacRegionInfo->Region)) ) + { + LoRaMacInitialization(&LoRaMacPrimitives, &LoRaMacCallbacks, LoRaParam->ActiveRegion); + } + else + { + APP_PRINTF("error: Region is not defined in the MW: set lorawan_conf.h accordingly\n\r"); + while(1) {} /* error: Region is not defined in the MW */ + } + +#if ( HYBRID_ENABLED == 1 ) + if ((LoRaParamInit->ActiveRegion == LORAMAC_REGION_AU915) || (LoRaParamInit->ActiveRegion == LORAMAC_REGION_US915)) + { + uint16_t channelMask[] = { 0x00FF, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000}; + mibReq.Type = MIB_CHANNELS_MASK; + mibReq.Param.ChannelsMask = channelMask; + LoRaMacMibSetRequestConfirm(&mibReq); + mibReq.Type = MIB_CHANNELS_DEFAULT_MASK; + mibReq.Param.ChannelsDefaultMask = channelMask; + LoRaMacMibSetRequestConfirm(&mibReq); + } + if (LoRaParamInit->ActiveRegion == LORAMAC_REGION_CN470) + { + uint16_t channelMask[] = { 0x00FF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}; + mibReq.Type = MIB_CHANNELS_MASK; + mibReq.Param.ChannelsMask = channelMask; + LoRaMacMibSetRequestConfirm(&mibReq); + mibReq.Type = MIB_CHANNELS_DEFAULT_MASK; + mibReq.Param.ChannelsDefaultMask = channelMask; + LoRaMacMibSetRequestConfirm(&mibReq); + } +#elif !defined (HYBRID_ENABLED) + APP_PRINTF("error: HYBRID_ENABLED has not been defined at compilation\n\r"); + #error HYBRID_ENABLED not defined ( shall be <0 or 1> ) +#endif /* HYBRID_ENABLED */ + + lora_config_otaa_set(LORA_ENABLE); + + mibReq.Type = MIB_DEV_EUI; + mibReq.Param.DevEui = lora_config.DevEui; + LoRaMacMibSetRequestConfirm(&mibReq); + + mibReq.Type = MIB_JOIN_EUI; + mibReq.Param.JoinEui = lora_config.JoinEui; + LoRaMacMibSetRequestConfirm(&mibReq); + + mibReq.Type = MIB_ADR; + mibReq.Param.AdrEnable = LoRaParamInit->AdrEnable; + LoRaMacMibSetRequestConfirm(&mibReq); + + mibReq.Type = MIB_PUBLIC_NETWORK; + mibReq.Param.EnablePublicNetwork = LoRaParamInit->EnablePublicNetwork; + LoRaMacMibSetRequestConfirm(&mibReq); + + mibReq.Type = MIB_APP_KEY; + mibReq.Param.AppKey = lora_config.AppKey; + LoRaMacMibSetRequestConfirm(&mibReq); + + mibReq.Type = MIB_NWK_KEY; + mibReq.Param.NwkKey = lora_config.NwkKey; + LoRaMacMibSetRequestConfirm(&mibReq); + + mibReq.Type = MIB_DEVICE_CLASS; + mibReq.Param.Class = CLASS_A; + LoRaMacMibSetRequestConfirm(&mibReq); + + if ( (LoRaParamInit->ActiveRegion == LORAMAC_REGION_EU868) + || (LoRaParamInit->ActiveRegion == LORAMAC_REGION_RU864) + || (LoRaParamInit->ActiveRegion == LORAMAC_REGION_CN779) + || (LoRaParamInit->ActiveRegion == LORAMAC_REGION_EU433)) + { + LoRaMacTestSetDutyCycleOn(true); + lora_config.duty_cycle = LORA_ENABLE; + } + else + { + lora_config.duty_cycle = LORA_DISABLE; + } + + mibReq.Type = MIB_SYSTEM_MAX_RX_ERROR; + mibReq.Param.SystemMaxRxError = 20; + LoRaMacMibSetRequestConfirm(&mibReq); + +#if ( LORAMAC_CLASSB_ACTIVE == 1 ) + if (LoRaMacRegionInfo->ClassB == 1) + { + DefaultPingSlotPeriodicity = LORAWAN_DEFAULT_PING_SLOT_PERIODICITY; + } +#endif /* LORAMAC_CLASSB_ACTIVE */ + + /*set Mac statein Idle*/ + LoRaMacStart(); +} + +LoraErrorStatus LORA_Join(void) +{ + LoraErrorStatus status; + if (lora_config.otaa == LORA_ENABLE) + { + MlmeReq_t mlmeReq; + + mlmeReq.Type = MLME_JOIN; + mlmeReq.Req.Join.Datarate = LoRaParamInit->TxDatarate; + + JoinParameters = mlmeReq.Req.Join; + + LoRaMacMlmeRequest(&mlmeReq); + + status = LORA_SUCCESS; + } + else + { + /*no Join in abp*/ + status = LORA_ERROR; + } + + return status; +} + +LoraFlagStatus LORA_JoinStatus(void) +{ + MibRequestConfirm_t mibReq; + + mibReq.Type = MIB_NETWORK_ACTIVATION; + + LoRaMacMibGetRequestConfirm(&mibReq); + + if (mibReq.Param.NetworkActivation == ACTIVATION_TYPE_NONE) + { + return LORA_RESET; + } + else + { + return LORA_SET; + } +} + + + +LoraErrorStatus LORA_send(lora_AppData_t *AppData, LoraConfirm_t IsTxConfirmed) +{ + McpsReq_t mcpsReq; + LoRaMacTxInfo_t txInfo; + + /*if certification test are on going, application data is not sent*/ + /* + if (certif->running() == true) + { + return LORA_ERROR; + }*/ + + if (LoRaMacQueryTxPossible(AppData->BuffSize, &txInfo) != LORAMAC_STATUS_OK) + { + // Send empty frame in order to flush MAC commands + mcpsReq.Type = MCPS_UNCONFIRMED; + mcpsReq.Req.Unconfirmed.fBuffer = NULL; + mcpsReq.Req.Unconfirmed.fBufferSize = 0; + mcpsReq.Req.Unconfirmed.Datarate = lora_config_tx_datarate_get() ; + } + else + { + if (IsTxConfirmed == LORAWAN_UNCONFIRMED_MSG) + { + mcpsReq.Type = MCPS_UNCONFIRMED; + mcpsReq.Req.Unconfirmed.fPort = AppData->Port; + mcpsReq.Req.Unconfirmed.fBufferSize = AppData->BuffSize; + mcpsReq.Req.Unconfirmed.fBuffer = AppData->Buff; + mcpsReq.Req.Unconfirmed.Datarate = lora_config_tx_datarate_get() ; + } + else + { + mcpsReq.Type = MCPS_CONFIRMED; + mcpsReq.Req.Confirmed.fPort = AppData->Port; + mcpsReq.Req.Confirmed.fBufferSize = AppData->BuffSize; + mcpsReq.Req.Confirmed.fBuffer = AppData->Buff; + mcpsReq.Req.Confirmed.NbTrials = 8; + mcpsReq.Req.Confirmed.Datarate = lora_config_tx_datarate_get() ; + } + } + if (LoRaMacMcpsRequest(&mcpsReq) == LORAMAC_STATUS_OK) + { + return LORA_SUCCESS; + } + return LORA_ERROR; +} + +#if ( LORAMAC_CLASSB_ACTIVE == 1 ) +#if defined( USE_DEVICE_TIMING ) +static LoraErrorStatus LORA_DeviceTimeReq(void) +{ + MlmeReq_t mlmeReq; + + mlmeReq.Type = MLME_DEVICE_TIME; + + if (LoRaMacMlmeRequest(&mlmeReq) == LORAMAC_STATUS_OK) + { + return LORA_SUCCESS; + } + else + { + return LORA_ERROR; + } +} +#else +static LoraErrorStatus LORA_BeaconTimeReq(void) +{ + MlmeReq_t mlmeReq; + + mlmeReq.Type = MLME_BEACON_TIMING; + + if (LoRaMacMlmeRequest(&mlmeReq) == LORAMAC_STATUS_OK) + { + return LORA_SUCCESS; + } + else + { + return LORA_ERROR; + } +} +#endif /* USE_DEVICE_TIMING */ + +static LoraErrorStatus LORA_BeaconReq(void) +{ + MlmeReq_t mlmeReq; + + mlmeReq.Type = MLME_BEACON_ACQUISITION; + + if (LoRaMacMlmeRequest(&mlmeReq) == LORAMAC_STATUS_OK) + { + return LORA_SUCCESS; + } + else + { + return LORA_ERROR; + } +} + +static LoraErrorStatus LORA_PingSlotReq(void) +{ + MlmeReq_t mlmeReq; + + mlmeReq.Type = MLME_LINK_CHECK; + LoRaMacMlmeRequest(&mlmeReq); + + mlmeReq.Type = MLME_PING_SLOT_INFO; + mlmeReq.Req.PingSlotInfo.PingSlot.Fields.Periodicity = DefaultPingSlotPeriodicity; + mlmeReq.Req.PingSlotInfo.PingSlot.Fields.RFU = 0; + + if (LoRaMacMlmeRequest(&mlmeReq) == LORAMAC_STATUS_OK) + { + return LORA_SUCCESS; + } + else + { + return LORA_ERROR; + } +} +#endif /* LORAMAC_CLASSB_ACTIVE */ + +LoraErrorStatus LORA_RequestClass(DeviceClass_t newClass) +{ + LoraErrorStatus Errorstatus = LORA_SUCCESS; + MibRequestConfirm_t mibReq; + DeviceClass_t currentClass; + + mibReq.Type = MIB_DEVICE_CLASS; + LoRaMacMibGetRequestConfirm(&mibReq); + + currentClass = mibReq.Param.Class; + /*attempt to swicth only if class update*/ + if (currentClass != newClass) + { + switch (newClass) + { + case CLASS_A: + { + mibReq.Param.Class = CLASS_A; + if (LoRaMacMibSetRequestConfirm(&mibReq) == LORAMAC_STATUS_OK) + { + /*switch is instantanuous*/ + LoRaMainCallbacks->LORA_ConfirmClass(CLASS_A); + } + else + { + Errorstatus = LORA_ERROR; + } + break; + } + case CLASS_B: + { +#if ( LORAMAC_CLASSB_ACTIVE == 1 ) + if (currentClass != CLASS_A) + { + Errorstatus = LORA_ERROR; + } + /*switch is not instantanuous*/ + Errorstatus = LORA_BeaconReq(); +#else + APP_PRINTF("warning: LORAMAC_CLASSB_ACTIVE has not been defined at compilation\n\r"); +#endif /* LORAMAC_CLASSB_ACTIVE */ + break; + } + case CLASS_C: + { + if (currentClass != CLASS_A) + { + Errorstatus = LORA_ERROR; + } + /*switch is instantanuous*/ + mibReq.Param.Class = CLASS_C; + if (LoRaMacMibSetRequestConfirm(&mibReq) == LORAMAC_STATUS_OK) + { + LoRaMainCallbacks->LORA_ConfirmClass(CLASS_C); + } + else + { + Errorstatus = LORA_ERROR; + } + break; + } + default: + break; + } + } + return Errorstatus; +} + +void LORA_GetCurrentClass(DeviceClass_t *currentClass) +{ + MibRequestConfirm_t mibReq; + + mibReq.Type = MIB_DEVICE_CLASS; + LoRaMacMibGetRequestConfirm(&mibReq); + + *currentClass = mibReq.Param.Class; +} + + +void lora_config_otaa_set(LoraState_t otaa) +{ + lora_config.otaa = otaa; + + if (lora_config.otaa == LORA_ENABLE) + { + APP_PPRINTF("OTAA Mode enabled\n\r"); + APP_PPRINTF("DevEui= %02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X\n\r", HEX8(lora_config.DevEui)); + APP_PPRINTF("JoinEui= %02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X\n\r", HEX8(lora_config.JoinEui)); + APP_PPRINTF("AppKey= %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n\r", HEX16(lora_config.AppKey)); + + mibReq.Type = MIB_NETWORK_ACTIVATION; + mibReq.Param.NetworkActivation = ACTIVATION_TYPE_NONE; + LoRaMacMibSetRequestConfirm(&mibReq); + } + else + { + APP_PPRINTF("ABP Mode enabled\n\r"); + APP_PPRINTF("DevEui= %02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X\n\r", HEX8(lora_config.DevEui)); + APP_PPRINTF("DevAdd= %08X\n\r", DevAddr) ; + APP_PPRINTF("NwkSKey= %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n\r", HEX16(lora_config.NwkSEncKey)); + APP_PPRINTF("AppSKey= %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n\r", HEX16(lora_config.AppSKey)); + + mibReq.Type = MIB_NET_ID; + mibReq.Param.NetID = LORAWAN_NETWORK_ID; + LoRaMacMibSetRequestConfirm(&mibReq); + + mibReq.Type = MIB_DEV_ADDR; + mibReq.Param.DevAddr = DevAddr; + LoRaMacMibSetRequestConfirm(&mibReq); + + mibReq.Type = MIB_F_NWK_S_INT_KEY; + mibReq.Param.FNwkSIntKey = lora_config.FNwkSIntKey; + LoRaMacMibSetRequestConfirm(&mibReq); + + mibReq.Type = MIB_S_NWK_S_INT_KEY; + mibReq.Param.SNwkSIntKey = lora_config.SNwkSIntKey; + LoRaMacMibSetRequestConfirm(&mibReq); + + mibReq.Type = MIB_NWK_S_ENC_KEY; + mibReq.Param.NwkSEncKey = lora_config.NwkSEncKey; + LoRaMacMibSetRequestConfirm(&mibReq); + + mibReq.Type = MIB_APP_S_KEY; + mibReq.Param.AppSKey = lora_config.AppSKey; + LoRaMacMibSetRequestConfirm(&mibReq); + + mibReq.Type = MIB_NETWORK_ACTIVATION; + mibReq.Param.NetworkActivation = ACTIVATION_TYPE_ABP; + LoRaMacMibSetRequestConfirm(&mibReq); + + // Enable legacy mode to operate according to LoRaWAN Spec. 1.0.3 + Version_t abpLrWanVersion; + + abpLrWanVersion.Fields.Major = 1; + abpLrWanVersion.Fields.Minor = 0; + abpLrWanVersion.Fields.Revision = 3; + abpLrWanVersion.Fields.Rfu = 0; + + mibReq.Type = MIB_ABP_LORAWAN_VERSION; + mibReq.Param.AbpLrWanVersion = abpLrWanVersion; + LoRaMacMibSetRequestConfirm(&mibReq); + } +} + + +LoraState_t lora_config_otaa_get(void) +{ + return lora_config.otaa; +} + +void lora_config_duty_cycle_set(LoraState_t duty_cycle) +{ + lora_config.duty_cycle = duty_cycle; + LoRaMacTestSetDutyCycleOn((duty_cycle == LORA_ENABLE) ? 1 : 0); +} + +LoraState_t lora_config_duty_cycle_get(void) +{ + return lora_config.duty_cycle; +} + +uint8_t *lora_config_deveui_get(void) +{ + return lora_config.DevEui; +} + +uint8_t *lora_config_joineui_get(void) +{ + return lora_config.JoinEui; +} + +void lora_config_joineui_set(uint8_t joineui[8]) +{ + UTIL_MEM_cpy_8(lora_config.JoinEui, joineui, sizeof(lora_config.JoinEui)); +} + +uint8_t *lora_config_appkey_get(void) +{ + return lora_config.AppKey; +} + +void lora_config_appkey_set(uint8_t appkey[16]) +{ + UTIL_MEM_cpy_8(lora_config.AppKey, appkey, sizeof(lora_config.AppKey)); + UTIL_MEM_cpy_8(lora_config.NwkKey, appkey, sizeof(lora_config.NwkKey)); + + mibReq.Type = MIB_APP_KEY; + mibReq.Param.AppKey = lora_config.AppKey; + LoRaMacMibSetRequestConfirm(&mibReq); + + mibReq.Type = MIB_NWK_KEY; + mibReq.Param.NwkKey = lora_config.NwkKey; + LoRaMacMibSetRequestConfirm(&mibReq); +} + +void lora_config_reqack_set(LoraConfirm_t reqack) +{ + lora_config.ReqAck = reqack; +} + +LoraConfirm_t lora_config_reqack_get(void) +{ + return lora_config.ReqAck; +} + +int8_t lora_config_snr_get(void) +{ + return lora_config.Snr; +} + +int16_t lora_config_rssi_get(void) +{ + return lora_config.Rssi; +} + +void lora_config_tx_datarate_set(int8_t TxDataRate) +{ + lora_config.TxDatarate = TxDataRate; +} + +int8_t lora_config_tx_datarate_get(void) +{ + return lora_config.TxDatarate; +} + +/* +static void OnCertifTimer(void *context) +{ + UTIL_SEQ_SetTask(CFG_SEQ_TASK_LORA_CERTIF_TX, CFG_SEQ_Prio_0); +} + +static void CertifSend(void) +{ + uint8_t Dummy[1] = {1}; + lora_AppData_t AppData; + AppData.Buff = Dummy; + AppData.BuffSize = sizeof(Dummy); + AppData.Port = 99; + + LORA_send(&AppData, LORAWAN_UNCONFIRMED_MSG); +} + +void lora_wan_certif(void) +{ + LoRaMacTestSetDutyCycleOn(false); + LORA_Join(); + TimerInit(&TxcertifTimer, OnCertifTimer); // 8s + TimerSetValue(&TxcertifTimer, 8000); // 8s + TimerStart(&TxcertifTimer); + UTIL_SEQ_RegTask(CFG_SEQ_TASK_LORA_CERTIF_TX, UTIL_SEQ_RFU, CertifSend); + +} +*/ + +LoRaMacRegion_t lora_region_get(void) +{ + return LoRaRegion; +} +/* +void LORA_TestInit(LoRaCertifCallback_t *callbacks) +{ + certif = callbacks; +} +*/ +#if ( LORAMAC_CLASSB_ACTIVE == 1 ) +LoRaMacClassBNvmCtx_t* LORA_Get_ClassBCtx(void) +{ + mibReq.Type = MIB_NVM_CTXS; + LoRaMacMibGetRequestConfirm( &mibReq ); + return ( LoRaMacClassBNvmCtx_t*) mibReq.Param.Contexts->ClassBNvmCtx; +} + +uint8_t LORA_Get_PingSlotPeriodicity(void) +{ + return DefaultPingSlotPeriodicity; +} + +void LORA_Set_PingSlotPeriodicity(uint8_t PingSlotPeriodicity) +{ + DefaultPingSlotPeriodicity= PingSlotPeriodicity; +} +#endif /* LORAMAC_CLASSB_ACTIVE */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/lora.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/lora.h new file mode 100644 index 00000000..402b88ca --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/lora.h @@ -0,0 +1,403 @@ +/** + ****************************************************************************** + * @file lora.h + * @author MCD Application Team + * @brief lora API to drive the lora state Machine + ****************************************************************************** + * @attention + * + *

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

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ + +#ifndef __LORA_H__ +#define __LORA_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "Commissioning.h" +#include "LoRaMac.h" +#include "app_conf.h" +#if defined (LORAMAC_CLASSB_ACTIVE) && ( LORAMAC_CLASSB_ACTIVE == 1 ) +#include "LoRaMacClassB.h" +#elif !defined (LORAMAC_CLASSB_ACTIVE) + #error LORAMAC_CLASSB_ACTIVE not defined +#endif /* LORAMAC_CLASSB_ACTIVE */ + +#include "region/Region.h" +#include "lora-test.h" + +/* Exported constants --------------------------------------------------------*/ +/*! +* LoRaWAN confirmed messages +*/ + +#define LORAWAN_ADR_ON 1 +#define LORAWAN_ADR_OFF 0 +/* Exported types ------------------------------------------------------------*/ + +/*! + * Application Data structure + */ +typedef struct +{ + /*point to the LoRa App data buffer*/ + uint8_t *Buff; + /*LoRa App data buffer size*/ + uint8_t BuffSize; + /*Port on which the LoRa App is data is sent/ received*/ + uint8_t Port; + +} lora_AppData_t; + + + +typedef enum +{ + LORA_RESET = 0, + LORA_SET = !LORA_RESET +} LoraFlagStatus; + +typedef enum +{ + LORA_DISABLE = 0, + LORA_ENABLE = !LORA_DISABLE +} LoraState_t; + +typedef enum +{ + LORA_ERROR = -1, + LORA_SUCCESS = 0 +} LoraErrorStatus; + +typedef enum +{ + LORAWAN_UNCONFIRMED_MSG = 0, + LORAWAN_CONFIRMED_MSG = !LORAWAN_UNCONFIRMED_MSG +} LoraConfirm_t; + +typedef enum +{ + LORA_TRUE = 0, + LORA_FALSE = !LORA_TRUE +} LoraBool_t; + +/*! + * LoRa State Machine states + */ +typedef enum eTxEventType +{ + /*! + * @brief AppdataTransmition issue based on timer every TxDutyCycleTime + */ + TX_ON_TIMER, + /*! + * @brief AppdataTransmition external event plugged on OnSendEvent( ) + */ + TX_ON_EVENT +} TxEventType_t; + + +/*! + * LoRa State Machine states + */ +typedef struct sLoRaParam +{ + /*! + * @brief Activation state of adaptativeDatarate + */ + bool AdrEnable; + /*! + * @brief Uplink datarate, if AdrEnable is off + */ + int8_t TxDatarate; + /*! + * @brief Enable or disable a public network + */ + bool EnablePublicNetwork; + /*! + * @brief region to open + */ + LoRaMacRegion_t ActiveRegion; +} LoRaParam_t; + +/* Lora Main callbacks*/ +typedef struct sLoRaMainCallback +{ + /*! + * @brief Get the current battery level + * + * @retval value battery level ( 0: very low, 254: fully charged ) + */ + uint8_t (*BoardGetBatteryLevel)(void); + /*! + * \brief Get the current temperature + * + * \retval value temperature in degreeCelcius( q7.8 ) + */ + uint16_t (*BoardGetTemperatureLevel)(void); + /*! + * @brief Gets the board 64 bits unique ID + * + * @param [IN] id Pointer to an array that will contain the Unique ID + */ + void (*BoardGetUniqueId)(uint8_t *id); + /*! + * Returns a pseudo random seed generated using the MCU Unique ID + * + * @retval seed Generated pseudo random seed + */ + uint32_t (*BoardGetRandomSeed)(void); + /*! + * @brief Process Rx Data received from Lora network + * + * @param [IN] AppData structure + * + */ + void (*LORA_RxData)(lora_AppData_t *AppData); + + /*! + * @brief callback indicating EndNode has jsu joiny + * + * @param [IN] None + */ + void (*LORA_HasJoined)(void); + /*! + * @brief Confirms the class change + * + * @param [IN] AppData is a buffer to process + * + * @param [IN] port is a Application port on wicth Appdata will be sent + * + * @param [IN] length is the number of recieved bytes + */ + void (*LORA_ConfirmClass)(DeviceClass_t Class); + /*! + * @brief callback indicating an uplink transmission is needed to allow + * a pending downlink transmission + * + * @param [IN] None + */ + void (*LORA_TxNeeded)(void); + /*! + *\brief Will be called each time a Radio IRQ is handled by the MAC + * layer. + * + *\warning Runs in a IRQ context. Should only change variables state. + */ + void (*MacProcessNotify)(void); + /*! + * @brief callback indicating a downlink transmission providing + * an acknowledgment for an uplink confirmed data message transmission + * + * @param [IN] None + */ + void (*LORA_McpsDataConfirm)(void); +} LoRaMainCallback_t; + + + +/* External variables --------------------------------------------------------*/ +/* Exported macros -----------------------------------------------------------*/ +/* Exported functions ------------------------------------------------------- */ +/** + * @brief Lora Initialisation + * @param [IN] LoRaMainCallback_t + * @param [IN] application parmaters + * @retval none + */ +void LORA_Init(LoRaMainCallback_t *callbacks, LoRaParam_t *LoRaParam); + +/** + * @brief Lora Certif Initialisation (Registers CB) + * @param [IN] LoRaCertifCallback_t + * @retval none + */ +void LORA_TestInit(LoRaCertifCallback_t *callbacks); +/** + * @brief run Lora classA state Machine + * @param [IN] none + * @retval none + */ +LoraErrorStatus LORA_send(lora_AppData_t *AppData, LoraConfirm_t IsTxConfirmed); + +/** + * @brief Join a Lora Network in classA + * @Note if the device is ABP, this is a pass through functon + * @param [IN] none + * @retval none + */ +LoraErrorStatus LORA_Join(void); + +/** + * @brief Check whether the Device is joined to the network + * @param [IN] none + * @retval returns LORA_SET if joined + */ +LoraFlagStatus LORA_JoinStatus(void); + +/** + * @brief change Lora Class + * @Note callback LORA_ConfirmClass informs upper layer that the change has occured + * @Note Only switch from class A to class B/C OR from class B/C to class A is allowed + * @Attention can be calld only in LORA_ClassSwitchSlot or LORA_RxData callbacks + * @param [IN] DeviceClass_t NewClass + * @retval LoraErrorStatus + */ +LoraErrorStatus LORA_RequestClass(DeviceClass_t newClass); + +/** + * @brief get the current Lora Class + * @param [IN] DeviceClass_t NewClass + * @retval None + */ +void LORA_GetCurrentClass(DeviceClass_t *currentClass); + +/** + * @brief Set join activation process: OTAA vs ABP + * @param Over The Air Activation status to set: enable or disable + * @retval None + */ +void lora_config_otaa_set(LoraState_t otaa); + +/** + * @brief Get join activation process: OTAA vs ABP + * @param None + * @retval ENABLE if OTAA is used, DISABLE if ABP is used + */ +LoraState_t lora_config_otaa_get(void); + +/** + * @brief Set duty cycle: ENABLE or DISABLE + * @param Duty cycle to set: enable or disable + * @retval None + */ +void lora_config_duty_cycle_set(LoraState_t duty_cycle); + +/** + * @brief Get Duty cycle: OTAA vs ABP + * @param None + * @retval ENABLE / DISABLE + */ +LoraState_t lora_config_duty_cycle_get(void); + +/** + * @brief Get Device EUI + * @param None + * @retval DevEUI + */ +uint8_t *lora_config_deveui_get(void); + +/** + * @brief Get Join Eui + * @param None + * @retval JoinEUI + */ +uint8_t *lora_config_joineui_get(void); + +/** + * @brief Set Join Eui + * @param JoinEUI + * @retval Nonoe + */ +void lora_config_joineui_set(uint8_t joineui[8]); + +/** + * @brief Get Application Key + * @param None + * @retval AppKey + */ +uint8_t *lora_config_appkey_get(void); + +/** + * @brief Set Application Key + * @param AppKey + * @retval None + */ +void lora_config_appkey_set(uint8_t appkey[16]); + +/** + * @brief Set whether or not acknowledgement is required + * @param ENABLE or DISABLE + * @retval None + */ +void lora_config_reqack_set(LoraConfirm_t reqack); + +/** + * @brief Get whether or not acknowledgement is required + * @param None + * @retval ENABLE or DISABLE + */ +LoraConfirm_t lora_config_reqack_get(void); + +/** + * @brief Get the SNR of the last received data + * @param None + * @retval SNR + */ +int8_t lora_config_snr_get(void); + +/** + * @brief Get the RSSI of the last received data + * @param None + * @retval RSSI + */ +int16_t lora_config_rssi_get(void); + +/** + * @brief Launch LoraWan certification tests + * @param None + * @retval The application port + */ +void lora_wan_certif(void); + +/** + * @brief set tx datarate + * @param None + * @retval The application port + */ +void lora_config_tx_datarate_set(int8_t TxDataRate); + +/** + * @brief get tx datarate + * @param None + * @retval tx datarate + */ +int8_t lora_config_tx_datarate_get(void); + +/** + * @brief get LoRaMac region + * @param None + * @retval LoRaMac region + */ +LoRaMacRegion_t lora_region_get(void); + +#if defined (LORAMAC_CLASSB_ACTIVE) && ( LORAMAC_CLASSB_ACTIVE == 1 ) + +LoRaMacClassBNvmCtx_t* LORA_Get_ClassBCtx(void); +uint8_t LORA_Get_PingSlotPeriodicity(void); +void LORA_Set_PingSlotPeriodicity(uint8_t PingSlotPeriodicity); + +#elif !defined (LORAMAC_CLASSB_ACTIVE) + #error LORAMAC_CLASSB_ACTIVE not defined +#endif + +#ifdef __cplusplus +} +#endif + +#endif /*__LORA_H__*/ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/lorawan_info.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/lorawan_info.c new file mode 100644 index 00000000..4dadc38a --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Patterns/Modem/lorawan_info.c @@ -0,0 +1,94 @@ +/** + ****************************************************************************** + * @file lorawan_info.c + * @author MCD Application Team + * @brief To give info to the application about LoraWAN configuration + ****************************************************************************** + * @attention + * + *

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

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ + +/* Includes ------------------------------------------------------------------*/ +#include "lorawan_info.h" +#include "LoRaMac.h" + +/* Private typedef -----------------------------------------------------------*/ +/* Private define ------------------------------------------------------------*/ +/* Private macro -------------------------------------------------------------*/ +/* Private variables ---------------------------------------------------------*/ +/* Private function prototypes -----------------------------------------------*/ +/* Exported variables --------------------------------------------------------*/ + +LoRaMacInfo_t LoRaMacInfoTable; + +/* Exported functions --------------------------------------------------------*/ +/** + * @brief initialises the LoraMacInfo capabilities table + * @param none + * @retval none + */ +void LoraMacInfo_Init(void) +{ + LoRaMacInfoTable.Region = 0; +#ifdef REGION_AS923 + LoRaMacInfoTable.Region |= (1<
© Copyright (c) 2019 STMicroelectronics. + * All rights reserved.
+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ +#ifndef __LORAWAN_INFO_H__ +#define __LORAWAN_INFO_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include + +/* Exported constants --------------------------------------------------------*/ +/* Exported types ------------------------------------------------------------*/ + +/*! + * To give info to the application about LoraWAN capability + * it can depend how it has been compiled (e.g. compiled regions ...) + * Params should be better uint32_t foe easier alignment with info_table concept + */ +typedef struct +{ + uint32_t Region; /*!< Combination of regions compiled on MW */ + uint32_t ClassB; /*!< 0: not compiled in Mw, 1 : compiled in MW */ +}LoRaMacInfo_t; + + +/* External variables --------------------------------------------------------*/ +/* Exported macros -----------------------------------------------------------*/ +/* Exported functions ------------------------------------------------------- */ + +/** + * @brief initialises the LoraMacInfo table + * @param none + * @retval none + */ +void LoraMacInfo_Init(void); + +/** + * @brief returns the pointer to the LoraMacInfo table + * @param none + * @retval LoRaMacInfoTable pointer + */ +LoRaMacInfo_t* LoraMacInfo_GetPtr(void); + +#ifdef __cplusplus +} +#endif + +#endif // __LORAWAN_INFO_H__ + diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Utilities/utilities.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Utilities/utilities.c new file mode 100644 index 00000000..120e08a2 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Utilities/utilities.c @@ -0,0 +1,94 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + +Description: Helper functions implementation + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis and Gregory Cristian +*/ +/** + ****************************************************************************** + * + * Portions COPYRIGHT 2019 STMicroelectronics + * + * @file utilities.c + * @author MCD Application Team + * @brief Helper functions implementation + ****************************************************************************** + */ + +#include "utilities.h" + +/*! + * Redefinition of rand() and srand() standard C functions. + * These functions are redefined in order to get the same behavior across + * different compiler toolchains implementations. + */ +// Standard random functions redefinition start +#define RAND_LOCAL_MAX 2147483647L + +static uint32_t next = 1; + +int32_t rand1( void ) +{ + return ( ( next = next * 1103515245L + 12345L ) % RAND_LOCAL_MAX ); +} + +void srand1( uint32_t seed ) +{ + next = seed; +} +// Standard random functions redefinition end + +int32_t randr( int32_t min, int32_t max ) +{ + return ( int32_t )rand1( ) % ( max - min + 1 ) + min; +} + +void memcpy1( uint8_t *dst, const uint8_t *src, uint16_t size ) +{ + while( size-- ) + { + *dst++ = *src++; + } +} + +void memcpyr( uint8_t *dst, const uint8_t *src, uint16_t size ) +{ + dst = dst + ( size - 1 ); + while( size-- ) + { + *dst-- = *src++; + } +} + +void memset1( uint8_t *dst, uint8_t value, uint16_t size ) +{ + while( size-- ) + { + *dst++ = value; + } +} + +int8_t Nibble2HexChar( uint8_t a ) +{ + if( a < 10 ) + { + return '0' + a; + } + else if( a < 16 ) + { + return 'A' + ( a - 10 ); + } + else + { + return '?'; + } +} +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Utilities/utilities.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Utilities/utilities.h new file mode 100644 index 00000000..5bbad2c3 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/Utilities/utilities.h @@ -0,0 +1,136 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + +Description: Helper functions implementation + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis and Gregory Cristian +*/ + + /** + ****************************************************************************** + * + * Portions COPYRIGHT 2019 STMicroelectronics + * + * @file utilities.h + * @author MCD Application Team + * @brief Header for driver utilities.c module + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __UTILITIES_H__ +#define __UTILITIES_H__ + +/* Includes ------------------------------------------------------------------*/ +#include +#include "utilities_conf.h" + + +/* Exported types ------------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ +/* External variables --------------------------------------------------------*/ +/* Exported macros -----------------------------------------------------------*/ +/* Defines -------------------------------------------------------------------*/ + + + +#define TIMERTIME_T_MAX ( ( uint32_t )~0 ) + +/*! + * @brief Returns the minimum value between a and b + * + * @param [IN] a 1st value + * @param [IN] b 2nd value + * @retval minValue Minimum value + */ +#ifndef MIN + #define MIN( a, b ) ( ( ( a ) < ( b ) ) ? ( a ) : ( b ) ) +#endif + +/*! + * @brief Returns the maximum value between a and b + * + * @param [IN] a 1st value + * @param [IN] b 2nd value + * @retval maxValue Maximum value + */ +#ifndef MAX + #define MAX( a, b ) ( ( ( a ) > ( b ) ) ? ( a ) : ( b ) ) +#endif + +/*! + * @brief Returns 2 raised to the power of n + * + * @param [IN] n power value + * @retval result of raising 2 to the power n + */ +#define POW2( n ) ( 1 << n ) + + + +/*! + * @brief Initializes the pseudo random generator initial value + * @param [IN] seed Pseudo random generator initial value + * @retval none + */ +void srand1( uint32_t seed ); + +/*! + * @brief Computes a random number between min and max + * @param [IN] min range minimum value + * @param [IN] max range maximum value + * @retval random random value in range min..max + */ +int32_t randr( int32_t min, int32_t max ); + +/*! + * @brief Computes a random number + * @param none + * @retval random value in range min..max + */ +int32_t rand1( void ); + +/*! + * @brief Copies size elements of src array to dst array + * @remark STM32 Standard memcpy function only works on pointers that are aligned + * @param [OUT] dst Destination array + * @param [IN] src Source array + * @param [IN] size Number of bytes to be copied + * @retval none + */ +void memcpy1( uint8_t *dst, const uint8_t *src, uint16_t size ); + +/*! + * @brief Copies size elements of src array to dst array reversing the byte order + * @param [OUT] dst Destination array + * @param [IN] src Source array + * @param [IN] size Number of bytes to be copied + * @retval none + */ +void memcpyr( uint8_t *dst, const uint8_t *src, uint16_t size ); + +/*! + * @brief Set size elements of dst array with value + * @remark STM32 Standard memset function only works on pointers that are aligned + * @param [OUT] dst Destination array + * @param [IN] value Default value + * @param [IN] size Number of bytes to be copied + * @retval none + */ +void memset1( uint8_t *dst, uint8_t value, uint16_t size ); + +/*! + * @brief Converts a nibble to an hexadecimal character + * @param [IN] a Nibble to be converted + * @retval hexChar Converted hexadecimal character + */ +int8_t Nibble2HexChar( uint8_t a ); + +#endif // __UTILITIES_H__ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/readme.md b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/readme.md new file mode 100644 index 00000000..5e042dc1 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/readme.md @@ -0,0 +1,771 @@ +# LoRaWAN endpoint stack implementation and example projects + + ______ _ + / _____) _ | | + ( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | + (______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013-2019 Semtech + + ___ _____ _ ___ _ _____ ___ ___ ___ ___ + / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + embedded.connectivity.solutions=============== + +## Introduction + +The aim of this project is to show an example of the endpoint LoRaWAN stack implementation. + +This project has 3 active branches in place. The **[master](https://github.com/Lora-net/LoRaMac-node/tree/master)** branch which provides the latest released source code ([v4.4.2](https://github.com/Lora-net/LoRaMac-node/releases/tag/v4.4.2)), the **[develop](https://github.com/Lora-net/LoRaMac-node/tree/develop)** branch which provides the current source code development status to be released next ([Milestone 4.4.3](https://github.com/Lora-net/LoRaMac-node/milestone/4)) and the **[feature/5.0.0](https://github.com/Lora-net/LoRaMac-node/tree/feature/5.0.0)** branch which provides a preview of the current source code development status for [LoRaWAN Specification v1.1](https://lora-alliance.org/resource-hub/lorawantm-specification-v11) specification.([Milestone 5.0.0](https://github.com/Lora-net/LoRaMac-node/milestone/3)) + +* The [master](https://github.com/Lora-net/LoRaMac-node/tree/master) branch implementation is based on [LoRaWAN Specification v1.0.3](https://lora-alliance.org/resource-hub/lorawantm-specification-v103) and [LoRaWAN Regional Parameters v1.0.3revA](https://www.lora-alliance.org/resource-hub/lorawantm-regional-parameters-v103reva) specifications. +ClassA, ClassB and ClassC end-device classes are fully implemented. + +* The [develop](https://github.com/Lora-net/LoRaMac-node/tree/develop) branch implementation is based on [LoRaWAN Specification v1.0.3](https://lora-alliance.org/resource-hub/lorawantm-specification-v103) and [LoRaWAN Regional Parameters v1.0.3revA](https://lora-alliance.org/resource-hub/lorawan-regional-parameters-v103reva) specifications. +ClassA, ClassB and ClassC end-device classes are fully implemented. + +* The [feature/5.0.0](https://github.com/Lora-net/LoRaMac-node/tree/feature/5.0.0) branch implementation is based on [LoRaWAN Specification v1.1](https://lora-alliance.org/resource-hub/lorawantm-specification-v11) and [LoRaWAN Regional Parameters v1.1rB](https://lora-alliance.org/resource-hub/lorawantm-regional-parameters-v11rb) specifications. +ClassA, ClassB and ClassC end-device classes are fully implemented. + +This project also provides SX1272/73, SX1276/77/78/79 and SX1261/2 radio drivers. + +For each currently supported platform example applications are provided. + +* **LoRaMac/classA**: ClassA end-device example application. + +* **LoRaMac/classB**: ClassB end-device example application. + +* **LoRaMac/classC**: ClassC end-device example application. + +* **LoRaMac/fuota-test-01**: FUOTA test scenario 01 end-device example application. (Based on provided application common packages) + +* **LoRaMac/periodic-uplink-lpp**: ClassA/B/C end-device example application. Periodically uplinks a frame using the Cayenne LPP protocol. (Based on provided application common packages) + +* **ping-pong**: Point to point RF link example application. + +* **rx-sensi**: Example application useful to measure the radio sensitivity level using an RF generator. + +* **tx-cw**: Example application to show how to generate an RF Continuous Wave transmission. + +**Note**: *Each LoRaWAN application example (LoRaMac/classX) includes an implementation of the LoRa-Alliance; LoRaWAN certification protocol.* + +**Note**: *The LoRaWAN stack API documentation can be found at: http://stackforce.github.io/LoRaMac-doc/* + +## Supported platforms + +This project currently provides support for the below platforms. +This project can be ported to other platforms using different MCU than the ones currently supported. +The [Porting Guide](http://stackforce.github.io/LoRaMac-doc/_p_o_r_t_i_n_g__g_u_i_d_e.html) document provides guide lines on how to port the project to other platforms. + +* NAMote72 + * [NAMote72 platform documentation](Doc/NAMote72-platform.md) + +* NucleoLxxx - Discovery kit + * [NucleoLxxx and Discovery kit platforms documentation](Doc/NucleoLxxx-platform.md) + +* SKiM880B, SKiM980A, SKiM881AXL + * [SKiM88xx platforms documentation](Doc/SKiM88xx-platform.md) + +* SAML21 + * [SAML21 platform documentation](Doc/SAML21-platform.md) + +## Usage + +A CMAKE building system is used in order to generate the right set of files to compile and debug the different projects. + +Further information can be found in [Development environment](Doc/development-environment.md) document. + +## Acknowledgments + +The mbed (https://mbed.org/) project was used at the beginning as source of +inspiration. + +This program uses the AES algorithm implementation (http://www.gladman.me.uk/) by Brian Gladman. + +This program uses the CMAC algorithm implementation +(http://www.cse.chalmers.se/research/group/dcs/masters/contikisec/) by Lander Casado, Philippas Tsigas. + +## Changelog + +### 2019-07-19, V4.4.2 + +* General + 1. Release based on "LoRaWAN specification 1.0.3" and "LoRaWAN Regional Parameters v1.0.3revA" + 2. Examples application refactoring plus the addition of the application status display on the serial port. (921600-8-N-1) + 3. Added new refactored application examples + * Added LoRa-Alliance defined application layer protocols support. Only FUOTA test scenario 01 required features are currently implemented. + - "Clock Synchronization" package + - "Fragmented data block transport" package + - "Remote multicast setup" package + 4. Added a NVM context management module. (Disabled by default) + 5. Added secure-element support + 6. Refactored timer.c/h, rtc-driver.c/h and added a systime.c/h module. + 7. Functions in ISR context have been moved to main context + 8. Replaced BoardDisableIrq and BoardEnableIrq functions by CRITICAL_SECTION_BEGIN and CRITICAL_SECTION_END respectively. + 9. Added B-L072Z-LRWAN1 platform support. + 10. Added NucleoL476 platform support. + 11. Added IMST new platforms. + 12. Removed SensorNode platform support + 13. Removed MoteII platform support + 14. Removed LoRaMote platform support + 15. Applied SX1272 and SX1276 radios errata note 3.1 to the radio drivers implementation. + 16. Fix printf/scanf functions when GCC is used as compiler. + +* LoRaWAN + 1. GitHub reported issues corrections. Please refer to [Release Version 4.4.2](https://github.com/Lora-net/LoRaMac-node/milestone/2) + 2. Heavily refactored the LoRaMac.c/h implementation. + 3. Added RU864 region support. + 4. Removed US915-Hybrid region support + 5. Added ClassB support. + 6. Added a callback to notify the upper layer to call LoRaMacProcess function + 7. Implemented the support for RxC windows (ClassC) required by the application layer protocols. + 8. Changed multicast channels handling according to the application layer protocols. + +**LoRaWAN pre-certification results** + +Please refer to [Releases pre-certification-results](https://github.com/Lora-net/LoRaMac-node/wiki/releases-pre-certification-results) document for further information. + +### 2018-03-07, V4.4.1 + +* General + 1. Release based on "LoRaWAN specification 1.0.2" and "LoRaWAN Regional Parameters v1.0.2rB" + 2. Added SX126x radio support. + 3. Added NucleoL073 and NucleoL152 board platforms support. + 4. Added Microchip/Atmel SAML21 Xplained Pro hardware platform support. + 5. Added CMAKE build system support + 6. Removed CoIDE projects support + 7. Removed Keil projects support + +* LoRaWAN + 1. GitHub reported issues corrections. Please refer to [Release Version 4.4.1](https://github.com/Lora-net/LoRaMac-node/milestone/1) + +**LoRaWAN pre-certification results** + +Please refer to [Releases pre-certification-results](https://github.com/Lora-net/LoRaMac-node/wiki/releases-pre-certification-results) document for further information. + +### 2017-09-08, V4.4.0 + +* General + 1. First release based on "LoRaWAN specification 1.0.2" and "LoRaWAN Regional Parameters v1.0.2rB" + 2. This version has passed the LoRa-Alliance compliance tests for the regions shown in the below table. + 3. GitHub reported issues corrections. + 4. Updated radio driver to perform the LBT carrier sense continuously for a given period of time. + +* LoRaWAN + 1. GitHub reported issues corrections. + 2. Updated implementation to support 1.0.2 specification additions. + 3. Added the support for all "LoRaWAN Regional Parameters v1.0.2rB" document defined regions. + +**LoRaWAN pre-certification results** + +Please refer to [Releases pre-certification-results](https://github.com/Lora-net/LoRaMac-node/wiki/releases-pre-certification-results) document for further information. + +### 2017-04-19, V4.3.2 + +* General (Last release based on LoRaWAN specification 1.0.1) + 1. This version has passed EU868 and US915 LoRa-Alliance compliance tests. + 2. GitHub reported issues corrections. + 3. Added an algorithm to automatically compute the Rx windows parameters. (Window symbolTimeout and Offset from downlink expected time) + 4. Added a workaround to reset the radio in case a TxTimeout occurs. + 5. Modified FSK modem handling to use the provided symbolTimeout (1 symbol equals 1 byte) when in RxSingle mode. + 6. Added newly defined TxCw(Tx Continuous Wave) certification protocol command. + 7. Added a fix for an overflow issue that could happen with NmeaStringSize variable. + 8. Improved GpioMcuInit function to first configure the output pin state before activating the pin. + +* LoRaWAN + 1. GitHub reported issues corrections. + 2. Changed the AdrAckCounter handling as expected by the test houses. + 3. Fix an issue where the node stopped transmitting. + 4. Removed useless LoRaMacPayload buffer. + 5. MAC layer indications handling simplification. + 6. Relocate parameter settings from ResetMacParameters to the initialization. + +**LoRaWAN pre-certification results** + +Please refer to [Releases pre-certification-results](https://github.com/Lora-net/LoRaMac-node/wiki/releases-pre-certification-results) document for further information. + +### 2017-02-27, V4.3.1 + +* General + 1. This version has passed EU868 and US915 LoRa-Alliance compliance tests. + 2. Update the MAC layer in order to be LoRaWAN version 1.0.1 compliant (Mainly US915 bug fixes) + 3. Removed api-v3 support from the project. + 4. GitHub reported issues corrections. + 5. Updated SensorNode projects according to the new MCU reference STM32L151CBU6. Bigger memories. + 6. Addition of MoteII platform based on the IMST module iM881A (STM32L051C8) + 7. Addition of NAMote72 platform + 8. Correct compliance test protocol command 0x06 behavior + 9. Added TxCw (Tx continuous wave) LoRaWAN compliance protocol command. + 10. Added TxContinuousWave support to the radio drivers. + 11. Updated ST HAL drivers. + * STM32L1xx_HAL-Driver : V1.2.0 + * STM32L0xx_HAL_Driver : V1.7.0 + +* LoRaWAN + 1. US band corrections in order to pass the LoRaWAN certification. + 2. GitHub reported issues corrections. + 3. Add region CN470 support. + +**LoRaWAN pre-certification results** + +Please refer to [Releases pre-certification-results](https://github.com/Lora-net/LoRaMac-node/wiki/releases-pre-certification-results) document for further information. + +### 2016-06-22, V4.3.0 + +* General + 1. This version has passed all LoRa-Alliance compliance tests. + 2. Update the MAC layer in order to be LoRaWAN version 1.0.1 compliant + 3. Applied to all application files the certification protocol change for LoRaWAN 1.0.1 compliance tests. + + **REMARK**: api-v3 application files aren't updated. + + 4. Add radio RX_TIMEOUT irq clear into the irq handler. + 5. Removed the end less loop from HAL_UART_ErrorCallback. + 6. Update of the STM32L0 HAL to version 1.6.0 + 7. Consolidated the line endings across all project files. + Windows line endings has been choose for almost every file. + +* LoRaWAN + 1. Updated maximum payload size for US band. + 2. Update datarate offset table for US band. + 3. Make MAC commands sticky + 4. Add retransmission back-off + 5. Remove the TxPower limitation for US band on LoRaMacMibSetRequestConfirm function. The power will be limited anyway when the SendFrameOnChannel functions is called. + 6. Issue(#81): Bug fix in function LoRaMacMlmeRequest case MLME_JOIN. Function will return LORAMAC_STATUS_BUSY in case the MAC is in status MAC_TX_DELAYED. + 7. Add debug pin support to LoRaMote platform. + 8. Updated and improved MPL3115 device driver. + 9. Issue(#83): Bug fix in parameter validation + 10. Issue(#84): Fix issue of CalibrateTimer function. + 11. RTC driver major update + 12. Applied pull request #87. + 13. Add a function to verify the RX frequency of window 2 for US band. + 14. Issue(#88): Bug fix in function PrepareFrame where repeated MAC commands were not handled correctly. + 15. Bug fix in OnRadioRxDone. Node now drops frames on port 0 with fOpts > 0. + 16. Bug fix in OnRadioRxDone. Node now receives frames with fOpts > 0 when no payload is present. + +**LoRaWAN pre-certification results** + +Please refer to [Releases pre-certification-results](https://github.com/Lora-net/LoRaMac-node/wiki/releases-pre-certification-results) document for further information. + +### 2016-05-13, V4.2.0 + +* General + 1. This version has passed all LoRa-Alliance compliance tests. + 2. Update STM32L1xx_HAL_Driver version to 1.5. Update related drivers and implementations accordingly. + + **REMARK**: This change implies that the time base had to be changed from microseconds to milliseconds. + + 3. Corrected the frequency check condition for // ERRATA 2.1 - Sensitivity Optimization with a 500 kHz Bandwidth + 4. Optimize radio drivers regarding FSK PER + 5. Resolve issue when calling SX127xInit function more than once + 6. Add a definition for the LoRaWAN device address. Add an IEEE_OUI for the LoRaWAN device EUI. + 7. Add a definition for the default datarate. + 8. Issue(#66) correction of functions SX1276SetOpMode and SX1272SetOpMode. + 9. Issue(#68): Fix for low level RF switch control. + 10. Increase RTC tick frequency for higher resolution. + 11. Update the radio wake up time. + +* LoRaWAN + 1. Issue(#56) correction + 2. Update channel mask handling for US915 hybrid mode to support any block in the channel mask. + 3. Issue(#63) correct the maximum payload length in RX mode. + 4. Fix Tx power setting loss for repeated join requests on US band. + 5. Introduce individual MIN and MAX datarates for RX and TX. + 6. Add the possibility to set and get the ChannelsDefaultDatarate. + 7. Optimization of the RX symbol timeout. + 8. Issue(#59): Add the possibility to set the uplink and downlink counter. + 9. Replace definition LORAMAC_DEFAULT_DATARATE by ChannelsDefaultDatarate in LoRaMacChannelAdd. + 10. Issue(#72): Fix of possible array overrun in LoRaMacChannelRemove. + 11. Introduce a new status MAC_RX_ABORT. Reset MAC_TX_RUNNING only in OnMacStateCheckTimerEvent. + 12. Accept MAC information of duplicated, confirmed downlinks. + 13. Issue(#74): Drop frames with a downlink counter difference greater or equal to MAX_FCNT_GAP. + +**LoRaWAN pre-certification results** + +Please refer to [Releases pre-certification-results](https://github.com/Lora-net/LoRaMac-node/wiki/releases-pre-certification-results) document for further information. + +### 2016-03-10, V4.1.0 + +* General + 1. This version has passed all mandatory LoRa-Alliance compliance tests. + + *One of the optional tests is unsuccessful (FSK downlinks PER on Rx1 and Rx2 windows) and is currently under investigation.* + 2. Removed support for Raisonance Ride7 IDE (Reduces the amount of work to be done at each new release) + 3. Removed the Bleeper-72 and Bleeper-76 platforms support as these are now deprecated. + 4. Application state machine. Relocate setting sleep state and update the duty cycle in compliance test mode. + 5. Bug fix in TimerIrqHandler. Now, it is possible to insert timers in callback. + 6. Changed TimerHwDelayMs function to be re-entrant. + 7. Corrected FSK modem packets bigger than 64 bytes handling (Issue #36) + +* LoRaWAN + 1. Rename attribute nbRetries to NbTrials in structure McpsReqConfirmed_t. (Issue #37) + 2. Updated implementation of SetNextChannel. Added enabling default channels in case of join request. (Issue #39) + 3. Add missing documentation about MIB_REPEATER_SUPPORT. (Issue #42). + 4. Add a new LoRaMacState to allow adding channels during TX procedure. (Issue #43) + 5. Relocate the activation of LoRaMacFlags.Bits.McpsInd in OnRadioRxDone. + 6. Add a new function PrepareRxDoneAbort to prepare a break-out of OnRadioRxDone in case of an error + 7. Activate default channels in case all others are disabled. (Issue #39) + 8. Bug fix in setting the default channel in case none is enabled. + 9. SRV_MAC_NEW_CHANNEL_REQ MAC command added a fix to the macIndex variable on US915 band. + 10. Start the MacStateCheckTimer in OnRxDone and related error cases with a short interval to handle events promptly. (Issue #44) + 11. Reset status of NodeAckRequested if we received an ACK or in case of timeout. + 12. Removed additional EU868 channels from the LoRaWAN implementation files. GitHub (Issue #49) + The creation of these additional channels has been moved to the application example. + 13. Improved and corrected AdrNextDr function. + +### 2015-12-18, V4.0.0 + +* General + 1. STACKFORCE new API integration + 2. Reverse the EUIs arrays in the MAC layer. + 3. LoRaWAN certification protocol implementation + 4. All reported issues and Pull requests have been addressed. + +### 2015-10-06, V3.4.1 + +* General + 1. Bug fixes + +* LoRaWAN + 1. Corrected downlink counter roll over management when several downlinks were missed. + 2. Corrected the Radio maximum payload length management. Radio was filtering received frames with a length bigger than the transmitted one. + 3. Applied Pull request #22 solution proposition. + +### 2015-10-30, V3.4.0 + +* General + 1. Changed all applications in order to have preprocessing definitions on top of the files and added relevant comments + 2. Applications LED control is no more done into the timer callback functions but instead on the main while loop. + 3. Added TimerStop function calls to each timer event callback. + 4. Corrected timings comments. Timing values are most of the time us based. + 5. Changed types names for stdint.h names. Helps on code portability + 6. Renamed rand and srand to rand1 and srand1. Helps on code portability + 7. Added some missing variables casts. Helps on code portability + 8. Removed NULL definition from board.h + 9. Added const variable attribute when necessary to functions prototypes + 10. Moved ID1, ID2 and ID3 definition from board.h to board.c, usb-cdc-board.c and usb-dfu-board.c + 11. Removed the definition of RAND_SEED. It has been replaced by a function named BoardGetRandomSeed + 12. Renamed BoardMeasureBatterieLevel to BoardGetBatteryLevel + 13. Added SetMaxPayloadLength API function to SX1272 and SX1276 radio drivers + 14. Changed the name of Radio API Status function to GetStatus + 15. AES/CMAC Changed types names for stdint.h names. Helps on code portability (Issue #20) + 16. Moved __ffs function from utilities.h to spi-board.c. This function is only used there. + 17. Utilities.c removed fputc function redefinition. + 18. Replaced the usage of __IO attribute by volatile. + +* LoRaWAN + 1. Added support for the US915 band (Normal mode and hybrid mode. Hybrid mode is a temporary configuration up until servers support it automatically) (Issue #16) + 2. Corrected and simplified the downlink sequence counter management. + 3. Removed the usage of PACKED attribute for data structures. + 4. Renamed LoRaMacEvent_t into LoRaMacCallbacks_t and added a function pointer for getting battery level status + 5. Renamed LoRaMacSetDutyCycleOn into LoRaMacSetTestDutyCycleOn + 6. Renamed LoRaMacSetMicTest into LoRaMacTestSetMic + 7. Increased the PHY buffer size to 250 + 8. Removed IsChannelFree check on LoRaMacSetNextChannel function. LoRaWAN is an ALHOA protocol. (Pull request #8) + 9. LoRaMacEventInfo.TxDatarate now returns LoRaWAN datarate (DR0 -> DR7) instead of (SF12 -> DF7) + 10. Corrected channel mask management for EU868 band. + 11. Corrected LoRaMacPrepareFrame behavior function when no applicative payload is present. + 12. LoRaMac-board.h now implements the settings for the PHY layers specified by LoRaWAN 1.0 specification. ( EU433, CN780, EU868, US915 ) (Issue #19) + 13. Added LORAMAC_MIN_RX1_DR_OFFSET and LORAMAC_MAX_RX1_DR_OFFSET definitions to LoRaMac-board.h. Can be different upon used PHY layer + 14. Added the limitation of output power according to the number of enabled channels for US915 band. + 15. Added the limitation of the applicative payload length according to the datarate. Does not yet take in account the MAC commands buffer. (Issue #15) + 16. Corrected MacCommandBufferIndex management. (Issue #18) + +### 2015-08-07, v3.3.0 + +* General + 1. Added the support for LoRaWAN Class C devices. + 2. Implemented the radios errata note workarounds. SX1276 errata 2.3 "Receiver Spurious Reception of a LoRa Signal" is not yet implemented. + 3. Increased FSK SyncWord timeout value in order to listen for longer time if a down link is available or not. Makes FSK downlink more reliable. + 4. Increased the UART USB FIFO buffer size in order to handle bigger chunks of data. + +* LoRaWAN + 1. Renamed data rates as per LoRaWAN specification. + 2. Added the support for LoRaWAN Class C devices. + 3. Handling of the MAC commands was done incorrectly the condition to verify the length of the buffer has changed from < to <=. + 4. Added the possibility to change the channel mask and number of repetitions trough SRV_MAC_LINK_ADR_REQ command when ADR is disabled. + 5. Corrected Rx1DrOffset management. In previous version DR1 was missing for all offsets. + 6. Changed confirmed messages function to use default datarate when ADR control is off. + 7. After a Join accept the node falls back to the default datarate. Enables the user to Join a network using a different datarate from its own default one. + 8. Corrected default FSK channel frequency. + 9. Solved a firmware freezing when one of the following situations arrived in OnRxDone callback: bad address, bad MIC, bad frame. (Pull request #10) + 10. Moved the MAC commands processing to the right places. FOpts field before the Payload and Port 0 just after the decryption. (Pull request #9) + 11. Weird conditions to check datarate on cmd mac SRV_MAC_NEW_CHANNEL_REQ (Pull request #7) + 12. Ignore join accept message if already joined (Pull request #6) + 13. Channel index verification should use OR on SRV_MAC_NEW_CHANNEL_REQ command (Pull request #5) + 14. Corrected the CFList management on JoinAccept. The for loop indexes were wrong. (Pull request #4) + 15. Correction of AES key size (Pull request #3) + +### 2015-04-30, v3.2.0 + +* General + 1. Updated LoRaMac implementation according to LoRaWAN R1.0 specification + 2. General cosmetics corrections + 3. Added the support of packed structures when using IAR tool chain + 4. Timers: Added a function to get the time in us. + 5. Timers: Added a typedef for time variables (TimerTime_t) + 6. Radios: Changed the TimeOnAir radio function to return a uint32_t value instead of a double. The value is in us. + 7. Radios: Corrected the 250 kHz bandwidth choice for the FSK modem + 8. GPS: Added a function that returns if the GPS has a fix or not. + 9. GPS: Changed the GetPosition functions to return a latitude and longitude of 0 and altitude of 65535 when no GPS fix. + +* LoRaWAN + 1. Removed support for previous LoRaMac/LoRaWAN specifications + 2. Added missing MAC commands and updated others when necessary + * Corrected the Port 0 MAC commands decryption + * Changed the way the upper layer is notified. Now it is only notified + when all the operations are finished. + + When a ClassA Tx cycle starts a timer is launched to check every second if everything is finished. + + * Added a new parameter to LoRaMacEventFlags structure that indicates on which Rx window the data has been received. + * Added a new parameter to LoRaMacEventFlags structure that indicates if there is applicative data on the received payload. + * Corrected ADR MAC command behavior + * DutyCycle enforcement implementation (EU868 PHY only) + + **REMARK 1** *The regulatory duty cycle enforcement is enabled by default + which means that for lower data rates the node may not transmit a new + frame as quickly as requested. + The formula used to compute the node idle time is* + + *Toff = TimeOnAir / DutyCycle - TxTimeOnAir* + + *Example:* + *A device just transmitted a 0.5 s long frame on one default channel. + This channel is in a sub-band allowing 1% duty-cycle. Therefore this + whole sub-band (868 MHz - 868.6 MHz) will be unavailable for 49.5 s.* + + **REMARK 2** *The duty cycle enforcement can be disabled for test + purposes by calling the LoRaMacSetDutyCycleOn function with false + parameter.* + * Implemented aggregated duty cycle management + * Added a function to create new channels + * Implemented the missing features on the JoinAccept MAC command + * Updated LoRaMacJoinDecrypt function to handle the CFList field. + 3. Due to duty cycle management the applicative API has changed. + All applications must be updated accordingly. + 4. Added the possibility to chose to use either public or private networks + +### 2015-01-30, v3.1.0 + +* General + 1. Started to add support for CooCox CoIDE Integrated Development Environment. + Currently only LoRaMote and SensorNode platform projects are available. + 2. Updated GCC compiler linker scripts. + 3. Added the support of different tool chains for the HardFault_Handler function. + + 4. Corrected Radio drivers I&Q signals inversion to be possible in Rx and in Tx. + Added some missing radio state machine initialization. + 5. Changed the RSSI values type from int8_t to int16_t. We can have RSSI values below -128 dBm. + 6. Corrected SNR computation on RxDone interrupt. + 7. Updated radio API to support FHSS and CAD handling. + 8. Corrected in SetRxConfig function the FSK modem preamble register name. + 9. Added an invalid bandwidth to the Bandwidths table in order to avoid an error + when selecting 250 kHz bandwidth when using FSK modem. + + 10. Corrected RTC alarm setup which could be set to an invalid date. + 11. Added another timer in order increment the tick counter without blocking the normal timer count. + 12. Added the possibility to switch between low power timers and normal timers on the fly. + 13. I2C driver corrected the 2 bytes internal address management. + Corrected buffer read function when more that 1 byte was to be read. + Added a function to wait for the I2C bus to become IDLE. + 14. Added an I2C EEPROM driver. + 15. Corrected and improved USB Virtual COM Port management files. + Corrected the USB CDC and USB UART drivers. + 16. Added the possibility to analyze a hard fault interrupt. + +* LoRaMac + 1. Corrected RxWindow2 Datarate management. + 2. SrvAckRequested variable was never reset. + 3. Corrected tstIndoor applications for LoRaMac R3.0 support. + 4. LoRaMac added the possibility to configure almost all the MAC parameters. + 5. Corrected the LoRaMacSetNextChannel function. + 6. Corrected the port 0 MAC command decoding. + 7. Changed all structures declarations to be packed. + 8. Corrected the Acknowledgment retries management when only 1 trial is needed. + Before the device was issuing at least 2 trials. + 9. Corrected server mac new channel req answer. + 10. Added the functions to read the Up and Down Link sequence counters. + 11. Corrected SRV_MAC_RX2_SETUP_REQ frequency handling. Added a 100 multiplication. + 12. Corrected SRV_MAC_NEW_CHANNEL_REQ. Removed the DutyCycle parameter decoding. + 13. Automatically activate the channel once it is created. + 14. Corrected NbRepTimeoutTimer initial value. RxWindow2Delay already contains RxWindow1Delay in it. + +### 2014-07-18, v3.0.0 + +* General + 1. Added to Radio API the possibility to select the modem. + 2. Corrected RSSI reading formulas as well as changed the RSSI and SNR values from double to int8_t type. + 3. Changed radio callbacks events to timeout when it is a timeout event and error when it is a CRC error. + 4. Radio API updated. + 5. Updated ping-pong applications. + 6. Updated tx-cw applications. + 7. Updated LoRaMac applications in order to handle LoRaMac returned functions calls status. + 8. Updated LoRaMac applications to toggle LED2 each time there is an application payload down link. + 9. Updated tstIndoor application to handle correctly more than 6 channels. + 10. Changed the MPL3115 altitude variable from unsigned to signed value. + 11. Replaced the usage of pow(2, n) by defining POW2 functions. Saves ~2 KBytes of code. + 12. Corrected an issue potentially arriving when LOW_POWER_MODE_ENABLE wasn't defined. + A timer interrupt could be generated while the TimerList could already be emptied. + +* LoRaMac + 1. Implemented LoRaMac specification R3.0 changes. + + 2. MAC commands implemented + * LinkCheckReq **YES** + * LinkCheckAns **YES** + * LinkADRReq **YES** + * LinkADRAns **YES** + * DutyCycleReq **YES** + * DutyCycleAns **YES** + * Rx2SetupReq **YES** + * Rx2SetupAns **YES** + * DevStatusReq **YES** + * DevStatusAns **YES** + * JoinReq **YES** + * JoinAccept **YES** + * NewChannelReq **YES** + * NewChannelAns **YES** + + 3. Features implemented + * Possibility to shut-down the device **YES** + + Possible by issuing DutyCycleReq MAC command. + * Duty cycle management enforcement **NO** + * Acknowledgments retries **YES** + * Unconfirmed messages retries **YES** + +### 2014-07-10, v2.3.RC2 + +* General + 1. Corrected all radios antenna switch low power mode handling. + 2. SX1276: Corrected antenna switch control. + +### 2014-06-06, v2.3.RC1 + +* General + 1. Added the support for SX1276 radio. + 2. Radio continuous reception mode correction. + 3. Radio driver RxDone callback function API has changed ( size parameter is no more a pointer). + Previous function prototype: + + void ( *RxDone )( uint8_t *payload, uint16_t *size, double rssi, double snr, uint8_t rawSnr ); + + New function prototype: + + void ( *RxDone )( uint8_t *payload, uint16_t size, double rssi, double snr, uint8_t rawSnr ); + + 4. Added Bleeper-76 and SensorNode platforms support. + 5. Added to the radio drivers a function that generates a random value from + RSSI readings. + 6. Added a project to transmit a continuous wave and a project to measure the + the radio sensitivity. + 7. Added a bootloader project for the LoRaMote and SensorNode platforms. + 8. The LoRaMac application for Bleeper platforms now sends the Selector and LED status plus the sensors values. + * The application payload for the Bleeper platforms is as follows: + + LoRaMac port 1: + + { 0xX0/0xX1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } + ---------- ---------- ---------- ---------- ---- + | | | | | + SELECTOR/LED PRESSURE TEMPERATURE ALTITUDE BATTERY + MSB nibble = SELECTOR (barometric) + LSB bit = LED + 9. Redefined rand() and srand() standard C functions. These functions are + redefined in order to get the same behavior across different compiler + tool chains implementations. + 10. GPS driver improvements. Made independent of the board platform. + 11. Simplified the RTC management. + 12. Added a function to the timer driver that checks if a timer is already in + the list or not. + 13. Added the UART Overrun bit exception handling to the UART driver. + 14. Removed dependency of spi-board files to the "__builtin_ffs" function. + This function is only available on GNU compiler tool suite. Removed --gnu + compiler option from Keil projects. Added own __ffs function + implementation to utilities.h file. + 15. Removed obsolete class1 devices support. + 16. Known bugs correction. + +* LoRaMac + 1. MAC commands implemented + * LinkCheckReq **YES** + * LinkCheckAns **YES** + * LinkADRReq **YES** + * LinkADRAns **YES** + * DutyCycleReq **YES** (LoRaMac specification R2.2.1) + * DutyCycleAns **YES** (LoRaMac specification R2.2.1) + * Rx2SetupReq **YES** (LoRaMac specification R2.2.1) + * Rx2SetupAns **YES** (LoRaMac specification R2.2.1) + * DevStatusReq **YES** + * DevStatusAns **YES** + * JoinReq **YES** + * JoinAccept **YES** (LoRaMac specification R2.2.1) + * NewChannelReq **YES** (LoRaMac specification R2.2.1) + * NewChannelAns **YES** (LoRaMac specification R2.2.1) + 2. Features implemented + * Possibility to shut-down the device **YES** + + Possible by issuing DutyCycleReq MAC command. + * Duty cycle management enforcement **NO** + * Acknowledgments retries **WORK IN PROGRESS** + + Not fully debugged. Disabled by default. + * Unconfirmed messages retries **WORK IN PROGRESS** (LoRaMac specification R2.2.1) + 3. Implemented LoRaMac specification R2.2.1 changes. + 4. Due to new specification the LoRaMacInitNwkIds LoRaMac API function had + to be modified. + + Previous function prototype: + + void LoRaMacInitNwkIds( uint32_t devAddr, uint8_t *nwkSKey, uint8_t *appSKey ); + + New function prototype: + + void LoRaMacInitNwkIds( uint32_t netID, uint32_t devAddr, uint8_t *nwkSKey, uint8_t *appSKey ); + 5. Changed the LoRaMac channels management. + 6. LoRaMac channels definition has been moved to LoRaMac-board.h file + located in each specific board directory. + +### 2014-04-07, v2.2.0 + +* General + 1. Added IMST SK-iM880A starter kit board support to the project. + * The application payload for the SK-iM880A platform is as follows: + + LoRaMac port 3: + + { 0x00/0x01, 0x00, 0x00, 0x00 } + ---------- ----- ---------- + | | | + LED POTI VDD + 2. Ping-Pong applications have been split per supported board. + 3. Corrected the SX1272 output power management. + Added a variable to store the current Radio channel. + Added missing FSK bit definition. + 4. Made fifo functions coding style coherent with the project. + 5. UART driver is now independent of the used MCU + +### 2014-03-28, v2.1.0 + +* General + 1. The timers and RTC management has been rewritten. + 2. Improved the UART and UP501 GPS drivers. + 3. Corrected GPIO pin names management. + 4. Corrected the antenna switch management in the SX1272 driver. + 5. Added to the radio driver the possibility to choose the preamble length + and rxSingle symbol timeout in reception. + 6. Added Hex coder selector driver for the Bleeper board. + 7. Changed copyright Unicode character to (C) in all source files. + +* LoRaMac + 1. MAC commands implemented + * LinkCheckReq **YES** + * LinkCheckAns **YES** + * LinkADRReq **YES** + * LinkADRAns **YES** + * DevStatusReq **YES** + * DevStatusAns **YES** + * JoinReq **YES** + * JoinAccept **YES** + 2. Added acknowledgments retries management. + Split the LoRaMacSendOnChannel function in LoRaMacPrepareFrame and + LoRaMacSendFrameOnChannel. LoRaMacSendOnChannel now calls the 2 newly + defined functions. + **WARNING**: By default the acknowledgment retries specific code isn't + enabled. The current http://iot.semtech.com server version doesn't support + it. + 3. Corrected issues on JoinRequest and JoinAccept MAC commands. + Added LORAMAC_EVENT_INFO_STATUS_MAC_ERROR event info status. + +### 2014-02-21, v2.0.0 + +* General + 1. The LoRaMac applications now sends the LED status plus the sensors values. + For the LoRaMote platform the application also sends the GPS coordinates. + * The application payload for the Bleeper platform is as follows: + + LoRaMac port 1: + + { 0x00/0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } + ---------- ---------- ---------- ---------- ---- + | | | | | + LED PRESSURE TEMPERATURE ALTITUDE BATTERY + (barometric) + * The application payload for the LoRaMote platform is as follows: + + LoRaMac port 2: + + { 0x00/0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } + ---------- ---------- ---------- ---------- ---- ---------------- ---------------- ---------- + | | | | | | | | + LED PRESSURE TEMPERATURE ALTITUDE BATTERY LATITUDE LONGITUDE ALTITUDE + (barometric) (gps) + 2. Adapted applications to the new MAC layer API. + 3. Added sensors drivers implementation. + 4. Corrected new or known issues. +* LoRaMac + 1. MAC commands implemented + * LinkCheckReq **YES** + * LinkCheckAns **YES** + * LinkADRReq **YES** + * LinkADRAns **YES** + * DevStatusReq **YES** + * DevStatusAns **YES** + * JoinReq **YES (Not tested)** + * JoinAccept **YES (Not tested)** + 2. New MAC layer application API implementation. +* Timers and RTC. + 1. Still some issues. They will be corrected on next revisions of the firmware. + +### 2014-01-24, v1.1.0 + +* LoRaMac + 1. MAC commands implemented + * LinkCheckReq **NO** + * LinkCheckAns **NO** + * LinkADRReq **YES** + * LinkADRAns **YES** + * DevStatusReq **YES** + * DevStatusAns **YES** + 2. Implemented an application LED control + If the server sends on port 1 an application payload of one byte with + the following contents: + + 0: LED off + 1: LED on + The node transmits periodically on port 1 the LED status on 1st byte and + the message "Hello World!!!!" the array looks like: + + { 0, 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!', '!', '!', '!' } +* Timers and RTC. + 1. Corrected issues existing in the previous version + 2. Known bugs are: + * There is an issue when launching an asynchronous Timer. Will be solved + in a future version + +### 2014-01-20, v1.1.RC1 + +* Added Doc directory. The directory contains: + 1. LoRa MAC specification + 2. Bleeper board schematic +* LoRaMac has been updated according to Release1 of the specification. Main changes are: + 1. MAC API changed. + 2. Frame format. + 3. ClassA first ADR implementation. + 4. MAC commands implemented + * LinkCheckReq **NO** + * LinkCheckAns **NO** + * LinkADRReq **YES** + * LinkADRAns **NO** + * DevStatusReq **NO** + * DevStatusAns **NO** +* Timers and RTC rewriting. Known bugs are: + 1. The Radio wakeup time is taken in account for all timings. + 2. When opening the second reception window the microcontroller sometimes doesn't enter in low power mode. + +### 2013-11-28, v1.0.0 + +* Initial version of the LoRa MAC node firmware implementation. diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/st_readme.txt b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/st_readme.txt new file mode 100644 index 00000000..8cc703e5 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/LoRaWAN/st_readme.txt @@ -0,0 +1,212 @@ + @verbatim + ****************************************************************************** + * @file st_readme.txt + * @author MCD Application Team + * @brief This file lists the main modification done by STMicroelectronics on + * LoRa for integration with STM32Cube solution. + * For more details on LoRa implementation on STM32Cube, please refer + * to UM2073 "STM32 LORA Expansion Package for STM32Cube " + ****************************************************************************** + * @attention + * + *

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

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + @endverbatim + +### V1.2.4/28-Nov-2019 ### +=============================== + + Implements LoRa Mac from Semtech/StackForce develop branch (31-Oct-2019 commits, version 4.4.2) + + + Patterns/basic and Patterns/modem directory structure change + + + update files license + + + certification obtained with LoRAWAN version v1.0.2 in the following regions: + - EU868 + - IN865 + - KR920 + - AS923 + - US915 + + +### V1.2.3/20-Sept-2019 ### +=============================== + + Implements LoRa Mac from Semtech/StackForce develop branch (17-Jun-2019 commits, 4.4.2 release candidate) + https://github.com/Lora-net/LoRaMac-node/tree/6231a5d65806142a6f887e6183227b8da9e49238 + + + Various bug fixes and enhancements + + + Removed inclusions of radio.h, utilities.h, etc. from loraMac.h API + + Lora.c/h and lora-test.c/h moved from Certifications/ to Patterns/Basic + +### V1.2.2/17-June-2019 ### +=============================== + + Trace logging & LoRa Mac version display updates + + Some function prototypes added + +### V1.2.1/13-December-2018 ### +=============================== + + Implements LoRa Mac 4.4.2-rc.5 from Semtech/StackForce develop branch + +### V1.2.0/10-July-2018 ### +=============================== + + Implements LoRa Mac 4.4.2-rc.1 from Semtech/StackForce develop branch + + + Supports Class B + + Features secure element API + + Added RU864 region + + Removed US915-Hybrid + +### V1.1.5/30-March-2018 ### +=============================== + + Implements LoRa Mac 4.4.1 from Semtech/StackForce + +### V1.1.4/08-January-2018 ### +=============================== + + Bug fix in McpsIndication related to downlink Application data buffer + +### V1.1.3/20-December-2017 ### +=============================== + + Implements LoRa Mac from Semtech/StackForce develop branch (10-Oct-2017 commits, 4.4.1 release candidate) + https://github.com/Lora-net/LoRaMac-node/tree/ae0c9cc5d53ebbc11437dfe9711a1c3a1114dc5f + + + Merge pull request #302 from nestorayuso/patch-3 + +### V1.1.2/08-September-2017 ### +=============================== + + Implements LoRa Mac 4.4.0 release from Semtech/StackForce + https://github.com/Lora-net/LoRaMac-node/tree/f42be67be402a40b3586724800771bfe13fb18e6 + + + AckTimeoutRetriesCounter must be reset every time a new request (unconfirmed or confirmed) is performed. + + Added verification for the power parameter in order to check that it is greater or equal to 0. + + + Enhancement for regions without FSK modulation support. + + Bug fix for RX window 2 in class c mode. + + AdrAckReq bit must be set to false as soon as we reach the lowest datarate. + + MacCommandsBufferIndex must be reset when the mac commands are being sent on port 0. + + Fixed RegionCommonSetBandTxDone in order to also update band->LastTxDoneTime when performing the Join procedure. + + Added verification of payload size for Unconfirmed and Confirmed messages depending on Dwell time. + + + Added missing Rx1 timeout handling. + + Updated all regions to use MAX output power by default. + + Merge remote-tracking branch 'origin/develop' into develop + + Bug fix in KR920 - update the maxEIRP calculation for continuous wave + + Bug fix in IN865 - Update the band of the default channels + + Bug fix in AS923 - for RX use always the payload limitation of dwell 0 + + Update function RegionCommonChanVerifyDr. Perform an 'AND' operation for security + + Move the verification of ADR parameters into the common section. Update all regions with the related changes + + Update comment for function RegionLinkAdrReq + + Apply variables to data structure LinkAdrReqParams_t + + Bug fix for KR920 + + Rename data structure LinkAdrParams_t + + Issue(#238): Apply missing variable in sSetBandTxDoneParams + + Bug fix in processing MAC commands for case SRV_MAC_TX_PARAM_SETUP_REQ. + + Isse(#238): Apply missing variable in struct sBand + + Update the MAC to enable the server to control the Channels Mask and the number of transmissions even ADR is off + + Issue(#238): Update the backoff procedure for all regions. Move code parts into the common section + + Update implementation to allow automatic MAC answers on port 0 + + Issue(#253): Delete all preconfigured channels when performing a join request + + Initialize variable phyParam in functions RegionXXGetPhyParam + + Remove assert_param from the radio drivers + + Synchronize function RegionXXTxConfig + + Update carrier sense functionality for LBT + + Issue(#259): Update comment for variable HasLoopedThroughMain + + Issue(#257): Fix typo in OnRxWindow2TimerEvent + + Change scientific notation to numeric notation + + Merge pull request #260 from clmklk/AU915 + + + AU915: update Datarate limits according to LoRaWan 1.0.2rB + + AU915: update Downstream datarate table according to LoRaWan 1.0.2rB + + + Merge pull request #225 from OpenChirp/patch_1 + +### V1.1.1/01-June-2017 ### +=============================== + + Implements LoRa Mac from Semtech/StackForce develop branch (30-May-2017 commits, 4.4.0 release candidate) + https://github.com/Lora-net/LoRaMac-node/tree/e2f35db75c1b449379d3b520c2d4e5922a9f5c81 + + + Issue(235): Update functions RegionXXNextChannel. + + Issue(238): Update initialization value of nextTxDelay. + + Issue(234): Report back the aggregated time off. + + Issue(232): Relocate function call to CalculateBackOff. + + Issue(239): Update band TX power to the maximum. + + Update LimitTxPower for US915. + + Bug fix in function RegionCN470Verify. + + Bug fix in function RegionAU915Verify. + + Issue(229): Fix issue when receiving frames in second RX2 in Class C. + + Add a ommand to get the next lower datarate. + + Group initializations + + Update regional definitions of KR920. + + Update regional definitions of EU868. + + Issue(239): Update regional definitions of AU915. + + Update regional definitions of AU915. + + Update regional definitions of AS923. + + Update TX power computations. + + + Remove duplicated call to ApplyDrOffset in function RegionRxConfig. + + Relocate the datarate, up- and downlink dwell time into a structure. + + Change API of RegionGetPhyParam and the related functions. + + Bug fix in function LoRaMacQueryTxPossible. + + Apply patch for dwell time and minimum datarate. + + Change the default datarate to DR_2 for AS923. + + Take dwell time for ADR calculations and datarate settings into account. + + Update LoRaMacQueryTxPossible to reset the MAC commands buffer. + + + Issue(#221): Add the dwell time in function ValidatePayloadLength. + + Increase the transmission and reception timeout for KR920. + + Bug fix in functions OnRxWindowXTimerEvent. + + Remove datarate assignment. + + Setup the downlink and uplink dwell time default value to 1. + + Add frequency range check for AS923 + + Issue(#221): Bug fix in max payload size calculation. + + + GitHub reported issues corrections. + + Changed the AdrAckCounter handling as expected by the test houses. + + Fix an issue where the node stopped transmitting. + + Removed useless LoRaMacPayload buffer. + + MAC layer indications handling simplification. + + Relocate parameter settings from ResetMacParameters to the initialization. + +### V1.1.0/27-February-2017 ### +=============================== + + Implements LoRa Mac 4.4.0 from Semtech/StackForce from the develop branch + +### V1.0.3/01-January-2017 ### +=============================== + + Read date between 2 successive read time to make sure date is ok + +### V1.0.2/15-November-2016 ### +=============================== + + Corrected 1 bug in LoRaMac-board.h: RX_WND_2_CHANNEL for EU is now back at DR_0 + + Corrected 1 bug in LoRaMac.c for dataRate adaptation + +### V1.0.1/15-September-2016 ### +=============================== + + Implements LoRa Mac 4.3.0 from Semtech/StackForce + +### V1.0.0/01-July-2016 ### +=============================== + + First R1.0.0 customized version for STM32Cube solution. + + Comissioning_template.h in /Conf contains all Lora Ids to connect on LoRa network + It is provided as a template. It must be moved to /Projects/inc/ as Comissioning.h + + All files in Conf/src are provided as template and must be copied in /Projects/src. + + All files in Conf/inc are provided as template and must be copied in /Projects/inc. + #if 0 and #endif must be removed to enable the template in the user directory + + Implements LoRa Mac 4.2.0 from Semtech/StackForce + + \Lora\Mac\LoRaMac.c : replace floating exponent e3 and e6 by int number + + \Lora\Utilities\delay.c : cast uint32_t + + Modified intensively timeServer.c + + new low layer interfacing Cube HAL (hw_rtc.c, hw_gpio.c and hw_spi.c) + + added lora.c as an interface layer to ease product integration + + + *

© COPYRIGHT STMicroelectronics

+ */ + \ No newline at end of file diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/LICENSE.txt b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/LICENSE.txt new file mode 100644 index 00000000..d6dfb1b8 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/LICENSE.txt @@ -0,0 +1,25 @@ +--- Revised BSD License --- +Copyright (c) 2013, SEMTECH S.A. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Semtech corporation nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL SEMTECH S.A. BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/radio.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/radio.h new file mode 100644 index 00000000..2885966d --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/radio.h @@ -0,0 +1,418 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + +Description: Generic radio driver definition + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis and Gregory Cristian +*/ +/** + ****************************************************************************** + * + * Portions COPYRIGHT 2019 STMicroelectronics + * + * @file radio.h + * @author MCD Application Team + * @brief generic radio driver definition + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __RADIO_H__ +#define __RADIO_H__ + +/* Includes ------------------------------------------------------------------*/ + +#include +#include + +/* Private typedef -----------------------------------------------------------*/ + +/*! + * Radio driver supported modems + */ +typedef enum +{ + MODEM_FSK = 0, + MODEM_LORA, + MODEM_BPSK, + MODEM_SIGFOX_TX, + MODEM_SIGFOX_RX, +}RadioModems_t; + +/*! + * Radio driver internal state machine states definition + */ +typedef enum +{ + RF_IDLE = 0, //!< The radio is idle + RF_RX_RUNNING, //!< The radio is in reception state + RF_TX_RUNNING, //!< The radio is in transmission state + RF_CAD, //!< The radio is doing channel activity detection +}RadioState_t; + +/*! + * \brief Radio driver callback functions + */ +typedef struct +{ + /*! + * \brief Tx Done callback prototype. + */ + void ( *TxDone )( void ); + /*! + * \brief Tx Timeout callback prototype. + */ + void ( *TxTimeout )( void ); + /*! + * \brief Rx Done callback prototype. + * + * \param [IN] payload Received buffer pointer + * \param [IN] size Received buffer size + * \param [IN] rssi RSSI value computed while receiving the frame [dBm] + * \param [IN] snr SNR value computed while receiving the frame [dB] + * FSK : N/A ( set to 0 ) + * LoRa: SNR value in dB + */ + void ( *RxDone )( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ); + /*! + * \brief Rx Timeout callback prototype. + */ + void ( *RxTimeout )( void ); + /*! + * \brief Rx Error callback prototype. + */ + void ( *RxError )( void ); + /*! + * \brief FHSS Change Channel callback prototype. + * + * \param [IN] currentChannel Index number of the current channel + */ + void ( *FhssChangeChannel )( uint8_t currentChannel ); + + /*! + * \brief CAD Done callback prototype. + * + * \param [IN] channelDetected Channel Activity detected during the CAD + */ + void ( *CadDone ) ( bool channelActivityDetected ); +}RadioEvents_t; + +/*! + * \brief Radio driver definition + */ +struct Radio_s +{ + /*! + * \brief Initializes the io + */ +// void ( *IoInit )( void ); + /*! + * \brief Deinitializes the io + */ +// void ( *IoDeInit )( void ); + /*! + * \brief Initializes the radio + * + * \param [IN] events Structure containing the driver callback functions + * \param [OUT] returns radioWakeUpTime + */ + uint32_t ( *Init )( RadioEvents_t *events ); + /*! + * Return current radio status + * + * \param status Radio status.[RF_IDLE, RF_RX_RUNNING, RF_TX_RUNNING] + */ + RadioState_t ( *GetStatus )( void ); + /*! + * \brief Configures the radio with the given modem + * + * \param [IN] modem Modem to be used [0: FSK, 1: LoRa] + */ + void ( *SetModem )( RadioModems_t modem ); + /*! + * \brief Sets the channel frequency + * + * \param [IN] freq Channel RF frequency + */ + void ( *SetChannel )( uint32_t freq ); + /*! + * \brief Checks if the channel is free for the given time + * + * \param [IN] modem Radio modem to be used [0: FSK, 1: LoRa] + * \param [IN] freq Channel RF frequency + * \param [IN] rssiThresh RSSI threshold + * \param [IN] maxCarrierSenseTime Max time while the RSSI is measured + * + * \retval isFree [true: Channel is free, false: Channel is not free] + */ + bool ( *IsChannelFree )( RadioModems_t modem, uint32_t freq, int16_t rssiThresh, uint32_t maxCarrierSenseTime ); + /*! + * \brief Generates a 32 bits random value based on the RSSI readings + * + * \remark This function sets the radio in LoRa modem mode and disables + * all interrupts. + * After calling this function either Radio.SetRxConfig or + * Radio.SetTxConfig functions must be called. + * + * \retval randomValue 32 bits random value + */ + uint32_t ( *Random )( void ); + /*! + * \brief Sets the reception parameters + * + * \param [IN] modem Radio modem to be used [0: FSK, 1: LoRa] + * \param [IN] bandwidth Sets the bandwidth + * FSK : >= 2600 and <= 250000 Hz + * LoRa: [0: 125 kHz, 1: 250 kHz, + * 2: 500 kHz, 3: Reserved] + * \param [IN] datarate Sets the Datarate + * FSK : 600..300000 bits/s + * LoRa: [6: 64, 7: 128, 8: 256, 9: 512, + * 10: 1024, 11: 2048, 12: 4096 chips] + * \param [IN] coderate Sets the coding rate (LoRa only) + * FSK : N/A ( set to 0 ) + * LoRa: [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] + * \param [IN] bandwidthAfc Sets the AFC Bandwidth (FSK only) + * FSK : >= 2600 and <= 250000 Hz + * LoRa: N/A ( set to 0 ) + * \param [IN] preambleLen Sets the Preamble length + * FSK : Number of bytes + * LoRa: Length in symbols (the hardware adds 4 more symbols) + * \param [IN] symbTimeout Sets the RxSingle timeout value + * FSK : timeout in number of bytes + * LoRa: timeout in symbols + * \param [IN] fixLen Fixed length packets [0: variable, 1: fixed] + * \param [IN] payloadLen Sets payload length when fixed length is used + * \param [IN] crcOn Enables/Disables the CRC [0: OFF, 1: ON] + * \param [IN] freqHopOn Enables disables the intra-packet frequency hopping + * FSK : N/A ( set to 0 ) + * LoRa: [0: OFF, 1: ON] + * \param [IN] hopPeriod Number of symbols between each hop + * FSK : N/A ( set to 0 ) + * LoRa: Number of symbols + * \param [IN] iqInverted Inverts IQ signals (LoRa only) + * FSK : N/A ( set to 0 ) + * LoRa: [0: not inverted, 1: inverted] + * \param [IN] rxContinuous Sets the reception in continuous mode + * [false: single mode, true: continuous mode] + */ + void ( *SetRxConfig )( RadioModems_t modem, uint32_t bandwidth, + uint32_t datarate, uint8_t coderate, + uint32_t bandwidthAfc, uint16_t preambleLen, + uint16_t symbTimeout, bool fixLen, + uint8_t payloadLen, + bool crcOn, bool freqHopOn, uint8_t hopPeriod, + bool iqInverted, bool rxContinuous ); + /*! + * \brief Sets the transmission parameters + * + * \param [IN] modem Radio modem to be used [0: FSK, 1: LoRa] + * \param [IN] power Sets the output power [dBm] + * \param [IN] fdev Sets the frequency deviation (FSK only) + * FSK : [Hz] + * LoRa: 0 + * \param [IN] bandwidth Sets the bandwidth (LoRa only) + * FSK : 0 + * LoRa: [0: 125 kHz, 1: 250 kHz, + * 2: 500 kHz, 3: Reserved] + * \param [IN] datarate Sets the Datarate + * FSK : 600..300000 bits/s + * LoRa: [6: 64, 7: 128, 8: 256, 9: 512, + * 10: 1024, 11: 2048, 12: 4096 chips] + * \param [IN] coderate Sets the coding rate (LoRa only) + * FSK : N/A ( set to 0 ) + * LoRa: [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] + * \param [IN] preambleLen Sets the preamble length + * FSK : Number of bytes + * LoRa: Length in symbols (the hardware adds 4 more symbols) + * \param [IN] fixLen Fixed length packets [0: variable, 1: fixed] + * \param [IN] crcOn Enables disables the CRC [0: OFF, 1: ON] + * \param [IN] freqHopOn Enables disables the intra-packet frequency hopping + * FSK : N/A ( set to 0 ) + * LoRa: [0: OFF, 1: ON] + * \param [IN] hopPeriod Number of symbols between each hop + * FSK : N/A ( set to 0 ) + * LoRa: Number of symbols + * \param [IN] iqInverted Inverts IQ signals (LoRa only) + * FSK : N/A ( set to 0 ) + * LoRa: [0: not inverted, 1: inverted] + * \param [IN] timeout Transmission timeout [ms] + */ + void ( *SetTxConfig )( RadioModems_t modem, int8_t power, uint32_t fdev, + uint32_t bandwidth, uint32_t datarate, + uint8_t coderate, uint16_t preambleLen, + bool fixLen, bool crcOn, bool freqHopOn, + uint8_t hopPeriod, bool iqInverted, uint32_t timeout ); + /*! + * \brief Checks if the given RF frequency is supported by the hardware + * + * \param [IN] frequency RF frequency to be checked + * \retval isSupported [true: supported, false: unsupported] + */ + bool ( *CheckRfFrequency )( uint32_t frequency ); + /*! + * \brief Computes the packet time on air in ms for the given payload + * + * \Remark Can only be called once SetRxConfig or SetTxConfig have been called + * + * \param [IN] modem Radio modem to be used [0: FSK, 1: LoRa] + * \param [IN] pktLen Packet payload length + * + * \retval airTime Computed airTime (ms) for the given packet payload length + */ + uint32_t ( *TimeOnAir )( RadioModems_t modem, uint8_t pktLen ); + /*! + * \brief Sends the buffer of size. Prepares the packet to be sent and sets + * the radio in transmission + * + * \param [IN]: buffer Buffer pointer + * \param [IN]: size Buffer size + */ + void ( *Send )( uint8_t *buffer, uint8_t size ); + /*! + * \brief Sets the radio in sleep mode + */ + void ( *Sleep )( void ); + /*! + * \brief Sets the radio in standby mode + */ + void ( *Standby )( void ); + /*! + * \brief Sets the radio in reception mode for the given time + * \param [IN] timeout Reception timeout [ms] + * [0: continuous, others timeout] + */ + void ( *Rx )( uint32_t timeout ); + /*! + * \brief Start a Channel Activity Detection + */ + void ( *StartCad )( void ); + /*! + * \brief Sets the radio in continuous wave transmission mode + * + * \param [IN]: freq Channel RF frequency + * \param [IN]: power Sets the output power [dBm] + * \param [IN]: time Transmission mode timeout [s] + */ + void ( *SetTxContinuousWave )( uint32_t freq, int8_t power, uint16_t time ); + /*! + * \brief Reads the current RSSI value + * + * \retval rssiValue Current RSSI value in [dBm] + */ + int16_t ( *Rssi )( RadioModems_t modem ); + /*! + * \brief Writes the radio register at the specified address + * + * \param [IN]: addr Register address + * \param [IN]: data New register value + */ + void ( *Write )( uint16_t addr, uint8_t data ); + /*! + * \brief Reads the radio register at the specified address + * + * \param [IN]: addr Register address + * \retval data Register value + */ + uint8_t ( *Read )( uint16_t addr ); + /*! + * \brief Writes multiple radio registers starting at address + * + * \param [IN] addr First Radio register address + * \param [IN] buffer Buffer containing the new register's values + * \param [IN] size Number of registers to be written + */ +// void ( *WriteBuffer )( uint16_t addr, uint8_t *buffer, uint8_t size ); + /*! + * \brief Reads multiple radio registers starting at address + * + * \param [IN] addr First Radio register address + * \param [OUT] buffer Buffer where to copy the registers data + * \param [IN] size Number of registers to be read + */ +// void ( *ReadBuffer )( uint16_t addr, uint8_t *buffer, uint8_t size ); + /*! + * \brief Sets the maximum payload length. + * + * \param [IN] modem Radio modem to be used [0: FSK, 1: LoRa] + * \param [IN] max Maximum payload length in bytes + */ + void ( *SetMaxPayloadLength )( RadioModems_t modem, uint8_t max ); + /*! + * \brief Sets the network to public or private. Updates the sync byte. + * + * \remark Applies to LoRa modem only + * + * \param [IN] enable if true, it enables a public network + */ + void ( *SetPublicNetwork )( bool enable ); + /*! + * \brief Gets the time required for the board plus radio to get out of sleep.[ms] + * + * \retval time Radio plus board wakeup time in ms. + */ + uint32_t ( *GetWakeupTime )( void ); + /*! + * \brief Process radio irq + */ + void ( *IrqProcess )( void ); + /* + * The next functions are available only on SX126x radios. + */ + /*! + * \brief Sets the radio in reception mode with Max LNA gain for the given time + * + * \remark Available on SX126x radios only. + * + * \param [IN] timeout Reception timeout [ms] + * [0: continuous, others timeout] + */ + void ( *RxBoosted )( uint32_t timeout ); + /*! + * \brief Sets the Rx duty cycle management parameters + * + * \remark Available on SX126x radios only. + * + * \param [in] rxTime Structure describing reception timeout value + * \param [in] sleepTime Structure describing sleep timeout value + */ + void ( *SetRxDutyCycle ) ( uint32_t rxTime, uint32_t sleepTime ); + + + + /*! + * @brief Sets the Transmitter in continuous PRBS mode + * + * \remark power and datarate shall be configured prior calling TxPrbs + */ + void (*TxPrbs) ( void ); + /*! + * @brief Sets the Transmitter in continuous unmodulated Carrier mode + */ + void (*TxCw) ( void ); +}; + +/*! + * \brief Radio driver + * + * \remark This variable is defined and initialized in the specific radio + * board implementation + */ +extern const struct Radio_s Radio; +/* Private defines -----------------------------------------------------------*/ +/* Private macros ------------------------------------------------------------*/ +/* Private variables ---------------------------------------------------------*/ +/* Global variables ----------------------------------------------------------*/ +/* Private function prototypes -----------------------------------------------*/ +/* Functions Definition ------------------------------------------------------*/ + +#endif // __RADIO_H__ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/stm32_radio_driver/mw_log_conf_template.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/stm32_radio_driver/mw_log_conf_template.h new file mode 100644 index 00000000..89809fc8 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/stm32_radio_driver/mw_log_conf_template.h @@ -0,0 +1,61 @@ +/** + ****************************************************************************** + * @file mw_log_conf.h + * @author MCD Application Team + * @brief Interface layer CM4 System to MBMUX (Mailbox Multiplexer) + ****************************************************************************** + * @attention + * + *

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

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __MW_LOG_CONF_H__ +#define __MW_LOG_CONF_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include +#include "trace.h" + +/* Exported types ------------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ +#define MW_LOG_ENABLED +/* Exported macro ------------------------------------------------------------*/ + +#ifdef MW_LOG_ENABLED +#define PPRINTF(...) do{ } while( 0!= TraceSend(TIME_STAMP_OFF, 0, __VA_ARGS__) ) //Polling Mode +#define TPRINTF(...) do{ {TraceSend(TIME_STAMP_ON, 0, __VA_ARGS__);} }while(0)//with timestamp +#define PRINTF(...) do{ {TraceSend(TIME_STAMP_OFF, 0, __VA_ARGS__);} }while(0) +#define MW_LOG(VL, TS, ...) do{ {TraceSend(TS, VL, __VA_ARGS__);} }while(0) + +#else +#define PPRINTF(...) +#define TPRINTF(...) +#define PRINTF(...) +#define MW_LOG(VL, TS, ...) +#endif + + +/* Exported functions ------------------------------------------------------- */ + + + +#ifdef __cplusplus +} +#endif + +#endif /*__MW_LOG_CONF_H__ */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/stm32_radio_driver/radio.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/stm32_radio_driver/radio.c new file mode 100644 index 00000000..930bb063 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/stm32_radio_driver/radio.c @@ -0,0 +1,1531 @@ +/*! + * \file radio.c + * + * \brief Radio driver API definition + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + */ +/** + ****************************************************************************** + * + * Portions COPYRIGHT 2019 STMicroelectronics + * + * @file radio.c + * @author MCD Application Team + * @brief radio API definition + ****************************************************************************** + */ +/* Includes ------------------------------------------------------------------*/ +#include + +#include "radio.h" +#include "timer.h" +#include "radio_driver.h" +#include "radio_conf.h" +#include "mw_log_conf.h" + +/* + * Local types definition + */ +/* + * Private types + */ +/*! + * Radio hardware and global parameters + */ +typedef struct SubgRf_s +{ + RadioModems_t modem; + bool RxContinuous; + uint32_t TxTimeout; + uint32_t RxTimeout; + bool publicNetwork; + PacketParams_t PacketParams; + PacketStatus_t PacketStatus; + ModulationParams_t ModulationParams; + RadioIrqMasks_t radioIrq; +}SubgRf_t; + +#define RADIO_BIT_MASK(__n) (~(1<<__n)) + +/* + * Private functions prototypes + */ + + +/* ********************************************************************** + * Interrupts functions prototypes + ************************************************************************/ +/*! + * @brief Process radio irq + */ +void RadioIrqProcess( void ); + +/*! + * @brief Tx timeout timer callback + */ +void RadioOnTxTimeoutIrq( void * context ); + +/*! + * @brief Rx timeout timer callback + */ +void RadioOnRxTimeoutIrq( void * context ); + + +/* ********************************************************************** + * Middleware Interface functions prototypes + ************************************************************************/ + +/*! + * \brief Initializes the radio + * + * \param [IN] events Structure containing the driver callback functions + */ +uint32_t RadioInit( RadioEvents_t *events ); + +/*! + * Return current radio status + * + * \param status Radio status.[RF_IDLE, RF_RX_RUNNING, RF_TX_RUNNING] + */ +RadioState_t RadioGetStatus( void ); + +/*! + * \brief Configures the radio with the given modem + * + * \param [IN] modem Modem to be used [0: FSK, 1: LoRa] + */ +void RadioSetModem( RadioModems_t modem ); + +/*! + * \brief Sets the channel frequency + * + * \param [IN] freq Channel RF frequency + */ +void RadioSetChannel( uint32_t freq ); + +/*! + * \brief Checks if the channel is free for the given time + * + * \param [IN] modem Radio modem to be used [0: FSK, 1: LoRa] + * \param [IN] freq Channel RF frequency + * \param [IN] rssiThresh RSSI threshold + * \param [IN] maxCarrierSenseTime Max time while the RSSI is measured + * + * \retval isFree [true: Channel is free, false: Channel is not free] + */ +bool RadioIsChannelFree( RadioModems_t modem, uint32_t freq, int16_t rssiThresh, uint32_t maxCarrierSenseTime ); + +/*! + * \brief Generates a 32 bits random value based on the RSSI readings + * + * \remark This function sets the radio in LoRa modem mode and disables + * all interrupts. + * After calling this function either Radio.SetRxConfig or + * Radio.SetTxConfig functions must be called. + * + * \retval randomValue 32 bits random value + */ +uint32_t RadioRandom( void ); + +/*! + * \brief Sets the reception parameters + * + * \param [IN] modem Radio modem to be used [0: FSK, 1: LoRa] + * \param [IN] bandwidth Sets the bandwidth + * FSK : >= 2600 and <= 250000 Hz + * LoRa: [0: 125 kHz, 1: 250 kHz, + * 2: 500 kHz, 3: Reserved] + * \param [IN] datarate Sets the Datarate + * FSK : 600..300000 bits/s + * LoRa: [6: 64, 7: 128, 8: 256, 9: 512, + * 10: 1024, 11: 2048, 12: 4096 chips] + * \param [IN] coderate Sets the coding rate (LoRa only) + * FSK : N/A ( set to 0 ) + * LoRa: [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] + * \param [IN] bandwidthAfc Sets the AFC Bandwidth (FSK only) + * FSK : >= 2600 and <= 250000 Hz + * LoRa: N/A ( set to 0 ) + * \param [IN] preambleLen Sets the Preamble length + * FSK : Number of bytes + * LoRa: Length in symbols (the hardware adds 4 more symbols) + * \param [IN] symbTimeout Sets the RxSingle timeout value + * FSK : timeout in number of bytes + * LoRa: timeout in symbols + * \param [IN] fixLen Fixed length packets [0: variable, 1: fixed] + * \param [IN] payloadLen Sets payload length when fixed length is used + * \param [IN] crcOn Enables/Disables the CRC [0: OFF, 1: ON] + * \param [IN] FreqHopOn Enables disables the intra-packet frequency hopping + * FSK : N/A ( set to 0 ) + * LoRa: [0: OFF, 1: ON] + * \param [IN] HopPeriod Number of symbols between each hop + * FSK : N/A ( set to 0 ) + * LoRa: Number of symbols + * \param [IN] iqInverted Inverts IQ signals (LoRa only) + * FSK : N/A ( set to 0 ) + * LoRa: [0: not inverted, 1: inverted] + * \param [IN] rxContinuous Sets the reception in continuous mode + * [false: single mode, true: continuous mode] + */ +void RadioSetRxConfig( RadioModems_t modem, uint32_t bandwidth, + uint32_t datarate, uint8_t coderate, + uint32_t bandwidthAfc, uint16_t preambleLen, + uint16_t symbTimeout, bool fixLen, + uint8_t payloadLen, + bool crcOn, bool FreqHopOn, uint8_t HopPeriod, + bool iqInverted, bool rxContinuous ); + +/*! + * \brief Sets the transmission parameters + * + * \param [IN] modem Radio modem to be used [0: FSK, 1: LoRa] + * \param [IN] power Sets the output power [dBm] + * \param [IN] fdev Sets the frequency deviation (FSK only) + * FSK : [Hz] + * LoRa: 0 + * \param [IN] bandwidth Sets the bandwidth (LoRa only) + * FSK : 0 + * LoRa: [0: 125 kHz, 1: 250 kHz, + * 2: 500 kHz, 3: Reserved] + * \param [IN] datarate Sets the Datarate + * FSK : 600..300000 bits/s + * LoRa: [6: 64, 7: 128, 8: 256, 9: 512, + * 10: 1024, 11: 2048, 12: 4096 chips] + * \param [IN] coderate Sets the coding rate (LoRa only) + * FSK : N/A ( set to 0 ) + * LoRa: [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] + * \param [IN] preambleLen Sets the preamble length + * FSK : Number of bytes + * LoRa: Length in symbols (the hardware adds 4 more symbols) + * \param [IN] fixLen Fixed length packets [0: variable, 1: fixed] + * \param [IN] crcOn Enables disables the CRC [0: OFF, 1: ON] + * \param [IN] FreqHopOn Enables disables the intra-packet frequency hopping + * FSK : N/A ( set to 0 ) + * LoRa: [0: OFF, 1: ON] + * \param [IN] HopPeriod Number of symbols between each hop + * FSK : N/A ( set to 0 ) + * LoRa: Number of symbols + * \param [IN] iqInverted Inverts IQ signals (LoRa only) + * FSK : N/A ( set to 0 ) + * LoRa: [0: not inverted, 1: inverted] + * \param [IN] timeout Transmission timeout [ms] + */ +void RadioSetTxConfig( RadioModems_t modem, int8_t power, uint32_t fdev, + uint32_t bandwidth, uint32_t datarate, + uint8_t coderate, uint16_t preambleLen, + bool fixLen, bool crcOn, bool FreqHopOn, + uint8_t HopPeriod, bool iqInverted, uint32_t timeout ); + +/*! + * \brief Checks if the given RF frequency is supported by the hardware + * + * \param [IN] frequency RF frequency to be checked + * \retval isSupported [true: supported, false: unsupported] + */ +bool RadioCheckRfFrequency( uint32_t frequency ); + +/*! + * \brief Computes the packet time on air in ms for the given payload + * + * \Remark Can only be called once SetRxConfig or SetTxConfig have been called + * + * \param [IN] modem Radio modem to be used [0: FSK, 1: LoRa] + * \param [IN] pktLen Packet payload length + * + * \retval airTime Computed airTime (ms) for the given packet payload length + */ +uint32_t RadioTimeOnAir( RadioModems_t modem, uint8_t pktLen ); + +/*! + * \brief Sends the buffer of size. Prepares the packet to be sent and sets + * the radio in transmission + * + * \param [IN]: buffer Buffer pointer + * \param [IN]: size Buffer size + */ +void RadioSend( uint8_t *buffer, uint8_t size ); + +/*! + * \brief Sets the radio in sleep mode + */ +void RadioSleep( void ); + +/*! + * \brief Sets the radio in standby mode + */ +void RadioStandby( void ); + +/*! + * \brief Sets the radio in reception mode for the given time + * \param [IN] timeout Reception timeout [ms] + * [0: continuous, others timeout] + */ +void RadioRx( uint32_t timeout ); + +/*! + * \brief Start a Channel Activity Detection + */ +void RadioStartCad( void ); + +/*! + * \brief Sets the radio in continuous wave transmission mode + * + * \param [IN]: freq Channel RF frequency + * \param [IN]: power Sets the output power [dBm] + * \param [IN]: time Transmission mode timeout [s] + */ +void RadioSetTxContinuousWave( uint32_t freq, int8_t power, uint16_t time ); + +/*! + * \brief Reads the current RSSI value + * + * \retval rssiValue Current RSSI value in [dBm] + */ +int16_t RadioRssi( RadioModems_t modem ); + +/*! + * \brief Writes the radio register at the specified address + * + * \param [IN]: addr Register address + * \param [IN]: data New register value + */ +void RadioWrite( uint16_t addr, uint8_t data ); + +/*! + * \brief Reads the radio register at the specified address + * + * \param [IN]: addr Register address + * \retval data Register value + */ +uint8_t RadioRead( uint16_t addr ); + +/*! + * \brief Writes multiple radio registers starting at address + * + * \param [IN] addr First Radio register address + * \param [IN] buffer Buffer containing the new register's values + * \param [IN] size Number of registers to be written + */ +void RadioWriteBuffer( uint16_t addr, uint8_t *buffer, uint8_t size ); + +/*! + * \brief Reads multiple radio registers starting at address + * + * \param [IN] addr First Radio register address + * \param [OUT] buffer Buffer where to copy the registers data + * \param [IN] size Number of registers to be read + */ +void RadioReadBuffer( uint16_t addr, uint8_t *buffer, uint8_t size ); + +/*! + * \brief Sets the maximum payload length. + * + * \param [IN] modem Radio modem to be used [0: FSK, 1: LoRa] + * \param [IN] max Maximum payload length in bytes + */ +void RadioSetMaxPayloadLength( RadioModems_t modem, uint8_t max ); + +/*! + * \brief Sets the network to public or private. Updates the sync byte. + * + * \remark Applies to LoRa modem only + * + * \param [IN] enable if true, it enables a public network + */ +void RadioSetPublicNetwork( bool enable ); + +/*! + * \brief Gets the time required for the board plus radio to get out of sleep.[ms] + * + * \retval time Radio plus board wakeup time in ms. + */ +uint32_t RadioGetWakeUpTime( void ); + + +/*! + * \brief Sets the radio in reception mode with Max LNA gain for the given time + * \param [IN] timeout Reception timeout [ms] + * [0: continuous, others timeout] + */ +void RadioRxBoosted( uint32_t timeout ); + +/*! + * \brief Sets the Rx duty cycle management parameters + * + * \param [in] rxTime Structure describing reception timeout value + * \param [in] sleepTime Structure describing sleep timeout value + */ +void RadioSetRxDutyCycle( uint32_t rxTime, uint32_t sleepTime ); + +/*! + * \brief Set Tx + * \param timeout + * \retval none + */ +void RadioTx( uint32_t timeout ); + + + +/*! +* @brief D-BPSK to BPSK +* +* @param [in] inBuffer buffer with frame to encode +* @param [out] OutBuffer buffer with frame encoded +* @param [in] size size of the payload to encode +*/ +static void payload_integration( uint8_t *outBuffer, uint8_t *inBuffer, uint8_t size); + +/*! + * \brief Set Tx PRBS modulated wave + * \retval none + */ +void RadioTxPrbs( void ); + +/*! + * \brief Set Tx continuous wave + * \retval none + */ +void RadioTxCw( void ); + + /* + * Private global constants + */ + +/*! + * Radio driver structure initialization + */ +const struct Radio_s Radio = +{ + RadioInit, + RadioGetStatus, + RadioSetModem, + RadioSetChannel, + RadioIsChannelFree, + RadioRandom, + RadioSetRxConfig, + RadioSetTxConfig, + RadioCheckRfFrequency, + RadioTimeOnAir, + RadioSend, + RadioSleep, + RadioStandby, + RadioRx, + RadioStartCad, + RadioSetTxContinuousWave, + RadioRssi, + RadioWrite, + RadioRead, + RadioSetMaxPayloadLength, + RadioSetPublicNetwork, + RadioGetWakeUpTime, + RadioIrqProcess, + RadioRxBoosted, + RadioSetRxDutyCycle, + RadioTxPrbs, + RadioTxCw, +}; + + + + /*! + * FSK bandwidth definition + */ +typedef struct +{ + uint32_t bandwidth; + uint8_t RegValue; +}FskBandwidth_t; + +/*! + * Precomputed FSK bandwidth registers values + */ +const FskBandwidth_t FskBandwidths[] = +{ + { 4800 , 0x1F }, + { 5800 , 0x17 }, + { 7300 , 0x0F }, + { 9700 , 0x1E }, + { 11700 , 0x16 }, + { 14600 , 0x0E }, + { 19500 , 0x1D }, + { 23400 , 0x15 }, + { 29300 , 0x0D }, + { 39000 , 0x1C }, + { 46900 , 0x14 }, + { 58600 , 0x0C }, + { 78200 , 0x1B }, + { 93800 , 0x13 }, + { 117300, 0x0B }, + { 156200, 0x1A }, + { 187200, 0x12 }, + { 234300, 0x0A }, + { 312000, 0x19 }, + { 373600, 0x11 }, + { 467000, 0x09 }, + { 500000, 0x00 }, // Invalid Bandwidth +}; + +const RadioLoRaBandwidths_t Bandwidths[] = { LORA_BW_125, LORA_BW_250, LORA_BW_500 }; + +// SF12 SF11 SF10 SF9 SF8 SF7 +static const double RadioLoRaSymbTime[3][6] = {{ 32.768, 16.384, 8.192, 4.096, 2.048, 1.024 }, // 125 KHz + { 16.384, 8.192, 4.096, 2.048, 1.024, 0.512 }, // 250 KHz + { 8.192, 4.096, 2.048, 1.024, 0.512, 0.256 }}; // 500 KHz + +static uint8_t MaxPayloadLength = RADIO_RX_BUF_SIZE; + + /* RF switch Power variable */ +static uint8_t AntSwitchPower; + +static uint8_t RadioRxPayload[RADIO_RX_BUF_SIZE]; + +bool IrqFired = false; + +/* + * Private global variables + */ + + + + +/*! + * Radio callbacks variable + */ +static RadioEvents_t* RadioEvents; + +/* + * Public global variables + */ + +/*! + * Radio hardware and global parameters + */ +SubgRf_t SubgRf; + + +/*! + * Tx and Rx timers + */ +TimerEvent_t TxTimeoutTimer; +TimerEvent_t RxTimeoutTimer; + +/*! + * Returns the known FSK bandwidth registers value + * + * \param [IN] bandwidth Bandwidth value in Hz + * \retval regValue Bandwidth register value. + */ +static uint8_t RadioGetFskBandwidthRegValue( uint32_t bandwidth ) +{ + uint8_t i; + + if( bandwidth == 0 ) + { + return( 0x1F ); + } + + for( i = 0; i < ( sizeof( FskBandwidths ) / sizeof( FskBandwidth_t ) ); i++ ) + { + if ( bandwidth < FskBandwidths[i].bandwidth ) + { + return FskBandwidths[i].RegValue; + } + } + // ERROR: Value not found + while(1){} +} + +uint32_t RadioInit( RadioEvents_t *events ) +{ + RadioEvents = events; + + SubgRf.RxContinuous = false; + SubgRf.TxTimeout = 0; + SubgRf.RxTimeout = 0; + + if( SUBGRF_Init( RadioOnDioIrq ) != SUBGRF_OK) + { + /* Initialization Error */ + while(1){} + } + SUBGRF_SetStandby( STDBY_RC ); + /*SubgRf.publicNetwork set to false*/ + RadioSetPublicNetwork( false ); + + SUBGRF_SetRegulatorMode( ); + + SUBGRF_SetBufferBaseAddress( 0x00, 0x00 ); + + SUBGRF_SetTxParams(RFO_LP, 0, RADIO_RAMP_200_US); + + /* Radio IRQ is set to DIO1 by default */ + SUBGRF_SetDioIrqParams( IRQ_RADIO_ALL, IRQ_RADIO_ALL, IRQ_RADIO_NONE, IRQ_RADIO_NONE ); + + + // Initialize driver timeout timers + TimerInit( &TxTimeoutTimer, RadioOnTxTimeoutIrq ); + TimerInit( &RxTimeoutTimer, RadioOnRxTimeoutIrq ); + TimerStop( &TxTimeoutTimer ); + TimerStop( &RxTimeoutTimer ); + + IrqFired = false; + return ( uint32_t )RadioGetWakeUpTime( ); + +} + +RadioState_t RadioGetStatus( void ) +{ + switch( SUBGRF_GetOperatingMode( ) ) + { + case MODE_TX: + return RF_TX_RUNNING; + case MODE_RX: + return RF_RX_RUNNING; + case RF_CAD: + return RF_CAD; + default: + return RF_IDLE; + } +} + +void RadioSetModem( RadioModems_t modem ) +{ + SubgRf.modem=modem ; + + switch( modem ) + { + default: + case MODEM_FSK: + SUBGRF_SetPacketType( PACKET_TYPE_GFSK ); + // When switching to GFSK mode the LoRa SyncWord register value is reset + // Thus, we also reset the RadioPublicNetwork variable + break; + case MODEM_LORA: + SUBGRF_SetPacketType( PACKET_TYPE_LORA ); + // Public/Private network register is reset when switching modems + RadioSetPublicNetwork( SubgRf.publicNetwork ); + break; + case MODEM_SIGFOX_TX: + SUBGRF_SetPacketType( PACKET_TYPE_BPSK ); + break; + case MODEM_SIGFOX_RX: + SUBGRF_SetPacketType( PACKET_TYPE_GFSK ); + break; + } +} + +void RadioSetChannel( uint32_t freq ) +{ + SUBGRF_SetRfFrequency( freq ); +} + +bool RadioIsChannelFree( RadioModems_t modem, uint32_t freq, int16_t rssiThresh, uint32_t maxCarrierSenseTime ) +{ + bool status = true; + int16_t rssi = 0; + uint32_t carrierSenseTime = 0; + + RadioStandby( ); + + RadioSetModem( modem ); + + RadioSetChannel( freq ); + + RadioRx( 0 ); + + RADIO_DELAY_MS( RadioGetWakeUpTime( ) ); + + carrierSenseTime = TimerGetCurrentTime( ); + + // Perform carrier sense for maxCarrierSenseTime + while( TimerGetElapsedTime( carrierSenseTime ) < maxCarrierSenseTime ) + { + rssi = RadioRssi( modem ); + + if( rssi > rssiThresh ) + { + status = false; + break; + } + } + RadioStandby( ); + return status; +} + +uint32_t RadioRandom( void ) +{ + uint32_t rnd = SUBGRF_GetRandom(); + + return rnd; +} + +void RadioSetRxConfig( RadioModems_t modem, uint32_t bandwidth, + uint32_t datarate, uint8_t coderate, + uint32_t bandwidthAfc, uint16_t preambleLen, + uint16_t symbTimeout, bool fixLen, + uint8_t payloadLen, + bool crcOn, bool freqHopOn, uint8_t hopPeriod, + bool iqInverted, bool rxContinuous ) +{ + + uint8_t modReg; + SubgRf.RxContinuous = rxContinuous; + if( rxContinuous == true ) + { + symbTimeout = 0; + } + if( fixLen == true ) + { + MaxPayloadLength = payloadLen; + } + else + { + MaxPayloadLength = 0xFF; + } + + switch( modem ) + { + + case MODEM_SIGFOX_RX: + SUBGRF_SetStopRxTimerOnPreambleDetect( true ); + SubgRf.ModulationParams.PacketType = PACKET_TYPE_GFSK; + + SubgRf.ModulationParams.Params.Gfsk.BitRate = datarate; + SubgRf.ModulationParams.Params.Gfsk.ModulationShaping = MOD_SHAPING_G_BT_05; + SubgRf.ModulationParams.Params.Gfsk.Fdev = 800; + SubgRf.ModulationParams.Params.Gfsk.Bandwidth = RadioGetFskBandwidthRegValue( bandwidth ); + + SubgRf.PacketParams.PacketType = PACKET_TYPE_GFSK; + SubgRf.PacketParams.Params.Gfsk.PreambleLength = ( preambleLen << 3 ); // convert byte into bit + SubgRf.PacketParams.Params.Gfsk.PreambleMinDetect = RADIO_PREAMBLE_DETECTOR_OFF; + SubgRf.PacketParams.Params.Gfsk.SyncWordLength = 2 << 3; // convert byte into bit + SubgRf.PacketParams.Params.Gfsk.AddrComp = RADIO_ADDRESSCOMP_FILT_OFF; + SubgRf.PacketParams.Params.Gfsk.HeaderType = RADIO_PACKET_FIXED_LENGTH; + SubgRf.PacketParams.Params.Gfsk.PayloadLength = MaxPayloadLength; + SubgRf.PacketParams.Params.Gfsk.CrcLength = RADIO_CRC_OFF; + + SubgRf.PacketParams.Params.Gfsk.DcFree = RADIO_DC_FREE_OFF; + + // RadioStandby( ); + RadioSetModem( MODEM_SIGFOX_RX ); + SUBGRF_SetModulationParams( &SubgRf.ModulationParams ); + SUBGRF_SetPacketParams( &SubgRf.PacketParams ); + SUBGRF_SetSyncWord( ( uint8_t[] ){0xB2, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } ); + SUBGRF_SetWhiteningSeed( 0x01FF ); + + /*• NO gfo reset (better sensitivity). Reg 0x8b8, bit4 = 0*/ + modReg= RadioRead(0x8b8); + modReg&=RADIO_BIT_MASK(4); +// modReg =modReg & (1<<2); + RadioWrite(0x8b8, modReg); + /*3. Lower the threshold of cfo_reset */ + RadioWrite(0x8b9, 0x4 ); + + /*•Bigger rssi_len (stability AGC). Reg 0x89b, bits[2 :4] = 0x1 */ + modReg= RadioRead(0x89b); + modReg&=( RADIO_BIT_MASK(2) & RADIO_BIT_MASK(3) & RADIO_BIT_MASK(4) ); + RadioWrite(0x89b, (modReg| (0x1<<3) ) ); + + /*•Biger afc_pbl_len (better frequency correction). Reg 0x6d1, bits[3 :4] = 0x3 */ + modReg= RadioRead(0x6d1); + modReg&=( RADIO_BIT_MASK(3) & RADIO_BIT_MASK(4) ); + RadioWrite(0x6d1, (modReg| (0x3<<3) )); + + /*••Use of new bit synchronizer (to avoid CRC errors during PER for payloads with a small amount of transitions). Reg 0x6ac, bits[4 :6] = 0x5 */ + modReg= RadioRead(0x6ac); + modReg&=( RADIO_BIT_MASK(4) & RADIO_BIT_MASK(5) & RADIO_BIT_MASK(6) ); + RadioWrite(0x6ac, (modReg| (0x5<<4) )); + + SubgRf.RxTimeout = ( uint32_t )( symbTimeout * ( ( 1.0 / ( double )datarate ) * 8.0 ) * 1000 ); + break; + case MODEM_FSK: + SUBGRF_SetStopRxTimerOnPreambleDetect( false ); + SubgRf.ModulationParams.PacketType = PACKET_TYPE_GFSK; + + SubgRf.ModulationParams.Params.Gfsk.BitRate = datarate; + SubgRf.ModulationParams.Params.Gfsk.ModulationShaping = MOD_SHAPING_G_BT_1; + SubgRf.ModulationParams.Params.Gfsk.Bandwidth = RadioGetFskBandwidthRegValue( bandwidth ); + + SubgRf.PacketParams.PacketType = PACKET_TYPE_GFSK; + SubgRf.PacketParams.Params.Gfsk.PreambleLength = ( preambleLen << 3 ); // convert byte into bit + SubgRf.PacketParams.Params.Gfsk.PreambleMinDetect = RADIO_PREAMBLE_DETECTOR_08_BITS; + SubgRf.PacketParams.Params.Gfsk.SyncWordLength = 3 << 3; // convert byte into bit + SubgRf.PacketParams.Params.Gfsk.AddrComp = RADIO_ADDRESSCOMP_FILT_OFF; + SubgRf.PacketParams.Params.Gfsk.HeaderType = ( fixLen == true ) ? RADIO_PACKET_FIXED_LENGTH : RADIO_PACKET_VARIABLE_LENGTH; + SubgRf.PacketParams.Params.Gfsk.PayloadLength = MaxPayloadLength; + if( crcOn == true ) + { + SubgRf.PacketParams.Params.Gfsk.CrcLength = RADIO_CRC_2_BYTES_CCIT; + } + else + { + SubgRf.PacketParams.Params.Gfsk.CrcLength = RADIO_CRC_OFF; + } + SubgRf.PacketParams.Params.Gfsk.DcFree = RADIO_DC_FREEWHITENING; + + RadioStandby( ); + RadioSetModem( ( SubgRf.ModulationParams.PacketType == PACKET_TYPE_GFSK ) ? MODEM_FSK : MODEM_LORA ); + SUBGRF_SetModulationParams( &SubgRf.ModulationParams ); + SUBGRF_SetPacketParams( &SubgRf.PacketParams ); + SUBGRF_SetSyncWord( ( uint8_t[] ){ 0xC1, 0x94, 0xC1, 0x00, 0x00, 0x00, 0x00, 0x00 } ); + SUBGRF_SetWhiteningSeed( 0x01FF ); + + SubgRf.RxTimeout = ( uint32_t )( symbTimeout * ( ( 1.0 / ( double )datarate ) * 8.0 ) * 1000 ); + break; + + case MODEM_LORA: + SUBGRF_SetStopRxTimerOnPreambleDetect( false ); + SUBGRF_SetLoRaSymbNumTimeout( symbTimeout ); + SubgRf.ModulationParams.PacketType = PACKET_TYPE_LORA; + SubgRf.ModulationParams.Params.LoRa.SpreadingFactor = ( RadioLoRaSpreadingFactors_t )datarate; + SubgRf.ModulationParams.Params.LoRa.Bandwidth = Bandwidths[bandwidth]; + SubgRf.ModulationParams.Params.LoRa.CodingRate = ( RadioLoRaCodingRates_t )coderate; + + if( ( ( bandwidth == 0 ) && ( ( datarate == 11 ) || ( datarate == 12 ) ) ) || + ( ( bandwidth == 1 ) && ( datarate == 12 ) ) ) + { + SubgRf.ModulationParams.Params.LoRa.LowDatarateOptimize = 0x01; + } + else + { + SubgRf.ModulationParams.Params.LoRa.LowDatarateOptimize = 0x00; + } + + SubgRf.PacketParams.PacketType = PACKET_TYPE_LORA; + + if( ( SubgRf.ModulationParams.Params.LoRa.SpreadingFactor == LORA_SF5 ) || + ( SubgRf.ModulationParams.Params.LoRa.SpreadingFactor == LORA_SF6 ) ) + { + if( preambleLen < 12 ) + { + SubgRf.PacketParams.Params.LoRa.PreambleLength = 12; + } + else + { + SubgRf.PacketParams.Params.LoRa.PreambleLength = preambleLen; + } + } + else + { + SubgRf.PacketParams.Params.LoRa.PreambleLength = preambleLen; + } + + SubgRf.PacketParams.Params.LoRa.HeaderType = ( RadioLoRaPacketLengthsMode_t )fixLen; + + SubgRf.PacketParams.Params.LoRa.PayloadLength = MaxPayloadLength; + SubgRf.PacketParams.Params.LoRa.CrcMode = ( RadioLoRaCrcModes_t )crcOn; + SubgRf.PacketParams.Params.LoRa.InvertIQ = ( RadioLoRaIQModes_t )iqInverted; + + RadioSetModem( ( SubgRf.ModulationParams.PacketType == PACKET_TYPE_GFSK ) ? MODEM_FSK : MODEM_LORA ); + SUBGRF_SetModulationParams( &SubgRf.ModulationParams ); + SUBGRF_SetPacketParams( &SubgRf.PacketParams ); + + // WORKAROUND - Optimizing the Inverted IQ Operation, see DS_SX1261-2_V1.2 datasheet chapter 15.4 + if( SubgRf.PacketParams.Params.LoRa.InvertIQ == LORA_IQ_INVERTED ) + { + // RegIqPolaritySetup = @address 0x0736 + SUBGRF_WriteRegister( 0x0736, SUBGRF_ReadRegister( 0x0736 ) & ~( 1 << 2 ) ); + } + else + { + // RegIqPolaritySetup @address 0x0736 + SUBGRF_WriteRegister( 0x0736, SUBGRF_ReadRegister( 0x0736 ) | ( 1 << 2 ) ); + } + // WORKAROUND END + + // Timeout Max, Timeout handled directly in SetRx function + SubgRf.RxTimeout = 0xFFFF; + + break; + default: + break; + } + +} + +void RadioSetTxConfig( RadioModems_t modem, int8_t power, uint32_t fdev, + uint32_t bandwidth, uint32_t datarate, + uint8_t coderate, uint16_t preambleLen, + bool fixLen, bool crcOn, bool freqHopOn, + uint8_t hopPeriod, bool iqInverted, uint32_t timeout ) +{ + switch( modem ) + { + case MODEM_SIGFOX_TX: + + RadioSetModem(MODEM_SIGFOX_TX); + SubgRf.ModulationParams.PacketType = PACKET_TYPE_BPSK; + SubgRf.ModulationParams.Params.Bpsk.BitRate = datarate; + SubgRf.ModulationParams.Params.Bpsk.ModulationShaping = MOD_SHAPING_DBPSK; + SUBGRF_SetModulationParams( &SubgRf.ModulationParams ); + + //SubgRf.PacketParams.Params.Bpsk.PayloadLength = frameSize + 1; // for added start and stop bit + // SubgRf.PacketParams.Params.Bpsk. + //SUBGRF_SetPacketParams( &SubgRf.PacketParams ); + break; + case MODEM_FSK: + SubgRf.ModulationParams.PacketType = PACKET_TYPE_GFSK; + SubgRf.ModulationParams.Params.Gfsk.BitRate = datarate; + + SubgRf.ModulationParams.Params.Gfsk.ModulationShaping = MOD_SHAPING_G_BT_1; + SubgRf.ModulationParams.Params.Gfsk.Bandwidth = RadioGetFskBandwidthRegValue( bandwidth ); + SubgRf.ModulationParams.Params.Gfsk.Fdev = fdev; + + SubgRf.PacketParams.PacketType = PACKET_TYPE_GFSK; + SubgRf.PacketParams.Params.Gfsk.PreambleLength = ( preambleLen << 3 ); // convert byte into bit + SubgRf.PacketParams.Params.Gfsk.PreambleMinDetect = RADIO_PREAMBLE_DETECTOR_08_BITS; + SubgRf.PacketParams.Params.Gfsk.SyncWordLength = 3 << 3 ; // convert byte into bit + SubgRf.PacketParams.Params.Gfsk.AddrComp = RADIO_ADDRESSCOMP_FILT_OFF; + SubgRf.PacketParams.Params.Gfsk.HeaderType = ( fixLen == true ) ? RADIO_PACKET_FIXED_LENGTH : RADIO_PACKET_VARIABLE_LENGTH; + + if( crcOn == true ) + { + SubgRf.PacketParams.Params.Gfsk.CrcLength = RADIO_CRC_2_BYTES_CCIT; + } + else + { + SubgRf.PacketParams.Params.Gfsk.CrcLength = RADIO_CRC_OFF; + } + SubgRf.PacketParams.Params.Gfsk.DcFree = RADIO_DC_FREEWHITENING; + + RadioStandby( ); + RadioSetModem( ( SubgRf.ModulationParams.PacketType == PACKET_TYPE_GFSK ) ? MODEM_FSK : MODEM_LORA ); + SUBGRF_SetModulationParams( &SubgRf.ModulationParams ); + SUBGRF_SetPacketParams( &SubgRf.PacketParams ); + SUBGRF_SetSyncWord( ( uint8_t[] ){ 0xC1, 0x94, 0xC1, 0x00, 0x00, 0x00, 0x00, 0x00 } ); + SUBGRF_SetWhiteningSeed( 0x01FF ); + break; + + case MODEM_LORA: + SubgRf.ModulationParams.PacketType = PACKET_TYPE_LORA; + SubgRf.ModulationParams.Params.LoRa.SpreadingFactor = ( RadioLoRaSpreadingFactors_t ) datarate; + SubgRf.ModulationParams.Params.LoRa.Bandwidth = Bandwidths[bandwidth]; + SubgRf.ModulationParams.Params.LoRa.CodingRate= ( RadioLoRaCodingRates_t )coderate; + + if( ( ( bandwidth == 0 ) && ( ( datarate == 11 ) || ( datarate == 12 ) ) ) || + ( ( bandwidth == 1 ) && ( datarate == 12 ) ) ) + { + SubgRf.ModulationParams.Params.LoRa.LowDatarateOptimize = 0x01; + } + else + { + SubgRf.ModulationParams.Params.LoRa.LowDatarateOptimize = 0x00; + } + + SubgRf.PacketParams.PacketType = PACKET_TYPE_LORA; + + if( ( SubgRf.ModulationParams.Params.LoRa.SpreadingFactor == LORA_SF5 ) || + ( SubgRf.ModulationParams.Params.LoRa.SpreadingFactor == LORA_SF6 ) ) + { + if( preambleLen < 12 ) + { + SubgRf.PacketParams.Params.LoRa.PreambleLength = 12; + } + else + { + SubgRf.PacketParams.Params.LoRa.PreambleLength = preambleLen; + } + } + else + { + SubgRf.PacketParams.Params.LoRa.PreambleLength = preambleLen; + } + + SubgRf.PacketParams.Params.LoRa.HeaderType = ( RadioLoRaPacketLengthsMode_t )fixLen; + SubgRf.PacketParams.Params.LoRa.PayloadLength = MaxPayloadLength; + SubgRf.PacketParams.Params.LoRa.CrcMode = ( RadioLoRaCrcModes_t )crcOn; + SubgRf.PacketParams.Params.LoRa.InvertIQ = ( RadioLoRaIQModes_t )iqInverted; + + RadioStandby( ); + RadioSetModem( ( SubgRf.ModulationParams.PacketType == PACKET_TYPE_GFSK ) ? MODEM_FSK : MODEM_LORA ); + SUBGRF_SetModulationParams( &SubgRf.ModulationParams ); + SUBGRF_SetPacketParams( &SubgRf.PacketParams ); + break; + default: break; + } + + // WORKAROUND - Modulation Quality with 500 kHz LoRa® Bandwidth, see DS_SX1261-2_V1.2 datasheet chapter 15.1 + if( ( modem == MODEM_LORA ) && ( SubgRf.ModulationParams.Params.LoRa.Bandwidth == LORA_BW_500 ) ) + { + // RegTxModulation = @address 0x0889 + SUBGRF_WriteRegister( 0x0889, SUBGRF_ReadRegister( 0x0889 ) & ~( 1 << 2 ) ); + } + else + { + // RegTxModulation = @address 0x0889 + SUBGRF_WriteRegister( 0x0889, SUBGRF_ReadRegister( 0x0889 ) | ( 1 << 2 ) ); + } + // WORKAROUND END + + AntSwitchPower = SUBGRF_SetTxPower( power, RADIO_RAMP_40_US ); + SubgRf.TxTimeout = timeout; +} + +bool RadioCheckRfFrequency( uint32_t frequency ) +{ + return true; +} + +uint32_t RadioTimeOnAir( RadioModems_t modem, uint8_t pktLen ) +{ + uint32_t airTime = 0; + + switch( modem ) + { + case MODEM_FSK: + { + airTime = (uint32_t)rint( ( 8 * ( SubgRf.PacketParams.Params.Gfsk.PreambleLength + + ( SubgRf.PacketParams.Params.Gfsk.SyncWordLength >> 3 ) + + ( ( SubgRf.PacketParams.Params.Gfsk.HeaderType == RADIO_PACKET_FIXED_LENGTH ) ? 0.0 : 1.0 ) + + pktLen + + ( ( SubgRf.PacketParams.Params.Gfsk.CrcLength == RADIO_CRC_2_BYTES ) ? 2.0 : 0 ) ) / + SubgRf.ModulationParams.Params.Gfsk.BitRate ) * 1e3 ); + } + break; + case MODEM_LORA: + { + double ts = RadioLoRaSymbTime[SubgRf.ModulationParams.Params.LoRa.Bandwidth - 4][12 - SubgRf.ModulationParams.Params.LoRa.SpreadingFactor]; + // time of preamble + double tPreamble = ( SubgRf.PacketParams.Params.LoRa.PreambleLength + 4.25 ) * ts; + // Symbol length of payload and time + double tmp = ceil( ( 8 * pktLen - 4 * SubgRf.ModulationParams.Params.LoRa.SpreadingFactor + + 28 + 16 * SubgRf.PacketParams.Params.LoRa.CrcMode - + ( ( SubgRf.PacketParams.Params.LoRa.HeaderType == LORA_PACKET_FIXED_LENGTH ) ? 20 : 0 ) ) / + ( double )( 4 * ( SubgRf.ModulationParams.Params.LoRa.SpreadingFactor - + ( ( SubgRf.ModulationParams.Params.LoRa.LowDatarateOptimize > 0 ) ? 2 : 0 ) ) ) ) * + ( ( SubgRf.ModulationParams.Params.LoRa.CodingRate % 4 ) + 4 ); + double nPayload = 8 + ( ( tmp > 0 ) ? tmp : 0 ); + double tPayload = nPayload * ts; + // Time on air + double tOnAir = tPreamble + tPayload; + // return milli seconds + airTime = (uint32_t) floor( tOnAir + 0.999 ); + } + break; + default :break; + } + + return airTime; +} + + +void RadioSend( uint8_t *buffer, uint8_t size ) +{ + /* Radio IRQ is set to DIO1 by default */ + SUBGRF_SetDioIrqParams( IRQ_TX_DONE | IRQ_RX_TX_TIMEOUT, + IRQ_TX_DONE | IRQ_RX_TX_TIMEOUT, + IRQ_RADIO_NONE, + IRQ_RADIO_NONE ); + +//Set DBG pin PB13 +#ifdef LET_SUBGHZ_MW_USING_DGB_LINE2_PIN + DBG_GPIO_SET(DGB_LINE2_PORT, DGB_LINE2_PIN); +#endif + +/* Set RF switch */ + SUBGRF_SetSwitch(AntSwitchPower, RFSWITCH_TX); + + switch(SubgRf.modem) + { + case MODEM_LORA: + { + SubgRf.PacketParams.Params.LoRa.PayloadLength = size; + SUBGRF_SetPacketParams( &SubgRf.PacketParams ); + SUBGRF_SendPayload( buffer, size, 0 ); + break; + } + case MODEM_FSK: + { + SubgRf.PacketParams.Params.Gfsk.PayloadLength = size; + SUBGRF_SetPacketParams( &SubgRf.PacketParams ); + SUBGRF_SendPayload( buffer, size, 0 ); + break; + } + + case MODEM_BPSK: + SubgRf.PacketParams.PacketType=PACKET_TYPE_BPSK; + SubgRf.PacketParams.Params.Bpsk.PayloadLength = size; + SUBGRF_SetPacketParams( &SubgRf.PacketParams ); + + SUBGRF_SendPayload( buffer, size, 0 ); + break; + case MODEM_SIGFOX_TX: + { + uint8_t outBuffer[35] = {0}; + /*from bpsk to dbpsk*/ + /*first 1 bit duplicated*/ + payload_integration( outBuffer, buffer, size ); + + SubgRf.PacketParams.PacketType=PACKET_TYPE_BPSK; + SubgRf.PacketParams.Params.Bpsk.PayloadLength = size+1; + SUBGRF_SetPacketParams( &SubgRf.PacketParams ); + + + if( SubgRf.ModulationParams.Params.Bpsk.BitRate == 100 ) + { + RadioWrite( 0x00F1, 0x0F ); // clean start-up LSB + RadioWrite( 0x00F0, 0x37 ); // clean start-up MSB + RadioWrite( 0x00F3, 0x70 ); // clean end of frame LSB + RadioWrite( 0x00F2, 0x1D ); // clean end of frame MSB + } + else // 600 bps + { + RadioWrite( 0x00F1, 0x2F ); // clean start-up LSB + RadioWrite( 0x00F0, 0x09 ); // clean start-up MSB + RadioWrite( 0x00F3, 0xE1 ); // clean end of frame LSB + RadioWrite( 0x00F2, 0x04 ); // clean end of frame MSB + } + + uint16_t bitNum = (size*8)+2; + RadioWrite( 0x00F4, ( bitNum >> 8 ) & 0x00FF ); // limit frame + RadioWrite( 0x00F5, bitNum & 0x00FF ); // limit frame + // + SUBGRF_SendPayload( outBuffer, size+1 , 0xFFFFFF ); + break; + } + default: + break; + } + + + TimerSetValue( &TxTimeoutTimer, SubgRf.TxTimeout ); + TimerStart( &TxTimeoutTimer ); +} + +void RadioSleep( void ) +{ + SleepParams_t params = { 0 }; + + params.Fields.WarmStart = 1; + SUBGRF_SetSleep( params ); + + RADIO_DELAY_MS( 2 ); +} + +void RadioStandby( void ) +{ + SUBGRF_SetStandby( STDBY_RC ); +} + +void RadioRx( uint32_t timeout ) +{ + + /* Radio IRQ is set to DIO1 by default */ + SUBGRF_SetDioIrqParams( IRQ_RADIO_ALL, //IRQ_RX_DONE | IRQ_RX_TX_TIMEOUT, + IRQ_RADIO_ALL, //IRQ_RX_DONE | IRQ_RX_TX_TIMEOUT, + IRQ_RADIO_NONE, + IRQ_RADIO_NONE ); + + if( timeout != 0 ) + { + TimerSetValue( &RxTimeoutTimer, timeout ); + TimerStart( &RxTimeoutTimer ); + } + + // Set DBG pin PB12 +#ifdef LET_SUBGHZ_MW_USING_DGB_LINE1_PIN + DBG_GPIO_SET(DGB_LINE1_PORT, DGB_LINE1_PIN); +#endif + /* RF switch configuration */ + SUBGRF_SetSwitch(AntSwitchPower, RFSWITCH_RX ); + if( SubgRf.RxContinuous == true ) + { + SUBGRF_SetRx( 0xFFFFFF ); // Rx Continuous + } + else + { + SUBGRF_SetRx( SubgRf.RxTimeout << 6 ); + } +} + +void RadioRxBoosted( uint32_t timeout ) +{ + SUBGRF_SetDioIrqParams( IRQ_RADIO_ALL, //IRQ_RX_DONE | IRQ_RX_TX_TIMEOUT, + IRQ_RADIO_ALL, //IRQ_RX_DONE | IRQ_RX_TX_TIMEOUT, + IRQ_RADIO_NONE, + IRQ_RADIO_NONE ); + + if( timeout != 0 ) + { + TimerSetValue( &RxTimeoutTimer, timeout ); + TimerStart( &RxTimeoutTimer ); + } + + /* RF switch configuration */ + SUBGRF_SetSwitch(AntSwitchPower, RFSWITCH_RX); + if( SubgRf.RxContinuous == true ) + { + SUBGRF_SetRxBoosted( 0xFFFFFF ); // Rx Continuous + } + else + { + SUBGRF_SetRxBoosted( SubgRf.RxTimeout << 6 ); + } +} + +void RadioSetRxDutyCycle( uint32_t rxTime, uint32_t sleepTime ) +{ + /* RF switch configuration */ + SUBGRF_SetSwitch(AntSwitchPower, RFSWITCH_RX); + SUBGRF_SetRxDutyCycle( rxTime, sleepTime ); +} + +void RadioStartCad( void ) +{ + SUBGRF_SetCad( ); +} + +void RadioTx( uint32_t timeout ) +{ + SUBGRF_SetTx( timeout << 6 ); +} + +void RadioSetTxContinuousWave( uint32_t freq, int8_t power, uint16_t time ) +{ + uint8_t antswitchpow; + + SUBGRF_SetRfFrequency( freq ); + + antswitchpow = SUBGRF_SetTxPower( power, RADIO_RAMP_40_US ); + + /* Set RF switch */ + SUBGRF_SetSwitch(antswitchpow, RFSWITCH_TX); + + SUBGRF_SetTxContinuousWave( ); + + TimerSetValue( &TxTimeoutTimer, (uint32_t)(time * 1e3 )); + TimerStart( &TxTimeoutTimer ); +} + +int16_t RadioRssi( RadioModems_t modem ) +{ + return SUBGRF_GetRssiInst( ); +} + +void RadioWrite( uint16_t addr, uint8_t data ) +{ + SUBGRF_WriteRegister(addr, data ); +} + +uint8_t RadioRead( uint16_t addr ) +{ + return SUBGRF_ReadRegister(addr); +} + +void RadioWriteBuffer( uint16_t addr, uint8_t *buffer, uint8_t size ) +{ + SUBGRF_WriteRegisters( addr, buffer, size ); +} + +void RadioReadBuffer( uint16_t addr, uint8_t *buffer, uint8_t size ) +{ + SUBGRF_ReadRegisters( addr, buffer, size ); +} + +//void RadioWriteFifo( uint8_t *buffer, uint8_t size ) +//{ +// SUBGRF_WriteBuffer( 0, buffer, size ); +//} +// +//void RadioReadFifo( uint8_t *buffer, uint8_t size ) +//{ +// SUBGRF_ReadBuffer( 0, buffer, size ); +//} + +void RadioSetMaxPayloadLength( RadioModems_t modem, uint8_t max ) +{ + if( modem == MODEM_LORA ) + { + SubgRf.PacketParams.Params.LoRa.PayloadLength = MaxPayloadLength = max; + SUBGRF_SetPacketParams( &SubgRf.PacketParams ); + } + else + { + if( SubgRf.PacketParams.Params.Gfsk.HeaderType == RADIO_PACKET_VARIABLE_LENGTH ) + { + SubgRf.PacketParams.Params.Gfsk.PayloadLength = MaxPayloadLength = max; + SUBGRF_SetPacketParams( &SubgRf.PacketParams ); + } + } +} + +void RadioSetPublicNetwork( bool enable ) +{ + uint8_t reg; + + SubgRf.publicNetwork= enable; + + if( enable == true ) + { + // Change LoRa modem SyncWord + reg = ( LORA_MAC_PUBLIC_SYNCWORD >> 8 ) & 0xFF; + SUBGRF_WriteRegisters( REG_LR_SYNCWORD, (uint8_t*)®, 1 ); + reg = LORA_MAC_PUBLIC_SYNCWORD & 0xFF; + SUBGRF_WriteRegisters( REG_LR_SYNCWORD + 1, (uint8_t*)®, 1 ); + } + else + { + // Change LoRa modem SyncWord + reg = ( LORA_MAC_PRIVATE_SYNCWORD >> 8 ) & 0xFF; + SUBGRF_WriteRegisters( REG_LR_SYNCWORD, (uint8_t*)®, 1 ); + reg = LORA_MAC_PRIVATE_SYNCWORD & 0xFF; + SUBGRF_WriteRegisters( REG_LR_SYNCWORD + 1, (uint8_t*)®, 1 ); + } +} + +uint32_t RadioGetWakeUpTime( void ) +{ + return (SUBGRF_GetRadioWakeUpTime() + RADIO_WAKEUP_TIME); +} + + +void RadioOnTxTimeoutIrq( void* context ) +{ + // Reset DBG pin PB13 +#ifdef LET_SUBGHZ_MW_USING_DGB_LINE2_PIN + DBG_GPIO_RST(DGB_LINE2_PORT, DGB_LINE2_PIN); +#endif + if( ( RadioEvents != NULL ) && ( RadioEvents->TxTimeout != NULL ) ) + { + RadioEvents->TxTimeout( ); + } +} + +void RadioOnRxTimeoutIrq( void* context ) +{ + // Reset DBG pin PB12 +#ifdef LET_SUBGHZ_MW_USING_DGB_LINE1_PIN + DBG_GPIO_RST(DGB_LINE1_PORT, DGB_LINE1_PIN); +#endif + if( ( RadioEvents != NULL ) && ( RadioEvents->RxTimeout != NULL ) ) + { + RadioEvents->RxTimeout( ); + } +} + +void RadioOnDioIrq( RadioIrqMasks_t radioIrq ) +{ + SubgRf.radioIrq=radioIrq; + + RadioIrqProcess(); + +} + + +void RadioIrqProcess(void ) +{ + PacketStatus_t RadioPktStatus; + uint8_t size; + + switch (SubgRf.radioIrq) + { + case IRQ_TX_DONE: + // Reset DBG pin PB13 +#ifdef LET_SUBGHZ_MW_USING_DGB_LINE2_PIN + DBG_GPIO_RST(DGB_LINE2_PORT, DGB_LINE2_PIN); +#endif + TimerStop( &TxTimeoutTimer ); + SUBGRF_SetStandby( STDBY_RC ); + if( ( RadioEvents != NULL ) && ( RadioEvents->TxDone != NULL ) ) + { + RadioEvents->TxDone( ); + } + break; + + case IRQ_RX_DONE: + // Reset DBG pin PB12 +#ifdef LET_SUBGHZ_MW_USING_DGB_LINE1_PIN + DBG_GPIO_RST(DGB_LINE1_PORT, DGB_LINE1_PIN); +#endif + TimerStop( &RxTimeoutTimer ); + if( SubgRf.RxContinuous == false ) + { + //!< Update operating mode state to a value lower than \ref MODE_STDBY_XOSC + SUBGRF_SetStandby( STDBY_RC ); + + // WORKAROUND - Implicit Header Mode Timeout Behavior, see DS_SX1261-2_V1.2 datasheet chapter 15.3 + // RegRtcControl = @address 0x0902 + SUBGRF_WriteRegister( 0x0902, 0x00 ); + // RegEventMask = @address 0x0944 + SUBGRF_WriteRegister( 0x0944, SUBGRF_ReadRegister( 0x0944 ) | ( 1 << 1 ) ); + // WORKAROUND END + } + SUBGRF_GetPayload( RadioRxPayload, &size , 255 ); + SUBGRF_GetPacketStatus( &RadioPktStatus ); + if( ( RadioEvents != NULL ) && ( RadioEvents->RxDone != NULL ) ) + { + switch (RadioPktStatus.packetType) + { + case PACKET_TYPE_LORA: + RadioEvents->RxDone( RadioRxPayload, size, RadioPktStatus.Params.LoRa.RssiPkt, RadioPktStatus.Params.LoRa.SnrPkt ); + break; + default: + RadioEvents->RxDone( RadioRxPayload, size, RadioPktStatus.Params.Gfsk.RssiAvg, (int8_t)(RadioPktStatus.Params.Gfsk.FreqError) ); + break; + } + } + break; + + case IRQ_CRC_ERROR: + + if( SubgRf.RxContinuous == false ) + { + //!< Update operating mode state to a value lower than \ref MODE_STDBY_XOSC + SUBGRF_SetStandby( STDBY_RC ); + } + if( ( RadioEvents != NULL ) && ( RadioEvents->RxError ) ) + { + RadioEvents->RxError( ); + } + break; + + + case IRQ_CAD_DONE: + //!< Update operating mode state to a value lower than \ref MODE_STDBY_XOSC + SUBGRF_SetStandby( STDBY_RC ); + if( ( RadioEvents != NULL ) && ( RadioEvents->CadDone != NULL ) ) + { + RadioEvents->CadDone( ( ( SubgRf.radioIrq & IRQ_CAD_ACTIVITY_DETECTED ) == IRQ_CAD_ACTIVITY_DETECTED ) ); + } + break; + + case IRQ_RX_TX_TIMEOUT: + if( SUBGRF_GetOperatingMode( ) == MODE_TX ) + { + // Reset DBG pin PB13 +#ifdef LET_SUBGHZ_MW_USING_DGB_LINE2_PIN + DBG_GPIO_RST(DGB_LINE2_PORT, DGB_LINE2_PIN); +#endif + TimerStop( &TxTimeoutTimer ); + //!< Update operating mode state to a value lower than \ref MODE_STDBY_XOSC + SUBGRF_SetStandby( STDBY_RC ); + if( ( RadioEvents != NULL ) && ( RadioEvents->TxTimeout != NULL ) ) + { + RadioEvents->TxTimeout( ); + } + } + else if( SUBGRF_GetOperatingMode( ) == MODE_RX ) + { + // Reset DBG pin PB12 +#ifdef LET_SUBGHZ_MW_USING_DGB_LINE1_PIN + DBG_GPIO_RST(DGB_LINE1_PORT, DGB_LINE1_PIN); +#endif + TimerStop( &RxTimeoutTimer ); + //!< Update operating mode state to a value lower than \ref MODE_STDBY_XOSC + SUBGRF_SetStandby( STDBY_RC ); + if( ( RadioEvents != NULL ) && ( RadioEvents->RxTimeout != NULL ) ) + { + RadioEvents->RxTimeout( ); + } + } + break; + + case IRQ_PREAMBLE_DETECTED: + MW_LOG( "PRE OK\n\r" ); + break; + + case IRQ_SYNCWORD_VALID: + + MW_LOG( "SYNC OK\n\r" ); + break; + + case IRQ_HEADER_VALID: + + MW_LOG( "HDR OK\n\r" ); + break; + + case IRQ_HEADER_ERROR: + TimerStop( &RxTimeoutTimer ); + if( SubgRf.RxContinuous == false ) + { + //!< Update operating mode state to a value lower than \ref MODE_STDBY_XOSC + SUBGRF_SetStandby( STDBY_RC ); + } + if( ( RadioEvents != NULL ) && ( RadioEvents->RxTimeout != NULL ) ) + { + RadioEvents->RxTimeout( ); + MW_LOG( "HDR KO\n\r" ); + } + break; + + default: + break; + + } +} + +void RadioTxPrbs( void ) +{ + SUBGRF_SetSwitch(AntSwitchPower, RFSWITCH_TX); + Radio.Write(0x6B8, 0x2d); // sel mode prbs9 instead of preamble + SUBGRF_SetTxInfinitePreamble(); + SUBGRF_SetTx(0x0fffff); +} + +void RadioTxCw( void ) +{ + SUBGRF_SetSwitch(AntSwitchPower, RFSWITCH_TX); + + SUBGRF_SetTxContinuousWave(); +} + +/*! +* @brief D-BPSK to BPSK +* +* @param [in] inBuffer buffer with frame to encode +* @param [out] outBuffer buffer with frame encoded +* @param [in] size size of the payload to encode +*/ +static void payload_integration( uint8_t *outBuffer, uint8_t *inBuffer, uint8_t size) +{ + uint8_t prevInt=0; + uint8_t currBit; + uint8_t index_bit; + uint8_t index_byte; + uint8_t index_bit_out; + uint8_t index_byte_out; + int i=0; + + for (i=0; i> index_bit) & 0x01; + /*integration*/ + prevInt ^= currBit; + /* write result integration in output*/ + outBuffer[index_byte_out]|= (prevInt << index_bit_out); + } + + outBuffer[size] =(prevInt<<7) | (prevInt<<6) | (( (!prevInt) & 0x01)<<5) ; +} + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/stm32_radio_driver/radio_conf_template.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/stm32_radio_driver/radio_conf_template.h new file mode 100644 index 00000000..12ca6737 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/stm32_radio_driver/radio_conf_template.h @@ -0,0 +1,51 @@ + /******************************************************************************* + * @file radio_conf.h + * @author MCD Application Team + * @brief driver sx1276 board (sx1276mb1mas for EU and sx1276mb1las for US) + ****************************************************************************** + * @attention + * + *

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

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __RADIO_CONF_H__ +#define __RADIO_CONF_H__ + +#ifdef __cplusplus + extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "platform.h" + +/* Exported types ------------------------------------------------------------*/ +/* Defines ------------------------*/ +#define RADIO_RX_BUF_SIZE 255 + +/* Function mapping */ +#define RADIO_DELAY_MS HAL_Delay +#define RADIO_MEMSET8( dest, value, size ) UTIL_MEM_cpy_8( dest, value, size) + +#define LET_SUBGHZ_MW_USING_DGB_LINE1_PIN +#define LET_SUBGHZ_MW_USING_DGB_LINE2_PIN + +/* Exported constants --------------------------------------------------------*/ +/* External variables --------------------------------------------------------*/ +/* Exported macros -----------------------------------------------------------*/ +/* Exported functions ------------------------------------------------------- */ + +#ifdef __cplusplus +} +#endif + +#endif /* __RADIO_CONF_H__*/ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/stm32_radio_driver/radio_driver.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/stm32_radio_driver/radio_driver.c new file mode 100644 index 00000000..95e4bc1c --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/stm32_radio_driver/radio_driver.c @@ -0,0 +1,1168 @@ +/* + ______ _ + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2017 Semtech + +Description: Generic SX126x driver implementation + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Authors: Miguel Luis, Gregory Cristian +*/ +/** + ****************************************************************************** + * + * Portions COPYRIGHT 2019 STMicroelectronics + * + * @file radio_driver.c + * @author MCD Application Team + * @brief radio driver implementation + ****************************************************************************** + */ + +/* Includes ------------------------------------------------------------------*/ +#include "radio_conf.h" +#include "mw_log_conf.h" +#include "stm32_lpm.h" + + + +/* + radio io function +*/ +/*============================================================================*/ +/** @addtogroup STM32WLXX_NUCLEO_RADIO_LOW_LEVEL_Exported_Functions + * @{ + */ + +/** + * @brief Init Radio Switch + * @retval BSP status + */ +int32_t BSP_RADIO_Init(void) +{ + GPIO_InitTypeDef gpio_init_structure = {0}; + + /* Enable the Radio Switch Clock */ + RF_SW_CTRL3_GPIO_CLK_ENABLE(); + + /* Configure the Radio Switch pin */ + gpio_init_structure.Pin = RF_SW_CTRL1_PIN; + gpio_init_structure.Mode = GPIO_MODE_OUTPUT_PP; + gpio_init_structure.Pull = GPIO_NOPULL; + gpio_init_structure.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + + HAL_GPIO_Init(RF_SW_CTRL1_GPIO_PORT, &gpio_init_structure); + + gpio_init_structure.Pin = RF_SW_CTRL2_PIN; + HAL_GPIO_Init(RF_SW_CTRL2_GPIO_PORT, &gpio_init_structure); + + gpio_init_structure.Pin = RF_SW_CTRL3_PIN; + HAL_GPIO_Init(RF_SW_CTRL3_GPIO_PORT, &gpio_init_structure); + + HAL_GPIO_WritePin(RF_SW_CTRL2_GPIO_PORT, RF_SW_CTRL2_PIN, GPIO_PIN_RESET); + HAL_GPIO_WritePin(RF_SW_CTRL1_GPIO_PORT, RF_SW_CTRL1_PIN, GPIO_PIN_RESET); + HAL_GPIO_WritePin(RF_SW_CTRL3_GPIO_PORT, RF_SW_CTRL3_PIN, GPIO_PIN_RESET); + + return BSP_ERROR_NONE; +} + +/** + * @brief DeInit Radio Swicth + * @retval BSP status + */ +int32_t BSP_RADIO_DeInit(void) +{ + RF_SW_CTRL3_GPIO_CLK_ENABLE(); + + /* Turn off switch */ + HAL_GPIO_WritePin(RF_SW_CTRL1_GPIO_PORT, RF_SW_CTRL1_PIN, GPIO_PIN_RESET); + HAL_GPIO_WritePin(RF_SW_CTRL2_GPIO_PORT, RF_SW_CTRL2_PIN, GPIO_PIN_RESET); + HAL_GPIO_WritePin(RF_SW_CTRL3_GPIO_PORT, RF_SW_CTRL3_PIN, GPIO_PIN_RESET); + + /* DeInit the Radio Switch pin */ + HAL_GPIO_DeInit(RF_SW_CTRL1_GPIO_PORT, RF_SW_CTRL1_PIN); + HAL_GPIO_DeInit(RF_SW_CTRL2_GPIO_PORT, RF_SW_CTRL2_PIN); + HAL_GPIO_DeInit(RF_SW_CTRL3_GPIO_PORT, RF_SW_CTRL3_PIN); + + return BSP_ERROR_NONE; +} + +/** + * @brief Configure Radio Switch. + * @param Config: Specifies the Radio RF switch path to be set. + * This parameter can be one of following parameters: + * @arg RADIO_SWITCH_OFF + * @arg RADIO_SWITCH_RX + * @arg RADIO_SWITCH_RFO_LP + * @arg RADIO_SWITCH_RFO_HP + * @retval BSP status + */ +int32_t BSP_RADIO_ConfigRFSwitch(BSP_RADIO_Switch_TypeDef Config) +{ + switch (Config) + { + case RADIO_SWITCH_OFF: + { + /* Turn off switch */ + HAL_GPIO_WritePin(RF_SW_CTRL3_GPIO_PORT, RF_SW_CTRL3_PIN, GPIO_PIN_RESET); + HAL_GPIO_WritePin(RF_SW_CTRL1_GPIO_PORT, RF_SW_CTRL1_PIN, GPIO_PIN_RESET); + HAL_GPIO_WritePin(RF_SW_CTRL2_GPIO_PORT, RF_SW_CTRL2_PIN, GPIO_PIN_RESET); + break; + } + case RADIO_SWITCH_RX: + { + /*Turns On in Rx Mode the RF Swicth */ + HAL_GPIO_WritePin(RF_SW_CTRL3_GPIO_PORT, RF_SW_CTRL3_PIN, GPIO_PIN_SET); + HAL_GPIO_WritePin(RF_SW_CTRL1_GPIO_PORT, RF_SW_CTRL1_PIN, GPIO_PIN_SET); + HAL_GPIO_WritePin(RF_SW_CTRL2_GPIO_PORT, RF_SW_CTRL2_PIN, GPIO_PIN_RESET); + break; + } + case RADIO_SWITCH_RFO_LP: + { + /*Turns On in Tx Low Power the RF Swicth */ + HAL_GPIO_WritePin(RF_SW_CTRL3_GPIO_PORT, RF_SW_CTRL3_PIN, GPIO_PIN_SET); + HAL_GPIO_WritePin(RF_SW_CTRL1_GPIO_PORT, RF_SW_CTRL1_PIN, GPIO_PIN_SET); + HAL_GPIO_WritePin(RF_SW_CTRL2_GPIO_PORT, RF_SW_CTRL2_PIN, GPIO_PIN_SET); + break; + } + case RADIO_SWITCH_RFO_HP: + { + /*Turns On in Tx High Power the RF Swicth */ + HAL_GPIO_WritePin(RF_SW_CTRL3_GPIO_PORT, RF_SW_CTRL3_PIN, GPIO_PIN_SET); + HAL_GPIO_WritePin(RF_SW_CTRL1_GPIO_PORT, RF_SW_CTRL1_PIN, GPIO_PIN_RESET); + HAL_GPIO_WritePin(RF_SW_CTRL2_GPIO_PORT, RF_SW_CTRL2_PIN, GPIO_PIN_SET); + break; + } + default: + break; + } + + return BSP_ERROR_NONE; +} + +/** + * @brief Return Board Configuration + * @retval + * RADIO_CONF_RFO_LP_HP + * RADIO_CONF_RFO_LP + * RADIO_CONF_RFO_HP + */ +int32_t BSP_RADIO_GetTxConfig(void) +{ + return RADIO_CONF_RFO_LP_HP; +} + +/** + * @brief Get Radio Wake Time + * @retval the wake upt time in ms + */ +int32_t BSP_RADIO_GetWakeUpTime(void) +{ + return RF_WAKEUP_TIME; +} + +/** + * @brief Get If TCXO is to be present on board + * @note never remove called by MW, + * @retval return 1 if present, 0 if not present + */ +int32_t BSP_RADIO_IsTCXO(void) +{ + return IS_TCXO_SUPPORTED; +} + +/** + * @brief Get If DCDC is to be present on board + * @note never remove called by MW, + * @retval return 1 if present, 0 if not present + */ +int32_t BSP_RADIO_IsDCDC(void) +{ + return IS_DCDC_SUPPORTED; +} + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ +/*=============================================================================*/ +/* + * Local types definition + */ +/*! + * @brief Radio registers definition + */ +typedef struct +{ + uint16_t Addr; //!< The address of the register + uint8_t Value; //!< The value of the register +}RadioRegisters_t; + +typedef enum +{ + RF_API_STATE_TCXO_OFF=0, + RF_API_STATE_TCXO_ON +} RF_API_Tcxo_State_t; +/*! +* \This is the workaround for tcxo bug in cut 1.0 +*/ +static void TCXO_Workaround( RF_API_Tcxo_State_t state ); + +/*! +* \This is the workaround for Reset bug in cut 1.0 +*/ +static void RadioReset_Workaround( void ); + +/*! + * @brief Stores the current packet type set in the radio + */ +static RadioPacketTypes_t PacketType; + +/*! + * @brief Stores the last frequency error measured on LoRa received packet + */ +volatile uint32_t FrequencyError = 0; + +/*! + * @brief Hold the status of the Image calibration + */ +static bool ImageCalibrated = false; + +/* + * @brief SUBGHZ_HandleTypeDef structure that contains + * the configuration information for SPI module + */ +SUBGHZ_HandleTypeDef hsubghz; +/* + * Private functions prototypes + */ + +/*! + * @brief Holds the internal operating mode of the radio + */ +RadioOperatingModes_t OperatingMode; + +void (*RadioOnDioIrqCb) ( RadioIrqMasks_t radioIrq ); + +/* + * Public functions + */ + +uint32_t SUBGRF_GetRadioWakeUpTime( void ) +{ + return ( uint32_t ) BSP_RADIO_GetWakeUpTime(); +} + +RadioOperatingModes_t SUBGRF_GetOperatingMode( void ) +{ + return OperatingMode; +} + +int32_t SUBGRF_Init( void (*RadioOnDioIrq) ( RadioIrqMasks_t radioIrq ) ) +{ + if ( RadioOnDioIrq!=NULL) + { + RadioOnDioIrqCb = RadioOnDioIrq; + } + + TCXO_Workaround(RF_API_STATE_TCXO_ON); + + hsubghz.Init.BaudratePrescaler=SUBGHZSPI_BAUDRATEPRESCALER_4; + + HAL_SUBGHZ_Init(&hsubghz); + + ImageCalibrated = false; + + RadioReset_Workaround(); + + SUBGRF_SetStandby( STDBY_RC ); + + if (1U ==BSP_RADIO_IsTCXO() ) + { + uint8_t reg = 0x2F; + + SUBGRF_SetTcxoMode( TCXO_CTRL_1_8V, 64 * BSP_RADIO_GetWakeUpTime() );// 100 ms + + HAL_SUBGHZ_WriteRegisters( &hsubghz, REG_XTA_TRIM, (uint8_t*)® , 1); + + /*enable calibration for cut1.1 and later*/ + if (LL_DBGMCU_GetRevisionID() !=0x1000) + { + CalibrationParams_t calibParam; + calibParam.Value = 0x7F; + SUBGRF_Calibrate( calibParam ); + } + } + /* Init RF Switch */ + BSP_RADIO_Init(); + + OperatingMode = MODE_STDBY_RC; + + TCXO_Workaround(RF_API_STATE_TCXO_OFF); + + return SUBGRF_OK; +} + +void SUBGRF_SetPayload( uint8_t *payload, uint8_t size ) +{ + HAL_SUBGHZ_WriteBuffer( &hsubghz, 0x00, payload, size ); +} + +uint8_t SUBGRF_GetPayload( uint8_t *buffer, uint8_t *size, uint8_t maxSize ) +{ + uint8_t offset = 0; + + SUBGRF_GetRxBufferStatus( size, &offset ); + if( *size > maxSize ) + { + return 1; + } + HAL_SUBGHZ_ReadBuffer( &hsubghz, offset, buffer, *size ); + return 0; +} + +void SUBGRF_SendPayload( uint8_t *payload, uint8_t size, uint32_t timeout) +{ + SUBGRF_SetPayload( payload, size ); + SUBGRF_SetTx( timeout ); +} + +void SUBGRF_SetSwitch (uint8_t power, RFState_t rxtx) +{ + BSP_RADIO_Switch_TypeDef state; + + if (rxtx == RFSWITCH_TX) + { + if (power == RFO_LP) + { + state = RADIO_SWITCH_RFO_LP; + } + if (power == RFO_HP) + { + state = RADIO_SWITCH_RFO_HP; + } + } + else + { + if (rxtx == RFSWITCH_RX) + { + state = RADIO_SWITCH_RX; + } + } + BSP_RADIO_ConfigRFSwitch(state); +} + +uint8_t SUBGRF_SetSyncWord( uint8_t *syncWord ) +{ + HAL_SUBGHZ_WriteRegisters( &hsubghz,REG_LR_SYNCWORDBASEADDRESS, syncWord, 8 ); + return 0; +} + +void SUBGRF_SetCrcSeed( uint16_t seed ) +{ + uint8_t buf[2]; + + buf[0] = ( uint8_t )( ( seed >> 8 ) & 0xFF ); + buf[1] = ( uint8_t )( seed & 0xFF ); + + switch( SUBGRF_GetPacketType( ) ) + { + case PACKET_TYPE_GFSK: + HAL_SUBGHZ_WriteRegisters( &hsubghz, REG_LR_CRCSEEDBASEADDR, buf, 2 ); + break; + default: + break; + } +} + +void SUBGRF_SetCrcPolynomial( uint16_t polynomial ) +{ + uint8_t buf[2]; + + buf[0] = ( uint8_t )( ( polynomial >> 8 ) & 0xFF ); + buf[1] = ( uint8_t )( polynomial & 0xFF ); + + switch( SUBGRF_GetPacketType( ) ) + { + case PACKET_TYPE_GFSK: + HAL_SUBGHZ_WriteRegisters( &hsubghz, REG_LR_CRCPOLYBASEADDR, buf, 2 ); + break; + + default: + break; + } +} + +void SUBGRF_SetWhiteningSeed( uint16_t seed ) +{ + uint8_t regValue = 0; + + switch( SUBGRF_GetPacketType( ) ) + { + case PACKET_TYPE_GFSK: + HAL_SUBGHZ_ReadRegisters( &hsubghz, REG_LR_WHITSEEDBASEADDR_MSB, ®Value, 1 ); + regValue = regValue & 0xFE; + regValue = ( ( seed >> 8 ) & 0x01 ) | regValue; + HAL_SUBGHZ_WriteRegisters( &hsubghz, REG_LR_WHITSEEDBASEADDR_MSB, (uint8_t*)®Value, 1 ); // only 1 bit. + HAL_SUBGHZ_WriteRegisters( &hsubghz, REG_LR_WHITSEEDBASEADDR_LSB, (uint8_t*)&seed, 1); + break; + + default: + break; + } +} + +uint32_t SUBGRF_GetRandom( void ) +{ + uint8_t buf[] = { 0, 0, 0, 0 }; + + // Set radio in continuous reception + SUBGRF_SetRfFrequency( 400e6 ); + + SUBGRF_SetRx( 0 ); + + HAL_Delay( 1 ); + + HAL_SUBGHZ_ReadRegisters( &hsubghz, RANDOM_NUMBER_GENERATORBASEADDR, buf, 4 ); + + SUBGRF_SetStandby( STDBY_RC ); + + return ( buf[0] << 24 ) | ( buf[1] << 16 ) | ( buf[2] << 8 ) | buf[3]; +} + +void SUBGRF_SetSleep( SleepParams_t sleepConfig ) +{ + BSP_RADIO_ConfigRFSwitch(RADIO_SWITCH_OFF); /* switch the antenna OFF by SW */ + + HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_SLEEP, &sleepConfig.Value, 1 ); + OperatingMode = MODE_SLEEP; + + TCXO_Workaround(RF_API_STATE_TCXO_OFF); +} + +void SUBGRF_SetStandby( RadioStandbyModes_t standbyConfig ) +{ + HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_STANDBY, ( uint8_t* )&standbyConfig, 1 ); + if( standbyConfig == STDBY_RC ) + { + OperatingMode = MODE_STDBY_RC; + } + else + { + OperatingMode = MODE_STDBY_XOSC; + } +} + +void SUBGRF_SetFs( void ) +{ + HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_FS, 0, 0 ); + OperatingMode = MODE_FS; +} + +void SUBGRF_SetTx( uint32_t timeout ) +{ + uint8_t buf[3]; + + OperatingMode = MODE_TX; + + buf[0] = ( uint8_t )( ( timeout >> 16 ) & 0xFF ); + buf[1] = ( uint8_t )( ( timeout >> 8 ) & 0xFF ); + buf[2] = ( uint8_t )( timeout & 0xFF ); + HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_TX, buf, 3 ); +} + +void SUBGRF_SetRx( uint32_t timeout ) +{ + uint8_t buf[3]; + + OperatingMode = MODE_RX; + + buf[0] = ( uint8_t )( ( timeout >> 16 ) & 0xFF ); + buf[1] = ( uint8_t )( ( timeout >> 8 ) & 0xFF ); + buf[2] = ( uint8_t )( timeout & 0xFF ); + HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_RX, buf, 3 ); + +} + +void SUBGRF_SetRxBoosted( uint32_t timeout ) +{ + uint8_t buf[3]; + uint8_t reg = 0x97; + OperatingMode = MODE_RX; + + HAL_SUBGHZ_WriteRegisters( &hsubghz, REG_RX_GAIN, (uint8_t*)®, 1 ); // max LNA gain, increase current by ~2mA for around ~3dB in sensivity + + buf[0] = ( uint8_t )( ( timeout >> 16 ) & 0xFF ); + buf[1] = ( uint8_t )( ( timeout >> 8 ) & 0xFF ); + buf[2] = ( uint8_t )( timeout & 0xFF ); + HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_RX, buf, 3 ); +} + +void SUBGRF_SetRxDutyCycle( uint32_t rxTime, uint32_t sleepTime ) +{ + uint8_t buf[6]; + + buf[0] = ( uint8_t )( ( rxTime >> 16 ) & 0xFF ); + buf[1] = ( uint8_t )( ( rxTime >> 8 ) & 0xFF ); + buf[2] = ( uint8_t )( rxTime & 0xFF ); + buf[3] = ( uint8_t )( ( sleepTime >> 16 ) & 0xFF ); + buf[4] = ( uint8_t )( ( sleepTime >> 8 ) & 0xFF ); + buf[5] = ( uint8_t )( sleepTime & 0xFF ); + HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_RXDUTYCYCLE, buf, 6 ); + OperatingMode = MODE_RX_DC; +} + +void SUBGRF_SetCad( void ) +{ + HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_CAD, 0, 0 ); + + OperatingMode = MODE_CAD; +} + +void SUBGRF_SetTxContinuousWave( void ) +{ + HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_TXCONTINUOUSWAVE, 0, 0 ); +} + +void SUBGRF_SetTxInfinitePreamble( void ) +{ + HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_TXCONTINUOUSPREAMBLE, 0, 0 ); +} + +void SUBGRF_SetStopRxTimerOnPreambleDetect( bool enable ) +{ + HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_STOPRXTIMERONPREAMBLE, ( uint8_t* )&enable, 1 ); +} + +void SUBGRF_SetLoRaSymbNumTimeout( uint8_t SymbNum ) +{ + HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_LORASYMBTIMEOUT, &SymbNum, 1 ); +} + +void SUBGRF_SetRegulatorMode( void ) +{ + RadioRegulatorMode_t mode; + + if (1U == BSP_RADIO_IsDCDC() ) + { + mode=USE_DCDC ; + } + else + { + mode=USE_LDO ; + } + + HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_REGULATORMODE, ( uint8_t* )&mode, 1 ); +} + +void SUBGRF_Calibrate( CalibrationParams_t calibParam ) +{ + HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_CALIBRATE, ( uint8_t* )&calibParam, 1 ); +} + +void SUBGRF_CalibrateImage( uint32_t freq ) +{ + uint8_t calFreq[2]; + + if( freq > 900000000 ) + { + calFreq[0] = 0xE1; + calFreq[1] = 0xE9; + } + else if( freq > 850000000 ) + { + calFreq[0] = 0xD7; + calFreq[1] = 0xD8; + } + else if( freq > 770000000 ) + { + calFreq[0] = 0xC1; + calFreq[1] = 0xC5; + } + else if( freq > 460000000 ) + { + calFreq[0] = 0x75; + calFreq[1] = 0x81; + } + else if( freq > 425000000 ) + { + calFreq[0] = 0x6B; + calFreq[1] = 0x6F; + } + HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_CALIBRATEIMAGE, calFreq, 2 ); +} + +void SUBGRF_SetPaConfig( uint8_t paDutyCycle, uint8_t hpMax, uint8_t deviceSel, uint8_t paLut ) +{ + uint8_t buf[4]; + + buf[0] = paDutyCycle; + buf[1] = hpMax; + buf[2] = deviceSel; + buf[3] = paLut; + HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_PACONFIG, buf, 4 ); +} + +void SUBGRF_SetRxTxFallbackMode( uint8_t fallbackMode ) +{ + HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_TXFALLBACKMODE, &fallbackMode, 1 ); +} + +void SUBGRF_SetDioIrqParams( uint16_t irqMask, uint16_t dio1Mask, uint16_t dio2Mask, uint16_t dio3Mask ) +{ + uint8_t buf[8]; + + buf[0] = ( uint8_t )( ( irqMask >> 8 ) & 0x00FF ); + buf[1] = ( uint8_t )( irqMask & 0x00FF ); + buf[2] = ( uint8_t )( ( dio1Mask >> 8 ) & 0x00FF ); + buf[3] = ( uint8_t )( dio1Mask & 0x00FF ); + buf[4] = ( uint8_t )( ( dio2Mask >> 8 ) & 0x00FF ); + buf[5] = ( uint8_t )( dio2Mask & 0x00FF ); + buf[6] = ( uint8_t )( ( dio3Mask >> 8 ) & 0x00FF ); + buf[7] = ( uint8_t )( dio3Mask & 0x00FF ); + HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_CFG_DIOIRQ, buf, 8 ); +} + +uint16_t SUBGRF_GetIrqStatus( void ) +{ + uint8_t irqStatus[2]; + + HAL_SUBGHZ_ExecGetCmd( &hsubghz, RADIO_GET_IRQSTATUS, irqStatus, 2 ); + return ( irqStatus[0] << 8 ) | irqStatus[1]; +} + +void SUBGRF_SetTcxoMode (RadioTcxoCtrlVoltage_t tcxoVoltage, uint32_t timeout ) +{ + uint8_t buf[4]; + + buf[0] = tcxoVoltage & 0x07; + buf[1] = ( uint8_t )( ( timeout >> 16 ) & 0xFF ); + buf[2] = ( uint8_t )( ( timeout >> 8 ) & 0xFF ); + buf[3] = ( uint8_t )( timeout & 0xFF ); + + HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_TCXOMODE, buf, 4 ); +} + +void SUBGRF_SetRfFrequency( uint32_t frequency ) +{ + uint8_t buf[4]; + uint32_t chan; + + TCXO_Workaround(RF_API_STATE_TCXO_ON); + + if( ImageCalibrated == false ) + { + SUBGRF_CalibrateImage( frequency ); + ImageCalibrated = true; + } + + //chan = ( uint32_t )( ( double )frequency / ( double )FREQ_STEP ); + SX_FREQ_TO_CHANNEL(chan, frequency); + + buf[0] = ( uint8_t )( ( chan >> 24 ) & 0xFF ); + buf[1] = ( uint8_t )( ( chan >> 16 ) & 0xFF ); + buf[2] = ( uint8_t )( ( chan >> 8 ) & 0xFF ); + buf[3] = ( uint8_t )( chan & 0xFF ); + HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_RFFREQUENCY, buf, 4 ); + + +} + +void SUBGRF_SetPacketType( RadioPacketTypes_t packetType ) +{ + uint8_t reg = 0x00; + // Save packet type internally to avoid questioning the radio + PacketType = packetType; + + if( packetType == PACKET_TYPE_GFSK ) + { + HAL_SUBGHZ_WriteRegisters( &hsubghz, REG_BIT_SYNC, (uint8_t*)®, 1 ); + } + HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_PACKETTYPE, ( uint8_t* )&packetType, 1 ); +} + +RadioPacketTypes_t SUBGRF_GetPacketType( void ) +{ + return PacketType; +} +uint8_t SUBGRF_SetTxPower( int8_t power, RadioRampTimes_t rampTime ) +{ + uint8_t paSelect; + + int32_t TxConfig = BSP_RADIO_GetTxConfig(); + + switch (TxConfig) + { + case RADIO_CONF_RFO_LP_HP: + { + if (power>15) + { + paSelect= RFO_HP; + } + else + { + paSelect= RFO_LP; + } + break; + } + case RADIO_CONF_RFO_LP: + { + paSelect= RFO_LP; + break; + } + case RADIO_CONF_RFO_HP: + { + paSelect= RFO_HP; + break; + } + default: + break; + } + + SUBGRF_SetTxParams( paSelect, power, rampTime ); + + return paSelect; +} +void SUBGRF_SetTxParams( uint8_t paSelect, int8_t power, RadioRampTimes_t rampTime ) +{ + uint8_t buf[2]; + uint8_t reg; + + if( paSelect == RFO_LP ) + { + if( power == 15 ) + { + SUBGRF_SetPaConfig( 0x06, 0x00, 0x01, 0x01 ); + } + else + { + SUBGRF_SetPaConfig( 0x04, 0x00, 0x01, 0x01 ); + } + if( power >= 14 ) + { + power = 14; + } + else if( power < -3 ) + { + power = -3; + } + reg = 0x18; + HAL_SUBGHZ_WriteRegisters( &hsubghz, REG_OCP, (uint8_t*)®, 1 ); // current max is 80 mA for the whole device + } + else // rfo_hp + { + SUBGRF_SetPaConfig( 0x04, 0x07, 0x00, 0x01 ); + if( power > 22 ) + { + power = 22; + } + else if( power < -3 ) + { + power = -3; + } + reg = 0x38; + HAL_SUBGHZ_WriteRegisters( &hsubghz, REG_OCP, (uint8_t*)®, 1 ); // current max 160mA for the whole device + } + buf[0] = power; + buf[1] = ( uint8_t )rampTime; + HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_TXPARAMS, buf, 2 ); +} + + + +void SUBGRF_SetModulationParams( ModulationParams_t *modulationParams ) +{ + uint8_t n; + uint32_t tempVal = 0; + uint8_t buf[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + // Check if required configuration corresponds to the stored packet type + // If not, silently update radio packet type + if( PacketType != modulationParams->PacketType ) + { + SUBGRF_SetPacketType( modulationParams->PacketType ); + } + + switch( modulationParams->PacketType ) + { + case PACKET_TYPE_GFSK: + n = 8; + tempVal = ( uint32_t )( 32 * ( ( double )XTAL_FREQ / ( double )modulationParams->Params.Gfsk.BitRate ) ); + buf[0] = ( tempVal >> 16 ) & 0xFF; + buf[1] = ( tempVal >> 8 ) & 0xFF; + buf[2] = tempVal & 0xFF; + buf[3] = modulationParams->Params.Gfsk.ModulationShaping; + buf[4] = modulationParams->Params.Gfsk.Bandwidth; + //tempVal = ( uint32_t )( ( double )modulationParams->Params.Gfsk.Fdev / ( double )FREQ_STEP ); + SX_FREQ_TO_CHANNEL(tempVal, modulationParams->Params.Gfsk.Fdev); + buf[5] = ( tempVal >> 16 ) & 0xFF; + buf[6] = ( tempVal >> 8 ) & 0xFF; + buf[7] = ( tempVal& 0xFF ); + HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_MODULATIONPARAMS, buf, n ); + break; + case PACKET_TYPE_BPSK: + n = 4; + tempVal = ( uint32_t )( 32 * ( ( double )XTAL_FREQ / ( double )modulationParams->Params.Bpsk.BitRate ) ); + + buf[0] = ( tempVal >> 16 ) & 0xFF; + buf[1] = ( tempVal >> 8 ) & 0xFF; + buf[2] = tempVal & 0xFF; + buf[3] = modulationParams->Params.Bpsk.ModulationShaping; + HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_MODULATIONPARAMS, buf, n ); + break; + case PACKET_TYPE_LORA: + n = 4; + buf[0] = modulationParams->Params.LoRa.SpreadingFactor; + buf[1] = modulationParams->Params.LoRa.Bandwidth; + buf[2] = modulationParams->Params.LoRa.CodingRate; + buf[3] = modulationParams->Params.LoRa.LowDatarateOptimize; + + HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_MODULATIONPARAMS, buf, n ); + + break; + case PACKET_TYPE_GMSK: + n = 5; + tempVal = ( uint32_t )( 32 * ( ( double )XTAL_FREQ / ( double )modulationParams->Params.Gfsk.BitRate ) ); + buf[0] = ( tempVal >> 16 ) & 0xFF; + buf[1] = ( tempVal >> 8 ) & 0xFF; + buf[2] = tempVal & 0xFF; + buf[3] = modulationParams->Params.Gfsk.ModulationShaping; + buf[4] = modulationParams->Params.Gfsk.Bandwidth; + HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_MODULATIONPARAMS, buf, n ); + break; + default: + case PACKET_TYPE_NONE: + break; + } +} + +void SUBGRF_SetPacketParams( PacketParams_t *packetParams ) +{ + uint8_t n; + uint8_t crcVal = 0; + uint8_t buf[9] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + // Check if required configuration corresponds to the stored packet type + // If not, silently update radio packet type + if( PacketType != packetParams->PacketType ) + { + SUBGRF_SetPacketType( packetParams->PacketType ); + } + + switch( packetParams->PacketType ) + { + case PACKET_TYPE_GMSK: + case PACKET_TYPE_GFSK: + if( packetParams->Params.Gfsk.CrcLength == RADIO_CRC_2_BYTES_IBM ) + { + SUBGRF_SetCrcSeed( CRC_IBM_SEED ); + SUBGRF_SetCrcPolynomial( CRC_POLYNOMIAL_IBM ); + crcVal = RADIO_CRC_2_BYTES; + } + else if( packetParams->Params.Gfsk.CrcLength == RADIO_CRC_2_BYTES_CCIT ) + { + SUBGRF_SetCrcSeed( CRC_CCITT_SEED ); + SUBGRF_SetCrcPolynomial( CRC_POLYNOMIAL_CCITT ); + crcVal = RADIO_CRC_2_BYTES_INV; + } + else + { + crcVal = packetParams->Params.Gfsk.CrcLength; + } + n = 9; + buf[0] = ( packetParams->Params.Gfsk.PreambleLength >> 8 ) & 0xFF; + buf[1] = packetParams->Params.Gfsk.PreambleLength; + buf[2] = packetParams->Params.Gfsk.PreambleMinDetect; + buf[3] = ( packetParams->Params.Gfsk.SyncWordLength /*<< 3*/ ); // convert from byte to bit + buf[4] = packetParams->Params.Gfsk.AddrComp; + buf[5] = packetParams->Params.Gfsk.HeaderType; + buf[6] = packetParams->Params.Gfsk.PayloadLength; + buf[7] = crcVal; + buf[8] = packetParams->Params.Gfsk.DcFree; + break; + case PACKET_TYPE_BPSK: + n = 1; + buf[0] = packetParams->Params.Bpsk.PayloadLength; + break; + case PACKET_TYPE_LORA: + n = 6; + buf[0] = ( packetParams->Params.LoRa.PreambleLength >> 8 ) & 0xFF; + buf[1] = packetParams->Params.LoRa.PreambleLength; + buf[2] = packetParams->Params.LoRa.HeaderType; + buf[3] = packetParams->Params.LoRa.PayloadLength; + buf[4] = packetParams->Params.LoRa.CrcMode; + buf[5] = packetParams->Params.LoRa.InvertIQ; + break; + default: + case PACKET_TYPE_NONE: + return; + } + HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_PACKETPARAMS, buf, n ); +} + +void SUBGRF_SetCadParams( RadioLoRaCadSymbols_t cadSymbolNum, uint8_t cadDetPeak, uint8_t cadDetMin, RadioCadExitModes_t cadExitMode, uint32_t cadTimeout ) +{ + uint8_t buf[7]; + + buf[0] = ( uint8_t )cadSymbolNum; + buf[1] = cadDetPeak; + buf[2] = cadDetMin; + buf[3] = ( uint8_t )cadExitMode; + buf[4] = ( uint8_t )( ( cadTimeout >> 16 ) & 0xFF ); + buf[5] = ( uint8_t )( ( cadTimeout >> 8 ) & 0xFF ); + buf[6] = ( uint8_t )( cadTimeout & 0xFF ); + HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_CADPARAMS, buf, 5 ); + OperatingMode = MODE_CAD; +} + +void SUBGRF_SetBufferBaseAddress( uint8_t txBaseAddress, uint8_t rxBaseAddress ) +{ + uint8_t buf[2]; + + buf[0] = txBaseAddress; + buf[1] = rxBaseAddress; + HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_BUFFERBASEADDRESS, buf, 2 ); +} + +RadioStatus_t SUBGRF_GetStatus( void ) +{ + uint8_t stat = 0; + RadioStatus_t status; + + HAL_SUBGHZ_ExecGetCmd( &hsubghz, RADIO_GET_STATUS, ( uint8_t * )&stat, 1 ); + status.Value = stat; + return status; +} + +int8_t SUBGRF_GetRssiInst( void ) +{ + uint8_t buf[1]; + int8_t rssi = 0; + + HAL_SUBGHZ_ExecGetCmd( &hsubghz, RADIO_GET_RSSIINST, buf, 1 ); + rssi = -buf[0] >> 1; + return rssi; +} + +void SUBGRF_GetRxBufferStatus( uint8_t *payloadLength, uint8_t *rxStartBufferPointer ) +{ + uint8_t status[2]; + uint8_t data; + + HAL_SUBGHZ_ExecGetCmd( &hsubghz, RADIO_GET_RXBUFFERSTATUS, status, 2 ); + + // In case of LORA fixed header, the payloadLength is obtained by reading + // the register REG_LR_PAYLOADLENGTH + HAL_SUBGHZ_ReadRegisters( &hsubghz, REG_LR_PACKETPARAMS, &data, 1 ); + if( ( SUBGRF_GetPacketType( ) == PACKET_TYPE_LORA ) && (data >> 7 == 1 ) ) + { + HAL_SUBGHZ_ReadRegisters( &hsubghz, REG_LR_PAYLOADLENGTH, &data, 1 ); + *payloadLength = data; + } + else + { + *payloadLength = status[0]; + } + *rxStartBufferPointer = status[1]; +} + +void SUBGRF_GetPacketStatus( PacketStatus_t *pktStatus ) +{ + uint8_t status[3]; + + HAL_SUBGHZ_ExecGetCmd( &hsubghz, RADIO_GET_PACKETSTATUS, status, 3 ); + + pktStatus->packetType = SUBGRF_GetPacketType( ); + switch( pktStatus->packetType ) + { + case PACKET_TYPE_GFSK: + pktStatus->Params.Gfsk.RxStatus = status[0]; + pktStatus->Params.Gfsk.RssiSync = -status[1] >> 1; + pktStatus->Params.Gfsk.RssiAvg = -status[2] >> 1; + pktStatus->Params.Gfsk.FreqError = 0; + break; + + case PACKET_TYPE_LORA: + pktStatus->Params.LoRa.RssiPkt = -status[0] >> 1; + ( status[1] < 128 ) ? ( pktStatus->Params.LoRa.SnrPkt = status[1] >> 2 ) : ( pktStatus->Params.LoRa.SnrPkt = ( ( status[1] - 256 ) >> 2 ) ); + pktStatus->Params.LoRa.SignalRssiPkt = -status[2] >> 1; + pktStatus->Params.LoRa.FreqError = FrequencyError; + break; + + default: + case PACKET_TYPE_NONE: + // In that specific case, we set everything in the pktStatus to zeros + // and reset the packet type accordingly + RADIO_MEMSET8( (uint8_t*)pktStatus, 0, sizeof( PacketStatus_t ) ); + pktStatus->packetType = PACKET_TYPE_NONE; + break; + } +} + +RadioError_t SUBGRF_GetErrors( void ) +{ + RadioError_t error; + + HAL_SUBGHZ_ExecGetCmd( &hsubghz, RADIO_GET_ERROR, ( uint8_t * )&error, 2 ); + return error; +} + +void SUBGRF_ClearIrqStatus( uint16_t irq ) +{ + uint8_t buf[2]; + + buf[0] = ( uint8_t )( ( ( uint16_t )irq >> 8 ) & 0x00FF ); + buf[1] = ( uint8_t )( ( uint16_t )irq & 0x00FF ); + HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_CLR_IRQSTATUS, buf, 2 ); +} + + +void SUBGRF_WriteRegister( uint16_t addr, uint8_t data ) +{ + HAL_SUBGHZ_WriteRegisters( &hsubghz, addr, (uint8_t*)&data, 1 ); +} + +uint8_t SUBGRF_ReadRegister( uint16_t addr ) +{ + uint8_t data; + HAL_SUBGHZ_ReadRegisters( &hsubghz, addr, &data, 1 ); + return data; +} + +void SUBGRF_WriteRegisters( uint16_t addr, uint8_t *buffer, uint8_t size ) +{ + HAL_SUBGHZ_WriteRegisters( &hsubghz, addr, buffer, size ); +} + +void SUBGRF_ReadRegisters( uint16_t addr, uint8_t *buffer, uint8_t size ) +{ + HAL_SUBGHZ_ReadRegisters( &hsubghz, addr, buffer, size ); +} + + +/* HAL_SUBGHz Callbacks definitions */ +void HAL_SUBGHZ_TxCpltCallback(SUBGHZ_HandleTypeDef *hsubghz) +{ + RadioOnDioIrqCb( IRQ_TX_DONE ); +} + +void HAL_SUBGHZ_RxCpltCallback(SUBGHZ_HandleTypeDef *hsubghz) +{ + RadioOnDioIrqCb( IRQ_RX_DONE ); +} + +void HAL_SUBGHZ_CRCErrorCallback (SUBGHZ_HandleTypeDef *hsubghz) +{ + RadioOnDioIrqCb( IRQ_CRC_ERROR); +} + +void HAL_SUBGHZ_CADDoneCallback(SUBGHZ_HandleTypeDef *hsubghz) +{ + RadioOnDioIrqCb( IRQ_CAD_DONE); +} + +void HAL_SUBGHZ_CADActivityDetectedCallback(SUBGHZ_HandleTypeDef *hsubghz) +{ + RadioOnDioIrqCb( IRQ_CAD_ACTIVITY_DETECTED); +} + +void HAL_SUBGHZ_RxTxTimeoutCallback(SUBGHZ_HandleTypeDef *hsubghz) +{ + RadioOnDioIrqCb( IRQ_RX_TX_TIMEOUT); +} + +void HAL_SUBGHZ_HeaderErrorCallback(SUBGHZ_HandleTypeDef *hsubghz) +{ + RadioOnDioIrqCb( IRQ_HEADER_ERROR); +} + +void HAL_SUBGHZ_PreambleDetectedCallback(SUBGHZ_HandleTypeDef *hsubghz) +{ + RadioOnDioIrqCb( IRQ_PREAMBLE_DETECTED); +} + +void HAL_SUBGHZ_SyncWordValidCallback(SUBGHZ_HandleTypeDef *hsubghz) +{ + RadioOnDioIrqCb( IRQ_SYNCWORD_VALID); +} + +void HAL_SUBGHZ_HeaderValidCallback(SUBGHZ_HandleTypeDef *hsubghz) +{ + RadioOnDioIrqCb( IRQ_HEADER_VALID); +} + + +static void TCXO_Workaround( RF_API_Tcxo_State_t state ) +{ + /* Workaround needed only for cut1.0*/ + uint16_t RevisionID = LL_DBGMCU_GetRevisionID(); + + if (1U ==BSP_RADIO_IsTCXO() && (RevisionID==0x1000) ) + { + switch (state) + { + case RF_API_STATE_TCXO_OFF: + LL_RCC_HSE_Disable(); + + LL_RCC_HSE_DisableTcxo(); + + LL_RCC_HSE_DisableBypass(); + + UTIL_LPM_SetStopMode( LPM_TCXO_WA_Id , UTIL_LPM_ENABLE); + + MW_LOG("TCXOFF\n\r"); + break; + + case RF_API_STATE_TCXO_ON: + LL_RCC_HSE_EnableTcxo(); + + LL_RCC_HSE_EnableBypass(); + + LL_RCC_HSE_Enable(); + + UTIL_LPM_SetStopMode( LPM_TCXO_WA_Id , UTIL_LPM_DISABLE); + + MW_LOG("TCXON\n\r"); + + while(LL_RCC_HSE_IsReady() == 0) + { + } + + break; + + default: + break; + } + } +} + +static void RadioReset_Workaround( void ) +{ + if ( LL_DBGMCU_GetRevisionID() ==0x1000 ) + { + uint8_t reg=0; + + HAL_SUBGHZ_ExecSetCmd( &hsubghz, RADIO_SET_SLEEP, ®, 1 ); + + RADIO_DELAY_MS( 2 ); + } +} + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ + diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/stm32_radio_driver/radio_driver.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/stm32_radio_driver/radio_driver.h new file mode 100644 index 00000000..729a32b1 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/stm32_radio_driver/radio_driver.h @@ -0,0 +1,1223 @@ +/* + ______ _ + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2017 Semtech + +Description: Generic SX126x driver implementation + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Authors: Miguel Luis, Gregory Cristian +*/ +/** + ****************************************************************************** + * + * Portions COPYRIGHT 2019 STMicroelectronics + * + * @file radio_driver.h + * @author MCD Application Team + * @brief Header for driver radio interface + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __RADIO_DRIVER_H__ +#define __RADIO_DRIVER_H__ + +/* Includes ------------------------------------------------------------------*/ + +#include +#include + +/*==========================================================================*/ +/** @addtogroup BSP + * @{ + */ + +/** @addtogroup STM32WLXX_NUCLEO STM32WLXX-NUCLEO + * @{ + */ + +/** @defgroup STM32WLXX_NUCLEO_RADIO_LOW_LEVEL RADIO LOW LEVEL + * @{ + */ + +/** @defgroup STM32WLXX_NUCLEO_RADIO_LOW_LEVEL_Exported_Types RADIO LOW LEVEL Exported Types + * @{ + */ + +typedef enum +{ + RADIO_SWITCH_OFF = 0, + RADIO_SWITCH_RX = 1, + RADIO_SWITCH_RFO_LP = 2, + RADIO_SWITCH_RFO_HP = 3, +}BSP_RADIO_Switch_TypeDef; + +/** + * @} + */ + +/** @defgroup STM32WLXX_NUCLEO_RADIO_LOW_LEVEL_Exported_Constants RADIO LOW LEVEL Exported Constants + * @{ + */ + +/** @defgroup STM32WLXX_NUCLEO_RADIO_LOW_LEVEL_RADIOCONFIG RADIO LOW LEVEL RADIO CONFIG Constants + * @{ + */ + +#define RADIO_CONF_RFO_LP_HP 0 +#define RADIO_CONF_RFO_LP 1 +#define RADIO_CONF_RFO_HP 2 + + +/* Common Error codes */ +#define BSP_ERROR_NONE 0 +#define BSP_ERROR_NO_INIT -1 +#define BSP_ERROR_WRONG_PARAM -2 +#define BSP_ERROR_BUSY -3 +#define BSP_ERROR_PERIPH_FAILURE -4 +#define BSP_ERROR_COMPONENT_FAILURE -5 +#define BSP_ERROR_UNKNOWN_FAILURE -6 +#define BSP_ERROR_UNKNOWN_COMPONENT -7 +#define BSP_ERROR_BUS_FAILURE -8 +#define BSP_ERROR_CLOCK_FAILURE -9 +#define BSP_ERROR_MSP_FAILURE -10 +#define BSP_ERROR_FEATURE_NOT_SUPPORTED -11 +/** + * @} + */ + +/** @defgroup STM32WLXX_NUCLEO_RADIO_LOW_LEVEL_RFSWITCH RADIO LOW LEVEL RF SWITCH Constants + * @{ + */ + +#define RF_SW_CTRL3_PIN GPIO_PIN_3 +#define RF_SW_CTRL3_GPIO_PORT GPIOC +#define RF_SW_CTRL3_GPIO_CLK_ENABLE() __HAL_RCC_GPIOC_CLK_ENABLE() +#define RF_SW_CTRL3_GPIO_CLK_DISABLE() __HAL_RCC_GPIOC_CLK_DISABLE() + +#define RF_SW_CTRL1_PIN GPIO_PIN_4 +#define RF_SW_CTRL1_GPIO_PORT GPIOC +#define RF_SW_CTRL1_GPIO_CLK_ENABLE() __HAL_RCC_GPIOC_CLK_ENABLE() +#define RF_SW_RX_GPIO_CLK_DISABLE() __HAL_RCC_GPIOC_CLK_DISABLE() + +#define RF_SW_CTRL2_PIN GPIO_PIN_5 +#define RF_SW_CTRL2_GPIO_PORT GPIOC +#define RF_SW_CTRL2_GPIO_CLK_ENABLE() __HAL_RCC_GPIOC_CLK_ENABLE() +#define RF_SW_CTRL2_GPIO_CLK_DISABLE() __HAL_RCC_GPIOC_CLK_DISABLE() + +#define RF_TCXO_VCC_PIN GPIO_PIN_0 +#define RF_TCXO_VCC_GPIO_PORT GPIOB +#define RF_TCXO_VCC_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE() +#define RF_TCXO_VCC_CLK_DISABLE() __HAL_RCC_GPIOB_CLK_DISABLE() + +/** @defgroup STM32WLXX_NUCLEO_CONFIG_Exported_Constants Exported Constants + * @{ + */ +/* COM usage define */ +#define USE_BSP_COM_FEATURE 0U + +/* COM log define */ +#define USE_COM_LOG 0U + +/* IRQ priorities */ +#define BSP_BUTTON_SWx_IT_PRIORITY 15U + +/* Radio maximum wakeup time (in ms) */ +#define RF_WAKEUP_TIME 10U + +/* Indicates whether or not TCXO is supported by the board + * 0: TCXO not supported + * 1: TCXO supported + */ +#define IS_TCXO_SUPPORTED 1U + +/* Indicates whether or not DCDC is supported by the board + * 0: DCDC not supported + * 1: DCDC supported + */ +#define IS_DCDC_SUPPORTED 1U + +/*================================================================================*/ + +/* Exported constants --------------------------------------------------------*/ +#define RFO_LP 1 +#define RFO_HP 2 + +/*! + * Generic SUBGRF_ error code + */ +#define SUBGRF_OK 0 +#define SUBGRF_ERROR -1 + +/*! + * Radio complete Wake-up Time with margin for temperature compensation + */ +#define RADIO_WAKEUP_TIME 3 // [ms] + +/*! + * @brief Compensation delay for SetAutoTx/Rx functions in 15.625 microseconds + */ +#define AUTO_RX_TX_OFFSET 2 + +/*! + * @brief LFSR initial value to compute IBM type CRC + */ +#define CRC_IBM_SEED 0xFFFF + +/*! + * @brief LFSR initial value to compute CCIT type CRC + */ +#define CRC_CCITT_SEED 0x1D0F + +/*! + * @brief Polynomial used to compute IBM CRC + */ +#define CRC_POLYNOMIAL_IBM 0x8005 + +/*! + * @brief Polynomial used to compute CCIT CRC + */ +#define CRC_POLYNOMIAL_CCITT 0x1021 + +/*! + * @brief The address of the register holding the first byte defining the CRC seed + * + */ +#define REG_LR_CRCSEEDBASEADDR 0x06BC + +/*! + * @brief The address of the register holding the first byte defining the CRC polynomial + */ +#define REG_LR_CRCPOLYBASEADDR 0x06BE + +/*! + * @brief The address of the register holding the first byte defining the whitening seed + */ +#define REG_LR_WHITSEEDBASEADDR_MSB 0x06B8 +#define REG_LR_WHITSEEDBASEADDR_LSB 0x06B9 + +/*! + * @brief The address of the register holding the packet configuration + */ +#define REG_LR_PACKETPARAMS 0x0704 + +/*! + * @brief The address of the register holding the payload size + */ +#define REG_LR_PAYLOADLENGTH 0x0702 + +/*! + * @brief The addresses of the registers holding SyncWords values + */ +#define REG_LR_SYNCWORDBASEADDRESS 0x06C0 + +/*! + * @brief The addresses of the register holding LoRa Modem SyncWord value + */ +#define REG_LR_SYNCWORD 0x0740 + +/*! + * Syncword for Private LoRa networks + */ +#define LORA_MAC_PRIVATE_SYNCWORD 0x1424 + +/*! + * Syncword for Public LoRa networks + */ +#define LORA_MAC_PUBLIC_SYNCWORD 0x3444 + +/*! + * The address of the register giving a 4 bytes random number + */ +#define RANDOM_NUMBER_GENERATORBASEADDR 0x0819 + +/*! + * The address of the register holding RX Gain value (0x94: power saving, 0x96: rx boosted) + */ +#define REG_RX_GAIN 0x08AC + +/*! + * The address of the register holding Bit Sync configuration + */ +#define REG_BIT_SYNC 0x06AC + +/*! + * Change the value on the device internal trimming capacitor + */ +#define REG_XTA_TRIM 0x0911 + +/*! + * Set the current max value in the over current protection + */ +#define REG_OCP 0x08E7 + +/* Exported types ------------------------------------------------------------*/ + +/*! + * @brief Structure describing the radio status + */ +typedef union RadioStatus_u +{ + uint8_t Value; + struct + { //bit order is lsb -> msb + uint8_t Reserved : 1; //!< Reserved + uint8_t CmdStatus : 3; //!< Command status + uint8_t ChipMode : 3; //!< Chip mode + uint8_t CpuBusy : 1; //!< Flag for CPU radio busy + }Fields; +}RadioStatus_t; + +/*! + * @brief Structure describing the error codes for callback functions + */ +typedef enum +{ + IRQ_HEADER_ERROR_CODE = 0x01, + IRQ_SYNCWORD_ERROR_CODE = 0x02, + IRQ_CRC_ERROR_CODE = 0x04, +}IrqErrorCode_t; + +enum IrqPblSyncHeaderCode_t +{ + IRQ_PBL_DETECT_CODE = 0x01, + IRQ_SYNCWORD_VALID_CODE = 0x02, + IRQ_HEADER_VALID_CODE = 0x04, +}; + + + +/*! + * @brief Declares the oscillator in use while in standby mode + * + * Using the STDBY_RC standby mode allow to reduce the energy consumption + * STDBY_XOSC should be used for time critical applications + */ +typedef enum +{ + STDBY_RC = 0x00, + STDBY_XOSC = 0x01, +}RadioStandbyModes_t; + +/*! + * @brief Declares the power regulation used to power the device + * + * This command allows the user to specify if DC-DC or LDO is used for power regulation. + * Using only LDO implies that the Rx or Tx current is doubled + */ +typedef enum +{ + USE_LDO = 0x00, // default + USE_DCDC = 0x01, +}RadioRegulatorMode_t; + +/*! + * @brief Represents the possible packet type (i.e. modem) used + */ +typedef enum +{ + PACKET_TYPE_GFSK = 0x00, + PACKET_TYPE_LORA = 0x01, + PACKET_TYPE_BPSK = 0x02, + PACKET_TYPE_GMSK = 0x03, + PACKET_TYPE_NONE = 0x0F, +}RadioPacketTypes_t; + +/*! + * @brief Represents the ramping time for power amplifier + */ +typedef enum +{ + RADIO_RAMP_10_US = 0x00, + RADIO_RAMP_20_US = 0x01, + RADIO_RAMP_40_US = 0x02, + RADIO_RAMP_80_US = 0x03, + RADIO_RAMP_200_US = 0x04, + RADIO_RAMP_800_US = 0x05, + RADIO_RAMP_1700_US = 0x06, + RADIO_RAMP_3400_US = 0x07, +}RadioRampTimes_t; + +/*! + * @brief Represents the number of symbols to be used for channel activity detection operation + */ +typedef enum +{ + LORA_CAD_01_SYMBOL = 0x00, + LORA_CAD_02_SYMBOL = 0x01, + LORA_CAD_04_SYMBOL = 0x02, + LORA_CAD_08_SYMBOL = 0x03, + LORA_CAD_16_SYMBOL = 0x04, +}RadioLoRaCadSymbols_t; + +/*! + * @brief Represents the Channel Activity Detection actions after the CAD operation is finished + */ +typedef enum +{ + LORA_CAD_ONLY = 0x00, + LORA_CAD_RX = 0x01, + LORA_CAD_LBT = 0x10, +}RadioCadExitModes_t; + +/*! + * @brief Represents the modulation shaping parameter + */ +typedef enum +{ + MOD_SHAPING_OFF = 0x00, + MOD_SHAPING_G_BT_03 = 0x08, + MOD_SHAPING_G_BT_05 = 0x09, + MOD_SHAPING_G_BT_07 = 0x0A, + MOD_SHAPING_G_BT_1 = 0x0B, + MOD_SHAPING_DBPSK = 0x16, +}RadioModShapings_t; + +/*! + * @brief Represents the modulation shaping parameter + */ +typedef enum +{ + RX_BW_4800 = 0x1F, + RX_BW_5800 = 0x17, + RX_BW_7300 = 0x0F, + RX_BW_9700 = 0x1E, + RX_BW_11700 = 0x16, + RX_BW_14600 = 0x0E, + RX_BW_19500 = 0x1D, + RX_BW_23400 = 0x15, + RX_BW_29300 = 0x0D, + RX_BW_39000 = 0x1C, + RX_BW_46900 = 0x14, + RX_BW_58600 = 0x0C, + RX_BW_78200 = 0x1B, + RX_BW_93800 = 0x13, + RX_BW_117300 = 0x0B, + RX_BW_156200 = 0x1A, + RX_BW_187200 = 0x12, + RX_BW_234300 = 0x0A, + RX_BW_312000 = 0x19, + RX_BW_373600 = 0x11, + RX_BW_467000 = 0x09, +}RadioRxBandwidth_t; + +/*! + * @brief Represents the possible spreading factor values in LoRa packet types + */ +typedef enum +{ + LORA_SF5 = 0x05, + LORA_SF6 = 0x06, + LORA_SF7 = 0x07, + LORA_SF8 = 0x08, + LORA_SF9 = 0x09, + LORA_SF10 = 0x0A, + LORA_SF11 = 0x0B, + LORA_SF12 = 0x0C, +}RadioLoRaSpreadingFactors_t; + +/*! + * @brief Represents the bandwidth values for LoRa packet type + */ +typedef enum +{ + LORA_BW_500 = 6, + LORA_BW_250 = 5, + LORA_BW_125 = 4, + LORA_BW_062 = 3, + LORA_BW_041 = 10, + LORA_BW_031 = 2, + LORA_BW_020 = 9, + LORA_BW_015 = 1, + LORA_BW_010 = 8, + LORA_BW_007 = 0, +}RadioLoRaBandwidths_t; + +/*! + * @brief Represents the coding rate values for LoRa packet type + */ +typedef enum +{ + LORA_CR_4_5 = 0x01, + LORA_CR_4_6 = 0x02, + LORA_CR_4_7 = 0x03, + LORA_CR_4_8 = 0x04, +}RadioLoRaCodingRates_t; + +/*! + * @brief Represents the preamble length used to detect the packet on Rx side + */ +typedef enum +{ + RADIO_PREAMBLE_DETECTOR_OFF = 0x00, //!< Preamble detection length off + RADIO_PREAMBLE_DETECTOR_08_BITS = 0x04, //!< Preamble detection length 8 bits + RADIO_PREAMBLE_DETECTOR_16_BITS = 0x05, //!< Preamble detection length 16 bits + RADIO_PREAMBLE_DETECTOR_24_BITS = 0x06, //!< Preamble detection length 24 bits + RADIO_PREAMBLE_DETECTOR_32_BITS = 0x07, //!< Preamble detection length 32 bit +}RadioPreambleDetection_t; + +/*! + * @brief Represents the possible combinations of SyncWord correlators activated + */ +typedef enum +{ + RADIO_ADDRESSCOMP_FILT_OFF = 0x00, //!< No correlator turned on, i.e. do not search for SyncWord + RADIO_ADDRESSCOMP_FILT_NODE = 0x01, + RADIO_ADDRESSCOMP_FILT_NODE_BROAD = 0x02, +}RadioAddressComp_t; + +/*! + * @brief Radio packet length mode + */ +typedef enum +{ + RADIO_PACKET_FIXED_LENGTH = 0x00, //!< The packet is known on both sides, no header included in the packet + RADIO_PACKET_VARIABLE_LENGTH = 0x01, //!< The packet is on variable size, header included +}RadioPacketLengthModes_t; + +/*! + * @brief Represents the CRC length + */ +typedef enum +{ + RADIO_CRC_OFF = 0x01, //!< No CRC in use + RADIO_CRC_1_BYTES = 0x00, + RADIO_CRC_2_BYTES = 0x02, + RADIO_CRC_1_BYTES_INV = 0x04, + RADIO_CRC_2_BYTES_INV = 0x06, + RADIO_CRC_2_BYTES_IBM = 0xF1, + RADIO_CRC_2_BYTES_CCIT = 0xF2, +}RadioCrcTypes_t; + +/*! + * @brief Radio whitening mode activated or deactivated + */ +typedef enum +{ + RADIO_DC_FREE_OFF = 0x00, + RADIO_DC_FREEWHITENING = 0x01, +}RadioDcFree_t; + +/*! + * @brief Holds the lengths mode of a LoRa packet type + */ +typedef enum +{ + LORA_PACKET_VARIABLE_LENGTH = 0x00, //!< The packet is on variable size, header included + LORA_PACKET_FIXED_LENGTH = 0x01, //!< The packet is known on both sides, no header included in the packet + LORA_PACKET_EXPLICIT = LORA_PACKET_VARIABLE_LENGTH, + LORA_PACKET_IMPLICIT = LORA_PACKET_FIXED_LENGTH, +}RadioLoRaPacketLengthsMode_t; + +/*! + * @brief Represents the CRC mode for LoRa packet type + */ +typedef enum +{ + LORA_CRC_ON = 0x01, //!< CRC activated + LORA_CRC_OFF = 0x00, //!< CRC not used +}RadioLoRaCrcModes_t; + +/*! + * @brief Represents the IQ mode for LoRa packet type + */ +typedef enum +{ + LORA_IQ_NORMAL = 0x00, + LORA_IQ_INVERTED = 0x01, +}RadioLoRaIQModes_t; + +/*! + * @brief Represents the volatge used to control the TCXO on/off from DIO3 + */ +typedef enum +{ + TCXO_CTRL_1_6V = 0x00, + TCXO_CTRL_1_7V = 0x01, + TCXO_CTRL_1_8V = 0x02, + TCXO_CTRL_2_2V = 0x03, + TCXO_CTRL_2_4V = 0x04, + TCXO_CTRL_2_7V = 0x05, + TCXO_CTRL_3_0V = 0x06, + TCXO_CTRL_3_3V = 0x07, +}RadioTcxoCtrlVoltage_t; + +/*! + * @brief Represents the interruption masks available for the radio + * + * @remark Note that not all these interruptions are available for all packet types + */ +typedef enum +{ + IRQ_RADIO_NONE = 0x0000, + IRQ_TX_DONE = 0x0001, + IRQ_RX_DONE = 0x0002, + IRQ_PREAMBLE_DETECTED = 0x0004, + IRQ_SYNCWORD_VALID = 0x0008, + IRQ_HEADER_VALID = 0x0010, + IRQ_HEADER_ERROR = 0x0020, + IRQ_CRC_ERROR = 0x0040, + IRQ_CAD_DONE = 0x0080, + IRQ_CAD_ACTIVITY_DETECTED = 0x0100, + IRQ_RX_TX_TIMEOUT = 0x0200, + IRQ_RADIO_ALL = 0xFFFF, +}RadioIrqMasks_t; + + + +/*! + * @brief The type describing the modulation parameters for every packet types + */ +typedef struct +{ + RadioPacketTypes_t PacketType; //!< Packet to which the modulation parameters are referring to. + struct + { + struct + { + uint32_t BitRate; + uint32_t Fdev; + RadioModShapings_t ModulationShaping; + uint8_t Bandwidth; + }Gfsk; + struct + { + uint32_t BitRate; + RadioModShapings_t ModulationShaping; + }Bpsk; + struct + { + RadioLoRaSpreadingFactors_t SpreadingFactor; //!< Spreading Factor for the LoRa modulation + RadioLoRaBandwidths_t Bandwidth; //!< Bandwidth for the LoRa modulation + RadioLoRaCodingRates_t CodingRate; //!< Coding rate for the LoRa modulation + uint8_t LowDatarateOptimize; //!< Indicates if the modem uses the low datarate optimization + }LoRa; + }Params; //!< Holds the modulation parameters structure +}ModulationParams_t; + +/*! + * @brief The type describing the packet parameters for every packet types + */ +typedef struct +{ + RadioPacketTypes_t PacketType; //!< Packet to which the packet parameters are referring to. + struct + { + /*! + * @brief Holds the GFSK packet parameters + */ + struct + { + uint16_t PreambleLength; //!< The preamble Tx length for GFSK packet type in bit + RadioPreambleDetection_t PreambleMinDetect; //!< The preamble Rx length minimal for GFSK packet type + uint8_t SyncWordLength; //!< The synchronization word length for GFSK packet type + RadioAddressComp_t AddrComp; //!< Activated SyncWord correlators + RadioPacketLengthModes_t HeaderType; //!< If the header is explicit, it will be transmitted in the GFSK packet. If the header is implicit, it will not be transmitted + uint8_t PayloadLength; //!< Size of the payload in the GFSK packet + RadioCrcTypes_t CrcLength; //!< Size of the CRC block in the GFSK packet + RadioDcFree_t DcFree; + }Gfsk; + /*! + * @brief Holds the BPSK packet parameters + */ + struct + { + uint8_t PayloadLength; //!< Size of the payload in the BPSK packet + }Bpsk; + /*! + * @brief Holds the LoRa packet parameters + */ + struct + { + uint16_t PreambleLength; //!< The preamble length is the number of LoRa symbols in the preamble + RadioLoRaPacketLengthsMode_t HeaderType; //!< If the header is explicit, it will be transmitted in the LoRa packet. If the header is implicit, it will not be transmitted + uint8_t PayloadLength; //!< Size of the payload in the LoRa packet + RadioLoRaCrcModes_t CrcMode; //!< Size of CRC block in LoRa packet + RadioLoRaIQModes_t InvertIQ; //!< Allows to swap IQ for LoRa packet + }LoRa; + }Params; //!< Holds the packet parameters structure +}PacketParams_t; + +/*! + * @brief Represents the packet status for every packet type + */ +typedef struct +{ + RadioPacketTypes_t packetType; //!< Packet to which the packet status are referring to. + struct + { + struct + { + uint8_t RxStatus; + int8_t RssiAvg; //!< The averaged RSSI + int8_t RssiSync; //!< The RSSI measured on last packet + uint32_t FreqError; + }Gfsk; + struct + { + int8_t RssiPkt; //!< The RSSI of the last packet + int8_t SnrPkt; //!< The SNR of the last packet + int8_t SignalRssiPkt; + uint32_t FreqError; + }LoRa; + }Params; +}PacketStatus_t; + +/*! + * @brief Represents the Rx internal counters values when GFSK or LoRa packet type is used + */ +typedef struct +{ + RadioPacketTypes_t packetType; //!< Packet to which the packet status are referring to. + uint16_t PacketReceived; + uint16_t CrcOk; + uint16_t LengthError; +}RxCounter_t; + +/*! + * @brief Represents a calibration configuration + */ +typedef union +{ + struct + { + uint8_t RC64KEnable : 1; //!< Calibrate RC64K clock + uint8_t RC13MEnable : 1; //!< Calibrate RC13M clock + uint8_t PLLEnable : 1; //!< Calibrate PLL + uint8_t ADCPulseEnable : 1; //!< Calibrate ADC Pulse + uint8_t ADCBulkNEnable : 1; //!< Calibrate ADC bulkN + uint8_t ADCBulkPEnable : 1; //!< Calibrate ADC bulkP + uint8_t ImgEnable : 1; + uint8_t : 1; + }Fields; + uint8_t Value; +}CalibrationParams_t; + +/*! + * @brief Represents a sleep mode configuration + */ +typedef union +{ + struct + { + uint8_t WakeUpRTC : 1; //!< Get out of sleep mode if wakeup signal received from RTC + uint8_t Reset : 1; + uint8_t WarmStart : 1; + uint8_t Reserved : 5; + }Fields; + uint8_t Value; +}SleepParams_t; + +/*! + * @brief Represents the possible radio system error states + */ +typedef union +{ + struct + { + uint8_t Rc64kCalib : 1; //!< RC 64kHz oscillator calibration failed + uint8_t Rc13mCalib : 1; //!< RC 13MHz oscillator calibration failed + uint8_t PllCalib : 1; //!< PLL calibration failed + uint8_t AdcCalib : 1; //!< ADC calibration failed + uint8_t ImgCalib : 1; //!< Image calibration failed + uint8_t XoscStart : 1; //!< XOSC oscillator failed to start + uint8_t PllLock : 1; //!< PLL lock failed + uint8_t BuckStart : 1; //!< Buck converter failed to start + uint8_t PaRamp : 1; //!< PA ramp failed + uint8_t : 7; //!< Reserved + }Fields; + uint16_t Value; +}RadioError_t; + +/*! + * @brief Represents the operating mode the radio is actually running + */ +typedef enum +{ + MODE_SLEEP = 0x00, //! The radio is in sleep mode + MODE_STDBY_RC, //! The radio is in standby mode with RC oscillator + MODE_STDBY_XOSC, //! The radio is in standby mode with XOSC oscillator + MODE_FS, //! The radio is in frequency synthesis mode + MODE_TX, //! The radio is in transmit mode + MODE_RX, //! The radio is in receive mode + MODE_RX_DC, //! The radio is in receive duty cycle mode + MODE_CAD //! The radio is in channel activity detection mode +}RadioOperatingModes_t; + +/*! + * Radio driver internal state machine states definition + */ +typedef enum +{ + RFSWITCH_RX = 0, //!< The radio is in RX + RFSWITCH_TX = 1 //!< The radio is in TX +}RFState_t; + +/*! + * SUBG_RF definitions + */ + +/*! + * @brief Provides the frequency of the chip running on the radio and the frequency step + * + * @remark These defines are used for computing the frequency divider to set the RF frequency + */ +#define XTAL_FREQ ( double )32000000 +//#define FREQ_DIV ( double )pow( 2.0, 25.0 ) +//#define FREQ_STEP ( double )( XTAL_FREQ / FREQ_DIV ) +#define FREQ_STEP_14 15625 /* (XTAL_FREQ / FREQ_DIV) >> 14 */ + +/* channel = Freq / FREQ_STEP */ +#define SX_FREQ_TO_CHANNEL( channel, freq ) \ + do \ + { \ + uint32_t initialFreqInt, initialFreqFrac; \ + initialFreqInt = freq / FREQ_STEP_14; \ + initialFreqFrac = freq - ( initialFreqInt * FREQ_STEP_14 ); \ + channel = ( initialFreqInt << 14 ) + ( ( ( initialFreqFrac << 14 ) + ( FREQ_STEP_14 / 2 ) ) / FREQ_STEP_14 ); \ + }while( 0 ) + + + +#define RX_BUFFER_SIZE 256 + +/* External variables --------------------------------------------------------*/ +/* Exported macros -----------------------------------------------------------*/ +/* Exported functions ------------------------------------------------------- */ + +/*! + * ============================================================================ + * Public functions prototypes + * ============================================================================ + */ + +/* + * SUBG_RF DIO IRQ callback functions prototype + */ + +/*! + * @brief DIO IRQ callback : must be provided by SUBG_RF clients + * @param none + * @retval none + */ +void RadioOnDioIrq( RadioIrqMasks_t radioIrq ); + +/*! + * @brief Initialises the bus for SUBG_RF driver communication + * @param none + * @retval error code 0: OK, negatif: fail + */ +int32_t SUBGRF_Init( void (*RadioOnDioIrqCb) ( RadioIrqMasks_t radioIrq ) ); + + /*! + * @brief Gets the current Operation Mode of the Radio + * @param none + * @retval RadioOperatingModes_t last operating mode + */ +RadioOperatingModes_t SUBGRF_GetOperatingMode( void ); + + + +/*! + * @brief Saves the payload to be send in the radio buffer + * @param [in] payload A pointer to the payload + * @param [in] size The size of the payload + * @retval none + */ +void SUBGRF_SetPayload( uint8_t *payload, uint8_t size ); + +/*! + * @brief Reads the payload received. If the received payload is longer + * than maxSize, then the method returns 1 and do not set size and payload. + * @param [out] payload A pointer to a buffer into which the payload will be copied + * @param [out] size A pointer to the size of the payload received + * @param [in] maxSize The maximal size allowed to copy into the buffer + * @retval none + */ +uint8_t SUBGRF_GetPayload( uint8_t *payload, uint8_t *size, uint8_t maxSize ); + +/*! + * @brief Sends a payload + * @param [in] payload A pointer to the payload to send + * @param [in] size The size of the payload to send + * @param [in] timeout The timeout for Tx operation + * @retval none + */ +void SUBGRF_SendPayload( uint8_t *payload, uint8_t size, uint32_t timeout ); +/*! + * @brief Sets RF switch for TX & RX + * @param [in] power Low Power or High Power board + * @param [in] rxtx RX/TX mode + * @retval none + */ +void SUBGRF_SetSwitch (uint8_t power, RFState_t rxtx); + +/*! + * @brief Sets the Sync Word given by index used in GFSK + * @param [in] syncWord SyncWord bytes ( 8 bytes ) + * @retval status [0: OK, 1: NOK] + */ +uint8_t SUBGRF_SetSyncWord( uint8_t *syncWord ); + +/*! + * @brief Sets the Initial value for the LFSR used for the CRC calculation + * @param [in] seed Initial LFSR value ( 2 bytes ) + * @retval none + */ +void SUBGRF_SetCrcSeed( uint16_t seed ); + +/*! + * @brief Sets the seed used for the CRC calculation + * @param [in] seed The seed value + * @retval none + */ +void SUBGRF_SetCrcPolynomial( uint16_t polynomial ); + +/*! + * @brief Sets the Initial value of the LFSR used for the whitening in GFSK protocols + * @param [in] seed Initial LFSR value + * @retval none + */ +void SUBGRF_SetWhiteningSeed( uint16_t seed ); + +/*! + * @brief Gets a 32 bits random value generated by the radio + * @param none + * @remark The radio must be in reception mode before executing this function + * @retval randomValue 32 bits random value + */ +uint32_t SUBGRF_GetRandom( void ); + +/*! + * @brief Sets the radio in sleep mode + * @param [in] sleepConfig The sleep configuration describing data + * retention and RTC wake-up + * @retval none + */ +void SUBGRF_SetSleep( SleepParams_t sleepConfig ); + +/*! + * @brief Sets the radio in configuration mode + * @param [in] mode The standby mode to put the radio into + * @retval none + */ +void SUBGRF_SetStandby( RadioStandbyModes_t mode ); + +/*! + * @brief Sets the radio in FS mode + * @param none + * @retval none + */ +void SUBGRF_SetFs( void ); + +/*! + * @brief Sets the radio in transmission mode + * @param [in] timeout : Structure describing the transmission timeout value + * @retval none + */ +void SUBGRF_SetTx( uint32_t timeout ); + +/*! + * @brief Sets the radio in reception mode + * @param [in] timeout : Structure describing the reception timeout value + * @retval none + */ +void SUBGRF_SetRx( uint32_t timeout ); + +/*! + * @brief Sets the radio in reception mode with Boosted LNA gain + * @param [in] timeout Structure describing the reception timeout value + * @retval none + */ +void SUBGRF_SetRxBoosted( uint32_t timeout ); + +/*! + * @brief Sets the Rx duty cycle management parameters + * @param [in] rxTime Structure describing reception timeout value + * @param [in] sleepTime Structure describing sleep timeout value + * @retval none + */ +void SUBGRF_SetRxDutyCycle( uint32_t rxTime, uint32_t sleepTime ); + +/*! + * @brief Sets the radio in CAD mode + * @param none + * @retval none + */ +void SUBGRF_SetCad( void ); + +/*! + * @brief Sets the radio in continuous wave transmission mode + * @param none + * @retval none + */ +void SUBGRF_SetTxContinuousWave( void ); + +/*! + * @brief Sets the radio in continuous preamble transmission mode + * @param none + * @retval none + */ +void SUBGRF_SetTxInfinitePreamble( void ); + +/*! + * @brief Decide which interrupt will stop the internal radio rx timer. + * @param [in] enable [0: Timer stop after header/syncword detection + * 1: Timer stop after preamble detection] + * @retval none + */ +void SUBGRF_SetStopRxTimerOnPreambleDetect( bool enable ); + +/*! + * @brief Set the number of symbol the radio will wait to validate a reception + * @param [in] SymbNum number of LoRa symbols + * @retval none + */ +void SUBGRF_SetLoRaSymbNumTimeout( uint8_t SymbNum ); + +/*! + * @brief Sets the power regulators operating mode + * @param [in] void + * @retval none + */ +void SUBGRF_SetRegulatorMode( void ); + +/*! + * @brief Calibrates the given radio block + * @param [in] calibParam The description of blocks to be calibrated + * @retval none + */ +void SUBGRF_Calibrate( CalibrationParams_t calibParam ); + +/*! + * @brief Calibrates the Image rejection depending of the frequency + * @param [in] freq The operating frequency + * @retval none + */ +void SUBGRF_CalibrateImage( uint32_t freq ); + +/*! + * @brief Activate the extention of the timeout when long preamble is used + * @param [in] enable The radio will extend the timeout to cope with long preamble + * @retval none + */ +void SUBGRF_SetLongPreamble( uint8_t enable ); + +/*! + * @brief Sets the transmission parameters + * @param [in] paDutyCycle Duty Cycle for the PA + * @param [in] hpMax 0 for RFO_LP, 7 for RFO_HP + * @param [in] deviceSel 1 for RFO_LP, 0 for RFO_HP + * @param [in] paLut 0 for 14dBm LUT, 1 for 22dBm LUT + * @retval none + */ +void SUBGRF_SetPaConfig( uint8_t paDutyCycle, uint8_t hpMax, uint8_t deviceSel, uint8_t paLut ); + +/*! + * @brief Defines into which mode the chip goes after a TX / RX done + * @param [in] fallbackMode The mode in which the radio goes + * @retval none + */ +void SUBGRF_SetRxTxFallbackMode( uint8_t fallbackMode ); + + + +/*! + * @brief Sets the IRQ mask and DIO masks + * @param [in] irqMask General IRQ mask + * @param [in] dio1Mask DIO1 mask + * @param [in] dio2Mask DIO2 mask + * @param [in] dio3Mask DIO3 mask + * @retval none + */ +void SUBGRF_SetDioIrqParams( uint16_t irqMask, uint16_t dio1Mask, uint16_t dio2Mask, uint16_t dio3Mask ); + +/*! + * @brief Returns the current IRQ status + * @retval irqStatus IRQ status + * @retval none + */ +uint16_t SUBGRF_GetIrqStatus( void ); + + +/*! + * @brief Indicates if the Radio main clock is supplied from a tcxo + * @param [in] tcxoVoltage voltage used to control the TCXO + * @param [in] timeout time given to the TCXO to go to 32MHz + * @retval none + */ +void SUBGRF_SetTcxoMode( RadioTcxoCtrlVoltage_t tcxoVoltage, uint32_t timeout ); + +/*! + * @brief Sets the RF frequency + * @param [in] frequency RF frequency [Hz] + * @retval none + */ +void SUBGRF_SetRfFrequency( uint32_t frequency ); + +/*! + * @brief Sets the radio for the given protocol + * @param [in] packetType [PACKET_TYPE_GFSK, PACKET_TYPE_LORA] + * @remark This method has to be called before SetRfFrequency, + * SetModulationParams and SetPacketParams + * @retval none + */ +void SUBGRF_SetPacketType( RadioPacketTypes_t packetType ); + +/*! + * @brief Gets the current radio protocol + * @param none + * @retval packetType [PACKET_TYPE_GFSK, PACKET_TYPE_LORA] + */ +RadioPacketTypes_t SUBGRF_GetPacketType( void ); + +/*! + * @brief Sets the transmission parameters + * + * @param [in] paSelect RegPaConfig PaSelect value (RFO_LP, RFO_HP, etc) + * @param [in] power RF output power [-18..13] dBm + * @param [in] rampTime Transmission ramp up time + * @retval none + */ +void SUBGRF_SetTxParams( uint8_t paSelect, int8_t power, RadioRampTimes_t rampTime ); + + + +uint8_t SUBGRF_SetTxPower( int8_t power, RadioRampTimes_t rampTime ); +/*! + * @brief Set the modulation parameters + * @param [in] modParams : structure describing the modulation parameters + * @retval none + */ +void SUBGRF_SetModulationParams( ModulationParams_t *modParams ); + +/*! + * @brief Sets the packet parameters + * @param [in] packetParams: structure describing the packet parameters + * @retval none + */ +void SUBGRF_SetPacketParams( PacketParams_t *packetParams ); + +/*! + * @brief Sets the Channel Activity Detection (CAD) parameters + * + * @param [in] cadSymbolNum The number of symbol to use for CAD operations + * [LORA_CAD_01_SYMBOL, LORA_CAD_02_SYMBOL, + * LORA_CAD_04_SYMBOL, LORA_CAD_08_SYMBOL, + * LORA_CAD_16_SYMBOL] + * @param [in] cadDetPeak Limite for detection of SNR peak used in the CAD + * @param [in] cadDetMin Set the minimum symbol recognition for CAD + * @param [in] cadExitMode Operation to be done at the end of CAD action + * [LORA_CAD_ONLY, LORA_CAD_RX, LORA_CAD_LBT] + * @param [in] cadTimeout Defines the timeout value to abort the CAD activity + * @retval none + */ +void SUBGRF_SetCadParams( RadioLoRaCadSymbols_t cadSymbolNum, uint8_t cadDetPeak, uint8_t cadDetMin, RadioCadExitModes_t cadExitMode, uint32_t cadTimeout ); + +/*! + * @brief Sets the data buffer base address for transmission and reception + * @param [in] txBaseAddress Transmission base address + * @param [in] rxBaseAddress Reception base address + * @retval none + */ +void SUBGRF_SetBufferBaseAddress( uint8_t txBaseAddress, uint8_t rxBaseAddress ); + +/*! + * @brief Gets the current radio status + * @param none + * @retval Radio status + */ +RadioStatus_t SUBGRF_GetStatus( void ); + +/*! + * @brief Returns the instantaneous RSSI value for the last packet received + * @param none + * @retval rssiInst Instantaneous RSSI + */ +int8_t SUBGRF_GetRssiInst( void ); + +/*! + * @brief Gets the last received packet buffer status + * @param [out] payloadLength Last received packet payload length + * @param [out] rxStartBuffer Last received packet buffer address pointer + * @retval none + */ +void SUBGRF_GetRxBufferStatus( uint8_t *payloadLength, uint8_t *rxStartBuffer ); + +/*! + * @brief Gets the last received packet payload length + * @param [out] pktStatus A structure of packet status + * @retval none + */ +void SUBGRF_GetPacketStatus( PacketStatus_t *pktStatus ); + +/*! + * @brief Returns the possible system erros + * @param none + * @retval sysErrors Value representing the possible sys failures + */ +RadioError_t SUBGRF_GetDeviceErrors( void ); + +/*! + * @brief Clears the IRQs + * @param [in] irq IRQ(s) to be cleared + * @retval none + */ +void SUBGRF_ClearIrqStatus( uint16_t irq ); + +/*! + * @brief Service to get the radio wake-up time. + * @param none + * @retval Value of the radio wake-up time. + */ +uint32_t SUBGRF_GetRadioWakeUpTime( void ); + +/*! + * @brief Get Error + * @param none + * @retval sysErrors Value representing the possible sys failures + */ +RadioError_t SUBGRF_GetErrors( void ); + +/*! + * @brief Write radio register + * @param adress of the register + * @param data to write + * @retval none + */ +void SUBGRF_WriteRegister( uint16_t addr, uint8_t data ); + +/*! + * @brief Read radio register + * @param adress of the register + * @retval Data read + */ +uint8_t SUBGRF_ReadRegister( uint16_t addr ); + +/*! + * @brief Write several consecutive radio register + * @param adress of the 1st register to write + * @param array of datas to write + * @param amount of datas to write + * @retval none + */ +void SUBGRF_WriteRegisters( uint16_t addr, uint8_t *buffer, uint8_t size ); + +/*! + * @brief Read several consecutive radio register + * @param adress of the 1st register to read + * @param array of datas to read + * @param amount of datas to read + * @retval none + */ +void SUBGRF_ReadRegisters( uint16_t addr, uint8_t *buffer, uint8_t size ); +#endif // __RADIO_DRIVER_H__ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ + diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/stm32_radio_driver/radio_mw_version.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/stm32_radio_driver/radio_mw_version.h new file mode 100644 index 00000000..3946e834 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Middlewares/SubGHz_Phy/stm32_radio_driver/radio_mw_version.h @@ -0,0 +1,56 @@ +/****************************************************************************** + * @file radio_mw_version.h + * @author MCD Application Team + * @brief defines the radio driver version + ****************************************************************************** + * @attention + * + *

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

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ + +#ifndef __RADIO_MW_VERSION_H__ +#define __RADIO_MW_VERSION_H__ + +#ifdef __cplusplus + extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ + +/* __SUBGH_RADIO_TYPE: 0x01 WTM32WL + 0x61 sx126x + 0x72 sx1272 + 0x76 sx1276 */ + +#define __SUBGH_RADIO_TYPE (0x01U) /*!< [31:24] main version */ +#define __SUBGH_MW_VERSION_MAIN (0x01U) /*!< [23:16] main version */ +#define __SUBGH_MW_VERSION_SUB1 (0x00U) /*!< [15:8] sub1 version */ +#define __SUBGH_MW_VERSION_SUB2 (0x00U) /*!< [7:0] sub2 version */ +#define __SUBGH_MW_VERSION ((__SUBGH_RADIO_TYPE <<24)\ + |(__SUBGH_MW_VERSION_MAIN << 16)\ + |(__SUBGH_MW_VERSION_SUB1 << 8 )\ + |(__SUBGH_MW_VERSION_SUB2)) + +/* Exported types ------------------------------------------------------------*/ +/* External variables --------------------------------------------------------*/ +/* Exported macros -----------------------------------------------------------*/ +/* Exported functions ------------------------------------------------------- */ + +#ifdef __cplusplus +} +#endif + +#endif /*__RADIO_MW_VERSION_H__*/ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Src/adc.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Src/adc.c new file mode 100644 index 00000000..eed304a7 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Src/adc.c @@ -0,0 +1,142 @@ +/** + ****************************************************************************** + * @file adc.c + * @author MCD Application Team + * @brief configuration of the ADC instances + ****************************************************************************** + * @attention + * + *

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

+ * + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 + * + ****************************************************************************** + */ + +/* Includes ------------------------------------------------------------------*/ +#include "platform.h" +#include "adc.h" +#include "dma.h" + +#ifdef HAL_ADC_MODULE_ENABLED +/* USER CODE BEGIN 0 */ + +/* USER CODE END 0 */ +ADC_HandleTypeDef hadc1; +DMA_HandleTypeDef hdma_adc1; + +/* Private function prototypes -----------------------------------------------*/ +/* Functions Definition ------------------------------------------------------*/ + + +/** + * @brief ADC1 Initialization Function + * @param None + * @retval None + */ + +void MX_ADC1_Init(void) +{ + /* USER CODE BEGIN ADC1_Init 0 */ + + /* USER CODE END ADC1_Init 0 */ + + + + /* USER CODE BEGIN ADC1_Init 1 */ + + /* USER CODE END ADC1_Init 1 */ + /** Common config + */ + hadc1.Instance = ADC1; + hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2; + hadc1.Init.Resolution = ADC_RESOLUTION_12B; + hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT; + hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE; /* Sequencer set to fully configurable: only the rank 1 is enabled (no scan sequence on several ranks) */ + hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV; + hadc1.Init.ContinuousConvMode = DISABLE; /* Continuous mode disabled to have only 1 conversion at each conversion trig */ + hadc1.Init.NbrOfConversion = 1; /* Parameter discarded because sequencer is disabled. Parameter relevancy depending on setting of parameter "ScanConvMode" */ + hadc1.Init.DiscontinuousConvMode = DISABLE; /* Parameter discarded because sequencer is disabled */ + hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START; /* Software start to trig the 1st conversion manually, without external event */ + hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; /* Parameter discarded because trig of conversion by software start (no external event) */ + hadc1.Init.DMAContinuousRequests = DISABLE; /* ADC with DMA transfer: continuous requests to DMA disabled (default state) since DMA is not used in this example. */ + hadc1.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN; + hadc1.Init.SamplingTimeCommon1 = LL_ADC_SAMPLINGTIME_160CYCLES_5; + hadc1.Init.OversamplingMode = DISABLE; + hadc1.Init.TriggerFrequencyMode = ADC_TRIGGER_FREQ_HIGH; + if (HAL_ADC_Init(&hadc1) != HAL_OK) + { + Error_Handler(); + } + + if (HAL_ADCEx_Calibration_Start(&hadc1) != HAL_OK) + { + Error_Handler(); + } + /* USER CODE BEGIN ADC1_Init 2 */ + + /* USER CODE END ADC1_Init 2 */ +} + + +/** +* @brief ADC MSP Initialization +* This function configures the hardware resources used in this example +* @param hadc: ADC handle pointer +* @retval None +*/ +void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc) +{ + + /*##-1- Enable peripherals and GPIO Clocks #################################*/ + /* Enable clock of GPIO associated to the peripheral channels */ + /* Enable clock of ADCx peripheral (core clock) */ + __HAL_RCC_ADC_CLK_ENABLE(); + + /* Note: In case of usage of asynchronous clock for ADC, with ADC setting */ + /* "AdcHandle.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIVx", */ + /* the clock source has to be enabled at RCC top level using function */ + /* "HAL_RCCEx_PeriphCLKConfig()" or macro "__HAL_RCC_ADC_CONFIG()" */ + /* (refer to comments in driver file header). */ + + /*##-4- Configure the NVIC #################################################*/ + /* NVIC configuration for ADC interrupt */ + /* Priority: high-priority */ + +} + +/** +* @brief ADC MSP De-Initialization +* This function freeze the hardware resources used in this example +* @param hadc: ADC handle pointer +* @retval None +*/ +void HAL_ADC_MspDeInit(ADC_HandleTypeDef *hadc) +{ + if (hadc->Instance == ADC1) + { + /* USER CODE BEGIN ADC1_MspDeInit 0 */ + + /* USER CODE END ADC1_MspDeInit 0 */ + /* Peripheral clock disable */ + __HAL_RCC_ADC_CLK_DISABLE(); + + /**ADC1 GPIO Configuration + PC3 ------> ADC1_IN4 + */ + /* USER CODE BEGIN ADC1_MspDeInit 1 */ + + /* USER CODE END ADC1_MspDeInit 1 */ + } + +} +#endif +/* USER CODE BEGIN 1 */ + +/* USER CODE END 1 */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Src/adc_if.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Src/adc_if.c new file mode 100644 index 00000000..9e0e469b --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Src/adc_if.c @@ -0,0 +1,179 @@ +/** + ****************************************************************************** + * @file adc_if.c + * @author MCD Application Team + * @brief Read status related to the chip (battery level, VREF, chip temperature) + ****************************************************************************** + * @attention + * + *

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

+ * + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 + * + ****************************************************************************** +*/ + +/* Includes ------------------------------------------------------------------*/ +#include "adc_if.h" +#include "app_system.h" +#ifdef HAL_ADC_MODULE_ENABLED +#include "adc.h" +#endif + +/* External variables ---------------------------------------------------------*/ +#ifdef HAL_ADC_MODULE_ENABLED +extern ADC_HandleTypeDef hadc1; +#endif +/* Private typedef -----------------------------------------------------------*/ +/* Private define ------------------------------------------------------------*/ + +/*from dtatsheet*/ +#define TEMPSENSOR_TYP_CAL1_V (( int32_t) 760) /* Internal temperature sensor, parameter V30 (unit: mV). Refer to device datasheet for min/typ/max values. */ +#define TEMPSENSOR_TYP_AVGSLOPE (( int32_t) 2500) /* Internal temperature sensor, parameter Avg_Slope (unit: uV/DegCelsius). Refer to device datasheet for min/typ/max values. */ +#define TEMPSENSOR_CAL_VREF ((uint32_t) 3000) /* Vdda value with which temperature sensor has been calibrated in production (+-10 mV). */ + +/* Private macro -------------------------------------------------------------*/ +#define __LL_ADC_CALC_VREFANALOG_VOLTAGE_TEMPORARY(__VREFINT_ADC_DATA__,\ + __ADC_RESOLUTION__) \ + (((uint32_t)(*VREFINT_CAL_ADDR) * VREFINT_CAL_VREF) \ + / __LL_ADC_CONVERT_DATA_RESOLUTION((__VREFINT_ADC_DATA__), \ + (__ADC_RESOLUTION__), \ + LL_ADC_RESOLUTION_12B) \ + ) + +/* Private variables ---------------------------------------------------------*/ + + +/* Private function prototypes -----------------------------------------------*/ + +static uint32_t ADC_ReadChannels(uint32_t channel); + +/* Functions Definition ------------------------------------------------------*/ + + +/** + * @brief This function initializes the ADC + * @param none + * @retval none + */ +void SYS_InitMeasurement(void) +{ + hadc1.Instance = ADC1; +} + +void SYS_DeInitMeasurement(void) +{ +} + +int16_t SYS_GetTemperatureLevel(void) +{ + int16_t temperatureDegreeC = 6400; /*25degreeC in q7.8*/ + +#ifdef HAL_ADC_MODULE_ENABLED + uint32_t measuredLevel = 0; + uint16_t batteryLevelmV = SYS_GetBatteryLevel(); + + measuredLevel = ADC_ReadChannels(ADC_CHANNEL_TEMPSENSOR); + + /* #warning "to be replaced by __LL_ADC_CALC_TEMPERATURE when calibration data will be set in prod"*/ + temperatureDegreeC = __LL_ADC_CALC_TEMPERATURE_TYP_PARAMS(TEMPSENSOR_TYP_AVGSLOPE, + TEMPSENSOR_TYP_CAL1_V, + TEMPSENSOR_CAL1_TEMP, + batteryLevelmV, + measuredLevel, + LL_ADC_RESOLUTION_12B); + /*to be replaced */ + APP_LOG("temp= %d\n\r", temperatureDegreeC); + + /* from int16 to q8.7*/ + temperatureDegreeC <<= 8; + +#endif /* HAL_ADC_MODULE_ENABLED */ + + return (int16_t) temperatureDegreeC; +} +/** + * @brief This function return the battery level + * @param none + * @retval the battery level in mV + */ +uint16_t SYS_GetBatteryLevel(void) +{ + uint16_t batteryLevelmV = 3300; +#ifdef HAL_ADC_MODULE_ENABLED + uint32_t measuredLevel = 0; + + measuredLevel = ADC_ReadChannels(ADC_CHANNEL_VREFINT); + + if (measuredLevel == 0) + { + batteryLevelmV = 0; + } + else + { + batteryLevelmV = (3300 * 1510) / measuredLevel; + /* #warning "to be replaced by __LL_ADC_CALC_VREFANALOG_VOLTAGE when calibration data will be set in prod" */ + } +#endif /* HAL_ADC_MODULE_ENABLED */ + + return batteryLevelmV; +} + +/* Private function definition -----------------------------------------------*/ +/** + * @brief This function reads the ADC channel + * @param Channel + * @retval Value + */ +static uint32_t ADC_ReadChannels(uint32_t channel) +{ + uint32_t ADCxConvertedValues = 0; +#ifdef HAL_ADC_MODULE_ENABLED + ADC_ChannelConfTypeDef sConfig = {0}; + + MX_ADC1_Init(); + /** Configure Regular Channel */ + sConfig.Channel = channel; + sConfig.Rank = ADC_REGULAR_RANK_1; + sConfig.SamplingTime = ADC_SAMPLINGTIME_COMMON_1; + if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) + { + Error_Handler(); + } + + if (HAL_ADC_Start(&hadc1) != HAL_OK) + { + /* Start Error */ + Error_Handler(); + } + /** Wait for end of conversion */ + HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY); + + /* Workaround for tempsensor value*/ + if (channel == ADC_CHANNEL_TEMPSENSOR) + { + if (HAL_ADC_Start(&hadc1) != HAL_OK) + { + /* Start Error */ + Error_Handler(); + } + /** Wait for end of conversion */ + HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY); + } + + /** Wait for end of conversion */ + HAL_ADC_Stop(&hadc1) ; /* it calls also ADC_Disable() */ + + ADCxConvertedValues = HAL_ADC_GetValue(&hadc1); + + HAL_ADC_DeInit(&hadc1); + +#endif /* HAL_ADC_MODULE_ENABLED */ + return ADCxConvertedValues; +} + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Src/dma.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Src/dma.c new file mode 100644 index 00000000..4a190677 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Src/dma.c @@ -0,0 +1,56 @@ +/** + ****************************************************************************** + * @file dma.c + * @author MCD Application Team + * @brief configuration of all the requested memory to memory DMA transfers + ****************************************************************************** + * @attention + * + *

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

+ * + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 + * + ****************************************************************************** + */ +/* Includes ------------------------------------------------------------------*/ +#include "dma.h" + +/* USER CODE BEGIN 0 */ + +/* USER CODE END 0 */ + +/*----------------------------------------------------------------------------*/ +/* Configure DMA */ +/*----------------------------------------------------------------------------*/ + +/* USER CODE BEGIN 1 */ + +/* USER CODE END 1 */ + +/** + * Enable DMA controller clock + */ +void MX_DMA_Init(void) +{ + /* DMA controller clock enable */ + __HAL_RCC_DMA1_CLK_ENABLE(); +#ifdef HAL_ADC_MODULE_ENABLED + /* DMA interrupt init */ + /* DMA1_Channel1_IRQn interrupt configuration */ + HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 1, 0); + HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn); +#endif + /* DMA1_Channel7_IRQn interrupt configuration */ + HAL_NVIC_SetPriority(DMA1_Channel7_IRQn, 1, 0); + HAL_NVIC_EnableIRQ(DMA1_Channel7_IRQn); +} + +/* USER CODE BEGIN 2 */ + +/* USER CODE END 2 */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Src/mcu_init.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Src/mcu_init.c index 1c31ba63..71587d52 100644 --- a/board/RHF0M0E5_STM32WLE5xx/BSP/Src/mcu_init.c +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Src/mcu_init.c @@ -29,9 +29,9 @@ void board_init(void) { HAL_Init(); SystemClock_Config(); - MX_LPUART1_UART_Init(); - MX_USART1_UART_Init(); - MX_USART2_UART_Init(); + + SystemApp_Init(); + MX_LoRaWAN_Init(); } /** @@ -75,6 +75,7 @@ void SystemClock_Config(void) } } + /* USER CODE BEGIN 4 */ /* USER CODE END 4 */ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Src/rtc.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Src/rtc.c new file mode 100644 index 00000000..ef084bc2 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Src/rtc.c @@ -0,0 +1,183 @@ +/** + ****************************************************************************** + * @file rtc.c + * @author MCD Application Team + * @brief Configures the RTC instances + ****************************************************************************** + * @attention + * + *

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

+ * + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 + * + ****************************************************************************** + */ + +/* Includes ------------------------------------------------------------------*/ +#include "rtc.h" + + +/* External variables --------------------------------------------------------*/ +/* Private typedef -----------------------------------------------------------*/ +RCC_PeriphCLKInitTypeDef PeriphClkInitStruct; +RTC_TimeTypeDef RTC_TimeStructureGet; +RTC_DateTypeDef RTC_DateStructureGet; + +RTC_HandleTypeDef hrtc; +/* Private define ------------------------------------------------------------*/ +/* Private macro -------------------------------------------------------------*/ +/* Private variables ---------------------------------------------------------*/ +/* Private function prototypes -----------------------------------------------*/ +/* Exported variables --------------------------------------------------------*/ + +/* Exported functions --------------------------------------------------------*/ +void MX_RTC_Init(void) +{ + RTC_TimeTypeDef sTime = {0}; + RTC_DateTypeDef sDate = {0}; + RTC_AlarmTypeDef sAlarm = {0}; + + /** Initialize RTC Only + */ + hrtc.Instance = RTC; + + hrtc.Init.HourFormat = RTC_HOURFORMAT_24; + hrtc.Init.AsynchPrediv = RTC_PREDIV_A; + hrtc.Init.SynchPrediv = RTC_PREDIV_S; + hrtc.Init.OutPut = RTC_OUTPUT_DISABLE; + hrtc.Init.OutPutRemap = RTC_OUTPUT_REMAP_NONE; + hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH; + hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN; + + if (HAL_RTC_Init(&hrtc) != HAL_OK) + { + Error_Handler(); + } + + /* USER CODE BEGIN Check_RTC_BKUP */ + + /* USER CODE END Check_RTC_BKUP */ + + /**Initialize RTC and set the Time and Date + */ + sTime.Hours = 0; + sTime.Minutes = 0; + sTime.Seconds = 0; + sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE; + sTime.StoreOperation = RTC_STOREOPERATION_RESET; + if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN) != HAL_OK) + { + Error_Handler(); + } + + + sDate.WeekDay = RTC_WEEKDAY_MONDAY; + sDate.Month = RTC_MONTH_JANUARY; + sDate.Date = 1; + sDate.Year = 0; + + if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN) != HAL_OK) + { + Error_Handler(); + } + + /**Enable the Alarm A + */ + sAlarm.AlarmTime.Hours = 0; + sAlarm.AlarmTime.Minutes = 0; + sAlarm.AlarmTime.Seconds = 0; + sAlarm.AlarmTime.SubSeconds = 0; + sAlarm.AlarmTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE; + sAlarm.AlarmTime.StoreOperation = RTC_STOREOPERATION_RESET; + sAlarm.AlarmMask = RTC_ALARMMASK_NONE; + sAlarm.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_SS14_10; + sAlarm.AlarmDateWeekDaySel = RTC_ALARMDATEWEEKDAYSEL_DATE; + sAlarm.AlarmDateWeekDay = 30; + sAlarm.Alarm = RTC_ALARM_A; + if (HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, RTC_FORMAT_BIN) != HAL_OK) + { + Error_Handler(); + } +} + +/** + * @brief RTC MSP Initialization + * This function configures the hardware resources used in this example: + * - Peripheral's clock enable + * @param hrtc: RTC handle pointer + * @note Care must be taken when HAL_RCCEx_PeriphCLKConfig() is used to select + * the RTC clock source; in this case the Backup domain will be reset in + * order to modify the RTC Clock source, as consequence RTC registers (including + * the backup registers) and RCC_CSR register are set to their reset values. + * @retval none + */ +void HAL_RTC_MspInit(RTC_HandleTypeDef *rtcHandle) +{ + RCC_OscInitTypeDef RCC_OscInitStruct = {0}; + + if (rtcHandle->Instance == RTC) + { + /* USER CODE BEGIN RTC_MspInit 0 */ + + /* RCC configuration */ + RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSE; + RCC_OscInitStruct.LSEState = RCC_LSE_ON; + RCC_OscInitStruct.LSIState = RCC_LSI_OFF; + RCC_OscInitStruct.HSEState = RCC_HSE_OFF; + RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; + if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) + { + Error_Handler(); + } + + __HAL_RCC_RTCAPB_CLK_ENABLE() ; + /* USER CODE END RTC_MspInit 0 */ + + /* RTC clock enable */ + __HAL_RCC_RTC_ENABLE(); + + /* RTC interrupt Init */ + HAL_NVIC_SetPriority(RTC_Alarm_IRQn, 0, 0); + HAL_NVIC_EnableIRQ(RTC_Alarm_IRQn); + + /* USER CODE BEGIN RTC_MspInit 1 */ + + /* USER CODE END RTC_MspInit 1 */ + + } +} + +/** + * @brief RTC MSP DeInitialization + * @param hrtc: RTC handle pointer + * @retval none + */ +void HAL_RTC_MspDeInit(RTC_HandleTypeDef *rtcHandle) +{ + if (rtcHandle->Instance == RTC) + { + /* USER CODE BEGIN RTC_MspDeInit 0 */ + + /* USER CODE END RTC_MspDeInit 0 */ + /* Peripheral clock disable */ + __HAL_RCC_RTC_DISABLE(); + /* RTC interrupt Deinit */ + HAL_NVIC_DisableIRQ(RTC_Alarm_IRQn); + /* USER CODE BEGIN RTC_MspDeInit 1 */ + + + /* USER CODE END RTC_MspDeInit 1 */ + + } +} + +/* USER CODE BEGIN 1 */ + +/* USER CODE END 1 */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ + diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Src/rtc_if.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Src/rtc_if.c new file mode 100644 index 00000000..f2974443 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Src/rtc_if.c @@ -0,0 +1,541 @@ +/******************************************************************************* + * @file rtc_if.c + * @author MCD Application Team + * @brief Handles the RTC + ****************************************************************************** + * @attention + * + *

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

+ * + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 + * + ****************************************************************************** + */ + + +/* Includes ------------------------------------------------------------------*/ +#include +#include "platform.h" +#include "rtc_if.h" +#include "stm32_lpm.h" +#include "app_system.h" +#include "stm32wlxx_ll_rtc.h" +/* External variables ---------------------------------------------------------*/ +extern RTC_HandleTypeDef hrtc; +#define RtcHandle hrtc + +/* Private typedef -----------------------------------------------------------*/ +typedef struct +{ + uint32_t Rtc_Time; /* Reference time */ + + RTC_TimeTypeDef RTC_Calndr_Time; /* Reference time in calendar format */ + + RTC_DateTypeDef RTC_Calndr_Date; /* Reference date in calendar format */ + +} RtcTimerContext_t; + +/* Private define ------------------------------------------------------------*/ + +/* MCU Wake Up Time */ +#define MIN_ALARM_DELAY 3 /* in ticks */ + +/* subsecond number of bits */ /* Now defined in main.h via MX GUI */ +//#define RTC_N_PREDIV_S 10 +//#define RTC_PREDIV_S ((1<>RTC_N_PREDIV_S) + +#define COMMON_FACTOR 3 +#define CONV_NUMER (MSEC_NUMBER>>COMMON_FACTOR) +#define CONV_DENOM (1<<(RTC_N_PREDIV_S-COMMON_FACTOR)) + +#define DAYS_IN_LEAP_YEAR (uint32_t) 366 + +#define DAYS_IN_YEAR (uint32_t) 365 + +#define SECONDS_IN_1DAY (uint32_t) 86400 + +#define SECONDS_IN_1HOUR (uint32_t) 3600 + +#define SECONDS_IN_1MINUTE (uint32_t) 60 + +#define MINUTES_IN_1HOUR (uint32_t) 60 + +#define HOURS_IN_1DAY (uint32_t) 24 + +#define DAYS_IN_MONTH_CORRECTION_NORM ((uint32_t) 0x99AAA0 ) +#define DAYS_IN_MONTH_CORRECTION_LEAP ((uint32_t) 0x445550 ) + + +/* Calculates ceiling(X/N) */ +#define DIVC(X,N) ( ( (X) + (N) -1 ) / (N) ) + + +/* Private macro -------------------------------------------------------------*/ +/* Private variables ---------------------------------------------------------*/ +/*! + * \brief Indicates if the RTC is already Initalized or not + */ +static bool RTC_Initalized = false; + +/*! + * \brief compensates MCU wakeup time + */ + +static bool McuWakeUpTimeInitialized = false; + +/*! + * \brief compensates MCU wakeup time + */ + +static int16_t McuWakeUpTimeCal = 0; + +/*! + * Number of days in each month on a normal year + */ +static const uint8_t DaysInMonth[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + +/*! + * Number of days in each month on a leap year + */ +static const uint8_t DaysInMonthLeapYear[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + +static RTC_AlarmTypeDef RTC_AlarmStructure; + + +/*! + * Keep the value of the RTC timer when the RTC alarm is set + * Set with the RTC_SetTimerContext function + * Value is kept as a Reference to calculate alarm + */ +static RtcTimerContext_t RtcTimerContext; + + +/* Private function prototypes -----------------------------------------------*/ +/*! + * @brief start wake up alarm + * @note alarm in RtcTimerContext.Rtc_Time + timeoutValue + * @param timeoutValue in ticks + * @retval none + */ +static void RTC_StartWakeUpAlarm(uint32_t timeoutValue); + +/*! + * @brief get current time from calendar in ticks + * @param pointer to RTC_DateStruct + * @param pointer to RTC_TimeStruct + * @retval time in ticks + */ +static uint32_t RTC_GetCalendarValue(RTC_DateTypeDef *RTC_DateStruct, RTC_TimeTypeDef *RTC_TimeStruct); + +/* Exported variables ---------------------------------------------------------*/ +/* Exported functions ---------------------------------------------------------*/ +/** + * @brief This function configures the source of the time base. + * @brief don't enable systick + * @param TickPriority: Tick interrupt priority. + * @retval HAL status + */ +HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority) +{ + /*Initialize the RTC services */ + RTC_Init(); + return HAL_OK; +} + +/** + * @brief Provide a tick value in millisecond measured using RTC + * @note This function overwrites the __weak one from HAL + * @retval tick value + */ +uint32_t HAL_GetTick(void) +{ + if (RTC_Initalized == false) + { + return 0; /* HAL_GetTick() based on RTC cannot be used until RTC is initialised */ + } + else + { + return RTC_GetTimerValue(); + } +} + +/** + * @brief This function provides delay (in ms) + * @param Delay: specifies the delay time length, in milliseconds. + * @retval None + */ +void HAL_Delay(__IO uint32_t Delay) +{ + RTC_DelayMs(Delay); /* based on RTC */ +} + + + +/** + * @brief Alarm A callback. + * @param RtcHandle: RTC handle + * @retval None + */ +void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *RtcHandle) +{ + //UTIL_TIMER_IRQ_Handler( ); +} + + + +void RTC_Init(void) +{ + RCC_PeriphCLKInitTypeDef PeriphClkInitStruct; + + if (RTC_Initalized == false) + { + /*##-1- Configue the RTC clock soucre ######################################*/ + + /* Select LSE as RTC clock source */ + PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC; + PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE; + if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) + { + /* Initialization Error */ + while (1); + } + + MX_RTC_Init(); + +#if defined (LPM_STOP_MODE_DISABLE) && (LPM_STOP_MODE_DISABLE == 0) /* LOW_POWER enabled */ + /* enable RTC ALARM EXTI (AIEC) line */ + LL_EXTI_EnableIT_0_31(LL_EXTI_LINE_17); + LL_EXTI_EnableRisingTrig_0_31(LL_EXTI_LINE_17); +#elif !defined (LPM_STOP_MODE_DISABLE) + #error LPM_STOP_MODE_DISABLE not defined +#endif + + /*Enable Direct Read of the calendar registers (not through Shadow) */ + HAL_RTCEx_EnableBypassShadow(&RtcHandle); + + /** Configure the Alarm A */ + HAL_RTC_DeactivateAlarm(&RtcHandle, RTC_ALARM_A); + + RTC_SetTimerContext(); + RTC_Initalized = true; + } +} + + +void RTC_setMcuWakeUpTime(void) +{ + RTC_TimeTypeDef RTC_TimeStruct; + RTC_DateTypeDef RTC_DateStruct; + + uint32_t now, hit; + int16_t McuWakeUpTime; + + if ((McuWakeUpTimeInitialized == false) && + (HAL_NVIC_GetPendingIRQ(RTC_Alarm_IRQn) == 1)) + { + /* warning: works ok if now is below 30 days + it is ok since it's done once at first alarm wake-up*/ + McuWakeUpTimeInitialized = true; + now = RTC_GetCalendarValue(&RTC_DateStruct, &RTC_TimeStruct); + + + HAL_RTC_GetAlarm(&RtcHandle, &RTC_AlarmStructure, RTC_ALARM_A, RTC_FORMAT_BIN); + hit = RTC_AlarmStructure.AlarmTime.Seconds + + 60 * (RTC_AlarmStructure.AlarmTime.Minutes + + 60 * (RTC_AlarmStructure.AlarmTime.Hours + + 24 * (RTC_AlarmStructure.AlarmDateWeekDay))); + hit = (hit << RTC_N_PREDIV_S) + (RTC_PREDIV_S - RTC_AlarmStructure.AlarmTime.SubSeconds); + + McuWakeUpTime = (int16_t)((now - hit)); + McuWakeUpTimeCal += McuWakeUpTime; + APP_LOG("Cal=%d, %d\n\r", McuWakeUpTimeCal, McuWakeUpTime); + } +} + +int16_t RTC_getMcuWakeUpTime(void) +{ + return McuWakeUpTimeCal; +} + + +uint32_t RTC_GetMinimumTimeout(void) +{ + return (MIN_ALARM_DELAY); +} + + +uint32_t RTC_ms2Tick(uint32_t timeMicroSec) +{ + /*return( ( timeMicroSec / RTC_ALARM_TIME_BASE ) ); */ + return (uint32_t)((((uint64_t)timeMicroSec) * CONV_DENOM) / CONV_NUMER); +} + + +uint32_t RTC_Tick2ms(uint32_t tick) +{ + /*return( ( timeMicroSec * RTC_ALARM_TIME_BASE ) ); */ + return (((uint64_t)(tick) * CONV_NUMER) / CONV_DENOM); +} + +uint32_t RTC_GetTimerElapsedTime(void) +{ + RTC_TimeTypeDef RTC_TimeStruct; + RTC_DateTypeDef RTC_DateStruct; + + uint32_t CalendarValue = RTC_GetCalendarValue(&RTC_DateStruct, &RTC_TimeStruct); + + return ((uint32_t)(CalendarValue - RtcTimerContext.Rtc_Time)); +} + +uint32_t RTC_GetTimerValue(void) +{ + RTC_TimeTypeDef RTC_TimeStruct; + RTC_DateTypeDef RTC_DateStruct; + + uint32_t CalendarValue = (uint32_t) RTC_GetCalendarValue(&RTC_DateStruct, &RTC_TimeStruct); + + return (CalendarValue); +} + + + + +void RTC_DelayMs(uint32_t delay) +{ + uint32_t delayValue = 0; + uint32_t timeout = 0; + + delayValue = RTC_ms2Tick(delay); + + /* Wait delay ms */ + timeout = RTC_GetTimerValue(); + while (((RTC_GetTimerValue() - timeout)) < delayValue) + { + __NOP(); + } +} + + +uint32_t RTC_SetTimerContext(void) +{ + RtcTimerContext.Rtc_Time = RTC_GetCalendarValue(&RtcTimerContext.RTC_Calndr_Date, &RtcTimerContext.RTC_Calndr_Time); + return (uint32_t) RtcTimerContext.Rtc_Time; +} + + +uint32_t RTC_GetTimerContext(void) +{ + return (uint32_t) RtcTimerContext.Rtc_Time; +} +/* Private functions ---------------------------------------------------------*/ + +/*! + * @brief start wake up alarm + * @note alarm in RtcTimerContext.Rtc_Time + timeoutValue + * @param timeoutValue in ticks + * @retval none + */ +static void RTC_StartWakeUpAlarm(uint32_t timeoutValue) +{ + uint16_t rtcAlarmSubSeconds = 0; + uint16_t rtcAlarmSeconds = 0; + uint16_t rtcAlarmMinutes = 0; + uint16_t rtcAlarmHours = 0; + uint16_t rtcAlarmDays = 0; + RTC_TimeTypeDef RTC_TimeStruct = RtcTimerContext.RTC_Calndr_Time; + RTC_DateTypeDef RTC_DateStruct = RtcTimerContext.RTC_Calndr_Date; + + //RTC_StopAlarm(); + + + /*reverse counter */ + rtcAlarmSubSeconds = RTC_PREDIV_S - RTC_TimeStruct.SubSeconds; + rtcAlarmSubSeconds += (timeoutValue & RTC_PREDIV_S); + /* convert timeout to seconds */ + timeoutValue >>= RTC_N_PREDIV_S; /* convert timeout in seconds */ + + /*convert microsecs to RTC format and add to 'Now' */ + rtcAlarmDays = RTC_DateStruct.Date; + while (timeoutValue >= SECONDS_IN_1DAY) + { + timeoutValue -= SECONDS_IN_1DAY; + rtcAlarmDays++; + } + + /* calc hours */ + rtcAlarmHours = RTC_TimeStruct.Hours; + while (timeoutValue >= SECONDS_IN_1HOUR) + { + timeoutValue -= SECONDS_IN_1HOUR; + rtcAlarmHours++; + } + + /* calc minutes */ + rtcAlarmMinutes = RTC_TimeStruct.Minutes; + while (timeoutValue >= SECONDS_IN_1MINUTE) + { + timeoutValue -= SECONDS_IN_1MINUTE; + rtcAlarmMinutes++; + } + + /* calc seconds */ + rtcAlarmSeconds = RTC_TimeStruct.Seconds + timeoutValue; + + /***** correct for modulo********/ + while (rtcAlarmSubSeconds >= (RTC_PREDIV_S + 1)) + { + rtcAlarmSubSeconds -= (RTC_PREDIV_S + 1); + rtcAlarmSeconds++; + } + + while (rtcAlarmSeconds >= SECONDS_IN_1MINUTE) + { + rtcAlarmSeconds -= SECONDS_IN_1MINUTE; + rtcAlarmMinutes++; + } + + while (rtcAlarmMinutes >= MINUTES_IN_1HOUR) + { + rtcAlarmMinutes -= MINUTES_IN_1HOUR; + rtcAlarmHours++; + } + + while (rtcAlarmHours >= HOURS_IN_1DAY) + { + rtcAlarmHours -= HOURS_IN_1DAY; + rtcAlarmDays++; + } + + if (RTC_DateStruct.Year % 4 == 0) + { + if (rtcAlarmDays > DaysInMonthLeapYear[ RTC_DateStruct.Month - 1 ]) + { + rtcAlarmDays = rtcAlarmDays % DaysInMonthLeapYear[ RTC_DateStruct.Month - 1 ]; + } + } + else + { + if (rtcAlarmDays > DaysInMonth[ RTC_DateStruct.Month - 1 ]) + { + rtcAlarmDays = rtcAlarmDays % DaysInMonth[ RTC_DateStruct.Month - 1 ]; + } + } + + /* Set RTC_AlarmStructure with calculated values*/ + RTC_AlarmStructure.AlarmTime.SubSeconds = RTC_PREDIV_S - rtcAlarmSubSeconds; + RTC_AlarmStructure.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK; + RTC_AlarmStructure.AlarmTime.Seconds = rtcAlarmSeconds; + RTC_AlarmStructure.AlarmTime.Minutes = rtcAlarmMinutes; + RTC_AlarmStructure.AlarmTime.Hours = rtcAlarmHours; + RTC_AlarmStructure.AlarmDateWeekDay = (uint8_t)rtcAlarmDays; + RTC_AlarmStructure.AlarmTime.TimeFormat = RTC_TimeStruct.TimeFormat; + RTC_AlarmStructure.AlarmDateWeekDaySel = RTC_ALARMDATEWEEKDAYSEL_DATE; + RTC_AlarmStructure.AlarmMask = RTC_ALARMMASK_NONE; + RTC_AlarmStructure.Alarm = RTC_ALARM_A; + RTC_AlarmStructure.AlarmTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE; + RTC_AlarmStructure.AlarmTime.StoreOperation = RTC_STOREOPERATION_RESET; + + /* Set RTC_Alarm */ + HAL_RTC_SetAlarm_IT(&RtcHandle, &RTC_AlarmStructure, RTC_FORMAT_BIN); +} + + +/*! + * @brief get current time from calendar in ticks + * @param pointer to RTC_DateStruct + * @param pointer to RTC_TimeStruct + * @retval time in ticks + */ +static uint32_t RTC_GetCalendarValue(RTC_DateTypeDef *RTC_DateStruct, RTC_TimeTypeDef *RTC_TimeStruct) +{ + uint32_t calendarValue = 0; + uint32_t first_read; + uint32_t correction; + + /* Get Time and Date*/ + HAL_RTC_GetTime(&RtcHandle, RTC_TimeStruct, RTC_FORMAT_BIN); + + + /* make sure it is correct due to asynchronus nature of RTC*/ + do + { + first_read = LL_RTC_TIME_GetSubSecond(RTC); + HAL_RTC_GetDate(&RtcHandle, RTC_DateStruct, RTC_FORMAT_BIN); + HAL_RTC_GetTime(&RtcHandle, RTC_TimeStruct, RTC_FORMAT_BIN); + + } + while (first_read != LL_RTC_TIME_GetSubSecond(RTC)); + + /* calculte amount of elapsed days since 01/01/2000 */ + calendarValue = DIVC((DAYS_IN_YEAR * 3 + DAYS_IN_LEAP_YEAR) * RTC_DateStruct->Year, 4); + + correction = ((RTC_DateStruct->Year % 4) == 0) ? DAYS_IN_MONTH_CORRECTION_LEAP : DAYS_IN_MONTH_CORRECTION_NORM ; + + calendarValue += (DIVC((RTC_DateStruct->Month - 1) * (30 + 31), 2) - (((correction >> ((RTC_DateStruct->Month - 1) * 2)) & 0x3))); + + calendarValue += (RTC_DateStruct->Date - 1); + + /* convert from days to seconds */ + calendarValue *= SECONDS_IN_1DAY; + + calendarValue += ((uint32_t)RTC_TimeStruct->Seconds + + ((uint32_t)RTC_TimeStruct->Minutes * SECONDS_IN_1MINUTE) + + ((uint32_t)RTC_TimeStruct->Hours * SECONDS_IN_1HOUR)) ; + + calendarValue = (calendarValue << RTC_N_PREDIV_S) + (RTC_PREDIV_S - RTC_TimeStruct->SubSeconds); + + return (calendarValue); +} + +/*! + * \brief Get system time + * \param [IN] pointer to ms + * + * \return uint32_t seconds + */ +uint32_t HW_RTC_GetCalendarTime(uint16_t *mSeconds) +{ + RTC_TimeTypeDef RTC_TimeStruct ; + RTC_DateTypeDef RTC_DateStruct; + uint32_t ticks; + + uint64_t calendarValue = RTC_GetCalendarValue(&RTC_DateStruct, &RTC_TimeStruct); + + uint32_t seconds = (uint32_t)(calendarValue >> RTC_N_PREDIV_S); + + ticks = (uint32_t) calendarValue & RTC_PREDIV_S; + + *mSeconds = RTC_Tick2ms(ticks); + + return seconds; +} + +void HW_RTC_BKUPWrite(uint32_t Data0, uint32_t Data1) +{ + HAL_RTCEx_BKUPWrite(&RtcHandle, RTC_BKP_DR0, Data0); + HAL_RTCEx_BKUPWrite(&RtcHandle, RTC_BKP_DR1, Data1); +} + +void HW_RTC_BKUPRead(uint32_t *Data0, uint32_t *Data1) +{ + *Data0 = HAL_RTCEx_BKUPRead(&RtcHandle, RTC_BKP_DR0); + *Data1 = HAL_RTCEx_BKUPRead(&RtcHandle, RTC_BKP_DR1); +} + + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ + diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Src/stm32_lpm_if.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Src/stm32_lpm_if.c new file mode 100644 index 00000000..2db1beb5 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Src/stm32_lpm_if.c @@ -0,0 +1,133 @@ +/******************************************************************************* + * @file stm32_lpm_if.c + * @author MCD Application Team + * @brief Low layer function to enter/exit low power modes (stop, sleep) + ****************************************************************************** + * @attention + * + *

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

+ * + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 + * + ****************************************************************************** + */ + +/* Includes ------------------------------------------------------------------*/ +#include "stm32_lpm_if.h" +#include "usart_if.h" + +/* External variables --------------------------------------------------------*/ +extern UART_HandleTypeDef huart1; + +/* Private typedef -----------------------------------------------------------*/ + +const struct UTIL_LPM_Driver_s UTIL_PowerDriver = +{ + PWR_EnterSleepMode, + PWR_ExitSleepMode, + + PWR_EnterStopMode, + PWR_ExitStopMode, + + PWR_EnterOffMode, + PWR_ExitOffMode, +}; + +/* Private define ------------------------------------------------------------*/ +/* Private macro -------------------------------------------------------------*/ +/* Private variables ---------------------------------------------------------*/ +/* Private function prototypes -----------------------------------------------*/ +/* Exported variables --------------------------------------------------------*/ +/* Exported functions --------------------------------------------------------*/ + +/** + * @brief Enters Low Power Off Mode + * @param none + * @retval none + */ +void PWR_EnterOffMode(void) +{ +} +/** + * @brief Exits Low Power Off Mode + * @param none + * @retval none + */ +void PWR_ExitOffMode(void) +{ +} + +/** + * @brief Enters Low Power Stop Mode + * @note ARM exists the function when waking up + * @param none + * @retval none + */ +void PWR_EnterStopMode(void) +{ + + /* USER CODE BEGIN 1 */ + /* Clear Status Flag before entering STOP/STANDBY Mode */ + LL_PWR_ClearFlag_C1STOP_C1STB(); + /* USER CODE END 1 */ + + /* USER CODE BEGIN 2 */ + HAL_PWREx_EnterSTOP2Mode(PWR_STOPENTRY_WFI); + /* USER CODE END 2 */ +} +/** + * @brief Exits Low Power Stop Mode + * @note Enable the pll at 32MHz + * @param none + * @retval none + */ +void PWR_ExitStopMode(void) +{ + /* USER CODE BEGIN 4 */ + /*Not retained periph: + ADC interface + DAC interface USARTx, TIMx, i2Cx, SPIx + SRAM ctrls, DMAx, DMAMux, AES, RNG, HSEM */ + /*to reenable lost DMA settings*/ + if (HAL_UART_Init(&huart1) != HAL_OK) + { + Error_Handler(); + } + + /* USER CODE END 4 */ +} + +/** + * @brief Enters Low Power Sleep Mode + * @note ARM exits the function when waking up + * @param none + * @retval none + */ +void PWR_EnterSleepMode(void) +{ + /* Suspend sysTick */ + HAL_SuspendTick(); + + /* USER CODE BEGIN 5 */ + HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI); + /* USER CODE END 5 */ +} + +/** + * @brief Enters Low Power Sleep Mode + * @note ARM exits the function when waking up + * @param none + * @retval none + */ +void PWR_ExitSleepMode(void) +{ + /* Suspend sysTick */ + HAL_ResumeTick(); + +} +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ + diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Src/stm32wlxx_it.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Src/stm32wlxx_it.c index 18226557..a57a0b1f 100644 --- a/board/RHF0M0E5_STM32WLE5xx/BSP/Src/stm32wlxx_it.c +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Src/stm32wlxx_it.c @@ -61,9 +61,8 @@ /* USER CODE END 0 */ /* External variables --------------------------------------------------------*/ -extern UART_HandleTypeDef hlpuart1; extern UART_HandleTypeDef huart1; -extern UART_HandleTypeDef huart2; +extern SUBGHZ_HandleTypeDef hsubghz; /* USER CODE BEGIN EV */ /* USER CODE END EV */ @@ -221,32 +220,9 @@ void USART1_IRQHandler(void) /* USER CODE END USART2_IRQn 1 */ } -/** - * @brief This function handles USART3 global interrupt. - */ -void USART2_IRQHandler(void) +void Radio_IRQHandler(void) { - /* USER CODE BEGIN USART2_IRQn 0 */ - - /* USER CODE END USART2_IRQn 0 */ - HAL_UART_IRQHandler(&huart2); - /* USER CODE BEGIN USART2_IRQn 1 */ - - /* USER CODE END USART2_IRQn 1 */ -} - -/** - * @brief This function handles LPUART1 global interrupt. - */ -void LPUART1_IRQHandler(void) -{ - /* USER CODE BEGIN LPUART1_IRQn 0 */ - - /* USER CODE END LPUART1_IRQn 0 */ - HAL_UART_IRQHandler(&hlpuart1); - /* USER CODE BEGIN LPUART1_IRQn 1 */ - - /* USER CODE END LPUART1_IRQn 1 */ + HAL_SUBGHZ_IRQHandler(&hsubghz); } /* USER CODE BEGIN 1 */ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Src/usart.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Src/usart.c index e44cc523..e38935f5 100644 --- a/board/RHF0M0E5_STM32WLE5xx/BSP/Src/usart.c +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Src/usart.c @@ -1,250 +1,80 @@ /** ****************************************************************************** - * File Name : USART.c - * Description : This file provides code for the configuration - * of the USART instances. + * @file usart.c + * @author MCD Application Team + * @brief configuration of the USART instances ****************************************************************************** * @attention * *

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

* - * This software component is licensed by ST under BSD 3-Clause license, - * the "License"; You may not use this file except in compliance with the - * License. You may obtain a copy of the License at: - * opensource.org/licenses/BSD-3-Clause + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 * ****************************************************************************** */ /* Includes ------------------------------------------------------------------*/ +#include "main.h" #include "usart.h" -/* USER CODE BEGIN 0 */ +#include "stdio.h" +/* External variables --------------------------------------------------------*/ +/* Private typedef -----------------------------------------------------------*/ -/* USER CODE END 0 */ - -UART_HandleTypeDef hlpuart1; UART_HandleTypeDef huart1; -UART_HandleTypeDef huart2; -/* LPUART1 init function */ - -void MX_LPUART1_UART_Init(void) -{ - - hlpuart1.Instance = LPUART1; - hlpuart1.Init.BaudRate = 115200; - hlpuart1.Init.WordLength = UART_WORDLENGTH_8B; - hlpuart1.Init.StopBits = UART_STOPBITS_1; - hlpuart1.Init.Parity = UART_PARITY_NONE; - hlpuart1.Init.Mode = UART_MODE_TX_RX; - hlpuart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; - hlpuart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE; - hlpuart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT; - if (HAL_UART_Init(&hlpuart1) != HAL_OK) - { - Error_Handler(); - } -} -/* USART1 init function */ +/* Private define ------------------------------------------------------------*/ +/* Private macro -------------------------------------------------------------*/ +/* Private variables ---------------------------------------------------------*/ +/* Private function prototypes -----------------------------------------------*/ +/* Functions Definition ------------------------------------------------------*/ void MX_USART1_UART_Init(void) { - huart1.Instance = USART1; - huart1.Init.BaudRate = 115200; + huart1.Instance = USART1; + huart1.Init.BaudRate = 115200; huart1.Init.WordLength = UART_WORDLENGTH_8B; - huart1.Init.StopBits = UART_STOPBITS_1; - huart1.Init.Parity = UART_PARITY_NONE; - huart1.Init.Mode = UART_MODE_TX_RX; - huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; + huart1.Init.StopBits = UART_STOPBITS_1; + huart1.Init.Parity = UART_PARITY_NONE; + huart1.Init.Mode = UART_MODE_TX_RX; + huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart1.Init.OverSampling = UART_OVERSAMPLING_16; - huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE; - huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT; - if (HAL_UART_Init(&huart1) != HAL_OK) + + if(HAL_UART_Init(&huart1) != HAL_OK) { Error_Handler(); } } -/* USART2 init function */ -void MX_USART2_UART_Init(void) +void HAL_UART_MspInit(UART_HandleTypeDef *uartHandle) { + GPIO_InitTypeDef GPIO_InitStruct = {0}; + RCC_PeriphCLKInitTypeDef PeriphClkInit = {0}; - huart2.Instance = USART2; - huart2.Init.BaudRate = 115200; - huart2.Init.WordLength = UART_WORDLENGTH_8B; - huart2.Init.StopBits = UART_STOPBITS_1; - huart2.Init.Parity = UART_PARITY_NONE; - huart2.Init.Mode = UART_MODE_TX_RX; - huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE; - huart2.Init.OverSampling = UART_OVERSAMPLING_16; - huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE; - huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT; - if (HAL_UART_Init(&huart2) != HAL_OK) - { - Error_Handler(); - } + /* Enable GPIO TX/RX clock */ + USARTx_TX_GPIO_CLK_ENABLE(); + USARTx_RX_GPIO_CLK_ENABLE(); + USARTx_CLK_ENABLE(); + + GPIO_InitStruct.Pin = USARTx_TX_Pin; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_PULLUP; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + GPIO_InitStruct.Alternate = USARTx_TX_AF; + HAL_GPIO_Init(USARTx_TX_GPIO_Port, &GPIO_InitStruct); + + GPIO_InitStruct.Pin = USARTx_RX_Pin; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + GPIO_InitStruct.Alternate = USARTx_RX_AF; + HAL_GPIO_Init(USARTx_RX_GPIO_Port, &GPIO_InitStruct); } - -void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle) -{ - - GPIO_InitTypeDef GPIO_InitStruct = {0}; - if(uartHandle->Instance==LPUART1) - { - /* USER CODE BEGIN LPUART1_MspInit 0 */ - - /* USER CODE END LPUART1_MspInit 0 */ - /* LPUART1 clock enable */ - __HAL_RCC_LPUART1_CLK_ENABLE(); - - __HAL_RCC_GPIOC_CLK_ENABLE(); - /**LPUART1 GPIO Configuration - PC0 ------> LPUART1_RX - PC1 ------> LPUART1_TX - */ - GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1; - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - GPIO_InitStruct.Pull = GPIO_NOPULL; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; - GPIO_InitStruct.Alternate = GPIO_AF8_LPUART1; - HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); - - /* LPUART1 interrupt Init */ - HAL_NVIC_SetPriority(LPUART1_IRQn, 0, 0); - HAL_NVIC_EnableIRQ(LPUART1_IRQn); - /* USER CODE BEGIN LPUART1_MspInit 1 */ - - /* USER CODE END LPUART1_MspInit 1 */ - } - else if(uartHandle->Instance==USART1) - { - /* USER CODE BEGIN USART1_MspInit 0 */ - - /* USER CODE END USART1_MspInit 0 */ - /* USART1 clock enable */ - __HAL_RCC_USART1_CLK_ENABLE(); - - __HAL_RCC_GPIOB_CLK_ENABLE(); - /**USART1 GPIO Configuration - PB6 ------> USART1_TX - PB7 ------> USART1_RX - */ - GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7; - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - GPIO_InitStruct.Pull = GPIO_NOPULL; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; - GPIO_InitStruct.Alternate = GPIO_AF7_USART1; - HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); - - /* USART1 interrupt Init */ - HAL_NVIC_SetPriority(USART1_IRQn, 0, 0); - HAL_NVIC_EnableIRQ(USART1_IRQn); - /* USER CODE BEGIN USART1_MspInit 1 */ - - /* USER CODE END USART1_MspInit 1 */ - } - else if(uartHandle->Instance==USART2) - { - /* USER CODE BEGIN USART2_MspInit 0 */ - - /* USER CODE END USART2_MspInit 0 */ - /* USART2 clock enable */ - __HAL_RCC_USART2_CLK_ENABLE(); - - __HAL_RCC_GPIOA_CLK_ENABLE(); - /**USART2 GPIO Configuration - PA2 ------> USART2_TX - PA3 ------> USART2_RX - */ - GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_3; - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - GPIO_InitStruct.Pull = GPIO_NOPULL; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; - GPIO_InitStruct.Alternate = GPIO_AF7_USART2; - HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); - - /* USART2 interrupt Init */ - HAL_NVIC_SetPriority(USART2_IRQn, 0, 0); - HAL_NVIC_EnableIRQ(USART2_IRQn); - /* USER CODE BEGIN USART2_MspInit 1 */ - - /* USER CODE END USART2_MspInit 1 */ - } - -} - -void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle) -{ - - if(uartHandle->Instance==LPUART1) - { - /* USER CODE BEGIN LPUART1_MspDeInit 0 */ - - /* USER CODE END LPUART1_MspDeInit 0 */ - /* Peripheral clock disable */ - __HAL_RCC_LPUART1_CLK_DISABLE(); - - /**LPUART1 GPIO Configuration - PC0 ------> LPUART1_RX - PC1 ------> LPUART1_TX - */ - HAL_GPIO_DeInit(GPIOC, GPIO_PIN_0|GPIO_PIN_1); - - /* LPUART1 interrupt Deinit */ - HAL_NVIC_DisableIRQ(LPUART1_IRQn); - /* USER CODE BEGIN LPUART1_MspDeInit 1 */ - - /* USER CODE END LPUART1_MspDeInit 1 */ - } - else if(uartHandle->Instance==USART1) - { - /* USER CODE BEGIN USART1_MspDeInit 0 */ - - /* USER CODE END USART1_MspDeInit 0 */ - /* Peripheral clock disable */ - __HAL_RCC_USART1_CLK_DISABLE(); - - /**USART3 GPIO Configuration - PB6 ------> USART1_TX - PB7 ------> USART1_RX - */ - HAL_GPIO_DeInit(GPIOB, GPIO_PIN_6|GPIO_PIN_7); - - /* USART3 interrupt Deinit */ - HAL_NVIC_DisableIRQ(USART1_IRQn); - /* USER CODE BEGIN USART1_MspDeInit 1 */ - - /* USER CODE END USART1_MspDeInit 1 */ - } - else if(uartHandle->Instance==USART2) - { - /* USER CODE BEGIN USART2_MspDeInit 0 */ - - /* USER CODE END USART2_MspDeInit 0 */ - /* Peripheral clock disable */ - __HAL_RCC_USART2_CLK_DISABLE(); - - /**USART2 GPIO Configuration - PA2 ------> USART2_TX - PA3 ------> USART2_RX - */ - HAL_GPIO_DeInit(GPIOA, GPIO_PIN_2|GPIO_PIN_3); - - /* USART2 interrupt Deinit */ - HAL_NVIC_DisableIRQ(USART2_IRQn); - /* USER CODE BEGIN USART2_MspDeInit 1 */ - - /* USER CODE END USART2_MspDeInit 1 */ - } -} - -/* USER CODE BEGIN 1 */ - -/* USER CODE END 1 */ - /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Src/usart_if.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Src/usart_if.c new file mode 100644 index 00000000..81381713 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Src/usart_if.c @@ -0,0 +1,83 @@ +/** + ****************************************************************************** + * @file : usart_if.c + * @author MCD Application Team + * @brief interfaces UART MX driver for hyperterminal communication + ****************************************************************************** + * @attention + * + *

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

+ * + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 + * + ****************************************************************************** + */ + +/* Includes ------------------------------------------------------------------*/ +#include "usart_if.h" + +/* Private typedef -----------------------------------------------------------*/ + +/* Private define ------------------------------------------------------------*/ +/* Private macro -------------------------------------------------------------*/ +/* Private variables ---------------------------------------------------------*/ +/* Uart Handle */ +extern UART_HandleTypeDef huart1; +#define UartHandle huart1 +uint8_t charRx; + +/* Private function prototypes -----------------------------------------------*/ +/** +* @brief TX complete callback +* @param None +* @return None +*/ +static void (*TxCpltCallback)(void*); +/** +* @brief RX complete callback +* @param char sent by user +* @return None +*/ +static void (*RxCpltCallback)(uint8_t *rxChar, uint16_t size, uint8_t error); + +/* Functions Definition ------------------------------------------------------*/ + +uint8_t vcom_Init(void (*cb)(void *)) +{ + TxCpltCallback = cb; + + MX_USART1_UART_Init(); + return 0; +} + +uint8_t vcom_DeInit(void) +{ + /*##-1- Reset peripherals ##################################################*/ +// __HAL_RCC_USART2_FORCE_RESET(); +// __HAL_RCC_USART2_RELEASE_RESET(); + + /*##-2- MspDeInit ##################################################*/ +// HAL_UART_MspDeInit(&UartHandle); + + /*##-3- Disable the NVIC for DMA ###########################################*/ + /* temorary while waiting CR 50840: MX implementation of MX_DMA_DeInit() */ + /* For the time being user should change mannualy the channel according to the MX settings */ + /* USER CODE BEGIN 1 */ +// HAL_NVIC_DisableIRQ(DMA1_Channel5_IRQn); +// + return 0; + /* USER CODE END 1 */ +} + +uint8_t vcom_Trace(uint8_t *p_data, uint16_t size) +{ +// HAL_UART_Transmit_DMA(&UartHandle, p_data, size); + HAL_UART_Transmit(&UartHandle, p_data, size, 1000); + return 0; +} + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/lpm/tiny_lpm/stm32_lpm.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/lpm/tiny_lpm/stm32_lpm.c new file mode 100644 index 00000000..1cfd17aa --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/lpm/tiny_lpm/stm32_lpm.c @@ -0,0 +1,180 @@ +/** + ****************************************************************************** + * @file stm32_lpm.c + * @author MCD Application Team + * @brief Low Power Manager + ****************************************************************************** + * @attention + * + *

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

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ + +/* Includes ------------------------------------------------------------------*/ +#include "stm32_lpm.h" +#include "utilities_conf.h" + +/* Private typedef -----------------------------------------------------------*/ +/* Private macros ------------------------------------------------------------*/ +#define UTIL_LPM_NO_BIT_SET (0) + +#ifndef UTIL_LPM_INIT_CRITICAL_SECTION + #define UTIL_LPM_INIT_CRITICAL_SECTION( ) +#endif + +#ifndef UTIL_LPM_ENTER_CRITICAL_SECTION + #define UTIL_LPM_ENTER_CRITICAL_SECTION( ) UTILS_ENTER_CRITICAL_SECTION( ) +#endif + +#ifndef UTIL_LPM_EXIT_CRITICAL_SECTION + #define UTIL_LPM_EXIT_CRITICAL_SECTION( ) UTILS_EXIT_CRITICAL_SECTION( ) +#endif + +/* Private function prototypes -----------------------------------------------*/ +/* Private variables ---------------------------------------------------------*/ +/* Private typedef -----------------------------------------------------------*/ +/* Private defines -----------------------------------------------------------*/ +/* Private macros ------------------------------------------------------------*/ +/* Private variables ---------------------------------------------------------*/ +static UTIL_LPM_bm_t StopModeDisable = UTIL_LPM_NO_BIT_SET; +static UTIL_LPM_bm_t OffModeDisable = UTIL_LPM_NO_BIT_SET; + +/* Global variables ----------------------------------------------------------*/ +/* Private function prototypes -----------------------------------------------*/ +/* Functions Definition ------------------------------------------------------*/ + +void UTIL_LPM_Init( void ) +{ + StopModeDisable = UTIL_LPM_NO_BIT_SET; + OffModeDisable = UTIL_LPM_NO_BIT_SET; + UTIL_LPM_INIT_CRITICAL_SECTION( ); +} + +void UTIL_LPM_DeInit( void ) +{ +} + +void UTIL_LPM_SetStopMode( UTIL_LPM_bm_t lpm_id_bm, UTIL_LPM_State_t state ) +{ + UTIL_LPM_ENTER_CRITICAL_SECTION( ); + + switch( state ) + { + case UTIL_LPM_DISABLE: + { + StopModeDisable |= lpm_id_bm; + break; + } + case UTIL_LPM_ENABLE: + { + StopModeDisable &= ( ~lpm_id_bm ); + break; + } + default: + break; + } + + UTIL_LPM_EXIT_CRITICAL_SECTION( ); +} + +void UTIL_LPM_SetOffMode( UTIL_LPM_bm_t lpm_id_bm, UTIL_LPM_State_t state ) +{ + UTIL_LPM_ENTER_CRITICAL_SECTION( ); + + switch(state) + { + case UTIL_LPM_DISABLE: + { + OffModeDisable |= lpm_id_bm; + break; + } + case UTIL_LPM_ENABLE: + { + OffModeDisable &= ( ~lpm_id_bm ); + break; + } + default: + break; + } + + UTIL_LPM_EXIT_CRITICAL_SECTION( ); +} + +UTIL_LPM_Mode_t UTIL_LPM_GetMode( void ) +{ + UTIL_LPM_Mode_t mode_selected; + + UTIL_LPM_ENTER_CRITICAL_SECTION( ); + + if( StopModeDisable != UTIL_LPM_NO_BIT_SET ) + { + /** + * At least one user disallows Stop Mode + */ + mode_selected = UTIL_LPM_SLEEPMODE; + } + else + { + if( OffModeDisable != UTIL_LPM_NO_BIT_SET ) + { + /** + * At least one user disallows Off Mode + */ + mode_selected = UTIL_LPM_STOPMODE; + } + else + { + mode_selected = UTIL_LPM_OFFMODE; + } + } + + UTIL_LPM_EXIT_CRITICAL_SECTION( ); + + return mode_selected; +} + +void UTIL_LPM_EnterLowPower( void ) +{ + UTIL_LPM_ENTER_CRITICAL_SECTION( ); + + if( StopModeDisable != UTIL_LPM_NO_BIT_SET ) + { + /** + * At least one user disallows Stop Mode + * SLEEP mode is required + */ + UTIL_PowerDriver.EnterSleepMode( ); + UTIL_PowerDriver.ExitSleepMode( ); + } + else + { + if( OffModeDisable != UTIL_LPM_NO_BIT_SET ) + { + /** + * At least one user disallows Off Mode + * STOP mode is required + */ + UTIL_PowerDriver.EnterStopMode( ); + UTIL_PowerDriver.ExitStopMode( ); + } + else + { + /** + * OFF mode is required + */ + UTIL_PowerDriver.EnterOffMode( ); + UTIL_PowerDriver.ExitOffMode( ); + } + } + + UTIL_LPM_EXIT_CRITICAL_SECTION( ); +} + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/lpm/tiny_lpm/stm32_lpm.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/lpm/tiny_lpm/stm32_lpm.h new file mode 100644 index 00000000..05553f5b --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/lpm/tiny_lpm/stm32_lpm.h @@ -0,0 +1,126 @@ +/** + ****************************************************************************** + * @file stm32_lpm.h + * @author MCD Application Team + * @brief Header for stm32_lpm.c module + ****************************************************************************** + * @attention + * + *

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

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** +*/ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32_LPM_H +#define __STM32_LPM_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "stdint.h" + +typedef uint32_t UTIL_LPM_bm_t; + +typedef enum +{ + UTIL_LPM_ENABLE=0, + UTIL_LPM_DISABLE, +} UTIL_LPM_State_t; + +typedef enum +{ + UTIL_LPM_SLEEPMODE, + UTIL_LPM_STOPMODE, + UTIL_LPM_OFFMODE, +} UTIL_LPM_Mode_t; + +/*! + * \brief LPM driver definition + */ +struct UTIL_LPM_Driver_s +{ + void (* EnterSleepMode) ( void ); + void (* ExitSleepMode) ( void ); + void (* EnterStopMode) ( void ); + void (* ExitStopMode) ( void ); + void (* EnterOffMode) ( void ); + void (* ExitOffMode) ( void ); +}; + +/*! + * + * \Remark : This structure is defined and initialized in the specific platform + * power implementation + */ +extern const struct UTIL_LPM_Driver_s UTIL_PowerDriver; + +/* Exported constants --------------------------------------------------------*/ +/* External variables --------------------------------------------------------*/ +/* Exported macros -----------------------------------------------------------*/ +/* Exported functions ------------------------------------------------------- */ + +/** + * @brief This API Initializes the LPM resources. + * @param None + * @retval None + */ +void UTIL_LPM_Init( void ); + +/** + * @brief This API Un-Initializes the LPM resources. + * @param None + * @retval None + */ +void UTIL_LPM_DeInit( void ); + +/** + * @brief This API returns the Low Power Mode selected that will be applied when the system will enter low power mode + * if there is no update between the time the mode is read with this API and the time the system enters + * low power mode. + * @param None + * @retval UTIL_LPM_Mode_t + */ +UTIL_LPM_Mode_t UTIL_LPM_GetMode( void ); + +/** + * @brief This API notifies the low power manager if the specified user allows the Stop mode or not. + * The default mode selection for all users is Stop Mode enabled + * @param lpm_id_bm: identifier of the user ( 1 bit per user ) + * @param state: Specify whether StopMode is allowed or not by this user + * @retval None + */ +void UTIL_LPM_SetStopMode( UTIL_LPM_bm_t lpm_id_bm, UTIL_LPM_State_t state ); + +/** + * @brief This API notifies the low power manager if the specified user allows the Off mode or not. + * The default mode selection for all users is Off mode enabled + * @param lpm_id_bm: identifier of the user ( 1 bit per user ) + * @param state: Specify whether OffMode is allowed or not by this user + * @retval None + */ +void UTIL_LPM_SetOffMode( UTIL_LPM_bm_t lpm_id_bm, UTIL_LPM_State_t state ); + +/** + * @brief This API is called by the low power manager in a critical section (PRIMASK bit set) to allow the + * application to implement dedicated code before entering Low Power Mode + * @param None + * @retval None + */ +void UTIL_LPM_EnterLowPower( void ); + +#ifdef __cplusplus +} +#endif + +#endif /*__STM32_LPM_H */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_mem.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_mem.c new file mode 100644 index 00000000..302fb223 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_mem.c @@ -0,0 +1,65 @@ +/** + ****************************************************************************** + * @file stm32_mem.c + * @author MCD Application Team + * @brief standard memory operation + ****************************************************************************** + * @attention + * + *

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

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ + +/* Includes ------------------------------------------------------------------*/ +#include "stdint.h" +#include "stm32_mem.h" + +/* Private typedef -----------------------------------------------------------*/ +/* Private defines -----------------------------------------------------------*/ +/* Private macros ------------------------------------------------------------*/ +/* Private variables ---------------------------------------------------------*/ +/* Global variables ----------------------------------------------------------*/ +/* Private function prototypes -----------------------------------------------*/ +/* Functions Definition ------------------------------------------------------*/ + +void UTIL_MEM_cpy_8( void *dst, const void *src, uint16_t size ) +{ + uint8_t* dst8= (uint8_t *) dst; + uint8_t* src8= (uint8_t *) src; + + while( size-- ) + { + *dst8++ = *src8++; + } +} + +void UTIL_MEM_cpyr_8( void *dst, const void *src, uint16_t size ) +{ + uint8_t* dst8= (uint8_t *) dst; + uint8_t* src8= (uint8_t *) src; + + dst8 = dst8 + ( size - 1 ); + while( size-- ) + { + *dst8-- = *src8++; + } +} + +void UTIL_MEM_set_8( void *dst, uint8_t value, uint16_t size ) +{ + uint8_t* dst8= (uint8_t *) dst; + while( size-- ) + { + *dst8++ = value; + } +} + + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_mem.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_mem.h new file mode 100644 index 00000000..5cfb8659 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_mem.h @@ -0,0 +1,76 @@ +/** + ****************************************************************************** + * @file stm32_mem.h + * @author MCD Application Team + * @brief standard memory operation + ****************************************************************************** + * @attention + * + *

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

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** +*/ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32_MEM_H__ +#define __STM32_MEM_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + /* Includes ------------------------------------------------------------------*/ +#include +#include "utilities_conf.h" + +/* Exported types ------------------------------------------------------------*/ + /* Exported constants --------------------------------------------------------*/ +/* Exported macro ------------------------------------------------------------*/ +/* ---- Memory mapping macros --------------------------------------- */ +#define UTIL_MEM_PLACE_IN_SECTION( __x__ ) UTIL_PLACE_IN_SECTION( __x__ ) +#define UTIL_MEM_ALIGN ALIGN + + + /* Exported functions ------------------------------------------------------- */ + /** + * @brief This API copies one buffer to another + * @param dst: output buffer to be filled + * @param src: input buffer + * @param size: size of 8b data + * @retval None + */ + void UTIL_MEM_cpy_8( void *dst, const void *src, uint16_t size ); + + /** + * @brief This API copies one buffer to another in reverse + * @param dst: output buffer to be filled + * @param src: input buffer + * @param size: size of 8b data + * @retval None + */ + void UTIL_MEM_cpyr_8( void *dst, const void *src, uint16_t size ); + + /** + * @brief This API copies one value into buffer + * @param dst: output buffer to be filled + * @param value: value + * @param size: size of 8b data + * @retval None + */ + void UTIL_MEM_set_8( void *dst, uint8_t value, uint16_t size ); + + +#ifdef __cplusplus +} +#endif + +#endif // __STM32_MEM_H__ +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ + + diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_systime.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_systime.c new file mode 100644 index 00000000..c155f42e --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_systime.c @@ -0,0 +1,351 @@ +/** +****************************************************************************** + * @file stm32_systime.c + * @author MCD Application Team + * @brief System time functions implementation + ****************************************************************************** + * @attention + * + *

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

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ + +/* Includes ------------------------------------------------------------------*/ +#include +#include "stm32_systime.h" +#include "rtc_if.h" + +#define END_OF_FEBRUARY_LEAP 60 //31+29 +#define END_OF_JULY_LEAP 213 //31+29+... + +#define END_OF_FEBRUARY_NORM 59 //31+28 +#define END_OF_JULY_NORM 212 //31+28+... + +#define UNIX_YEAR 68 //1968 is leap year + +//UNIX time 0 = start at 01:00:00, 01/01/1970 +#define UNIX_HOUR_OFFSET ( ( TM_DAYS_IN_LEAP_YEAR + TM_DAYS_IN_YEAR ) * TM_SECONDS_IN_1DAY ) + +/*! + * \brief Correction factors + */ +#define DAYS_IN_MONTH_CORRECTION_NORM ( (uint32_t )0x99AAA0 ) +#define DAYS_IN_MONTH_CORRECTION_LEAP ( (uint32_t )0x445550 ) + + +/* 365.25 = (366 + 365 + 365 + 365)/4 */ +#define DIV_365_25( X ) ( ( ( X ) * 91867 + 22750 ) >> 25 ) + +#define DIV_APPROX_86400( X ) ( ( ( X ) >> 18 ) + ( ( X ) >> 17 ) ) + +#define DIV_APPROX_1000( X ) ( ( ( X ) >> 10 ) +( ( X ) >> 16 ) + ( ( X ) >> 17 ) ) + +#define DIV_APPROX_60( X ) ( ( ( X ) * 17476 ) >> 20 ) + +#define DIV_APPROX_61( X ) ( ( ( X ) * 68759 ) >> 22 ) + +#define MODULO_7( X ) ( ( X ) -( ( ( ( ( X ) + 1 ) * 299593 ) >> 21 ) * 7 ) ) + +/*! + * \brief Calculates ceiling( X / N ) + */ +#define DIVC( X, N ) ( ( ( X ) + ( N ) -1 ) / ( N ) ) + +#define DIVC_BY_4( X ) ( ( ( X ) + 3 ) >>2 ) + +#define DIVC_BY_2( X ) ( ( ( X ) + 1 ) >> 1 ) + +static uint32_t CalendarGetMonth( uint32_t days, uint32_t year ); +static void CalendarDiv86400( uint32_t in, uint32_t* out, uint32_t* remainder ); +static uint32_t CalendarDiv61( uint32_t in ); +static void CalendarDiv60( uint32_t in, uint32_t* out, uint32_t* remainder ); + +const char *WeekDayString[]={ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; + +SysTime_t SysTimeAdd( SysTime_t a, SysTime_t b ) +{ + SysTime_t c = { .Seconds = 0, .SubSeconds = 0 }; + + c.Seconds = a.Seconds + b.Seconds; + c.SubSeconds = a.SubSeconds + b.SubSeconds; + if( c.SubSeconds >= 1000 ) + { + c.Seconds++; + c.SubSeconds -= 1000; + } + return c; +} + +SysTime_t SysTimeSub( SysTime_t a, SysTime_t b ) +{ + SysTime_t c = { .Seconds = 0, .SubSeconds = 0 }; + + c.Seconds = a.Seconds - b.Seconds; + c.SubSeconds = a.SubSeconds - b.SubSeconds; + if( c.SubSeconds < 0 ) + { + c.Seconds--; + c.SubSeconds += 1000; + } + return c; +} + +void SysTimeSet( SysTime_t sysTime ) +{ + SysTime_t DeltaTime; + + SysTime_t calendarTime = { .Seconds = 0, .SubSeconds = 0 }; + + calendarTime.Seconds = HW_RTC_GetCalendarTime( ( uint16_t* )&calendarTime.SubSeconds ); + + // sysTime is epoch + DeltaTime = SysTimeSub( sysTime, calendarTime ); + + HW_RTC_BKUPWrite( DeltaTime.Seconds, ( uint32_t )DeltaTime.SubSeconds ); +} + +SysTime_t SysTimeGet( void ) +{ + SysTime_t calendarTime = { .Seconds = 0, .SubSeconds = 0 }; + SysTime_t sysTime = { .Seconds = 0, .SubSeconds = 0 }; + SysTime_t DeltaTime; + + calendarTime.Seconds = HW_RTC_GetCalendarTime( ( uint16_t* )&calendarTime.SubSeconds ); + + HW_RTC_BKUPRead( &DeltaTime.Seconds, ( uint32_t* )&DeltaTime.SubSeconds ); + + sysTime = SysTimeAdd( DeltaTime, calendarTime ); + + return sysTime; +} + + +SysTime_t SysTimeGetMcuTime( void ) +{ + SysTime_t calendarTime = { .Seconds = 0, .SubSeconds = 0 }; + + calendarTime.Seconds = HW_RTC_GetCalendarTime( ( uint16_t* )&calendarTime.SubSeconds ); + + return calendarTime; +} + +uint32_t SysTimeToMs( SysTime_t sysTime ) +{ + SysTime_t DeltaTime; + HW_RTC_BKUPRead( &DeltaTime.Seconds, ( uint32_t* )&DeltaTime.SubSeconds ); + SysTime_t calendarTime = SysTimeSub( sysTime, DeltaTime ); + return calendarTime.Seconds * 1000 + calendarTime.SubSeconds; +} + +SysTime_t SysTimeFromMs( uint32_t timeMs ) +{ + uint32_t seconds = timeMs / 1000; + SysTime_t sysTime = { .Seconds = seconds, .SubSeconds = timeMs - seconds * 1000 }; + SysTime_t DeltaTime = { 0 }; + HW_RTC_BKUPRead( &DeltaTime.Seconds, ( uint32_t* )&DeltaTime.SubSeconds ); + + return SysTimeAdd( sysTime, DeltaTime ); +} + +uint32_t SysTimeMkTime( const struct tm* localtime ) +{ + uint32_t nbdays; + uint32_t nbsecs; + uint32_t year = localtime->tm_year - UNIX_YEAR; + uint32_t correctionMonth[4] = + { + DAYS_IN_MONTH_CORRECTION_LEAP, + DAYS_IN_MONTH_CORRECTION_NORM, + DAYS_IN_MONTH_CORRECTION_NORM, + DAYS_IN_MONTH_CORRECTION_NORM + }; + + nbdays = DIVC( ( TM_DAYS_IN_YEAR * 3 + TM_DAYS_IN_LEAP_YEAR ) * year, 4 ); + + nbdays += ( DIVC_BY_2( ( localtime->tm_mon ) * ( 30 + 31 ) ) - + ( ( ( correctionMonth[year % 4] >> ( ( localtime->tm_mon ) * 2 ) ) & 0x03 ) ) ); + + nbdays += ( localtime->tm_mday - 1 ); + + // Convert from days to seconds + nbsecs = nbdays * TM_SECONDS_IN_1DAY; + + nbsecs += ( ( uint32_t )localtime->tm_sec + + ( ( uint32_t )localtime->tm_min * TM_SECONDS_IN_1MINUTE ) + + ( ( uint32_t )localtime->tm_hour * TM_SECONDS_IN_1HOUR ) ); + return nbsecs - UNIX_HOUR_OFFSET; +} + + + +void SysTimeLocalTime( const uint32_t timestamp, struct tm *localtime ) +{ + uint32_t correctionMonth[4] = + { + DAYS_IN_MONTH_CORRECTION_LEAP, + DAYS_IN_MONTH_CORRECTION_NORM, + DAYS_IN_MONTH_CORRECTION_NORM, + DAYS_IN_MONTH_CORRECTION_NORM + }; + uint32_t weekDays = 1; // Monday 1st January 1968 + uint32_t seconds; + uint32_t minutes; + uint32_t days; + uint32_t divOut; + uint32_t divReminder; + + CalendarDiv86400( timestamp + UNIX_HOUR_OFFSET, &days, &seconds ); + + // Calculates seconds + CalendarDiv60( seconds, &minutes, &divReminder ); + localtime->tm_sec = ( uint8_t )divReminder; + + // Calculates minutes and hours + CalendarDiv60( minutes, &divOut, &divReminder); + localtime->tm_min = ( uint8_t )divReminder; + localtime->tm_hour = ( uint8_t )divOut; + + // Calculates year + localtime->tm_year = DIV_365_25( days ); + days-= DIVC_BY_4( ( TM_DAYS_IN_YEAR * 3 + TM_DAYS_IN_LEAP_YEAR ) * localtime->tm_year ); + + localtime->tm_yday = days; + + // Calculates month + localtime->tm_mon = CalendarGetMonth( days, localtime->tm_year ); + + // calculates weekdays + weekDays += DIVC_BY_4( ( localtime->tm_year * 5 ) ); + weekDays += days; + localtime->tm_wday = MODULO_7( weekDays ); + + days -= ( DIVC_BY_2( ( localtime->tm_mon ) * ( 30 + 31 ) ) - + ( ( ( correctionMonth[localtime->tm_year % 4] >> ( ( localtime->tm_mon ) * 2 ) ) & 0x03 ) ) ); + + // Convert 0 to 1 indexed. + localtime->tm_mday = days + 1; + + localtime->tm_year += UNIX_YEAR; + + localtime->tm_isdst = -1; +} + +static uint32_t CalendarGetMonth( uint32_t days, uint32_t year ) +{ + uint32_t month; + if( ( year % 4 ) == 0 ) + { /*leap year*/ + if( days < END_OF_FEBRUARY_LEAP ) + { // January or February + // month = days * 2 / ( 30 + 31 ); + month = CalendarDiv61( days * 2 ); + } + else if( days < END_OF_JULY_LEAP ) + { + month = CalendarDiv61( ( days - END_OF_FEBRUARY_LEAP ) * 2 ) + 2; + } + else + { + month = CalendarDiv61( ( days - END_OF_JULY_LEAP ) * 2 ) + 7; + } + } + else + { + if( days < END_OF_FEBRUARY_NORM ) + { // January or February + month = CalendarDiv61( days * 2 ); + } + else if( days < END_OF_JULY_NORM ) + { + month = CalendarDiv61( ( days - END_OF_FEBRUARY_NORM ) * 2 ) + 2; + } + else + { + month = CalendarDiv61( ( days - END_OF_JULY_NORM ) * 2 ) + 7; + } + } + return month; +} + +static void CalendarDiv86400( uint32_t in, uint32_t* out, uint32_t* remainder ) +{ +#if 0 + *remainder = in % SECONDS_IN_1DAY; + *out = in / SECONDS_IN_1DAY; +#else + uint32_t outTemp = 0; + uint32_t divResult = DIV_APPROX_86400( in ); + + while( divResult >=1 ) + { + outTemp += divResult; + in -= divResult * 86400; + divResult= DIV_APPROX_86400( in ); + } + if( in >= 86400 ) + { + outTemp += 1; + in -= 86400; + } + + *remainder = in; + *out = outTemp; +#endif +} + +static uint32_t CalendarDiv61( uint32_t in ) +{ +#if 0 + return( in / 61 ); +#else + uint32_t outTemp = 0; + uint32_t divResult = DIV_APPROX_61( in ); + while( divResult >=1 ) + { + outTemp += divResult; + in -= divResult * 61; + divResult = DIV_APPROX_61( in ); + } + if( in >= 61 ) + { + outTemp += 1; + in -= 61; + } + return outTemp; +#endif +} + +static void CalendarDiv60( uint32_t in, uint32_t* out, uint32_t* remainder ) +{ +#if 0 + *remainder = in % 60; + *out = in / 60; +#else + uint32_t outTemp = 0; + uint32_t divResult = DIV_APPROX_60( in ); + + while( divResult >=1 ) + { + outTemp += divResult; + in -= divResult * 60; + divResult = DIV_APPROX_60( in ); + } + if( in >= 60 ) + { + outTemp += 1; + in -= 60; + } + *remainder = in; + *out = outTemp; +#endif +} + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ + diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_systime.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_systime.h new file mode 100644 index 00000000..46f85703 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_systime.h @@ -0,0 +1,175 @@ +/** +****************************************************************************** +* @file stm32_systime.h +* @author MCD Application Team +* @brief System time functions implementation +****************************************************************************** +* @attention +* +*

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

+* +* This software component is licensed by ST under BSD 3-Clause license, +* the "License"; You may not use this file except in compliance with the +* License. You may obtain a copy of the License at: +* opensource.org/licenses/BSD-3-Clause +* +****************************************************************************** +*/ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32_SYS_TIME_H__ +#define __STM32_SYS_TIME_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + /* Includes ------------------------------------------------------------------*/ +#include +#include "time.h" + + + /* Exported constants --------------------------------------------------------*/ + + + /*! + * @brief Days, Hours, Minutes and seconds of systime.h + */ +#define TM_DAYS_IN_LEAP_YEAR ( ( uint32_t ) 366U ) +#define TM_DAYS_IN_YEAR ( ( uint32_t ) 365U ) +#define TM_SECONDS_IN_1DAY ( ( uint32_t )86400U ) +#define TM_SECONDS_IN_1HOUR ( ( uint32_t ) 3600U ) +#define TM_SECONDS_IN_1MINUTE ( ( uint32_t ) 60U ) +#define TM_MINUTES_IN_1HOUR ( ( uint32_t ) 60U ) +#define TM_HOURS_IN_1DAY ( ( uint32_t ) 24U ) + + + /*! + * @brief Months of systime.h + */ +#define TM_MONTH_JANUARY ( ( uint8_t ) 0U ) +#define TM_MONTH_FEBRUARY ( ( uint8_t ) 1U ) +#define TM_MONTH_MARCH ( ( uint8_t ) 2U ) +#define TM_MONTH_APRIL ( ( uint8_t ) 3U ) +#define TM_MONTH_MAY ( ( uint8_t ) 4U ) +#define TM_MONTH_JUNE ( ( uint8_t ) 5U ) +#define TM_MONTH_JULY ( ( uint8_t ) 6U ) +#define TM_MONTH_AUGUST ( ( uint8_t ) 7U ) +#define TM_MONTH_SEPTEMBER ( ( uint8_t ) 8U ) +#define TM_MONTH_OCTOBER ( ( uint8_t ) 9U ) +#define TM_MONTH_NOVEMBER ( ( uint8_t )10U ) +#define TM_MONTH_DECEMBER ( ( uint8_t )11U ) + + /*! + * @brief Week days of systime.h + */ +#define TM_WEEKDAY_SUNDAY ( ( uint8_t )0U ) +#define TM_WEEKDAY_MONDAY ( ( uint8_t )1U ) +#define TM_WEEKDAY_TUESDAY ( ( uint8_t )2U ) +#define TM_WEEKDAY_WEDNESDAY ( ( uint8_t )3U ) +#define TM_WEEKDAY_THURSDAY ( ( uint8_t )4U ) +#define TM_WEEKDAY_FRIDAY ( ( uint8_t )5U ) +#define TM_WEEKDAY_SATURDAY ( ( uint8_t )6U ) + + /*! + * @brief Number of seconds elapsed between Unix and GPS epoch + */ +#define UNIX_GPS_EPOCH_OFFSET 315964800 + /* External variables --------------------------------------------------------*/ + + /*! + * @brief Structure holding the system time in seconds and milliseconds. + */ + typedef struct SysTime_s + { + uint32_t Seconds; + int16_t SubSeconds; + }SysTime_t; + + /* Exported macros -----------------------------------------------------------*/ + /* Exported functions ------------------------------------------------------- */ + /*! + * @brief Adds 2 SysTime_t values + * + * @param a Value + * @param b Value to added + * + * @retval result Addition result (SysTime_t value) + */ + SysTime_t SysTimeAdd( SysTime_t a, SysTime_t b ); + + /*! + * @brief Subtracts 2 SysTime_t values + * + * @param a Value + * @param b Value to be subtracted + * + * @retval result Subtraction result (SysTime_t value) + */ + SysTime_t SysTimeSub( SysTime_t a, SysTime_t b ); + + /*! + * @brief Sets new system time + * + * @param sysTime New seconds/sub-seconds since UNIX epoch origin + */ + void SysTimeSet( SysTime_t sysTime ); + + /*! + * @brief Gets current system time + * + * @retval sysTime Current seconds/sub-seconds since UNIX epoch origin + */ + SysTime_t SysTimeGet( void ); + + /*! + * @brief Gets current MCU system time + * + * @retval sysTime Current seconds/sub-seconds since Mcu started + */ + SysTime_t SysTimeGetMcuTime( void ); + + /*! + * Converts the given SysTime to the equivalent RTC value in milliseconds + * + * @param [IN] sysTime System time to be converted + * + * @retval timeMs The RTC converted time value in ms + */ + uint32_t SysTimeToMs( SysTime_t sysTime ); + + /*! + * Converts the given RTC value in milliseconds to the equivalent SysTime + * + * \param [IN] timeMs The RTC time value in ms to be converted + * + * \retval sysTime Converted system time + */ +SysTime_t SysTimeFromMs( uint32_t timeMs ); + + /*! + * @brief Convert a calendar time into time since UNIX epoch as a uint32_t. + * + * @param [IN] localtime Pointer to the object containing the calendar time + * @retval timestamp The calendar time as seconds since UNIX epoch. + */ + uint32_t SysTimeMkTime( const struct tm* localtime ); + + /*! + * @brief Converts a given time in seconds since UNIX epoch into calendar time. + * + * @param [IN] timestamp The time since UNIX epoch to convert into calendar time. + * @param [OUT] localtime Pointer to the calendar time object which will contain + the result of the conversion. + */ + void SysTimeLocalTime( const uint32_t timestamp, struct tm *localtime ); + +#ifdef __cplusplus +} +#endif + +#endif // __STM32_SYS_TIME_H__ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ + diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_tiny_sscanf.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_tiny_sscanf.c new file mode 100644 index 00000000..c411eeb0 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_tiny_sscanf.c @@ -0,0 +1,976 @@ +/****************************************************************************** + * @file stm32_tiny_sscanf.c + * @author MCD Application Team + * @brief Tiny implementation of sscanf + ****************************************************************************** + * @attention + * + *

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

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ + + +/* + * This code is derived from + * https://github.com/rapid7/metasploit-payloads/, in c/meterpreter/source/bionic/libc/stdio/sscanf.c + * It has been derived in order to optimize code size. In this context + * all the formats are not supported. Current supported formats are + * %hx, %hhx, %ul, %d,... + * when TINY_SSCANF is defined + * + * When TINY_NO_OX is defined, this is not possible to sscanf("%x") of "0xab", + * only "ab" is possible + * + * When TINY_SPACE_NOT_SPECIALCASE is defined, "space" is not a special character. + * That means that we expect a single space, and not any of ispace() character + * (space, tabs,...) + */ + +/* Includes ------------------------------------------------------------------*/ +#include +#include +#include +#include +#include +#include +#include "stm32_tiny_sscanf.h" + +/* Private typedef -----------------------------------------------------------*/ +/* Private define ------------------------------------------------------------*/ +#define TINY_SSCANF +#define TINY_NO_OX +#define TINY_SPACE_NOT_SPECIALCASE + +/* Private macro -------------------------------------------------------------*/ +/* Private variables ---------------------------------------------------------*/ +/* Functions Definition ------------------------------------------------------*/ + +#ifdef FLOATING_POINT +#include "floatio.h" +#endif + +#define BUF 513 /* Maximum length of numeric string. */ + +/* + * Flags used during conversion. + */ +#define LONG 0x00001 /* l: long or double */ +#define SHORT 0x00004 /* h: short */ +#define SHORTSHORT 0x00008 /* hh: 8 bit integer */ +#define UNSIGNED 0x00800 /* %[oupxX] conversions */ +#ifdef TINY_SSCANF +#else +#define LONGDBL 0x00002 /* L: long double; unimplemented */ +#define LLONG 0x00010 /* ll: long long (+ deprecated q: quad) */ +#define POINTER 0x00020 /* p: void * (as hex) */ +#define SIZEINT 0x00040 /* z: (signed) size_t */ +#define MAXINT 0x00080 /* j: intmax_t */ +#define PTRINT 0x00100 /* t: ptrdiff_t */ +#define NOSKIP 0x00200 /* [ or c: do not skip blanks */ +#define SUPPRESS 0x00400 /* *: suppress assignment */ +#endif + +/* + * The following are used in numeric conversions only: + * SIGNOK, HAVESIGN, NDIGITS, DPTOK, and EXPOK are for floating point; + * SIGNOK, HAVESIGN, NDIGITS, PFXOK, and NZDIGITS are for integral. + */ +#define SIGNOK 0x01000 /* +/- is (still) legal */ +#define HAVESIGN 0x02000 /* sign detected */ +#define NDIGITS 0x04000 /* no digits detected */ + +#define DPTOK 0x08000 /* (float) decimal point is still legal */ +#define EXPOK 0x10000 /* (float) exponent (e+3, etc) still legal */ + +#ifdef TINY_NO_OX +#else +#define PFXOK 0x08000 /* 0x prefix is (still) legal */ +#define NZDIGITS 0x10000 /* no zero digits detected */ +#endif + +/* + * Conversion types. + */ +#define CT_INT 3 /* integer, i.e., strtoimax or strtoumax */ +#define CT_FLOAT 4 /* floating, i.e., strtod */ + +#ifdef TINY_SSCANF +#else +#define CT_CHAR 0 /* %c conversion */ +#define CT_CCL 1 /* %[...] conversion */ +#define CT_STRING 2 /* %s conversion */ +#endif + +#define u_char unsigned char +#define u_long unsigned long + +#ifdef TINY_SSCANF +#else +static u_char *__sccl(char *, u_char *); +#endif + +#define VFSCANF tiny_vfscanf + +#if !defined(VFSCANF) +#define VFSCANF vfscanf +#endif + + +#define __srefill(_x) 1 +#define ungetc(_c, _fp) do { (_c), fp_p--; fp_r++; } while (0) + +/* + * vfscanf + */ +static inline int +VFSCANF(const char *str, const char *fmt0, __Va_list ap) +{ + u_char *fmt = (u_char *)fmt0; + int c; /* character from format, or conversion */ + size_t width; /* field width, or 0 */ + char *p; /* points into all kinds of strings */ + int flags; /* flags as defined above */ + int nassigned; /* number of fields assigned */ + int nread; /* number of characters consumed from fp */ + int base; /* base argument to strtoimax/strtouimax */ + char buf[BUF]; /* buffer for numeric conversions */ + const char *fp_p; + int fp_r; + uintmax_t value; + int sign_minus; + + +#ifdef TINY_SSCANF +#else + int n; /* handy integer */ + char *p0; /* saves original value of p when necessary */ + char ccltab[256]; /* character class table for %[...] */ +#endif + + /* `basefix' is used to avoid `if' tests in the integer scanner */ +#ifdef TINY_SSCANF + /* basefix[] can be removed as we do not support %i */ +#else + static short basefix[17] = + { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; +#endif + + fp_p = str; + fp_r = strlen(str); + + nassigned = 0; + nread = 0; + base = 0; /* XXX just to keep gcc happy */ + for (;;) { + c = *fmt++; + if (c == 0) + return (nassigned); +#ifdef TINY_SPACE_NOT_SPECIALCASE +#else + if (isspace(c)) { + while ((fp_r > 0 || __srefill(fp) == 0) && + isspace(*fp_p)) + nread++, fp_r--, fp_p++; + continue; + } +#endif + if (c != '%') + goto literal; + width = 0; + flags = 0; + /* + * switch on the format. continue if done; + * break once format type is derived. + */ +again: c = *fmt++; + switch (c) { + case '%': +literal: + if (fp_r <= 0 && __srefill(fp)) + goto input_failure; + if (*fp_p != c) + goto match_failure; + fp_r--, fp_p++; + nread++; + continue; + +#ifdef TINY_SSCANF +#else + case '*': + flags |= SUPPRESS; + goto again; + case 'j': + flags |= MAXINT; + goto again; + case 'L': + flags |= LONGDBL; + goto again; +#endif + case 'h': + if (*fmt == 'h') { + fmt++; + flags |= SHORTSHORT; + } else { + flags |= SHORT; + } + goto again; + case 'l': +#ifdef TINY_SSCANF + /* %ll not supported */ + flags |= LONG; + goto again; +#else + if (*fmt == 'l') { + fmt++; + flags |= LLONG; + } else { + flags |= LONG; + } + goto again; +#endif + +#ifdef TINY_SSCANF +#else + case 'q': + flags |= LLONG; /* deprecated */ + goto again; + case 't': + flags |= PTRINT; + goto again; + case 'z': + flags |= SIZEINT; + goto again; +#endif + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + width = width * 10 + c - '0'; + goto again; + + /* + * Conversions. + * Those marked `compat' are for 4.[123]BSD compatibility. + * + * (According to ANSI, E and X formats are supposed + * to the same as e and x. Sorry about that.) + */ + case 'D': /* compat */ + flags |= LONG; + /* FALLTHROUGH */ + case 'd': + c = CT_INT; + base = 10; + break; + +#ifdef TINY_SSCANF + /* + * We do not support %i to remove potential base=8 in the following + * Hence basefix can be removed + */ +#else + case 'i': + c = CT_INT; + base = 0; + break; +#endif + +#ifdef TINY_SSCANF +#else + case 'O': /* compat */ + flags |= LONG; + /* FALLTHROUGH */ + case 'o': + c = CT_INT; + flags |= UNSIGNED; + base = 8; + break; +#endif + + case 'u': + c = CT_INT; + flags |= UNSIGNED; + base = 10; + break; + + case 'X': + case 'x': +#ifdef TINY_NO_OX +#else + flags |= PFXOK; /* enable 0x prefixing */ +#endif + c = CT_INT; + flags |= UNSIGNED; + base = 16; + break; + +#ifdef FLOATING_POINT + case 'E': + case 'G': + case 'e': + case 'f': + case 'g': + c = CT_FLOAT; + break; +#endif + +#ifdef TINY_SSCANF +#else + case 's': + c = CT_STRING; + break; + + case '[': + fmt = __sccl(ccltab, fmt); + flags |= NOSKIP; + c = CT_CCL; + break; + + case 'c': + flags |= NOSKIP; + c = CT_CHAR; + break; + + case 'p': /* pointer format is like hex */ + flags |= POINTER | PFXOK; + c = CT_INT; + flags |= UNSIGNED; + base = 16; + break; + + case 'n': + if (flags & SUPPRESS) + continue; + if (flags & SHORTSHORT) + *va_arg(ap, char *) = nread; + else if (flags & SHORT) + *va_arg(ap, short *) = nread; + else if (flags & LONG) + *va_arg(ap, long *) = nread; + else if (flags & SIZEINT) + *va_arg(ap, size_t *) = nread; + else if (flags & PTRINT) + *va_arg(ap, ptrdiff_t *) = nread; + else if (flags & LLONG) + *va_arg(ap, long long *) = nread; + else if (flags & MAXINT) + *va_arg(ap, intmax_t *) = nread; + else + *va_arg(ap, int *) = nread; + continue; +#endif + + /* + * Disgusting backwards compatibility hacks. XXX + */ + case '\0': /* compat */ + return (EOF); + + default: /* compat */ +#ifdef TINY_SSCANF +#else + if (isupper(c)) + flags |= LONG; + c = CT_INT; + base = 10; +#endif + break; + } + + /* + * We have a conversion that requires input. + */ + if (fp_r <= 0 && __srefill(fp)) + goto input_failure; + + + /* + * Consume leading white space, except for formats + * that suppress this. + */ +#ifdef TINY_SSCANF +#else + if ((flags & NOSKIP) == 0) { + while (isspace(*fp_p)) { + nread++; + if (--fp_r > 0) + fp_p++; + else if (__srefill(fp)) + goto input_failure; + } + /* + * Note that there is at least one character in + * the buffer, so conversions that do not set NOSKIP + * ca no longer result in an input failure. + */ + } +#endif + + /* + * Do the conversion. + */ + switch (c) { +#ifdef TINY_SSCANF +#else + case CT_CHAR: + /* scan arbitrary characters (sets NOSKIP) */ + if (width == 0) + width = 1; + if (flags & SUPPRESS) { + size_t sum = 0; + for (;;) { + if ((n = fp_r) < (int)width) { + sum += n; + width -= n; + fp_p += n; + if (__srefill(fp)) { + if (sum == 0) + goto input_failure; + break; + } + } else { + sum += width; + fp_r -= width; + fp_p += width; + break; + } + } + nread += sum; + } else { + size_t r = fread((void *)va_arg(ap, char *), 1, + width, fp); + + if (r == 0) + goto input_failure; + nread += r; + nassigned++; + } + break; +#endif + +#ifdef TINY_SSCANF +#else + case CT_CCL: + /* scan a (nonempty) character class (sets NOSKIP) */ + if (width == 0) + width = (size_t)~0; /* `infinity' */ + /* take only those things in the class */ + if (flags & SUPPRESS) { + n = 0; + while (ccltab[*fp_p]) { + n++, fp_r--, fp_p++; + if (--width == 0) + break; + if (fp_r <= 0 && __srefill(fp)) { + if (n == 0) + goto input_failure; + break; + } + } + if (n == 0) + goto match_failure; + } else { + p0 = p = va_arg(ap, char *); + while (ccltab[*fp_p]) { + fp_r--; + *p++ = *fp_p++; + if (--width == 0) + break; + if (fp_r <= 0 && __srefill(fp)) { + if (p == p0) + goto input_failure; + break; + } + } + n = p - p0; + if (n == 0) + goto match_failure; + *p = '\0'; + nassigned++; + } + nread += n; + break; +#endif + +#ifdef TINY_SSCANF +#else + case CT_STRING: + /* like CCL, but zero-length string OK, & no NOSKIP */ + if (width == 0) + width = (size_t)~0; + if (flags & SUPPRESS) { + n = 0; + while (!isspace(*fp_p)) { + n++, fp_r--, fp_p++; + if (--width == 0) + break; + if (fp_r <= 0 && __srefill(fp)) + break; + } + nread += n; + } else { + p0 = p = va_arg(ap, char *); + while (!isspace(*fp_p)) { + fp_r--; + *p++ = *fp_p++; + if (--width == 0) + break; + if (fp_r <= 0 && __srefill(fp)) + break; + } + *p = '\0'; + nread += p - p0; + nassigned++; + } + continue; +#endif + + case CT_INT: + /* scan an integer as if by strtoimax/strtoumax */ +#ifdef hardway + if (width == 0 || width > sizeof(buf) - 1) + width = sizeof(buf) - 1; +#else + /* size_t is unsigned, hence this optimisation */ + if (--width > sizeof(buf) - 2) + width = sizeof(buf) - 2; + width++; +#endif + +#ifdef TINY_NO_OX + flags |= SIGNOK | NDIGITS; +#else + flags |= SIGNOK | NDIGITS | NZDIGITS; +#endif + + sign_minus = 0; + value = 0; + for (p = buf; width; width--) { + c = *fp_p; + /* + * Switch on the character; `goto ok' + * if we accept it as a part of number. + */ + switch (c) { + + /* + * The digit 0 is always legal, but is + * special. For %i conversions, if no + * digits (zero or nonzero) have been + * scanned (only signs), we will have + * base==0. In that case, we should set + * it to 8 and enable 0x prefixing. + * Also, if we have not scanned zero digits + * before this, do not turn off prefixing + * (someone else will turn it off if we + * have scanned any nonzero digits). + */ + case '0': +#ifdef TINY_NO_OX + /* FALLTHROUGH */ +#else +#ifdef TINY_SSCANF +#else + if (base == 0) { + base = 8; + flags |= PFXOK; + } +#endif + if (!(flags & NDIGITS)) { + value = value * base; + } + + if (flags & NZDIGITS) + flags &= ~(SIGNOK|NZDIGITS|NDIGITS); + else + flags &= ~(SIGNOK|PFXOK|NDIGITS); + goto ok; +#endif + +#ifdef TINY_SSCANF + /* we only support base 10 and 16 */ + case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + case '8': case '9': +#ifdef TINY_NO_OX + flags &= ~(SIGNOK | NDIGITS); +#else + flags &= ~(SIGNOK | PFXOK | NDIGITS); +#endif + value = value * base + c - '0'; + goto ok; +#else + /* 1 through 7 always legal */ + case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + base = basefix[base]; + flags &= ~(SIGNOK | PFXOK | NDIGITS); + value = value * base + c - '0'; + goto ok; + + /* digits 8 and 9 ok iff decimal or hex */ + case '8': case '9': + base = basefix[base]; + if (base <= 8) + break; /* not legal here */ + flags &= ~(SIGNOK | PFXOK | NDIGITS); + value = value * base + c - '0'; + goto ok; +#endif + + /* letters ok iff hex */ + case 'A': case 'B': case 'C': + case 'D': case 'E': case 'F': + /* no need to fix base here */ + if (base <= 10) + break; /* not legal here */ +#ifdef TINY_NO_OX + flags &= ~(SIGNOK | NDIGITS); +#else + flags &= ~(SIGNOK | PFXOK | NDIGITS); +#endif + value = value * base + c - 'A' + 10; + goto ok; + + case 'a': case 'b': case 'c': + case 'd': case 'e': case 'f': + /* no need to fix base here */ + if (base <= 10) + break; /* not legal here */ +#ifdef TINY_NO_OX + flags &= ~(SIGNOK | NDIGITS); +#else + flags &= ~(SIGNOK | PFXOK | NDIGITS); +#endif + value = value * base + c - 'a' + 10; + goto ok; + + /* sign ok only as first character */ + case '-': + if (!(flags & HAVESIGN)) { + sign_minus = 1; + } + /* FALLTHROUGH */ + case '+': + if (flags & SIGNOK) { + flags &= ~SIGNOK; + flags |= HAVESIGN; + goto ok; + } + break; + + /* + * x ok iff flag still set and 2nd char (or + * 3rd char if we have a sign). + */ +#ifdef TINY_NO_OX +#else + case 'x': case 'X': + if ((flags & PFXOK) && p == + buf + 1 + !!(flags & HAVESIGN)) { + base = 16; /* if %i */ + flags &= ~PFXOK; + goto ok; + } + break; +#endif + } + + /* + * If we got here, c is not a legal character + * for a number. Stop accumulating digits. + */ + break; + ok: + /* + * c is legal: store it and look at the next. + */ + *p++ = c; + if (--fp_r > 0) + fp_p++; + else if (__srefill(fp)) + break; /* EOF */ + } + /* + * If we had only a sign, it is no good; push + * back the sign. If the number ends in `x', + * it was [sign] '0' 'x', so push back the x + * and treat it as [sign] '0'. + */ + if (flags & NDIGITS) { + if (p > buf) + { + --c; + --p; + ungetc(c++, fp); + /* There is a dummy post-increment to + avoid an unused value warning */ + } + goto match_failure; + } +#ifdef TINY_NO_OX +#else + c = ((u_char *)p)[-1]; + if (c == 'x' || c == 'X') { + --p; + ungetc(c, fp); + } +#endif + +#ifdef TINY_SSCANF + { +#else + if ((flags & SUPPRESS) == 0) { +#endif + + *p = '\0'; + if (sign_minus) + value = -value; + +#ifdef TINY_SSCANF +#else + if (flags & POINTER) + *va_arg(ap, void **) = + (void *)(uintptr_t)value; + else if (flags & MAXINT) + *va_arg(ap, intmax_t *) = value; + else if (flags & LLONG) + *va_arg(ap, long long *) = value; + else if (flags & SIZEINT) + *va_arg(ap, size_t *) = value; + else if (flags & PTRINT) + *va_arg(ap, ptrdiff_t *) = value; + else +#endif + if (flags & LONG) + *va_arg(ap, long *) = value; + else if (flags & SHORT) + *va_arg(ap, short *) = value; + else if (flags & SHORTSHORT) + *va_arg(ap, char *) = value; + else + *va_arg(ap, int *) = value; + nassigned++; + } + nread += p - buf; + break; + +#ifdef FLOATING_POINT + case CT_FLOAT: + /* scan a floating point number as if by strtod */ +#ifdef hardway + if (width == 0 || width > sizeof(buf) - 1) + width = sizeof(buf) - 1; +#else + /* size_t is unsigned, hence this optimisation */ + if (--width > sizeof(buf) - 2) + width = sizeof(buf) - 2; + width++; +#endif + flags |= SIGNOK | NDIGITS | DPTOK | EXPOK; + for (p = buf; width; width--) { + c = *fp->_p; + /* + * This code mimicks the integer conversion + * code, but is much simpler. + */ + switch (c) { + + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + case '8': case '9': + flags &= ~(SIGNOK | NDIGITS); + goto fok; + + case '+': case '-': + if (flags & SIGNOK) { + flags &= ~SIGNOK; + goto fok; + } + break; + case '.': + if (flags & DPTOK) { + flags &= ~(SIGNOK | DPTOK); + goto fok; + } + break; + case 'e': case 'E': + /* no exponent without some digits */ + if ((flags&(NDIGITS|EXPOK)) == EXPOK) { + flags = + (flags & ~(EXPOK|DPTOK)) | + SIGNOK | NDIGITS; + goto fok; + } + break; + } + break; + fok: + *p++ = c; + if (--fp->_r > 0) + fp->_p++; + else if (__srefill(fp)) + break; /* EOF */ + } + /* + * If no digits, might be missing exponent digits + * (just give back the exponent) or might be missing + * regular digits, but had sign and/or decimal point. + */ + if (flags & NDIGITS) { + if (flags & EXPOK) { + /* no digits at all */ + while (p > buf) + ungetc(*(u_char *)--p, fp); + goto match_failure; + } + /* just a bad exponent (e and maybe sign) */ + c = *(u_char *)--p; + if (c != 'e' && c != 'E') { + (void) ungetc(c, fp);/* sign */ + c = *(u_char *)--p; + } + (void) ungetc(c, fp); + } + if ((flags & SUPPRESS) == 0) { + double res; + + *p = '\0'; + res = strtod(buf, (char **) NULL); + if (flags & LONGDBL) + *va_arg(ap, long double *) = res; + else if (flags & LONG) + *va_arg(ap, double *) = res; + else + *va_arg(ap, float *) = res; + nassigned++; + } + nread += p - buf; + break; +#endif /* FLOATING_POINT */ + } + } +input_failure: + return (nassigned ? nassigned : -1); +match_failure: + return (nassigned); +} + +#ifdef TINY_SSCANF +#else +/* + * Fill in the given table from the scanset at the given format + * (just after `['). Return a pointer to the character past the + * closing `]'. The table has a 1 wherever characters should be + * considered part of the scanset. + */ +static u_char * +__sccl(char *tab, u_char *fmt) +{ + int c, n, v; + + /* first `clear' the whole table */ + c = *fmt++; /* first char hat => negated scanset */ + if (c == '^') { + v = 1; /* default => accept */ + c = *fmt++; /* get new first char */ + } else + v = 0; /* default => reject */ + /* should probably use memset here */ + for (n = 0; n < 256; n++) + tab[n] = v; + if (c == 0) + return (fmt - 1);/* format ended before closing ] */ + + /* + * Now set the entries corresponding to the actual scanset + * to the opposite of the above. + * + * The first character may be ']' (or '-') without being special; + * the last character may be '-'. + */ + v = 1 - v; + for (;;) { + tab[c] = v; /* take character c */ +doswitch: + n = *fmt++; /* and examine the next */ + switch (n) { + + case 0: /* format ended too soon */ + return (fmt - 1); + + case '-': + /* + * A scanset of the form + * [01+-] + * is defined as `the digit 0, the digit 1, + * the character +, the character -', but + * the effect of a scanset such as + * [a-zA-Z0-9] + * is implementation defined. The V7 Unix + * scanf treats `a-z' as `the letters a through + * z', but treats `a-a' as `the letter a, the + * character -, and the letter a'. + * + * For compatibility, the `-' is not considerd + * to define a range if the character following + * it is either a close bracket (required by ANSI) + * or is not numerically greater than the character + * we just stored in the table (c). + */ + n = *fmt; + if (n == ']' || n < c) { + c = '-'; + break; /* resume the for(;;) */ + } + fmt++; + do { /* fill in the range */ + tab[++c] = v; + } while (c < n); +#if 1 /* XXX another disgusting compatibility hack */ + /* + * Alas, the V7 Unix scanf also treats formats + * such as [a-c-e] as `the letters a through e'. + * This too is permitted by the standard.... + */ + goto doswitch; +#else + c = *fmt++; + if (c == 0) + return (fmt - 1); + if (c == ']') + return (fmt); +#endif + break; + + case ']': /* end of scanset */ + return (fmt); + + default: /* just another character */ + c = n; + break; + } + } + /* NOTREACHED */ +} +#endif + +int +tiny_sscanf(const char *str, const char *fmt, ...) +{ + int ret; + va_list ap; + + va_start(ap, fmt); + ret = tiny_vfscanf(str, fmt, ap); + va_end(ap); + return (ret); +} + + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_tiny_sscanf.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_tiny_sscanf.h new file mode 100644 index 00000000..f19f8631 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_tiny_sscanf.h @@ -0,0 +1,67 @@ +/******************************************************************************* +* @file stm32_tiny_sscanf.h +* @author MCD Application Team +* @brief Header for driver tiny_sscanf.c module +****************************************************************************** +* @attention +* +*

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

+* +* This software component is licensed by ST under BSD 3-Clause license, +* the "License"; You may not use this file except in compliance with the +* License. You may obtain a copy of the License at: +* opensource.org/licenses/BSD-3-Clause +* +****************************************************************************** +*/ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32_TINY_SSCANF_H__ +#define __STM32_TINY_SSCANF_H__ + +#ifdef __cplusplus +extern "C" { +#endif + + /* Includes ------------------------------------------------------------------*/ + /* Exported types ------------------------------------------------------------*/ + /* Exported constants --------------------------------------------------------*/ + /* External variables --------------------------------------------------------*/ + /* Exported macros -----------------------------------------------------------*/ + /* Exported functions ------------------------------------------------------- */ + + /** + * @brief Read formatted data from string + * + * Reads data from s and stores them according to parameter format into the + * locations given by the additional arguments, as if scanf was used, but + * reading from s instead of the standard input (stdin). + * + * The additional arguments should point to already allocated objects of the + * type specified by their corresponding format specifier within the format string. + * + * @param C string that the function processes as its source to retrieve the data. + * @param C string that contains a format string that follows the same specifications + * as format in scanf (see scanf for details). + * @param Depending on the format string, the function may expect a sequence of + * additional arguments, each containing a pointer to allocated storage + * where the interpretation of the extracted characters is stored with + * the appropriate type. + * There should be at least as many of these arguments as the number of + * values stored by the format specifiers. Additional arguments are + * ignored by the function. + * @retval The number of items in the argument list successfully filled. This + * count can match the expected number of items or be less (even zero) + * in the case of a matching failure + * @note Current supported formats are %hx, %hhx, %ul, %d,... + */ + int tiny_sscanf(const char *str, const char *fmt, ...); + +#ifdef __cplusplus +} +#endif + +#endif /* __STM32_TINY_SSCANF_H__ */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_tiny_vsnprintf.c b/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_tiny_vsnprintf.c new file mode 100644 index 00000000..a71e84cc --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_tiny_vsnprintf.c @@ -0,0 +1,669 @@ +/****************************************************************************** + * @file stm32_tiny_vsnprintf.c + * @author MCD Application Team + * @brief Tiny implementation of vsnprintf like function + ****************************************************************************** + * @attention + * + *

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

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ +/* + * Following implementation is adapted from original one + * https://github.com/jpbonn/coremark_lm32/blob/master/ee_printf.c + */ + +/* Includes ------------------------------------------------------------------*/ +#include "stm32_tiny_vsnprintf.h" + +/* Private typedef -----------------------------------------------------------*/ +/* Private defines -----------------------------------------------------------*/ +#define TINY_PRINTF + +#define ZEROPAD (1<<0) /* Pad with zero */ +#define SIGN (1<<1) /* Unsigned/signed long */ +#define UPPERCASE (1<<6) /* 'ABCDEF' */ +#ifdef TINY_PRINTF +#else +#define PLUS (1<<2) /* Show plus */ +#define HEX_PREP (1<<5) /* 0x */ +#define SPACE (1<<3) /* Spacer */ +#define LEFT (1<<4) /* Left justified */ +#endif + +#define is_digit(c) ((c) >= '0' && (c) <= '9') + +/* Private macros ------------------------------------------------------------*/ +/* Private variables ---------------------------------------------------------*/ +/* Global variables ----------------------------------------------------------*/ +/* Private function prototypes -----------------------------------------------*/ + +static char *lower_digits = "0123456789abcdefghijklmnopqrstuvwxyz"; +static char *upper_digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + +/* Functions Definition ------------------------------------------------------*/ +#ifdef TINY_PRINTF +#else +static size_t strnlen(const char *s, size_t count); + +static size_t strnlen(const char *s, size_t count) +{ + const char *sc; + for (sc = s; *sc != '\0' && count--; ++sc); + return sc - s; +} +#endif + +static int ee_skip_atoi(const char **s) +{ + int i = 0; + while (is_digit(**s)) i = i*10 + *((*s)++) - '0'; + return i; +} + +#define ASSIGN_STR(_c) do { *str++ = (_c); max_size--; if (max_size == 0) return str; } while (0) + +static char *ee_number(char *str, int max_size, long num, int base, int size, int precision, int type) +{ + char c; + char sign, tmp[66]; + char *dig = lower_digits; + int i; + + if (type & UPPERCASE) dig = upper_digits; +#ifdef TINY_PRINTF +#else + if (type & LEFT) type &= ~ZEROPAD; +#endif + if (base < 2 || base > 36) return 0; + + c = (type & ZEROPAD) ? '0' : ' '; + sign = 0; + if (type & SIGN) + { + if (num < 0) + { + sign = '-'; + num = -num; + size--; + } +#ifdef TINY_PRINTF +#else + else if (type & PLUS) + { + sign = '+'; + size--; + } + else if (type & SPACE) + { + sign = ' '; + size--; + } +#endif + } + +#ifdef TINY_PRINTF +#else + if (type & HEX_PREP) + { + if (base == 16) + size -= 2; + else if (base == 8) + size--; + } +#endif + + i = 0; + + if (num == 0) + tmp[i++] = '0'; + else + { + while (num != 0) + { + tmp[i++] = dig[((unsigned long) num) % (unsigned) base]; + num = ((unsigned long) num) / (unsigned) base; + } + } + + if (i > precision) precision = i; + size -= precision; + if (!(type & (ZEROPAD /* TINY option | LEFT */))) while (size-- > 0) ASSIGN_STR(' '); + if (sign) ASSIGN_STR(sign); + +#ifdef TINY_PRINTF +#else + if (type & HEX_PREP) + { + if (base == 8) + ASSIGN_STR('0'); + else if (base == 16) + { + ASSIGN_STR('0'); + ASSIGN_STR(lower_digits[33]); + } + } +#endif + +#ifdef TINY_PRINTF + while (size-- > 0) ASSIGN_STR(c); +#else + if (!(type & LEFT)) while (size-- > 0) ASSIGN_STR(c); +#endif + while (i < precision--) ASSIGN_STR('0'); + while (i-- > 0) ASSIGN_STR(tmp[i]); + while (size-- > 0) ASSIGN_STR(' '); + + return str; +} + +#ifdef TINY_PRINTF +#else +static char *eaddr(char *str, unsigned char *addr, int size, int precision, int type) +{ + char tmp[24]; + char *dig = lower_digits; + int i, len; + + if (type & UPPERCASE) dig = upper_digits; + len = 0; + for (i = 0; i < 6; i++) + { + if (i != 0) tmp[len++] = ':'; + tmp[len++] = dig[addr[i] >> 4]; + tmp[len++] = dig[addr[i] & 0x0F]; + } + + if (!(type & LEFT)) while (len < size--) *str++ = ' '; + for (i = 0; i < len; ++i) *str++ = tmp[i]; + while (len < size--) *str++ = ' '; + + return str; +} + +static char *iaddr(char *str, unsigned char *addr, int size, int precision, int type) +{ + char tmp[24]; + int i, n, len; + + len = 0; + for (i = 0; i < 4; i++) + { + if (i != 0) tmp[len++] = '.'; + n = addr[i]; + + if (n == 0) + tmp[len++] = lower_digits[0]; + else + { + if (n >= 100) + { + tmp[len++] = lower_digits[n / 100]; + n = n % 100; + tmp[len++] = lower_digits[n / 10]; + n = n % 10; + } + else if (n >= 10) + { + tmp[len++] = lower_digits[n / 10]; + n = n % 10; + } + + tmp[len++] = lower_digits[n]; + } + } + + if (!(type & LEFT)) while (len < size--) *str++ = ' '; + for (i = 0; i < len; ++i) *str++ = tmp[i]; + while (len < size--) *str++ = ' '; + + return str; +} +#endif + +#ifdef HAS_FLOAT + +char *ecvtbuf(double arg, int ndigits, int *decpt, int *sign, char *buf); +char *fcvtbuf(double arg, int ndigits, int *decpt, int *sign, char *buf); +static void ee_bufcpy(char *d, char *s, int count); + +void ee_bufcpy(char *pd, char *ps, int count) { + char *pe=ps+count; + while (ps!=pe) + *pd++=*ps++; +} + +static void parse_float(double value, char *buffer, char fmt, int precision) +{ + int decpt, sign, exp, pos; + char *fdigits = NULL; + char cvtbuf[80]; + int capexp = 0; + int magnitude; + + if (fmt == 'G' || fmt == 'E') + { + capexp = 1; + fmt += 'a' - 'A'; + } + + if (fmt == 'g') + { + fdigits = ecvtbuf(value, precision, &decpt, &sign, cvtbuf); + magnitude = decpt - 1; + if (magnitude < -4 || magnitude > precision - 1) + { + fmt = 'e'; + precision -= 1; + } + else + { + fmt = 'f'; + precision -= decpt; + } + } + + if (fmt == 'e') + { + fdigits = ecvtbuf(value, precision + 1, &decpt, &sign, cvtbuf); + + if (sign) *buffer++ = '-'; + *buffer++ = *fdigits; + if (precision > 0) *buffer++ = '.'; + ee_bufcpy(buffer, fdigits + 1, precision); + buffer += precision; + *buffer++ = capexp ? 'E' : 'e'; + + if (decpt == 0) + { + if (value == 0.0) + exp = 0; + else + exp = -1; + } + else + exp = decpt - 1; + + if (exp < 0) + { + *buffer++ = '-'; + exp = -exp; + } + else + *buffer++ = '+'; + + buffer[2] = (exp % 10) + '0'; + exp = exp / 10; + buffer[1] = (exp % 10) + '0'; + exp = exp / 10; + buffer[0] = (exp % 10) + '0'; + buffer += 3; + } + else if (fmt == 'f') + { + fdigits = fcvtbuf(value, precision, &decpt, &sign, cvtbuf); + if (sign) *buffer++ = '-'; + if (*fdigits) + { + if (decpt <= 0) + { + *buffer++ = '0'; + *buffer++ = '.'; + for (pos = 0; pos < -decpt; pos++) *buffer++ = '0'; + while (*fdigits) *buffer++ = *fdigits++; + } + else + { + pos = 0; + while (*fdigits) + { + if (pos++ == decpt) *buffer++ = '.'; + *buffer++ = *fdigits++; + } + } + } + else + { + *buffer++ = '0'; + if (precision > 0) + { + *buffer++ = '.'; + for (pos = 0; pos < precision; pos++) *buffer++ = '0'; + } + } + } + + *buffer = '\0'; +} + +static void decimal_point(char *buffer) +{ + while (*buffer) + { + if (*buffer == '.') return; + if (*buffer == 'e' || *buffer == 'E') break; + buffer++; + } + + if (*buffer) + { + int n = strnlen(buffer,256); + while (n > 0) + { + buffer[n + 1] = buffer[n]; + n--; + } + + *buffer = '.'; + } + else + { + *buffer++ = '.'; + *buffer = '\0'; + } +} + +static void cropzeros(char *buffer) +{ + char *stop; + + while (*buffer && *buffer != '.') buffer++; + if (*buffer++) + { + while (*buffer && *buffer != 'e' && *buffer != 'E') buffer++; + stop = buffer--; + while (*buffer == '0') buffer--; + if (*buffer == '.') buffer--; + while (buffer!=stop) + *++buffer=0; + } +} + +static char *flt(char *str, double num, int size, int precision, char fmt, int flags) +{ + char tmp[80]; + char c, sign; + int n, i; + + // Left align means no zero padding +#ifdef TINY_PRINTF +#else + if (flags & LEFT) flags &= ~ZEROPAD; +#endif + + // Determine padding and sign char + c = (flags & ZEROPAD) ? '0' : ' '; + sign = 0; + if (flags & SIGN) + { + if (num < 0.0) + { + sign = '-'; + num = -num; + size--; + } +#ifdef TINY_PRINTF +#else + else if (flags & PLUS) + { + sign = '+'; + size--; + } + else if (flags & SPACE) + { + sign = ' '; + size--; + } +#endif + } + + // Compute the precision value + if (precision < 0) + precision = 6; // Default precision: 6 + + // Convert floating point number to text + parse_float(num, tmp, fmt, precision); + +#ifdef TINY_PRINTF +#else + if ((flags & HEX_PREP) && precision == 0) decimal_point(tmp); +#endif + if (fmt == 'g' && !(flags & HEX_PREP)) cropzeros(tmp); + + n = strnlen(tmp,256); + + // Output number with alignment and padding + size -= n; + if (!(flags & (ZEROPAD | LEFT))) while (size-- > 0) *str++ = ' '; + if (sign) *str++ = sign; + if (!(flags & LEFT)) while (size-- > 0) *str++ = c; + for (i = 0; i < n; i++) *str++ = tmp[i]; + while (size-- > 0) *str++ = ' '; + + return str; +} + +#endif + +#define CHECK_STR_SIZE(_buf, _str, _size) \ + if ((((_str) - (_buf)) >= ((_size)-1))) { break; } + +int tiny_vsnprintf_like(char *buf, const int size, const char *fmt, va_list args) +{ + unsigned long num; + int base; + char *str; + int len; + int i; + char *s; + + int flags; // Flags to number() + + int field_width; // Width of output field + int precision; // Min. # of digits for integers; max number of chars for from string + int qualifier; // 'h', 'l', or 'L' for integer fields + + if (size <= 0) + { + return 0; + } + + for (str = buf; *fmt || ((str - buf) >= size-1); fmt++) + { + CHECK_STR_SIZE(buf, str, size); + + if (*fmt != '%') + { + *str++ = *fmt; + continue; + } + + // Process flags + flags = 0; +#ifdef TINY_PRINTF + /* Support %0, but not %-, %+, %space and %# */ + fmt++; + if (*fmt == '0') + { + flags |= ZEROPAD; + } +#else +repeat: + fmt++; // This also skips first '%' + switch (*fmt) + { + case '-': flags |= LEFT; goto repeat; + case '+': flags |= PLUS; goto repeat; + case ' ': flags |= SPACE; goto repeat; + case '#': flags |= HEX_PREP; goto repeat; + case '0': flags |= ZEROPAD; goto repeat; + } +#endif + + // Get field width + field_width = -1; + if (is_digit(*fmt)) + field_width = ee_skip_atoi(&fmt); +#ifdef TINY_PRINTF + /* Does not support %* */ +#else + else if (*fmt == '*') + { + fmt++; + field_width = va_arg(args, int); + if (field_width < 0) + { + field_width = -field_width; + flags |= LEFT; + } + } +#endif + + // Get the precision + precision = -1; +#ifdef TINY_PRINTF + /* Does not support %. */ +#else + if (*fmt == '.') + { + ++fmt; + if (is_digit(*fmt)) + precision = ee_skip_atoi(&fmt); + else if (*fmt == '*') + { + ++fmt; + precision = va_arg(args, int); + } + if (precision < 0) precision = 0; + } +#endif + + // Get the conversion qualifier + qualifier = -1; +#ifdef TINY_PRINTF + /* Does not support %l and %L */ +#else + if (*fmt == 'l' || *fmt == 'L') + { + qualifier = *fmt; + fmt++; + } +#endif + + // Default base + base = 10; + + switch (*fmt) + { + case 'c': +#ifdef TINY_PRINTF +#else + if (!(flags & LEFT)) +#endif + while (--field_width > 0) *str++ = ' '; + *str++ = (unsigned char) va_arg(args, int); +#ifdef TINY_PRINTF +#else + while (--field_width > 0) *str++ = ' '; +#endif + continue; + + case 's': + s = va_arg(args, char *); + if (!s) s = ""; +#ifdef TINY_PRINTF + len = strlen(s); +#else + len = strnlen(s, precision); + if (!(flags & LEFT)) +#endif + while (len < field_width--) *str++ = ' '; + for (i = 0; i < len; ++i) *str++ = *s++; +#ifdef TINY_PRINTF +#else + while (len < field_width--) *str++ = ' '; +#endif + continue; + +#ifdef TINY_PRINTF + /* Does not support %p, %A, %a, %o */ +#else + case 'p': + if (field_width == -1) + { + field_width = 2 * sizeof(void *); + flags |= ZEROPAD; + } + str = ee_number(str, (size - (str - buf)), (unsigned long) va_arg(args, void *), 16, field_width, precision, flags); + continue; + + case 'A': + flags |= UPPERCASE; + + case 'a': + if (qualifier == 'l') + str = eaddr(str, va_arg(args, unsigned char *), field_width, precision, flags); + else + str = iaddr(str, va_arg(args, unsigned char *), field_width, precision, flags); + continue; + + // Integer number formats - set up the flags and "break" + case 'o': + base = 8; + break; +#endif + + case 'X': + flags |= UPPERCASE; + + case 'x': + base = 16; + break; + + case 'd': + case 'i': + flags |= SIGN; + + case 'u': + break; + +#ifdef HAS_FLOAT + + case 'f': + str = flt(str, va_arg(args, double), field_width, precision, *fmt, flags | SIGN); + continue; + +#endif + + default: + if (*fmt != '%') *str++ = '%'; + CHECK_STR_SIZE(buf, str, size); + if (*fmt) + *str++ = *fmt; + else + --fmt; + CHECK_STR_SIZE(buf, str, size); + continue; + } + + if (qualifier == 'l') + num = va_arg(args, unsigned long); + else if (flags & SIGN) + num = va_arg(args, int); + else + num = va_arg(args, unsigned int); + + str = ee_number(str, ((size - 1) - (str - buf)), num, base, field_width, precision, flags); + } + + *str = '\0'; + return str - buf; +} +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_tiny_vsnprintf.h b/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_tiny_vsnprintf.h new file mode 100644 index 00000000..a7685997 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/BSP/Utilities/misc/stm32_tiny_vsnprintf.h @@ -0,0 +1,62 @@ +/****************************************************************************** + * @file tiny_vsnprintf.h + * @author MCD Application Team + * @brief Header for tiny_vsnprintf.c module + ****************************************************************************** + * @attention + * + *

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

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** +*/ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32_TINY_VSNPRINTF_H__ +#define __STM32_TINY_VSNPRINTF_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include +#include +/* Exported types ------------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ +/* External variables --------------------------------------------------------*/ +/* Exported functions ------------------------------------------------------- */ + +/** + * @brief Tiny implementation of vsnprintf() like function + * + * It has been adapted so that: + * - Tiny implementation, when defining TINY_PRINTF, is available. In such as case, + * not all the format are available. Instead, only %02X, %x, %d, %u, %s and %c are available. + * %f,, %+, %#, %- and others are excluded + * - Provide a snprintf like implementation. The size of the buffer is provided, + * and the length of the filled buffer is returned (not including the final '\0' char). + * The string may be truncated + * @param Pointer to a buffer where the resulting C-string is stored. The buffer should have a size of + * at least n characters. + * @param Maximum number of bytes to be used in the buffer. The generated string has a length of at + * most n-1, leaving space for the additional terminating null character. + * @param C string that contains a format string that follows the same specifications as format + * in printf (see printf for details). + * @param A value identifying a variable arguments list initialized with va_start. + * @retval The number of written char (note that this is different from vsnprintf() + */ +int tiny_vsnprintf_like(char *buf, const int size, const char *fmt, va_list args); + +#ifdef __cplusplus +} +#endif + +#endif /* __STM32_TINY_VSNPRINTF_H__*/ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/Commissioning.h b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/Commissioning.h new file mode 100644 index 00000000..07f45097 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/Commissioning.h @@ -0,0 +1,95 @@ +/** + ********************************************************************************************************************************************************** + * @file commissioning.h + * @author MCD Application Team + * @brief End device commissioning parameters + ****************************************************************************** + * @attention + * + *

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

+ * + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 + * + ****************************************************************************** + */ +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __LORA_COMMISSIONING_H__ +#define __LORA_COMMISSIONING_H__ + +#ifdef __cplusplus +extern "C" { +#endif +/*! + ****************************************************************************** + ********************************** WARNING *********************************** + ****************************************************************************** + The crypto-element implementation supports both 1.0.x and 1.1.x LoRaWAN + versions of the specification. + Thus it has been decided to use the 1.1.x keys and EUI name definitions. + The below table shows the names equivalence between versions: + +-------------------+-------------------------+ + | 1.0.x | 1.1.x | + +===================+=========================+ + | LORAWAN_DEVICE_EUI| LORAWAN_DEVICE_EUI | + +-------------------+-------------------------+ + | LORAWAN_APP_EUI | LORAWAN_JOIN_EUI | + +-------------------+-------------------------+ + | N/A | LORAWAN_APP_KEY | + +-------------------+-------------------------+ + | LORAWAN_APP_KEY | LORAWAN_NWK_KEY | + +-------------------+-------------------------+ + | LORAWAN_NWK_S_KEY | LORAWAN_F_NWK_S_INT_KEY | + +-------------------+-------------------------+ + | LORAWAN_NWK_S_KEY | LORAWAN_S_NWK_S_INT_KEY | + +-------------------+-------------------------+ + | LORAWAN_NWK_S_KEY | LORAWAN_NWK_S_ENC_KEY | + +-------------------+-------------------------+ + | LORAWAN_APP_S_KEY | LORAWAN_APP_S_KEY | + +-------------------+-------------------------+ + ****************************************************************************** + ****************************************************************************** + ****************************************************************************** + */ +/*! + * Indicates if the end-device is to be connected to a private or public network + */ +#define LORAWAN_PUBLIC_NETWORK true + +/*root key*/ +#define LORAWAN_APP_KEY {0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C } + +/*for ABP================================================================================*/ +#define STATIC_DEVICE_ADDRESS 1 //if 0 , using a ramdom address + +#define LORAWAN_NETWORK_ID ( uint32_t )0 + +#define LORAWAN_DEVICE_ADDRESS ( uint32_t )0x0100000a +#define LORAWAN_NWK_KEY { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C } + + +/*for 1.1.x*/ +#define LORAWAN_F_NWK_S_INT_KEY { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C } +#define LORAWAN_S_NWK_S_INT_KEY { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C } +#define LORAWAN_NWK_S_ENC_KEY { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C } +#define LORAWAN_APP_S_KEY { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C } + +/*for OTAA================================================================================*/ +#define STATIC_DEVICE_EUI 1 //if 0 , using a ramdom EUI + +#define IEEE_OUI 0x01, 0x01, 0x01 +#define LORAWAN_DEVICE_EUI { IEEE_OUI, 0x01, 0x01, 0x01, 0x04, 0x02 } +#define LORAWAN_JOIN_EUI /*APP_EUI*/ { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 } + + + + + +#ifdef __cplusplus +} +#endif + +#endif /* __LORA_COMMISSIONING_H__ */ diff --git a/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/app_conf.h b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/app_conf.h new file mode 100644 index 00000000..ed79f997 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/app_conf.h @@ -0,0 +1,109 @@ +/** + ****************************************************************************** + * @file app_conf.h + * @author MCD Application Team + * @brief applic configuration, e.g. : debug, trace, low power, sensors + ****************************************************************************** + * @attention + * + *

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

+ * + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 + * + ****************************************************************************** + */ + + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __APP_CONF_H__ +#define __APP_CONF_H__ + +#ifdef __cplusplus +extern "C" { +#endif + + +/****************************************************************************** + * lw APP configuration + ******************************************************************************/ + +/* lw application configuration (Mw is configured by lorawan_conf.h) */ +#define LORAWAN_DEFAULT_ACTIVE_REGION LORAMAC_REGION_CN470ALI + + + +/*enable Low power*/ +#define LOW_POWER_ENBLE 0 + +/* LPM_STOP_MODE_DISABLE = 0 : LowPowerMode enabled : MCU enters stop2 mode*/ +/* LPM_STOP_MODE_DISABLE = 1 : LowPowerMode disabled : MCU enters sleep mode only */ +#define LPM_STOP_MODE_DISABLE 0 + + +/****************************************************************************** + * classB configuration + ******************************************************************************/ +/*using class b must define LORAMAC_CLASSB_ENABLED and active ClassB*/ +/* Class B ------------------------------------*/ +//#define LORAMAC_CLASSB_ENABLED +#define LORAMAC_CLASSB_ACTIVE 0 /* 0 if not active, 1 if active */ + + +/****************************************************************************** + * DEBUGGING configuration + ******************************************************************************/ + +/* Enable trace logs. Note verbose level can be selected in app_system.h */ +#define APP_LOG_ENABLED 1 +#define MW_LOG_ENABLED 1 +/* +0 only print the informtion +1.print the function and line +2 print the file , line ,function +*/ +#define LOG_LEVEL 0 + + + +/****************************************************************************** + * Sequencer + ******************************************************************************/ +#define CFG_SEQ_Prio_0 0 + +/** +* This is the list of task id required by the application +* Each Id shall be in the range 0..1<31 +*/ +#define CFG_SEQ_TASK_LORAMAC_PROCESS (1 << 1) +#define CFG_SEQ_TASK_LORA_SEND_ON_TX_TIMER (1 << 2) +#define CFG_SEQ_TASK_LORA_CERTIF_PROCESS (1 << 3) + +/****************************************************************************** + * LOW POWER MANAGER + ******************************************************************************/ +/** + * Supported requester to the MCU Low Power Manager - can be increased up to 32 + * It lits a bit mapping of all user of the Low Power Manager + */ +typedef enum +{ + LPM_APPLI_Id = (1 << 0), + LPM_LIB_Id = (1 << 1), + LPM_RTC_Id = (1 << 2), + LPM_GPS_Id = (1 << 3), + LPM_UART_RX_Id = (1 << 4), + LPM_UART_TX_Id = (1 << 5), + LPM_TCXO_WA_Id = (1 << 6), +} LPM_Id_t; + +#ifdef __cplusplus +} +#endif + +#endif /* __APP_CONF_H__ */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/app_lorawan.c b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/app_lorawan.c new file mode 100644 index 00000000..4e6fba42 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/app_lorawan.c @@ -0,0 +1,329 @@ + +/* Includes ------------------------------------------------------------------*/ +#include "timer.h" +#include "app_system.h" +#include "app_lorawan.h" +//#include "stm32_seq.h" +#include "stm32_lpm.h" + +#include "adc_if.h" +#include "lorawan_version.h" + +#include + + +#define APP_TX_DUTYCYCLE 30000 //ms +#define LORAWAN_DOWNLINK_PORT 2 +#define LORAWAN_UPLINK_PORT 10 + +/*! + * user main_callback function declare , you need to implement them + * and register. + */ +static uint8_t LORA_GetBatteryLevel(void); +static uint16_t LORA_GetTemperatureLevel(void); +static void GetUniqueId(uint8_t *id); +static uint32_t GetRandomSeed(void); + +static void LoraMacProcessNotify(void); + + +void mac_event_callback(APP_EVENT_t evt,const void* param); + +static LoRaMainCallback_t LoRaMainCallbacks = { mac_event_callback, + LoraMacProcessNotify, + LORA_GetBatteryLevel, + LORA_GetTemperatureLevel, + GetUniqueId, + GetRandomSeed, + }; + + + +#define LORAWAN_APP_DATA_BUFF_SIZE 64 +static uint8_t app_data_buff[LORAWAN_APP_DATA_BUFF_SIZE]; +lw_app_data_t app_data = { app_data_buff, 0, 0 }; + + +/*send task function declaration*/ +static TimerEvent_t send_timer; +//static k_timer_t send_timer; +static void send_process(void *args); +static void cycle_send_event(void *context); +static void Lora_start_send(void); + +k_task_t lora_mac_process_task; +k_stack_t lora_mac_process_task_stack[512]; + +k_sem_t lora_mac_process_sem; + +//MAC²ãÈÎÎñ´¦Àí +void LoraMacProcess_task_entry(void *args) +{ + while(1) + { + tos_sem_pend(&lora_mac_process_sem, TOS_TIME_FOREVER); + LoRaMacProcess(NULL); + } + +} +/*Exported functions ---------------------------------------------------------*/ +void MX_LoRaWAN_Init(void) +{ + printf("Lora node powered by TencentOS-tiny\r\n"); + + /* Configure the Lora Stack*/ + lw_init(&LoRaMainCallbacks); + + APP_LOG("APP_VERSION= %02X.%02X.%02X.%02X\r\n", (uint8_t)(__APP_VERSION >> 24), (uint8_t)(__APP_VERSION >> 16), (uint8_t)(__APP_VERSION >> 8), (uint8_t)__APP_VERSION); + APP_LOG("MAC_VERSION= %02X.%02X.%02X.%02X\r\n", (uint8_t)(__LORA_MAC_VERSION >> 24), (uint8_t)(__LORA_MAC_VERSION >> 16), (uint8_t)(__LORA_MAC_VERSION >> 8), (uint8_t)__LORA_MAC_VERSION); + + /*Class*/ + lw_request_class(CLASS_A); + + /*channel 8-16*/ + LWChannel_mask_t ch={0xFF00,0,0,0,0,0}; + lw_current_chmask_set(&ch); + + /*tx dr*/ + lw_config_tx_datarate_set(DR_3); + + /*OTAA*/ + lw_config_otaa_set(LORA_ENABLE); + + /*enable adr*/ + lw_adr_set(LORA_ENABLE); + + /*set retry*/ + lw_confirm_retry_set(3); + + printf("lorawan init ok.\r\n"); +} + +void application_entry(void *arg) +{ + + //´´½¨ÐźÅÁ¿ + tos_sem_create_max(&lora_mac_process_sem, 0, 1); + + //´´½¨ÈÎÎñ + tos_task_create(&lora_mac_process_task, + "lora_mac_process_task", + LoraMacProcess_task_entry, + NULL, + 2, + lora_mac_process_task_stack, + sizeof(lora_mac_process_task_stack), + 10); + + Lora_start_send(); +} + + +/*============================================================== +create a send task ,the task is periodic trigger +================================================================*/ +void Lora_start_send(void) +{ + /* send everytime timer elapses */ + TimerInit(&send_timer, cycle_send_event); + TimerSetValue(&send_timer, APP_TX_DUTYCYCLE); + cycle_send_event(NULL); +} + +static void cycle_send_event(void *args) +{ + //´¥·¢´ËÈÎÎñ + //UTIL_SEQ_SetTask(CFG_SEQ_TASK_LORA_SEND_ON_TX_TIMER, CFG_SEQ_Prio_0); + send_process(NULL); + /*Wait for next tx slot*/ + TimerStart(&send_timer); + //tos_timer_start(&send_timer); +} + +static void send_process(void *args) +{ + int16_t temperature = 0; + uint8_t batteryLevel=0; + + /*Check if it is in OTAA mode*/ + if(LORA_ENABLE == lw_config_otaa_get()) + { + /*check if it is already joined*/ + if(lw_join_status() != LORA_SET) + { + lw_join(); + return; + } + } + + APP_LOG("SEND REQUEST=======================================>\n\r"); + /*fill the data*/ + uint32_t i = 0; + + app_data.port = LORAWAN_UPLINK_PORT; + + batteryLevel = LORA_GetBatteryLevel(); /* 1 (very low) to 254 (fully charged) */ + temperature = SYS_GetTemperatureLevel(); /* report Temperature */ + + app_data.buff[i++] = batteryLevel * 100 / 254; + app_data.buff[i++] = temperature>>8; + app_data.buff[i++] = temperature&0xff; + + app_data.size = i; + + /*you can set the channel by using these function*/ +// LWChannel_mask_t ch_r={0}; +// LWChannel_mask_t ch_w={0x00FF,0,0,0,0,0}; + +// lw_current_chmask_set(&ch_w); +// APP_LOG("ch_write: %04X %04X %04X %04X %04X %04X\r\n",ch_w.mask[0],ch_w.mask[1],ch_w.mask[2],ch_w.mask[3],ch_w.mask[4],ch_w.mask[5]); +// lw_current_chmask_get(&ch_r); +// APP_LOG("ch_read: %04X %04X %04X %04X %04X %04X\r\n",ch_r.mask[0],ch_r.mask[1],ch_r.mask[2],ch_r.mask[3],ch_r.mask[4],ch_r.mask[5]); + + + lw_send(&app_data, LORAWAN_CONFIRMED_MSG); + + +} + + +void mac_event_callback(APP_EVENT_t evt,const void* param) +{ + switch(evt) + { + case APP_RX_EVENT:{ + lw_app_data_t* rx_data = (lw_app_data_t*)param; + + switch(rx_data->port) + { + case 2: + APP_LOG("%s\r\n",rx_data->buff); + APP_LOG("size = %d\r\n",rx_data->size); + APP_LOG("<===============================>");break; + case 3: + lw_request_class(CLASS_A);break; + case 4: + lw_request_class(CLASS_B);break; + case 5: + lw_request_class(CLASS_C);break; + } + + if(rx_data->fpending) + { + app_data.size = 0; + app_data.port = LORAWAN_UPLINK_PORT; + lw_send(&app_data, LORAWAN_UNCONFIRMED_MSG); + } + + }break; + + case APP_JOINED_EVENT:{ + APP_LOG("end node joined \n\r"); + //lw_request_class(CLASS_A); + }break; + + case APP_CONFIRM_ClASS_EVENT:{ + DeviceClass_t* cur_class = (DeviceClass_t*)param; + APP_LOG("==================Switch to Class%c done\n\r","ABC"[*cur_class]); + /*Optionnal*/ + /*informs the server that switch has occurred ASAP*/ + app_data.size = 0; + app_data.port = LORAWAN_UPLINK_PORT; + lw_send(&app_data, LORAWAN_UNCONFIRMED_MSG); + }break; + + case APP_TX_DONE_EVENT:{ + lw_app_data_t* tx_done = (lw_app_data_t*)param; + + if(tx_done->ack){ + APP_LOG("Network Server \"ack\" an uplink data confirmed message transmission\n\r"); + } + else{ + APP_LOG("tx =======> down\n\r"); + } + }break; + + default :break; + } +} +static void LoraMacProcessNotify(void) +{ + //UTIL_SEQ_SetTask(CFG_SEQ_TASK_LORAMAC_PROCESS, CFG_SEQ_Prio_0); + //ÊÍ·ÅÐźÅÁ¿ + tos_sem_post(&lora_mac_process_sem); +} + +/** + * @brief This function return the battery level + * @param none + * @retval the battery level 1 (very low) to 254 (fully charged) + */ +static uint8_t LORA_GetBatteryLevel(void) +{ + /* USER CODE BEGIN 1 */ + uint16_t batteryLevelmV; + uint8_t batteryLevel = 0; + + batteryLevelmV = (uint16_t) SYS_GetBatteryLevel(); + + /* Convert batterey level from mV to linea scale: 1 (very low) to 254 (fully charged) */ + if(batteryLevelmV > VDD_BAT) + { + batteryLevel = 254; + } + else if(batteryLevelmV < VDD_MIN) + { + batteryLevel = 0; + } + else + { + batteryLevel = (((uint32_t)(batteryLevelmV - VDD_MIN) * 254) / (VDD_BAT - VDD_MIN)); + } + APP_LOG("VDDA= %d\n\r", batteryLevel); + + return batteryLevel; /* 1 (very low) to 254 (fully charged) */ + /* USER CODE END 1 */ +} + +static uint16_t LORA_GetTemperatureLevel(void) +{ + return (uint16_t) SYS_GetTemperatureLevel(); +} + + +/** + * @brief This function return a random seed + * @note based on the device unique ID + * @param None + * @retval see + */ +static uint32_t GetRandomSeed(void) +{ + return ((HAL_GetUIDw0()) ^ (HAL_GetUIDw1()) ^ (HAL_GetUIDw2())); +} + +/** + * @brief This function return a unique ID + * @param unique ID + * @retval none + */ +static void GetUniqueId(uint8_t *id) +{ + uint32_t ID_1_3_val = HAL_GetUIDw0() + HAL_GetUIDw2(); + uint32_t ID_2_val = HAL_GetUIDw1(); + + id[7] = (ID_1_3_val) >> 24; + id[6] = (ID_1_3_val) >> 16; + id[5] = (ID_1_3_val) >> 8; + id[4] = (ID_1_3_val); + id[3] = (ID_2_val) >> 24; + id[2] = (ID_2_val) >> 16; + id[1] = (ID_2_val) >> 8; + id[0] = (ID_2_val); +} + + + + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/app_lorawan.h b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/app_lorawan.h new file mode 100644 index 00000000..abd99aae --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/app_lorawan.h @@ -0,0 +1,57 @@ +/** + ****************************************************************************** + * @file app_lorawan.h + * @author MCD Application Team + * @brief header of application of the LRWAN Middleware + ****************************************************************************** + * @attention + * + *

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

+ * + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 + * + ****************************************************************************** + */ +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __APP_LORAWAN_H__ +#define __APP_LORAWAN_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "lora.h" + +/* Exported constants --------------------------------------------------------*/ +/* Exported types ------------------------------------------------------------*/ +/* External variables --------------------------------------------------------*/ +/* Exported macros -----------------------------------------------------------*/ +/* Exported functions ------------------------------------------------------- */ + +/** +* @brief Init Lora Application +* @param None +* @retval None +*/ +void MX_LoRaWAN_Init(void); +/** +* @brief Send Lora Data +* @param None +* @retval None +*/ +void MX_LoRaWAN_Process(void); + + +#ifdef __cplusplus +} +#endif + +#endif /*__APP_LORAWAN_H__*/ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ + diff --git a/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/app_system.c b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/app_system.c new file mode 100644 index 00000000..15d573e4 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/app_system.c @@ -0,0 +1,128 @@ +#include +#include +#include "main.h" +#include "app_system.h" +#include "adc_if.h" +//#include "stm32_seq.h" + +#include "stm32_systime.h" +#include "stm32_lpm.h" + +#include "usart_if.h" +/** + * @brief Set all pins such to minimized consumption (necessary for some STM32 families) + * @param none + * @retval None + */ +static void Gpio_PreInit(void); +/** + * @brief Returns sec and msec based on the systime in use + * @param none + * @retval none + */ +static void TimestampNow(uint8_t *buff, uint16_t* size); + +/* Exported functions ---------------------------------------------------------*/ + +/** + * @brief initialises the system (dbg pins, trace, mbmux, systiemr, LPM, ...) + * @param none + * @retval none + */ +void SystemApp_Init(void) +{ + Gpio_PreInit(); + + vcom_Init(NULL); + + /*Initialize the temperature and Battery measurement services */ + SYS_InitMeasurement(); + +#if LOW_POWER_MODE_ENBLE + /*Init low power manager*/ + UTIL_LPM_Init(); + /* Disable Stand-by mode */ + UTIL_LPM_SetOffMode(LPM_APPLI_Id, UTIL_LPM_DISABLE); + + #if defined (LPM_STOP_MODE_DISABLE) && (LPM_STOP_MODE_DISABLE == 1) + /* Disable Stop Mode */ + UTIL_LPM_SetStopMode(LPM_APPLI_Id, UTIL_LPM_DISABLE); + #elif !defined (LPM_STOP_MODE_DISABLE) + #error LPM_STOP_MODE_DISABLE not defined + #endif +#endif +} + +/** + * @brief redefines __weak function in stm32_seq.c such to enter low power + * @param none + * @retval none + */ +void UTIL_SEQ_Idle(void) +{ +#if LOW_POWER_MODE_ENBLE + UTIL_LPM_EnterLowPower(); +#endif +} + +/* Private functions ---------------------------------------------------------*/ + +static void TimestampNow(uint8_t *buff, uint16_t* size) +{ + SysTime_t curtime = SysTimeGet(); + sprintf((char *)buff, "%ds%d:", curtime.Seconds, curtime.SubSeconds); + *size = strlen((char *)buff); +} + + + +static void Gpio_PreInit(void) +{ + GPIO_InitTypeDef GPIO_InitStruct = {0}; + + /* Configure all IOs in analog input */ + /* Except PA143 and PA14 (SWCLK and SWD) for debug*/ + /* PA13 and PA14 are configured in debug_init */ + /* Configure all GPIO as analog to reduce current consumption on non used IOs */ + /* Enable GPIOs clock */ + __HAL_RCC_GPIOA_CLK_ENABLE(); + __HAL_RCC_GPIOB_CLK_ENABLE(); + __HAL_RCC_GPIOC_CLK_ENABLE(); + __HAL_RCC_GPIOH_CLK_ENABLE(); + + GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = GPIO_NOPULL; + /* All GPIOs except debug pins (SWCLK and SWD) */ + GPIO_InitStruct.Pin = GPIO_PIN_All & (~(GPIO_PIN_13 | GPIO_PIN_14)); + HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); + + /* All GPIOs */ + GPIO_InitStruct.Pin = GPIO_PIN_All; + HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); + HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); + HAL_GPIO_Init(GPIOH, &GPIO_InitStruct); + + /* Disable GPIOs clock */ + __HAL_RCC_GPIOA_CLK_DISABLE(); + __HAL_RCC_GPIOB_CLK_DISABLE(); + __HAL_RCC_GPIOC_CLK_DISABLE(); + __HAL_RCC_GPIOH_CLK_DISABLE(); +} + +/* Disable StopMode when traces need to be printed */ +void UTIL_ADV_TRACE_PreSendHook (void) +{ +#if LOW_POWER_MODE_ENBLE + UTIL_LPM_SetStopMode(LPM_UART_TX_Id , UTIL_LPM_DISABLE ); +#endif +} +/* Reenable StopMode when traces have been printed */ +void UTIL_ADV_TRACE_PostSendHook (void) +{ +#if LOW_POWER_MODE_ENBLE + UTIL_LPM_SetStopMode(LPM_UART_TX_Id , UTIL_LPM_ENABLE ); +#endif +} + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ + diff --git a/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/app_system.h b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/app_system.h new file mode 100644 index 00000000..ff147310 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/app_system.h @@ -0,0 +1,71 @@ +/** + ****************************************************************************** + * @file app_system.h + * @author MCD Application Team + * @brief function prototypes for app_system.c file + ****************************************************************************** + * @attention + * + *

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

+ * + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 + * + ****************************************************************************** + */ +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __APP_SYSTEM_H__ +#define __APP_SYSTEM_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include +#include "app_conf.h" + +#include "mw_log_conf.h" +#include + +/* Exported types ------------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ + +/* External variables --------------------------------------------------------*/ +/* Exported macros -----------------------------------------------------------*/ + +#if defined (APP_LOG_ENABLED) && (APP_LOG_ENABLED == 1) + #define APP_LOG LOG_PRINT + #define APP_PPRINTF printf + #define APP_TPRINTF printf + #define APP_PRINTF printf +#elif defined (APP_LOG_ENABLED) && (APP_LOG_ENABLED == 0) + #define APP_LOG(...) + #define APP_PPRINTF(...) + #define APP_TPRINTF(...) + #define APP_PRINTF(...) +#else + #error "APP_LOG_ENABLED not defined or out of range <0,1>" +#endif + +/* Defines -------------------------------------------------------------------*/ +/* Exported functions ------------------------------------------------------- */ + +/** + * @brief initialises the system (dbg pins, trace, systiemr, LPM, ...) + * @param none + * @retval none + */ +void SystemApp_Init(void); + + +#ifdef __cplusplus +} +#endif + +#endif /* __APP_SYSTEM_H__ */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/lorawan_version.h b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/lorawan_version.h new file mode 100644 index 00000000..429ad4b7 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/App/lorawan_version.h @@ -0,0 +1,52 @@ +/****************************************************************************** + * @file lorawan_version.h + * @author MCD Application Team + * @brief defines the lora mac version + ****************************************************************************** +* @attention + * + *

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

+ * + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __LORAWAN_VERSION_H__ +#define __LORAWAN_VERSION_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "lora_mac_version.h" +/* Exported constants --------------------------------------------------------*/ +#define __APP_VERSION_MAIN (0x00U) /*!< [31:24] main version */ +#define __APP_VERSION_SUB1 (0x04U) /*!< [23:16] sub1 version */ +#define __APP_VERSION_SUB2 (0x00U) /*!< [15:8] sub2 version */ +#define __APP_VERSION_RC (0x00U) /*!< [7:0] release candidate */ +#define __APP_VERSION ((__APP_VERSION_MAIN <<24)\ + |(__APP_VERSION_SUB1 << 16)\ + |(__APP_VERSION_SUB2 << 8 )\ + |(__APP_VERSION_RC)) + +#define __MAC_VERSION (uint32_t) (LORA_MAC_VERSION ) + +/* Exported types ------------------------------------------------------------*/ +/* External variables --------------------------------------------------------*/ +/* Exported macros -----------------------------------------------------------*/ +/* Exported functions ------------------------------------------------------- */ + +#ifdef __cplusplus +} +#endif + +#endif /*__LORAWAN_VERSION_H__*/ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/lorawan_conf.h b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/lorawan_conf.h new file mode 100644 index 00000000..d2ae10af --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/lorawan_conf.h @@ -0,0 +1,91 @@ +/** + ****************************************************************************** + * @file lorawan_conf.h + * @author MCD Application Team + * @brief configures lorawan instances + ****************************************************************************** + * @attention + * + *

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

+ * + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __LORAWAN_CONF_H__ +#define __LORAWAN_CONF_H__ + +#ifdef __cplusplus +extern "C" { +#endif +/* Includes ------------------------------------------------------------------*/ +#include "stm32_systime.h" +#include "app_conf.h" + +/* Exported types ------------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ +/* External variables --------------------------------------------------------*/ +/* Exported macros -----------------------------------------------------------*/ +/* Exported functions ------------------------------------------------------- */ +/* Defines ------------------------------------------------------------------*/ +#ifndef CRITICAL_SECTION_BEGIN +#define CRITICAL_SECTION_BEGIN( ) UTILS_ENTER_CRITICAL_SECTION( ) +#endif +#ifndef CRITICAL_SECTION_END +#define CRITICAL_SECTION_END( ) UTILS_EXIT_CRITICAL_SECTION( ) +#endif + +/* Region ------------------------------------*/ +/* the region listed here will be linked in the MW code */ +/* the applic (on app_conf.h) shall just configure one region at the time */ +//#define REGION_AS923 +//#define REGION_AU915 +#define REGION_CN470 +#define REGION_CN470ALI +//#define REGION_CN779 +//#define REGION_EU433 +#define REGION_EU868 +//#define REGION_KR920 +//#define REGION_IN865 +#define REGION_US915 +//#define REGION_RU864 + + + + +#ifdef LORAMAC_CLASSB_ENABLED +/* CLASS B LSE crystall calibration*/ + /*! + * \brief Temperature coefficient of the clock source + */ +#define RTC_TEMP_COEFFICIENT ( -0.035 ) + +/*! + * \brief Temperature coefficient deviation of the clock source + */ +#define RTC_TEMP_DEV_COEFFICIENT ( 0.0035 ) + +/*! + * \brief Turnover temperature of the clock source + */ +#define RTC_TEMP_TURNOVER ( 25.0 ) + +/*! + * \brief Turnover temperature deviation of the clock source + */ +#define RTC_TEMP_DEV_TURNOVER ( 5.0 ) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __LORAWAN_CONF_H__ */ +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ + diff --git a/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/mw_log_conf.h b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/mw_log_conf.h new file mode 100644 index 00000000..af4aa905 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/mw_log_conf.h @@ -0,0 +1,73 @@ +/** + ****************************************************************************** + * @file mw_log_conf.h + * @author MCD Application Team + * @brief Interface layer CM4 System to MBMUX (Mailbox Multiplexer) + ******************************************************************************* + * @attention + * + *

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

+ * + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 + * + ****************************************************************************** + */ + + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __MW_LOG_CONF_H__ +#define __MW_LOG_CONF_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include +#include +#include "app_conf.h" +/* Exported types ------------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ + +/* Exported macros -----------------------------------------------------------*/ + +#if LOG_LEVEL == 0 + #define LOG_PRINT printf +#elif LOG_LEVEL == 1 + #define LOG_PRINT(...) do{printf("<>\r\n"\ + ,__LINE__, __FUNCTION__);\ + printf(__VA_ARGS__);}while(0); +#elif LOG_LEVEL == 2 + #define LOG_PRINT(...) do{printf("<<<%s ->line:%d ;function:%s >>>\r\n"\ + ,__FILE__,__LINE__, __FUNCTION__);\ + printf(__VA_ARGS__);}while(0); +#endif + +#if defined (MW_LOG_ENABLED) && (MW_LOG_ENABLED == 1) + #define PPRINTF printf + #define TPRINTF printf + #define PRINTF printf + #define MW_LOG LOG_PRINT +#else + #define PPRINTF(...) + #define TPRINTF(...) + #define PRINTF(...) + #define MW_LOG(...) +#endif + +/* Defines -------------------------------------------------------------------*/ +/* Exported functions ------------------------------------------------------- */ + + + +#ifdef __cplusplus +} +#endif + +#endif /*__MW_LOG_CONF_H__ */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/radio_conf.h b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/radio_conf.h new file mode 100644 index 00000000..14ef11a7 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/radio_conf.h @@ -0,0 +1,64 @@ +/******************************************************************************* + * @file radio_conf.h + * @author MCD Application Team + * @brief Header of radio configuration + ****************************************************************************** + * @attention + * + *

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

+ * + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 + * + ****************************************************************************** + */ +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __RADIO_CONF_H__ +#define __RADIO_CONF_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "platform.h" +#include "mw_log_conf.h" + +#include "radio_driver.h" +#include "app_conf.h" +#include "stm32_mem.h" + +#ifndef CRITICAL_SECTION_BEGIN +#define CRITICAL_SECTION_BEGIN( ) UTILS_ENTER_CRITICAL_SECTION( ) +#endif +#ifndef CRITICAL_SECTION_END +#define CRITICAL_SECTION_END( ) UTILS_EXIT_CRITICAL_SECTION( ) +#endif +/* Exported types ------------------------------------------------------------*/ +/* Defines ------------------------*/ +#define RADIO_RX_BUF_SIZE 255 + +//#define LET_SUBGHZ_MW_USING_DGB_LINE1_PIN +//#define LET_SUBGHZ_MW_USING_DGB_LINE2_PIN + +/* Function mapping */ +#define RADIO_DELAY_MS HAL_Delay +#define RADIO_MEMSET8( dest, value, size ) UTIL_MEM_set_8(dest, value, size ) + + +/* Exported constants --------------------------------------------------------*/ +/* External variables --------------------------------------------------------*/ +/* Exported macros -----------------------------------------------------------*/ +/* Exported functions ------------------------------------------------------- */ + + +#ifdef __cplusplus +} +#endif + +#endif /* __RADIO_CONF_H__*/ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/systime.h b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/systime.h new file mode 100644 index 00000000..31ab25db --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/systime.h @@ -0,0 +1,41 @@ +/****************************************************************************** + * @file systime.h + * @author MCD Application Team + * @brief map middleware systime + * @attention + * + *

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

+ * + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 + * + ****************************************************************************** + */ +/* Define to prevent recursive inclusion -------------------------------------*/ + +#ifndef __SYSTIME_H__ +#define __SYSTIME_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "stm32_systime.h" +/* Exported constants --------------------------------------------------------*/ +/* Exported types ------------------------------------------------------------*/ +/* External variables --------------------------------------------------------*/ +/* Exported macros -----------------------------------------------------------*/ +/* Exported functions ------------------------------------------------------- */ + + +#ifdef __cplusplus +} +#endif + +#endif /*__SYSTIME_H__*/ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/timer.h b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/timer.h new file mode 100644 index 00000000..97912016 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/timer.h @@ -0,0 +1,72 @@ +/****************************************************************************** + * @file timer.h + * @author MCD Application Team + * @brief wrapper to timer server + ****************************************************************************** + * @attention + * + *

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

+ * + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 + * + ****************************************************************************** + */ +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __TIMER_H__ +#define __TIMER_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +//#include "stm32_timer.h" +#include + +/* Exported types ------------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ +#define TIMER_MAX_VAL 0xFFFFFFFFU + +/* External variables --------------------------------------------------------*/ +/* Exported macros -----------------------------------------------------------*/ +#define TimerTime_t k_tick_t + +#define TimerEvent_t k_timer_t + +#define TimerInit(HANDLE, CB) do {\ + tos_timer_create( HANDLE, TIMER_MAX_VAL, 0, CB, NULL, TOS_OPT_TIMER_ONESHOT);\ + } while(0) + +#define TimerSetValue(HANDLE, TIMEOUT) do{ \ + tos_timer_delay_change(HANDLE, TIMEOUT);\ + } while(0) + +#define TimerStart(HANDLE) do {\ + tos_timer_start(HANDLE);\ + } while(0) + +#define TimerStop(HANDLE) do {\ + tos_timer_stop(HANDLE);\ + } while(0) + +#define TimerGetCurrentTime TIMER_GetCurrentTime + +#define TimerGetElapsedTime TIMER_GetElapsedTime + +uint8_t TIMER_GetCurrentTime(void); + +uint8_t TIMER_GetElapsedTime(TimerTime_t past); + +/* Exported functions ------------------------------------------------------- */ + +#ifdef __cplusplus +} +#endif + +#endif /* __TIMER_H__*/ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/timer_wrapper.c b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/timer_wrapper.c new file mode 100644 index 00000000..7e541fab --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/timer_wrapper.c @@ -0,0 +1,18 @@ +#include +#include "timer.h" +#include "rtc_if.h" + + +uint8_t TIMER_GetCurrentTime(void) +{ + uint32_t now = RTC_GetTimerValue( ); + return RTC_Tick2ms(now); +} + +uint8_t TIMER_GetElapsedTime(TimerTime_t past) +{ + uint32_t nowInTicks = RTC_GetTimerValue(); + uint32_t pastInTicks = RTC_ms2Tick(past); + /* intentional wrap around. Works Ok if tick duation below 1ms */ + return RTC_Tick2ms(nowInTicks - pastInTicks); +} \ No newline at end of file diff --git a/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/utilities_conf.h b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/utilities_conf.h new file mode 100644 index 00000000..914fa6f5 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/Target/utilities_conf.h @@ -0,0 +1,118 @@ +/** + ****************************************************************************** + * @file utilities_conf.h + * @author MCD Application Team + * @brief Configuration file to utilities + ****************************************************************************** + * @attention + * + *

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

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ + + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef UTILITIES_CONF_H +#define UTILITIES_CONF_H + +#ifdef __cplusplus +extern "C" { +#endif + + + +/* Includes ------------------------------------------------------------------*/ +#include "stm32wlxx.h" + +/* definitions to be provided to "sequencer" utility */ +#include "stm32_mem.h" +/* definition and callback for tiny_vsnprintf */ +#include "stm32_tiny_vsnprintf.h" + +/* Exported types ------------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ + +/****************************************************************************** + * Common + ******************************************************************************/ +#if defined(__CC_ARM) +#define UTIL_PLACE_IN_SECTION( __x__ ) __attribute__((section (__x__), zero_init)) +#elif defined(__ICCARM__) +#define UTIL_PLACE_IN_SECTION( __x__ ) __attribute__((section (__x__))) +#else /* __GNUC__ */ +#define UTIL_PLACE_IN_SECTION( __x__ ) __attribute__((section (__x__))) +#endif + +#undef ALIGN +#ifdef WIN32 +#define ALIGN(n) +#else +#define ALIGN(n) __attribute__((aligned(n))) +#endif + +/*---------------------------------------------------------------------------*/ +/* sequencer configuration */ +/*---------------------------------------------------------------------------*/ +#define UTIL_SEQ_RFU 0 + +#define UTIL_SEQ_INIT_CRITICAL_SECTION( ) UTILS_INIT_CRITICAL_SECTION() +#define UTIL_SEQ_ENTER_CRITICAL_SECTION( ) UTILS_ENTER_CRITICAL_SECTION() +#define UTIL_SEQ_EXIT_CRITICAL_SECTION( ) UTILS_EXIT_CRITICAL_SECTION() +#define UTIL_SEQ_MEMSET8( dest, value, size ) UTIL_MEM_set_8( dest, value, size ) + + +/* External variables --------------------------------------------------------*/ +/* Exported macros -----------------------------------------------------------*/ +#define UTILS_INIT_CRITICAL_SECTION() +#define UTILS_ENTER_CRITICAL_SECTION() uint32_t primask_bit= __get_PRIMASK();\ + __disable_irq() +#define UTILS_EXIT_CRITICAL_SECTION() __set_PRIMASK(primask_bit) + + +/* Exported functions ------------------------------------------------------- */ +/****************************************************************************** + * trace\advanced + * the define option + * UTIL_ADV_TRACE_CONDITIONNAL shall be defined if you want use conditionnal function + * UTIL_ADV_TRACE_UNCHUNK_MODE shall be defined if you want use the unchunk mode + * + ******************************************************************************/ + + +#define VLEVEL_OFF 0 +#define VLEVEL_L 1 /* just essential traces */ +#define VLEVEL_M 2 +#define VLEVEL_H 3 /* all traces */ + +#define TS_OFF 0 /*Log without TimeStamp*/ +#define TS_ON 1 /*Log with TimeStamp*/ + +#define T_REG_OFF 0 /*Log without bitmask*/ + +#define UTIL_ADV_TRACE_CONDITIONNAL +#define UTIL_ADV_TRACE_UNCHUNK_MODE +#define UTIL_ADV_TRACE_DEBUG(...) +#define UTIL_ADV_TRACE_INIT_CRITICAL_SECTION( ) UTILS_INIT_CRITICAL_SECTION() +#define UTIL_ADV_TRACE_ENTER_CRITICAL_SECTION( ) UTILS_ENTER_CRITICAL_SECTION() +#define UTIL_ADV_TRACE_EXIT_CRITICAL_SECTION( ) UTILS_EXIT_CRITICAL_SECTION() +#define UTIL_ADV_TRACE_TMP_BUF_SIZE (256U) +#define UTIL_ADV_TRACE_TMP_MAX_TIMESTMAP_SIZE (15U) +#define UTIL_ADV_TRACE_FIFO_SIZE (512U) +#define UTIL_ADV_TRACE_MEMSET8( dest, value, size) UTIL_MEM_set_8((dest),(value),(size)) +#define UTIL_ADV_TRACE_VSNPRINTF(...) tiny_vsnprintf_like(__VA_ARGS__) + + +#ifdef __cplusplus +} +#endif + +#endif /*UTILITIES_CONF_H */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/TencentOS_Tiny.uvoptx b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/TencentOS_Tiny.uvoptx new file mode 100644 index 00000000..3eb26311 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/TencentOS_Tiny.uvoptx @@ -0,0 +1,1666 @@ + + + + 1.0 + +
### uVision Project, (C) Keil Software
+ + + *.c + *.s*; *.src; *.a* + *.obj; *.o + *.lib + *.txt; *.h; *.inc + *.plm + *.cpp + 0 + + + + 0 + 0 + + + + TencentOS_Tiny + 0x4 + ARM-ADS + + 12000000 + + 1 + 1 + 0 + 1 + 0 + + + 1 + 65535 + 0 + 0 + 0 + + + 79 + 66 + 8 + .\List\ + + + 1 + 1 + 1 + 0 + 1 + 1 + 0 + 1 + 0 + 0 + 0 + 0 + + + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 0 + 0 + + + 1 + 0 + 1 + + 18 + + 0 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 0 + 0 + 1 + 0 + 0 + 6 + + + + + + + + + + + STLink\ST-LINKIII-KEIL_SWO.dll + + + + 0 + JL2CM3 + -U260106173 -O78 -S2 -ZTIFSpeedSel5000 -A0 -C0 -JU1 -JI127.0.0.1 -JP0 -RST0 -TO18 -TC10000000 -TP21 -TDS8000 -TDT0 -TDC1F -TIEFFFFFFFF -TIP8 -TB1 -TFE0 -FO15 -FD20000000 -FC1000 -FN1 -FF0STM32WLxx_CM4.FLM -FS08000000 -FL040000 -FP0($$Device:STM32WLE5JCIx$CMSIS\Flash\STM32WLxx_CM4.FLM) + + + 0 + UL2CM3 + UL2CM3(-S0 -C0 -P0 ) -FN1 -FC1000 -FD20000000 -FF0STM32WLxx_CM4 -FL040000 -FS08000000 -FP0($$Device:STM32WLE5JCIx$CMSIS\Flash\STM32WLxx_CM4.FLM) + + + 0 + ST-LINKIII-KEIL_SWO + -U066DFF495056805087213734 -O206 -SF4000 -C0 -A0 -I0 -HNlocalhost -HP7184 -P1 -N00("ARM CoreSight SW-DP") -D00(6BA02477) -L00(0) -TO18 -TC10000000 -TP21 -TDS8000 -TDT0 -TDC1F -TIEFFFFFFFF -TIP8 -FO15 -FD20000000 -FC1000 -FN1 -FF0STM32WLxx_CM4.FLM -FS08000000 -FL040000 -FP0($$Device:STM32WLE5JCIx$CMSIS\Flash\STM32WLxx_CM4.FLM) + + + 0 + ARMRTXEVENTFLAGS + -L70 -Z18 -C0 -M0 -T1 + + + 0 + DLGTARM + (1010=-1,-1,-1,-1,0)(1007=-1,-1,-1,-1,0)(1008=-1,-1,-1,-1,0)(1009=-1,-1,-1,-1,0)(1012=-1,-1,-1,-1,0) + + + 0 + ARMDBGFLAGS + + + + 0 + DLGUARM + (105=-1,-1,-1,-1,0) + + + + + 0 + 0 + 122 + 0 +
134244104
+ 0 + 0 + 0 + 0 + 0 + 1 + ..\..\..\..\kernel\core\tos_task.c + + \\TencentOS_Tiny\../../../../kernel/core/tos_task.c\122 +
+ + 1 + 0 + 105 + 1 +
134218200
+ 0 + 0 + 0 + 0 + 0 + 1 + ..\..\..\..\arch\arm\arm-v7m\cortex-m4\armcc\port_s.S + + \\TencentOS_Tiny\../../../../arch/arm/arm-v7m/cortex-m4/armcc/port_s.S\105 +
+ + 2 + 0 + 97 + 1 +
134218196
+ 0 + 0 + 0 + 0 + 0 + 1 + ..\..\..\..\arch\arm\arm-v7m\cortex-m4\armcc\port_s.S + + \\TencentOS_Tiny\../../../../arch/arm/arm-v7m/cortex-m4/armcc/port_s.S\97 +
+
+ + + 0 + 1 + task + + + + 0 + + + 0 + 1 + 1 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + + + + 0 + 0 + 0 + + + + + + + + +
+
+ + + Application/MDK-ARM + 0 + 0 + 0 + 0 + + 1 + 1 + 2 + 0 + 0 + 0 + .\startup_stm32wle5xx.s + startup_stm32wle5xx.s + 0 + 0 + + + + + Application/User + 1 + 0 + 0 + 0 + + 2 + 2 + 1 + 0 + 0 + 0 + ..\..\BSP\Src\adc.c + adc.c + 0 + 0 + + + 2 + 3 + 1 + 0 + 0 + 0 + ..\..\BSP\Src\adc_if.c + adc_if.c + 0 + 0 + + + 2 + 4 + 1 + 0 + 0 + 0 + ..\..\BSP\Src\dma.c + dma.c + 0 + 0 + + + 2 + 5 + 1 + 0 + 0 + 0 + ..\..\BSP\Src\gpio.c + gpio.c + 0 + 0 + + + 2 + 6 + 1 + 0 + 0 + 0 + ..\..\BSP\Src\main.c + main.c + 0 + 0 + + + 2 + 7 + 1 + 0 + 0 + 0 + ..\..\BSP\Src\mcu_init.c + mcu_init.c + 0 + 0 + + + 2 + 8 + 1 + 0 + 0 + 0 + ..\..\BSP\Src\rtc.c + rtc.c + 0 + 0 + + + 2 + 9 + 1 + 0 + 0 + 0 + ..\..\BSP\Src\rtc_if.c + rtc_if.c + 0 + 0 + + + 2 + 10 + 1 + 0 + 0 + 0 + ..\..\BSP\Src\stm32_lpm_if.c + stm32_lpm_if.c + 0 + 0 + + + 2 + 11 + 1 + 0 + 0 + 0 + ..\..\BSP\Src\stm32wlxx_hal_msp.c + stm32wlxx_hal_msp.c + 0 + 0 + + + 2 + 12 + 1 + 0 + 0 + 0 + ..\..\BSP\Src\stm32wlxx_it.c + stm32wlxx_it.c + 0 + 0 + + + 2 + 13 + 1 + 0 + 0 + 0 + ..\..\BSP\Src\usart.c + usart.c + 0 + 0 + + + 2 + 14 + 1 + 0 + 0 + 0 + ..\..\BSP\Src\usart_if.c + usart_if.c + 0 + 0 + + + + + Drivers/STM32WLxx_HAL_Driver + 0 + 0 + 0 + 0 + + 3 + 15 + 1 + 0 + 0 + 0 + ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal.c + stm32wlxx_hal.c + 0 + 0 + + + 3 + 16 + 1 + 0 + 0 + 0 + ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_cortex.c + stm32wlxx_hal_cortex.c + 0 + 0 + + + 3 + 17 + 1 + 0 + 0 + 0 + ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_dma.c + stm32wlxx_hal_dma.c + 0 + 0 + + + 3 + 18 + 1 + 0 + 0 + 0 + ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_dma_ex.c + stm32wlxx_hal_dma_ex.c + 0 + 0 + + + 3 + 19 + 1 + 0 + 0 + 0 + ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_exti.c + stm32wlxx_hal_exti.c + 0 + 0 + + + 3 + 20 + 1 + 0 + 0 + 0 + ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_flash.c + stm32wlxx_hal_flash.c + 0 + 0 + + + 3 + 21 + 1 + 0 + 0 + 0 + ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_flash_ex.c + stm32wlxx_hal_flash_ex.c + 0 + 0 + + + 3 + 22 + 1 + 0 + 0 + 0 + ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_gpio.c + stm32wlxx_hal_gpio.c + 0 + 0 + + + 3 + 23 + 1 + 0 + 0 + 0 + ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_pwr.c + stm32wlxx_hal_pwr.c + 0 + 0 + + + 3 + 24 + 1 + 0 + 0 + 0 + ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_pwr_ex.c + stm32wlxx_hal_pwr_ex.c + 0 + 0 + + + 3 + 25 + 1 + 0 + 0 + 0 + ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_rcc.c + stm32wlxx_hal_rcc.c + 0 + 0 + + + 3 + 26 + 1 + 0 + 0 + 0 + ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_rcc_ex.c + stm32wlxx_hal_rcc_ex.c + 0 + 0 + + + 3 + 27 + 1 + 0 + 0 + 0 + ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_tim.c + stm32wlxx_hal_tim.c + 0 + 0 + + + 3 + 28 + 1 + 0 + 0 + 0 + ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_tim_ex.c + stm32wlxx_hal_tim_ex.c + 0 + 0 + + + 3 + 29 + 1 + 0 + 0 + 0 + ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_uart.c + stm32wlxx_hal_uart.c + 0 + 0 + + + 3 + 30 + 1 + 0 + 0 + 0 + ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_uart_ex.c + stm32wlxx_hal_uart_ex.c + 0 + 0 + + + 3 + 31 + 1 + 0 + 0 + 0 + ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_adc.c + stm32wlxx_hal_adc.c + 0 + 0 + + + 3 + 32 + 1 + 0 + 0 + 0 + ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_adc_ex.c + stm32wlxx_hal_adc_ex.c + 0 + 0 + + + 3 + 33 + 1 + 0 + 0 + 0 + ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_rtc.c + stm32wlxx_hal_rtc.c + 0 + 0 + + + 3 + 34 + 1 + 0 + 0 + 0 + ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_rtc_ex.c + stm32wlxx_hal_rtc_ex.c + 0 + 0 + + + 3 + 35 + 1 + 0 + 0 + 0 + ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_subghz.c + stm32wlxx_hal_subghz.c + 0 + 0 + + + + + Drivers/CMSIS + 0 + 0 + 0 + 0 + + 4 + 36 + 1 + 0 + 0 + 0 + ..\..\BSP\Src\system_stm32wlxx.c + system_stm32wlxx.c + 0 + 0 + + + + + tos/arch + 0 + 0 + 0 + 0 + + 5 + 37 + 1 + 0 + 0 + 0 + ..\..\..\..\arch\arm\arm-v7m\common\tos_cpu.c + tos_cpu.c + 0 + 0 + + + 5 + 38 + 1 + 0 + 0 + 0 + ..\..\..\..\arch\arm\arm-v7m\cortex-m4\armcc\port_c.c + port_c.c + 0 + 0 + + + 5 + 39 + 2 + 0 + 0 + 0 + ..\..\..\..\arch\arm\arm-v7m\cortex-m4\armcc\port_s.S + port_s.S + 0 + 0 + + + + + tos/kernel + 0 + 0 + 0 + 0 + + 6 + 40 + 1 + 0 + 0 + 0 + ..\..\..\..\kernel\core\tos_barrier.c + tos_barrier.c + 0 + 0 + + + 6 + 41 + 1 + 0 + 0 + 0 + ..\..\..\..\kernel\core\tos_binary_heap.c + tos_binary_heap.c + 0 + 0 + + + 6 + 42 + 1 + 0 + 0 + 0 + ..\..\..\..\kernel\core\tos_bitmap.c + tos_bitmap.c + 0 + 0 + + + 6 + 43 + 1 + 0 + 0 + 0 + ..\..\..\..\kernel\core\tos_char_fifo.c + tos_char_fifo.c + 0 + 0 + + + 6 + 44 + 1 + 0 + 0 + 0 + ..\..\..\..\kernel\core\tos_completion.c + tos_completion.c + 0 + 0 + + + 6 + 45 + 1 + 0 + 0 + 0 + ..\..\..\..\kernel\core\tos_countdownlatch.c + tos_countdownlatch.c + 0 + 0 + + + 6 + 46 + 1 + 0 + 0 + 0 + ..\..\..\..\kernel\core\tos_event.c + tos_event.c + 0 + 0 + + + 6 + 47 + 1 + 0 + 0 + 0 + ..\..\..\..\kernel\core\tos_global.c + tos_global.c + 0 + 0 + + + 6 + 48 + 1 + 0 + 0 + 0 + ..\..\..\..\kernel\core\tos_mail_queue.c + tos_mail_queue.c + 0 + 0 + + + 6 + 49 + 1 + 0 + 0 + 0 + ..\..\..\..\kernel\core\tos_message_queue.c + tos_message_queue.c + 0 + 0 + + + 6 + 50 + 1 + 0 + 0 + 0 + ..\..\..\..\kernel\core\tos_mmblk.c + tos_mmblk.c + 0 + 0 + + + 6 + 51 + 1 + 0 + 0 + 0 + ..\..\..\..\kernel\core\tos_mmheap.c + tos_mmheap.c + 0 + 0 + + + 6 + 52 + 1 + 0 + 0 + 0 + ..\..\..\..\kernel\core\tos_mutex.c + tos_mutex.c + 0 + 0 + + + 6 + 53 + 1 + 0 + 0 + 0 + ..\..\..\..\kernel\core\tos_pend.c + tos_pend.c + 0 + 0 + + + 6 + 54 + 1 + 0 + 0 + 0 + ..\..\..\..\kernel\core\tos_priority_mail_queue.c + tos_priority_mail_queue.c + 0 + 0 + + + 6 + 55 + 1 + 0 + 0 + 0 + ..\..\..\..\kernel\core\tos_priority_message_queue.c + tos_priority_message_queue.c + 0 + 0 + + + 6 + 56 + 1 + 0 + 0 + 0 + ..\..\..\..\kernel\core\tos_priority_queue.c + tos_priority_queue.c + 0 + 0 + + + 6 + 57 + 1 + 0 + 0 + 0 + ..\..\..\..\kernel\core\tos_ring_queue.c + tos_ring_queue.c + 0 + 0 + + + 6 + 58 + 1 + 0 + 0 + 0 + ..\..\..\..\kernel\core\tos_robin.c + tos_robin.c + 0 + 0 + + + 6 + 59 + 1 + 0 + 0 + 0 + ..\..\..\..\kernel\core\tos_rwlock.c + tos_rwlock.c + 0 + 0 + + + 6 + 60 + 1 + 0 + 0 + 0 + ..\..\..\..\kernel\core\tos_sched.c + tos_sched.c + 0 + 0 + + + 6 + 61 + 1 + 0 + 0 + 0 + ..\..\..\..\kernel\core\tos_sem.c + tos_sem.c + 0 + 0 + + + 6 + 62 + 1 + 0 + 0 + 0 + ..\..\..\..\kernel\core\tos_stopwatch.c + tos_stopwatch.c + 0 + 0 + + + 6 + 63 + 1 + 0 + 0 + 0 + ..\..\..\..\kernel\core\tos_sys.c + tos_sys.c + 0 + 0 + + + 6 + 64 + 1 + 0 + 0 + 0 + ..\..\..\..\kernel\core\tos_task.c + tos_task.c + 0 + 0 + + + 6 + 65 + 1 + 0 + 0 + 0 + ..\..\..\..\kernel\core\tos_tick.c + tos_tick.c + 0 + 0 + + + 6 + 66 + 1 + 0 + 0 + 0 + ..\..\..\..\kernel\core\tos_time.c + tos_time.c + 0 + 0 + + + 6 + 67 + 1 + 0 + 0 + 0 + ..\..\..\..\kernel\core\tos_timer.c + tos_timer.c + 0 + 0 + + + + + tos/cmsis + 0 + 0 + 0 + 0 + + 7 + 68 + 1 + 0 + 0 + 0 + ..\..\..\..\osal\cmsis_os\cmsis_os.c + cmsis_os.c + 0 + 0 + + + + + examples + 1 + 0 + 0 + 0 + + + + TOS-CONFIG + 0 + 0 + 0 + 0 + + 9 + 69 + 5 + 0 + 0 + 0 + ..\..\TOS_CONFIG\tos_config.h + tos_config.h + 0 + 0 + + + + + APP + 1 + 0 + 0 + 0 + + 10 + 70 + 1 + 0 + 0 + 0 + .\App\app_lorawan.c + app_lorawan.c + 0 + 0 + + + 10 + 71 + 1 + 0 + 0 + 0 + .\App\app_system.c + app_system.c + 0 + 0 + + + 10 + 72 + 5 + 0 + 0 + 0 + .\App\app_conf.h + app_conf.h + 0 + 0 + + + 10 + 73 + 5 + 0 + 0 + 0 + .\App\Commissioning.h + Commissioning.h + 0 + 0 + + + 10 + 74 + 1 + 0 + 0 + 0 + .\Target\timer_wrapper.c + timer_wrapper.c + 0 + 0 + + + + + lorawan/pattern + 0 + 0 + 0 + 0 + + 11 + 75 + 1 + 0 + 0 + 0 + ..\..\BSP\Middlewares\LoRaWAN\Patterns\Basic\lora.c + lora.c + 0 + 0 + + + 11 + 76 + 1 + 0 + 0 + 0 + ..\..\BSP\Middlewares\LoRaWAN\Patterns\Basic\lorawan_info.c + lorawan_info.c + 0 + 0 + + + + + lorawan/mac + 0 + 0 + 0 + 0 + + 12 + 77 + 1 + 0 + 0 + 0 + ..\..\BSP\Middlewares\LoRaWAN\Crypto\aes.c + aes.c + 0 + 0 + + + 12 + 78 + 1 + 0 + 0 + 0 + ..\..\BSP\Middlewares\LoRaWAN\Crypto\cmac.c + cmac.c + 0 + 0 + + + 12 + 79 + 1 + 0 + 0 + 0 + ..\..\BSP\Middlewares\LoRaWAN\Crypto\soft-se.c + soft-se.c + 0 + 0 + + + 12 + 80 + 1 + 0 + 0 + 0 + ..\..\BSP\Middlewares\LoRaWAN\Mac\LoRaMac.c + LoRaMac.c + 0 + 0 + + + 12 + 81 + 1 + 0 + 0 + 0 + ..\..\BSP\Middlewares\LoRaWAN\Mac\LoRaMacAdr.c + LoRaMacAdr.c + 0 + 0 + + + 12 + 82 + 1 + 0 + 0 + 0 + ..\..\BSP\Middlewares\LoRaWAN\Mac\LoRaMacClassB.c + LoRaMacClassB.c + 0 + 0 + + + 12 + 83 + 1 + 0 + 0 + 0 + ..\..\BSP\Middlewares\LoRaWAN\Mac\LoRaMacCommands.c + LoRaMacCommands.c + 0 + 0 + + + 12 + 84 + 1 + 0 + 0 + 0 + ..\..\BSP\Middlewares\LoRaWAN\Mac\LoRaMacConfirmQueue.c + LoRaMacConfirmQueue.c + 0 + 0 + + + 12 + 85 + 1 + 0 + 0 + 0 + ..\..\BSP\Middlewares\LoRaWAN\Mac\LoRaMacCrypto.c + LoRaMacCrypto.c + 0 + 0 + + + 12 + 86 + 1 + 0 + 0 + 0 + ..\..\BSP\Middlewares\LoRaWAN\Mac\LoRaMacParser.c + LoRaMacParser.c + 0 + 0 + + + 12 + 87 + 1 + 0 + 0 + 0 + ..\..\BSP\Middlewares\LoRaWAN\Mac\LoRaMacSerializer.c + LoRaMacSerializer.c + 0 + 0 + + + + + lorawan/region + 0 + 0 + 0 + 0 + + 13 + 88 + 1 + 0 + 0 + 0 + ..\..\BSP\Middlewares\LoRaWAN\Mac\region\Region.c + Region.c + 0 + 0 + + + 13 + 89 + 1 + 0 + 0 + 0 + ..\..\BSP\Middlewares\LoRaWAN\Mac\region\RegionAS923.c + RegionAS923.c + 0 + 0 + + + 13 + 90 + 1 + 0 + 0 + 0 + ..\..\BSP\Middlewares\LoRaWAN\Mac\region\RegionAU915.c + RegionAU915.c + 0 + 0 + + + 13 + 91 + 1 + 0 + 0 + 0 + ..\..\BSP\Middlewares\LoRaWAN\Mac\region\RegionCN470.c + RegionCN470.c + 0 + 0 + + + 13 + 92 + 1 + 0 + 0 + 0 + ..\..\BSP\Middlewares\LoRaWAN\Mac\region\RegionCN470ALI.c + RegionCN470ALI.c + 0 + 0 + + + 13 + 93 + 1 + 0 + 0 + 0 + ..\..\BSP\Middlewares\LoRaWAN\Mac\region\RegionCN779.c + RegionCN779.c + 0 + 0 + + + 13 + 94 + 1 + 0 + 0 + 0 + ..\..\BSP\Middlewares\LoRaWAN\Mac\region\RegionCommon.c + RegionCommon.c + 0 + 0 + + + 13 + 95 + 1 + 0 + 0 + 0 + ..\..\BSP\Middlewares\LoRaWAN\Mac\region\RegionEU433.c + RegionEU433.c + 0 + 0 + + + 13 + 96 + 1 + 0 + 0 + 0 + ..\..\BSP\Middlewares\LoRaWAN\Mac\region\RegionEU868.c + RegionEU868.c + 0 + 0 + + + 13 + 97 + 1 + 0 + 0 + 0 + ..\..\BSP\Middlewares\LoRaWAN\Mac\region\RegionIN865.c + RegionIN865.c + 0 + 0 + + + 13 + 98 + 1 + 0 + 0 + 0 + ..\..\BSP\Middlewares\LoRaWAN\Mac\region\RegionKR920.c + RegionKR920.c + 0 + 0 + + + 13 + 99 + 1 + 0 + 0 + 0 + ..\..\BSP\Middlewares\LoRaWAN\Mac\region\RegionRU864.c + RegionRU864.c + 0 + 0 + + + 13 + 100 + 1 + 0 + 0 + 0 + ..\..\BSP\Middlewares\LoRaWAN\Mac\region\RegionUS915.c + RegionUS915.c + 0 + 0 + + + + + lorawan/common + 0 + 0 + 0 + 0 + + 14 + 101 + 1 + 0 + 0 + 0 + ..\..\BSP\Middlewares\LoRaWAN\Utilities\utilities.c + utilities.c + 0 + 0 + + + + + SubGHz_Phy/radio + 0 + 0 + 0 + 0 + + 15 + 102 + 1 + 0 + 0 + 0 + ..\..\BSP\Middlewares\SubGHz_Phy\stm32_radio_driver\radio.c + radio.c + 0 + 0 + + + 15 + 103 + 1 + 0 + 0 + 0 + ..\..\BSP\Middlewares\SubGHz_Phy\stm32_radio_driver\radio_driver.c + radio_driver.c + 0 + 0 + + + + + Utilities + 0 + 0 + 0 + 0 + + 16 + 104 + 1 + 0 + 0 + 0 + ..\..\BSP\Utilities\lpm\tiny_lpm\stm32_lpm.c + stm32_lpm.c + 0 + 0 + + + 16 + 105 + 1 + 0 + 0 + 0 + ..\..\BSP\Utilities\misc\stm32_mem.c + stm32_mem.c + 0 + 0 + + + 16 + 106 + 1 + 0 + 0 + 0 + ..\..\BSP\Utilities\misc\stm32_systime.c + stm32_systime.c + 0 + 0 + + + + + ::CMSIS + 0 + 0 + 0 + 1 + + +
diff --git a/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/TencentOS_Tiny.uvprojx b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/TencentOS_Tiny.uvprojx new file mode 100644 index 00000000..7c730423 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/TencentOS_Tiny.uvprojx @@ -0,0 +1,1026 @@ + + + + 2.1 + +
### uVision Project, (C) Keil Software
+ + + + TencentOS_Tiny + 0x4 + ARM-ADS + 6140000::V6.14::ARMCLANG + 1 + + + STM32WLE5JCIx + STMicroelectronics + Keil.STM32WLxx_DFP.1.0.2.OEM + http://www.keil.com/pack + IRAM(0x20000000,0x10000) IROM(0x08000000,0x40000) CPUTYPE("Cortex-M4") FPU2 CLOCK(12000000) ELITTLE + + + UL2CM3(-S0 -C0 -P0 -FD20000000 -FC1000 -FN1 -FF0STM32WLxx_CM4 -FS08000000 -FL040000 -FP0($$Device:STM32WLE5JCIx$Drivers\CMSIS\Flash\STM32WLxx_CM4.FLM)) + 0 + $$Device:STM32WLE5JCIx$Drivers\CMSIS\Device\ST\STM32WLxx\Include\stm32wlxx.h + + + + + + + + + + $$Device:STM32WLE5JCIx$Drivers\CMSIS\SVD\STM32WL5x_CM4.svd + 0 + 0 + + + + + + + 0 + 0 + 0 + 0 + 1 + + .\Obj\ + TencentOS_Tiny + 1 + 0 + 0 + 1 + 0 + .\List\ + 1 + 0 + 0 + + 0 + 0 + + + 0 + 0 + 0 + 0 + + + 0 + 0 + + + 0 + 0 + 0 + 0 + + + 0 + 0 + + + 0 + 0 + 0 + 0 + + 0 + + + + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 3 + + + 0 + + + SARMCM3.DLL + -REMAP -MPU + DCM.DLL + -pCM4 + SARMCM3.DLL + -MPU + TCM.DLL + -pCM4 + + + + 1 + 0 + 0 + 0 + 16 + + + + + 1 + 0 + 0 + 1 + 1 + 4107 + + 1 + BIN\UL2CM3.DLL + + + + + + 0 + + + + 0 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 0 + 1 + 1 + 0 + 1 + 1 + 0 + 0 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 0 + 0 + "Cortex-M4" + + 0 + 0 + 0 + 1 + 1 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 8 + 1 + 0 + 0 + 0 + 3 + 3 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 1 + 0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x20000000 + 0x10000 + + + 1 + 0x8000000 + 0x40000 + + + 0 + 0x0 + 0x0 + + + 1 + 0x0 + 0x0 + + + 1 + 0x0 + 0x0 + + + 1 + 0x0 + 0x0 + + + 1 + 0x8000000 + 0x40000 + + + 1 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x20000000 + 0x10000 + + + 0 + 0x0 + 0x0 + + + + + + 1 + 7 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 3 + 0 + 0 + 1 + 0 + 0 + 3 + 3 + 1 + 1 + 0 + 0 + 0 + + + USE_HAL_DRIVER,STM32WLE5xx,CORE_CM4 + + ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Inc;..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Inc\Legacy;..\..\..\..\platform\vendor_bsp\st\CMSIS\Device\ST\STM32WLxx\Include;..\..\..\..\platform\vendor_bsp\st\CMSIS\Include;..\..\..\..\arch\arm\arm-v7m\common\include;..\..\..\..\arch\arm\arm-v7m\cortex-m4\armcc;..\..\..\..\kernel\core\include;..\..\..\..\kernel\pm\include;..\..\..\..\osal\cmsis_os;..\..\TOS_CONFIG;..\..\BSP\Middlewares\LoRaWAN\Conf;..\..\BSP\Middlewares\LoRaWAN\Crypto;..\..\BSP\Middlewares\LoRaWAN\Mac;..\..\BSP\Middlewares\LoRaWAN\Patterns\Basic;..\..\BSP\Middlewares\LoRaWAN\Utilities;..\..\BSP\Middlewares\SubGHz_Phy;..\..\BSP\Middlewares\SubGHz_Phy\stm32_radio_driver;..\..\BSP\Inc;.\App;.\Target;..\..\BSP\Utilities\lpm\tiny_lpm;..\..\BSP\Utilities\misc + + + + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 4 + + + + + + + + + 0 + 0 + 0 + 0 + 1 + 0 + 0x08000000 + 0x20000000 + + + + + --diag_suppress L6314W + + + + + + + + Application/MDK-ARM + + + startup_stm32wle5xx.s + 2 + .\startup_stm32wle5xx.s + + + + + Application/User + + + adc.c + 1 + ..\..\BSP\Src\adc.c + + + adc_if.c + 1 + ..\..\BSP\Src\adc_if.c + + + dma.c + 1 + ..\..\BSP\Src\dma.c + + + gpio.c + 1 + ..\..\BSP\Src\gpio.c + + + main.c + 1 + ..\..\BSP\Src\main.c + + + mcu_init.c + 1 + ..\..\BSP\Src\mcu_init.c + + + rtc.c + 1 + ..\..\BSP\Src\rtc.c + + + rtc_if.c + 1 + ..\..\BSP\Src\rtc_if.c + + + stm32_lpm_if.c + 1 + ..\..\BSP\Src\stm32_lpm_if.c + + + stm32wlxx_hal_msp.c + 1 + ..\..\BSP\Src\stm32wlxx_hal_msp.c + + + stm32wlxx_it.c + 1 + ..\..\BSP\Src\stm32wlxx_it.c + + + usart.c + 1 + ..\..\BSP\Src\usart.c + + + usart_if.c + 1 + ..\..\BSP\Src\usart_if.c + + + + + Drivers/STM32WLxx_HAL_Driver + + + stm32wlxx_hal.c + 1 + ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal.c + + + stm32wlxx_hal_cortex.c + 1 + ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_cortex.c + + + stm32wlxx_hal_dma.c + 1 + ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_dma.c + + + stm32wlxx_hal_dma_ex.c + 1 + ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_dma_ex.c + + + stm32wlxx_hal_exti.c + 1 + ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_exti.c + + + stm32wlxx_hal_flash.c + 1 + ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_flash.c + + + stm32wlxx_hal_flash_ex.c + 1 + ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_flash_ex.c + + + stm32wlxx_hal_gpio.c + 1 + ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_gpio.c + + + stm32wlxx_hal_pwr.c + 1 + ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_pwr.c + + + stm32wlxx_hal_pwr_ex.c + 1 + ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_pwr_ex.c + + + stm32wlxx_hal_rcc.c + 1 + ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_rcc.c + + + stm32wlxx_hal_rcc_ex.c + 1 + ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_rcc_ex.c + + + stm32wlxx_hal_tim.c + 1 + ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_tim.c + + + stm32wlxx_hal_tim_ex.c + 1 + ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_tim_ex.c + + + stm32wlxx_hal_uart.c + 1 + ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_uart.c + + + stm32wlxx_hal_uart_ex.c + 1 + ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_uart_ex.c + + + stm32wlxx_hal_adc.c + 1 + ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_adc.c + + + stm32wlxx_hal_adc_ex.c + 1 + ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_adc_ex.c + + + stm32wlxx_hal_rtc.c + 1 + ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_rtc.c + + + stm32wlxx_hal_rtc_ex.c + 1 + ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_rtc_ex.c + + + stm32wlxx_hal_subghz.c + 1 + ..\..\..\..\platform\vendor_bsp\st\STM32WLxx_HAL_Driver\Src\stm32wlxx_hal_subghz.c + + + + + Drivers/CMSIS + + + system_stm32wlxx.c + 1 + ..\..\BSP\Src\system_stm32wlxx.c + + + + + tos/arch + + + tos_cpu.c + 1 + ..\..\..\..\arch\arm\arm-v7m\common\tos_cpu.c + + + port_c.c + 1 + ..\..\..\..\arch\arm\arm-v7m\cortex-m4\armcc\port_c.c + + + port_s.S + 2 + ..\..\..\..\arch\arm\arm-v7m\cortex-m4\armcc\port_s.S + + + + + tos/kernel + + + tos_barrier.c + 1 + ..\..\..\..\kernel\core\tos_barrier.c + + + tos_binary_heap.c + 1 + ..\..\..\..\kernel\core\tos_binary_heap.c + + + tos_bitmap.c + 1 + ..\..\..\..\kernel\core\tos_bitmap.c + + + tos_char_fifo.c + 1 + ..\..\..\..\kernel\core\tos_char_fifo.c + + + tos_completion.c + 1 + ..\..\..\..\kernel\core\tos_completion.c + + + tos_countdownlatch.c + 1 + ..\..\..\..\kernel\core\tos_countdownlatch.c + + + tos_event.c + 1 + ..\..\..\..\kernel\core\tos_event.c + + + tos_global.c + 1 + ..\..\..\..\kernel\core\tos_global.c + + + tos_mail_queue.c + 1 + ..\..\..\..\kernel\core\tos_mail_queue.c + + + tos_message_queue.c + 1 + ..\..\..\..\kernel\core\tos_message_queue.c + + + tos_mmblk.c + 1 + ..\..\..\..\kernel\core\tos_mmblk.c + + + tos_mmheap.c + 1 + ..\..\..\..\kernel\core\tos_mmheap.c + + + tos_mutex.c + 1 + ..\..\..\..\kernel\core\tos_mutex.c + + + tos_pend.c + 1 + ..\..\..\..\kernel\core\tos_pend.c + + + tos_priority_mail_queue.c + 1 + ..\..\..\..\kernel\core\tos_priority_mail_queue.c + + + tos_priority_message_queue.c + 1 + ..\..\..\..\kernel\core\tos_priority_message_queue.c + + + tos_priority_queue.c + 1 + ..\..\..\..\kernel\core\tos_priority_queue.c + + + tos_ring_queue.c + 1 + ..\..\..\..\kernel\core\tos_ring_queue.c + + + tos_robin.c + 1 + ..\..\..\..\kernel\core\tos_robin.c + + + tos_rwlock.c + 1 + ..\..\..\..\kernel\core\tos_rwlock.c + + + tos_sched.c + 1 + ..\..\..\..\kernel\core\tos_sched.c + + + tos_sem.c + 1 + ..\..\..\..\kernel\core\tos_sem.c + + + tos_stopwatch.c + 1 + ..\..\..\..\kernel\core\tos_stopwatch.c + + + tos_sys.c + 1 + ..\..\..\..\kernel\core\tos_sys.c + + + tos_task.c + 1 + ..\..\..\..\kernel\core\tos_task.c + + + tos_tick.c + 1 + ..\..\..\..\kernel\core\tos_tick.c + + + tos_time.c + 1 + ..\..\..\..\kernel\core\tos_time.c + + + tos_timer.c + 1 + ..\..\..\..\kernel\core\tos_timer.c + + + + + tos/cmsis + + + cmsis_os.c + 1 + ..\..\..\..\osal\cmsis_os\cmsis_os.c + + + + + examples + + + TOS-CONFIG + + + tos_config.h + 5 + ..\..\TOS_CONFIG\tos_config.h + + + + + APP + + + app_lorawan.c + 1 + .\App\app_lorawan.c + + + app_system.c + 1 + .\App\app_system.c + + + app_conf.h + 5 + .\App\app_conf.h + + + Commissioning.h + 5 + .\App\Commissioning.h + + + timer_wrapper.c + 1 + .\Target\timer_wrapper.c + + + + + lorawan/pattern + + + lora.c + 1 + ..\..\BSP\Middlewares\LoRaWAN\Patterns\Basic\lora.c + + + lorawan_info.c + 1 + ..\..\BSP\Middlewares\LoRaWAN\Patterns\Basic\lorawan_info.c + + + + + lorawan/mac + + + aes.c + 1 + ..\..\BSP\Middlewares\LoRaWAN\Crypto\aes.c + + + cmac.c + 1 + ..\..\BSP\Middlewares\LoRaWAN\Crypto\cmac.c + + + soft-se.c + 1 + ..\..\BSP\Middlewares\LoRaWAN\Crypto\soft-se.c + + + LoRaMac.c + 1 + ..\..\BSP\Middlewares\LoRaWAN\Mac\LoRaMac.c + + + LoRaMacAdr.c + 1 + ..\..\BSP\Middlewares\LoRaWAN\Mac\LoRaMacAdr.c + + + LoRaMacClassB.c + 1 + ..\..\BSP\Middlewares\LoRaWAN\Mac\LoRaMacClassB.c + + + LoRaMacCommands.c + 1 + ..\..\BSP\Middlewares\LoRaWAN\Mac\LoRaMacCommands.c + + + LoRaMacConfirmQueue.c + 1 + ..\..\BSP\Middlewares\LoRaWAN\Mac\LoRaMacConfirmQueue.c + + + LoRaMacCrypto.c + 1 + ..\..\BSP\Middlewares\LoRaWAN\Mac\LoRaMacCrypto.c + + + LoRaMacParser.c + 1 + ..\..\BSP\Middlewares\LoRaWAN\Mac\LoRaMacParser.c + + + LoRaMacSerializer.c + 1 + ..\..\BSP\Middlewares\LoRaWAN\Mac\LoRaMacSerializer.c + + + + + lorawan/region + + + Region.c + 1 + ..\..\BSP\Middlewares\LoRaWAN\Mac\region\Region.c + + + RegionAS923.c + 1 + ..\..\BSP\Middlewares\LoRaWAN\Mac\region\RegionAS923.c + + + RegionAU915.c + 1 + ..\..\BSP\Middlewares\LoRaWAN\Mac\region\RegionAU915.c + + + RegionCN470.c + 1 + ..\..\BSP\Middlewares\LoRaWAN\Mac\region\RegionCN470.c + + + RegionCN470ALI.c + 1 + ..\..\BSP\Middlewares\LoRaWAN\Mac\region\RegionCN470ALI.c + + + RegionCN779.c + 1 + ..\..\BSP\Middlewares\LoRaWAN\Mac\region\RegionCN779.c + + + RegionCommon.c + 1 + ..\..\BSP\Middlewares\LoRaWAN\Mac\region\RegionCommon.c + + + RegionEU433.c + 1 + ..\..\BSP\Middlewares\LoRaWAN\Mac\region\RegionEU433.c + + + RegionEU868.c + 1 + ..\..\BSP\Middlewares\LoRaWAN\Mac\region\RegionEU868.c + + + RegionIN865.c + 1 + ..\..\BSP\Middlewares\LoRaWAN\Mac\region\RegionIN865.c + + + RegionKR920.c + 1 + ..\..\BSP\Middlewares\LoRaWAN\Mac\region\RegionKR920.c + + + RegionRU864.c + 1 + ..\..\BSP\Middlewares\LoRaWAN\Mac\region\RegionRU864.c + + + RegionUS915.c + 1 + ..\..\BSP\Middlewares\LoRaWAN\Mac\region\RegionUS915.c + + + + + lorawan/common + + + utilities.c + 1 + ..\..\BSP\Middlewares\LoRaWAN\Utilities\utilities.c + + + + + SubGHz_Phy/radio + + + radio.c + 1 + ..\..\BSP\Middlewares\SubGHz_Phy\stm32_radio_driver\radio.c + + + radio_driver.c + 1 + ..\..\BSP\Middlewares\SubGHz_Phy\stm32_radio_driver\radio_driver.c + + + + + Utilities + + + stm32_lpm.c + 1 + ..\..\BSP\Utilities\lpm\tiny_lpm\stm32_lpm.c + + + stm32_mem.c + 1 + ..\..\BSP\Utilities\misc\stm32_mem.c + + + stm32_systime.c + 1 + ..\..\BSP\Utilities\misc\stm32_systime.c + + + + + ::CMSIS + + + + + + + + + + + + + + + + + + + + + + <Project Info> + + + + + + 0 + 1 + + + + +
diff --git a/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/startup_stm32wle5xx.s b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/startup_stm32wle5xx.s new file mode 100644 index 00000000..7f965b48 --- /dev/null +++ b/board/RHF0M0E5_STM32WLE5xx/KEIL/lorawan/startup_stm32wle5xx.s @@ -0,0 +1,359 @@ +;****************************************************************************** +;* File Name : startup_stm32wle5xx_cm4.s +;* Author : MCD Application Team +;* Description : STM32WLE5xx devices vector table for MDK-ARM toolchain. +;* This module performs: +;* - Set the initial SP +;* - Set the initial PC == Reset_Handler +;* - Set the vector table entries with the exceptions ISR address +;* - Branches to __main in the C library (which eventually +;* calls main()). +;* After Reset the CortexM4 processor is in Thread mode, +;* priority is Privileged, and the Stack is set to Main. +;* <<< Use Configuration Wizard in Context Menu >>> +;****************************************************************************** +;* @attention +;* +;* Copyright (c) 2019 STMicroelectronics. All rights reserved. +;* +;* This software component is licensed by ST under BSD 3-Clause license, +;* the "License"; You may not use this file except in compliance with the +;* License. You may obtain a copy of the License at: +;* opensource.org/licenses/BSD-3-Clause +;* +;****************************************************************************** + +; Amount of memory (in bytes) allocated for Stack +; Tailor this value to your application needs +; Stack Configuration +; Stack Size (in Bytes) <0x0-0xFFFFFFFF:8> +; + +Stack_Size EQU 0x00000800 + + AREA STACK, NOINIT, READWRITE, ALIGN=3 +Stack_Mem SPACE Stack_Size +__initial_sp + + +; Heap Configuration +; Heap Size (in Bytes) <0x0-0xFFFFFFFF:8> +; + +Heap_Size EQU 0x00000200 + + AREA HEAP, NOINIT, READWRITE, ALIGN=3 +__heap_base +Heap_Mem SPACE Heap_Size +__heap_limit + + PRESERVE8 + THUMB + + +; Vector Table Mapped to Address 0 at Reset + AREA RESET, DATA, READONLY + EXPORT __Vectors + EXPORT __Vectors_End + EXPORT __Vectors_Size + +__Vectors DCD __initial_sp ; Top of Stack + DCD Reset_Handler ; Reset Handler + DCD NMI_Handler ; NMI Handler + DCD HardFault_Handler ; Hard Fault Handler + DCD MemManage_Handler ; MPU Fault Handler + DCD BusFault_Handler ; Bus Fault Handler + DCD UsageFault_Handler ; Usage Fault Handler + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD SVC_Handler ; SVCall Handler + DCD DebugMon_Handler ; Debug Monitor Handler + DCD 0 ; Reserved + DCD PendSV_Handler ; PendSV Handler + DCD SysTick_Handler ; SysTick Handler + + ; External Interrupts + DCD WWDG_IRQHandler ; Window WatchDog + DCD PVD_PVM_IRQHandler ; PVD and PVM detector + DCD TAMP_STAMP_LSECSS_SSRU_IRQHandler ; RTC Tamper, RTC TimeStamp, LSECSS and RTC SSR Underflow Interrupts + DCD RTC_WKUP_IRQHandler ; RTC Wakeup Interrupt + DCD FLASH_IRQHandler ; FLASH global Interrupt + DCD RCC_IRQHandler ; RCC Interrupt + DCD EXTI0_IRQHandler ; EXTI Line 0 Interrupt + DCD EXTI1_IRQHandler ; EXTI Line 1 Interrupt + DCD EXTI2_IRQHandler ; EXTI Line 2 Interrupt + DCD EXTI3_IRQHandler ; EXTI Line 3 Interrup + DCD EXTI4_IRQHandler ; EXTI Line 4 Interrupt + DCD DMA1_Channel1_IRQHandler ; DMA1 Channel 1 Interrupt + DCD DMA1_Channel2_IRQHandler ; DMA1 Channel 2 Interrupt + DCD DMA1_Channel3_IRQHandler ; DMA1 Channel 3 Interrupt + DCD DMA1_Channel4_IRQHandler ; DMA1 Channel 4 Interrupt + DCD DMA1_Channel5_IRQHandler ; DMA1 Channel 5 Interrupt + DCD DMA1_Channel6_IRQHandler ; DMA1 Channel 6 Interrupt + DCD DMA1_Channel7_IRQHandler ; DMA1 Channel 7 Interrupt + DCD ADC1_IRQHandler ; ADC1 Interrupt + DCD DAC_IRQHandler ; DAC Interrupt + DCD 0 ; Reserved + DCD COMP_IRQHandler ; COMP1 and COMP2 Interrupts + DCD EXTI9_5_IRQHandler ; EXTI Lines [9:5] Interrupt + DCD TIM1_BRK_IRQHandler ; TIM1 Break Interrupt + DCD TIM1_UP_IRQHandler ; TIM1 Update Interrupts + DCD TIM1_TRG_COM_IRQHandler ; TIM1 Trigger and Communication Interrupts + DCD TIM1_CC_IRQHandler ; TIM1 Capture Compare Interrupt + DCD TIM2_IRQHandler ; TIM2 Global Interrupt + DCD TIM16_IRQHandler ; TIM16 Global Interrupt + DCD TIM17_IRQHandler ; TIM17 Global Interrupt + DCD I2C1_EV_IRQHandler ; I2C1 Event Interrupt + DCD I2C1_ER_IRQHandler ; I2C1 Error Interrupt + DCD I2C2_EV_IRQHandler ; I2C2 Event Interrupt + DCD I2C2_ER_IRQHandler ; I2C2 Error Interrupt + DCD SPI1_IRQHandler ; SPI1 Interrupt + DCD SPI2_IRQHandler ; SPI2 Interrupt + DCD USART1_IRQHandler ; USART1 Interrupt + DCD USART2_IRQHandler ; USART2 Interrupt + DCD LPUART1_IRQHandler ; LPUART1 Interrupt + DCD LPTIM1_IRQHandler ; LPTIM1 Interrupt + DCD LPTIM2_IRQHandler ; LPTIM2 Interrupt + DCD EXTI15_10_IRQHandler ; EXTI Lines1[15:10 ]Interrupts + DCD RTC_Alarm_IRQHandler ; RTC Alarms (A and B) Interrupt + DCD LPTIM3_IRQHandler ; LPTIM3 Interrupt + DCD SUBGHZSPI_IRQHandler ; SUBGHZSPI Interrupt + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD HSEM_IRQHandler ; HSEM0 Interrupt + DCD I2C3_EV_IRQHandler ; I2C3 Event Interrupt + DCD I2C3_ER_IRQHandler ; I2C3 Error Interrupt + DCD Radio_IRQHandler ; Radio Interrupt + DCD AES_IRQHandler ; AES Interrupt + DCD RNG_IRQHandler ; RNG1 Interrupt + DCD PKA_IRQHandler ; PKA Interrupt + DCD DMA2_Channel1_IRQHandler ; DMA2 Channel 1 Interrupt + DCD DMA2_Channel2_IRQHandler ; DMA2 Channel 2 Interrupt + DCD DMA2_Channel3_IRQHandler ; DMA2 Channel 3 Interrupt + DCD DMA2_Channel4_IRQHandler ; DMA2 Channel 4 Interrupt + DCD DMA2_Channel5_IRQHandler ; DMA2 Channel 5 Interrupt + DCD DMA2_Channel6_IRQHandler ; DMA2 Channel 6 Interrupt + DCD DMA2_Channel7_IRQHandler ; DMA2 Channel 7 Interrupt + DCD DMAMUX1_OVR_IRQHandler ; DMAMUX overrun Interrupt + +__Vectors_End + +__Vectors_Size EQU __Vectors_End - __Vectors + + AREA |.text|, CODE, READONLY + +; Reset handler +Reset_Handler PROC + EXPORT Reset_Handler [WEAK] + IMPORT SystemInit + IMPORT __main + + LDR R0, =SystemInit + BLX R0 + LDR R0, =__main + BX R0 + ENDP + +; Dummy Exception Handlers (infinite loops which can be modified) + +NMI_Handler PROC + EXPORT NMI_Handler [WEAK] + B . + ENDP +HardFault_Handler\ + PROC + EXPORT HardFault_Handler [WEAK] + B . + ENDP +MemManage_Handler\ + PROC + EXPORT MemManage_Handler [WEAK] + B . + ENDP +BusFault_Handler\ + PROC + EXPORT BusFault_Handler [WEAK] + B . + ENDP +UsageFault_Handler\ + PROC + EXPORT UsageFault_Handler [WEAK] + B . + ENDP +SVC_Handler PROC + EXPORT SVC_Handler [WEAK] + B . + ENDP +DebugMon_Handler\ + PROC + EXPORT DebugMon_Handler [WEAK] + B . + ENDP +PendSV_Handler PROC + EXPORT PendSV_Handler [WEAK] + B . + ENDP +SysTick_Handler PROC + EXPORT SysTick_Handler [WEAK] + B . + ENDP + +Default_Handler PROC + + EXPORT WWDG_IRQHandler [WEAK] + EXPORT PVD_PVM_IRQHandler [WEAK] + EXPORT TAMP_STAMP_LSECSS_SSRU_IRQHandler [WEAK] + EXPORT RTC_WKUP_IRQHandler [WEAK] + EXPORT FLASH_IRQHandler [WEAK] + EXPORT RCC_IRQHandler [WEAK] + EXPORT EXTI0_IRQHandler [WEAK] + EXPORT EXTI1_IRQHandler [WEAK] + EXPORT EXTI2_IRQHandler [WEAK] + EXPORT EXTI3_IRQHandler [WEAK] + EXPORT EXTI4_IRQHandler [WEAK] + EXPORT DMA1_Channel1_IRQHandler [WEAK] + EXPORT DMA1_Channel2_IRQHandler [WEAK] + EXPORT DMA1_Channel3_IRQHandler [WEAK] + EXPORT DMA1_Channel4_IRQHandler [WEAK] + EXPORT DMA1_Channel5_IRQHandler [WEAK] + EXPORT DMA1_Channel6_IRQHandler [WEAK] + EXPORT DMA1_Channel7_IRQHandler [WEAK] + EXPORT ADC1_IRQHandler [WEAK] + EXPORT DAC_IRQHandler [WEAK] + EXPORT COMP_IRQHandler [WEAK] + EXPORT EXTI9_5_IRQHandler [WEAK] + EXPORT TIM1_BRK_IRQHandler [WEAK] + EXPORT TIM1_UP_IRQHandler [WEAK] + EXPORT TIM1_TRG_COM_IRQHandler [WEAK] + EXPORT TIM1_CC_IRQHandler [WEAK] + EXPORT TIM2_IRQHandler [WEAK] + EXPORT TIM16_IRQHandler [WEAK] + EXPORT TIM17_IRQHandler [WEAK] + EXPORT I2C1_EV_IRQHandler [WEAK] + EXPORT I2C1_ER_IRQHandler [WEAK] + EXPORT I2C2_EV_IRQHandler [WEAK] + EXPORT I2C2_ER_IRQHandler [WEAK] + EXPORT SPI1_IRQHandler [WEAK] + EXPORT SPI2_IRQHandler [WEAK] + EXPORT USART1_IRQHandler [WEAK] + EXPORT USART2_IRQHandler [WEAK] + EXPORT LPUART1_IRQHandler [WEAK] + EXPORT LPTIM1_IRQHandler [WEAK] + EXPORT LPTIM2_IRQHandler [WEAK] + EXPORT EXTI15_10_IRQHandler [WEAK] + EXPORT RTC_Alarm_IRQHandler [WEAK] + EXPORT LPTIM3_IRQHandler [WEAK] + EXPORT SUBGHZSPI_IRQHandler [WEAK] + EXPORT HSEM_IRQHandler [WEAK] + EXPORT I2C3_EV_IRQHandler [WEAK] + EXPORT I2C3_ER_IRQHandler [WEAK] + EXPORT Radio_IRQHandler [WEAK] + EXPORT AES_IRQHandler [WEAK] + EXPORT RNG_IRQHandler [WEAK] + EXPORT PKA_IRQHandler [WEAK] + EXPORT DMA2_Channel1_IRQHandler [WEAK] + EXPORT DMA2_Channel2_IRQHandler [WEAK] + EXPORT DMA2_Channel3_IRQHandler [WEAK] + EXPORT DMA2_Channel4_IRQHandler [WEAK] + EXPORT DMA2_Channel5_IRQHandler [WEAK] + EXPORT DMA2_Channel6_IRQHandler [WEAK] + EXPORT DMA2_Channel7_IRQHandler [WEAK] + EXPORT DMAMUX1_OVR_IRQHandler [WEAK] + +WWDG_IRQHandler +PVD_PVM_IRQHandler +TAMP_STAMP_LSECSS_SSRU_IRQHandler +RTC_WKUP_IRQHandler +FLASH_IRQHandler +RCC_IRQHandler +EXTI0_IRQHandler +EXTI1_IRQHandler +EXTI2_IRQHandler +EXTI3_IRQHandler +EXTI4_IRQHandler +DMA1_Channel1_IRQHandler +DMA1_Channel2_IRQHandler +DMA1_Channel3_IRQHandler +DMA1_Channel4_IRQHandler +DMA1_Channel5_IRQHandler +DMA1_Channel6_IRQHandler +DMA1_Channel7_IRQHandler +ADC1_IRQHandler +DAC_IRQHandler +COMP_IRQHandler +EXTI9_5_IRQHandler +TIM1_BRK_IRQHandler +TIM1_UP_IRQHandler +TIM1_TRG_COM_IRQHandler +TIM1_CC_IRQHandler +TIM2_IRQHandler +TIM16_IRQHandler +TIM17_IRQHandler +I2C1_EV_IRQHandler +I2C1_ER_IRQHandler +I2C2_EV_IRQHandler +I2C2_ER_IRQHandler +SPI1_IRQHandler +SPI2_IRQHandler +USART1_IRQHandler +USART2_IRQHandler +LPUART1_IRQHandler +LPTIM1_IRQHandler +LPTIM2_IRQHandler +EXTI15_10_IRQHandler +RTC_Alarm_IRQHandler +LPTIM3_IRQHandler +SUBGHZSPI_IRQHandler +HSEM_IRQHandler +I2C3_EV_IRQHandler +I2C3_ER_IRQHandler +Radio_IRQHandler +AES_IRQHandler +RNG_IRQHandler +PKA_IRQHandler +DMA2_Channel1_IRQHandler +DMA2_Channel2_IRQHandler +DMA2_Channel3_IRQHandler +DMA2_Channel4_IRQHandler +DMA2_Channel5_IRQHandler +DMA2_Channel6_IRQHandler +DMA2_Channel7_IRQHandler +DMAMUX1_OVR_IRQHandler + + B . + + ENDP + + ALIGN + +;******************************************************************************* +; User Stack and Heap initialization +;******************************************************************************* + IF :DEF:__MICROLIB + + EXPORT __initial_sp + EXPORT __heap_base + EXPORT __heap_limit + + ELSE + + IMPORT __use_two_region_memory + EXPORT __user_initial_stackheap + +__user_initial_stackheap + + LDR R0, = Heap_Mem + LDR R1, =(Stack_Mem + Stack_Size) + LDR R2, = (Heap_Mem + Heap_Size) + LDR R3, = Stack_Mem + BX LR + + ALIGN + + ENDIF + + END + +;************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE*****