fdsf
Fork of nRF51822 by
Revision 608:d9f8ffc6fc07, committed 2016-01-11
- Comitter:
- vincent.coubard@arm.com
- Date:
- Mon Jan 11 22:33:57 2016 +0000
- Parent:
- 607:e98331f1d6b5
- Child:
- 609:e640f9176975
- Commit message:
- sync with nordic sdk 2.2.0
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/nordic_sdk/components/ble/common/ble_conn_state.c Mon Jan 11 22:33:57 2016 +0000 @@ -0,0 +1,414 @@ +/* + * Copyright (c) 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 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 other + * contributors to this software may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "ble_conn_state.h" +#include <stdbool.h> +#include <stdint.h> +#include <string.h> +#include "ble.h" +#include "sdk_mapped_flags.h" +#include "app_error.h" + + +#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 + + +#define BLE_CONN_STATE_N_DEFAULT_FLAGS 5 /**< The number of flags kept for each connection, excluding user flags. */ +#define BLE_CONN_STATE_N_FLAGS (BLE_CONN_STATE_N_DEFAULT_FLAGS + BLE_CONN_STATE_N_USER_FLAGS) /**< The number of flags kept for each connection, including user flags. */ + + +/**@brief Structure containing all the flag collections maintained by the Connection State module. + */ +typedef struct +{ + sdk_mapped_flags_t valid_flags; /**< Flags indicating which connection handles are valid. */ + sdk_mapped_flags_t connected_flags; /**< Flags indicating which connections are connected, since disconnected connection handles will not immediately be invalidated. */ + sdk_mapped_flags_t central_flags; /**< Flags indicating in which connections the local device is the central. */ + sdk_mapped_flags_t encrypted_flags; /**< Flags indicating which connections are encrypted. */ + sdk_mapped_flags_t mitm_protected_flags; /**< Flags indicating which connections have encryption with protection from man-in-the-middle attacks. */ + sdk_mapped_flags_t user_flags[BLE_CONN_STATE_N_USER_FLAGS]; /**< Flags that can be reserved by the user. The flags will be cleared when a connection is invalidated, otherwise, the user is wholly responsible for the flag states. */ +} ble_conn_state_flag_collections_t; + + +/**@brief Structure containing the internal state of the Connection State module. + */ +typedef struct +{ + uint16_t acquired_flags; /**< Bitmap for keeping track of which user flags have been acquired. */ + uint16_t valid_conn_handles[SDK_MAPPED_FLAGS_N_KEYS]; /**< List of connection handles used as keys for the sdk_mapped_flags module. */ + union + { + ble_conn_state_flag_collections_t flags; /**< Flag collections kept by the Connection State module. */ + sdk_mapped_flags_t flag_array[BLE_CONN_STATE_N_FLAGS]; /**< Flag collections as array to allow use of @ref sdk_mapped_flags_bulk_update_by_key() when setting all flags. */ + }; +} ble_conn_state_t; + + +#if defined(__CC_ARM) + #pragma pop +#elif defined(__ICCARM__) + /* leave anonymous unions enabled */ +#elif defined(__GNUC__) + /* anonymous unions are enabled by default */ +#endif + + +static ble_conn_state_t m_bcs = {0}; /**< Instantiation of the internal state. */ + + +/**@brief Function for resetting all internal memory to the values it had at initialization. + */ +void bcs_internal_state_reset(void) +{ + memset( &m_bcs, 0, sizeof(ble_conn_state_t) ); +} + + +/**@brief Function for activating a connection record. + * + * @param p_record The record to activate. + * @param conn_handle The connection handle to copy into the record. + * @param role The role of the connection. + * + * @return whether the record was activated successfully. + */ +static bool record_activate(uint16_t conn_handle) +{ + uint16_t available_index = sdk_mapped_flags_first_key_index_get(~m_bcs.flags.valid_flags); + + if (available_index != SDK_MAPPED_FLAGS_INVALID_INDEX) + { + m_bcs.valid_conn_handles[available_index] = conn_handle; + sdk_mapped_flags_update_by_key(m_bcs.valid_conn_handles, + &m_bcs.flags.connected_flags, + conn_handle, + 1); + sdk_mapped_flags_update_by_key(m_bcs.valid_conn_handles, + &m_bcs.flags.valid_flags, + conn_handle, + 1); + + return true; + } + + return false; +} + + +/**@brief Function for marking a connection record as invalid and resetting the values. + * + * @param p_record The record to invalidate. + */ +static void record_invalidate(uint16_t conn_handle) +{ + sdk_mapped_flags_bulk_update_by_key(m_bcs.valid_conn_handles, + m_bcs.flag_array, + BLE_CONN_STATE_N_FLAGS, + conn_handle, + 0); +} + + +/**@brief Function for marking a connection as disconnected. See @ref BLE_CONN_STATUS_DISCONNECTED. + * + * @param p_record The record of the connection to set as disconnected. + */ +static void record_set_disconnected(uint16_t conn_handle) +{ + sdk_mapped_flags_update_by_key(m_bcs.valid_conn_handles, + &m_bcs.flags.connected_flags, + conn_handle, + 0); +} + + +/**@brief Function for invalidating records with a @ref BLE_CONN_STATUS_DISCONNECTED + * connection status + */ +static void record_purge_disconnected() +{ + sdk_mapped_flags_key_list_t disconnected_list; + + disconnected_list = sdk_mapped_flags_key_list_get( + m_bcs.valid_conn_handles, + (~m_bcs.flags.connected_flags) & (m_bcs.flags.valid_flags)); + + for (int i = 0; i < disconnected_list.len; i++) + { + record_invalidate(disconnected_list.flag_keys[i]); + } +} + + +/**@brief Function for checking if a user flag has been acquired. + * + * @param[in] flag_id Which flag to check. + * + * @return Whether the flag has been acquired. + */ +static bool user_flag_is_acquired(ble_conn_state_user_flag_id_t flag_id) +{ + return ((m_bcs.acquired_flags & (1 << flag_id)) != 0); +} + + +/**@brief Function for marking a user flag as acquired. + * + * @param[in] flag_id Which flag to mark. + */ +static void user_flag_acquire(ble_conn_state_user_flag_id_t flag_id) +{ + m_bcs.acquired_flags |= (1 << flag_id); +} + + +void ble_conn_state_init(void) +{ + bcs_internal_state_reset(); +} + + +void ble_conn_state_on_ble_evt(ble_evt_t * p_ble_evt) +{ + switch (p_ble_evt->header.evt_id) + { + case BLE_GAP_EVT_CONNECTED: + record_purge_disconnected(); + + if ( !record_activate(p_ble_evt->evt.gap_evt.conn_handle) ) + { + // No more records available. Should not happen. + APP_ERROR_HANDLER(NRF_ERROR_NO_MEM); + } + else + { +#if defined(TARGET_MCU_NRF51_16K_S110) || defined(TARGET_MCU_NRF51_32K_S110) + bool is_central = false; +#elif defined(TARGET_MCU_NRF51_16K_S120) || defined(TARGET_MCU_NRF51_32K_S120) + bool is_central = true; +#else + bool is_central = + (p_ble_evt->evt.gap_evt.params.connected.role == BLE_GAP_ROLE_CENTRAL); +#endif + + sdk_mapped_flags_update_by_key(m_bcs.valid_conn_handles, + &m_bcs.flags.central_flags, + p_ble_evt->evt.gap_evt.conn_handle, + is_central); + } + + break; + + case BLE_GAP_EVT_DISCONNECTED: + record_set_disconnected(p_ble_evt->evt.gap_evt.conn_handle); + break; + + case BLE_GAP_EVT_CONN_SEC_UPDATE: + sdk_mapped_flags_update_by_key( + m_bcs.valid_conn_handles, + &m_bcs.flags.encrypted_flags, + p_ble_evt->evt.gap_evt.conn_handle, + (p_ble_evt->evt.gap_evt.params.conn_sec_update.conn_sec.sec_mode.lv > 1)); + sdk_mapped_flags_update_by_key( + m_bcs.valid_conn_handles, + &m_bcs.flags.mitm_protected_flags, + p_ble_evt->evt.gap_evt.conn_handle, + (p_ble_evt->evt.gap_evt.params.conn_sec_update.conn_sec.sec_mode.lv > 2)); + break; + } +} + + +bool ble_conn_state_valid(uint16_t conn_handle) +{ + return sdk_mapped_flags_get_by_key(m_bcs.valid_conn_handles, + m_bcs.flags.valid_flags, + conn_handle); +} + + +uint8_t ble_conn_state_role(uint16_t conn_handle) +{ + uint8_t role = BLE_GAP_ROLE_INVALID; + + if ( sdk_mapped_flags_get_by_key(m_bcs.valid_conn_handles, m_bcs.flags.valid_flags, conn_handle) ) + { + bool central = sdk_mapped_flags_get_by_key(m_bcs.valid_conn_handles, + m_bcs.flags.central_flags, + conn_handle); + + role = central ? BLE_GAP_ROLE_CENTRAL : BLE_GAP_ROLE_PERIPH; + } + + return role; +} + + +ble_conn_state_status_t ble_conn_state_status(uint16_t conn_handle) +{ + ble_conn_state_status_t conn_status = BLE_CONN_STATUS_INVALID; + bool valid = sdk_mapped_flags_get_by_key(m_bcs.valid_conn_handles, + m_bcs.flags.valid_flags, + conn_handle); + + if (valid) + { + bool connected = sdk_mapped_flags_get_by_key(m_bcs.valid_conn_handles, + m_bcs.flags.connected_flags, + conn_handle); + + conn_status = connected ? BLE_CONN_STATUS_CONNECTED : BLE_CONN_STATUS_DISCONNECTED; + } + + return conn_status; +} + + +bool ble_conn_state_encrypted(uint16_t conn_handle) +{ + return sdk_mapped_flags_get_by_key(m_bcs.valid_conn_handles, + m_bcs.flags.encrypted_flags, + conn_handle); +} + + +bool ble_conn_state_mitm_protected(uint16_t conn_handle) +{ + return sdk_mapped_flags_get_by_key(m_bcs.valid_conn_handles, + m_bcs.flags.mitm_protected_flags, + conn_handle); +} + + +uint32_t ble_conn_state_n_connections(void) +{ + return sdk_mapped_flags_n_flags_set(m_bcs.flags.connected_flags); +} + + +uint32_t ble_conn_state_n_centrals(void) +{ + return sdk_mapped_flags_n_flags_set((m_bcs.flags.central_flags) & (m_bcs.flags.connected_flags)); +} + + +uint32_t ble_conn_state_n_peripherals(void) +{ + return sdk_mapped_flags_n_flags_set((~m_bcs.flags.central_flags) & (m_bcs.flags.connected_flags)); +} + + +sdk_mapped_flags_key_list_t ble_conn_state_conn_handles(void) +{ + return sdk_mapped_flags_key_list_get(m_bcs.valid_conn_handles, m_bcs.flags.valid_flags); +} + + +sdk_mapped_flags_key_list_t ble_conn_state_central_handles(void) +{ + return sdk_mapped_flags_key_list_get(m_bcs.valid_conn_handles, + (m_bcs.flags.central_flags) & (m_bcs.flags.connected_flags)); +} + + +sdk_mapped_flags_key_list_t ble_conn_state_periph_handles(void) +{ + return sdk_mapped_flags_key_list_get(m_bcs.valid_conn_handles, + (~m_bcs.flags.central_flags) & (m_bcs.flags.connected_flags)); +} + + +ble_conn_state_user_flag_id_t ble_conn_state_user_flag_acquire(void) +{ + for (ble_conn_state_user_flag_id_t i = BLE_CONN_STATE_USER_FLAG0; + i < BLE_CONN_STATE_N_USER_FLAGS; + i++) + { + if ( !user_flag_is_acquired(i) ) + { + user_flag_acquire(i); + return i; + } + } + + return BLE_CONN_STATE_USER_FLAG_INVALID; +} + + +bool ble_conn_state_user_flag_get(uint16_t conn_handle, ble_conn_state_user_flag_id_t flag_id) +{ + if (user_flag_is_acquired(flag_id)) + { + return sdk_mapped_flags_get_by_key(m_bcs.valid_conn_handles, + m_bcs.flags.user_flags[flag_id], + conn_handle); + } + else + { + return false; + } +} + + +void ble_conn_state_user_flag_set(uint16_t conn_handle, + ble_conn_state_user_flag_id_t flag_id, + bool value) +{ + if (user_flag_is_acquired(flag_id)) + { + sdk_mapped_flags_update_by_key(m_bcs.valid_conn_handles, + &m_bcs.flags.user_flags[flag_id], + conn_handle, + value); + } +} + + +sdk_mapped_flags_t ble_conn_state_user_flag_collection(ble_conn_state_user_flag_id_t flag_id) +{ + if ( user_flag_is_acquired(flag_id) ) + { + return m_bcs.flags.user_flags[flag_id]; + } + else + { + return 0; + } +} +
--- a/source/nordic_sdk/components/ble/peer_manager/id_manager.h Mon Jan 11 10:29:13 2016 +0000 +++ b/source/nordic_sdk/components/ble/peer_manager/id_manager.h Mon Jan 11 22:33:57 2016 +0000 @@ -229,6 +229,38 @@ */ bool im_address_resolve(ble_gap_addr_t const * p_addr, ble_gap_irk_t const * p_irk); +/**@brief Function for calculating the ah() hash function described in Bluetooth core specification + * 4.2 section 3.H.2.2.2. + * + * @detail BLE uses a hash function to calculate the first half of a resolvable address + * from the second half of the address and an irk. This function will use the ECB + * periferal to hash these data acording to the Bluetooth core specification. + * + * @note The ECB expect little endian input and output. + * This function expect big endian and will reverse the data as necessary. + * + * @param[in] p_k The key used in the hash function. + * For address resolution this is should be the irk. + * The array must have a length of 16. + * @param[in] p_r The rand used in the hash function. For generating a new address + * this would be a random number. For resolving a resolvable address + * this would be the last half of the address being resolved. + * The array must have a length of 3. + * @param[out] p_local_hash The result of the hash operation. For address resolution this + * will match the first half of the address being resolved if and only + * if the irk used in the hash function is the same one used to generate + * the address. + * The array must have a length of 16. + * + * @note ====IMPORTANT==== + * This is a special modification to the original nRF51 SDK required by the mbed BLE API + * to be able to generate BLE private resolvable addresses. This function is used by + * the BLE API implementation for nRF5xSecurityManager::getAddressFromBondTable() in the + * ble-nrf51822 yotta module. + * ================= + */ +void ah(uint8_t const * p_k, uint8_t const * p_r, uint8_t * p_local_hash); + /** @} */ #endif /* PEER_ID_MANAGER_H__ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/nordic_sdk/components/ble/peer_manager/peer_data.c Mon Jan 11 22:33:57 2016 +0000 @@ -0,0 +1,166 @@ +/* + * Copyright (c) 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 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 other + * contributors to this software may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#include "peer_data.h" + +#include <stdint.h> +#include <string.h> +#include "peer_manager_types.h" +#include "fds.h" + + + +void peer_data_parts_get(pm_peer_data_const_t const * p_peer_data, fds_record_chunk_t * p_chunks, uint16_t * p_n_chunks) +{ + if (p_n_chunks == NULL) + { + } + else if ((p_peer_data == NULL) || (p_chunks == NULL)) + { + *p_n_chunks = 0; + } + else + { + switch (p_peer_data->data_type) + { + case PM_PEER_DATA_ID_BONDING: + p_chunks[0].p_data = p_peer_data->data.p_bonding_data; + p_chunks[0].length_words = p_peer_data->length_words; + *p_n_chunks = 1; + break; + case PM_PEER_DATA_ID_SERVICE_CHANGED_PENDING: + p_chunks[0].p_data = p_peer_data->data.p_service_changed_pending; + p_chunks[0].length_words = p_peer_data->length_words; + *p_n_chunks = 1; + break; + case PM_PEER_DATA_ID_GATT_LOCAL: + p_chunks[0].p_data = p_peer_data->data.p_local_gatt_db; + p_chunks[0].length_words = PM_N_WORDS(PM_LOCAL_DB_LEN_OVERHEAD_BYTES); + p_chunks[1].p_data = p_peer_data->data.p_local_gatt_db->p_data; + p_chunks[1].length_words = p_peer_data->length_words - p_chunks[0].length_words; + *p_n_chunks = 2; + break; + case PM_PEER_DATA_ID_GATT_REMOTE: + p_chunks[0].p_data = p_peer_data->data.p_remote_gatt_db; + p_chunks[0].length_words = PM_N_WORDS(PM_REMOTE_DB_LEN_OVERHEAD_BYTES); + p_chunks[1].p_data = p_peer_data->data.p_remote_gatt_db->p_data; + p_chunks[1].length_words = p_peer_data->length_words - p_chunks[0].length_words; + *p_n_chunks = 2; + break; + case PM_PEER_DATA_ID_APPLICATION: + p_chunks[0].p_data = p_peer_data->data.p_application_data; + p_chunks[0].length_words = p_peer_data->length_words; + *p_n_chunks = 1; + break; + default: + *p_n_chunks = 0; + break; + } + } +} + + +ret_code_t peer_data_deserialize(pm_peer_data_flash_t const * p_in_data, pm_peer_data_t * p_out_data) +{ + if ((p_in_data == NULL) || (p_out_data == NULL)) + { + return NRF_ERROR_NULL; + } + else + { + if (p_out_data->length_words < p_in_data->length_words) + { + p_out_data->length_words = p_in_data->length_words; + return NRF_ERROR_NO_MEM; + } + p_out_data->length_words = p_in_data->length_words; + p_out_data->data_type = p_in_data->data_type; + + switch (p_in_data->data_type) + { + case PM_PEER_DATA_ID_BONDING: + *p_out_data->data.p_bonding_data = *p_in_data->data.p_bonding_data; + break; + case PM_PEER_DATA_ID_SERVICE_CHANGED_PENDING: + *p_out_data->data.p_service_changed_pending = *p_in_data->data.p_service_changed_pending; + break; + case PM_PEER_DATA_ID_GATT_LOCAL: + if (p_out_data->data.p_local_gatt_db->p_data == NULL) + { + return NRF_ERROR_NULL; + } + if (p_out_data->data.p_local_gatt_db->len < p_in_data->data.p_local_gatt_db->len) + { + p_out_data->data.p_local_gatt_db->len = p_in_data->data.p_local_gatt_db->len; + return NRF_ERROR_NO_MEM; + } + else + { + p_out_data->data.p_local_gatt_db->flags = p_in_data->data.p_local_gatt_db->flags; + p_out_data->data.p_local_gatt_db->len = p_in_data->data.p_local_gatt_db->len; + memcpy(p_out_data->data.p_local_gatt_db->p_data, + p_in_data->data.p_local_gatt_db->p_data, + p_in_data->data.p_local_gatt_db->len); + } + break; + case PM_PEER_DATA_ID_GATT_REMOTE: + if (p_out_data->data.p_remote_gatt_db->p_data == NULL) + { + return NRF_ERROR_NULL; + } + if (p_out_data->data.p_remote_gatt_db->service_count < p_in_data->data.p_remote_gatt_db->service_count) + { + p_out_data->data.p_remote_gatt_db->service_count = p_in_data->data.p_remote_gatt_db->service_count; + return NRF_ERROR_NO_MEM; + } + else + { + p_out_data->data.p_remote_gatt_db->service_count = p_in_data->data.p_remote_gatt_db->service_count; + memcpy(p_out_data->data.p_remote_gatt_db->p_data, + p_in_data->data.p_remote_gatt_db->p_data, + p_in_data->data.p_remote_gatt_db->service_count * sizeof(ble_gatt_db_srv_t)); + } + break; + case PM_PEER_DATA_ID_APPLICATION: + memcpy(p_out_data->data.p_application_data, + p_in_data->data.p_application_data, + p_in_data->length_words * 4); + break; + default: + break; + } + } + return NRF_SUCCESS; +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/nordic_sdk/components/ble/peer_manager/peer_data.h Mon Jan 11 22:33:57 2016 +0000 @@ -0,0 +1,73 @@ +/* + * Copyright (c) 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 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 other + * contributors to this software may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#ifndef PEER_DATA_H__ +#define PEER_DATA_H__ + +#include <stdint.h> +#include "peer_manager_types.h" +#include "fds.h" + + +/** + * @defgroup peer_data Peer Data + * @ingroup peer_manager + * @{ + * @brief An internal module of @ref peer_manager. This module defines the structure of the data + * that is managed by the @ref peer_manager. It also provides functions for parsing the data. + */ + + +/**@brief Function for enumerating the separate (non-contiguous) parts of the peer data. + * + * @param[in] p_peer_data The peer data to enumerate. + * @param[out] p_chunks The resulting chunks. This must be an array of at least 2 elements. + * @param[out] p_n_chunks The number of chunks. If this is 0, something went wrong. + */ +void peer_data_parts_get(pm_peer_data_const_t const * p_peer_data, fds_record_chunk_t * p_chunks, uint16_t * p_n_chunks); + + +/**@brief Function for converting @ref pm_peer_data_flash_t into @ref pm_peer_data_t. + * + * @param[in] p_in_data The source data. + * @param[out] p_out_data The target data structure. + * + * @retval NRF_SUCCESS Successful conversion. + * @retval NRF_ERROR_NULL A parameter was NULL. + * @retval NRF_ERROR_NO_MEM A buffer was not large enough. + */ +ret_code_t peer_data_deserialize(pm_peer_data_flash_t const * p_in_data, pm_peer_data_t * p_out_data); + +/** @} */ + +#endif /* PEER_DATA_H__ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/nordic_sdk/components/ble/peer_manager/peer_data_storage.c Mon Jan 11 22:33:57 2016 +0000 @@ -0,0 +1,688 @@ +/* + * Copyright (c) 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 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 other + * contributors to this software may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#include "peer_data_storage.h" + +#include <stdint.h> +#include <string.h> +#include "sdk_errors.h" +#include "peer_manager_types.h" +#include "peer_id.h" +#include "peer_data.h" +#include "fds.h" + +#define MAX_REGISTRANTS 6 /**< The number of user that can register with the module. */ + +#define MODULE_INITIALIZED (m_pds.n_registrants > 0) /**< Expression which is true when the module is initialized. */ + +/**@brief Macro for verifying that the module is initialized. It will cause the function to return + * @ref NRF_ERROR_INVALID_STATE if not. + */ +#define VERIFY_MODULE_INITIALIZED() \ +do \ +{ \ + if (!MODULE_INITIALIZED) \ + { \ + return NRF_ERROR_INVALID_STATE; \ + } \ +} while(0) + + +/**@brief Macro for verifying that the module is initialized. It will cause the function to return + * if not. + */ +#define VERIFY_MODULE_INITIALIZED_VOID() \ +do \ +{ \ + if (!MODULE_INITIALIZED) \ + { \ + return; \ + } \ +} while(0) + + +/**@brief Macro for verifying that the param is not NULL. It will cause the function to return + * if not. + * + * @param[in] param The variable to check if is NULL. + */ +#define VERIFY_PARAM_NOT_NULL(param) \ +do \ +{ \ + if (param == NULL) \ + { \ + return NRF_ERROR_NULL; \ + } \ +} while(0) + + +/**@brief Macro for verifying that param is not zero. It will cause the function to return + * if not. + * + * @param[in] param The variable to check if is zero. + */ +#define VERIFY_PARAM_NOT_ZERO(param) \ +do \ +{ \ + if (param == 0) \ + { \ + return NRF_ERROR_NULL; \ + } \ +} while(0) + + +/**@brief Macro for verifying that the peer id is within a valid range + * + * @param[in] id The peer data id to check. + */ +#define VERIFY_PEER_ID_IN_RANGE(id) \ +do \ +{ \ + if ((id >= PM_PEER_ID_N_AVAILABLE_IDS)) \ + { \ + return NRF_ERROR_INVALID_PARAM; \ + } \ +} while (0) + + +/**@brief Macro for verifying that the peer data id is withing a valid range + * + * @param[in] id The peer data id to check. + */ +#define VERIFY_PEER_DATA_ID_IN_RANGE(id) \ +do \ +{ \ + if (!PM_PEER_DATA_ID_IS_VALID(id)) \ + { \ + return NRF_ERROR_INVALID_PARAM; \ + } \ +} while (0) + + +#define PEER_IDS_INITIALIZE() \ +do \ +{ \ + if (!m_pds.peer_ids_initialized) \ + { \ + peer_ids_init(); \ + } \ +} while (0) + + +typedef struct +{ + bool peer_ids_initialized; + pds_evt_handler_t evt_handlers[MAX_REGISTRANTS]; + uint8_t n_registrants; +} pds_t; + +static pds_t m_pds = {.n_registrants = 0}; + +static void internal_state_reset(pds_t * p_pds) +{ + memset(p_pds, 0, sizeof(pds_t)); +} + +/**@brief Function for dispatching outbound events to all registered event handlers. + * + * @param[in] p_event The event to dispatch. + */ +static void pds_evt_send(pds_evt_t * p_event) +{ + for (int i = 0; i < m_pds.n_registrants; i++) + { + m_pds.evt_handlers[i](p_event); + } +} + +/**@brief Function to convert peer id to instance id + * + * @param[in] peer_id Peer id to convert to instance id + * + * @return Value as instance id + */ +static fds_instance_id_t convert_peer_id_to_instance_id(pm_peer_id_t peer_id) +{ + return (fds_instance_id_t)(peer_id + peer_id_to_instance_id); +} + +/**@brief Function to convert peer data id to type id + * + * @param[in] peer_data_id Peer data id to convert to type_id + * + * @return Value as type id + */ +static fds_type_id_t convert_peer_data_id_to_type_id(pm_peer_data_id_t peer_data_id) +{ + return (fds_type_id_t)peer_data_id + (fds_type_id_t)peer_data_id_to_type_id; +} + + +/**@brief Function to convert peer data id to type id + * + * @param[in] peer_data_id Peer data id to convert to type_id + * + * @return Value as type id + */ +static pm_peer_id_t convert_instance_id_to_peer_id(fds_instance_id_t instance_id) +{ + return (pm_peer_id_t)(instance_id + instance_id_to_peer_id); +} + + +/**@brief Function to type id to peer data id + * + * @param[in] type_id Type id to convert to peer data id + * + * @return Value as peer data id + */ +static pm_peer_data_id_t convert_type_id_to_peer_data_id(fds_type_id_t type_id) +{ + return (pm_peer_data_id_t)(type_id + instance_id_to_peer_id); +} + + +static ret_code_t find_fds_item(pm_peer_id_t peer_id, + pm_peer_data_id_t data_id, + fds_record_desc_t * const p_desc) +{ + fds_find_token_t find_tok; + + VERIFY_PEER_ID_IN_RANGE(peer_id); + VERIFY_PEER_DATA_ID_IN_RANGE(data_id); + // pp_record verified outside + + fds_type_id_t type_id = convert_peer_data_id_to_type_id(data_id); + fds_instance_id_t instance_id = convert_peer_id_to_instance_id(peer_id); + + return fds_find(type_id, instance_id, p_desc, &find_tok); +} + + +static void peer_ids_init() +{ + fds_record_t record; + fds_record_desc_t record_desc; + fds_find_token_t find_tok; + fds_type_id_t const type_id = convert_peer_data_id_to_type_id(PM_PEER_DATA_ID_BONDING); + pm_peer_id_t peer_id; + + if (!m_pds.peer_ids_initialized) + { + while(fds_find_by_type(type_id, &record_desc, &find_tok) == NRF_SUCCESS) + { + fds_open(&record_desc, &record); + fds_close(&record_desc); + peer_id = convert_instance_id_to_peer_id(record.header.ic.instance); + peer_id_allocate(peer_id); + } + + m_pds.peer_ids_initialized = true; + } +} + +//uint32_t size_pad_to_mult_of_four(uint32_t unpadded_size) +//{ +// return (unpadded_size + 3) & 3; +//} + +static void fds_evt_handler(ret_code_t result, + fds_cmd_id_t cmd, + fds_record_id_t record_id, + fds_record_key_t record_key + /*fds_record_t const * const p_record*/) +{ + pds_evt_t evt; + switch(cmd) + { + case FDS_CMD_INIT: + + break; + + case FDS_CMD_UPDATE: + case FDS_CMD_WRITE: + evt.peer_id = convert_instance_id_to_peer_id(record_key.instance); + evt.evt_id = (result == NRF_SUCCESS) ? PDS_EVT_STORED : PDS_EVT_ERROR_STORE; + evt.data_id = convert_type_id_to_peer_data_id(record_key.type); + evt.store_token = record_id; + pds_evt_send(&evt); + break; + + case FDS_CMD_CLEAR: + evt.peer_id = convert_instance_id_to_peer_id(record_key.instance); + evt.evt_id = (result == NRF_SUCCESS) ? PDS_EVT_CLEARED : PDS_EVT_ERROR_CLEAR; + evt.data_id = convert_type_id_to_peer_data_id(record_key.type); + evt.store_token = record_id; + pds_evt_send(&evt); + break; + + case FDS_CMD_CLEAR_INST: + { + if ((record_key.type == FDS_TYPE_ID_INVALID) && + (record_key.instance != FDS_TYPE_ID_INVALID)) + { + pm_peer_id_t peer_id = convert_instance_id_to_peer_id(record_key.instance); + + evt.peer_id = peer_id; + evt.data_id = PM_PEER_DATA_ID_INVALID; + if (result == NRF_SUCCESS) + { + evt.evt_id = PDS_EVT_PEER_ID_CLEAR; + peer_id_free(peer_id); + } + else + { + evt.evt_id = PDS_EVT_ERROR_PEER_ID_CLEAR; + } + } + else + { + // TODO: Not supported yet (clear many without clearing peer_id) + } + + pds_evt_send(&evt); + } + break; + + case FDS_CMD_GC: + evt.peer_id = convert_instance_id_to_peer_id(record_key.instance); + evt.evt_id = PDS_EVT_COMPRESSED; + evt.data_id = convert_type_id_to_peer_data_id(record_key.type); + evt.store_token = record_id; + pds_evt_send(&evt); + break; + + default: + + break; + } +} + + +ret_code_t pds_register(pds_evt_handler_t evt_handler) +{ + if (m_pds.n_registrants >= MAX_REGISTRANTS) + { + return NRF_ERROR_NO_MEM; + } + + VERIFY_PARAM_NOT_NULL(evt_handler); + + if (!MODULE_INITIALIZED) + { + ret_code_t retval; + internal_state_reset(&m_pds); + peer_id_init(); + + fds_cb_t cb = fds_evt_handler; + retval = fds_register(cb); + if(retval != NRF_SUCCESS) + { + return retval; + } + + retval = fds_init(); + if(retval != NRF_SUCCESS) + { + return retval; + } + } + + m_pds.evt_handlers[m_pds.n_registrants] = evt_handler; + m_pds.n_registrants += 1; + + return NRF_SUCCESS; + +} + + +ret_code_t pds_peer_data_read_ptr_get(pm_peer_id_t peer_id, + pm_peer_data_id_t data_id, + pm_peer_data_flash_t * p_data, + pm_store_token_t * p_token) +{ + ret_code_t retval; + + fds_record_t record; + fds_record_desc_t record_desc; + + VERIFY_MODULE_INITIALIZED(); + VERIFY_PEER_ID_IN_RANGE(peer_id); + VERIFY_PEER_DATA_ID_IN_RANGE(data_id); + + retval = find_fds_item(peer_id, data_id, &record_desc); + if (retval != NRF_SUCCESS) + { + return retval; + } + + // Shouldn't fail, unless record is cleared. + fds_open(&record_desc, &record); + // No need to keep it open, since we are not reading. + fds_close(&record_desc); + + //NRF_LOG_PRINTF("Found item with peer_id: %d, data_id: %d, Address: %p\r\n", record.p_data); + + if (p_data != NULL) + { + p_data->data_type = data_id; + p_data->length_words = record.header.tl.length_words; + + p_data->data.p_application_data = (uint8_t const*)record.p_data; + } + + if (p_token != NULL) + { + *p_token = (uint32_t)record.header.id; + } + + return retval; +} + + +ret_code_t pds_peer_data_lock(pm_store_token_t store_token) +{ + VERIFY_MODULE_INITIALIZED(); + VERIFY_PARAM_NOT_ZERO(store_token); + + // TODO: Not implemented yet in fds + + return NRF_SUCCESS; +} + + +ret_code_t pds_peer_data_verify(pm_store_token_t store_token) +{ + VERIFY_MODULE_INITIALIZED(); + VERIFY_PARAM_NOT_ZERO(store_token); + + // TODO: Not implemented yet in fds + + return NRF_SUCCESS; +} + + +ret_code_t pds_peer_data_read(pm_peer_id_t peer_id, + pm_peer_data_id_t data_id, + pm_peer_data_t * p_data, + fds_length_t * p_len_words) +{ + VERIFY_PEER_ID_IN_RANGE(peer_id); + VERIFY_PEER_DATA_ID_IN_RANGE(data_id); + VERIFY_PARAM_NOT_NULL(p_len_words); + VERIFY_PARAM_NOT_NULL(p_data); + + ret_code_t err_code; + pm_peer_data_flash_t peer_data_flash; + + err_code = pds_peer_data_read_ptr_get(peer_id, data_id, &peer_data_flash, NULL); + + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + if ((*p_len_words) == 0) + { + (*p_len_words) = peer_data_flash.length_words; + return NRF_SUCCESS; + } + else if ((*p_len_words) < peer_data_flash.length_words) + { + return NRF_ERROR_NO_MEM; + } + + VERIFY_PARAM_NOT_NULL(p_data->data.p_application_data); + + err_code = peer_data_deserialize(&peer_data_flash, p_data); + + return err_code; +} + + +ret_code_t pds_peer_data_write_prepare(pm_peer_data_const_t const * p_peer_data, + pm_prepare_token_t * p_prepare_token) +{ + ret_code_t retval; + + VERIFY_MODULE_INITIALIZED(); + VERIFY_PARAM_NOT_NULL(p_peer_data); + VERIFY_PARAM_NOT_NULL(p_prepare_token); + VERIFY_PEER_DATA_ID_IN_RANGE(p_peer_data->data_type); + + retval = fds_reserve((fds_write_token_t*)p_prepare_token, p_peer_data->length_words); + return retval; +} + + +ret_code_t pds_peer_data_write_prepare_cancel(pm_prepare_token_t prepare_token) +{ + ret_code_t retval; + + VERIFY_MODULE_INITIALIZED(); + VERIFY_PARAM_NOT_ZERO(prepare_token); + + retval = fds_reserve_cancel((fds_write_token_t*)&prepare_token); + return retval; +} + + +ret_code_t pds_peer_data_write_prepared(pm_peer_id_t peer_id, + pm_peer_data_const_t const * p_peer_data, + pm_prepare_token_t prepare_token, + pm_store_token_t * p_store_token) +{ + ret_code_t retval; + fds_record_desc_t record_desc; + fds_record_key_t record_key; + fds_record_chunk_t chunks[2]; + uint16_t n_chunks; + + VERIFY_MODULE_INITIALIZED(); + //VERIFY_PARAM_NOT_ZERO(prepare_token); + VERIFY_PARAM_NOT_NULL(p_peer_data); + VERIFY_PEER_ID_IN_RANGE(peer_id); + VERIFY_PEER_DATA_ID_IN_RANGE(p_peer_data->data_type); + + // Fill in the keys. + record_key.type = convert_peer_data_id_to_type_id(p_peer_data->data_type); + record_key.instance = convert_peer_id_to_instance_id(peer_id); + + // Create chunks. + peer_data_parts_get(p_peer_data, chunks, &n_chunks); + + retval = fds_write_reserved((fds_write_token_t*)&prepare_token, &record_desc, + record_key, n_chunks, chunks); + + if ((retval == NRF_SUCCESS) && (p_store_token != NULL)) + { + fds_record_id_from_desc(&record_desc, (fds_record_id_t*)p_store_token); + } + + return retval; +} + + +ret_code_t pds_peer_data_write(pm_peer_id_t peer_id, + pm_peer_data_const_t const * p_peer_data, + pm_store_token_t * p_store_token) +{ + ret_code_t retval; + fds_record_desc_t record_desc; + fds_record_key_t record_key; + fds_record_chunk_t chunks[2]; + uint16_t n_chunks; + + VERIFY_MODULE_INITIALIZED(); + VERIFY_PEER_ID_IN_RANGE(peer_id); + VERIFY_PEER_DATA_ID_IN_RANGE(p_peer_data->data_type); + + // Fill in the keys. + record_key.type = convert_peer_data_id_to_type_id(p_peer_data->data_type); + record_key.instance = convert_peer_id_to_instance_id(peer_id); + + // Create chunks + peer_data_parts_get(p_peer_data, chunks, &n_chunks); + + // Request write + retval = fds_write(&record_desc, record_key, n_chunks, chunks); + + if ((retval == NRF_SUCCESS) && (p_store_token != NULL)) + { + fds_record_id_from_desc(&record_desc, (fds_record_id_t*)p_store_token); + } + + return retval; +} + + +ret_code_t pds_peer_data_update(pm_peer_id_t peer_id, + pm_peer_data_const_t const * p_peer_data, + pm_store_token_t old_token, + pm_store_token_t * p_store_token) +{ + ret_code_t retval; + fds_record_desc_t record_desc; + fds_record_key_t record_key; + fds_record_chunk_t chunks[2]; + uint16_t n_chunks; + + VERIFY_MODULE_INITIALIZED(); + VERIFY_PEER_DATA_ID_IN_RANGE(p_peer_data->data_type); + VERIFY_PARAM_NOT_NULL(p_peer_data); + + record_key.type = convert_peer_data_id_to_type_id(p_peer_data->data_type); + record_key.instance = convert_peer_id_to_instance_id(peer_id); + + // Create chunks + peer_data_parts_get(p_peer_data, chunks, &n_chunks); + + fds_descriptor_from_rec_id(&record_desc, (fds_record_id_t)old_token); + + retval = fds_update(&record_desc, record_key, n_chunks, chunks); + + if ((retval == NRF_SUCCESS) && (p_store_token != NULL)) + { + fds_record_id_from_desc(&record_desc, (fds_record_id_t*)p_store_token); + } + + return retval; +} + +ret_code_t pds_peer_data_clear(pm_peer_id_t peer_id, pm_peer_data_id_t data_id) +{ + ret_code_t retval; + fds_type_id_t type_id; + fds_instance_id_t instance_id; + fds_record_desc_t record_desc; + fds_find_token_t find_tok; + + VERIFY_MODULE_INITIALIZED(); + VERIFY_PEER_ID_IN_RANGE(peer_id); + VERIFY_PEER_DATA_ID_IN_RANGE(data_id); + + type_id = convert_peer_data_id_to_type_id(data_id); + instance_id = convert_peer_id_to_instance_id(peer_id); + + retval = fds_find(type_id, instance_id, &record_desc, &find_tok); + if(retval != NRF_SUCCESS) + { + return retval; + } + + retval = fds_clear(&record_desc); + return retval; +} + + +pm_peer_id_t pds_peer_id_allocate(void) +{ + if (!MODULE_INITIALIZED) + { + return PM_PEER_ID_INVALID; + } + PEER_IDS_INITIALIZE(); + return peer_id_allocate(PM_PEER_ID_INVALID); +} + + +ret_code_t pds_peer_id_free(pm_peer_id_t peer_id) +{ + ret_code_t retval; + fds_instance_id_t instance_id; + + VERIFY_MODULE_INITIALIZED(); + VERIFY_PEER_ID_IN_RANGE(peer_id); + PEER_IDS_INITIALIZE(); + + instance_id = convert_peer_id_to_instance_id(peer_id); + + retval = fds_clear_by_instance(instance_id); + return retval; +} + + +bool pds_peer_id_is_allocated(pm_peer_id_t peer_id) +{ + if (!MODULE_INITIALIZED) + { + return false; + } + PEER_IDS_INITIALIZE(); + + return peer_id_is_allocated(peer_id); +} + + +pm_peer_id_t pds_next_peer_id_get(pm_peer_id_t prev_peer_id) +{ + if (!MODULE_INITIALIZED) + { + return PM_PEER_ID_INVALID; + } + PEER_IDS_INITIALIZE(); + + return peer_id_next_id_get(prev_peer_id); +} + + +uint32_t pds_n_peers(void) +{ + if (!MODULE_INITIALIZED) + { + return 0; + } + PEER_IDS_INITIALIZE(); + return peer_id_n_ids(); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/nordic_sdk/components/ble/peer_manager/peer_data_storage.h Mon Jan 11 22:33:57 2016 +0000 @@ -0,0 +1,370 @@ +/* + * Copyright (c) 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 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 other + * contributors to this software may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#ifndef PEER_DATA_STORAGE_H__ +#define PEER_DATA_STORAGE_H__ + + +#include "stdint.h" +#include "sdk_errors.h" +#include "ble_gap.h" +#include "peer_manager_types.h" +#include "fds.h" + + +/** + * @defgroup peer_data_storage Peer Data Storage + * @ingroup peer_manager + * @{ + * @brief An internal module of @ref peer_manager. This module provides a Peer Manager-specific API + * to the persistent storage. + */ + +#define PDS_PREPARE_TOKEN_INVALID 0 +#define PDS_STORE_TOKEN_INVALID 0 + + +typedef enum +{ + peer_id_to_instance_id = 16384, + instance_id_to_peer_id = -peer_id_to_instance_id, + peer_data_id_to_type_id = 32768, + type_id_to_peer_data_id = -peer_data_id_to_type_id, +} pds_convert_t; + + +/**@brief The types of events that can come from the peer_data_storage module. + */ +typedef enum +{ + PDS_EVT_STORED, /**< The specified data has been successfully stored. */ + PDS_EVT_CLEARED, /**< The specified data has been successfully cleared. */ + PDS_EVT_PEER_ID_CLEAR, /**< The peer id has been successfully cleared. */ + PDS_EVT_ERROR_STORE, /**< The specified data could not be stored. */ + PDS_EVT_ERROR_CLEAR, /**< The specified data could not be cleared. */ + PDS_EVT_ERROR_PEER_ID_CLEAR, /**< The peer id has been successfully cleared. */ + PDS_EVT_COMPRESSED, /**< A compress procedure has finished successfully. */ +} pds_evt_id_t; + + +/**@brief Events that can come from the peer_data_storage module. + */ +typedef struct +{ + pds_evt_id_t evt_id; /**< The type of event. */ + pm_peer_id_t peer_id; /**< The peer the event pertains to. */ + pm_peer_data_id_t data_id; /**< The data the event pertains to. */ + pm_store_token_t store_token; +} pds_evt_t; + + +/**@brief Event handler for events from the peer_data_storage module. + * + * @param[in] event The event that has happened. + * @param[in] peer_id The id of the peer the event pertains to. + * @param[in] flags The data the event pertains to. + */ +typedef void (*pds_evt_handler_t)(pds_evt_t const * p_event); + + +/**@brief Function for registering for events from the peer database. + * + * @note This function will initialize the module if it is not already initialized. + * + * @param[in] evt_handler Event handler to register. + * + * @retval NRF_SUCCESS Registration successful. + * @retval NRF_ERROR_NO_MEM No more event handlers can be registered. + * @retval NRF_ERROR_NULL evt_handler was NULL. + * @retval NRF_ERROR_INVALID_PARAM Unexpected return code from @ref pm_buffer_init. + * @retval NRF_ERROR_INVALID_STATE FDS has not been initalized. + */ +ret_code_t pds_register(pds_evt_handler_t evt_handler); + + +#if 0 +/**@brief Function for initializing Peer Data storage and registering a + * callback for its events. + * + * @param[in] evt_handler Event handler to register. + * + * @retval NRF_SUCCESS Registration successful. + * @retval NRF_ERROR_NO_MEM No more event handlers can be registered. + * @retval NRF_ERROR_NULL evt_handler was NULL. + * @retval NRF_ERROR_INVALID_STATE FDS has not completed initialization. + */ +ret_code_t pds_init(pds_evt_handler_t evt_handler); +#endif + +/**@brief Function for retrieving a direct pointer to peer data in persistent storage. + * + * @param[in] peer_id The id of the peer whose data to read. + * @param[in] data_id Which data to get. + * @param[out] p_data The peer data pointer. + * @param[out] p_token Token that can be used to lock data in flash and check data validity. + * + * @retval NRF_SUCCESS The pointer was successfully retrieved. + * @retval NRF_ERROR_INVALID_PARAM Invalid data_id. + * @retval NRF_ERROR_NULL p_data was NULL. + * @retval NRF_ERROR_NOT_FOUND The requested data was not found in persistent storage. + * @retval NRF_ERROR_INVALID_STATE Module is not initialized. + */ +ret_code_t pds_peer_data_read_ptr_get(pm_peer_id_t peer_id, + pm_peer_data_id_t data_id, + pm_peer_data_flash_t * p_data, + pm_store_token_t * p_token); + +/**@brief Function to lock the flash data (to defer compression from invalidating data) + * + * @param[in] store_token The token representing the item to lock + * + */ +ret_code_t pds_peer_data_lock(pm_store_token_t store_token); + + +/**@brief Function to verify flash data integrity + * + * @param[in] store_token The token representing the item to lock + * + * @retval NRF_SUCCESS The data integrity is valid. + * @retval NRF_ERROR_NULL The token is invalid. + * @retval NRF_ERROR_INVALID_DATA The data integrity is not valid. + * @retval NRF_ERROR_INVALID_STATE Module is not initialized. + */ +ret_code_t pds_peer_data_verify(pm_store_token_t store_token); + + +/**@brief Function for retrieving peer data from persistent storage by making a copy + * + * @param[in] peer_id The id of the peer whose data to read. + * @param[in] data_id Which piece of data to read. + * @param[out] p_data Pointer to the peer data. + * @param[in,out] p_len_words Length available to copy to (in words). + * If set to NULL, then no copy will be made and the + * length will be reflected in p_len_words after the call returns. + * + * @retval NRF_SUCCESS The read was successful. + * @retval NRF_ERROR_INVALID_PARAM Invalid data_id. + * @retval NRF_ERROR_NULL data contained a NULL pointer. + * @retval NRF_ERROR_NOT_FOUND The requested data was not found in persistent storage. + * @retval NRF_ERROR_NO_MEM The length of stored data too large to copy out + * @retval NRF_ERROR_INVALID_STATE Module is not initialized. + */ +ret_code_t pds_peer_data_read(pm_peer_id_t peer_id, + pm_peer_data_id_t data_id, + pm_peer_data_t * p_data, + fds_length_t * p_len_words); + + +/**@brief Function for preparing persistent storage for a write. + * + * @details If this call succeeds, space is reserved in persistent storage, so the write will fit. + * + * @note If space has already been prepared for this peer_id/data_id pair, no new space will be + * reserved, unless the previous reservation had too small size. + * + * @param[in] p_peer_data Data to prepare for. The data needs not be ready, but length and type + * values must. + * @param[out] p_prepare_token A token identifying the prepared memory area. + * + * @retval NRF_SUCCESS The call was successful. + * @retval NRF_ERROR_INVALID_PARAM Invalid data ID. + * @retval NRF_ERROR_INVALID_LENGTH Data length above the maximum allowed. + * @retval NRF_ERROR_NO_MEM No space available in persistent storage. + * @retval NRF_ERROR_INVALID_STATE Module is not initialized. + */ +ret_code_t pds_peer_data_write_prepare(pm_peer_data_const_t const * p_peer_data, + pm_prepare_token_t * p_prepare_token); + + +/**@brief Function for undoing a previous call to @ref pds_peer_data_write_prepare. + * + * @param[in] prepare_token A token identifying the prepared memory area to cancel. + * + * @retval NRF_SUCCESS The call was successful. + * @retval NRF_ERROR_NOT_FOUND Invalid peer ID and/or prepare token. + * @retval NRF_ERROR_INVALID_STATE Module is not initialized. + */ +ret_code_t pds_peer_data_write_prepare_cancel(pm_prepare_token_t prepare_token); + + +/**@brief Function for writing prepared (reserved) peer data to persistent storage. + * + * @details Writing happens asynchronously. Expect a @ref PDS_EVT_STORED or @ref PDS_EVT_ERROR_STORE + * event. + * + * @param[in] peer_id The id of the peer the data pertains to. + * @param[in] p_peer_data The peer data. + * @param[in] prepare_token A token identifying the prepared memory area to write into. If + * the prepare token is invalid, e.g. PDS_PREPARE_TOKEN_INVALID, the + * prepare/write sequence will happen atomically. + * @param[out] p_store_token A token identifying this particular store operation. The token can be + * used to identify events pertaining to this operation. + * + * @retval NRF_SUCCESS The write was initiated successfully. + * @retval NRF_ERROR_INVALID_PARAM Invalid data ID or store_flags. + * @retval NRF_ERROR_INVALID_LENGTH Length of data longer than in prepare call. + * @retval NRF_ERROR_NULL data contained a NULL pointer. + * @retval NRF_ERROR_NO_MEM No space available in persistent storage. This can only happen + * if p_prepare_token is NULL. + * @retval NRF_ERROR_BUSY FDS or underlying modules are busy and can't take any + * more requests + * @retval NRF_ERROR_INVALID_STATE Module is not initialized. + */ +ret_code_t pds_peer_data_write_prepared(pm_peer_id_t peer_id, + pm_peer_data_const_t const * p_peer_data, + pm_prepare_token_t prepare_token, + pm_store_token_t * p_store_token); + + +/**@brief Function for writing peer data to persistent storage. + * + * @details Writing happens asynchronously. Expect a @ref PDS_EVT_STORED or @ref PDS_EVT_ERROR_STORE + * event. + * + * @param[in] peer_id The id of the peer the data pertains to. + * @param[in] p_peer_data The peer data. + * @param[out] p_store_token A token identifying this particular store operation. The token can be + * used to identify events pertaining to this operation. + * + * @retval NRF_SUCCESS The write was initiated successfully. + * @retval NRF_ERROR_INVALID_PARAM Invalid data ID or store_flags. + * @retval NRF_ERROR_NULL Data contained a NULL pointer. + * @retval NRF_ERROR_NO_MEM No space available in persistent storage. This can only happen + * if p_prepare_token is NULL. + * @retval NRF_ERROR_BUSY FDS or underlying modules are busy and can't take any + * more requests + * @retval NRF_ERROR_INVALID_STATE Module is not initialized. + */ +ret_code_t pds_peer_data_write(pm_peer_id_t peer_id, + pm_peer_data_const_t const * p_peer_data, + pm_store_token_t * p_store_token); + + +/**@brief Function for updating currently stored peer data to a new version + * + * @details Updating happens asynchronously. + * Expect a @ref PDS_EVT_STORED or @ref PDS_EVT_ERROR_STORE for the store token + * and a @ref PDS_EVT_ERROR_CLEAR or @ref PDS_EVT_ERROR_CLEAR for the old token + * + * @param[in] peer_id The peer which the data is associated to. + * @param[in] peer_data New data. + * @param[in] old_token Store token for the old data. + * @param[out] p_store_token Store token for the new data. + * + * @retval NRF_SUCESS The update was initiated successfully + * @retval NRF_ERROR_NOT_FOUND The old store token was invalid. + * @retval NRF_ERROR_NULL Data contained a NULL pointer. + * @retval NRF_ERROR_NO_MEM No space available in persistent storage. + * @retval NRF_ERROR_BUSY FDS or underlying modules are busy and can't take any + * more requests + * @retval NRF_ERROR_INVALID_STATE Module is not initialized. + */ +ret_code_t pds_peer_data_update(pm_peer_id_t peer_id, + pm_peer_data_const_t const * peer_data, + pm_store_token_t old_token, + pm_store_token_t * p_store_token); + + +/**@brief Function for clearing peer data from persistent storage. + * + * @details Clearing happens asynchronously. Expect a @ref PDS_EVT_CLEARED or @ref PDS_EVT_ERROR_CLEAR + * event. + * + * @param[in] peer_id The id of the peer the data pertains to. + * @param[in] data_id Which data to clear. + * + * @retval NRF_SUCCESS The clear was initiated successfully. + * @retval NRF_ERROR_INVALID_PARAM Data ID or was invalid. + * @retval NRF_ERROR_NOT_FOUND Nothing to clear for this peer ID. + * @retval NRF_ERROR_INVALID_STATE Module is not initialized. + */ +ret_code_t pds_peer_data_clear(pm_peer_id_t peer_id, pm_peer_data_id_t data_id); + + +/**@brief Function for claiming an unused peer ID. + * + * @return The first unused peer ID. + * @retval PM_PEER_ID_INVALID If no peer ID is available or module is not initialized. + */ +pm_peer_id_t pds_peer_id_allocate(void); + + +/**@brief Function for freeing a peer ID and clearing all data associated with it in persistent + * storage. + * + * @param[in] peer_id Peer ID to free. + * + * @retval NRF_SUCCESS The clear was initiated successfully + * @retval NRF_ERROR_BUSY Another peer_id clear was already requested or fds queue full + */ +ret_code_t pds_peer_id_free(pm_peer_id_t peer_id); + + +/**@brief Function for finding out whether a peer ID is in use. + * + * @param[in] peer_id The peer ID to inquire about. + * + * @retval true peer_id is in use. + * @retval false peer_id is free, or the module is not initialized. + */ +bool pds_peer_id_is_allocated(pm_peer_id_t peer_id); + + +/**@brief Function for getting the next peer ID in the sequence of all used peer IDs. Can be + * used to loop through all used peer IDs. + * + * @note @ref PM_PEER_ID_INVALID is considered to be before the first and after the last ordinary + * peer ID. + * + * @param[in] prev_peer_id The previous peer ID. + * + * @return The next peer ID. + * @return The first ordinary peer ID if prev_peer_id was @ref PM_PEER_ID_INVALID. + * @retval PM_PEER_ID_INVALID if prev_peer_id was the last ordinary peer ID or the module + * is not initialized. + */ +pm_peer_id_t pds_next_peer_id_get(pm_peer_id_t prev_peer_id); + + +/**@brief Function for querying the number of valid peer IDs available. I.E the number of peers + * in persistent storage. + * + * @return The number of valid peer IDs, or 0 if module is not initialized. + */ +uint32_t pds_n_peers(void); + + +/** @} */ + +#endif /* PEER_DATA_STORAGE_H__ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/nordic_sdk/components/ble/peer_manager/peer_database.c Mon Jan 11 22:33:57 2016 +0000 @@ -0,0 +1,768 @@ +/* + * Copyright (c) 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 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 other + * contributors to this software may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#include "peer_database.h" + +#include <string.h> +#include "peer_manager_types.h" +#include "peer_data_storage.h" +#include "pm_buffer.h" + + +#define MAX_REGISTRANTS 6 /**< The number of user that can register with the module. */ + +#define MODULE_INITIALIZED (m_pdb.n_registrants > 0) /**< Expression which is true when the module is initialized. */ + +#define N_WRITE_BUFFERS 8 /**< The number of write buffers available. */ +#define N_WRITE_BUFFER_RECORDS (N_WRITE_BUFFERS) /**< The number of write buffer records. */ + +/**@brief Macro for verifying that the module is initialized. It will cause the function to return + * @ref NRF_ERROR_INVALID_STATE if not. + */ +#define VERIFY_MODULE_INITIALIZED() \ +do \ +{ \ + if (!MODULE_INITIALIZED) \ + { \ + return NRF_ERROR_INVALID_STATE; \ + } \ +} while(0) + + +/**@brief Macro for verifying that the module is initialized. It will cause the function to return + * if not. + */ +#define VERIFY_MODULE_INITIALIZED_VOID()\ +do \ +{ \ + if (!MODULE_INITIALIZED) \ + { \ + return; \ + } \ +} while(0) + + +/**@brief Macro for verifying that the module is initialized. It will cause the function to return + * if not. + * + * @param[in] param The variable to check if is NULL. + */ +#define VERIFY_PARAM_NOT_NULL(param) \ +do \ +{ \ + if (param == NULL) \ + { \ + return NRF_ERROR_NULL; \ + } \ +} while(0) + + +typedef struct +{ + pm_peer_id_t peer_id; + pm_peer_data_id_t data_id; + uint8_t buffer_block_id; + uint8_t store_busy : 1; + uint8_t store_flash_full : 1; + uint8_t store_requested : 1; + uint32_t n_bufs; + pm_prepare_token_t prepare_token; + pm_store_token_t store_token; +} pdb_buffer_record_t; + +typedef struct +{ + pdb_evt_handler_t evt_handlers[MAX_REGISTRANTS]; + uint8_t n_registrants; + pm_buffer_t write_buffer; + pdb_buffer_record_t write_buffer_records[N_WRITE_BUFFER_RECORDS]; + uint32_t n_writes; +} pdb_t; + +static pdb_t m_pdb = {.n_registrants = 0}; + + +/**@brief Function for invalidating a record of a write buffer allocation. + * + * @param[in] p_record The record to invalidate. + */ +static void write_buffer_record_invalidate(pdb_buffer_record_t * p_record) +{ + p_record->peer_id = PM_PEER_ID_INVALID; + p_record->data_id = PM_PEER_DATA_ID_INVALID; + p_record->buffer_block_id = BUFFER_INVALID_ID; + p_record->store_busy = false; + p_record->store_flash_full = false; + p_record->store_requested = false; + p_record->n_bufs = 0; + p_record->prepare_token = PDS_PREPARE_TOKEN_INVALID; + p_record->store_token = PDS_STORE_TOKEN_INVALID; +} + + +/**@brief Function for finding a record of a write buffer allocation. + * + * @param[in] peer_id The peer ID in the record. + * @param[in] data_id The data ID in the record. + * + * @return A pointer to the matching record, or NULL if none was found. + */ +static pdb_buffer_record_t * write_buffer_record_find(pm_peer_id_t peer_id, + pm_peer_data_id_t data_id) +{ + for (int i = 0; i < N_WRITE_BUFFER_RECORDS; i++) + { + if ((m_pdb.write_buffer_records[i].peer_id == peer_id) + && (m_pdb.write_buffer_records[i].data_id == data_id)) + { + return &m_pdb.write_buffer_records[i]; + } + } + return NULL; +} + + +/**@brief Function for finding an available record for write buffer allocation. + * + * @return A pointer to the available record, or NULL if none was found. + */ +static pdb_buffer_record_t * write_buffer_record_find_unused(void) +{ + return write_buffer_record_find(PM_PEER_ID_INVALID, PM_PEER_DATA_ID_INVALID); +} + + +/**@brief Function for gracefully deactivating a write buffer record. + * + * @details This function will first release any buffers, then invalidate the record. + * + * @param[inout] p_write_buffer_record The record to release. + * + * @return A pointer to the matching record, or NULL if none was found. + */ +static void write_buffer_record_release(pdb_buffer_record_t * p_write_buffer_record) +{ + for (int i = 0; i < p_write_buffer_record->n_bufs; i++) + { + pm_buffer_release(&m_pdb.write_buffer, p_write_buffer_record->buffer_block_id + i); + } + + write_buffer_record_invalidate(p_write_buffer_record); +} + + +static void write_buffer_record_get(pdb_buffer_record_t ** pp_write_buffer_record, pm_peer_id_t peer_id, pm_peer_data_id_t data_id) +{ + if (pp_write_buffer_record == NULL) + { + return; + } + *pp_write_buffer_record = write_buffer_record_find_unused(); + if (*pp_write_buffer_record == NULL) + { + // This also means the buffer is full. + return; + } + (*pp_write_buffer_record)->peer_id = peer_id; + (*pp_write_buffer_record)->data_id = data_id; +} + + +/**@brief Function for dispatching outbound events to all registered event handlers. + * + * @param[in] p_event The event to dispatch. + */ +static void pdb_evt_send(pdb_evt_t * p_event) +{ + for (int i = 0; i < m_pdb.n_registrants; i++) + { + m_pdb.evt_handlers[i](p_event); + } +} + + +/**@brief Function for resetting the internal state of the Peer Database module. + * + * @param[out] p_event The event to dispatch. + */ +static void internal_state_reset(pdb_t * pdb) +{ + memset(pdb, 0, sizeof(pdb_t)); + for (int i = 0; i < N_WRITE_BUFFER_RECORDS; i++) + { + write_buffer_record_invalidate(&pdb->write_buffer_records[i]); + } +} + + +/**@brief Function for handling events from the Peer Data Storage module. + * + * @param[in] p_event The event to handle. + */ +static void pds_evt_handler(pds_evt_t const * p_event) +{ + ret_code_t err_code; + pdb_buffer_record_t * p_write_buffer_record; + bool retry_flash_full = false; + pdb_evt_t event = + { + .peer_id = p_event->peer_id, + .data_id = p_event->data_id, + }; + + p_write_buffer_record = write_buffer_record_find(p_event->peer_id, p_event->data_id); + + switch (p_event->evt_id) + { + case PDS_EVT_STORED: + if ( (p_write_buffer_record != NULL) + //&& (p_write_buffer_record->store_token == p_event->store_token) + && (p_write_buffer_record->store_requested)) + { + write_buffer_record_release(p_write_buffer_record); + event.evt_id = PDB_EVT_WRITE_BUF_STORED; + pdb_evt_send(&event); + } + else + { + event.evt_id = PDB_EVT_RAW_STORED; + pdb_evt_send(&event); + } + break; + case PDS_EVT_ERROR_STORE: + if ( (p_write_buffer_record != NULL) + && (p_write_buffer_record->store_token == p_event->store_token) + && (p_write_buffer_record->store_requested)) + { + // Retry if internal buffer. + m_pdb.n_writes++; + p_write_buffer_record->store_requested = false; + p_write_buffer_record->store_busy = true; + } + else + { + event.evt_id = PDB_EVT_RAW_STORE_FAILED; + pdb_evt_send(&event); + } + break; + case PDS_EVT_CLEARED: + event.evt_id = PDB_EVT_CLEARED; + pdb_evt_send(&event); + break; + case PDS_EVT_ERROR_CLEAR: + event.evt_id = PDB_EVT_CLEAR_FAILED; + pdb_evt_send(&event); + break; + case PDS_EVT_COMPRESSED: + retry_flash_full = true; + event.evt_id = PDB_EVT_COMPRESSED; + pdb_evt_send(&event); + break; + default: + break; + } + + if (m_pdb.n_writes > 0) + { + for (int i = 0; i < N_WRITE_BUFFER_RECORDS; i++) + { + if ((m_pdb.write_buffer_records[i].store_busy) + || (m_pdb.write_buffer_records[i].store_flash_full && retry_flash_full)) + { + err_code = pdb_write_buf_store(m_pdb.write_buffer_records[i].peer_id, + m_pdb.write_buffer_records[i].data_id); + if (err_code != NRF_SUCCESS) + { + event.peer_id = m_pdb.write_buffer_records[i].peer_id; + event.data_id = m_pdb.write_buffer_records[i].data_id; + if (err_code == NRF_ERROR_NO_MEM) + { + event.evt_id = PDB_EVT_ERROR_NO_MEM; + } + else + { + event.evt_id = PDB_EVT_ERROR_UNEXPECTED; + } + + pdb_evt_send(&event); + break; + } + } + } + } +} + + +ret_code_t pdb_register(pdb_evt_handler_t evt_handler) +{ + if (m_pdb.n_registrants >= MAX_REGISTRANTS) + { + return NRF_ERROR_NO_MEM; + } + + VERIFY_PARAM_NOT_NULL(evt_handler); + + if (!MODULE_INITIALIZED) + { + ret_code_t err_code; + + internal_state_reset(&m_pdb); + err_code = pds_register(pds_evt_handler); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + PM_BUFFER_INIT(&m_pdb.write_buffer, N_WRITE_BUFFERS, PDB_WRITE_BUF_SIZE, err_code); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + } + + m_pdb.evt_handlers[m_pdb.n_registrants] = evt_handler; + m_pdb.n_registrants += 1; + + return NRF_SUCCESS; +} + + +pm_peer_id_t pdb_peer_allocate(void) +{ + if (!MODULE_INITIALIZED) + { + return PM_PEER_ID_INVALID; + } + + return pds_peer_id_allocate(); +} + + +ret_code_t pdb_peer_free(pm_peer_id_t peer_id) +{ + VERIFY_MODULE_INITIALIZED(); + + return pds_peer_id_free(peer_id); +} + + +ret_code_t pdb_read_buf_get(pm_peer_id_t peer_id, + pm_peer_data_id_t data_id, + pm_peer_data_flash_t * p_peer_data, + pm_store_token_t * p_token) +{ + VERIFY_MODULE_INITIALIZED(); + + return pds_peer_data_read_ptr_get(peer_id, data_id, p_peer_data, p_token); +} + + +static void peer_data_point_to_buffer(pm_peer_data_t * p_peer_data, pm_peer_data_id_t data_id, uint8_t * p_buffer_memory, uint16_t n_bufs) +{ + uint16_t n_bytes = n_bufs * PDB_WRITE_BUF_SIZE; + p_peer_data->data_type = data_id; + + switch(p_peer_data->data_type) + { + case PM_PEER_DATA_ID_BONDING: + p_peer_data->data.p_bonding_data = (pm_peer_data_bonding_t *)p_buffer_memory; + p_peer_data->length_words = PM_BONDING_DATA_N_WORDS(); + break; + case PM_PEER_DATA_ID_SERVICE_CHANGED_PENDING: + p_peer_data->data.p_service_changed_pending = (bool *)p_buffer_memory; + p_peer_data->length_words = PM_SC_STATE_N_WORDS(); + break; + case PM_PEER_DATA_ID_GATT_LOCAL: + p_peer_data->data.p_local_gatt_db = (pm_peer_data_local_gatt_db_t *)p_buffer_memory; + p_peer_data->length_words = PM_LOCAL_DB_N_WORDS(n_bytes); + break; + case PM_PEER_DATA_ID_GATT_REMOTE: + p_peer_data->data.p_remote_gatt_db = (pm_peer_data_remote_gatt_db_t *)p_buffer_memory; + p_peer_data->length_words = PM_REMOTE_DB_N_WORDS(n_bytes / sizeof(ble_gatt_db_srv_t)); + break; + case PM_PEER_DATA_ID_APPLICATION: + p_peer_data->data.p_application_data = p_buffer_memory; + p_peer_data->length_words = PM_N_WORDS(n_bytes); + break; + default: + p_peer_data->length_words = 0; + break; + } +} + + +static void peer_data_const_point_to_buffer(pm_peer_data_const_t * p_peer_data, pm_peer_data_id_t data_id, uint8_t * p_buffer_memory, uint32_t n_bufs) +{ + peer_data_point_to_buffer((pm_peer_data_t*)p_peer_data, data_id, p_buffer_memory, n_bufs); +} + + +ret_code_t pdb_write_buf_get(pm_peer_id_t peer_id, + pm_peer_data_id_t data_id, + uint32_t n_bufs, + pm_peer_data_t * p_peer_data) +{ + VERIFY_MODULE_INITIALIZED(); + VERIFY_PARAM_NOT_NULL(p_peer_data); + if ( !PM_PEER_DATA_ID_IS_VALID(data_id) + || (n_bufs == 0) + || (n_bufs > N_WRITE_BUFFERS) + || !pds_peer_id_is_allocated(peer_id)) + { + return NRF_ERROR_INVALID_PARAM; + } + + pdb_buffer_record_t * write_buffer_record; + uint8_t * p_buffer_memory; + + write_buffer_record = write_buffer_record_find(peer_id, data_id); + + if ((write_buffer_record != NULL) && (write_buffer_record->n_bufs < n_bufs)) + { + // @TODO: Copy? + // Existing buffer is too small. + for (uint8_t i = 0; i < write_buffer_record->n_bufs; i++) + { + pm_buffer_release(&m_pdb.write_buffer, write_buffer_record->buffer_block_id + i); + } + write_buffer_record_invalidate(write_buffer_record); + write_buffer_record = NULL; + } + else if ((write_buffer_record != NULL) && write_buffer_record->n_bufs > n_bufs) + { + // Release excess blocks. + for (uint8_t i = n_bufs; i < write_buffer_record->n_bufs; i++) + { + pm_buffer_release(&m_pdb.write_buffer, write_buffer_record->buffer_block_id + i); + } + } + + if (write_buffer_record == NULL) + { + write_buffer_record_get(&write_buffer_record, peer_id, data_id); + if (write_buffer_record == NULL) + { + return NRF_ERROR_BUSY; + } + } + + if (write_buffer_record->buffer_block_id == BUFFER_INVALID_ID) + { + write_buffer_record->buffer_block_id = pm_buffer_block_acquire(&m_pdb.write_buffer, n_bufs); + + if (write_buffer_record->buffer_block_id == BUFFER_INVALID_ID) + { + write_buffer_record_invalidate(write_buffer_record); + return NRF_ERROR_BUSY; + } + } + + write_buffer_record->n_bufs = n_bufs; + + p_buffer_memory = pm_buffer_ptr_get(&m_pdb.write_buffer, write_buffer_record->buffer_block_id); + + if (p_buffer_memory == NULL) + { + return NRF_ERROR_INTERNAL; + } + + peer_data_point_to_buffer(p_peer_data, data_id, p_buffer_memory, n_bufs); + switch(data_id) + { + case PM_PEER_DATA_ID_BONDING: + /* No action needed. */ + break; + case PM_PEER_DATA_ID_SERVICE_CHANGED_PENDING: + /* No action needed. */ + break; + case PM_PEER_DATA_ID_GATT_LOCAL: + { + uint32_t size_offset = sizeof(pm_peer_data_local_gatt_db_t); + p_peer_data->data.p_local_gatt_db->p_data = &p_buffer_memory[size_offset]; + p_peer_data->data.p_local_gatt_db->len = (PDB_WRITE_BUF_SIZE*n_bufs)-size_offset; + } + break; + case PM_PEER_DATA_ID_GATT_REMOTE: + { + uint32_t size_offset = sizeof(pm_peer_data_remote_gatt_db_t); + p_peer_data->data.p_remote_gatt_db->p_data = (ble_gatt_db_srv_t*)&(p_buffer_memory[size_offset]); + p_peer_data->data.p_remote_gatt_db->service_count + = ((PDB_WRITE_BUF_SIZE*n_bufs)-size_offset)/sizeof(ble_gatt_db_srv_t); + } + break; + case PM_PEER_DATA_ID_APPLICATION: + { + p_peer_data->data.p_application_data = p_buffer_memory; + } + break; + default: + // Invalid data_id. This should have been picked up earlier. + return NRF_ERROR_INTERNAL; + } + + return NRF_SUCCESS; +} + + +ret_code_t pdb_write_buf_release(pm_peer_id_t peer_id, pm_peer_data_id_t data_id) +{ + VERIFY_MODULE_INITIALIZED(); + + ret_code_t err_code = NRF_SUCCESS; + pdb_buffer_record_t * p_write_buffer_record; + p_write_buffer_record = write_buffer_record_find(peer_id, data_id); + + if (p_write_buffer_record == NULL) + { + return NRF_ERROR_NOT_FOUND; + } + + if (p_write_buffer_record->prepare_token != PDS_PREPARE_TOKEN_INVALID) + { + err_code = pds_peer_data_write_prepare_cancel(p_write_buffer_record->prepare_token); + if (err_code != NRF_SUCCESS) + { + err_code = NRF_ERROR_INTERNAL; + } + } + + write_buffer_record_release(p_write_buffer_record); + + return err_code; +} + + +ret_code_t pdb_write_buf_store_prepare(pm_peer_id_t peer_id, pm_peer_data_id_t data_id) +{ + VERIFY_MODULE_INITIALIZED(); + + ret_code_t err_code = NRF_SUCCESS; + pdb_buffer_record_t * p_write_buffer_record; + p_write_buffer_record = write_buffer_record_find(peer_id, data_id); + + if (p_write_buffer_record == NULL) + { + return NRF_ERROR_NOT_FOUND; + } + + if (p_write_buffer_record->prepare_token == PDS_PREPARE_TOKEN_INVALID) + { + uint8_t * p_buffer_memory = pm_buffer_ptr_get(&m_pdb.write_buffer, p_write_buffer_record->buffer_block_id); + pm_peer_data_const_t peer_data = {.data_type = data_id}; + + if (p_buffer_memory == NULL) + { + return NRF_ERROR_INTERNAL; + } + + peer_data_const_point_to_buffer(&peer_data, data_id, p_buffer_memory, p_write_buffer_record->n_bufs); + + err_code = pds_peer_data_write_prepare(&peer_data, &p_write_buffer_record->prepare_token); + if (err_code == NRF_ERROR_INVALID_LENGTH) + { + return NRF_ERROR_INTERNAL; + } + } + + return err_code; +} + + +static ret_code_t write_or_update(pm_peer_id_t peer_id, + pm_peer_data_id_t data_id, + pm_peer_data_const_t * p_peer_data, + pm_store_token_t * p_store_token, + pm_prepare_token_t prepare_token) +{ + pm_peer_data_flash_t old_peer_data; + pm_store_token_t old_store_token; + ret_code_t err_code = pds_peer_data_read_ptr_get(peer_id, data_id, &old_peer_data, &old_store_token); + + if (err_code == NRF_SUCCESS) + { + pds_peer_data_write_prepare_cancel(prepare_token); + err_code = pds_peer_data_update(peer_id, p_peer_data, old_store_token, p_store_token); + } + else if (err_code == NRF_ERROR_NOT_FOUND) + { + if (prepare_token == PDS_PREPARE_TOKEN_INVALID) + { + err_code = pds_peer_data_write(peer_id, p_peer_data, p_store_token); + } + else + { + err_code = pds_peer_data_write_prepared(peer_id, p_peer_data, prepare_token, p_store_token); + } + } + return err_code; +} + + +ret_code_t pdb_write_buf_store(pm_peer_id_t peer_id, + pm_peer_data_id_t data_id) +{ + VERIFY_MODULE_INITIALIZED(); + + ret_code_t err_code = NRF_SUCCESS; + pdb_buffer_record_t * p_write_buffer_record; + uint8_t * p_buffer_memory; + pm_peer_data_const_t peer_data = {.data_type = data_id}; + + + p_write_buffer_record = write_buffer_record_find(peer_id, data_id); + + if (p_write_buffer_record == NULL) + { + return NRF_ERROR_NOT_FOUND; + } + + if (p_write_buffer_record->store_requested) + { + return NRF_SUCCESS; + } + + p_buffer_memory = pm_buffer_ptr_get(&m_pdb.write_buffer, p_write_buffer_record->buffer_block_id); + + if (p_buffer_memory == NULL) + { + return NRF_ERROR_INTERNAL; + } + + peer_data_const_point_to_buffer(&peer_data, data_id, p_buffer_memory, p_write_buffer_record->n_bufs); + + switch (data_id) + { + case PM_PEER_DATA_ID_BONDING: + peer_data.length_words = PM_BONDING_DATA_N_WORDS(); + break; + case PM_PEER_DATA_ID_SERVICE_CHANGED_PENDING: + peer_data.length_words = PM_SC_STATE_N_WORDS(); + break; + case PM_PEER_DATA_ID_GATT_LOCAL: + peer_data.length_words = PM_LOCAL_DB_N_WORDS(peer_data.data.p_local_gatt_db->len); + break; + case PM_PEER_DATA_ID_GATT_REMOTE: + peer_data.length_words = PM_REMOTE_DB_N_WORDS(peer_data.data.p_remote_gatt_db->service_count); + break; + case PM_PEER_DATA_ID_APPLICATION: + peer_data.length_words = PM_N_WORDS(p_write_buffer_record->n_bufs * PDB_WRITE_BUF_SIZE); + break; + default: + return NRF_ERROR_INVALID_PARAM; + } + + err_code = write_or_update(peer_id, data_id, &peer_data, &p_write_buffer_record->store_token, p_write_buffer_record->prepare_token); + + if (p_write_buffer_record->store_busy && p_write_buffer_record->store_flash_full) + { + m_pdb.n_writes--; + } + + if (err_code == NRF_SUCCESS) + { + p_write_buffer_record->store_requested = true; + p_write_buffer_record->store_busy = false; + p_write_buffer_record->store_flash_full = false; + } + else + { + if (err_code == NRF_ERROR_BUSY) + { + m_pdb.n_writes++; + p_write_buffer_record->store_busy = true; + p_write_buffer_record->store_flash_full = false; + err_code = NRF_SUCCESS; + } + else if (err_code == NRF_ERROR_NO_MEM) + { + m_pdb.n_writes++; + p_write_buffer_record->store_busy = false; + p_write_buffer_record->store_flash_full = true; + } + else if ((err_code != NRF_ERROR_NO_MEM) && (err_code != NRF_ERROR_INVALID_PARAM)) + { + err_code = NRF_ERROR_INTERNAL; + } + } + + return err_code; +} + + +ret_code_t pdb_clear(pm_peer_id_t peer_id, pm_peer_data_id_t data_id) +{ + VERIFY_MODULE_INITIALIZED(); + + return pds_peer_data_clear(peer_id, data_id); +} + + +uint32_t pdb_n_peers(void) +{ + if (!MODULE_INITIALIZED) + { + return 0; + } + + return pds_n_peers(); +} + + +pm_peer_id_t pdb_next_peer_id_get(pm_peer_id_t prev_peer_id) +{ + if (!MODULE_INITIALIZED) + { + return PM_PEER_ID_INVALID; + } + + return pds_next_peer_id_get(prev_peer_id); +} + + +ret_code_t pdb_raw_read(pm_peer_id_t peer_id, + pm_peer_data_id_t data_id, + pm_peer_data_t * p_peer_data) +{ + VERIFY_MODULE_INITIALIZED(); + return pds_peer_data_read(peer_id, data_id, p_peer_data, &p_peer_data->length_words); +} + + +ret_code_t pdb_raw_store(pm_peer_id_t peer_id, + pm_peer_data_const_t * p_peer_data, + pm_store_token_t * p_store_token) +{ + VERIFY_MODULE_INITIALIZED(); + + return write_or_update(peer_id, p_peer_data->data_type, p_peer_data, p_store_token, PDS_PREPARE_TOKEN_INVALID); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/nordic_sdk/components/ble/peer_manager/peer_id.c Mon Jan 11 22:33:57 2016 +0000 @@ -0,0 +1,131 @@ +/* + * Copyright (c) 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 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 other + * contributors to this software may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#include "peer_id.h" + +#include <stdint.h> +#include <string.h> +#include "sdk_errors.h" +#include "peer_manager_types.h" +#include "pm_mutex.h" + + +typedef struct +{ + uint8_t peer_ids[MUTEX_STORAGE_SIZE(PM_PEER_ID_N_AVAILABLE_IDS)]; /*< bitmap. */ +} pi_t; + + +static pi_t m_pi = {.peer_ids = {0}}; + + +static void internal_state_reset(pi_t * p_pi) +{ + memset(p_pi, 0, sizeof(pi_t)); +} + + +void peer_id_init(void) +{ + internal_state_reset(&m_pi); + pm_mutex_init(m_pi.peer_ids, PM_PEER_ID_N_AVAILABLE_IDS); +} + + +pm_peer_id_t peer_id_allocate(pm_peer_id_t peer_id) +{ + pm_peer_id_t allocated_peer_id = PM_PEER_ID_INVALID; + if (peer_id == PM_PEER_ID_INVALID) + { + allocated_peer_id = pm_mutex_lock_first_available(m_pi.peer_ids, PM_PEER_ID_N_AVAILABLE_IDS); + if (allocated_peer_id == PM_PEER_ID_N_AVAILABLE_IDS) + { + allocated_peer_id = PM_PEER_ID_INVALID; + } + } + else if (peer_id < PM_PEER_ID_N_AVAILABLE_IDS) + { + bool lock_success = pm_mutex_lock(m_pi.peer_ids, peer_id); + allocated_peer_id = lock_success ? peer_id : PM_PEER_ID_INVALID; + } + return allocated_peer_id; +} + + +void peer_id_free(pm_peer_id_t peer_id) +{ + if (peer_id < PM_PEER_ID_N_AVAILABLE_IDS) + { + pm_mutex_unlock(m_pi.peer_ids, peer_id); + } +} + + +bool peer_id_is_allocated(pm_peer_id_t peer_id) +{ + if (peer_id < PM_PEER_ID_N_AVAILABLE_IDS) + { + return pm_mutex_lock_status_get(m_pi.peer_ids, peer_id); + } + return false; +} + + +pm_peer_id_t peer_id_next_id_get(pm_peer_id_t prev_peer_id) +{ + pm_peer_id_t i = (prev_peer_id == PM_PEER_ID_INVALID) ? 0 : (prev_peer_id + 1); + for (; i < PM_PEER_ID_N_AVAILABLE_IDS; i++) + { + if (pm_mutex_lock_status_get(m_pi.peer_ids, i)) + { + return i; + } + } + + return PM_PEER_ID_INVALID; +} + + +uint32_t peer_id_n_ids(void) +{ + uint32_t n_ids = 0; + + for (pm_peer_id_t i = 0; i < PM_PEER_ID_N_AVAILABLE_IDS; i++) + { + n_ids += pm_mutex_lock_status_get(m_pi.peer_ids, i); + } + + return n_ids; +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/nordic_sdk/components/ble/peer_manager/peer_id.h Mon Jan 11 22:33:57 2016 +0000 @@ -0,0 +1,112 @@ +/* + * Copyright (c) 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 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 other + * contributors to this software may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#ifndef PEER_ID_H__ +#define PEER_ID_H__ + + +#include "stdint.h" +#include "sdk_errors.h" +#include "ble_gap.h" +#include "peer_manager_types.h" + + +/** + * @defgroup peer_id Peer IDs + * @ingroup peer_manager + * @{ + * @brief An internal module of @ref peer_manager. This module keeps track of which peer IDs are in + * use and which are free. + */ + + +/**@brief Function for initializing the module. + */ +void peer_id_init(void); + + +/**@brief Function for claiming an unused peer ID. + * + * @param peer_id The peer ID to allocate. If this is @ref PM_PEER_ID_INVALID, the first available + * will be allocated. + * + * @return The allocated peer ID. + * @retval PM_PEER_ID_INVALID If no peer ID could be allocated or module is not initialized. + */ +pm_peer_id_t peer_id_allocate(pm_peer_id_t peer_id); + + +/**@brief Function for freeing a peer ID and clearing all data associated with it in persistent + * storage. + * + * @param[in] peer_id Peer ID to free. + */ +void peer_id_free(pm_peer_id_t peer_id); + + +/**@brief Function for finding out whether a peer ID is in use. + * + * @param[in] peer_id The peer ID to inquire about. + * + * @retval true peer_id is in use. + * @retval false peer_id is free, or the module is not initialized. + */ +bool peer_id_is_allocated(pm_peer_id_t peer_id); + + +/**@brief Function for getting the next peer ID in the sequence of all used peer IDs. Can be + * used to loop through all used peer IDs. + * + * @note @ref PM_PEER_ID_INVALID is considered to be before the first and after the last ordinary + * peer ID. + * + * @param[in] prev_peer_id The previous peer ID. + * + * @return The next peer ID. + * @return The first used peer ID if prev_peer_id was @ref PM_PEER_ID_INVALID. + * @retval PM_PEER_ID_INVALID if prev_peer_id was the last ordinary peer ID or the module is + * not initialized. + */ +pm_peer_id_t peer_id_next_id_get(pm_peer_id_t prev_peer_id); + + +/**@brief Function for querying the number of valid peer IDs available. I.E the number of peers + * in persistent storage. + * + * @return The number of valid peer IDs, or 0 if module is not initialized. + */ +uint32_t peer_id_n_ids(void); + +/** @} */ + +#endif /* PEER_ID_H__ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/nordic_sdk/components/ble/peer_manager/pm_buffer.c Mon Jan 11 22:33:57 2016 +0000 @@ -0,0 +1,142 @@ +/* + * Copyright (c) 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 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 other + * contributors to this software may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#include "pm_buffer.h" + +#include <stdbool.h> +#include <string.h> +#include "nrf_error.h" +#include "pm_mutex.h" + + +#define BUFFER_IS_VALID(p_buffer) ((p_buffer != NULL) \ + && (p_buffer->p_memory != NULL) \ + && (p_buffer->p_mutex != NULL)) + + + +ret_code_t pm_buffer_init(pm_buffer_t * p_buffer, + uint8_t * p_buffer_memory, + uint32_t buffer_memory_size, + uint8_t * p_mutex_memory, + uint32_t mutex_memory_size, + uint32_t n_blocks, + uint32_t block_size) +{ + if ( (p_buffer != NULL) + && (p_buffer_memory != NULL) + && (p_mutex_memory != NULL) + && (buffer_memory_size >= (n_blocks*block_size)) + && (mutex_memory_size >= MUTEX_STORAGE_SIZE(n_blocks)) + && (n_blocks != 0) + && (block_size != 0)) + { + p_buffer->p_memory = p_buffer_memory; + p_buffer->p_mutex = p_mutex_memory; + p_buffer->n_blocks = n_blocks; + p_buffer->block_size = block_size; + pm_mutex_init(p_buffer->p_mutex, n_blocks); + + return NRF_SUCCESS; + } + else + { + return NRF_ERROR_INVALID_PARAM; + } +} + + +uint8_t pm_buffer_block_acquire(pm_buffer_t * p_buffer, uint32_t n_blocks) +{ + if (!BUFFER_IS_VALID(p_buffer)) + { + return ( BUFFER_INVALID_ID ); + } + + uint8_t first_locked_mutex = BUFFER_INVALID_ID; + + for (uint8_t i = 0; i < p_buffer->n_blocks; i++) + { + if (pm_mutex_lock(p_buffer->p_mutex, i)) + { + if (first_locked_mutex == BUFFER_INVALID_ID) + { + first_locked_mutex = i; + } + if ((i - first_locked_mutex + 1) == n_blocks) + { + return first_locked_mutex; + } + } + else if (first_locked_mutex != BUFFER_INVALID_ID) + { + for (uint8_t j = first_locked_mutex; j < i; j++) + { + pm_buffer_release(p_buffer, j); + } + first_locked_mutex = BUFFER_INVALID_ID; + } + } + + return ( BUFFER_INVALID_ID ); +} + + +uint8_t * pm_buffer_ptr_get(pm_buffer_t * p_buffer, uint8_t id) +{ + if (!BUFFER_IS_VALID(p_buffer)) + { + return ( NULL ); + } + + if ( (id != BUFFER_INVALID_ID) + && pm_mutex_lock_status_get(p_buffer->p_mutex, id) ) + { + return ( &p_buffer->p_memory[id*p_buffer->block_size] ); + } + else + { + return ( NULL ); + } +} + + +void pm_buffer_release(pm_buffer_t * p_buffer, uint8_t id) +{ + if ( BUFFER_IS_VALID(p_buffer) + && (id != BUFFER_INVALID_ID) + && pm_mutex_lock_status_get(p_buffer->p_mutex, id)) + { + pm_mutex_unlock(p_buffer->p_mutex, id); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/nordic_sdk/components/ble/peer_manager/pm_buffer.h Mon Jan 11 22:33:57 2016 +0000 @@ -0,0 +1,133 @@ +/* + * Copyright (c) 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 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 other + * contributors to this software may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#ifndef BUFFER_H__ +#define BUFFER_H__ + +#include <stdint.h> +#include "sdk_errors.h" +#include "pm_mutex.h" + + +/** + * @defgroup pm_buffer Buffer + * @ingroup peer_manager + * @{ + * @brief An internal module of @ref peer_manager. This module provides a simple buffer. + */ + + +#define BUFFER_INVALID_ID 0xFF + +#define PM_BUFFER_INIT(p_buffer, n_blocks, block_size, err_code) \ +do \ +{ \ + static uint8_t buffer_memory[(n_blocks) * (block_size)]; \ + static uint8_t mutex_memory[MUTEX_STORAGE_SIZE(n_blocks)]; \ + err_code = pm_buffer_init((p_buffer), \ + buffer_memory, \ + (n_blocks) * (block_size), \ + mutex_memory, \ + MUTEX_STORAGE_SIZE(n_blocks), \ + (n_blocks), \ + (block_size)); \ +} while(0) + + +typedef struct +{ + uint8_t * p_memory; /**< The storage for all buffer entries. The size of the buffer must be n_blocks*block_size. */ + uint8_t * p_mutex; /**< A mutex group with one mutex for each buffer entry. */ + uint32_t n_blocks; /**< The number of allocatable blocks in the buffer. */ + uint32_t block_size; /**< The size of each block in the buffer. */ +} pm_buffer_t; + +/**@brief Function for initializing a buffer instance. + * + * @param[out] p_buffer The buffer instance to initialize. + * @param[in] p_buffer_memory The memory this buffer will use. + * @param[in] buffer_memory_size The size of p_buffer_memory. This must be at least + * n_blocks*block_size. + * @param[in] p_mutex_memory The memory for the mutexes. This must be at least + * @ref MUTEX_STORAGE_SIZE(n_blocks). + * @param[in] mutex_memory_size The size of p_mutex_memory. + * @param[in] n_blocks The number of blocks in the buffer. + * @param[in] block_size The size of each block. + * + * @retval NRF_SUCCESS Successfully initialized buffer instance. + * @retval NRF_ERROR_INVALID_PARAM A parameter was 0 or NULL or a size was too small. + */ +ret_code_t pm_buffer_init(pm_buffer_t * p_buffer, + uint8_t * p_buffer_memory, + uint32_t buffer_memory_size, + uint8_t * p_mutex_memory, + uint32_t mutex_memory_size, + uint32_t n_blocks, + uint32_t block_size); + + +/**@brief Function for acquiring a buffer block in a buffer. + * + * @param[in] p_buffer The buffer instance acquire from. + * @param[in] n_blocks The number of contiguous blocks to acquire. + * + * @return The id of the acquired block, if successful. + * @retval BUFFER_INVALID_ID If unsuccessful. + */ +uint8_t pm_buffer_block_acquire(pm_buffer_t * p_buffer, uint32_t n_blocks); + + +/**@brief Function for getting a pointer to a specific buffer block. + * + * @param[in] p_buffer The buffer instance get from. + * @param[in] id The id of the buffer to get the pointer for. + * + * @return A pointer to the buffer for the specified id, if the id is valid. + * @retval NULL If the id is invalid. + */ +uint8_t * pm_buffer_ptr_get(pm_buffer_t * p_buffer, uint8_t id); + + +/**@brief Function for releasing a buffer block. + * + * @param[in] p_buffer The buffer instance containing the block to release. + * @param[in] id The id of the block to release. + */ +void pm_buffer_release(pm_buffer_t * p_buffer, uint8_t id); + + +#endif // BUFFER_H__ + +/** + * @} + */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/nordic_sdk/components/ble/peer_manager/pm_mutex.c Mon Jan 11 22:33:57 2016 +0000 @@ -0,0 +1,135 @@ +/* + * Copyright (c) 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 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 other + * contributors to this software may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#include "pm_mutex.h" + +#include <stdbool.h> +#include <string.h> +#include "nrf_error.h" +#include "app_util_platform.h" + + + +/**@brief Locks the mutex defined by the mask. + * + * @param p_mutex pointer to the mutex storage. + * @param mutex_mask the mask identifying the mutex position. + * + * @retval true if the mutex could be locked. + * @retval false if the mutex was already locked. + */ +static bool lock_by_mask(uint8_t * p_mutex, uint8_t mutex_mask) +{ + bool success = false; + + if ( (*p_mutex & mutex_mask) == 0 ) + { + CRITICAL_REGION_ENTER(); + if ( (*p_mutex & mutex_mask) == 0 ) + { + *p_mutex |= mutex_mask; + + success = true; + } + CRITICAL_REGION_EXIT(); + } + + return ( success ); +} + + +void pm_mutex_init(uint8_t * p_mutex, uint16_t mutex_size) +{ + if (p_mutex != NULL) + { + memset(&p_mutex[0], 0, MUTEX_STORAGE_SIZE(mutex_size)); + } +} + + +bool pm_mutex_lock(uint8_t * p_mutex, uint16_t mutex_id) +{ + if (p_mutex != NULL) + { + return ( lock_by_mask(&(p_mutex[mutex_id >> 3]), (1 << (mutex_id & 0x07))) ); + } + else + { + return false; + } +} + + +void pm_mutex_unlock(uint8_t * p_mutex, uint16_t mutex_id) +{ + uint8_t mutex_base = mutex_id >> 3; + uint8_t mutex_mask = (1 << (mutex_id & 0x07)); + + if ((p_mutex != NULL) + && (p_mutex[mutex_base] & mutex_mask)) + { + CRITICAL_REGION_ENTER(); + p_mutex[mutex_base] &= ~mutex_mask; + CRITICAL_REGION_EXIT(); + } +} + + +uint16_t pm_mutex_lock_first_available(uint8_t * p_mutex, uint16_t mutex_size) +{ + if (p_mutex != NULL) + { + for ( uint16_t i = 0; i < mutex_size; i++ ) + { + if ( lock_by_mask(&(p_mutex[i >> 3]), 1 << (i & 0x07)) ) + { + return ( i ); + } + } + } + + return ( mutex_size ); +} + + +bool pm_mutex_lock_status_get(uint8_t * p_mutex, uint16_t mutex_id) +{ + if (p_mutex != NULL) + { + return ( (p_mutex[mutex_id >> 3] & (1 << (mutex_id & 0x07))) ); + } + else + { + return true; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/nordic_sdk/components/ble/peer_manager/pm_mutex.h Mon Jan 11 22:33:57 2016 +0000 @@ -0,0 +1,108 @@ +/* + * Copyright (c) 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 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 other + * contributors to this software may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + + +#ifndef MUTEX_H__ +#define MUTEX_H__ + + +#include <stdint.h> +#include <stdbool.h> + +/** + * @defgroup pm_mutex Mutex + * @ingroup peer_manager + * @{ + * @brief An internal module of @ref peer_manager. This module provides thread-safe mutexes. + */ + + +/**@brief Defines the storage size of a specified mutex group. + * + * @param number_of_mutexes the number of mutexes in the group. + */ +#define MUTEX_STORAGE_SIZE(number_of_mutexes) ((7 + (number_of_mutexes)) >> 3) + + +/**@brief Initializes a mutex group. + * + * @param[in] p_mutex Pointer to the mutex group. See @ref MUTEX_STORAGE_SIZE(). + * @param[in] mutex_size The size of the mutex group in number of mutexes. + */ +void pm_mutex_init(uint8_t * p_mutex, uint16_t mutex_size); + + +/**@brief Locks the mutex specified by the bit id. + * + * @param[inout] p_mutex Pointer to the mutex group. + * @param[in] mutex_bit_id The bit id of the mutex. + * + * @retval true if it was possible to lock the mutex. + * @retval false otherwise. + */ +bool pm_mutex_lock(uint8_t * p_mutex, uint16_t mutex_bit_id); + + +/**@brief Locks the first unlocked mutex within the mutex group. + * + * @param[in, out] p_mutex Pointer to the mutex group. + * @param[in] mutex_size The size of the mutex group. + * + * @return The first unlocked mutex id in the group. + * @retval group-size if there was no unlocked mutex available. + */ +uint16_t pm_mutex_lock_first_available(uint8_t * p_mutex, uint16_t mutex_size); + + +/**@brief Unlocks the mutex specified by the bit id. + * + * @param[in, out] p_mutex Pointer to the mutex group. + * @param[in] mutex_bit_id The bit id of the mutex. + */ +void pm_mutex_unlock(uint8_t * p_mutex, uint16_t mutex_bit_id); + + +/**@brief Gets the locking status of the specified mutex. + * + * @param[in, out] p_mutex Pointer to the mutex group. + * @param[in] mutex_bit_id The bit id of the mutex. + * + * @retval true if the mutex was locked. + * @retval false otherwise. + */ +bool pm_mutex_lock_status_get(uint8_t * p_mutex, uint16_t mutex_bit_id); + + +#endif // MUTEX_H__ + +/** @} */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/nordic_sdk/components/libraries/experimental_section_vars/section_vars.h Mon Jan 11 22:33:57 2016 +0000 @@ -0,0 +1,323 @@ +/* + * Copyright (c) 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 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 other + * contributors to this software may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef SECTION_VARS_H__ +#define SECTION_VARS_H__ + +#include "app_util.h" + +#if defined __ICC_ARM__ + +// turn on language extensions for iar +#pragma language=extended + +#endif + +/** + * @defgroup section_vars Section variables + * @ingroup app_common + * @{ + * @brief Section variables. + */ + +/**@brief Macro to delay macro expression of pragma + * + */ +#define NRF_PRAGMA(x) _Pragma(#x) + + +/**@brief Macro to register section by name in code + * + * @param[in] section_name Name of the section to register + **/ +#if defined __CC_ARM + +// Not required by this compiler +#define NRF_SECTION_VARS_REGISTER_SECTION(section_name) + +#elif defined __GNUC__ + +// Not required by this compiler +#define NRF_SECTION_VARS_REGISTER_SECTION(section_name) + +#elif defined __ICCARM__ + +#define NRF_SECTION_VARS_REGISTER_SECTION(section_name) NRF_PRAGMA(section = ## #section_name ) + +#else + +#error TODO + +#endif + +/*lint -save -e27 */ + +/**@brief Macro for accessing start of a named data section by symbol + * + * @details The symbol that this macro resolves to is used to access the section + * by start address. + * + * @param[in] section_name Name of the section + */ +#if defined __CC_ARM + +#define NRF_SECTION_VARS_START_SYMBOL(section_name) section_name ## $$Base + +#elif defined __GNUC__ + +#define NRF_SECTION_VARS_START_SYMBOL(section_name) __start_ ## section_name + +#elif defined __ICCARM__ + +#define NRF_SECTION_VARS_START_SYMBOL(section_name) __section_begin(#section_name) + +#else + +#error TODO + +#endif + + +/**@brief Macro for accessing end of a named data section by symbol + * + * @details The symbol that this macro resolves to is used to access the section + * by end address. + * + * @param[in] section_name Name of the section + */ +#if defined __CC_ARM + +#define NRF_SECTION_VARS_END_SYMBOL(section_name) section_name ## $$Limit + +#elif defined __GNUC__ + +#define NRF_SECTION_VARS_END_SYMBOL(section_name) __stop_ ## section_name + +#elif defined __ICCARM__ + +#define NRF_SECTION_VARS_END_SYMBOL(section_name) __section_end(#section_name) + +#endif + +/*lint -restore */ + + +/**@brief Macro for accessing Length of a named section + * + * @details This macro is used to get the size of a named section. + * + * @param[in] section_name Name of the section + */ + +#if defined __CC_ARM + +#define NRF_SECTION_VARS_LENGTH(section_name) \ + ((uint32_t)&NRF_SECTION_VARS_END_SYMBOL(section_name) - (uint32_t)&NRF_SECTION_VARS_START_SYMBOL(section_name)) + +#elif defined __GNUC__ + + #define NRF_SECTION_VARS_LENGTH(section_name) \ + ((uint32_t)&NRF_SECTION_VARS_END_SYMBOL(section_name) - (uint32_t)&NRF_SECTION_VARS_START_SYMBOL(section_name)) + +#elif defined __ICCARM__ + + #define NRF_SECTION_VARS_LENGTH(section_name) \ + ((uint32_t)NRF_SECTION_VARS_END_SYMBOL(section_name) - (uint32_t)NRF_SECTION_VARS_START_SYMBOL(section_name)) + +#else + +#error TODO + +#endif + + +/**@brief Macro for accessing the start address of a named section + * + * param[in] section_name Name of the section to get the start address from + */ +#if defined __CC_ARM + +#define NRF_SECTION_VARS_START_ADDR(section_name) (uint32_t)&NRF_SECTION_VARS_START_SYMBOL(section_name) + +#elif defined __GNUC__ + +#define NRF_SECTION_VARS_START_ADDR(section_name) (uint32_t)&NRF_SECTION_VARS_START_SYMBOL(section_name) + +#elif defined __ICCARM__ + +#define NRF_SECTION_VARS_START_ADDR(section_name) (uint32_t)iar_ ## section_name ## _start + +#else + +#error TODO + +#endif + + +/*@brief Macro for accessing the end address of a named section + * + * @param[in] section_name Name of the section to get end address from + */ +#if defined __CC_ARM + +#define NRF_SECTION_VARS_END_ADDR(section_name) (uint32_t)&NRF_SECTION_VARS_END_SYMBOL(section_name) + +#elif defined __GNUC__ + +#define NRF_SECTION_VARS_END_ADDR(section_name) (uint32_t)&NRF_SECTION_VARS_END_SYMBOL(section_name) + +#elif defined __ICCARM__ + +#define NRF_SECTION_VARS_END_ADDR(section_name) (uint32_t)iar_ ## section_name ## _end + +#else + +#error TODO + +#endif + + +/**@brief Macro for declaring symbols for named sections + * + * @note These external declarations of section specific symbols are required for the linker in GCC and Keil (not IAR) + * + * @param[in] type_name Name of the type stored in the section + * @param[in] section_name Name of the section + */ +#if defined __CC_ARM + +#define NRF_SECTION_VARS_REGISTER_SYMBOLS(type_name, section_name) \ + extern type_name* NRF_SECTION_VARS_START_SYMBOL(section_name); \ + extern void* NRF_SECTION_VARS_END_SYMBOL(section_name) + +#elif defined __GNUC__ + +#define NRF_SECTION_VARS_REGISTER_SYMBOLS(type_name, section_name) \ + extern type_name* NRF_SECTION_VARS_START_SYMBOL(section_name); \ + extern void* NRF_SECTION_VARS_END_SYMBOL(section_name) + +#elif defined __ICCARM__ + +// No symbol registration required for IAR +#define NRF_SECTION_VARS_REGISTER_SYMBOLS(type_name, section_name) \ + extern void* iar_ ## section_name ## _start = __section_begin(#section_name); \ + extern void* iar_ ## section_name ## _end = __section_end(#section_name) + +#else + +#error TODO + +#endif + + +/**@brief Macro to add symbols to a named section + * + * @details The symbols are placed in a named section. All calls to this macro + * will result in symbols being placed in a contiguous manner in the named section. + * This macro ensures that the symbol is not removed because of optimization level. + * + * @warning There is no guarantee for ordering of placement. If ordering is required + * + * @warning The symbols added in the named section must be word aligned to + * ensure that compilers do not pad the section during symbol placement. + * + * @param[in] section_name Name of the section + * @param[in] type_def Datatype of the symbol to place in the given section + */ +#if defined __CC_ARM + +#define NRF_SECTION_VARS_ADD(section_name, type_def) \ + static type_def __attribute__((section( #section_name ))) __attribute__((used)) + +#elif defined __GNUC__ + +#define NRF_SECTION_VARS_ADD(section_name, type_def) \ + static type_def __attribute__ ((section( #section_name ))) __attribute__ ((used)) + +#elif defined __ICCARM__ + +#define NRF_SECTION_VARS_ADD(section_name, type_def) \ + __root type_def @ #section_name + +#else + +#error TODO + +#endif + + +/**@brief Macro to get symbol from named section + * + * @warning The stored symbol can only be resolved using this macro if the + * type of the data is word aligned. The operation of acquiring + * the stored symbol relies on sizeof of the stored type, no + * padding can exist in the named section in between individual + * stored items or this macro will fail. + * + * @param[in] i Index of item in section + * @param[in] type_name Type name of item in section + * @param[in] section_name Name of the section + */ + +#if defined __CC_ARM + +#define NRF_SECTION_VARS_GET(i, type_name, section_name) \ + (type_name*)(NRF_SECTION_VARS_START_ADDR(section_name) + i * sizeof(type_name)) + +#elif defined __GNUC__ + +#define NRF_SECTION_VARS_GET(i, type_name, section_name) \ + (type_name*)(NRF_SECTION_VARS_START_ADDR(section_name) + i * sizeof(type_name)) + +#elif defined __ICCARM__ + +#define NRF_SECTION_VARS_GET(i, type_name, section_name) \ + (type_name*)iar_ ## section_name ## _start + (i * sizeof(type_name)) + +#else + +#error TODO + +#endif + + +/**@brief Macro to get number of items in named section + * + * @param[in] type_name Type name of item in section + * @param[in] section_name Name of the section + */ +#define NRF_SECTION_VARS_COUNT(type_name, section_name) \ + NRF_SECTION_VARS_LENGTH(section_name) / sizeof(type_name) + +/** @} */ + +#endif // SECTION_VARS_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/nordic_sdk/components/libraries/fds/fds.c Mon Jan 11 22:33:57 2016 +0000 @@ -0,0 +1,2090 @@ +/* + * Copyright (c) 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 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 other + * contributors to this software may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "fds.h" +#include <stdint.h> +#include <string.h> +#include <stdbool.h> +#include "fds_config.h" +#include "fds_types_internal.h" +#include "fstorage.h" +#include "nrf_error.h" +#include "app_util.h" + + +/** Our fstorage configuration. + * The other fields will be assigned automatically during compilation. */ +FS_SECTION_VARS_ADD(fs_config_t fs_config) = { .cb = fs_callback, .num_pages = FDS_MAX_PAGES }; + +static uint32_t const fds_page_tag_swap[] = {FDS_PAGE_TAG_WORD_0_SWAP, FDS_PAGE_TAG_WORD_1, + FDS_PAGE_TAG_WORD_2, FDS_PAGE_TAG_WORD_3}; + +static uint32_t const fds_page_tag_valid[] = {FDS_PAGE_TAG_WORD_0_VALID, FDS_PAGE_TAG_WORD_1, + FDS_PAGE_TAG_WORD_2, FDS_PAGE_TAG_WORD_3}; + +static uint32_t const fds_page_tag_gc = FDS_PAGE_TAG_WORD_3_GC; + +static fds_tl_t const m_fds_tl_invalid = { .type = FDS_TYPE_ID_INVALID, + .length_words = 0xFFFF }; + +/**@brief Internal status flags. */ +static uint8_t volatile m_flags; + +static uint8_t m_users; +static fds_cb_t m_cb_table[FDS_MAX_USERS]; + +/**@brief The last record ID. Setup page by page_scan() during pages_init(). */ +static fds_record_id_t m_last_rec_id; + +/**@brief The internal queues. */ +static fds_cmd_queue_t m_cmd_queue; +static fds_chunk_queue_t m_chunk_queue; + +/**@brief Holds the state of pages. Setup by fds_init(). */ +static fds_page_t m_pages[FDS_MAX_PAGES]; +static bool m_swap_page_avail = false; + +static fds_gc_data_t m_gc; +static uint16_t m_gc_runs; + +static uint8_t volatile m_counter; + + +static void app_notify(ret_code_t result, + fds_cmd_id_t cmd, + fds_record_id_t record_id, + fds_record_key_t record_key) +{ + for (uint8_t user = 0; user < FDS_MAX_USERS; user++) + { + if (m_cb_table[user] != NULL) + { + m_cb_table[user](result, cmd, record_id, record_key); + } + } +} + + +static void atomic_counter_inc() +{ + CRITICAL_SECTION_ENTER(); + m_counter++; + CRITICAL_SECTION_EXIT(); +} + + +static void atomic_counter_dec() +{ + CRITICAL_SECTION_ENTER(); + m_counter--; + CRITICAL_SECTION_EXIT(); +} + + +static bool atomic_counter_is_zero() +{ + bool ret; + CRITICAL_SECTION_ENTER(); + ret = (m_counter == 0); + CRITICAL_SECTION_EXIT(); + return ret; +} + + +static void flag_set(fds_flags_t flag) +{ + CRITICAL_SECTION_ENTER(); + m_flags |= flag; + CRITICAL_SECTION_EXIT(); +} + + +static void flag_clear(fds_flags_t flag) +{ + CRITICAL_SECTION_ENTER(); + m_flags &= ~(flag); + CRITICAL_SECTION_EXIT(); +} + + +static bool flag_is_set(fds_flags_t flag) +{ + bool ret; + CRITICAL_SECTION_ENTER(); + ret = (m_flags & flag); + CRITICAL_SECTION_EXIT(); + return ret; +} + + +/**@brief Function to check if a header has valid information. */ +static __INLINE bool header_is_valid(fds_header_t const * const p_header) +{ + return ((p_header->tl.type != FDS_TYPE_ID_INVALID) && + (p_header->ic.instance != FDS_INSTANCE_ID_INVALID)); +} + + +static bool address_within_page_bounds(uint32_t const * const p_addr) +{ + return (p_addr >= fs_config.p_start_addr) && + (p_addr <= fs_config.p_end_addr) && + (is_word_aligned(p_addr)); +} + + +/**@brief Internal function to identify the page type. */ +static fds_page_type_t page_identify(uint16_t page_number) +{ + uint32_t const * const p_page_addr = m_pages[page_number].start_addr; + + uint32_t const word0 = *(p_page_addr); + uint32_t const word1 = *(p_page_addr + 1); + uint32_t const word2 = *(p_page_addr + 2); + uint32_t const word3 = *(p_page_addr + 3); + + if (word1 != FDS_PAGE_TAG_WORD_1) + { + return FDS_PAGE_UNDEFINED; + } + + if (word2 != FDS_PAGE_TAG_WORD_2) + { + return FDS_PAGE_UNDEFINED; + } + + if (word3 == FDS_PAGE_TAG_WORD_3) + { + if (word0 == FDS_PAGE_TAG_WORD_0_SWAP) + { + return FDS_PAGE_SWAP; + } + + if (word0 == FDS_PAGE_TAG_WORD_0_VALID) + { + return FDS_PAGE_VALID; + } + } + else if (word3 == FDS_PAGE_TAG_WORD_3_GC) + { + if (word0 == FDS_PAGE_TAG_WORD_0_SWAP || word0 == FDS_PAGE_TAG_WORD_0_VALID) + { + return FDS_PAGE_GC; + } + } + + return FDS_PAGE_UNDEFINED; +} + + +static uint16_t page_by_addr(uint32_t const * const p_addr) +{ + if (p_addr == NULL) + { + return 0; + } + + // Compute the BYTES offset from the beginning of the first page. + uint32_t const byte_offset = (uint32_t)p_addr - (uint32_t)m_pages[0].start_addr; + +// See nrf.h. +#if defined (NRF51) + return byte_offset >> 10; // Divide by page size (1024). +#elif defined (NRF52) + return byte_offset >> 12; // Divide by page size (4096). +#else + #error "Device family must be defined. See nrf.h." +#endif +} + + +// NOTE: depends on m_pages.write_offset to function. +static bool page_has_space(uint16_t page, fds_length_t length_words) +{ + if (page >= FDS_MAX_PAGES) + { + return false; + } + + CRITICAL_SECTION_ENTER(); + length_words += m_pages[page].write_offset; + length_words += m_pages[page].words_reserved; + CRITICAL_SECTION_EXIT(); + + return (length_words < FS_PAGE_SIZE_WORDS); +} + + +/**@brief This function scans a page to determine how many words have + * been written to it. This information is used to set the page + * write offset during initialization (mount). Additionally, this + * function will update the last known record ID as it proceeds. + */ +static void page_scan(uint16_t page, uint16_t volatile * words_written) +{ + uint32_t const * p_addr = (m_pages[page].start_addr + FDS_PAGE_TAG_SIZE); + + *words_written = FDS_PAGE_TAG_SIZE; + + // A corrupt TL might cause problems. + while ((p_addr < m_pages[page].start_addr + FS_PAGE_SIZE_WORDS) && + (*p_addr != FDS_ERASED_WORD)) + { + fds_header_t const * const p_header = (fds_header_t*)p_addr; + + /** Note: DO NOT check for the validity of the header using + * header_is_valid() here. If an header has an invalid type (0x0000) + * or a missing instance (0xFFFF) then we WANT to skip it. + */ + + // Update the last known record id. + if (p_header->id > m_last_rec_id) + { + m_last_rec_id = p_header->id; + } + + // Jump to the next record. + p_addr += (FDS_HEADER_SIZE + p_header->tl.length_words); + *words_written += (FDS_HEADER_SIZE + p_header->tl.length_words); + } +} + + +static bool page_is_empty(uint16_t page) +{ + uint32_t const * const p_addr = m_pages[page].start_addr; + + for (uint16_t i = 0; i < FS_PAGE_SIZE_WORDS; i++) + { + if (*(p_addr + i) != FDS_ERASED_WORD) + { + return false; + } + } + + return true; +} + + +static ret_code_t page_id_from_virtual_id(uint16_t vpage_id, uint16_t * p_page_id) +{ + for (uint16_t i = 0; i < FDS_MAX_PAGES; i++) + { + if (m_pages[i].vpage_id == vpage_id) + { + *p_page_id = i; + return NRF_SUCCESS; + } + } + + return NRF_ERROR_NOT_FOUND; +} + + +static ret_code_t page_from_virtual_id(uint16_t vpage_id, fds_page_t ** p_page) +{ + for (uint16_t i = 0; i < FDS_MAX_PAGES; i++) + { + if (m_pages[i].vpage_id == vpage_id) + { + *p_page = &m_pages[i]; + return NRF_SUCCESS; + } + } + + return NRF_ERROR_NOT_FOUND; +} + + +static uint32_t record_id_new() +{ + return ++m_last_rec_id; +} + + +/**@brief Tags a page as swap, i.e., reserved for GC. */ +static ret_code_t page_tag_write_swap(uint16_t page) +{ + return fs_store(&fs_config, + m_pages[page].start_addr, + (uint32_t const *)&fds_page_tag_swap, + FDS_PAGE_TAG_SIZE); +} + + +/**@brief Tags a page as valid, i.e, ready for storage. */ +static ret_code_t page_tag_write_valid(uint16_t page) +{ + return fs_store(&fs_config, + m_pages[page].start_addr, + (uint32_t const *)&fds_page_tag_valid, + FDS_PAGE_TAG_SIZE); +} + + +/**@brief Tags a valid page as being garbage collected. */ +static ret_code_t page_tag_write_gc(uint16_t page) +{ + return fs_store(&fs_config, + m_pages[page].start_addr + 3, + (uint32_t const *)&fds_page_tag_gc, + 1 /*Words*/); +} + + +/**@brief Given a page and a record, finds the next valid record. */ +static ret_code_t scan_next_valid(uint16_t page, uint32_t const ** p_record) +{ + uint32_t const * p_next_rec = (*p_record); + + if (p_next_rec == NULL) + { + // This if the first invocation on this page, start from the beginning. + p_next_rec = m_pages[page].start_addr + FDS_PAGE_TAG_SIZE; + } + else + { + // Jump to the next record. + p_next_rec += (FDS_HEADER_SIZE + ((fds_header_t*)(*p_record))->tl.length_words); + } + + // Scan until we find a valid record or until the end of the page. + + /** README: We might seek until the write_offset is reached, but it might not + * known at this point. */ + while ((p_next_rec < (m_pages[page].start_addr + FS_PAGE_SIZE_WORDS)) && + (*p_next_rec != FDS_ERASED_WORD)) // Did we jump to an erased word? + { + fds_header_t const * const p_header = (fds_header_t*)p_next_rec; + + if (header_is_valid(p_header)) + { + // Bingo! + *p_record = p_next_rec; + return NRF_SUCCESS; + } + else + { + // The item is not valid, jump to the next. + p_next_rec += (FDS_HEADER_SIZE + (p_header->tl.length_words)); + } + } + + return NRF_ERROR_NOT_FOUND; +} + + +static ret_code_t seek_record(fds_record_desc_t * const p_desc) +{ + uint32_t const * p_record; + uint16_t page; + bool seek_all_pages = false; + + if ((p_desc->ptr_magic == FDS_MAGIC_HWORD) && + (p_desc->gc_magic == m_gc_runs)) + { + // No need to seek the file. + return NRF_SUCCESS; + } + + /** The pointer in the descriptor is not initialized, or GC + * has been run since the last time it was retrieved. + * We must seek the record again. */ + + // Obtain the physical page ID. + if (page_id_from_virtual_id(p_desc->vpage_id, &page) != NRF_SUCCESS) + { + page = 0; + seek_all_pages = true; + } + + do { + // Let's find the address from where we should start seeking the record. + p_record = m_pages[page].start_addr + FDS_PAGE_TAG_SIZE; + + /** Seek for a record with matching ID. + * We might get away with seeking to the page write offset, if it is known. */ + + while ((p_record < (m_pages[page].start_addr + FS_PAGE_SIZE_WORDS)) && + (*p_record != FDS_ERASED_WORD)) + { + fds_header_t const * const p_header = (fds_header_t*)p_record; + + if ((p_header->id != p_desc->record_id) || + (!header_is_valid(p_header))) + { + // ID doesnt't match or the record has been cleared. Jump to the next record. + p_record += FDS_HEADER_SIZE + p_header->tl.length_words; + } + else + { + // Update the pointer in the descriptor. + p_desc->p_rec = p_record; + p_desc->ptr_magic = FDS_MAGIC_HWORD; + p_desc->gc_magic = m_gc_runs; + + return NRF_SUCCESS; + } + } + } while (seek_all_pages ? page++ < FDS_MAX_PAGES : 0); + + return NRF_ERROR_NOT_FOUND; +} + + +static ret_code_t find_record(fds_type_id_t const * const p_type, + fds_instance_id_t const * const p_inst, + fds_record_desc_t * const p_desc, + fds_find_token_t * const p_token) +{ + if (!flag_is_set(FDS_FLAG_INITIALIZED)) + { + return NRF_ERROR_INVALID_STATE; + } + + // Here we distinguish between the first invocation and the and the others. + if ((p_token->magic != FDS_MAGIC_WORD) || + !address_within_page_bounds(p_token->p_addr)) // Is the address is really okay? + { + // Initialize the token. + p_token->magic = FDS_MAGIC_WORD; + p_token->vpage_id = 0; + p_token->p_addr = NULL; + } + else + { + // Look past the last record address. + p_token->p_addr += (FDS_HEADER_SIZE + ((fds_header_t*)p_token->p_addr)->tl.length_words); + } + + // Begin (or resume) searching for a record. + for (; p_token->vpage_id < FDS_MAX_PAGES; p_token->vpage_id++) + { + uint16_t page = 0; + + // Obtain the physical page ID. + page_id_from_virtual_id(p_token->vpage_id, &page); + + if (m_pages[page].page_type != FDS_PAGE_VALID) + { + // Skip this page. + continue; + } + + if (p_token->p_addr == NULL) + { + // If it's the first time the function is run, initialize the pointer. + p_token->p_addr = m_pages[page].start_addr + FDS_PAGE_TAG_SIZE; + } + + // Seek a valid record on this page, starting from the address stored in the token. + while ((p_token->p_addr < (m_pages[page].start_addr + FS_PAGE_SIZE_WORDS)) && + (*p_token->p_addr != FDS_ERASED_WORD)) // Did we jump to an erased word? + { + fds_header_t const * const p_header = (fds_header_t*)p_token->p_addr; + + if (header_is_valid(p_header)) + { + // A valid record was found, check its header for a match. + bool item_match = false; + + if (p_type != NULL) + { + if (p_header->tl.type == *p_type) + { + item_match = true; + } + } + + if (p_inst != NULL) + { + if (p_header->ic.instance == *p_inst) + { + item_match = (p_type == NULL) ? true : item_match && true; + } + else + { + item_match = false; + } + } + + if (item_match) + { + // We found the record! Update the descriptor. + p_desc->vpage_id = m_pages[page].vpage_id; + p_desc->record_id = p_header->id; + + p_desc->p_rec = p_token->p_addr; + p_desc->ptr_magic = FDS_MAGIC_HWORD; + p_desc->gc_magic = m_gc_runs; + + return NRF_SUCCESS; + } + } + // Jump to the next record. + p_token->p_addr += (FDS_HEADER_SIZE + (p_header->tl.length_words)); + } + + /** We have seeked an entire page. Set the address in the token to NULL + * so that it will be set again on the next iteration. */ + p_token->p_addr = NULL; + } + + /** If we couldn't find the record, zero the token structure + * so that it can be reused. */ + p_token->magic = 0x00; + + return NRF_ERROR_NOT_FOUND; +} + + +static void gc_init() +{ + // Set which pages to GC. + for (uint16_t i = 0; i < FDS_MAX_PAGES; i++) + { + m_gc.do_gc_page[i] = (m_pages[i].page_type == FDS_PAGE_VALID); + } +} + + +static void gc_reset() +{ + m_gc.state = BEGIN; + m_gc.cur_page = 0; + m_gc.p_scan_addr = NULL; +} + + +static void gc_set_state(fds_gc_state_t new_state) +{ + m_gc.state = new_state; +} + + +static ret_code_t gc_get_next_page(uint16_t * const next_page) +{ + for (uint16_t i = 0; i < FDS_MAX_PAGES; i++) + { + if (m_gc.do_gc_page[i]) + { + uint16_t records_open; + + CRITICAL_SECTION_ENTER(); + records_open = m_pages[i].records_open; + CRITICAL_SECTION_EXIT(); + + // Do not attempt to GC this page anymore. + m_gc.do_gc_page[i] = false; + + // Only GC pages with no open records. + if (records_open == 0) + { + *next_page = i; + return NRF_SUCCESS; + } + } + } + + return NRF_ERROR_NOT_FOUND; +} + + +static ret_code_t gc_page() +{ + ret_code_t ret; + + ret = gc_get_next_page(&m_gc.cur_page); + + // No pages left to GC. GC has terminated. Reset GC data. + if (ret != NRF_SUCCESS) + { + gc_reset(); + + return COMMAND_COMPLETED; + } + + // Prepare to GC the page. + gc_set_state(GC_PAGE); + + // Flag the page as being garbage collected. + ret = page_tag_write_gc(m_gc.cur_page); + + if (ret != NRF_SUCCESS) + { + return ret; + } + + return COMMAND_EXECUTING; +} + + +static ret_code_t gc_copy_record() +{ + ret_code_t fs_ret; + + // We have found a record to copy. + fds_record_t const * const p_record = (fds_record_t*)m_gc.p_scan_addr; + + gc_set_state(COPY_RECORD); + + // Copy the item to swap. + fs_ret = fs_store(&fs_config, + m_pages[m_gc.swap_page].start_addr + m_pages[m_gc.swap_page].write_offset, + (uint32_t*)p_record, + FDS_HEADER_SIZE + p_record->header.tl.length_words); + + if (fs_ret != NRF_SUCCESS) + { + // Oops :( + // This is an error. Can we recover? + } + + // Remember to update the swap page write offset. + m_pages[m_gc.swap_page].write_offset += (FDS_HEADER_SIZE + p_record->header.tl.length_words); + + return COMMAND_EXECUTING; +} + + +static ret_code_t gc_ready_swap_page() +{ + ret_code_t fs_ret; + + /** A page has been scanned through. All valid records found were copied to swap. + * The swap page can now be flagged as a valid page. */ + gc_set_state(READY_SWAP); + + fs_ret = page_tag_write_valid(m_gc.swap_page); + if (fs_ret != NRF_SUCCESS) + { + return fs_ret; + } + + /** Do not update the page type in the internal page structure (m_pages) + * right away. (why?) */ + return COMMAND_EXECUTING; +} + + +static ret_code_t gc_seek_record() +{ + // Let's find a valid record which has not been copied yet. + if (scan_next_valid(m_gc.cur_page, &m_gc.p_scan_addr) == NRF_SUCCESS) + { + /** The record is guaranteed to fit in the destination page, + * so we don't need to check its size. */ + return gc_copy_record(); + } + else + { + /** No more (uncopied) records left on this page. + * The swap page can now be marked as a valid page. */ + return gc_ready_swap_page(); + } +} + + +static ret_code_t gc_new_swap_page() +{ + ret_code_t fs_ret; + uint16_t vpage_id; + + gc_set_state(NEW_SWAP); + + // Save the swap page virtual page ID. + vpage_id = m_pages[m_gc.swap_page].vpage_id; + + /** The swap page has been marked as valid in Flash. We copy the GC'ed page + * write_offset and virtual page ID. */ + m_pages[m_gc.swap_page].page_type = FDS_PAGE_VALID; + m_pages[m_gc.swap_page].vpage_id = m_pages[m_gc.cur_page].vpage_id; + m_pages[m_gc.swap_page].words_reserved = m_pages[m_gc.cur_page].words_reserved; + + // The new swap page is now the page we just GC. + m_gc.swap_page = m_gc.cur_page; + + // Update the write_offset, words_reserved and vpage_id fields for the new swap page. + m_pages[m_gc.swap_page].page_type = FDS_PAGE_SWAP; + m_pages[m_gc.swap_page].vpage_id = vpage_id; + m_pages[m_gc.swap_page].write_offset = FDS_PAGE_TAG_SIZE; + m_pages[m_gc.swap_page].words_reserved = 0; + + /** Finally, erase the new swap page. Remember we still have to flag this + * new page as swap, but we'll wait the callback for this operation to do so. */ + fs_ret = fs_erase(&fs_config, + (uint32_t*)m_pages[m_gc.swap_page].start_addr, + FS_PAGE_SIZE_WORDS); + + if (fs_ret != NRF_SUCCESS) + { + return fs_ret; + } + + return COMMAND_EXECUTING; +} + + +static ret_code_t gc_new_swap_page_init() +{ + ret_code_t fs_ret; + + gc_set_state(INIT_SWAP); + + fs_ret = page_tag_write_swap(m_gc.swap_page); + if (fs_ret != NRF_SUCCESS) + { + return fs_ret; + } + + return COMMAND_EXECUTING; +} + + +static ret_code_t gc_execute(uint32_t result) +{ + // TODO: Handle resuming GC. + + ret_code_t ret; + + if (result != NRF_SUCCESS) + { + // An operation failed. Report to the application. + return result; + } + + switch (m_gc.state) + { + case BEGIN: + { + // Increment the number of times the GC has been run. + m_gc_runs++; + // Sets up a list of pages to GC. + gc_init(); + // Go ! + ret = gc_page(); + } break; + + case GC_PAGE: + /** A page has been successfully flagged as being GC. + * Look for valid records to copy. */ + ret = gc_seek_record(); + break; + + case COPY_RECORD: + /** A record has been copied to swap. + * Look for more records to copy. */ + ret = gc_seek_record(); + break; + + case READY_SWAP: + /** The swap page has been flagged as 'valid' (ready). + * Let's prepare a new swap page. */ + ret = gc_new_swap_page(); + break; + + case NEW_SWAP: + // A new swap page has been prepared. Let's flag it as swap. + ret = gc_new_swap_page_init(); + break; + + case INIT_SWAP: + /** The swap was flagged as swap in flash. Let's compress another page. + * Be sure to update the address where to scan from. */ + m_gc.p_scan_addr = NULL; + ret = gc_page(); + break; + + default: + // Should really not happen. + ret = NRF_ERROR_INTERNAL; + break; + } + + return ret; +} + + +/**@brief Function for initializing the command queue. */ +static void queues_init(void) +{ + memset(&m_cmd_queue, 0, sizeof(fds_cmd_queue_t)); + memset(&m_chunk_queue, 0, sizeof(fds_chunk_queue_t)); +} + + +void cmd_queue_next(fds_cmd_t ** pp_cmd) +{ + if (*pp_cmd != &m_cmd_queue.cmd[FDS_CMD_QUEUE_SIZE - 1]) + { + (*pp_cmd)++; + return; + } + + *pp_cmd = &m_cmd_queue.cmd[0]; +} + + +void chunk_queue_next(fds_record_chunk_t ** pp_chunk) +{ + if ((*pp_chunk) != &m_chunk_queue.chunk[FDS_CHUNK_QUEUE_SIZE - 1]) + { + (*pp_chunk)++; + return; + } + + *pp_chunk = &m_chunk_queue.chunk[0]; +} + + +/**@brief Advances one position in the command queue. Returns true if the queue is not empty. */ +static bool cmd_queue_advance(void) +{ + // Reset the current element. + memset(&m_cmd_queue.cmd[m_cmd_queue.rp], 0, sizeof(fds_cmd_t)); + + CRITICAL_SECTION_ENTER(); + if (m_cmd_queue.count != 0) + { + // Advance in the queue, wrapping around if necessary. + m_cmd_queue.rp = (m_cmd_queue.rp + 1) % FDS_CMD_QUEUE_SIZE; + m_cmd_queue.count--; + } + CRITICAL_SECTION_EXIT(); + + return m_cmd_queue.count != 0; +} + + +/**@brief Returns the current chunk, and advances to the next in the queue. */ +static bool chunk_queue_get_and_advance(fds_record_chunk_t ** pp_chunk) +{ + bool chunk_popped = false; + + CRITICAL_SECTION_ENTER(); + if (m_chunk_queue.count != 0) + { + // Point to the current chunk and advance the queue. + *pp_chunk = &m_chunk_queue.chunk[m_chunk_queue.rp]; + + m_chunk_queue.rp = (m_chunk_queue.rp + 1) % FDS_CHUNK_QUEUE_SIZE; + m_chunk_queue.count--; + + chunk_popped = true; + } + CRITICAL_SECTION_EXIT(); + + return chunk_popped; +} + + +static bool chunk_queue_skip(uint8_t num_op) +{ + bool chunk_skipped = false; + + CRITICAL_SECTION_ENTER(); + if (num_op <= m_chunk_queue.count) + { + m_chunk_queue.count -= num_op; + chunk_skipped = true; + } + CRITICAL_SECTION_EXIT(); + + return chunk_skipped; +} + + +/**@brief Reserves resources on both queues. */ +static ret_code_t queue_reserve(uint8_t num_cmd, + uint8_t num_chunks, + fds_cmd_t ** pp_cmd, + fds_record_chunk_t ** pp_chunk) +{ + uint8_t cmd_index; + uint8_t chunk_index; + + // This is really just being safe. + if (pp_cmd == NULL || ((pp_chunk == NULL) && (num_chunks != 0))) + { + return NRF_ERROR_NULL; + } + + if (num_cmd == 0) + { + return NRF_ERROR_INVALID_DATA; + } + + CRITICAL_SECTION_ENTER(); + + // Ensure there is enough space in the queues. + if ((m_cmd_queue.count > FDS_CMD_QUEUE_SIZE - num_cmd) || + (m_chunk_queue.count > FDS_CHUNK_QUEUE_SIZE - num_chunks)) + { + CRITICAL_SECTION_EXIT(); + return NRF_ERROR_BUSY; + } + + // Find the write position in the commands queue. + cmd_index = m_cmd_queue.count; + cmd_index += m_cmd_queue.rp; + cmd_index = cmd_index % FDS_CMD_QUEUE_SIZE; + + *pp_cmd = &m_cmd_queue.cmd[cmd_index]; + m_cmd_queue.count += num_cmd; + + /* If no operations are associated with the command, such as is the case + * for initialization and compression, pp_chunk can be NULL. */ + if (num_chunks != 0) + { + chunk_index = m_chunk_queue.count; + chunk_index += m_chunk_queue.rp; + chunk_index = chunk_index % FDS_CHUNK_QUEUE_SIZE; + + *pp_chunk = &m_chunk_queue.chunk[chunk_index]; + m_chunk_queue.count += num_chunks; + } + + CRITICAL_SECTION_EXIT(); + + return NRF_SUCCESS; +} + + +/**@brief Cancel the reservation on resources on queues. */ +static void queue_reserve_cancel(uint8_t num_cmd, uint8_t num_chunks) +{ + CRITICAL_SECTION_ENTER(); + m_cmd_queue.count -= num_cmd; + m_chunk_queue.count -= num_chunks; + CRITICAL_SECTION_EXIT(); +} + + +static void pages_init(uint16_t * const p_pages_avail, + bool * const p_write_page_tag, + bool * const p_resume_comp) +{ + *p_pages_avail = 0; + *p_write_page_tag = false; + *p_resume_comp = false; + + /** Scan pages and setup page data. + * This function does NOT perform write operations in flash. */ + for (uint16_t i = 0; i < FDS_MAX_PAGES; i++) + { + // Initialize page data. Note that start_addr must be set BEFORE invoking page_identify(). + m_pages[i].start_addr = fs_config.p_start_addr + (i * FS_PAGE_SIZE_WORDS); + m_pages[i].write_offset = FDS_PAGE_TAG_SIZE; + m_pages[i].vpage_id = i; + m_pages[i].records_open = 0; + m_pages[i].words_reserved = 0; + + m_pages[i].page_type = page_identify(i); + + switch (m_pages[i].page_type) + { + case FDS_PAGE_UNDEFINED: + { + if (page_is_empty(i)) + { + /* We have found an erased page, which can be initialized. + * This will require a write in flash. */ + m_pages[i].page_type = FDS_PAGE_ERASED; + *p_write_page_tag = true; + } + } break; + + case FDS_PAGE_VALID: + { + /** If a page is valid, we update its write offset. + * Additionally, page_scan will update the last known record ID. */ + page_scan(i, &m_pages[i].write_offset); + (*p_pages_avail)++; + } break; + + case FDS_PAGE_SWAP: + { + m_gc.swap_page = i; + m_swap_page_avail = true; + } break; + + case FDS_PAGE_GC: + { + /** There is an ongoing garbage collection. + * We should resume the operation, which we don't yet. */ + m_gc.cur_page = i; + m_gc.state = GC_PAGE; + *p_resume_comp = true; + } break; + + default: + break; + } + } +} + + +// NOTE: Adds FDS_HEADER_SIZE automatically. +static ret_code_t write_space_reserve(uint16_t length_words, uint16_t * vpage_id) +{ + bool space_reserved = false; + uint16_t total_len_words = length_words + FDS_HEADER_SIZE; + + if (total_len_words >= FS_PAGE_SIZE_WORDS - FDS_PAGE_TAG_SIZE) + { + return NRF_ERROR_INVALID_LENGTH; + } + + for (uint16_t page = 0; page < FDS_MAX_PAGES; page++) + { + if ((m_pages[page].page_type == FDS_PAGE_VALID) && + (page_has_space(page, total_len_words))) + { + space_reserved = true; + *vpage_id = m_pages[page].vpage_id; + + CRITICAL_SECTION_ENTER(); + m_pages[page].words_reserved += total_len_words; + CRITICAL_SECTION_EXIT(); + + break; + } + } + + return space_reserved ? NRF_SUCCESS : NRF_ERROR_NO_MEM; +} + + +static bool chunk_is_aligned(fds_record_chunk_t const * const p_chunk, uint8_t num_parts) +{ + for (uint8_t i = 0; i < num_parts; i++) + { + if (!is_word_aligned(p_chunk[i].p_data)) + { + return false; + } + } + + return true; +} + + +static ret_code_t init_execute(uint32_t result, uint32_t const * p_page_addr) +{ + uint16_t cur_page; + bool page_tag_written = false; + + if (result != NRF_SUCCESS) + { + // Oops. Error. + return result; + } + + // Here we just distinguish between the first invocation and the others. + cur_page = p_page_addr == NULL ? 0 : page_by_addr(p_page_addr) + 1; + + if (cur_page == FDS_MAX_PAGES) + { + // We have finished. We'd need to set some flags. + flag_set(FDS_FLAG_INITIALIZED); + flag_clear(FDS_FLAG_INITIALIZING); + + return COMMAND_COMPLETED; + } + + while (cur_page < FDS_MAX_PAGES && !page_tag_written) + { + if (m_pages[cur_page].page_type == FDS_PAGE_ERASED) + { + page_tag_written = true; + + if (m_swap_page_avail) + { + if (page_tag_write_valid(cur_page) != NRF_SUCCESS) + { + // Oops. Error. + } + // Update the page type. + m_pages[cur_page].page_type = FDS_PAGE_VALID; + } + else + { + if (page_tag_write_swap(cur_page) != NRF_SUCCESS) + { + // Oops. Error. + } + // Update the page type. + m_pages[cur_page].page_type = FDS_PAGE_SWAP; + + /** Update compression data. We set this information in init_pages + * if it is available, otherwise, we should set it here. */ + m_swap_page_avail = true; + m_gc.swap_page = cur_page; + } + } + + cur_page++; + } + + if (!page_tag_written) + { + if (m_swap_page_avail) + { + return COMMAND_COMPLETED; + } + else + { + // There is no empty space to use as swap. + // Notify user that no compression is available? + } + } + + return COMMAND_EXECUTING; +} + + +/**@brief Function to execute write and update commands. + * + */ +static ret_code_t store_execute(uint32_t result, fds_cmd_t * const p_cmd) +{ + ret_code_t fs_ret; + fds_record_chunk_t * p_chunk = NULL; + fds_page_t * p_page = NULL; + uint32_t * p_write_addr; + + // Using virtual page IDs allows other operations to be queued even if GC has been requested. + page_from_virtual_id(p_cmd->vpage_id, &p_page); + + if (result != NRF_SUCCESS) + { + // The previous operation has failed, update the page data. + p_page->write_offset += (FDS_HEADER_SIZE + (p_cmd->chunk_offset - FDS_WRITE_OFFSET_DATA)); + p_page->words_reserved -= (FDS_HEADER_SIZE + (p_cmd->chunk_offset - FDS_WRITE_OFFSET_DATA)); + + return result; + } + + // Compute the write address (just syntatic sugar). + p_write_addr = (uint32_t*)(p_page->start_addr + p_page->write_offset); + + // Execute the operation. + switch (p_cmd->op_code) + { + case FDS_OP_WRITE_TL: + { + fs_ret = fs_store(&fs_config, + p_write_addr + FDS_WRITE_OFFSET_TL, + (uint32_t*)&p_cmd->record_header.tl, + FDS_HEADER_SIZE_TL /*Words*/); + + // Set the next operation to be executed. + p_cmd->op_code = FDS_OP_WRITE_ID; + + } break; + + case FDS_OP_WRITE_ID: + { + fs_ret = fs_store(&fs_config, + p_write_addr + FDS_WRITE_OFFSET_ID, + (uint32_t*)&p_cmd->record_header.id, + FDS_HEADER_SIZE_ID /*Words*/); + + p_cmd->op_code = FDS_OP_WRITE_CHUNK; + + } break; + + case FDS_OP_WRITE_CHUNK: + { + // Decrement the number of chunks left to write. + p_cmd->num_chunks--; + + // Retrieve the chunk to be written. + chunk_queue_get_and_advance(&p_chunk); + + fs_ret = fs_store(&fs_config, + p_write_addr + p_cmd->chunk_offset, + p_chunk->p_data, + p_chunk->length_words); + + // Accumulate the offset. + p_cmd->chunk_offset += p_chunk->length_words; + + if (p_cmd->num_chunks == 0) + { + /** We have written all the record chunks; we'll write + * IC last as a mean to 'validate' the record. */ + p_cmd->op_code = FDS_OP_WRITE_IC; + } + + } break; + + case FDS_OP_WRITE_IC: + { + fs_ret = fs_store(&fs_config, + p_write_addr + FDS_WRITE_OFFSET_IC, + (uint32_t*)&p_cmd->record_header.ic, + FDS_HEADER_SIZE_IC /*Words*/); + + // This is the final operation. + p_cmd->op_code = FDS_OP_DONE; + + } break; + + case FDS_OP_DONE: + { + // We have successfully written down the IC. The command has completed successfully. + p_page->write_offset += (FDS_HEADER_SIZE + (p_cmd->chunk_offset - FDS_WRITE_OFFSET_DATA)); + p_page->words_reserved -= (FDS_HEADER_SIZE + (p_cmd->chunk_offset - FDS_WRITE_OFFSET_DATA)); + + return COMMAND_COMPLETED; + + }; + + default: + fs_ret = NRF_ERROR_INTERNAL; + break; + } + + // If fs_store did not succeed, the command has failed. + if (fs_ret != NRF_SUCCESS) + { + /** We're not going to receive a callback from fstorage + * so we update the page data right away. */ + p_page->write_offset += (FDS_HEADER_SIZE + (p_cmd->chunk_offset - FDS_WRITE_OFFSET_DATA)); + p_page->words_reserved -= (FDS_HEADER_SIZE + (p_cmd->chunk_offset - FDS_WRITE_OFFSET_DATA)); + + // We should propagate the error from fstorage. + return fs_ret; + } + + // An operation has successfully been executed. Wait for the callback. + return COMMAND_EXECUTING; +} + + +static ret_code_t clear_execute(ret_code_t result, fds_cmd_t * const p_cmd) +{ + ret_code_t ret; + fds_record_desc_t desc; + + // This must persist across calls. + static fds_find_token_t tok; + + if (result != NRF_SUCCESS) + { + // A previous operation has failed. Propagate the error. + return result; + } + + switch (p_cmd->op_code) + { + case FDS_OP_CLEAR_TL: + { + // We were provided a descriptor for the record. + desc.vpage_id = p_cmd->vpage_id; + desc.record_id = p_cmd->record_header.id; + + /** Unfortunately, we always seek the record in this case, + * because we don't buffer an entire record descriptor in the + * fds_cmd_t structure. Keep in mind though, that we will + * seek one page at most. */ + if (seek_record(&desc) != NRF_SUCCESS) + { + // The record never existed, or it is already cleared. + ret = NRF_ERROR_NOT_FOUND; + } + else + { + // Copy the record key, so that it may be returned in the callback. + p_cmd->record_header.tl.type = ((fds_header_t*)desc.p_rec)->tl.type; + p_cmd->record_header.ic.instance = ((fds_header_t*)desc.p_rec)->ic.instance; + + ret = fs_store(&fs_config, + desc.p_rec, + (uint32_t*)&m_fds_tl_invalid, + FDS_HEADER_SIZE_TL); + } + + p_cmd->op_code = FDS_OP_DONE; + + } break; + + case FDS_OP_CLEAR_INSTANCE: + { + if (find_record(NULL, &p_cmd->record_header.ic.instance, + &desc, &tok) != NRF_SUCCESS) + { + // No more records to be found. + p_cmd->op_code = FDS_OP_DONE; + + // Zero the token, so that we may reuse it. + memset(&tok, 0, sizeof(fds_find_token_t)); + + /** We won't receive a callback, since no flash operation + * was initiated. The command has finished. */ + ret = COMMAND_COMPLETED; + } + else + { + ret = fs_store(&fs_config, + desc.p_rec, + (uint32_t*)&m_fds_tl_invalid, + FDS_HEADER_SIZE_TL); + } + } break; + + case FDS_OP_DONE: + { + /** The last operation completed successfully. + * The command has finished. Return. */ + ret = COMMAND_COMPLETED; + } break; + + default: + ret = NRF_ERROR_INVALID_DATA; + break; + } + + // Await for the operation result. + return ret; +} + + +static ret_code_t cmd_queue_process(void) +{ + ret_code_t ret; + fds_cmd_t * const p_cmd = &m_cmd_queue.cmd[m_cmd_queue.rp]; + + switch (p_cmd->id) + { + case FDS_CMD_INIT: + ret = init_execute(NRF_SUCCESS, NULL); + break; + + case FDS_CMD_WRITE: + case FDS_CMD_UPDATE: + ret = store_execute(NRF_SUCCESS, p_cmd); + break; + + case FDS_CMD_CLEAR: + case FDS_CMD_CLEAR_INST: + ret = clear_execute(NRF_SUCCESS, p_cmd); + break; + + case FDS_CMD_GC: + ret = gc_execute(NRF_SUCCESS); + break; + + default: + ret = NRF_ERROR_FORBIDDEN; + break; + } + + if ((ret == COMMAND_EXECUTING) || (ret == COMMAND_COMPLETED)) + { + return NRF_SUCCESS; + } + + // This is an error. + return ret; +} + + +static ret_code_t cmd_queue_process_start(void) +{ + bool start_processing = false; + + if (!flag_is_set(FDS_FLAG_PROCESSING)) + { + flag_set(FDS_FLAG_PROCESSING); + start_processing = true; + } + + if (!start_processing) + { + // We are awaiting a callback, so there is no need to manually start queue processing. + return NRF_SUCCESS; + } + + return cmd_queue_process(); +} + + +static void fs_callback(uint8_t op_code, + uint32_t result, + uint32_t const * p_data, + fs_length_t length) +{ + ret_code_t ret; + fds_cmd_t * p_cmd = &m_cmd_queue.cmd[m_cmd_queue.rp]; + fds_record_key_t record_key; + + switch (p_cmd->id) + { + case FDS_CMD_INIT: + ret = init_execute(result, p_data); + break; + + case FDS_CMD_WRITE: + case FDS_CMD_UPDATE: + ret = store_execute(result, p_cmd); + break; + + case FDS_CMD_CLEAR: + case FDS_CMD_CLEAR_INST: + ret = clear_execute(result, p_cmd); + break; + + case FDS_CMD_GC: + ret = gc_execute(result); + break; + + default: + // Should not happen. + ret = NRF_ERROR_INTERNAL; + break; + } + + if (ret == COMMAND_EXECUTING /*=NRF_SUCCESS*/) + { + /** The current command is still being processed. + * The command queue does not need to advance. */ + return; + } + + // Initialize the fds_record_key_t structure needed for the callback. + record_key.type = p_cmd->record_header.tl.type; + record_key.instance = p_cmd->record_header.ic.instance; + + // The command has either completed or an operation (and thus the command) has failed. + if (ret == COMMAND_COMPLETED) + { + // The command has completed successfully. Notify the application. + app_notify(NRF_SUCCESS, p_cmd->id, p_cmd->record_header.id, record_key); + } + else + { + /** An operation has failed. This is fatal for the execution of a command. + * Skip other operations associated with the current command. + * Notify the user of the failure. */ + chunk_queue_skip(p_cmd->num_chunks); + app_notify(ret /*=result*/, p_cmd->id, p_cmd->record_header.id, record_key); + } + + // Advance the command queue, and if there is still something in the queue, process it. + if (cmd_queue_advance()) + { + /** Only process the queue if there are no pending commands being queued, since they + * will begin to process the queue on their own. Be sure to clear + * the flag FDS_FLAG_PROCESSING though ! */ + if (atomic_counter_is_zero()) + { + cmd_queue_process(); + } + else + { + flag_clear(FDS_FLAG_PROCESSING); + } + } + else + { + /** No more elements in the queue. Clear the FDS_FLAG_PROCESSING flag, + * so that new commands can start the queue processing. */ + flag_clear(FDS_FLAG_PROCESSING); + } +} + + +ret_code_t fds_init() +{ + ret_code_t fs_ret; + fds_cmd_t * p_cmd; + uint16_t pages_avail; + bool write_page_tag; + bool resume_compression; + + fds_record_key_t const dummy_key = {.type = FDS_TYPE_ID_INVALID, + .instance = FDS_INSTANCE_ID_INVALID}; + + if (flag_is_set(FDS_FLAG_INITIALIZED)) + { + // Notify immediately. + app_notify(NRF_SUCCESS, FDS_CMD_INIT, 0 /*unused*/, dummy_key /*unused*/); + return NRF_SUCCESS; + } + + if (flag_is_set(FDS_FLAG_INITIALIZING)) + { + return NRF_ERROR_INVALID_STATE; + } + + fs_ret = fs_init(); + if (fs_ret != NRF_SUCCESS) + { + // fs_init() failed, propagate the error. + return fs_ret; + } + + queues_init(); + + /** Initialize the last known record to zero. + * Its value will be updated by page_scan() called in pages_init(). */ + m_last_rec_id = 0; + + // Initialize the page table containing all info on pages (address, type etc). + pages_init(&pages_avail, &write_page_tag, &resume_compression); + + if (pages_avail == 0 && !write_page_tag) + { + return NRF_ERROR_NO_MEM; + } + + /** This flag means fds_init() has been called. However, + * the module is NOT yet initialized. */ + flag_set(FDS_FLAG_INITIALIZING); + + if (resume_compression) + { + return NRF_SUCCESS; + } + + if (write_page_tag) + { + if (queue_reserve(FDS_CMD_QUEUE_SIZE_INIT, 0, &p_cmd, NULL) != NRF_SUCCESS) + { + // Should never happen. + return NRF_ERROR_BUSY; + } + + // Initialize the command in the queue. + p_cmd->id = FDS_CMD_INIT; + + return cmd_queue_process_start(); + } + else + { + /* No flash operation is necessary for initialization. + * We can notify the application immediately. */ + flag_set (FDS_FLAG_INITIALIZED); + flag_clear(FDS_FLAG_INITIALIZING); + app_notify(NRF_SUCCESS, FDS_CMD_INIT, 0 /*unused*/, dummy_key /*unused*/); + } + + return NRF_SUCCESS; +} + + +ret_code_t fds_open(fds_record_desc_t * const p_desc, + fds_record_t * const p_record) +{ + uint16_t page; + + if (p_desc == NULL || p_record == NULL) + { + return NRF_ERROR_NULL; + } + + if (page_id_from_virtual_id(p_desc->vpage_id, &page) != NRF_SUCCESS) + { + // Should not happen. + return NRF_ERROR_INVALID_DATA; + } + + // Seek the record if necessary. + if (seek_record(p_desc) == NRF_SUCCESS) + { + if (header_is_valid((fds_header_t*)p_desc->p_rec)) + { + CRITICAL_SECTION_ENTER(); + m_pages[page].records_open++; + CRITICAL_SECTION_EXIT(); + + p_record->header = *((fds_header_t*)p_desc->p_rec); + p_record->p_data = (p_desc->p_rec + FDS_HEADER_SIZE); + + return NRF_SUCCESS; + } + } + + /** The record could not be found. + * It either never existed or it has been cleared. */ + return NRF_ERROR_NOT_FOUND; +} + + +ret_code_t fds_close(fds_record_desc_t const * const p_desc) +{ + uint16_t page; + + if (p_desc == NULL) + { + return NRF_ERROR_NULL; + } + + if (page_id_from_virtual_id(p_desc->vpage_id, &page) != NRF_SUCCESS) + { + return NRF_ERROR_INVALID_DATA; + } + + CRITICAL_SECTION_ENTER(); + m_pages[page].records_open--; + CRITICAL_SECTION_EXIT(); + + return NRF_SUCCESS; +} + + +static ret_code_t write_enqueue(fds_record_desc_t * const p_desc, + fds_record_key_t key, + uint8_t num_chunks, + fds_record_chunk_t chunks[], + fds_write_token_t const * const p_tok, + bool do_update) +{ + ret_code_t ret; + fds_cmd_t * p_cmd; + fds_record_chunk_t * p_chunk = NULL; + uint16_t vpage_id; + uint16_t length_words = 0; + uint8_t cmd_queue_elems; + + if (!flag_is_set(FDS_FLAG_INITIALIZED)) + { + return NRF_ERROR_INVALID_STATE; + } + + if ((key.type == FDS_TYPE_ID_INVALID) || + (key.instance == FDS_INSTANCE_ID_INVALID)) + { + return NRF_ERROR_INVALID_DATA; + } + + if (!chunk_is_aligned(chunks, num_chunks)) + { + return NRF_ERROR_INVALID_ADDR; + } + + cmd_queue_elems = do_update ? FDS_CMD_QUEUE_SIZE_UPDATE : FDS_CMD_QUEUE_SIZE_WRITE; + + // Reserve space on both queues, and obtain pointers to the first elements reserved. + ret = queue_reserve(cmd_queue_elems, + num_chunks, + &p_cmd, + &p_chunk); + + if (ret != NRF_SUCCESS) + { + return ret; + } + + // No space was previously reserved for this operation. + if (p_tok == NULL) + { + // Compute the total length of the record. + for (uint8_t i = 0; i < num_chunks; i++) + { + length_words += chunks[i].length_words; + } + + /** Find a page where we can write the data. Reserve the space necessary + * to write the metadata as well. */ + ret = write_space_reserve(length_words, &vpage_id); + if (ret != NRF_SUCCESS) + { + // If there is no space available, cancel the queue reservation. + queue_reserve_cancel(cmd_queue_elems, num_chunks); + return ret; + } + } + else + { + length_words = p_tok->length_words; + vpage_id = p_tok->vpage_id; + } + + // Initialize the command. + p_cmd->id = do_update ? FDS_CMD_UPDATE : FDS_CMD_WRITE; + p_cmd->op_code = FDS_OP_WRITE_TL; + p_cmd->num_chunks = num_chunks; + p_cmd->chunk_offset = FDS_WRITE_OFFSET_DATA; + p_cmd->vpage_id = vpage_id; + + // Fill in the header information. + p_cmd->record_header.id = record_id_new(); + p_cmd->record_header.tl.type = key.type; + p_cmd->record_header.tl.length_words = length_words; + p_cmd->record_header.ic.instance = key.instance; + p_cmd->record_header.ic.checksum = 0; + + // Buffer the record chunks in the queue. + for (uint8_t i = 0; i < num_chunks; i++) + { + p_chunk->p_data = chunks[i].p_data; + p_chunk->length_words = chunks[i].length_words; + chunk_queue_next(&p_chunk); + } + + if (do_update) + { + // Clear + cmd_queue_next(&p_cmd); + p_cmd->id = FDS_CMD_CLEAR; + p_cmd->op_code = FDS_OP_CLEAR_TL; + + p_cmd->vpage_id = p_desc->vpage_id; + p_cmd->record_header.id = p_desc->record_id; + } + + // Initialize the record descriptor, if provided. + if (p_desc != NULL) + { + p_desc->vpage_id = vpage_id; + // Don't invoke record_id_new() again. + p_desc->record_id = p_cmd->record_header.id; + } + + return cmd_queue_process_start(); +} + + +ret_code_t fds_reserve(fds_write_token_t * const p_tok, uint16_t length_words) +{ + uint16_t vpage_id; + + if (!flag_is_set(FDS_FLAG_INITIALIZED)) + { + return NRF_ERROR_INVALID_STATE; + } + + if (p_tok == NULL) + { + return NRF_ERROR_NULL; + } + + // Reserve space on the page. write_space_reserve() accounts for the header. + if (write_space_reserve(length_words, &vpage_id) == NRF_SUCCESS) + { + p_tok->vpage_id = vpage_id; + p_tok->length_words = length_words; + + return NRF_SUCCESS; + } + + return NRF_ERROR_NO_MEM; +} + + +ret_code_t fds_reserve_cancel(fds_write_token_t * const p_tok) +{ + fds_page_t * p_page; + + if (!flag_is_set(FDS_FLAG_INITIALIZED)) + { + return NRF_ERROR_INVALID_STATE; + } + + if (p_tok == NULL) + { + return NRF_ERROR_NULL; + } + + if (page_from_virtual_id(p_tok->vpage_id, &p_page) != NRF_SUCCESS) + { + // Could not find the virtual page. This shouldn't happen. + return NRF_ERROR_INVALID_DATA; + } + + if ((p_page->words_reserved - p_tok->length_words) < 0) + { + /** We are trying to cancel a reservation for more words than how many are + * currently reserved on the page. This is shouldn't happen. */ + return NRF_ERROR_INVALID_DATA; + } + + // Free the space which had been reserved. + p_page->words_reserved -= p_tok->length_words; + + // Clean the token. + p_tok->vpage_id = 0; + p_tok->length_words = 0; + + return NRF_SUCCESS; +} + + +ret_code_t fds_write(fds_record_desc_t * const p_desc, + fds_record_key_t key, + uint8_t num_chunks, + fds_record_chunk_t chunks[]) +{ + ret_code_t ret; + atomic_counter_inc(); + ret = write_enqueue(p_desc, key, num_chunks, chunks, NULL, false /*not an update*/); + atomic_counter_dec(); + return ret; +} + + +ret_code_t fds_write_reserved(fds_write_token_t const * const p_tok, + fds_record_desc_t * const p_desc, + fds_record_key_t key, + uint8_t num_chunks, + fds_record_chunk_t chunks[]) +{ + ret_code_t ret; + atomic_counter_inc(); + ret = write_enqueue(p_desc, key, num_chunks, chunks, p_tok, false /*not an update*/); + atomic_counter_dec(); + return ret; +} + + +static ret_code_t clear_enqueue(fds_record_desc_t * const p_desc) +{ + ret_code_t ret; + fds_cmd_t * p_cmd; + + if (!flag_is_set(FDS_FLAG_INITIALIZED)) + { + return NRF_ERROR_INVALID_STATE; + } + + if (p_desc == NULL) + { + return NRF_ERROR_NULL; + } + + ret = queue_reserve(FDS_CMD_QUEUE_SIZE_CLEAR, 0, &p_cmd, NULL); + + if (ret != NRF_SUCCESS) + { + return ret; + } + + // Initialize the command. + p_cmd->id = FDS_CMD_CLEAR; + p_cmd->op_code = FDS_OP_CLEAR_TL; + + p_cmd->record_header.id = p_desc->record_id; + p_cmd->vpage_id = p_desc->vpage_id; + + return cmd_queue_process_start(); +} + + +ret_code_t fds_clear(fds_record_desc_t * const p_desc) +{ + ret_code_t ret; + atomic_counter_inc(); + ret = clear_enqueue(p_desc); + atomic_counter_dec(); + return ret; +} + + +static ret_code_t clear_by_instance_enqueue(fds_instance_id_t instance) +{ + ret_code_t ret; + fds_cmd_t * p_cmd; + + if (!flag_is_set(FDS_FLAG_INITIALIZED)) + { + return NRF_ERROR_INVALID_STATE; + } + + ret = queue_reserve(FDS_CMD_QUEUE_SIZE_CLEAR, 0, &p_cmd, NULL); + + if (ret != NRF_SUCCESS) + { + return ret; + } + + p_cmd->id = FDS_CMD_CLEAR_INST; + p_cmd->op_code = FDS_OP_CLEAR_INSTANCE; + + p_cmd->record_header.ic.instance = instance; + + return cmd_queue_process_start(); +} + +ret_code_t fds_clear_by_instance(fds_instance_id_t instance) +{ + ret_code_t ret; + atomic_counter_inc(); + ret = clear_by_instance_enqueue(instance); + atomic_counter_dec(); + return ret; +} + + +ret_code_t fds_update(fds_record_desc_t * const p_desc, + fds_record_key_t key, + uint8_t num_chunks, + fds_record_chunk_t chunks[]) +{ + ret_code_t ret; + atomic_counter_inc(); + ret = write_enqueue(p_desc, key, num_chunks, chunks, NULL, true /*update*/); + atomic_counter_dec(); + return ret; +} + + +static ret_code_t gc_enqueue() +{ + ret_code_t ret; + fds_cmd_t * p_cmd; + + if (!flag_is_set(FDS_FLAG_INITIALIZED)) + { + return NRF_ERROR_INVALID_STATE; + } + + ret = queue_reserve(FDS_CMD_QUEUE_SIZE_GC, 0, &p_cmd, NULL); + if (ret != NRF_SUCCESS) + { + return ret; + } + + p_cmd->id = FDS_CMD_GC; + + // Set compression parameters. + m_gc.state = BEGIN; + + return cmd_queue_process_start(); +} + + +ret_code_t fds_gc() +{ + ret_code_t ret; + atomic_counter_inc(); + ret = gc_enqueue(); + atomic_counter_dec(); + return ret; +} + + +ret_code_t fds_find(fds_type_id_t type, + fds_instance_id_t instance, + fds_record_desc_t * const p_desc, + fds_find_token_t * const p_token) +{ + if (p_desc == NULL || p_token == NULL) + { + return NRF_ERROR_NULL; + } + + return find_record(&type, &instance, p_desc, p_token); +} + + +ret_code_t fds_find_by_type(fds_type_id_t type, + fds_record_desc_t * const p_desc, + fds_find_token_t * const p_token) +{ + if (p_desc == NULL || p_token == NULL) + { + return NRF_ERROR_NULL; + } + + return find_record(&type, NULL, p_desc, p_token); +} + + +ret_code_t fds_find_by_instance(fds_instance_id_t instance, + fds_record_desc_t * const p_desc, + fds_find_token_t * const p_token) +{ + if (p_desc == NULL || p_token == NULL) + { + return NRF_ERROR_NULL; + } + + return find_record(NULL, &instance, p_desc, p_token); +} + + +ret_code_t fds_register(fds_cb_t cb) +{ + if (m_users == FDS_MAX_USERS) + { + return NRF_ERROR_NO_MEM; + } + + m_cb_table[m_users] = cb; + m_users++; + + return NRF_SUCCESS; +} + + +bool fds_descriptor_match(fds_record_desc_t const * const p_desc1, + fds_record_desc_t const * const p_desc2) +{ + if ((p_desc1 == NULL) || (p_desc2 == NULL)) + { + return false; + } + + return (p_desc1->record_id == p_desc2->record_id); +} + + +ret_code_t fds_descriptor_from_rec_id(fds_record_desc_t * const p_desc, + fds_record_id_t record_id) +{ + if (p_desc == NULL) + { + return NRF_ERROR_NULL; + } + + p_desc->record_id = record_id; + p_desc->vpage_id = FDS_VPAGE_ID_UNKNOWN; + + return NRF_SUCCESS; +} + +ret_code_t fds_record_id_from_desc(fds_record_desc_t const * const p_desc, + fds_record_id_t * const p_record_id) +{ + if (p_desc == NULL || p_record_id == NULL) + { + return NRF_ERROR_NULL; + } + + *p_record_id = p_desc->record_id; + + return NRF_SUCCESS; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/nordic_sdk/components/libraries/fds/fds.h Mon Jan 11 22:33:57 2016 +0000 @@ -0,0 +1,566 @@ +/* + * Copyright (c) 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 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 other + * contributors to this software may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef FDS_H__ +#define FDS_H__ + +/** + * @defgroup flash_data_storage Flash Data Storage + * @ingroup app_common + * @{ + * @brief Flash Data Storage (FDS). + * + * @details Flash Data Storage (FDS) is a minimalistic filesystem for the on-chip flash. + * It can be used to manipulate @e records, which consist of a piece of data, made up + * of one or more chunks, and an associated key pair. + */ + +#include <stdint.h> +#include <stdbool.h> +#include "sdk_errors.h" + + +/**@brief */ +#define SIZEOF_WORDS(val) (sizeof(val) / 4) + +/**@brief Reserved type key used to flag cleared records. + * May not be used as a record key by the application. */ +#define FDS_TYPE_ID_INVALID (0x0000) +/**@brief Reserved instance key used to check for missing or corrupted metadata. + * May not be used as a record key by the application. */ +#define FDS_INSTANCE_ID_INVALID (0xFFFF) + + +typedef uint32_t fds_record_id_t; +typedef uint16_t fds_type_id_t; +typedef uint16_t fds_length_t; +typedef uint16_t fds_instance_id_t; +typedef uint16_t fds_checksum_t; + + +/**@brief A piece of a record metadata, keeping information about one of its keys (type) and its + * lenght, expressed in 4 byte words. */ +typedef struct +{ + fds_type_id_t type; /**< The record type ID. */ + fds_length_t length_words; /**< Length of the record's data, in 4 byte words. */ +} fds_tl_t; + + +/**@brief A piece of a record metadata, keeping information about one of its keys (instance) and + * its checksum. */ +typedef struct +{ + fds_instance_id_t instance; /**< The record instance ID. */ + fds_checksum_t checksum; /**< Checksum of the entire record, including the metadata. */ +} fds_ic_t; + + +/**@brief The record metadata. */ +typedef struct +{ + fds_tl_t tl; /**< See @ref fds_tl_t. */ + fds_ic_t ic; /**< See @ref fds_ic_t. */ + fds_record_id_t id; /**< The unique record ID (32 bits). */ +} fds_header_t; + + +typedef fds_header_t fds_record_header_t; + +/**@brief The record descriptor structure, used to manipulate a record. + * @note This structure is meant to be opaque to the user, who does not need to access + * any of its fields. + * @note This structure does not need special initialization. + * @warning Do not reuse the same descriptor for different records. If you do, be sure to set + * its fields to zero. */ +typedef struct +{ + uint32_t record_id; /**< The unique record ID. */ + uint32_t const * p_rec; /**< The last known record address in flash. */ + uint16_t vpage_id; /**< The virtual page ID in which the record is stored. */ + uint16_t gc_magic; /**< Number of times the GC algorithm has been run. */ + uint16_t ptr_magic; /**< Used to verify the validity of p_rec. */ +} fds_record_desc_t; + + +/**@brief The record key, used to lookup records. + * @note The uniqueness of either field is not enforced by the system. */ +typedef struct +{ + uint16_t type; + uint16_t instance; +} fds_record_key_t; + + +/**@brief Structure used for reading a record back from flash memory. */ +typedef struct +{ + // TODO: the header should be a pointer. + fds_header_t header; /**< The record header (metadata), as stored in flash. */ + uint32_t const * p_data; /**< The record data. */ +} fds_record_t; + + +/**@brief A record chunk, containing a piece of data to be stored in a record. + * + * @note p_data must be aligned on a (4 bytes) word boundary. + */ +typedef struct +{ + void const * p_data; /**< Pointer to the data to store. Must be word aligned. */ + fds_length_t length_words; /**< Length of data pointed by p_data, in 4 byte words. */ +} fds_record_chunk_t; + + +/**@brief A token to a reserved space in flash, created by @ref fds_reserve. + * Use @ref fds_write_reserved to write the record in the reserved space, + * or @ref fds_reserve_cancel to cancel the reservation. + */ +typedef struct +{ + uint16_t vpage_id; /**< The virtual ID of the page where space was reserved. */ + fds_length_t length_words; /**< The amount of space reserved, in 4 byte words. */ +} fds_write_token_t; + + +/**@brief A token to keep information about the progress of @ref fds_find, @ref fds_find_by_type + * and @ref fds_find_by_instance operations. + * @note This structure is meant to be opaque to the user, who does not need to access any of its + * fields. + * @note The token does not need special initialization. + * @warning Do not reuse the same token to search for different records. If you do, be sure to set + * its fields to zero. */ +typedef struct +{ + uint32_t const * p_addr; + uint32_t magic; + uint16_t vpage_id; +} fds_find_token_t; + + +typedef enum +{ + FDS_CMD_NONE, /**< No command. */ + FDS_CMD_INIT, /**< Module initialization commnad. Used in @ref fds_init */ + FDS_CMD_WRITE, /**< Write command. Used in @ref fds_write and @ref fds_write_reserved. */ + FDS_CMD_UPDATE, /**< Update command. Used in @ref fds_update. */ + FDS_CMD_CLEAR, /**< Clear record command. Used in @ref fds_clear and @ref fds_update. */ + FDS_CMD_CLEAR_INST, /**< Clear instance command. Used in @ref fds_clear_by_instance. */ + FDS_CMD_GC /**< Garbage collection. Used in @ref fds_gc. */ +} fds_cmd_id_t; + + +/**@brief Flash data storage callback function. + * + * @param result Result of the command. + * @param cmd The command associated with the callback. + * @param record_id The unique ID of the record associated with the callback. + * @param record_key The key pair of the record associated with the callback. + */ +typedef void (*fds_cb_t)(ret_code_t result, + fds_cmd_id_t cmd, + fds_record_id_t record_id, + fds_record_key_t record_key); + + +/**@brief Function to register a callback for the module events. + * @details The maximum amount of callback which can be registered can be configured by + * changing the FDS_MAX_USERS macro in fds_config.h. + * + * @param[in] cb The callback function. + * + * + * @retval NRF_SUCCESS Success. + * @retval NRF_ERROR_NO_MEM Error. Maximum number of registered callbacks reached. + */ +ret_code_t fds_register(fds_cb_t cb); + + +/**@brief Function to initialize the module. + * + * @details This function initializes the module and installs the filesystem, if it is not + * installed yet. + * + * @note This function is asynchronous. Completion is reported with a callback through the + * registered event handler. To be able to receive such callback, be sure to call + * @ref fds_register before calling @ref fds_init. + * + * @retval NRF_SUCCESS Success. The command was queued. + * @retval NRF_ERROR_INVALID_STATE Error. The module is currently undergoing initialization. + * @retval NRF_ERROR_NO_MEM Error. Insufficient space to install the filesystem, or + * insufficient resources to perform the installation. + */ +ret_code_t fds_init(void); + + +/**@brief Function to write a record to flash. + * + * @details This function can be used to write a record to flash. A record data consists of + * multiple chunks and is supplied to the function as an array of fds_record_chunk_t + * structures. The maximum lenght of a record data may not exceed the size of one flash + * page minus FDS_HEADER_SIZE words. + * + * @note This function is asynchronous, therefore, completion is reported with a callback + * through the registered event handler. + * + * @note The record data must be aligned on a 4 byte boundary, and because it is not buffered + * internally, it must be kept in memory by the application until the callback for the + * command has been received, i.e., the command completed. + * + * @param[out] p_desc The record descriptor. It may be NULL. + * @param[in] key The record key pair. + * @param[in] num_chunks The number of elements in the chunks array. + * @param[in] chunks An array of chunks making up the record data. + * + * @retval NRF_SUCCESS Success. The command was queued. + * @retval NRF_ERROR_INVALID_STATE Error. The module is not initialized. + * @retval NRF_ERROR_INVALID_DATA Error. The key contains an invalid type or instance. + * @retval NRF_ERROR_INVALID_ADDR Error. The record data is not aligned on a 4 byte boundary. + * @retval NRF_ERROR_INVALID_LENGTH Error. The record length exceeds the maximum lenght. + * @retval NRF_ERROR_BUSY Error. Insufficient internal resources to queue the operation. + * @retval NRF_ERROR_NO_MEM Error. No flash space available to store the record. + */ +ret_code_t fds_write(fds_record_desc_t * const p_desc, + fds_record_key_t key, + uint8_t num_chunks, + fds_record_chunk_t chunks[]); + + +/**@brief Function to reserve space for a record. + * + * @details This function can be used to reserve flash space to store a record, which can be + * later written down using @ref fds_write_reserved. It is possible to cancel a + * reservation by using @ref fds_reserve_cancel. + * + * @param[out] p_tok A token which can be used to write a record in the reserved space + * using @ref fds_write_reserved. + * @param[in] length_words The lenght of the record data, in 4 byte words. + * + * @retval NRF_SUCCESS Success. Flash space was successfully reserved. + * @retval NRF_ERROR_NULL Error. p_tok is NULL. + * @retval NRF_ERROR_INVALID_STATE Error. The module is not initialized. + * @retval NRF_ERROR_NO_MEM Error. Insufficient space. + */ +ret_code_t fds_reserve(fds_write_token_t * const p_tok, uint16_t length_words); + + +/**@brief Function to cancel a space reservation. + * + * @param[in] p_tok The token produced by @ref fds_reserve, identifying the reservation to cancel. + * + * @retval NRF_SUCCESS Success. The reservation was canceled. + * @retval NRF_ERROR_INVALID_STATE Error. The module is not initialized. + * @retval NRF_ERROR_NULL Error. p_tok is NULL. + * @retval NRF_ERROR_INVALID_DATA Error. p_tok contains invalid data. + */ +ret_code_t fds_reserve_cancel(fds_write_token_t * const p_tok); + + +/**@brief Function to write a record to flash, the space for which has been previously reserved + * using @ref fds_reserve. + * + * @details This function behaves similarly to @ref fds_write, with the exception that it never + * fails with NRF_ERROR_NO_MEM. + * + * @note This function is asynchronous, therefore, completion is reported with a callback + * through the registered event handler. + * + * @note The record data must be aligned on a 4 byte boundary, and because it is not buffered + * internally, it must be kept in memory by the application until the callback for the + * command has been received, i.e., the command completed. + * + * @param[in] p_tok The token return by @ref fds_reserve. + * @param[out] p_desc The record descriptor. It may be NULL. + * @param[in] key The record key pair. + * @param[in] num_chunks The number of elements in the chunks array. + * @param[in] chunks An array of chunks making up the record data. + * + * @retval NRF_SUCCESS Success. The command was queued. + * @retval NRF_ERROR_INVALID_STATE Error. The module is not initialized. + * @retval NRF_ERROR_INVALID_DATA Error. The key contains an invalid type or instance. + * @retval NRF_ERROR_INVALID_ADDR Error. The record data is not aligned on a 4 byte boundary. + * @retval NRF_ERROR_INVALID_LENGTH Error. The record length exceeds the maximum lenght. + * @retval NRF_ERROR_BUSY Error. Insufficient internal resources to queue the operation. + */ +ret_code_t fds_write_reserved(fds_write_token_t const * const p_tok, + fds_record_desc_t * const p_desc, + fds_record_key_t key, + uint8_t num_chunks, + fds_record_chunk_t chunks[]); + + +/**@brief Function to clear a record. + * + * @details Clearing a record has the effect of preventing the system from retrieving the record + * descriptor using the @ref fds_find, @ref fds_find_by_type and @ref fds_find_by_instance + * functions. Additionally, @ref fds_open calls shall fail when supplied a descritpor for + * a record which has been cleared. Clearing a record does not free the space it occupies + * in flash. The reclaim flash space used by cleared records, use @ref fds_gc. + * + * @note This function is asynchronous, therefore, completion is reported with a callback + * through the registered event handler. + * + * @param[in] p_desc The descriptor of the record to clear. + * + * @retval NRF_SUCCESS Success. The command was queued. + * @retval NRF_ERROR_INVALID_STATE Error. The module is not initialized. + * @retval NRF_ERROR_NULL Error. p_desc is NULL. + * @retval NRF_ERROR_BUSY Error. Insufficient internal resources to queue the operation. + */ +ret_code_t fds_clear(fds_record_desc_t * const p_desc); + + +/**@brief Function to clear all records with a given instance. + * + * @details Clearing a record has the effect of preventing the system from retrieving the record + * descriptor using the @ref fds_find, @ref fds_find_by_type and @ref fds_find_by_instance + * functions. Additionally, @ref fds_open calls shall fail when supplied a descritpor for + * a record which has been cleared. Clearing a record does not free the space it occupies + * in flash. The reclaim flash space used by cleared records, use @ref fds_gc. + * + * @note This function is asynchronous, therefore, completion is reported with a callback + * through the registered event handler. Only one callback will be issued. The record + * instance ID in the key parameter of the callback will contain the instance ID passed as + * parameter to this function. The record ID parameter will be zero, and the type ID equal + * to FDS_TYPE_ID_INVALID. + * + * @param[in] instance The instance ID of the records to clear. + * + * @retval NRF_SUCCESS Success. The command was queued. + * @retval NRF_ERROR_INVALID_STATE Error. The module is not initialized. + * @retval NRF_ERROR_NULL Error. p_desc is NULL. + * @retval NRF_ERROR_BUSY Error. Insufficient internal resources to queue the operation. + */ +ret_code_t fds_clear_by_instance(fds_instance_id_t instance); + + +/**@brief Function to update an existing record. + * + * @details Updating a record writes a new record with the given key and data in flash, and then + * clears the old record. + * + * @note This function is asynchronous, therefore, completion is reported with a callback + * through the registered event handler. Two callbacks will be issued, one to signal that + * the updated record has been written down, and another to signal that the old one has been + * cleared. + * + * @note The record data must be aligned on a 4 byte boundary, and because it is not buffered + * internally, it must be kept in memory by the application until the callback for the + * command has been received, i.e., the command completed. + * + * @param[in, out] p_desc The descriptor of the record to update. The descriptor of the updated + * record, after the function has returned with NRF_SUCCESS. + * @param[in] key The record new key pair. + * @param[in] num_chunks The number of elements in the chunks array. + * @param[in] chunks An array of chunks making up the record new data. + * + * @retval NRF_SUCCESS Success. The command was queued. + * @retval NRF_ERROR_INVALID_STATE Error. The module is not initialized. + * @retval NRF_ERROR_INVALID_DATA Error. The key contains an invalid type or instance. + * @retval NRF_ERROR_INVALID_ADDR Error. The record data is not aligned on a 4 byte boundary. + * @retval NRF_ERROR_INVALID_LENGTH Error. The record length exceeds the maximum lenght. + * @retval NRF_ERROR_BUSY Error. Insufficient internal resources to queue the operation. + * @retval NRF_ERROR_NO_MEM Error. No flash space available to store the record. + */ +ret_code_t fds_update(fds_record_desc_t * const p_desc, + fds_record_key_t key, + uint8_t num_chunks, + fds_record_chunk_t chunks[]); + + +/**@brief Function to search for records with a given key pair. + * + * @details Because types are not unique, to search for the next record with the given key call + * the function again and supply the same fds_find_token_t structure to resume searching + * from the last record found. + * + * @param[in] type The record type ID. + * @param[in] instance The record instance ID. + * @param[out] p_desc The descriptor of the record found. + * @param[out] p_token A token containing information about the progress of the operation. + * + * @retval NRF_SUCCESS Success. A record was found. + * @retval NRF_ERROR_INVALID_STATE Error. The module is not initialized. + * @retval NRF_ERROR_NULL Error. Either p_desc or p_token are NULL. + * @retval NRF_ERROR_NOT_FOUND Error. No record with the given key pair was found. + */ +ret_code_t fds_find(fds_type_id_t type, + fds_instance_id_t instance, + fds_record_desc_t * const p_desc, + fds_find_token_t * const p_token); + + +/**@brief Function to search for records with a given type. + * + * @details Because types are not unique, to search for the next record with the given key call + * the function again and supply the same fds_find_token_t structure to resume searching + * from the last record found. + * + * @param[in] type The type ID in the record key. + * @param[out] p_desc The descriptor of the record found. + * @param[out] p_token A token containing information about the progress of the operation. + * + * @retval NRF_SUCCESS Success. A record was found. + * @retval NRF_ERROR_INVALID_STATE Error. The module is not initialized. + * @retval NRF_ERROR_NULL Error. Either p_desc or p_token are NULL. + * @retval NRF_ERROR_NOT_FOUND Error. No record with the given type was found. + */ + ret_code_t fds_find_by_type(fds_type_id_t type, + fds_record_desc_t * const p_desc, + fds_find_token_t * const p_token); + + +/**@brief Function to search for records with a given instance. + * + * @details Because types are not unique, to search for the next record with the given key call + * the function again and supply the same fds_find_token_t structure to resume searching + * from the last record found. + * + * @param[in] instance The instance ID in the record key. + * @param[out] p_desc The descriptor of the record found. + * @param[out] p_token A token containing information about the progress of the operation. + * + * @retval NRF_SUCCESS Success. A record was found. + * @retval NRF_ERROR_INVALID_STATE Error. The module is not initialized. + * @retval NRF_ERROR_NULL Error. Either p_desc or p_token are NULL. + * @retval NRF_ERROR_NOT_FOUND Error. No record with the given instance was found. + */ +ret_code_t fds_find_by_instance(fds_instance_id_t instance, + fds_record_desc_t * const p_desc, + fds_find_token_t * const p_token); + + +/**@brief Function to open a record for reading. + * + * @details Function to read a record which has been written to flash. This function initializes + * a fds_record_t structure which can be used to access the record data as well as + * its associated metadata. The pointers provided in the fds_record_t structure are + * pointers to flash memory. Opening a record with @ref fds_open prevents the garbage + * collection to run on the flash page in which record is stored, therefore the contents + * of the memory pointed by the fds_record_t p_data field is guaranteed to remain + * unmodified, as long as the record is kept open. + * + * @note When you are done reading a record, close it using @ref fds_close so that successive + * garbage collections can reclaim space on the page where the record is stored, if necessary. + * + * @param[in] p_desc The descriptor of the record to open. + * @param[out] p_record The record data and metadata, as stored in flash. + * + * @retval NRF_SUCCESS Success. The record was opened. + * @retval NRF_ERROR_NOT_FOUND Error. The record was not found. It may have been cleared, or it + * may have not been written yet. + * @retval NRF_ERROR_INVALID_DATA Error. The descriptor contains invalid data. + * @retval NRF_ERROR_NULL Error. Either p_desc or p_record are NULL. + */ +ret_code_t fds_open(fds_record_desc_t * const p_desc, + fds_record_t * const p_record); + + +/**@brief Function to close a record, after its contents have been read. + * + * @details Closing a record allows garbage collection to be run on the page in which the + * record being closed is stored (if no other records remain open on that page). + * + * @note Closing a record, does NOT invalidate its descriptor, which can be safely supplied to + * all functions which accept a descriptor as a parameter. + * + * @param[in] p_desc The descriptor of the record to close. + * + * @retval NRF_SUCCESS Success. The record was closed. + * @retval NRF_ERROR_NULL Error. p_desc is NULL. + * @retval NRF_ERROR_INVALID_DATA Error. The descriptor contains invalid data. + */ +ret_code_t fds_close(fds_record_desc_t const * const p_desc); + + +/**@brief Function to perform a garbage collection. + * + * @details Garbage collection reclaims the flash space occupied by records which have been cleared + * using @ref fds_clear. + * + * @note This function is asynchronous, therefore, completion is reported with a callback + * through the registered event handler. + */ +ret_code_t fds_gc(void); + + +/**@brief Function to compare two record descriptors. + * + * @param[in] p_desc_one First descriptor. + * @param[in] p_desc_two Second descriptor. + * + * @retval true If the descriptors identify the same record. + * @retval false Otherwise. + */ +bool fds_descriptor_match(fds_record_desc_t const * const p_desc_one, + fds_record_desc_t const * const p_desc_two); + + +/**@brief Function to obtain a descriptor from a record ID. + * + * @details This function can be used to reconstruct a descriptor from a record ID, such as the + * one passed to the callback function. + * + * @warning This function does not check if a record with the given record ID exists or not. If a + * non-existing record ID is supplied, the resulting descriptor will cause other functions + * to fail when used as parameter. + * + * @param[out] p_desc The descriptor of the record with given record ID. + * @param[in] record_id The record ID for which to provide a descriptor. + * + * @retval NRF_SUCCESS Success. + * @retval NRF_ERROR_NULL Error. p_desc is NULL. + */ +ret_code_t fds_descriptor_from_rec_id(fds_record_desc_t * const p_desc, + fds_record_id_t record_id); + +/**@brief Function to obtain a record ID from a record descriptor. + * + * @details This function can be used to extract a record ID from a descriptor. It may be used + * in the callback function to determine which record the callback is associated to, if + * you have its descriptor. + * + * @warning This function does not check the record descriptor sanity. If the descriptor is + * uninitialized, or has been tampered with, the resulting record ID may be invalid. + * + * @param[in] p_desc The descriptor from which to extract the record ID. + * @param[out] p_record_id The record ID contained in the given descriptor. + * + * @retval NRF_SUCCESS Success. + * @retval NRF_ERROR_NULL Error. Either p_desc is NULL or p_record_id is NULL. + */ +ret_code_t fds_record_id_from_desc(fds_record_desc_t const * const p_desc, + fds_record_id_t * const p_record_id); + +/** @} */ + +#endif // FDS_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/nordic_sdk/components/libraries/fds/fds_config.h Mon Jan 11 22:33:57 2016 +0000 @@ -0,0 +1,65 @@ +/* + * Copyright (c) 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 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 other + * contributors to this software may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef FDS_CONFIG_H__ +#define FDS_CONFIG_H__ + + /** + * @file fds_config.h + * + * @addtogroup flash_data_storage + * @{ + */ + +/**@brief Configures the size of the internal queue. */ +#define FDS_CMD_QUEUE_SIZE (8) +/**@brief Determines how many @ref fds_record_chunk_t structures can be buffered at any time. */ +#define FDS_CHUNK_QUEUE_SIZE (8) + +/**@brief Configures the number of physical flash pages to use. Out of the total, one is reserved + * for garbage collection, hence, two pages is the minimum: one for the application data + * and one for the system. */ +#define FDS_MAX_PAGES (2) +/**@brief Configures the maximum number of callbacks which can be registred. */ +#define FDS_MAX_USERS (10) + +/** Page tag definitions. */ +#define FDS_PAGE_TAG_WORD_0_SWAP (0xA5A5A5A5) +#define FDS_PAGE_TAG_WORD_0_VALID (0xA4A4A4A4) +#define FDS_PAGE_TAG_WORD_1 (0xAABBCCDD) +#define FDS_PAGE_TAG_WORD_2 (0xAABB01DD) /**< Includes version. */ +#define FDS_PAGE_TAG_WORD_3 (0x1CEB00DA) +#define FDS_PAGE_TAG_WORD_3_GC (0x1CEB00D8) + +/** @} */ + +#endif // FDS_CONFIG_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/nordic_sdk/components/libraries/fds/fds_types_internal.h Mon Jan 11 22:33:57 2016 +0000 @@ -0,0 +1,178 @@ +/* + * Copyright (c) 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 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 other + * contributors to this software may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef FDS_TYPES_INTERNAL__ +#define FDS_TYPES_INTERNAL__ + +#include "fds.h" +#include <stdint.h> +#include <stdbool.h> +#include "nrf_soc.h" + + +#define COMMAND_EXECUTING (NRF_SUCCESS) +#define COMMAND_COMPLETED (0x1234) +//#define COMMAND_FAILED (0x1236) + +#define FDS_MAGIC_HWORD (0xF11E) +#define FDS_MAGIC_WORD (0x15ABE11A) +#define FDS_ERASED_WORD (0xFFFFFFFF) + +#define FDS_PAGE_TAG_SIZE (4) /**< Page tag size, in 4 byte words. */ + +#define FDS_VPAGE_ID_UNKNOWN (0xFFFF) + +#define FDS_WRITE_OFFSET_TL (0) /**< Offset of TL from the record base address, in 4 byte words. */ +#define FDS_WRITE_OFFSET_IC (1) /**< Offset of IC from the record base address, in 4 byte words. */ +#define FDS_WRITE_OFFSET_ID (2) /**< Offset of ID from the record base address, in 4 byte words. */ +#define FDS_WRITE_OFFSET_DATA (3) /**< Offset of the data (chunks) 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_ID (1) /**< Size of the IC 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 (3) /**< Size of the whole header, in 4 byte words. */ + +#define FDS_CMD_QUEUE_SIZE_INIT (1) +#define FDS_CMD_QUEUE_SIZE_WRITE (1) +#define FDS_CMD_QUEUE_SIZE_CLEAR (1) +#define FDS_CMD_QUEUE_SIZE_UPDATE (2) +#define FDS_CMD_QUEUE_SIZE_GC (1) + + +static uint8_t m_nested_critical; + +/** Macros to enable and disable application interrupts. */ +#define CRITICAL_SECTION_ENTER() //sd_nvic_critical_region_enter(&m_nested_critical) +#define CRITICAL_SECTION_EXIT() //sd_nvic_critical_region_exit ( m_nested_critical) + +/**@brief Page types. */ +typedef enum +{ + FDS_PAGE_UNDEFINED, /**< Undefined page type. */ + FDS_PAGE_ERASED, /**< Page is erased. */ + FDS_PAGE_VALID, /**< Page is ready for storage. */ + FDS_PAGE_SWAP, /**< Page is reserved for GC. */ + FDS_PAGE_GC /**< Page is being garbage collected. */ +} fds_page_type_t; + + +typedef enum +{ + FDS_OP_NONE = 0x00, /**< No operation. */ + FDS_OP_WRITE_TL, /**< Write the type and length. */ + FDS_OP_WRITE_ID, /**< Write the record ID. */ + FDS_OP_WRITE_CHUNK, /**< Write the record value. */ + FDS_OP_WRITE_IC, /**< Write the instance and checksum. */ + FDS_OP_CLEAR_TL, + FDS_OP_CLEAR_INSTANCE, + FDS_OP_DONE, +} fds_opcode_t; + + +typedef enum +{ + FDS_FLAG_INITIALIZING = (1 << 0), /**< TODO: Not really needed atm? */ + FDS_FLAG_INITIALIZED = (1 << 1), /**< Flag indicating that flash data storage has been initialized. */ + FDS_FLAG_PROCESSING = (1 << 2), /**< Flag indicating that queue is being processed. */ + FDS_FLAG_CAN_GC = (1 << 3), /**< Flag indicating that fds can regain data by performing garbage collection. */ +} fds_flags_t; + + +typedef struct +{ + uint32_t const * start_addr; + uint16_t vpage_id; /**< The page logical ID. */ + uint16_t volatile write_offset; /**< The page write offset, in 4 bytes words. */ + uint16_t volatile words_reserved; /**< The amount of words reserved by fds_write_reserve() on this page. */ + uint16_t volatile records_open; + fds_page_type_t page_type : 4; /**< The page type. */ +} fds_page_t; + + +typedef struct +{ + fds_cmd_id_t id : 4; /**< The ID of the command. */ + fds_opcode_t op_code : 4; + uint8_t num_chunks; /**< Number of operations this command has left in the operation queue. */ + uint16_t chunk_offset; /**< Offset used for writing the record value(s), in 4 byte words. */ + uint16_t vpage_id; /**< The virtual page ID where we reserved the flash space for this command. */ + fds_record_header_t record_header; +} fds_cmd_t; + + +/**@brief Defines command queue, an element is free if the op_code field is not invalid. + * + * @details Defines commands enqueued for flash access. At any point in time, this queue has one or + * more flash access operations pending if the count field is not zero. When the queue is + * not empty, the rp (read pointer) field points to the flash access command in progress + * or, if none is in progress, the command to be requested next. The queue implements a + * simple first in first out algorithm. Data addresses are assumed to be resident. + */ +typedef struct +{ + fds_cmd_t cmd[FDS_CMD_QUEUE_SIZE]; /**< Array to maintain flash access operation details. */ + uint8_t volatile rp; /**< The index of the command being executed. */ + uint8_t volatile count; /**< Number of elements in the queue. */ +} fds_cmd_queue_t; + + +typedef struct +{ + fds_record_chunk_t chunk[FDS_CHUNK_QUEUE_SIZE]; + uint8_t volatile rp; + uint8_t volatile count; +} fds_chunk_queue_t; + + +typedef enum +{ + NONE, + BEGIN, + RESUME, + GC_PAGE, + COPY_RECORD, + READY_SWAP, + NEW_SWAP, + INIT_SWAP +} fds_gc_state_t; + + +typedef struct +{ + uint16_t cur_page; + uint16_t swap_page; + uint32_t const * p_scan_addr; + fds_gc_state_t state; + bool do_gc_page[FDS_MAX_PAGES]; +} fds_gc_data_t; + +#endif // FDS_TYPES_INTERNAL__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/nordic_sdk/components/libraries/fstorage/fstorage.c Mon Jan 11 22:33:57 2016 +0000 @@ -0,0 +1,569 @@ +/* + * Copyright (c) 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 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 other + * contributors to this software may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "fstorage.h" +#include <stdio.h> +#include <string.h> +#include <stdbool.h> +#include "fstorage_config.h" +#include "nrf_error.h" +#include "nrf_soc.h" + + +#define FS_FLAG_INIT (1 << 0) /**< fstorage has been initialized. */ +#define FS_FLAG_PROCESSING (1 << 1) /**< fstorage is executing queued flash operations. */ +#define FS_FLAG_FLASH_REQ_PENDING (1 << 2) /**< fstorage is waiting for a flash operation initiated by another module to complete. */ + + +/**@brief Macro invocation that registers section fs_data. + * + * @details Required for compilation. + */ +NRF_SECTION_VARS_REGISTER_SECTION(fs_data); + + +/**@brief Macro invocation that declares symbols used to find the beginning and end of the section fs_data. + * + * @details Required for compilation. + */ +NRF_SECTION_VARS_REGISTER_SYMBOLS(fs_config_t, fs_data); + + +/**@defgroup Section vars helper macros. + * + * @details Macros used to manipulate registered section variables. + */ + /**@brief Get section variable with fstorage configuration by index. */ +#define FS_SECTION_VARS_GET(i) NRF_SECTION_VARS_GET(i, fs_config_t, fs_data) + /**@brief Get the number of registered section variables. */ +#define FS_SECTION_VARS_COUNT NRF_SECTION_VARS_COUNT(fs_config_t, fs_data) + /**@brief Get the start address of the registered section variables. */ +#define FS_SECTION_VARS_START_ADDR NRF_SECTION_VARS_START_ADDR(fs_data) + /**@brief Get the end address of the registered section variables. */ +#define FS_SECTION_VARS_END_ADDR NRF_SECTION_VARS_END_ADDR(fs_data) + +/** @} */ + + +/**@brief The command queue element. + * + * @details Encapsulate details of a command requested to this module. + */ +typedef struct +{ + fs_config_t const * p_config; /**< The configuration of the user who requested the operation. */ + uint8_t op_code; /**< Operation code. */ + uint32_t const * p_src; /**< Pointer to the data to be written to flash. The data must be kept in memory until the operation has finished. */ + uint32_t const * p_addr; /**< Destination of the data in flash. */ + fs_length_t length_words; /**< Length of the operation */ + fs_length_t offset; /**< Offset of the operation if operation is done in chunks */ +} fs_cmd_t; + + +/**@brief Structure that defines the command queue + * + * @details This queue holds flash operations requested to the module. + * The data to be written must be kept in memory until the write operation is completed, + * i.e., a callback indicating completion is received by the application. + */ +typedef struct +{ + uint8_t rp; /**< The current element being processed. */ + uint8_t count; /**< Number of elements in the queue. */ + fs_cmd_t cmd[FS_CMD_QUEUE_SIZE]; /**< Array to maintain flash access operation details. */ +} fs_cmd_queue_t; + + +static uint8_t m_flags; /**< FStorage status flags. */ +static fs_cmd_queue_t m_cmd_queue; /**< Flash operation request queue. */ +static uint16_t m_retry_count = 0; /**< Number of times a single flash operation was retried. */ + + +// Function prototypes +static ret_code_t queue_process(void); +static ret_code_t queue_process_impl(void); +static void app_notify(uint32_t result, fs_cmd_t const * p_cmd); + + +/**@brief Macro to check that the configuration is non-NULL and within +* valid section variable memory bounds. + * + * @param[in] config Configuration to check. + */ +#define FS_CHECK_CONFIG(config) \ + ((FS_SECTION_VARS_START_ADDR < config) && (config < FS_SECTION_VARS_END_ADDR)) + + +/**@brief Function to check that the configuration is non-NULL and within +* valid section variable memory bounds. + * + * @param[in] config Configuration to check. + */ +static bool check_config(fs_config_t const * const config) +{ + if (config == NULL) + { + return false; + } + + if ((FS_SECTION_VARS_START_ADDR <= (uint32_t)config) && ((uint32_t)config < FS_SECTION_VARS_END_ADDR)) + { + return true; + } + else + { + return false; + } +} + + +/**@brief Function to initialize the queue. */ +static void queue_init(void) +{ + memset(&m_cmd_queue, 0, sizeof(fs_cmd_queue_t)); +} + + +/**@brief Function to reset a queue item to its default values. + * + * @param index Index of the queue element. + */ +static void cmd_reset(uint32_t index) +{ + memset(&m_cmd_queue.cmd[index], 0, sizeof(fs_cmd_t)); +} + + +/**@brief Function to enqueue flash access command + * + * @param[in] config Registered configuration. + * @param[in] op_code Operation code. + * @param[in] address Destination of the data. + * @param[in] p_src Source of data or NULL if n/a. + * @param[in] length Length of the data, in 4 byte words. + * + * @retval NRF_SUCCESS Success. Command enqueued. + * @retval NRF_ERROR_NO_MEM Error. Queue is full. + * @retval Any error returned by the SoftDevice flash API. + */ +static ret_code_t cmd_enqueue(fs_config_t const * p_config, + uint8_t op_code, + uint32_t const * p_addr, + uint32_t const * p_src, + fs_length_t length_words) +{ + fs_cmd_t * p_cmd; + uint8_t write_pos; + + if (m_cmd_queue.count == FS_CMD_QUEUE_SIZE - 1) + { + return NRF_ERROR_NO_MEM; + } + + write_pos = (m_cmd_queue.rp + m_cmd_queue.count) % FS_CMD_QUEUE_SIZE; + + p_cmd = &m_cmd_queue.cmd[write_pos]; + + p_cmd->p_config = p_config; + p_cmd->op_code = op_code; + p_cmd->p_src = p_src; + p_cmd->p_addr = p_addr; + p_cmd->length_words = length_words; + + m_cmd_queue.count++; + + return queue_process(); +} + + +/**@brief Function to consume queue item and notify the return value of the operation. + * + * @details This function will report the result and remove the command from the queue after + * notification. + */ +static void cmd_consume(uint32_t result, const fs_cmd_t * p_cmd) +{ + // Consume the current item on the queue. + uint8_t rp = m_cmd_queue.rp; + + m_cmd_queue.count--; + if (m_cmd_queue.count == 0) + { + // There are no elements left. Stop processing the queue. + m_flags &= ~FS_FLAG_PROCESSING; + } + + if (++(m_cmd_queue.rp) == FS_CMD_QUEUE_SIZE) + { + m_cmd_queue.rp = 0; + } + + // Notify upon successful operation. + app_notify(result, p_cmd); + + // Reset the queue element. + cmd_reset(rp); +} + + +/**@brief Function to store data to flash. + * + * @param[in] p_cmd The queue element associated with the operation. + * + * @retval NRF_SUCCESS Success. The request was sent to the SoftDevice. + * @retval Any error returned by the SoftDevice flash API. + */ +static __INLINE uint32_t store_execute(fs_cmd_t const * const p_cmd) +{ + // Write in chunks if write-size is larger than FS_MAX_WRITE_SIZE. + fs_length_t const length = ((p_cmd->length_words - p_cmd->offset) < FS_MAX_WRITE_SIZE_WORDS) ? + (p_cmd->length_words - p_cmd->offset) : FS_MAX_WRITE_SIZE_WORDS; + + return sd_flash_write((uint32_t*)p_cmd->p_addr + p_cmd->offset /* destination */, + (uint32_t*)p_cmd->p_src + p_cmd->offset /* source */, + length); +} + + +/**@brief Function to erase a page. + * + * @param[in] p_cmd The queue element associated with the operation. + * + * @retval NRF_SUCCESS Success. The request was sent to the SoftDevice. + * @retval Any error returned by the SoftDevice flash API. + */ +static __INLINE uint32_t erase_execute(fs_cmd_t const * const p_cmd) +{ + // Erase the page. + return sd_flash_page_erase((uint32_t)(p_cmd->p_addr + p_cmd->offset) / FS_PAGE_SIZE); +} + + +/**@brief Function to process the current element in the queue and return the result. + * + * @retval NRF_SUCCESS Success. + * @retval NRF_ERROR_FORBIDDEN Error. Undefined command. + * @retval Any error returned by the SoftDevice flash API. + */ +static uint32_t queue_process_impl(void) +{ + uint32_t ret; + + fs_cmd_t const * const p_cmd = &m_cmd_queue.cmd[m_cmd_queue.rp]; + + switch (p_cmd->op_code) + { + case FS_OP_STORE: + ret = store_execute(p_cmd); + break; + + case FS_OP_ERASE: + ret = erase_execute(p_cmd); + break; + + case FS_OP_NONE: + ret = NRF_SUCCESS; + break; + + default: + ret = NRF_ERROR_FORBIDDEN; + break; + } + + return ret; +} + + +/**@brief Starts processing the queue if there are no pending flash operations + * for which we are awaiting a callback. + */ +static ret_code_t queue_process(void) +{ + ret_code_t ret = NRF_SUCCESS; + + /** If the queue is not being processed, and there are still + * some elements in it, then start processing. */ + if ( !(m_flags & FS_FLAG_PROCESSING) && + (m_cmd_queue.count > 0)) + { + m_flags |= FS_FLAG_PROCESSING; + + ret = queue_process_impl(); + + /** There is ongoing flash-operation which was not + * initiated by fstorage. */ + if (ret == NRF_ERROR_BUSY) + { + // Wait for a system callback. + m_flags |= FS_FLAG_FLASH_REQ_PENDING; + + // Stop processing the queue. + m_flags &= ~FS_FLAG_PROCESSING; + + ret = NRF_SUCCESS; + } + else if (ret != NRF_SUCCESS) + { + // Another error has occurred. + app_notify(ret, &m_cmd_queue.cmd[m_cmd_queue.rp]); + } + } + + // If we are already processing the queue, return immediately. + return ret; +} + + +/**@brief Flash operation success callback handler. + * + * @details This function updates read/write pointers. + * This function resets retry count. + */ +static __INLINE void on_operation_success(void) +{ + fs_cmd_t * const p_cmd = &m_cmd_queue.cmd[m_cmd_queue.rp]; + + m_retry_count = 0; + + switch (p_cmd->op_code) + { + case FS_OP_STORE: + // Update the offset on successful write. + p_cmd->offset += FS_MAX_WRITE_SIZE_WORDS; + break; + + case FS_OP_ERASE: + // Update the offset to correspond to the page that has been erased. + p_cmd->offset += FS_PAGE_SIZE_WORDS; + break; + } + + // If offset is equal to or larger than length, then the operation has finished. + if (p_cmd->offset >= p_cmd->length_words) + { + cmd_consume(NRF_SUCCESS, p_cmd); + } + + queue_process(); +} + + +/**@brief Flash operation failure callback handler. + * + * @details Function to keep track of retries and notify failures. + */ +static __INLINE void on_operation_failure(uint32_t sys_evt) +{ + const fs_cmd_t * p_cmd; + + if (++m_retry_count > FS_CMD_MAX_RETRIES) + { + p_cmd = &m_cmd_queue.cmd[m_cmd_queue.rp]; + cmd_consume(NRF_ERROR_TIMEOUT, p_cmd); + } + + queue_process(); +} + + +/**@brief Function to notify users. + * + * @param[in] result Result of the flash operation. + * @param[in] p_cmd The command associated with the callback. + */ +static void app_notify(uint32_t result, fs_cmd_t const * const p_cmd) +{ + p_cmd->p_config->cb(p_cmd->op_code, result, p_cmd->p_addr, p_cmd->length_words); +} + + +ret_code_t fs_init(void) +{ + uint16_t lowest_index = 0; + uint16_t lowest_order = 0xFFFF; + uint32_t * current_end = (uint32_t*)FS_PAGE_END_ADDR; + uint32_t num_left = FS_SECTION_VARS_COUNT; + + queue_init(); + + /** Assign pages to registered users, beginning with the ones with the lowest + * order, which will be assigned pages with the lowest memory address. */ + do + { + fs_config_t * p_config; + for (uint16_t i = 0; i < FS_SECTION_VARS_COUNT; i++) + { + p_config = FS_SECTION_VARS_GET(i); + + // Skip the ones which have the end-address already set. + if (p_config->p_end_addr != NULL) + continue; + + if (p_config->page_order < lowest_order) + { + lowest_order = p_config->page_order; + lowest_index = i; + } + } + + p_config = FS_SECTION_VARS_GET(lowest_index); + + p_config->p_end_addr = current_end; + p_config->p_start_addr = p_config->p_end_addr - (p_config->num_pages * FS_PAGE_SIZE_WORDS); + + current_end = p_config->p_start_addr; + lowest_order = 0xFFFF; + + } while ( --num_left > 0 ); + + m_flags |= FS_FLAG_INIT; + + return NRF_SUCCESS; +} + + +ret_code_t fs_store(fs_config_t const * p_config, + uint32_t const * p_addr, + uint32_t const * const p_data, + fs_length_t length_words) +{ + if ((m_flags & FS_FLAG_INIT) == 0) + { + return NRF_ERROR_INVALID_STATE; + } + + if (!check_config(p_config)) + { + return NRF_ERROR_FORBIDDEN; + } + + if (!is_word_aligned(p_addr)) + { + return NRF_ERROR_INVALID_ADDR; + } + + // Check that the erase operation is on pages owned by this user (configuration). + if ((p_addr < p_config->p_start_addr) || ((p_addr + length_words) > p_config->p_end_addr)) + { + return NRF_ERROR_INVALID_ADDR; + } + + return cmd_enqueue(p_config, FS_OP_STORE, p_addr, p_data, length_words); +} + + +ret_code_t fs_erase(fs_config_t const * p_config, + uint32_t * const p_addr, + fs_length_t const length_words) +{ + if ((m_flags & FS_FLAG_INIT) == 0) + { + return NRF_ERROR_INVALID_STATE; + } + + if (!check_config(p_config)) + { + return NRF_ERROR_FORBIDDEN; + } + + /** Check that the address is aligned on a page boundary and the length to erase + * is a multiple of the page size. */ + if (((uint32_t)p_addr & (FS_PAGE_SIZE - 1)) || + (length_words & (FS_PAGE_SIZE_WORDS - 1))) + { + return NRF_ERROR_INVALID_ADDR; + } + + // Check that the erase operation is on pages owned by this user (configuration). + if ((p_addr < p_config->p_start_addr) || ((p_addr + length_words) > p_config->p_end_addr)) + { + return NRF_ERROR_INVALID_ADDR; + } + + return cmd_enqueue(p_config, FS_OP_ERASE, p_addr, NULL, length_words); +} + + +/**@brief Function to handle system events from the SoftDevice. + * + * @details This function should be dispatched system events if any of the modules used by + * the application rely on FStorage. Examples include @ref Peer Manager and + * @ref Flash Data Storage. + * + * @param[in] sys_evt System Event received. + */ +void fs_sys_event_handler(uint32_t sys_evt) +{ + if (m_flags & FS_FLAG_PROCESSING) + { + /** A flash operation was initiated by this module. + * Handle its result. */ + switch (sys_evt) + { + case NRF_EVT_FLASH_OPERATION_SUCCESS: + on_operation_success(); + break; + + case NRF_EVT_FLASH_OPERATION_ERROR: + on_operation_failure(sys_evt); + break; + } + } + else if ((m_flags & FS_FLAG_FLASH_REQ_PENDING)) + { + /** A flash operation was initiated outside this module. + * We have now receveid a callback which indicates it has + * finished. Clear the FS_FLAG_FLASH_REQ_PENDING flag. */ + m_flags &= ~FS_FLAG_FLASH_REQ_PENDING; + + // Resume processing the queue, if necessary. + queue_process(); + } +} + + +// Just for testing out section vars (across many compilers). +void fs_debug_print() +{ + printf("fs start address: 0x%08lx\r\n", (unsigned long)FS_SECTION_VARS_START_ADDR); + printf("fs end address: 0x%08lx\r\n", (unsigned long)FS_SECTION_VARS_END_ADDR); + printf("Num items: 0x%08lx\r\n", (unsigned long)FS_SECTION_VARS_COUNT); + printf("===== ITEMS %lu =====\r\n", (unsigned long)FS_SECTION_VARS_COUNT); + + for(int i = 0; i < FS_SECTION_VARS_COUNT; i++) + { + fs_config_t* config = FS_SECTION_VARS_GET(i); + printf( "Address: 0x%08lx, CB: 0x%08lx\r\n", + (unsigned long)config, (unsigned long)config->cb ); + } + printf("\r\n"); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/nordic_sdk/components/libraries/fstorage/fstorage.h Mon Jan 11 22:33:57 2016 +0000 @@ -0,0 +1,176 @@ +/* + * Copyright (c) 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 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 other + * contributors to this software may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#ifndef FS_H__ +#define FS_H__ + + /** @file + * + * @defgroup fstorage FStorage + * @{ + * @ingroup app_common + * @brief Module which provides low level functionality to store data to flash. + * + */ + + +#include <stdint.h> +#include "section_vars.h" +#include "fstorage_config.h" +#include "sdk_errors.h" + + +typedef uint16_t fs_length_t; + + +typedef enum +{ + FS_OP_NONE = 0, + FS_OP_STORE = 1, + FS_OP_ERASE = 2 +} fs_oper_t; + + +/**@brief Callback for flash operations. + * + * @param[in] op_code Flash access operation code. + * @param[in] result Result of the operation. + * @param[in] data Pointer to resulting data (or NULL if not in use). + * @param[in] length_words Length of data in words. + */ +typedef void (*fs_cb_t)(uint8_t op_code, + uint32_t result, + uint32_t const * p_data, + fs_length_t length_words); + + +/**@brief Function prototype for a callback handler. + * + * @details This function is expected to be implemented by the module that + * registers for fstorage usage. Its usage is described + * in the function pointer type fs_cb_t. + * + * @param[in] op_code Flash operation code. + * @param[in] result Result of the flash operation. + * @param[in] p_data Pointer to the resulting data (or NULL if not in use). + * @param[in] length_words Length of data in words. + */ +static void fs_callback(uint8_t op_code, + uint32_t result, + uint32_t const * p_data, + fs_length_t length_words); + + +/**@brief Flash storage config variable. + * + * @details The fstorage module will update the start_addr and end_address according to + * ordering rules and the number of pages requested by the fstorage module user. + */ +typedef struct +{ + const fs_cb_t cb; /**< Callback to run when flash operation has completed. */ + const uint8_t num_pages; /**< The number of pages to reserve for flash storage. */ + const uint8_t page_order; /**< The order used to allocate pages. */ + uint32_t * p_start_addr; /**< Pointer to the start address of the allocated flash storage. Set by running @ref fs_init. */ + uint32_t * p_end_addr; /**< Pointer to the end address of the allcoated flash storage. Set by running @ref fs_init. */ +} fs_config_t; + + +/**@brief Macro for registering of flash storage configuration variable. + * + * @details This macro is expected to be invoked in the code unit that that require + * flash storage. Invoking this places the registered configuration variable + * in a section named "fs_data" that the fstorage module uses during initialization + * and regular operation. + */ +#define FS_SECTION_VARS_ADD(type_def) NRF_SECTION_VARS_ADD(fs_data, type_def) + + +/**@brief Function to initialize FStorage. + * + * @details This function allocates flash data pages according to the + * number requested in the config variable. The data used to initialize. + * the fstorage is section placed variables in the data section "fs_data". + */ +ret_code_t fs_init(void); + + +/**@brief Function to store data in flash. + * + * @warning The data to be written to flash has to be kept in memory until the operation has + * terminated, i.e., a callback is received. + * + * @param[in] p_config Const pointer to configiguration of module user that requests a store operation. + * @param[in] p_addr Write address of store operation. + * @param[in] p_data Pointer to the data to store. + * @param[in] length_words Length of the data to store. + * + * @retval NRF_SUCCESS Success. Command queued. + * @retval NRF_ERROR_INVALID_STATE Error. The module is not initialized. + * @retval NRF_ERROR_INVALID_ADDR Error. Data is unaligned or invalid configuration. + * @retval Any error returned by the SoftDevice flash API. + */ +ret_code_t fs_store(fs_config_t const * p_config, + uint32_t const * p_addr, + uint32_t const * const p_data, + fs_length_t length_words); + + +/** Function to erase a page in flash. + * + * @note The erase address must be aligned on a page boundary. The length in words must be + * equivalent to the page size. + * + * @param[in] p_config Pointer to the configuration of the user that requests the operation. + * @param[in] p_addr Address of page to erase (the same as first word in the page). + * @param[in] length_words Length (in 4 byte words) of the area to erase. + * + * @retval NRF_SUCCESS Success. Command queued. + * @retval NRF_ERROR_INVALID_STATE Error. The module is not initialized. + * @retval NRF_ERROR_INVALID_ADDR Error. Data is unaligned or invalid configuration. + * @retval Any error returned by the SoftDevice flash API. + */ +ret_code_t fs_erase(fs_config_t const * p_config, + uint32_t * const p_addr, + fs_length_t length_words); + + +/**@brief Function to call to handle events from the SoftDevice + * + * @param sys_evt System event from the SoftDevice + */ +void fs_sys_event_handler(uint32_t sys_evt); + +/** @} */ + +#endif // FS_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/nordic_sdk/components/libraries/fstorage/fstorage_config.h Mon Jan 11 22:33:57 2016 +0000 @@ -0,0 +1,113 @@ +/* + * Copyright (c) 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 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 other + * contributors to this software may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef FS_CONFIG_H__ +#define FS_CONFIG_H__ + +#include <stdint.h> +#include "nrf.h" + +/** + * @defgroup fstorage_config FStorage configuration + * @ingroup fstorage + * @{ + * @brief FStorage configuration. + */ + + +/**@brief Macro for max number of operations in the fs cmd queue. + */ +#define FS_CMD_QUEUE_SIZE (8) + + +/**@brief Macro for max number of retries for a flash command before it notifies as failed. + */ +#define FS_CMD_MAX_RETRIES (3) + + +/**@brief Macro for the content of a flash address that has not been written to. + */ +#define FS_EMPTY_MASK (0xFFFFFFFF) + + +/**@brief Macro for flash page size according to chip family + */ +#if defined (NRF51) + #define FS_PAGE_SIZE (1024) +#elif defined (NRF52) + #define FS_PAGE_SIZE (4096) +#else + #error "Device family must be defined. See nrf.h." +#endif + + +/*@brief Macro for flash page size according to chip family +*/ +#define FS_PAGE_SIZE_WORDS (FS_PAGE_SIZE/4) + + +/**@brief Static inline function that provides last page address + * + * @note If there is a bootloader present the bootloader address read from UICR + * will act as the page beyond the end of the available flash storage + */ +static __INLINE uint32_t fs_flash_page_end_addr() +{ + uint32_t const bootloader_addr = NRF_UICR->NRFFW[0]; + return ((bootloader_addr != FS_EMPTY_MASK) ? + bootloader_addr : NRF_FICR->CODESIZE * FS_PAGE_SIZE); +} + + +/**@brief Macro for last page address + * + * @note If there is a bootloader present the bootloader address read from UICR + * will act as the page beyond the end of the available flash storage + */ +#define FS_PAGE_END_ADDR fs_flash_page_end_addr() + + +/**@brief Macro to describe the write + * + */ +#if defined (NRF51) + #define FS_MAX_WRITE_SIZE_WORDS (256) +#elif defined (NRF52) + #define FS_MAX_WRITE_SIZE_WORDS (1024) +#else + #error "Device family must be defined. see nrf.h" +#endif + +/** @} */ + +#endif // FS_CONFIG_H__ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/nordic_sdk/components/libraries/fstorage/fstorage_nosd.c Mon Jan 11 22:33:57 2016 +0000 @@ -0,0 +1,32 @@ +/* + * Copyright (c) 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 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 other + * contributors to this software may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/nordic_sdk/components/libraries/util/sdk_mapped_flags.c Mon Jan 11 22:33:57 2016 +0000 @@ -0,0 +1,181 @@ +/* + * Copyright (c) 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 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 other + * contributors to this software may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "sdk_mapped_flags.h" +#include <stdint.h> +#include <stdbool.h> +#include <stddef.h> +#include "compiler_abstraction.h" + + +/**@brief Function for setting the state of a flag to true. + * + * @note This function does not check whether the index is valid. + * + * @param[in] p_flags The collection of flags to modify. + * @param[in] index The index of the flag to modify. + */ +static __INLINE void sdk_mapped_flags_set_by_index(sdk_mapped_flags_t * p_flags, uint16_t index) +{ + *p_flags |= (1U << index); +} + + +/**@brief Function for setting the state of a flag to false. + * + * @note This function does not check whether the index is valid. + * + * @param[in] p_flags The collection of flags to modify. + * @param[in] index The index of the flag to modify. + */ +static __INLINE void sdk_mapped_flags_clear_by_index(sdk_mapped_flags_t * p_flags, uint16_t index) +{ + *p_flags &= ~(1U << index); +} + + +/**@brief Function for getting the state of a flag. + * + * @note This function does not check whether the index is valid. + * + * @param[in] p_flags The collection of flags to read. + * @param[in] index The index of the flag to get. + */ +static __INLINE bool sdk_mapped_flags_get_by_index(sdk_mapped_flags_t flags, uint16_t index) +{ + return ((flags & (1 << index)) != 0); +} + + + +uint16_t sdk_mapped_flags_first_key_index_get(sdk_mapped_flags_t flags) +{ + for (uint16_t i = 0; i < SDK_MAPPED_FLAGS_N_KEYS; i++) + { + if (sdk_mapped_flags_get_by_index(flags, i)) + { + return i; + } + } + return SDK_MAPPED_FLAGS_INVALID_INDEX; +} + + +void sdk_mapped_flags_update_by_key(uint16_t * p_keys, + sdk_mapped_flags_t * p_flags, + uint16_t key, + bool value) +{ + sdk_mapped_flags_bulk_update_by_key(p_keys, p_flags, 1, key, value); +} + + +void sdk_mapped_flags_bulk_update_by_key(uint16_t * p_keys, + sdk_mapped_flags_t * p_flags, + uint32_t n_flag_collections, + uint16_t key, + bool value) +{ + if ((p_keys != NULL) && (p_flags != NULL) && (n_flag_collections > 0)) + { + for (int i = 0; i < SDK_MAPPED_FLAGS_N_KEYS; i++) + { + if (p_keys[i] == key) + { + for (int j = 0; j < n_flag_collections; j++) + { + if (value) + { + sdk_mapped_flags_set_by_index(&p_flags[j], i); + } + else + { + sdk_mapped_flags_clear_by_index(&p_flags[j], i); + } + } + return; + } + } + } +} + + +bool sdk_mapped_flags_get_by_key(uint16_t * p_keys, sdk_mapped_flags_t flags, uint16_t key) +{ + if (p_keys != NULL) + { + for (int i = 0; i < SDK_MAPPED_FLAGS_N_KEYS; i++) + { + if (p_keys[i] == key) + { + return sdk_mapped_flags_get_by_index(flags, i); + } + } + } + return false; +} + + +sdk_mapped_flags_key_list_t sdk_mapped_flags_key_list_get(uint16_t * p_keys, + sdk_mapped_flags_t flags) +{ + sdk_mapped_flags_key_list_t key_list; + key_list.len = 0; + + if (p_keys != NULL) + { + for (int i = 0; i < SDK_MAPPED_FLAGS_N_KEYS; i++) + { + if (sdk_mapped_flags_get_by_index(flags, i)) + { + key_list.flag_keys[key_list.len++] = p_keys[i]; + } + } + } + + return key_list; +} + + +uint32_t sdk_mapped_flags_n_flags_set(sdk_mapped_flags_t flags) +{ + uint32_t n_flags_set = 0; + + for (int i = 0; i < SDK_MAPPED_FLAGS_N_KEYS; i++) + { + if (sdk_mapped_flags_get_by_index(flags, i)) + { + n_flags_set += 1; + } + } + return n_flags_set; +}