diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_COMMON/ble/ble_radio_notification/ble_radio_notification.c b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_COMMON/ble/ble_radio_notification/ble_radio_notification.c new file mode 100644 index 0000000000..44b2bf9ee8 --- /dev/null +++ b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_COMMON/ble/ble_radio_notification/ble_radio_notification.c @@ -0,0 +1,87 @@ +/** + * Copyright (c) 2012 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "ble_radio_notification.h" +#include + + +static bool m_radio_active = false; /**< Current radio state. */ +static ble_radio_notification_evt_handler_t m_evt_handler = NULL; /**< Application event handler for handling Radio Notification events. */ + + +void SWI1_IRQHandler(void) +{ + m_radio_active = !m_radio_active; + if (m_evt_handler != NULL) + { + m_evt_handler(m_radio_active); + } +} + + +uint32_t ble_radio_notification_init(uint32_t irq_priority, + uint8_t distance, + ble_radio_notification_evt_handler_t evt_handler) +{ + uint32_t err_code; + + m_evt_handler = evt_handler; + + // Initialize Radio Notification software interrupt + err_code = sd_nvic_ClearPendingIRQ(SWI1_IRQn); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + err_code = sd_nvic_SetPriority(SWI1_IRQn, irq_priority); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + err_code = sd_nvic_EnableIRQ(SWI1_IRQn); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + // Configure the event + return sd_radio_notification_cfg_set(NRF_RADIO_NOTIFICATION_TYPE_INT_ON_BOTH, distance); +} diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_COMMON/ble/ble_radio_notification/ble_radio_notification.h b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_COMMON/ble/ble_radio_notification/ble_radio_notification.h new file mode 100644 index 0000000000..0f89b2f5dd --- /dev/null +++ b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_COMMON/ble/ble_radio_notification/ble_radio_notification.h @@ -0,0 +1,82 @@ +/** + * Copyright (c) 2012 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file + * + * @defgroup ble_radio_notification Radio Notification Event Handler + * @{ + * @ingroup ble_sdk_lib + * @brief Module for propagating Radio Notification events to the application. + */ + +#ifndef BLE_RADIO_NOTIFICATION_H__ +#define BLE_RADIO_NOTIFICATION_H__ + +#include +#include +#include "nrf_soc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/**@brief Application radio notification event handler type. */ +typedef void (*ble_radio_notification_evt_handler_t) (bool radio_active); + +/**@brief Function for initializing the Radio Notification module. + * + * @param[in] irq_priority Interrupt priority for the Radio Notification interrupt handler. + * @param[in] distance The time from an Active event until the radio is activated. + * @param[in] evt_handler Handler to be executed when a radio notification event has been + * received. + * + * @return NRF_SUCCESS on successful initialization, otherwise an error code. + */ +uint32_t ble_radio_notification_init(uint32_t irq_priority, + uint8_t distance, + ble_radio_notification_evt_handler_t evt_handler); + + +#ifdef __cplusplus +} +#endif + +#endif // BLE_RADIO_NOTIFICATION_H__ + +/** @} */ diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_COMMON/libraries/bootloader/dfu/nrf_dfu_mbr.c b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_COMMON/libraries/bootloader/dfu/nrf_dfu_mbr.c new file mode 100644 index 0000000000..974a46652f --- /dev/null +++ b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_COMMON/libraries/bootloader/dfu/nrf_dfu_mbr.c @@ -0,0 +1,109 @@ +/** + * Copyright (c) 2016 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "nrf_dfu_mbr.h" +#include "nrf_mbr.h" +#include "nrf_dfu_types.h" +#include "nrf_log.h" +#include "nrf_bootloader_info.h" + +#define MBR_IRQ_FORWARD_ADDRESS_ADDRESS (0x20000000) //!< The address of the variable that decides where the MBR forwards interrupts + +uint32_t nrf_dfu_mbr_copy_bl(uint32_t * p_src, uint32_t len) +{ + uint32_t ret_val; + uint32_t const len_words = len / sizeof(uint32_t); + + sd_mbr_command_t command = + { + .command = SD_MBR_COMMAND_COPY_BL, + .params.copy_bl.bl_src = p_src, + .params.copy_bl.bl_len = len_words + }; + + ret_val = sd_mbr_command(&command); + + return ret_val; +} + + +uint32_t nrf_dfu_mbr_init_sd(void) +{ + uint32_t ret_val; + + sd_mbr_command_t command = + { + .command = SD_MBR_COMMAND_INIT_SD + }; + + ret_val = sd_mbr_command(&command); + + return ret_val; +} + + +uint32_t nrf_dfu_mbr_irq_forward_address_set(void) +{ + uint32_t ret_val = NRF_ERROR_INVALID_PARAM; + uint32_t address = MBR_SIZE; + + NRF_LOG_DEBUG("running irq table set"); + +#ifndef BLE_STACK_SUPPORT_REQD + sd_mbr_command_t command = + { + .command = SD_MBR_COMMAND_IRQ_FORWARD_ADDRESS_SET, + .params.irq_forward_address_set.address = address, + }; + + ret_val = sd_mbr_command(&command); +#endif + + if (ret_val == NRF_ERROR_INVALID_PARAM) + { + // Manually set the forward address if this MBR doesn't have the command. + *(uint32_t *)(MBR_IRQ_FORWARD_ADDRESS_ADDRESS) = address; + + ret_val = NRF_SUCCESS; + } + + NRF_LOG_DEBUG("After running irq table set"); + + return ret_val; +} diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_COMMON/libraries/bootloader/dfu/nrf_dfu_mbr.h b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_COMMON/libraries/bootloader/dfu/nrf_dfu_mbr.h new file mode 100644 index 0000000000..2fcd1b361d --- /dev/null +++ b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_COMMON/libraries/bootloader/dfu/nrf_dfu_mbr.h @@ -0,0 +1,90 @@ +/** + * Copyright (c) 2016 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/**@file + * + * @defgroup sdk_nrf_dfu_mbr MBR functions + * @{ + * @ingroup nrf_dfu + */ + +#ifndef NRF_DFU_MBR_H__ +#define NRF_DFU_MBR_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Function for copying the bootloader using an MBR command. + * + * @param[in] p_src Source address of the bootloader data to copy. + * @param[in] len Length of the data to copy in bytes. + * + * @return This function will return only if the command request could not be run. + * See @ref sd_mbr_command_copy_bl_t for possible return values. + */ +uint32_t nrf_dfu_mbr_copy_bl(uint32_t * p_src, uint32_t len); + + +/** @brief Function for initializing the SoftDevice using an MBR command. + * + * @retval NRF_SUCCESS If the SoftDevice was initialized successfully. + * Any other return value indicates that the SoftDevice + * could not be initialized. + */ +uint32_t nrf_dfu_mbr_init_sd(void); + + +/** @brief Function for setting the address of the IRQ table to the app's using an MBR command. + * + * @retval NRF_SUCCESS If the address of the new irq table was set. Any other + * return value indicates that the address could not be set. + */ +uint32_t nrf_dfu_mbr_irq_forward_address_set(void); + + +#ifdef __cplusplus +} +#endif + +#endif // NRF_DFU_MBR_H__ + +/** @} */ diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_COMMON/libraries/bootloader/dfu/nrf_dfu_types.h b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_COMMON/libraries/bootloader/dfu/nrf_dfu_types.h new file mode 100644 index 0000000000..5ccd290103 --- /dev/null +++ b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_COMMON/libraries/bootloader/dfu/nrf_dfu_types.h @@ -0,0 +1,302 @@ +/** + * Copyright (c) 2016 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/**@file + * + * @defgroup sdk_nrf_dfu_types DFU types + * @{ + * @ingroup nrf_dfu + */ + +#ifndef NRF_DFU_TYPES_H__ +#define NRF_DFU_TYPES_H__ + +#include +#include + +#include "nrf.h" +#include "nrf_mbr.h" +#include "app_util_platform.h" +#include "sdk_config.h" + +#if defined(NRF_DFU_TRANSPORT_BLE) && NRF_DFU_TRANSPORT_BLE +#include "ble_gap.h" +#define SYSTEM_SERVICE_ATT_SIZE 8 /**< Size of the system service attribute length including CRC-16 at the end. */ +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + +#define INIT_COMMAND_MAX_SIZE 256 /**< Maximum size of the init command stored in dfu_settings. */ + +/** @brief Size of a flash page. This value is used for calculating the size of the reserved + * flash space in the bootloader region. + */ +#if defined(NRF51) + #define CODE_PAGE_SIZE (PAGE_SIZE_IN_WORDS * sizeof(uint32_t)) +#elif defined(NRF52) || defined(NRF52840_XXAA) + #define CODE_PAGE_SIZE (MBR_PAGE_SIZE_IN_WORDS * sizeof(uint32_t)) +#else + #error "Architecture not set." +#endif + +/** @brief Maximum size of a data object.*/ +#if defined(NRF51) + #define DATA_OBJECT_MAX_SIZE (CODE_PAGE_SIZE * 4) +#elif defined(NRF52_SERIES) || defined (__SDK_DOXYGEN__) + #define DATA_OBJECT_MAX_SIZE (CODE_PAGE_SIZE) +#else + #error "Architecture not set." +#endif + +/** @brief Page location of the bootloader settings address. + */ +#if defined (NRF51) + #define BOOTLOADER_SETTINGS_ADDRESS (0x0003FC00UL) +#elif defined( NRF52810_XXAA ) + #define BOOTLOADER_SETTINGS_ADDRESS (0x0002F000UL) +#elif defined( NRF52832_XXAA ) + #define BOOTLOADER_SETTINGS_ADDRESS (0x0007F000UL) +#elif defined(NRF52840_XXAA) + #define BOOTLOADER_SETTINGS_ADDRESS (0x000FF000UL) +#else + #error No valid target set for BOOTLOADER_SETTINGS_ADDRESS. +#endif + +#define BOOTLOADER_SETTINGS_PAGE_SIZE (CODE_PAGE_SIZE) + +/** + * @brief MBR parameters page in UICR. + * + * Register location in UICR where the page address of the MBR parameters page is stored (only used by the nRF52 MBR). + * + * @note If the value at the given location is 0xFFFFFFFF, no MBR parameters page is set. + */ +#define NRF_UICR_MBR_PARAMS_PAGE_ADDRESS (NRF_UICR_BASE + 0x18) +#define NRF_MBR_PARAMS_PAGE_SIZE (CODE_PAGE_SIZE) + +/** @brief Page location of the MBR parameters page address. + */ +#if defined(NRF52840_XXAA) || defined(NRF52840_XXAA_ENGA) + #define NRF_MBR_PARAMS_PAGE_ADDRESS (0x000FE000UL) +#elif defined(NRF52832_XXAA) + #define NRF_MBR_PARAMS_PAGE_ADDRESS (0x0007E000UL) +#elif defined(NRF52810_XXAA) + #define NRF_MBR_PARAMS_PAGE_ADDRESS (0x0002E000UL) +#endif + +/** @brief Size (in bytes) of the flash area reserved for application data. + * + * The area is found at the end of the application area, next to the start of + * the bootloader. This area will not be erased by the bootloader during a + * firmware upgrade. The default value is 3 pages which matches the size used + * in most SDK examples. + */ +#ifndef DFU_APP_DATA_RESERVED +#define DFU_APP_DATA_RESERVED (CODE_PAGE_SIZE * 3) +#endif + +/** @brief Total size of the region between the SoftDevice and the bootloader. + */ +#define DFU_REGION_END(bootloader_start_addr) ((bootloader_start_addr) - (DFU_APP_DATA_RESERVED)) + +#ifdef BLE_STACK_SUPPORT_REQD +#define DFU_REGION_START (nrf_dfu_bank0_start_addr()) +#else +#define DFU_REGION_START (MBR_SIZE) +#endif + +#define DFU_REGION_TOTAL_SIZE ((DFU_REGION_END) - (DFU_REGION_START)) + +#define NRF_DFU_CURRENT_BANK_0 0x00 +#define NRF_DFU_CURRENT_BANK_1 0x01 + +#define NRF_DFU_BANK_LAYOUT_DUAL 0x00 +#define NRF_DFU_BANK_LAYOUT_SINGLE 0x01 + +/** @brief DFU bank state codes. + * + * @details The DFU bank state indicates the content of a bank: + * A valid image of a certain type or an invalid image. + */ + +#define NRF_DFU_BANK_INVALID 0x00 /**< Invalid image. */ +#define NRF_DFU_BANK_VALID_APP 0x01 /**< Valid application. */ +#define NRF_DFU_BANK_VALID_SD 0xA5 /**< Valid SoftDevice. */ +#define NRF_DFU_BANK_VALID_BL 0xAA /**< Valid bootloader. */ +#define NRF_DFU_BANK_VALID_SD_BL 0xAC /**< Valid SoftDevice and bootloader. */ + +/** @brief Description of a single bank. */ +#pragma pack(4) +typedef struct +{ + uint32_t image_size; /**< Size of the image in the bank. */ + uint32_t image_crc; /**< CRC of the image. If set to 0, the CRC is ignored. */ + uint32_t bank_code; /**< Identifier code for the bank. */ +} nrf_dfu_bank_t; + +/**@brief DFU progress. + * + * Be aware of the difference between objects and firmware images. A firmware image consists of multiple objects, each of a maximum size @ref DATA_OBJECT_MAX_SIZE. + * + * @note The union inside this struct is cleared when CREATE_OBJECT of command type is executed, and when there is a valid post-validation. + * In DFU activation (after reset) the @ref dfu_progress_t::update_start_address will be used in case of a SD/SD+BL update. + */ +ANON_UNIONS_ENABLE; +typedef struct +{ + uint32_t command_size; /**< The size of the current init command stored in the DFU settings. */ + uint32_t command_offset; /**< The offset of the currently received init command data. The offset will increase as the init command is received. */ + uint32_t command_crc; /**< The calculated CRC of the init command (calculated after the transfer is completed). */ + uint32_t data_object_size; /**< The size of the last object created. Note that this size is not the size of the whole firmware image.*/ + union + { + struct + { + uint32_t firmware_image_crc; /**< CRC value of the current firmware (continuously calculated as data is received). */ + uint32_t firmware_image_crc_last; /**< The CRC of the last executed object. */ + uint32_t firmware_image_offset; /**< The offset of the current firmware image being transferred. Note that this offset is the offset in the entire firmware image and not only the current object. */ + uint32_t firmware_image_offset_last;/**< The offset of the last executed object from the start of the firmware image. */ + }; + struct + { + uint32_t update_start_address; /**< Value indicating the start address of the new firmware (before copy). It's always used, but it's most important for an SD/SD+BL update where the SD changes size or if the DFU process had a power loss when updating a SD with changed size. */ + }; + }; +} dfu_progress_t; +ANON_UNIONS_DISABLE; + +/** @brief Event types in the bootloader and DFU process. */ +typedef enum +{ + NRF_DFU_EVT_DFU_INITIALIZED, /**< Starting DFU. */ + NRF_DFU_EVT_TRANSPORT_ACTIVATED, /**< Transport activated (e.g. BLE connected, USB plugged in). */ + NRF_DFU_EVT_TRANSPORT_DEACTIVATED, /**< Transport deactivated (e.g. BLE disconnected, USB plugged out). */ + NRF_DFU_EVT_DFU_STARTED, /**< DFU process started. */ + NRF_DFU_EVT_OBJECT_RECEIVED, /**< A DFU data object has been received. */ + NRF_DFU_EVT_DFU_FAILED, /**< DFU process has failed, been interrupted, or hung. */ + NRF_DFU_EVT_DFU_COMPLETED, /**< DFU process completed. */ + NRF_DFU_EVT_DFU_ABORTED, /**< DFU process aborted. */ +} nrf_dfu_evt_type_t; + +/** + * @brief Function for notifying DFU state. + */ +typedef void (*nrf_dfu_observer_t)(nrf_dfu_evt_type_t notification); + + +#if defined(NRF_DFU_TRANSPORT_BLE) && NRF_DFU_TRANSPORT_BLE + +typedef struct +{ + uint32_t crc; /**< CRC of the rest of the parameters in this struct. */ + ble_gap_id_key_t ble_id; /**< BLE GAP identity key of the device that initiated the DFU process. */ + ble_gap_enc_key_t enc_key; /**< Encryption key structure containing encrypted diversifier and LTK for reestablishing the bond. */ + uint8_t sys_serv_attr[SYSTEM_SERVICE_ATT_SIZE]; /**< System service attributes for restoring of Service Changed Indication setting in DFU mode. */ +} nrf_dfu_peer_data_t; + +typedef enum +{ + DFU_PEER_DATA_STATE_INVALID = 0, + DFU_PEER_DATA_STATE_INITIALIZED = 1, + DFU_PEER_DATA_STATE_WRITE_REQUESTED = 2, + DFU_PEER_DATA_STATE_WRITE_FINISHED = 3, + DFU_PEER_DATA_STATE_WRITE_FAILED = 4, +} nrf_dfu_peer_data_state_t; + +typedef struct +{ + uint32_t crc; /**< CRC of the rest of the parameters in this struct. Calculated by the bootloader. */ + uint8_t name[20]; /**< New advertisement name to set. */ + uint32_t len; /**< Length of the advertisement name. */ +} nrf_dfu_adv_name_t; + +typedef enum +{ + DFU_ADV_NAME_STATE_INVALID = 0, + DFU_ADV_NAME_STATE_INITIALIZED = 1, + DFU_ADV_NAME_STATE_WRITE_REQUESTED = 2, + DFU_ADV_NAME_STATE_WRITE_FINISHED = 3, + DFU_ADV_NAME_STATE_WRITE_FAILED = 4, +} nrf_dfu_set_adv_name_state_t; + +#endif // NRF_DFU_TRANSPORT_BLE + + +/**@brief DFU settings for application and bank data. + */ +typedef struct +{ + uint32_t crc; /**< CRC for the stored DFU settings, not including the CRC itself. If 0xFFFFFFF, the CRC has never been calculated. */ + uint32_t settings_version; /**< Version of the current DFU settings struct layout. */ + uint32_t app_version; /**< Version of the last stored application. */ + uint32_t bootloader_version; /**< Version of the last stored bootloader. */ + + uint32_t bank_layout; /**< Bank layout: single bank or dual bank. This value can change. */ + uint32_t bank_current; /**< The bank that is currently used. */ + + nrf_dfu_bank_t bank_0; /**< Bank 0. */ + nrf_dfu_bank_t bank_1; /**< Bank 1. */ + + uint32_t write_offset; /**< Write offset for the current operation. */ + uint32_t sd_size; /**< Size of the SoftDevice. */ + + dfu_progress_t progress; /**< Current DFU progress. */ + + uint32_t enter_buttonless_dfu; + uint8_t init_command[INIT_COMMAND_MAX_SIZE]; /**< Buffer for storing the init command. */ + +#if defined(NRF_DFU_TRANSPORT_BLE) && NRF_DFU_TRANSPORT_BLE + nrf_dfu_peer_data_t peer_data; /**< Not included in calculated CRC. */ + nrf_dfu_adv_name_t adv_name; /**< Not included in calculated CRC. */ +#endif // NRF_DFU_TRANSPORT_BLE + +} nrf_dfu_settings_t; + +#pragma pack() // revert pack settings + +#ifdef __cplusplus +} +#endif + +#endif // NRF_DFU_TYPES_H__ + +/** @} */ diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_COMMON/libraries/bootloader/nrf_bootloader_info.h b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_COMMON/libraries/bootloader/nrf_bootloader_info.h new file mode 100644 index 0000000000..812cddbf37 --- /dev/null +++ b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_COMMON/libraries/bootloader/nrf_bootloader_info.h @@ -0,0 +1,188 @@ +/** + * Copyright (c) 2016 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/**@file + * + * @defgroup nrf_bootloader_info Bootloader Information + * @{ + * @ingroup nrf_bootloader + */ + +#ifndef NRF_BOOTLOADER_INFO_H__ +#define NRF_BOOTLOADER_INFO_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "app_util.h" +#include "nrf.h" +#include "nrf_mbr.h" + +/** @brief Macro for getting the start address of the bootloader image. + * + * The macro is not a compile time symbol. It cannot be used as a + * constant expression, for example, inside a static assert or linker script + * at-placement. + */ +#ifndef BOOTLOADER_START_ADDR +#if (__LINT__ == 1) + #define BOOTLOADER_START_ADDR (0x3AC00) +#elif defined(CODE_START) + #define BOOTLOADER_START_ADDR (CODE_START) +#else + #error Not a valid compiler/linker for BOOTLOADER_START_ADDR. +#endif +#endif + + +/** @brief Macro for getting the size of the bootloader image. + */ +#ifndef BOOTLOADER_SIZE +#if defined ( NRF51 ) + #define BOOTLOADER_SIZE (BOOTLOADER_SETTINGS_ADDRESS - BOOTLOADER_START_ADDR) +#elif defined( NRF52_SERIES ) + #define BOOTLOADER_SIZE (NRF_MBR_PARAMS_PAGE_ADDRESS - BOOTLOADER_START_ADDR) +#elif (__LINT__ == 1) + #define BOOTLOADER_SIZE (0x6000) +#endif +#endif + + +/** + * @brief Bootloader start address in UICR. + * + * Register location in UICR where the bootloader start address is stored. + * + * @note If the value at the given location is 0xFFFFFFFF, the bootloader address is not set. + */ +#define NRF_UICR_BOOTLOADER_START_ADDRESS (NRF_UICR_BASE + 0x14) + + +// The following macros are for accessing the SoftDevice information structure, +// which is found inside the SoftDevice binary. + +/** @brief Macro for converting an offset inside the SoftDevice information struct to an absolute address. + */ +#define SD_INFO_ABS_OFFSET_GET(baseaddr, offset) ((baseaddr) + (SOFTDEVICE_INFO_STRUCT_OFFSET) + (offset)) + +/** @brief Macros for reading a byte or a word at a particular offset inside a SoftDevice information struct. + * Use MBR_SIZE as baseaddr when the SoftDevice is installed just above the MBR (the usual case). + */ +#define SD_OFFSET_GET_UINT32(baseaddr, offset) (*((uint32_t *) SD_INFO_ABS_OFFSET_GET(baseaddr, offset))) +#define SD_OFFSET_GET_UINT16(baseaddr, offset) (*((uint16_t *) SD_INFO_ABS_OFFSET_GET(baseaddr, offset))) +#define SD_OFFSET_GET_UINT8(baseaddr, offset) (*((uint8_t *) SD_INFO_ABS_OFFSET_GET(baseaddr, offset))) + + +#ifdef BLE_STACK_SUPPORT_REQD +#include "nrf_sdm.h" +#else +/** @brief The offset inside the SoftDevice at which the information struct is placed. + * To see the layout of the information struct, see the SoftDevice specification. + */ +#define SOFTDEVICE_INFO_STRUCT_OFFSET (0x2000) + +#define SD_INFO_STRUCT_SIZE(baseaddr) SD_OFFSET_GET_UINT8(baseaddr, 0x00) + +/** @brief Macro for reading the size of a SoftDevice at a given base address. + */ +#ifndef SD_SIZE_GET +#define SD_SIZE_GET(baseaddr) SD_OFFSET_GET_UINT32(baseaddr, 0x08) +#endif + +/** @brief Macro for reading the version of a SoftDevice at a given base address. + * This expression checks the length of the information struct to see if the version is present. + * The version number is constructed like this: + * major_version * 1000000 + minor_version * 1000 + bugfix_version + */ +#ifndef SD_VERSION_GET +#define SD_VERSION_GET(baseaddr) ((SD_INFO_STRUCT_SIZE(baseaddr) > (0x14)) \ + ? SD_OFFSET_GET_UINT32(baseaddr, 0x14) \ + : 0) +#endif +#endif + + +/** @brief Macro for reading the magic number of a SoftDevice at a given base address. + */ +#ifndef SD_MAGIC_NUMBER_GET +#define SD_MAGIC_NUMBER_GET(baseaddr) SD_OFFSET_GET_UINT32(baseaddr, 0x04) +#endif + +/** @brief Macro for getting the absolute address of the magic number. + */ +#define SD_MAGIC_NUMBER_ABS_OFFSET_GET(baseaddr) SD_INFO_ABS_OFFSET_GET(baseaddr, 0x04) + +/** @brief The number present at a specific location in all SoftDevices. + */ +#define SD_MAGIC_NUMBER ((uint32_t)0x51B1E5DB) + +/** @brief Whether a SoftDevice is at its regular location. + */ +#ifndef SD_PRESENT +#define SD_PRESENT ((SD_MAGIC_NUMBER_GET(MBR_SIZE)) == (SD_MAGIC_NUMBER)) +#endif + +/** @brief The multiplier for the major version of the SoftDevice. See \ref SD_VERSION_GET + */ +#define SD_MAJOR_VERSION_MULTIPLIER (1000000) + +/** @brief Read the major version of the SoftDevice from the raw version number. See \ref SD_VERSION_GET. + */ +#define SD_MAJOR_VERSION_EXTRACT(raw_version) ((raw_version)/SD_MAJOR_VERSION_MULTIPLIER) + + +#define BOOTLOADER_DFU_GPREGRET_MASK (0xB0) /**< Magic pattern written to GPREGRET register to signal between main app and DFU. The 3 lower bits are assumed to be used for signalling purposes.*/ +#define BOOTLOADER_DFU_START_BIT_MASK (0x01) /**< Bit mask to signal from main application to enter DFU mode using a buttonless service. */ + +#define BOOTLOADER_DFU_GPREGRET2_MASK (0xA8) /**< Magic pattern written to GPREGRET2 register to signal between main app and DFU. The 3 lower bits are assumed to be used for signalling purposes.*/ +#define BOOTLOADER_DFU_SKIP_CRC_BIT_MASK (0x01) /**< Bit mask to signal from main application that CRC-check is not needed for image verification. */ + + +#define BOOTLOADER_DFU_START (BOOTLOADER_DFU_GPREGRET_MASK | BOOTLOADER_DFU_START_BIT_MASK) /**< Magic number to signal that bootloader should enter DFU mode because of signal from Buttonless DFU in main app.*/ +#define BOOTLOADER_DFU_SKIP_CRC (BOOTLOADER_DFU_GPREGRET2_MASK | BOOTLOADER_DFU_SKIP_CRC_BIT_MASK) /**< Magic number to signal that CRC can be skipped due to low power modes.*/ + + + +#ifdef __cplusplus +} +#endif + +#endif // #ifndef NRF_BOOTLOADER_INFO_H__ +/** @} */ diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_COMMON/softdevice/common/nrf_sdh.c b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_COMMON/softdevice/common/nrf_sdh.c new file mode 100644 index 0000000000..39a861f8e0 --- /dev/null +++ b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_COMMON/softdevice/common/nrf_sdh.c @@ -0,0 +1,430 @@ +/** + * Copyright (c) 2017 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "sdk_common.h" +#if NRF_MODULE_ENABLED(NRF_SDH) + +#include "nrf_sdh.h" + +#include + +#include "nrf_sdm.h" +#include "nrf_nvic.h" +#include "sdk_config.h" +#include "app_error.h" +#include "app_util_platform.h" + + +#define NRF_LOG_MODULE_NAME nrf_sdh +#if NRF_SDH_LOG_ENABLED + #define NRF_LOG_LEVEL NRF_SDH_LOG_LEVEL + #define NRF_LOG_INFO_COLOR NRF_SDH_INFO_COLOR + #define NRF_LOG_DEBUG_COLOR NRF_SDH_DEBUG_COLOR +#else + #define NRF_LOG_LEVEL 0 +#endif // NRF_SDH_LOG_ENABLED +#include "nrf_log.h" +NRF_LOG_MODULE_REGISTER(); + + +// Validate configuration options. + +#if (NRF_SDH_DISPATCH_MODEL == NRF_SDH_DISPATCH_MODEL_APPSH) + #if (!APP_SCHEDULER_ENABLED) + #error app_scheduler is required when NRF_SDH_DISPATCH_MODEL is set to NRF_SDH_DISPATCH_MODEL_APPSH + #endif + #include "app_scheduler.h" +#endif + +#if ( (NRF_SDH_CLOCK_LF_SRC == NRF_CLOCK_LF_SRC_RC) \ + && (NRF_SDH_CLOCK_LF_ACCURACY != NRF_CLOCK_LF_ACCURACY_500_PPM)) + #warning Please select NRF_CLOCK_LF_ACCURACY_500_PPM when using NRF_CLOCK_LF_SRC_RC +#endif + + +// Create section "sdh_req_observers". +NRF_SECTION_SET_DEF(sdh_req_observers, nrf_sdh_req_observer_t, NRF_SDH_REQ_OBSERVER_PRIO_LEVELS); + +// Create section "sdh_state_observers". +NRF_SECTION_SET_DEF(sdh_state_observers, nrf_sdh_state_observer_t, NRF_SDH_STATE_OBSERVER_PRIO_LEVELS); + +// Create section "sdh_stack_observers". +NRF_SECTION_SET_DEF(sdh_stack_observers, nrf_sdh_stack_observer_t, NRF_SDH_STACK_OBSERVER_PRIO_LEVELS); + + +static bool m_nrf_sdh_enabled; /**< Variable to indicate whether the SoftDevice is enabled. */ +static bool m_nrf_sdh_suspended; /**< Variable to indicate whether this module is suspended. */ +static bool m_nrf_sdh_continue; /**< Variable to indicate whether enable/disable process was started. */ + + +/**@brief Function for notifying request observers. + * + * @param[in] evt Type of request event. + */ +static ret_code_t sdh_request_observer_notify(nrf_sdh_req_evt_t req) +{ + nrf_section_iter_t iter; + + NRF_LOG_DEBUG("State request: 0x%08X", req); + + for (nrf_section_iter_init(&iter, &sdh_req_observers); + nrf_section_iter_get(&iter) != NULL; + nrf_section_iter_next(&iter)) + { + nrf_sdh_req_observer_t * p_observer; + nrf_sdh_req_evt_handler_t handler; + + p_observer = (nrf_sdh_req_observer_t *) nrf_section_iter_get(&iter); + handler = p_observer->handler; + + if (handler(req, p_observer->p_context)) + { + NRF_LOG_DEBUG("Notify observer 0x%08X => ready", p_observer); + } + else + { + // Process is stopped. + NRF_LOG_DEBUG("Notify observer 0x%08X => blocking", p_observer); + return NRF_ERROR_BUSY; + } + } + return NRF_SUCCESS; +} + + +/**@brief Function for stage request observers. + * + * @param[in] evt Type of stage event. + */ +static void sdh_state_observer_notify(nrf_sdh_state_evt_t evt) +{ + nrf_section_iter_t iter; + + NRF_LOG_DEBUG("State change: 0x%08X", evt); + + for (nrf_section_iter_init(&iter, &sdh_state_observers); + nrf_section_iter_get(&iter) != NULL; + nrf_section_iter_next(&iter)) + { + nrf_sdh_state_observer_t * p_observer; + nrf_sdh_state_evt_handler_t handler; + + p_observer = (nrf_sdh_state_observer_t *) nrf_section_iter_get(&iter); + handler = p_observer->handler; + + handler(evt, p_observer->p_context); + } +} + + +static void softdevices_evt_irq_enable(void) +{ +#ifdef SOFTDEVICE_PRESENT + ret_code_t ret_code = sd_nvic_EnableIRQ((IRQn_Type)SD_EVT_IRQn); + APP_ERROR_CHECK(ret_code); +#else + // In case of serialization, NVIC must be accessed directly. + NVIC_EnableIRQ(SD_EVT_IRQn); +#endif +} + + +static void softdevice_evt_irq_disable(void) +{ +#ifdef SOFTDEVICE_PRESENT + ret_code_t ret_code = sd_nvic_DisableIRQ((IRQn_Type)SD_EVT_IRQn); + APP_ERROR_CHECK(ret_code); +#else + // In case of serialization, NVIC must be accessed directly. + NVIC_DisableIRQ(SD_EVT_IRQn); +#endif +} + + +#ifndef S140 +static void swi_interrupt_priority_workaround(void) +{ + // The priority of SoftDevice SWI SD_EVT_IRQn and RADIO_NOTIFICATION_IRQn in + // S132 v5.0.0, S112 v5.0.0, S212 v5.0.0 and S332 v5.0.0 is set to 6. + // Change it to APP_IRQ_PRIORITY_LOWEST (7) so that they do not preempt peripherals' interrupts. + +#ifdef SOFTDEVICE_PRESENT + ret_code_t ret_code; + ret_code = sd_nvic_SetPriority(SD_EVT_IRQn, APP_IRQ_PRIORITY_LOWEST); + APP_ERROR_CHECK(ret_code); + ret_code = sd_nvic_SetPriority(RADIO_NOTIFICATION_IRQn, APP_IRQ_PRIORITY_LOWEST); + APP_ERROR_CHECK(ret_code); +#else + // In case of serialization, NVIC must be accessed directly. + NVIC_SetPriority(SD_EVT_IRQn, APP_IRQ_PRIORITY_LOWEST); + NVIC_SetPriority(RADIO_NOTIFICATION_IRQn, APP_IRQ_PRIORITY_LOWEST); +#endif +} +#endif + + +ret_code_t nrf_sdh_enable_request(void) +{ + ret_code_t ret_code; + + if (m_nrf_sdh_enabled) + { + return NRF_ERROR_INVALID_STATE; + } + + m_nrf_sdh_continue = true; + + // Notify observers about SoftDevice enable request. + if (sdh_request_observer_notify(NRF_SDH_EVT_ENABLE_REQUEST) == NRF_ERROR_BUSY) + { + // Enable process was stopped. + return NRF_SUCCESS; + } + + // Notify observers about starting SoftDevice enable process. + sdh_state_observer_notify(NRF_SDH_EVT_STATE_ENABLE_PREPARE); + + nrf_clock_lf_cfg_t const clock_lf_cfg = + { + .source = NRF_SDH_CLOCK_LF_SRC, + .rc_ctiv = NRF_SDH_CLOCK_LF_RC_CTIV, + .rc_temp_ctiv = NRF_SDH_CLOCK_LF_RC_TEMP_CTIV, + .accuracy = NRF_SDH_CLOCK_LF_ACCURACY + }; + + CRITICAL_REGION_ENTER(); +#ifdef ANT_LICENSE_KEY + ret_code = sd_softdevice_enable(&clock_lf_cfg, app_error_fault_handler, ANT_LICENSE_KEY); +#else + ret_code = sd_softdevice_enable(&clock_lf_cfg, app_error_fault_handler); +#endif + m_nrf_sdh_enabled = (ret_code == NRF_SUCCESS); + CRITICAL_REGION_EXIT(); + + if (ret_code != NRF_SUCCESS) + { + return ret_code; + } + + m_nrf_sdh_continue = false; + m_nrf_sdh_suspended = false; + +#ifndef S140 + // Set the interrupt priority after enabling the SoftDevice, since + // sd_softdevice_enable() sets the SoftDevice interrupt priority. + swi_interrupt_priority_workaround(); +#endif + + // Enable event interrupt. + // Interrupt priority has already been set by the stack. + softdevices_evt_irq_enable(); + + // Notify observers about a finished SoftDevice enable process. + sdh_state_observer_notify(NRF_SDH_EVT_STATE_ENABLED); + + return NRF_SUCCESS; +} + + +ret_code_t nrf_sdh_disable_request(void) +{ + ret_code_t ret_code; + + if (!m_nrf_sdh_enabled) + { + return NRF_ERROR_INVALID_STATE; + } + + m_nrf_sdh_continue = true; + + // Notify observers about SoftDevice disable request. + if (sdh_request_observer_notify(NRF_SDH_EVT_DISABLE_REQUEST) == NRF_ERROR_BUSY) + { + // Disable process was stopped. + return NRF_SUCCESS; + } + + // Notify observers about starting SoftDevice disable process. + sdh_state_observer_notify(NRF_SDH_EVT_STATE_DISABLE_PREPARE); + + CRITICAL_REGION_ENTER(); + ret_code = sd_softdevice_disable(); + m_nrf_sdh_enabled = false; + CRITICAL_REGION_EXIT(); + + if (ret_code != NRF_SUCCESS) + { + return ret_code; + } + + m_nrf_sdh_continue = false; + + softdevice_evt_irq_disable(); + + // Notify observers about a finished SoftDevice enable process. + sdh_state_observer_notify(NRF_SDH_EVT_STATE_DISABLED); + + return NRF_SUCCESS; +} + + +ret_code_t nrf_sdh_request_continue(void) +{ + if (!m_nrf_sdh_continue) + { + return NRF_ERROR_INVALID_STATE; + } + + if (m_nrf_sdh_enabled) + { + return nrf_sdh_disable_request(); + } + else + { + return nrf_sdh_enable_request(); + } +} + + +bool nrf_sdh_is_enabled(void) +{ + return m_nrf_sdh_enabled; +} + + +void nrf_sdh_suspend(void) +{ + if (!m_nrf_sdh_enabled) + { + return; + } + + softdevice_evt_irq_disable(); + m_nrf_sdh_suspended = true; +} + + +void nrf_sdh_resume(void) +{ + if ((!m_nrf_sdh_suspended) || (!m_nrf_sdh_enabled)) + { + return; + } + + // Force calling ISR again to make sure that events not previously pulled have been processed. +#ifdef SOFTDEVICE_PRESENT + ret_code_t ret_code = sd_nvic_SetPendingIRQ((IRQn_Type)SD_EVT_IRQn); + APP_ERROR_CHECK(ret_code); +#else + NVIC_SetPendingIRQ((IRQn_Type)SD_EVT_IRQn); +#endif + + softdevices_evt_irq_enable(); + + m_nrf_sdh_suspended = false; +} + + +bool nrf_sdh_is_suspended(void) +{ + return (!m_nrf_sdh_enabled) || (m_nrf_sdh_suspended); +} + + +void nrf_sdh_evts_poll(void) +{ + nrf_section_iter_t iter; + + // Notify observers about pending SoftDevice event. + for (nrf_section_iter_init(&iter, &sdh_stack_observers); + nrf_section_iter_get(&iter) != NULL; + nrf_section_iter_next(&iter)) + { + nrf_sdh_stack_observer_t * p_observer; + nrf_sdh_stack_evt_handler_t handler; + + p_observer = (nrf_sdh_stack_observer_t *) nrf_section_iter_get(&iter); + handler = p_observer->handler; + + handler(p_observer->p_context); + } +} + + +#if (NRF_SDH_DISPATCH_MODEL == NRF_SDH_DISPATCH_MODEL_INTERRUPT) + +void SD_EVT_IRQHandler(void) +{ + nrf_sdh_evts_poll(); +} + +#elif (NRF_SDH_DISPATCH_MODEL == NRF_SDH_DISPATCH_MODEL_APPSH) + +/**@brief Function for polling SoftDevice events. + * + * @note This function is compatible with @ref app_sched_event_handler_t. + * + * @param[in] p_event_data Pointer to the event data. + * @param[in] event_size Size of the event data. + */ +static void appsh_events_poll(void * p_event_data, uint16_t event_size) +{ + UNUSED_PARAMETER(p_event_data); + UNUSED_PARAMETER(event_size); + + nrf_sdh_evts_poll(); +} + + +void SD_EVT_IRQHandler(void) +{ + ret_code_t ret_code = app_sched_event_put(NULL, 0, appsh_events_poll); + APP_ERROR_CHECK(ret_code); +} + +#elif (NRF_SDH_DISPATCH_MODEL == NRF_SDH_DISPATCH_MODEL_POLLING) + +#else + +#error "Unknown SoftDevice handler dispatch model." + +#endif // NRF_SDH_DISPATCH_MODEL + +#endif // NRF_MODULE_ENABLED(NRF_SDH) diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_COMMON/softdevice/common/nrf_sdh.h b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_COMMON/softdevice/common/nrf_sdh.h new file mode 100644 index 0000000000..a63e4cb7b3 --- /dev/null +++ b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_COMMON/softdevice/common/nrf_sdh.h @@ -0,0 +1,305 @@ +/** + * Copyright (c) 2017 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/** @file + * + * @defgroup nrf_sdh SoftDevice Handler + * @{ + * @ingroup app_common + * @brief API for initializing and disabling the SoftDevice. + */ + +#ifndef NRF_SDH_H__ +#define NRF_SDH_H__ + +#include +#include "sdk_config.h" +#include "sdk_errors.h" +#include "nrf_section_iter.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name Softdevice Handler dispatch models + * @{ + * @ingroup nrf_sdh */ + +/**@brief SoftDevice events are passed to the application from the interrupt context. */ +#define NRF_SDH_DISPATCH_MODEL_INTERRUPT 0 + +/**@brief SoftDevice events are passed to the application using @ref app_scheduler. + * + * @note @ref app_scheduler must be initialized before enabling the SoftDevice handler. + */ +#define NRF_SDH_DISPATCH_MODEL_APPSH 1 + +/**@brief SoftDevice events are polled manually using @ref nrf_sdh_evts_poll(). + * + * @note In this mode, a user application can also implement SD_EVT_IRQHandler() to receive a + * notification about incoming events. + */ +#define NRF_SDH_DISPATCH_MODEL_POLLING 2 + +/** @} */ + +/** + * @name SoftDevice Handler state change requests + * @{ + * @ingroup nrf_sdh */ + +/**@brief SoftDevice Handler state requests. */ +typedef enum +{ + NRF_SDH_EVT_ENABLE_REQUEST, //!< Request to enable the SoftDevice. + NRF_SDH_EVT_DISABLE_REQUEST, //!< Request to disable the SoftDevice. +} nrf_sdh_req_evt_t; + +/**@brief SoftDevice Handler state request handler. + * + * @retval true If ready for the SoftDevice to change state. + * @retval false If not ready for the SoftDevice to change state. + * If false is returned, the state change is aborted. + */ +typedef bool (*nrf_sdh_req_evt_handler_t)(nrf_sdh_req_evt_t request, void * p_context); + +/**@brief SoftDevice Handler state request observer. */ +typedef struct +{ + nrf_sdh_req_evt_handler_t handler; //!< Request handler. + void * p_context; //!< A parameter to the handler function. +} const nrf_sdh_req_observer_t; + +/**@brief Macro for registering a SoftDevice state change request observer. + * + * An observer of SoftDevice state change requests receives requests to change the state of the + * SoftDevice from enabled to disabled and vice versa. These requests may or may not be acknowledged + * by the observer, depending on the value returned by its request handler function. Thus, a + * request observer has the capability to defer the change of state of the SoftDevice. If it does + * so, it has the responsibility to call @ref nrf_sdh_request_continue when it is ready to let the + * SoftDevice change its state. If such capability is not necessary and you only need to be informed + * about changes of the SoftDevice state, use the @ref NRF_SDH_STATE_OBSERVER macro instead. + * + * @note This macro places the observer in a section named "sdh_req_observers". + * + * @param[in] _observer Name of the observer. + * @param[in] _prio Priority of the observer's event handler. + * The smaller the number, the higher the priority. + * @hideinitializer + */ +#define NRF_SDH_REQUEST_OBSERVER(_observer, _prio) \ +STATIC_ASSERT(NRF_SDH_ENABLED, "NRF_SDH_ENABLED not set!"); \ +STATIC_ASSERT(_prio < NRF_SDH_REQ_OBSERVER_PRIO_LEVELS, "Priority level unavailable."); \ +/*lint -esym(528,*_observer) -esym(529,*_observer) : Symbol not referenced. */ \ +NRF_SECTION_SET_ITEM_REGISTER(sdh_req_observers, _prio, nrf_sdh_req_observer_t const _observer) + +/** @} */ + +/** + * @name SoftDevice Handler state events + * @{ + * @ingroup nrf_sdh */ + +/**@brief SoftDevice Handler state events. */ +typedef enum +{ + NRF_SDH_EVT_STATE_ENABLE_PREPARE, //!< SoftDevice is going to be enabled. + NRF_SDH_EVT_STATE_ENABLED, //!< SoftDevice is enabled. + NRF_SDH_EVT_STATE_DISABLE_PREPARE, //!< SoftDevice is going to be disabled. + NRF_SDH_EVT_STATE_DISABLED, //!< SoftDevice is disabled. +} nrf_sdh_state_evt_t; + +/**@brief SoftDevice Handler state event handler. */ +typedef void (*nrf_sdh_state_evt_handler_t)(nrf_sdh_state_evt_t state, void * p_context); + +/**@brief SoftDevice Handler state observer. */ +typedef struct +{ + nrf_sdh_state_evt_handler_t handler; //!< State event handler. + void * p_context; //!< A parameter to the event handler. +} const nrf_sdh_state_observer_t; + +/**@brief Macro for registering a SoftDevice state observer. + * + * A SoftDevice state observer receives events when the SoftDevice state has changed or is + * about to change. These events are only meant to inform the state observer, which, contrary + * to a state change request observer, does not have the capability to defer the change of state. + * If such capability is required, use the @ref NRF_SDH_REQUEST_OBSERVER macro instead. + * + * This macro places the observer in a section named "sdh_state_observers". + * + * @param[in] _observer Name of the observer. + * @param[in] _prio Priority of the observer's event handler. + * The smaller the number, the higher the priority. + * @hideinitializer + */ +#define NRF_SDH_STATE_OBSERVER(_observer, _prio) \ +STATIC_ASSERT(NRF_SDH_ENABLED, "NRF_SDH_ENABLED not set!"); \ +STATIC_ASSERT(_prio < NRF_SDH_STATE_OBSERVER_PRIO_LEVELS, "Priority level unavailable."); \ +/*lint -esym(528,*_observer) -esym(529,*_observer) : Symbol not referenced. */ \ +NRF_SECTION_SET_ITEM_REGISTER(sdh_state_observers, _prio, static nrf_sdh_state_observer_t const _observer) + +/** @} */ + +/** + * @name SoftDevice stack events + * @{ + * @ingroup nrf_sdh */ + +/**@brief SoftDevice stack event handler. */ +typedef void (*nrf_sdh_stack_evt_handler_t)(void * p_evt); + +/**@brief SoftDevice stack event observer. */ +typedef struct +{ + nrf_sdh_stack_evt_handler_t handler; //!< SoftDevice event handler. + void * p_context; //!< A parameter to the event handler. +} const nrf_sdh_stack_observer_t; + +/**@brief Macro for registering a SoftDevice stack events observer. + * + * A SoftDevice stack event observer receives all events from the SoftDevice. These events can be + * either BLE, ANT, or SoC events. If you need to receive BLE, ANT, or SoC events separately, use the + * @ref NRF_SDH_BLE_OBSERVER, @ref NRF_SDH_ANT_OBSERVER, or @ref NRF_SDH_SOC_OBSERVER macros + * respectively. + * + * @note This macro places the observer in a section named "sdh_stack_observers". + * + * @param[in] _observer Name of the observer. + * @param[in] _prio Priority of the observer's event handler. + * The smaller the number, the higher the priority. + ** @hideinitializer + */ +#define NRF_SDH_STACK_OBSERVER(_observer, _prio) \ +STATIC_ASSERT(NRF_SDH_ENABLED, "NRF_SDH_ENABLED not set!"); \ +STATIC_ASSERT(_prio < NRF_SDH_STACK_OBSERVER_PRIO_LEVELS, "Priority level unavailable."); \ +/*lint -esym(528,*_observer) -esym(529,*_observer) : Symbol not referenced. */ \ +NRF_SECTION_SET_ITEM_REGISTER(sdh_stack_observers, _prio, static nrf_sdh_stack_observer_t const _observer) + +/** @} */ + +/**@brief Function for requesting to enable the SoftDevice. + * + * This function issues a @ref NRF_SDH_EVT_ENABLE_REQUEST request to all observers that + * were registered using the @ref NRF_SDH_REQUEST_OBSERVER macro. The observers may or + * may not acknowledge the request. If all observers acknowledge the request, the + * SoftDevice will be enabled. Otherwise, the process will be stopped and the observers + * that did not acknowledge have the responsibility to restart it by calling + * @ref nrf_sdh_request_continue when they are ready for the SoftDevice to change state. + * + * @retval NRF_SUCCESS The process is started. + * @retval NRF_ERROR_INVALID_STATE The SoftDevice is already enabled. + */ +ret_code_t nrf_sdh_enable_request(void); + + +/**@brief Function for requesting to disable the SoftDevice. + * + * This function issues a @ref NRF_SDH_EVT_DISABLE_REQUEST request to all observers that + * were registered using the @ref NRF_SDH_REQUEST_OBSERVER macro. The observers may or + * may not acknowledge the request. If all observers acknowledge the request, the + * SoftDevice will be disabled. Otherwise, the process will be stopped and the observers + * that did not acknowledge have the responsibility to restart it by calling + * @ref nrf_sdh_request_continue when they are ready for the SoftDevice to change state. + * + * @retval NRF_SUCCESS The process is started. + * @retval NRF_ERROR_INVALID_STATE The SoftDevice is already disabled. + */ +ret_code_t nrf_sdh_disable_request(void); + + +/**@brief Function for restarting the SoftDevice Enable/Disable process. + * + * Modules which did not acknowledge a @ref NRF_SDH_EVT_ENABLE_REQUEST or + * @ref NRF_SDH_EVT_DISABLE_REQUEST request must call this function to restart the + * SoftDevice state change process. + * + * @retval NRF_SUCCESS The process is restarted. + * @retval NRF_ERROR_INVALID_STATE No state change request was pending. + */ +ret_code_t nrf_sdh_request_continue(void); + + +/**@brief Function for retrieving the SoftDevice state. + * + * @retval true If the SoftDevice is enabled. + * @retval false If the SoftDevice is disabled. + */ +bool nrf_sdh_is_enabled(void); + + +/**@brief Function for stopping the incoming stack events. + * + * This function disables the SoftDevice interrupt. To resume polling for events, + * call @ref nrf_sdh_resume. + */ +void nrf_sdh_suspend(void); + + +/**@brief Function for resuming polling incoming events from the SoftDevice. */ +void nrf_sdh_resume(void); + + +/**@brief Function for retrieving the information about the module state. + * + * @retval true The SoftDevice handler is paused, and it will not fetch events from the stack. + * @retval false The SoftDevice handler is running, and it will fetch and dispatch events from + * the stack to the registered stack observers. + */ +bool nrf_sdh_is_suspended(void); + + +/**@brief Function for polling stack events from the SoftDevice. + * + * The events are passed to the application using the registered event handlers. + * + * @note @ref NRF_SDH_DISPATCH_MODEL_POLLING must be selected to use this function. + */ +void nrf_sdh_evts_poll(void); + + +#ifdef __cplusplus +} +#endif + +#endif // NRF_SDH_H__ + +/** @} */ diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_COMMON/softdevice/common/nrf_sdh_ant.c b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_COMMON/softdevice/common/nrf_sdh_ant.c new file mode 100644 index 0000000000..27d1ed5ec6 --- /dev/null +++ b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_COMMON/softdevice/common/nrf_sdh_ant.c @@ -0,0 +1,162 @@ +/** + * Copyright (c) 2017 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "sdk_common.h" +#if NRF_MODULE_ENABLED(NRF_SDH_ANT) + +#include "nrf_sdh_ant.h" + +#include "nrf_sdh.h" +#include "app_error.h" +#include "nrf_strerror.h" +#include "ant_interface.h" + + +#define NRF_LOG_MODULE_NAME nrf_sdh_ant +#if NRF_SDH_ANT_LOG_ENABLED + #define NRF_LOG_LEVEL NRF_SDH_ANT_LOG_LEVEL + #define NRF_LOG_INFO_COLOR NRF_SDH_ANT_INFO_COLOR + #define NRF_LOG_DEBUG_COLOR NRF_SDH_ANT_DEBUG_COLOR +#else + #define NRF_LOG_LEVEL 0 +#endif // NRF_SDH_ANT_LOG_ENABLED +#include "nrf_log.h" +NRF_LOG_MODULE_REGISTER(); + + +STATIC_ASSERT(NRF_SDH_ANT_TOTAL_CHANNELS_ALLOCATED <= MAX_ANT_CHANNELS); +STATIC_ASSERT(NRF_SDH_ANT_ENCRYPTED_CHANNELS <= NRF_SDH_ANT_TOTAL_CHANNELS_ALLOCATED); + +// Create section set "sdh_ant_observers". +NRF_SECTION_SET_DEF(sdh_ant_observers, nrf_sdh_ant_evt_observer_t, NRF_SDH_ANT_OBSERVER_PRIO_LEVELS); + +// Memory buffer provided in order to support channel configuration. +__ALIGN(4) static uint8_t m_ant_stack_buffer[NRF_SDH_ANT_BUF_SIZE]; + + +static bool m_stack_is_enabled; + + +ret_code_t nrf_sdh_ant_enable(void) +{ + ANT_ENABLE ant_enable_cfg = + { + .ucTotalNumberOfChannels = NRF_SDH_ANT_TOTAL_CHANNELS_ALLOCATED, + .ucNumberOfEncryptedChannels = NRF_SDH_ANT_ENCRYPTED_CHANNELS, + .usNumberOfEvents = NRF_SDH_ANT_EVENT_QUEUE_SIZE, + .pucMemoryBlockStartLocation = m_ant_stack_buffer, + .usMemoryBlockByteSize = sizeof(m_ant_stack_buffer), + }; + + ret_code_t ret_code = sd_ant_enable(&ant_enable_cfg); + if (ret_code == NRF_SUCCESS) + { + m_stack_is_enabled = true; + } + else + { + NRF_LOG_ERROR("sd_ant_enable() returned %s.", nrf_strerror_get(ret_code)); + } + + return ret_code; +} + + +/**@brief Function for polling ANT events. + * + * @param[in] p_context Context of the observer. + */ +static void nrf_sdh_ant_evts_poll(void * p_context) +{ + UNUSED_VARIABLE(p_context); + + ret_code_t ret_code; + +#ifndef SER_CONNECTIVITY + if (!m_stack_is_enabled) + { + return; + } +#else + UNUSED_VARIABLE(m_stack_is_enabled); +#endif // SER_CONNECTIVITY + + while (true) + { + ant_evt_t ant_evt; + + ret_code = sd_ant_event_get(&ant_evt.channel, &ant_evt.event, ant_evt.message.aucMessage); + if (ret_code != NRF_SUCCESS) + { + break; + } + + NRF_LOG_DEBUG("ANT Event 0x%02X Channel 0x%02X", ant_evt.event, ant_evt.channel); + + // Forward the event to ANT observers. + nrf_section_iter_t iter; + for (nrf_section_iter_init(&iter, &sdh_ant_observers); + nrf_section_iter_get(&iter) != NULL; + nrf_section_iter_next(&iter)) + { + nrf_sdh_ant_evt_observer_t * p_observer; + nrf_sdh_ant_evt_handler_t handler; + + p_observer = (nrf_sdh_ant_evt_observer_t *) nrf_section_iter_get(&iter); + handler = p_observer->handler; + + handler(&ant_evt, p_observer->p_context); + } + } + + if (ret_code != NRF_ERROR_NOT_FOUND) + { + APP_ERROR_HANDLER(ret_code); + } +} + + +NRF_SDH_STACK_OBSERVER(m_nrf_sdh_ant_evts_poll, NRF_SDH_ANT_STACK_OBSERVER_PRIO) = +{ + .handler = nrf_sdh_ant_evts_poll, + .p_context = NULL, +}; + +#endif // NRF_MODULE_ENABLED(NRF_SDH_ANT) diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_COMMON/softdevice/common/nrf_sdh_ant.h b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_COMMON/softdevice/common/nrf_sdh_ant.h new file mode 100644 index 0000000000..10811d49a5 --- /dev/null +++ b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_COMMON/softdevice/common/nrf_sdh_ant.h @@ -0,0 +1,182 @@ +/** + * Copyright (c) 2017 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/**@file + * + * @defgroup nrf_sdh_ant ANT support in SoftDevice Handler + * @{ + * @ingroup nrf_sdh + * @brief This file contains the declarations of types and functions required for ANT stack support. + */ + +#ifndef NRF_SDH_ANT_H__ +#define NRF_SDH_ANT_H__ + +#include "ant_parameters.h" +#include "app_util.h" +#include "nrf_section_iter.h" +#include "sdk_errors.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define NRF_SDH_ANT_EVT_CHANNEL_FIELD_SIZE 1 //!< Size of the channel field in ANT stack event +#define NRF_SDH_ANT_EVT_EVENT_FIELD_SIZE 1 //!< Size of the event field in ANT stack event + +/**@brief Size of the buffer provided to the ANT SoftDevice. + * @hideinitializer + */ +#define NRF_SDH_ANT_BUF_SIZE ANT_ENABLE_GET_REQUIRED_SPACE( \ + NRF_SDH_ANT_TOTAL_CHANNELS_ALLOCATED, \ + NRF_SDH_ANT_ENCRYPTED_CHANNELS, \ + NRF_SDH_ANT_BURST_QUEUE_SIZE, \ + NRF_SDH_ANT_EVENT_QUEUE_SIZE) + +/**@brief Size of the buffer provided to the ANT SoftDevice to receive ANT events. */ +#define NRF_SDH_ANT_MESSAGE_SIZE ((CEIL_DIV(MESG_BUFFER_SIZE, sizeof(uint32_t))) * sizeof(uint32_t)) + +/**@brief Size of the buffer provided to the Events Scheduler to hold ANT events. */ +#define NRF_SDH_ANT_EVT_BUF_SIZE ((CEIL_DIV(NRF_SDH_ANT_MESSAGE_SIZE + \ + NRF_SDH_ANT_EVT_CHANNEL_FIELD_SIZE + \ + NRF_SDH_ANT_EVT_EVENT_FIELD_SIZE, \ + sizeof(uint32_t))) * sizeof(uint32_t)) + + +#if !(defined(__LINT__)) +/**@brief Macro for registering an ANT observer. Modules that want to be + * notified about ANT events must register the handler using this macro. + * + * @details This macro places the observer in a section named "sdh_ant_observers". + * + * @param[in] _name Observer name. + * @param[in] _prio Priority of the observer event handler. + * The smaller the number, the higher the priority. + * @param[in] _handler ANT event handler. + * @param[in] _context Parameter to the event handler. + * @hideinitializer + */ +#define NRF_SDH_ANT_OBSERVER(_name, _prio, _handler, _context) \ +STATIC_ASSERT(NRF_SDH_ANT_ENABLED, "NRF_SDH_ANT_ENABLED not set!"); \ +STATIC_ASSERT(_prio < NRF_SDH_ANT_OBSERVER_PRIO_LEVELS, "Priority level unavailable."); \ +NRF_SECTION_SET_ITEM_REGISTER(sdh_ant_observers, _prio, static nrf_sdh_ant_evt_observer_t _name) = \ +{ \ + .handler = _handler, \ + .p_context = _context \ +} + +/**@brief Macro for registering an array of @ref nrf_sdh_ant_evt_observer_t. + * Modules that want to be notified about ANT events must register the handler using + * this macro. + * + * Each observer's handler will be dispatched an event with its relative context from @p _context. + * This macro places the observer in a section named "sdh_ant_observers". + * + * @param[in] _name Observer name. + * @param[in] _prio Priority of the observer event handler. + * The smaller the number, the higher the priority. + * @param[in] _handler ANT event handler. + * @param[in] _context An array of parameters to the event handler. + * @param[in] _cnt Number of observers to register. + * @hideinitializer + */ +#define NRF_SDH_ANT_OBSERVERS(_name, _prio, _handler, _context, _cnt) \ +STATIC_ASSERT(NRF_SDH_ANT_ENABLED, "NRF_SDH_ANT_ENABLED not set!"); \ +STATIC_ASSERT(_prio < NRF_SDH_ANT_OBSERVER_PRIO_LEVELS, "Priority level unavailable."); \ +NRF_SECTION_SET_ITEM_REGISTER(sdh_ant_observers, _prio, static nrf_sdh_ant_evt_observer_t _name[_cnt]) = \ +{ \ + MACRO_REPEAT_FOR(_cnt, HANDLER_SET, _handler, _context) \ +} + +#if !(defined(DOXYGEN)) +#define HANDLER_SET(_idx, _handler, _context) \ +{ \ + .handler = _handler, \ + .p_context = _context[_idx], \ +}, +#endif + +#else // __LINT__ + +/* Swallow semicolons */ +/*lint -save -esym(528, *) -esym(529, *) : Symbol not referenced. */ +#define NRF_SDH_ANT_OBSERVER(A, B, C, D) static int semicolon_swallow_##A +#define NRF_SDH_ANT_OBSERVERS(A, B, C, D, E) static int semicolon_swallow_##A +/*lint -restore */ + +#endif + + +/**@brief ANT stack event. */ +typedef struct +{ + ANT_MESSAGE message; //!< ANT Message. + uint8_t channel; //!< Channel number. + uint8_t event; //!< Event code. +} ant_evt_t; + +/**@brief ANT stack event handler. */ +typedef void (*nrf_sdh_ant_evt_handler_t)(ant_evt_t * p_ant_evt, void * p_context); + +/**@brief ANT event observer. */ +typedef struct +{ + nrf_sdh_ant_evt_handler_t handler; //!< ANT event handler. + void * p_context; //!< A parameter to the event handler. +} const nrf_sdh_ant_evt_observer_t; + + +/**@brief Function for configuring and enabling the ANT stack. + * + * @details The function sets the channel configuration for the stack using the parameters + * provided in the @c sdk_config file. It also assigns a correspondingly large + * buffer as a static resource. + */ +ret_code_t nrf_sdh_ant_enable(void); + +#ifdef __cplusplus +} +#endif + +#endif // NRF_SDH_ANT_H__ + +/** @} */ diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_COMMON/softdevice/common/nrf_sdh_ble.c b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_COMMON/softdevice/common/nrf_sdh_ble.c new file mode 100644 index 0000000000..ed882dc88e --- /dev/null +++ b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_COMMON/softdevice/common/nrf_sdh_ble.c @@ -0,0 +1,324 @@ +/** + * Copyright (c) 2017 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "sdk_common.h" +#if NRF_MODULE_ENABLED(NRF_SDH_BLE) + +#include "nrf_sdh_ble.h" + +#include "nrf_sdh.h" +#include "app_error.h" +#include "nrf_strerror.h" + + +#define NRF_LOG_MODULE_NAME nrf_sdh_ble +#if NRF_SDH_BLE_LOG_ENABLED + #define NRF_LOG_LEVEL NRF_SDH_BLE_LOG_LEVEL + #define NRF_LOG_INFO_COLOR NRF_SDH_BLE_INFO_COLOR + #define NRF_LOG_DEBUG_COLOR NRF_SDH_BLE_DEBUG_COLOR +#else + #define NRF_LOG_LEVEL 0 +#endif // NRF_SDH_BLE_LOG_ENABLED +#include "nrf_log.h" +NRF_LOG_MODULE_REGISTER(); + + +// Create section set "sdh_ble_observers". +NRF_SECTION_SET_DEF(sdh_ble_observers, nrf_sdh_ble_evt_observer_t, NRF_SDH_BLE_OBSERVER_PRIO_LEVELS); + + +//lint -save -e10 -e19 -e40 -e27 Illegal character (0x24) +#if defined(__CC_ARM) + extern uint32_t Image$$RW_IRAM1$$Base; + uint32_t const * const m_ram_start = &Image$$RW_IRAM1$$Base; +#elif defined(__ICCARM__) + extern uint32_t __ICFEDIT_region_RAM_start__; + uint32_t const * const m_ram_start = &__ICFEDIT_region_RAM_start__; +#elif defined(__SES_ARM) + extern uint32_t __app_ram_start__; + uint32_t const * const m_ram_start = &__app_ram_start__; +#elif defined(__GNUC__) + extern uint32_t __data_start__; + uint32_t const * const m_ram_start = &__data_start__; +#endif +//lint -restore + +#define RAM_START 0x20000000 +#define APP_RAM_START (uint32_t)m_ram_start + + +static bool m_stack_is_enabled; + + +ret_code_t nrf_sdh_ble_app_ram_start_get(uint32_t * p_app_ram_start) +{ + if (p_app_ram_start == NULL) + { + return NRF_ERROR_NULL; + } + + *p_app_ram_start = APP_RAM_START; + + return NRF_SUCCESS; +} + + +ret_code_t nrf_sdh_ble_default_cfg_set(uint8_t conn_cfg_tag, uint32_t * p_ram_start) +{ + uint32_t ret_code; + + ret_code = nrf_sdh_ble_app_ram_start_get(p_ram_start); + if (ret_code != NRF_SUCCESS) + { + return ret_code; + } + +#ifdef S112 + STATIC_ASSERT(NRF_SDH_BLE_CENTRAL_LINK_COUNT == 0, "When using s112, NRF_SDH_BLE_CENTRAL_LINK_COUNT must be 0."); +#endif + + // Overwrite some of the default settings of the BLE stack. + // If any of the calls to sd_ble_cfg_set() fail, log the error but carry on so that + // wrong RAM settings can be caught by nrf_sdh_ble_enable() and a meaningful error + // message will be printed to the user suggesting the correct value. + ble_cfg_t ble_cfg; + +#if (NRF_SDH_BLE_TOTAL_LINK_COUNT != 0) + // Configure the connection count. + memset(&ble_cfg, 0, sizeof(ble_cfg)); + ble_cfg.conn_cfg.conn_cfg_tag = conn_cfg_tag; + ble_cfg.conn_cfg.params.gap_conn_cfg.conn_count = NRF_SDH_BLE_TOTAL_LINK_COUNT; + ble_cfg.conn_cfg.params.gap_conn_cfg.event_length = NRF_SDH_BLE_GAP_EVENT_LENGTH; + + ret_code = sd_ble_cfg_set(BLE_CONN_CFG_GAP, &ble_cfg, *p_ram_start); + if (ret_code != NRF_SUCCESS) + { + NRF_LOG_ERROR("sd_ble_cfg_set() returned %s when attempting to set BLE_CONN_CFG_GAP.", + nrf_strerror_get(ret_code)); + } + + // Configure the connection roles. + memset(&ble_cfg, 0, sizeof(ble_cfg)); + ble_cfg.gap_cfg.role_count_cfg.periph_role_count = NRF_SDH_BLE_PERIPHERAL_LINK_COUNT; +#ifndef S112 + ble_cfg.gap_cfg.role_count_cfg.central_role_count = NRF_SDH_BLE_CENTRAL_LINK_COUNT; + ble_cfg.gap_cfg.role_count_cfg.central_sec_count = MIN(NRF_SDH_BLE_CENTRAL_LINK_COUNT, + BLE_GAP_ROLE_COUNT_CENTRAL_SEC_DEFAULT); +#endif + + ret_code = sd_ble_cfg_set(BLE_GAP_CFG_ROLE_COUNT, &ble_cfg, *p_ram_start); + if (ret_code != NRF_SUCCESS) + { + NRF_LOG_ERROR("sd_ble_cfg_set() returned %s when attempting to set BLE_GAP_CFG_ROLE_COUNT.", + nrf_strerror_get(ret_code)); + } + + // Configure the maximum ATT MTU. +#if (NRF_SDH_BLE_GATT_MAX_MTU_SIZE != 23) + memset(&ble_cfg, 0x00, sizeof(ble_cfg)); + ble_cfg.conn_cfg.conn_cfg_tag = conn_cfg_tag; + ble_cfg.conn_cfg.params.gatt_conn_cfg.att_mtu = NRF_SDH_BLE_GATT_MAX_MTU_SIZE; + + ret_code = sd_ble_cfg_set(BLE_CONN_CFG_GATT, &ble_cfg, *p_ram_start); + if (ret_code != NRF_SUCCESS) + { + NRF_LOG_ERROR("sd_ble_cfg_set() returned %s when attempting to set BLE_CONN_CFG_GATT.", + nrf_strerror_get(ret_code)); + } +#endif // NRF_SDH_BLE_GATT_MAX_MTU_SIZE != 23 +#endif // NRF_SDH_BLE_TOTAL_LINK_COUNT != 0 + + // Configure number of custom UUIDS. + memset(&ble_cfg, 0, sizeof(ble_cfg)); + ble_cfg.common_cfg.vs_uuid_cfg.vs_uuid_count = NRF_SDH_BLE_VS_UUID_COUNT; + + ret_code = sd_ble_cfg_set(BLE_COMMON_CFG_VS_UUID, &ble_cfg, *p_ram_start); + if (ret_code != NRF_SUCCESS) + { + NRF_LOG_ERROR("sd_ble_cfg_set() returned %s when attempting to set BLE_COMMON_CFG_VS_UUID.", + nrf_strerror_get(ret_code)); + } + + // Configure the GATTS attribute table. + memset(&ble_cfg, 0x00, sizeof(ble_cfg)); + ble_cfg.gatts_cfg.attr_tab_size.attr_tab_size = NRF_SDH_BLE_GATTS_ATTR_TAB_SIZE; + + ret_code = sd_ble_cfg_set(BLE_GATTS_CFG_ATTR_TAB_SIZE, &ble_cfg, *p_ram_start); + if (ret_code != NRF_SUCCESS) + { + NRF_LOG_ERROR("sd_ble_cfg_set() returned %s when attempting to set BLE_GATTS_CFG_ATTR_TAB_SIZE.", + nrf_strerror_get(ret_code)); + } + + // Configure Service Changed characteristic. + memset(&ble_cfg, 0x00, sizeof(ble_cfg)); + ble_cfg.gatts_cfg.service_changed.service_changed = NRF_SDH_BLE_SERVICE_CHANGED; + + ret_code = sd_ble_cfg_set(BLE_GATTS_CFG_SERVICE_CHANGED, &ble_cfg, *p_ram_start); + if (ret_code != NRF_SUCCESS) + { + NRF_LOG_ERROR("sd_ble_cfg_set() returned %s when attempting to set BLE_GATTS_CFG_SERVICE_CHANGED.", + nrf_strerror_get(ret_code)); + } + + return NRF_SUCCESS; +} + + +/**@brief Function for finding the end address of the RAM. */ +static uint32_t ram_end_address_get(void) +{ + uint32_t ram_total_size; + +#ifdef NRF51 + uint32_t block_size = NRF_FICR->SIZERAMBLOCKS; + ram_total_size = block_size * NRF_FICR->NUMRAMBLOCK; +#else + ram_total_size = NRF_FICR->INFO.RAM * 1024; +#endif + + return RAM_START + ram_total_size; +} + + +ret_code_t nrf_sdh_ble_enable(uint32_t * const p_app_ram_start) +{ + // Start of RAM, obtained from linker symbol. + uint32_t const app_ram_start_link = *p_app_ram_start; + + ret_code_t ret_code = sd_ble_enable(p_app_ram_start); + if (*p_app_ram_start > app_ram_start_link) + { + NRF_LOG_WARNING("Insufficient RAM allocated for the SoftDevice."); + + NRF_LOG_WARNING("Change the RAM start location from 0x%x to 0x%x.", + app_ram_start_link, *p_app_ram_start); + NRF_LOG_WARNING("Maximum RAM size for application is 0x%x.", + ram_end_address_get() - (*p_app_ram_start)); + } + else + { + NRF_LOG_DEBUG("RAM starts at 0x%x", app_ram_start_link); + if (*p_app_ram_start != app_ram_start_link) + { + NRF_LOG_DEBUG("RAM start location can be adjusted to 0x%x.", *p_app_ram_start); + + NRF_LOG_DEBUG("RAM size for application can be adjusted to 0x%x.", + ram_end_address_get() - (*p_app_ram_start)); + } + } + + if (ret_code == NRF_SUCCESS) + { + m_stack_is_enabled = true; + } + else + { + NRF_LOG_ERROR("sd_ble_enable() returned %s.", nrf_strerror_get(ret_code)); + } + + return ret_code; +} + + +/**@brief Function for polling BLE events. + * + * @param[in] p_context Context of the observer. + */ +static void nrf_sdh_ble_evts_poll(void * p_context) +{ + UNUSED_VARIABLE(p_context); + + ret_code_t ret_code; + + if (!m_stack_is_enabled) + { + return; + } + + while (true) + { + /*lint -save -e(587) */ + __ALIGN(4) uint8_t evt_buffer[NRF_SDH_BLE_EVT_BUF_SIZE]; + /*lint -restore */ + + ble_evt_t * p_ble_evt; + uint16_t evt_len = (uint16_t)sizeof(evt_buffer); + + ret_code = sd_ble_evt_get(evt_buffer, &evt_len); + if (ret_code != NRF_SUCCESS) + { + break; + } + + p_ble_evt = (ble_evt_t *)evt_buffer; + + NRF_LOG_DEBUG("BLE event: 0x%x.", p_ble_evt->header.evt_id); + + // Forward the event to BLE observers. + nrf_section_iter_t iter; + for (nrf_section_iter_init(&iter, &sdh_ble_observers); + nrf_section_iter_get(&iter) != NULL; + nrf_section_iter_next(&iter)) + { + nrf_sdh_ble_evt_observer_t * p_observer; + nrf_sdh_ble_evt_handler_t handler; + + p_observer = (nrf_sdh_ble_evt_observer_t *)nrf_section_iter_get(&iter); + handler = p_observer->handler; + + handler(p_ble_evt, p_observer->p_context); + } + } + + if (ret_code != NRF_ERROR_NOT_FOUND) + { + APP_ERROR_HANDLER(ret_code); + } +} + + +NRF_SDH_STACK_OBSERVER(m_nrf_sdh_ble_evts_poll, NRF_SDH_BLE_STACK_OBSERVER_PRIO) = +{ + .handler = nrf_sdh_ble_evts_poll, + .p_context = NULL, +}; + +#endif // NRF_MODULE_ENABLED(NRF_SDH_BLE) diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_COMMON/softdevice/common/nrf_sdh_ble.h b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_COMMON/softdevice/common/nrf_sdh_ble.h new file mode 100644 index 0000000000..3693367f2f --- /dev/null +++ b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_COMMON/softdevice/common/nrf_sdh_ble.h @@ -0,0 +1,184 @@ +/** + * Copyright (c) 2017 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/**@file + * + * @defgroup nrf_sdh_ble BLE support in SoftDevice Handler + * @{ + * @ingroup nrf_sdh + * @brief This file contains the declarations of types and functions required for BLE stack + * support. + */ + +#ifndef NRF_SDH_BLE_H__ +#define NRF_SDH_BLE_H__ + +#include "app_util.h" +#include "nrf_ble.h" +#include "nrf_section_iter.h" +#include "sdk_config.h" +#include "sdk_errors.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Size of the buffer for a BLE event. */ +#define NRF_SDH_BLE_EVT_BUF_SIZE BLE_EVT_LEN_MAX(NRF_SDH_BLE_GATT_MAX_MTU_SIZE) + + +#if !(defined(__LINT__)) +/**@brief Macro for registering @ref nrf_sdh_soc_evt_observer_t. Modules that want to be + * notified about SoC events must register the handler using this macro. + * + * @details This macro places the observer in a section named "sdh_soc_observers". + * + * @param[in] _name Observer name. + * @param[in] _prio Priority of the observer event handler. + * The smaller the number, the higher the priority. + * @param[in] _handler BLE event handler. + * @param[in] _context Parameter to the event handler. + * @hideinitializer + */ +#define NRF_SDH_BLE_OBSERVER(_name, _prio, _handler, _context) \ +STATIC_ASSERT(NRF_SDH_BLE_ENABLED, "NRF_SDH_BLE_ENABLED not set!"); \ +STATIC_ASSERT(_prio < NRF_SDH_BLE_OBSERVER_PRIO_LEVELS, "Priority level unavailable."); \ +NRF_SECTION_SET_ITEM_REGISTER(sdh_ble_observers, _prio, static nrf_sdh_ble_evt_observer_t _name) = \ +{ \ + .handler = _handler, \ + .p_context = _context \ +} + +/**@brief Macro for registering an array of @ref nrf_sdh_ble_evt_observer_t. + * Modules that want to be notified about SoC events must register the handler using + * this macro. + * + * Each observer's handler will be dispatched an event with its relative context from @p _context. + * This macro places the observer in a section named "sdh_ble_observers". + * + * @param[in] _name Observer name. + * @param[in] _prio Priority of the observer event handler. + * The smaller the number, the higher the priority. + * @param[in] _handler BLE event handler. + * @param[in] _context An array of parameters to the event handler. + * @param[in] _cnt Number of observers to register. + * @hideinitializer + */ +#define NRF_SDH_BLE_OBSERVERS(_name, _prio, _handler, _context, _cnt) \ +STATIC_ASSERT(NRF_SDH_BLE_ENABLED, "NRF_SDH_BLE_ENABLED not set!"); \ +STATIC_ASSERT(_prio < NRF_SDH_BLE_OBSERVER_PRIO_LEVELS, "Priority level unavailable."); \ +NRF_SECTION_SET_ITEM_REGISTER(sdh_ble_observers, _prio, static nrf_sdh_ble_evt_observer_t _name[_cnt]) = \ +{ \ + MACRO_REPEAT_FOR(_cnt, HANDLER_SET, _handler, _context) \ +} + +#if !(defined(DOXYGEN)) +#define HANDLER_SET(_idx, _handler, _context) \ +{ \ + .handler = _handler, \ + .p_context = _context[_idx], \ +}, +#endif + +#else // __LINT__ + +/* Swallow semicolons */ +/*lint -save -esym(528, *) -esym(529, *) : Symbol not referenced. */ +#define NRF_SDH_BLE_OBSERVER(A, B, C, D) static int semicolon_swallow_##A +#define NRF_SDH_BLE_OBSERVERS(A, B, C, D, E) static int semicolon_swallow_##A +/*lint -restore */ + +#endif + + +/**@brief BLE stack event handler. */ +typedef void (*nrf_sdh_ble_evt_handler_t)(ble_evt_t const * p_ble_evt, void * p_context); + +/**@brief BLE event observer. */ +typedef struct +{ + nrf_sdh_ble_evt_handler_t handler; //!< BLE event handler. + void * p_context; //!< A parameter to the event handler. +} const nrf_sdh_ble_evt_observer_t; + + +/**@brief Function for retrieving the address of the start of application's RAM. + * + * @param[out] p_app_ram_start Address of the start of application's RAM. + * + * @retval NRF_SUCCESS If the address was successfully retrieved. + * @retval NRF_ERROR_NULL If @p p_app_ram_start was @c NULL. + */ +ret_code_t nrf_sdh_ble_app_ram_start_get(uint32_t * p_app_ram_start); + + +/**@brief Set the default BLE stack configuration. + * + * This function configures the BLE stack with the settings specified in the + * SoftDevice handler BLE configuration. The following configurations will be set: + * - Number of peripheral links + * - Number of central links + * - ATT MTU size (for the given connection) + * - Vendor specific UUID count + * - GATTS Attribute table size + * - Service changed + * + * @param[in] conn_cfg_tag The connection to configure. + * @param[out] p_ram_start Application RAM start address. + */ +ret_code_t nrf_sdh_ble_default_cfg_set(uint8_t conn_cfg_tag, uint32_t * p_ram_start); + + +/**@brief Function for configuring and enabling the BLE stack. + * + * @param[in] p_app_ram_start Address of the start of application's RAM. + */ +ret_code_t nrf_sdh_ble_enable(uint32_t * p_app_ram_start); + + +#ifdef __cplusplus +} +#endif + +#endif // NRF_SDH_BLE_H__ + +/** @} */ diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_COMMON/softdevice/common/nrf_sdh_soc.c b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_COMMON/softdevice/common/nrf_sdh_soc.c new file mode 100644 index 0000000000..496e731274 --- /dev/null +++ b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_COMMON/softdevice/common/nrf_sdh_soc.c @@ -0,0 +1,118 @@ +/** + * Copyright (c) 2017 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "sdk_common.h" +#if NRF_MODULE_ENABLED(NRF_SDH_SOC) + +#include "nrf_sdh_soc.h" + +#include "nrf_sdh.h" +#include "nrf_soc.h" +#include "app_error.h" + + +#define NRF_LOG_MODULE_NAME nrf_sdh_soc +#if NRF_SDH_SOC_LOG_ENABLED + #define NRF_LOG_LEVEL NRF_SDH_SOC_LOG_LEVEL + #define NRF_LOG_INFO_COLOR NRF_SDH_SOC_INFO_COLOR + #define NRF_LOG_DEBUG_COLOR NRF_SDH_SOC_DEBUG_COLOR +#else + #define NRF_LOG_LEVEL 0 +#endif // NRF_SDH_SOC_LOG_ENABLED +#include "nrf_log.h" +NRF_LOG_MODULE_REGISTER(); + + +// Create section set "sdh_soc_observers". +NRF_SECTION_SET_DEF(sdh_soc_observers, nrf_sdh_soc_evt_observer_t, NRF_SDH_SOC_OBSERVER_PRIO_LEVELS); + + +/**@brief Function for polling SoC events. + * + * @param[in] p_context Context of the observer. + */ +static void nrf_sdh_soc_evts_poll(void * p_context) +{ + ret_code_t ret_code; + + UNUSED_VARIABLE(p_context); + + while (true) + { + uint32_t evt_id; + + ret_code = sd_evt_get(&evt_id); + if (ret_code != NRF_SUCCESS) + { + break; + } + + NRF_LOG_DEBUG("SoC event: 0x%x.", evt_id); + + // Forward the event to SoC observers. + nrf_section_iter_t iter; + for (nrf_section_iter_init(&iter, &sdh_soc_observers); + nrf_section_iter_get(&iter) != NULL; + nrf_section_iter_next(&iter)) + { + nrf_sdh_soc_evt_observer_t * p_observer; + nrf_sdh_soc_evt_handler_t handler; + + p_observer = (nrf_sdh_soc_evt_observer_t *) nrf_section_iter_get(&iter); + handler = p_observer->handler; + + handler(evt_id, p_observer->p_context); + } + } + + if (ret_code != NRF_ERROR_NOT_FOUND) + { + APP_ERROR_HANDLER(ret_code); + } +} + + +NRF_SDH_STACK_OBSERVER(m_nrf_sdh_soc_evts_poll, NRF_SDH_SOC_STACK_OBSERVER_PRIO) = +{ + .handler = nrf_sdh_soc_evts_poll, + .p_context = NULL, +}; + +#endif // NRF_MODULE_ENABLED(NRF_SDH_SOC) diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_COMMON/softdevice/common/nrf_sdh_soc.h b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_COMMON/softdevice/common/nrf_sdh_soc.h new file mode 100644 index 0000000000..295ce09366 --- /dev/null +++ b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_COMMON/softdevice/common/nrf_sdh_soc.h @@ -0,0 +1,143 @@ +/** + * Copyright (c) 2017 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/**@file + * + * @defgroup nrf_sdh_soc SoC support in SoftDevice Handler + * @{ + * @ingroup nrf_sdh + * @brief This file contains the declarations of types and functions required for SoftDevice Handler + * SoC support. + */ + +#ifndef NRF_SDH_SOC_H__ +#define NRF_SDH_SOC_H__ + +#include "sdk_common.h" +#include "nrf_section_iter.h" +#include "nrf_soc.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +#if !(defined(__LINT__)) +/**@brief Macro for registering @ref nrf_sdh_soc_evt_observer_t. Modules that want to be + * notified about SoC events must register the handler using this macro. + * + * @details This macro places the observer in a section named "sdh_soc_observers". + * + * @param[in] _name Observer name. + * @param[in] _prio Priority of the observer event handler. + * The smaller the number, the higher the priority. + * @param[in] _handler SoC event handler. + * @param[in] _context Parameter to the event handler. + * @hideinitializer + */ +#define NRF_SDH_SOC_OBSERVER(_name, _prio, _handler, _context) \ +STATIC_ASSERT(NRF_SDH_SOC_ENABLED, "NRF_SDH_SOC_ENABLED not set!"); \ +STATIC_ASSERT(_prio < NRF_SDH_SOC_OBSERVER_PRIO_LEVELS, "Priority level unavailable."); \ +NRF_SECTION_SET_ITEM_REGISTER(sdh_soc_observers, _prio, static nrf_sdh_soc_evt_observer_t _name) = \ +{ \ + .handler = _handler, \ + .p_context = _context \ +} + +/**@brief Macro for registering an array of @ref nrf_sdh_soc_evt_observer_t. + * Modules that want to be notified about SoC events must register the handler using + * this macro. + * + * Each observer's handler will be dispatched an event with its relative context from @p _context. + * This macro places the observer in a section named "sdh_soc_observers". + * + * @param[in] _name Observer name. + * @param[in] _prio Priority of the observer event handler. + * The smaller the number, the higher the priority. + * @param[in] _handler SoC event handler. + * @param[in] _context An array of parameters to the event handler. + * @param[in] _cnt Number of observers to register. + * @hideinitializer + */ +#define NRF_SDH_SOC_EVENT_OBSERVERS(_name, _prio, _handler, _context, _cnt) \ +STATIC_ASSERT(NRF_SDH_SOC_ENABLED, "NRF_SDH_SOC_ENABLED not set!"); \ +STATIC_ASSERT(_prio < NRF_SDH_SOC_OBSERVER_PRIO_LEVELS, "Priority level unavailable."); \ +NRF_SECTION_SET_ITEM_REGISTER(sdh_soc_observers, _prio, static nrf_sdh_soc_evt_observer_t _name[_cnt]) = \ +{ \ + MACRO_REPEAT_FOR(_cnt, HANDLER_SET, _handler, _context) \ +} + +#if !(defined(DOXYGEN)) +#define HANDLER_SET(_idx, _handler, _context) \ +{ \ + .handler = _handler, \ + .p_context = _context[_idx], \ +}, +#endif + +#else // __LINT__ + +/* Swallow semicolons */ +/*lint -save -esym(528, *) -esym(529, *) : Symbol not referenced. */ +#define NRF_SDH_SOC_OBSERVER(A, B, C, D) static int semicolon_swallow_##A +#define NRF_SDH_SOC_OBSERVERS(A, B, C, D, E) static int semicolon_swallow_##A +/*lint -restore */ + +#endif + + +/**@brief SoC event handler. */ +typedef void (*nrf_sdh_soc_evt_handler_t) (uint32_t evt_id, void * p_context); + +/**@brief SoC event observer. */ +typedef struct +{ + nrf_sdh_soc_evt_handler_t handler; //!< SoC event handler. + void * p_context; //!< A parameter to the event handler. +} const nrf_sdh_soc_evt_observer_t; + + +#ifdef __cplusplus +} +#endif + +#endif // NRF_SDH_SOC_H__ + +/** @} */ diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_S140_FULL/headers/ble.h b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_S140_FULL/headers/nrf_ble.h similarity index 100% rename from targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_S140_FULL/headers/ble.h rename to targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_S140_FULL/headers/nrf_ble.h diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_S140_FULL/mbed_lib.json b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_S140_FULL/mbed_lib.json new file mode 100644 index 0000000000..315f77a6fd --- /dev/null +++ b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/TARGET_SOFTDEVICE_S140_FULL/mbed_lib.json @@ -0,0 +1,38 @@ +{ + "name": "softdevice", + "macros": [ + "SOFTDEVICE_PRESENT=1", + "S140", + "BLE_STACK_SUPPORT_REQD", + "NRF_SDH_CLOCK_LF_SRC=1", + "NRF_SDH_CLOCK_LF_RC_CTIV=0", + "NRF_SDH_CLOCK_LF_RC_TEMP_CTIV=0", + "NRF_SDH_CLOCK_LF_XTAL_ACCURACY=7", + "NRF_SD_BLE_API_VERSION=5", + "NRF_SDH_ENABLED=1", + "NRF_SDH_BLE_ENABLED=1", + "PEER_MANAGER_ENABLED=1", + "NRF_SDH_BLE_GATT_MAX_MTU_SIZE=23", + "NRF_SDH_BLE_OBSERVER_PRIO_LEVELS=4", + "NRF_SDH_BLE_GAP_EVENT_LENGTH=3", + "BLE_ADV_BLE_OBSERVER_PRIO=1", + "BLE_CONN_STATE_BLE_OBSERVER_PRIO=0", + "BLE_CONN_PARAMS_BLE_OBSERVER_PRIO=1", + "NRF_BLE_GATT_BLE_OBSERVER_PRIO=1", + "NRF_SDH_DISPATCH_MODEL=2", + "NRF_SDH_SOC_ENABLED=1", + "NRF_SDH_STACK_OBSERVER_PRIO_LEVELS=2", + "NRF_SDH_STATE_OBSERVER_PRIO_LEVELS=2", + "NRF_SDH_SOC_OBSERVER_PRIO_LEVELS=2", + "NRF_SDH_REQ_OBSERVER_PRIO_LEVELS=2", + "NRF_SDH_BLE_STACK_OBSERVER_PRIO=0", + "NRF_SDH_SOC_STACK_OBSERVER_PRIO=0", + "FDS_BACKEND=2", + "SWI_DISABLE1=1" + ], + "target_overrides": { + "*": { + "target.bootloader_img": "hex/s140_nrf52_6.0.0_softdevice.hex" + } + } +} diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/components/libraries/fds/fds.c b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/components/libraries/fds/fds.c new file mode 100644 index 0000000000..99ddc66931 --- /dev/null +++ b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/components/libraries/fds/fds.c @@ -0,0 +1,2164 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "sdk_common.h" +#if NRF_MODULE_ENABLED(FDS) +#include "fds.h" +#include "fds_internal_defs.h" + +#include +#include +#include +#include "nrf_error.h" +#include "nrf_atomic.h" +#include "nrf_atfifo.h" + +#include "nrf_fstorage.h" +#if (FDS_BACKEND == NRF_FSTORAGE_SD) +#include "nrf_fstorage_sd.h" +#elif (FDS_BACKEND == NRF_FSTORAGE_NVMC) +#include "nrf_fstorage_nvmc.h" +#else +#error Invalid FDS backend. +#endif + +#if (FDS_CRC_CHECK_ON_READ) +#include "crc16.h" +#endif + + +static void fs_event_handler(nrf_fstorage_evt_t * evt); + +NRF_FSTORAGE_DEF(nrf_fstorage_t m_fs) = +{ + // The flash area boundaries are set in fds_init(). + .evt_handler = fs_event_handler, +}; + +// Internal status flags. +static struct +{ + bool volatile initialized; + nrf_atomic_flag_t initializing; +} m_flags; + +// The number of queued operations. +// Incremented by queue_start() and decremented by queue_has_next(). +static nrf_atomic_u32_t m_queued_op_cnt; + +// The number of registered users and their callback functions. +static nrf_atomic_u32_t m_users; +static fds_cb_t m_cb_table[FDS_MAX_USERS]; + +// The latest (largest) record ID written so far. +static nrf_atomic_u32_t m_latest_rec_id; + +// Queue of fds operations. +NRF_ATFIFO_DEF(m_queue, fds_op_t, FDS_OP_QUEUE_SIZE); + +// Structures used to hold informations about virtual pages. +static fds_page_t m_pages[FDS_DATA_PAGES]; +static fds_swap_page_t m_swap_page; + +// Garbage collection data. +static fds_gc_data_t m_gc; + + +static void event_send(fds_evt_t const * const p_evt) +{ + for (uint32_t user = 0; user < FDS_MAX_USERS; user++) + { + if (m_cb_table[user] != NULL) + { + m_cb_table[user](p_evt); + } + } +} + + +static void event_prepare(fds_op_t const * const p_op, fds_evt_t * const p_evt) +{ + switch (p_op->op_code) + { + case FDS_OP_INIT: + p_evt->id = FDS_EVT_INIT; + break; + + case FDS_OP_WRITE: + p_evt->id = FDS_EVT_WRITE; + p_evt->write.file_id = p_op->write.header.file_id; + p_evt->write.record_key = p_op->write.header.record_key; + p_evt->write.record_id = p_op->write.header.record_id; + p_evt->write.is_record_updated = 0; + break; + + case FDS_OP_UPDATE: + p_evt->id = FDS_EVT_UPDATE; + p_evt->write.file_id = p_op->write.header.file_id; + p_evt->write.record_key = p_op->write.header.record_key; + p_evt->write.record_id = p_op->write.header.record_id; + p_evt->write.is_record_updated = (p_op->write.step == FDS_OP_WRITE_DONE); + break; + + case FDS_OP_DEL_RECORD: + p_evt->id = FDS_EVT_DEL_RECORD; + p_evt->del.file_id = p_op->del.file_id; + p_evt->del.record_key = p_op->del.record_key; + p_evt->del.record_id = p_op->del.record_to_delete; + break; + + case FDS_OP_DEL_FILE: + p_evt->id = FDS_EVT_DEL_FILE; + p_evt->del.file_id = p_op->del.file_id; + p_evt->del.record_key = FDS_RECORD_KEY_DIRTY; + p_evt->del.record_id = 0; + break; + + case FDS_OP_GC: + p_evt->id = FDS_EVT_GC; + break; + + default: + // Should not happen. + break; + } +} + + +static bool header_has_next(fds_header_t const * p_hdr, uint32_t const * p_page_end) +{ + uint32_t const * const p_hdr32 = (uint32_t*)p_hdr; + return ( ( p_hdr32 < p_page_end) + && (*p_hdr32 != FDS_ERASED_WORD)); // Check last to be on the safe side (dereference) +} + + +// Jump to the next header. +static fds_header_t const * header_jump(fds_header_t const * const p_hdr) +{ + return (fds_header_t*)((uint32_t*)p_hdr + FDS_HEADER_SIZE + p_hdr->length_words); +} + + +static fds_header_status_t header_check(fds_header_t const * p_hdr, uint32_t const * p_page_end) +{ + if (((uint32_t*)header_jump(p_hdr) > p_page_end)) + { + // The length field would jump across the page boundary. + // FDS won't allow writing such a header, therefore it has been corrupted. + return FDS_HEADER_CORRUPT; + } + + if ( (p_hdr->file_id == FDS_FILE_ID_INVALID) + || (p_hdr->record_key == FDS_RECORD_KEY_DIRTY)) + { + return FDS_HEADER_DIRTY; + } + + return FDS_HEADER_VALID; +} + + +static bool address_is_valid(uint32_t const * const p_addr) +{ + return ((p_addr != NULL) && + (p_addr >= (uint32_t*)m_fs.start_addr) && + (p_addr <= (uint32_t*)m_fs.end_addr) && + (is_word_aligned(p_addr))); +} + + +// Reads a page tag, and determines if the page is used to store data or as swap. +static fds_page_type_t page_identify(uint32_t const * const p_page_addr) +{ + if ( (p_page_addr == NULL) // Should never happen. + || (p_page_addr[FDS_PAGE_TAG_WORD_0] != FDS_PAGE_TAG_MAGIC)) + { + return FDS_PAGE_UNDEFINED; + } + + switch (p_page_addr[FDS_PAGE_TAG_WORD_1]) + { + case FDS_PAGE_TAG_SWAP: + return FDS_PAGE_SWAP; + + case FDS_PAGE_TAG_DATA: + return FDS_PAGE_DATA; + + default: + return FDS_PAGE_UNDEFINED; + } +} + + +static bool page_is_erased(uint32_t const * const p_page_addr) +{ + for (uint32_t i = 0; i < FDS_PAGE_SIZE; i++) + { + if (*(p_page_addr + i) != FDS_ERASED_WORD) + { + return false; + } + } + + return true; +} + + +// NOTE: Must be called from within a critical section. +static bool page_has_space(uint16_t page, uint16_t length_words) +{ + length_words += m_pages[page].write_offset; + length_words += m_pages[page].words_reserved; + return (length_words < FDS_PAGE_SIZE); +} + + +// Given a pointer to a record, find the index of the page on which it is stored. +// Returns FDS_SUCCESS if the page is found, FDS_ERR_NOT_FOUND otherwise. +static ret_code_t page_from_record(uint16_t * const p_page, uint32_t const * const p_rec) +{ + ret_code_t ret = FDS_ERR_NOT_FOUND; + + CRITICAL_SECTION_ENTER(); + for (uint16_t i = 0; i < FDS_DATA_PAGES; i++) + { + if ((p_rec > m_pages[i].p_addr) && + (p_rec < m_pages[i].p_addr + FDS_PAGE_SIZE)) + { + ret = FDS_SUCCESS; + *p_page = i; + break; + } + } + CRITICAL_SECTION_EXIT(); + + return ret; +} + + +// Scan a page to determine how many words have been written to it. +// This information is used to set the page write offset during initialization. +// Additionally, this function updates the latest record ID as it proceeds. +// If an invalid record header is found, the can_gc argument is set to true. +static void page_scan(uint32_t const * p_addr, + uint16_t * const words_written, + bool * const can_gc) +{ + uint32_t const * const p_page_end = p_addr + FDS_PAGE_SIZE; + + p_addr += FDS_PAGE_TAG_SIZE; + *words_written = FDS_PAGE_TAG_SIZE; + + fds_header_t const * p_header = (fds_header_t*)p_addr; + + while (header_has_next(p_header, p_page_end)) + { + fds_header_status_t hdr = header_check(p_header, p_page_end); + + if (hdr == FDS_HEADER_VALID) + { + // Update the latest (largest) record ID. + if (p_header->record_id > m_latest_rec_id) + { + m_latest_rec_id = p_header->record_id; + } + } + else + { + if (can_gc != NULL) + { + *can_gc = true; + } + + if (hdr == FDS_HEADER_CORRUPT) + { + // It could happen that a record has a corrupt header which would set a + // wrong offset for this page. In such cases, update this value to its maximum, + // to ensure that no new records will be written to this page and to enable + // correct statistics reporting by fds_stat(). + *words_written = FDS_PAGE_SIZE; + + // We can't continue to scan this page. + return; + } + } + + *words_written += (FDS_HEADER_SIZE + p_header->length_words); + p_header = header_jump(p_header); + } +} + + +static void page_offsets_update(fds_page_t * const p_page, fds_op_t const * p_op) +{ + // If the first part of the header has been written correctly, update the offset as normal. + // Even if the record has not been written completely, fds is still able to continue normal + // operation. Incomplete records will be deleted the next time garbage collection is run. + // If we failed at the very beginning of the write operation, restore the offset + // to the previous value so that no holes will be left in the flash. + if (p_op->write.step > FDS_OP_WRITE_RECORD_ID) + { + p_page->write_offset += (FDS_HEADER_SIZE + p_op->write.header.length_words); + } + + p_page->words_reserved -= (FDS_HEADER_SIZE + p_op->write.header.length_words); +} + + +// Tags a page as swap, i.e., reserved for GC. +static ret_code_t page_tag_write_swap(void) +{ + // The tag needs to be statically allocated since it is not buffered by fstorage. + static uint32_t const page_tag_swap[] = {FDS_PAGE_TAG_MAGIC, FDS_PAGE_TAG_SWAP}; + return nrf_fstorage_write(&m_fs, (uint32_t)m_swap_page.p_addr, page_tag_swap, FDS_PAGE_TAG_SIZE * sizeof(uint32_t), NULL); +} + + +// Tags a page as data, i.e, ready for storage. +static ret_code_t page_tag_write_data(uint32_t const * const p_page_addr) +{ + // The tag needs to be statically allocated since it is not buffered by fstorage. + static uint32_t const page_tag_data[] = {FDS_PAGE_TAG_MAGIC, FDS_PAGE_TAG_DATA}; + return nrf_fstorage_write(&m_fs, (uint32_t)p_page_addr, page_tag_data, FDS_PAGE_TAG_SIZE * sizeof(uint32_t), NULL); +} + + +// Reserve space on a page. +// NOTE: this function takes into the account the space required for the record header. +static ret_code_t write_space_reserve(uint16_t length_words, uint16_t * p_page) +{ + bool space_reserved = false; + uint16_t const total_len_words = length_words + FDS_HEADER_SIZE; + + if (total_len_words >= FDS_PAGE_SIZE - FDS_PAGE_TAG_SIZE) + { + return FDS_ERR_RECORD_TOO_LARGE; + } + + CRITICAL_SECTION_ENTER(); + for (uint16_t page = 0; page < FDS_DATA_PAGES; page++) + { + if ((m_pages[page].page_type == FDS_PAGE_DATA) && + (page_has_space(page, total_len_words))) + { + space_reserved = true; + *p_page = page; + + m_pages[page].words_reserved += total_len_words; + break; + } + } + CRITICAL_SECTION_EXIT(); + + return (space_reserved) ? FDS_SUCCESS : FDS_ERR_NO_SPACE_IN_FLASH; +} + + +// Undo a write_space_reserve() call. +// NOTE: Must be called within a critical section. +static void write_space_free(uint16_t length_words, uint16_t page) +{ + m_pages[page].words_reserved -= (length_words + FDS_HEADER_SIZE); +} + + +static uint32_t record_id_new(void) +{ + return nrf_atomic_u32_add(&m_latest_rec_id, 1); +} + + +// Given a page and a record, find the next valid record on that page. +// If p_record is NULL, search from the beginning of the page, +// otherwise, resume searching from p_record. +// Return true if a record is found, false otherwise. +// If no record is found, p_record is unchanged. +static bool record_find_next(uint16_t page, uint32_t const ** p_record) +{ + uint32_t const * p_page_end = (m_pages[page].p_addr + FDS_PAGE_SIZE); + + // If this is the first call on this page, start searching from its beginning. + // Otherwise, jump to the next record. + fds_header_t const * p_header = (fds_header_t*)(*p_record); + + if (p_header != NULL) + { + p_header = header_jump(p_header); + } + else + { + p_header = (fds_header_t*)(m_pages[page].p_addr + FDS_PAGE_TAG_SIZE); + } + + // Read records from the page until: + // - a valid record is found or + // - the last record on a page is found + + while (header_has_next(p_header, p_page_end)) + { + switch (header_check(p_header, p_page_end)) + { + case FDS_HEADER_VALID: + *p_record = (uint32_t*)p_header; + return true; + + case FDS_HEADER_DIRTY: + p_header = header_jump(p_header); + break; + + case FDS_HEADER_CORRUPT: + // We can't reliably jump over this record. + // There is nothing more we can do on this page. + return false; + } + } + + // No more valid records on this page. + return false; +} + + +// Find a record given its descriptor and retrive the page in which the record is stored. +// NOTE: Do not pass NULL as an argument for p_page. +static bool record_find_by_desc(fds_record_desc_t * const p_desc, uint16_t * const p_page) +{ + // If the gc_run_count field in the descriptor matches our counter, then the record has + // not been moved. If the address is valid, and the record ID matches, there is no need + // to find the record again. Only lookup the page in which the record is stored. + + if ((address_is_valid(p_desc->p_record)) && + (p_desc->gc_run_count == m_gc.run_count) && + (p_desc->record_id == ((fds_header_t*)p_desc->p_record)->record_id)) + { + return (page_from_record(p_page, p_desc->p_record) == FDS_SUCCESS); + } + + // Otherwise, find the record in flash. + for (*p_page = 0; *p_page < FDS_DATA_PAGES; (*p_page)++) + { + // Set p_record to NULL to make record_find_next() search from the beginning of the page. + uint32_t const * p_record = NULL; + + while (record_find_next(*p_page, &p_record)) + { + fds_header_t const * const p_header = (fds_header_t*)p_record; + if (p_header->record_id == p_desc->record_id) + { + p_desc->p_record = p_record; + p_desc->gc_run_count = m_gc.run_count; + return true; + } + } + } + + return false; +} + + +// Search for a record and return its descriptor. +// If p_file_id is NULL, only the record key will be used for matching. +// If p_record_key is NULL, only the file ID will be used for matching. +// If both are NULL, it will iterate through all records. +static ret_code_t record_find(uint16_t const * p_file_id, + uint16_t const * p_record_key, + fds_record_desc_t * p_desc, + fds_find_token_t * p_token) +{ + if (!m_flags.initialized) + { + return FDS_ERR_NOT_INITIALIZED; + } + + if (p_desc == NULL || p_token == NULL) + { + return FDS_ERR_NULL_ARG; + } + + // Begin (or resume) searching for a record. + for (; p_token->page < FDS_DATA_PAGES; p_token->page++) + { + if (m_pages[p_token->page].page_type != FDS_PAGE_DATA) + { + // It might be that the page is FDS_PAGE_UNDEFINED. + // Skip this page. + continue; + } + + while (record_find_next(p_token->page, &p_token->p_addr)) + { + fds_header_t const * p_header = (fds_header_t*)p_token->p_addr; + + // A valid record was found, check its header for a match. + if ((p_file_id != NULL) && + (p_header->file_id != *p_file_id)) + { + continue; + } + + if ((p_record_key != NULL) && + (p_header->record_key != *p_record_key)) + { + continue; + } + + // Record found; update the descriptor. + p_desc->record_id = p_header->record_id; + p_desc->p_record = p_token->p_addr; + p_desc->gc_run_count = m_gc.run_count; + + return FDS_SUCCESS; + } + + // We have scanned an entire page. Set the address in the token to NULL + // so that it will be updated in the next iteration. + p_token->p_addr = NULL; + } + + return FDS_ERR_NOT_FOUND; +} + + +// Retrieve statistics about dirty records on a page. +static void records_stat(uint16_t page, + uint16_t * p_valid_records, + uint16_t * p_dirty_records, + uint16_t * p_freeable_words, + bool * p_corruption) +{ + fds_header_t const * p_header = (fds_header_t*)(m_pages[page].p_addr + FDS_PAGE_TAG_SIZE); + uint32_t const * const p_page_end = (m_pages[page].p_addr + FDS_PAGE_SIZE); + + while (header_has_next(p_header, p_page_end)) + { + switch (header_check(p_header, p_page_end)) + { + case FDS_HEADER_DIRTY: + *p_dirty_records += 1; + *p_freeable_words += FDS_HEADER_SIZE + p_header->length_words; + p_header = header_jump(p_header); + break; + + case FDS_HEADER_VALID: + *p_valid_records += 1; + p_header = header_jump(p_header); + break; + + case FDS_HEADER_CORRUPT: + { + *p_dirty_records += 1; + *p_freeable_words += (p_page_end - (uint32_t*)p_header); + *p_corruption = true; + // We can't continue on this page. + return; + } + + default: + break; + } + } +} + + +// Get a buffer on the queue of operations. +static fds_op_t * queue_buf_get(nrf_atfifo_item_put_t * p_iput_ctx) +{ + fds_op_t * const p_op = (fds_op_t*) nrf_atfifo_item_alloc(m_queue, p_iput_ctx); + + memset(p_op, 0x00, sizeof(fds_op_t)); + return p_op; +} + + +// Commit a buffer to the queue of operations. +static void queue_buf_store(nrf_atfifo_item_put_t * p_iput_ctx) +{ + (void) nrf_atfifo_item_put(m_queue, p_iput_ctx); +} + + +// Load the next operation from the queue. +static fds_op_t * queue_load(nrf_atfifo_item_get_t * p_iget_ctx) +{ + return (fds_op_t*) nrf_atfifo_item_get(m_queue, p_iget_ctx); +} + + +// Free the currently loaded operation. +static void queue_free(nrf_atfifo_item_get_t * p_iget_ctx) +{ + // Free the current queue element. + (void) nrf_atfifo_item_free(m_queue, p_iget_ctx); +} + + +static bool queue_has_next(void) +{ + // Decrement the number of queued operations. + ASSERT(m_queued_op_cnt != 0); + return nrf_atomic_u32_sub(&m_queued_op_cnt, 1); +} + + +// This function is called during initialization to setup the page structure (m_pages) and +// provide additional information regarding eventual further initialization steps. +static fds_init_opts_t pages_init(void) +{ + uint32_t ret = NO_PAGES; + uint16_t page = 0; + uint16_t total_pages_available = FDS_VIRTUAL_PAGES; + bool swap_set_but_not_found = false; + + for (uint16_t i = 0; i < FDS_VIRTUAL_PAGES; i++) + { + uint32_t const * const p_page_addr = (uint32_t*)m_fs.start_addr + (i * FDS_PAGE_SIZE); + fds_page_type_t const page_type = page_identify(p_page_addr); + + switch (page_type) + { + case FDS_PAGE_UNDEFINED: + { + if (page_is_erased(p_page_addr)) + { + if (m_swap_page.p_addr != NULL) + { + // If a swap page is already set, flag the page as erased (in m_pages) + // and try to tag it as data (in flash) later on during initialization. + m_pages[page].page_type = FDS_PAGE_ERASED; + m_pages[page].p_addr = p_page_addr; + m_pages[page].write_offset = FDS_PAGE_TAG_SIZE; + + // This is a candidate for a potential new swap page, in case the + // current swap is going to be promoted to complete a GC instance. + m_gc.cur_page = page; + page++; + } + else + { + // If there is no swap page yet, use this one. + m_swap_page.p_addr = p_page_addr; + m_swap_page.write_offset = FDS_PAGE_TAG_SIZE; + swap_set_but_not_found = true; + } + + ret |= PAGE_ERASED; + } + else + { + // The page contains non-FDS data. + // Do not initialize or use this page. + total_pages_available--; + m_pages[page].p_addr = p_page_addr; + m_pages[page].page_type = FDS_PAGE_UNDEFINED; + page++; + } + } break; + + case FDS_PAGE_DATA: + { + m_pages[page].page_type = FDS_PAGE_DATA; + m_pages[page].p_addr = p_page_addr; + + // Scan the page to compute its write offset and determine whether or not the page + // can be garbage collected. Additionally, update the latest kwown record ID. + page_scan(p_page_addr, &m_pages[page].write_offset, &m_pages[page].can_gc); + + ret |= PAGE_DATA; + page++; + } break; + + case FDS_PAGE_SWAP: + { + if (swap_set_but_not_found) + { + m_pages[page].page_type = FDS_PAGE_ERASED; + m_pages[page].p_addr = m_swap_page.p_addr; + m_pages[page].write_offset = FDS_PAGE_TAG_SIZE; + + page++; + } + + m_swap_page.p_addr = p_page_addr; + // If the swap is promoted, this offset should be kept, otherwise, + // it should be set to FDS_PAGE_TAG_SIZE. + page_scan(p_page_addr, &m_swap_page.write_offset, NULL); + + ret |= (m_swap_page.write_offset == FDS_PAGE_TAG_SIZE) ? + PAGE_SWAP_CLEAN : PAGE_SWAP_DIRTY; + } break; + + default: + // Shouldn't happen. + break; + } + } + + if (total_pages_available < 2) + { + ret &= NO_PAGES; + } + + return (fds_init_opts_t)ret; +} + + +// Write the first part of a record header (the key and length). +static ret_code_t record_header_write_begin(fds_op_t * const p_op, uint32_t * const p_addr) +{ + ret_code_t ret; + + // Write the record ID next. + p_op->write.step = FDS_OP_WRITE_RECORD_ID; + + ret = nrf_fstorage_write(&m_fs, (uint32_t)(p_addr + FDS_OFFSET_TL), + &p_op->write.header.record_key, FDS_HEADER_SIZE_TL * sizeof(uint32_t), NULL); + + return (ret == NRF_SUCCESS) ? FDS_SUCCESS : FDS_ERR_BUSY; +} + + +static ret_code_t record_header_write_id(fds_op_t * const p_op, uint32_t * const p_addr) +{ + ret_code_t ret; + + // If this record has no data, write the last part of the header directly. + // Otherwise, write the record data next. + p_op->write.step = (p_op->write.p_data != NULL) ? + FDS_OP_WRITE_DATA : FDS_OP_WRITE_HEADER_FINALIZE; + + ret = nrf_fstorage_write(&m_fs, (uint32_t)(p_addr + FDS_OFFSET_ID), + &p_op->write.header.record_id, FDS_HEADER_SIZE_ID * sizeof(uint32_t), NULL); + + return (ret == NRF_SUCCESS) ? FDS_SUCCESS : FDS_ERR_BUSY; +} + + +static ret_code_t record_header_write_finalize(fds_op_t * const p_op, uint32_t * const p_addr) +{ + ret_code_t ret; + + // If this is a simple write operation, then this is the last step. + // If this is an update instead, delete the old record next. + p_op->write.step = (p_op->op_code == FDS_OP_UPDATE) ? + FDS_OP_WRITE_FLAG_DIRTY : FDS_OP_WRITE_DONE; + + ret = nrf_fstorage_write(&m_fs, (uint32_t)(p_addr + FDS_OFFSET_IC), + &p_op->write.header.file_id, FDS_HEADER_SIZE_IC * sizeof(uint32_t), NULL); + + return (ret == NRF_SUCCESS) ? FDS_SUCCESS : FDS_ERR_BUSY; +} + + +static ret_code_t record_header_flag_dirty(uint32_t * const p_record, uint16_t page_to_gc) +{ + // Used to flag a record as dirty, i.e. ready for garbage collection. + // Must be statically allocated since it will be written to flash. + __ALIGN(4) static uint32_t const dirty_header = {0xFFFF0000}; + + // Flag the record as dirty. + ret_code_t ret; + + ret = nrf_fstorage_write(&m_fs, (uint32_t)p_record, + &dirty_header, FDS_HEADER_SIZE_TL * sizeof(uint32_t), NULL); + + if (ret != NRF_SUCCESS) + { + return FDS_ERR_BUSY; + } + + m_pages[page_to_gc].can_gc = true; + + return FDS_SUCCESS; +} + + +static ret_code_t record_find_and_delete(fds_op_t * const p_op) +{ + ret_code_t ret; + uint16_t page; + fds_record_desc_t desc = {0}; + + desc.record_id = p_op->del.record_to_delete; + + if (record_find_by_desc(&desc, &page)) + { + fds_header_t const * const p_header = (fds_header_t const *)desc.p_record; + + // Copy the record key and file ID, so that they can be returned in the event. + // In case this function is run as part of an update, there is no need to copy + // the file ID and record key since they are present in the header stored + // in the queue element. + + p_op->del.file_id = p_header->file_id; + p_op->del.record_key = p_header->record_key; + + // Flag the record as dirty. + ret = record_header_flag_dirty((uint32_t*)desc.p_record, page); + } + else + { + // The record never existed, or it has already been deleted. + ret = FDS_ERR_NOT_FOUND; + } + + return ret; +} + + +// Finds a record within a file and flags it as dirty. +static ret_code_t file_find_and_delete(fds_op_t * const p_op) +{ + ret_code_t ret; + fds_record_desc_t desc; + + // This token must persist across calls. + static fds_find_token_t tok = {0}; + + // Pass NULL to ignore the record key. + ret = record_find(&p_op->del.file_id, NULL, &desc, &tok); + + if (ret == FDS_SUCCESS) + { + // A record was found: flag it as dirty. + ret = record_header_flag_dirty((uint32_t*)desc.p_record, tok.page); + } + else // FDS_ERR_NOT_FOUND + { + // No more records were found. Zero the token, so that it can be reused. + memset(&tok, 0x00, sizeof(fds_find_token_t)); + } + + return ret; +} + + +// Writes record data to flash. +static ret_code_t record_write_data(fds_op_t * const p_op, uint32_t * const p_addr) +{ + ret_code_t ret; + + p_op->write.step = FDS_OP_WRITE_HEADER_FINALIZE; + + ret = nrf_fstorage_write(&m_fs, (uint32_t)(p_addr + FDS_OFFSET_DATA), + p_op->write.p_data, p_op->write.header.length_words * sizeof(uint32_t), NULL); + + return (ret == NRF_SUCCESS) ? FDS_SUCCESS : FDS_ERR_BUSY; +} + + +#if (FDS_CRC_CHECK_ON_READ) +static bool crc_verify_success(uint16_t crc, uint16_t len_words, uint32_t const * const p_data) +{ + uint16_t computed_crc; + + // The CRC is computed on the entire record, except the CRC field itself. + // The record header is 12 bytes, out of these we have to skip bytes 6 to 8 where the + // CRC itself is stored. Then we compute the CRC for the rest of the record, from byte 8 of + // the header (where the record ID begins) to the end of the record data. + computed_crc = crc16_compute((uint8_t const *)p_data, 6, NULL); + computed_crc = crc16_compute((uint8_t const *)p_data + 8, + (FDS_HEADER_SIZE_ID + len_words) * sizeof(uint32_t), + &computed_crc); + + return (computed_crc == crc); +} +#endif + + +static void gc_init(void) +{ + m_gc.run_count++; + m_gc.cur_page = 0; + m_gc.resume = false; + + // Setup which pages to GC. Defer checking for open records and the can_gc flag, + // as other operations might change those while GC is running. + for (uint16_t i = 0; i < FDS_DATA_PAGES; i++) + { + m_gc.do_gc_page[i] = (m_pages[i].page_type == FDS_PAGE_DATA); + } +} + + +// Obtain the next page to be garbage collected. +// Returns true if there are pages left to garbage collect, returns false otherwise. +static bool gc_page_next(uint16_t * const p_next_page) +{ + bool ret = false; + + for (uint16_t i = 0; i < FDS_DATA_PAGES; i++) + { + if (m_gc.do_gc_page[i]) + { + // Do not attempt to GC this page again. + m_gc.do_gc_page[i] = false; + + // Only GC pages with no open records and with some records which have been deleted. + if ((m_pages[i].records_open == 0) && (m_pages[i].can_gc == true)) + { + *p_next_page = i; + ret = true; + break; + } + } + } + + return ret; +} + + +static ret_code_t gc_swap_erase(void) +{ + m_gc.state = GC_DISCARD_SWAP; + m_swap_page.write_offset = FDS_PAGE_TAG_SIZE; + + return nrf_fstorage_erase(&m_fs, (uint32_t)m_swap_page.p_addr, FDS_PHY_PAGES_IN_VPAGE, NULL); +} + + +// Erase the page being garbage collected, or erase the swap in case there are any open +// records on the page being garbage collected. +static ret_code_t gc_page_erase(void) +{ + uint32_t ret; + uint16_t const gc = m_gc.cur_page; + + if (m_pages[gc].records_open == 0) + { + m_gc.state = GC_ERASE_PAGE; + + ret = nrf_fstorage_erase(&m_fs, (uint32_t)m_pages[gc].p_addr, FDS_PHY_PAGES_IN_VPAGE, NULL); + } + else + { + // If there are open records, stop garbage collection on this page. + // Discard the swap and try to garbage collect another page. + ret = gc_swap_erase(); + } + + return ret; +} + + +// Copy the current record to swap. +static ret_code_t gc_record_copy(void) +{ + fds_header_t const * const p_header = (fds_header_t*)m_gc.p_record_src; + uint32_t const * const p_dest = m_swap_page.p_addr + m_swap_page.write_offset; + uint16_t const record_len = FDS_HEADER_SIZE + p_header->length_words; + + m_gc.state = GC_COPY_RECORD; + + // Copy the record to swap; it is guaranteed to fit in the destination page, + // so there is no need to check its size. This will either succeed or timeout. + return nrf_fstorage_write(&m_fs, (uint32_t)p_dest, m_gc.p_record_src, + record_len * sizeof(uint32_t), + NULL); +} + + +static ret_code_t gc_record_find_next(void) +{ + ret_code_t ret; + + // Find the next valid record to copy. + if (record_find_next(m_gc.cur_page, &m_gc.p_record_src)) + { + ret = gc_record_copy(); + } + else + { + // No more records left to copy on this page; swap pages. + ret = gc_page_erase(); + } + + return ret; +} + + +// Promote the swap by tagging it as a data page. +static ret_code_t gc_swap_promote(void) +{ + m_gc.state = GC_PROMOTE_SWAP; + return page_tag_write_data(m_pages[m_gc.cur_page].p_addr); +} + + +// Tag the page just garbage collected as swap. +static ret_code_t gc_tag_new_swap(void) +{ + m_gc.state = GC_TAG_NEW_SWAP; + m_gc.p_record_src = NULL; + return page_tag_write_swap(); +} + + +static ret_code_t gc_next_page(void) +{ + if (!gc_page_next(&m_gc.cur_page)) + { + // No pages left to GC; GC has terminated. Reset the state. + m_gc.state = GC_BEGIN; + m_gc.cur_page = 0; + m_gc.p_record_src = NULL; + + return FDS_OP_COMPLETED; + } + + return gc_record_find_next(); +} + + +// Update the swap page offeset after a record has been successfully copied to it. +static void gc_update_swap_offset(void) +{ + fds_header_t const * const p_header = (fds_header_t*)m_gc.p_record_src; + uint16_t const record_len = FDS_HEADER_SIZE + p_header->length_words; + + m_swap_page.write_offset += record_len; +} + + +static void gc_swap_pages(void) +{ + // The page being garbage collected will be the new swap page, + // and the current swap will be used as a data page (promoted). + uint32_t const * const p_addr = m_swap_page.p_addr; + + m_swap_page.p_addr = m_pages[m_gc.cur_page].p_addr; + m_pages[m_gc.cur_page].p_addr = p_addr; + + // Keep the offset for this page, but reset it for the swap. + m_pages[m_gc.cur_page].write_offset = m_swap_page.write_offset; + m_swap_page.write_offset = FDS_PAGE_TAG_SIZE; +} + + +static void gc_state_advance(void) +{ + switch (m_gc.state) + { + case GC_BEGIN: + gc_init(); + m_gc.state = GC_NEXT_PAGE; + break; + + // A record was successfully copied. + case GC_COPY_RECORD: + gc_update_swap_offset(); + m_gc.state = GC_FIND_NEXT_RECORD; + break; + + // A page was successfully erased. Prepare to promote the swap. + case GC_ERASE_PAGE: + gc_swap_pages(); + m_gc.state = GC_PROMOTE_SWAP; + break; + + // Swap was discarded because the page being GC'ed had open records. + case GC_DISCARD_SWAP: + // Swap was successfully promoted. + case GC_PROMOTE_SWAP: + // Prepare to tag the page just GC'ed as swap. + m_gc.state = GC_TAG_NEW_SWAP; + break; + + case GC_TAG_NEW_SWAP: + m_gc.state = GC_NEXT_PAGE; + break; + + default: + // Should not happen. + break; + } +} + + +// Initialize the filesystem. +static ret_code_t init_execute(uint32_t prev_ret, fds_op_t * const p_op) +{ + ret_code_t ret = FDS_ERR_INTERNAL; + + if (prev_ret != NRF_SUCCESS) + { + // A previous operation has timed out. + m_flags.initializing = false; + return FDS_ERR_OPERATION_TIMEOUT; + } + + switch (p_op->init.step) + { + case FDS_OP_INIT_TAG_SWAP: + { + // The page write offset was determined previously by pages_init(). + p_op->init.step = FDS_OP_INIT_TAG_DATA; + ret = page_tag_write_swap(); + } break; + + case FDS_OP_INIT_TAG_DATA: + { + // Tag remaining erased pages as data. + bool write_reqd = false; + for (uint16_t i = 0; i < FDS_DATA_PAGES; i++) + { + if (m_pages[i].page_type == FDS_PAGE_ERASED) + { + m_pages[i].page_type = FDS_PAGE_DATA; + write_reqd = true; + ret = page_tag_write_data(m_pages[i].p_addr); + break; + } + } + if (!write_reqd) + { + m_flags.initialized = true; + m_flags.initializing = false; + return FDS_OP_COMPLETED; + } + } break; + + case FDS_OP_INIT_ERASE_SWAP: + { + // If the swap is going to be discarded then reset its write_offset. + p_op->init.step = FDS_OP_INIT_TAG_SWAP; + m_swap_page.write_offset = FDS_PAGE_TAG_SIZE; + + ret = nrf_fstorage_erase(&m_fs, (uint32_t)m_swap_page.p_addr, FDS_PHY_PAGES_IN_VPAGE, NULL); + } break; + + case FDS_OP_INIT_PROMOTE_SWAP: + { + p_op->init.step = FDS_OP_INIT_TAG_SWAP; + + // When promoting the swap, keep the write_offset set by pages_init(). + ret = page_tag_write_data(m_swap_page.p_addr); + + uint16_t const gc = m_gc.cur_page; + uint32_t const * const p_old_swap = m_swap_page.p_addr; + + // Execute the swap. + m_swap_page.p_addr = m_pages[gc].p_addr; + m_pages[gc].p_addr = p_old_swap; + + // Copy the offset from the swap to the new page. + m_pages[gc].write_offset = m_swap_page.write_offset; + m_swap_page.write_offset = FDS_PAGE_TAG_SIZE; + + m_pages[gc].page_type = FDS_PAGE_DATA; + } break; + + default: + // Should not happen. + break; + } + + if (ret != FDS_SUCCESS) + { + // fstorage queue was full. + m_flags.initializing = false; + return FDS_ERR_BUSY; + } + + return FDS_OP_EXECUTING; +} + + +// Executes write and update operations. +static ret_code_t write_execute(uint32_t prev_ret, fds_op_t * const p_op) +{ + ret_code_t ret; + uint32_t * p_write_addr; + fds_page_t * const p_page = &m_pages[p_op->write.page]; + + // This must persist across calls. + static fds_record_desc_t desc = {0}; + // When a record is updated, this variable will hold the page where the old + // copy was stored. This will be used to set the can_gc flag when the header is + // invalidated (FDS_OP_WRITE_FLAG_DIRTY). + static uint16_t page; + + if (prev_ret != NRF_SUCCESS) + { + // The previous operation has timed out, update offsets. + page_offsets_update(p_page, p_op); + return FDS_ERR_OPERATION_TIMEOUT; + } + + // Compute the address where to write data. + p_write_addr = (uint32_t*)(p_page->p_addr + p_page->write_offset); + + // Execute the current step of the operation, and set one to be executed next. + switch (p_op->write.step) + { + case FDS_OP_WRITE_FIND_RECORD: + { + // The first step of updating a record constists of locating the copy to be deleted. + // If the old copy couldn't be found for any reason then the update should fail. + // This prevents duplicates when queuing multiple updates of the same record. + desc.p_record = NULL; + desc.record_id = p_op->write.record_to_delete; + + if (!record_find_by_desc(&desc, &page)) + { + return FDS_ERR_NOT_FOUND; + } + // Setting the step is redundant since we are falling through. + } + // Fallthrough to FDS_OP_WRITE_HEADER_BEGIN. + + case FDS_OP_WRITE_HEADER_BEGIN: + ret = record_header_write_begin(p_op, p_write_addr); + break; + + case FDS_OP_WRITE_RECORD_ID: + ret = record_header_write_id(p_op, p_write_addr); + break; + + case FDS_OP_WRITE_DATA: + ret = record_write_data(p_op, p_write_addr); + break; + + case FDS_OP_WRITE_HEADER_FINALIZE: + ret = record_header_write_finalize(p_op, p_write_addr); + break; + + case FDS_OP_WRITE_FLAG_DIRTY: + p_op->write.step = FDS_OP_WRITE_DONE; + ret = record_header_flag_dirty((uint32_t*)desc.p_record, page); + break; + + case FDS_OP_WRITE_DONE: + ret = FDS_OP_COMPLETED; + +#if (FDS_CRC_CHECK_ON_WRITE) + if (!crc_verify_success(p_op->write.header.crc16, + p_op->write.header.length_words, + p_write_addr)) + { + ret = FDS_ERR_CRC_CHECK_FAILED; + } +#endif + break; + + default: + ret = FDS_ERR_INTERNAL; + break; + } + + // An operation has either completed or failed. It may have failed because fstorage + // ran out of memory, or because the user tried to delete a record which did not exist. + if (ret != FDS_OP_EXECUTING) + { + // There won't be another callback for this operation, so update the page offset now. + page_offsets_update(p_page, p_op); + } + + return ret; +} + + +static ret_code_t delete_execute(uint32_t prev_ret, fds_op_t * const p_op) +{ + ret_code_t ret; + + if (prev_ret != NRF_SUCCESS) + { + return FDS_ERR_OPERATION_TIMEOUT; + } + + switch (p_op->del.step) + { + case FDS_OP_DEL_RECORD_FLAG_DIRTY: + p_op->del.step = FDS_OP_DEL_DONE; + ret = record_find_and_delete(p_op); + break; + + case FDS_OP_DEL_FILE_FLAG_DIRTY: + ret = file_find_and_delete(p_op); + if (ret == FDS_ERR_NOT_FOUND) + { + // No more records could be found. + // There won't be another callback for this operation, so return now. + ret = FDS_OP_COMPLETED; + } + break; + + case FDS_OP_DEL_DONE: + ret = FDS_OP_COMPLETED; + break; + + default: + ret = FDS_ERR_INTERNAL; + break; + } + + return ret; +} + + +static ret_code_t gc_execute(uint32_t prev_ret) +{ + ret_code_t ret; + + if (prev_ret != NRF_SUCCESS) + { + return FDS_ERR_OPERATION_TIMEOUT; + } + + if (m_gc.resume) + { + m_gc.resume = false; + } + else + { + gc_state_advance(); + } + + switch (m_gc.state) + { + case GC_NEXT_PAGE: + ret = gc_next_page(); + break; + + case GC_FIND_NEXT_RECORD: + ret = gc_record_find_next(); + break; + + case GC_COPY_RECORD: + ret = gc_record_copy(); + break; + + case GC_ERASE_PAGE: + ret = gc_page_erase(); + break; + + case GC_PROMOTE_SWAP: + ret = gc_swap_promote(); + break; + + case GC_TAG_NEW_SWAP: + ret = gc_tag_new_swap(); + break; + + default: + // Should not happen. + ret = FDS_ERR_INTERNAL; + break; + } + + // Either FDS_OP_EXECUTING, FDS_OP_COMPLETED, FDS_ERR_BUSY or FDS_ERR_INTERNAL. + return ret; +} + + +static void queue_process(ret_code_t result) +{ + static fds_op_t * m_p_cur_op; // Current fds operation. + static nrf_atfifo_item_get_t m_iget_ctx; // Queue context for the current operation. + + while (true) + { + if (m_p_cur_op == NULL) + { + // Load the next from the queue if no operation is being executed. + m_p_cur_op = queue_load(&m_iget_ctx); + } + + /* We can reach here in three ways: + * from queue_start(): something was just queued + * from the fstorage event handler: an operation is being executed + * looping: we only loop if there are operations still in the queue + * + * In all these three cases, m_p_cur_op != NULL. + */ + ASSERT(m_p_cur_op != NULL); + + switch (m_p_cur_op->op_code) + { + case FDS_OP_INIT: + result = init_execute(result, m_p_cur_op); + break; + + case FDS_OP_WRITE: + case FDS_OP_UPDATE: + result = write_execute(result, m_p_cur_op); + break; + + case FDS_OP_DEL_RECORD: + case FDS_OP_DEL_FILE: + result = delete_execute(result, m_p_cur_op); + break; + + case FDS_OP_GC: + result = gc_execute(result); + break; + + default: + result = FDS_ERR_INTERNAL; + break; + } + + if (result == FDS_OP_EXECUTING) + { + // The operation has not completed yet. Wait for the next system event. + break; + } + + // The operation has completed (either successfully or with an error). + // - send an event to the user + // - free the operation buffer + // - execute any other queued operations + + fds_evt_t evt = + { + // The operation might have failed for one of the following reasons: + // FDS_ERR_BUSY - flash subsystem can't accept the operation + // FDS_ERR_OPERATION_TIMEOUT - flash subsystem timed out + // FDS_ERR_CRC_CHECK_FAILED - a CRC check failed + // FDS_ERR_NOT_FOUND - no record found (delete/update) + .result = (result == FDS_OP_COMPLETED) ? FDS_SUCCESS : result, + }; + + event_prepare(m_p_cur_op, &evt); + event_send(&evt); + + // Zero the pointer to the current operation so that this function + // will fetch a new one from the queue next time it is run. + m_p_cur_op = NULL; + + // The result of the operation must be reset upon re-entering the loop to ensure + // the next operation won't be affected by eventual errors in previous operations. + result = NRF_SUCCESS; + + // Free the queue element used by the current operation. + queue_free(&m_iget_ctx); + + if (!queue_has_next()) + { + // No more elements left. Nothing to do. + break; + } + } +} + + +static void queue_start(void) +{ + if (!nrf_atomic_u32_fetch_add(&m_queued_op_cnt, 1)) + { + queue_process(NRF_SUCCESS); + } +} + + +static void fs_event_handler(nrf_fstorage_evt_t * p_evt) +{ + queue_process(p_evt->result); +} + + +// Enqueues write and update operations. +static ret_code_t write_enqueue(fds_record_desc_t * const p_desc, + fds_record_t const * const p_record, + fds_reserve_token_t const * const p_tok, + fds_op_code_t op_code) +{ + ret_code_t ret; + uint16_t page; + uint16_t crc = 0; + uint16_t length_words = 0; + fds_op_t * p_op; + nrf_atfifo_item_put_t iput_ctx; + + if (!m_flags.initialized) + { + return FDS_ERR_NOT_INITIALIZED; + } + + if (p_record == NULL) + { + return FDS_ERR_NULL_ARG; + } + + if ((p_record->file_id == FDS_FILE_ID_INVALID) || + (p_record->key == FDS_RECORD_KEY_DIRTY)) + { + return FDS_ERR_INVALID_ARG; + } + + if (!is_word_aligned(p_record->data.p_data)) + { + return FDS_ERR_UNALIGNED_ADDR; + } + + // No space was previously reserved in flash for this operation. + if (p_tok == NULL) + { + // Find a page where to write data. + length_words = p_record->data.length_words; + ret = write_space_reserve(length_words, &page); + + if (ret != FDS_SUCCESS) + { + // There is either not enough space in flash (FDS_ERR_NO_SPACE_IN_FLASH) or + // the record exceeds the size of virtual page (FDS_ERR_RECORD_TOO_LARGE). + return ret; + } + } + else + { + page = p_tok->page; + length_words = p_tok->length_words; + } + + // Get a buffer on the queue of operations. + p_op = queue_buf_get(&iput_ctx); + if (p_op == NULL) + { + CRITICAL_SECTION_ENTER(); + write_space_free(length_words, page); + CRITICAL_SECTION_EXIT(); + return FDS_ERR_NO_SPACE_IN_QUEUES; + } + + // Initialize the operation. + p_op->op_code = op_code; + p_op->write.step = FDS_OP_WRITE_HEADER_BEGIN; + p_op->write.page = page; + p_op->write.p_data = p_record->data.p_data; + p_op->write.header.record_id = record_id_new(); + p_op->write.header.file_id = p_record->file_id; + p_op->write.header.record_key = p_record->key; + p_op->write.header.length_words = length_words; + + if (op_code == FDS_OP_UPDATE) + { + p_op->write.step = FDS_OP_WRITE_FIND_RECORD; + // Save the record ID of the record to be updated. + p_op->write.record_to_delete = p_desc->record_id; + } + +#if (FDS_CRC_CHECK_ON_READ) + // First, compute the CRC for the first 6 bytes of the header which contain the + // record key, length and file ID, then, compute the CRC of the record ID (4 bytes). + crc = crc16_compute((uint8_t*)&p_op->write.header, 6, NULL); + crc = crc16_compute((uint8_t*)&p_op->write.header.record_id, 4, &crc); + + // Compute the CRC for the record data. + crc = crc16_compute((uint8_t*)p_record->data.p_data, + p_record->data.length_words * sizeof(uint32_t), &crc); +#endif + + p_op->write.header.crc16 = crc; + + queue_buf_store(&iput_ctx); + + // Initialize the record descriptor, if provided. + if (p_desc != NULL) + { + p_desc->p_record = NULL; + // Don't invoke record_id_new() again ! + p_desc->record_id = p_op->write.header.record_id; + p_desc->record_is_open = false; + p_desc->gc_run_count = m_gc.run_count; + } + + // Start processing the queue, if necessary. + queue_start(); + + return FDS_SUCCESS; +} + + +ret_code_t fds_register(fds_cb_t cb) +{ + ret_code_t ret; + + if (m_users == FDS_MAX_USERS) + { + ret = FDS_ERR_USER_LIMIT_REACHED; + } + else + { + m_cb_table[m_users] = cb; + (void) nrf_atomic_u32_add(&m_users, 1); + + ret = FDS_SUCCESS; + } + + return ret; +} + + +static uint32_t flash_end_addr(void) +{ + uint32_t const bootloader_addr = NRF_UICR->NRFFW[0]; + uint32_t const page_sz = NRF_FICR->CODEPAGESIZE; +#ifndef NRF52810_XXAA + uint32_t const code_sz = NRF_FICR->CODESIZE; +#else + // Number of flash pages, necessary to emulate the NRF52810 on NRF52832. + uint32_t const code_sz = 48; +#endif + + return (bootloader_addr != 0xFFFFFFFF) ? bootloader_addr : (code_sz * page_sz); +} + + +static void flash_bounds_set(void) +{ + uint32_t flash_size = (FDS_PHY_PAGES * FDS_PHY_PAGE_SIZE * sizeof(uint32_t)); + m_fs.end_addr = flash_end_addr(); + m_fs.start_addr = m_fs.end_addr - flash_size; +} + + +static ret_code_t flash_subsystem_init(void) +{ + flash_bounds_set(); + + #if (FDS_BACKEND == NRF_FSTORAGE_SD) + return nrf_fstorage_init(&m_fs, &nrf_fstorage_sd, NULL); + #elif (FDS_BACKEND == NRF_FSTORAGE_NVMC) + return nrf_fstorage_init(&m_fs, &nrf_fstorage_nvmc, NULL); + #else + #error Invalid FDS_BACKEND. + #endif +} + + +static void queue_init(void) +{ + (void) NRF_ATFIFO_INIT(m_queue); +} + + +ret_code_t fds_init(void) +{ + ret_code_t ret; + fds_evt_t const evt_success = + { + .id = FDS_EVT_INIT, + .result = FDS_SUCCESS, + }; + + if (m_flags.initialized) + { + // No initialization is necessary. Notify the application immediately. + event_send(&evt_success); + return FDS_SUCCESS; + } + + if (nrf_atomic_flag_set_fetch(&m_flags.initializing)) + { + // If we were already initializing, return. + return FDS_SUCCESS; + } + + // Otherwise, the flag is set and we proceed to initialization. + + ret = flash_subsystem_init(); + if (ret != NRF_SUCCESS) + { + return ret; + } + + queue_init(); + + // Initialize the page structure (m_pages), and determine which + // initialization steps are required given the current state of the filesystem. + + fds_init_opts_t init_opts = pages_init(); + + switch (init_opts) + { + case NO_PAGES: + case NO_SWAP: + return FDS_ERR_NO_PAGES; + + case ALREADY_INSTALLED: + { + // No initialization is necessary. Notify the application immediately. + m_flags.initialized = true; + m_flags.initializing = false; + event_send(&evt_success); + return FDS_SUCCESS; + } + + default: + break; + } + + // A write operation is necessary to initialize the fileystem. + + nrf_atfifo_item_put_t iput_ctx; + + fds_op_t * p_op = queue_buf_get(&iput_ctx); + if (p_op == NULL) + { + return FDS_ERR_NO_SPACE_IN_QUEUES; + } + + p_op->op_code = FDS_OP_INIT; + + switch (init_opts) + { + case FRESH_INSTALL: + case TAG_SWAP: + p_op->init.step = FDS_OP_INIT_TAG_SWAP; + break; + + case PROMOTE_SWAP: + case PROMOTE_SWAP_INST: + p_op->init.step = FDS_OP_INIT_PROMOTE_SWAP; + break; + + case DISCARD_SWAP: + p_op->init.step = FDS_OP_INIT_ERASE_SWAP; + break; + + case TAG_DATA: + case TAG_DATA_INST: + p_op->init.step = FDS_OP_INIT_TAG_DATA; + break; + + default: + // Should not happen. + break; + } + + queue_buf_store(&iput_ctx); + queue_start(); + + return FDS_SUCCESS; +} + + +ret_code_t fds_record_open(fds_record_desc_t * const p_desc, + fds_flash_record_t * const p_flash_rec) +{ + uint16_t page; + + if ((p_desc == NULL) || (p_flash_rec == NULL)) + { + return FDS_ERR_NULL_ARG; + } + + // Find the record if necessary. + if (record_find_by_desc(p_desc, &page)) + { + fds_header_t const * const p_header = (fds_header_t*)p_desc->p_record; + +#if (FDS_CRC_CHECK_ON_READ) + if (!crc_verify_success(p_header->crc16, + p_header->length_words, + p_desc->p_record)) + { + return FDS_ERR_CRC_CHECK_FAILED; + } +#endif + + (void) nrf_atomic_u32_add(&m_pages[page].records_open, 1); + + // Initialize p_flash_rec. + p_flash_rec->p_header = p_header; + p_flash_rec->p_data = (p_desc->p_record + FDS_HEADER_SIZE); + + // Set the record as open in the descriptor. + p_desc->record_is_open = true; + + return FDS_SUCCESS; + } + + // The record could not be found. + // It either never existed or it has been deleted. + return FDS_ERR_NOT_FOUND; +} + + +ret_code_t fds_record_close(fds_record_desc_t * const p_desc) +{ + ret_code_t ret; + uint16_t page; + + if (p_desc == NULL) + { + return FDS_ERR_NULL_ARG; + } + + if (record_find_by_desc((fds_record_desc_t*)p_desc, &page)) + { + CRITICAL_SECTION_ENTER(); + if ((m_pages[page].records_open > 0) && (p_desc->record_is_open)) + { + + m_pages[page].records_open--; + p_desc->record_is_open = false; + + ret = FDS_SUCCESS; + } + else + { + ret = FDS_ERR_NO_OPEN_RECORDS; + } + CRITICAL_SECTION_EXIT(); + } + else + { + ret = FDS_ERR_NOT_FOUND; + } + + return ret; +} + + +ret_code_t fds_reserve(fds_reserve_token_t * const p_tok, uint16_t length_words) +{ + ret_code_t ret; + uint16_t page; + + if (!m_flags.initialized) + { + return FDS_ERR_NOT_INITIALIZED; + } + + if (p_tok == NULL) + { + return FDS_ERR_NULL_ARG; + } + + ret = write_space_reserve(length_words, &page); + + if (ret == FDS_SUCCESS) + { + p_tok->page = page; + p_tok->length_words = length_words; + } + + return ret; +} + + +ret_code_t fds_reserve_cancel(fds_reserve_token_t * const p_tok) +{ + ret_code_t ret; + + if (!m_flags.initialized) + { + return FDS_ERR_NOT_INITIALIZED; + } + + if (p_tok == NULL) + { + return FDS_ERR_NULL_ARG; + } + + if (p_tok->page > FDS_DATA_PAGES) + { + // The page does not exist. This shouldn't happen. + return FDS_ERR_INVALID_ARG; + } + + fds_page_t const * const p_page = &m_pages[p_tok->page]; + + CRITICAL_SECTION_ENTER(); + if ((FDS_HEADER_SIZE + p_tok->length_words) <= p_page->words_reserved) + { + // Free reserved space. + write_space_free(p_tok->length_words, p_tok->page); + + // Clean the token. + p_tok->page = 0; + p_tok->length_words = 0; + ret = FDS_SUCCESS; + } + else + { + // We are trying to cancel a reservation of more words than how many are + // currently reserved on the page. Clearly, this shouldn't happen. + ret = FDS_ERR_INVALID_ARG; + } + CRITICAL_SECTION_EXIT(); + + return ret; +} + + +ret_code_t fds_record_write(fds_record_desc_t * const p_desc, + fds_record_t const * const p_record) +{ + return write_enqueue(p_desc, p_record, NULL, FDS_OP_WRITE); +} + + +ret_code_t fds_record_write_reserved(fds_record_desc_t * const p_desc, + fds_record_t const * const p_record, + fds_reserve_token_t const * const p_tok) +{ + // A NULL token is not allowed when writing to a reserved space. + if (p_tok == NULL) + { + return FDS_ERR_NULL_ARG; + } + + return write_enqueue(p_desc, p_record, p_tok, FDS_OP_WRITE); +} + + +ret_code_t fds_record_update(fds_record_desc_t * const p_desc, + fds_record_t const * const p_record) +{ + // A NULL descriptor is not allowed when updating a record. + if (p_desc == NULL) + { + return FDS_ERR_NULL_ARG; + } + + return write_enqueue(p_desc, p_record, NULL, FDS_OP_UPDATE); +} + + +ret_code_t fds_record_delete(fds_record_desc_t * const p_desc) +{ + fds_op_t * p_op; + nrf_atfifo_item_put_t iput_ctx; + + if (!m_flags.initialized) + { + return FDS_ERR_NOT_INITIALIZED; + } + + if (p_desc == NULL) + { + return FDS_ERR_NULL_ARG; + } + + p_op = queue_buf_get(&iput_ctx); + if (p_op == NULL) + { + return FDS_ERR_NO_SPACE_IN_QUEUES; + } + + p_op->op_code = FDS_OP_DEL_RECORD; + p_op->del.step = FDS_OP_DEL_RECORD_FLAG_DIRTY; + p_op->del.record_to_delete = p_desc->record_id; + + queue_buf_store(&iput_ctx); + queue_start(); + + return FDS_SUCCESS; +} + + +ret_code_t fds_file_delete(uint16_t file_id) +{ + fds_op_t * p_op; + nrf_atfifo_item_put_t iput_ctx; + + if (!m_flags.initialized) + { + return FDS_ERR_NOT_INITIALIZED; + } + + if (file_id == FDS_FILE_ID_INVALID) + { + return FDS_ERR_INVALID_ARG; + } + + p_op = queue_buf_get(&iput_ctx); + if (p_op == NULL) + { + return FDS_ERR_NO_SPACE_IN_QUEUES; + } + + p_op->op_code = FDS_OP_DEL_FILE; + p_op->del.step = FDS_OP_DEL_FILE_FLAG_DIRTY; + p_op->del.file_id = file_id; + + queue_buf_store(&iput_ctx); + queue_start(); + + return FDS_SUCCESS; +} + + +ret_code_t fds_gc(void) +{ + fds_op_t * p_op; + nrf_atfifo_item_put_t iput_ctx; + + if (!m_flags.initialized) + { + return FDS_ERR_NOT_INITIALIZED; + } + + p_op = queue_buf_get(&iput_ctx); + if (p_op == NULL) + { + return FDS_ERR_NO_SPACE_IN_QUEUES; + } + + p_op->op_code = FDS_OP_GC; + + queue_buf_store(&iput_ctx); + + if (m_gc.state != GC_BEGIN) + { + // Resume GC by retrying the last step. + m_gc.resume = true; + } + + queue_start(); + + return FDS_SUCCESS; +} + + +ret_code_t fds_record_iterate(fds_record_desc_t * const p_desc, + fds_find_token_t * const p_token) +{ + return record_find(NULL, NULL, p_desc, p_token); +} + + +ret_code_t fds_record_find(uint16_t file_id, + uint16_t record_key, + fds_record_desc_t * const p_desc, + fds_find_token_t * const p_token) +{ + return record_find(&file_id, &record_key, p_desc, p_token); +} + + +ret_code_t fds_record_find_by_key(uint16_t record_key, + fds_record_desc_t * const p_desc, + fds_find_token_t * const p_token) +{ + return record_find(NULL, &record_key, p_desc, p_token); +} + + +ret_code_t fds_record_find_in_file(uint16_t file_id, + fds_record_desc_t * const p_desc, + fds_find_token_t * const p_token) +{ + return record_find(&file_id, NULL, p_desc, p_token); +} + + +ret_code_t fds_descriptor_from_rec_id(fds_record_desc_t * const p_desc, + uint32_t record_id) +{ + if (p_desc == NULL) + { + return FDS_ERR_NULL_ARG; + } + + // Zero the descriptor and set the record_id field. + memset(p_desc, 0x00, sizeof(fds_record_desc_t)); + p_desc->record_id = record_id; + + return FDS_SUCCESS; +} + + +ret_code_t fds_record_id_from_desc(fds_record_desc_t const * const p_desc, + uint32_t * const p_record_id) +{ + if ((p_desc == NULL) || (p_record_id == NULL)) + { + return FDS_ERR_NULL_ARG; + } + + *p_record_id = p_desc->record_id; + + return FDS_SUCCESS; +} + + +ret_code_t fds_stat(fds_stat_t * const p_stat) +{ + uint16_t const words_in_page = FDS_PAGE_SIZE; + // The largest number of free contiguous words on any page. + uint16_t contig_words = 0; + + if (!m_flags.initialized) + { + return FDS_ERR_NOT_INITIALIZED; + } + + if (p_stat == NULL) + { + return FDS_ERR_NULL_ARG; + } + + memset(p_stat, 0x00, sizeof(fds_stat_t)); + + p_stat->pages_available = FDS_VIRTUAL_PAGES; + + for (uint16_t page = 0; page < FDS_DATA_PAGES; page++) + { + uint16_t const words_used = m_pages[page].write_offset + m_pages[page].words_reserved; + + if (page_identify(m_pages[page].p_addr) == FDS_PAGE_UNDEFINED) + { + p_stat->pages_available--; + } + + p_stat->open_records += m_pages[page].records_open; + p_stat->words_reserved += m_pages[page].words_reserved; + p_stat->words_used += words_used; + + contig_words = (words_in_page - words_used); + if (contig_words > p_stat->largest_contig) + { + p_stat->largest_contig = contig_words; + } + + records_stat(page, + &p_stat->valid_records, + &p_stat->dirty_records, + &p_stat->freeable_words, + &p_stat->corruption); + } + + return FDS_SUCCESS; +} + +#endif //NRF_MODULE_ENABLED(FDS) diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/components/libraries/fds/fds.h b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/components/libraries/fds/fds.h new file mode 100644 index 0000000000..09d1eea08f --- /dev/null +++ b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/components/libraries/fds/fds.h @@ -0,0 +1,700 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef FDS_H__ +#define FDS_H__ + +/** + * @defgroup fds Flash Data Storage + * @ingroup app_common + * @{ + * + * @brief Flash Data Storage (FDS). + * + * @details Flash Data Storage is a minimalistic, record-oriented file system for the on-chip + * flash. Files are stored as a collection of records of variable length. FDS supports + * synchronous read operations and asynchronous write operations (write, update, + * and delete). FDS can be used from multiple threads. + */ + +#include +#include +#include "sdk_errors.h" +#include "app_util_platform.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/**@brief Invalid file ID. + * + * This value must not be used as a file ID by the application. + */ +#define FDS_FILE_ID_INVALID (0xFFFF) + + +/**@brief Record key for deleted records. + * + * This key is used to flag a record as "dirty", which means that it should be removed during + * the next garbage collection. This value must not be used as a record key by the application. + */ +#define FDS_RECORD_KEY_DIRTY (0x0000) + + +/**@brief FDS return values. + */ +enum +{ + FDS_SUCCESS = NRF_SUCCESS, //!< The operation completed successfully. + FDS_ERR_OPERATION_TIMEOUT, //!< Error. The operation timed out. + FDS_ERR_NOT_INITIALIZED, //!< Error. The module has not been initialized. + FDS_ERR_UNALIGNED_ADDR, //!< Error. The input data is not aligned to a word boundary. + FDS_ERR_INVALID_ARG, //!< Error. The parameter contains invalid data. + FDS_ERR_NULL_ARG, //!< Error. The parameter is NULL. + FDS_ERR_NO_OPEN_RECORDS, //!< Error. The record is not open, so it cannot be closed. + FDS_ERR_NO_SPACE_IN_FLASH, //!< Error. There is no space in flash memory. + FDS_ERR_NO_SPACE_IN_QUEUES, //!< Error. There is no space in the internal queues. + FDS_ERR_RECORD_TOO_LARGE, //!< Error. The record exceeds the maximum allowed size. + FDS_ERR_NOT_FOUND, //!< Error. The record was not found. + FDS_ERR_NO_PAGES, //!< Error. No flash pages are available. + FDS_ERR_USER_LIMIT_REACHED, //!< Error. The maximum number of users has been reached. + FDS_ERR_CRC_CHECK_FAILED, //!< Error. The CRC check failed. + FDS_ERR_BUSY, //!< Error. The underlying flash subsystem was busy. + FDS_ERR_INTERNAL, //!< Error. An internal error occurred. +}; + + +/**@brief The record metadata as stored in flash. + * @warning Do not edit or reorder the fields in this structure. + */ +typedef struct +{ + uint16_t record_key; //!< The record key. + uint16_t length_words; //!< The length of the record data (in 4-byte words). + uint16_t file_id; //!< The ID of the file that the record belongs to. + uint16_t crc16; //!< CRC16-CCITT check value. + /* The CRC is calculated over the entire record as stored in flash, + * including the record metadata except the CRC field itself. + */ + uint32_t record_id; //!< The unique record ID (32 bits). +} fds_header_t; + + +/**@brief The record descriptor structure that is used to manipulate records. + * + * This structure is used by the FDS module. You must provide the descriptor to the module when + * you manipulate existing records. However, you should never modify it or use any of its fields. + * + * @note Never reuse the same descriptor for different records. + */ +typedef struct +{ + uint32_t record_id; //!< The unique record ID. + uint32_t const * p_record; //!< The last known location of the record in flash. + uint16_t gc_run_count; //!< Number of times garbage collection has been run. + bool record_is_open; //!< Whether the record is currently open. +} fds_record_desc_t; + + +/**@brief Structure that can be used to read the contents of a record stored in flash. + * + * This structure does not reflect the physical layout of a record in flash, but it points + * to the locations where the record header (metadata) and the record data are stored. + */ +typedef struct +{ + fds_header_t const * p_header; //!< Location of the record header in flash. + void const * p_data; //!< Location of the record data in flash. +} fds_flash_record_t; + + +/**@brief A record to be written to flash. */ +typedef struct +{ + uint16_t file_id; //!< The ID of the file that the record belongs to. + uint16_t key; //!< The record key. + struct + { + void const * p_data; + uint32_t length_words; + } data; +} fds_record_t; + + +/**@brief A token to a reserved space in flash, created by @ref fds_reserve. + * + * This token can be used to write the record in the reserved space (@ref fds_record_write_reserved) + * or to cancel the reservation (@ref fds_reserve_cancel). + */ +typedef struct +{ + uint16_t page; //!< The logical ID of the page where space was reserved. + uint16_t length_words; //!< The amount of space reserved (in 4-byte words). +} fds_reserve_token_t; + + +/**@brief A token to keep information about the progress of @ref fds_record_find, + * @ref fds_record_find_by_key, and @ref fds_record_find_in_file. + * + * @note Always zero-initialize the token before using it for the first time. + * @note Never reuse the same token to search for different records. + */ +typedef struct +{ + uint32_t const * p_addr; + uint16_t page; +} fds_find_token_t; + + +/**@brief FDS event IDs. + */ +typedef enum +{ + FDS_EVT_INIT, //!< Event for @ref fds_init. + FDS_EVT_WRITE, //!< Event for @ref fds_record_write and @ref fds_record_write_reserved. + FDS_EVT_UPDATE, //!< Event for @ref fds_record_update. + FDS_EVT_DEL_RECORD, //!< Event for @ref fds_record_delete. + FDS_EVT_DEL_FILE, //!< Event for @ref fds_file_delete. + FDS_EVT_GC //!< Event for @ref fds_gc. +} fds_evt_id_t; + + +ANON_UNIONS_ENABLE; + +/**@brief An FDS event. */ +typedef struct +{ + fds_evt_id_t id; //!< The event ID. See @ref fds_evt_id_t. + ret_code_t result; //!< The result of the operation related to this event. + union + { + struct + { + uint32_t record_id; + uint16_t file_id; + uint16_t record_key; + bool is_record_updated; + } write; //!< Information for @ref FDS_EVT_WRITE and @ref FDS_EVT_UPDATE events. + struct + { + uint32_t record_id; + uint16_t file_id; + uint16_t record_key; + } del; //!< Information for @ref FDS_EVT_DEL_RECORD and @ref FDS_EVT_DEL_FILE events. + }; +} fds_evt_t; + +ANON_UNIONS_DISABLE; + + +/**@brief File system statistics. */ +typedef struct +{ + uint16_t pages_available; //!< The number of pages available. + uint16_t open_records; //!< The number of open records. + uint16_t valid_records; //!< The number of valid records. + uint16_t dirty_records; //!< The number of deleted ("dirty") records. + uint16_t words_reserved; //!< The number of words reserved by @ref fds_reserve(). + + /**@brief The number of words written to flash, including those reserved for future writes. */ + uint16_t words_used; + + /**@brief The largest number of free contiguous words in the file system. + * + * This number indicates the largest record that can be stored by FDS. + * It takes into account all reservations for future writes. + */ + uint16_t largest_contig; + + /**@brief The largest number of words that can be reclaimed by garbage collection. + * + * The actual amount of space freed by garbage collection might be less than this value if + * records are open while garbage collection is run. + */ + uint16_t freeable_words; + + /**@brief Filesystem corruption has been detected. + * + * One or more corrupted records were detected. FDS will heal the filesystem automatically + * next time garbage collection is run, but some data may be lost. + * + * @note: This flag is unrelated to CRC failures. + */ + bool corruption; +} fds_stat_t; + + +/**@brief FDS event handler function prototype. + * + * @param p_evt The event. + */ +typedef void (*fds_cb_t)(fds_evt_t const * p_evt); + + +/**@brief Function for registering an FDS event handler. + * + * The maximum amount of handlers that can be registered can be configured by changing the value + * of @ref FDS_MAX_USERS in fds_config.h. + * + * @param[in] cb The event handler function. + * + * @retval FDS_SUCCESS If the event handler was registered successfully. + * @retval FDS_ERR_USER_LIMIT_REACHED If the maximum number of registered callbacks is reached. + */ +ret_code_t fds_register(fds_cb_t cb); + + +/**@brief Function for initializing the module. + * + * This function initializes the module and installs the file system (unless it is installed + * already). + * + * This function is asynchronous. Completion is reported through an event. Make sure to call + * @ref fds_register before calling @ref fds_init so that you receive the completion event. + * + * @retval FDS_SUCCESS If the operation was queued successfully. + * @retval FDS_ERR_NO_PAGES If there is no space available in flash memory to install the + * file system. + */ +ret_code_t fds_init(void); + + +/**@brief Function for writing a record to flash. + * + * There are no restrictions on the file ID and the record key, except that the record key must be + * different from @ref FDS_RECORD_KEY_DIRTY and the file ID must be different from + * @ref FDS_FILE_ID_INVALID. In particular, no restrictions are made regarding the uniqueness of + * the file ID or the record key. All records with the same file ID are grouped into one file. + * If no file with the specified ID exists, it is created. There can be multiple records with the + * same record key in a file. + * + * Some modules need exclusive use of certain file IDs and record keys. + * See @ref lib_fds_functionality_keys for details. + * + * Record data can consist of multiple chunks. The data must be aligned to a 4 byte boundary, and + * because it is not buffered internally, it must be kept in memory until the callback for the + * operation has been received. The length of the data must not exceed @ref FDS_VIRTUAL_PAGE_SIZE + * words minus 14 bytes. + * + * This function is asynchronous. Completion is reported through an event that is sent to + * the registered event handler function. + * + * @param[out] p_desc The descriptor of the record that was written. Pass NULL if you do not + * need the descriptor. + * @param[in] p_record The record to be written to flash. + * + * @retval FDS_SUCCESS If the operation was queued successfully. + * @retval FDS_ERR_NOT_INITIALIZED If the module is not initialized. + * @retval FDS_ERR_NULL_ARG If @p p_record is NULL. + * @retval FDS_ERR_INVALID_ARG If the file ID or the record key is invalid. + * @retval FDS_ERR_UNALIGNED_ADDR If the record data is not aligned to a 4 byte boundary. + * @retval FDS_ERR_RECORD_TOO_LARGE If the record data exceeds the maximum length. + * @retval FDS_ERR_NO_SPACE_IN_QUEUES If the operation queue is full or there are more record + * chunks than can be buffered. + * @retval FDS_ERR_NO_SPACE_IN_FLASH If there is not enough free space in flash to store the + * record. + */ +ret_code_t fds_record_write(fds_record_desc_t * p_desc, + fds_record_t const * p_record); + + +/**@brief Function for reserving space in flash. + * + * This function can be used to reserve space in flash memory. To write a record into the reserved + * space, use @ref fds_record_write_reserved. Alternatively, use @ref fds_reserve_cancel to cancel + * a reservation. + * + * Note that this function does not write any data to flash. + * + * @param[out] p_token A token that can be used to write a record in the reserved space or + * cancel the reservation. + * @param[in] length_words The length of the record data (in 4-byte words). + * + * @retval FDS_SUCCESS If the flash space was reserved successfully. + * @retval FDS_ERR_NOT_INITIALIZED If the module is not initialized. + * @retval FDS_ERR_NULL_ARG If @p p_token is NULL instead of a valid token address. + * @retval FDS_ERR_RECORD_TOO_LARGE If the record length exceeds the maximum length. + * @retval FDS_ERR_NO_SPACE_IN_FLASH If there is not enough free space in flash to store the + * record. + */ +ret_code_t fds_reserve(fds_reserve_token_t * p_token, uint16_t length_words); + + +/**@brief Function for canceling an @ref fds_reserve operation. + * + * @param[in] p_token The token that identifies the reservation, produced by @ref fds_reserve. + * + * @retval FDS_SUCCESS If the reservation was canceled. + * @retval FDS_ERR_NOT_INITIALIZED If the module is not initialized. + * @retval FDS_ERR_NULL_ARG If @p p_token is NULL instead of a valid token address. + * @retval FDS_ERR_INVALID_ARG If @p p_token contains invalid data. + */ +ret_code_t fds_reserve_cancel(fds_reserve_token_t * p_token); + + +/**@brief Function for writing a record to a space in flash that was reserved using + * @ref fds_reserve. + * + * There are no restrictions on the file ID and the record key, except that the record key must be + * different from @ref FDS_RECORD_KEY_DIRTY and the file ID must be different from + * @ref FDS_FILE_ID_INVALID. In particular, no restrictions are made regarding the uniqueness of + * the file ID or the record key. All records with the same file ID are grouped into one file. + * If no file with the specified ID exists, it is created. There can be multiple records with the + * same record key in a file. + * + * Record data can consist of multiple chunks. The data must be aligned to a 4 byte boundary, and + * because it is not buffered internally, it must be kept in memory until the callback for the + * operation has been received. The length of the data must not exceed @ref FDS_VIRTUAL_PAGE_SIZE + * words minus 14 bytes. + * + * This function is asynchronous. Completion is reported through an event that is sent to the + * registered event handler function. + * + * @note + * This function behaves similarly to @ref fds_record_write, with the exception that it never + * fails with the error @ref FDS_ERR_NO_SPACE_IN_FLASH. + * + * @param[out] p_desc The descriptor of the record that was written. Pass NULL if you do not + * need the descriptor. + * @param[in] p_record The record to be written to flash. + * @param[in] p_token The token that identifies the space reserved in flash. + * + * @retval FDS_SUCCESS If the operation was queued successfully. + * @retval FDS_ERR_NOT_INITIALIZED If the module is not initialized. + * @retval FDS_ERR_NULL_ARG If @p p_token is NULL instead of a valid token address. + * @retval FDS_ERR_INVALID_ARG If the file ID or the record key is invalid. + * @retval FDS_ERR_UNALIGNED_ADDR If the record data is not aligned to a 4 byte boundary. + * @retval FDS_ERR_RECORD_TOO_LARGE If the record data exceeds the maximum length. + * @retval FDS_ERR_NO_SPACE_IN_QUEUES If the operation queue is full or there are more record + * chunks than can be buffered. + */ +ret_code_t fds_record_write_reserved(fds_record_desc_t * p_desc, + fds_record_t const * p_record, + fds_reserve_token_t const * p_token); + + +/**@brief Function for deleting a record. + * + * Deleted records cannot be located using @ref fds_record_find, @ref fds_record_find_by_key, or + * @ref fds_record_find_in_file. Additionally, they can no longer be opened using + * @ref fds_record_open. + * + * Note that deleting a record does not free the space it occupies in flash memory. + * To reclaim flash space used by deleted records, call @ref fds_gc to run garbage collection. + * + * This function is asynchronous. Completion is reported through an event that is sent to the + * registered event handler function. + * + * @param[in] p_desc The descriptor of the record that should be deleted. + * + * @retval FDS_SUCCESS If the operation was queued successfully. + * @retval FDS_ERR_NOT_INITIALIZED If the module is not initialized. + * @retval FDS_ERR_NULL_ARG If the specified record descriptor @p p_desc is NULL. + * @retval FDS_ERR_NO_SPACE_IN_QUEUES If the operation queue is full. + */ +ret_code_t fds_record_delete(fds_record_desc_t * p_desc); + + +/**@brief Function for deleting all records in a file. + * + * This function deletes a file, including all its records. Deleted records cannot be located + * using @ref fds_record_find, @ref fds_record_find_by_key, or @ref fds_record_find_in_file. + * Additionally, they can no longer be opened using @ref fds_record_open. + * + * Note that deleting records does not free the space they occupy in flash memory. + * To reclaim flash space used by deleted records, call @ref fds_gc to run garbage collection. + * + * This function is asynchronous. Completion is reported through an event that is sent to the + * registered event handler function. + * + * @param[in] file_id The ID of the file to be deleted. + * + * @retval FDS_SUCCESS If the operation was queued successfully. + * @retval FDS_ERR_NOT_INITIALIZED If the module is not initialized. + * @retval FDS_ERR_INVALID_ARG If the specified @p file_id is invalid. + * @retval FDS_ERR_NO_SPACE_IN_QUEUES If the operation queue is full. + */ +ret_code_t fds_file_delete(uint16_t file_id); + + +/**@brief Function for updating a record. + * + * Updating a record first writes a new record (@p p_record) to flash and then deletes the + * old record (identified by @p p_desc). + * + * There are no restrictions on the file ID and the record key, except that the record key must be + * different from @ref FDS_RECORD_KEY_DIRTY and the file ID must be different from + * @ref FDS_FILE_ID_INVALID. In particular, no restrictions are made regarding the uniqueness of + * the file ID or the record key. All records with the same file ID are grouped into one file. + * If no file with the specified ID exists, it is created. There can be multiple records with the + * same record key in a file. + * + * Record data can consist of multiple chunks. The data must be aligned to a 4 byte boundary, and + * because it is not buffered internally, it must be kept in memory until the callback for the + * operation has been received. The length of the data must not exceed @ref FDS_VIRTUAL_PAGE_SIZE + * words minus 14 bytes. + * + * This function is asynchronous. Completion is reported through an event that is sent to the + * registered event handler function. + * + * @param[in, out] p_desc The descriptor of the record to update. When the function + * returns with FDS_SUCCESS, this parameter contains the + * descriptor of the newly written record. + * @param[in] p_record The updated record to be written to flash. + * + * @retval FDS_SUCCESS If the operation was queued successfully. + * @retval FDS_ERR_NOT_INITIALIZED If the module is not initialized. + * @retval FDS_ERR_INVALID_ARG If the file ID or the record key is invalid. + * @retval FDS_ERR_UNALIGNED_ADDR If the record data is not aligned to a 4 byte boundary. + * @retval FDS_ERR_RECORD_TOO_LARGE If the record data exceeds the maximum length. + * @retval FDS_ERR_NO_SPACE_IN_QUEUES If the operation queue is full or there are more record + * chunks than can be buffered. + * @retval FDS_ERR_NO_SPACE_IN_FLASH If there is not enough free space in flash to store the + * updated record. + */ +ret_code_t fds_record_update(fds_record_desc_t * p_desc, + fds_record_t const * p_record); + + +/**@brief Function for iterating through all records in flash. + * + * To search for the next record, call the function again and supply the same @ref fds_find_token_t + * structure to resume searching from the last record that was found. + * + * Note that the order with which records are iterated is not defined. + * + * @param[out] p_desc The descriptor of the record that was found. + * @param[out] p_token A token containing information about the progress of the operation. + * + * @retval FDS_SUCCESS If a record was found. + * @retval FDS_ERR_NOT_INITIALIZED If the module is not initialized. + * @retval FDS_ERR_NULL_ARG If @p p_desc or @p p_token is NULL. + * @retval FDS_ERR_NOT_FOUND If no matching record was found. + */ +ret_code_t fds_record_iterate(fds_record_desc_t * p_desc, + fds_find_token_t * p_token); + + +/**@brief Function for searching for records with a given record key in a file. + * + * This function finds the first record in a file that has the given record key. To search for the + * next record with the same key in the file, call the function again and supply the same + * @ref fds_find_token_t structure to resume searching from the last record that was found. + * + * @param[in] file_id The file ID. + * @param[in] record_key The record key. + * @param[out] p_desc The descriptor of the record that was found. + * @param[out] p_token A token containing information about the progress of the operation. + * + * @retval FDS_SUCCESS If a record was found. + * @retval FDS_ERR_NOT_INITIALIZED If the module is not initialized. + * @retval FDS_ERR_NULL_ARG If @p p_desc or @p p_token is NULL. + * @retval FDS_ERR_NOT_FOUND If no matching record was found. + */ +ret_code_t fds_record_find(uint16_t file_id, + uint16_t record_key, + fds_record_desc_t * p_desc, + fds_find_token_t * p_token); + + +/**@brief Function for searching for records with a given record key. + * + * This function finds the first record with a given record key, independent of the file it + * belongs to. To search for the next record with the same key, call the function again and supply + * the same @ref fds_find_token_t structure to resume searching from the last record that was found. + * + * @param[in] record_key The record key. + * @param[out] p_desc The descriptor of the record that was found. + * @param[out] p_token A token containing information about the progress of the operation. + * + * @retval FDS_SUCCESS If a record was found. + * @retval FDS_ERR_NOT_INITIALIZED If the module is not initialized. + * @retval FDS_ERR_NULL_ARG If @p p_desc or @p p_token is NULL. + * @retval FDS_ERR_NOT_FOUND If no record with the given key was found. + */ +ret_code_t fds_record_find_by_key(uint16_t record_key, + fds_record_desc_t * p_desc, + fds_find_token_t * p_token); + + +/**@brief Function for searching for any record in a file. + * + * This function finds the first record in a file, independent of its record key. + * To search for the next record in the same file, call the function again and supply the same + * @ref fds_find_token_t structure to resume searching from the last record that was found. + * + * @param[in] file_id The file ID. + * @param[out] p_desc The descriptor of the record that was found. + * @param[out] p_token A token containing information about the progress of the operation. + * + * @retval FDS_SUCCESS If a record was found. + * @retval FDS_ERR_NOT_INITIALIZED If the module is not initialized. + * @retval FDS_ERR_NULL_ARG If @p p_desc or @p p_token is NULL. + * @retval FDS_ERR_NOT_FOUND If no matching record was found. + */ +ret_code_t fds_record_find_in_file(uint16_t file_id, + fds_record_desc_t * p_desc, + fds_find_token_t * p_token); + + +/**@brief Function for opening a record for reading. + * + * This function opens a record that is stored in flash, so that it can be read. The function + * initializes an @ref fds_flash_record_t structure, which can be used to access the record data as + * well as its associated metadata. The pointers provided in the @ref fds_flash_record_t structure + * are pointers to flash memory. + * + * Opening a record with @ref fds_record_open prevents garbage collection to run on the virtual + * flash page in which record is stored, so that the contents of the memory pointed by fields in + * @ref fds_flash_record_t are guaranteed to remain unmodified as long as the record is kept open. + * + * When you are done reading a record, call @ref fds_record_close to close it. Garbage collection + * can then reclaim space on the virtual page where the record is stored. Note that you must + * provide the same descriptor for @ref fds_record_close as you did for this function. + * + * @param[in] p_desc The descriptor of the record to open. + * @param[out] p_flash_record The record, as stored in flash. + * + * @retval FDS_SUCCESS If the record was opened successfully. + * @retval FDS_ERR_NULL_ARG If @p p_desc or @p p_flash_record is NULL. + * @retval FDS_ERR_NOT_FOUND If the record was not found. It might have been deleted, or + * it might not have been written yet. + * @retval FDS_ERR_CRC_CHECK_FAILED If the CRC check for the record failed. + */ +ret_code_t fds_record_open(fds_record_desc_t * p_desc, + fds_flash_record_t * p_flash_record); + + +/**@brief Function for closing a record. + * + * Closing a record allows garbage collection to run on the virtual page in which the record is + * stored (if no other records remain open on that page). The descriptor passed as an argument + * must be the same as the one used to open the record using @ref fds_record_open. + * + * Note that closing a record does not invalidate its descriptor. You can still supply the + * descriptor to all functions that accept a record descriptor as a parameter. + * + * @param[in] p_desc The descriptor of the record to close. + * + * @retval FDS_SUCCESS If the record was closed successfully. + * @retval FDS_ERR_NULL_ARG If @p p_desc is NULL. + * @retval FDS_ERR_NO_OPEN_RECORDS If the record is not open. + * @retval FDS_ERR_NOT_FOUND If the record could not be found. + */ +ret_code_t fds_record_close(fds_record_desc_t * p_desc); + + +/**@brief Function for running garbage collection. + * + * Garbage collection reclaims the flash space that is occupied by records that have been deleted, + * or that failed to be completely written due to, for example, a power loss. + * + * This function is asynchronous. Completion is reported through an event that is sent to the + * registered event handler function. + * + * @retval FDS_SUCCESS If the operation was queued successfully. + * @retval FDS_ERR_NOT_INITIALIZED If the module is not initialized. + * @retval FDS_ERR_NO_SPACE_IN_QUEUES If the operation queue is full. + */ +ret_code_t fds_gc(void); + + +/**@brief Function for obtaining a descriptor from a record ID. + * + * This function can be used to reconstruct a descriptor from a record ID, like the one that is + * passed to the callback function. + * + * @note + * This function does not check whether a record with the given record ID exists. + * If a non-existing record ID is supplied, the resulting descriptor is invalid and will cause + * other functions to fail when it is supplied as parameter. + * + * @param[out] p_desc The descriptor of the record with the given record ID. + * @param[in] record_id The record ID for which a descriptor should be returned. + * + * @retval FDS_SUCCESS If a descriptor was returned. + * @retval FDS_ERR_NULL_ARG If @p p_desc is NULL. + */ +ret_code_t fds_descriptor_from_rec_id(fds_record_desc_t * p_desc, + uint32_t record_id); + + +/**@brief Function for obtaining a record ID from a record descriptor. + * + * This function can be used to extract a record ID from a descriptor. For example, you could use + * it in the callback function to compare the record ID of an event to the record IDs of the + * records for which you have a descriptor. + * + * @warning + * This function does not check whether the record descriptor is valid. If the descriptor is not + * initialized or has been tampered with, the resulting record ID might be invalid. + * + * @param[in] p_desc The descriptor from which the record ID should be extracted. + * @param[out] p_record_id The record ID that is contained in the given descriptor. + * + * @retval FDS_SUCCESS If a record ID was returned. + * @retval FDS_ERR_NULL_ARG If @p p_desc or @p p_record_id is NULL. + */ +ret_code_t fds_record_id_from_desc(fds_record_desc_t const * p_desc, + uint32_t * p_record_id); + + +/**@brief Function for retrieving file system statistics. + * + * This function retrieves file system statistics, such as the number of open records, the space + * that can be reclaimed by garbage collection, and others. + * + * @param[out] p_stat File system statistics. + * + * @retval FDS_SUCCESS If the statistics were returned successfully. + * @retval FDS_ERR_NOT_INITIALIZED If the module is not initialized. + * @retval FDS_ERR_NULL_ARG If @p p_stat is NULL. + */ +ret_code_t fds_stat(fds_stat_t * p_stat); + + +/** @} */ + + +#ifdef __cplusplus +} +#endif + +#endif // FDS_H__ diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/components/libraries/fds/fds_internal_defs.h b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/components/libraries/fds/fds_internal_defs.h new file mode 100644 index 0000000000..7502b43750 --- /dev/null +++ b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/components/libraries/fds/fds_internal_defs.h @@ -0,0 +1,327 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef FDS_INTERNAL_DEFS_H__ +#define FDS_INTERNAL_DEFS_H__ +#include "sdk_config.h" +#include +#include + +#if defined (FDS_THREADS) + #include "nrf_soc.h" + #include "app_util_platform.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define FDS_PAGE_TAG_SIZE (2) // Page tag size, in 4-byte words. +#define FDS_PAGE_TAG_WORD_0 (0) // Offset of the first word in the page tag from the page address. +#define FDS_PAGE_TAG_WORD_1 (1) // Offset of the second word in the page tag from the page address. + +// Page tag constants +#define FDS_PAGE_TAG_MAGIC (0xDEADC0DE) +#define FDS_PAGE_TAG_SWAP (0xF11E01FF) +#define FDS_PAGE_TAG_DATA (0xF11E01FE) + +#define FDS_ERASED_WORD (0xFFFFFFFF) + +#define FDS_OFFSET_TL (0) // Offset of TL from the record base address, in 4-byte words. +#define FDS_OFFSET_IC (1) // Offset of IC from the record base address, in 4-byte words. +#define FDS_OFFSET_ID (2) // Offset of ID from the record base address, in 4-byte words. +#define FDS_OFFSET_DATA (3) // Offset of the data from the record base address, in 4-byte words. + +#define FDS_HEADER_SIZE_TL (1) // Size of the TL part of the header, in 4-byte words. +#define FDS_HEADER_SIZE_IC (1) // Size of the IC part of the header, in 4-byte words. +#define FDS_HEADER_SIZE_ID (1) // Size of the record ID in the header, in 4-byte words. +#define FDS_HEADER_SIZE (3) // Size of the whole header, in 4-byte words. + +#define FDS_OP_EXECUTING (NRF_SUCCESS) +#define FDS_OP_COMPLETED (0x1D1D) + +#define NRF_FSTORAGE_NVMC 1 +#define NRF_FSTORAGE_SD 2 + +// The size of a physical page, in 4-byte words. +#if defined(NRF51) + #define FDS_PHY_PAGE_SIZE (256) +#else + #define FDS_PHY_PAGE_SIZE (1024) +#endif + +// The number of physical pages to be used. This value is configured indirectly. +#define FDS_PHY_PAGES ((FDS_VIRTUAL_PAGES * FDS_VIRTUAL_PAGE_SIZE) / FDS_PHY_PAGE_SIZE) + +// The size of a virtual page, in number of physical pages. +#define FDS_PHY_PAGES_IN_VPAGE (FDS_VIRTUAL_PAGE_SIZE / FDS_PHY_PAGE_SIZE) + +// The number of pages available to store data; which is the total minus one (the swap). +#define FDS_DATA_PAGES (FDS_VIRTUAL_PAGES - 1) + + // Just a shorter name for the size, in words, of a virtual page. +#define FDS_PAGE_SIZE (FDS_VIRTUAL_PAGE_SIZE) + + +#if (FDS_VIRTUAL_PAGE_SIZE % FDS_PHY_PAGE_SIZE != 0) + #error "FDS_VIRTUAL_PAGE_SIZE must be a multiple of the size of a physical page." +#endif + +#if (FDS_VIRTUAL_PAGES < 2) + #error "FDS requires at least two virtual pages." +#endif + + +// Page types. +typedef enum +{ + FDS_PAGE_DATA, // Page is ready for storage. + FDS_PAGE_SWAP, // Page is reserved for garbage collection. + FDS_PAGE_ERASED, // Page is erased. + FDS_PAGE_UNDEFINED, // Undefined page type. +} fds_page_type_t; + + +typedef enum +{ + FDS_HEADER_VALID, // Valid header. + FDS_HEADER_DIRTY, // Header is incomplete, or record has been deleted. + FDS_HEADER_CORRUPT // Header contains corrupt information, not related to CRC. +} fds_header_status_t; + + +typedef struct +{ + fds_page_type_t page_type; // The page type. + uint32_t const * p_addr; // The address of the page. + uint16_t write_offset; // The page write offset, in 4-byte words. + uint16_t words_reserved; // The amount of words reserved. + uint32_t volatile records_open; // The number of open records. + bool can_gc; // Indicates that there are some records that have been deleted. +} fds_page_t; + + +typedef struct +{ + uint32_t const * p_addr; + uint16_t write_offset; +} fds_swap_page_t; + + +// FDS op-codes. +typedef enum +{ + FDS_OP_NONE, + FDS_OP_INIT, // Initialize the module. + FDS_OP_WRITE, // Write a record to flash. + FDS_OP_UPDATE, // Update a record. + FDS_OP_DEL_RECORD, // Delete a record. + FDS_OP_DEL_FILE, // Delete a file. + FDS_OP_GC // Run garbage collection. +} fds_op_code_t; + + +typedef enum +{ + FDS_OP_INIT_TAG_SWAP, + FDS_OP_INIT_TAG_DATA, + FDS_OP_INIT_ERASE_SWAP, + FDS_OP_INIT_PROMOTE_SWAP, +} fds_init_step_t; + + +typedef enum +{ + FDS_OP_WRITE_HEADER_BEGIN, // Write the record key and length. + FDS_OP_WRITE_HEADER_FINALIZE, // Write the file ID and CRC. + FDS_OP_WRITE_RECORD_ID, // Write the record ID. + FDS_OP_WRITE_DATA, // Write the record data. + FDS_OP_WRITE_FIND_RECORD, + FDS_OP_WRITE_FLAG_DIRTY, // Flag a record as dirty (as part of an update operation). + FDS_OP_WRITE_DONE, +} fds_write_step_t; + + +typedef enum +{ + FDS_OP_DEL_RECORD_FLAG_DIRTY, // Flag a record as dirty. + FDS_OP_DEL_FILE_FLAG_DIRTY, // Flag multiple records as dirty. + FDS_OP_DEL_DONE, +} fds_delete_step_t; + + +#if defined(__CC_ARM) + #pragma push + #pragma anon_unions +#elif defined(__ICCARM__) + #pragma language=extended +#elif defined(__GNUC__) + // anonymous unions are enabled by default +#endif + +typedef struct +{ + fds_op_code_t op_code; // The opcode for the operation. + union + { + struct + { + fds_init_step_t step; // The current step the operation is at. + } init; + struct + { + fds_header_t header; + void const * p_data; + uint16_t page; // The page the flash space for this command was reserved. + fds_write_step_t step; // The current step the operation is at. + uint32_t record_to_delete; // The record to delete in case this is an update. + } write; + struct + { + fds_delete_step_t step; + uint16_t file_id; + uint16_t record_key; + uint32_t record_to_delete; + } del; + }; +} fds_op_t; + +#if defined(__CC_ARM) + #pragma pop +#elif defined(__ICCARM__) + // leave anonymous unions enabled +#elif defined(__GNUC__) + // anonymous unions are enabled by default +#endif + + +enum +{ + PAGE_ERASED = 0x1, // One or more erased pages found. + PAGE_DATA = 0x2, // One or more data pages found. + PAGE_SWAP_CLEAN = 0x4, // A clean (empty) swap page was found. + PAGE_SWAP_DIRTY = 0x8, // A dirty (non-empty) swap page was found. +}; + + +typedef enum +{ + // No erased pages or FDS pages found. + // This is a fatal error. + NO_PAGES, + + // The filesystem can not be garbage collected. + // This is a fatal error. + NO_SWAP = (PAGE_DATA), + + // Perform a fresh installation. + FRESH_INSTALL = (PAGE_ERASED), + + // Tag an erased page as swap. + TAG_SWAP = (PAGE_ERASED | PAGE_DATA), + + // Tag all erased pages as data. + TAG_DATA = (PAGE_ERASED | PAGE_SWAP_CLEAN), + + // Tag all remaining erased pages as data. + TAG_DATA_INST = (PAGE_ERASED | PAGE_DATA | PAGE_SWAP_CLEAN), + + // The swap is dirty, likely because the device powered off during GC. + // Because there is also an erased page, assume that that page has been garbage collected. + // Hence, tag the swap as data (promote), an erased page as swap and remaining pages as data. + PROMOTE_SWAP = (PAGE_ERASED | PAGE_SWAP_DIRTY), + + // Tag the swap as data (promote), an erased page as swap and remaining pages as data. + PROMOTE_SWAP_INST = (PAGE_ERASED | PAGE_DATA | PAGE_SWAP_DIRTY), + + // The swap is dirty (written) and there are no erased pages. It is likely that the device + // powered off during GC. It is safe to discard (erase) the swap, since data that was + // swapped out still lies in one of the valid pages. + DISCARD_SWAP = (PAGE_DATA | PAGE_SWAP_DIRTY), + + // Do nothing. + ALREADY_INSTALLED = (PAGE_DATA | PAGE_SWAP_CLEAN), + +} fds_init_opts_t; + + +typedef enum +{ + GC_BEGIN, // Begin GC. + GC_NEXT_PAGE, // GC a page. + GC_FIND_NEXT_RECORD, // Find a valid record to copy. + GC_COPY_RECORD, // Copy a valid record to swap. + GC_ERASE_PAGE, // Erase the page being garbage collected. + GC_DISCARD_SWAP, // Erase (discard) the swap page. + GC_PROMOTE_SWAP, // Tag the swap as valid. + GC_TAG_NEW_SWAP // Tag a freshly erased (GCed) page as swap. +} fds_gc_state_t; + + +// Holds garbage collection status and related data. +typedef struct +{ + fds_gc_state_t state; // The current GC step. + uint16_t cur_page; // The current page being garbage collected. + uint32_t const * p_record_src; // The current record being copied to swap. + uint16_t run_count; // Total number of times GC was run. + bool do_gc_page[FDS_DATA_PAGES]; // Controls which pages to garbage collect. + bool resume; // Whether or not GC should be resumed. +} fds_gc_data_t; + + +// Macros to enable and disable application interrupts. +#if defined (FDS_THREADS) + + #define CRITICAL_SECTION_ENTER() CRITICAL_REGION_ENTER() + #define CRITICAL_SECTION_EXIT() CRITICAL_REGION_EXIT() + +#else + + #define CRITICAL_SECTION_ENTER() + #define CRITICAL_SECTION_EXIT() + +#endif + + +#ifdef __cplusplus +} +#endif + +#endif // FDS_INTERNAL_DEFS_H__ diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/integration/nrfx/legacy/ble_flash/ble_flash.c b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/integration/nrfx/legacy/ble_flash/ble_flash.c new file mode 100644 index 0000000000..25368adaee --- /dev/null +++ b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/integration/nrfx/legacy/ble_flash/ble_flash.c @@ -0,0 +1,313 @@ +/** + * Copyright (c) 2012 - 2017, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "ble_flash.h" +#include +#include +#include +#include "nrf_soc.h" +#include "nordic_common.h" +#include "nrf_error.h" +#include "nrf.h" +#include "app_util.h" + + +static volatile bool m_radio_active = false; /**< TRUE if radio is active (or about to become active), FALSE otherwise. */ + + +uint16_t ble_flash_crc16_compute(uint8_t * p_data, uint16_t size, uint16_t * p_crc) +{ + uint16_t i; + uint16_t crc = (p_crc == NULL) ? 0xffff : *p_crc; + + for (i = 0; i < size; i++) + { + crc = (unsigned char)(crc >> 8) | (crc << 8); + crc ^= p_data[i]; + crc ^= (unsigned char)(crc & 0xff) >> 4; + crc ^= (crc << 8) << 4; + crc ^= ((crc & 0xff) << 4) << 1; + } + return crc; +} + + +/**@brief Function for erasing a page in flash. + * + * @param[in] p_page Pointer to first word in page to be erased. + */ +static void flash_page_erase(uint32_t * p_page) +{ + // Turn on flash erase enable and wait until the NVMC is ready. + NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Een << NVMC_CONFIG_WEN_Pos); + while (NRF_NVMC->READY == NVMC_READY_READY_Busy) + { + // Do nothing. + } + + // Erase page. + NRF_NVMC->ERASEPAGE = (uint32_t)p_page; + while (NRF_NVMC->READY == NVMC_READY_READY_Busy) + { + // Do nothing. + } + + // Turn off flash erase enable and wait until the NVMC is ready. + NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos); + while (NRF_NVMC->READY == NVMC_READY_READY_Busy) + { + // Do nothing + } +} + + +/**@brief Function for writing one word to flash. Unprotected write, which can interfere with radio communication. + * + * @details This function DOES NOT use the m_radio_active variable, but will force the write even + * when the radio is active. To be used only from @ref ble_flash_page_write. + * + * @note Flash location to be written must have been erased previously. + * + * @param[in] p_address Pointer to flash location to be written. + * @param[in] value Value to write to flash. + */ +static void flash_word_unprotected_write(uint32_t * p_address, uint32_t value) +{ + // Turn on flash write enable and wait until the NVMC is ready. + NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos); + while (NRF_NVMC->READY == NVMC_READY_READY_Busy) + { + // Do nothing. + } + *p_address = value; + + // Wait flash write to finish + while (NRF_NVMC->READY == NVMC_READY_READY_Busy) + { + // Do nothing. + } + + // Turn off flash write enable and wait until the NVMC is ready. + NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos); + while (NRF_NVMC->READY == NVMC_READY_READY_Busy) + { + // Do nothing. + } +} + + +/**@brief Function for writing one word to flash. + * + * @note Flash location to be written must have been erased previously. + * + * @param[in] p_address Pointer to flash location to be written. + * @param[in] value Value to write to flash. + */ +static void flash_word_write(uint32_t * p_address, uint32_t value) +{ + // If radio is active, wait for it to become inactive. + while (m_radio_active) + { + // Do nothing (just wait for radio to become inactive). + (void) sd_app_evt_wait(); + } + + // Turn on flash write enable and wait until the NVMC is ready. + NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos); + while (NRF_NVMC->READY == NVMC_READY_READY_Busy) + { + // Do nothing. + } + + *p_address = value; + // Wait flash write to finish + while (NRF_NVMC->READY == NVMC_READY_READY_Busy) + { + // Do nothing. + } + // Turn off flash write enable and wait until the NVMC is ready. + NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos); + while (NRF_NVMC->READY == NVMC_READY_READY_Busy) + { + // Do nothing + } +} + + +uint32_t ble_flash_word_write(uint32_t * p_address, uint32_t value) +{ + flash_word_write(p_address, value); + return NRF_SUCCESS; +} + + +uint32_t ble_flash_block_write(uint32_t * p_address, uint32_t * p_in_array, uint16_t word_count) +{ + uint16_t i; + + for (i = 0; i < word_count; i++) + { + flash_word_write(p_address, p_in_array[i]); + p_address++; + } + + return NRF_SUCCESS; +} + + +uint32_t ble_flash_page_erase(uint8_t page_num) +{ + uint32_t * p_page = (uint32_t *)(BLE_FLASH_PAGE_SIZE * page_num); + flash_page_erase(p_page); + + return NRF_SUCCESS; +} + + +uint32_t ble_flash_page_write(uint8_t page_num, uint32_t * p_in_array, uint8_t word_count) +{ + int i; + uint32_t * p_page; + uint32_t * p_curr_addr; + uint16_t in_data_crc; + uint16_t flash_crc; + uint32_t flash_header; + + p_page = (uint32_t *)(BLE_FLASH_PAGE_SIZE * page_num); + p_curr_addr = p_page; + + // Calculate CRC of the data to write. + in_data_crc = ble_flash_crc16_compute((uint8_t *)p_in_array, + word_count * sizeof(uint32_t), + NULL); + + // Compare the calculated to the one in flash. + flash_header = *p_curr_addr; + flash_crc = (uint16_t)flash_header; + + if (flash_crc == in_data_crc) + { + // Data is the same as the data already stored in flash, return without modifying flash. + return NRF_SUCCESS; + } + + // Erase flash page + flash_page_erase(p_page); + + // Reserve space for magic number (for detecting if flash content is valid). + p_curr_addr++; + + // Reserve space for saving word_count. + p_curr_addr++; + + // Write data + for (i = 0; i < word_count; i++) + { + flash_word_unprotected_write(p_curr_addr, p_in_array[i]); + p_curr_addr++; + } + + // Write number of elements. + flash_word_write(p_page + 1, (uint32_t)(word_count)); + + // Write magic number and CRC to indicate that flash content is valid. + flash_header = BLE_FLASH_MAGIC_NUMBER | (uint32_t)in_data_crc; + flash_word_write(p_page, flash_header); + + return NRF_SUCCESS; +} + + +uint32_t ble_flash_page_read(uint8_t page_num, uint32_t * p_out_array, uint8_t * p_word_count) +{ + int byte_count; + uint32_t * p_page; + uint32_t * p_curr_addr; + uint32_t flash_header; + uint32_t calc_header; + uint16_t calc_crc; + uint32_t tmp; + + p_page = (uint32_t *)(BLE_FLASH_PAGE_SIZE * page_num); + p_curr_addr = p_page; + + // Check if block is valid + flash_header = *p_curr_addr; + tmp = flash_header & 0xFFFF0000; + if (tmp != BLE_FLASH_MAGIC_NUMBER) + { + *p_word_count = 0; + return NRF_ERROR_NOT_FOUND; + } + p_curr_addr++; + + // Read number of elements + *p_word_count = (uint8_t)(*(p_curr_addr)); + p_curr_addr++; + + // Read data + byte_count = (*p_word_count) * sizeof(uint32_t); + memcpy(p_out_array, p_curr_addr, byte_count); + + // Check CRC + calc_crc = ble_flash_crc16_compute((uint8_t *)p_out_array, + (*p_word_count) * sizeof(uint32_t), + NULL); + calc_header = BLE_FLASH_MAGIC_NUMBER | (uint32_t)calc_crc; + + if (calc_header != flash_header) + { + return NRF_ERROR_NOT_FOUND; + } + + return NRF_SUCCESS; +} + + +uint32_t ble_flash_page_addr(uint8_t page_num, uint32_t ** pp_page_addr) +{ + *pp_page_addr = (uint32_t *)(BLE_FLASH_PAGE_SIZE * page_num); + return NRF_SUCCESS; +} + + +void ble_flash_on_radio_active_evt(bool radio_active) +{ + m_radio_active = radio_active; +} diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/integration/nrfx/legacy/ble_flash/ble_flash.h b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/integration/nrfx/legacy/ble_flash/ble_flash.h new file mode 100644 index 0000000000..8fffeb62a7 --- /dev/null +++ b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/integration/nrfx/legacy/ble_flash/ble_flash.h @@ -0,0 +1,178 @@ +/** + * Copyright (c) 2012 - 2017, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file + * + * @defgroup ble_flash_module Flash Manager + * @{ + * @ingroup ble_sdk_lib + * @brief Module for accessing flash memory. + * + * @details It contains functions for reading, writing and erasing one page in flash. + * + * The module uses the first 32 bits of the flash page to write a magic number in order to + * determine if the page has been written or not. + * + * @note Be careful not to use a page number in the SoftDevice area (which currently occupies the + * range 0 to 127), or in your application space! In both cases, this would end up + * with a hard fault. + */ + +#ifndef BLE_FLASH_H__ +#define BLE_FLASH_H__ + +#include +#include +#include "nrf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define BLE_FLASH_PAGE_SIZE ((uint16_t)NRF_FICR->CODEPAGESIZE) /**< Size of one flash page. */ +#define BLE_FLASH_MAGIC_NUMBER 0x45DE0000 /**< Magic value to identify if flash contains valid data. */ +#define BLE_FLASH_EMPTY_MASK 0xFFFFFFFF /**< Bit mask that defines an empty address in flash. */ + + +/**@brief Macro for getting the end of the flash available for application. + * + * @details The result flash page number indicates the end boundary of the flash available + * to the application. If a bootloader is used, the end will be the start of the + * bootloader region. Otherwise, the end will be the size of the flash. + */ +#define BLE_FLASH_PAGE_END \ + ((NRF_UICR->NRFFW[0] != BLE_FLASH_EMPTY_MASK) \ + ? (NRF_UICR->NRFFW[0] / BLE_FLASH_PAGE_SIZE) \ + : NRF_FICR->CODESIZE) + +/**@brief Function for erasing the specified flash page, and then writes the given data to this page. + * + * @warning This operation blocks the CPU. DO NOT use while in a connection! + * + * @param[in] page_num Page number to update. + * @param[in] p_in_array Pointer to a RAM area containing the elements to write in flash. + * This area has to be 32 bits aligned. + * @param[in] word_count Number of 32 bits words to write in flash. + * + * @return NRF_SUCCESS on successful flash write, otherwise an error code. + */ +uint32_t ble_flash_page_write(uint8_t page_num, uint32_t * p_in_array, uint8_t word_count); + +/**@brief Function for reading data from flash to RAM. + * + * @param[in] page_num Page number to read. + * @param[out] p_out_array Pointer to a RAM area where the found data will be written. + * This area has to be 32 bits aligned. + * @param[out] p_word_count Number of 32 bits words read. + * + * @return NRF_SUCCESS on successful upload, NRF_ERROR_NOT_FOUND if no valid data has been found + * in flash (first 32 bits not equal to the MAGIC_NUMBER + CRC). + */ +uint32_t ble_flash_page_read(uint8_t page_num, uint32_t * p_out_array, uint8_t * p_word_count); + +/**@brief Function for erasing a flash page. + * + * @note This operation blocks the CPU, so it should not be done while the radio is running! + * + * @param[in] page_num Page number to erase. + * + * @return NRF_SUCCESS on success, an error_code otherwise. + */ +uint32_t ble_flash_page_erase(uint8_t page_num); + +/**@brief Function for writing one word to flash. + * + * @note Flash location to be written must have been erased previously. + * + * @param[in] p_address Pointer to flash location to be written. + * @param[in] value Value to write to flash. + * + * @return NRF_SUCCESS. + */ +uint32_t ble_flash_word_write(uint32_t * p_address, uint32_t value); + +/**@brief Function for writing a data block to flash. + * + * @note Flash locations to be written must have been erased previously. + * + * @param[in] p_address Pointer to start of flash location to be written. + * @param[in] p_in_array Pointer to start of flash block to be written. + * @param[in] word_count Number of words to be written. + * + * @return NRF_SUCCESS. + */ +uint32_t ble_flash_block_write(uint32_t * p_address, uint32_t * p_in_array, uint16_t word_count); + +/**@brief Function for computing pointer to start of specified flash page. + * + * @param[in] page_num Page number. + * @param[out] pp_page_addr Pointer to start of flash page. + * + * @return NRF_SUCCESS. + */ +uint32_t ble_flash_page_addr(uint8_t page_num, uint32_t ** pp_page_addr); + +/**@brief Function for calculating a 16 bit CRC using the CRC-16-CCITT scheme. + * + * @param[in] p_data Pointer to data on which the CRC is to be calculated. + * @param[in] size Number of bytes on which the CRC is to be calculated. + * @param[in] p_crc Initial CRC value (if NULL, a preset value is used as the initial value). + * + * @return Calculated CRC. + */ +uint16_t ble_flash_crc16_compute(uint8_t * p_data, uint16_t size, uint16_t * p_crc); + +/**@brief Function for handling flashing module Radio Notification event. + * + * @note For flash writing to work safely while in a connection or while advertising, this function + * MUST be called from the Radio Notification module's event handler (see + * @ref ble_radio_notification for details). + * + * @param[in] radio_active TRUE if radio is active (or about to become active), FALSE otherwise. + */ +void ble_flash_on_radio_active_evt(bool radio_active); + + +#ifdef __cplusplus +} +#endif + +#endif // BLE_FLASH_H__ + +/** @} */ diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/integration/nrfx/legacy/nrf_drv_twi.h b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/integration/nrfx/legacy/nrf_drv_twi.h index a876a9c812..b0a2cd8946 100644 --- a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/integration/nrfx/legacy/nrf_drv_twi.h +++ b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/integration/nrfx/legacy/nrf_drv_twi.h @@ -135,8 +135,8 @@ typedef struct * @brief Macro for creating a TWI master driver instance. */ // Mbed - need these -#define NRF_TWIM0_ENABLED 1 -#define NRF_TWIM1_ENABLED 1 +//#define NRF_TWIM0_ENABLED 1 +//#define NRF_TWIM1_ENABLED 1 #define NRF_DRV_TWI_INSTANCE(id) NRF_DRV_TWI_INSTANCE_(id) #define NRF_DRV_TWI_INSTANCE_(id) NRF_DRV_TWI_INSTANCE_ ## id diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/integration/nrfx/nrfx_glue.h b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/integration/nrfx/nrfx_glue.h index 464c6ee79b..673b98de9c 100644 --- a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/integration/nrfx/nrfx_glue.h +++ b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/integration/nrfx/nrfx_glue.h @@ -55,7 +55,7 @@ extern "C" { */ // RF - remove the legacy layer... this is preventing driver instances from being generated -//#include +#include #include diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/modules/nrfx/drivers/src/nrfx_uarte.c b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/modules/nrfx/drivers/src/nrfx_uarte.c index 1d5bf090ae..cfe40ba5b0 100644 --- a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/modules/nrfx/drivers/src/nrfx_uarte.c +++ b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/modules/nrfx/drivers/src/nrfx_uarte.c @@ -1,6 +1,6 @@ /** * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA - * + * * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, @@ -566,7 +566,7 @@ static void uarte_irq_handler(NRF_UARTE_Type * p_uarte, } } -#if NRFX_CHECK(NRFX_UARTE0_ENABLED) +#if ( NRFX_CHECK(NRFX_UARTE0_ENABLED) && !(NRFX_CHECK(NRFX_UART0_ENABLED))) void nrfx_uarte_0_irq_handler(void) { uarte_irq_handler(NRF_UARTE0, &m_cb[NRFX_UARTE0_INST_IDX]);