Add missing implementations for peer_manager. This is required by armcc,

even if implementations doesn't makes it into final binary
This commit is contained in:
Vincent Coubard 2016-01-11 14:42:49 +00:00
parent a0faa63aae
commit 51c333fe69
25 changed files with 7554 additions and 0 deletions

View File

@ -3,6 +3,7 @@ Module to contain files provided by the nordic nRF51 SDK. The latest version of
## Changes made to Nordic files ## Changes made to Nordic files
The files are kept the same as much as possible to the Nordic SDK. Modifications are made in order to integrate with mbed. The files are kept the same as much as possible to the Nordic SDK. Modifications are made in order to integrate with mbed.
- ble/common/ble_conn_state.c: Preprocessor tests regarding S110, S120 or S130 macro should be replace by TARGET_MCU_NRF51_XXK_SXXX tests
## Porting new versions of Nordic SDK ## Porting new versions of Nordic SDK
A list of files currently requierd by mbed is maintained in [script/required_files.txt](https://github.com/ARMmbed/nrf51-sdk/blob/master/script/required_files.txt). [A python script](https://github.com/ARMmbed/nrf51-sdk/blob/master/script/pick_nrf51_files.py) is written to help porting from nordic sdk releases. **required_files.txt** is parsed to find a list of filenames. The script searches for these filenames in the sdk folder, and copy then into the yotta module mirroring the folder structure in the sdk. **extraIncludes** is automatically added to module.json to allow direct inclusion of noridc headers with just the filename. A list of files currently requierd by mbed is maintained in [script/required_files.txt](https://github.com/ARMmbed/nrf51-sdk/blob/master/script/required_files.txt). [A python script](https://github.com/ARMmbed/nrf51-sdk/blob/master/script/pick_nrf51_files.py) is written to help porting from nordic sdk releases. **required_files.txt** is parsed to find a list of filenames. The script searches for these filenames in the sdk folder, and copy then into the yotta module mirroring the folder structure in the sdk. **extraIncludes** is automatically added to module.json to allow direct inclusion of noridc headers with just the filename.

View File

@ -41,6 +41,9 @@
"source/nordic_sdk/components/libraries/scheduler", "source/nordic_sdk/components/libraries/scheduler",
"source/nordic_sdk/components/libraries/timer", "source/nordic_sdk/components/libraries/timer",
"source/nordic_sdk/components/libraries/util", "source/nordic_sdk/components/libraries/util",
"source/nordic_sdk/components/libraries/fds",
"source/nordic_sdk/components/libraries/fstorage",
"source/nordic_sdk/components/libraries/experimental_section_vars",
"source/nordic_sdk/components/softdevice/common/softdevice_handler", "source/nordic_sdk/components/softdevice/common/softdevice_handler",
"source/nordic_sdk/components/softdevice/s130/headers", "source/nordic_sdk/components/softdevice/s130/headers",
"source/nordic_sdk/components/toolchain" "source/nordic_sdk/components/toolchain"

View File

@ -107,6 +107,23 @@
source/nordic-sdk/components/softdevice/s130/include/nrf_svc.h source/nordic-sdk/components/softdevice/s130/include/nrf_svc.h
source/nordic-sdk/components/softdevice/s130/include/softdevice_assert.h source/nordic-sdk/components/softdevice/s130/include/softdevice_assert.h
source/nordic-sdk/components/drivers_nrf/hal/nrf_wdt.h source/nordic-sdk/components/drivers_nrf/hal/nrf_wdt.h
source/nordic-sdk/components/ble/common/ble_conn_state.c
source/nordic-sdk/components/ble/peer_manager/peer_data.c
source/nordic-sdk/components/ble/peer_manager/peer_data.h
source/nordic-sdk/components/ble/peer_manager/peer_data_storage.c
source/nordic-sdk/components/ble/peer_manager/peer_data_storage.h
source/nordic-sdk/components/ble/peer_manager/peer_database.c
source/nordic-sdk/components/ble/peer_manager/peer_id.c
source/nordic-sdk/components/ble/peer_manager/peer_id.h
source/nordic-sdk/components/ble/peer_manager/pm_buffer.c
source/nordic-sdk/components/ble/peer_manager/pm_buffer.h
source/nordic-sdk/components/ble/peer_manager/pm_mutex.c
source/nordic-sdk/components/ble/peer_manager/pm_mutex.h
source/nordic-sdk/components/libraries/experimental_section_vars/
source/nordic-sdk/components/libraries/fds/
source/nordic-sdk/components/libraries/fstorage/
source/nordic-sdk/components/libraries/util/sdk_mapped_flags.c
# from mbed-hal-nrf51822-mcu # from mbed-hal-nrf51822-mcu
mbed-hal-nrf51822-mcu/lib/nordic_sdk/components/libraries/crc16/crc16.h mbed-hal-nrf51822-mcu/lib/nordic_sdk/components/libraries/crc16/crc16.h

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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__ */

View File

@ -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();
}

View File

@ -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__ */

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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__ */

View File

@ -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);
}
}

View File

@ -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__
/**
* @}
*/

View File

@ -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;
}
}

View File

@ -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__
/** @} */

View File

@ -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__

File diff suppressed because it is too large Load Diff

View File

@ -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__

View File

@ -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__

View File

@ -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__

View File

@ -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");
}

View File

@ -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__

View File

@ -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__

View File

@ -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.
*
*/

View File

@ -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;
}