diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..072dcef --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,29 @@ +# LR11xx driver changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [v2.1.0] 2022-03-28 + +### Added + +* [radio] `lr11xx_radio_apply_high_acp_workaround()` function +* [radio] `lr11xx_radio_set_rssi_calibration()` function +* [radio] `LR11XX_RADIO_LORA_BW_200`, `LR11XX_RADIO_LORA_BW_400` and `LR11XX_RADIO_LORA_BW_800` entries to `lr11xx_radio_lora_bw_t` +* [radio] `LR11XX_RADIO_GFSK_PKT_VAR_LEN_SX128X_COMP` entry in `lr11xx_radio_gfsk_pkt_len_modes_t` to support compatibility with SX128x +* [radio] `LR11XX_RADIO_GFSK_DC_FREE_WHITENING_SX128X_COMP` entry in `lr11xx_radio_gfsk_dc_free_t` to support compatibility with SX128x +* [GNSS] `lr1110_gnss_get_consumption` function +* [Wi-Fi] `lr1110_wifi_get_consumption` function +* [Wi-Fi] `lr11xx_wifi_are_scan_mode_result_format_compatible` function + +### Changed + +* [radio] Call to `lr11xx_radio_apply_high_acp_workaround()` in `lr11xx_radio_set_tx_with_timeout_in_rtc_step()`, `lr11xx_radio_set_rx_with_timeout_in_rtc_step()`, `lr11xx_radio_set_cad()` and `lr11xx_radio_set_tx_infinite_preamble()` (can be removed by defining `LR11XX_DISABLE_HIGH_ACP_WORKAROUND`) +* [Wi-Fi] Define type `lr11xx_wifi_country_code_str_t` for Wi-Fi country code data and update `lr11xx_wifi_extended_full_result_t` and `lr11xx_wifi_country_code_t` to use it. + +## [v1.0.0] Unreleased + +### Added + +* [all] Initial version - based on LR1110 driver v7.0.0 diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..292ef9b --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,30 @@ +License for the code produced by Semtech contained in this project +------------------------------------------------------------------ + +The Clear BSD License +Copyright Semtech Corporation 2022. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted (subject to the limitations in the disclaimer +below) provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Semtech corporation nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY +THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT +NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..05629c5 --- /dev/null +++ b/README.md @@ -0,0 +1,101 @@ +# LR11xx driver + +This package proposes an implementation in C of the driver for **LR11XX** radio component. + +## Components + +The driver is split in several components: + +- Bootloader +- Register / memory access +- System configuration +- Radio +- Wi-Fi Passive Scanning +- GNSS Scan Scanning +- Crypto engine + +### Bootloader + +This component is used to update the firmware. + +### Register / memory access + +This component is used to read / write data from registers or internal memory. + +### System configuration + +This component is used to interact with system-wide parameters like clock sources, integrated RF switches, etc. + +### Radio + +This component is used to send / receive data through the different modems (LoRa and GFSK) or perform a LoRa CAD (Channel Activity Detection). Parameters like power amplifier selection, output power and fallback modes are also accessible through this component. + +### Wi-Fi Passive Scanning + +This component is used to configure and initiate the passive scanning of the Wi-Fi signals that can be shared to request a geolocation. + +### GNSS Scanning + +This component is used to configure and initiate the acquisition of GNSS signals that can be shared to request a geolocation. + +### Crypto engine + +This component is used to set and derive keys in the internal keychain and perform cryptographic operations with the integrated hardware accelerator. + +## Structure + +Each component is based on different files: + +- lr11xx_component.c: implementation of the functions related to component +- lr11xx_component.h: declarations of the functions related to component +- lr11xx_component_types.h: type definitions related to components + +## HAL + +The HAL (Hardware Abstraction Layer) is a collection of functions that the user shall implement to write platform-dependent calls to the host. The list of functions is the following: + +- lr11xx_hal_reset() +- lr11xx_hal_wakeup() +- lr11xx_hal_write() +- lr11xx_hal_read() +- lr11xx_hal_direct_read() + +## LR11xx firmware known limitations + +### High ACP (Adjacent Channel Power) + +#### Description + +When the chip wakes up from sleep mode with retention, a parameter is not reconfigured properly. This misconfiguration can lead to an unexpectedly high adjacent channel power in all subsequent transmissions. + +The issue appears only in LoRa modulation, for all bandwidths except for 500kHz and 800kHz. + +The following firmware versions are affected: + +- LR1110 firmware from 0x0303 to 0x0307 +- LR1120 firmware 0x0101 + +### Workaround + +The workaround is to reset the bit 30 in the register `0x00F30054` when the chip wakes up from sleep mode with retention. + +This workaround does not solve the case where `LR11XX_RADIO_MODE_SLEEP` is configured with `lr11xx_radio_auto_tx_rx` and the chip is set to Rx mode. This is dues to the fact that the workaround cannot be applied before the subsequent transmission, automatically launched by the chip after waking up from sleep mode with retention. + +#### Implementation 1 + +The first implementation - enabled by default in the driver - adds an implicit call updating the parameter to each function that could set the chip in transmission - directly or not -: + +- `lr11xx_radio_set_tx_with_timeout_in_rtc_step` +- `lr11xx_radio_set_tx_infinite_preamble` +- `lr11xx_radio_set_rx_with_timeout_in_rtc_step` - in case `lr11xx_radio_auto_tx_rx` has been enabled +- `lr11xx_radio_set_cad` - in case `LR11XX_RADIO_CAD_EXIT_MODE_TX` has been set with `lr11xx_radio_set_cad_params` + +This implementation can be disabled by defining the macro `LR11XX_DISABLE_HIGH_ACP_WORKAROUND`. This disabling will be useful when, in the future, a new firmware integrating a fix is released and does not require the workaround anymore. + +The main advantage of this implementation is that it is transparent to the user who only needs to update the driver without changing its application. The main drawback is that the implicit call is done systematically even when not required. + +#### Implementation 2 + +The second method requires the user to explicitly call the function `lr11xx_radio_apply_high_acp_workaround` when the chip wakes up from sleep mode with retention (note: to ease the implementation, it can be called when the chip wakes up from any sleep mode). + +This method requires the macro `LR11XX_DISABLE_HIGH_ACP_WORKAROUND` to be defined so the implementation 1 of the workaround (enabled by default) is disabled. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..926f826 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,43 @@ +# @file +# +# @brief Sets CMake target_sources and target_include_directories +# +# --- The Clear BSD License --- +# Copyright Semtech Corporation 2021. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted (subject to the limitations in the disclaimer +# below) provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the Semtech corporation nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY +# THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +# CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT +# NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +include(lr11xx_driver_module.cmake) + +target_sources(${LR11XX_DRIVER_MODULE_TARGET} + PRIVATE + ${LR11XX_DRIVER_MODULE_C_SOURCES} +) + +target_include_directories(${LR11XX_DRIVER_MODULE_TARGET} + PUBLIC + ${LR11XX_DRIVER_MODULE_C_INCLUDES} +) diff --git a/src/lr11xx_bootloader.c b/src/lr11xx_bootloader.c new file mode 100644 index 0000000..cffe508 --- /dev/null +++ b/src/lr11xx_bootloader.c @@ -0,0 +1,294 @@ +/*! + * @file lr11xx_bootloader.c + * + * @brief Bootloader driver implementation for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include "lr11xx_bootloader.h" +#include "lr11xx_hal.h" + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE MACROS----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE CONSTANTS ------------------------------------------------------- + */ + +#define LR11XX_FLASH_DATA_MAX_LENGTH_UINT32 ( 64 ) +#define LR11XX_FLASH_DATA_MAX_LENGTH_UINT8 ( LR11XX_FLASH_DATA_MAX_LENGTH_UINT32 * 4 ) + +#define LR11XX_BL_CMD_NO_PARAM_LENGTH ( 2 ) +#define LR11XX_BL_GET_STATUS_CMD_LENGTH ( 2 + 4 ) +#define LR11XX_BL_VERSION_CMD_LENGTH LR11XX_BL_CMD_NO_PARAM_LENGTH +#define LR11XX_BL_ERASE_FLASH_CMD_LENGTH LR11XX_BL_CMD_NO_PARAM_LENGTH +#define LR11XX_BL_WRITE_FLASH_ENCRYPTED_CMD_LENGTH ( LR11XX_BL_CMD_NO_PARAM_LENGTH + 4 ) +#define LR11XX_BL_REBOOT_CMD_LENGTH ( LR11XX_BL_CMD_NO_PARAM_LENGTH + 1 ) +#define LR11XX_BL_GET_PIN_CMD_LENGTH ( LR11XX_BL_CMD_NO_PARAM_LENGTH ) +#define LR11XX_BL_READ_CHIP_EUI_CMD_LENGTH ( LR11XX_BL_CMD_NO_PARAM_LENGTH ) +#define LR11XX_BL_READ_JOIN_EUI_CMD_LENGTH ( LR11XX_BL_CMD_NO_PARAM_LENGTH ) + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE TYPES ----------------------------------------------------------- + */ + +/*! + * @brief Operating codes for bootloader-related operations + */ +enum +{ + LR11XX_BL_GET_STATUS_OC = 0x0100, + LR11XX_BL_GET_VERSION_OC = 0x0101, + LR11XX_BL_ERASE_FLASH_OC = 0x8000, + LR11XX_BL_WRITE_FLASH_ENCRYPTED_OC = 0x8003, + LR11XX_BL_REBOOT_OC = 0x8005, + LR11XX_BL_GET_PIN_OC = 0x800B, + LR11XX_BL_READ_CHIP_EUI_OC = 0x800C, + LR11XX_BL_READ_JOIN_EUI_OC = 0x800D, +}; + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE VARIABLES ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DECLARATION ------------------------------------------- + */ + +/*! + * @brief Returns the minimum of the operand given as parameter and the maximum allowed block size + * + * @param [in] operand Size to compare + * + * @returns Minimum between operand and @ref LR11XX_FLASH_DATA_MAX_LENGTH_UINT32 + */ +static uint8_t lr11xx_bootloader_get_min_from_operand_and_max_block_size( uint32_t operand ); + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS DEFINITION --------------------------------------------- + */ + +lr11xx_status_t lr11xx_bootloader_get_status( const void* context, lr11xx_bootloader_stat1_t* stat1, + lr11xx_bootloader_stat2_t* stat2, + lr11xx_bootloader_irq_mask_t* irq_status ) +{ + uint8_t data[LR11XX_BL_GET_STATUS_CMD_LENGTH]; + + const lr11xx_status_t status = + ( lr11xx_status_t ) lr11xx_hal_direct_read( context, data, LR11XX_BL_GET_STATUS_CMD_LENGTH ); + + if( status == LR11XX_STATUS_OK ) + { + stat1->is_interrupt_active = ( ( data[0] & 0x01 ) != 0 ) ? true : false; + stat1->command_status = ( lr11xx_bootloader_command_status_t ) ( data[0] >> 1 ); + + stat2->is_running_from_flash = ( ( data[1] & 0x01 ) != 0 ) ? true : false; + stat2->chip_mode = ( lr11xx_bootloader_chip_modes_t ) ( ( data[1] & 0x0F ) >> 1 ); + stat2->reset_status = ( lr11xx_bootloader_reset_status_t ) ( ( data[1] & 0xF0 ) >> 4 ); + + *irq_status = + ( ( lr11xx_bootloader_irq_mask_t ) data[2] << 24 ) + ( ( lr11xx_bootloader_irq_mask_t ) data[3] << 16 ) + + ( ( lr11xx_bootloader_irq_mask_t ) data[4] << 8 ) + ( ( lr11xx_bootloader_irq_mask_t ) data[5] << 0 ); + } + + return status; +} + +lr11xx_status_t lr11xx_bootloader_clear_reset_status_info( const void* context ) +{ + const uint8_t cbuffer[LR11XX_BL_CMD_NO_PARAM_LENGTH] = { + ( uint8_t ) ( LR11XX_BL_GET_STATUS_OC >> 8 ), + ( uint8_t ) ( LR11XX_BL_GET_STATUS_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_BL_CMD_NO_PARAM_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_bootloader_get_version( const void* context, lr11xx_bootloader_version_t* version ) +{ + const uint8_t cbuffer[LR11XX_BL_VERSION_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_BL_GET_VERSION_OC >> 8 ), + ( uint8_t ) ( LR11XX_BL_GET_VERSION_OC >> 0 ), + }; + uint8_t rbuffer[LR11XX_BL_VERSION_LENGTH] = { 0x00 }; + + const lr11xx_status_t status = ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_BL_VERSION_CMD_LENGTH, + rbuffer, LR11XX_BL_VERSION_LENGTH ); + + if( status == LR11XX_STATUS_OK ) + { + version->hw = rbuffer[0]; + version->type = rbuffer[1]; + version->fw = ( ( uint16_t ) rbuffer[2] << 8 ) + ( uint16_t ) rbuffer[3]; + } + + return status; +} + +lr11xx_status_t lr11xx_bootloader_erase_flash( const void* context ) +{ + const uint8_t cbuffer[LR11XX_BL_ERASE_FLASH_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_BL_ERASE_FLASH_OC >> 8 ), + ( uint8_t ) ( LR11XX_BL_ERASE_FLASH_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_BL_ERASE_FLASH_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_bootloader_write_flash_encrypted( const void* context, const uint32_t offset, + const uint32_t* data, uint8_t length ) +{ + const uint8_t cbuffer[LR11XX_BL_WRITE_FLASH_ENCRYPTED_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_BL_WRITE_FLASH_ENCRYPTED_OC >> 8 ), + ( uint8_t ) ( LR11XX_BL_WRITE_FLASH_ENCRYPTED_OC >> 0 ), + ( uint8_t ) ( offset >> 24 ), + ( uint8_t ) ( offset >> 16 ), + ( uint8_t ) ( offset >> 8 ), + ( uint8_t ) ( offset >> 0 ), + }; + + uint8_t cdata[256] = { 0 }; + for( uint8_t index = 0; index < length; index++ ) + { + uint8_t* cdata_local = &cdata[index * sizeof( uint32_t )]; + + cdata_local[0] = ( uint8_t ) ( data[index] >> 24 ); + cdata_local[1] = ( uint8_t ) ( data[index] >> 16 ); + cdata_local[2] = ( uint8_t ) ( data[index] >> 8 ); + cdata_local[3] = ( uint8_t ) ( data[index] >> 0 ); + } + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_BL_WRITE_FLASH_ENCRYPTED_CMD_LENGTH, cdata, + length * sizeof( uint32_t ) ); +} + +lr11xx_status_t lr11xx_bootloader_write_flash_encrypted_full( const void* context, const uint32_t offset, + const uint32_t* buffer, const uint32_t length ) +{ + uint32_t remaining_length = length; + uint32_t local_offset = offset; + uint32_t loop = 0; + + while( remaining_length != 0 ) + { + const lr11xx_status_t status = lr11xx_bootloader_write_flash_encrypted( + context, local_offset, buffer + loop * LR11XX_FLASH_DATA_MAX_LENGTH_UINT32, + lr11xx_bootloader_get_min_from_operand_and_max_block_size( remaining_length ) ); + + if( status != LR11XX_STATUS_OK ) + { + return status; + } + + local_offset += LR11XX_FLASH_DATA_MAX_LENGTH_UINT8; + remaining_length = ( remaining_length < LR11XX_FLASH_DATA_MAX_LENGTH_UINT32 ) + ? 0 + : ( remaining_length - LR11XX_FLASH_DATA_MAX_LENGTH_UINT32 ); + + loop++; + } + + return LR11XX_STATUS_OK; +} + +lr11xx_status_t lr11xx_bootloader_reboot( const void* context, const bool stay_in_bootloader ) +{ + const uint8_t cbuffer[LR11XX_BL_REBOOT_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_BL_REBOOT_OC >> 8 ), + ( uint8_t ) ( LR11XX_BL_REBOOT_OC >> 0 ), + ( stay_in_bootloader == true ) ? 0x03 : 0x00, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_BL_REBOOT_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_bootloader_read_pin( const void* context, lr11xx_bootloader_pin_t pin ) +{ + const uint8_t cbuffer[LR11XX_BL_GET_PIN_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_BL_GET_PIN_OC >> 8 ), + ( uint8_t ) ( LR11XX_BL_GET_PIN_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_BL_GET_PIN_CMD_LENGTH, pin, + LR11XX_BL_PIN_LENGTH ); +} + +lr11xx_status_t lr11xx_bootloader_read_chip_eui( const void* context, lr11xx_bootloader_chip_eui_t chip_eui ) +{ + const uint8_t cbuffer[LR11XX_BL_READ_CHIP_EUI_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_BL_READ_CHIP_EUI_OC >> 8 ), + ( uint8_t ) ( LR11XX_BL_READ_CHIP_EUI_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_BL_READ_CHIP_EUI_CMD_LENGTH, chip_eui, + LR11XX_BL_CHIP_EUI_LENGTH ); +} + +lr11xx_status_t lr11xx_bootloader_read_join_eui( const void* context, lr11xx_bootloader_join_eui_t join_eui ) +{ + const uint8_t cbuffer[LR11XX_BL_READ_JOIN_EUI_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_BL_READ_JOIN_EUI_OC >> 8 ), + ( uint8_t ) ( LR11XX_BL_READ_JOIN_EUI_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_BL_READ_JOIN_EUI_CMD_LENGTH, join_eui, + LR11XX_BL_JOIN_EUI_LENGTH ); +} + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DEFINITION -------------------------------------------- + */ + +uint8_t lr11xx_bootloader_get_min_from_operand_and_max_block_size( uint32_t operand ) +{ + if( operand > LR11XX_FLASH_DATA_MAX_LENGTH_UINT32 ) + { + return LR11XX_FLASH_DATA_MAX_LENGTH_UINT32; + } + else + { + return ( uint8_t ) operand; + } +} + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/src/lr11xx_bootloader.h b/src/lr11xx_bootloader.h new file mode 100644 index 0000000..23b2c2a --- /dev/null +++ b/src/lr11xx_bootloader.h @@ -0,0 +1,208 @@ +/*! + * @file lr11xx_bootloader.h + * + * @brief Bootloader driver definition for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR11XX_BOOTLOADER_H +#define LR11XX_BOOTLOADER_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include "lr11xx_bootloader_types.h" +#include "lr11xx_types.h" + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +typedef uint32_t lr11xx_bootloader_irq_mask_t; + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +/*! + * @brief Return the status registers and interrupt flags + * + * @remark To simplify system integration, this function does not actually execute the GetStatus command, which would + * require bidirectional SPI communication. It obtains the stat1, stat2, and irq_status values by performing an ordinary + * SPI read (which is required to send null/NOP bytes on the MOSI line). This is possible since the LR11XX returns these + * values automatically whenever a read that does not directly follow a response-carrying command is performed. Unlike + * with the GetStatus command, however, the reset status information is NOT cleared by this command. The function @ref + * lr11xx_bootloader_clear_reset_status_info may be used for this purpose when necessary. + * + * @param [in] context Chip implementation context + * @param [out] stat1 Content of status register 1 + * @param [out] stat2 Content of status register 2 + * @param [out] irq_status Interrupt flags + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_bootloader_get_status( const void* context, lr11xx_bootloader_stat1_t* stat1, + lr11xx_bootloader_stat2_t* stat2, + lr11xx_bootloader_irq_mask_t* irq_status ); + +/*! + * @brief Clear the reset status information stored in stat2 + * + * @param [in] context Chip implementation context + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_bootloader_clear_reset_status_info( const void* context ); + +/*! + * @brief Return the version of the system (hardware and software) + * + * @param [in] context Chip implementation context + * @param [out] version Pointer to the structure holding the system version + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_bootloader_get_version( const void* context, lr11xx_bootloader_version_t* version ); + +/*! + * @brief Erase the whole flash memory of the chip + * + * This function shall be called before any attempt to write a new firmware in flash memory + * + * @param [in] context Chip implementation context + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_bootloader_erase_flash( const void* context ); + +/*! + * @brief Write encrypted data in program flash memory of the chip + * + * This function shall be used when updating the encrypted flash content of the LR11XX. + * The encrypted flash payload to transfer shall be represented as an array of words (i.e. 4-byte values). + * + * @param [in] context Chip implementation context + * @param [in] offset The offset from start register of flash + * @param [in] buffer Buffer holding the encrypted content. Its size in words must be at least length + * @param [in] length Number of words (i.e. 4 bytes) in the buffer to transfer + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_bootloader_write_flash_encrypted( const void* context, const uint32_t offset, + const uint32_t* buffer, const uint8_t length ); + +/*! + * @brief Write encrypted data in program flash memory of the chip + * + * This function shall be used when updating the encrypted flash content of the LR11XX. + * The encrypted flash payload to transfer shall be represented as an array of words (ie 4-byte values). + * + * @param [in] context Chip implementation context + * @param [in] offset The offset from start register of flash + * @param [in] buffer Buffer holding the encrypted content. Its size in words must be at least length + * @param [in] length Number of words (i.e. 4 bytes) in the buffer to transfer + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_bootloader_write_flash_encrypted_full( const void* context, const uint32_t offset, + const uint32_t* buffer, const uint32_t length ); + +/*! + * @brief Software reset of the chip. + * + * This method should be used to reboot the chip in a specified mode. + * Rebooting in flash mode presumes that the content in flash memory is not corrupted (i.e. the integrity check + * performed by the bootloader before executing the first instruction in flash is OK). + * + * @param [in] context Chip implementation context + * @param [in] stay_in_bootloader Selector to stay in bootloader or execute flash code after reboot. If true, the + * bootloader will not execute the flash code but activate SPI interface to allow firmware upgrade + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_bootloader_reboot( const void* context, const bool stay_in_bootloader ); + +/*! + * @brief Returns the 4-byte PIN which can be used to claim a device on cloud services. + * + * @param [in] context Chip implementation context + * @param [out] pin Pointer to the array to be populated with the PIN + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_bootloader_read_pin( const void* context, lr11xx_bootloader_pin_t pin ); + +/*! + * @brief Read and return the Chip EUI + * + * @param [in] context Chip implementation context + * @param [out] chip_eui The buffer to be filled with chip EUI of the LR11XX. It is up to the application to ensure + * chip_eui is long enough to hold the chip EUI + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_bootloader_read_chip_eui( const void* context, lr11xx_bootloader_chip_eui_t chip_eui ); + +/*! + * @brief Read and return the Join EUI + * + * @param [in] context Chip implementation context + * @param [out] join_eui The buffer to be filled with Join EUI of the LR11XX. It is up to the application to ensure + * join_eui is long enough to hold the join EUI + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_bootloader_read_join_eui( const void* context, lr11xx_bootloader_join_eui_t join_eui ); + +#ifdef __cplusplus +} +#endif + +#endif // LR11XX_BOOTLOADER_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/src/lr11xx_bootloader_types.h b/src/lr11xx_bootloader_types.h new file mode 100644 index 0000000..257d019 --- /dev/null +++ b/src/lr11xx_bootloader_types.h @@ -0,0 +1,179 @@ +/*! + * @file lr11xx_bootloader_types.h + * + * @brief Bootloader driver types for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR11XX_BOOTLOADER_TYPES_H +#define LR11XX_BOOTLOADER_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include +#include + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/*! + * @brief Length in byte of the LR11XX version blob + */ +#define LR11XX_BL_VERSION_LENGTH ( 4 ) + +/*! + * @brief Length in bytes of a PIN + */ +#define LR11XX_BL_PIN_LENGTH ( 4 ) + +/*! + * @brief Length in bytes of a chip EUI + */ +#define LR11XX_BL_CHIP_EUI_LENGTH ( 8 ) + +/*! + * @brief Length in bytes of a join EUI + */ +#define LR11XX_BL_JOIN_EUI_LENGTH ( 8 ) + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/*! + * @brief Fixed-length array to store a PIN + */ +typedef uint8_t lr11xx_bootloader_pin_t[LR11XX_BL_PIN_LENGTH]; + +/*! + * @brief Fixed-length array to store a chipEUI + */ +typedef uint8_t lr11xx_bootloader_chip_eui_t[LR11XX_BL_CHIP_EUI_LENGTH]; + +/*! + * @brief Fixed-length array to store a joinEUI + */ +typedef uint8_t lr11xx_bootloader_join_eui_t[LR11XX_BL_JOIN_EUI_LENGTH]; + +/*! + * @brief Chip modes + */ +typedef enum lr11xx_bootloader_chip_modes_e +{ + LR11XX_BOOTLOADER_CHIP_MODE_SLEEP = 0x00, + LR11XX_BOOTLOADER_CHIP_MODE_STBY_RC = 0x01, + LR11XX_BOOTLOADER_CHIP_MODE_STBY_XOSC = 0x02, + LR11XX_BOOTLOADER_CHIP_MODE_FS = 0x03, + LR11XX_BOOTLOADER_CHIP_MODE_RX = 0x04, + LR11XX_BOOTLOADER_CHIP_MODE_TX = 0x05, + LR11XX_BOOTLOADER_CHIP_MODE_LOC = 0x06, +} lr11xx_bootloader_chip_modes_t; + +/*! + * @brief Reset status + */ +typedef enum lr11xx_bootloader_reset_status_e +{ + LR11XX_BOOTLOADER_RESET_STATUS_CLEARED = 0x00, + LR11XX_BOOTLOADER_RESET_STATUS_ANALOG = 0x01, + LR11XX_BOOTLOADER_RESET_STATUS_EXTERNAL = 0x02, + LR11XX_BOOTLOADER_RESET_STATUS_SYSTEM = 0x03, + LR11XX_BOOTLOADER_RESET_STATUS_WATCHDOG = 0x04, + LR11XX_BOOTLOADER_RESET_STATUS_IOCD_RESTART = 0x05, + LR11XX_BOOTLOADER_RESET_STATUS_RTC_RESTART = 0x06, +} lr11xx_bootloader_reset_status_t; + +/*! + * @brief Command status + */ +typedef enum lr11xx_bootloader_command_status_e +{ + LR11XX_BOOTLOADER_CMD_STATUS_FAIL = 0x00, + LR11XX_BOOTLOADER_CMD_STATUS_PERR = 0x01, + LR11XX_BOOTLOADER_CMD_STATUS_OK = 0x02, + LR11XX_BOOTLOADER_CMD_STATUS_DATA = 0x03, +} lr11xx_bootloader_command_status_t; + +/*! + * @brief Status register 1 structure definition + */ +typedef struct lr11xx_bootloader_stat1_s +{ + lr11xx_bootloader_command_status_t command_status; + bool is_interrupt_active; +} lr11xx_bootloader_stat1_t; + +/*! + * @brief Status register 2 structure definition + */ +typedef struct lr11xx_bootloader_stat2_s +{ + lr11xx_bootloader_reset_status_t reset_status; + lr11xx_bootloader_chip_modes_t chip_mode; + bool is_running_from_flash; +} lr11xx_bootloader_stat2_t; + +/*! + * @brief Bootloader version structure definition + */ +typedef struct lr11xx_bootloader_version_s +{ + uint8_t hw; + uint8_t type; + uint16_t fw; +} lr11xx_bootloader_version_t; + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +#ifdef __cplusplus +} +#endif + +#endif // LR11XX_BOOTLOADER_TYPES_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/src/lr11xx_crypto_engine.c b/src/lr11xx_crypto_engine.c new file mode 100644 index 0000000..9d4128a --- /dev/null +++ b/src/lr11xx_crypto_engine.c @@ -0,0 +1,491 @@ +/*! + * @file lr11xx_crypto_engine.c + * + * @brief Cryptographic engine driver implementation for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include "lr11xx_crypto_engine.h" +#include "lr11xx_hal.h" + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE MACROS----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE CONSTANTS ------------------------------------------------------- + */ + +#define LR11XX_CRYPTO_SELECT_CMD_LENGTH ( 2 + 1 ) +#define LR11XX_CRYPTO_SET_KEY_CMD_LENGTH ( 2 + 17 ) +#define LR11XX_CRYPTO_DERIVE_KEY_CMD_LENGTH ( 2 + 18 ) +#define LR11XX_CRYPTO_PROCESS_JOIN_ACCEPT_CMD_LENGTH ( 2 + 3 + 12 + 32 ) +#define LR11XX_CRYPTO_COMPUTE_AES_CMAC_CMD_LENGTH ( 2 + 1 + 272 ) +#define LR11XX_CRYPTO_VERIFY_AES_CMAC_CMD_LENGTH ( 2 + 1 + 4 + 256 ) +#define LR11XX_CRYPTO_AES_ENCRYPT_CMD_LENGTH ( 2 + 1 + 256 ) +#define LR11XX_CRYPTO_AES_DECRYPT_CMD_LENGTH ( 2 + 1 + 256 ) +#define LR11XX_CRYPTO_STORE_TO_FLASH_CMD_LENGTH ( 2 ) +#define LR11XX_CRYPTO_RESTORE_FROM_FLASH_CMD_LENGTH ( 2 ) +#define LR11XX_CRYPTO_SET_PARAMETER_CMD_LENGTH ( 2 + 1 + 4 ) +#define LR11XX_CRYPTO_GET_PARAMETER_CMD_LENGTH ( 2 + 1 ) + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE TYPES ----------------------------------------------------------- + */ + +/*! + * @brief Operating codes for crypto-related operations + */ +enum +{ + LR11XX_CRYPTO_SELECT_OC = 0x0500, + LR11XX_CRYPTO_SET_KEY_OC = 0x0502, + LR11XX_CRYPTO_DERIVE_KEY_OC = 0x0503, + LR11XX_CRYPTO_PROCESS_JOIN_ACCEPT_OC = 0x0504, + LR11XX_CRYPTO_COMPUTE_AES_CMAC_OC = 0x0505, + LR11XX_CRYPTO_VERIFY_AES_CMAC_OC = 0x0506, + LR11XX_CRYPTO_ENCRYPT_AES_01_OC = 0x0507, + LR11XX_CRYPTO_ENCRYPT_AES_OC = 0x0508, + LR11XX_CRYPTO_DECRYPT_AES_OC = 0x0509, + LR11XX_CRYPTO_STORE_TO_FLASH_OC = 0x050A, + LR11XX_CRYPTO_RESTORE_FROM_FLASH_OC = 0x050B, + LR11XX_CRYPTO_SET_PARAMETER_OC = 0x050D, + LR11XX_CRYPTO_GET_PARAMETER_OC = 0x050E, +}; + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE VARIABLES ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DECLARATION ------------------------------------------- + */ + +/*! + * @brief Helper function that fill the cbuffer provided in first argument with the command opcode, the key id + * and the data to encrypt/decrypt/compute aes cmac + * + * @param [out] cbuffer Buffer used to build the frame + * @param [in] opcode Opcode to be added to the frame + * @param [in] key_id Key ID to be added to the frame + * @param [in] data Data to be added to the frame + * @param [in] length Number of bytes from data to be added to the frame + * + * @warning The caller MUST ensure cbuffer is array is big enough to contain opcode, key_id, and data! + */ +static void lr11xx_crypto_fill_cbuffer_opcode_key_data( uint8_t* cbuffer, uint16_t opcode, uint8_t key_id, + const uint8_t* data, uint16_t length ); + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS DEFINITION --------------------------------------------- + */ + +lr11xx_status_t lr11xx_crypto_select( const void* context, const lr11xx_crypto_element_t element ) +{ + uint8_t cbuffer[LR11XX_CRYPTO_SELECT_CMD_LENGTH] = { 0x00 }; + + cbuffer[0] = ( uint8_t ) ( LR11XX_CRYPTO_SELECT_OC >> 8 ); + cbuffer[1] = ( uint8_t ) ( LR11XX_CRYPTO_SELECT_OC >> 0 ); + + cbuffer[2] = element; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_CRYPTO_SELECT_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_crypto_set_key( const void* context, lr11xx_crypto_status_t* status, const uint8_t key_id, + const lr11xx_crypto_key_t key ) +{ + uint8_t cbuffer[LR11XX_CRYPTO_SET_KEY_CMD_LENGTH] = { 0x00 }; + uint8_t rbuffer[LR11XX_CRYPTO_STATUS_LENGTH] = { 0x00 }; + + cbuffer[0] = ( uint8_t ) ( LR11XX_CRYPTO_SET_KEY_OC >> 8 ); + cbuffer[1] = ( uint8_t ) ( LR11XX_CRYPTO_SET_KEY_OC >> 0 ); + + cbuffer[2] = key_id; + + for( uint8_t index = 0; index < sizeof( lr11xx_crypto_key_t ); index++ ) + { + cbuffer[3 + index] = key[index]; + } + + const lr11xx_hal_status_t hal_status = + lr11xx_hal_read( context, cbuffer, LR11XX_CRYPTO_SET_KEY_CMD_LENGTH, rbuffer, LR11XX_CRYPTO_STATUS_LENGTH ); + + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + *status = ( lr11xx_crypto_status_t ) rbuffer[0]; + } + + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_crypto_derive_key( const void* context, lr11xx_crypto_status_t* status, const uint8_t src_key_id, + const uint8_t dest_key_id, const lr11xx_crypto_nonce_t nonce ) +{ + uint8_t cbuffer[LR11XX_CRYPTO_DERIVE_KEY_CMD_LENGTH] = { 0x00 }; + uint8_t rbuffer[LR11XX_CRYPTO_STATUS_LENGTH] = { 0x00 }; + + cbuffer[0] = ( uint8_t ) ( LR11XX_CRYPTO_DERIVE_KEY_OC >> 8 ); + cbuffer[1] = ( uint8_t ) ( LR11XX_CRYPTO_DERIVE_KEY_OC >> 0 ); + + cbuffer[2] = src_key_id; + cbuffer[3] = dest_key_id; + + for( uint8_t index = 0; index < LR11XX_CRYPTO_NONCE_LENGTH; index++ ) + { + cbuffer[4 + index] = nonce[index]; + } + + const lr11xx_hal_status_t hal_status = + lr11xx_hal_read( context, cbuffer, LR11XX_CRYPTO_DERIVE_KEY_CMD_LENGTH, rbuffer, LR11XX_CRYPTO_STATUS_LENGTH ); + + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + *status = ( lr11xx_crypto_status_t ) rbuffer[0]; + } + + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_crypto_process_join_accept( const void* context, lr11xx_crypto_status_t* status, + const uint8_t dec_key_id, const uint8_t ver_key_id, + const lr11xx_crypto_lorawan_version_t lorawan_version, + const uint8_t* header, const uint8_t* data_in, const uint8_t length, + uint8_t* data_out ) +{ + uint8_t cbuffer[LR11XX_CRYPTO_PROCESS_JOIN_ACCEPT_CMD_LENGTH] = { 0x00 }; + uint8_t rbuffer[LR11XX_CRYPTO_STATUS_LENGTH + 32] = { 0x00 }; + uint8_t header_length = ( lorawan_version == 0 ) ? 1 : 12; + + cbuffer[0] = ( uint8_t ) ( LR11XX_CRYPTO_PROCESS_JOIN_ACCEPT_OC >> 8 ); + cbuffer[1] = ( uint8_t ) ( LR11XX_CRYPTO_PROCESS_JOIN_ACCEPT_OC >> 0 ); + + cbuffer[2] = dec_key_id; + cbuffer[3] = ver_key_id; + cbuffer[4] = ( uint8_t ) lorawan_version; + + for( uint8_t index = 0; index < header_length; index++ ) + { + cbuffer[5 + index] = header[index]; + } + + for( uint8_t index = 0; index < length; index++ ) + { + cbuffer[5 + header_length + index] = data_in[index]; + } + + const lr11xx_hal_status_t hal_status = + lr11xx_hal_read( context, cbuffer, 2 + 3 + header_length + length, rbuffer, 1 + length ); + + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + *status = ( lr11xx_crypto_status_t ) rbuffer[0]; + + if( *status == LR11XX_CRYPTO_STATUS_SUCCESS ) + { + for( uint8_t index = 0; index < length; index++ ) + { + data_out[index] = rbuffer[1 + index]; + } + } + } + + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_crypto_compute_aes_cmac( const void* context, lr11xx_crypto_status_t* status, + const uint8_t key_id, const uint8_t* data, const uint16_t length, + lr11xx_crypto_mic_t mic ) +{ + uint8_t cbuffer[LR11XX_CRYPTO_COMPUTE_AES_CMAC_CMD_LENGTH] = { 0x00 }; + uint8_t rbuffer[LR11XX_CRYPTO_STATUS_LENGTH + LR11XX_CRYPTO_MIC_LENGTH] = { 0x00 }; + + lr11xx_crypto_fill_cbuffer_opcode_key_data( cbuffer, LR11XX_CRYPTO_COMPUTE_AES_CMAC_OC, key_id, data, length ); + + const lr11xx_hal_status_t hal_status = lr11xx_hal_read( context, cbuffer, 3 + length, rbuffer, + LR11XX_CRYPTO_STATUS_LENGTH + LR11XX_CRYPTO_MIC_LENGTH ); + + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + *status = ( lr11xx_crypto_status_t ) rbuffer[0]; + + if( *status == LR11XX_CRYPTO_STATUS_SUCCESS ) + { + for( uint8_t index = 0; index < LR11XX_CRYPTO_MIC_LENGTH; index++ ) + { + mic[index] = rbuffer[1 + index]; + } + } + } + + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_crypto_verify_aes_cmac( const void* context, lr11xx_crypto_status_t* status, + const uint8_t key_id, const uint8_t* data, const uint16_t length, + const lr11xx_crypto_mic_t mic ) +{ + uint8_t cbuffer[LR11XX_CRYPTO_VERIFY_AES_CMAC_CMD_LENGTH] = { 0x00 }; + uint8_t rbuffer[LR11XX_CRYPTO_STATUS_LENGTH] = { 0x00 }; + + cbuffer[0] = ( uint8_t ) ( LR11XX_CRYPTO_VERIFY_AES_CMAC_OC >> 8 ); + cbuffer[1] = ( uint8_t ) ( LR11XX_CRYPTO_VERIFY_AES_CMAC_OC >> 0 ); + + cbuffer[2] = key_id; + + for( uint8_t index = 0; index < LR11XX_CRYPTO_MIC_LENGTH; index++ ) + { + cbuffer[3 + index] = mic[index]; + } + + for( uint16_t index = 0; index < length; index++ ) + { + cbuffer[3 + LR11XX_CRYPTO_MIC_LENGTH + index] = data[index]; + } + + const lr11xx_hal_status_t hal_status = lr11xx_hal_read( context, cbuffer, 3 + LR11XX_CRYPTO_MIC_LENGTH + length, + rbuffer, LR11XX_CRYPTO_STATUS_LENGTH ); + + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + *status = ( lr11xx_crypto_status_t ) rbuffer[0]; + } + + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_crypto_aes_encrypt_01( const void* context, lr11xx_crypto_status_t* status, const uint8_t key_id, + const uint8_t* data, const uint16_t length, uint8_t* result ) +{ + uint8_t cbuffer[LR11XX_CRYPTO_AES_ENCRYPT_CMD_LENGTH] = { 0x00 }; + uint8_t rbuffer[LR11XX_CRYPTO_STATUS_LENGTH + LR11XX_CRYPTO_DATA_MAX_LENGTH] = { 0x00 }; + + lr11xx_crypto_fill_cbuffer_opcode_key_data( cbuffer, LR11XX_CRYPTO_ENCRYPT_AES_01_OC, key_id, data, length ); + + const lr11xx_hal_status_t hal_status = + lr11xx_hal_read( context, cbuffer, 3 + length, rbuffer, LR11XX_CRYPTO_STATUS_LENGTH + length ); + + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + *status = ( lr11xx_crypto_status_t ) rbuffer[0]; + + if( *status == LR11XX_CRYPTO_STATUS_SUCCESS ) + { + for( uint16_t index = 0; index < length; index++ ) + { + result[index] = rbuffer[1 + index]; + } + } + } + + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_crypto_aes_encrypt( const void* context, lr11xx_crypto_status_t* status, const uint8_t key_id, + const uint8_t* data, const uint16_t length, uint8_t* result ) +{ + uint8_t cbuffer[LR11XX_CRYPTO_AES_ENCRYPT_CMD_LENGTH] = { 0x00 }; + uint8_t rbuffer[LR11XX_CRYPTO_STATUS_LENGTH + LR11XX_CRYPTO_DATA_MAX_LENGTH] = { 0x00 }; + + lr11xx_crypto_fill_cbuffer_opcode_key_data( cbuffer, LR11XX_CRYPTO_ENCRYPT_AES_OC, key_id, data, length ); + + const lr11xx_hal_status_t hal_status = + lr11xx_hal_read( context, cbuffer, 3 + length, rbuffer, LR11XX_CRYPTO_STATUS_LENGTH + length ); + + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + *status = ( lr11xx_crypto_status_t ) rbuffer[0]; + + if( *status == LR11XX_CRYPTO_STATUS_SUCCESS ) + { + for( uint16_t index = 0; index < length; index++ ) + { + result[index] = rbuffer[1 + index]; + } + } + } + + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_crypto_aes_decrypt( const void* context, lr11xx_crypto_status_t* status, const uint8_t key_id, + const uint8_t* data, const uint16_t length, uint8_t* result ) +{ + uint8_t cbuffer[LR11XX_CRYPTO_AES_DECRYPT_CMD_LENGTH] = { 0x00 }; + uint8_t rbuffer[LR11XX_CRYPTO_STATUS_LENGTH + LR11XX_CRYPTO_DATA_MAX_LENGTH] = { 0x00 }; + + lr11xx_crypto_fill_cbuffer_opcode_key_data( cbuffer, LR11XX_CRYPTO_DECRYPT_AES_OC, key_id, data, length ); + + const lr11xx_hal_status_t hal_status = + lr11xx_hal_read( context, cbuffer, 3 + length, rbuffer, LR11XX_CRYPTO_STATUS_LENGTH + length ); + + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + *status = ( lr11xx_crypto_status_t ) rbuffer[0]; + + if( *status == LR11XX_CRYPTO_STATUS_SUCCESS ) + { + for( uint16_t index = 0; index < length; index++ ) + { + result[index] = rbuffer[1 + index]; + } + } + } + + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_crypto_store_to_flash( const void* context, lr11xx_crypto_status_t* status ) +{ + uint8_t cbuffer[LR11XX_CRYPTO_STORE_TO_FLASH_CMD_LENGTH] = { 0x00 }; + uint8_t rbuffer[LR11XX_CRYPTO_STATUS_LENGTH] = { 0x00 }; + + cbuffer[0] = ( uint8_t ) ( LR11XX_CRYPTO_STORE_TO_FLASH_OC >> 8 ); + cbuffer[1] = ( uint8_t ) ( LR11XX_CRYPTO_STORE_TO_FLASH_OC >> 0 ); + + const lr11xx_hal_status_t hal_status = lr11xx_hal_read( context, cbuffer, LR11XX_CRYPTO_STORE_TO_FLASH_CMD_LENGTH, + rbuffer, LR11XX_CRYPTO_STATUS_LENGTH ); + + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + *status = ( lr11xx_crypto_status_t ) rbuffer[0]; + } + + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_crypto_restore_from_flash( const void* context, lr11xx_crypto_status_t* status ) +{ + uint8_t cbuffer[LR11XX_CRYPTO_RESTORE_FROM_FLASH_CMD_LENGTH] = { 0x00 }; + uint8_t rbuffer[LR11XX_CRYPTO_STATUS_LENGTH] = { 0x00 }; + + cbuffer[0] = ( uint8_t ) ( LR11XX_CRYPTO_RESTORE_FROM_FLASH_OC >> 8 ); + cbuffer[1] = ( uint8_t ) ( LR11XX_CRYPTO_RESTORE_FROM_FLASH_OC >> 0 ); + + const lr11xx_hal_status_t hal_status = lr11xx_hal_read( + context, cbuffer, LR11XX_CRYPTO_RESTORE_FROM_FLASH_CMD_LENGTH, rbuffer, LR11XX_CRYPTO_STATUS_LENGTH ); + + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + *status = ( lr11xx_crypto_status_t ) rbuffer[0]; + } + + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_crypto_set_parameter( const void* context, lr11xx_crypto_status_t* status, + const uint8_t param_id, const lr11xx_crypto_param_t parameter ) +{ + uint8_t cbuffer[LR11XX_CRYPTO_SET_PARAMETER_CMD_LENGTH] = { 0x00 }; + uint8_t rbuffer[LR11XX_CRYPTO_STATUS_LENGTH] = { 0x00 }; + + cbuffer[0] = ( uint8_t ) ( LR11XX_CRYPTO_SET_PARAMETER_OC >> 8 ); + cbuffer[1] = ( uint8_t ) ( LR11XX_CRYPTO_SET_PARAMETER_OC >> 0 ); + + cbuffer[2] = param_id; + + for( uint8_t index = 0; index < LR11XX_CRYPTO_PARAMETER_LENGTH; index++ ) + { + cbuffer[3 + index] = parameter[index]; + } + + const lr11xx_hal_status_t hal_status = lr11xx_hal_read( context, cbuffer, LR11XX_CRYPTO_SET_PARAMETER_CMD_LENGTH, + rbuffer, LR11XX_CRYPTO_STATUS_LENGTH ); + + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + *status = ( lr11xx_crypto_status_t ) rbuffer[0]; + } + + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_crypto_get_parameter( const void* context, lr11xx_crypto_status_t* status, + const uint8_t param_id, lr11xx_crypto_param_t parameter ) +{ + uint8_t cbuffer[LR11XX_CRYPTO_GET_PARAMETER_CMD_LENGTH] = { 0x00 }; + uint8_t rbuffer[LR11XX_CRYPTO_STATUS_LENGTH + LR11XX_CRYPTO_PARAMETER_LENGTH] = { 0x00 }; + + cbuffer[0] = ( uint8_t ) ( LR11XX_CRYPTO_GET_PARAMETER_OC >> 8 ); + cbuffer[1] = ( uint8_t ) ( LR11XX_CRYPTO_GET_PARAMETER_OC >> 0 ); + + cbuffer[2] = param_id; + + const lr11xx_hal_status_t hal_status = + lr11xx_hal_read( context, cbuffer, LR11XX_CRYPTO_GET_PARAMETER_CMD_LENGTH, rbuffer, + LR11XX_CRYPTO_STATUS_LENGTH + LR11XX_CRYPTO_PARAMETER_LENGTH ); + + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + *status = ( lr11xx_crypto_status_t ) rbuffer[0]; + + if( *status == LR11XX_CRYPTO_STATUS_SUCCESS ) + { + for( uint8_t index = 0; index < LR11XX_CRYPTO_PARAMETER_LENGTH; index++ ) + { + parameter[index] = rbuffer[1 + index]; + } + } + } + + return ( lr11xx_status_t ) hal_status; +} + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DEFINITION -------------------------------------------- + */ + +static void lr11xx_crypto_fill_cbuffer_opcode_key_data( uint8_t* cbuffer, uint16_t opcode, uint8_t key_id, + const uint8_t* data, uint16_t length ) +{ + cbuffer[0] = ( uint8_t ) ( opcode >> 8 ); + cbuffer[1] = ( uint8_t ) ( opcode >> 0 ); + + cbuffer[2] = key_id; + + for( uint16_t index = 0; index < length; index++ ) + { + cbuffer[3 + index] = data[index]; + } +} + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/src/lr11xx_crypto_engine.h b/src/lr11xx_crypto_engine.h new file mode 100644 index 0000000..1d6528c --- /dev/null +++ b/src/lr11xx_crypto_engine.h @@ -0,0 +1,292 @@ +/*! + * @file lr11xx_crypto_engine.h + * + * @brief Cryptographic engine driver definition for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR11XX_CRYPTO_ENGINE_H +#define LR11XX_CRYPTO_ENGINE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include "lr11xx_crypto_engine_types.h" +#include "lr11xx_types.h" + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +/*! + * @brief Select the crypto element to be used + * + * By default, the internal crypto engine is selected. It is not needed to call this command if one plans to use the + * internal crypto engine. + * + * @param [in] context Chip implementation context + * @param [in] element The type of crypto element to use + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_crypto_select( const void* context, const lr11xx_crypto_element_t element ); + +/*! + * @brief Set a key in the previously selected crypto element. + * + * @param [in] context Chip implementation context + * @param [out] status The status returned by the execution of this cryptographic function + * @param [in] key_id The identifier of the key to be set + * @param [in] key The key to be set + * + * @see lr11xx_crypto_derive_key + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_crypto_set_key( const void* context, lr11xx_crypto_status_t* status, const uint8_t key_id, + const lr11xx_crypto_key_t key ); + +/*! + * @brief Derive a key previously set. + * + * @param [in] context Chip implementation context + * @param [out] status The status returned by the execution of this cryptographic function + * @param [in] src_key_id The identifier of the key to be derived + * @param [in] dest_key_id The identifier where the derived key will be stored after call to @ref + * lr11xx_crypto_store_to_flash + * @param [in] nonce The nonce to be used to perform the derivation + * + * @see lr11xx_crypto_set_key + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_crypto_derive_key( const void* context, lr11xx_crypto_status_t* status, const uint8_t src_key_id, + const uint8_t dest_key_id, const lr11xx_crypto_nonce_t nonce ); + +/*! + * @brief Perform the needed operations to extract the payload from a join accept message. + * + * @param [in] context Chip implementation context + * @param [out] status The status returned by the execution of this cryptographic function + * @param [in] dec_key_id The identifier of the key used for message decryption + * @param [in] ver_key_id The identifier of the key used for MIC verification + * @param [in] lorawan_version LoRaWAN version to know the size of the header + * @param [in] header The header to compute (length linked to lorawan_version) + * @param [in] data The data to compute + * @param [in] length The length in bytes of the data to compute + * @param [out] data_out Placeholder for the decrypted data + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_crypto_process_join_accept( const void* context, lr11xx_crypto_status_t* status, + const uint8_t dec_key_id, const uint8_t ver_key_id, + const lr11xx_crypto_lorawan_version_t lorawan_version, + const uint8_t* header, const uint8_t* data, const uint8_t length, + uint8_t* data_out ); + +/*! + * @brief Compute an AES-CMAC. + * + * @param [in] context Chip implementation context + * @param [out] status The status returned by the execution of this cryptographic function + * @param [in] key_id The identifier of the keyused for the computation + * @param [in] data The data to compute + * @param [in] length The length in bytes of the data to compute + * @param [out] mic Placeholder for the computed MIC (first 4 bytes of the AES-CMAC) + * + * @see lr11xx_crypto_verify_aes_cmac + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_crypto_compute_aes_cmac( const void* context, lr11xx_crypto_status_t* status, + const uint8_t key_id, const uint8_t* data, const uint16_t length, + lr11xx_crypto_mic_t mic ); + +/*! + * @brief Compute an AES-CMAC and make a comparison with a value given as parameter. + * + * @param [in] context Chip implementation context + * @param [out] status The status returned by the execution of this cryptographic function + * @param [in] key_id The identifier of the key to be used for the computation + * @param [in] data The data to compute + * @param [in] length The length in bytes of the data to compute + * @param [in] mic The MIC value (first 4 bytes of the CMAC) use for comparison + * + * @see lr11xx_crypto_compute_aes_cmac + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_crypto_verify_aes_cmac( const void* context, lr11xx_crypto_status_t* status, + const uint8_t key_id, const uint8_t* data, const uint16_t length, + const lr11xx_crypto_mic_t mic ); + +/*! + * @brief Compute an AES encryption with a key ID specified in parameter. + * + * @param [in] context Chip implementation context + * @param [out] status The status returned by the execution of this cryptographic function + * @param [in] key_id The identifier of the key to be used for the computation + * @param [in] data The data to encrypt + * @param [in] length The length in bytes of the data to encrypt - this value shall be a multiple of 16 + * @param [out] result A pointer to a data buffer that will be filled with the encrypted data. Values of this buffer are + * meaningful if and only if the return status is LR11XX_CRYPTO_STATUS_SUCCESS + * + * @see lr11xx_crypto_set_key, lr11xx_crypto_derive_key + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_crypto_aes_encrypt_01( const void* context, lr11xx_crypto_status_t* status, const uint8_t key_id, + const uint8_t* data, const uint16_t length, uint8_t* result ); + +/*! + * @brief Compute an AES encryption with a key ID specified in parameter. + * + * @param [in] context Chip implementation context + * @param [out] status The status returned by the execution of this cryptographic function + * @param [in] key_id The identifier of the key to be used for the computation + * @param [in] data The data to encrypt + * @param [in] length The length in bytes of the data to encrypt - this value shall be a multiple of 16 + * @param [out] result A pointer to a data buffer that will be filled with the encrypted data. Values of this buffer are + * meaningful if and only if the return status is LR11XX_CRYPTO_STATUS_SUCCESS + * + * @see lr11xx_crypto_set_key, lr11xx_crypto_derive_key + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_crypto_aes_encrypt( const void* context, lr11xx_crypto_status_t* status, const uint8_t key_id, + const uint8_t* data, const uint16_t length, uint8_t* result ); + +/*! + * @brief Compute an AES decryption with a key ID specified in parameter. + * + * @param [in] context Chip implementation context + * @param [out] status The status returned by the execution of this cryptographic function + * @param [in] key_id The identifier of the key to be used for the computation + * @param [in] data The data to decrypt + * @param [in] length The length in bytes of the data to decrypt - this value shall be a multiple of 16 + * @param [out] result A pointer to a data buffer that will be filled with the decrypted data. Values of this buffer are + * meaningful if and only if the return status is LR11XX_CRYPTO_STATUS_SUCCESS + * + * @see lr11xx_crypto_set_key, lr11xx_crypto_derive_key + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_crypto_aes_decrypt( const void* context, lr11xx_crypto_status_t* status, const uint8_t key_id, + const uint8_t* data, const uint16_t length, uint8_t* result ); + +/*! + * @brief Store the crypto data (keys, parameters) from RAM into the flash memory. + * + * @param [in] context Chip implementation context + * @param [out] status The status returned by the execution of this cryptographic function + * + * @see lr11xx_crypto_restore_from_flash + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_crypto_store_to_flash( const void* context, lr11xx_crypto_status_t* status ); + +/*! + * @brief Restore the crypto data (keys, parameters) from flash memory into RAM. + * + * @param [in] context Chip implementation context + * @param [out] status The status returned by the execution of this cryptographic function + * + * @see lr11xx_crypto_store_to_flash + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_crypto_restore_from_flash( const void* context, lr11xx_crypto_status_t* status ); + +/*! + * @brief Set a specific parameter identified by param_id in the crypto RAM. + * + * This function does not store a parameter in the flash memory. The parameters shall be stored after using @ref + * lr11xx_crypto_store_to_flash command. + * + * @param [in] context Chip implementation context + * @param [out] status The status returned by the execution of this cryptographic function + * @param [in] param_id The identifier of the parameter to be set + * @param [in] parameter The parameter to be set + * + * @see lr11xx_crypto_get_parameter + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_crypto_set_parameter( const void* context, lr11xx_crypto_status_t* status, + const uint8_t param_id, const lr11xx_crypto_param_t parameter ); + +/*! + * @brief Get a specific parameter identified by paramID from the crypto RAM. + * + * This function does not fetch a parameter from the flash memory. The parameters shall be restored before using @ref + * lr11xx_crypto_restore_from_flash command. + * + * @param [in] context Chip implementation context + * @param [out] status The status returned by the execution of this cryptographic function + * @param [in] param_id The identifier of the parameter to get + * @param [out] parameter The placeholder to store the parameter + * + * @see lr11xx_crypto_set_parameter + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_crypto_get_parameter( const void* context, lr11xx_crypto_status_t* status, + const uint8_t param_id, lr11xx_crypto_param_t parameter ); + +#ifdef __cplusplus +} +#endif + +#endif // LR11XX_CRYPTO_ENGINE_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/src/lr11xx_crypto_engine_types.h b/src/lr11xx_crypto_engine_types.h new file mode 100644 index 0000000..50e2b88 --- /dev/null +++ b/src/lr11xx_crypto_engine_types.h @@ -0,0 +1,199 @@ +/*! + * @file lr11xx_crypto_engine_types.h + * + * @brief Cryptographic engine driver types for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR11XX_CRYPTO_ENGINE_TYPES_H +#define LR11XX_CRYPTO_ENGINE_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/*! + * @brief Length in bytes of a MIC + */ +#define LR11XX_CRYPTO_MIC_LENGTH 0x04 + +/*! + * @brief Length in bytes of a AES CMAC + */ +#define LR11XX_CRYPTO_AES_CMAC_LENGTH 0x10 + +/*! + * @brief Maximum length in bytes of data to be encrypted / decrypted + */ +#define LR11XX_CRYPTO_DATA_MAX_LENGTH 0x0100 + +/*! + * @brief Length in bytes of a key for AES computation + */ +#define LR11XX_CRYPTO_KEY_LENGTH 0x10 + +/*! + * @brief Length in bytes of a nonce + */ +#define LR11XX_CRYPTO_NONCE_LENGTH 0x10 + +/*! + * @brief Length in bytes of a crypto parameter + */ +#define LR11XX_CRYPTO_PARAMETER_LENGTH 0x04 + +/*! + * @brief Length in bytes of the status returned by an API + */ +#define LR11XX_CRYPTO_STATUS_LENGTH 0x01 + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/*! + * @brief Fixed-length array to store an AES CMAC + */ +typedef uint8_t lr11xx_crypto_mic_t[LR11XX_CRYPTO_MIC_LENGTH]; + +/*! + * @brief Fixed-length array to store an AES CMAC + */ +typedef uint8_t lr11xx_crypto_aes_cmac_t[LR11XX_CRYPTO_AES_CMAC_LENGTH]; + +/*! + * @brief Fixed-length array to store a crypto key + */ +typedef uint8_t lr11xx_crypto_key_t[LR11XX_CRYPTO_KEY_LENGTH]; + +/*! + * @brief Fixed-length array to store a crypto nonce + */ +typedef uint8_t lr11xx_crypto_nonce_t[LR11XX_CRYPTO_NONCE_LENGTH]; + +/*! + * @brief Fixed-length array to store a crypto parameter + */ +typedef uint8_t lr11xx_crypto_param_t[LR11XX_CRYPTO_PARAMETER_LENGTH]; + +/*! + * @brief The supported crypto elements + */ +typedef enum +{ + LR11XX_CRYPTO_ELEMENT_CRYPTO_ENGINE = 0x00, //!< Internal crypto engine (default) + LR11XX_CRYPTO_ELEMENT_SECURE_ELEMENT = 0x01, //!< External secure element +} lr11xx_crypto_element_t; + +/*! + * @brief The status returned by the crypto API + */ +typedef enum +{ + LR11XX_CRYPTO_STATUS_SUCCESS = 0x00, //!< The API command was successful + LR11XX_CRYPTO_STATUS_ERROR_FAIL_CMAC = 0x01, //!< AES-CMAC invalid or comparison failed + LR11XX_CRYPTO_STATUS_ERROR_INVALID_KEY_ID = 0x03, //!< Invalid key ID (source, destination) + LR11XX_CRYPTO_STATUS_ERROR_BUFFER_SIZE = 0x05, //!< Invalid data buffer size + LR11XX_CRYPTO_STATUS_ERROR = 0x06, //!< Other error +} lr11xx_crypto_status_t; + +/*! + * @brief The supported LoRaWAN versions + */ +typedef enum +{ + LR11XX_CRYPTO_LORAWAN_VERSION_1_0_X = 0x00, + LR11XX_CRYPTO_LORAWAN_VERSION_1_1_X = 0x01, +} lr11xx_crypto_lorawan_version_t; + +/*! + * @brief Crypto keys table index definition. + */ +typedef enum lr11xx_crypto_keys_idx_e +{ + LR11XX_CRYPTO_KEYS_IDX_MOTHER_KEY = 1, + LR11XX_CRYPTO_KEYS_IDX_NWK_KEY = 2, + LR11XX_CRYPTO_KEYS_IDX_APP_KEY = 3, + LR11XX_CRYPTO_KEYS_IDX_J_S_ENC_KEY = 4, + LR11XX_CRYPTO_KEYS_IDX_J_S_INT_KEY = 5, + LR11XX_CRYPTO_KEYS_IDX_GP_KE_KEY_0 = 6, + LR11XX_CRYPTO_KEYS_IDX_GP_KE_KEY_1 = 7, + LR11XX_CRYPTO_KEYS_IDX_GP_KE_KEY_2 = 8, + LR11XX_CRYPTO_KEYS_IDX_GP_KE_KEY_3 = 9, + LR11XX_CRYPTO_KEYS_IDX_GP_KE_KEY_4 = 10, + LR11XX_CRYPTO_KEYS_IDX_GP_KE_KEY_5 = 11, + LR11XX_CRYPTO_KEYS_IDX_APP_S_KEY = 12, + LR11XX_CRYPTO_KEYS_IDX_F_NWK_S_INT_KEY = 13, + LR11XX_CRYPTO_KEYS_IDX_S_NWK_S_INT_KEY = 14, + LR11XX_CRYPTO_KEYS_IDX_NWK_S_ENC_KEY = 15, + LR11XX_CRYPTO_KEYS_IDX_RFU_0 = 16, + LR11XX_CRYPTO_KEYS_IDX_RFU_1 = 17, + LR11XX_CRYPTO_KEYS_IDX_MC_APP_S_KEY_0 = 18, + LR11XX_CRYPTO_KEYS_IDX_MC_APP_S_KEY_1 = 19, + LR11XX_CRYPTO_KEYS_IDX_MC_APP_S_KEY_2 = 20, + LR11XX_CRYPTO_KEYS_IDX_MC_APP_S_KEY_3 = 21, + LR11XX_CRYPTO_KEYS_IDX_MC_NWK_S_KEY_0 = 22, + LR11XX_CRYPTO_KEYS_IDX_MC_NWK_S_KEY_1 = 23, + LR11XX_CRYPTO_KEYS_IDX_MC_NWK_S_KEY_2 = 24, + LR11XX_CRYPTO_KEYS_IDX_MC_NWK_S_KEY_3 = 25, + LR11XX_CRYPTO_KEYS_IDX_GP0 = 26, + LR11XX_CRYPTO_KEYS_IDX_GP1 = 27, +} lr11xx_crypto_keys_idx_t; + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +#ifdef __cplusplus +} +#endif + +#endif // LR11XX_CRYPTO_ENGINE_TYPES_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/src/lr11xx_driver_module.cmake b/src/lr11xx_driver_module.cmake new file mode 100644 index 0000000..6c7bf17 --- /dev/null +++ b/src/lr11xx_driver_module.cmake @@ -0,0 +1,48 @@ +# @file lr11xx_driver_module.cmake +# +# @brief Defines CMake source files and include directories for this module +# +# --- The Clear BSD License --- +# Copyright Semtech Corporation 2021. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted (subject to the limitations in the disclaimer +# below) provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the Semtech corporation nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY +# THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +# CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT +# NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +set(LR11XX_DRIVER_MODULE_C_SOURCES + ${CMAKE_CURRENT_LIST_DIR}/lr11xx_bootloader.c + ${CMAKE_CURRENT_LIST_DIR}/lr11xx_crypto_engine.c + ${CMAKE_CURRENT_LIST_DIR}/lr11xx_driver_version.c + ${CMAKE_CURRENT_LIST_DIR}/lr11xx_gnss.c + ${CMAKE_CURRENT_LIST_DIR}/lr11xx_lr_fhss.c + ${CMAKE_CURRENT_LIST_DIR}/lr11xx_radio.c + ${CMAKE_CURRENT_LIST_DIR}/lr11xx_radio_timings.c + ${CMAKE_CURRENT_LIST_DIR}/lr11xx_regmem.c + ${CMAKE_CURRENT_LIST_DIR}/lr11xx_system.c + ${CMAKE_CURRENT_LIST_DIR}/lr11xx_wifi.c + ) + +set(LR11XX_DRIVER_MODULE_C_INCLUDES + ${CMAKE_CURRENT_LIST_DIR}/. + ) diff --git a/src/lr11xx_driver_version.c b/src/lr11xx_driver_version.c new file mode 100644 index 0000000..42355bf --- /dev/null +++ b/src/lr11xx_driver_version.c @@ -0,0 +1,88 @@ +/*! + * @file lr11xx_driver_version.c + * + * @brief Placeholder to keep the version of LR11XX driver. + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include "lr11xx_driver_version.h" + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE MACROS----------------------------------------------------------- + */ + +#define STR_HELPER( x ) #x +#define STR( x ) STR_HELPER( x ) + +#define LR11XX_DRIVER_VERSION_FULL \ + "v" STR( LR11XX_DRIVER_VERSION_MAJOR ) "." STR( LR11XX_DRIVER_VERSION_MINOR ) "." STR( LR11XX_DRIVER_VERSION_PATCH ) + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE CONSTANTS ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE TYPES ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE VARIABLES ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DECLARATION ------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS DEFINITION --------------------------------------------- + */ + +const char* lr11xx_driver_version_get_version_string( void ) +{ + return ( const char* ) LR11XX_DRIVER_VERSION_FULL; +} + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DEFINITION -------------------------------------------- + */ + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/src/lr11xx_driver_version.h b/src/lr11xx_driver_version.h new file mode 100644 index 0000000..aba3e22 --- /dev/null +++ b/src/lr11xx_driver_version.h @@ -0,0 +1,90 @@ +/*! + * @file lr11xx_driver_version.h + * + * @brief Placeholder to keep the version of LR11XX driver. + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR11XX_DRIVER_VERSION_H +#define LR11XX_DRIVER_VERSION_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +#define LR11XX_DRIVER_VERSION_MAJOR 2 +#define LR11XX_DRIVER_VERSION_MINOR 1 +#define LR11XX_DRIVER_VERSION_PATCH 0 + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +/*! + * @brief Compare version information with current ones + * + * This macro expands to true boolean value if the version information provided in argument is compatible or + * retro-compatible with the version of this code base + */ +#define LR11XX_DRIVER_VERSION_CHECK( x, y, z ) \ + ( x == LR11XX_DRIVER_VERSION_MAJOR && \ + ( y < LR11XX_DRIVER_VERSION_MINOR || \ + ( y == LR11XX_DRIVER_VERSION_MINOR && z <= LR11XX_DRIVER_VERSION_PATCH ) ) ) + +const char* lr11xx_driver_version_get_version_string( void ); + +#ifdef __cplusplus +} +#endif + +#endif // LR11XX_DRIVER_VERSION_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/src/lr11xx_gnss.c b/src/lr11xx_gnss.c new file mode 100644 index 0000000..c9368f3 --- /dev/null +++ b/src/lr11xx_gnss.c @@ -0,0 +1,742 @@ +/*! + * @file lr11xx_gnss.c + * + * @brief GNSS scan driver implementation for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include "lr11xx_gnss.h" +#include "lr11xx_regmem.h" +#include "lr11xx_system_types.h" +#include "lr11xx_hal.h" + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE MACROS----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE CONSTANTS ------------------------------------------------------- + */ + +#define LR11XX_GNSS_SET_CONSTELLATION_CMD_LENGTH ( 2 + 1 ) +#define LR11XX_GNSS_READ_CONSTELLATION_CMD_LENGTH ( 2 ) +#define LR11XX_GNSS_SET_ALMANAC_UPDATE_CMD_LENGTH ( 2 + 1 ) +#define LR11XX_GNSS_READ_ALMANAC_UPDATE_CMD_LENGTH ( 2 ) +#define LR11XX_GNSS_READ_FW_VERSION_CMD_LENGTH ( 2 ) +#define LR11XX_GNSS_READ_SUPPORTED_CONSTELLATION_CMD_LENGTH ( 2 ) +#define LR11XX_GNSS_SET_SCAN_MODE_CMD_LENGTH ( 2 + 1 ) +#define LR11XX_GNSS_SCAN_AUTONOMOUS_CMD_LENGTH ( 2 + 7 ) +#define LR11XX_GNSS_SCAN_ASSISTED_CMD_LENGTH ( 2 + 7 ) +#define LR11XX_GNSS_SCAN_GET_RES_SIZE_CMD_LENGTH ( 2 ) +#define LR11XX_GNSS_SCAN_READ_RES_CMD_LENGTH ( 2 ) +#define LR11XX_GNSS_ALMANAC_UPDATE_CMD_LENGTH ( 2 ) +#define LR11XX_GNSS_ALMANAC_READ_CMD_LENGTH ( 2 ) +#define LR11XX_GNSS_SET_ASSISTANCE_POSITION_CMD_LENGTH ( 2 + 4 ) +#define LR11XX_GNSS_READ_ASSISTANCE_POSITION_CMD_LENGTH ( 2 ) +#define LR11XX_GNSS_PUSH_SOLVER_MSG_CMD_LENGTH ( 2 ) +#define LR11XX_GNSS_PUSH_DM_MSG_CMD_LENGTH ( 2 ) +#define LR11XX_GNSS_GET_CONTEXT_STATUS_CMD_LENGTH ( 2 ) +#define LR11XX_GNSS_SCAN_GET_TIMINGS_CMD_LENGTH ( 2 ) +#define LR11XX_GNSS_GET_NB_SV_SATELLITES_CMD_LENGTH ( 2 ) +#define LR11XX_GNSS_GET_SV_SATELLITES_CMD_LENGTH ( 2 ) + +#define LR11XX_GNSS_ALMANAC_READ_RBUFFER_LENGTH ( 6 ) +#define LR11XX_GNSS_ALMANAC_DATE_LENGTH ( 2 ) +#define LR11XX_GNSS_ALMANAC_UPDATE_MAX_NB_OF_BLOCKS \ + ( ( LR11XX_CMD_LENGTH_MAX - LR11XX_GNSS_ALMANAC_UPDATE_CMD_LENGTH ) / LR11XX_GNSS_SINGLE_ALMANAC_WRITE_SIZE ) +#define LR11XX_GNSS_READ_ALMANAC_TEMPBUFFER_SIZE_BYTE ( 47 ) +#define LR11XX_GNSS_SCAN_GET_TIMINGS_RBUFFER_LENGTH ( 8 ) +#define LR11XX_GNSS_MAX_DETECTED_SV ( 32 ) +#define LR11XX_GNSS_DETECTED_SV_SINGLE_LENGTH ( 4 ) +#define LR11XX_GNSS_MAX_DETECTED_SV_BUFFER_LENGTH \ + ( LR11XX_GNSS_MAX_DETECTED_SV * LR11XX_GNSS_DETECTED_SV_SINGLE_LENGTH ) +#define LR11XX_GNSS_READ_FIRMWARE_VERSION_RBUFFER_LENGTH ( 2 ) + +#define LR11XX_GNSS_SCALING_LATITUDE 90 +#define LR11XX_GNSS_SCALING_LONGITUDE 180 +#define LR11XX_GNSS_SNR_TO_CNR_OFFSET ( 31 ) + +#define LR11XX_GNSS_SCAN_RESULT_DESTINATION_INDEX ( 0 ) + +/*! + * @brief GNSS scan power consumption + * + * @note these numbers are given for information, it should be modified according to the used hardware. + */ +#define LR11XX_GNSS_RADIO_ACQUISITION_GPS_UA_DCDC ( 15000 ) +#define LR11XX_GNSS_RADIO_ACQUISITION_BEIDOU_UA_DCDC ( 16500 ) +#define LR11XX_GNSS_COMPUTATION_UA_DCDC ( 3100 ) +#define LR11XX_GNSS_RADIO_ACQUISITION_GPS_UA_LDO ( 24500 ) +#define LR11XX_GNSS_RADIO_ACQUISITION_BEIDOU_UA_LDO ( 27300 ) +#define LR11XX_GNSS_COMPUTATION_UA_LDO ( 5000 ) +#define LR11XX_GNSS_IDLE_MODE_UA ( 1500 ) + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE TYPES ----------------------------------------------------------- + */ + +/*! + * @brief Operating codes for GNSS-related operations + */ +enum +{ + LR11XX_GNSS_SET_CONSTELLATION_OC = 0x0400, //!< Set the constellation to use + LR11XX_GNSS_READ_CONSTELLATION_OC = 0x0401, //!< Read the used constellations + LR11XX_GNSS_SET_ALMANAC_UPDATE_OC = 0x0402, //!< Set almanac update configuration + LR11XX_GNSS_READ_ALMANAC_UPDATE_OC = 0x0403, //!< Read the almanac update configuration + LR11XX_GNSS_READ_FW_VERSION_OC = 0x0406, //!< Read the firmware version + LR11XX_GNSS_READ_SUPPORTED_CONSTELLATION_OC = 0x0407, //!< Read the supported constellations + LR11XX_GNSS_SET_SCAN_MODE_OC = 0x0408, //!< Define single or double capture + LR11XX_GNSS_SCAN_AUTONOMOUS_OC = 0x0409, //!< Launch an autonomous scan + LR11XX_GNSS_SCAN_ASSISTED_OC = 0x040A, //!< Launch an assisted scan + LR11XX_GNSS_SCAN_GET_RES_SIZE_OC = 0x040C, //!< Get the size of the output payload + LR11XX_GNSS_SCAN_READ_RES_OC = 0x040D, //!< Read the byte stream + LR11XX_GNSS_ALMANAC_UPDATE_OC = 0x040E, //!< Update the almanac + LR11XX_GNSS_ALMANAC_READ_OC = 0x040F, //!< Read all almanacs + LR11XX_GNSS_SET_ASSISTANCE_POSITION_OC = 0x0410, //!< Set the assistance position + LR11XX_GNSS_READ_ASSISTANCE_POSITION_OC = 0x0411, //!< Read the assistance position + LR11XX_GNSS_PUSH_SOLVER_MSG_OC = 0x0414, //!< Push messages coming from the solver + LR11XX_GNSS_PUSH_DM_MSG_OC = 0x0415, //!< Push messages coming from the device management + LR11XX_GNSS_GET_CONTEXT_STATUS_OC = 0x0416, //!< Read the context + LR11XX_GNSS_GET_NB_SATELLITES_OC = 0x0417, //!< Get the number of satellites detected during a scan + LR11XX_GNSS_GET_SATELLITES_OC = 0x0418, //!< Get the list of satellites detected during a scan + LR11XX_GNSS_GET_TIMINGS_OC = 0x0419, //!< Get the time spent in signal acquisition and analysis +}; + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE VARIABLES ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DECLARATION ------------------------------------------- + */ + +/*! + * @brief Helper function that convert an array of uint8_t into a uint32_t single value + * + * @warning It is up to the caller to ensure that value points to an array of at least sizeof(uint32_t) elements. + * + * @param [in] value Array of uint8_t to be translated into a uint32_t + * + * @returns 32-bit value + */ +static uint32_t lr11xx_gnss_uint8_to_uint32( uint8_t value[4] ); + +/*! + * @brief Returns the minimum of the operand given as parameter and the maximum allowed number of blocks + * + * @param [in] operand Size to compare + * + * @returns Minimum between operand and @ref LR11XX_GNSS_ALMANAC_UPDATE_MAX_NB_OF_BLOCKS + */ +static uint16_t lr11xx_gnss_get_min_from_operand_and_max_nb_of_blocks( uint16_t operand ); + +/*! + * @brief Get the almanac base address and size + * + * @param [in] context Chip implementation context + * @param [out] address Start address of the almanac in memory + * @param [out] size Size of the almanac in byte + * + * @returns Operation status + */ +static lr11xx_status_t lr11xx_gnss_get_almanac_address_size( const void* context, uint32_t* address, uint16_t* size ); + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS DEFINITION --------------------------------------------- + */ + +lr11xx_status_t lr11xx_gnss_get_result_size( const void* context, uint16_t* result_size ) +{ + const uint8_t cbuffer[LR11XX_GNSS_SCAN_GET_RES_SIZE_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_GNSS_SCAN_GET_RES_SIZE_OC >> 8 ), + ( uint8_t ) ( LR11XX_GNSS_SCAN_GET_RES_SIZE_OC >> 0 ), + }; + uint8_t rbuffer[sizeof( uint16_t )] = { 0 }; + + const lr11xx_hal_status_t hal_status = + lr11xx_hal_read( context, cbuffer, LR11XX_GNSS_SCAN_GET_RES_SIZE_CMD_LENGTH, rbuffer, sizeof( uint16_t ) ); + + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + *result_size = ( ( uint16_t ) rbuffer[0] << 8 ) + ( ( uint16_t ) rbuffer[1] ); + } + + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_gnss_read_results( const void* context, uint8_t* result_buffer, + const uint16_t result_buffer_size ) +{ + const uint8_t cbuffer[LR11XX_GNSS_SCAN_READ_RES_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_GNSS_SCAN_READ_RES_OC >> 8 ), + ( uint8_t ) ( LR11XX_GNSS_SCAN_READ_RES_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_GNSS_SCAN_READ_RES_CMD_LENGTH, result_buffer, + result_buffer_size ); +} + +lr11xx_status_t lr11xx_gnss_get_timings( const void* context, lr11xx_gnss_timings_t* timings ) +{ + const uint8_t cbuffer[LR11XX_GNSS_SCAN_GET_TIMINGS_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_GNSS_GET_TIMINGS_OC >> 8 ), + ( uint8_t ) ( LR11XX_GNSS_GET_TIMINGS_OC >> 0 ), + }; + uint8_t rbuffer[LR11XX_GNSS_SCAN_GET_TIMINGS_RBUFFER_LENGTH] = { 0 }; + const lr11xx_hal_status_t hal_status = lr11xx_hal_read( context, cbuffer, LR11XX_GNSS_SCAN_GET_TIMINGS_CMD_LENGTH, + rbuffer, LR11XX_GNSS_SCAN_GET_TIMINGS_RBUFFER_LENGTH ); + + timings->computation_ms = lr11xx_gnss_uint8_to_uint32( &rbuffer[0] ) / 1000; + timings->radio_ms = lr11xx_gnss_uint8_to_uint32( &rbuffer[4] ) / 1000; + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_gnss_almanac_update( const void* context, const uint8_t* blocks, const uint8_t nb_of_blocks ) +{ + const uint8_t cbuffer[LR11XX_GNSS_ALMANAC_UPDATE_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_GNSS_ALMANAC_UPDATE_OC >> 8 ), + ( uint8_t ) ( LR11XX_GNSS_ALMANAC_UPDATE_OC >> 0 ), + }; + + uint16_t remaining_nb_of_blocks = nb_of_blocks; + + while( remaining_nb_of_blocks > 0 ) + { + const uint16_t nb_of_blocks_to_write = + lr11xx_gnss_get_min_from_operand_and_max_nb_of_blocks( remaining_nb_of_blocks ); + + const uint8_t* blocks_to_write = + blocks + ( nb_of_blocks - remaining_nb_of_blocks ) * LR11XX_GNSS_SINGLE_ALMANAC_WRITE_SIZE; + + const lr11xx_hal_status_t status = + lr11xx_hal_write( context, cbuffer, LR11XX_GNSS_ALMANAC_UPDATE_CMD_LENGTH, blocks_to_write, + nb_of_blocks_to_write * LR11XX_GNSS_SINGLE_ALMANAC_WRITE_SIZE ); + + if( status != LR11XX_HAL_STATUS_OK ) + { + return ( lr11xx_status_t ) status; + } + + remaining_nb_of_blocks -= nb_of_blocks_to_write; + } + + return LR11XX_STATUS_OK; +} + +lr11xx_status_t lr11xx_gnss_read_almanac( const void* context, + lr11xx_gnss_almanac_full_read_bytestream_t almanac_bytestream ) +{ + uint32_t almanac_address = 0; + uint16_t almanac_size = 0; + lr11xx_status_t status = lr11xx_gnss_get_almanac_address_size( context, &almanac_address, &almanac_size ); + if( status != LR11XX_STATUS_OK ) + { + return status; + } + + const uint8_t N_READ_ALMANAC_REGMEM32 = 15; + + for( uint8_t index_regmem32 = 0; index_regmem32 < N_READ_ALMANAC_REGMEM32; index_regmem32++ ) + { + const uint16_t local_bytestream_index_burst = + index_regmem32 * LR11XX_GNSS_READ_ALMANAC_TEMPBUFFER_SIZE_BYTE * 4; + uint32_t temporary_buffer[LR11XX_GNSS_READ_ALMANAC_TEMPBUFFER_SIZE_BYTE] = { 0 }; + + const lr11xx_status_t local_status = lr11xx_regmem_read_regmem32( + context, almanac_address, temporary_buffer, LR11XX_GNSS_READ_ALMANAC_TEMPBUFFER_SIZE_BYTE ); + if( local_status != LR11XX_STATUS_OK ) + { + return local_status; + } + + almanac_address += ( LR11XX_GNSS_READ_ALMANAC_TEMPBUFFER_SIZE_BYTE * 4 ); + + for( uint8_t index_local_temp = 0; index_local_temp < LR11XX_GNSS_READ_ALMANAC_TEMPBUFFER_SIZE_BYTE; + index_local_temp++ ) + { + const uint16_t local_bytestream_index = local_bytestream_index_burst + ( index_local_temp * 4 ); + almanac_bytestream[local_bytestream_index + 0] = ( uint8_t ) ( temporary_buffer[index_local_temp] >> 0 ); + almanac_bytestream[local_bytestream_index + 1] = ( uint8_t ) ( temporary_buffer[index_local_temp] >> 8 ); + almanac_bytestream[local_bytestream_index + 2] = ( uint8_t ) ( temporary_buffer[index_local_temp] >> 16 ); + almanac_bytestream[local_bytestream_index + 3] = ( uint8_t ) ( temporary_buffer[index_local_temp] >> 24 ); + } + } + return status; +} + +lr11xx_status_t lr11xx_gnss_get_almanac_age_for_satellite( const void* context, const lr11xx_gnss_satellite_id_t sv_id, + uint16_t* almanac_age ) +{ + uint32_t almanac_base_address = 0; + uint16_t almanac_size = 0; + const lr11xx_status_t status_get_almanac_address_size = + lr11xx_gnss_get_almanac_address_size( context, &almanac_base_address, &almanac_size ); + + if( status_get_almanac_address_size != LR11XX_STATUS_OK ) + { + return status_get_almanac_address_size; + } + + const uint16_t offset_almanac_date = sv_id * LR11XX_GNSS_SINGLE_ALMANAC_READ_SIZE + 1; + uint8_t raw_almanac_date[LR11XX_GNSS_ALMANAC_DATE_LENGTH] = { 0 }; + + const lr11xx_status_t status_read_mem = lr11xx_regmem_read_mem8( + context, almanac_base_address + offset_almanac_date, raw_almanac_date, LR11XX_GNSS_ALMANAC_DATE_LENGTH ); + if( status_read_mem == LR11XX_STATUS_OK ) + { + // Note: the memory on LR11XX is LSB first. As the 2-byte wide almanac age is obtained by calling the _mem8, the + // conversion to uint16_t here is done LSB first + ( *almanac_age ) = + ( ( ( uint16_t ) raw_almanac_date[1] ) << 8 ) + ( ( ( uint16_t ) raw_almanac_date[0] ) << 0 ); + } + return status_read_mem; +} + +lr11xx_status_t lr11xx_gnss_get_almanac_address_size( const void* context, uint32_t* address, uint16_t* size ) +{ + const uint8_t cbuffer[LR11XX_GNSS_ALMANAC_READ_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_GNSS_ALMANAC_READ_OC >> 8 ), + ( uint8_t ) ( LR11XX_GNSS_ALMANAC_READ_OC >> 0 ), + }; + uint8_t rbuffer[LR11XX_GNSS_ALMANAC_READ_RBUFFER_LENGTH] = { 0 }; + + const lr11xx_hal_status_t hal_status = lr11xx_hal_read( context, cbuffer, LR11XX_GNSS_ALMANAC_READ_CMD_LENGTH, + rbuffer, LR11XX_GNSS_ALMANAC_READ_RBUFFER_LENGTH ); + + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + *address = lr11xx_gnss_uint8_to_uint32( &rbuffer[0] ); + *size = ( ( ( uint16_t ) rbuffer[4] ) << 8 ) | ( ( uint16_t ) rbuffer[5] ); + } + + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_gnss_push_solver_msg( const void* context, const uint8_t* payload, const uint16_t payload_size ) +{ + const uint8_t cbuffer[LR11XX_GNSS_PUSH_SOLVER_MSG_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_GNSS_PUSH_SOLVER_MSG_OC >> 8 ), + ( uint8_t ) ( LR11XX_GNSS_PUSH_SOLVER_MSG_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_GNSS_PUSH_SOLVER_MSG_CMD_LENGTH, payload, + payload_size ); +} + +lr11xx_status_t lr11xx_gnss_set_constellations_to_use( const void* context, + const lr11xx_gnss_constellation_mask_t constellation_to_use ) +{ + const uint8_t cbuffer[LR11XX_GNSS_SET_CONSTELLATION_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_GNSS_SET_CONSTELLATION_OC >> 8 ), + ( uint8_t ) ( LR11XX_GNSS_SET_CONSTELLATION_OC >> 0 ), + constellation_to_use, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_GNSS_SET_CONSTELLATION_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_gnss_read_used_constellations( const void* context, + lr11xx_gnss_constellation_mask_t* constellations_used ) +{ + const uint8_t cbuffer[LR11XX_GNSS_READ_CONSTELLATION_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_GNSS_READ_CONSTELLATION_OC >> 8 ), + ( uint8_t ) ( LR11XX_GNSS_READ_CONSTELLATION_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_GNSS_READ_CONSTELLATION_CMD_LENGTH, + constellations_used, sizeof( *constellations_used ) ); +} + +lr11xx_status_t lr11xx_gnss_set_almanac_update( const void* context, + const lr11xx_gnss_constellation_mask_t constellations_to_update ) +{ + const uint8_t cbuffer[LR11XX_GNSS_SET_ALMANAC_UPDATE_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_GNSS_SET_ALMANAC_UPDATE_OC >> 8 ), + ( uint8_t ) ( LR11XX_GNSS_SET_ALMANAC_UPDATE_OC >> 0 ), + constellations_to_update, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_GNSS_SET_ALMANAC_UPDATE_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_gnss_read_almanac_update( const void* context, + lr11xx_gnss_constellation_mask_t* constellations_to_update ) +{ + const uint8_t cbuffer[LR11XX_GNSS_READ_ALMANAC_UPDATE_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_GNSS_READ_ALMANAC_UPDATE_OC >> 8 ), + ( uint8_t ) ( LR11XX_GNSS_READ_ALMANAC_UPDATE_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_GNSS_READ_ALMANAC_UPDATE_CMD_LENGTH, + constellations_to_update, sizeof( *constellations_to_update ) ); +} + +lr11xx_status_t lr11xx_gnss_read_firmware_version( const void* context, lr11xx_gnss_version_t* version ) +{ + const uint8_t cbuffer[LR11XX_GNSS_READ_FW_VERSION_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_GNSS_READ_FW_VERSION_OC >> 8 ), + ( uint8_t ) ( LR11XX_GNSS_READ_FW_VERSION_OC >> 0 ), + }; + uint8_t rbuffer[LR11XX_GNSS_READ_FIRMWARE_VERSION_RBUFFER_LENGTH] = { 0 }; + + const lr11xx_hal_status_t hal_status = lr11xx_hal_read( context, cbuffer, LR11XX_GNSS_READ_FW_VERSION_CMD_LENGTH, + rbuffer, LR11XX_GNSS_READ_FIRMWARE_VERSION_RBUFFER_LENGTH ); + + version->gnss_firmware = rbuffer[0]; + version->gnss_almanac = rbuffer[1]; + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_gnss_read_supported_constellations( const void* context, + lr11xx_gnss_constellation_mask_t* supported_constellations ) +{ + const uint8_t cbuffer[LR11XX_GNSS_READ_SUPPORTED_CONSTELLATION_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_GNSS_READ_SUPPORTED_CONSTELLATION_OC >> 8 ), + ( uint8_t ) ( LR11XX_GNSS_READ_SUPPORTED_CONSTELLATION_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_GNSS_READ_SUPPORTED_CONSTELLATION_CMD_LENGTH, + supported_constellations, sizeof( *supported_constellations ) ); +} + +lr11xx_status_t lr11xx_gnss_set_scan_mode( const void* context, const lr11xx_gnss_scan_mode_t scan_mode ) +{ + const uint8_t cbuffer[LR11XX_GNSS_SET_SCAN_MODE_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_GNSS_SET_SCAN_MODE_OC >> 8 ), + ( uint8_t ) ( LR11XX_GNSS_SET_SCAN_MODE_OC >> 0 ), + ( uint8_t ) scan_mode, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_GNSS_SET_SCAN_MODE_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_gnss_scan_autonomous( const void* context, const lr11xx_gnss_date_t date, + const lr11xx_gnss_search_mode_t effort_mode, + const uint8_t gnss_input_parameters, const uint8_t nb_sat ) +{ + const uint8_t cbuffer[LR11XX_GNSS_SCAN_AUTONOMOUS_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_GNSS_SCAN_AUTONOMOUS_OC >> 8 ), + ( uint8_t ) ( LR11XX_GNSS_SCAN_AUTONOMOUS_OC >> 0 ), + ( uint8_t ) ( date >> 24 ), + ( uint8_t ) ( date >> 16 ), + ( uint8_t ) ( date >> 8 ), + ( uint8_t ) ( date >> 0 ), + ( uint8_t ) effort_mode, + gnss_input_parameters, + nb_sat, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_GNSS_SCAN_AUTONOMOUS_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_gnss_scan_assisted( const void* context, const lr11xx_gnss_date_t date, + const lr11xx_gnss_search_mode_t effort_mode, + const uint8_t gnss_input_parameters, const uint8_t nb_sat ) +{ + const uint8_t cbuffer[LR11XX_GNSS_SCAN_ASSISTED_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_GNSS_SCAN_ASSISTED_OC >> 8 ), + ( uint8_t ) ( LR11XX_GNSS_SCAN_ASSISTED_OC >> 0 ), + ( uint8_t ) ( date >> 24 ), + ( uint8_t ) ( date >> 16 ), + ( uint8_t ) ( date >> 8 ), + ( uint8_t ) ( date >> 0 ), + ( uint8_t ) effort_mode, + gnss_input_parameters, + nb_sat, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_GNSS_SCAN_ASSISTED_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_gnss_set_assistance_position( + const void* context, const lr11xx_gnss_solver_assistance_position_t* assistance_position ) +{ + const int16_t latitude = ( ( assistance_position->latitude * 2048 ) / LR11XX_GNSS_SCALING_LATITUDE ); + const int16_t longitude = ( ( assistance_position->longitude * 2048 ) / LR11XX_GNSS_SCALING_LONGITUDE ); + const uint8_t cbuffer[LR11XX_GNSS_SET_ASSISTANCE_POSITION_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_GNSS_SET_ASSISTANCE_POSITION_OC >> 8 ), + ( uint8_t ) ( LR11XX_GNSS_SET_ASSISTANCE_POSITION_OC >> 0 ), + ( uint8_t ) ( latitude >> 8 ), + ( uint8_t ) ( latitude >> 0 ), + ( uint8_t ) ( longitude >> 8 ), + ( uint8_t ) ( longitude >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_GNSS_SET_ASSISTANCE_POSITION_CMD_LENGTH, 0, + 0 ); +} + +lr11xx_status_t lr11xx_gnss_read_assistance_position( const void* context, + lr11xx_gnss_solver_assistance_position_t* assistance_position ) +{ + uint8_t position_buffer[4] = { 0x00 }; + int16_t position_tmp; + const uint8_t cbuffer[LR11XX_GNSS_READ_ASSISTANCE_POSITION_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_GNSS_READ_ASSISTANCE_POSITION_OC >> 8 ), + ( uint8_t ) ( LR11XX_GNSS_READ_ASSISTANCE_POSITION_OC >> 0 ), + }; + + const lr11xx_hal_status_t hal_status = lr11xx_hal_read( + context, cbuffer, LR11XX_GNSS_READ_ASSISTANCE_POSITION_CMD_LENGTH, position_buffer, sizeof( position_buffer ) ); + + position_tmp = ( int16_t ) ( ( ( uint16_t ) position_buffer[0] << 8 ) + position_buffer[1] ); + assistance_position->latitude = ( ( float ) ( position_tmp ) *LR11XX_GNSS_SCALING_LATITUDE ) / 2048; + + position_tmp = ( int16_t ) ( ( ( uint16_t ) position_buffer[2] << 8 ) + position_buffer[3] ); + assistance_position->longitude = ( ( float ) ( position_tmp ) *LR11XX_GNSS_SCALING_LONGITUDE ) / 2048; + + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_gnss_push_dmc_msg( const void* context, uint8_t* dmc_msg, uint16_t dmc_msg_len ) +{ + const uint8_t cbuffer[LR11XX_GNSS_PUSH_DM_MSG_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_GNSS_PUSH_DM_MSG_OC >> 8 ), + ( uint8_t ) ( LR11XX_GNSS_PUSH_DM_MSG_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_GNSS_PUSH_DM_MSG_CMD_LENGTH, dmc_msg, + dmc_msg_len ); +} + +lr11xx_status_t lr11xx_gnss_get_context_status( const void* context, + lr11xx_gnss_context_status_bytestream_t context_status ) +{ + const uint8_t cbuffer[LR11XX_GNSS_GET_CONTEXT_STATUS_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_GNSS_GET_CONTEXT_STATUS_OC >> 8 ), + ( uint8_t ) ( LR11XX_GNSS_GET_CONTEXT_STATUS_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_GNSS_GET_CONTEXT_STATUS_CMD_LENGTH, + context_status, LR11XX_GNSS_CONTEXT_STATUS_LENGTH ); +} + +lr11xx_status_t lr11xx_gnss_get_nb_detected_satellites( const void* context, uint8_t* nb_detected_satellites ) +{ + const uint8_t cbuffer[LR11XX_GNSS_GET_NB_SV_SATELLITES_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_GNSS_GET_NB_SATELLITES_OC >> 8 ), + ( uint8_t ) ( LR11XX_GNSS_GET_NB_SATELLITES_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_GNSS_GET_NB_SV_SATELLITES_CMD_LENGTH, + nb_detected_satellites, 1 ); +} + +lr11xx_status_t lr11xx_gnss_get_detected_satellites( + const void* context, const uint8_t nb_detected_satellites, + lr11xx_gnss_detected_satellite_t* detected_satellite_id_snr_doppler ) +{ + const uint8_t max_satellites_to_fetch = + ( LR11XX_GNSS_MAX_DETECTED_SV > nb_detected_satellites ) ? nb_detected_satellites : LR11XX_GNSS_MAX_DETECTED_SV; + const uint16_t read_size = max_satellites_to_fetch * LR11XX_GNSS_DETECTED_SV_SINGLE_LENGTH; + uint8_t result_buffer[LR11XX_GNSS_MAX_DETECTED_SV_BUFFER_LENGTH] = { 0 }; + + const uint8_t cbuffer[LR11XX_GNSS_GET_SV_SATELLITES_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_GNSS_GET_SATELLITES_OC >> 8 ), + ( uint8_t ) ( LR11XX_GNSS_GET_SATELLITES_OC >> 0 ), + }; + + const lr11xx_hal_status_t hal_status = + lr11xx_hal_read( context, cbuffer, LR11XX_GNSS_GET_SV_SATELLITES_CMD_LENGTH, result_buffer, read_size ); + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + for( uint8_t index_satellite = 0; index_satellite < max_satellites_to_fetch; index_satellite++ ) + { + const uint16_t local_result_buffer_index = index_satellite * LR11XX_GNSS_DETECTED_SV_SINGLE_LENGTH; + lr11xx_gnss_detected_satellite_t* local_satellite_result = + &detected_satellite_id_snr_doppler[index_satellite]; + + local_satellite_result->satellite_id = result_buffer[local_result_buffer_index]; + local_satellite_result->cnr = result_buffer[local_result_buffer_index + 1] + LR11XX_GNSS_SNR_TO_CNR_OFFSET; + local_satellite_result->doppler = ( int16_t ) ( ( result_buffer[local_result_buffer_index + 2] << 8 ) + + ( result_buffer[local_result_buffer_index + 3] << 0 ) ); + } + } + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_gnss_parse_context_status_buffer( + const lr11xx_gnss_context_status_bytestream_t context_status_bytestream, + lr11xx_gnss_context_status_t* context_status ) +{ + lr11xx_status_t status = LR11XX_STATUS_ERROR; + + if( ( ( lr11xx_gnss_destination_t ) context_status_bytestream[0] == LR11XX_GNSS_DESTINATION_DMC ) && + ( ( lr11xx_gnss_message_dmc_opcode_t ) context_status_bytestream[1] == LR11XX_GNSS_DMC_STATUS ) ) + { + context_status->firmware_version = context_status_bytestream[2]; + + context_status->global_almanac_crc = + ( ( uint32_t ) context_status_bytestream[3] << 0 ) + ( ( uint32_t ) context_status_bytestream[4] << 8 ) + + ( ( uint32_t ) context_status_bytestream[5] << 16 ) + ( ( uint32_t ) context_status_bytestream[6] << 24 ); + + context_status->error_code = ( lr11xx_gnss_error_code_t ) ( context_status_bytestream[7] >> 4 ); + + context_status->almanac_update_gps = + ( ( context_status_bytestream[7] & LR11XX_GNSS_DMC_ALMANAC_UPDATE_GPS_MASK ) != 0 ) ? true : false; + + context_status->almanac_update_beidou = + ( ( context_status_bytestream[7] & LR11XX_GNSS_DMC_ALMANAC_UPDATE_BEIDOU_MASK ) != 0 ) ? true : false; + + context_status->freq_search_space = + ( lr11xx_gnss_freq_search_space_t ) ( ( ( ( context_status_bytestream[7] & + LR11XX_GNSS_DMC_FREQUENCY_SEARCH_SPACE_MSB_MASK ) >> + LR11XX_GNSS_DMC_FREQUENCY_SEARCH_SPACE_MSB_POS ) + << 1 ) + + ( ( context_status_bytestream[8] & + LR11XX_GNSS_DMC_FREQUENCY_SEARCH_SPACE_LSB_MASK ) >> + LR11XX_GNSS_DMC_FREQUENCY_SEARCH_SPACE_LSB_POS ) ); + + status = LR11XX_STATUS_OK; + } + + return status; +} + +lr11xx_status_t lr11xx_gnss_get_result_destination( const uint8_t* result_buffer, const uint16_t result_buffer_size, + lr11xx_gnss_destination_t* destination ) +{ + lr11xx_status_t status = LR11XX_STATUS_ERROR; + + if( result_buffer_size != 0 ) + { + switch( result_buffer[LR11XX_GNSS_SCAN_RESULT_DESTINATION_INDEX] ) + { + case LR11XX_GNSS_DESTINATION_HOST: + { + *destination = LR11XX_GNSS_DESTINATION_HOST; + status = LR11XX_STATUS_OK; + break; + } + case LR11XX_GNSS_DESTINATION_SOLVER: + { + *destination = LR11XX_GNSS_DESTINATION_SOLVER; + status = LR11XX_STATUS_OK; + break; + } + case LR11XX_GNSS_DESTINATION_DMC: + { + *destination = LR11XX_GNSS_DESTINATION_DMC; + status = LR11XX_STATUS_OK; + break; + } + } + } + + return status; +} + +uint16_t lr11xx_gnss_compute_almanac_age( uint16_t almanac_date, + uint16_t nb_days_between_epoch_and_last_gps_time_rollover, + uint16_t nb_days_since_epoch ) +{ + return nb_days_since_epoch - ( almanac_date + nb_days_between_epoch_and_last_gps_time_rollover ); +} + +uint32_t lr11xx_gnss_get_consumption( lr11xx_system_reg_mode_t regulator, lr11xx_gnss_timings_t timings, + lr11xx_gnss_constellation_mask_t constellations_used ) +{ + uint32_t gnss_scan_consumption_uah = 0; + uint16_t gnss_computation_ua = 0; + uint16_t gnss_radio_acquisition_gps_ua = 0; + uint16_t gnss_radio_acquisition_beidou_ua = 0; + + if( regulator == LR11XX_SYSTEM_REG_MODE_DCDC ) + { + gnss_computation_ua = LR11XX_GNSS_COMPUTATION_UA_DCDC; + gnss_radio_acquisition_gps_ua = LR11XX_GNSS_RADIO_ACQUISITION_GPS_UA_DCDC; + gnss_radio_acquisition_beidou_ua = LR11XX_GNSS_RADIO_ACQUISITION_BEIDOU_UA_DCDC; + } + else + { + gnss_computation_ua = LR11XX_GNSS_COMPUTATION_UA_LDO; + gnss_radio_acquisition_gps_ua = LR11XX_GNSS_RADIO_ACQUISITION_GPS_UA_LDO; + gnss_radio_acquisition_beidou_ua = LR11XX_GNSS_RADIO_ACQUISITION_BEIDOU_UA_LDO; + } + + gnss_scan_consumption_uah = timings.computation_ms * gnss_computation_ua; + + switch( constellations_used ) + { + case LR11XX_GNSS_GPS_MASK: + gnss_scan_consumption_uah += timings.radio_ms * gnss_radio_acquisition_gps_ua; + break; + case LR11XX_GNSS_BEIDOU_MASK: + gnss_scan_consumption_uah += timings.radio_ms * gnss_radio_acquisition_beidou_ua; + break; + case LR11XX_GNSS_GPS_MASK | LR11XX_GNSS_BEIDOU_MASK: + gnss_scan_consumption_uah += + timings.radio_ms * ( ( gnss_radio_acquisition_gps_ua + gnss_radio_acquisition_beidou_ua ) / 2 ); + break; + default: + break; + } + + gnss_scan_consumption_uah = gnss_scan_consumption_uah / ( 3600000 - ( timings.computation_ms + timings.radio_ms ) ); + + return gnss_scan_consumption_uah; +} + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DEFINITION -------------------------------------------- + */ + +uint32_t lr11xx_gnss_uint8_to_uint32( uint8_t* value ) +{ + return ( ( ( uint32_t ) value[0] ) << 24 ) + ( ( ( uint32_t ) value[1] ) << 16 ) + + ( ( ( uint32_t ) value[2] ) << 8 ) + ( ( ( uint32_t ) value[3] ) << 0 ); +} + +uint16_t lr11xx_gnss_get_min_from_operand_and_max_nb_of_blocks( uint16_t operand ) +{ + if( operand > LR11XX_GNSS_ALMANAC_UPDATE_MAX_NB_OF_BLOCKS ) + { + return LR11XX_GNSS_ALMANAC_UPDATE_MAX_NB_OF_BLOCKS; + } + else + { + return operand; + } +} + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/src/lr11xx_gnss.h b/src/lr11xx_gnss.h new file mode 100644 index 0000000..ebd54ef --- /dev/null +++ b/src/lr11xx_gnss.h @@ -0,0 +1,437 @@ +/*! + * @file lr11xx_gnss.h + * + * @brief GNSS scan driver definition for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR11XX_GNSS_H +#define LR11XX_GNSS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include "lr11xx_gnss_types.h" +#include "lr11xx_system_types.h" +#include "lr11xx_types.h" + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +/*! + * @brief Get the size of results + * + * This method returns the size in bytes of the results available in LR11XX result buffer. + * + * @param [in] context Chip implementation context + * @param [out] result_size Result size + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_get_result_size( const void* context, uint16_t* result_size ); + +/*! + * @brief Read GNSS results + * + * The GNSS results are pushed into a buffer directly. This buffer is provided by the application using the driver. It + * MUST be long enough to contains at least result_buffer_size bytes. + * + * @warning No check is done on result_buffer size. If this application provided buffer is too small, there will be a + * buffer overflow bug! + * + * @param [in] context Chip implementation context + * @param [out] result_buffer Application provided buffer to be filled with result + * @param [in] result_buffer_size The number of bytes to read from the LR11XX + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_read_results( const void* context, uint8_t* result_buffer, + const uint16_t result_buffer_size ); + +/*! + * @brief Get the time spent in signal acquisition and signal analysis + * + * These timings allow to compute the current consumption of the last GNSS scan. + * + * @param [in] context Chip implementation context + * @param [out] timings GNSS timings of last GNSS scan + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_get_timings( const void* context, lr11xx_gnss_timings_t* timings ); + +/*! + * @brief Update almanacs given as parameter + * + * @remark Note that information header and almanacs for all 128 SV (i.e. 129 20-byte long blocks) must be updated in a + * row for the whole operation to be successful. Therefore, this function must be called as many times as needed without + * any other operations in between. + * + * @param [in] context Chip implementation context + * @param [in] blocks Buffer containing at least (nb_of_blocks * LR11XX_GNSS_SINGLE_ALMANAC_WRITE_SIZE) bytes of almanac + * @param [in] nb_of_blocks Number of blocks to transfer + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_almanac_update( const void* context, const uint8_t* blocks, const uint8_t nb_of_blocks ); + +/*! + * @brief Read the almanac + * + * @param [in] context Chip implementation context + * @param [out] almanac_bytestream The bytestream of the almanac + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_read_almanac( const void* context, + lr11xx_gnss_almanac_full_read_bytestream_t almanac_bytestream ); + +/*! + * @brief Get almanac age for a satellite + * + * @param [in] context Chip implementation context + * @param [in] sv_id ID of the satellite corresponding the to almanac requested + * @param [out] almanac_age Almanac age in days since last GPS time overlap + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_get_almanac_age_for_satellite( const void* context, const lr11xx_gnss_satellite_id_t sv_id, + uint16_t* almanac_age ); + +/*! + * @brief Push data received from solver to LR11XX + * + * @param [in] context Chip implementation context + * @param [in] payload Payload received from solver + * @param [in] payload_size Size of the payload received from solver (in bytes) + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_push_solver_msg( const void* context, const uint8_t* payload, const uint16_t payload_size ); + +/*! + * @brief Activate the GNSS scan constellation + * + * @param [in] context Chip implementation context + * @param [in] constellation_mask Bit mask of the constellations to use. See @ref lr11xx_gnss_constellation_t for the + * possible values + * + * @returns Operation status + * + * @see lr11xx_gnss_read_used_constellations + */ +lr11xx_status_t lr11xx_gnss_set_constellations_to_use( const void* context, + const lr11xx_gnss_constellation_mask_t constellation_mask ); + +/*! + * @brief Read constellation used by the GNSS scanner from the almanac update configuration + * + * @param [in] context Chip implementation context + * @param [out] constellations_used Bit mask of the constellations used. See @ref lr11xx_gnss_constellation_t for the + * possible values + * + * @returns Operation status + * + * @see lr11xx_gnss_set_constellations_to_use + */ +lr11xx_status_t lr11xx_gnss_read_used_constellations( const void* context, + lr11xx_gnss_constellation_mask_t* constellations_used ); + +/*! + * @brief Activate the almanac update + * + * @param [in] context Chip implementation context + * @param [in] constellations_to_update Bit mask of the constellations to mark to update. See @ref + * lr11xx_gnss_constellation_t for the possible values + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_set_almanac_update( const void* context, + const lr11xx_gnss_constellation_mask_t constellations_to_update ); + +/*! + * @brief Function to read the almanac update configuration + * + * @param [in] context Chip implementation context + * @param [out] constellations_to_update Bit mask of the constellations to mark to update. See @ref + * lr11xx_gnss_constellation_t for the possible values + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_read_almanac_update( const void* context, + lr11xx_gnss_constellation_mask_t* constellations_to_update ); + +/*! + * @brief Function to read the GNSS firmware version + * + * @param [in] context Chip implementation context + * @param [in] version GNSS Firmware version currently running on the chip + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_read_firmware_version( const void* context, lr11xx_gnss_version_t* version ); + +/*! + * @brief Function to read the supported constellation, GPS or BEIDOU other constellations + * + * @param [in] context Chip implementation context + * @param [out] supported_constellations Bit mask of the constellations used. See @ref lr11xx_gnss_constellation_t for + * the possible values + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_read_supported_constellations( const void* context, + lr11xx_gnss_constellation_mask_t* supported_constellations ); + +/*! + * @brief Function to set the GNSS scan mode configuration + * + * @param [in] context Chip implementation context + * @param [in] scan_mode GNSS scan mode + * + * @returns Operation status + * + * @ref lr11xx_gnss_scan_mode_t + */ +lr11xx_status_t lr11xx_gnss_set_scan_mode( const void* context, const lr11xx_gnss_scan_mode_t scan_mode ); + +/*! + * @brief Gnss scan with no assisted parameters needed + * + * @param [in] context Chip implementation context + * @param [in] date The actual date of scan. Its format is the number of seconds elapsed since January the 6th 1980 + * 00:00:00 with leap seconds included. + * @param [in] effort_mode Effort mode @ref lr11xx_gnss_search_mode_t + * @param [in] gnss_input_parameters Bit mask indicating which information is added in the output payload @ref + * lr11xx_gnss_result_fields_legacy_e + * @param [in] nb_sat The expected number of satellite to provide. This value must be in the range [0:128] + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_scan_autonomous( const void* context, const lr11xx_gnss_date_t date, + const lr11xx_gnss_search_mode_t effort_mode, + const uint8_t gnss_input_parameters, const uint8_t nb_sat ); + +/*! + * @brief Gnss scan with assisted parameters. + * + * @param [in] context Chip implementation context + * @param [in] date The actual date of scan. Its format is the number of seconds elapsed since January the 6th 1980 + * 00:00:00 with leap seconds included. + * @param [in] effort_mode Effort mode @ref lr11xx_gnss_search_mode_t + * @param [in] gnss_input_parameters Bit mask indicating which information is added in the output payload @ref + * lr11xx_gnss_result_fields_legacy_e + * @param [in] nb_sat The expected number of satellite to provide. This value must be in the range [0:128] + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_scan_assisted( const void* context, const lr11xx_gnss_date_t date, + const lr11xx_gnss_search_mode_t effort_mode, + const uint8_t gnss_input_parameters, const uint8_t nb_sat ); + +/*! + * @brief Function to set the assistance position. + * + * @param [in] context Chip implementation context + * @param [in] assistance_position, latitude 12 bits and longitude 12 bits + * + * @ref See lr11xx_gnss_solver_assistance_position_t + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_set_assistance_position( + const void* context, const lr11xx_gnss_solver_assistance_position_t* assistance_position ); + +/*! + * @brief Function to read the assistance position. + * + * The assistance position read may be different from the one set beforehand with @ref + * lr11xx_gnss_set_assistance_position due to a scaling computation. + * + * @param [in] context Chip implementation context + * @param [in] assistance_position, latitude 12 bits and longitude 12 bits + * + * @ref See lr11xx_gnss_solver_assistance_position_t + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_read_assistance_position( const void* context, + lr11xx_gnss_solver_assistance_position_t* assistance_position ); + +/*! + * @brief Host receives an update from the network or assembles itself the update message and send it to the LR11XX. + * + * @param [in] context Chip implementation context + * @param [in] dmc_msg buffer containing the update the network + * @param [in] dmc_msg_len length of this buffer + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_push_dmc_msg( const void* context, uint8_t* dmc_msg, uint16_t dmc_msg_len ); + +/*! + * @brief Get the GNSS context status + * + * This function returns the GNSS context status as a raw buffer. It is possible to use + * lr11xx_gnss_parse_context_status_buffer to obtain the details of the context status. + * + * @param [in] context Chip implementation context + * @param [out] context_status_buffer Pointer to a buffer to be filled with context status information. Must be at least + * 7 bytes long. It is up to the caller to ensure there is enough place in this buffer. + * + * @returns Operation status + * + * @see lr11xx_gnss_parse_context_status_buffer + */ +lr11xx_status_t lr11xx_gnss_get_context_status( const void* context, + lr11xx_gnss_context_status_bytestream_t context_status_buffer ); + +/*! + * @brief Get the number of detected satellites during last scan + * + * @param [in] context Chip implementation context + * @param [out] nb_detected_satellites Number of satellites detected + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_get_nb_detected_satellites( const void* context, uint8_t* nb_detected_satellites ); + +/*! + * @brief Get the satellites detected on last scan with their IDs, C/N (aka CNR) and doppler + * + * @note Doppler is returned with 6ppm accuracy. + * + * @param [in] context Chip implementation context + * @param [in] nb_detected_satellites Number of detected satellites on last scan (obtained by calling + * lr11xx_gnss_get_nb_detected_satellites) + * @param [out] detected_satellite_id_snr_doppler Pointer to an array of structures of size big enough to contain + * nb_detected_satellites elements + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_get_detected_satellites( + const void* context, const uint8_t nb_detected_satellites, + lr11xx_gnss_detected_satellite_t* detected_satellite_id_snr_doppler ); + +/** + * @brief Parse a raw buffer of context status + * + * @param [in] context_status_bytestream The raw buffer of context status to parse. It is up to the caller to ensure the + * buffer is at least LR11XX_GNSS_CONTEXT_STATUS_LENGTH bytes long + * @param [out] context_status Pointer to a structure of lr11xx_gnss_context_status_t to be filled with information from + * context_status_bytestream + * + * @returns Operation status + * + * @see lr11xx_gnss_get_context_status + */ +lr11xx_status_t lr11xx_gnss_parse_context_status_buffer( + const lr11xx_gnss_context_status_bytestream_t context_status_bytestream, + lr11xx_gnss_context_status_t* context_status ); + +/** + * @brief Extract the destination from the result returned by a GNSS scan + * + * @param [in] result_buffer Pointer to the buffer holding the result + * @param [in] result_buffer_size Size of the result in byte + * @param [out] destination Destination of the result + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_get_result_destination( const uint8_t* result_buffer, const uint16_t result_buffer_size, + lr11xx_gnss_destination_t* destination ); + +/** + * @brief Helper function that computes the age of an almanac. + * + * This function does not call the LR11XX. + * The almanac age is computed based on the following elements: + * - almanac age as obtained from lr11xx_gnss_get_almanac_age_for_satellite + * - the number of days elapsed between Epoch (January 6th 1980) and the GPS rollover reference of the current + * almanac + * - the GPS date of today expressed in number of days elapsed since Epoch + * + * @remark It is important to use for nb_days_between_epoch_and_corresponding_gps_time_rollover the GPS time rollover + * corresponding to the reference of the almanac_date. This is especially true when current date is just after a GPS + * time rollover. + * + * @param [in] almanac_date Almanac date as obtained from lr11xx_gnss_get_almanac_age_for_satellite + * @param [in] nb_days_between_epoch_and_corresponding_gps_time_rollover Number of days elapsed between GPS Epoch and + * the GPS rollover corresponding to the almanac_date + * @param [in] nb_days_since_epoch Number of days elapsed between January 6th 1980 and now + * + * @returns Age of the almanac expressed in number of days between its start valid instant and now + */ +uint16_t lr11xx_gnss_compute_almanac_age( uint16_t almanac_date, + uint16_t nb_days_between_epoch_and_corresponding_gps_time_rollover, + uint16_t nb_days_since_epoch ); + +/** + * @brief Compute the power consumption in uAh based on the time spent in signal acquisition and signal analysis. + * + * @param [in] regulator The regulator used during last GNSS scan + * @param [in] timings Timings allowing to compute the current consumption + * @param [in] constellations_used Bit mask of the constellations used + * + * @returns Current consumption in uAh + */ +uint32_t lr11xx_gnss_get_consumption( lr11xx_system_reg_mode_t regulator, lr11xx_gnss_timings_t timings, + lr11xx_gnss_constellation_mask_t constellations_used ); + +#ifdef __cplusplus +} +#endif + +#endif // LR11XX_GNSS_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/src/lr11xx_gnss_types.h b/src/lr11xx_gnss_types.h new file mode 100644 index 0000000..8473525 --- /dev/null +++ b/src/lr11xx_gnss_types.h @@ -0,0 +1,312 @@ +/*! + * @file lr11xx_gnss_types.h + * + * @brief GNSS scan driver types for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR11XX_GNSS_TYPES_H +#define LR11XX_GNSS_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include +#include + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/*! + * @brief Maximal buffer size + */ +#define LR11XX_GNSS_MAX_SIZE_ARRAY 2820 //!< (128sv * 22bytes + 4bytes for CRC) + +/*! + * @brief Number of almanacs in full update payload + */ +#define LR11XX_GNSS_FULL_UPDATE_N_ALMANACS ( 128 ) + +/*! + * @brief Size of the almanac of a single satellite when reading + */ +#define LR11XX_GNSS_SINGLE_ALMANAC_READ_SIZE ( 22 ) + +/*! + * @brief Size of the almanac of a single satellite when writing + */ +#define LR11XX_GNSS_SINGLE_ALMANAC_WRITE_SIZE ( 20 ) + +/*! + * @brief Size of the almanac of the GNSS context status buffer + */ +#define LR11XX_GNSS_CONTEXT_STATUS_LENGTH ( 9 ) + +/*! + * @brief Size of the whole almanac when reading + */ +#define LR11XX_GNSS_FULL_ALMANAC_READ_BUFFER_SIZE \ + ( ( LR11XX_GNSS_FULL_UPDATE_N_ALMANACS * LR11XX_GNSS_SINGLE_ALMANAC_READ_SIZE ) + 4 ) + +#define LR11XX_GNSS_DMC_ALMANAC_UPDATE_POS ( 1U ) +#define LR11XX_GNSS_DMC_ALMANAC_UPDATE_GPS_MASK ( 0x01UL << LR11XX_GNSS_DMC_ALMANAC_UPDATE_POS ) +#define LR11XX_GNSS_DMC_ALMANAC_UPDATE_BEIDOU_MASK ( 0x02UL << LR11XX_GNSS_DMC_ALMANAC_UPDATE_POS ) + +#define LR11XX_GNSS_DMC_FREQUENCY_SEARCH_SPACE_MSB_POS ( 0U ) +#define LR11XX_GNSS_DMC_FREQUENCY_SEARCH_SPACE_MSB_MASK ( 0x01UL << LR11XX_GNSS_DMC_FREQUENCY_SEARCH_SPACE_MSB_POS ) + +#define LR11XX_GNSS_DMC_FREQUENCY_SEARCH_SPACE_LSB_POS ( 7U ) +#define LR11XX_GNSS_DMC_FREQUENCY_SEARCH_SPACE_LSB_MASK ( 0x01UL << LR11XX_GNSS_DMC_FREQUENCY_SEARCH_SPACE_LSB_POS ) + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/*! + * @brief Satellite ID type + */ +typedef uint8_t lr11xx_gnss_satellite_id_t; + +/*! + * @brief Bit mask indicating which information is added in the output payload - to be used with @ref + * LR11XX_GNSS_SCAN_MODE_0_SINGLE_SCAN_LEGACY + */ +enum lr11xx_gnss_result_fields_legacy_e +{ + LR11XX_GNSS_RESULTS_LEGACY_PSEUDO_RANGE_MASK = ( 1 << 0 ), //!< Add pseudo-range information if set + LR11XX_GNSS_RESULTS_LEGACY_DOPPLER_MASK = ( 1 << 1 ), //!< Add all Doppler information if set - up to 5 if not + LR11XX_GNSS_RESULTS_LEGACY_BIT_CHANGE_MASK = ( 1 << 2 ), //!< Add bit change if set +}; + +/*! + * @brief bit mask indicating which information is added in the output payload - to be used with @ref + * LR11XX_GNSS_SCAN_MODE_3_SINGLE_SCAN_AND_5_FAST_SCANS + */ +enum lr11xx_gnss_result_fields_e +{ + LR11XX_GNSS_RESULTS_DOPPLER_ENABLE_MASK = ( 1 << 0 ), //!< Add Doppler information if set + LR11XX_GNSS_RESULTS_DOPPLER_MASK = ( 1 << 1 ), //!< Add up to 14 Doppler if set - up to 7 if not. Valid if @ref + //!< LR11XX_GNSS_RESULTS_DOPPLER_ENABLE_MASK is set + LR11XX_GNSS_RESULTS_BIT_CHANGE_MASK = ( 1 << 2 ), //!< Add bit change if set +}; + +/*! + * @brief Constellation identifiers + */ +typedef enum +{ + LR11XX_GNSS_GPS_MASK = 0x01, + LR11XX_GNSS_BEIDOU_MASK = 0x02, +} lr11xx_gnss_constellation_t; + +/*! + * @brief Bit mask of constellation configurations + * + * @see lr11xx_gnss_constellation_t + */ +typedef uint8_t lr11xx_gnss_constellation_mask_t; + +/*! + * @brief Search mode for GNSS scan + */ +typedef enum +{ + LR11XX_GNSS_OPTION_DEFAULT = 0x00, //!< Search all requested satellites or fail + LR11XX_GNSS_OPTION_BEST_EFFORT = 0x01, //!< Add additional search if not all satellites are found +} lr11xx_gnss_search_mode_t; + +/*! + * @brief GNSS response type indicates the destination: Host MCU, GNSS solver or GNSS DMC + */ +typedef enum +{ + LR11XX_GNSS_DESTINATION_HOST = 0x00, //!< Host MCU + LR11XX_GNSS_DESTINATION_SOLVER = 0x01, //!< GNSS Solver + LR11XX_GNSS_DESTINATION_DMC = 0x02, //!< GNSS DMC +} lr11xx_gnss_destination_t; + +/*! + * @brief Message to host indicating the status of the message + */ +typedef enum +{ + LR11XX_GNSS_HOST_OK = 0x00, + LR11XX_GNSS_HOST_UNEXPECTED_CMD = 0x01, + LR11XX_GNSS_HOST_UNIMPLEMENTED_CMD = 0x02, + LR11XX_GNSS_HOST_INVALID_PARAMETERS = 0x03, + LR11XX_GNSS_HOST_MESSAGE_SANITY_CHECK_ERROR = 0x04, + LR11XX_GNSS_HOST_IQ_CAPTURE_FAILS = 0x05, + LR11XX_GNSS_HOST_NO_TIME = 0x06, + LR11XX_GNSS_HOST_NO_SATELLITE_DETECTED = 0x07, + LR11XX_GNSS_HOST_ALMANAC_IN_FLASH_TOO_OLD = 0x08, + LR11XX_GNSS_HOST_ALMANAC_UPDATE_FAILS_CRC_ERROR = 0x09, + LR11XX_GNSS_HOST_ALMANAC_UPDATE_FAILS_FLASH_INTEGRITY_ERROR = 0x0A, + LR11XX_GNSS_HOST_ALMANAC_UPDATE_NOT_ALLOWED = 0x0B, + LR11XX_GNSS_HOST_ALMANAC_CRC_ERROR = 0x0C, + LR11XX_GNSS_HOST_ALMANAC_VERSION_NOT_SUPPORTED = 0x0D, + LR11XX_GNSS_HOST_NOT_ENOUGH_SV_DETECTED_TO_BUILD_A_NAV_MESSAGE = 0x10, +} lr11xx_gnss_message_host_status_t; + +/*! + * @brief Message to DMC operation code + */ +typedef enum +{ + LR11XX_GNSS_DMC_STATUS = 0x18, //!< Status message in payload +} lr11xx_gnss_message_dmc_opcode_t; + +/*! + * @brief GNSS single or double scan mode + */ +typedef enum +{ + LR11XX_GNSS_SCAN_MODE_0_SINGLE_SCAN_LEGACY = 0x00, //!< Generated NAV message format = NAV1 + LR11XX_GNSS_SCAN_MODE_3_SINGLE_SCAN_AND_5_FAST_SCANS = 0x03, //!< Generated NAV message format = NAV2 +} lr11xx_gnss_scan_mode_t; + +/*! + * @brief GNSS error codes + */ +typedef enum lr11xx_gnss_error_code_e +{ + LR11XX_GNSS_NO_ERROR = 0, + LR11XX_GNSS_ERROR_ALMANAC_TOO_OLD = 1, + LR11XX_GNSS_ERROR_UPDATE_CRC_MISMATCH = 2, + LR11XX_GNSS_ERROR_UPDATE_FLASH_MEMORY_INTEGRITY = 3, + LR11XX_GNSS_ERROR_ALMANAC_UPDATE_NOT_ALLOWED = 4, //!< Impossible to update more than one constellation at a time +} lr11xx_gnss_error_code_t; + +/*! + * @brief GNSS frequency search space + */ +typedef enum lr11xx_gnss_freq_search_space_e +{ + LR11XX_GNSS_FREQUENCY_SEARCH_SPACE_250_HZ = 0, + LR11XX_GNSS_FREQUENCY_SEARCH_SPACE_500_HZ = 1, + LR11XX_GNSS_FREQUENCY_SEARCH_SPACE_1_KHZ = 2, + LR11XX_GNSS_FREQUENCY_SEARCH_SPACE_2_KHZ = 3, +} lr11xx_gnss_freq_search_space_t; + +/*! + * @brief Representation of absolute time for GNSS operations + * + * The GNSS absolute time is represented as a 32 bits word that is the number of seconds elapsed since January 6th 1980, + * 00:00:00 + * + * The GNSS absolute time must take into account the Leap Seconds between UTC time and GPS time. + */ +typedef uint32_t lr11xx_gnss_date_t; + +/*! + * @brief Buffer that holds data for all almanacs full update - when reading + */ +typedef uint8_t lr11xx_gnss_almanac_full_read_bytestream_t[LR11XX_GNSS_FULL_ALMANAC_READ_BUFFER_SIZE]; + +/*! + * @brief Buffer that holds data for context status + */ +typedef uint8_t lr11xx_gnss_context_status_bytestream_t[LR11XX_GNSS_CONTEXT_STATUS_LENGTH]; + +/*! + * @brief Assistance position. + */ +typedef struct lr11xx_gnss_solver_assistance_position_s +{ + float latitude; //!< Latitude 12 bits (latitude in degree * 2048/90) with resolution 0.044° + float longitude; //!< Longitude 12 bits (longitude in degree * 2048/180) with resolution 0.088° +} lr11xx_gnss_solver_assistance_position_t; + +/*! + * @brief Detected SV structure + */ +typedef struct lr11xx_gnss_detected_satellite_s +{ + lr11xx_gnss_satellite_id_t satellite_id; + int8_t cnr; //!< Carrier-to-noise ration (C/N) in dB + int16_t doppler; //!< SV doppler in Hz +} lr11xx_gnss_detected_satellite_t; + +/*! + * @brief GNSS timings of the LR11XX + */ +typedef struct lr11xx_gnss_timings_s +{ + uint32_t radio_ms; + uint32_t computation_ms; +} lr11xx_gnss_timings_t; + +/*! + * @brief Version structure of the LR11XX GNSS firmware + */ +typedef struct lr11xx_gnss_version_s +{ + uint8_t gnss_firmware; //!< Version of the firmware + uint8_t gnss_almanac; //!< Version of the almanac format +} lr11xx_gnss_version_t; + +/*! + * @brief Structure for GNSS context status + */ +typedef struct lr11xx_gnss_context_status_s +{ + uint8_t firmware_version; + uint32_t global_almanac_crc; + lr11xx_gnss_error_code_t error_code; + bool almanac_update_gps; + bool almanac_update_beidou; + lr11xx_gnss_freq_search_space_t freq_search_space; +} lr11xx_gnss_context_status_t; + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +#ifdef __cplusplus +} +#endif + +#endif // LR11XX_GNSS_TYPES_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/src/lr11xx_hal.h b/src/lr11xx_hal.h new file mode 100644 index 0000000..0c90695 --- /dev/null +++ b/src/lr11xx_hal.h @@ -0,0 +1,198 @@ +/*! + * @file lr11xx_hal.h + * + * @brief Hardware Abstraction Layer (HAL) interface for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR11XX_HAL_H +#define LR11XX_HAL_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include +#include + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/** + * @brief Write this to SPI bus while reading data, or as a dummy/placeholder + */ +#define LR11XX_NOP ( 0x00 ) + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/*! + * @brief LR11XX HAL status + */ +typedef enum lr11xx_hal_status_e +{ + LR11XX_HAL_STATUS_OK = 0, + LR11XX_HAL_STATUS_ERROR = 3, +} lr11xx_hal_status_t; + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +/*! + * @brief Radio data transfer - write + * + * @param [in] context Radio implementation parameters + * @param [in] command Pointer to the buffer to be transmitted + * @param [in] command_length Buffer size to be transmitted + * @param [in] data Pointer to the buffer to be transmitted + * @param [in] data_length Buffer size to be transmitted + * + * @returns Operation status + */ +lr11xx_hal_status_t lr11xx_hal_write( const void* context, const uint8_t* command, const uint16_t command_length, + const uint8_t* data, const uint16_t data_length ); + +/*! + * @brief Radio data transfer - read + * + * @remark This is a two-step radio read operation. It consists of writing the command, releasing then re-asserting the + * NSS line, then reading a discarded dummy byte followed by data_length bytes of response data from the transceiver. + * While reading the dummy bytes and the response data, the implementation of this function must ensure that only zero + * bytes (NOP) are written to the SPI bus. + * + * @param [in] context Radio implementation parameters + * @param [in] command Pointer to the buffer to be transmitted + * @param [in] command_length Buffer size to be transmitted + * @param [out] data Pointer to the buffer to be received + * @param [in] data_length Buffer size to be received + * + * @returns Operation status + * + * @remark Some hardware SPI implementations write arbitary values on the MOSI line while reading. If this is done on + * the LR11XX, non-zero values may be interpreted as commands. This driver does not exploit this functionality, and + * expects that zeros be sent on the MOSI line when this command is reading the command response data. + */ +lr11xx_hal_status_t lr11xx_hal_read( const void* context, const uint8_t* command, const uint16_t command_length, + uint8_t* data, const uint16_t data_length ); + +/*! + * @brief Direct read from the SPI bus + * + * @remark Unlike @ref lr11xx_hal_read, this is a simple direct SPI bus SS/read/nSS operation. While reading the + * response data, the implementation of this function must ensure that only zero bytes (NOP) are written to the SPI bus. + * + * @remark Formerly, that function depended on a lr11xx_hal_write_read API function, which required bidirectional SPI + * communication. Given that all other radio functionality can be implemented with unidirectional SPI, it has been + * decided to make this HAL API change to simplify implementation requirements. + * + * @remark Only required by the @ref lr11xx_system_get_status and @ref lr11xx_bootloader_get_status commands + * + * @param [in] context Radio implementation parameters + * @param [out] data Pointer to the buffer to be received + * @param [in] data_length Buffer size to be received + * + * @returns Operation status + */ +lr11xx_hal_status_t lr11xx_hal_direct_read( const void* context, uint8_t* data, const uint16_t data_length ); + +/*! + * @brief Reset the radio + * + * @param [in] context Radio implementation parameters + * + * @returns Operation status + */ +lr11xx_hal_status_t lr11xx_hal_reset( const void* context ); + +/*! + * @brief Wake the radio up. + * + * @param [in] context Radio implementation parameters + * + * @returns Operation status + */ +lr11xx_hal_status_t lr11xx_hal_wakeup( const void* context ); + +/*! + * @brief Return the computed CRC + * + * @param [in] initial_value initial value of the CRC + * @param [in] buffer Buffer containing data used to compute the CRC + * @param [in] length Length of buffer + * + * @returns CRC value + */ +inline static uint8_t lr11xx_hal_compute_crc( const uint8_t initial_value, const uint8_t* buffer, uint16_t length ) +{ + uint8_t crc = initial_value; + + for( uint16_t i = 0; i < length; i++ ) + { + uint8_t extract = buffer[i]; + uint8_t sum; + + for( uint8_t j = 8; j > 0; j-- ) + { + sum = ( crc ^ extract ) & 0x01; + crc >>= 1; + + if( sum != 0 ) + { + crc ^= 0x65; + } + + extract >>= 1; + } + } + + return crc; +} + +#ifdef __cplusplus +} +#endif + +#endif // LR11XX_HAL_H diff --git a/src/lr11xx_lr_fhss.c b/src/lr11xx_lr_fhss.c new file mode 100644 index 0000000..cfdac8d --- /dev/null +++ b/src/lr11xx_lr_fhss.c @@ -0,0 +1,290 @@ +/*! + * @file lr11xx_lr_fhss.c + * + * @brief LR_FHSS driver implementation for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include "lr11xx_lr_fhss.h" +#include "lr11xx_radio.h" +#include "lr11xx_hal.h" + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE MACROS----------------------------------------------------------- + */ + +#define LR11XX_LR_FHSS_PKT_TYPE_LR_FHSS ( 0x04 ) +#define LR_FHSS_BITRATE_IN_256_BPS_STEPS ( 125000 ) +#define LR11XX_LR_FHSS_SET_MODULATION_PARAMS_LR_FHSS_CMD_LENGTH ( 2 + 5 ) +#define LR11XX_LR_FHSS_BUILD_FRAME_LENGTH ( 2 + 9 ) +#define LR11XX_LR_FHSS_SET_SYNC_WORD_LENGTH ( 2 + 0 ) +#define LR11XX_LR_FHSS_SET_MODULATION_PARAM_DIVIDE_BITRATE_BY_256 ( 0x80000000 ) +#define LR11XX_LR_FHSS_HEADER_BITS ( 114 ) +#define LR11XX_LR_FHSS_FRAG_BITS ( 48 ) +#define LR11XX_LR_FHSS_BLOCK_PREAMBLE_BITS ( 2 ) +#define LR11XX_LR_FHSS_BLOCK_BITS ( LR11XX_LR_FHSS_FRAG_BITS + LR11XX_LR_FHSS_BLOCK_PREAMBLE_BITS ) +#define LR11XX_LR_FHSS_SYNCWORD_LENGTH ( 4 ) + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE CONSTANTS ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE TYPES ----------------------------------------------------------- + */ + +/*! + * @brief Operating codes for radio-related operations + */ +enum +{ + LR11XX_LR_FHSS_SET_MODULATION_PARAM_OC = 0x020F, + LR11XX_LR_FHSS_BUILD_FRAME_OC = 0x022C, + LR11XX_LR_FHSS_SET_SYNC_WORD_OC = 0x022D, +}; + +/*! + * @brief Hopping enable/disabled enumerations for \ref lr11xx_lr_fhss_build_frame + */ +typedef enum +{ + LR11XX_LR_FHSS_HOPPING_DISABLE = 0x00, + LR11XX_LR_FHSS_HOPPING_ENABLE = 0x01, +} lr11xx_lr_fhss_hopping_configuration_t; + +/*! + * @brief Pulse shape configurations + */ +typedef enum +{ + LR11XX_LR_FHSS_PULSE_SHAPE_BT_1 = 0x0B //!< Gaussian BT 1.0 +} lr11xx_lr_fhss_pulse_shape_t; + +/*! + * @brief Modulation configuration for LR_FHSS packets + */ +typedef struct lr11xx_lr_fhss_mod_params_lr_fhss_s +{ + uint32_t br_in_bps; //!< LR_FHSS bitrate [bit/s] + lr11xx_lr_fhss_pulse_shape_t pulse_shape; //!< LR_FHSS pulse shape +} lr11xx_lr_fhss_mod_params_lr_fhss_t; + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE VARIABLES ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DECLARATION ------------------------------------------- + */ + +/*! + * @brief Set the modulation parameters for LR_FHSS + * + * The command @ref lr11xx_lr_fhss_set_pkt_type must be called prior this one. + * + * @param [in] context Chip implementation context + * @param [in] mod_params The structure of modulation configuration + * + * @returns Operation status + * + * @see lr11xx_lr_fhss_set_pkt_type + */ +static lr11xx_status_t lr11xx_lr_fhss_set_lr_fhss_mod_params( const void* context, + const lr11xx_lr_fhss_mod_params_lr_fhss_t* mod_params ); + +/*! + * @brief Set the syncword for LR_FHSS + * + * Default value: 0x2C0F7995 + * + * @param [in] context Chip implementation context + * @param [in] sync_word The syncword to set. It is up to the caller to ensure this array is at least four bytes long + * + * @returns Operation status + */ +static lr11xx_status_t lr11xx_lr_fhss_set_sync_word( const void* context, + const uint8_t sync_word[LR11XX_LR_FHSS_SYNCWORD_LENGTH] ); + +/*! + * @brief Get the bit count and block count for a LR-FHSS frame + * + * @param [in] params Parameter structure + * @param [in] payload_length Length of physical payload, in bytes + * + * @returns Length of physical payload, in bits + */ + +static uint16_t lr11xx_lr_fhss_get_nb_bits( const lr_fhss_v1_params_t* params, uint16_t payload_length ); + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS DEFINITION --------------------------------------------- + */ + +lr11xx_status_t lr11xx_lr_fhss_init( const void* context ) +{ + const lr11xx_status_t set_packet_type_status = + lr11xx_radio_set_pkt_type( context, LR11XX_LR_FHSS_PKT_TYPE_LR_FHSS ); + if( set_packet_type_status != LR11XX_STATUS_OK ) + { + return set_packet_type_status; + } + + const lr11xx_lr_fhss_mod_params_lr_fhss_t mod_lr_fhss = { + .br_in_bps = LR11XX_LR_FHSS_SET_MODULATION_PARAM_DIVIDE_BITRATE_BY_256 + LR_FHSS_BITRATE_IN_256_BPS_STEPS, + .pulse_shape = LR11XX_LR_FHSS_PULSE_SHAPE_BT_1 + }; + + const lr11xx_status_t set_modulation_param_status = lr11xx_lr_fhss_set_lr_fhss_mod_params( context, &mod_lr_fhss ); + return set_modulation_param_status; +} + +lr11xx_status_t lr11xx_lr_fhss_build_frame( const void* context, const lr11xx_lr_fhss_params_t* lr_fhss_params, + uint16_t hop_sequence_id, const uint8_t* payload, uint8_t payload_length ) +{ + // Since the build_frame command is last, it is possible to check status through stat1 + + lr11xx_status_t status = lr11xx_lr_fhss_set_sync_word( context, lr_fhss_params->lr_fhss_params.sync_word ); + if( status != LR11XX_STATUS_OK ) + { + return status; + } + + const uint8_t cbuffer[LR11XX_LR_FHSS_BUILD_FRAME_LENGTH] = { + ( uint8_t ) ( LR11XX_LR_FHSS_BUILD_FRAME_OC >> 8 ), + ( uint8_t ) ( LR11XX_LR_FHSS_BUILD_FRAME_OC >> 0 ), + ( uint8_t ) lr_fhss_params->lr_fhss_params.header_count, + ( uint8_t ) lr_fhss_params->lr_fhss_params.cr, + ( uint8_t ) lr_fhss_params->lr_fhss_params.modulation_type, + ( uint8_t ) lr_fhss_params->lr_fhss_params.grid, + ( uint8_t ) ( lr_fhss_params->lr_fhss_params.enable_hopping ? LR11XX_LR_FHSS_HOPPING_ENABLE + : LR11XX_LR_FHSS_HOPPING_DISABLE ), + ( uint8_t ) lr_fhss_params->lr_fhss_params.bw, + ( uint8_t ) ( hop_sequence_id >> 8 ), + ( uint8_t ) ( hop_sequence_id >> 0 ), + ( uint8_t ) lr_fhss_params->device_offset, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_LR_FHSS_BUILD_FRAME_LENGTH, payload, + payload_length ); +} + +uint32_t lr11xx_lr_fhss_get_time_on_air_in_ms( const lr11xx_lr_fhss_params_t* params, uint16_t payload_length ) +{ + // Multiply by 1000 / 488.28125, or equivalently 256/125, rounding up + return ( ( lr11xx_lr_fhss_get_nb_bits( ¶ms->lr_fhss_params, payload_length ) << 8 ) + 124 ) / 125; +} + +unsigned int lr11xx_lr_fhss_get_hop_sequence_count( const lr11xx_lr_fhss_params_t* lr_fhss_params ) +{ + if( ( lr_fhss_params->lr_fhss_params.grid == LR_FHSS_V1_GRID_25391_HZ ) || + ( ( lr_fhss_params->lr_fhss_params.grid == LR_FHSS_V1_GRID_3906_HZ ) && + ( lr_fhss_params->lr_fhss_params.bw < LR_FHSS_V1_BW_335938_HZ ) ) ) + { + return 384; + } + return 512; +} + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DEFINITION --------------------------------------------- + */ + +lr11xx_status_t lr11xx_lr_fhss_set_lr_fhss_mod_params( const void* radio, + const lr11xx_lr_fhss_mod_params_lr_fhss_t* mod_params ) +{ + const uint8_t cbuffer[LR11XX_LR_FHSS_SET_MODULATION_PARAMS_LR_FHSS_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_LR_FHSS_SET_MODULATION_PARAM_OC >> 8 ), + ( uint8_t ) ( LR11XX_LR_FHSS_SET_MODULATION_PARAM_OC >> 0 ), + ( uint8_t ) ( mod_params->br_in_bps >> 24 ), + ( uint8_t ) ( mod_params->br_in_bps >> 16 ), + ( uint8_t ) ( mod_params->br_in_bps >> 8 ), + ( uint8_t ) ( mod_params->br_in_bps >> 0 ), + ( uint8_t ) mod_params->pulse_shape, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( radio, cbuffer, + LR11XX_LR_FHSS_SET_MODULATION_PARAMS_LR_FHSS_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_lr_fhss_set_sync_word( const void* context, + const uint8_t sync_word[LR11XX_LR_FHSS_SYNCWORD_LENGTH] ) +{ + const uint8_t cbuffer[LR11XX_LR_FHSS_SET_SYNC_WORD_LENGTH] = { + ( uint8_t ) ( LR11XX_LR_FHSS_SET_SYNC_WORD_OC >> 8 ), + ( uint8_t ) ( LR11XX_LR_FHSS_SET_SYNC_WORD_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_LR_FHSS_SET_SYNC_WORD_LENGTH, sync_word, + LR11XX_LR_FHSS_SYNCWORD_LENGTH ); +} + +uint16_t lr11xx_lr_fhss_get_nb_bits( const lr_fhss_v1_params_t* params, uint16_t payload_length ) +{ + uint16_t length_bits = ( payload_length + 2 ) * 8 + 6; + switch( params->cr ) + { + case LR_FHSS_V1_CR_5_6: + length_bits = ( ( length_bits * 6 ) + 4 ) / 5; + break; + + case LR_FHSS_V1_CR_2_3: + length_bits = length_bits * 3 / 2; + break; + + case LR_FHSS_V1_CR_1_2: + length_bits = length_bits * 2; + break; + + case LR_FHSS_V1_CR_1_3: + length_bits = length_bits * 3; + break; + } + + uint16_t payload_bits = ( length_bits / LR11XX_LR_FHSS_FRAG_BITS ) * LR11XX_LR_FHSS_BLOCK_BITS; + uint16_t last_block_bits = length_bits % LR11XX_LR_FHSS_FRAG_BITS; + if( last_block_bits > 0 ) + { + payload_bits += last_block_bits + 2; + } + + return LR11XX_LR_FHSS_HEADER_BITS * params->header_count + payload_bits; +} \ No newline at end of file diff --git a/src/lr11xx_lr_fhss.h b/src/lr11xx_lr_fhss.h new file mode 100644 index 0000000..9a2711a --- /dev/null +++ b/src/lr11xx_lr_fhss.h @@ -0,0 +1,128 @@ +/*! + * @file lr11xx_lr_fhss.h + * + * @brief LR_FHSS driver definition for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR11XX_LR_FHSS_H +#define LR11XX_LR_FHSS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include "lr11xx_lr_fhss_types.h" +#include "lr11xx_types.h" + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/** + * @brief Length, in bytes, of a LR-FHSS sync word + */ +#define LR_FHSS_SYNC_WORD_BYTES ( 4 ) + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +/*! + * @brief Initialize the LR_FHSS + * + * @param [in] context Chip implementation context + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_lr_fhss_init( const void* context ); + +/*! + * @brief Configure a payload to be sent with LR_FHSS + * + * When calling this method, lr11xx_lr_fhss_set_sync_word is implicitely called to configure the sync word. + * Note that the syncword must be 4 bytes long. + * + * @param [in] context Chip implementation context + * @param [in] lr_fhss_params Parameter configuration structure of the LRFHSS + * @param [in] hop_sequence_id Seed used to derive the hopping sequence pattern. Only the nine LSBs are taken into + * account + * @param [in] payload The payload to send. It is the responsibility of the caller to ensure that this references an + * array containing at least payload_length elements + * @param [in] payload_length The length of the payload + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_lr_fhss_build_frame( const void* context, const lr11xx_lr_fhss_params_t* lr_fhss_params, + uint16_t hop_sequence_id, const uint8_t* payload, uint8_t payload_length ); + +/*! + * @brief Get the time on air in ms for LR-FHSS transmission + * + * @param [in] params LR11XX LR-FHSS parameter structure + * @param [in] payload_length Length of application-layer payload + * + * @returns Time-on-air value in ms for LR-FHSS transmission + */ +uint32_t lr11xx_lr_fhss_get_time_on_air_in_ms( const lr11xx_lr_fhss_params_t* params, uint16_t payload_length ); + +/** + * @brief Return the number of hop sequences available using the given parameters + * + * @param [in] lr_fhss_params Parameter configuration structure of the LRFHSS + * + * @return Returns the number of valid hop sequences (512 or 384) + */ +unsigned int lr11xx_lr_fhss_get_hop_sequence_count( const lr11xx_lr_fhss_params_t* lr_fhss_params ); + +#ifdef __cplusplus +} +#endif + +#endif // LR11XX_LR_FHSS_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/src/lr11xx_lr_fhss_types.h b/src/lr11xx_lr_fhss_types.h new file mode 100644 index 0000000..2e37a06 --- /dev/null +++ b/src/lr11xx_lr_fhss_types.h @@ -0,0 +1,65 @@ +/*! + * @file lr11xx_lr_fhss_types.h + * + * @brief LR_FHSS types definition for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR11XX_LR_FHSS_TYPES_H +#define LR11XX_LR_FHSS_TYPES_H + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include "lr_fhss_v1_base_types.h" + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/*! + * @brief LR FHSS parameter structure + */ +typedef struct +{ + lr_fhss_v1_params_t lr_fhss_params; //!< Base LR FHSS parameters + int8_t device_offset; //> 8 ), + ( uint8_t ) ( LR11XX_RADIO_RESET_STATS_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_RESET_STATS_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_radio_get_gfsk_stats( const void* context, lr11xx_radio_stats_gfsk_t* stats ) +{ + const uint8_t cbuffer[LR11XX_RADIO_GET_STATS_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_GET_STATS_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_GET_STATS_OC >> 0 ), + }; + uint8_t rbuffer[sizeof( lr11xx_radio_stats_gfsk_t )] = { 0x00 }; + + const lr11xx_status_t status = ( lr11xx_status_t ) lr11xx_hal_read( + context, cbuffer, LR11XX_RADIO_GET_STATS_CMD_LENGTH, rbuffer, sizeof( lr11xx_radio_stats_gfsk_t ) ); + + if( status == LR11XX_STATUS_OK ) + { + stats->nb_pkt_received = ( ( uint16_t ) rbuffer[0] << 8 ) + ( uint16_t ) rbuffer[1]; + stats->nb_pkt_crc_error = ( ( uint16_t ) rbuffer[2] << 8 ) + ( uint16_t ) rbuffer[3]; + stats->nb_pkt_len_error = ( ( uint16_t ) rbuffer[4] << 8 ) + ( uint16_t ) rbuffer[5]; + } + + return status; +} + +lr11xx_status_t lr11xx_radio_get_lora_stats( const void* context, lr11xx_radio_stats_lora_t* stats ) +{ + const uint8_t cbuffer[LR11XX_RADIO_GET_STATS_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_GET_STATS_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_GET_STATS_OC >> 0 ), + }; + uint8_t rbuffer[sizeof( lr11xx_radio_stats_lora_t )] = { 0x00 }; + + const lr11xx_status_t status = ( lr11xx_status_t ) lr11xx_hal_read( + context, cbuffer, LR11XX_RADIO_GET_STATS_CMD_LENGTH, rbuffer, sizeof( lr11xx_radio_stats_lora_t ) ); + + if( status == LR11XX_STATUS_OK ) + { + stats->nb_pkt_received = ( ( uint16_t ) rbuffer[0] << 8 ) + ( uint16_t ) rbuffer[1]; + stats->nb_pkt_crc_error = ( ( uint16_t ) rbuffer[2] << 8 ) + ( uint16_t ) rbuffer[3]; + stats->nb_pkt_header_error = ( ( uint16_t ) rbuffer[4] << 8 ) + ( uint16_t ) rbuffer[5]; + stats->nb_pkt_falsesync = ( ( uint16_t ) rbuffer[6] << 8 ) + ( uint16_t ) rbuffer[7]; + } + + return status; +} + +lr11xx_status_t lr11xx_radio_get_pkt_type( const void* context, lr11xx_radio_pkt_type_t* pkt_type ) +{ + const uint8_t cbuffer[LR11XX_RADIO_GET_PKT_TYPE_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_GET_PKT_TYPE_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_GET_PKT_TYPE_OC >> 0 ), + }; + uint8_t pkt_type_raw = 0; + + const lr11xx_status_t status = + ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_RADIO_GET_PKT_TYPE_CMD_LENGTH, &pkt_type_raw, 1 ); + + if( status == LR11XX_STATUS_OK ) + { + *pkt_type = ( lr11xx_radio_pkt_type_t ) pkt_type_raw; + } + + return status; +} + +lr11xx_status_t lr11xx_radio_get_rx_buffer_status( const void* context, + lr11xx_radio_rx_buffer_status_t* rx_buffer_status ) +{ + const uint8_t cbuffer[LR11XX_RADIO_GET_RXBUFFER_STATUS_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_GET_RXBUFFER_STATUS_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_GET_RXBUFFER_STATUS_OC >> 0 ), + }; + uint8_t rbuffer[sizeof( *rx_buffer_status )] = { 0x00 }; + + const lr11xx_status_t status = ( lr11xx_status_t ) lr11xx_hal_read( + context, cbuffer, LR11XX_RADIO_GET_RXBUFFER_STATUS_CMD_LENGTH, rbuffer, sizeof( *rx_buffer_status ) ); + + if( status == LR11XX_STATUS_OK ) + { + rx_buffer_status->pld_len_in_bytes = rbuffer[0]; + rx_buffer_status->buffer_start_pointer = rbuffer[1]; + } + + return status; +} + +lr11xx_status_t lr11xx_radio_get_gfsk_pkt_status( const void* context, lr11xx_radio_pkt_status_gfsk_t* pkt_status ) +{ + const uint8_t cbuffer[LR11XX_RADIO_GET_PKT_STATUS_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_GET_PKT_STATUS_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_GET_PKT_STATUS_OC >> 0 ), + }; + uint8_t rbuffer[4] = { 0x00 }; + + const lr11xx_status_t status = + ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_RADIO_GET_PKT_STATUS_CMD_LENGTH, rbuffer, 4 ); + + if( status == LR11XX_STATUS_OK ) + { + pkt_status->rssi_sync_in_dbm = -( int8_t ) ( rbuffer[0] >> 1 ); + pkt_status->rssi_avg_in_dbm = -( int8_t ) ( rbuffer[1] >> 1 ); + pkt_status->rx_len_in_bytes = rbuffer[2]; + pkt_status->is_addr_err = ( ( rbuffer[3] & 0x20 ) != 0 ) ? true : false; + pkt_status->is_crc_err = ( ( rbuffer[3] & 0x10 ) != 0 ) ? true : false; + pkt_status->is_len_err = ( ( rbuffer[3] & 0x08 ) != 0 ) ? true : false; + pkt_status->is_abort_err = ( ( rbuffer[3] & 0x04 ) != 0 ) ? true : false; + pkt_status->is_received = ( ( rbuffer[3] & 0x02 ) != 0 ) ? true : false; + pkt_status->is_sent = ( ( rbuffer[3] & 0x01 ) != 0 ) ? true : false; + } + + return status; +} + +lr11xx_status_t lr11xx_radio_get_lora_pkt_status( const void* context, lr11xx_radio_pkt_status_lora_t* pkt_status ) +{ + const uint8_t cbuffer[LR11XX_RADIO_GET_PKT_STATUS_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_GET_PKT_STATUS_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_GET_PKT_STATUS_OC >> 0 ), + }; + uint8_t rbuffer[3] = { 0x00 }; + + const lr11xx_status_t status = + ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_RADIO_GET_PKT_STATUS_CMD_LENGTH, rbuffer, 3 ); + + if( status == LR11XX_STATUS_OK ) + { + pkt_status->rssi_pkt_in_dbm = -( int8_t ) ( rbuffer[0] >> 1 ); + pkt_status->snr_pkt_in_db = ( ( ( int8_t ) rbuffer[1] ) + 2 ) >> 2; + pkt_status->signal_rssi_pkt_in_dbm = -( int8_t ) ( rbuffer[2] >> 1 ); + } + + return status; +} + +lr11xx_status_t lr11xx_radio_get_rssi_inst( const void* context, int8_t* rssi_in_dbm ) +{ + const uint8_t cbuffer[LR11XX_RADIO_GET_RSSI_INST_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_GET_RSSI_INST_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_GET_RSSI_INST_OC >> 0 ), + }; + uint8_t rssi = 0; + + const lr11xx_status_t status = ( lr11xx_status_t ) lr11xx_hal_read( + context, cbuffer, LR11XX_RADIO_GET_RSSI_INST_CMD_LENGTH, &rssi, sizeof( rssi ) ); + + if( status == LR11XX_STATUS_OK ) + { + *rssi_in_dbm = -( int8_t ) ( rssi >> 1 ); + } + + return status; +} + +lr11xx_status_t lr11xx_radio_set_gfsk_sync_word( const void* context, + const uint8_t gfsk_sync_word[LR11XX_RADIO_GFSK_SYNC_WORD_LENGTH] ) +{ + const uint8_t cbuffer[LR11XX_RADIO_SET_GFSK_SYNC_WORD_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_GFSK_SYNC_WORD_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_GFSK_SYNC_WORD_OC >> 0 ), + gfsk_sync_word[0], + gfsk_sync_word[1], + gfsk_sync_word[2], + gfsk_sync_word[3], + gfsk_sync_word[4], + gfsk_sync_word[5], + gfsk_sync_word[6], + gfsk_sync_word[7], + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_GFSK_SYNC_WORD_CMD_LENGTH, 0, 0 ); +} + +#ifndef LR11XX_DISABLE_WARNINGS +#warning \ + "The function lr11xx_radio_set_lora_sync_word replaces the \ +deprecated function lr11xx_radio_set_lora_public_network. \ +lr11xx_radio_set_lora_sync_word, however, is incompatible \ +with chip firmware versions prior to 0x303. For those legacy chips \ +only, please use lr11xx_radio_set_lora_public_network. \ +To deactivate this warning, define C preprocessor symbol \ +LR11XX_DISABLE_WARNINGS." +#endif +lr11xx_status_t lr11xx_radio_set_lora_sync_word( const void* context, const uint8_t sync_word ) +{ + const uint8_t cbuffer[LR11XX_RADIO_SET_LORA_SYNC_WORD_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_LORA_SYNC_WORD_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_LORA_SYNC_WORD_OC >> 0 ), + sync_word, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_LORA_SYNC_WORD_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_radio_set_lora_public_network( const void* context, + const lr11xx_radio_lora_network_type_t network_type ) +{ + const uint8_t cbuffer[LR11XX_RADIO_SET_LORA_PUBLIC_NETWORK_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_LORA_PUBLIC_NETWORK_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_LORA_PUBLIC_NETWORK_OC >> 0 ), + ( uint8_t ) network_type, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_LORA_SYNC_WORD_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_radio_set_rx( const void* context, const uint32_t timeout_in_ms ) +{ + const uint32_t timeout_in_rtc_step = lr11xx_radio_convert_time_in_ms_to_rtc_step( timeout_in_ms ); + + return lr11xx_radio_set_rx_with_timeout_in_rtc_step( context, timeout_in_rtc_step ); +} + +lr11xx_status_t lr11xx_radio_set_rx_with_timeout_in_rtc_step( const void* context, const uint32_t timeout ) +{ + lr11xx_status_t status = LR11XX_STATUS_ERROR; + const uint8_t cbuffer[LR11XX_RADIO_SET_RX_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_RX_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_RX_OC >> 0 ), + ( uint8_t ) ( timeout >> 16 ), + ( uint8_t ) ( timeout >> 8 ), + ( uint8_t ) ( timeout >> 0 ), + }; + + do + { + status = LR11XX_RADIO_APPLY_HIGH_ACP_WORKAROUND( context ); + if( status != LR11XX_STATUS_OK ) + { + break; + } + + status = ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_RX_CMD_LENGTH, 0, 0 ); + if( status != LR11XX_STATUS_OK ) + { + break; + } + } while( 0 ); + + return status; +} + +lr11xx_status_t lr11xx_radio_set_tx( const void* context, const uint32_t timeout_in_ms ) +{ + const uint32_t timeout_in_rtc_step = lr11xx_radio_convert_time_in_ms_to_rtc_step( timeout_in_ms ); + + return lr11xx_radio_set_tx_with_timeout_in_rtc_step( context, timeout_in_rtc_step ); +} + +lr11xx_status_t lr11xx_radio_set_tx_with_timeout_in_rtc_step( const void* context, const uint32_t timeout_in_rtc_step ) +{ + lr11xx_status_t status = LR11XX_STATUS_ERROR; + + const uint8_t cbuffer[LR11XX_RADIO_SET_TX_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_TX_OC >> 8 ), ( uint8_t ) ( LR11XX_RADIO_SET_TX_OC >> 0 ), + ( uint8_t ) ( timeout_in_rtc_step >> 16 ), ( uint8_t ) ( timeout_in_rtc_step >> 8 ), + ( uint8_t ) ( timeout_in_rtc_step >> 0 ), + }; + + do + { + status = LR11XX_RADIO_APPLY_HIGH_ACP_WORKAROUND( context ); + if( status != LR11XX_STATUS_OK ) + { + break; + } + + status = ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_TX_CMD_LENGTH, 0, 0 ); + if( status != LR11XX_STATUS_OK ) + { + break; + } + } while( 0 ); + + return status; +} + +lr11xx_status_t lr11xx_radio_set_rf_freq( const void* context, const uint32_t freq_in_hz ) +{ + const uint8_t cbuffer[LR11XX_RADIO_SET_RF_FREQUENCY_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_RF_FREQUENCY_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_RF_FREQUENCY_OC >> 0 ), + ( uint8_t ) ( freq_in_hz >> 24 ), + ( uint8_t ) ( freq_in_hz >> 16 ), + ( uint8_t ) ( freq_in_hz >> 8 ), + ( uint8_t ) ( freq_in_hz >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_RF_FREQUENCY_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_radio_auto_tx_rx( const void* context, const uint32_t delay, + const lr11xx_radio_intermediary_mode_t intermediary_mode, + const uint32_t timeout ) +{ + const uint8_t cbuffer[LR11XX_RADIO_SET_AUTO_TX_RX_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_AUTOTXRX_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_AUTOTXRX_OC >> 0 ), + ( uint8_t ) ( delay >> 16 ), + ( uint8_t ) ( delay >> 8 ), + ( uint8_t ) ( delay ), + ( uint8_t ) intermediary_mode, + ( uint8_t ) ( timeout >> 16 ), + ( uint8_t ) ( timeout >> 8 ), + ( uint8_t ) ( timeout ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_AUTO_TX_RX_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_radio_set_cad_params( const void* context, const lr11xx_radio_cad_params_t* cad_params ) +{ + const uint8_t cbuffer[LR11XX_RADIO_SET_CAD_PARAMS_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_CAD_PARAMS_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_CAD_PARAMS_OC >> 0 ), + cad_params->cad_symb_nb, + cad_params->cad_detect_peak, + cad_params->cad_detect_min, + ( uint8_t ) cad_params->cad_exit_mode, + ( uint8_t ) ( cad_params->cad_timeout >> 16 ), + ( uint8_t ) ( cad_params->cad_timeout >> 8 ), + ( uint8_t ) ( cad_params->cad_timeout ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_CAD_PARAMS_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_radio_set_pkt_type( const void* context, const lr11xx_radio_pkt_type_t pkt_type ) +{ + const uint8_t cbuffer[LR11XX_RADIO_SET_PKT_TYPE_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_PKT_TYPE_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_PKT_TYPE_OC >> 0 ), + ( uint8_t ) pkt_type, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_PKT_TYPE_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_radio_set_gfsk_mod_params( const void* context, + const lr11xx_radio_mod_params_gfsk_t* mod_params ) +{ + const uint8_t cbuffer[LR11XX_RADIO_SET_MODULATION_PARAMS_GFSK_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_MODULATION_PARAM_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_MODULATION_PARAM_OC >> 0 ), + ( uint8_t ) ( mod_params->br_in_bps >> 24 ), + ( uint8_t ) ( mod_params->br_in_bps >> 16 ), + ( uint8_t ) ( mod_params->br_in_bps >> 8 ), + ( uint8_t ) ( mod_params->br_in_bps >> 0 ), + ( uint8_t ) mod_params->pulse_shape, + ( uint8_t ) mod_params->bw_dsb_param, + ( uint8_t ) ( mod_params->fdev_in_hz >> 24 ), + ( uint8_t ) ( mod_params->fdev_in_hz >> 16 ), + ( uint8_t ) ( mod_params->fdev_in_hz >> 8 ), + ( uint8_t ) ( mod_params->fdev_in_hz >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_MODULATION_PARAMS_GFSK_CMD_LENGTH, + 0, 0 ); +} + +lr11xx_status_t lr11xx_radio_set_lora_mod_params( const void* context, + const lr11xx_radio_mod_params_lora_t* mod_params ) +{ + const uint8_t cbuffer[LR11XX_RADIO_SET_MODULATION_PARAMS_LORA_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_MODULATION_PARAM_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_MODULATION_PARAM_OC >> 0 ), + ( uint8_t ) mod_params->sf, + ( uint8_t ) mod_params->bw, + ( uint8_t ) mod_params->cr, + ( uint8_t ) mod_params->ldro, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_MODULATION_PARAMS_LORA_CMD_LENGTH, + 0, 0 ); +} + +lr11xx_status_t lr11xx_radio_set_gfsk_pkt_params( const void* context, + const lr11xx_radio_pkt_params_gfsk_t* pkt_params ) +{ + const uint8_t cbuffer[LR11XX_RADIO_SET_PKT_PARAM_GFSK_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_PKT_PARAM_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_PKT_PARAM_OC >> 0 ), + ( uint8_t ) ( pkt_params->preamble_len_in_bits >> 8 ), + ( uint8_t ) ( pkt_params->preamble_len_in_bits >> 0 ), + ( uint8_t ) ( pkt_params->preamble_detector ), + pkt_params->sync_word_len_in_bits, + ( uint8_t ) ( pkt_params->address_filtering ), + ( uint8_t ) ( pkt_params->header_type ), + pkt_params->pld_len_in_bytes, + ( uint8_t ) ( pkt_params->crc_type ), + ( uint8_t ) ( pkt_params->dc_free ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_PKT_PARAM_GFSK_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_radio_set_lora_pkt_params( const void* context, + const lr11xx_radio_pkt_params_lora_t* pkt_params ) +{ + const uint8_t cbuffer[LR11XX_RADIO_SET_PKT_PARAM_LORA_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_PKT_PARAM_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_PKT_PARAM_OC >> 0 ), + ( uint8_t ) ( pkt_params->preamble_len_in_symb >> 8 ), + ( uint8_t ) ( pkt_params->preamble_len_in_symb >> 0 ), + ( uint8_t ) ( pkt_params->header_type ), + pkt_params->pld_len_in_bytes, + ( uint8_t ) ( pkt_params->crc ), + ( uint8_t ) ( pkt_params->iq ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_PKT_PARAM_LORA_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_radio_set_tx_params( const void* context, const int8_t pwr_in_dbm, + const lr11xx_radio_ramp_time_t ramp_time ) +{ + const uint8_t cbuffer[LR11XX_RADIO_SET_TX_PARAMS_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_TX_PARAMS_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_TX_PARAMS_OC >> 0 ), + ( uint8_t ) pwr_in_dbm, + ( uint8_t ) ramp_time, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_TX_PARAMS_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_radio_set_pkt_address( const void* context, const uint8_t node_address, + const uint8_t broadcast_address ) +{ + const uint8_t cbuffer[LR11XX_RADIO_SET_PKT_ADDRESS_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_PKT_ADRS_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_PKT_ADRS_OC >> 0 ), + node_address, + broadcast_address, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_PKT_ADDRESS_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_radio_set_rx_tx_fallback_mode( const void* context, + const lr11xx_radio_fallback_modes_t fallback_mode ) +{ + const uint8_t cbuffer[LR11XX_RADIO_SET_RX_TX_FALLBACK_MODE_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_RX_TX_FALLBACK_MODE_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_RX_TX_FALLBACK_MODE_OC >> 0 ), + fallback_mode, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_RX_TX_FALLBACK_MODE_CMD_LENGTH, 0, + 0 ); +} + +lr11xx_status_t lr11xx_radio_set_rx_duty_cycle( const void* context, const uint32_t rx_period_in_ms, + const uint32_t sleep_period_in_ms, + const lr11xx_radio_rx_duty_cycle_mode_t mode ) +{ + const uint32_t rx_period_in_rtc_step = lr11xx_radio_convert_time_in_ms_to_rtc_step( rx_period_in_ms ); + const uint32_t sleep_period_in_rtc_step = lr11xx_radio_convert_time_in_ms_to_rtc_step( sleep_period_in_ms ); + + return lr11xx_radio_set_rx_duty_cycle_with_timings_in_rtc_step( context, rx_period_in_rtc_step, + sleep_period_in_rtc_step, mode ); +} + +lr11xx_status_t lr11xx_radio_set_rx_duty_cycle_with_timings_in_rtc_step( const void* context, + const uint32_t rx_period_in_rtc_step, + const uint32_t sleep_period_in_rtc_step, + const lr11xx_radio_rx_duty_cycle_mode_t mode ) +{ + const uint8_t cbuffer[LR11XX_RADIO_SET_RX_DUTY_CYCLE_MODE_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_RX_DUTY_CYCLE_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_RX_DUTY_CYCLE_OC >> 0 ), + ( uint8_t ) ( rx_period_in_rtc_step >> 16 ), + ( uint8_t ) ( rx_period_in_rtc_step >> 8 ), + ( uint8_t ) ( rx_period_in_rtc_step >> 0 ), + ( uint8_t ) ( sleep_period_in_rtc_step >> 16 ), + ( uint8_t ) ( sleep_period_in_rtc_step >> 8 ), + ( uint8_t ) ( sleep_period_in_rtc_step >> 0 ), + ( uint8_t ) mode, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_RX_DUTY_CYCLE_MODE_CMD_LENGTH, 0, + 0 ); +} + +lr11xx_status_t lr11xx_radio_set_pa_cfg( const void* context, const lr11xx_radio_pa_cfg_t* pa_cfg ) +{ + const uint8_t cbuffer[LR11XX_RADIO_SET_PA_CFG_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_PA_CFG_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_PA_CFG_OC >> 0 ), + ( uint8_t ) pa_cfg->pa_sel, + ( uint8_t ) pa_cfg->pa_reg_supply, + pa_cfg->pa_duty_cycle, + pa_cfg->pa_hp_sel, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_PA_CFG_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_radio_stop_timeout_on_preamble( const void* context, const bool stop_timeout_on_preamble ) +{ + const uint8_t cbuffer[LR11XX_RADIO_STOP_TIMEOUT_ON_PREAMBLE_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_STOP_TIMEOUT_ON_PREAMBLE_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_STOP_TIMEOUT_ON_PREAMBLE_OC >> 0 ), + ( uint8_t ) stop_timeout_on_preamble, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_STOP_TIMEOUT_ON_PREAMBLE_CMD_LENGTH, 0, + 0 ); +} + +lr11xx_status_t lr11xx_radio_set_cad( const void* context ) +{ + lr11xx_status_t status = LR11XX_STATUS_ERROR; + const uint8_t cbuffer[LR11XX_RADIO_SET_CAD_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_CAD_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_CAD_OC >> 0 ), + }; + + do + { + status = LR11XX_RADIO_APPLY_HIGH_ACP_WORKAROUND( context ); + if( status != LR11XX_STATUS_OK ) + { + break; + } + + status = ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_CAD_CMD_LENGTH, 0, 0 ); + if( status != LR11XX_STATUS_OK ) + { + break; + } + } while( 0 ); + + return status; +} + +lr11xx_status_t lr11xx_radio_set_tx_cw( const void* context ) +{ + const uint8_t cbuffer[LR11XX_RADIO_SET_TX_CW_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_TX_CW_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_TX_CW_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_TX_CW_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_radio_set_tx_infinite_preamble( const void* context ) +{ + lr11xx_status_t status = LR11XX_STATUS_ERROR; + const uint8_t cbuffer[LR11XX_RADIO_SET_TX_INFINITE_PREAMBLE_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_TX_INFINITE_PREAMBLE_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_TX_INFINITE_PREAMBLE_OC >> 0 ), + }; + + do + { + status = LR11XX_RADIO_APPLY_HIGH_ACP_WORKAROUND( context ); + if( status != LR11XX_STATUS_OK ) + { + break; + } + + status = ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, + LR11XX_RADIO_SET_TX_INFINITE_PREAMBLE_CMD_LENGTH, 0, 0 ); + if( status != LR11XX_STATUS_OK ) + { + break; + } + } while( 0 ); + + return status; +} + +lr11xx_status_t lr11xx_radio_set_lora_sync_timeout( const void* context, const uint8_t nb_symbol ) +{ + const uint8_t cbuffer[LR11XX_RADIO_SET_LORA_SYNC_TIMEOUT_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_LORA_SYNC_TIMEOUT_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_LORA_SYNC_TIMEOUT_OC >> 0 ), + nb_symbol, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_LORA_SYNC_TIMEOUT_CMD_LENGTH, 0, + 0 ); +} + +lr11xx_status_t lr11xx_radio_set_gfsk_crc_params( const void* context, const uint32_t seed, const uint32_t polynomial ) +{ + const uint8_t cbuffer[LR11XX_RADIO_SET_GFSK_CRC_PARAMS_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_GFSK_CRC_PARAMS_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_GFSK_CRC_PARAMS_OC >> 0 ), + ( uint8_t ) ( seed >> 24 ), + ( uint8_t ) ( seed >> 16 ), + ( uint8_t ) ( seed >> 8 ), + ( uint8_t ) ( seed >> 0 ), + ( uint8_t ) ( polynomial >> 24 ), + ( uint8_t ) ( polynomial >> 16 ), + ( uint8_t ) ( polynomial >> 8 ), + ( uint8_t ) ( polynomial >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_GFSK_CRC_PARAMS_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_radio_set_gfsk_whitening_seed( const void* context, const uint16_t seed ) +{ + const uint8_t cbuffer[LR11XX_RADIO_SET_GFSK_WHITENING_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_GFSK_WHITENING_PARAMS_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_GFSK_WHITENING_PARAMS_OC >> 0 ), + ( uint8_t ) ( seed >> 8 ), + ( uint8_t ) ( seed >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_GFSK_WHITENING_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_radio_cfg_rx_boosted( const void* context, const bool enable_boost_mode ) +{ + const uint8_t cbuffer[LR11XX_RADIO_SET_RX_BOOSTED_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_RX_BOOSTED_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_RX_BOOSTED_OC >> 0 ), + ( enable_boost_mode == true ) ? 0x01 : 0x00, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_RX_BOOSTED_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_radio_set_rssi_calibration( const void* context, + const lr11xx_radio_rssi_calibration_table_t* rssi_cal_table ) +{ + const uint8_t cbuffer[LR11XX_RADIO_SET_RSSI_CALIBRATION_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_RSSI_CALIBRATION_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_RSSI_CALIBRATION_OC >> 0 ), + ( uint8_t ) ( ( ( rssi_cal_table->gain_tune.g11 & 0x0F ) << 4 ) + ( rssi_cal_table->gain_tune.g10 & 0x0F ) ), + ( uint8_t ) ( ( ( rssi_cal_table->gain_tune.g9 & 0x0F ) << 4 ) + ( rssi_cal_table->gain_tune.g8 & 0x0F ) ), + ( uint8_t ) ( ( ( rssi_cal_table->gain_tune.g7 & 0x0F ) << 4 ) + ( rssi_cal_table->gain_tune.g6 & 0x0F ) ), + ( uint8_t ) ( ( ( rssi_cal_table->gain_tune.g5 & 0x0F ) << 4 ) + ( rssi_cal_table->gain_tune.g4 & 0x0F ) ), + ( uint8_t ) ( ( ( rssi_cal_table->gain_tune.g13hp6 & 0x0F ) << 4 ) + + ( rssi_cal_table->gain_tune.g13hp5 & 0x0F ) ), + ( uint8_t ) ( ( ( rssi_cal_table->gain_tune.g13hp4 & 0x0F ) << 4 ) + + ( rssi_cal_table->gain_tune.g13hp3 & 0x0F ) ), + ( uint8_t ) ( ( ( rssi_cal_table->gain_tune.g13hp2 & 0x0F ) << 4 ) + + ( rssi_cal_table->gain_tune.g13hp1 & 0x0F ) ), + ( uint8_t ) ( ( ( rssi_cal_table->gain_tune.g13 & 0x0F ) << 4 ) + ( rssi_cal_table->gain_tune.g12 & 0x0F ) ), + ( uint8_t ) ( rssi_cal_table->gain_tune.g13hp7 & 0x0F ), + ( uint8_t ) ( rssi_cal_table->gain_offset >> 8 ), + ( uint8_t ) ( rssi_cal_table->gain_offset >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_RSSI_CALIBRATION_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_radio_get_gfsk_rx_bandwidth( uint32_t bw_in_hz, lr11xx_radio_gfsk_bw_t* bw_parameter ) +{ + if( bw_in_hz <= 4800 ) + { + *bw_parameter = LR11XX_RADIO_GFSK_BW_4800; + } + else if( bw_in_hz <= 5800 ) + { + *bw_parameter = LR11XX_RADIO_GFSK_BW_5800; + } + else if( bw_in_hz <= 7300 ) + { + *bw_parameter = LR11XX_RADIO_GFSK_BW_7300; + } + else if( bw_in_hz <= 9700 ) + { + *bw_parameter = LR11XX_RADIO_GFSK_BW_9700; + } + else if( bw_in_hz <= 11700 ) + { + *bw_parameter = LR11XX_RADIO_GFSK_BW_11700; + } + else if( bw_in_hz <= 14600 ) + { + *bw_parameter = LR11XX_RADIO_GFSK_BW_14600; + } + else if( bw_in_hz <= 19500 ) + { + *bw_parameter = LR11XX_RADIO_GFSK_BW_19500; + } + else if( bw_in_hz <= 23400 ) + { + *bw_parameter = LR11XX_RADIO_GFSK_BW_23400; + } + else if( bw_in_hz <= 29300 ) + { + *bw_parameter = LR11XX_RADIO_GFSK_BW_29300; + } + else if( bw_in_hz <= 39000 ) + { + *bw_parameter = LR11XX_RADIO_GFSK_BW_39000; + } + else if( bw_in_hz <= 46900 ) + { + *bw_parameter = LR11XX_RADIO_GFSK_BW_46900; + } + else if( bw_in_hz <= 58600 ) + { + *bw_parameter = LR11XX_RADIO_GFSK_BW_58600; + } + else if( bw_in_hz <= 78200 ) + { + *bw_parameter = LR11XX_RADIO_GFSK_BW_78200; + } + else if( bw_in_hz <= 93800 ) + { + *bw_parameter = LR11XX_RADIO_GFSK_BW_93800; + } + else if( bw_in_hz <= 117300 ) + { + *bw_parameter = LR11XX_RADIO_GFSK_BW_117300; + } + else if( bw_in_hz <= 156200 ) + { + *bw_parameter = LR11XX_RADIO_GFSK_BW_156200; + } + else if( bw_in_hz <= 187200 ) + { + *bw_parameter = LR11XX_RADIO_GFSK_BW_187200; + } + else if( bw_in_hz <= 234300 ) + { + *bw_parameter = LR11XX_RADIO_GFSK_BW_234300; + } + else if( bw_in_hz <= 312000 ) + { + *bw_parameter = LR11XX_RADIO_GFSK_BW_312000; + } + else if( bw_in_hz <= 373600 ) + { + *bw_parameter = LR11XX_RADIO_GFSK_BW_373600; + } + else if( bw_in_hz <= 467000 ) + { + *bw_parameter = LR11XX_RADIO_GFSK_BW_467000; + } + else + { + return LR11XX_STATUS_ERROR; + } + + return LR11XX_STATUS_OK; +} + +uint32_t lr11xx_radio_get_lora_time_on_air_numerator( const lr11xx_radio_pkt_params_lora_t* pkt_p, + const lr11xx_radio_mod_params_lora_t* mod_p ) +{ + const int32_t pld_len_in_bytes = pkt_p->pld_len_in_bytes; + const int32_t sf = mod_p->sf; + const bool pld_is_fix = pkt_p->header_type == LR11XX_RADIO_LORA_PKT_IMPLICIT; + + int32_t fine_synch = ( sf <= 6 ) ? 1 : 0; + bool long_interleaving = ( mod_p->cr > 4 ); + + int32_t total_bytes_nb = pld_len_in_bytes + ( ( pkt_p->crc == LR11XX_RADIO_LORA_CRC_ON ) ? 2 : 0 ); + int32_t tx_bits_symbol = sf - 2 * ( mod_p->ldro != 0 ? 1 : 0 ); + + int32_t ceil_numerator; + int32_t ceil_denominator; + + uint32_t intermed; + + int32_t symbols_nb_data; + int32_t tx_infobits_header; + int32_t tx_infobits_payload; + + if( long_interleaving ) + { + const int32_t fec_rate_numerator = 4; + const int32_t fec_rate_denominator = ( mod_p->cr + ( mod_p->cr == 7 ? 1 : 0 ) ); + + if( pld_is_fix ) + { + int32_t tx_bits_symbol_start = sf - 2 + 2 * fine_synch; + if( 8 * total_bytes_nb * fec_rate_denominator <= 7 * fec_rate_numerator * tx_bits_symbol_start ) + { + ceil_numerator = 8 * total_bytes_nb * fec_rate_denominator; + ceil_denominator = fec_rate_numerator * tx_bits_symbol_start; + } + else + { + int32_t tx_codedbits_header = tx_bits_symbol_start * 8; + ceil_numerator = 8 * fec_rate_numerator * tx_bits_symbol + 8 * total_bytes_nb * fec_rate_denominator - + fec_rate_numerator * tx_codedbits_header; + ceil_denominator = fec_rate_numerator * tx_bits_symbol; + } + } + else + { + tx_infobits_header = ( sf * 4 + fine_synch * 8 - 28 ) & ~0x07; + if( tx_infobits_header < 8 * total_bytes_nb ) + { + if( tx_infobits_header > 8 * pld_len_in_bytes ) + { + tx_infobits_header = 8 * pld_len_in_bytes; + } + } + tx_infobits_payload = 8 * total_bytes_nb - tx_infobits_header; + if( tx_infobits_payload < 0 ) + { + tx_infobits_payload = 0; + } + + ceil_numerator = tx_infobits_payload * fec_rate_denominator + 8 * fec_rate_numerator * tx_bits_symbol; + ceil_denominator = fec_rate_numerator * tx_bits_symbol; + } + } + else + { + tx_infobits_header = sf * 4 + fine_synch * 8 - 8; + + if( !pld_is_fix ) + { + tx_infobits_header -= 20; + } + + tx_infobits_payload = 8 * total_bytes_nb - tx_infobits_header; + + if( tx_infobits_payload < 0 ) + tx_infobits_payload = 0; + + ceil_numerator = tx_infobits_payload; + ceil_denominator = 4 * tx_bits_symbol; + } + + symbols_nb_data = ( ( ceil_numerator + ceil_denominator - 1 ) / ceil_denominator ); + if( !long_interleaving ) + { + symbols_nb_data = symbols_nb_data * ( mod_p->cr + 4 ) + 8; + } + intermed = pkt_p->preamble_len_in_symb + 4 + 2 * fine_synch + symbols_nb_data; + + return ( uint32_t ) ( ( 4 * intermed + 1 ) * ( 1 << ( sf - 2 ) ) ) - 1; +} + +uint32_t lr11xx_radio_get_lora_bw_in_hz( lr11xx_radio_lora_bw_t bw ) +{ + uint32_t bw_in_hz = 0; + + switch( bw ) + { + case LR11XX_RADIO_LORA_BW_10: + bw_in_hz = 10417UL; + break; + case LR11XX_RADIO_LORA_BW_15: + bw_in_hz = 15625UL; + break; + case LR11XX_RADIO_LORA_BW_20: + bw_in_hz = 20833UL; + break; + case LR11XX_RADIO_LORA_BW_31: + bw_in_hz = 31250UL; + break; + case LR11XX_RADIO_LORA_BW_41: + bw_in_hz = 41667UL; + break; + case LR11XX_RADIO_LORA_BW_62: + bw_in_hz = 62500UL; + break; + case LR11XX_RADIO_LORA_BW_125: + bw_in_hz = 125000UL; + break; + case LR11XX_RADIO_LORA_BW_250: + bw_in_hz = 250000UL; + break; + case LR11XX_RADIO_LORA_BW_500: + bw_in_hz = 500000UL; + break; + case LR11XX_RADIO_LORA_BW_200: + bw_in_hz = 203000UL; + break; + case LR11XX_RADIO_LORA_BW_400: + bw_in_hz = 406000UL; + break; + case LR11XX_RADIO_LORA_BW_800: + bw_in_hz = 812000UL; + break; + } + + return bw_in_hz; +} + +uint32_t lr11xx_radio_get_lora_time_on_air_in_ms( const lr11xx_radio_pkt_params_lora_t* pkt_p, + const lr11xx_radio_mod_params_lora_t* mod_p ) +{ + uint32_t numerator = 1000U * lr11xx_radio_get_lora_time_on_air_numerator( pkt_p, mod_p ); + uint32_t denominator = lr11xx_radio_get_lora_bw_in_hz( mod_p->bw ); + // Perform integral ceil() + return ( numerator + denominator - 1 ) / denominator; +} + +uint32_t lr11xx_radio_get_gfsk_time_on_air_numerator( const lr11xx_radio_pkt_params_gfsk_t* pkt_p ) +{ + uint8_t header_len_in_bits; + + switch( pkt_p->header_type ) + { + case LR11XX_RADIO_GFSK_PKT_FIX_LEN: + { + header_len_in_bits = 0; + break; + } + case LR11XX_RADIO_GFSK_PKT_VAR_LEN: + { + header_len_in_bits = 8; + break; + } + case LR11XX_RADIO_GFSK_PKT_VAR_LEN_SX128X_COMP: + { + header_len_in_bits = 9; + break; + } + default: + { + return 0; + } + } + + return pkt_p->preamble_len_in_bits + header_len_in_bits + pkt_p->sync_word_len_in_bits + + ( ( pkt_p->pld_len_in_bytes + + ( pkt_p->address_filtering == LR11XX_RADIO_GFSK_ADDRESS_FILTERING_DISABLE ? 0 : 1 ) + + lr11xx_radio_get_gfsk_crc_len_in_bytes( pkt_p->crc_type ) ) + << 3 ); +} + +uint32_t lr11xx_radio_get_gfsk_time_on_air_in_ms( const lr11xx_radio_pkt_params_gfsk_t* pkt_p, + const lr11xx_radio_mod_params_gfsk_t* mod_p ) +{ + uint32_t numerator = 1000U * lr11xx_radio_get_gfsk_time_on_air_numerator( pkt_p ); + uint32_t denominator = mod_p->br_in_bps; + + // Perform integral ceil() + return ( numerator + denominator - 1 ) / denominator; +} + +uint32_t lr11xx_radio_convert_time_in_ms_to_rtc_step( uint32_t time_in_ms ) +{ + return ( uint32_t ) ( time_in_ms * LR11XX_RTC_FREQ_IN_HZ / 1000 ); +} + +lr11xx_status_t lr11xx_radio_get_lora_rx_info( const void* context, bool* is_crc_present, lr11xx_radio_lora_cr_t* cr ) +{ + const uint8_t cbuffer[LR11XX_RADIO_GET_LORA_RX_INFO_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_GET_LORA_RX_INFO_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_GET_LORA_RX_INFO_OC >> 0 ), + }; + uint8_t rbuffer; + + const lr11xx_status_t status = + ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_RADIO_GET_LORA_RX_INFO_CMD_LENGTH, &rbuffer, 1 ); + + if( status == LR11XX_STATUS_OK ) + { + *is_crc_present = ( ( ( rbuffer & ( 0x01 << 4 ) ) != 0 ) ) ? true : false; + *cr = ( lr11xx_radio_lora_cr_t ) ( rbuffer & 0x07 ); + } + + return status; +} + +lr11xx_status_t lr11xx_radio_apply_high_acp_workaround( const void* context ) +{ + return lr11xx_regmem_write_regmem32_mask( context, 0x00F30054, 1 << 30, 0 << 30 ); +} + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DEFINITION -------------------------------------------- + */ + +static inline uint32_t lr11xx_radio_get_gfsk_crc_len_in_bytes( lr11xx_radio_gfsk_crc_type_t crc_type ) +{ + switch( crc_type ) + { + case LR11XX_RADIO_GFSK_CRC_OFF: + return 0; + case LR11XX_RADIO_GFSK_CRC_1_BYTE: + return 1; + case LR11XX_RADIO_GFSK_CRC_2_BYTES: + return 2; + case LR11XX_RADIO_GFSK_CRC_1_BYTE_INV: + return 1; + case LR11XX_RADIO_GFSK_CRC_2_BYTES_INV: + return 2; + } + + return 0; +} + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/src/lr11xx_radio.h b/src/lr11xx_radio.h new file mode 100644 index 0000000..bcf2b0f --- /dev/null +++ b/src/lr11xx_radio.h @@ -0,0 +1,776 @@ +/*! + * @file lr11xx_radio.h + * + * @brief Radio driver definition for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR11XX_RADIO_H +#define LR11XX_RADIO_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include "lr11xx_radio_types.h" +#include "lr11xx_types.h" + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/*! + * @brief Length in byte of the GFSK sync word + */ +#define LR11XX_RADIO_GFSK_SYNC_WORD_LENGTH 8 + +/*! + * @brief Default GFSK sync word value + */ +#define LR11XX_RADIO_GFSK_SYNC_WORD_DEFAULT \ + { \ + 0x97, 0x23, 0x52, 0x25, 0x56, 0x53, 0x65, 0x64 \ + } + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +/*! + * @brief Reset internal statistics of the received packets + * + * @param [in] context Chip implementation context + * + * @returns Operation status + * + * @see lr11xx_radio_get_gfsk_stats, lr11xx_radio_get_lora_stats + */ +lr11xx_status_t lr11xx_radio_reset_stats( const void* context ); + +/*! + * @brief Get the internal statistics of the GFSK received packets + * + * Internal statistics are reset on Power on Reset, by entering sleep mode without memory retention, or by calling @ref + * lr11xx_radio_reset_stats. + * + * @param [in] context Chip implementation context + * @param [out] stats The statistics structure of the received packets + * + * @returns Operation status + * + * @see lr11xx_radio_reset_stats + */ +lr11xx_status_t lr11xx_radio_get_gfsk_stats( const void* context, lr11xx_radio_stats_gfsk_t* stats ); + +/*! + * @brief Get the internal statistics of the LoRa received packets + * + * Internal statistics are reset on Power on Reset, by entering sleep mode without memory retention, or by calling @ref + * lr11xx_radio_reset_stats. + * + * @param [in] context Chip implementation context + * @param [out] stats The statistics structure of the received packets + * + * @returns Operation status + * + * @see lr11xx_radio_reset_stats + */ +lr11xx_status_t lr11xx_radio_get_lora_stats( const void* context, lr11xx_radio_stats_lora_t* stats ); + +/*! + * @brief Get the packet type currently configured + * + * @param [in] context Chip implementation context + * @param [out] pkt_type The packet type currently configured + * + * @returns Operation status + * + * @see lr11xx_radio_set_pkt_type + */ +lr11xx_status_t lr11xx_radio_get_pkt_type( const void* context, lr11xx_radio_pkt_type_t* pkt_type ); + +/*! + * @brief Get the length of last received packet, and the offset in the RX internal buffer of the first byte of the + * received payload + * + * @param [in] context Chip implementation context + * @param [out] rx_buffer_status The structure of RX buffer status + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_radio_get_rx_buffer_status( const void* context, + lr11xx_radio_rx_buffer_status_t* rx_buffer_status ); + +/*! + * @brief Get the status of last GFSK received packet + * + * The value depends on the received packet type + * + * @param [in] context Chip implementation context + * @param [out] pkt_status The last received packet status + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_radio_get_gfsk_pkt_status( const void* context, lr11xx_radio_pkt_status_gfsk_t* pkt_status ); + +/*! + * @brief Get the status of last LoRa received packet + * + * The value depends on the received packet type + * + * @param [in] context Chip implementation context + * @param [out] pkt_status The last received packet status + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_radio_get_lora_pkt_status( const void* context, lr11xx_radio_pkt_status_lora_t* pkt_status ); + +/*! + * @brief Get the instantaneous RSSI. + * + * This command can be used during reception of a packet + * + * @param [in] context Chip implementation context + * @param [out] rssi_in_dbm Instantaneous RSSI. + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_radio_get_rssi_inst( const void* context, int8_t* rssi_in_dbm ); + +/*! + * @brief Set the GFSK modem sync word + * + * This command is used to set the GFSK nodem sync word. This command expects a 8-byte long array to be passed as sync + * word parameter. By default, the value is 0x9723522556536564. + * + * @param [in] context Chip implementation context + * @param [in] gfsk_sync_word The sync word to be configured + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_radio_set_gfsk_sync_word( const void* context, + const uint8_t gfsk_sync_word[LR11XX_RADIO_GFSK_SYNC_WORD_LENGTH] ); + +/*! + * @brief Set the LoRa modem sync word + * + * @param [in] context Chip implementation context + * @param [in] sync_word The sync word to be configured + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_radio_set_lora_sync_word( const void* context, const uint8_t sync_word ); + +/*! + * @brief Set the LoRa modem sync word to private / public + * + * This command is used to select which LoRa network is selected + * + * @param [in] context Chip implementation context + * @param [in] network_type The network type to be configured + * + * @returns Operation status + * + * @warning This function is deprecated. Use lr11xx_radio_set_lora_sync_word for chip firmware equal to or more recent + * than 0x303. + */ +lr11xx_status_t lr11xx_radio_set_lora_public_network( const void* context, + const lr11xx_radio_lora_network_type_t network_type ); + +/*! + * @brief Start RX operations with a timeout in millisecond + * + * This command sets the LR11XX to RX mode. The radio must have been configured before using this command with @ref + * lr11xx_radio_set_pkt_type + * + * By default, the timeout parameter allows to return automatically to standby RC mode if no packets have been received + * after a certain amount of time. This behavior can be altered by @ref lr11xx_radio_set_rx_tx_fallback_mode and @ref + * lr11xx_radio_auto_tx_rx. + * + * @remark To set the radio in Rx continuous mode, the function @ref lr11xx_radio_set_rx_with_timeout_in_rtc_step has to + * be called with \p timeout_in_rtc_step set to 0xFFFFFF + * + * @param [in] context Chip implementation context + * @param [in] timeout_in_ms The timeout configuration for RX operation + * + * @returns Operation status + * + * @see lr11xx_radio_set_pkt_type, lr11xx_radio_set_rx_tx_fallback_mode + */ +lr11xx_status_t lr11xx_radio_set_rx( const void* context, const uint32_t timeout_in_ms ); + +/*! + * @brief Start RX operations with a timeout in RTC step + * + * This command sets the LR11XX to RX mode. The radio must have been configured before using this command with @ref + * lr11xx_radio_set_pkt_type + * + * By default, the timeout parameter allows to return automatically to standby RC mode if no packets have been received + * after a certain amount of time. This behavior can be altered by @ref lr11xx_radio_set_rx_tx_fallback_mode and @ref + * lr11xx_radio_auto_tx_rx. + * + * The timeout duration is obtained by: + * \f$ timeout\_duration\_ms = timeout \times \frac{1}{32.768} \f$ + * + * Maximal timeout value is 0xFFFFFF, which gives a maximal timeout of 511 seconds. + * + * The timeout argument can also have the following special values: + * + * + *
Special values Meaning
0x000000 RX single: LR11XX stays in RX mode until a + * packet is received, then switch to standby RC mode
0xFFFFFF + * RX continuous: LR11XX stays in RX mode even after reception of a + * packet + *
+ * + * @param [in] context Chip implementation context + * @param [in] timeout_in_rtc_step The timeout configuration for RX operation + * + * @returns Operation status + * + * @see lr11xx_radio_set_pkt_type, lr11xx_radio_set_rx_tx_fallback_mode + */ +lr11xx_status_t lr11xx_radio_set_rx_with_timeout_in_rtc_step( const void* context, const uint32_t timeout_in_rtc_step ); + +/*! + * @brief Start TX operations + * + * This command sets the LR11XX to TX mode. The radio must have been configured before using this command with @ref + * lr11xx_radio_set_pkt_type + * + * By default, the timeout parameter allows to return automatically to standby RC mode if the packet has not been + * completely transmitted after a certain amount of time. This behavior can be altered by @ref + * lr11xx_radio_set_rx_tx_fallback_mode and @ref lr11xx_radio_auto_tx_rx. + * + * @param [in] context Chip implementation context + * @param [in] timeout_in_ms The timeout configuration for TX operation + * + * @returns Operation status + * + * @see lr11xx_radio_set_pkt_type, lr11xx_radio_set_rx_tx_fallback_mode + */ +lr11xx_status_t lr11xx_radio_set_tx( const void* context, const uint32_t timeout_in_ms ); + +/*! + * @brief Start TX operations + * + * This command sets the LR11XX to TX mode. The radio must have been configured before using this command with @ref + * lr11xx_radio_set_pkt_type + * + * By default, the timeout parameter allows to return automatically to standby RC mode if the packet has not been + * completely transmitted after a certain amount of time. This behavior can be altered by @ref + * lr11xx_radio_set_rx_tx_fallback_mode and @ref lr11xx_radio_auto_tx_rx. + * + * The timeout duration is obtained by: + * \f$ timeout\_duration\_ms = timeout \times \frac{1}{32.768} \f$ + * + * Maximal value is 0xFFFFFF. + * + * If the timeout argument is 0, then no timeout is used. + * + * @param [in] context Chip implementation context + * @param [in] timeout_in_rtc_step The timeout configuration for TX operation + * + * @returns Operation status + * + * @see lr11xx_radio_set_pkt_type, lr11xx_radio_set_rx_tx_fallback_mode + */ +lr11xx_status_t lr11xx_radio_set_tx_with_timeout_in_rtc_step( const void* context, const uint32_t timeout_in_rtc_step ); + +/*! + * @brief Set the frequency for future radio operations. + * + * This commands does not set frequency for Wi-Fi and GNSS scan operations. + * + * @param [in] context Chip implementation context + * @param [in] freq_in_hz The frequency in Hz to set for radio operations + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_radio_set_rf_freq( const void* context, const uint32_t freq_in_hz ); + +/*! + * @brief Configure automatic TX after RX or automatic RX after TX + * + * After issuing this command, using the command @ref SetTx will make the LR11XX doing the following: + * - Enter TX mode as usual + * - Enter configurable Intermediary mode during configurable delay + * - Enter RX mode + * + * Similarly, after a @ref SetRx command, the LR11XX will do the following: + * - Enter RX mode as usual + * - Enter configurable Intermediary mode during configurable delay + * - Enter TX mode + * + * In case delay is 0, the LR11XX does not enter Intermediary mode and directly enter the following mode. + * + * To disable this behavior, use this function with delay set to 0xFFFFFFFF. + * + * @param [in] context Chip implementation context + * @param [in] delay Time to spend in Intermediary mode expressed as steps of \f$\frac{1}{32.768 KHz}\f$ steps. + * @param [in] intermediary_mode The mode the LR11XX enters after first mode completion during delay time + * @param [in] timeout The timeout duration of the automatic RX or TX, expressed as steps of \f$ \frac{1}{32.768KHz} \f$ + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_radio_auto_tx_rx( const void* context, const uint32_t delay, + const lr11xx_radio_intermediary_mode_t intermediary_mode, + const uint32_t timeout ); + +/*! + * @brief Set Channel Activity Detection configuration + * + * @param [in] context Chip implementation context + * @param [in] cad_params The structure defining CAD configuration + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_radio_set_cad_params( const void* context, const lr11xx_radio_cad_params_t* cad_params ); + +/*! + * @brief Set the packet type + * + * @param [in] context Chip implementation context + * @param [in] pkt_type Packet type to set + * + * @returns Operation status + * + * @see lr11xx_radio_get_pkt_type + */ +lr11xx_status_t lr11xx_radio_set_pkt_type( const void* context, const lr11xx_radio_pkt_type_t pkt_type ); + +/*! + * @brief Set the modulation parameters for GFSK packets + * + * The command @ref lr11xx_radio_set_pkt_type must be called prior this one. + * + * @param [in] context Chip implementation context + * @param [in] mod_params The structure of modulation configuration + * + * @returns Operation status + * + * @see lr11xx_radio_set_pkt_type + */ +lr11xx_status_t lr11xx_radio_set_gfsk_mod_params( const void* context, + const lr11xx_radio_mod_params_gfsk_t* mod_params ); + +/*! + * @brief Set the modulation parameters for LoRa packets + * + * The command @ref lr11xx_radio_set_pkt_type must be called prior this one. + * + * @param [in] context Chip implementation context + * @param [in] mod_params The structure of modulation configuration + * + * @returns Operation status + * + * @see lr11xx_radio_set_pkt_type + */ +lr11xx_status_t lr11xx_radio_set_lora_mod_params( const void* context, + const lr11xx_radio_mod_params_lora_t* mod_params ); + +/*! + * @brief Set the packet parameters for GFSK packets + * + * The command @ref lr11xx_radio_set_pkt_type must be called prior this one. + * + * @param [in] context Chip implementation context + * @param [in] pkt_params The structure of packet configuration + * + * @returns Operation status + * + * @see lr11xx_radio_set_pkt_type, lr11xx_radio_set_gfsk_mod_params + */ +lr11xx_status_t lr11xx_radio_set_gfsk_pkt_params( const void* context, + const lr11xx_radio_pkt_params_gfsk_t* pkt_params ); + +/*! + * @brief Set the packet parameters for LoRa packets + * + * The command @ref lr11xx_radio_set_pkt_type must be called prior this one. + * + * @param [in] context Chip implementation context + * @param [in] pkt_params The structure of packet configuration + * + * @returns Operation status + * + * @see lr11xx_radio_set_pkt_type, lr11xx_radio_set_lora_mod_params + */ +lr11xx_status_t lr11xx_radio_set_lora_pkt_params( const void* context, + const lr11xx_radio_pkt_params_lora_t* pkt_params ); + +/*! + * @brief Set the parameters for TX power and power amplifier ramp time + * + * The command @ref lr11xx_radio_set_pa_cfg must be called prior calling + * lr11xx_radio_set_tx_params. + * + * The range of possible TX output power values depends on PA selected with @ref lr11xx_radio_set_pa_cfg : + * - for LPA: power value goes from -17dBm to +14dBm (ie. from 0xEF to 0x0E) + * - for HPA: power value goes from -9dBm to +22dBm (ie. from 0xF7 to 0x16) + * + * Moreover, to use TX output power value higher than +10dBm, the @ref REGPASUPPLY_VBAT supply must have been selected + * with @ref lr11xx_radio_set_pa_cfg. + * + * @param [in] context Chip implementation context + * @param [in] pwr_in_dbm The TX output power in dBm + * @param [in] ramp_time The ramping time configuration for the PA + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_radio_set_tx_params( const void* context, const int8_t pwr_in_dbm, + const lr11xx_radio_ramp_time_t ramp_time ); + +/*! + * @brief Sets the Node and Broadcast address used for GFSK + * + * This setting is used only when filtering is enabled. + * + * @param [in] context Chip implementation context + * @param [in] node_address The node address used as filter + * @param [in] broadcast_address The broadcast address used as filter + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_radio_set_pkt_address( const void* context, const uint8_t node_address, + const uint8_t broadcast_address ); + +/*! + * @brief Alter the chip mode after successfull transmission or reception operation + * + * This setting is not used during Rx Duty Cycle mode or Auto Tx Rx. + * + * @param [in] context Chip implementation context + * @param [in] fallback_mode The chip mode to enter after successfull transmission or reception. + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_radio_set_rx_tx_fallback_mode( const void* context, + const lr11xx_radio_fallback_modes_t fallback_mode ); + +/*! + * @brief Configure and start a Rx Duty Cycle operation + * + * It executes the following steps: + * 1. Reception: enters reception state for duration defined by rx_period + * - If mode is LR11XX_RADIO_RX_DUTY_CYCLE_MODE_RX: it is standard RX mode + * - If mode is LR11XX_RADIO_RX_DUTY_CYCLE_MODE_CAD (only in LoRa) : it is CAD operation + * 2. Depending on the over-the-air activity detection: + * - In case of positive over-the-air detection, the rx_period timeout is recomputed to the value + * \f$2 \times rx\_period + sleep\_period\f$ + * - If no air activity is detected, the LR11XX goes back to sleep mode with retention for a duration defined by + * sleep_period + * 3. On wake-up, the LR11XX restarts the process with the reception state. + * + * @remark If mode is configured to @ref LR11XX_RADIO_RX_DUTY_CYCLE_MODE_CAD, then the CAD configuration used in step 1. + * is the one set from the last call to @ref lr11xx_radio_set_cad_params. + * + * @param [in] context Chip implementation context + * @param [in] rx_period_in_ms The length of Rx period + * @param [in] sleep_period_in_ms The length of sleep period + * @param [in] mode The operation mode during Rx phase + * + * @returns Operation status + * + * @see lr11xx_radio_set_cad_params + */ +lr11xx_status_t lr11xx_radio_set_rx_duty_cycle( const void* context, const uint32_t rx_period_in_ms, + const uint32_t sleep_period_in_ms, + const lr11xx_radio_rx_duty_cycle_mode_t mode ); + +/*! + * @brief Configure and start a Rx Duty Cycle operation + * + * It executes the following steps: + * 1. Reception: enters reception state for duration defined by rx_period + * - If mode is LR11XX_RADIO_RX_DUTY_CYCLE_MODE_RX: it is standard RX mode + * - If mode is LR11XX_RADIO_RX_DUTY_CYCLE_MODE_CAD (only in LoRa) : it is CAD operation + * 2. Depending on the over-the-air activity detection: + * - In case of positive over-the-air detection, the rx_period timeout is recomputed to the value + * \f$2 \times rx\_period + sleep\_period\f$ + * - If no air activity is detected, the LR11XX goes back to sleep mode with retention for a duration defined by + * sleep_period + * 3. On wake-up, the LR11XX restarts the process with the reception state. + * + * @remark If mode is configured to @ref LR11XX_RADIO_RX_DUTY_CYCLE_MODE_CAD, then the CAD configuration used in step 1. + * is the one set from the last call to @ref lr11xx_radio_set_cad_params. + * + * @param [in] context Chip implementation context + * @param [in] rx_period_in_rtc_step The length of Rx period + * @param [in] sleep_period_in_rtc_step The length of sleep period + * @param [in] mode The operation mode during Rx phase + * + * @returns Operation status + * + * @see lr11xx_radio_set_cad_params + */ +lr11xx_status_t lr11xx_radio_set_rx_duty_cycle_with_timings_in_rtc_step( const void* context, + const uint32_t rx_period_in_rtc_step, + const uint32_t sleep_period_in_rtc_step, + const lr11xx_radio_rx_duty_cycle_mode_t mode ); + +/*! + * @brief Set the Power Amplifier configuration + * + * It must be called prior using @ref lr11xx_radio_set_tx_params. + * + * @param [in] context Chip implementation context + * @param [in] pa_cfg The structure for PA configuration + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_radio_set_pa_cfg( const void* context, const lr11xx_radio_pa_cfg_t* pa_cfg ); + +/*! + * @brief Define on which event the Rx timeout shall be stopped + * + * The two options are: + * - Syncword / Header detection + * - Preamble detection + * + * @param [in] context Chip implementation context + * @param [in] stop_timeout_on_preamble The choice of the event to be taken into account + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_radio_stop_timeout_on_preamble( const void* context, const bool stop_timeout_on_preamble ); + +/*! + * @brief Start the CAD mode + * + * The LoRa packet type shall be selected before this function is called. The fallback mode is configured with + * lr11xx_radio_set_cad_params. + * + * @param [in] context Chip implementation context + * + * @returns Operation status + * + * @see lr11xx_radio_set_cad_params, lr11xx_radio_set_pkt_type + */ +lr11xx_status_t lr11xx_radio_set_cad( const void* context ); + +/*! + * @brief Set the device into Tx continuous wave (RF tone). + * + * A packet type shall be selected before this function is called. + * + * @param [in] context Chip implementation context + * + * @returns Operation status + * + * @see lr11xx_radio_set_pkt_type + */ +lr11xx_status_t lr11xx_radio_set_tx_cw( const void* context ); + +/*! + * @brief Set the device into Tx continuous preamble (modulated signal). + * + * @param [in] context Chip implementation context + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_radio_set_tx_infinite_preamble( const void* context ); + +/*! + * @brief Configure the LoRa modem to issue a RX timeout after an exact number of symbols given in parameter if no LoRa + * modulation is detected + * + * @param [in] context Chip implementation context + * @param [in] nb_symbol number of symbols to compute the timeout + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_radio_set_lora_sync_timeout( const void* context, const uint8_t nb_symbol ); + +/*! + * @brief Configure the seed and the polynomial used to compute CRC in GFSK packet + * + * @param [in] context Chip implementation context + * @param [in] seed Seed used to compute the CRC value + * @param [in] polynomial Polynomial used to compute the CRC value + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_radio_set_gfsk_crc_params( const void* context, const uint32_t seed, const uint32_t polynomial ); + +/*! + * @brief Configure the whitening seed used in GFSK packet + * + * @param [in] context Chip implementation context + * @param [in] seed Whitening seed value + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_radio_set_gfsk_whitening_seed( const void* context, const uint16_t seed ); + +/*! + * @brief Configure the boost mode in reception + * + * @param [in] context Chip implementation context + * @param [in] enable_boost_mode Boost mode activation + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_radio_cfg_rx_boosted( const void* context, const bool enable_boost_mode ); + +/*! + * @brief Set RSSI calibration table + * + * @param [in] context Chip implementation context + * @param [in] rssi_cal_table RSSI calibration table + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_radio_set_rssi_calibration( const void* context, + const lr11xx_radio_rssi_calibration_table_t* rssi_cal_table ); + +/*! + * @brief Gets the radio bw parameter for a given bandwidth in Hz + * + * @param [in] bw_in_hz Requested GFSK Rx bandwidth + * @param [out] bw_parameter Radio parameter immediately above requested bw_in_hz + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_radio_get_gfsk_rx_bandwidth( uint32_t bw_in_hz, lr11xx_radio_gfsk_bw_t* bw_parameter ); + +/** + * @brief Compute the numerator for LoRa time-on-air computation. + * + * @remark To get the actual time-on-air in seconds, this value has to be divided by the LoRa bandwidth in Hertz. + * + * @param [in] pkt_p Pointer to the structure holding the LoRa packet parameters + * @param [in] mod_p Pointer to the structure holding the LoRa modulation parameters + * + * @returns LoRa time-on-air numerator + */ +uint32_t lr11xx_radio_get_lora_time_on_air_numerator( const lr11xx_radio_pkt_params_lora_t* pkt_p, + const lr11xx_radio_mod_params_lora_t* mod_p ); + +/** + * @brief Get the actual value in Hertz of a given LoRa bandwidth + * + * @param [in] bw LoRa bandwidth parameter + * + * @returns Actual LoRa bandwidth in Hertz + */ +uint32_t lr11xx_radio_get_lora_bw_in_hz( lr11xx_radio_lora_bw_t bw ); + +/*! + * @brief Get the time on air in ms for LoRa transmission + * + * @param [in] pkt_p Pointer to a structure holding the LoRa packet parameters + * @param [in] mod_p Pointer to a structure holding the LoRa modulation parameters + * + * @returns Time-on-air value in ms for LoRa transmission + */ +uint32_t lr11xx_radio_get_lora_time_on_air_in_ms( const lr11xx_radio_pkt_params_lora_t* pkt_p, + const lr11xx_radio_mod_params_lora_t* mod_p ); + +/** + * @brief Compute the numerator for GFSK time-on-air computation. + * + * @remark To get the actual time-on-air in seconds, this value has to be divided by the GFSK bitrate in bits per + * second. + * + * @param [in] pkt_p Pointer to the structure holding the GFSK packet parameters + * + * @returns GFSK time-on-air numerator + */ +uint32_t lr11xx_radio_get_gfsk_time_on_air_numerator( const lr11xx_radio_pkt_params_gfsk_t* pkt_p ); + +/** + * @brief Get the time on air in ms for GFSK transmission + * + * @param [in] pkt_p Pointer to a structure holding the GFSK packet parameters + * @param [in] mod_p Pointer to a structure holding the GFSK modulation parameters + * + * @returns Time-on-air value in ms for GFSK transmission + */ +uint32_t lr11xx_radio_get_gfsk_time_on_air_in_ms( const lr11xx_radio_pkt_params_gfsk_t* pkt_p, + const lr11xx_radio_mod_params_gfsk_t* mod_p ); + +/** + * @brief Get the number of RTC steps for a given time in millisecond + * + * @param [in] time_in_ms Timeout in millisecond + * + * @returns Number of RTC steps + */ +uint32_t lr11xx_radio_convert_time_in_ms_to_rtc_step( uint32_t time_in_ms ); + +/** + * @brief Get the information from the last received LoRa packet header (if @ref LR11XX_RADIO_LORA_PKT_EXPLICIT) or the + * locally configured settings (if @ref LR11XX_RADIO_LORA_PKT_IMPLICIT) + * + * @remark This function can be called only if @ref LR11XX_RADIO_PKT_TYPE_LORA is selected with @ref + * lr11xx_radio_set_pkt_type + * + * @param [in] context Chip implementation context + * @param [out] is_crc_present CRC configuration + * @param [out] cr LoRa coding rate + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_radio_get_lora_rx_info( const void* context, bool* is_crc_present, lr11xx_radio_lora_cr_t* cr ); + +/*! + * @brief Apply the workaround for the high ACP limitation + * + * @param [in] context Chip implementation context + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_radio_apply_high_acp_workaround( const void* context ); + +#ifdef __cplusplus +} +#endif + +#endif // LR11XX_RADIO_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/src/lr11xx_radio_timings.c b/src/lr11xx_radio_timings.c new file mode 100644 index 0000000..a27eeeb --- /dev/null +++ b/src/lr11xx_radio_timings.c @@ -0,0 +1,233 @@ +/** + * @file lr11xx_radio_timings.c + * + * @brief LR11XX timing helper functions implementation + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include "lr11xx_radio_timings.h" +#include "lr11xx_radio.h" + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE MACROS----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE CONSTANTS ------------------------------------------------------- + */ + +/** + * @brief Time in microsecond taken by the chip to process the Rx done interrupt + */ +#define RX_DONE_IRQ_PROCESSING_TIME_IN_US 74 + +/** + * @brief Time in microsecond taken by the chip to process the Tx done interrupt + */ +#define TX_DONE_IRQ_PROCESSING_TIME_IN_US 111 + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE TYPES ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE VARIABLES ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DECLARATION ------------------------------------------- + */ + +/** + * @brief Get the power amplifier ramp time for a given power amplifier ramp time parameter + * + * @param [in] ramp_time Power amplifier ramp time parameter + * + * @returns Ramp time in microsecond + */ +static uint32_t lr11xx_radio_timings_get_pa_ramp_time_in_us( const lr11xx_radio_ramp_time_t ramp_time ); + +/** + * @brief Get the LoRa reception input delay + * + * @param [in] bw LoRa bandwidth + * + * @returns LoRa reception input delay in microsecond + */ +static uint32_t lr11xx_radio_timings_get_lora_rx_input_delay_in_us( lr11xx_radio_lora_bw_t bw ); + +/** + * @brief Get the LoRa symbol time + * + * @param [in] bw LoRa bandwidth + * @param [in] sf LoRa spreading factor + * + * @returns LoRa symbol time in microsecond + */ +static uint32_t lr11xx_radio_timings_get_lora_symb_time_in_us( const lr11xx_radio_lora_sf_t sf, + const lr11xx_radio_lora_bw_t bw ); + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS DEFINITION --------------------------------------------- + */ + +uint32_t lr11xx_radio_timings_get_delay_between_last_bit_sent_and_rx_done_in_us( + const lr11xx_radio_mod_params_lora_t* mod_params ) +{ + return lr11xx_radio_timings_get_lora_rx_input_delay_in_us( mod_params->bw ) + + 2 * lr11xx_radio_timings_get_lora_symb_time_in_us( mod_params->sf, mod_params->bw ) + + RX_DONE_IRQ_PROCESSING_TIME_IN_US; +} + +uint32_t lr11xx_radio_timings_get_delay_between_last_bit_sent_and_tx_done_in_us( + const lr11xx_radio_ramp_time_t ramp_time ) +{ + return lr11xx_radio_timings_get_pa_ramp_time_in_us( ramp_time ) + TX_DONE_IRQ_PROCESSING_TIME_IN_US; +} + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DEFINITION -------------------------------------------- + */ + +static uint32_t lr11xx_radio_timings_get_pa_ramp_time_in_us( const lr11xx_radio_ramp_time_t ramp_time ) +{ + switch( ramp_time ) + { + case LR11XX_RADIO_RAMP_16_US: + { + return 16; + } + case LR11XX_RADIO_RAMP_32_US: + { + return 32; + } + case LR11XX_RADIO_RAMP_48_US: + { + return 48; + } + case LR11XX_RADIO_RAMP_64_US: + { + return 64; + } + case LR11XX_RADIO_RAMP_80_US: + { + return 80; + } + case LR11XX_RADIO_RAMP_96_US: + { + return 96; + } + case LR11XX_RADIO_RAMP_112_US: + { + return 112; + } + case LR11XX_RADIO_RAMP_128_US: + { + return 128; + } + case LR11XX_RADIO_RAMP_144_US: + { + return 144; + } + case LR11XX_RADIO_RAMP_160_US: + { + return 160; + } + case LR11XX_RADIO_RAMP_176_US: + { + return 176; + } + case LR11XX_RADIO_RAMP_192_US: + { + return 192; + } + case LR11XX_RADIO_RAMP_208_US: + { + return 208; + } + case LR11XX_RADIO_RAMP_240_US: + { + return 240; + } + case LR11XX_RADIO_RAMP_272_US: + { + return 272; + } + case LR11XX_RADIO_RAMP_304_US: + { + return 304; + } + default: + return 0; + } +} + +static uint32_t lr11xx_radio_timings_get_lora_rx_input_delay_in_us( lr11xx_radio_lora_bw_t bw ) +{ + switch( bw ) + { + case LR11XX_RADIO_LORA_BW_500: + { + return 16; + } + case LR11XX_RADIO_LORA_BW_250: + { + return 31; + } + case LR11XX_RADIO_LORA_BW_125: + { + return 57; + } + default: + { + return 0; + } + } +} + +static uint32_t lr11xx_radio_timings_get_lora_symb_time_in_us( const lr11xx_radio_lora_sf_t sf, + const lr11xx_radio_lora_bw_t bw ) +{ + return ( 1 << ( uint8_t ) sf ) * 1000000 / lr11xx_radio_get_lora_bw_in_hz( bw ); +} + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/src/lr11xx_radio_timings.h b/src/lr11xx_radio_timings.h new file mode 100644 index 0000000..6270d56 --- /dev/null +++ b/src/lr11xx_radio_timings.h @@ -0,0 +1,95 @@ +/** + * @file lr11xx_radio_timings.h + * + * @brief LR11XX timing helper functions definition + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR11XX_RADIO_TIMINGS_H +#define LR11XX_RADIO_TIMINGS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include "lr11xx_radio_types.h" + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +/** + * @brief Get the time between the last bit sent (on Tx side) and the Rx done event (on Rx side) + * + * @param [in] mod_params Pointer to a structure holding the LoRa modulation parameters used for the computation + * + * @returns Delay in microsecond + */ +uint32_t lr11xx_radio_timings_get_delay_between_last_bit_sent_and_rx_done_in_us( + const lr11xx_radio_mod_params_lora_t* mod_params ); + +/** + * @brief Get the time between the last bit sent and the Tx done event + * + * @param [in] ramp_time Power amplifier ramp time + * + * @returns Delay in microsecond + */ +uint32_t lr11xx_radio_timings_get_delay_between_last_bit_sent_and_tx_done_in_us( + const lr11xx_radio_ramp_time_t ramp_time ); + +#ifdef __cplusplus +} +#endif + +#endif // LR11XX_RADIO_TIMINGS_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/src/lr11xx_radio_types.h b/src/lr11xx_radio_types.h new file mode 100644 index 0000000..ed1820e --- /dev/null +++ b/src/lr11xx_radio_types.h @@ -0,0 +1,573 @@ +/*! + * @file lr11xx_radio_types.h + * + * @brief Radio driver types for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR11XX_RADIO_TYPES_H +#define LR11XX_RADIO_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include +#include + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/*! + * @brief Power Amplifier Selection values + * + * - Low-power Power Amplifier can reach up to 14dBm + * - High-power Power Amplifier can reach up to 22 dBm + */ +typedef enum +{ + LR11XX_RADIO_PA_SEL_LP = 0x00, //!< Low-power Power Amplifier + LR11XX_RADIO_PA_SEL_HP = 0x01, //!< High-power Power Amplifier + LR11XX_RADIO_PA_SEL_HF = 0x02, //!< High-frequency Power Amplifier +} lr11xx_radio_pa_selection_t; + +/*! + * @brief GFSK Address Filtering configurations + * + * If Address Filtering is enabled but a wrong address is received, therefore the reception is aborted and the address + * error flag of packet status is set. + */ +typedef enum +{ + LR11XX_RADIO_GFSK_ADDRESS_FILTERING_DISABLE = 0x00, //!< Filter deactivated + LR11XX_RADIO_GFSK_ADDRESS_FILTERING_NODE_ADDRESS = 0x01, //!< Filter on Node Address + LR11XX_RADIO_GFSK_ADDRESS_FILTERING_NODE_AND_BROADCAST_ADDRESSES = + 0x02, //!< Filtering on Node and Broadcast addresses +} lr11xx_radio_gfsk_address_filtering_t; + +/*! + * @brief Chip mode after successfull transmission or reception + * + * Unused for RX duty cycle and AutoTxRx operations + */ +typedef enum +{ + LR11XX_RADIO_FALLBACK_STDBY_RC = 0x01, //!< Standby RC (Default) + LR11XX_RADIO_FALLBACK_STDBY_XOSC = 0x02, //!< Standby XOSC + LR11XX_RADIO_FALLBACK_FS = 0x03 //!< FS +} lr11xx_radio_fallback_modes_t; + +/*! + * @brief Ramping time for PA + * + * This parameter is the ramping time of the PA. A high value improves spectral quality. + */ +typedef enum +{ + LR11XX_RADIO_RAMP_16_US = 0x00, //!< 16 us Ramp Time + LR11XX_RADIO_RAMP_32_US = 0x01, //!< 32 us Ramp Time + LR11XX_RADIO_RAMP_48_US = 0x02, //!< 48 us Ramp Time (Default) + LR11XX_RADIO_RAMP_64_US = 0x03, //!< 64 us Ramp Time + LR11XX_RADIO_RAMP_80_US = 0x04, //!< 80 us Ramp Time + LR11XX_RADIO_RAMP_96_US = 0x05, //!< 96 us Ramp Time + LR11XX_RADIO_RAMP_112_US = 0x06, //!< 112 us Ramp Time + LR11XX_RADIO_RAMP_128_US = 0x07, //!< 128 us Ramp Time + LR11XX_RADIO_RAMP_144_US = 0x08, //!< 144 us Ramp Time + LR11XX_RADIO_RAMP_160_US = 0x09, //!< 160 us Ramp Time + LR11XX_RADIO_RAMP_176_US = 0x0A, //!< 176 us Ramp Time + LR11XX_RADIO_RAMP_192_US = 0x0B, //!< 192 us Ramp Time + LR11XX_RADIO_RAMP_208_US = 0x0C, //!< 208 us Ramp Time + LR11XX_RADIO_RAMP_240_US = 0x0D, //!< 240 us Ramp Time + LR11XX_RADIO_RAMP_272_US = 0x0E, //!< 272 us Ramp Time + LR11XX_RADIO_RAMP_304_US = 0x0F, //!< 304 us Ramp Time +} lr11xx_radio_ramp_time_t; + +/*! + * @brief LoRa network type configuration + */ +typedef enum +{ + LR11XX_RADIO_LORA_NETWORK_PRIVATE = 0x00, //!< LoRa private network + LR11XX_RADIO_LORA_NETWORK_PUBLIC = 0x01, //!< LoRa public network +} lr11xx_radio_lora_network_type_t; + +/*! + * @brief LoRa Spreading Factor configurations + */ +typedef enum +{ + LR11XX_RADIO_LORA_SF5 = 0x05, //!< Spreading Factor 5 + LR11XX_RADIO_LORA_SF6 = 0x06, //!< Spreading Factor 6 + LR11XX_RADIO_LORA_SF7 = 0x07, //!< Spreading Factor 7 + LR11XX_RADIO_LORA_SF8 = 0x08, //!< Spreading Factor 8 + LR11XX_RADIO_LORA_SF9 = 0x09, //!< Spreading Factor 9 + LR11XX_RADIO_LORA_SF10 = 0x0A, //!< Spreading Factor 10 + LR11XX_RADIO_LORA_SF11 = 0x0B, //!< Spreading Factor 11 + LR11XX_RADIO_LORA_SF12 = 0x0C, //!< Spreading Factor 12 +} lr11xx_radio_lora_sf_t; + +/*! + * @brief LoRa Bandwidth configurations + */ +typedef enum +{ + LR11XX_RADIO_LORA_BW_10 = 0x08, //!< Bandwidth 10.42 kHz + LR11XX_RADIO_LORA_BW_15 = 0x01, //!< Bandwidth 15.63 kHz + LR11XX_RADIO_LORA_BW_20 = 0x09, //!< Bandwidth 20.83 kHz + LR11XX_RADIO_LORA_BW_31 = 0x02, //!< Bandwidth 31.25 kHz + LR11XX_RADIO_LORA_BW_41 = 0x0A, //!< Bandwidth 41.67 kHz + LR11XX_RADIO_LORA_BW_62 = 0x03, //!< Bandwidth 62.50 kHz + LR11XX_RADIO_LORA_BW_125 = 0x04, //!< Bandwidth 125.00 kHz + LR11XX_RADIO_LORA_BW_250 = 0x05, //!< Bandwidth 250.00 kHz + LR11XX_RADIO_LORA_BW_500 = 0x06, //!< Bandwidth 500.00 kHz + LR11XX_RADIO_LORA_BW_200 = 0x0D, //!< Bandwidth 203.00 kHz, 2G4 and compatible with LR112x chips only + LR11XX_RADIO_LORA_BW_400 = 0x0E, //!< Bandwidth 406.00 kHz, 2G4 and compatible with LR112x chips only + LR11XX_RADIO_LORA_BW_800 = 0x0F, //!< Bandwidth 812.00 kHz, 2G4 and compatible with LR112x chips only +} lr11xx_radio_lora_bw_t; + +/*! + * @brief LoRa Coding Rate configurations + */ +typedef enum +{ + LR11XX_RADIO_LORA_NO_CR = 0x00, //!< No Coding Rate + LR11XX_RADIO_LORA_CR_4_5 = 0x01, //!< Coding Rate 4/5 Short Interleaver + LR11XX_RADIO_LORA_CR_4_6 = 0x02, //!< Coding Rate 4/6 Short Interleaver + LR11XX_RADIO_LORA_CR_4_7 = 0x03, //!< Coding Rate 4/7 Short Interleaver + LR11XX_RADIO_LORA_CR_4_8 = 0x04, //!< Coding Rate 4/8 Short Interleaver + LR11XX_RADIO_LORA_CR_LI_4_5 = 0x05, //!< Coding Rate 4/5 Long Interleaver + LR11XX_RADIO_LORA_CR_LI_4_6 = 0x06, //!< Coding Rate 4/6 Long Interleaver + LR11XX_RADIO_LORA_CR_LI_4_8 = 0x07, //!< Coding Rate 4/8 Long Interleaver +} lr11xx_radio_lora_cr_t; + +/*! + * @brief Values for intermediary mode + */ +typedef enum +{ + LR11XX_RADIO_MODE_SLEEP = 0x00, //!< Sleep / Not recommended with LR1110 FW from 0x0303 to 0x0307 and LR1120 FW + //!< 0x0101 in case of transition from Rx to Tx in LoRa + LR11XX_RADIO_MODE_STANDBY_RC = 0x01, //!< Standby RC + LR11XX_RADIO_MODE_STANDBY_XOSC = 0x02, //!< Standby XOSC + LR11XX_RADIO_MODE_FS = 0x03 //!< Frequency Synthesis +} lr11xx_radio_intermediary_mode_t; + +/*! + * @brief GFSK Cyclic Redundancy Check configurations + * + * If this value is set to something other than CRC_OFF, a CRC is automatically computed and added after the end of the + * payload on transmitter side. On receiver side, the CRC check is automatically processed. + */ +typedef enum +{ + LR11XX_RADIO_GFSK_CRC_OFF = 0x01, //!< CRC check deactivated + LR11XX_RADIO_GFSK_CRC_1_BYTE = 0x00, + LR11XX_RADIO_GFSK_CRC_2_BYTES = 0x02, + LR11XX_RADIO_GFSK_CRC_1_BYTE_INV = 0x04, + LR11XX_RADIO_GFSK_CRC_2_BYTES_INV = 0x06, +} lr11xx_radio_gfsk_crc_type_t; + +/*! + * @brief GFSK data whitening configurations + */ +typedef enum +{ + LR11XX_RADIO_GFSK_DC_FREE_OFF = 0x00, //!< Whitening deactivated + LR11XX_RADIO_GFSK_DC_FREE_WHITENING = 0x01, //!< Whitening enabled + LR11XX_RADIO_GFSK_DC_FREE_WHITENING_SX128X_COMP = 0x03, //!< Whitening enabled - SX128x compatibility +} lr11xx_radio_gfsk_dc_free_t; + +/*! + * @brief GFSK Header Type configurations + * + * This parameter indicates whether or not the payload length is sent and read over the air. + * + * If the payload length is known beforehand by both transmitter and receiver, therefore there is no need to send it + * over the air. Otherwise, setting this parameter to LR11XX_RADIO_GFSK_PKT_VAR_LEN will make the modem to automatically + * prepand a byte containing the payload length to the the payload on transmitter side. On receiver side, this first + * byte is read to set the payload length to read. + * + * This configuration is only available for GFSK packet types. + */ +typedef enum +{ + LR11XX_RADIO_GFSK_PKT_FIX_LEN = 0x00, //!< Payload length is not sent/read over the air + LR11XX_RADIO_GFSK_PKT_VAR_LEN = 0x01, //!< Payload length is sent/read over the air + LR11XX_RADIO_GFSK_PKT_VAR_LEN_SX128X_COMP = + 0x02, //!< Payload length is sent/read over the air - SX128x compatibility +} lr11xx_radio_gfsk_pkt_len_modes_t; + +/*! + * @brief GFSK Preamble Detector Length configurations + * + * This parameter sets the minimum length of preamble bits to be received to continue reception of incoming packet. If a + * packet with preamble length lower than this value is being received, the reception stops without generating IRQ. + * + * This parameter has no impact on TX operations. + */ +typedef enum +{ + LR11XX_RADIO_GFSK_PREAMBLE_DETECTOR_OFF = 0x00, + LR11XX_RADIO_GFSK_PREAMBLE_DETECTOR_MIN_8BITS = 0x04, + LR11XX_RADIO_GFSK_PREAMBLE_DETECTOR_MIN_16BITS = 0x05, + LR11XX_RADIO_GFSK_PREAMBLE_DETECTOR_MIN_24BITS = 0x06, + LR11XX_RADIO_GFSK_PREAMBLE_DETECTOR_MIN_32BITS = 0x07 +} lr11xx_radio_gfsk_preamble_detector_t; + +/*! + * @brief LoRa Cyclic Redundancy Check configurations + */ +typedef enum +{ + LR11XX_RADIO_LORA_CRC_OFF = 0x00, //!< CRC deactivated + LR11XX_RADIO_LORA_CRC_ON = 0x01, //!< CRC activated +} lr11xx_radio_lora_crc_t; + +/*! + * @brief LoRa Header type configurations + */ +typedef enum +{ + LR11XX_RADIO_LORA_PKT_EXPLICIT = 0x00, //!< Explicit header: transmitted over the air + LR11XX_RADIO_LORA_PKT_IMPLICIT = 0x01, //!< Implicit header: not transmitted over the air +} lr11xx_radio_lora_pkt_len_modes_t; + +/*! + * @brief LoRa IQ mode configurations + * + * LoRa IQ modes are mutually exclusives: a physical packet sent with standard IQ will not be received by a receiver + * configured with inverted IQ. + */ +typedef enum +{ + LR11XX_RADIO_LORA_IQ_STANDARD = 0x00, //!< IQ standard + LR11XX_RADIO_LORA_IQ_INVERTED = 0x01, //!< IQ inverted +} lr11xx_radio_lora_iq_t; + +/*! + * @brief Packet type values + */ +typedef enum +{ + LR11XX_RADIO_PKT_NONE = 0x00, //!< State after cold start, Wi-Fi or GNSS capture + LR11XX_RADIO_PKT_TYPE_GFSK = 0x01, //!< GFSK modulation + LR11XX_RADIO_PKT_TYPE_LORA = 0x02, //!< LoRa modulation +} lr11xx_radio_pkt_type_t; + +/*! + * @brief Select power amplifier supply source + */ +typedef enum +{ + LR11XX_RADIO_PA_REG_SUPPLY_VREG = 0x00, //!< Power amplifier supplied by the main regulator + LR11XX_RADIO_PA_REG_SUPPLY_VBAT = 0x01 //!< Power amplifier supplied by the battery +} lr11xx_radio_pa_reg_supply_t; + +/*! + * @brief RX Duty Cycle Modes + */ +typedef enum +{ + LR11XX_RADIO_RX_DUTY_CYCLE_MODE_RX = 0x00, //!< LoRa/GFSK: Uses Rx for listening to packets + LR11XX_RADIO_RX_DUTY_CYCLE_MODE_CAD = 0x01, //!< Only in LoRa: Uses CAD to listen for over-the-air activity +} lr11xx_radio_rx_duty_cycle_mode_t; + +/*! + * @brief GFSK Bandwidth configurations + */ +typedef enum +{ + LR11XX_RADIO_GFSK_BW_4800 = 0x1F, //!< Bandwidth 4.8 kHz DSB + LR11XX_RADIO_GFSK_BW_5800 = 0x17, //!< Bandwidth 5.8 kHz DSB + LR11XX_RADIO_GFSK_BW_7300 = 0x0F, //!< Bandwidth 7.3 kHz DSB + LR11XX_RADIO_GFSK_BW_9700 = 0x1E, //!< Bandwidth 9.7 kHz DSB + LR11XX_RADIO_GFSK_BW_11700 = 0x16, //!< Bandwidth 11.7 kHz DSB + LR11XX_RADIO_GFSK_BW_14600 = 0x0E, //!< Bandwidth 14.6 kHz DSB + LR11XX_RADIO_GFSK_BW_19500 = 0x1D, //!< Bandwidth 19.5 kHz DSB + LR11XX_RADIO_GFSK_BW_23400 = 0x15, //!< Bandwidth 23.4 kHz DSB + LR11XX_RADIO_GFSK_BW_29300 = 0x0D, //!< Bandwidth 29.3 kHz DSB + LR11XX_RADIO_GFSK_BW_39000 = 0x1C, //!< Bandwidth 39.0 kHz DSB + LR11XX_RADIO_GFSK_BW_46900 = 0x14, //!< Bandwidth 46.9 kHz DSB + LR11XX_RADIO_GFSK_BW_58600 = 0x0C, //!< Bandwidth 58.6 kHz DSB + LR11XX_RADIO_GFSK_BW_78200 = 0x1B, //!< Bandwidth 78.2 kHz DSB + LR11XX_RADIO_GFSK_BW_93800 = 0x13, //!< Bandwidth 93.8 kHz DSB + LR11XX_RADIO_GFSK_BW_117300 = 0x0B, //!< Bandwidth 117.3 kHz DSB + LR11XX_RADIO_GFSK_BW_156200 = 0x1A, //!< Bandwidth 156.2 kHz DSB + LR11XX_RADIO_GFSK_BW_187200 = 0x12, //!< Bandwidth 187.2 kHz DSB + LR11XX_RADIO_GFSK_BW_234300 = 0x0A, //!< Bandwidth 232.3 kHz DSB + LR11XX_RADIO_GFSK_BW_312000 = 0x19, //!< Bandwidth 312.0 kHz DSB + LR11XX_RADIO_GFSK_BW_373600 = 0x11, //!< Bandwidth 373.6 kHz DSB + LR11XX_RADIO_GFSK_BW_467000 = 0x09 //!< Bandwidth 467.0 kHz DSB +} lr11xx_radio_gfsk_bw_t; + +/*! + * @brief Possible automatic actions when Channel Activity Detection operations terminate + * + * For RADIO_EXIT_MODE_CAD_RX, LR11XX enters RX mode on activity detected. The timeout value for this RX operation is + * defined as: + * + * \f$ 31.25us \times timeout \f$ + * + * With \f$ timeout \f$ defined in RadioCadParams_t::timeout + * + * If the CAD operation is negative with RADIO_CAD_EXIT_MODE_RX or if CAD operation is positive with + * RADIO_CAD_EXIT_MODE_TX, therefore the LR11XX enters Standby RC mode. + */ +typedef enum +{ + LR11XX_RADIO_CAD_EXIT_MODE_STANDBYRC = 0x00, //!< Enter standby RC mode after CAD operation + LR11XX_RADIO_CAD_EXIT_MODE_RX = 0x01, //!< Enter in RX mode if an activity is detected + LR11XX_RADIO_CAD_EXIT_MODE_TX = 0x10, //!< Enter in TX mode if no activity is detected +} lr11xx_radio_cad_exit_mode_t; + +/*! + * @brief Pulse shape configurations + */ +typedef enum +{ + LR11XX_RADIO_GFSK_PULSE_SHAPE_OFF = 0x00, //!< No filter applied + LR11XX_RADIO_GFSK_PULSE_SHAPE_BT_03 = 0x08, //!< Gaussian BT 0.3 + LR11XX_RADIO_GFSK_PULSE_SHAPE_BT_05 = 0x09, //!< Gaussian BT 0.5 + LR11XX_RADIO_GFSK_PULSE_SHAPE_BT_07 = 0x0A, //!< Gaussian BT 0.7 + LR11XX_RADIO_GFSK_PULSE_SHAPE_BT_1 = 0x0B //!< Gaussian BT 1.0 +} lr11xx_radio_gfsk_pulse_shape_t; + +/*! + * @brief Channel Activity Detection parameters + * + * Parameters detPeak and detMin are to be used for tuning the sensitivity of Channel Activity Detection. It depends on + * Spreading Factor, Bandwidth and symbolNum. + * + * For detPeak, the 5 MSBits are encoding the integer part, the 3 LSBits are encoding 1/8 of the decimal part. For + * instance, \f$detPeak = 50\f$ (= 0x32) leads to a ratio being \f$6 + 2 * 1/8 = 6.25\f$. + * + * detMin is unit free and represents the ratio between the minimal power of a correlation peak and measurement gain + * that can be considered as a peak detection. It helps to avoid detection on noise. Authorized values a from 0 to 181. + */ +typedef struct lr11xx_radio_cad_params_s +{ + uint8_t cad_symb_nb; //!< Number of symbols used for CAD detection + uint8_t cad_detect_peak; //!< Ratio for CAD between correlator peak and average + //!< (Default 0x32) + uint8_t cad_detect_min; //!< Minimum power of the correlation peak to be + //!< considered as a positive CAD (Default 0x0A) + lr11xx_radio_cad_exit_mode_t cad_exit_mode; //!< Automated action on CAD completion + uint32_t cad_timeout; //!< Value used to compute timeout +} lr11xx_radio_cad_params_t; + +/*! + * @brief Status of GFSK received packet + */ +typedef struct lr11xx_radio_pkt_status_gfsk_s +{ + int8_t rssi_sync_in_dbm; //!< RSSI value latched on detection of the last received packet Sync Address + int8_t rssi_avg_in_dbm; //!< RSSI averaged over the payload of the last received packet + uint8_t rx_len_in_bytes; //!< Length of the last received packet [Bytes] + bool is_addr_err; //!< Address filtering status. Asserted if received packet address does not match node address + //!< nor broadcast address + bool is_crc_err; //!< CRC status of the current packet (applicable only in RX, with CRC enabled) + bool is_len_err; //!< Asserted when the length of last received packet is greater than the maximal length + //!< (applicable only in RX with variable length packet) + bool is_abort_err; //!< Asserted when the current packet has been aborted (applicable in RX and TX) + bool is_received; //!< Asserted when packet reception is done (applicable in RX) + bool is_sent; //!< Asserted when packet transmission is done (applicable in TX) +} lr11xx_radio_pkt_status_gfsk_t; + +/*! + * @brief Status of received packet + */ +typedef struct lr11xx_radio_pkt_status_lora_s +{ + int8_t rssi_pkt_in_dbm; //!< Average RSSI over last received packet. + int8_t snr_pkt_in_db; //!< SNR estimated on last received packet. + int8_t signal_rssi_pkt_in_dbm; //!< RSSI of last packet latched after +} lr11xx_radio_pkt_status_lora_t; + +/*! + * @brief Length and offset of received packet + */ +typedef struct lr11xx_radio_rx_buffer_status_s +{ + uint8_t pld_len_in_bytes; //!< Length of received packet [Bytes] + uint8_t buffer_start_pointer; //!< Offset in the reception buffer of + //!< first byte received [Bytes] +} lr11xx_radio_rx_buffer_status_t; + +/*! + * @brief GFSK packet statistic structure + */ +typedef struct lr11xx_radio_stats_gfsk_s +{ + uint16_t nb_pkt_received; //!< Total number of received packets + uint16_t nb_pkt_crc_error; //!< Total number of received packets with CRC error + uint16_t nb_pkt_len_error; //!< Total number of received packets with a length error +} lr11xx_radio_stats_gfsk_t; + +/*! + * @brief LoRa packet statistic structure + */ +typedef struct lr11xx_radio_stats_lora_s +{ + uint16_t nb_pkt_received; //!< Total number of received packets + uint16_t nb_pkt_crc_error; //!< Total number of received packets with CRC error + uint16_t nb_pkt_header_error; //!< Total number of packets with header error + uint16_t nb_pkt_falsesync; //!< Total number of false sync +} lr11xx_radio_stats_lora_t; + +/*! + * @brief Modulation configuration for GFSK packet + */ +typedef struct lr11xx_radio_mod_params_gfsk_s +{ + uint32_t br_in_bps; //!< GFSK bitrate [bit/s] + lr11xx_radio_gfsk_pulse_shape_t pulse_shape; //!< GFSK pulse shape + lr11xx_radio_gfsk_bw_t bw_dsb_param; //!< GFSK bandwidth + uint32_t fdev_in_hz; //!< GFSK frequency deviation [Hz] +} lr11xx_radio_mod_params_gfsk_t; + +/*! + * @brief Modulation configuration for LoRa packet + */ +typedef struct lr11xx_radio_mod_params_lora_s +{ + lr11xx_radio_lora_sf_t sf; //!< LoRa spreading factor + lr11xx_radio_lora_bw_t bw; //!< LoRa bandwidth + lr11xx_radio_lora_cr_t cr; //!< LoRa coding rate + uint8_t ldro; //!< LoRa LDRO +} lr11xx_radio_mod_params_lora_t; + +/*! + * @brief Packet parameter configuration for GFSK packets + */ +typedef struct lr11xx_radio_pkt_params_gfsk_s +{ + uint16_t preamble_len_in_bits; //!< GFSK Preamble length [bits] + lr11xx_radio_gfsk_preamble_detector_t preamble_detector; //!< GFSK Preamble detection configuration + uint8_t sync_word_len_in_bits; //!< GFSK Syncword length [bits] + lr11xx_radio_gfsk_address_filtering_t address_filtering; //!< GFSK Address filtering/comparison configuration + lr11xx_radio_gfsk_pkt_len_modes_t header_type; //!< GFSK Header type configuration + uint8_t pld_len_in_bytes; //!< GFSK Payload length [bytes] + lr11xx_radio_gfsk_crc_type_t crc_type; //!< GFSK CRC configuration + lr11xx_radio_gfsk_dc_free_t dc_free; //!< GFSK Whitening configuration +} lr11xx_radio_pkt_params_gfsk_t; + +/*! + * @brief Packet parameter configuration for LoRa packets + */ +typedef struct lr11xx_radio_pkt_params_lora_s +{ + uint16_t preamble_len_in_symb; //!< LoRa Preamble length [symbols] + lr11xx_radio_lora_pkt_len_modes_t header_type; //!< LoRa Header type configuration + uint8_t pld_len_in_bytes; //!< LoRa Payload length [bytes] + lr11xx_radio_lora_crc_t crc; //!< LoRa CRC configuration + lr11xx_radio_lora_iq_t iq; //!< LoRa IQ configuration +} lr11xx_radio_pkt_params_lora_t; + +/*! + * @brief Configuration of Power Amplifier + * + * @ref pa_duty_cycle controls the duty cycle of Power Amplifier according to: + * \f$ dutycycle = 0.2 + 0.04 \times pa_duty_cycle \f$ + * It can be used to adapt the TX multi-band operation using a single-matching network. + * + * The allowed duty cycle values for LPA are from 0.2 to 0.48 (by step of 0.04). Therefore possible values for + * pa_duty_cycle go from 0 to 7. + * + * The allowed duty cycle values for HPA go from 0.2 to 0.36 (by step of 0.04). Therefore in this case, the possible + * values for pa_duty_cycle go from 0 to 4. + * + * @ref pa_hp_sel controls the number of slices for HPA according to: \f$ \#slices = pa_hp_sel + 1 \f$ + */ +typedef struct lr11xx_radio_pa_cfg_s +{ + lr11xx_radio_pa_selection_t pa_sel; //!< Power Amplifier selection + lr11xx_radio_pa_reg_supply_t pa_reg_supply; //!< Power Amplifier regulator supply source + uint8_t pa_duty_cycle; //!< Power Amplifier duty cycle (Default 0x04) + uint8_t pa_hp_sel; //!< Number of slices for HPA (Default 0x07) +} lr11xx_radio_pa_cfg_t; + +/*! + * @brief RSSI calibration table + */ +typedef struct lr11xx_radio_rssi_calibration_table_s +{ + struct + { + uint8_t g11; + uint8_t g10; + uint8_t g9; + uint8_t g8; + uint8_t g7; + uint8_t g6; + uint8_t g5; + uint8_t g4; + uint8_t g13hp6; + uint8_t g13hp5; + uint8_t g13hp4; + uint8_t g13hp3; + uint8_t g13hp2; + uint8_t g13hp1; + uint8_t g13; + uint8_t g12; + uint8_t g13hp7; + } gain_tune; //!< Used to set gain tune value for RSSI calibration + + int16_t gain_offset; //!< Used to set gain offset value for RSSI calibration +} lr11xx_radio_rssi_calibration_table_t; + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +#ifdef __cplusplus +} +#endif + +#endif // LR11XX_RADIO_TYPES_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/src/lr11xx_regmem.c b/src/lr11xx_regmem.c new file mode 100644 index 0000000..3c5312a --- /dev/null +++ b/src/lr11xx_regmem.c @@ -0,0 +1,314 @@ +/*! + * @file lr11xx_regmem.c + * + * @brief Register/memory driver implementation for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include "lr11xx_regmem.h" +#include "lr11xx_hal.h" + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE MACROS----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE CONSTANTS ------------------------------------------------------- + */ + +#define LR11XX_REGMEM_CLEAR_RXBUFFER_CMD_LENGTH 2 +#define LR11XX_REGMEM_WRITE_REGMEM32_CMD_LENGTH ( 2 + 4 ) +#define LR11XX_REGMEM_READ_REGMEM32_CMD_LENGTH ( 2 + 4 + 1 ) +#define LR11XX_REGMEM_WRITE_MEM8_CMD_LENGTH ( 2 + 4 ) +#define LR11XX_REGMEM_READ_MEM8_CMD_LENGTH ( 2 + 4 + 1 ) +#define LR11XX_REGMEM_WRITE_BUFFER8_CMD_LENGTH ( 2 ) +#define LR11XX_REGMEM_READ_BUFFER8_CMD_LENGTH ( 2 + 2 ) +#define LR11XX_REGMEM_WRITE_REGMEM32_MASK_CMD_LENGTH ( 2 + 4 + 4 + 4 ) + +#define LR11XX_REGMEM_BUFFER_SIZE_MAX ( 256 ) + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE TYPES ----------------------------------------------------------- + */ + +/*! + * @brief Operating codes for register and memory related operations + */ +enum +{ + LR11XX_REGMEM_WRITE_REGMEM32_OC = 0x0105, + LR11XX_REGMEM_READ_REGMEM32_OC = 0x0106, + LR11XX_REGMEM_WRITE_MEM8_OC = 0x0107, + LR11XX_REGMEM_READ_MEM8_OC = 0x0108, + LR11XX_REGMEM_WRITE_BUFFER8_OC = 0x0109, + LR11XX_REGMEM_READ_BUFFER8_OC = 0x010A, + LR11XX_REGMEM_CLEAR_RXBUFFER_OC = 0x010B, + LR11XX_REGMEM_WRITE_REGMEM32_MASK_OC = 0x010C, +}; + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE VARIABLES ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DECLARATION ------------------------------------------- + */ + +/*! + * @brief Helper function that fill both cbuffer with opcode and memory address + * + * It is typically used in read/write regmem32 functions. + * + * @warning It is up to the caller to ensure cbuffer is big enough to contain opcode and address! + */ +static void lr11xx_regmem_fill_cbuffer_opcode_address( uint8_t* cbuffer, uint16_t opcode, uint32_t address ); + +/*! + * @brief Helper function that fill both cbuffer with opcode memory address, and data length to read + * + * It is typically used in read functions. + * + * @warning It is up to the caller to ensure cbuffer is big enough to contain opcode and address! + */ +static void lr11xx_regmem_fill_cbuffer_opcode_address_length( uint8_t* cbuffer, uint16_t opcode, uint32_t address, + uint8_t length ); + +/*! + * @brief Helper function that fill both cbuffer with data + * + * It is typically used in write write regmem32 functions. + * + * @warning It is up to the caller to ensure cdata is big enough to contain all data! + */ +static void lr11xx_regmem_fill_cdata( uint8_t* cdata, const uint32_t* data, uint8_t data_length ); + +/*! + * @brief Helper function that fill both cbuffer and cdata buffers with opcode, memory address and data + * + * It is typically used to factorize and write regmem32 operations. Behind the scene it calls the other helpers + * lr11xx_regmem_fill_cbuffer_opcode_address and lr11xx_regmem_fill_cdata. + * + * @warning It is up to the caller to ensure cbuffer and cdata are big enough to contain their respective information! + */ +static void lr11xx_regmem_fill_cbuffer_cdata_opcode_address_data( uint8_t* cbuffer, uint8_t* cdata, uint16_t opcode, + uint32_t address, const uint32_t* data, + uint8_t data_length ); + +/*! + * @brief Helper function that convert an array of uint8_t into an array of uint32_t + * + * Typically used in the read function returning uint32_t array. + * + * @warning It is up to the caller to ensure the raw_buffer is of length at least "out_buffer_length * + * sizeof(uint32_t)"! + */ +static void lr11xx_regmem_fill_out_buffer_from_raw_buffer( uint32_t* out_buffer, const uint8_t* raw_buffer, + uint8_t out_buffer_length ); + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS DEFINITION --------------------------------------------- + */ + +lr11xx_status_t lr11xx_regmem_write_regmem32( const void* context, const uint32_t address, const uint32_t* buffer, + const uint8_t length ) +{ + uint8_t cbuffer[LR11XX_REGMEM_WRITE_REGMEM32_CMD_LENGTH]; + uint8_t cdata[LR11XX_REGMEM_BUFFER_SIZE_MAX]; + + lr11xx_regmem_fill_cbuffer_cdata_opcode_address_data( cbuffer, cdata, LR11XX_REGMEM_WRITE_REGMEM32_OC, address, + buffer, length ); + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_REGMEM_WRITE_REGMEM32_CMD_LENGTH, cdata, + length * sizeof( uint32_t ) ); +} + +lr11xx_status_t lr11xx_regmem_read_regmem32( const void* context, const uint32_t address, uint32_t* buffer, + const uint8_t length ) +{ + uint8_t cbuffer[LR11XX_REGMEM_READ_REGMEM32_CMD_LENGTH]; + lr11xx_status_t status = LR11XX_STATUS_ERROR; + + lr11xx_regmem_fill_cbuffer_opcode_address_length( cbuffer, LR11XX_REGMEM_READ_REGMEM32_OC, address, length ); + + status = ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_REGMEM_READ_REGMEM32_CMD_LENGTH, + ( uint8_t* ) buffer, length * sizeof( uint32_t ) ); + + if( status == LR11XX_STATUS_OK ) + { + lr11xx_regmem_fill_out_buffer_from_raw_buffer( buffer, ( const uint8_t* ) buffer, length ); + } + + return status; +} + +lr11xx_status_t lr11xx_regmem_write_mem8( const void* context, const uint32_t address, const uint8_t* buffer, + const uint8_t length ) +{ + uint8_t cbuffer[LR11XX_REGMEM_WRITE_MEM8_CMD_LENGTH]; + + lr11xx_regmem_fill_cbuffer_opcode_address( cbuffer, LR11XX_REGMEM_WRITE_MEM8_OC, address ); + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_REGMEM_WRITE_MEM8_CMD_LENGTH, buffer, + length ); +} + +lr11xx_status_t lr11xx_regmem_read_mem8( const void* context, const uint32_t address, uint8_t* buffer, + const uint8_t length ) +{ + uint8_t cbuffer[LR11XX_REGMEM_READ_MEM8_CMD_LENGTH]; + + lr11xx_regmem_fill_cbuffer_opcode_address_length( cbuffer, LR11XX_REGMEM_READ_MEM8_OC, address, length ); + + return ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_REGMEM_READ_MEM8_CMD_LENGTH, buffer, length ); +} + +lr11xx_status_t lr11xx_regmem_write_buffer8( const void* context, const uint8_t* buffer, const uint8_t length ) +{ + const uint8_t cbuffer[LR11XX_REGMEM_WRITE_BUFFER8_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_REGMEM_WRITE_BUFFER8_OC >> 8 ), + ( uint8_t ) ( LR11XX_REGMEM_WRITE_BUFFER8_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_REGMEM_WRITE_BUFFER8_CMD_LENGTH, buffer, + length ); +} + +lr11xx_status_t lr11xx_regmem_read_buffer8( const void* context, uint8_t* buffer, const uint8_t offset, + const uint8_t length ) +{ + const uint8_t cbuffer[LR11XX_REGMEM_READ_BUFFER8_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_REGMEM_READ_BUFFER8_OC >> 8 ), + ( uint8_t ) ( LR11XX_REGMEM_READ_BUFFER8_OC >> 0 ), + offset, + length, + }; + + return ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_REGMEM_READ_BUFFER8_CMD_LENGTH, buffer, + length ); +} + +lr11xx_status_t lr11xx_regmem_clear_rxbuffer( const void* context ) +{ + const uint8_t cbuffer[LR11XX_REGMEM_CLEAR_RXBUFFER_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_REGMEM_CLEAR_RXBUFFER_OC >> 8 ), + ( uint8_t ) ( LR11XX_REGMEM_CLEAR_RXBUFFER_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_REGMEM_CLEAR_RXBUFFER_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_regmem_write_regmem32_mask( const void* context, const uint32_t address, const uint32_t mask, + const uint32_t data ) +{ + uint8_t cbuffer[LR11XX_REGMEM_WRITE_REGMEM32_MASK_CMD_LENGTH]; + + lr11xx_regmem_fill_cbuffer_opcode_address( cbuffer, LR11XX_REGMEM_WRITE_REGMEM32_MASK_OC, address ); + + cbuffer[6] = ( uint8_t ) ( mask >> 24 ); + cbuffer[7] = ( uint8_t ) ( mask >> 16 ); + cbuffer[8] = ( uint8_t ) ( mask >> 8 ); + cbuffer[9] = ( uint8_t ) ( mask >> 0 ); + + cbuffer[10] = ( uint8_t ) ( data >> 24 ); + cbuffer[11] = ( uint8_t ) ( data >> 16 ); + cbuffer[12] = ( uint8_t ) ( data >> 8 ); + cbuffer[13] = ( uint8_t ) ( data >> 0 ); + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_REGMEM_WRITE_REGMEM32_MASK_CMD_LENGTH, 0, 0 ); +} + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DEFINITION -------------------------------------------- + */ + +void lr11xx_regmem_fill_cbuffer_opcode_address( uint8_t* cbuffer, uint16_t opcode, uint32_t address ) +{ + cbuffer[0] = ( uint8_t ) ( opcode >> 8 ); + cbuffer[1] = ( uint8_t ) ( opcode >> 0 ); + + cbuffer[2] = ( uint8_t ) ( address >> 24 ); + cbuffer[3] = ( uint8_t ) ( address >> 16 ); + cbuffer[4] = ( uint8_t ) ( address >> 8 ); + cbuffer[5] = ( uint8_t ) ( address >> 0 ); +} + +void lr11xx_regmem_fill_cbuffer_opcode_address_length( uint8_t* cbuffer, uint16_t opcode, uint32_t address, + uint8_t length ) +{ + lr11xx_regmem_fill_cbuffer_opcode_address( cbuffer, opcode, address ); + cbuffer[6] = length; +} + +void lr11xx_regmem_fill_cdata( uint8_t* cdata, const uint32_t* data, uint8_t data_length ) +{ + for( uint16_t index = 0; index < data_length; index++ ) + { + uint8_t* cdata_local = &cdata[index * sizeof( uint32_t )]; + + cdata_local[0] = ( uint8_t ) ( data[index] >> 24 ); + cdata_local[1] = ( uint8_t ) ( data[index] >> 16 ); + cdata_local[2] = ( uint8_t ) ( data[index] >> 8 ); + cdata_local[3] = ( uint8_t ) ( data[index] >> 0 ); + } +} + +void lr11xx_regmem_fill_cbuffer_cdata_opcode_address_data( uint8_t* cbuffer, uint8_t* cdata, uint16_t opcode, + uint32_t address, const uint32_t* data, uint8_t data_length ) +{ + lr11xx_regmem_fill_cbuffer_opcode_address( cbuffer, opcode, address ); + lr11xx_regmem_fill_cdata( cdata, data, data_length ); +} + +void lr11xx_regmem_fill_out_buffer_from_raw_buffer( uint32_t* out_buffer, const uint8_t* raw_buffer, + uint8_t out_buffer_length ) +{ + for( uint8_t out_index = 0; out_index < out_buffer_length; out_index++ ) + { + const uint8_t* raw_buffer_local = &raw_buffer[out_index * 4]; + + out_buffer[out_index] = ( ( uint32_t ) raw_buffer_local[0] << 24 ) + + ( ( uint32_t ) raw_buffer_local[1] << 16 ) + ( ( uint32_t ) raw_buffer_local[2] << 8 ) + + ( ( uint32_t ) raw_buffer_local[3] << 0 ); + } +} + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/src/lr11xx_regmem.h b/src/lr11xx_regmem.h new file mode 100644 index 0000000..9a89eb9 --- /dev/null +++ b/src/lr11xx_regmem.h @@ -0,0 +1,202 @@ +/*! + * @file lr11xx_regmem.h + * + * @brief Register/memory driver definition for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR11XX_REGMEM_H +#define LR11XX_REGMEM_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include +#include "lr11xx_types.h" + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +/*! + * @brief Write words into register memory space of LR11XX. + * + * A word is 32-bit long. The writing operations write contiguously in register memory, starting at the address + * provided. + * + * @param [in] context Chip implementation context + * @param [in] address The register memory address to start writing operation + * @param [in] data The buffer of words to write into memory. Its size must be enough to contain length words. + * @param [in] length Number of words to write into memory + * + * @returns Operation status + * + * @see lr11xx_regmem_read_regmem32 + */ +lr11xx_status_t lr11xx_regmem_write_regmem32( const void* context, const uint32_t address, const uint32_t* buffer, + const uint8_t length ); + +/*! + * @brief Read words into register memory space of LR11XX. + * + * A word is 32-bit long. The reading operations read contiguously from register memory, starting at the address + * provided. + * + * @param [in] context Chip implementation context + * @param [in] address The register memory address to start reading operation + * @param [in] length Number of words to read from memory + * @param [out] buffer Pointer to a words array to be filled with content from memory. Its size must be enough to + * contain at least length words. + * + * @returns Operation status + * + * @see lr11xx_regmem_write_regmem32 + */ +lr11xx_status_t lr11xx_regmem_read_regmem32( const void* context, const uint32_t address, uint32_t* buffer, + const uint8_t length ); + +/*! + * @brief Write bytes into register memory space of LR11XX. + * + * A byte is 8-bit long. The writing operations write contiguously in register memory, starting at the address provided. + * + * @param [in] context Chip implementation context + * @param [in] address The register memory address to start writing operation + * @param [in] data The buffer of bytes to write into memory. Its size must be enough to contain length bytes + * @param [in] length Number of bytes to write into memory + * + * @returns Operation status + * + * @see lr11xx_regmem_read_mem8 + */ +lr11xx_status_t lr11xx_regmem_write_mem8( const void* context, const uint32_t address, const uint8_t* buffer, + const uint8_t length ); + +/*! + * @brief Read bytes into register memory space of LR11XX. + * + * A byte is 8-bit long. The reading operations read contiguously from register memory, starting at the address + * provided. + * + * @param [in] context Chip implementation context + * @param [in] address The register memory address to start reading operation + * @param [in] length Number of bytes to read from memory + * @param [in] buffer Pointer to a byte array to be filled with content from memory. Its size must be enough to contain + * at least length bytes + * + * @returns Operation status + * + * @see lr11xx_regmem_write_mem8 + */ +lr11xx_status_t lr11xx_regmem_read_mem8( const void* context, const uint32_t address, uint8_t* buffer, + const uint8_t length ); + +/*! + * @brief Write bytes into radio TX buffer memory space of LR11XX. + * + * @param [in] context Chip implementation context + * @param [in] data The buffer of bytes to write into radio buffer. Its size must be enough to contain length bytes + * @param [in] length Number of bytes to write into radio buffer + * + * @returns Operation status + * + * @see lr11xx_regmem_read_buffer8 + */ +lr11xx_status_t lr11xx_regmem_write_buffer8( const void* context, const uint8_t* buffer, const uint8_t length ); + +/*! + * @brief Read bytes from radio RX buffer memory space of LR11XX. + * + * @param [in] context Chip implementation context + * @param [in] offset Memory offset to start reading + * @param [in] length Number of bytes to read from radio buffer + * @param [in] data Pointer to a byte array to be filled with content from radio buffer. Its size must be enough to + * contain at least length bytes + * + * @returns Operation status + * + * @see lr11xx_regmem_write_buffer8 + */ +lr11xx_status_t lr11xx_regmem_read_buffer8( const void* context, uint8_t* buffer, const uint8_t offset, + const uint8_t length ); + +/*! + * @brief Clear radio RX buffer + * + * Set to 0x00 all content of the radio RX buffer + * + * @param [in] context Chip implementation context + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_regmem_clear_rxbuffer( const void* context ); + +/*! + * @brief Read-modify-write data at given register/memory address + * + * @param [in] context Chip implementation context + * @param [in] address The register memory address to be modified + * @param [in] mask The mask to be applied on read data + * @param [in] data The data to be written + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_regmem_write_regmem32_mask( const void* context, const uint32_t address, const uint32_t mask, + const uint32_t data ); + +#ifdef __cplusplus +} +#endif + +#endif // LR11XX_REGMEM_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/src/lr11xx_system.c b/src/lr11xx_system.c new file mode 100644 index 0000000..cc7f372 --- /dev/null +++ b/src/lr11xx_system.c @@ -0,0 +1,659 @@ +/*! + * @file lr11xx_system.c + * + * @brief System driver implementation for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include + +#include "lr11xx_system.h" +#include "lr11xx_hal.h" + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE MACROS----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE CONSTANTS ------------------------------------------------------- + */ + +#define LR11XX_SYSTEM_GET_VERSION_CMD_LENGTH ( 2 ) +#define LR11XX_SYSTEM_GET_ERRORS_CMD_LENGTH ( 2 ) +#define LR11XX_SYSTEM_CLEAR_ERRORS_CMD_LENGTH ( 2 ) +#define LR11XX_SYSTEM_CALIBRATE_CMD_LENGTH ( 2 + 1 ) +#define LR11XX_SYSTEM_SET_REGMODE_CMD_LENGTH ( 2 + 1 ) +#define LR11XX_SYSTEM_CALIBRATE_IMAGE_CMD_LENGTH ( 2 + 2 ) +#define LR11XX_SYSTEM_SET_DIO_AS_RF_SWITCH_CMD_LENGTH ( 2 + 8 ) +#define LR11XX_SYSTEM_SET_DIO_IRQ_PARAMS_CMD_LENGTH ( 2 + 8 ) +#define LR11XX_SYSTEM_CLEAR_IRQ_CMD_LENGTH ( 2 + 4 ) +#define LR11XX_SYSTEM_CFG_LFCLK_CMD_LENGTH ( 2 + 1 ) +#define LR11XX_SYSTEM_SET_TCXO_MODE_CMD_LENGTH ( 2 + 4 ) +#define LR11XX_SYSTEM_REBOOT_CMD_LENGTH ( 2 + 1 ) +#define LR11XX_SYSTEM_GET_VBAT_CMD_LENGTH ( 2 ) +#define LR11XX_SYSTEM_GET_TEMP_CMD_LENGTH ( 2 ) +#define LR11XX_SYSTEM_SET_SLEEP_CMD_LENGTH ( 2 + 5 ) +#define LR11XX_SYSTEM_SET_STANDBY_CMD_LENGTH ( 2 + 1 ) +#define LR11XX_SYSTEM_SET_FS_CMD_LENGTH ( 2 ) +#define LR11XX_SYSTEM_ERASE_INFOPAGE_CMD_LENGTH ( 2 + 1 ) +#define LR11XX_SYSTEM_WRITE_INFOPAGE_CMD_LENGTH ( 2 + 3 ) +#define LR11XX_SYSTEM_READ_INFOPAGE_CMD_LENGTH ( 2 + 4 ) +#define LR11XX_SYSTEM_READ_UID_CMD_LENGTH ( 2 ) +#define LR11XX_SYSTEM_READ_JOIN_EUI_CMD_LENGTH ( 2 ) +#define LR11XX_SYSTEM_READ_PIN_CMD_LENGTH ( 2 ) +#define LR11XX_SYSTEM_READ_PIN_CUSTOM_EUI_CMD_LENGTH ( LR11XX_SYSTEM_READ_PIN_CMD_LENGTH + 17 ) +#define LR11XX_SYSTEM_GET_RANDOM_CMD_LENGTH ( 2 ) +#define LR11XX_SYSTEM_ENABLE_SPI_CRC_CMD_LENGTH ( 3 ) +#define LR11XX_SYSTEM_DRIVE_DIO_IN_SLEEP_MODE_CMD_LENGTH ( 3 ) + +#define LR11XX_SYSTEM_GET_STATUS_DIRECT_READ_LENGTH ( 6 ) + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE TYPES ----------------------------------------------------------- + */ + +/*! + * @brief Operating codes for system-related operations + */ +enum +{ + LR11XX_SYSTEM_GET_STATUS_OC = 0x0100, + LR11XX_SYSTEM_GET_VERSION_OC = 0x0101, + LR11XX_SYSTEM_GET_ERRORS_OC = 0x010D, + LR11XX_SYSTEM_CLEAR_ERRORS_OC = 0x010E, + LR11XX_SYSTEM_CALIBRATE_OC = 0x010F, + LR11XX_SYSTEM_SET_REGMODE_OC = 0x0110, + LR11XX_SYSTEM_CALIBRATE_IMAGE_OC = 0x0111, + LR11XX_SYSTEM_SET_DIO_AS_RF_SWITCH_OC = 0x0112, + LR11XX_SYSTEM_SET_DIOIRQPARAMS_OC = 0x0113, + LR11XX_SYSTEM_CLEAR_IRQ_OC = 0x0114, + LR11XX_SYSTEM_CFG_LFCLK_OC = 0x0116, + LR11XX_SYSTEM_SET_TCXO_MODE_OC = 0x0117, + LR11XX_SYSTEM_REBOOT_OC = 0x0118, + LR11XX_SYSTEM_GET_VBAT_OC = 0x0119, + LR11XX_SYSTEM_GET_TEMP_OC = 0x011A, + LR11XX_SYSTEM_SET_SLEEP_OC = 0x011B, + LR11XX_SYSTEM_SET_STANDBY_OC = 0x011C, + LR11XX_SYSTEM_SET_FS_OC = 0x011D, + LR11XX_SYSTEM_GET_RANDOM_OC = 0x0120, + LR11XX_SYSTEM_ERASE_INFOPAGE_OC = 0x0121, + LR11XX_SYSTEM_WRITE_INFOPAGE_OC = 0x0122, + LR11XX_SYSTEM_READ_INFOPAGE_OC = 0x0123, + LR11XX_SYSTEM_READ_UID_OC = 0x0125, + LR11XX_SYSTEM_READ_JOIN_EUI_OC = 0x0126, + LR11XX_SYSTEM_READ_PIN_OC = 0x0127, + LR11XX_SYSTEM_ENABLE_SPI_CRC_OC = 0x0128, + LR11XX_SYSTEM_DRIVE_DIO_IN_SLEEP_MODE_OC = 0x012A, +}; + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE VARIABLES ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DECLARATION ------------------------------------------- + */ + +/*! + * @brief Fill stat1 structure with data from stat1_byte + * + * @param [in] stat1_byte stat1 byte + * @param [out] stat1 stat1 structure + */ +static void lr11xx_system_convert_stat1_byte_to_enum( uint8_t stat1_byte, lr11xx_system_stat1_t* stat1 ); + +/*! + * @brief Fill stat2 structure with data from stat2_byte + * + * @param [in] stat2_byte stat2 byte + * @param [out] stat2 stat2 structure + */ +static void lr11xx_system_convert_stat2_byte_to_enum( uint8_t stat2_byte, lr11xx_system_stat2_t* stat2 ); + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS DEFINITION --------------------------------------------- + */ + +lr11xx_status_t lr11xx_system_reset( const void* context ) +{ + return ( lr11xx_status_t ) lr11xx_hal_reset( context ); +} + +lr11xx_status_t lr11xx_system_get_status( const void* context, lr11xx_system_stat1_t* stat1, + lr11xx_system_stat2_t* stat2, lr11xx_system_irq_mask_t* irq_status ) +{ + uint8_t data[LR11XX_SYSTEM_GET_STATUS_DIRECT_READ_LENGTH]; + lr11xx_status_t status; + + status = ( lr11xx_status_t ) lr11xx_hal_direct_read( context, data, LR11XX_SYSTEM_GET_STATUS_DIRECT_READ_LENGTH ); + + if( status == LR11XX_STATUS_OK ) + { + lr11xx_system_convert_stat1_byte_to_enum( data[0], stat1 ); + lr11xx_system_convert_stat2_byte_to_enum( data[1], stat2 ); + if( irq_status != NULL ) + { + *irq_status = ( ( lr11xx_system_irq_mask_t ) data[2] << 24 ) + + ( ( lr11xx_system_irq_mask_t ) data[3] << 16 ) + + ( ( lr11xx_system_irq_mask_t ) data[4] << 8 ) + ( ( lr11xx_system_irq_mask_t ) data[5] << 0 ); + } + } + + return status; +} + +lr11xx_status_t lr11xx_system_clear_reset_status_info( const void* context ) +{ + uint8_t cbuffer[2] = { + ( uint8_t ) ( LR11XX_SYSTEM_GET_STATUS_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_GET_STATUS_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, sizeof( cbuffer ), 0, 0 ); +} + +lr11xx_status_t lr11xx_system_get_version( const void* context, lr11xx_system_version_t* version ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_GET_VERSION_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_GET_VERSION_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_GET_VERSION_OC >> 0 ), + }; + uint8_t rbuffer[LR11XX_SYSTEM_VERSION_LENGTH] = { 0x00 }; + + const lr11xx_status_t status = ( lr11xx_status_t ) lr11xx_hal_read( + context, cbuffer, LR11XX_SYSTEM_GET_VERSION_CMD_LENGTH, rbuffer, LR11XX_SYSTEM_VERSION_LENGTH ); + + if( status == LR11XX_STATUS_OK ) + { + version->hw = rbuffer[0]; + version->type = rbuffer[1]; + version->fw = ( ( uint16_t ) rbuffer[2] << 8 ) + ( uint16_t ) rbuffer[3]; + } + + return status; +} + +lr11xx_status_t lr11xx_system_get_errors( const void* context, lr11xx_system_errors_t* errors ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_GET_ERRORS_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_GET_ERRORS_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_GET_ERRORS_OC >> 0 ), + }; + uint8_t rbuffer[sizeof( errors )] = { 0x00 }; + + const lr11xx_status_t status = ( lr11xx_status_t ) lr11xx_hal_read( + context, cbuffer, LR11XX_SYSTEM_GET_ERRORS_CMD_LENGTH, rbuffer, sizeof( *errors ) ); + + if( status == LR11XX_STATUS_OK ) + { + *errors = ( ( uint16_t ) rbuffer[0] << 8 ) + ( uint16_t ) rbuffer[1]; + } + + return status; +} + +lr11xx_status_t lr11xx_system_clear_errors( const void* context ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_CLEAR_ERRORS_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_CLEAR_ERRORS_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_CLEAR_ERRORS_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_SYSTEM_CLEAR_ERRORS_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_system_calibrate( const void* context, const uint8_t calib_param ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_CALIBRATE_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_CALIBRATE_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_CALIBRATE_OC >> 0 ), + calib_param, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_SYSTEM_CALIBRATE_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_system_set_reg_mode( const void* context, const lr11xx_system_reg_mode_t reg_mode ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_SET_REGMODE_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_SET_REGMODE_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_SET_REGMODE_OC >> 0 ), + ( uint8_t ) reg_mode, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_SYSTEM_SET_REGMODE_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_system_calibrate_image( const void* context, const uint8_t freq1, const uint8_t freq2 ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_CALIBRATE_IMAGE_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_CALIBRATE_IMAGE_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_CALIBRATE_IMAGE_OC >> 0 ), + freq1, + freq2, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_SYSTEM_CALIBRATE_IMAGE_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_system_calibrate_image_in_mhz( const void* context, const uint16_t freq1_in_mhz, + const uint16_t freq2_in_mhz ) +{ + // Perform a floor() to get a value for freq1 corresponding to a frequency lower than or equal to freq1_in_mhz + const uint8_t freq1 = freq1_in_mhz / LR11XX_SYSTEM_IMAGE_CALIBRATION_STEP_IN_MHZ; + + // Perform a ceil() to get a value for freq2 corresponding to a frequency higher than or equal to freq2_in_mhz + const uint8_t freq2 = ( freq2_in_mhz + LR11XX_SYSTEM_IMAGE_CALIBRATION_STEP_IN_MHZ - 1 ) / + LR11XX_SYSTEM_IMAGE_CALIBRATION_STEP_IN_MHZ; + + return lr11xx_system_calibrate_image( context, freq1, freq2 ); +} + +lr11xx_status_t lr11xx_system_set_dio_as_rf_switch( const void* context, + const lr11xx_system_rfswitch_cfg_t* rf_switch_cfg ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_SET_DIO_AS_RF_SWITCH_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_SET_DIO_AS_RF_SWITCH_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_SET_DIO_AS_RF_SWITCH_OC >> 0 ), + rf_switch_cfg->enable, + rf_switch_cfg->standby, + rf_switch_cfg->rx, + rf_switch_cfg->tx, + rf_switch_cfg->tx_hp, + rf_switch_cfg->tx_hf, + rf_switch_cfg->gnss, + rf_switch_cfg->wifi, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_SYSTEM_SET_DIO_AS_RF_SWITCH_CMD_LENGTH, 0, + 0 ); +} + +lr11xx_status_t lr11xx_system_set_dio_irq_params( const void* context, + const lr11xx_system_irq_mask_t irqs_to_enable_dio1, + const lr11xx_system_irq_mask_t irqs_to_enable_dio2 ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_SET_DIO_IRQ_PARAMS_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_SET_DIOIRQPARAMS_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_SET_DIOIRQPARAMS_OC >> 0 ), + ( uint8_t ) ( irqs_to_enable_dio1 >> 24 ), + ( uint8_t ) ( irqs_to_enable_dio1 >> 16 ), + ( uint8_t ) ( irqs_to_enable_dio1 >> 8 ), + ( uint8_t ) ( irqs_to_enable_dio1 >> 0 ), + ( uint8_t ) ( irqs_to_enable_dio2 >> 24 ), + ( uint8_t ) ( irqs_to_enable_dio2 >> 16 ), + ( uint8_t ) ( irqs_to_enable_dio2 >> 8 ), + ( uint8_t ) ( irqs_to_enable_dio2 >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_SYSTEM_SET_DIO_IRQ_PARAMS_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_system_clear_irq_status( const void* context, const lr11xx_system_irq_mask_t irqs_to_clear ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_CLEAR_IRQ_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_CLEAR_IRQ_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_CLEAR_IRQ_OC >> 0 ), + ( uint8_t ) ( irqs_to_clear >> 24 ), + ( uint8_t ) ( irqs_to_clear >> 16 ), + ( uint8_t ) ( irqs_to_clear >> 8 ), + ( uint8_t ) ( irqs_to_clear >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_SYSTEM_CLEAR_IRQ_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_system_get_and_clear_irq_status( const void* context, lr11xx_system_irq_mask_t* irq ) +{ + lr11xx_system_irq_mask_t lr11xx_irq_mask = LR11XX_SYSTEM_IRQ_NONE; + + lr11xx_status_t status = lr11xx_system_get_irq_status( context, &lr11xx_irq_mask ); + + if( ( status == LR11XX_STATUS_OK ) && ( lr11xx_irq_mask != 0 ) ) + { + status = lr11xx_system_clear_irq_status( context, lr11xx_irq_mask ); + } + if( ( status == LR11XX_STATUS_OK ) && ( irq != NULL ) ) + { + *irq = lr11xx_irq_mask; + } + + return status; +} + +lr11xx_status_t lr11xx_system_cfg_lfclk( const void* context, const lr11xx_system_lfclk_cfg_t lfclock_cfg, + const bool wait_for_32k_ready ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_CFG_LFCLK_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_CFG_LFCLK_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_CFG_LFCLK_OC >> 0 ), + ( uint8_t ) ( lfclock_cfg | ( wait_for_32k_ready << 2 ) ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_SYSTEM_CFG_LFCLK_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_system_set_tcxo_mode( const void* context, const lr11xx_system_tcxo_supply_voltage_t tune, + const uint32_t timeout ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_SET_TCXO_MODE_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_SET_TCXO_MODE_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_SET_TCXO_MODE_OC >> 0 ), + ( uint8_t ) tune, + ( uint8_t ) ( timeout >> 16 ), + ( uint8_t ) ( timeout >> 8 ), + ( uint8_t ) ( timeout >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_SYSTEM_SET_TCXO_MODE_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_system_reboot( const void* context, const bool stay_in_bootloader ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_REBOOT_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_REBOOT_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_REBOOT_OC >> 0 ), + ( stay_in_bootloader == true ) ? 0x03 : 0x00, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_SYSTEM_REBOOT_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_system_get_vbat( const void* context, uint8_t* vbat ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_GET_VBAT_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_GET_VBAT_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_GET_VBAT_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_SYSTEM_GET_VBAT_CMD_LENGTH, vbat, + sizeof( *vbat ) ); +} + +lr11xx_status_t lr11xx_system_get_temp( const void* context, uint16_t* temp ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_GET_TEMP_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_GET_TEMP_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_GET_TEMP_OC >> 0 ), + }; + uint8_t rbuffer[sizeof( uint16_t )] = { 0x00 }; + + const lr11xx_status_t status = ( lr11xx_status_t ) lr11xx_hal_read( + context, cbuffer, LR11XX_SYSTEM_GET_TEMP_CMD_LENGTH, rbuffer, sizeof( uint16_t ) ); + + if( status == LR11XX_STATUS_OK ) + { + *temp = ( ( uint16_t ) rbuffer[0] << 8 ) + ( uint16_t ) rbuffer[1]; + } + + return status; +} + +lr11xx_status_t lr11xx_system_set_sleep( const void* context, const lr11xx_system_sleep_cfg_t sleep_cfg, + const uint32_t sleep_time ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_SET_SLEEP_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_SET_SLEEP_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_SET_SLEEP_OC >> 0 ), + ( sleep_cfg.is_rtc_timeout << 1 ) + sleep_cfg.is_warm_start, + ( uint8_t ) ( sleep_time >> 24 ), + ( uint8_t ) ( sleep_time >> 16 ), + ( uint8_t ) ( sleep_time >> 8 ), + ( uint8_t ) ( sleep_time >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_SYSTEM_SET_SLEEP_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_system_set_standby( const void* context, const lr11xx_system_standby_cfg_t standby_cfg ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_SET_STANDBY_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_SET_STANDBY_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_SET_STANDBY_OC >> 0 ), + ( uint8_t ) standby_cfg, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_SYSTEM_SET_STANDBY_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_system_wakeup( const void* context ) +{ + return ( lr11xx_status_t ) lr11xx_hal_wakeup( context ); +} + +lr11xx_status_t lr11xx_system_set_fs( const void* context ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_SET_FS_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_SET_FS_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_SET_FS_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_SYSTEM_SET_FS_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_system_erase_infopage( const void* context, const lr11xx_system_infopage_id_t infopage_id ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_ERASE_INFOPAGE_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_ERASE_INFOPAGE_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_ERASE_INFOPAGE_OC >> 0 ), + ( uint8_t ) infopage_id, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_SYSTEM_ERASE_INFOPAGE_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_system_write_infopage( const void* context, const lr11xx_system_infopage_id_t infopage_id, + const uint16_t address, const uint32_t* data, const uint8_t length ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_WRITE_INFOPAGE_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_WRITE_INFOPAGE_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_WRITE_INFOPAGE_OC >> 0 ), + ( uint8_t ) infopage_id, + ( uint8_t ) ( address >> 8 ), + ( uint8_t ) ( address >> 0 ), + }; + uint8_t cdata[256]; + + for( uint16_t index = 0; index < length; index++ ) + { + uint8_t* cdata_local = &cdata[index * sizeof( uint32_t )]; + + cdata_local[0] = ( uint8_t ) ( data[index] >> 24 ); + cdata_local[1] = ( uint8_t ) ( data[index] >> 16 ); + cdata_local[2] = ( uint8_t ) ( data[index] >> 8 ); + cdata_local[3] = ( uint8_t ) ( data[index] >> 0 ); + } + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_SYSTEM_WRITE_INFOPAGE_CMD_LENGTH, cdata, + length * sizeof( uint32_t ) ); +} + +lr11xx_status_t lr11xx_system_read_infopage( const void* context, const lr11xx_system_infopage_id_t infopage_id, + const uint16_t address, uint32_t* data, const uint8_t length ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_READ_INFOPAGE_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_READ_INFOPAGE_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_READ_INFOPAGE_OC >> 0 ), + ( uint8_t ) infopage_id, + ( uint8_t ) ( address >> 8 ), + ( uint8_t ) ( address >> 0 ), + length, + }; + + const lr11xx_status_t status = ( lr11xx_status_t ) lr11xx_hal_read( + context, cbuffer, LR11XX_SYSTEM_READ_INFOPAGE_CMD_LENGTH, ( uint8_t* ) data, length * sizeof( *data ) ); + + if( status == LR11XX_STATUS_OK ) + { + for( uint8_t index = 0; index < length; index++ ) + { + uint8_t* buffer_local = ( uint8_t* ) &data[index]; + + data[index] = ( ( uint32_t ) buffer_local[0] << 24 ) + ( ( uint32_t ) buffer_local[1] << 16 ) + + ( ( uint32_t ) buffer_local[2] << 8 ) + ( ( uint32_t ) buffer_local[3] << 0 ); + } + } + + return status; +} + +lr11xx_status_t lr11xx_system_read_uid( const void* context, lr11xx_system_uid_t unique_identifier ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_READ_UID_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_READ_UID_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_READ_UID_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_SYSTEM_READ_UID_CMD_LENGTH, unique_identifier, + LR11XX_SYSTEM_UID_LENGTH ); +} + +lr11xx_status_t lr11xx_system_read_join_eui( const void* context, lr11xx_system_join_eui_t join_eui ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_READ_JOIN_EUI_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_READ_JOIN_EUI_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_READ_JOIN_EUI_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_SYSTEM_READ_JOIN_EUI_CMD_LENGTH, join_eui, + LR11XX_SYSTEM_JOIN_EUI_LENGTH ); +} + +lr11xx_status_t lr11xx_system_read_pin( const void* context, lr11xx_system_pin_t pin ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_READ_PIN_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_READ_PIN_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_READ_PIN_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_SYSTEM_READ_PIN_CMD_LENGTH, pin, + LR11XX_SYSTEM_PIN_LENGTH ); +} + +lr11xx_status_t lr11xx_system_read_pin_custom_eui( const void* context, lr11xx_system_uid_t device_eui, + lr11xx_system_join_eui_t join_eui, uint8_t rfu, + lr11xx_system_pin_t pin ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_READ_PIN_CUSTOM_EUI_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_READ_PIN_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_READ_PIN_OC >> 0 ), + device_eui[0], + device_eui[1], + device_eui[2], + device_eui[3], + device_eui[4], + device_eui[5], + device_eui[6], + device_eui[7], + join_eui[0], + join_eui[1], + join_eui[2], + join_eui[3], + join_eui[4], + join_eui[5], + join_eui[6], + join_eui[7], + rfu, + }; + + return ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_SYSTEM_READ_PIN_CUSTOM_EUI_CMD_LENGTH, pin, + LR11XX_SYSTEM_PIN_LENGTH ); +} + +lr11xx_status_t lr11xx_system_get_random_number( const void* context, uint32_t* random_number ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_GET_RANDOM_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_GET_RANDOM_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_GET_RANDOM_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_SYSTEM_GET_RANDOM_CMD_LENGTH, + ( uint8_t* ) random_number, sizeof( uint32_t ) ); +} + +lr11xx_status_t lr11xx_system_enable_spi_crc( const void* context, bool enable_crc ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_ENABLE_SPI_CRC_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_ENABLE_SPI_CRC_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_ENABLE_SPI_CRC_OC >> 0 ), + ( enable_crc == true ) ? 0x01 : 0x00, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_SYSTEM_ENABLE_SPI_CRC_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_system_drive_dio_in_sleep_mode( const void* context, bool enable_drive ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_DRIVE_DIO_IN_SLEEP_MODE_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_DRIVE_DIO_IN_SLEEP_MODE_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_DRIVE_DIO_IN_SLEEP_MODE_OC >> 0 ), + ( enable_drive == true ) ? 0x01 : 0x00, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_SYSTEM_DRIVE_DIO_IN_SLEEP_MODE_CMD_LENGTH, 0, + 0 ); +} + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DEFINITION -------------------------------------------- + */ + +static void lr11xx_system_convert_stat1_byte_to_enum( uint8_t stat1_byte, lr11xx_system_stat1_t* stat1 ) +{ + if( stat1 != NULL ) + { + stat1->is_interrupt_active = ( ( stat1_byte & 0x01 ) != 0 ) ? true : false; + stat1->command_status = ( lr11xx_system_command_status_t ) ( stat1_byte >> 1 ); + } +} + +static void lr11xx_system_convert_stat2_byte_to_enum( uint8_t stat2_byte, lr11xx_system_stat2_t* stat2 ) +{ + if( stat2 != NULL ) + { + stat2->is_running_from_flash = ( ( stat2_byte & 0x01 ) != 0 ) ? true : false; + stat2->chip_mode = ( lr11xx_system_chip_modes_t ) ( ( stat2_byte & 0x0F ) >> 1 ); + stat2->reset_status = ( lr11xx_system_reset_status_t ) ( ( stat2_byte & 0xF0 ) >> 4 ); + } +} + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/src/lr11xx_system.h b/src/lr11xx_system.h new file mode 100644 index 0000000..0142689 --- /dev/null +++ b/src/lr11xx_system.h @@ -0,0 +1,585 @@ +/*! + * @file lr11xx_system.h + * + * @brief System driver definition for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR11XX_SYSTEM_H +#define LR11XX_SYSTEM_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include "lr11xx_system_types.h" +#include "lr11xx_types.h" + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/*! + * @brief Frequency step in MHz used to compute the image calibration parameter + * + * @see lr11xx_system_calibrate_image_in_mhz + */ +#define LR11XX_SYSTEM_IMAGE_CALIBRATION_STEP_IN_MHZ 4 + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +/*! + * @brief Reset the radio + * + * @param [in] context Chip implementation context + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_system_reset( const void* context ); + +/** + * @brief Wake the radio up from sleep mode. + * + * @param [in] context Chip implementation context. + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_system_wakeup( const void* context ); + +/*! + * @brief Return stat1, stat2, and irq_status + * + * @param [in] context Chip implementation context + * @param [out] stat1 Pointer to a variable for holding stat1. Can be NULL. + * @param [out] stat2 Pointer to a variable for holding stat2. Can be NULL. + * @param [out] irq_status Pointer to a variable for holding irq_status. Can be NULL. + * + * @returns Operation status + * + * @remark To simplify system integration, this function does not actually execute the GetStatus command, which would + * require bidirectional SPI communication. It obtains the stat1, stat2, and irq_status values by performing an ordinary + * SPI read (which is required to send null/NOP bytes on the MOSI line). This is possible since the LR11XX returns these + * values automatically whenever a read that does not directly follow a response-carrying command is performed. + * Unlike with the GetStatus command, however, the reset status information is NOT cleared by this command. The function + * @ref lr11xx_system_clear_reset_status_info may be used for this purpose when necessary. + */ +lr11xx_status_t lr11xx_system_get_status( const void* context, lr11xx_system_stat1_t* stat1, + lr11xx_system_stat2_t* stat2, lr11xx_system_irq_mask_t* irq_status ); + +/*! + * @brief Clear the reset status information stored in stat2 + * + * @param [in] context Chip implementation context + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_system_clear_reset_status_info( const void* context ); + +/*! + * @brief Return irq_status + * + * @param [in] context Chip implementation context + * @param [out] irq_status irq_status status variable + * + * @returns Operation status + */ +static inline lr11xx_status_t lr11xx_system_get_irq_status( const void* context, lr11xx_system_irq_mask_t* irq_status ) +{ + return lr11xx_system_get_status( context, 0, 0, irq_status ); +} + +/*! + * @brief Return the version of the system (hardware and software) + * + * @param [in] context Chip implementation context + * @param [out] version Pointer to the structure holding the system version + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_system_get_version( const void* context, lr11xx_system_version_t* version ); + +/*! + * @brief Return the system errors + * + * Errors may be fixed following: + * - calibration error can be fixed by attempting another RC calibration; + * - XOsc related errors may be due to hardware problems, can be fixed by reset; + * - PLL lock related errors can be due to not-locked PLL, or by attempting to use an out-of-band frequency, can be + * fixed by executing a PLL calibration, or by using other frequencies. + * + * @param [in] context Chip implementation context + * @param [out] errors Pointer to a value holding error flags + * + * @returns Operation status + * + * @see lr11xx_system_calibrate, lr11xx_system_calibrate_image, lr11xx_system_clear_errors + */ +lr11xx_status_t lr11xx_system_get_errors( const void* context, uint16_t* errors ); + +/*! + * @brief Clear all error flags pending. + * + * This function cannot be used to clear flags individually. + * + * @param [in] context Chip implementation context + * + * @returns Operation status + * + * @see lr11xx_system_get_errors + */ +lr11xx_status_t lr11xx_system_clear_errors( const void* context ); + +/*! + * @brief lr11xx_system_calibrate the requested blocks + * + * This function can be called in any mode of the chip. + * + * The chip will return to standby RC mode on exit. Potential calibration issues can be read out with + * lr11xx_system_get_errors command. + * + * @param [in] context Chip implementation context + * @param [in] calib_param Structure holding the reference to blocks to be calibrated + * + * @returns Operation status + * + * @see lr11xx_system_get_errors + */ +lr11xx_status_t lr11xx_system_calibrate( const void* context, const uint8_t calib_param ); + +/*! + * @brief Configure the regulator mode to be used in specific modes + * + * This function shall only be called in standby RC mode. + * + * The reg_mode parameter defines if the DC-DC converter is switched on in the following modes: STANDBY XOSC, FS, RX, TX + * and RX_CAPTURE. + * + * @param [in] context Chip implementation context + * @param [in] reg_mode Regulator mode configuration + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_system_set_reg_mode( const void* context, const lr11xx_system_reg_mode_t reg_mode ); + +/*! + * @brief Launch an image calibration valid for all frequencies inside an interval, in steps + * + * This function can be called in any mode of the chip. + * + * The chip will return to standby RC mode on exit. Potential calibration issues can be read out with + * lr11xx_system_get_errors command. + * + * The frequencies given in parameters are defined in 4MHz step (Eg. 900MHz corresponds to 0xE1). If freq1 = freq2, only + * one calibration is performed. + * + * @param [in] context Chip implementation context + * @param [in] freq1 Image calibration interval lower bound, in steps + * @param [in] freq2 Image calibration interval upper bound, in steps + * + * @remark freq1 must be less than or equal to freq2 + * + * @returns Operation status + * + * @see lr11xx_system_get_errors + */ +lr11xx_status_t lr11xx_system_calibrate_image( const void* context, const uint8_t freq1, const uint8_t freq2 ); + +/*! + * @brief Launch an image calibration valid for all frequencies inside an interval, in MHz + * + * @remark This function relies on @ref lr11xx_system_calibrate_image + * + * @param [in] context Chip implementation context + * @param [in] freq1_in_mhz Image calibration interval lower bound, in MHz + * @param [in] freq2_in_mhz Image calibration interval upper bound, in MHz + * + * @remark freq1 must be less than or equal to freq2 + * + * @returns Operation status + * + * @see lr11xx_system_calibrate_image + */ +lr11xx_status_t lr11xx_system_calibrate_image_in_mhz( const void* context, const uint16_t freq1_in_mhz, + const uint16_t freq2_in_mhz ); + +/*! + * @brief Set the RF switch configurations for each RF setup + * + * This function shall only be called in standby RC mode. + * + * By default, no DIO is used to control a RF switch. All DIOs are set in High-Z mode. + * + * @param [in] context Chip implementation context + * @param [in] rf_switch_cfg Pointer to a structure that holds the switches configuration + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_system_set_dio_as_rf_switch( const void* context, + const lr11xx_system_rfswitch_cfg_t* rf_switch_cfg ); + +/*! + * @brief Set which interrupt signals are redirected to the dedicated DIO pin + * + * By default, no interrupt signal is redirected. + * + * The dedicated DIO pin will remain asserted until all redirected interrupt signals are cleared with a call to + * lr11xx_system_clear_irq_status. + * + * @param [in] context Chip implementation context + * @param [in] irqs_to_enable_dio1 Variable that holds the interrupt mask for dio1 + * @param [in] irqs_to_enable_dio2 Variable that holds the interrupt mask for dio2 + * + * @returns Operation status + * + * @see lr11xx_system_clear_irq_status + */ +lr11xx_status_t lr11xx_system_set_dio_irq_params( const void* context, + const lr11xx_system_irq_mask_t irqs_to_enable_dio1, + const lr11xx_system_irq_mask_t irqs_to_enable_dio2 ); + +/*! + * @brief Clear requested bits in the internal pending interrupt register + * + * @param [in] context Chip implementation context + * @param [in] irqs_to_clear Variable that holds the interrupts to be cleared + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_system_clear_irq_status( const void* context, const lr11xx_system_irq_mask_t irqs_to_clear ); + +/** + * @brief This helper function clears any radio irq status flags that are set and returns the flags that were cleared. + * + * @param [in] context Chip implementation context. + * @param [out] irq Pointer to a variable for holding the system interrupt status. Can be NULL. + * + * @returns Operation status + * + * @see lr11xx_system_get_irq_status, lr11xx_system_clear_irq_status + */ +lr11xx_status_t lr11xx_system_get_and_clear_irq_status( const void* context, lr11xx_system_irq_mask_t* irq ); + +/*! + * @brief Defines which clock is used as Low Frequency (LF) clock + * + * @param [in] context Chip implementation context + * @param [in] lfclock_cfg Low frequency clock configuration + * @param [in] wait_for_32k_ready Tells the radio if it has to check if 32k source is ready before driving busy low + * + * @returns Operation status + * + * @see lr11xx_system_calibrate, lr11xx_system_calibrate_image + */ +lr11xx_status_t lr11xx_system_cfg_lfclk( const void* context, const lr11xx_system_lfclk_cfg_t lfclock_cfg, + const bool wait_for_32k_ready ); + +/*! + * @brief Enable and configure TCXO supply voltage and detection timeout + * + * This function shall only be called in standby RC mode. + * + * The timeout parameter is the maximum time the firmware waits for the TCXO to be ready. The timeout duration is given + * by: \f$ timeout\_duration\_us = timeout \times 30.52 \f$ + * + * The TCXO mode can be disabled by setting timeout parameter to 0. + * + * @param [in] context Chip implementation context + * @param [in] tune Supply voltage value + * @param [in] timeout Gating time before which the radio starts its Rx / Tx operation + * + * @returns Operation status + * + * @see lr11xx_system_calibrate, lr11xx_system_calibrate_image + */ +lr11xx_status_t lr11xx_system_set_tcxo_mode( const void* context, const lr11xx_system_tcxo_supply_voltage_t tune, + const uint32_t timeout ); + +/*! + * @brief Software reset of the chip. + * + * This function should be used to reboot the chip in a specified mode. Rebooting in flash mode presumes that the + * content in flash memory is not corrupted (i.e. the integrity check performed by the bootloader before executing the + * first instruction in flash is OK). + * + * @param [in] context Chip implementation context + * @param [in] stay_in_bootloader Selector to stay in bootloader or execute flash code after reboot. If true, the + * bootloader will not execute the flash code but activate SPI interface to allow firmware upgrade + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_system_reboot( const void* context, const bool stay_in_bootloader ); + +/*! + * @brief Returns the value of Vbat + * + * Vbat value (in V) is a function of Vana (typ. 1.35V) using the following + * formula: \f$ Vbat_{V} = (5 \times \frac{Vbat}{255} - 1) \times Vana \f$ + * + * @param [in] context Chip implementation context + * @param [out] vbat A pointer to the Vbat value + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_system_get_vbat( const void* context, uint8_t* vbat ); + +/*! + * @brief Returns the value of Temp + * + * The temperature (in °C) is a function of Vana (typ. 1.35V), Vbe25 (Vbe voltage @ 25°C, typ. 0.7295V) and VbeSlope + * (typ. -1.7mV/°C) using the following formula: + * \f$ Temperature_{°C} = (\frac{Temp(10:0)}{2047} \times Vana - Vbe25) \times \frac{1000}{VbeSlope} + 25 \f$ + * + * @remark If a TCXO is used, make sure to configure it with @ref lr11xx_system_set_tcxo_mode before calling this + * function + * + * @param [in] context Chip implementation context + * @param [out] temp A pointer to the Temp value + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_system_get_temp( const void* context, uint16_t* temp ); + +/*! + * @brief Set the device into Sleep or Deep Sleep Mode + * + * The sleep_cfg parameter defines in which sleep mode the device is put and if it wakes up after a given time on the + * RTC event. + * + * The sleep_time parameter is taken into account only when RtcTimeout = 1. It sets the sleep time in number of clock + * cycles: \f$ sleep\_time\_ms = sleep_time \times \frac{1}{32.768} \f$ + * + * @param [in] context Chip implementation context + * @param [in] sleep_cfg Sleep mode configuration + * @param [in] sleep_time Value of the RTC timeout (if RtcTimeout = 1) + * + * @returns Operation status + * + * @see lr11xx_system_set_standby, lr11xx_system_set_fs + */ +lr11xx_status_t lr11xx_system_set_sleep( const void* context, const lr11xx_system_sleep_cfg_t sleep_cfg, + const uint32_t sleep_time ); + +/*! + * @brief Set the device into the requested Standby mode + * + * @param [in] context Chip implementation context + * @param [in] standby_cfg Stand by mode configuration (RC or XOSC) + * + * @returns Operation status + * + * @see lr11xx_system_set_sleep, lr11xx_system_set_fs + */ +lr11xx_status_t lr11xx_system_set_standby( const void* context, const lr11xx_system_standby_cfg_t standby_cfg ); + +/*! + * @brief Set the device into Frequency Synthesis (FS) mode + * + * @param [in] context Chip implementation context + * + * @returns Operation status + * + * @see lr11xx_system_set_standby, lr11xx_system_set_sleep + */ +lr11xx_status_t lr11xx_system_set_fs( const void* context ); + +/*! + * @brief Erase an info page + * + * @param [in] context Chip implementation context + * @param [in] info_page_id Info page to be erased. Only LR11XX_SYSTEM_INFOPAGE_1 is allowed. + * + * @returns Operation status + * + * @see lr11xx_system_write_infopage, lr11xx_system_read_infopage + */ +lr11xx_status_t lr11xx_system_erase_infopage( const void* context, const lr11xx_system_infopage_id_t info_page_id ); + +/*! + * @brief Write data in an info page + * + * @param [in] context Chip implementation context + * @param [in] info_page_id Info page where data are written. Only LR11XX_SYSTEM_INFOPAGE_1 is allowed. + * @param [in] address Address within the info page (aligned on 32-bit data) + * @param [in] data Pointer to the data to write (data buffer shall be - at least - length words long) + * @param [in] length Number of 32-bit data to write (maximum value is 64) + * + * @returns Operation status + * + * @see lr11xx_system_erase_infopage, lr11xx_system_read_infopage + */ +lr11xx_status_t lr11xx_system_write_infopage( const void* context, const lr11xx_system_infopage_id_t info_page_id, + const uint16_t address, const uint32_t* data, const uint8_t length ); + +/*! + * @brief Read data from an info page + * + * It is possible to cross from page 0 to 1 if (address + length >= 512) + * + * @param [in] context Chip implementation context + * @param [in] info_page_id Info page where data are read + * @param [in] address Address within the info page (aligned on 32-bit data) + * @param [out] data Pointer to the data to read (data buffer shall be - at least - length words long) + * @param [in] length Number of 32-bit data to read (maximum value is 64) + * + * @returns Operation status + * + * @see lr11xx_system_erase_infopage, lr11xx_system_write_infopage + */ +lr11xx_status_t lr11xx_system_read_infopage( const void* context, const lr11xx_system_infopage_id_t info_page_id, + const uint16_t address, uint32_t* data, const uint8_t length ); + +/*! + * @brief Read and return the Unique Identifier of the LR11XX + * + * @param [in] context Chip implementation context + * @param [out] unique_identifier The buffer to be filled with the Unique Identifier of the LR11XX. It is up to the + * application to ensure unique_identifier is long enough to hold the unique identifier + * + * @returns Operation status + * + * @see LR11XX_SYSTEM_UID_LENGTH + */ +lr11xx_status_t lr11xx_system_read_uid( const void* context, lr11xx_system_uid_t unique_identifier ); + +/*! + * @brief Read and return the Join EUI of the LR11XX + * + * @param [in] context Chip implementation context + * @param [out] join_eui The buffer to be filled with Join EUI of the LR11XX. It is up to the application to ensure + * join_eui is long enough to hold the join EUI + * + * @returns Operation status + * + * @see LR11XX_SYSTEM_JOIN_EUI_LENGTH + */ +lr11xx_status_t lr11xx_system_read_join_eui( const void* context, lr11xx_system_join_eui_t join_eui ); + +/*! + * @brief Compute and return the PIN of the LR11XX based on factory default EUIs + * + * @remark Calling this command also triggers a derivation of network and application keys (available as @ref + * LR11XX_CRYPTO_KEYS_IDX_NWK_KEY and @ref LR11XX_CRYPTO_KEYS_IDX_APP_KEY) based on factory default EUIs + * + * @param [in] context Chip implementation context + * @param [out] pin The buffer to be filled with PIN of the LR11XX. It is up to the application to ensure pin is long + * enough to hold the PIN + * + * @returns Operation status + * + * @see LR11XX_SYSTEM_PIN_LENGTH + */ +lr11xx_status_t lr11xx_system_read_pin( const void* context, lr11xx_system_pin_t pin ); + +/*! + * @brief Compute and return the PIN of the LR11XX based on EUIs provided as parameters + * + * @remark Calling this command also triggers a derivation of network and application keys (available as @ref + * LR11XX_CRYPTO_KEYS_IDX_NWK_KEY and @ref LR11XX_CRYPTO_KEYS_IDX_APP_KEY) based on EUIs provided as parameters + * + * @param [in] context Chip implementation context + * @param [in] device_eui Custom Device EUI + * @param [in] join_eui Custom Join EUI + * @param [in] rfu Parameter RFU - shall be set to 0x00 + * @param [out] pin The buffer to be filled with PIN of the LR11XX. It is up to the application to ensure pin is long + * enough to hold the PIN + * + * @returns Operation status + * + * @see LR11XX_SYSTEM_PIN_LENGTH + */ +lr11xx_status_t lr11xx_system_read_pin_custom_eui( const void* context, lr11xx_system_uid_t device_eui, + lr11xx_system_join_eui_t join_eui, uint8_t rfu, + lr11xx_system_pin_t pin ); + +/*! + * @brief Read and return a 32-bit random number + * + * @remark Radio operating mode must be set into standby. + * + * @param [in] context Chip implementation context + * @param [out] random_number 32-bit random number + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_system_get_random_number( const void* context, uint32_t* random_number ); + +/*! + * @brief Enable the CRC on SPI transactions + * + * @remark This command shall always be sent with a CRC (to both enable and disable the feature). The function does not + * take care of this additional byte - which is under the responsibility of the underlying HAL functions + * + * @param [in] context Chip implementation context + * @param [in] enable_crc CRC + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_system_enable_spi_crc( const void* context, bool enable_crc ); + +/*! + * @brief Configure the GPIO drive in sleep mode + * + * @remark GPIO stands for RF switch and IRQ line DIOs + * + * @note This command is available from firmware version 0x0306 + * + * @param [in] context Chip implementation context + * @param [in] enable_drive GPIO drive configuration (true: enabled / false: disabled) + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_system_drive_dio_in_sleep_mode( const void* context, bool enable_drive ); + +#ifdef __cplusplus +} +#endif + +#endif // LR11XX_SYSTEM_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/src/lr11xx_system_types.h b/src/lr11xx_system_types.h new file mode 100644 index 0000000..23dec41 --- /dev/null +++ b/src/lr11xx_system_types.h @@ -0,0 +1,330 @@ +/*! + * @file lr11xx_system_types.h + * + * @brief System driver types for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR11XX_SYSTEM_TYPES_H +#define LR11XX_SYSTEM_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include +#include + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/*! + * @brief Length in byte of the LR11XX version blob + */ +#define LR11XX_SYSTEM_VERSION_LENGTH ( 4 ) + +/*! + * @brief Length of the LR11XX Unique Identifier in bytes + * + * The LR11XX Unique Identifiers is an 8 byte long buffer + */ +#define LR11XX_SYSTEM_UID_LENGTH ( 8 ) +#define LR11XX_SYSTEM_JOIN_EUI_LENGTH ( 8 ) +#define LR11XX_SYSTEM_PIN_LENGTH ( 4 ) + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/** + * @brief Fixed-length array to store a UID + */ +typedef uint8_t lr11xx_system_uid_t[LR11XX_SYSTEM_UID_LENGTH]; + +/** + * @brief Fixed-length array to store a joinEUI + */ +typedef uint8_t lr11xx_system_join_eui_t[LR11XX_SYSTEM_JOIN_EUI_LENGTH]; + +/** + * @brief Fixed-length array to store a PIN + */ +typedef uint8_t lr11xx_system_pin_t[LR11XX_SYSTEM_PIN_LENGTH]; + +/** + * @brief Type to store system interrupt flags + */ +typedef uint32_t lr11xx_system_irq_mask_t; + +/** + * @brief Interrupt flags + */ +enum lr11xx_system_irq_e +{ + LR11XX_SYSTEM_IRQ_NONE = ( 0 << 0 ), + LR11XX_SYSTEM_IRQ_TX_DONE = ( 1 << 2 ), + LR11XX_SYSTEM_IRQ_RX_DONE = ( 1 << 3 ), + LR11XX_SYSTEM_IRQ_PREAMBLE_DETECTED = ( 1 << 4 ), + LR11XX_SYSTEM_IRQ_SYNC_WORD_HEADER_VALID = ( 1 << 5 ), + LR11XX_SYSTEM_IRQ_HEADER_ERROR = ( 1 << 6 ), + LR11XX_SYSTEM_IRQ_CRC_ERROR = ( 1 << 7 ), + LR11XX_SYSTEM_IRQ_CAD_DONE = ( 1 << 8 ), + LR11XX_SYSTEM_IRQ_CAD_DETECTED = ( 1 << 9 ), + LR11XX_SYSTEM_IRQ_TIMEOUT = ( 1 << 10 ), + LR11XX_SYSTEM_IRQ_LR_FHSS_INTRA_PKT_HOP = ( 1 << 11 ), + LR11XX_SYSTEM_IRQ_GNSS_SCAN_DONE = ( 1 << 19 ), + LR11XX_SYSTEM_IRQ_WIFI_SCAN_DONE = ( 1 << 20 ), + LR11XX_SYSTEM_IRQ_EOL = ( 1 << 21 ), + LR11XX_SYSTEM_IRQ_CMD_ERROR = ( 1 << 22 ), + LR11XX_SYSTEM_IRQ_ERROR = ( 1 << 23 ), + LR11XX_SYSTEM_IRQ_FSK_LEN_ERROR = ( 1 << 24 ), + LR11XX_SYSTEM_IRQ_FSK_ADDR_ERROR = ( 1 << 25 ), + LR11XX_SYSTEM_IRQ_ALL_MASK = + LR11XX_SYSTEM_IRQ_TX_DONE | LR11XX_SYSTEM_IRQ_RX_DONE | LR11XX_SYSTEM_IRQ_PREAMBLE_DETECTED | + LR11XX_SYSTEM_IRQ_SYNC_WORD_HEADER_VALID | LR11XX_SYSTEM_IRQ_HEADER_ERROR | LR11XX_SYSTEM_IRQ_CRC_ERROR | + LR11XX_SYSTEM_IRQ_CAD_DONE | LR11XX_SYSTEM_IRQ_CAD_DETECTED | LR11XX_SYSTEM_IRQ_TIMEOUT | + LR11XX_SYSTEM_IRQ_LR_FHSS_INTRA_PKT_HOP | LR11XX_SYSTEM_IRQ_GNSS_SCAN_DONE | LR11XX_SYSTEM_IRQ_WIFI_SCAN_DONE | + LR11XX_SYSTEM_IRQ_EOL | LR11XX_SYSTEM_IRQ_CMD_ERROR | LR11XX_SYSTEM_IRQ_ERROR | + LR11XX_SYSTEM_IRQ_FSK_LEN_ERROR | LR11XX_SYSTEM_IRQ_FSK_ADDR_ERROR, +}; + +/** + * @brief Calibration flags + */ +enum lr11xx_system_calibration_e +{ + LR11XX_SYSTEM_CALIB_LF_RC_MASK = ( 1 << 0 ), + LR11XX_SYSTEM_CALIB_HF_RC_MASK = ( 1 << 1 ), + LR11XX_SYSTEM_CALIB_PLL_MASK = ( 1 << 2 ), + LR11XX_SYSTEM_CALIB_ADC_MASK = ( 1 << 3 ), + LR11XX_SYSTEM_CALIB_IMG_MASK = ( 1 << 4 ), + LR11XX_SYSTEM_CALIB_PLL_TX_MASK = ( 1 << 5 ), +}; + +typedef uint8_t lr11xx_system_cal_mask_t; + +/** + * @brief Error flags + */ +enum lr11xx_system_errors_e +{ + LR11XX_SYSTEM_ERRORS_LF_RC_CALIB_MASK = ( 1 << 0 ), + LR11XX_SYSTEM_ERRORS_HF_RC_CALIB_MASK = ( 1 << 1 ), + LR11XX_SYSTEM_ERRORS_ADC_CALIB_MASK = ( 1 << 2 ), + LR11XX_SYSTEM_ERRORS_PLL_CALIB_MASK = ( 1 << 3 ), + LR11XX_SYSTEM_ERRORS_IMG_CALIB_MASK = ( 1 << 4 ), + LR11XX_SYSTEM_ERRORS_HF_XOSC_START_MASK = ( 1 << 5 ), + LR11XX_SYSTEM_ERRORS_LF_XOSC_START_MASK = ( 1 << 6 ), + LR11XX_SYSTEM_ERRORS_PLL_LOCK_MASK = ( 1 << 7 ), +}; + +typedef uint16_t lr11xx_system_errors_t; + +/** + * @brief Chip modes + */ +typedef enum +{ + LR11XX_SYSTEM_CHIP_MODE_SLEEP = 0x00, + LR11XX_SYSTEM_CHIP_MODE_STBY_RC = 0x01, + LR11XX_SYSTEM_CHIP_MODE_STBY_XOSC = 0x02, + LR11XX_SYSTEM_CHIP_MODE_FS = 0x03, + LR11XX_SYSTEM_CHIP_MODE_RX = 0x04, + LR11XX_SYSTEM_CHIP_MODE_TX = 0x05, + LR11XX_SYSTEM_CHIP_MODE_LOC = 0x06, +} lr11xx_system_chip_modes_t; + +/** + * @brief Reset status + */ +typedef enum +{ + LR11XX_SYSTEM_RESET_STATUS_CLEARED = 0x00, + LR11XX_SYSTEM_RESET_STATUS_ANALOG = 0x01, + LR11XX_SYSTEM_RESET_STATUS_EXTERNAL = 0x02, + LR11XX_SYSTEM_RESET_STATUS_SYSTEM = 0x03, + LR11XX_SYSTEM_RESET_STATUS_WATCHDOG = 0x04, + LR11XX_SYSTEM_RESET_STATUS_IOCD_RESTART = 0x05, + LR11XX_SYSTEM_RESET_STATUS_RTC_RESTART = 0x06, +} lr11xx_system_reset_status_t; + +/** + * @brief Command status + */ +typedef enum +{ + LR11XX_SYSTEM_CMD_STATUS_FAIL = 0x00, + LR11XX_SYSTEM_CMD_STATUS_PERR = 0x01, + LR11XX_SYSTEM_CMD_STATUS_OK = 0x02, + LR11XX_SYSTEM_CMD_STATUS_DATA = 0x03, +} lr11xx_system_command_status_t; + +/** + * @brief Low-frequency clock modes + */ +typedef enum +{ + LR11XX_SYSTEM_LFCLK_RC = 0x00, //!< (Default) + LR11XX_SYSTEM_LFCLK_XTAL = 0x01, + LR11XX_SYSTEM_LFCLK_EXT = 0x02 +} lr11xx_system_lfclk_cfg_t; + +/** + * @brief Regulator modes + */ +typedef enum +{ + LR11XX_SYSTEM_REG_MODE_LDO = 0x00, //!< (Default) + LR11XX_SYSTEM_REG_MODE_DCDC = 0x01, +} lr11xx_system_reg_mode_t; + +/** + * @brief Info page ID + */ +typedef enum +{ + LR11XX_SYSTEM_INFOPAGE_0 = 0x00, //!< Info page #0 + LR11XX_SYSTEM_INFOPAGE_1 = 0x01, //!< Info page #1 +} lr11xx_system_infopage_id_t; + +/** + * @brief RF switch configuration pin + */ +enum lr11xx_system_rfswitch_cfg_pin_e +{ + LR11XX_SYSTEM_RFSW0_HIGH = ( 1 << 0 ), + LR11XX_SYSTEM_RFSW1_HIGH = ( 1 << 1 ), + LR11XX_SYSTEM_RFSW2_HIGH = ( 1 << 2 ), + LR11XX_SYSTEM_RFSW3_HIGH = ( 1 << 3 ), + LR11XX_SYSTEM_RFSW4_HIGH = ( 1 << 4 ), +}; + +/** + * @brief RF switch configuration structure definition + */ +typedef struct lr11xx_system_rfswitch_cfg_s +{ + uint8_t enable; + uint8_t standby; + uint8_t rx; + uint8_t tx; + uint8_t tx_hp; + uint8_t tx_hf; + uint8_t gnss; + uint8_t wifi; +} lr11xx_system_rfswitch_cfg_t; + +/** + * @brief Stand by configuration values + */ +typedef enum +{ + LR11XX_SYSTEM_STANDBY_CFG_RC = 0x00, + LR11XX_SYSTEM_STANDBY_CFG_XOSC = 0x01 +} lr11xx_system_standby_cfg_t; + +/** + * @brief TCXO supply voltage values + */ +typedef enum +{ + LR11XX_SYSTEM_TCXO_CTRL_1_6V = 0x00, //!< Supply voltage = 1.6v + LR11XX_SYSTEM_TCXO_CTRL_1_7V = 0x01, //!< Supply voltage = 1.7v + LR11XX_SYSTEM_TCXO_CTRL_1_8V = 0x02, //!< Supply voltage = 1.8v + LR11XX_SYSTEM_TCXO_CTRL_2_2V = 0x03, //!< Supply voltage = 2.2v + LR11XX_SYSTEM_TCXO_CTRL_2_4V = 0x04, //!< Supply voltage = 2.4v + LR11XX_SYSTEM_TCXO_CTRL_2_7V = 0x05, //!< Supply voltage = 2.7v + LR11XX_SYSTEM_TCXO_CTRL_3_0V = 0x06, //!< Supply voltage = 3.0v + LR11XX_SYSTEM_TCXO_CTRL_3_3V = 0x07, //!< Supply voltage = 3.3v +} lr11xx_system_tcxo_supply_voltage_t; + +/** + * @brief Status register 1 structure definition + */ +typedef struct lr11xx_system_stat1_s +{ + lr11xx_system_command_status_t command_status; + bool is_interrupt_active; +} lr11xx_system_stat1_t; + +/** + * @brief Status register 2 structure definition + */ +typedef struct lr11xx_system_stat2_s +{ + lr11xx_system_reset_status_t reset_status; + lr11xx_system_chip_modes_t chip_mode; + bool is_running_from_flash; +} lr11xx_system_stat2_t; + +/** + * @brief Version structure definition + */ +typedef struct lr11xx_system_version_s +{ + uint8_t hw; + uint8_t type; + uint16_t fw; +} lr11xx_system_version_t; + +/** + * @brief Sleep configuration structure definition + */ +typedef struct lr11xx_system_sleep_cfg_s +{ + bool is_warm_start; + bool is_rtc_timeout; +} lr11xx_system_sleep_cfg_t; + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +#ifdef __cplusplus +} +#endif + +#endif // LR11XX_SYSTEM_TYPES_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/src/lr11xx_types.h b/src/lr11xx_types.h new file mode 100644 index 0000000..be52d7a --- /dev/null +++ b/src/lr11xx_types.h @@ -0,0 +1,76 @@ +/*! + * @file lr11xx_types.h + * + * @brief Type definitions for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR11XX_TYPES_H +#define LR11XX_TYPES_H + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +#define LR11XX_CMD_LENGTH_MAX ( 512 ) + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/** + * @brief LR11XX status + */ +typedef enum lr11xx_status_e +{ + LR11XX_STATUS_OK = 0, + LR11XX_STATUS_ERROR = 3, +} lr11xx_status_t; + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +#endif // LR11XX_TYPES_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/src/lr11xx_wifi.c b/src/lr11xx_wifi.c new file mode 100644 index 0000000..68d51b1 --- /dev/null +++ b/src/lr11xx_wifi.c @@ -0,0 +1,934 @@ +/*! + * @file lr11xx_wifi.c + * + * @brief Wi-Fi passive scan driver implementation for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include "lr11xx_wifi.h" +#include "lr11xx_system_types.h" +#include "lr11xx_hal.h" + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE MACROS----------------------------------------------------------- + */ + +#ifndef MIN +#define MIN( a, b ) ( ( a > b ) ? b : a ) +#endif // MIN + +/*! + * @brief Check if a value is in between min and max - included + */ +#define IS_BETWEEN( value, min, max ) ( ( min <= value ) && ( value <= max ) ) + +/*! + * @brief Check if a value is in between 0x80 and 0xBF - included + */ +#define IS_BETWEEN_0x80_AND_0xBF( value ) IS_BETWEEN( value, 0x80, 0xBF ) + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE CONSTANTS ------------------------------------------------------- + */ + +#define LR11XX_WIFI_BASIC_COMPLETE_RESULT_SIZE ( 22 ) +#define LR11XX_WIFI_BASIC_MAC_TYPE_CHANNEL_RESULT_SIZE ( 9 ) + +#define LR11XX_WIFI_MAX_SIZE_PER_SPI( single_size ) \ + ( single_size * ( LR11XX_WIFI_MAX_RESULT_PER_TRANSACTION( single_size ) ) ) + +#define LR11XX_WIFI_MAX_RESULT_PER_TRANSACTION( single_size ) \ + ( MIN( ( LR11XX_WIFI_READ_RESULT_LIMIT ) / ( single_size ), LR11XX_WIFI_N_RESULTS_MAX_PER_CHUNK ) ) + +#define LR11XX_WIFI_ALL_CUMULATIVE_TIMING_SIZE ( 16 ) +#define LR11XX_WIFI_VERSION_SIZE ( 2 ) +#define LR11XX_WIFI_READ_RESULT_LIMIT ( 1020 ) +#define LR11XX_WIFI_COUNTRY_RESULT_LENGTH_SIZE ( 1 ) +#define LR11XX_WIFI_EXTENDED_COMPLETE_RESULT_SIZE ( 79 ) +#define LR11XX_WIFI_SCAN_SINGLE_COUNTRY_CODE_RESULT_SIZE ( 10 ) +#define LR11XX_WIFI_MAX_COUNTRY_CODE_RESULT_SIZE \ + ( LR11XX_WIFI_MAX_COUNTRY_CODE * LR11XX_WIFI_SCAN_SINGLE_COUNTRY_CODE_RESULT_SIZE ) + +// Command length +#define LR11XX_WIFI_SCAN_CMD_LENGTH ( 2 + 9 ) +#define LR11XX_WIFI_SEARCH_COUNTRY_CODE_CMD_LENGTH ( 2 + 7 ) +#define LR11XX_WIFI_SCAN_TIME_LIMIT_CMD_LENGTH ( 2 + 9 ) +#define LR11XX_WIFI_COUNTRY_CODE_TIME_LIMIT_CMD_LENGTH ( 2 + 7 ) +#define LR11XX_WIFI_GET_RESULT_SIZE_CMD_LENGTH ( 2 ) +#define LR11XX_WIFI_READ_RESULT_CMD_LENGTH ( 2 + 3 ) +#define LR11XX_WIFI_RESET_CUMUL_TIMING_CMD_LENGTH ( 2 ) +#define LR11XX_WIFI_READ_CUMUL_TIMING_CMD_LENGTH ( 2 ) +#define LR11XX_WIFI_GET_SIZE_COUNTRY_RESULT_CMD_LENGTH ( 2 ) +#define LR11XX_WIFI_READ_COUNTRY_CODE_CMD_LENGTH ( 2 + 2 ) +#define LR11XX_WIFI_CFG_TIMESTAMP_AP_PHONE_CMD_LENGTH ( 2 + 4 ) +#define LR11XX_WIFI_GET_VERSION_CMD_LENGTH ( 2 ) + +/*! + * @brief Wi-Fi scan power consumption + * + * @note these numbers are given for information, it should be modified according to the used hardware. + */ +#define LR11XX_WIFI_CORRELATION_UA ( 12000 ) +#define LR11XX_WIFI_CAPTURE_UA ( 12000 ) +#define LR11XX_WIFI_DEMODULATION_UA ( 4000 ) + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE TYPES ----------------------------------------------------------- + */ + +/*! + * @brief Operating codes for Wi-Fi-related operations + */ +enum +{ + LR11XX_WIFI_SCAN_OC = 0x0300, + LR11XX_WIFI_SCAN_TIME_LIMIT = 0x0301, + LR11XX_WIFI_SEARCH_COUNTRY_CODE_OC = 0x0302, + LR11XX_WIFI_COUNTRY_CODE_TIME_LIMIT_OC = 0x0303, + LR11XX_WIFI_GET_RESULT_SIZE_OC = 0x0305, + LR11XX_WIFI_READ_RESULT_OC = 0x0306, + LR11XX_WIFI_RESET_CUMUL_TIMING_OC = 0x0307, + LR11XX_WIFI_READ_CUMUL_TIMING_OC = 0x0308, + LR11XX_WIFI_GET_SIZE_COUNTRY_RESULT_OC = 0x0309, + LR11XX_WIFI_READ_COUNTRY_CODE_OC = 0x030A, + LR11XX_WIFI_CONFIGURE_TIMESTAMP_AP_PHONE_OC = 0x030B, + LR11XX_WIFI_GET_VERSION_OC = 0x0320, +}; + +/*! + * @brief Wi-Fi scan results interface + */ +typedef union +{ + lr11xx_wifi_basic_complete_result_t* basic_complete; + lr11xx_wifi_basic_mac_type_channel_result_t* basic_mac_type_channel; + lr11xx_wifi_extended_full_result_t* extended_complete; +} lr11xx_wifi_result_interface_t; + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE VARIABLES ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DECLARATION ------------------------------------------- + */ + +/*! + * @brief Return a uint16 value by reading a buffer of uint8 from index. + * + * This function interpret the array MSB first. It is equivalent to: + * return array[index] * 256 + array[index+1] + * + * @returns The uint16 value + */ +static uint16_t uint16_from_array( const uint8_t* array, const uint16_t index ); + +/*! + * @brief Return a uint64 value by reading a buffer of uint8 from index. + * + * This function interpret the array MSB first. + * + * @returns The uint64 value + */ +static uint64_t uint64_from_array( const uint8_t* array, const uint16_t index ); + +/*! + * @brief Propagate the result buffer interpretation depending on the format_code selected + * + * @see interpret_basic_complete_result_from_buffer, interpret_basic_mac_type_channel_result_from_buffer, + * interpret_extended_full_result_from_buffer + */ +static void generic_results_interpreter( const uint8_t n_result_to_parse, const uint8_t index_result_start_writing, + const uint8_t* buffer, lr11xx_wifi_result_interface_t result_interface, + const lr11xx_wifi_result_format_t format_code ); + +/*! + * @brief Parse basic complete result + */ +static void interpret_basic_complete_result_from_buffer( const uint8_t nb_results, + const uint8_t index_result_start_writing, + const uint8_t* buffer, + lr11xx_wifi_basic_complete_result_t* result ); + +/*! + * @brief Parse basic MAC - type - channel result + */ +static void interpret_basic_mac_type_channel_result_from_buffer( const uint8_t nb_results, + const uint8_t index_result_start_writing, + const uint8_t* buffer, + lr11xx_wifi_basic_mac_type_channel_result_t* result ); + +/*! + * @brief Parse extended full result + */ +static void interpret_extended_full_result_from_buffer( const uint8_t nb_results, + const uint8_t index_result_start_writing, const uint8_t* buffer, + lr11xx_wifi_extended_full_result_t* result ); + +/*! + * @brief Parse basic MAC - type - channel result + */ +static lr11xx_status_t fetch_and_aggregate_all_results( const void* context, const uint8_t index_result_start, + const uint8_t nb_results, + const uint8_t nb_results_per_chunk_max, + const lr11xx_wifi_result_format_t result_format_code, + uint8_t* result_buffer, + lr11xx_wifi_result_interface_t result_structures ); + +/*! + * @brief Share the size of a result format + * + * @returns Size in byte of the format given as parameter + */ +static uint8_t lr11xx_wifi_get_result_size_from_format( const lr11xx_wifi_result_format_t format ); + +/*! + * @brief Fetch results from the radio after a successful Wi-Fi passive scan + * + * @returns Operation status + */ +static lr11xx_hal_status_t lr11xx_wifi_read_results_helper( const void* context, const uint8_t start_index, + const uint8_t n_elem, uint8_t* buffer, + const lr11xx_wifi_result_format_t result_format ); + +/*! + * @brief Extract Wi-Fi MAC address from a buffer + */ +static void lr11xx_wifi_read_mac_address_from_buffer( const uint8_t* buffer, const uint16_t index_in_buffer, + lr11xx_wifi_mac_address_t mac_address ); + +/*! + * @brief Share the format code corresponding to a result format + * + * @returns Format code + */ +static uint8_t lr11xx_wifi_get_format_code( const lr11xx_wifi_result_format_t format ); + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS DEFINITION --------------------------------------------- + */ + +lr11xx_status_t lr11xx_wifi_scan( const void* context, const lr11xx_wifi_signal_type_scan_t signal_type, + const lr11xx_wifi_channel_mask_t channels, const lr11xx_wifi_mode_t scan_mode, + const uint8_t max_results, const uint8_t nb_scan_per_channel, + const uint16_t timeout_in_ms, const bool abort_on_timeout ) +{ + const uint8_t cbuffer[LR11XX_WIFI_SCAN_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_WIFI_SCAN_OC >> 8 ), + ( uint8_t ) ( LR11XX_WIFI_SCAN_OC >> 0 ), + ( uint8_t ) signal_type, + ( uint8_t ) ( channels >> 8 ), + ( uint8_t ) ( channels >> 0 ), + ( uint8_t ) scan_mode, + max_results, + nb_scan_per_channel, + ( uint8_t ) ( timeout_in_ms >> 8 ), + ( uint8_t ) ( timeout_in_ms >> 0 ), + ( uint8_t ) ( ( abort_on_timeout == true ) ? 1 : 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_WIFI_SCAN_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_wifi_search_country_code( const void* context, const lr11xx_wifi_channel_mask_t channels_mask, + const uint8_t nb_max_results, const uint8_t nb_scan_per_channel, + const uint16_t timeout_in_ms, const bool abort_on_timeout ) +{ + const uint8_t cbuffer[LR11XX_WIFI_SEARCH_COUNTRY_CODE_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_WIFI_SEARCH_COUNTRY_CODE_OC >> 8 ), + ( uint8_t ) ( LR11XX_WIFI_SEARCH_COUNTRY_CODE_OC >> 0 ), + ( uint8_t ) ( channels_mask >> 8 ), + ( uint8_t ) ( channels_mask >> 0 ), + nb_max_results, + nb_scan_per_channel, + ( uint8_t ) ( timeout_in_ms >> 8 ), + ( uint8_t ) ( timeout_in_ms >> 0 ), + ( uint8_t ) ( ( abort_on_timeout == true ) ? 1 : 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_WIFI_SEARCH_COUNTRY_CODE_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_wifi_scan_time_limit( const void* radio, const lr11xx_wifi_signal_type_scan_t signal_type, + const lr11xx_wifi_channel_mask_t channels, + const lr11xx_wifi_mode_t scan_mode, const uint8_t max_results, + const uint16_t timeout_per_channel_ms, const uint16_t timeout_per_scan_ms ) +{ + const uint8_t cbuffer[LR11XX_WIFI_SCAN_TIME_LIMIT_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_WIFI_SCAN_TIME_LIMIT >> 8 ), + ( uint8_t ) ( LR11XX_WIFI_SCAN_TIME_LIMIT >> 0 ), + ( uint8_t ) signal_type, + ( uint8_t ) ( channels >> 8 ), + ( uint8_t ) ( channels >> 0 ), + ( uint8_t ) scan_mode, + max_results, + ( uint8_t ) ( timeout_per_channel_ms >> 8 ), + ( uint8_t ) ( timeout_per_channel_ms >> 0 ), + ( uint8_t ) ( timeout_per_scan_ms >> 8 ), + ( uint8_t ) ( timeout_per_scan_ms >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( radio, cbuffer, LR11XX_WIFI_SCAN_TIME_LIMIT_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_wifi_search_country_code_time_limit( const void* radio, + const lr11xx_wifi_channel_mask_t channels, + const uint8_t max_results, + const uint16_t timeout_per_channel_ms, + const uint16_t timeout_per_scan_ms ) +{ + const uint8_t cbuffer[LR11XX_WIFI_COUNTRY_CODE_TIME_LIMIT_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_WIFI_COUNTRY_CODE_TIME_LIMIT_OC >> 8 ), + ( uint8_t ) ( LR11XX_WIFI_COUNTRY_CODE_TIME_LIMIT_OC >> 0 ), + ( uint8_t ) ( channels >> 8 ), + ( uint8_t ) ( channels >> 0 ), + ( uint8_t ) max_results, + ( uint8_t ) ( timeout_per_channel_ms >> 8 ), + ( uint8_t ) ( timeout_per_channel_ms >> 0 ), + ( uint8_t ) ( timeout_per_scan_ms >> 8 ), + ( uint8_t ) ( timeout_per_scan_ms >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( radio, cbuffer, LR11XX_WIFI_COUNTRY_CODE_TIME_LIMIT_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_wifi_get_nb_results( const void* context, uint8_t* nb_results ) +{ + const uint8_t cbuffer[LR11XX_WIFI_GET_RESULT_SIZE_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_WIFI_GET_RESULT_SIZE_OC >> 8 ), + ( uint8_t ) ( LR11XX_WIFI_GET_RESULT_SIZE_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_WIFI_GET_RESULT_SIZE_CMD_LENGTH, nb_results, + sizeof( *nb_results ) ); +} + +lr11xx_status_t lr11xx_wifi_read_basic_complete_results( const void* context, const uint8_t start_result_index, + const uint8_t nb_results, + lr11xx_wifi_basic_complete_result_t* results ) +{ + uint8_t result_buffer[LR11XX_WIFI_MAX_SIZE_PER_SPI( LR11XX_WIFI_BASIC_COMPLETE_RESULT_SIZE )] = { 0 }; + const uint8_t nb_results_per_chunk_max = + LR11XX_WIFI_MAX_RESULT_PER_TRANSACTION( LR11XX_WIFI_BASIC_COMPLETE_RESULT_SIZE ); + + lr11xx_wifi_result_interface_t result_interface = { 0 }; + result_interface.basic_complete = results; + + return fetch_and_aggregate_all_results( context, start_result_index, nb_results, nb_results_per_chunk_max, + LR11XX_WIFI_RESULT_FORMAT_BASIC_COMPLETE, result_buffer, result_interface ); +} + +lr11xx_status_t lr11xx_wifi_read_basic_mac_type_channel_results( const void* context, const uint8_t start_result_index, + const uint8_t nb_results, + lr11xx_wifi_basic_mac_type_channel_result_t* results ) +{ + uint8_t result_buffer[LR11XX_WIFI_MAX_SIZE_PER_SPI( LR11XX_WIFI_BASIC_MAC_TYPE_CHANNEL_RESULT_SIZE )] = { 0 }; + const uint8_t nb_results_per_chunk_max = + LR11XX_WIFI_MAX_RESULT_PER_TRANSACTION( LR11XX_WIFI_BASIC_MAC_TYPE_CHANNEL_RESULT_SIZE ); + + lr11xx_wifi_result_interface_t result_interface = { 0 }; + result_interface.basic_mac_type_channel = results; + + return fetch_and_aggregate_all_results( context, start_result_index, nb_results, nb_results_per_chunk_max, + LR11XX_WIFI_RESULT_FORMAT_BASIC_MAC_TYPE_CHANNEL, result_buffer, + result_interface ); +} + +lr11xx_status_t lr11xx_wifi_read_extended_full_results( const void* radio, const uint8_t start_result_index, + const uint8_t nb_results, + lr11xx_wifi_extended_full_result_t* results ) +{ + uint8_t result_buffer[LR11XX_WIFI_MAX_SIZE_PER_SPI( LR11XX_WIFI_EXTENDED_COMPLETE_RESULT_SIZE )] = { 0 }; + const uint8_t nb_results_per_chunk_max = + LR11XX_WIFI_MAX_RESULT_PER_TRANSACTION( LR11XX_WIFI_EXTENDED_COMPLETE_RESULT_SIZE ); + + lr11xx_wifi_result_interface_t result_interface = { 0 }; + result_interface.extended_complete = results; + + return fetch_and_aggregate_all_results( radio, start_result_index, nb_results, nb_results_per_chunk_max, + LR11XX_WIFI_RESULT_FORMAT_EXTENDED_FULL, result_buffer, result_interface ); +} + +lr11xx_status_t lr11xx_wifi_reset_cumulative_timing( const void* context ) +{ + const uint8_t cbuffer[LR11XX_WIFI_RESET_CUMUL_TIMING_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_WIFI_RESET_CUMUL_TIMING_OC >> 8 ), + ( uint8_t ) ( LR11XX_WIFI_RESET_CUMUL_TIMING_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_WIFI_RESET_CUMUL_TIMING_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_wifi_read_cumulative_timing( const void* context, lr11xx_wifi_cumulative_timings_t* timing ) +{ + const uint8_t cbuffer[LR11XX_WIFI_READ_CUMUL_TIMING_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_WIFI_READ_CUMUL_TIMING_OC >> 8 ), + ( uint8_t ) ( LR11XX_WIFI_READ_CUMUL_TIMING_OC >> 0 ), + }; + uint8_t buffer_out[LR11XX_WIFI_ALL_CUMULATIVE_TIMING_SIZE] = { 0 }; + + const lr11xx_hal_status_t hal_status = lr11xx_hal_read( context, cbuffer, LR11XX_WIFI_READ_CUMUL_TIMING_CMD_LENGTH, + buffer_out, LR11XX_WIFI_ALL_CUMULATIVE_TIMING_SIZE ); + + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + timing->rx_detection_us = + ( buffer_out[0] << 24 ) + ( buffer_out[1] << 16 ) + ( buffer_out[2] << 8 ) + buffer_out[3]; + timing->rx_correlation_us = + ( buffer_out[4] << 24 ) + ( buffer_out[5] << 16 ) + ( buffer_out[6] << 8 ) + buffer_out[7]; + timing->rx_capture_us = + ( buffer_out[8] << 24 ) + ( buffer_out[9] << 16 ) + ( buffer_out[10] << 8 ) + buffer_out[11]; + timing->demodulation_us = + ( buffer_out[12] << 24 ) + ( buffer_out[13] << 16 ) + ( buffer_out[14] << 8 ) + buffer_out[15]; + } + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_wifi_get_nb_country_code_results( const void* context, uint8_t* country_result_size ) +{ + const uint8_t cbuffer[LR11XX_WIFI_GET_SIZE_COUNTRY_RESULT_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_WIFI_GET_SIZE_COUNTRY_RESULT_OC >> 8 ), + ( uint8_t ) ( LR11XX_WIFI_GET_SIZE_COUNTRY_RESULT_OC >> 0 ), + }; + uint8_t rbuffer[LR11XX_WIFI_COUNTRY_RESULT_LENGTH_SIZE] = { 0 }; + + const lr11xx_hal_status_t hal_status = + lr11xx_hal_read( context, cbuffer, LR11XX_WIFI_GET_SIZE_COUNTRY_RESULT_CMD_LENGTH, rbuffer, + LR11XX_WIFI_COUNTRY_RESULT_LENGTH_SIZE ); + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + ( *country_result_size ) = rbuffer[0]; + } + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_wifi_read_country_code_results( const void* context, const uint8_t start_result_index, + const uint8_t nb_country_results, + lr11xx_wifi_country_code_t* country_code_results ) +{ + const uint8_t cbuffer[LR11XX_WIFI_READ_COUNTRY_CODE_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_WIFI_READ_COUNTRY_CODE_OC >> 8 ), + ( uint8_t ) ( LR11XX_WIFI_READ_COUNTRY_CODE_OC >> 0 ), + start_result_index, + nb_country_results, + }; + uint8_t rbuffer[LR11XX_WIFI_MAX_COUNTRY_CODE_RESULT_SIZE] = { 0 }; + const uint16_t country_code_result_size_to_read = + nb_country_results * LR11XX_WIFI_SCAN_SINGLE_COUNTRY_CODE_RESULT_SIZE; + + const lr11xx_hal_status_t hal_status = lr11xx_hal_read( context, cbuffer, LR11XX_WIFI_READ_COUNTRY_CODE_CMD_LENGTH, + rbuffer, country_code_result_size_to_read ); + + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + for( uint8_t result_index = 0; result_index < nb_country_results; result_index++ ) + { + const uint8_t local_index = result_index * LR11XX_WIFI_SCAN_SINGLE_COUNTRY_CODE_RESULT_SIZE; + lr11xx_wifi_country_code_t* local_country_code_result = &country_code_results[result_index]; + + local_country_code_result->country_code[0] = rbuffer[local_index + 0]; + local_country_code_result->country_code[1] = rbuffer[local_index + 1]; + local_country_code_result->io_regulation = rbuffer[local_index + 2]; + local_country_code_result->channel_info_byte = rbuffer[local_index + 3]; + + for( uint8_t field_mac_index = 0; field_mac_index < LR11XX_WIFI_MAC_ADDRESS_LENGTH; field_mac_index++ ) + { + local_country_code_result->mac_address[field_mac_index] = + rbuffer[local_index + ( LR11XX_WIFI_MAC_ADDRESS_LENGTH - field_mac_index - 1 ) + 4]; + } + } + } + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_wifi_cfg_timestamp_ap_phone( const void* context, uint32_t timestamp_in_s ) +{ + const uint8_t cbuffer[LR11XX_WIFI_CFG_TIMESTAMP_AP_PHONE_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_WIFI_CONFIGURE_TIMESTAMP_AP_PHONE_OC >> 8 ), + ( uint8_t ) ( LR11XX_WIFI_CONFIGURE_TIMESTAMP_AP_PHONE_OC >> 0 ), + ( uint8_t ) ( timestamp_in_s >> 24 ), + ( uint8_t ) ( timestamp_in_s >> 16 ), + ( uint8_t ) ( timestamp_in_s >> 8 ), + ( uint8_t ) ( timestamp_in_s >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_WIFI_CFG_TIMESTAMP_AP_PHONE_CMD_LENGTH, 0, + 0 ); +} + +lr11xx_status_t lr11xx_wifi_read_version( const void* context, lr11xx_wifi_version_t* wifi_version ) +{ + const uint8_t cbuffer[LR11XX_WIFI_GET_VERSION_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_WIFI_GET_VERSION_OC >> 8 ), + ( uint8_t ) ( LR11XX_WIFI_GET_VERSION_OC >> 0 ), + }; + uint8_t rbuffer[LR11XX_WIFI_VERSION_SIZE] = { 0 }; + const lr11xx_hal_status_t hal_status = + lr11xx_hal_read( context, cbuffer, LR11XX_WIFI_GET_VERSION_CMD_LENGTH, rbuffer, LR11XX_WIFI_VERSION_SIZE ); + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + wifi_version->major = rbuffer[0]; + wifi_version->minor = rbuffer[1]; + } + return ( lr11xx_status_t ) hal_status; +} + +uint8_t lr11xx_wifi_get_nb_results_max_per_chunk( void ) +{ + return ( uint8_t ) LR11XX_WIFI_N_RESULTS_MAX_PER_CHUNK; +} + +void lr11xx_wifi_parse_channel_info( const lr11xx_wifi_channel_info_byte_t channel_info, lr11xx_wifi_channel_t* channel, + bool* rssi_validity, lr11xx_wifi_mac_origin_t* mac_origin_estimation ) +{ + ( *channel ) = lr11xx_wifi_extract_channel_from_info_byte( channel_info ); + ( *mac_origin_estimation ) = ( lr11xx_wifi_mac_origin_t ) ( ( channel_info & 0x30 ) >> 4 ); + ( *rssi_validity ) = ( ( channel_info & 0x40 ) == 0 ) ? true : false; +} + +lr11xx_wifi_channel_t lr11xx_wifi_extract_channel_from_info_byte( const lr11xx_wifi_channel_info_byte_t channel_info ) +{ + return ( lr11xx_wifi_channel_t ) ( channel_info & 0x0F ); +} + +void lr11xx_wifi_parse_frame_type_info( const lr11xx_wifi_frame_type_info_byte_t frame_type_info, + lr11xx_wifi_frame_type_t* frame_type, + lr11xx_wifi_frame_sub_type_t* frame_sub_type, bool* to_ds, bool* from_ds ) +{ + ( *frame_type ) = ( lr11xx_wifi_frame_type_t ) ( ( frame_type_info >> 6 ) & 0x03 ); + ( *frame_sub_type ) = ( lr11xx_wifi_frame_sub_type_t ) ( ( frame_type_info >> 2 ) & 0x0F ); + ( *to_ds ) = ( bool ) ( ( frame_type_info >> 1 ) & 0x01 ); + ( *from_ds ) = ( bool ) ( frame_type_info & 0x01 ); +} + +void lr11xx_wifi_parse_data_rate_info( const lr11xx_wifi_datarate_info_byte_t data_rate_info, + lr11xx_wifi_signal_type_result_t* wifi_signal_type, + lr11xx_wifi_datarate_t* wifi_data_rate ) +{ + ( *wifi_signal_type ) = lr11xx_wifi_extract_signal_type_from_data_rate_info( data_rate_info ); + ( *wifi_data_rate ) = ( lr11xx_wifi_datarate_t ) ( data_rate_info >> 2 ); +} + +lr11xx_wifi_signal_type_result_t lr11xx_wifi_extract_signal_type_from_data_rate_info( + const lr11xx_wifi_datarate_info_byte_t data_rate_info ) +{ + return ( lr11xx_wifi_signal_type_result_t ) ( data_rate_info & 0x03 ); +} + +uint64_t lr11xx_wifi_get_consumption( lr11xx_system_reg_mode_t regulator, lr11xx_wifi_cumulative_timings_t timing ) +{ + uint64_t wifi_scan_consumption_uah = 0; + + wifi_scan_consumption_uah = ( timing.rx_capture_us * LR11XX_WIFI_CAPTURE_UA ) + + ( timing.demodulation_us * LR11XX_WIFI_DEMODULATION_UA ) + + ( timing.rx_correlation_us * LR11XX_WIFI_CORRELATION_UA ); + + wifi_scan_consumption_uah = + wifi_scan_consumption_uah / + ( 3600000000 - ( timing.rx_capture_us + timing.demodulation_us + timing.rx_correlation_us ) ); + + if( regulator == LR11XX_SYSTEM_REG_MODE_LDO ) + { + wifi_scan_consumption_uah *= 2; + } + + return wifi_scan_consumption_uah; +} + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DEFINITION -------------------------------------------- + */ + +static lr11xx_hal_status_t lr11xx_wifi_read_results_helper( const void* context, const uint8_t start_index, + const uint8_t n_elem, uint8_t* buffer, + const lr11xx_wifi_result_format_t result_format ) +{ + const uint8_t size_single_elem = lr11xx_wifi_get_result_size_from_format( result_format ); + const uint8_t result_format_code = lr11xx_wifi_get_format_code( result_format ); + const uint8_t cbuffer[LR11XX_WIFI_READ_RESULT_CMD_LENGTH] = { ( uint8_t ) ( LR11XX_WIFI_READ_RESULT_OC >> 8 ), + ( uint8_t ) ( LR11XX_WIFI_READ_RESULT_OC & 0x00FF ), + start_index, n_elem, result_format_code }; + const uint16_t size_total = n_elem * size_single_elem; + return lr11xx_hal_read( context, cbuffer, LR11XX_WIFI_READ_RESULT_CMD_LENGTH, buffer, size_total ); +} + +static uint16_t uint16_from_array( const uint8_t* array, const uint16_t index ) +{ + return ( uint16_t ) ( array[index] << 8 ) + ( ( uint16_t ) ( array[index + 1] ) ); +} + +static uint64_t uint64_from_array( const uint8_t* array, const uint16_t index ) +{ + return ( ( uint64_t ) ( array[index] ) << 56 ) + ( ( uint64_t ) ( array[index + 1] ) << 48 ) + + ( ( uint64_t ) ( array[index + 2] ) << 40 ) + ( ( uint64_t ) ( array[index + 3] ) << 32 ) + + ( ( uint64_t ) ( array[index + 4] ) << 24 ) + ( ( uint64_t ) ( array[index + 5] ) << 16 ) + + ( ( uint64_t ) ( array[index + 6] ) << 8 ) + ( uint64_t ) ( array[index + 7] ); +} + +static void lr11xx_wifi_read_mac_address_from_buffer( const uint8_t* buffer, const uint16_t index_in_buffer, + lr11xx_wifi_mac_address_t mac_address ) +{ + for( uint8_t field_mac_index = 0; field_mac_index < LR11XX_WIFI_MAC_ADDRESS_LENGTH; field_mac_index++ ) + { + mac_address[field_mac_index] = buffer[index_in_buffer + field_mac_index]; + } +} + +static uint8_t lr11xx_wifi_get_format_code( const lr11xx_wifi_result_format_t format ) +{ + uint8_t format_code = 0x00; + switch( format ) + { + case LR11XX_WIFI_RESULT_FORMAT_BASIC_COMPLETE: + { + format_code = 0x01; + break; + } + case LR11XX_WIFI_RESULT_FORMAT_BASIC_MAC_TYPE_CHANNEL: + { + format_code = 0x04; + break; + } + case LR11XX_WIFI_RESULT_FORMAT_EXTENDED_FULL: + { + format_code = 0x01; + break; + } + } + return format_code; +} + +static uint8_t lr11xx_wifi_get_result_size_from_format( const lr11xx_wifi_result_format_t format ) +{ + uint8_t result_size = 0; + switch( format ) + { + case LR11XX_WIFI_RESULT_FORMAT_BASIC_COMPLETE: + { + result_size = LR11XX_WIFI_BASIC_COMPLETE_RESULT_SIZE; + break; + } + case LR11XX_WIFI_RESULT_FORMAT_BASIC_MAC_TYPE_CHANNEL: + { + result_size = LR11XX_WIFI_BASIC_MAC_TYPE_CHANNEL_RESULT_SIZE; + break; + } + case LR11XX_WIFI_RESULT_FORMAT_EXTENDED_FULL: + { + result_size = LR11XX_WIFI_EXTENDED_COMPLETE_RESULT_SIZE; + break; + } + } + return result_size; +} + +static lr11xx_status_t fetch_and_aggregate_all_results( const void* context, const uint8_t index_result_start, + const uint8_t nb_results, + const uint8_t nb_results_per_chunk_max, + const lr11xx_wifi_result_format_t result_format_code, + uint8_t* result_buffer, + lr11xx_wifi_result_interface_t result_structures ) +{ + uint8_t index_to_read = index_result_start; + uint8_t index_result_start_writing = 0; + uint8_t remaining_results = nb_results; + + lr11xx_hal_status_t hal_status = LR11XX_HAL_STATUS_OK; + while( remaining_results > 0 ) + { + uint8_t results_to_read = MIN( remaining_results, nb_results_per_chunk_max ); + + lr11xx_hal_status_t local_hal_status = lr11xx_wifi_read_results_helper( context, index_to_read, results_to_read, + result_buffer, result_format_code ); + if( local_hal_status != LR11XX_HAL_STATUS_OK ) + { + return ( lr11xx_status_t ) local_hal_status; + } + + generic_results_interpreter( results_to_read, index_result_start_writing, result_buffer, result_structures, + result_format_code ); + + // Reset the content of the result_buffer in case there are still results to fetch + { + const uint16_t result_buffer_size = + LR11XX_WIFI_MAX_SIZE_PER_SPI( lr11xx_wifi_get_result_size_from_format( result_format_code ) ); + for( uint16_t index = 0; index < result_buffer_size; index++ ) + { + result_buffer[index] = 0; + } + } + + index_to_read += results_to_read; + index_result_start_writing += results_to_read; + remaining_results -= results_to_read; + } + return ( lr11xx_status_t ) hal_status; +} + +static void generic_results_interpreter( const uint8_t n_result_to_parse, const uint8_t index_result_start_writing, + const uint8_t* buffer, lr11xx_wifi_result_interface_t result_interface, + const lr11xx_wifi_result_format_t format_code ) +{ + switch( format_code ) + { + case LR11XX_WIFI_RESULT_FORMAT_BASIC_COMPLETE: + { + interpret_basic_complete_result_from_buffer( n_result_to_parse, index_result_start_writing, buffer, + result_interface.basic_complete ); + break; + } + + case LR11XX_WIFI_RESULT_FORMAT_BASIC_MAC_TYPE_CHANNEL: + { + interpret_basic_mac_type_channel_result_from_buffer( n_result_to_parse, index_result_start_writing, buffer, + result_interface.basic_mac_type_channel ); + break; + } + + case LR11XX_WIFI_RESULT_FORMAT_EXTENDED_FULL: + { + interpret_extended_full_result_from_buffer( n_result_to_parse, index_result_start_writing, buffer, + result_interface.extended_complete ); + break; + } + } +} + +static void interpret_basic_complete_result_from_buffer( const uint8_t nb_results, + const uint8_t index_result_start_writing, + const uint8_t* buffer, + lr11xx_wifi_basic_complete_result_t* result ) +{ + for( uint8_t result_index = 0; result_index < nb_results; result_index++ ) + { + const uint16_t local_index_start = LR11XX_WIFI_BASIC_COMPLETE_RESULT_SIZE * result_index; + lr11xx_wifi_basic_complete_result_t* local_wifi_result = &result[index_result_start_writing + result_index]; + local_wifi_result->data_rate_info_byte = buffer[local_index_start + 0]; + local_wifi_result->channel_info_byte = buffer[local_index_start + 1]; + local_wifi_result->rssi = buffer[local_index_start + 2]; + local_wifi_result->frame_type_info_byte = buffer[local_index_start + 3]; + lr11xx_wifi_read_mac_address_from_buffer( buffer, local_index_start + 4, local_wifi_result->mac_address ); + local_wifi_result->phi_offset = uint16_from_array( buffer, local_index_start + 10 ); + local_wifi_result->timestamp_us = uint64_from_array( buffer, local_index_start + 12 ); + local_wifi_result->beacon_period_tu = uint16_from_array( buffer, local_index_start + 20 ); + } +} + +static void interpret_basic_mac_type_channel_result_from_buffer( const uint8_t nb_results, + const uint8_t index_result_start_writing, + const uint8_t* buffer, + lr11xx_wifi_basic_mac_type_channel_result_t* result ) +{ + for( uint8_t result_index = 0; result_index < nb_results; result_index++ ) + { + const uint16_t local_index_start = LR11XX_WIFI_BASIC_MAC_TYPE_CHANNEL_RESULT_SIZE * result_index; + lr11xx_wifi_basic_mac_type_channel_result_t* local_wifi_result = + &result[index_result_start_writing + result_index]; + local_wifi_result->data_rate_info_byte = buffer[local_index_start + 0]; + local_wifi_result->channel_info_byte = buffer[local_index_start + 1]; + local_wifi_result->rssi = buffer[local_index_start + 2]; + lr11xx_wifi_read_mac_address_from_buffer( buffer, local_index_start + 3, local_wifi_result->mac_address ); + } +} + +void interpret_extended_full_result_from_buffer( const uint8_t nb_results, const uint8_t index_result_start_writing, + const uint8_t* buffer, lr11xx_wifi_extended_full_result_t* result ) +{ + for( uint8_t result_index = 0; result_index < nb_results; result_index++ ) + { + const uint16_t local_index_start = LR11XX_WIFI_EXTENDED_COMPLETE_RESULT_SIZE * result_index; + lr11xx_wifi_extended_full_result_t* local_wifi_result = &result[index_result_start_writing + result_index]; + + local_wifi_result->data_rate_info_byte = buffer[local_index_start + 0]; + local_wifi_result->channel_info_byte = buffer[local_index_start + 1]; + local_wifi_result->rssi = buffer[local_index_start + 2]; + local_wifi_result->rate = buffer[local_index_start + 3]; + local_wifi_result->service = uint16_from_array( buffer, local_index_start + 4 ); + local_wifi_result->length = uint16_from_array( buffer, local_index_start + 6 ); + local_wifi_result->frame_control = uint16_from_array( buffer, local_index_start + 8 ); + lr11xx_wifi_read_mac_address_from_buffer( buffer, local_index_start + 10, local_wifi_result->mac_address_1 ); + lr11xx_wifi_read_mac_address_from_buffer( buffer, local_index_start + 16, local_wifi_result->mac_address_2 ); + lr11xx_wifi_read_mac_address_from_buffer( buffer, local_index_start + 22, local_wifi_result->mac_address_3 ); + local_wifi_result->timestamp_us = uint64_from_array( buffer, local_index_start + 28 ); + local_wifi_result->beacon_period_tu = uint16_from_array( buffer, local_index_start + 36 ); + local_wifi_result->seq_control = uint16_from_array( buffer, local_index_start + 38 ); + for( uint8_t ssid_index = 0; ssid_index < LR11XX_WIFI_RESULT_SSID_LENGTH; ssid_index++ ) + { + local_wifi_result->ssid_bytes[ssid_index] = buffer[local_index_start + ssid_index + 40]; + } + local_wifi_result->current_channel = buffer[local_index_start + 72]; + local_wifi_result->country_code[0] = buffer[local_index_start + 73]; + local_wifi_result->country_code[1] = buffer[local_index_start + 74]; + local_wifi_result->io_regulation = buffer[local_index_start + 75]; + local_wifi_result->fcs_check_byte.is_fcs_checked = ( ( buffer[local_index_start + 76] & 0x01 ) == 0x01 ); + local_wifi_result->fcs_check_byte.is_fcs_ok = ( ( buffer[local_index_start + 76] & 0x02 ) == 0x02 ); + local_wifi_result->phi_offset = uint16_from_array( buffer, local_index_start + 77 ); + } +} + +bool lr11xx_wifi_is_well_formed_utf8_byte_sequence( const uint8_t* buffer, const uint8_t length ) +{ + uint8_t index = 0; + + while( index < length ) + { + if( IS_BETWEEN( buffer[index], 0x00, 0x7F ) ) + { + index += 1; + continue; + } + + if( length - index >= 2 ) + { + if( IS_BETWEEN( buffer[index], 0xC2, 0xDF ) && IS_BETWEEN_0x80_AND_0xBF( buffer[index + 1] ) ) + { + index += 2; + continue; + } + + if( length - index >= 3 ) + { + if( ( buffer[index] == 0xE0 ) && IS_BETWEEN( buffer[index + 1], 0xA0, 0xBF ) && + IS_BETWEEN_0x80_AND_0xBF( buffer[index + 2] ) ) + { + index += 3; + continue; + } + else if( IS_BETWEEN( buffer[index], 0xE1, 0xEC ) && IS_BETWEEN_0x80_AND_0xBF( buffer[index + 1] ) && + IS_BETWEEN_0x80_AND_0xBF( buffer[index + 2] ) ) + { + index += 3; + continue; + } + else if( ( buffer[index] == 0xED ) && IS_BETWEEN( buffer[index + 1], 0x80, 0x9F ) && + IS_BETWEEN_0x80_AND_0xBF( buffer[index + 2] ) ) + { + index += 3; + continue; + } + else if( IS_BETWEEN( buffer[index], 0xEE, 0xEF ) && IS_BETWEEN_0x80_AND_0xBF( buffer[index + 1] ) && + IS_BETWEEN_0x80_AND_0xBF( buffer[index + 2] ) ) + { + index += 3; + continue; + } + + if( length - index >= 4 ) + { + if( ( buffer[index] == 0xF0 ) && IS_BETWEEN( buffer[index + 1], 0x90, 0xBF ) && + IS_BETWEEN_0x80_AND_0xBF( buffer[index + 2] ) && IS_BETWEEN_0x80_AND_0xBF( buffer[index + 3] ) ) + { + index += 4; + continue; + } + else if( IS_BETWEEN( buffer[index], 0xF1, 0xF3 ) && IS_BETWEEN_0x80_AND_0xBF( buffer[index + 1] ) && + IS_BETWEEN_0x80_AND_0xBF( buffer[index + 2] ) && + IS_BETWEEN_0x80_AND_0xBF( buffer[index + 3] ) ) + { + index += 4; + continue; + } + else if( ( buffer[index] == 0xF4 ) && IS_BETWEEN( buffer[index + 1], 0x80, 0x8F ) && + IS_BETWEEN_0x80_AND_0xBF( buffer[index + 2] ) && + IS_BETWEEN_0x80_AND_0xBF( buffer[index + 3] ) ) + { + index += 4; + continue; + } + } + } + } + + return false; + } + + return true; +} + +bool lr11xx_wifi_are_scan_mode_result_format_compatible( lr11xx_wifi_mode_t scan_mode, + lr11xx_wifi_result_format_t result_format ) +{ + switch( scan_mode ) + { + case LR11XX_WIFI_SCAN_MODE_BEACON: + case LR11XX_WIFI_SCAN_MODE_BEACON_AND_PKT: + { + switch( result_format ) + { + case LR11XX_WIFI_RESULT_FORMAT_BASIC_COMPLETE: + case LR11XX_WIFI_RESULT_FORMAT_BASIC_MAC_TYPE_CHANNEL: + { + return true; + } + + default: + { + return false; + } + } + break; + } + + case LR11XX_WIFI_SCAN_MODE_FULL_BEACON: + case LR11XX_WIFI_SCAN_MODE_UNTIL_SSID: + { + switch( result_format ) + { + case LR11XX_WIFI_RESULT_FORMAT_EXTENDED_FULL: + { + return true; + } + default: + { + return false; + } + } + } + + default: + { + return false; + } + } +} + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/src/lr11xx_wifi.h b/src/lr11xx_wifi.h new file mode 100644 index 0000000..e860f35 --- /dev/null +++ b/src/lr11xx_wifi.h @@ -0,0 +1,628 @@ +/*! + * @file lr11xx_wifi.h + * + * @brief Wi-Fi passive scan driver definition for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR11XX_WIFI_H +#define LR11XX_WIFI_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include "lr11xx_regmem.h" +#include "lr11xx_wifi_types.h" +#include "lr11xx_types.h" +#include "lr11xx_system_types.h" + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +#ifndef LR11XX_WIFI_N_RESULTS_MAX_PER_CHUNK +/*! + * @brief The number of results max to fetch per SPI communication with the chip + * + * This macro is used by the internals of the driver to size the internal + * buffers of the driver used in the *read results* functions. + * + * It can be defined externally at compile time, or just before including this file. + * + * Its value can be programmatically obtained at runtime by calling lr11xx_wifi_get_nb_results_max_per_chunk() function. + * + * Its default value is set to the maximum number of results saved by LR11XX chip. + * + * @warning Its value must be in the range [1,32] (inclusive). Defining out of this range leads to undefined behavior. + */ +#define LR11XX_WIFI_N_RESULTS_MAX_PER_CHUNK LR11XX_WIFI_MAX_RESULTS +#endif // LR11XX_WIFI_N_RESULTS_MAX_PER_CHUNK + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +/*! + * @brief Start a Wi-Fi passive scan operation + * + * During the complete passive scan operation, the LR11XX remains busy and cannot receive any commands. Using this + * command **DOES** reset the results already obtained by previous passive scan operations. + * + * The result can be read at the end of the passive scan issuing the command lr11xx_wifi_get_nb_results (to get the + * number of results to read) and lr11xx_wifi_read_basic_complete_results or + * lr11xx_wifi_read_basic_mac_type_channel_results to actually get the result bytes. + * + * @param [in] context Chip implementation context + * @param [in] signal_type The type of Wi-Fi Signals to scan for. If LR11XX_WIFI_TYPE_SCAN_B_G_N is selected, the LR11XX + * already starts by scanning all selected channels for Wi-Fi signals B. Then the LR11XX scans all selected channels for + * Wi-Fi signals G/N. + * @param [in] channels Mask of the Wi-Fi channels to scan + * @param [in] scan_mode Scan mode to execute + * @param [in] max_results The maximal number of results to gather. When this limit is reached, the passive scan + * automatically stop. Range of allowed values is [1:32]. Note that value 0 is forbidden. + * @param [in] nb_scan_per_channel The number of internal scan sequences per channel scanned. Range of accepted values + * is [1:255]. Note that value 0 is forbidden. + * @param [in] timeout_in_ms The maximal duration of a single preamble search. Expressed in ms. Range of allowed values + * is [1:65535]. Note that value 0 is forbidden. + * @param [in] abort_on_timeout If true, the beacon search jumps to next configured Wi-Fi channel (or stop if there is + * no more channel to scan) as soon as a search timeout is encountered + * + * @returns Operation status + * + * @see lr11xx_wifi_read_basic_complete_results, lr11xx_wifi_read_basic_mac_type_channel_results + */ +lr11xx_status_t lr11xx_wifi_scan( const void* context, const lr11xx_wifi_signal_type_scan_t signal_type, + const lr11xx_wifi_channel_mask_t channels, const lr11xx_wifi_mode_t scan_mode, + const uint8_t max_results, const uint8_t nb_scan_per_channel, + const uint16_t timeout_in_ms, const bool abort_on_timeout ); + +/*! + * @brief Start a Wi-Fi passive scan for country codes extraction + * + * This command starts a Wi-Fi passive scan operation for Beacons and Probe Responses on Wi-Fi type B only. It is to be + * used to extract the Country Code fields. + * + * During the passive scan, the results are filtered to keep only single MAC addresses. + * + * @param [in] context Chip implementation context + * @param [in] channels_mask Mask of the Wi-Fi channels to scan + * @param [in] nb_max_results The maximum number of country code to gather. When this limit is reached, the passive scan + * automatically stops. Maximal value is 32 + * @param [in] nb_scan_per_channel Maximal number of scan attempts per channel. Maximal value is 255 + * @param [in] timeout_in_ms The maximal duration of a single beacon search. Expressed in ms. Maximal value is 65535 ms + * @param [in] abort_on_timeout If true, the beacon search jumps to next configured Wi-Fi channel (or stop if there is + * no more channel to scan) as soon as a search timeout is encountered + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_wifi_search_country_code( const void* context, const lr11xx_wifi_channel_mask_t channels_mask, + const uint8_t nb_max_results, const uint8_t nb_scan_per_channel, + const uint16_t timeout_in_ms, const bool abort_on_timeout ); + +/*! + * @brief Start a Wi-Fi passive scan operation with duration stop conditions + * + * This passive scan API does not require the number of scan per channel, so + * that it searches for Wi-Fi signals until it finds one, or until the + * exhaustion of timeout_per_scan_ms or timeout_per_channel_ms. + * + * The maximal duration of a scan is determined by the number of channels to scan times the timeout_per_channel_ms + * configured. However, this duration may be exceeded depending on the crystal drift of the clock source and on the + * instant the last Wi-Fi signal is detected by the device. + * Therefore the maximal duration of a Wi-Fi scan with this API is provided by the following equations: + * + * For signal type being `LR11XX_WIFI_TYPE_SCAN_B`, `LR11XX_WIFI_TYPE_SCAN_G` or `LR11XX_WIFI_TYPE_SCAN_N`: + * + * \f$ T_{max} = N_{channel} \times ((1 + Xtal_{precision})timeout\_per\_channel + T_{offset} ) \f$ + * + * \f$ Xtal_{precision} \f$ depends on the crystal used as clock source. + * If the clock source is configured with 32kHz internal RC, then \f$ Xtal_{precision} = 1/100 \f$ + * + * \f$ T_{offset} \f$ depends on the \f$ signal\_type \f$ and the \f$scan\_mode\f$ selected: + * + * - LR11XX_WIFI_TYPE_SCAN_B: + * - if \f$scan\_mode != LR11XX\_WIFI\_SCAN\_MODE\_FULL\_BEACON\f$: 2.31 ms + * - if \f$scan\_mode == LR11XX\_WIFI\_SCAN\_MODE\_FULL\_BEACON\f$: 9.59 ms + * - LR11XX_WIFI_TYPE_SCAN_G: + * - if \f$scan\_mode != LR11XX\_WIFI\_SCAN\_MODE\_FULL\_BEACON\f$: 52.55 ms + * - if \f$scan\_mode == LR11XX\_WIFI\_SCAN\_MODE\_FULL\_BEACON\f$: N/A + * + * For signal type being `LR11XX_WIFI_TYPE_SCAN_B_G_N`: + * + * \f$ T_{max} = 2 \times N_{channel} \times (1 + Xtal_{precision})timeout\_per\_channel + T_{offset} \f$ + * + * \f$ T_{offset} \f$ depends on the \f$scan\_mode\f$ selected: + * - \f$scan\_mode != LR11XX\_WIFI\_SCAN\_MODE\_FULL\_BEACON\f$: 54.86 ms + * - \f$scan\_mode == LR11XX\_WIFI\_SCAN\_MODE\_FULL\_BEACON\f$: 9.59 ms. + * + * @note With \f$scan\_mode != LR11XX\_WIFI\_SCAN\_MODE\_FULL\_BEACON\f$ the T_offset is actually the worst case of + * Wi-Fi type B and Wi-Fi type G/N. Moreover, the Wi-Fi types G and N are scanned within the same steps (it is not two + * different scans). So the T_offset is the addition of 2.31 + 52.55 = 54.86. + * + * @note With \f$scan\_mode == LR11XX\_WIFI\_SCAN\_MODE\_FULL\_BEACON\f$, only Wi-Fi types B can be scanned. So scans + * for Wi-Fi types G/N are silently discarded. Therefore the T_offset is the same as for scan with Wi-Fi type B. + * + * @param [in] context Chip implementation context + * @param [in] signal_type The type of Wi-Fi Signals to scan for. If LR11XX_WIFI_TYPE_SCAN_B_G_N is selected, the LR11XX + * already starts by scanning all selected channels for Wi-Fi signals B. Then the LR11XX scans all selected channels for + * Wi-Fi signals G/N. + * @param [in] channels Mask of the Wi-Fi channels to scan + * @param [in] scan_mode Scan mode to execute + * @param [in] max_results The maximal number of results to gather. When this + * limit is reached, the passive scan automatically stop. Maximal value is 32 + * @param [in] timeout_per_channel_ms The time to spend scanning one channel. Expressed in ms. Value 0 is forbidden and + * will result in the raise of WIFI_SCAN_DONE interrupt, with stat1.command_status being set to + * LR11XX_SYSTEM_CMD_STATUS_PERR + * @param [in] timeout_per_scan_ms The maximal time to spend in preamble detection for each single scan. The time spent + * on preamble search is reset at each new preamble search. If the time spent on preamble search reach this timeout, the + * scan on the current channel stops and start on next channel. If set to 0, the command will keep listening until + * exhaustion of timeout_per_channel_ms or until nb_max_results is reached. Expressed in ms. Range of allowed values is + * [0:65535]. + * + * @returns Operation status + * + * @see lr11xx_wifi_read_basic_results, lr11xx_wifi_read_extended_results + */ +lr11xx_status_t lr11xx_wifi_scan_time_limit( const void* context, const lr11xx_wifi_signal_type_scan_t signal_type, + const lr11xx_wifi_channel_mask_t channels, + const lr11xx_wifi_mode_t scan_mode, const uint8_t max_results, + const uint16_t timeout_per_channel_ms, + const uint16_t timeout_per_scan_ms ); + +/*! + * @brief Start a Wi-Fi passive scan for country codes extraction with duration stop conditions + * + * This command starts a Wi-Fi passive scan operation for Beacons and Probe Responses on Wi-Fi type B only. It is to be + * used to extract the Country Code fields. + * This passive scan API does not require the number of scan per channel, so that it searches for Wi-Fi signals until it + * finds one, or until the exhaustion of timeout_per_scan_ms or timeout_per_channel_ms. + * + * The maximal duration of a scan is determined by the number of channels to scan times the timeout_per_channel_ms + * configured. However, this duration may be exceeded depending on the crystal drift of the clock source and on the + * instant the last Wi-Fi signal is detected by the device. + * Therefore the maximal duration of a Wi-Fi scan with this API is provided by the following equation: + * + * \f$ T_{max} = N_{channel} \times ((1 + Xtal_{precision})timeout\_per\_channel + T_{offset} ) \f$ + * + * \f$ Xtal_{precision} \f$ depends on the crystal used as clock source. + * If the clock source is configured with 32kHz internal RC, then \f$ Xtal_{precision} = 1/100 \f$ + * + * \f$ T_{offset} \f$ is always the same: 9.59 ms. + * + * @param [in] context Chip implementation context + * @param [in] channels_mask Mask of the Wi-Fi channels to scan + * @param [in] nb_max_results The maximum number of country code to gather. When this limit is reached, the passive scan + * automatically stops. Maximal value is 32 + * @param [in] timeout_per_channel_ms The time to spend scanning one channel. Expressed in ms. Value 0 is forbidden and + * will result in the raise of WIFI_SCAN_DONE interrupt, with stat1.command_status being set to + * LR11XX_SYSTEM_CMD_STATUS_PERR + * @param [in] timeout_per_scan_ms The maximal time to spend in preamble detection for each single scan. The time spent + * on preamble search is reset at each new preamble search. If the time spent on preamble search reach this timeout, the + * scan on the current channel stops and start on next channel. If set to 0, the command will keep listening until + * exhaustion of timeout_per_channel_ms or until nb_max_results is reached. Expressed in ms. Range of allowed values is + * [0:65535]. + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_wifi_search_country_code_time_limit( const void* context, + const lr11xx_wifi_channel_mask_t channels_mask, + const uint8_t nb_max_results, + const uint16_t timeout_per_channel_ms, + const uint16_t timeout_per_scan_ms ); + +/*! + * @brief Returns the number of results currently available in LR11XX + * + * It can be called before lr11xx_wifi_read_basic_complete_results or lr11xx_wifi_read_basic_mac_type_channel_results to + * know the number of results. + * + * @param [in] context Chip implementation context + * @param [out] nb_results The number of results available in the LR11XX + * + * @returns Operation status + * + * @see lr11xx_wifi_read_basic_complete_results, lr11xx_wifi_read_basic_mac_type_channel_results + */ +lr11xx_status_t lr11xx_wifi_get_nb_results( const void* context, uint8_t* nb_results ); + +/*! + * @brief Read basic complete results + * + * This function can be used to fetch all results in a row, or one after the other. + * It corresponds to result format @ref ::LR11XX_WIFI_RESULT_FORMAT_BASIC_COMPLETE. + * + * An example of usage to fetch all results in a row is: + * \code{.cpp} + * uint8_t nb_results = 0; + * lr11xx_wifi_get_nb_results(&radio, &nb_results); + * lr11xx_wifi_basic_complete_result_t all_results[LR11XX_WIFI_MAX_RESULTS] = {0}; + * lr11xx_wifi_read_basic_complete_results(&radio, 0, nb_results, all_results); + * \endcode + * + * On the other hand, fetching result one after the other: + * \code{.cpp} + * uint8_t nb_results = 0; + * lr11xx_wifi_get_nb_results(&radio, &nb_results); + * lr11xx_wifi_basic_complete_result_t single_results = {0}; + * for(uint8_t index_result = 0; index_result < nb_results; index_result++){ + * lr11xx_wifi_read_basic_complete_results(&radio, index_result, 1, &single_results); + * // Do something with single_results + * } + * \endcode + * + * @remark This result fetching function **MUST** be used only if the scan function call was made with Scan Mode set to + * @ref ::LR11XX_WIFI_SCAN_MODE_BEACON or @ref ::LR11XX_WIFI_SCAN_MODE_BEACON_AND_PKT. + * Refer to @ref lr11xx_wifi_are_scan_mode_result_format_compatible to know which scan mode and result format are + * compatible. + * + * @param [in] radio Radio abstraction + * @param [in] start_result_index Result index from which starting to fetch the results + * @param [in] nb_results Number of results to fetch + * @param [out] results Pointer to an array of result structures to populate. It is up to the caller to ensure this + * array can hold at least nb_results elements. + * + * @returns Operation status + * + * @see lr11xx_wifi_are_scan_mode_result_format_compatible, lr11xx_wifi_read_basic_mac_type_channel_results, + * lr11xx_wifi_read_extended_full_results + */ +lr11xx_status_t lr11xx_wifi_read_basic_complete_results( const void* context, const uint8_t start_result_index, + const uint8_t nb_results, + lr11xx_wifi_basic_complete_result_t* results ); + +/*! + * @brief Read basic MAC, Wi-Fi type and channel results + * + * This function can be used to fetch all results in a row, or one after the other. + * It corresponds to result format @ref ::LR11XX_WIFI_RESULT_FORMAT_BASIC_MAC_TYPE_CHANNEL. + * + * An example of usage to fetch all results in a row is: + * \code{.cpp} + * uint8_t nb_results = 0; + * lr11xx_wifi_get_nb_results(&radio, &nb_results); + * lr11xx_wifi_basic_mac_type_channel_result_t all_results[LR11XX_WIFI_MAX_RESULTS] = {0}; + * lr11xx_wifi_read_basic_mac_type_channel_results(&radio, 0, nb_results, all_results); + * \endcode + * + * On the other hand, fetching result one after the other: + * \code{.cpp} + * uint8_t nb_results = 0; + * lr11xx_wifi_get_nb_results(&radio, &nb_results); + * lr11xx_wifi_basic_mac_type_channel_result_t single_results = {0}; + * for(uint8_t index_result = 0; index_result < nb_results; index_result++){ + * lr11xx_wifi_read_basic_mac_type_channel_results(&radio, index_result, 1, &single_results); + * // Do something with single_results + * } + * \endcode + * + * @remark This result fetching function **MUST** be used only if the scan function call was made with Scan Mode set to + * @ref ::LR11XX_WIFI_SCAN_MODE_BEACON or @ref ::LR11XX_WIFI_SCAN_MODE_BEACON_AND_PKT. + * Refer to @ref lr11xx_wifi_are_scan_mode_result_format_compatible to know which scan mode and result format are + * compatible. + * + * @param [in] radio Radio abstraction + * @param [in] start_result_index Result index from which starting to fetch the results + * @param [in] nb_results Number of results to fetch + * @param [out] results Pointer to an array of result structures to populate. It is up to the caller to ensure this + * array can hold at least nb_results elements. + * + * @returns Operation status + * + * @see lr11xx_wifi_are_scan_mode_result_format_compatible, lr11xx_wifi_read_basic_complete_results, + * lr11xx_wifi_read_extended_full_results + */ +lr11xx_status_t lr11xx_wifi_read_basic_mac_type_channel_results( const void* context, const uint8_t start_result_index, + const uint8_t nb_results, + lr11xx_wifi_basic_mac_type_channel_result_t* results ); + +/*! + * @brief Read extended complete results + * + * This function can be used to fetch all results in a row, or one after the other. + * It corresponds to result format @ref ::LR11XX_WIFI_RESULT_FORMAT_EXTENDED_FULL. + * + * An example of usage to fetch all results in a row is: + * \code{.cpp} + * uint8_t nb_results = 0; + * lr11xx_wifi_get_nb_results(&radio, &nb_results); + * lr11xx_wifi_extended_full_result_t all_results[LR11XX_WIFI_MAX_RESULTS] = {0}; + * lr11xx_wifi_read_extended_full_results(&radio, 0, nb_results, all_results); + * \endcode + * + * On the other hand, fetching result one after the other: + * \code{.cpp} + * uint8_t nb_results = 0; + * lr11xx_wifi_get_nb_results(&radio, &nb_results); + * lr11xx_wifi_extended_full_result_t single_results = {0}; + * for(uint8_t index_result = 0; index_result < nb_results; index_result++){ + * lr11xx_wifi_read_extended_full_results(&radio, index_result, 1, &single_results); + * // Do something with single_results + * } + * \endcode + * + * @remark This result fetching function **MUST** be used only if the scan function call was made with Scan Mode set to + * @ref ::LR11XX_WIFI_SCAN_MODE_FULL_BEACON. + * Refer to @ref lr11xx_wifi_are_scan_mode_result_format_compatible to know which scan mode and result format are + * compatible. + * + * @param [in] radio Radio abstraction + * @param [in] start_result_index Result index from which starting to fetch the results + * @param [in] nb_results Number of results to fetch + * @param [out] results Pointer to an array of result structures to populate. It is up to the caller to ensure this + * array can hold at least nb_results elements. + * + * @returns Operation status + * + * @see lr11xx_wifi_are_scan_mode_result_format_compatible, lr11xx_wifi_read_basic_complete_results, + * lr11xx_wifi_read_basic_mac_type_channel_results + */ +lr11xx_status_t lr11xx_wifi_read_extended_full_results( const void* radio, const uint8_t start_result_index, + const uint8_t nb_results, + lr11xx_wifi_extended_full_result_t* results ); + +/*! + * @brief Reset the internal counters of cumulative timing + * + * @param [in] context Chip implementation context + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_wifi_reset_cumulative_timing( const void* context ); + +/*! + * @brief Read the internal counters of cumulative timing + * + * @param [in] context Chip implementation context + * @param [out] timing A pointer to the cumulative timing structure to populate + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_wifi_read_cumulative_timing( const void* context, lr11xx_wifi_cumulative_timings_t* timing ); + +/*! + * @brief Get size of country code search results + * + * @param [in] context Chip implementation context + * @param [out] nb_country_code_results Number of country results to read + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_wifi_get_nb_country_code_results( const void* context, uint8_t* nb_country_code_results ); + +/*! + * @brief Read country code results + * + * The total number of country code results to read is obtained from a previous call to + * lr11xx_wifi_get_nb_country_code_results + * + * @param [in] context Chip implementation context + * @param [in] start_result_index The result index to start reading results from + * @param [in] nb_country_results Number of country code results to read + * @param [out] country_code_results An array of lr11xx_wifi_country_code_t to be filled. It is up to the application to + * ensure this array is big enough to hold nb_country_results elements + * + * @returns Operation status + * + * @see lr11xx_wifi_get_nb_country_code_results, lr11xx_wifi_search_country_code + */ +lr11xx_status_t lr11xx_wifi_read_country_code_results( const void* context, const uint8_t start_result_index, + const uint8_t nb_country_results, + lr11xx_wifi_country_code_t* country_code_results ); + +/*! + * @brief Configure the timestamp used to discriminate mobile access points from gateways. + * + * This filtering is based on the hypothesis that mobile access points have timestamp shorter than gateways. + * + * @param [in] context Chip implementation context + * @param [in] timestamp_in_s Timestamp value in second + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_wifi_cfg_timestamp_ap_phone( const void* context, uint32_t timestamp_in_s ); + +/*! + * @brief Get the internal wifi firmware version + * + * @param [in] context Chip implementation context + * @param [out] wifi_version The wifi version structure populated with version numbers + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_wifi_read_version( const void* context, lr11xx_wifi_version_t* wifi_version ); + +/*! + * @brief Retreive channel information from channel info byte + * + * This method is to be called with on the WiFi channel info byte of a scan result. + * + * As the WiFi passive scan allows to get Access Point MAC address from Packet WiFi frames, it is possible that the + * frame does not comes from the Access Point, but from a device. In that case, the RSSI reported by LR11XX is the one + * of the frame received from the device and not from the Access Point. The rssi_validity flag allows to detect that + * case. + * + * It is possible for an Access Point to be a mobile AP, which is of low interest for location purpose. The LR11XX tries + * to detect mobile AP based on Access Point up time and set the flag mac_origin_estimation accordingly. + * + * @param [in] channel_info The channel info byte to retrieve channel information from. It is obtained from WiFi + * passive scan result + * @param [out] channel The channel of the scanned mac address + * @param [out] rssi_validity The validity of the scanned MAC address + * @param [out] mac_origin_estimation Indicates the estimation of MAC address origin by LR11XX + * + * @see lr11xx_wifi_read_basic_complete_results, lr11xx_wifi_read_basic_mac_type_channel_results, + * lr11xx_wifi_cfg_timestamp_ap_phone + */ +void lr11xx_wifi_parse_channel_info( const lr11xx_wifi_channel_info_byte_t channel_info, lr11xx_wifi_channel_t* channel, + bool* rssi_validity, lr11xx_wifi_mac_origin_t* mac_origin_estimation ); + +/*! + * @brief Helper method to retrieve channel from channel info byte + * + * @param [in] channel_info The chanel info byte from passive scan result + * + * @returns The channel of scanned MAC address + * + * @see lr11xx_wifi_parse_channel_info + */ +lr11xx_wifi_channel_t lr11xx_wifi_extract_channel_from_info_byte( const lr11xx_wifi_channel_info_byte_t channel_info ); + +/*! + * @brief Retrieve the Frame Type, Frame Subtype, To/From DS fields from a frame info byte + * + * This method is intended to be called on the channel info byte of a passive scan result structure. + * + * The from_ds/to_ds (Distribution Station) fields have the following meaning: + * + * + * + * + *
to_ds value from_ds value Meaning
False False Frame was between two Stations + *
True False Frame was from Station to + * Access Point
False True Frame was sent + * from Access Point or Distribution Stations
True + * True Mesh network only, frame was between Stations
+ * + * @param [in] frame_type_info The frame info byte from passive scan result + * @param [out] frame_type The Frame Type of the received frame + * @param [out] frame_sub_type The Frame SubType of the frame received + * @param [out] to_ds to_ds field of the frame received + * @param [out] from_ds from_ds field of the frame received + */ +void lr11xx_wifi_parse_frame_type_info( const lr11xx_wifi_frame_type_info_byte_t frame_type_info, + lr11xx_wifi_frame_type_t* frame_type, + lr11xx_wifi_frame_sub_type_t* frame_sub_type, bool* to_ds, bool* from_ds ); + +/*! + * @brief Retrieve the data rate information from data rate info byte + * + * This method is intended to be called on a data rate info byte of a passive scan result structure. + * + * @param [in] data_rate_info The data rate info byte from a passive scan result + * @param [out] wifi_signal_type The wifi signal type of the scanned frame + * @param [out] wifi_data_rate The data rate of the scanned frame + */ +void lr11xx_wifi_parse_data_rate_info( const lr11xx_wifi_datarate_info_byte_t data_rate_info, + lr11xx_wifi_signal_type_result_t* wifi_signal_type, + lr11xx_wifi_datarate_t* wifi_data_rate ); + +/*! + * @brief Return the maximal number of results to read per SPI communication + * + * This function **DOES NOT** communicates with the LR11XX. It returns the driver maximal number of Wi-Fi results it can + * retrieve per SPI communication. + * + * @remark It is a driver limitation, not a LR11XX limitation, that avoid allocating temporary buffers of size too big + * when reading Wi-Fi passive scan results. + * + * @see LR11XX_WIFI_N_RESULTS_MAX_PER_CHUNK + * + * @returns The maximal number of results to fetch per SPI calls + */ +uint8_t lr11xx_wifi_get_nb_results_max_per_chunk( void ); + +/*! + * @brief Helper method to retrieve the signal type from data rate info byte + * + * @param [in] data_rate_info The data rate info byte from a passive scan result + * + * @returns The Signal Type of the scanned frame + */ +lr11xx_wifi_signal_type_result_t lr11xx_wifi_extract_signal_type_from_data_rate_info( + const lr11xx_wifi_datarate_info_byte_t data_rate_info ); + +/*! + * @brief Helper function to check if a buffer is a well-formed UTF-8 byte sequence + * + * @param [in] buffer The buffer holding the bytes to be analyzed + * @param [in] length The number of bytes in the buffer + * + * @returns The result of the check + */ +bool lr11xx_wifi_is_well_formed_utf8_byte_sequence( const uint8_t* buffer, const uint8_t length ); + +/*! + * @brief Check that Wi-Fi scan mode and result format are compatible + * + * The possible combination of Wi-Fi scan modes and result format are the following: + * + * + *
Scan Mode Type/Sub-type selected Corresponding read result function + *
@ref ::LR11XX_WIFI_SCAN_MODE_BEACON Management/Beacon and Management/Probe Response + * @ref lr11xx_wifi_read_basic_complete_results, @ref lr11xx_wifi_read_basic_mac_type_channel_results
+ * @ref ::LR11XX_WIFI_SCAN_MODE_BEACON_AND_PKT Some from Management, Control and Data Types
+ * @ref ::LR11XX_WIFI_SCAN_MODE_FULL_BEACON Management/Beacon and Management/Probe Response @ref + * lr11xx_wifi_read_extended_full_results
@ref ::LR11XX_WIFI_SCAN_MODE_UNTIL_SSID Management/Beacon and + * Management/Probe Response - until SSID field + *
+ * + * @param scan_mode The scan mode used when calling the scan API + * @param result_format The result format used when calling the read result API + * @retval true The scan mode and result format are compatible + * @retval false The scan mode and result format are not compatible. + */ +bool lr11xx_wifi_are_scan_mode_result_format_compatible( lr11xx_wifi_mode_t scan_mode, + lr11xx_wifi_result_format_t result_format ); + +/** + * @brief Compute the power consumption in uAh based on the cumulative timing. + * + * @param [in] regulator The regulator used during last Wi-Fi passive scan + * @param [in] timing Cumulative timing structure to use for computation + * + * @returns Current consumption in uAh + */ +uint64_t lr11xx_wifi_get_consumption( lr11xx_system_reg_mode_t regulator, lr11xx_wifi_cumulative_timings_t timing ); + +#ifdef __cplusplus +} +#endif + +#endif // LR11XX_WIFI_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/src/lr11xx_wifi_types.h b/src/lr11xx_wifi_types.h new file mode 100644 index 0000000..44cc244 --- /dev/null +++ b/src/lr11xx_wifi_types.h @@ -0,0 +1,410 @@ +/*! + * @file lr11xx_wifi_types.h + * + * @brief Wi-Fi passive scan driver types for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR11XX_WIFI_TYPES_H +#define LR11XX_WIFI_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include +#include + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +#define LR11XX_WIFI_MAC_ADDRESS_LENGTH ( 6 ) +#define LR11XX_WIFI_MAX_RESULTS ( 32 ) +#define LR11XX_WIFI_RESULT_SSID_LENGTH ( 32 ) +#define LR11XX_WIFI_MAX_COUNTRY_CODE ( 32 ) +#define LR11XX_WIFI_STR_COUNTRY_CODE_SIZE ( 2 ) + +#define LR11XX_WIFI_CHANNEL_1_POS ( 0U ) //!< Channel at frequency 2.412 GHz +#define LR11XX_WIFI_CHANNEL_1_MASK ( 0x01UL << LR11XX_WIFI_CHANNEL_1_POS ) +#define LR11XX_WIFI_CHANNEL_2_POS ( 1U ) //!< Channel at frequency 2.417 GHz +#define LR11XX_WIFI_CHANNEL_2_MASK ( 0x01UL << LR11XX_WIFI_CHANNEL_2_POS ) +#define LR11XX_WIFI_CHANNEL_3_POS ( 2U ) //!< Channel at frequency 2.422 GHz +#define LR11XX_WIFI_CHANNEL_3_MASK ( 0x01UL << LR11XX_WIFI_CHANNEL_3_POS ) +#define LR11XX_WIFI_CHANNEL_4_POS ( 3U ) //!< Channel at frequency 2.427 GHz +#define LR11XX_WIFI_CHANNEL_4_MASK ( 0x01UL << LR11XX_WIFI_CHANNEL_4_POS ) +#define LR11XX_WIFI_CHANNEL_5_POS ( 4U ) //!< Channel at frequency 2.432 GHz +#define LR11XX_WIFI_CHANNEL_5_MASK ( 0x01UL << LR11XX_WIFI_CHANNEL_5_POS ) +#define LR11XX_WIFI_CHANNEL_6_POS ( 5U ) //!< Channel at frequency 2.437 GHz +#define LR11XX_WIFI_CHANNEL_6_MASK ( 0x01UL << LR11XX_WIFI_CHANNEL_6_POS ) +#define LR11XX_WIFI_CHANNEL_7_POS ( 6U ) //!< Channel at frequency 2.442 GHz +#define LR11XX_WIFI_CHANNEL_7_MASK ( 0x01UL << LR11XX_WIFI_CHANNEL_7_POS ) +#define LR11XX_WIFI_CHANNEL_8_POS ( 7U ) //!< Channel at frequency 2.447 GHz +#define LR11XX_WIFI_CHANNEL_8_MASK ( 0x01UL << LR11XX_WIFI_CHANNEL_8_POS ) +#define LR11XX_WIFI_CHANNEL_9_POS ( 8U ) //!< Channel at frequency 2.452 GHz +#define LR11XX_WIFI_CHANNEL_9_MASK ( 0x01UL << LR11XX_WIFI_CHANNEL_9_POS ) +#define LR11XX_WIFI_CHANNEL_10_POS ( 9U ) //!< Channel at frequency 2.457 GHz +#define LR11XX_WIFI_CHANNEL_10_MASK ( 0x01UL << LR11XX_WIFI_CHANNEL_10_POS ) +#define LR11XX_WIFI_CHANNEL_11_POS ( 10U ) //!< Channel at frequency 2.462 GHz +#define LR11XX_WIFI_CHANNEL_11_MASK ( 0x01UL << LR11XX_WIFI_CHANNEL_11_POS ) +#define LR11XX_WIFI_CHANNEL_12_POS ( 11U ) //!< Channel at frequency 2.467 GHz +#define LR11XX_WIFI_CHANNEL_12_MASK ( 0x01UL << LR11XX_WIFI_CHANNEL_12_POS ) +#define LR11XX_WIFI_CHANNEL_13_POS ( 12U ) //!< Channel at frequency 2.472 GHz +#define LR11XX_WIFI_CHANNEL_13_MASK ( 0x01UL << LR11XX_WIFI_CHANNEL_13_POS ) +#define LR11XX_WIFI_CHANNEL_14_POS ( 13U ) //!< Channel at frequency 2.484 GHz +#define LR11XX_WIFI_CHANNEL_14_MASK ( 0x01UL << LR11XX_WIFI_CHANNEL_14_POS ) + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/*! + * @brief Type to store a Wi-Fi channel mask + */ +typedef uint16_t lr11xx_wifi_channel_mask_t; + +/*! + * @brief Type to store a Wi-Fi channel info byte + */ +typedef uint8_t lr11xx_wifi_channel_info_byte_t; + +/*! + * @brief Type to store a Wi-Fi datarate info byte + */ +typedef uint8_t lr11xx_wifi_datarate_info_byte_t; + +/*! + * @brief Type to store a Wi-Fi frame type info byte + */ +typedef uint8_t lr11xx_wifi_frame_type_info_byte_t; + +/*! + * @brief Type to store a Wi-Fi frame sub_type + */ +typedef uint8_t lr11xx_wifi_frame_sub_type_t; + +/*! + * @brief Wi-Fi FCS info byte + */ +typedef struct lr11xx_wifi_fcs_info_byte_s +{ + bool is_fcs_ok; //!< True if the LR11XX has checked the FCS and the check succeeded + bool is_fcs_checked; //!< True if the LR11XX has checked the FCS +} lr11xx_wifi_fcs_info_byte_t; + +/*! + * @brief Type to store a MAC address + */ +typedef uint8_t lr11xx_wifi_mac_address_t[LR11XX_WIFI_MAC_ADDRESS_LENGTH]; + +/*! + * @brief Type to store the Country Code + */ +typedef uint8_t lr11xx_wifi_country_code_str_t[LR11XX_WIFI_STR_COUNTRY_CODE_SIZE]; + +/*! + * @brief Wi-Fi Channels index + */ +typedef enum +{ + LR11XX_WIFI_NO_CHANNEL = 0x00, + LR11XX_WIFI_CHANNEL_1 = 0x01, //!< Channel at frequency 2.412 GHz + LR11XX_WIFI_CHANNEL_2 = 0x02, //!< Channel at frequency 2.417 GHz + LR11XX_WIFI_CHANNEL_3 = 0x03, //!< Channel at frequency 2.422 GHz + LR11XX_WIFI_CHANNEL_4 = 0x04, //!< Channel at frequency 2.427 GHz + LR11XX_WIFI_CHANNEL_5 = 0x05, //!< Channel at frequency 2.432 GHz + LR11XX_WIFI_CHANNEL_6 = 0x06, //!< Channel at frequency 2.437 GHz + LR11XX_WIFI_CHANNEL_7 = 0x07, //!< Channel at frequency 2.442 GHz + LR11XX_WIFI_CHANNEL_8 = 0x08, //!< Channel at frequency 2.447 GHz + LR11XX_WIFI_CHANNEL_9 = 0x09, //!< Channel at frequency 2.452 GHz + LR11XX_WIFI_CHANNEL_10 = 0x0A, //!< Channel at frequency 2.457 GHz + LR11XX_WIFI_CHANNEL_11 = 0x0B, //!< Channel at frequency 2.462 GHz + LR11XX_WIFI_CHANNEL_12 = 0x0C, //!< Channel at frequency 2.467 GHz + LR11XX_WIFI_CHANNEL_13 = 0x0D, //!< Channel at frequency 2.472 GHz + LR11XX_WIFI_CHANNEL_14 = 0x0E, //!< Channel at frequency 2.484 GHz + LR11XX_WIFI_ALL_CHANNELS = 0x0F, +} lr11xx_wifi_channel_t; + +/*! + * @brief WiFi theoretical Datarates + */ +typedef enum +{ + LR11XX_WIFI_DATARATE_1_MBPS = 1, + LR11XX_WIFI_DATARATE_2_MBPS = 2, + LR11XX_WIFI_DATARATE_6_MBPS = 3, + LR11XX_WIFI_DATARATE_9_MBPS = 4, + LR11XX_WIFI_DATARATE_12_MBPS = 5, + LR11XX_WIFI_DATARATE_18_MBPS = 6, + LR11XX_WIFI_DATARATE_24_MBPS = 7, + LR11XX_WIFI_DATARATE_36_MBPS = 8, + LR11XX_WIFI_DATARATE_48_MBPS = 9, + LR11XX_WIFI_DATARATE_54_MBPS = 10, + LR11XX_WIFI_DATARATE_6_5_MBPS = 11, + LR11XX_WIFI_DATARATE_13_MBPS = 12, + LR11XX_WIFI_DATARATE_19_5_MBPS = 13, + LR11XX_WIFI_DATARATE_26_MBPS = 14, + LR11XX_WIFI_DATARATE_39_MBPS = 15, + LR11XX_WIFI_DATARATE_52_MBPS = 16, + LR11XX_WIFI_DATARATE_58_MBPS = 17, + LR11XX_WIFI_DATARATE_65_MBPS = 18, + LR11XX_WIFI_DATARATE_7_2_MBPS = 19, + LR11XX_WIFI_DATARATE_14_4_MBPS = 20, + LR11XX_WIFI_DATARATE_21_7_MBPS = 21, + LR11XX_WIFI_DATARATE_28_9_MBPS = 22, + LR11XX_WIFI_DATARATE_43_3_MBPS = 23, + LR11XX_WIFI_DATARATE_57_8_MBPS = 24, + LR11XX_WIFI_DATARATE_65_2_MBPS = 25, + LR11XX_WIFI_DATARATE_72_2_MBPS = 26, +} lr11xx_wifi_datarate_t; + +/*! + * @brief WiFi Frame Types + */ +typedef enum +{ + LR11XX_WIFI_FRAME_TYPE_MANAGEMENT = 0x00, + LR11XX_WIFI_FRAME_TYPE_CONTROL = 0x01, + LR11XX_WIFI_FRAME_TYPE_DATA = 0x02, +} lr11xx_wifi_frame_type_t; + +/*! + * @brief The WiFi MAC address origin + * + * @see lr11xx_wifi_parse_channel_info for details about the MAC address origin estimation of the LR11XX + */ +typedef enum +{ + LR11XX_WIFI_ORIGIN_BEACON_FIX_AP = 1, //!< MAC address extracted from a packet coming from a fix Access Point + LR11XX_WIFI_ORIGIN_BEACON_MOBILE_AP = 2, //!< MAC address extracted from a packet coming from a mobile Access Point + LR11XX_WIFI_ORIGIN_UNKNOWN = 3, //!< Impossible to determine the origin of the packet the MAC is extracted from +} lr11xx_wifi_mac_origin_t; + +/*! + * @brief Wi-Fi signal type for passive scanning configuration + * + * Note it is not possible to configure the WiFi passive scanning to search Wi-Fi type N GreenField. Only Wi-Fi type N + * Mixed Mode can be scanned by LR11XX. + * + * @warning ::LR11XX_WIFI_TYPE_SCAN_G and ::LR11XX_WIFI_TYPE_SCAN_N configurations are implemented the same way, and + * both will scan Wi-Fi type G **AND** Wi-Fi type N. + */ +typedef enum +{ + LR11XX_WIFI_TYPE_SCAN_B = 0x01, //!< Wi-Fi B + LR11XX_WIFI_TYPE_SCAN_G = 0x02, //!< Wi-Fi G + LR11XX_WIFI_TYPE_SCAN_N = 0x03, //!< Wi-Fi N + LR11XX_WIFI_TYPE_SCAN_B_G_N = 0x04, //!< Wi-Fi B and Wi-Fi G/N +} lr11xx_wifi_signal_type_scan_t; + +/*! + * @brief Wi-Fi signal type for passive scan results + * + * Note that the Wi-Fi N detected is Wi-Fi N Mixed mode, and not GreenField. + */ +typedef enum +{ + LR11XX_WIFI_TYPE_RESULT_B = 0x01, //!< WiFi B + LR11XX_WIFI_TYPE_RESULT_G = 0x02, //!< WiFi G + LR11XX_WIFI_TYPE_RESULT_N = 0x03, //!< WiFi N +} lr11xx_wifi_signal_type_result_t; + +/*! + * @brief Wi-Fi scan mode + * + * When the LR11XX receives a Wi-Fi frame, it starts demodulating it. Depending on the scan mode selected, only some + * Wi-Fi frame type/sub-types are to be kept. The demodulation step is stopped as soon as the LR11XX detects the current + * Wi-Fi frame is not of the required type/sub-types. This saves scan time and consumption. + * + * A Wi-Fi frame is never completely demodulated. The ::LR11XX_WIFI_SCAN_MODE_FULL_BEACON uses a special configuration + * allowing to demodulate more fields (until Frame Check Sequence field), at a price of higher scan duration and higher + * consumption. + * + * @note Not all results formats are available depending on the scan mode selected. Refer to + * @ref lr11xx_wifi_are_scan_mode_result_format_compatible to know which result formats are available depending on scan + * mode selected. + * + * @see lr11xx_wifi_are_scan_mode_result_format_compatible + */ +typedef enum +{ + LR11XX_WIFI_SCAN_MODE_BEACON = + 1, //!< Exposes Beacons and Probe Responses Access Points frames until Period Beacon field (Basic result) + LR11XX_WIFI_SCAN_MODE_BEACON_AND_PKT = + 2, //!< Exposes some Management Access Points frames until Period Beacon field, and some other packets frame + //!< until third Mac Address field (Basic result) + LR11XX_WIFI_SCAN_MODE_FULL_BEACON = + 4, //!< Exposes Beacons and Probes Responses Access Points frames until Frame Check Sequence (FCS) field + //!< (Extended result). In this mode, only signal type LR11XX_WIFI_TYPE_SCAN_B is executed and other signal + //!< types are silently discarded. + LR11XX_WIFI_SCAN_MODE_UNTIL_SSID = 5, //!< Exposes Beacons and Probes Responses Access Points frames until the end + //!< of SSID field (Extended result) - available since firmware 0x0306 +} lr11xx_wifi_mode_t; + +/*! + * @brief Cumulative timings + * + * This structure is representing the cumulative time spent in the different modes of Wi-Fi passive scanning procedure. + * All timings are provided in [us]. + * */ +typedef struct lr11xx_wifi_cumulative_timings_s +{ + uint32_t rx_detection_us; //!< Cumulative time spent during NFE or TOA + uint32_t rx_correlation_us; //!< Cumulative time spent during preamble detection + uint32_t rx_capture_us; //!< Cumulative time spent during signal acquisition + uint32_t demodulation_us; //!< Cumulative time spent during software demodulation +} lr11xx_wifi_cumulative_timings_t; + +/*! + * @brief Basic complete result structure + * + * The beacon period is expressed in TU (Time Unit). 1 TU is 1024 microseconds. + */ +typedef struct lr11xx_wifi_basic_complete_result_s +{ + lr11xx_wifi_datarate_info_byte_t data_rate_info_byte; + lr11xx_wifi_channel_info_byte_t channel_info_byte; + int8_t rssi; + lr11xx_wifi_frame_type_info_byte_t frame_type_info_byte; + lr11xx_wifi_mac_address_t mac_address; + int16_t phi_offset; + uint64_t timestamp_us; //!< Indicate the up-time of the Access Point transmitting the Beacon [us] + uint16_t beacon_period_tu; +} lr11xx_wifi_basic_complete_result_t; + +/*! + * @brief Basic MAC, type, channel result structure + */ +typedef struct lr11xx_wifi_basic_mac_type_channel_result_s +{ + lr11xx_wifi_datarate_info_byte_t data_rate_info_byte; + lr11xx_wifi_channel_info_byte_t channel_info_byte; + int8_t rssi; + lr11xx_wifi_mac_address_t mac_address; +} lr11xx_wifi_basic_mac_type_channel_result_t; + +/*! + * @brief Extended full result structure + * + * @note The beacon period is expressed in TU (Time Unit). 1 TU is 1024 microseconds. + * + * @remark When used with @ref ::LR11XX_WIFI_SCAN_MODE_UNTIL_SSID, the following field are always set to 0: + * - field is_fcs_ok and is_fcs_checked in fcs_check_byte structure + * - current_channel + * - country_code + * - io_regulation + */ +typedef struct +{ + lr11xx_wifi_datarate_info_byte_t data_rate_info_byte; + lr11xx_wifi_channel_info_byte_t channel_info_byte; + int8_t rssi; + uint8_t rate; //!< Rate index + uint16_t service; //!< Service value + uint16_t length; //!< Length of MPDU (in microseconds for WiFi B, bytes for WiFi G) + uint16_t frame_control; //!< Frame Control structure + lr11xx_wifi_mac_address_t mac_address_1; + lr11xx_wifi_mac_address_t mac_address_2; + lr11xx_wifi_mac_address_t mac_address_3; + uint64_t timestamp_us; //!< Indicate the up-time of the Access Point + //!< transmitting the Beacon [us] + uint16_t beacon_period_tu; + uint16_t seq_control; //!< Sequence Control value + uint8_t ssid_bytes[LR11XX_WIFI_RESULT_SSID_LENGTH]; //!< Service Set + //!< IDentifier + lr11xx_wifi_channel_t current_channel; //!< Current channel indicated in the Wi-Fi frame + lr11xx_wifi_country_code_str_t country_code; //!< Country Code + uint8_t io_regulation; //!< Input Output Regulation + lr11xx_wifi_fcs_info_byte_t fcs_check_byte; //!< Frame Check Sequence info + int16_t phi_offset; +} lr11xx_wifi_extended_full_result_t; + +/*! + * @brief Wi-Fi scan result formats + * + * @note Result format to use depends on the scan mode selected when calling @ref lr11xx_wifi_scan or @ref + * lr11xx_wifi_scan_time_limit API. Refer to @ref lr11xx_wifi_are_scan_mode_result_format_compatible to know which + * result formats are available depending on scan mode selected. + * + * @see lr11xx_wifi_are_scan_mode_result_format_compatible + */ +typedef enum +{ + LR11XX_WIFI_RESULT_FORMAT_BASIC_COMPLETE, //!< Basic complete result format: @ref + //!< lr11xx_wifi_basic_complete_result_t + LR11XX_WIFI_RESULT_FORMAT_BASIC_MAC_TYPE_CHANNEL, //!< Basic MAC/type/channel result format: @ref + //!< lr11xx_wifi_basic_mac_type_channel_result_t + LR11XX_WIFI_RESULT_FORMAT_EXTENDED_FULL, //!< Extended full result format: @ref lr11xx_wifi_extended_full_result_t +} lr11xx_wifi_result_format_t; + +/*! + * @brief Wi-Fi country code structure + */ +typedef struct lr11xx_wifi_country_code_s +{ + lr11xx_wifi_country_code_str_t country_code; + uint8_t io_regulation; //!< Input Output Regulation + lr11xx_wifi_channel_info_byte_t channel_info_byte; + lr11xx_wifi_mac_address_t mac_address; +} lr11xx_wifi_country_code_t; + +/*! + * @brief Wi-Fi firmware version + */ +typedef struct lr11xx_wifi_version_s +{ + uint8_t major; + uint8_t minor; +} lr11xx_wifi_version_t; + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +#ifdef __cplusplus +} +#endif + +#endif // LR11XX_WIFI_TYPES_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/src/lr_fhss_v1_base_types.h b/src/lr_fhss_v1_base_types.h new file mode 100644 index 0000000..c6afffd --- /dev/null +++ b/src/lr_fhss_v1_base_types.h @@ -0,0 +1,127 @@ +/** + * @file lr_fhss_v1_base_types.h + * + * @brief Radio-independent LR-FHSS base type definitions, version 1 + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR_FHSS_V1_BASE_TYPES_H__ +#define LR_FHSS_V1_BASE_TYPES_H__ + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include +#include + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/** + * @brief LR-FHSS modulation type + */ +typedef enum lr_fhss_v1_modulation_type_e +{ + LR_FHSS_V1_MODULATION_TYPE_GMSK_488 = 0, +} lr_fhss_v1_modulation_type_t; + +/** + * @brief LR-FHSS coding rate + */ +typedef enum lr_fhss_v1_cr_e +{ + LR_FHSS_V1_CR_5_6 = 0x00, + LR_FHSS_V1_CR_2_3 = 0x01, + LR_FHSS_V1_CR_1_2 = 0x02, + LR_FHSS_V1_CR_1_3 = 0x03, +} lr_fhss_v1_cr_t; + +/** + * @brief LR-FHSS grid + */ +typedef enum lr_fhss_v1_grid_e +{ + LR_FHSS_V1_GRID_25391_HZ = 0x00, + LR_FHSS_V1_GRID_3906_HZ = 0x01, +} lr_fhss_v1_grid_t; + +/** + * @brief LR-FHSS bandwidth + */ +typedef enum lr_fhss_v1_bw_e +{ + LR_FHSS_V1_BW_39063_HZ = 0x00, + LR_FHSS_V1_BW_85938_HZ = 0x01, + LR_FHSS_V1_BW_136719_HZ = 0x02, + LR_FHSS_V1_BW_183594_HZ = 0x03, + LR_FHSS_V1_BW_335938_HZ = 0x04, + LR_FHSS_V1_BW_386719_HZ = 0x05, + LR_FHSS_V1_BW_722656_HZ = 0x06, + LR_FHSS_V1_BW_773438_HZ = 0x07, + LR_FHSS_V1_BW_1523438_HZ = 0x08, + LR_FHSS_V1_BW_1574219_HZ = 0x09, +} lr_fhss_v1_bw_t; + +/** + * @brief LR-FHSS parameter structure + */ +typedef struct lr_fhss_v1_params_s +{ + const uint8_t* sync_word; /**< 4-byte sync word */ + lr_fhss_v1_modulation_type_t modulation_type; + lr_fhss_v1_cr_t cr; + lr_fhss_v1_grid_t grid; + lr_fhss_v1_bw_t bw; + bool enable_hopping; + uint8_t header_count; /**< Number of header blocks */ +} lr_fhss_v1_params_t; + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +#endif // LR_FHSS_V1_BASE_TYPES_H__ + +/* --- EOF ------------------------------------------------------------------ */