Nano RGB LED mesh

Dependents:   nRF51822

Fork of nrf51-sdk by Nordic Semiconductor

Files at this revision

API Documentation at this revision

Comitter:
vcoubard
Date:
Thu Apr 07 17:38:12 2016 +0100
Parent:
37:8f65b11f95aa
Child:
39:7dafb381465b
Commit message:
Synchronized with git rev 51c333fe
Author: Vincent Coubard
Add missing implementations for peer_manager. This is required by armcc,
even if implementations doesn't makes it into final binary

Changed in this revision

README.md Show annotated file Show diff for this revision Revisions of this file
source/nordic_sdk/components/ble/common/ble_conn_state.c Show annotated file Show diff for this revision Revisions of this file
source/nordic_sdk/components/ble/peer_manager/id_manager.h Show annotated file Show diff for this revision Revisions of this file
source/nordic_sdk/components/ble/peer_manager/peer_data.c Show annotated file Show diff for this revision Revisions of this file
source/nordic_sdk/components/ble/peer_manager/peer_data.h Show annotated file Show diff for this revision Revisions of this file
source/nordic_sdk/components/ble/peer_manager/peer_data_storage.c Show annotated file Show diff for this revision Revisions of this file
source/nordic_sdk/components/ble/peer_manager/peer_data_storage.h Show annotated file Show diff for this revision Revisions of this file
source/nordic_sdk/components/ble/peer_manager/peer_database.c Show annotated file Show diff for this revision Revisions of this file
source/nordic_sdk/components/ble/peer_manager/peer_id.c Show annotated file Show diff for this revision Revisions of this file
source/nordic_sdk/components/ble/peer_manager/peer_id.h Show annotated file Show diff for this revision Revisions of this file
source/nordic_sdk/components/ble/peer_manager/pm_buffer.c Show annotated file Show diff for this revision Revisions of this file
source/nordic_sdk/components/ble/peer_manager/pm_buffer.h Show annotated file Show diff for this revision Revisions of this file
source/nordic_sdk/components/ble/peer_manager/pm_mutex.c Show annotated file Show diff for this revision Revisions of this file
source/nordic_sdk/components/ble/peer_manager/pm_mutex.h Show annotated file Show diff for this revision Revisions of this file
source/nordic_sdk/components/libraries/experimental_section_vars/section_vars.h Show annotated file Show diff for this revision Revisions of this file
source/nordic_sdk/components/libraries/fds/fds.c Show annotated file Show diff for this revision Revisions of this file
source/nordic_sdk/components/libraries/fds/fds.h Show annotated file Show diff for this revision Revisions of this file
source/nordic_sdk/components/libraries/fds/fds_config.h Show annotated file Show diff for this revision Revisions of this file
source/nordic_sdk/components/libraries/fds/fds_types_internal.h Show annotated file Show diff for this revision Revisions of this file
source/nordic_sdk/components/libraries/fstorage/fstorage.c Show annotated file Show diff for this revision Revisions of this file
source/nordic_sdk/components/libraries/fstorage/fstorage.h Show annotated file Show diff for this revision Revisions of this file
source/nordic_sdk/components/libraries/fstorage/fstorage_config.h Show annotated file Show diff for this revision Revisions of this file
source/nordic_sdk/components/libraries/fstorage/fstorage_nosd.c Show annotated file Show diff for this revision Revisions of this file
source/nordic_sdk/components/libraries/util/sdk_mapped_flags.c Show annotated file Show diff for this revision Revisions of this file
--- a/README.md	Thu Apr 07 17:38:10 2016 +0100
+++ b/README.md	Thu Apr 07 17:38:12 2016 +0100
@@ -3,6 +3,7 @@
 
 ## 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.
+    - 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
 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.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/source/nordic_sdk/components/ble/common/ble_conn_state.c	Thu Apr 07 17:38:12 2016 +0100
@@ -0,0 +1,413 @@
+/*
+ * Copyright (c) Nordic Semiconductor ASA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright notice, this
+ *   list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright notice, this
+ *   list of conditions and the following disclaimer in the documentation and/or
+ *   other materials provided with the distribution.
+ *
+ *   3. Neither the name of Nordic Semiconductor ASA nor the names of other
+ *   contributors to this software may be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "ble_conn_state.h"
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include "ble.h"
+#include "sdk_mapped_flags.h"
+#include "app_error.h"
+
+
+#if defined(__CC_ARM)
+  #pragma push
+  #pragma anon_unions
+#elif defined(__ICCARM__)
+  #pragma language=extended
+#elif defined(__GNUC__)
+  /* anonymous unions are enabled by default */
+#endif
+
+
+#define BLE_CONN_STATE_N_DEFAULT_FLAGS 5                                                       /**< The number of flags kept for each connection, excluding user flags. */
+#define BLE_CONN_STATE_N_FLAGS (BLE_CONN_STATE_N_DEFAULT_FLAGS + BLE_CONN_STATE_N_USER_FLAGS)  /**< The number of flags kept for each connection, including user flags. */
+
+
+/**@brief Structure containing all the flag collections maintained by the Connection State module.
+ */
+typedef struct
+{
+    sdk_mapped_flags_t valid_flags;                                 /**< Flags indicating which connection handles are valid. */
+    sdk_mapped_flags_t connected_flags;                             /**< Flags indicating which connections are connected, since disconnected connection handles will not immediately be invalidated. */
+    sdk_mapped_flags_t central_flags;                               /**< Flags indicating in which connections the local device is the central. */
+    sdk_mapped_flags_t encrypted_flags;                             /**< Flags indicating which connections are encrypted. */
+    sdk_mapped_flags_t mitm_protected_flags;                        /**< Flags indicating which connections have encryption with protection from man-in-the-middle attacks. */
+    sdk_mapped_flags_t user_flags[BLE_CONN_STATE_N_USER_FLAGS];     /**< Flags that can be reserved by the user. The flags will be cleared when a connection is invalidated, otherwise, the user is wholly responsible for the flag states. */
+} ble_conn_state_flag_collections_t;
+
+
+/**@brief Structure containing the internal state of the Connection State module.
+ */
+typedef struct
+{
+    uint16_t           acquired_flags;                              /**< Bitmap for keeping track of which user flags have been acquired. */
+    uint16_t           valid_conn_handles[SDK_MAPPED_FLAGS_N_KEYS]; /**< List of connection handles used as keys for the sdk_mapped_flags module. */
+    union
+    {
+        ble_conn_state_flag_collections_t flags;                              /**< Flag collections kept by the Connection State module. */
+        sdk_mapped_flags_t                flag_array[BLE_CONN_STATE_N_FLAGS]; /**< Flag collections as array to allow use of @ref sdk_mapped_flags_bulk_update_by_key() when setting all flags. */
+    };
+} ble_conn_state_t;
+
+
+#if defined(__CC_ARM)
+  #pragma pop
+#elif defined(__ICCARM__)
+  /* leave anonymous unions enabled */
+#elif defined(__GNUC__)
+  /* anonymous unions are enabled by default */
+#endif
+
+
+static ble_conn_state_t m_bcs = {0}; /**< Instantiation of the internal state. */
+
+
+/**@brief Function for resetting all internal memory to the values it had at initialization.
+ */
+void bcs_internal_state_reset(void)
+{
+    memset( &m_bcs, 0, sizeof(ble_conn_state_t) );
+}
+
+
+/**@brief Function for activating a connection record.
+ *
+ * @param p_record     The record to activate.
+ * @param conn_handle  The connection handle to copy into the record.
+ * @param role         The role of the connection.
+ *
+ * @return whether the record was activated successfully.
+ */
+static bool record_activate(uint16_t conn_handle)
+{
+    uint16_t available_index = sdk_mapped_flags_first_key_index_get(~m_bcs.flags.valid_flags);
+
+    if (available_index != SDK_MAPPED_FLAGS_INVALID_INDEX)
+    {
+        m_bcs.valid_conn_handles[available_index] = conn_handle;
+        sdk_mapped_flags_update_by_key(m_bcs.valid_conn_handles,
+                                      &m_bcs.flags.connected_flags,
+                                       conn_handle,
+                                       1);
+        sdk_mapped_flags_update_by_key(m_bcs.valid_conn_handles,
+                                      &m_bcs.flags.valid_flags,
+                                       conn_handle,
+                                       1);
+
+        return true;
+    }
+
+    return false;
+}
+
+
+/**@brief Function for marking a connection record as invalid and resetting the values.
+ *
+ * @param p_record  The record to invalidate.
+ */
+static void record_invalidate(uint16_t conn_handle)
+{
+    sdk_mapped_flags_bulk_update_by_key(m_bcs.valid_conn_handles,
+                                        m_bcs.flag_array,
+                                        BLE_CONN_STATE_N_FLAGS,
+                                        conn_handle,
+                                        0);
+}
+
+
+/**@brief Function for marking a connection as disconnected. See @ref BLE_CONN_STATUS_DISCONNECTED.
+ *
+ * @param p_record   The record of the connection to set as disconnected.
+ */
+static void record_set_disconnected(uint16_t conn_handle)
+{
+    sdk_mapped_flags_update_by_key(m_bcs.valid_conn_handles,
+                                  &m_bcs.flags.connected_flags,
+                                   conn_handle,
+                                   0);
+}
+
+
+/**@brief Function for invalidating records with a @ref BLE_CONN_STATUS_DISCONNECTED
+ *        connection status
+ */
+static void record_purge_disconnected()
+{
+    sdk_mapped_flags_key_list_t disconnected_list;
+
+    disconnected_list = sdk_mapped_flags_key_list_get(
+                                   m_bcs.valid_conn_handles,
+                                 (~m_bcs.flags.connected_flags) & (m_bcs.flags.valid_flags));
+
+    for (int i = 0; i < disconnected_list.len; i++)
+    {
+        record_invalidate(disconnected_list.flag_keys[i]);
+    }
+}
+
+
+/**@brief Function for checking if a user flag has been acquired.
+ *
+ * @param[in]  flag_id  Which flag to check.
+ *
+ * @return  Whether the flag has been acquired.
+ */
+static bool user_flag_is_acquired(ble_conn_state_user_flag_id_t flag_id)
+{
+    return ((m_bcs.acquired_flags & (1 << flag_id)) != 0);
+}
+
+
+/**@brief Function for marking a user flag as acquired.
+ *
+ * @param[in]  flag_id  Which flag to mark.
+ */
+static void user_flag_acquire(ble_conn_state_user_flag_id_t flag_id)
+{
+    m_bcs.acquired_flags |= (1 << flag_id);
+}
+
+
+void ble_conn_state_init(void)
+{
+    bcs_internal_state_reset();
+}
+
+
+void ble_conn_state_on_ble_evt(ble_evt_t * p_ble_evt)
+{
+    switch (p_ble_evt->header.evt_id)
+    {
+        case BLE_GAP_EVT_CONNECTED:
+            record_purge_disconnected();
+
+            if ( !record_activate(p_ble_evt->evt.gap_evt.conn_handle) )
+            {
+                // No more records available. Should not happen.
+                APP_ERROR_HANDLER(NRF_ERROR_NO_MEM);
+            }
+            else
+            {
+#if defined(TARGET_MCU_NRF51_16K_S110) || defined(TARGET_MCU_NRF51_32K_S110)
+                bool is_central = false;
+#elif defined(TARGET_MCU_NRF51_16K_S120) || defined(TARGET_MCU_NRF51_32K_S120)
+                bool is_central = true;
+#else
+                bool is_central =
+                        (p_ble_evt->evt.gap_evt.params.connected.role == BLE_GAP_ROLE_CENTRAL);
+#endif
+
+                sdk_mapped_flags_update_by_key(m_bcs.valid_conn_handles,
+                                              &m_bcs.flags.central_flags,
+                                               p_ble_evt->evt.gap_evt.conn_handle,
+                                               is_central);
+            }
+
+            break;
+
+        case BLE_GAP_EVT_DISCONNECTED:
+            record_set_disconnected(p_ble_evt->evt.gap_evt.conn_handle);
+            break;
+
+        case BLE_GAP_EVT_CONN_SEC_UPDATE:
+            sdk_mapped_flags_update_by_key(
+                          m_bcs.valid_conn_handles,
+                         &m_bcs.flags.encrypted_flags,
+                          p_ble_evt->evt.gap_evt.conn_handle,
+                         (p_ble_evt->evt.gap_evt.params.conn_sec_update.conn_sec.sec_mode.lv > 1));
+            sdk_mapped_flags_update_by_key(
+                          m_bcs.valid_conn_handles,
+                         &m_bcs.flags.mitm_protected_flags,
+                          p_ble_evt->evt.gap_evt.conn_handle,
+                         (p_ble_evt->evt.gap_evt.params.conn_sec_update.conn_sec.sec_mode.lv > 2));
+            break;
+    }
+}
+
+
+bool ble_conn_state_valid(uint16_t conn_handle)
+{
+    return sdk_mapped_flags_get_by_key(m_bcs.valid_conn_handles,
+                                       m_bcs.flags.valid_flags,
+                                       conn_handle);
+}
+
+
+uint8_t ble_conn_state_role(uint16_t conn_handle)
+{
+    uint8_t role = BLE_GAP_ROLE_INVALID;
+
+    if ( sdk_mapped_flags_get_by_key(m_bcs.valid_conn_handles, m_bcs.flags.valid_flags, conn_handle) )
+    {
+        bool central = sdk_mapped_flags_get_by_key(m_bcs.valid_conn_handles,
+                                                   m_bcs.flags.central_flags,
+                                                   conn_handle);
+
+        role = central ? BLE_GAP_ROLE_CENTRAL : BLE_GAP_ROLE_PERIPH;
+    }
+
+    return role;
+}
+
+
+ble_conn_state_status_t ble_conn_state_status(uint16_t conn_handle)
+{
+    ble_conn_state_status_t conn_status = BLE_CONN_STATUS_INVALID;
+    bool valid = sdk_mapped_flags_get_by_key(m_bcs.valid_conn_handles,
+                                             m_bcs.flags.valid_flags,
+                                             conn_handle);
+
+    if (valid)
+    {
+        bool connected = sdk_mapped_flags_get_by_key(m_bcs.valid_conn_handles,
+                                                     m_bcs.flags.connected_flags,
+                                                     conn_handle);
+
+        conn_status = connected ? BLE_CONN_STATUS_CONNECTED : BLE_CONN_STATUS_DISCONNECTED;
+    }
+
+    return conn_status;
+}
+
+
+bool ble_conn_state_encrypted(uint16_t conn_handle)
+{
+    return sdk_mapped_flags_get_by_key(m_bcs.valid_conn_handles,
+                                       m_bcs.flags.encrypted_flags,
+                                       conn_handle);
+}
+
+
+bool ble_conn_state_mitm_protected(uint16_t conn_handle)
+{
+    return sdk_mapped_flags_get_by_key(m_bcs.valid_conn_handles,
+                                       m_bcs.flags.mitm_protected_flags,
+                                       conn_handle);
+}
+
+
+uint32_t ble_conn_state_n_connections(void)
+{
+    return sdk_mapped_flags_n_flags_set(m_bcs.flags.connected_flags);
+}
+
+
+uint32_t ble_conn_state_n_centrals(void)
+{
+    return sdk_mapped_flags_n_flags_set((m_bcs.flags.central_flags) & (m_bcs.flags.connected_flags));
+}
+
+
+uint32_t ble_conn_state_n_peripherals(void)
+{
+    return sdk_mapped_flags_n_flags_set((~m_bcs.flags.central_flags) & (m_bcs.flags.connected_flags));
+}
+
+
+sdk_mapped_flags_key_list_t ble_conn_state_conn_handles(void)
+{
+    return sdk_mapped_flags_key_list_get(m_bcs.valid_conn_handles, m_bcs.flags.valid_flags);
+}
+
+
+sdk_mapped_flags_key_list_t ble_conn_state_central_handles(void)
+{
+    return sdk_mapped_flags_key_list_get(m_bcs.valid_conn_handles,
+                                        (m_bcs.flags.central_flags) & (m_bcs.flags.connected_flags));
+}
+
+
+sdk_mapped_flags_key_list_t ble_conn_state_periph_handles(void)
+{
+    return sdk_mapped_flags_key_list_get(m_bcs.valid_conn_handles,
+                                        (~m_bcs.flags.central_flags) & (m_bcs.flags.connected_flags));
+}
+
+
+ble_conn_state_user_flag_id_t ble_conn_state_user_flag_acquire(void)
+{
+    for (ble_conn_state_user_flag_id_t i = BLE_CONN_STATE_USER_FLAG0;
+                                       i < BLE_CONN_STATE_N_USER_FLAGS;
+                                       i++)
+    {
+        if ( !user_flag_is_acquired(i) )
+        {
+            user_flag_acquire(i);
+            return i;
+        }
+    }
+
+    return BLE_CONN_STATE_USER_FLAG_INVALID;
+}
+
+
+bool ble_conn_state_user_flag_get(uint16_t conn_handle, ble_conn_state_user_flag_id_t flag_id)
+{
+    if (user_flag_is_acquired(flag_id))
+    {
+        return sdk_mapped_flags_get_by_key(m_bcs.valid_conn_handles,
+                                           m_bcs.flags.user_flags[flag_id],
+                                           conn_handle);
+    }
+    else
+    {
+        return false;
+    }
+}
+
+
+void ble_conn_state_user_flag_set(uint16_t                      conn_handle,
+                                  ble_conn_state_user_flag_id_t flag_id,
+                                  bool                          value)
+{
+    if (user_flag_is_acquired(flag_id))
+    {
+        sdk_mapped_flags_update_by_key(m_bcs.valid_conn_handles,
+                                      &m_bcs.flags.user_flags[flag_id],
+                                       conn_handle,
+                                       value);
+    }
+}
+
+
+sdk_mapped_flags_t ble_conn_state_user_flag_collection(ble_conn_state_user_flag_id_t flag_id)
+{
+    if ( user_flag_is_acquired(flag_id) )
+    {
+        return m_bcs.flags.user_flags[flag_id];
+    }
+    else
+    {
+        return 0;
+    }
+}
--- a/source/nordic_sdk/components/ble/peer_manager/id_manager.h	Thu Apr 07 17:38:10 2016 +0100
+++ b/source/nordic_sdk/components/ble/peer_manager/id_manager.h	Thu Apr 07 17:38:12 2016 +0100
@@ -229,38 +229,6 @@
  */
 bool im_address_resolve(ble_gap_addr_t const * p_addr, ble_gap_irk_t const * p_irk);
 
-/**@brief Function for calculating the ah() hash function described in Bluetooth core specification
- *        4.2 section 3.H.2.2.2.
- *
- * @detail  BLE uses a hash function to calculate the first half of a resolvable address
- *          from the second half of the address and an irk. This function will use the ECB
- *          periferal to hash these data acording to the Bluetooth core specification.
- *
- * @note The ECB expect little endian input and output.
- *       This function expect big endian and will reverse the data as necessary.
- *
- * @param[in]  p_k          The key used in the hash function.
- *                          For address resolution this is should be the irk.
- *                          The array must have a length of 16.
- * @param[in]  p_r          The rand used in the hash function. For generating a new address
- *                          this would be a random number. For resolving a resolvable address
- *                          this would be the last half of the address being resolved.
- *                          The array must have a length of 3.
- * @param[out] p_local_hash The result of the hash operation. For address resolution this
- *                          will match the first half of the address being resolved if and only
- *                          if the irk used in the hash function is the same one used to generate
- *                          the address.
- *                          The array must have a length of 16.
- *
- * @note    ====IMPORTANT====
- *          This is a special modification to the original nRF51 SDK required by the mbed BLE API
- *          to being able to generate BLE private resolvable addresses. This function is used by
- *          the BLE API implementation for nRF5xSecurityManager::getAddressFromBondTable() in the
- *          ble-nrf51822 yotta module.
- *          =================
- */
-void ah(uint8_t const * p_k, uint8_t const * p_r, uint8_t * p_local_hash);
-
 /** @} */
 
 #endif /* PEER_ID_MANAGER_H__ */
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/source/nordic_sdk/components/ble/peer_manager/peer_data.c	Thu Apr 07 17:38:12 2016 +0100
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) Nordic Semiconductor ASA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright notice, this
+ *   list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright notice, this
+ *   list of conditions and the following disclaimer in the documentation and/or
+ *   other materials provided with the distribution.
+ *
+ *   3. Neither the name of Nordic Semiconductor ASA nor the names of other
+ *   contributors to this software may be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+
+#include "peer_data.h"
+
+#include <stdint.h>
+#include <string.h>
+#include "peer_manager_types.h"
+#include "fds.h"
+
+
+
+void peer_data_parts_get(pm_peer_data_const_t const * p_peer_data, fds_record_chunk_t * p_chunks, uint16_t * p_n_chunks)
+{
+    if (p_n_chunks == NULL)
+    {
+    }
+    else if ((p_peer_data == NULL) || (p_chunks == NULL))
+    {
+        *p_n_chunks = 0;
+    }
+    else
+    {
+        switch (p_peer_data->data_type)
+        {
+            case PM_PEER_DATA_ID_BONDING:
+                p_chunks[0].p_data       = p_peer_data->data.p_bonding_data;
+                p_chunks[0].length_words = p_peer_data->length_words;
+                *p_n_chunks = 1;
+                break;
+            case PM_PEER_DATA_ID_SERVICE_CHANGED_PENDING:
+                p_chunks[0].p_data       = p_peer_data->data.p_service_changed_pending;
+                p_chunks[0].length_words = p_peer_data->length_words;
+                *p_n_chunks = 1;
+                break;
+            case PM_PEER_DATA_ID_GATT_LOCAL:
+                p_chunks[0].p_data       = p_peer_data->data.p_local_gatt_db;
+                p_chunks[0].length_words = PM_N_WORDS(PM_LOCAL_DB_LEN_OVERHEAD_BYTES);
+                p_chunks[1].p_data       = p_peer_data->data.p_local_gatt_db->p_data;
+                p_chunks[1].length_words = p_peer_data->length_words - p_chunks[0].length_words;
+                *p_n_chunks = 2;
+                break;
+            case PM_PEER_DATA_ID_GATT_REMOTE:
+                p_chunks[0].p_data       = p_peer_data->data.p_remote_gatt_db;
+                p_chunks[0].length_words = PM_N_WORDS(PM_REMOTE_DB_LEN_OVERHEAD_BYTES);
+                p_chunks[1].p_data       = p_peer_data->data.p_remote_gatt_db->p_data;
+                p_chunks[1].length_words = p_peer_data->length_words - p_chunks[0].length_words;
+                *p_n_chunks = 2;
+                break;
+            case PM_PEER_DATA_ID_APPLICATION:
+                p_chunks[0].p_data       = p_peer_data->data.p_application_data;
+                p_chunks[0].length_words = p_peer_data->length_words;
+                *p_n_chunks = 1;
+                break;
+            default:
+                *p_n_chunks = 0;
+                break;
+        }
+    }
+}
+
+
+ret_code_t peer_data_deserialize(pm_peer_data_flash_t const * p_in_data, pm_peer_data_t * p_out_data)
+{
+    if ((p_in_data == NULL) || (p_out_data == NULL))
+    {
+        return NRF_ERROR_NULL;
+    }
+    else
+    {
+        if (p_out_data->length_words < p_in_data->length_words)
+        {
+            p_out_data->length_words = p_in_data->length_words;
+            return NRF_ERROR_NO_MEM;
+        }
+        p_out_data->length_words = p_in_data->length_words;
+        p_out_data->data_type    = p_in_data->data_type;
+
+        switch (p_in_data->data_type)
+        {
+            case PM_PEER_DATA_ID_BONDING:
+                *p_out_data->data.p_bonding_data = *p_in_data->data.p_bonding_data;
+                break;
+            case PM_PEER_DATA_ID_SERVICE_CHANGED_PENDING:
+                *p_out_data->data.p_service_changed_pending = *p_in_data->data.p_service_changed_pending;
+                break;
+            case PM_PEER_DATA_ID_GATT_LOCAL:
+                if (p_out_data->data.p_local_gatt_db->p_data == NULL)
+                {
+                    return NRF_ERROR_NULL;
+                }
+                if (p_out_data->data.p_local_gatt_db->len < p_in_data->data.p_local_gatt_db->len)
+                {
+                    p_out_data->data.p_local_gatt_db->len = p_in_data->data.p_local_gatt_db->len;
+                    return NRF_ERROR_NO_MEM;
+                }
+                else
+                {
+                    p_out_data->data.p_local_gatt_db->flags = p_in_data->data.p_local_gatt_db->flags;
+                    p_out_data->data.p_local_gatt_db->len   = p_in_data->data.p_local_gatt_db->len;
+                    memcpy(p_out_data->data.p_local_gatt_db->p_data,
+                           p_in_data->data.p_local_gatt_db->p_data,
+                           p_in_data->data.p_local_gatt_db->len);
+                }
+                break;
+            case PM_PEER_DATA_ID_GATT_REMOTE:
+                if (p_out_data->data.p_remote_gatt_db->p_data == NULL)
+                {
+                    return NRF_ERROR_NULL;
+                }
+                if (p_out_data->data.p_remote_gatt_db->service_count < p_in_data->data.p_remote_gatt_db->service_count)
+                {
+                    p_out_data->data.p_remote_gatt_db->service_count = p_in_data->data.p_remote_gatt_db->service_count;
+                    return NRF_ERROR_NO_MEM;
+                }
+                else
+                {
+                    p_out_data->data.p_remote_gatt_db->service_count = p_in_data->data.p_remote_gatt_db->service_count;
+                    memcpy(p_out_data->data.p_remote_gatt_db->p_data,
+                           p_in_data->data.p_remote_gatt_db->p_data,
+                           p_in_data->data.p_remote_gatt_db->service_count * sizeof(ble_gatt_db_srv_t));
+                }
+                break;
+            case PM_PEER_DATA_ID_APPLICATION:
+                memcpy(p_out_data->data.p_application_data,
+                       p_in_data->data.p_application_data,
+                       p_in_data->length_words * 4);
+                break;
+            default:
+                break;
+        }
+    }
+    return NRF_SUCCESS;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/source/nordic_sdk/components/ble/peer_manager/peer_data.h	Thu Apr 07 17:38:12 2016 +0100
@@ -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__ */
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/source/nordic_sdk/components/ble/peer_manager/peer_data_storage.c	Thu Apr 07 17:38:12 2016 +0100
@@ -0,0 +1,687 @@
+/*
+ * Copyright (c) Nordic Semiconductor ASA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright notice, this
+ *   list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright notice, this
+ *   list of conditions and the following disclaimer in the documentation and/or
+ *   other materials provided with the distribution.
+ *
+ *   3. Neither the name of Nordic Semiconductor ASA nor the names of other
+ *   contributors to this software may be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+
+#include "peer_data_storage.h"
+
+#include <stdint.h>
+#include <string.h>
+#include "sdk_errors.h"
+#include "peer_manager_types.h"
+#include "peer_id.h"
+#include "peer_data.h"
+#include "fds.h"
+
+#define MAX_REGISTRANTS    6                         /**< The number of user that can register with the module. */
+
+#define MODULE_INITIALIZED (m_pds.n_registrants > 0) /**< Expression which is true when the module is initialized. */
+
+/**@brief Macro for verifying that the module is initialized. It will cause the function to return
+ *        @ref NRF_ERROR_INVALID_STATE if not.
+ */
+#define VERIFY_MODULE_INITIALIZED()         \
+do                                          \
+{                                           \
+    if (!MODULE_INITIALIZED)                \
+    {                                       \
+        return NRF_ERROR_INVALID_STATE;     \
+    }                                       \
+} while(0)
+
+
+/**@brief Macro for verifying that the module is initialized. It will cause the function to return
+ *        if not.
+ */
+#define VERIFY_MODULE_INITIALIZED_VOID()    \
+do                                          \
+{                                           \
+    if (!MODULE_INITIALIZED)                \
+    {                                       \
+        return;                             \
+    }                                       \
+} while(0)
+
+
+/**@brief Macro for verifying that the param is not NULL. It will cause the function to return
+ *        if not.
+ *
+ * @param[in] param  The variable to check if is NULL.
+ */
+#define VERIFY_PARAM_NOT_NULL(param)        \
+do                                          \
+{                                           \
+    if (param == NULL)                      \
+    {                                       \
+        return NRF_ERROR_NULL;              \
+    }                                       \
+} while(0)
+
+
+/**@brief Macro for verifying that param is not zero. It will cause the function to return
+ *        if not.
+ *
+ * @param[in] param  The variable to check if is zero.
+ */
+#define VERIFY_PARAM_NOT_ZERO(param)        \
+do                                          \
+{                                           \
+    if (param == 0)                         \
+    {                                       \
+        return NRF_ERROR_NULL;              \
+    }                                       \
+} while(0)
+
+
+/**@brief Macro for verifying that the peer id is within a valid range
+ *
+ * @param[in]   id      The peer data id to check.
+ */
+#define VERIFY_PEER_ID_IN_RANGE(id)         \
+do                                          \
+{                                           \
+    if ((id >= PM_PEER_ID_N_AVAILABLE_IDS)) \
+    {                                       \
+        return NRF_ERROR_INVALID_PARAM;     \
+    }                                       \
+} while (0)
+
+
+/**@brief Macro for verifying that the peer data id is withing a valid range
+ *
+ * @param[in]   id      The peer data id to check.
+ */
+#define VERIFY_PEER_DATA_ID_IN_RANGE(id)    \
+do                                          \
+{                                           \
+    if (!PM_PEER_DATA_ID_IS_VALID(id))      \
+    {                                       \
+        return NRF_ERROR_INVALID_PARAM;     \
+    }                                       \
+} while (0)
+
+
+#define PEER_IDS_INITIALIZE()               \
+do                                          \
+{                                           \
+    if (!m_pds.peer_ids_initialized)        \
+    {                                       \
+        peer_ids_init();                    \
+    }                                       \
+} while (0)
+
+
+typedef struct
+{
+    bool                peer_ids_initialized;
+    pds_evt_handler_t   evt_handlers[MAX_REGISTRANTS];
+    uint8_t             n_registrants;
+} pds_t;
+
+static pds_t m_pds = {.n_registrants = 0};
+
+static void internal_state_reset(pds_t * p_pds)
+{
+    memset(p_pds, 0, sizeof(pds_t));
+}
+
+/**@brief Function for dispatching outbound events to all registered event handlers.
+ *
+ * @param[in]  p_event  The event to dispatch.
+ */
+static void pds_evt_send(pds_evt_t * p_event)
+{
+    for (int i = 0; i < m_pds.n_registrants; i++)
+    {
+        m_pds.evt_handlers[i](p_event);
+    }
+}
+
+/**@brief Function to convert peer id to instance id
+ *
+ * @param[in] peer_id   Peer id to convert to instance id
+ *
+ * @return  Value as instance id
+ */
+static fds_instance_id_t convert_peer_id_to_instance_id(pm_peer_id_t peer_id)
+{
+    return (fds_instance_id_t)(peer_id + peer_id_to_instance_id);
+}
+
+/**@brief Function to convert peer data id to type id
+ *
+ * @param[in]   peer_data_id    Peer data id to convert to type_id
+ *
+ * @return Value as type id
+ */
+static fds_type_id_t convert_peer_data_id_to_type_id(pm_peer_data_id_t peer_data_id)
+{
+    return (fds_type_id_t)peer_data_id + (fds_type_id_t)peer_data_id_to_type_id;
+}
+
+
+/**@brief Function to convert peer data id to type id
+ *
+ * @param[in]   peer_data_id    Peer data id to convert to type_id
+ *
+ * @return Value as type id
+ */
+static pm_peer_id_t convert_instance_id_to_peer_id(fds_instance_id_t instance_id)
+{
+    return (pm_peer_id_t)(instance_id + instance_id_to_peer_id);
+}
+
+
+/**@brief Function to type id to peer data id
+ *
+ * @param[in]   type_id    Type id to convert to peer data id
+ *
+ * @return Value as peer data id
+ */
+static pm_peer_data_id_t convert_type_id_to_peer_data_id(fds_type_id_t type_id)
+{
+    return (pm_peer_data_id_t)(type_id + instance_id_to_peer_id);
+}
+
+
+static ret_code_t find_fds_item(pm_peer_id_t              peer_id,
+                                pm_peer_data_id_t         data_id,
+                                fds_record_desc_t * const p_desc)
+{
+    fds_find_token_t find_tok;
+
+    VERIFY_PEER_ID_IN_RANGE(peer_id);
+    VERIFY_PEER_DATA_ID_IN_RANGE(data_id);
+    // pp_record verified outside
+
+    fds_type_id_t       type_id     = convert_peer_data_id_to_type_id(data_id);
+    fds_instance_id_t   instance_id = convert_peer_id_to_instance_id(peer_id);
+
+    return fds_find(type_id, instance_id, p_desc, &find_tok);
+}
+
+
+static void peer_ids_init()
+{
+    fds_record_t            record;
+    fds_record_desc_t       record_desc;
+    fds_find_token_t        find_tok;
+    fds_type_id_t     const type_id = convert_peer_data_id_to_type_id(PM_PEER_DATA_ID_BONDING);
+    pm_peer_id_t            peer_id;
+
+    if (!m_pds.peer_ids_initialized)
+    {
+        while(fds_find_by_type(type_id, &record_desc, &find_tok) == NRF_SUCCESS)
+        {
+            fds_open(&record_desc, &record);
+            fds_close(&record_desc);
+            peer_id = convert_instance_id_to_peer_id(record.header.ic.instance);
+            peer_id_allocate(peer_id);
+        }
+
+        m_pds.peer_ids_initialized = true;
+    }
+}
+
+//uint32_t size_pad_to_mult_of_four(uint32_t unpadded_size)
+//{
+//    return (unpadded_size + 3) & 3;
+//}
+
+static void fds_evt_handler(ret_code_t          result,
+                            fds_cmd_id_t        cmd,
+                            fds_record_id_t     record_id,
+                            fds_record_key_t    record_key
+                            /*fds_record_t  const * const p_record*/)
+{
+    pds_evt_t evt;
+    switch(cmd)
+    {
+        case FDS_CMD_INIT:
+
+            break;
+
+        case FDS_CMD_UPDATE:
+        case FDS_CMD_WRITE:
+            evt.peer_id = convert_instance_id_to_peer_id(record_key.instance);
+            evt.evt_id = (result == NRF_SUCCESS) ? PDS_EVT_STORED : PDS_EVT_ERROR_STORE;
+            evt.data_id = convert_type_id_to_peer_data_id(record_key.type);
+            evt.store_token = record_id;
+            pds_evt_send(&evt);
+            break;
+
+        case FDS_CMD_CLEAR:
+            evt.peer_id = convert_instance_id_to_peer_id(record_key.instance);
+            evt.evt_id = (result == NRF_SUCCESS) ? PDS_EVT_CLEARED : PDS_EVT_ERROR_CLEAR;
+            evt.data_id = convert_type_id_to_peer_data_id(record_key.type);
+            evt.store_token = record_id;
+            pds_evt_send(&evt);
+            break;
+
+        case FDS_CMD_CLEAR_INST:
+            {
+                if ((record_key.type     == FDS_TYPE_ID_INVALID) &&
+                    (record_key.instance != FDS_TYPE_ID_INVALID))
+                {
+                    pm_peer_id_t peer_id = convert_instance_id_to_peer_id(record_key.instance);
+
+                    evt.peer_id = peer_id;
+                    evt.data_id = PM_PEER_DATA_ID_INVALID;
+                    if (result == NRF_SUCCESS)
+                    {
+                        evt.evt_id = PDS_EVT_PEER_ID_CLEAR;
+                        peer_id_free(peer_id);
+                    }
+                    else
+                    {
+                        evt.evt_id = PDS_EVT_ERROR_PEER_ID_CLEAR;
+                    }
+                }
+                else
+                {
+                    // TODO: Not supported yet (clear many without clearing peer_id)
+                }
+
+                pds_evt_send(&evt);
+            }
+            break;
+
+        case FDS_CMD_GC:
+            evt.peer_id = convert_instance_id_to_peer_id(record_key.instance);
+            evt.evt_id = PDS_EVT_COMPRESSED;
+            evt.data_id = convert_type_id_to_peer_data_id(record_key.type);
+            evt.store_token = record_id;
+            pds_evt_send(&evt);
+            break;
+
+        default:
+
+            break;
+    }
+}
+
+
+ret_code_t pds_register(pds_evt_handler_t evt_handler)
+{
+    if (m_pds.n_registrants >= MAX_REGISTRANTS)
+    {
+        return NRF_ERROR_NO_MEM;
+    }
+
+    VERIFY_PARAM_NOT_NULL(evt_handler);
+
+    if (!MODULE_INITIALIZED)
+    {
+        ret_code_t retval;
+        internal_state_reset(&m_pds);
+        peer_id_init();
+
+        fds_cb_t cb = fds_evt_handler;
+        retval = fds_register(cb);
+        if(retval != NRF_SUCCESS)
+        {
+            return retval;
+        }
+
+        retval = fds_init();
+        if(retval != NRF_SUCCESS)
+        {
+            return retval;
+        }
+    }
+
+    m_pds.evt_handlers[m_pds.n_registrants] = evt_handler;
+    m_pds.n_registrants += 1;
+
+    return NRF_SUCCESS;
+
+}
+
+
+ret_code_t pds_peer_data_read_ptr_get(pm_peer_id_t            peer_id,
+                                      pm_peer_data_id_t       data_id,
+                                      pm_peer_data_flash_t  * p_data,
+                                      pm_store_token_t      * p_token)
+{
+    ret_code_t retval;
+
+    fds_record_t      record;
+    fds_record_desc_t record_desc;
+
+    VERIFY_MODULE_INITIALIZED();
+    VERIFY_PEER_ID_IN_RANGE(peer_id);
+    VERIFY_PEER_DATA_ID_IN_RANGE(data_id);
+
+    retval = find_fds_item(peer_id, data_id, &record_desc);
+    if (retval != NRF_SUCCESS)
+    {
+        return retval;
+    }
+
+    // Shouldn't fail, unless record is cleared.
+    fds_open(&record_desc, &record);
+    // No need to keep it open, since we are not reading.
+    fds_close(&record_desc);
+
+    //NRF_LOG_PRINTF("Found item with peer_id: %d, data_id: %d, Address: %p\r\n", record.p_data);
+
+    if (p_data != NULL)
+    {
+        p_data->data_type    = data_id;
+        p_data->length_words = record.header.tl.length_words;
+
+        p_data->data.p_application_data = (uint8_t const*)record.p_data;
+    }
+
+    if (p_token != NULL)
+    {
+        *p_token = (uint32_t)record.header.id;
+    }
+
+    return retval;
+}
+
+
+ret_code_t pds_peer_data_lock(pm_store_token_t store_token)
+{
+    VERIFY_MODULE_INITIALIZED();
+    VERIFY_PARAM_NOT_ZERO(store_token);
+
+    // TODO: Not implemented yet in fds
+
+    return NRF_SUCCESS;
+}
+
+
+ret_code_t pds_peer_data_verify(pm_store_token_t store_token)
+{
+    VERIFY_MODULE_INITIALIZED();
+    VERIFY_PARAM_NOT_ZERO(store_token);
+
+    // TODO: Not implemented yet in fds
+
+    return NRF_SUCCESS;
+}
+
+
+ret_code_t pds_peer_data_read(pm_peer_id_t          peer_id,
+                              pm_peer_data_id_t     data_id,
+                              pm_peer_data_t      * p_data,
+                              fds_length_t        * p_len_words)
+{
+    VERIFY_PEER_ID_IN_RANGE(peer_id);
+    VERIFY_PEER_DATA_ID_IN_RANGE(data_id);
+    VERIFY_PARAM_NOT_NULL(p_len_words);
+    VERIFY_PARAM_NOT_NULL(p_data);
+
+    ret_code_t err_code;
+    pm_peer_data_flash_t peer_data_flash;
+
+    err_code = pds_peer_data_read_ptr_get(peer_id, data_id, &peer_data_flash, NULL);
+
+    if (err_code != NRF_SUCCESS)
+    {
+        return err_code;
+    }
+
+    if ((*p_len_words) == 0)
+    {
+        (*p_len_words) = peer_data_flash.length_words;
+        return NRF_SUCCESS;
+    }
+    else if ((*p_len_words) < peer_data_flash.length_words)
+    {
+        return NRF_ERROR_NO_MEM;
+    }
+
+    VERIFY_PARAM_NOT_NULL(p_data->data.p_application_data);
+
+    err_code = peer_data_deserialize(&peer_data_flash, p_data);
+
+    return err_code;
+}
+
+
+ret_code_t pds_peer_data_write_prepare(pm_peer_data_const_t const * p_peer_data,
+                                       pm_prepare_token_t         * p_prepare_token)
+{
+    ret_code_t retval;
+
+    VERIFY_MODULE_INITIALIZED();
+    VERIFY_PARAM_NOT_NULL(p_peer_data);
+    VERIFY_PARAM_NOT_NULL(p_prepare_token);
+    VERIFY_PEER_DATA_ID_IN_RANGE(p_peer_data->data_type);
+
+    retval = fds_reserve((fds_write_token_t*)p_prepare_token, p_peer_data->length_words);
+    return retval;
+}
+
+
+ret_code_t pds_peer_data_write_prepare_cancel(pm_prepare_token_t prepare_token)
+{
+    ret_code_t retval;
+
+    VERIFY_MODULE_INITIALIZED();
+    VERIFY_PARAM_NOT_ZERO(prepare_token);
+
+    retval = fds_reserve_cancel((fds_write_token_t*)&prepare_token);
+    return retval;
+}
+
+
+ret_code_t pds_peer_data_write_prepared(pm_peer_id_t                    peer_id,
+                                        pm_peer_data_const_t    const * p_peer_data,
+                                        pm_prepare_token_t              prepare_token,
+                                        pm_store_token_t              * p_store_token)
+{
+    ret_code_t         retval;
+    fds_record_desc_t  record_desc;
+    fds_record_key_t   record_key;
+    fds_record_chunk_t chunks[2];
+    uint16_t           n_chunks;
+
+    VERIFY_MODULE_INITIALIZED();
+    //VERIFY_PARAM_NOT_ZERO(prepare_token);
+    VERIFY_PARAM_NOT_NULL(p_peer_data);
+    VERIFY_PEER_ID_IN_RANGE(peer_id);
+    VERIFY_PEER_DATA_ID_IN_RANGE(p_peer_data->data_type);
+
+    // Fill in the keys.
+    record_key.type     = convert_peer_data_id_to_type_id(p_peer_data->data_type);
+    record_key.instance = convert_peer_id_to_instance_id(peer_id);
+
+    // Create chunks.
+    peer_data_parts_get(p_peer_data, chunks, &n_chunks);
+
+    retval = fds_write_reserved((fds_write_token_t*)&prepare_token, &record_desc,
+                                record_key, n_chunks, chunks);
+
+    if ((retval == NRF_SUCCESS) && (p_store_token != NULL))
+    {
+        fds_record_id_from_desc(&record_desc, (fds_record_id_t*)p_store_token);
+    }
+
+    return retval;
+}
+
+
+ret_code_t pds_peer_data_write(pm_peer_id_t                 peer_id,
+                               pm_peer_data_const_t const * p_peer_data,
+                               pm_store_token_t           * p_store_token)
+{
+    ret_code_t          retval;
+    fds_record_desc_t   record_desc;
+    fds_record_key_t    record_key;
+    fds_record_chunk_t  chunks[2];
+    uint16_t            n_chunks;
+
+    VERIFY_MODULE_INITIALIZED();
+    VERIFY_PEER_ID_IN_RANGE(peer_id);
+    VERIFY_PEER_DATA_ID_IN_RANGE(p_peer_data->data_type);
+
+    // Fill in the keys.
+    record_key.type     = convert_peer_data_id_to_type_id(p_peer_data->data_type);
+    record_key.instance = convert_peer_id_to_instance_id(peer_id);
+
+    // Create chunks
+    peer_data_parts_get(p_peer_data, chunks, &n_chunks);
+
+    // Request write
+    retval = fds_write(&record_desc, record_key, n_chunks, chunks);
+
+    if ((retval == NRF_SUCCESS) && (p_store_token != NULL))
+    {
+        fds_record_id_from_desc(&record_desc, (fds_record_id_t*)p_store_token);
+    }
+
+    return retval;
+}
+
+
+ret_code_t pds_peer_data_update(pm_peer_id_t                 peer_id,
+                                pm_peer_data_const_t const * p_peer_data,
+                                pm_store_token_t             old_token,
+                                pm_store_token_t           * p_store_token)
+{
+    ret_code_t         retval;
+    fds_record_desc_t  record_desc;
+    fds_record_key_t   record_key;
+    fds_record_chunk_t chunks[2];
+    uint16_t           n_chunks;
+
+    VERIFY_MODULE_INITIALIZED();
+    VERIFY_PEER_DATA_ID_IN_RANGE(p_peer_data->data_type);
+    VERIFY_PARAM_NOT_NULL(p_peer_data);
+
+    record_key.type     = convert_peer_data_id_to_type_id(p_peer_data->data_type);
+    record_key.instance = convert_peer_id_to_instance_id(peer_id);
+
+    // Create chunks
+    peer_data_parts_get(p_peer_data, chunks, &n_chunks);
+
+    fds_descriptor_from_rec_id(&record_desc, (fds_record_id_t)old_token);
+
+    retval = fds_update(&record_desc, record_key, n_chunks, chunks);
+
+    if ((retval == NRF_SUCCESS) && (p_store_token != NULL))
+    {
+        fds_record_id_from_desc(&record_desc, (fds_record_id_t*)p_store_token);
+    }
+
+    return retval;
+}
+
+ret_code_t pds_peer_data_clear(pm_peer_id_t peer_id, pm_peer_data_id_t data_id)
+{
+    ret_code_t        retval;
+    fds_type_id_t     type_id;
+    fds_instance_id_t instance_id;
+    fds_record_desc_t record_desc;
+    fds_find_token_t  find_tok;
+
+    VERIFY_MODULE_INITIALIZED();
+    VERIFY_PEER_ID_IN_RANGE(peer_id);
+    VERIFY_PEER_DATA_ID_IN_RANGE(data_id);
+
+    type_id     = convert_peer_data_id_to_type_id(data_id);
+    instance_id = convert_peer_id_to_instance_id(peer_id);
+
+    retval = fds_find(type_id, instance_id, &record_desc, &find_tok);
+    if(retval != NRF_SUCCESS)
+    {
+        return retval;
+    }
+
+    retval = fds_clear(&record_desc);
+    return retval;
+}
+
+
+pm_peer_id_t pds_peer_id_allocate(void)
+{
+    if (!MODULE_INITIALIZED)
+    {
+        return PM_PEER_ID_INVALID;
+    }
+    PEER_IDS_INITIALIZE();
+    return peer_id_allocate(PM_PEER_ID_INVALID);
+}
+
+
+ret_code_t pds_peer_id_free(pm_peer_id_t peer_id)
+{
+    ret_code_t retval;
+    fds_instance_id_t instance_id;
+
+    VERIFY_MODULE_INITIALIZED();
+    VERIFY_PEER_ID_IN_RANGE(peer_id);
+    PEER_IDS_INITIALIZE();
+
+    instance_id = convert_peer_id_to_instance_id(peer_id);
+
+    retval = fds_clear_by_instance(instance_id);
+    return retval;
+}
+
+
+bool pds_peer_id_is_allocated(pm_peer_id_t peer_id)
+{
+    if (!MODULE_INITIALIZED)
+    {
+        return false;
+    }
+    PEER_IDS_INITIALIZE();
+
+    return peer_id_is_allocated(peer_id);
+}
+
+
+pm_peer_id_t pds_next_peer_id_get(pm_peer_id_t prev_peer_id)
+{
+    if (!MODULE_INITIALIZED)
+    {
+        return PM_PEER_ID_INVALID;
+    }
+    PEER_IDS_INITIALIZE();
+
+    return peer_id_next_id_get(prev_peer_id);
+}
+
+
+uint32_t pds_n_peers(void)
+{
+    if (!MODULE_INITIALIZED)
+    {
+        return 0;
+    }
+    PEER_IDS_INITIALIZE();
+    return peer_id_n_ids();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/source/nordic_sdk/components/ble/peer_manager/peer_data_storage.h	Thu Apr 07 17:38:12 2016 +0100
@@ -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__ */
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/source/nordic_sdk/components/ble/peer_manager/peer_database.c	Thu Apr 07 17:38:12 2016 +0100
@@ -0,0 +1,767 @@
+/*
+ * Copyright (c) Nordic Semiconductor ASA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright notice, this
+ *   list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright notice, this
+ *   list of conditions and the following disclaimer in the documentation and/or
+ *   other materials provided with the distribution.
+ *
+ *   3. Neither the name of Nordic Semiconductor ASA nor the names of other
+ *   contributors to this software may be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+
+#include "peer_database.h"
+
+#include <string.h>
+#include "peer_manager_types.h"
+#include "peer_data_storage.h"
+#include "pm_buffer.h"
+
+
+#define MAX_REGISTRANTS    6                         /**< The number of user that can register with the module. */
+
+#define MODULE_INITIALIZED (m_pdb.n_registrants > 0) /**< Expression which is true when the module is initialized. */
+
+#define N_WRITE_BUFFERS        8                     /**< The number of write buffers available. */
+#define N_WRITE_BUFFER_RECORDS (N_WRITE_BUFFERS)     /**< The number of write buffer records. */
+
+/**@brief Macro for verifying that the module is initialized. It will cause the function to return
+ *        @ref NRF_ERROR_INVALID_STATE if not.
+ */
+#define VERIFY_MODULE_INITIALIZED()     \
+do                                      \
+{                                       \
+    if (!MODULE_INITIALIZED)            \
+    {                                   \
+        return NRF_ERROR_INVALID_STATE; \
+    }                                   \
+} while(0)
+
+
+/**@brief Macro for verifying that the module is initialized. It will cause the function to return
+ *        if not.
+ */
+#define VERIFY_MODULE_INITIALIZED_VOID()\
+do                                      \
+{                                       \
+    if (!MODULE_INITIALIZED)            \
+    {                                   \
+        return;                         \
+    }                                   \
+} while(0)
+
+
+/**@brief Macro for verifying that the module is initialized. It will cause the function to return
+ *        if not.
+ *
+ * @param[in] param  The variable to check if is NULL.
+ */
+#define VERIFY_PARAM_NOT_NULL(param)    \
+do                                      \
+{                                       \
+    if (param == NULL)                  \
+    {                                   \
+        return NRF_ERROR_NULL;          \
+    }                                   \
+} while(0)
+
+
+typedef struct
+{
+    pm_peer_id_t        peer_id;
+    pm_peer_data_id_t   data_id;
+    uint8_t             buffer_block_id;
+    uint8_t             store_busy       : 1;
+    uint8_t             store_flash_full : 1;
+    uint8_t             store_requested  : 1;
+    uint32_t            n_bufs;
+    pm_prepare_token_t  prepare_token;
+    pm_store_token_t    store_token;
+} pdb_buffer_record_t;
+
+typedef struct
+{
+    pdb_evt_handler_t   evt_handlers[MAX_REGISTRANTS];
+    uint8_t             n_registrants;
+    pm_buffer_t         write_buffer;
+    pdb_buffer_record_t write_buffer_records[N_WRITE_BUFFER_RECORDS];
+    uint32_t            n_writes;
+} pdb_t;
+
+static pdb_t m_pdb = {.n_registrants = 0};
+
+
+/**@brief Function for invalidating a record of a write buffer allocation.
+ *
+ * @param[in]  p_record  The record to invalidate.
+ */
+static void write_buffer_record_invalidate(pdb_buffer_record_t * p_record)
+{
+    p_record->peer_id          = PM_PEER_ID_INVALID;
+    p_record->data_id          = PM_PEER_DATA_ID_INVALID;
+    p_record->buffer_block_id  = BUFFER_INVALID_ID;
+    p_record->store_busy       = false;
+    p_record->store_flash_full = false;
+    p_record->store_requested  = false;
+    p_record->n_bufs           = 0;
+    p_record->prepare_token    = PDS_PREPARE_TOKEN_INVALID;
+    p_record->store_token      = PDS_STORE_TOKEN_INVALID;
+}
+
+
+/**@brief Function for finding a record of a write buffer allocation.
+ *
+ * @param[in]  peer_id  The peer ID in the record.
+ * @param[in]  data_id  The data ID in the record.
+ *
+ * @return  A pointer to the matching record, or NULL if none was found.
+ */
+static pdb_buffer_record_t * write_buffer_record_find(pm_peer_id_t      peer_id,
+                                                      pm_peer_data_id_t data_id)
+{
+    for (int i = 0; i < N_WRITE_BUFFER_RECORDS; i++)
+    {
+        if   ((m_pdb.write_buffer_records[i].peer_id == peer_id)
+           && (m_pdb.write_buffer_records[i].data_id == data_id))
+        {
+            return &m_pdb.write_buffer_records[i];
+        }
+    }
+    return NULL;
+}
+
+
+/**@brief Function for finding an available record for write buffer allocation.
+ *
+ * @return  A pointer to the available record, or NULL if none was found.
+ */
+static pdb_buffer_record_t * write_buffer_record_find_unused(void)
+{
+    return write_buffer_record_find(PM_PEER_ID_INVALID, PM_PEER_DATA_ID_INVALID);
+}
+
+
+/**@brief Function for gracefully deactivating a write buffer record.
+ *
+ * @details This function will first release any buffers, then invalidate the record.
+ *
+ * @param[inout] p_write_buffer_record  The record to release.
+ *
+ * @return  A pointer to the matching record, or NULL if none was found.
+ */
+static void write_buffer_record_release(pdb_buffer_record_t * p_write_buffer_record)
+{
+    for (int i = 0; i < p_write_buffer_record->n_bufs; i++)
+    {
+        pm_buffer_release(&m_pdb.write_buffer, p_write_buffer_record->buffer_block_id + i);
+    }
+
+    write_buffer_record_invalidate(p_write_buffer_record);
+}
+
+
+static void write_buffer_record_get(pdb_buffer_record_t ** pp_write_buffer_record, pm_peer_id_t peer_id, pm_peer_data_id_t data_id)
+{
+    if (pp_write_buffer_record == NULL)
+    {
+        return;
+    }
+    *pp_write_buffer_record = write_buffer_record_find_unused();
+    if (*pp_write_buffer_record == NULL)
+    {
+        // This also means the buffer is full.
+        return;
+    }
+    (*pp_write_buffer_record)->peer_id = peer_id;
+    (*pp_write_buffer_record)->data_id = data_id;
+}
+
+
+/**@brief Function for dispatching outbound events to all registered event handlers.
+ *
+ * @param[in]  p_event  The event to dispatch.
+ */
+static void pdb_evt_send(pdb_evt_t * p_event)
+{
+    for (int i = 0; i < m_pdb.n_registrants; i++)
+    {
+        m_pdb.evt_handlers[i](p_event);
+    }
+}
+
+
+/**@brief Function for resetting the internal state of the Peer Database module.
+ *
+ * @param[out] p_event  The event to dispatch.
+ */
+static void internal_state_reset(pdb_t * pdb)
+{
+    memset(pdb, 0, sizeof(pdb_t));
+    for (int i = 0; i < N_WRITE_BUFFER_RECORDS; i++)
+    {
+        write_buffer_record_invalidate(&pdb->write_buffer_records[i]);
+    }
+}
+
+
+/**@brief Function for handling events from the Peer Data Storage module.
+ *
+ * @param[in]  p_event  The event to handle.
+ */
+static void pds_evt_handler(pds_evt_t const * p_event)
+{
+    ret_code_t            err_code;
+    pdb_buffer_record_t * p_write_buffer_record;
+    bool                  retry_flash_full = false;
+    pdb_evt_t             event =
+    {
+        .peer_id = p_event->peer_id,
+        .data_id = p_event->data_id,
+    };
+
+    p_write_buffer_record = write_buffer_record_find(p_event->peer_id, p_event->data_id);
+
+    switch (p_event->evt_id)
+    {
+        case PDS_EVT_STORED:
+            if (   (p_write_buffer_record != NULL)
+                //&& (p_write_buffer_record->store_token == p_event->store_token)
+                && (p_write_buffer_record->store_requested))
+            {
+                write_buffer_record_release(p_write_buffer_record);
+                event.evt_id = PDB_EVT_WRITE_BUF_STORED;
+                pdb_evt_send(&event);
+            }
+            else
+            {
+                event.evt_id = PDB_EVT_RAW_STORED;
+                pdb_evt_send(&event);
+            }
+            break;
+        case PDS_EVT_ERROR_STORE:
+            if (   (p_write_buffer_record != NULL)
+                && (p_write_buffer_record->store_token == p_event->store_token)
+                && (p_write_buffer_record->store_requested))
+            {
+                // Retry if internal buffer.
+                m_pdb.n_writes++;
+                p_write_buffer_record->store_requested = false;
+                p_write_buffer_record->store_busy      = true;
+            }
+            else
+            {
+                event.evt_id = PDB_EVT_RAW_STORE_FAILED;
+                pdb_evt_send(&event);
+            }
+            break;
+        case PDS_EVT_CLEARED:
+            event.evt_id = PDB_EVT_CLEARED;
+            pdb_evt_send(&event);
+            break;
+        case PDS_EVT_ERROR_CLEAR:
+            event.evt_id = PDB_EVT_CLEAR_FAILED;
+            pdb_evt_send(&event);
+            break;
+        case PDS_EVT_COMPRESSED:
+            retry_flash_full = true;
+            event.evt_id = PDB_EVT_COMPRESSED;
+            pdb_evt_send(&event);
+            break;
+        default:
+            break;
+    }
+
+    if (m_pdb.n_writes > 0)
+    {
+        for (int i = 0; i < N_WRITE_BUFFER_RECORDS; i++)
+        {
+            if  ((m_pdb.write_buffer_records[i].store_busy)
+              || (m_pdb.write_buffer_records[i].store_flash_full && retry_flash_full))
+            {
+                err_code = pdb_write_buf_store(m_pdb.write_buffer_records[i].peer_id,
+                                               m_pdb.write_buffer_records[i].data_id);
+                if (err_code != NRF_SUCCESS)
+                {
+                    event.peer_id = m_pdb.write_buffer_records[i].peer_id;
+                    event.data_id = m_pdb.write_buffer_records[i].data_id;
+                    if (err_code == NRF_ERROR_NO_MEM)
+                    {
+                        event.evt_id = PDB_EVT_ERROR_NO_MEM;
+                    }
+                    else
+                    {
+                        event.evt_id = PDB_EVT_ERROR_UNEXPECTED;
+                    }
+
+                    pdb_evt_send(&event);
+                    break;
+                }
+            }
+        }
+    }
+}
+
+
+ret_code_t pdb_register(pdb_evt_handler_t evt_handler)
+{
+    if (m_pdb.n_registrants >= MAX_REGISTRANTS)
+    {
+        return NRF_ERROR_NO_MEM;
+    }
+
+    VERIFY_PARAM_NOT_NULL(evt_handler);
+
+    if (!MODULE_INITIALIZED)
+    {
+        ret_code_t err_code;
+
+        internal_state_reset(&m_pdb);
+        err_code = pds_register(pds_evt_handler);
+        if (err_code != NRF_SUCCESS)
+        {
+            return err_code;
+        }
+        PM_BUFFER_INIT(&m_pdb.write_buffer, N_WRITE_BUFFERS, PDB_WRITE_BUF_SIZE, err_code);
+        if (err_code != NRF_SUCCESS)
+        {
+            return err_code;
+        }
+    }
+
+    m_pdb.evt_handlers[m_pdb.n_registrants] = evt_handler;
+    m_pdb.n_registrants += 1;
+
+    return NRF_SUCCESS;
+}
+
+
+pm_peer_id_t pdb_peer_allocate(void)
+{
+    if (!MODULE_INITIALIZED)
+    {
+        return PM_PEER_ID_INVALID;
+    }
+
+    return pds_peer_id_allocate();
+}
+
+
+ret_code_t pdb_peer_free(pm_peer_id_t peer_id)
+{
+    VERIFY_MODULE_INITIALIZED();
+
+    return pds_peer_id_free(peer_id);
+}
+
+
+ret_code_t pdb_read_buf_get(pm_peer_id_t           peer_id,
+                            pm_peer_data_id_t      data_id,
+                            pm_peer_data_flash_t * p_peer_data,
+                            pm_store_token_t     * p_token)
+{
+    VERIFY_MODULE_INITIALIZED();
+
+    return pds_peer_data_read_ptr_get(peer_id, data_id, p_peer_data, p_token);
+}
+
+
+static void peer_data_point_to_buffer(pm_peer_data_t * p_peer_data, pm_peer_data_id_t data_id, uint8_t * p_buffer_memory, uint16_t n_bufs)
+{
+    uint16_t n_bytes = n_bufs * PDB_WRITE_BUF_SIZE;
+    p_peer_data->data_type    = data_id;
+
+    switch(p_peer_data->data_type)
+    {
+        case PM_PEER_DATA_ID_BONDING:
+            p_peer_data->data.p_bonding_data = (pm_peer_data_bonding_t *)p_buffer_memory;
+            p_peer_data->length_words = PM_BONDING_DATA_N_WORDS();
+            break;
+        case PM_PEER_DATA_ID_SERVICE_CHANGED_PENDING:
+            p_peer_data->data.p_service_changed_pending = (bool *)p_buffer_memory;
+            p_peer_data->length_words = PM_SC_STATE_N_WORDS();
+            break;
+        case PM_PEER_DATA_ID_GATT_LOCAL:
+            p_peer_data->data.p_local_gatt_db = (pm_peer_data_local_gatt_db_t *)p_buffer_memory;
+            p_peer_data->length_words = PM_LOCAL_DB_N_WORDS(n_bytes);
+            break;
+        case PM_PEER_DATA_ID_GATT_REMOTE:
+            p_peer_data->data.p_remote_gatt_db = (pm_peer_data_remote_gatt_db_t *)p_buffer_memory;
+            p_peer_data->length_words = PM_REMOTE_DB_N_WORDS(n_bytes / sizeof(ble_gatt_db_srv_t));
+            break;
+        case PM_PEER_DATA_ID_APPLICATION:
+            p_peer_data->data.p_application_data = p_buffer_memory;
+            p_peer_data->length_words = PM_N_WORDS(n_bytes);
+            break;
+        default:
+            p_peer_data->length_words = 0;
+            break;
+    }
+}
+
+
+static void peer_data_const_point_to_buffer(pm_peer_data_const_t * p_peer_data, pm_peer_data_id_t data_id,  uint8_t * p_buffer_memory, uint32_t n_bufs)
+{
+    peer_data_point_to_buffer((pm_peer_data_t*)p_peer_data, data_id, p_buffer_memory, n_bufs);
+}
+
+
+ret_code_t pdb_write_buf_get(pm_peer_id_t       peer_id,
+                             pm_peer_data_id_t  data_id,
+                             uint32_t           n_bufs,
+                             pm_peer_data_t   * p_peer_data)
+{
+    VERIFY_MODULE_INITIALIZED();
+    VERIFY_PARAM_NOT_NULL(p_peer_data);
+    if (   !PM_PEER_DATA_ID_IS_VALID(data_id)
+        || (n_bufs == 0)
+        || (n_bufs > N_WRITE_BUFFERS)
+        || !pds_peer_id_is_allocated(peer_id))
+    {
+        return NRF_ERROR_INVALID_PARAM;
+    }
+
+    pdb_buffer_record_t * write_buffer_record;
+    uint8_t             * p_buffer_memory;
+
+    write_buffer_record = write_buffer_record_find(peer_id, data_id);
+
+    if ((write_buffer_record != NULL) && (write_buffer_record->n_bufs < n_bufs))
+    {
+        // @TODO: Copy?
+        // Existing buffer is too small.
+        for (uint8_t i = 0; i < write_buffer_record->n_bufs; i++)
+        {
+            pm_buffer_release(&m_pdb.write_buffer, write_buffer_record->buffer_block_id + i);
+        }
+        write_buffer_record_invalidate(write_buffer_record);
+        write_buffer_record = NULL;
+    }
+    else if ((write_buffer_record != NULL) && write_buffer_record->n_bufs > n_bufs)
+    {
+        // Release excess blocks.
+        for (uint8_t i = n_bufs; i < write_buffer_record->n_bufs; i++)
+        {
+            pm_buffer_release(&m_pdb.write_buffer, write_buffer_record->buffer_block_id + i);
+        }
+    }
+
+    if (write_buffer_record == NULL)
+    {
+        write_buffer_record_get(&write_buffer_record, peer_id, data_id);
+        if (write_buffer_record == NULL)
+        {
+            return NRF_ERROR_BUSY;
+        }
+    }
+
+    if (write_buffer_record->buffer_block_id == BUFFER_INVALID_ID)
+    {
+        write_buffer_record->buffer_block_id = pm_buffer_block_acquire(&m_pdb.write_buffer, n_bufs);
+
+        if (write_buffer_record->buffer_block_id == BUFFER_INVALID_ID)
+        {
+            write_buffer_record_invalidate(write_buffer_record);
+            return NRF_ERROR_BUSY;
+        }
+    }
+
+    write_buffer_record->n_bufs = n_bufs;
+
+    p_buffer_memory = pm_buffer_ptr_get(&m_pdb.write_buffer, write_buffer_record->buffer_block_id);
+
+    if (p_buffer_memory == NULL)
+    {
+        return NRF_ERROR_INTERNAL;
+    }
+
+    peer_data_point_to_buffer(p_peer_data, data_id, p_buffer_memory, n_bufs);
+    switch(data_id)
+    {
+        case PM_PEER_DATA_ID_BONDING:
+            /* No action needed. */
+            break;
+        case PM_PEER_DATA_ID_SERVICE_CHANGED_PENDING:
+            /* No action needed. */
+            break;
+        case PM_PEER_DATA_ID_GATT_LOCAL:
+        {
+            uint32_t size_offset = sizeof(pm_peer_data_local_gatt_db_t);
+            p_peer_data->data.p_local_gatt_db->p_data = &p_buffer_memory[size_offset];
+            p_peer_data->data.p_local_gatt_db->len    = (PDB_WRITE_BUF_SIZE*n_bufs)-size_offset;
+        }
+            break;
+        case PM_PEER_DATA_ID_GATT_REMOTE:
+        {
+            uint32_t size_offset = sizeof(pm_peer_data_remote_gatt_db_t);
+            p_peer_data->data.p_remote_gatt_db->p_data = (ble_gatt_db_srv_t*)&(p_buffer_memory[size_offset]);
+            p_peer_data->data.p_remote_gatt_db->service_count
+                            = ((PDB_WRITE_BUF_SIZE*n_bufs)-size_offset)/sizeof(ble_gatt_db_srv_t);
+        }
+            break;
+        case PM_PEER_DATA_ID_APPLICATION:
+        {
+            p_peer_data->data.p_application_data = p_buffer_memory;
+        }
+            break;
+        default:
+            // Invalid data_id. This should have been picked up earlier.
+            return NRF_ERROR_INTERNAL;
+    }
+
+    return NRF_SUCCESS;
+}
+
+
+ret_code_t pdb_write_buf_release(pm_peer_id_t peer_id, pm_peer_data_id_t data_id)
+{
+    VERIFY_MODULE_INITIALIZED();
+
+    ret_code_t            err_code = NRF_SUCCESS;
+    pdb_buffer_record_t * p_write_buffer_record;
+    p_write_buffer_record = write_buffer_record_find(peer_id, data_id);
+
+    if (p_write_buffer_record == NULL)
+    {
+        return NRF_ERROR_NOT_FOUND;
+    }
+
+    if (p_write_buffer_record->prepare_token != PDS_PREPARE_TOKEN_INVALID)
+    {
+        err_code = pds_peer_data_write_prepare_cancel(p_write_buffer_record->prepare_token);
+        if (err_code != NRF_SUCCESS)
+        {
+            err_code = NRF_ERROR_INTERNAL;
+        }
+    }
+
+    write_buffer_record_release(p_write_buffer_record);
+
+    return err_code;
+}
+
+
+ret_code_t pdb_write_buf_store_prepare(pm_peer_id_t peer_id, pm_peer_data_id_t data_id)
+{
+    VERIFY_MODULE_INITIALIZED();
+
+    ret_code_t            err_code = NRF_SUCCESS;
+    pdb_buffer_record_t * p_write_buffer_record;
+    p_write_buffer_record = write_buffer_record_find(peer_id, data_id);
+
+    if (p_write_buffer_record == NULL)
+    {
+        return NRF_ERROR_NOT_FOUND;
+    }
+
+    if (p_write_buffer_record->prepare_token == PDS_PREPARE_TOKEN_INVALID)
+    {
+        uint8_t * p_buffer_memory = pm_buffer_ptr_get(&m_pdb.write_buffer, p_write_buffer_record->buffer_block_id);
+        pm_peer_data_const_t peer_data = {.data_type = data_id};
+
+        if (p_buffer_memory == NULL)
+        {
+            return NRF_ERROR_INTERNAL;
+        }
+
+        peer_data_const_point_to_buffer(&peer_data, data_id, p_buffer_memory, p_write_buffer_record->n_bufs);
+
+        err_code = pds_peer_data_write_prepare(&peer_data, &p_write_buffer_record->prepare_token);
+        if (err_code == NRF_ERROR_INVALID_LENGTH)
+        {
+            return NRF_ERROR_INTERNAL;
+        }
+    }
+
+    return err_code;
+}
+
+
+static ret_code_t write_or_update(pm_peer_id_t           peer_id,
+                                  pm_peer_data_id_t      data_id,
+                                  pm_peer_data_const_t * p_peer_data,
+                                  pm_store_token_t     * p_store_token,
+                                  pm_prepare_token_t     prepare_token)
+{
+    pm_peer_data_flash_t old_peer_data;
+    pm_store_token_t     old_store_token;
+    ret_code_t err_code = pds_peer_data_read_ptr_get(peer_id, data_id, &old_peer_data, &old_store_token);
+
+    if (err_code == NRF_SUCCESS)
+    {
+        pds_peer_data_write_prepare_cancel(prepare_token);
+        err_code = pds_peer_data_update(peer_id, p_peer_data, old_store_token, p_store_token);
+    }
+    else if (err_code == NRF_ERROR_NOT_FOUND)
+    {
+        if (prepare_token == PDS_PREPARE_TOKEN_INVALID)
+        {
+            err_code = pds_peer_data_write(peer_id, p_peer_data, p_store_token);
+        }
+        else
+        {
+            err_code = pds_peer_data_write_prepared(peer_id, p_peer_data, prepare_token, p_store_token);
+        }
+    }
+    return err_code;
+}
+
+
+ret_code_t pdb_write_buf_store(pm_peer_id_t      peer_id,
+                               pm_peer_data_id_t data_id)
+{
+    VERIFY_MODULE_INITIALIZED();
+
+    ret_code_t            err_code = NRF_SUCCESS;
+    pdb_buffer_record_t * p_write_buffer_record;
+    uint8_t             * p_buffer_memory;
+    pm_peer_data_const_t  peer_data = {.data_type = data_id};
+
+
+    p_write_buffer_record = write_buffer_record_find(peer_id, data_id);
+
+    if (p_write_buffer_record == NULL)
+    {
+        return NRF_ERROR_NOT_FOUND;
+    }
+
+    if (p_write_buffer_record->store_requested)
+    {
+        return NRF_SUCCESS;
+    }
+
+    p_buffer_memory = pm_buffer_ptr_get(&m_pdb.write_buffer, p_write_buffer_record->buffer_block_id);
+
+    if (p_buffer_memory == NULL)
+    {
+        return NRF_ERROR_INTERNAL;
+    }
+
+    peer_data_const_point_to_buffer(&peer_data, data_id, p_buffer_memory, p_write_buffer_record->n_bufs);
+
+    switch (data_id)
+    {
+        case PM_PEER_DATA_ID_BONDING:
+            peer_data.length_words = PM_BONDING_DATA_N_WORDS();
+            break;
+        case PM_PEER_DATA_ID_SERVICE_CHANGED_PENDING:
+            peer_data.length_words = PM_SC_STATE_N_WORDS();
+            break;
+        case PM_PEER_DATA_ID_GATT_LOCAL:
+            peer_data.length_words = PM_LOCAL_DB_N_WORDS(peer_data.data.p_local_gatt_db->len);
+            break;
+        case PM_PEER_DATA_ID_GATT_REMOTE:
+            peer_data.length_words = PM_REMOTE_DB_N_WORDS(peer_data.data.p_remote_gatt_db->service_count);
+            break;
+        case PM_PEER_DATA_ID_APPLICATION:
+            peer_data.length_words = PM_N_WORDS(p_write_buffer_record->n_bufs * PDB_WRITE_BUF_SIZE);
+            break;
+        default:
+            return NRF_ERROR_INVALID_PARAM;
+    }
+
+    err_code = write_or_update(peer_id, data_id, &peer_data, &p_write_buffer_record->store_token, p_write_buffer_record->prepare_token);
+
+    if (p_write_buffer_record->store_busy && p_write_buffer_record->store_flash_full)
+    {
+        m_pdb.n_writes--;
+    }
+
+    if (err_code == NRF_SUCCESS)
+    {
+        p_write_buffer_record->store_requested  = true;
+        p_write_buffer_record->store_busy       = false;
+        p_write_buffer_record->store_flash_full = false;
+    }
+    else
+    {
+        if (err_code == NRF_ERROR_BUSY)
+        {
+            m_pdb.n_writes++;
+            p_write_buffer_record->store_busy       = true;
+            p_write_buffer_record->store_flash_full = false;
+            err_code = NRF_SUCCESS;
+        }
+        else if (err_code == NRF_ERROR_NO_MEM)
+        {
+            m_pdb.n_writes++;
+            p_write_buffer_record->store_busy       = false;
+            p_write_buffer_record->store_flash_full = true;
+        }
+        else if ((err_code != NRF_ERROR_NO_MEM) && (err_code != NRF_ERROR_INVALID_PARAM))
+        {
+            err_code = NRF_ERROR_INTERNAL;
+        }
+    }
+
+    return err_code;
+}
+
+
+ret_code_t pdb_clear(pm_peer_id_t peer_id, pm_peer_data_id_t data_id)
+{
+    VERIFY_MODULE_INITIALIZED();
+
+    return pds_peer_data_clear(peer_id, data_id);
+}
+
+
+uint32_t pdb_n_peers(void)
+{
+    if (!MODULE_INITIALIZED)
+    {
+        return 0;
+    }
+
+    return pds_n_peers();
+}
+
+
+pm_peer_id_t pdb_next_peer_id_get(pm_peer_id_t prev_peer_id)
+{
+    if (!MODULE_INITIALIZED)
+    {
+        return PM_PEER_ID_INVALID;
+    }
+
+    return pds_next_peer_id_get(prev_peer_id);
+}
+
+
+ret_code_t pdb_raw_read(pm_peer_id_t      peer_id,
+                        pm_peer_data_id_t data_id,
+                        pm_peer_data_t  * p_peer_data)
+{
+    VERIFY_MODULE_INITIALIZED();
+    return pds_peer_data_read(peer_id, data_id, p_peer_data, &p_peer_data->length_words);
+}
+
+
+ret_code_t pdb_raw_store(pm_peer_id_t           peer_id,
+                         pm_peer_data_const_t * p_peer_data,
+                         pm_store_token_t     * p_store_token)
+{
+    VERIFY_MODULE_INITIALIZED();
+    
+    return write_or_update(peer_id, p_peer_data->data_type, p_peer_data, p_store_token, PDS_PREPARE_TOKEN_INVALID);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/source/nordic_sdk/components/ble/peer_manager/peer_id.c	Thu Apr 07 17:38:12 2016 +0100
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) Nordic Semiconductor ASA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright notice, this
+ *   list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright notice, this
+ *   list of conditions and the following disclaimer in the documentation and/or
+ *   other materials provided with the distribution.
+ *
+ *   3. Neither the name of Nordic Semiconductor ASA nor the names of other
+ *   contributors to this software may be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+
+#include "peer_id.h"
+
+#include <stdint.h>
+#include <string.h>
+#include "sdk_errors.h"
+#include "peer_manager_types.h"
+#include "pm_mutex.h"
+
+
+typedef struct
+{
+    uint8_t peer_ids[MUTEX_STORAGE_SIZE(PM_PEER_ID_N_AVAILABLE_IDS)]; /*< bitmap. */
+} pi_t;
+
+
+static pi_t m_pi = {.peer_ids = {0}};
+
+
+static void internal_state_reset(pi_t * p_pi)
+{
+    memset(p_pi, 0, sizeof(pi_t));
+}
+
+
+void peer_id_init(void)
+{
+    internal_state_reset(&m_pi);
+    pm_mutex_init(m_pi.peer_ids, PM_PEER_ID_N_AVAILABLE_IDS);
+}
+
+
+pm_peer_id_t peer_id_allocate(pm_peer_id_t peer_id)
+{
+    pm_peer_id_t allocated_peer_id = PM_PEER_ID_INVALID;
+    if (peer_id == PM_PEER_ID_INVALID)
+    {
+        allocated_peer_id = pm_mutex_lock_first_available(m_pi.peer_ids, PM_PEER_ID_N_AVAILABLE_IDS);
+        if (allocated_peer_id == PM_PEER_ID_N_AVAILABLE_IDS)
+        {
+            allocated_peer_id = PM_PEER_ID_INVALID;
+        }
+    }
+    else if (peer_id < PM_PEER_ID_N_AVAILABLE_IDS)
+    {
+        bool lock_success = pm_mutex_lock(m_pi.peer_ids, peer_id);
+        allocated_peer_id = lock_success ? peer_id : PM_PEER_ID_INVALID;
+    }
+    return allocated_peer_id;
+}
+
+
+void peer_id_free(pm_peer_id_t peer_id)
+{
+    if (peer_id < PM_PEER_ID_N_AVAILABLE_IDS)
+    {
+        pm_mutex_unlock(m_pi.peer_ids, peer_id);
+    }
+}
+
+
+bool peer_id_is_allocated(pm_peer_id_t peer_id)
+{
+    if (peer_id < PM_PEER_ID_N_AVAILABLE_IDS)
+    {
+        return pm_mutex_lock_status_get(m_pi.peer_ids, peer_id);
+    }
+    return false;
+}
+
+
+pm_peer_id_t peer_id_next_id_get(pm_peer_id_t prev_peer_id)
+{
+    pm_peer_id_t i = (prev_peer_id == PM_PEER_ID_INVALID) ? 0 : (prev_peer_id + 1);
+    for (; i < PM_PEER_ID_N_AVAILABLE_IDS; i++)
+    {
+        if (pm_mutex_lock_status_get(m_pi.peer_ids, i))
+        {
+            return i;
+        }
+    }
+
+    return PM_PEER_ID_INVALID;
+}
+
+
+uint32_t peer_id_n_ids(void)
+{
+    uint32_t n_ids = 0;
+
+    for (pm_peer_id_t i = 0; i < PM_PEER_ID_N_AVAILABLE_IDS; i++)
+    {
+        n_ids += pm_mutex_lock_status_get(m_pi.peer_ids, i);
+    }
+
+    return n_ids;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/source/nordic_sdk/components/ble/peer_manager/peer_id.h	Thu Apr 07 17:38:12 2016 +0100
@@ -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__ */
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/source/nordic_sdk/components/ble/peer_manager/pm_buffer.c	Thu Apr 07 17:38:12 2016 +0100
@@ -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);
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/source/nordic_sdk/components/ble/peer_manager/pm_buffer.h	Thu Apr 07 17:38:12 2016 +0100
@@ -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__
+
+/**
+ * @}
+ */
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/source/nordic_sdk/components/ble/peer_manager/pm_mutex.c	Thu Apr 07 17:38:12 2016 +0100
@@ -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;
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/source/nordic_sdk/components/ble/peer_manager/pm_mutex.h	Thu Apr 07 17:38:12 2016 +0100
@@ -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__
+
+/** @} */
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/source/nordic_sdk/components/libraries/experimental_section_vars/section_vars.h	Thu Apr 07 17:38:12 2016 +0100
@@ -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__
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/source/nordic_sdk/components/libraries/fds/fds.c	Thu Apr 07 17:38:12 2016 +0100
@@ -0,0 +1,2089 @@
+/*
+ * Copyright (c) Nordic Semiconductor ASA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright notice, this
+ *   list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright notice, this
+ *   list of conditions and the following disclaimer in the documentation and/or
+ *   other materials provided with the distribution.
+ *
+ *   3. Neither the name of Nordic Semiconductor ASA nor the names of other
+ *   contributors to this software may be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "fds.h"
+#include <stdint.h>
+#include <string.h>
+#include <stdbool.h>
+#include "fds_config.h"
+#include "fds_types_internal.h"
+#include "fstorage.h"
+#include "nrf_error.h"
+#include "app_util.h"
+
+
+/** Our fstorage configuration.
+ *  The other fields will be assigned automatically during compilation. */
+FS_SECTION_VARS_ADD(fs_config_t fs_config) = { .cb = fs_callback, .num_pages = FDS_MAX_PAGES };
+
+static uint32_t const fds_page_tag_swap[]   = {FDS_PAGE_TAG_WORD_0_SWAP, FDS_PAGE_TAG_WORD_1,
+                                               FDS_PAGE_TAG_WORD_2,      FDS_PAGE_TAG_WORD_3};
+
+static uint32_t const fds_page_tag_valid[]  = {FDS_PAGE_TAG_WORD_0_VALID, FDS_PAGE_TAG_WORD_1,
+                                               FDS_PAGE_TAG_WORD_2,       FDS_PAGE_TAG_WORD_3};
+
+static uint32_t const fds_page_tag_gc       = FDS_PAGE_TAG_WORD_3_GC;
+
+static fds_tl_t const m_fds_tl_invalid      = { .type = FDS_TYPE_ID_INVALID,
+                                                .length_words = 0xFFFF };
+
+/**@brief Internal status flags. */
+static uint8_t           volatile m_flags;       
+
+static uint8_t                    m_users;
+static fds_cb_t                   m_cb_table[FDS_MAX_USERS];
+
+/**@brief The last record ID. Setup page by page_scan() during pages_init(). */
+static fds_record_id_t            m_last_rec_id;
+
+/**@brief The internal queues. */
+static fds_cmd_queue_t            m_cmd_queue;   
+static fds_chunk_queue_t          m_chunk_queue;
+
+/**@brief Holds the state of pages. Setup by fds_init(). */
+static fds_page_t                 m_pages[FDS_MAX_PAGES];
+static bool                       m_swap_page_avail = false;
+
+static fds_gc_data_t              m_gc;
+static uint16_t                   m_gc_runs;
+
+static uint8_t          volatile  m_counter;
+
+
+static void app_notify(ret_code_t       result,
+                       fds_cmd_id_t     cmd,
+                       fds_record_id_t  record_id,
+                       fds_record_key_t record_key)
+{
+    for (uint8_t user = 0; user < FDS_MAX_USERS; user++)
+    {
+        if (m_cb_table[user] != NULL)
+        {
+            m_cb_table[user](result, cmd, record_id, record_key);
+        }
+    }
+}
+
+
+static void atomic_counter_inc()
+{
+    CRITICAL_SECTION_ENTER();
+    m_counter++;
+    CRITICAL_SECTION_EXIT();
+}
+
+
+static void atomic_counter_dec()
+{
+    CRITICAL_SECTION_ENTER();
+    m_counter--;
+    CRITICAL_SECTION_EXIT();
+}
+
+
+static bool atomic_counter_is_zero()
+{
+    bool ret;
+    CRITICAL_SECTION_ENTER();
+    ret = (m_counter == 0);
+    CRITICAL_SECTION_EXIT();
+    return ret;
+}
+
+
+static void flag_set(fds_flags_t flag)
+{
+    CRITICAL_SECTION_ENTER();
+    m_flags |= flag;
+    CRITICAL_SECTION_EXIT();
+}
+
+
+static void flag_clear(fds_flags_t flag)
+{
+    CRITICAL_SECTION_ENTER();
+    m_flags &= ~(flag);
+    CRITICAL_SECTION_EXIT();
+}
+
+
+static bool flag_is_set(fds_flags_t flag)
+{
+    bool ret;
+    CRITICAL_SECTION_ENTER();
+    ret = (m_flags & flag);
+    CRITICAL_SECTION_EXIT();
+    return ret;
+}
+
+
+/**@brief Function to check if a header has valid information. */
+static __INLINE bool header_is_valid(fds_header_t const * const p_header)
+{
+    return ((p_header->tl.type     != FDS_TYPE_ID_INVALID) &&
+            (p_header->ic.instance != FDS_INSTANCE_ID_INVALID));
+}
+
+
+static bool address_within_page_bounds(uint32_t const * const p_addr)
+{
+    return (p_addr >= fs_config.p_start_addr) &&
+           (p_addr <= fs_config.p_end_addr) &&
+           (is_word_aligned(p_addr));
+}
+
+
+/**@brief Internal function to identify the page type. */
+static fds_page_type_t page_identify(uint16_t page_number)
+{
+    uint32_t const * const p_page_addr = m_pages[page_number].start_addr;
+
+    uint32_t const word0 = *(p_page_addr);
+    uint32_t const word1 = *(p_page_addr + 1);
+    uint32_t const word2 = *(p_page_addr + 2);
+    uint32_t const word3 = *(p_page_addr + 3);
+
+    if (word1 != FDS_PAGE_TAG_WORD_1)
+    {
+        return FDS_PAGE_UNDEFINED;
+    }
+
+    if (word2 != FDS_PAGE_TAG_WORD_2)
+    {
+        return FDS_PAGE_UNDEFINED;
+    }
+
+    if (word3 == FDS_PAGE_TAG_WORD_3)
+    {
+        if (word0 == FDS_PAGE_TAG_WORD_0_SWAP)
+        {
+            return FDS_PAGE_SWAP;
+        }
+
+        if (word0 == FDS_PAGE_TAG_WORD_0_VALID)
+        {
+            return FDS_PAGE_VALID;
+        }
+    }
+    else if (word3 == FDS_PAGE_TAG_WORD_3_GC)
+    {
+        if (word0 == FDS_PAGE_TAG_WORD_0_SWAP || word0 == FDS_PAGE_TAG_WORD_0_VALID)
+        {
+            return FDS_PAGE_GC;
+        }
+    }
+
+    return FDS_PAGE_UNDEFINED;
+}
+
+
+static uint16_t page_by_addr(uint32_t const * const p_addr)
+{
+    if (p_addr == NULL)
+    {
+        return 0;
+    }
+
+    // Compute the BYTES offset from the beginning of the first page.
+    uint32_t const byte_offset = (uint32_t)p_addr - (uint32_t)m_pages[0].start_addr;
+
+// See nrf.h.
+#if defined (NRF51)
+    return byte_offset >> 10; // Divide by page size (1024).
+#elif defined (NRF52)
+    return byte_offset >> 12; // Divide by page size (4096).
+#else
+    #error "Device family must be defined. See nrf.h."
+#endif
+}
+
+
+// NOTE: depends on m_pages.write_offset to function.
+static bool page_has_space(uint16_t page, fds_length_t length_words)
+{
+    if (page >= FDS_MAX_PAGES)
+    {
+        return false;
+    }
+
+    CRITICAL_SECTION_ENTER();
+    length_words +=  m_pages[page].write_offset;
+    length_words +=  m_pages[page].words_reserved;
+    CRITICAL_SECTION_EXIT();
+
+    return (length_words < FS_PAGE_SIZE_WORDS);
+}
+
+
+/**@brief This function scans a page to determine how many words have
+ *        been written to it. This information is used to set the page
+ *        write offset during initialization (mount). Additionally, this
+ *        function will update the last known record ID as it proceeds.
+ */
+static void page_scan(uint16_t page, uint16_t volatile * words_written)
+{
+    uint32_t const * p_addr = (m_pages[page].start_addr + FDS_PAGE_TAG_SIZE);
+
+    *words_written = FDS_PAGE_TAG_SIZE;
+
+    // A corrupt TL might cause problems.
+    while ((p_addr < m_pages[page].start_addr + FS_PAGE_SIZE_WORDS) &&
+           (*p_addr != FDS_ERASED_WORD))
+    {
+        fds_header_t const * const p_header = (fds_header_t*)p_addr;
+
+        /** Note: DO NOT check for the validity of the header using
+         *  header_is_valid() here. If an header has an invalid type (0x0000)
+         *  or a missing instance (0xFFFF) then we WANT to skip it.
+         */
+
+         // Update the last known record id.
+         if (p_header->id > m_last_rec_id)
+         {
+            m_last_rec_id = p_header->id;
+         }
+
+         // Jump to the next record.
+         p_addr         += (FDS_HEADER_SIZE + p_header->tl.length_words);
+         *words_written += (FDS_HEADER_SIZE + p_header->tl.length_words);
+    }
+}
+
+
+static bool page_is_empty(uint16_t page)
+{
+    uint32_t const * const p_addr = m_pages[page].start_addr;
+
+    for (uint16_t i = 0; i < FS_PAGE_SIZE_WORDS; i++)
+    {
+        if (*(p_addr + i) != FDS_ERASED_WORD)
+        {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+
+static ret_code_t page_id_from_virtual_id(uint16_t vpage_id, uint16_t * p_page_id)
+{
+    for (uint16_t i = 0; i < FDS_MAX_PAGES; i++)
+    {
+        if (m_pages[i].vpage_id == vpage_id)
+        {
+            *p_page_id = i;
+            return NRF_SUCCESS;
+        }
+    }
+
+    return NRF_ERROR_NOT_FOUND;
+}
+
+
+static ret_code_t page_from_virtual_id(uint16_t vpage_id, fds_page_t ** p_page)
+{
+    for (uint16_t i = 0; i < FDS_MAX_PAGES; i++)
+    {
+        if (m_pages[i].vpage_id == vpage_id)
+        {
+            *p_page = &m_pages[i];
+            return NRF_SUCCESS;
+        }
+    }
+
+    return NRF_ERROR_NOT_FOUND;
+}
+
+
+static uint32_t record_id_new()
+{
+    return ++m_last_rec_id;
+}
+
+
+/**@brief Tags a page as swap, i.e., reserved for GC. */
+static ret_code_t page_tag_write_swap(uint16_t page)
+{
+    return fs_store(&fs_config,
+                    m_pages[page].start_addr,
+                    (uint32_t const *)&fds_page_tag_swap,
+                    FDS_PAGE_TAG_SIZE);
+}
+
+
+/**@brief Tags a page as valid, i.e, ready for storage. */
+static ret_code_t page_tag_write_valid(uint16_t page)
+{
+    return fs_store(&fs_config,
+                    m_pages[page].start_addr,
+                    (uint32_t const *)&fds_page_tag_valid,
+                    FDS_PAGE_TAG_SIZE);
+}
+
+
+/**@brief Tags a valid page as being garbage collected. */
+static ret_code_t page_tag_write_gc(uint16_t page)
+{
+    return fs_store(&fs_config,
+                    m_pages[page].start_addr + 3,
+                    (uint32_t const *)&fds_page_tag_gc,
+                    1 /*Words*/);
+}
+
+
+/**@brief Given a page and a record, finds the next valid record. */
+static ret_code_t scan_next_valid(uint16_t page, uint32_t const ** p_record)
+{
+    uint32_t const * p_next_rec = (*p_record);
+
+    if (p_next_rec == NULL)
+    {
+        // This if the first invocation on this page, start from the beginning.
+        p_next_rec = m_pages[page].start_addr + FDS_PAGE_TAG_SIZE;
+    }
+    else
+    {
+        // Jump to the next record.
+        p_next_rec += (FDS_HEADER_SIZE + ((fds_header_t*)(*p_record))->tl.length_words);
+    }
+
+    // Scan until we find a valid record or until the end of the page.
+
+    /** README: We might seek until the write_offset is reached, but it might not
+     *  known at this point. */
+    while ((p_next_rec < (m_pages[page].start_addr + FS_PAGE_SIZE_WORDS)) &&
+           (*p_next_rec != FDS_ERASED_WORD)) // Did we jump to an erased word?
+    {
+        fds_header_t const * const p_header = (fds_header_t*)p_next_rec;
+
+        if (header_is_valid(p_header))
+        {
+            // Bingo!
+            *p_record = p_next_rec;
+            return NRF_SUCCESS;
+        }
+        else
+        {
+            // The item is not valid, jump to the next.
+            p_next_rec += (FDS_HEADER_SIZE + (p_header->tl.length_words));
+        }
+    }
+
+    return NRF_ERROR_NOT_FOUND;
+}
+
+
+static ret_code_t seek_record(fds_record_desc_t * const p_desc)
+{
+    uint32_t const * p_record;
+    uint16_t         page;
+    bool             seek_all_pages = false;
+
+    if ((p_desc->ptr_magic == FDS_MAGIC_HWORD) &&
+        (p_desc->gc_magic  == m_gc_runs))
+    {
+        // No need to seek the file.
+        return NRF_SUCCESS;
+    }
+
+    /** The pointer in the descriptor is not initialized, or GC
+     *  has been run since the last time it was retrieved.
+     *  We must seek the record again. */
+
+    // Obtain the physical page ID.
+    if (page_id_from_virtual_id(p_desc->vpage_id, &page) != NRF_SUCCESS)
+    {
+        page = 0;
+        seek_all_pages = true;
+    }
+
+    do {
+        // Let's find the address from where we should start seeking the record.
+        p_record = m_pages[page].start_addr + FDS_PAGE_TAG_SIZE;
+
+        /** Seek for a record with matching ID.
+         *  We might get away with seeking to the page write offset, if it is known. */
+
+        while ((p_record < (m_pages[page].start_addr + FS_PAGE_SIZE_WORDS)) &&
+               (*p_record != FDS_ERASED_WORD))
+        {
+            fds_header_t const * const p_header = (fds_header_t*)p_record;
+
+            if ((p_header->id != p_desc->record_id) ||
+                (!header_is_valid(p_header)))
+            {
+                // ID doesnt't match or the record has been cleared. Jump to the next record.
+                p_record += FDS_HEADER_SIZE + p_header->tl.length_words;
+            }
+            else
+            {
+                // Update the pointer in the descriptor.
+                p_desc->p_rec     = p_record;
+                p_desc->ptr_magic = FDS_MAGIC_HWORD;
+                p_desc->gc_magic  = m_gc_runs;
+
+                return NRF_SUCCESS;
+            }
+        }
+    } while (seek_all_pages ? page++ < FDS_MAX_PAGES : 0);
+
+    return NRF_ERROR_NOT_FOUND;
+}
+
+
+static ret_code_t find_record(fds_type_id_t     const * const p_type,
+                              fds_instance_id_t const * const p_inst,
+                              fds_record_desc_t       * const p_desc,
+                              fds_find_token_t        * const p_token)
+{
+    if (!flag_is_set(FDS_FLAG_INITIALIZED))
+    {
+        return NRF_ERROR_INVALID_STATE;
+    }
+
+    // Here we distinguish between the first invocation and the and the others.
+    if ((p_token->magic != FDS_MAGIC_WORD) ||
+        !address_within_page_bounds(p_token->p_addr)) // Is the address is really okay?
+    {
+        // Initialize the token.
+        p_token->magic    = FDS_MAGIC_WORD;
+        p_token->vpage_id = 0;
+        p_token->p_addr   = NULL;
+    }
+    else
+    {
+        // Look past the last record address.
+         p_token->p_addr += (FDS_HEADER_SIZE + ((fds_header_t*)p_token->p_addr)->tl.length_words);
+    }
+
+    // Begin (or resume) searching for a record.
+    for (; p_token->vpage_id < FDS_MAX_PAGES; p_token->vpage_id++)
+    {
+        uint16_t page = 0;
+
+        // Obtain the physical page ID.
+        page_id_from_virtual_id(p_token->vpage_id, &page);
+
+        if (m_pages[page].page_type != FDS_PAGE_VALID)
+        {
+            // Skip this page.
+            continue;
+        }
+
+        if (p_token->p_addr == NULL)
+        {
+            // If it's the first time the function is run, initialize the pointer.
+            p_token->p_addr = m_pages[page].start_addr + FDS_PAGE_TAG_SIZE;
+        }
+
+        // Seek a valid record on this page, starting from the address stored in the token.
+        while ((p_token->p_addr < (m_pages[page].start_addr + FS_PAGE_SIZE_WORDS)) &&
+               (*p_token->p_addr != FDS_ERASED_WORD)) // Did we jump to an erased word?
+        {
+            fds_header_t const * const p_header = (fds_header_t*)p_token->p_addr;
+
+            if (header_is_valid(p_header))
+            {
+                // A valid record was found, check its header for a match.
+                bool item_match = false;
+
+                if (p_type != NULL)
+                {
+                    if (p_header->tl.type == *p_type)
+                    {
+                        item_match = true;
+                    }
+                }
+
+                if (p_inst != NULL)
+                {
+                    if (p_header->ic.instance == *p_inst)
+                    {
+                        item_match = (p_type == NULL) ? true : item_match && true;
+                    }
+                    else
+                    {
+                        item_match = false;
+                    }
+                }
+
+                if (item_match)
+                {
+                    // We found the record! Update the descriptor.
+                    p_desc->vpage_id  = m_pages[page].vpage_id;
+                    p_desc->record_id = p_header->id;
+
+                    p_desc->p_rec     = p_token->p_addr;
+                    p_desc->ptr_magic = FDS_MAGIC_HWORD;
+                    p_desc->gc_magic  = m_gc_runs;
+
+                    return NRF_SUCCESS;
+                }
+            }
+            // Jump to the next record.
+            p_token->p_addr += (FDS_HEADER_SIZE + (p_header->tl.length_words));
+        }
+
+        /** We have seeked an entire page. Set the address in the token to NULL
+         *  so that it will be set again on the next iteration. */
+        p_token->p_addr = NULL;
+    }
+
+    /** If we couldn't find the record, zero the token structure
+     *  so that it can be reused. */
+    p_token->magic = 0x00;
+
+    return NRF_ERROR_NOT_FOUND;
+}
+
+
+static void gc_init()
+{
+    // Set which pages to GC.
+    for (uint16_t i = 0; i < FDS_MAX_PAGES; i++)
+    {
+        m_gc.do_gc_page[i] = (m_pages[i].page_type == FDS_PAGE_VALID);
+    }
+}
+
+
+static void gc_reset()
+{
+    m_gc.state       = BEGIN;
+    m_gc.cur_page    = 0;
+    m_gc.p_scan_addr = NULL;
+}
+
+
+static void gc_set_state(fds_gc_state_t new_state)
+{
+    m_gc.state = new_state;
+}
+
+
+static ret_code_t gc_get_next_page(uint16_t * const next_page)
+{
+    for (uint16_t i = 0; i < FDS_MAX_PAGES; i++)
+    {        
+        if (m_gc.do_gc_page[i])
+        {
+            uint16_t records_open;
+
+            CRITICAL_SECTION_ENTER();
+            records_open = m_pages[i].records_open;
+            CRITICAL_SECTION_EXIT();
+
+            // Do not attempt to GC this page anymore.
+            m_gc.do_gc_page[i] = false;
+
+            // Only GC pages with no open records.
+            if (records_open == 0)
+            {
+                *next_page = i;
+                return NRF_SUCCESS;
+            }   
+        }
+    }
+
+    return NRF_ERROR_NOT_FOUND;
+}
+
+
+static ret_code_t gc_page()
+{
+    ret_code_t ret;
+
+    ret = gc_get_next_page(&m_gc.cur_page);
+
+    // No pages left to GC. GC has terminated. Reset GC data.
+    if (ret != NRF_SUCCESS)
+    {    
+        gc_reset();
+
+        return COMMAND_COMPLETED;
+    }
+
+    // Prepare to GC the page.
+    gc_set_state(GC_PAGE);
+
+    // Flag the page as being garbage collected.
+    ret = page_tag_write_gc(m_gc.cur_page);
+
+    if (ret != NRF_SUCCESS)
+    {
+        return ret;
+    }
+
+    return COMMAND_EXECUTING;
+}
+
+
+static ret_code_t gc_copy_record()
+{
+    ret_code_t fs_ret;
+
+    // We have found a record to copy.
+    fds_record_t const * const p_record = (fds_record_t*)m_gc.p_scan_addr;
+
+    gc_set_state(COPY_RECORD);
+
+    // Copy the item to swap.
+    fs_ret = fs_store(&fs_config,
+                      m_pages[m_gc.swap_page].start_addr + m_pages[m_gc.swap_page].write_offset,
+                      (uint32_t*)p_record,
+                      FDS_HEADER_SIZE + p_record->header.tl.length_words);
+
+    if (fs_ret != NRF_SUCCESS)
+    {
+        // Oops :(
+        // This is an error. Can we recover?
+    }
+
+    // Remember to update the swap page write offset.
+    m_pages[m_gc.swap_page].write_offset += (FDS_HEADER_SIZE + p_record->header.tl.length_words);
+
+    return COMMAND_EXECUTING;
+}
+
+
+static ret_code_t gc_ready_swap_page()
+{
+    ret_code_t fs_ret;
+
+    /** A page has been scanned through. All valid records found were copied to swap.
+     *  The swap page can now be flagged as a valid page. */
+    gc_set_state(READY_SWAP);
+
+    fs_ret = page_tag_write_valid(m_gc.swap_page);
+    if (fs_ret != NRF_SUCCESS)
+    {
+        return fs_ret;
+    }
+
+    /** Do not update the page type in the internal page structure (m_pages)
+     *  right away. (why?) */
+    return COMMAND_EXECUTING;
+}
+
+
+static ret_code_t gc_seek_record()
+{
+    // Let's find a valid record which has not been copied yet.
+    if (scan_next_valid(m_gc.cur_page, &m_gc.p_scan_addr) == NRF_SUCCESS)
+    {
+        /** The record is guaranteed to fit in the destination page,
+         *  so we don't need to check its size. */
+        return gc_copy_record();
+    }
+    else
+    {
+        /** No more (uncopied) records left on this page.
+         *  The swap page can now be marked as a valid page. */
+        return gc_ready_swap_page();
+    }
+}
+
+
+static ret_code_t gc_new_swap_page()
+{
+    ret_code_t fs_ret;
+    uint16_t   vpage_id;
+
+    gc_set_state(NEW_SWAP);
+
+    // Save the swap page virtual page ID.
+    vpage_id = m_pages[m_gc.swap_page].vpage_id;
+
+    /** The swap page has been marked as valid in Flash. We copy the GC'ed page
+     *  write_offset and virtual page ID. */
+    m_pages[m_gc.swap_page].page_type      = FDS_PAGE_VALID;
+    m_pages[m_gc.swap_page].vpage_id       = m_pages[m_gc.cur_page].vpage_id;
+    m_pages[m_gc.swap_page].words_reserved = m_pages[m_gc.cur_page].words_reserved;
+
+    // The new swap page is now the page we just GC.
+    m_gc.swap_page = m_gc.cur_page;
+
+    // Update the write_offset, words_reserved and vpage_id fields for the new swap page.
+    m_pages[m_gc.swap_page].page_type      = FDS_PAGE_SWAP;
+    m_pages[m_gc.swap_page].vpage_id       = vpage_id;
+    m_pages[m_gc.swap_page].write_offset   = FDS_PAGE_TAG_SIZE;
+    m_pages[m_gc.swap_page].words_reserved = 0;
+
+    /** Finally, erase the new swap page. Remember we still have to flag this
+     *  new page as swap, but we'll wait the callback for this operation to do so. */
+    fs_ret = fs_erase(&fs_config,
+                      (uint32_t*)m_pages[m_gc.swap_page].start_addr,
+                      FS_PAGE_SIZE_WORDS);
+
+    if (fs_ret != NRF_SUCCESS)
+    {
+        return fs_ret;
+    }
+
+    return COMMAND_EXECUTING;
+}
+
+
+static ret_code_t gc_new_swap_page_init()
+{
+    ret_code_t fs_ret;
+
+    gc_set_state(INIT_SWAP);
+
+    fs_ret = page_tag_write_swap(m_gc.swap_page);
+    if (fs_ret != NRF_SUCCESS)
+    {
+        return fs_ret;
+    }
+
+    return COMMAND_EXECUTING;
+}
+
+
+static ret_code_t gc_execute(uint32_t result)
+{
+    // TODO: Handle resuming GC.
+
+    ret_code_t ret;
+
+    if (result != NRF_SUCCESS)
+    {
+        // An operation failed. Report to the application.
+        return result;
+    }
+
+    switch (m_gc.state)
+    {
+        case BEGIN:
+        {
+            // Increment the number of times the GC has been run.
+            m_gc_runs++;
+            // Sets up a list of pages to GC.
+            gc_init();
+            // Go !
+            ret = gc_page();
+        } break;
+
+        case GC_PAGE:
+            /** A page has been successfully flagged as being GC.
+             *  Look for valid records to copy. */
+            ret = gc_seek_record();
+            break;
+
+        case COPY_RECORD:
+            /** A record has been copied to swap.
+             *  Look for more records to copy. */
+            ret = gc_seek_record();
+            break;
+
+        case READY_SWAP:
+            /** The swap page has been flagged as 'valid' (ready).
+             *  Let's prepare a new swap page. */
+            ret = gc_new_swap_page();
+            break;
+
+        case NEW_SWAP:
+            // A new swap page has been prepared. Let's flag it as swap.
+            ret = gc_new_swap_page_init();
+            break;
+
+        case INIT_SWAP:
+            /** The swap was flagged as swap in flash. Let's compress another page.
+             *  Be sure to update the address where to scan from. */
+            m_gc.p_scan_addr = NULL;
+            ret = gc_page();
+            break;
+
+        default:
+            // Should really not happen.
+            ret = NRF_ERROR_INTERNAL;
+            break;
+    }
+
+    return ret;
+}
+
+
+/**@brief Function for initializing the command queue. */
+static void queues_init(void)
+{
+    memset(&m_cmd_queue,   0, sizeof(fds_cmd_queue_t));
+    memset(&m_chunk_queue, 0, sizeof(fds_chunk_queue_t));
+}
+
+
+void cmd_queue_next(fds_cmd_t ** pp_cmd)
+{
+    if (*pp_cmd != &m_cmd_queue.cmd[FDS_CMD_QUEUE_SIZE - 1])
+    {
+        (*pp_cmd)++;
+        return;
+    }
+
+    *pp_cmd = &m_cmd_queue.cmd[0];
+}
+
+
+void chunk_queue_next(fds_record_chunk_t ** pp_chunk)
+{
+    if ((*pp_chunk) != &m_chunk_queue.chunk[FDS_CHUNK_QUEUE_SIZE - 1])
+    {
+        (*pp_chunk)++;
+        return;
+    }
+
+    *pp_chunk = &m_chunk_queue.chunk[0];
+}
+
+
+/**@brief Advances one position in the command queue. Returns true if the queue is not empty. */
+static bool cmd_queue_advance(void)
+{
+    // Reset the current element.
+    memset(&m_cmd_queue.cmd[m_cmd_queue.rp], 0, sizeof(fds_cmd_t));
+
+    CRITICAL_SECTION_ENTER();
+    if (m_cmd_queue.count != 0)
+    {
+        // Advance in the queue, wrapping around if necessary.
+        m_cmd_queue.rp = (m_cmd_queue.rp + 1) % FDS_CMD_QUEUE_SIZE;
+        m_cmd_queue.count--;
+    }
+    CRITICAL_SECTION_EXIT();
+
+    return m_cmd_queue.count != 0;
+}
+
+
+/**@brief Returns the current chunk, and advances to the next in the queue. */
+static bool chunk_queue_get_and_advance(fds_record_chunk_t ** pp_chunk)
+{
+    bool chunk_popped = false;
+
+    CRITICAL_SECTION_ENTER();
+    if (m_chunk_queue.count != 0)
+    {
+        // Point to the current chunk and advance the queue.
+        *pp_chunk = &m_chunk_queue.chunk[m_chunk_queue.rp];
+
+        m_chunk_queue.rp = (m_chunk_queue.rp + 1) % FDS_CHUNK_QUEUE_SIZE;
+        m_chunk_queue.count--;
+
+        chunk_popped = true;
+    }
+    CRITICAL_SECTION_EXIT();
+
+    return chunk_popped;
+}
+
+
+static bool chunk_queue_skip(uint8_t num_op)
+{
+    bool chunk_skipped = false;
+
+    CRITICAL_SECTION_ENTER();
+    if (num_op <= m_chunk_queue.count)
+    {
+        m_chunk_queue.count -= num_op;
+        chunk_skipped = true;
+    }
+    CRITICAL_SECTION_EXIT();
+
+    return chunk_skipped;
+}
+
+
+/**@brief Reserves resources on both queues. */
+static ret_code_t queue_reserve(uint8_t               num_cmd,
+                                uint8_t               num_chunks,
+                                fds_cmd_t          ** pp_cmd,
+                                fds_record_chunk_t ** pp_chunk)
+{
+    uint8_t cmd_index;
+    uint8_t chunk_index;
+
+    // This is really just being safe.
+    if (pp_cmd == NULL || ((pp_chunk == NULL) && (num_chunks != 0)))
+    {
+        return NRF_ERROR_NULL;
+    }
+
+    if (num_cmd == 0)
+    {
+        return NRF_ERROR_INVALID_DATA;
+    }
+
+    CRITICAL_SECTION_ENTER();
+
+    // Ensure there is enough space in the queues.
+    if ((m_cmd_queue.count   > FDS_CMD_QUEUE_SIZE - num_cmd) ||
+        (m_chunk_queue.count > FDS_CHUNK_QUEUE_SIZE - num_chunks))
+    {
+        CRITICAL_SECTION_EXIT();
+        return NRF_ERROR_BUSY;
+    }
+
+    // Find the write position in the commands queue.
+    cmd_index  = m_cmd_queue.count;
+    cmd_index += m_cmd_queue.rp;
+    cmd_index  = cmd_index % FDS_CMD_QUEUE_SIZE;
+
+    *pp_cmd = &m_cmd_queue.cmd[cmd_index];
+    m_cmd_queue.count += num_cmd;
+
+    /* If no operations are associated with the command, such as is the case 
+     * for initialization and compression, pp_chunk can be NULL. */
+    if (num_chunks != 0)
+    {
+        chunk_index  = m_chunk_queue.count;
+        chunk_index += m_chunk_queue.rp;
+        chunk_index  = chunk_index % FDS_CHUNK_QUEUE_SIZE;
+
+        *pp_chunk = &m_chunk_queue.chunk[chunk_index];
+        m_chunk_queue.count += num_chunks;
+    }
+
+    CRITICAL_SECTION_EXIT();
+
+    return NRF_SUCCESS;
+}
+
+
+/**@brief Cancel the reservation on resources on queues. */
+static void queue_reserve_cancel(uint8_t num_cmd, uint8_t num_chunks)
+{
+    CRITICAL_SECTION_ENTER();
+    m_cmd_queue.count   -= num_cmd;
+    m_chunk_queue.count -= num_chunks;
+    CRITICAL_SECTION_EXIT();
+}
+
+
+static void pages_init(uint16_t * const p_pages_avail,
+                       bool     * const p_write_page_tag,
+                       bool     * const p_resume_comp)
+{
+    *p_pages_avail    = 0;
+    *p_write_page_tag = false;
+    *p_resume_comp    = false;
+
+    /** Scan pages and setup page data.
+     *  This function does NOT perform write operations in flash. */
+    for (uint16_t i = 0; i < FDS_MAX_PAGES; i++)
+    {
+        // Initialize page data. Note that start_addr must be set BEFORE invoking page_identify().
+        m_pages[i].start_addr     = fs_config.p_start_addr + (i * FS_PAGE_SIZE_WORDS);
+        m_pages[i].write_offset   = FDS_PAGE_TAG_SIZE;
+        m_pages[i].vpage_id       = i;
+        m_pages[i].records_open   = 0;
+        m_pages[i].words_reserved = 0;
+
+        m_pages[i].page_type      = page_identify(i);
+
+        switch (m_pages[i].page_type)
+        {
+            case FDS_PAGE_UNDEFINED:
+            {
+                if (page_is_empty(i))
+                {
+                    /* We have found an erased page, which can be initialized.
+                     * This will require a write in flash. */
+                    m_pages[i].page_type = FDS_PAGE_ERASED;
+                    *p_write_page_tag = true;
+                }
+            } break;
+
+            case FDS_PAGE_VALID:
+            {
+                /** If a page is valid, we update its write offset.
+                 *  Additionally, page_scan will update the last known record ID. */
+                page_scan(i, &m_pages[i].write_offset);
+                (*p_pages_avail)++;
+            } break;
+
+            case FDS_PAGE_SWAP:
+            {
+                m_gc.swap_page    = i;
+                m_swap_page_avail = true;
+            } break;
+
+            case FDS_PAGE_GC:
+            {
+                /** There is an ongoing garbage collection.
+                 *  We should resume the operation, which we don't yet. */
+                m_gc.cur_page   = i;
+                m_gc.state      = GC_PAGE;
+                *p_resume_comp  = true;
+            } break;
+
+            default:
+                break;
+        }
+    }
+}
+
+
+// NOTE: Adds FDS_HEADER_SIZE automatically.
+static ret_code_t write_space_reserve(uint16_t length_words, uint16_t * vpage_id)
+{
+    bool     space_reserved  = false;
+    uint16_t total_len_words = length_words + FDS_HEADER_SIZE;
+
+    if (total_len_words >= FS_PAGE_SIZE_WORDS - FDS_PAGE_TAG_SIZE)
+    {
+        return NRF_ERROR_INVALID_LENGTH;
+    }
+
+    for (uint16_t page = 0; page < FDS_MAX_PAGES; page++)
+    {
+        if ((m_pages[page].page_type == FDS_PAGE_VALID) &&
+            (page_has_space(page, total_len_words)))
+        {
+            space_reserved = true;
+            *vpage_id      = m_pages[page].vpage_id;
+
+            CRITICAL_SECTION_ENTER();
+            m_pages[page].words_reserved += total_len_words;
+            CRITICAL_SECTION_EXIT();
+            
+            break;
+        }
+    }
+
+    return space_reserved ? NRF_SUCCESS : NRF_ERROR_NO_MEM;
+}
+
+
+static bool chunk_is_aligned(fds_record_chunk_t const * const p_chunk, uint8_t num_parts)
+{
+    for (uint8_t i = 0; i < num_parts; i++)
+    {
+        if (!is_word_aligned(p_chunk[i].p_data))
+        {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+
+static ret_code_t init_execute(uint32_t result, uint32_t const * p_page_addr)
+{
+    uint16_t   cur_page;
+    bool       page_tag_written = false;
+
+    if (result != NRF_SUCCESS)
+    {
+        // Oops. Error.
+        return result;
+    }
+
+    // Here we just distinguish between the first invocation and the others.
+    cur_page = p_page_addr == NULL ? 0 : page_by_addr(p_page_addr) + 1;
+
+    if (cur_page == FDS_MAX_PAGES)
+    {
+        // We have finished. We'd need to set some flags.
+        flag_set(FDS_FLAG_INITIALIZED);
+        flag_clear(FDS_FLAG_INITIALIZING);
+
+        return COMMAND_COMPLETED;
+    }
+
+    while (cur_page < FDS_MAX_PAGES && !page_tag_written)
+    {
+        if (m_pages[cur_page].page_type == FDS_PAGE_ERASED)
+        {
+            page_tag_written = true;
+
+            if (m_swap_page_avail)
+            {
+                if (page_tag_write_valid(cur_page) != NRF_SUCCESS)
+                {
+                    // Oops. Error.
+                }
+                // Update the page type.
+                m_pages[cur_page].page_type = FDS_PAGE_VALID;
+            }
+            else
+            {
+                if (page_tag_write_swap(cur_page) != NRF_SUCCESS)
+                {
+                    // Oops. Error.
+                }
+                // Update the page type.
+                m_pages[cur_page].page_type = FDS_PAGE_SWAP;
+
+                /** Update compression data. We set this information in init_pages
+                 *  if it is available, otherwise, we should set it here. */
+                m_swap_page_avail = true;
+                m_gc.swap_page = cur_page;
+            }
+        }
+
+        cur_page++;
+    }
+
+    if (!page_tag_written)
+    {
+        if (m_swap_page_avail)
+        {
+            return COMMAND_COMPLETED;
+        }
+        else
+        {
+            // There is no empty space to use as swap.
+            // Notify user that no compression is available?
+        }
+    }
+
+    return COMMAND_EXECUTING;
+}
+
+
+/**@brief Function to execute write and update commands.
+ *
+ */
+static ret_code_t store_execute(uint32_t result, fds_cmd_t * const p_cmd)
+{
+    ret_code_t           fs_ret;
+    fds_record_chunk_t * p_chunk = NULL;
+    fds_page_t         * p_page  = NULL;
+    uint32_t           * p_write_addr;
+
+    // Using virtual page IDs allows other operations to be queued even if GC has been requested.
+    page_from_virtual_id(p_cmd->vpage_id, &p_page);
+
+    if (result != NRF_SUCCESS)
+    {
+        // The previous operation has failed, update the page data.
+        p_page->write_offset   += (FDS_HEADER_SIZE + (p_cmd->chunk_offset - FDS_WRITE_OFFSET_DATA));
+        p_page->words_reserved -= (FDS_HEADER_SIZE + (p_cmd->chunk_offset - FDS_WRITE_OFFSET_DATA));
+
+        return result;
+    }
+
+    // Compute the write address (just syntatic sugar).
+    p_write_addr = (uint32_t*)(p_page->start_addr + p_page->write_offset);
+
+    // Execute the operation.
+    switch (p_cmd->op_code)
+    {
+        case FDS_OP_WRITE_TL:
+        {
+            fs_ret = fs_store(&fs_config,
+                              p_write_addr + FDS_WRITE_OFFSET_TL,
+                              (uint32_t*)&p_cmd->record_header.tl,
+                              FDS_HEADER_SIZE_TL /*Words*/);
+
+            // Set the next operation to be executed.
+            p_cmd->op_code = FDS_OP_WRITE_ID;
+
+        } break;
+
+        case FDS_OP_WRITE_ID:
+        {
+            fs_ret = fs_store(&fs_config,
+                              p_write_addr + FDS_WRITE_OFFSET_ID,
+                              (uint32_t*)&p_cmd->record_header.id,
+                              FDS_HEADER_SIZE_ID /*Words*/);
+
+            p_cmd->op_code = FDS_OP_WRITE_CHUNK;
+
+        } break;
+
+        case FDS_OP_WRITE_CHUNK:
+        {
+            // Decrement the number of chunks left to write.
+            p_cmd->num_chunks--;
+
+            // Retrieve the chunk to be written.
+            chunk_queue_get_and_advance(&p_chunk);
+
+            fs_ret = fs_store(&fs_config,
+                              p_write_addr + p_cmd->chunk_offset,
+                              p_chunk->p_data,
+                              p_chunk->length_words);
+
+            // Accumulate the offset.
+            p_cmd->chunk_offset += p_chunk->length_words;
+
+            if (p_cmd->num_chunks == 0)
+            {
+                /** We have written all the record chunks; we'll write
+                 *  IC last as a mean to 'validate' the record. */
+                p_cmd->op_code = FDS_OP_WRITE_IC;
+            }
+
+        } break;
+
+        case FDS_OP_WRITE_IC:
+        {
+            fs_ret = fs_store(&fs_config,
+                              p_write_addr + FDS_WRITE_OFFSET_IC,
+                              (uint32_t*)&p_cmd->record_header.ic,
+                              FDS_HEADER_SIZE_IC /*Words*/);
+
+            // This is the final operation.
+            p_cmd->op_code = FDS_OP_DONE;
+
+        } break;
+
+        case FDS_OP_DONE:
+        {
+            // We have successfully written down the IC. The command has completed successfully.
+            p_page->write_offset   += (FDS_HEADER_SIZE + (p_cmd->chunk_offset - FDS_WRITE_OFFSET_DATA));
+            p_page->words_reserved -= (FDS_HEADER_SIZE + (p_cmd->chunk_offset - FDS_WRITE_OFFSET_DATA));
+
+            return COMMAND_COMPLETED;
+
+        };
+
+        default:
+            fs_ret = NRF_ERROR_INTERNAL;
+            break;
+    }
+
+    // If fs_store did not succeed, the command has failed.
+    if (fs_ret != NRF_SUCCESS)
+    {
+        /** We're not going to receive a callback from fstorage
+         *  so we update the page data right away. */
+        p_page->write_offset   += (FDS_HEADER_SIZE + (p_cmd->chunk_offset - FDS_WRITE_OFFSET_DATA));
+        p_page->words_reserved -= (FDS_HEADER_SIZE + (p_cmd->chunk_offset - FDS_WRITE_OFFSET_DATA));
+
+        // We should propagate the error from fstorage.
+        return fs_ret;
+    }
+
+    // An operation has successfully been executed. Wait for the callback.
+    return COMMAND_EXECUTING;
+}
+
+
+static ret_code_t clear_execute(ret_code_t result, fds_cmd_t * const p_cmd)
+{
+    ret_code_t        ret;
+    fds_record_desc_t desc;
+
+    // This must persist across calls.
+    static fds_find_token_t tok;
+
+    if (result != NRF_SUCCESS)
+    {
+        // A previous operation has failed. Propagate the error.
+        return result;
+    }
+
+    switch (p_cmd->op_code)
+    {
+        case FDS_OP_CLEAR_TL:
+        {
+            // We were provided a descriptor for the record.
+            desc.vpage_id  = p_cmd->vpage_id;
+            desc.record_id = p_cmd->record_header.id;
+
+            /** Unfortunately, we always seek the record in this case,
+             *  because we don't buffer an entire record descriptor in the
+             *  fds_cmd_t structure. Keep in mind though, that we will
+             *  seek one page at most. */
+            if (seek_record(&desc) != NRF_SUCCESS)
+            {
+                // The record never existed, or it is already cleared.
+                ret = NRF_ERROR_NOT_FOUND;
+            }
+            else
+            {
+                // Copy the record key, so that it may be returned in the callback.
+                p_cmd->record_header.tl.type     = ((fds_header_t*)desc.p_rec)->tl.type;
+                p_cmd->record_header.ic.instance = ((fds_header_t*)desc.p_rec)->ic.instance;
+
+                ret = fs_store(&fs_config,
+                               desc.p_rec,
+                               (uint32_t*)&m_fds_tl_invalid,
+                               FDS_HEADER_SIZE_TL);
+            }
+
+            p_cmd->op_code = FDS_OP_DONE;
+
+        } break;
+
+        case FDS_OP_CLEAR_INSTANCE:
+        {
+            if (find_record(NULL, &p_cmd->record_header.ic.instance,
+                            &desc, &tok) != NRF_SUCCESS)
+            {
+                // No more records to be found.
+                p_cmd->op_code = FDS_OP_DONE;
+
+                // Zero the token, so that we may reuse it.
+                memset(&tok, 0, sizeof(fds_find_token_t));
+
+                /** We won't receive a callback, since no flash operation
+                 *  was initiated. The command has finished. */
+                ret = COMMAND_COMPLETED;
+            }
+            else
+            {
+                ret = fs_store(&fs_config,
+                               desc.p_rec,
+                               (uint32_t*)&m_fds_tl_invalid,
+                               FDS_HEADER_SIZE_TL);
+            }
+        } break;
+
+        case FDS_OP_DONE:
+        {
+            /** The last operation completed successfully.
+             *  The command has finished. Return. */
+            ret = COMMAND_COMPLETED;
+        } break;
+
+        default:
+            ret = NRF_ERROR_INVALID_DATA;
+            break;
+    }
+
+    // Await for the operation result.
+    return ret;
+}
+
+
+static ret_code_t cmd_queue_process(void)
+{
+    ret_code_t        ret;
+    fds_cmd_t * const p_cmd = &m_cmd_queue.cmd[m_cmd_queue.rp];
+
+    switch (p_cmd->id)
+    {
+        case FDS_CMD_INIT:
+            ret = init_execute(NRF_SUCCESS, NULL);
+            break;
+
+        case FDS_CMD_WRITE:
+        case FDS_CMD_UPDATE:
+            ret = store_execute(NRF_SUCCESS, p_cmd);
+            break;
+
+        case FDS_CMD_CLEAR:
+        case FDS_CMD_CLEAR_INST:
+            ret = clear_execute(NRF_SUCCESS, p_cmd);
+            break;
+
+        case FDS_CMD_GC:
+            ret = gc_execute(NRF_SUCCESS);
+            break;
+
+        default:
+            ret = NRF_ERROR_FORBIDDEN;
+            break;
+    }
+
+    if ((ret == COMMAND_EXECUTING) || (ret == COMMAND_COMPLETED))
+    {
+        return NRF_SUCCESS;
+    }
+
+    // This is an error.
+    return ret;
+}
+
+
+static ret_code_t cmd_queue_process_start(void)
+{
+    bool start_processing = false;
+
+    if (!flag_is_set(FDS_FLAG_PROCESSING))
+    {
+        flag_set(FDS_FLAG_PROCESSING);
+        start_processing = true;
+    }
+
+    if (!start_processing)
+    {
+        // We are awaiting a callback, so there is no need to manually start queue processing.
+        return NRF_SUCCESS;
+    }
+
+    return cmd_queue_process();
+}
+
+
+static void fs_callback(uint8_t           op_code,
+                        uint32_t          result,
+                        uint32_t  const * p_data,
+                        fs_length_t       length)
+{
+    ret_code_t         ret;
+    fds_cmd_t        * p_cmd = &m_cmd_queue.cmd[m_cmd_queue.rp];
+    fds_record_key_t   record_key;
+
+    switch (p_cmd->id)
+    {
+        case FDS_CMD_INIT:
+            ret = init_execute(result, p_data);
+            break;
+
+        case FDS_CMD_WRITE:
+        case FDS_CMD_UPDATE:
+            ret = store_execute(result, p_cmd);
+            break;
+
+        case FDS_CMD_CLEAR:
+        case FDS_CMD_CLEAR_INST:
+            ret = clear_execute(result, p_cmd);
+            break;
+
+        case FDS_CMD_GC:
+            ret = gc_execute(result);
+            break;
+
+        default:
+            // Should not happen.
+            ret = NRF_ERROR_INTERNAL;
+            break;
+    }
+
+    if (ret == COMMAND_EXECUTING /*=NRF_SUCCESS*/)
+    {
+        /** The current command is still being processed.
+         *  The command queue does not need to advance. */
+        return;
+    }
+
+    // Initialize the fds_record_key_t structure needed for the callback.
+    record_key.type     = p_cmd->record_header.tl.type;
+    record_key.instance = p_cmd->record_header.ic.instance;
+
+    // The command has either completed or an operation (and thus the command) has failed.
+    if (ret == COMMAND_COMPLETED)
+    {
+        // The command has completed successfully. Notify the application.
+        app_notify(NRF_SUCCESS, p_cmd->id, p_cmd->record_header.id, record_key);
+    }
+    else
+    {
+        /** An operation has failed. This is fatal for the execution of a command.
+         *  Skip other operations associated with the current command.
+         *  Notify the user of the failure.  */
+        chunk_queue_skip(p_cmd->num_chunks);
+        app_notify(ret /*=result*/, p_cmd->id, p_cmd->record_header.id, record_key);
+    }
+
+    // Advance the command queue, and if there is still something in the queue, process it.
+    if (cmd_queue_advance())
+    {
+        /** Only process the queue if there are no pending commands being queued, since they
+         *  will begin to process the queue on their own. Be sure to clear
+         *  the flag FDS_FLAG_PROCESSING though ! */
+        if (atomic_counter_is_zero())
+        {
+            cmd_queue_process();
+        }
+        else
+        {
+            flag_clear(FDS_FLAG_PROCESSING);
+        }
+    }
+    else
+    {
+        /** No more elements in the queue. Clear the FDS_FLAG_PROCESSING flag,
+         *  so that new commands can start the queue processing. */
+        flag_clear(FDS_FLAG_PROCESSING);
+    }
+}
+
+
+ret_code_t fds_init()
+{
+    ret_code_t   fs_ret;
+    fds_cmd_t  * p_cmd;
+    uint16_t     pages_avail;
+    bool         write_page_tag;
+    bool         resume_compression;
+
+    fds_record_key_t const dummy_key = {.type     = FDS_TYPE_ID_INVALID,
+                                        .instance = FDS_INSTANCE_ID_INVALID};
+
+    if (flag_is_set(FDS_FLAG_INITIALIZED))
+    {
+        // Notify immediately.
+        app_notify(NRF_SUCCESS, FDS_CMD_INIT, 0 /*unused*/, dummy_key /*unused*/);
+        return NRF_SUCCESS;
+    }
+
+    if (flag_is_set(FDS_FLAG_INITIALIZING))
+    {
+        return NRF_ERROR_INVALID_STATE;
+    }
+
+    fs_ret = fs_init();
+    if (fs_ret != NRF_SUCCESS)
+    {
+        // fs_init() failed, propagate the error.
+        return fs_ret;
+    }
+
+    queues_init();
+
+    /** Initialize the last known record to zero.
+     *  Its value will be updated by page_scan() called in pages_init(). */
+    m_last_rec_id = 0;
+
+    // Initialize the page table containing all info on pages (address, type etc).
+    pages_init(&pages_avail, &write_page_tag, &resume_compression);
+
+    if (pages_avail == 0 && !write_page_tag)
+    {
+        return NRF_ERROR_NO_MEM;
+    }
+
+    /** This flag means fds_init() has been called. However,
+     *  the module is NOT yet initialized. */
+    flag_set(FDS_FLAG_INITIALIZING);
+
+    if (resume_compression)
+    {
+        return NRF_SUCCESS;
+    }
+
+    if (write_page_tag)
+    {
+        if (queue_reserve(FDS_CMD_QUEUE_SIZE_INIT, 0, &p_cmd, NULL) != NRF_SUCCESS)
+        {
+            // Should never happen.
+            return NRF_ERROR_BUSY;
+        }
+
+        // Initialize the command in the queue.
+        p_cmd->id = FDS_CMD_INIT;
+
+        return cmd_queue_process_start();
+    }
+    else
+    {
+        /* No flash operation is necessary for initialization.
+         * We can notify the application immediately. */
+        flag_set  (FDS_FLAG_INITIALIZED);
+        flag_clear(FDS_FLAG_INITIALIZING);
+        app_notify(NRF_SUCCESS, FDS_CMD_INIT, 0 /*unused*/, dummy_key /*unused*/);
+    }
+
+    return NRF_SUCCESS;
+}
+
+
+ret_code_t fds_open(fds_record_desc_t * const p_desc,
+                    fds_record_t      * const p_record)
+{
+    uint16_t page;
+
+    if (p_desc == NULL || p_record == NULL)
+    {
+        return NRF_ERROR_NULL;
+    }
+
+    if (page_id_from_virtual_id(p_desc->vpage_id, &page) != NRF_SUCCESS)
+    {
+        // Should not happen.
+        return NRF_ERROR_INVALID_DATA;
+    }
+
+    // Seek the record if necessary.
+    if (seek_record(p_desc) == NRF_SUCCESS)
+    {
+        if (header_is_valid((fds_header_t*)p_desc->p_rec))
+        {
+            CRITICAL_SECTION_ENTER();
+            m_pages[page].records_open++;
+            CRITICAL_SECTION_EXIT();
+
+            p_record->header = *((fds_header_t*)p_desc->p_rec);
+            p_record->p_data = (p_desc->p_rec + FDS_HEADER_SIZE);
+
+            return NRF_SUCCESS;
+        }
+    }
+
+    /** The record could not be found.
+     *  It either never existed or it has been cleared. */
+    return NRF_ERROR_NOT_FOUND;
+}
+
+
+ret_code_t fds_close(fds_record_desc_t const * const p_desc)
+{
+    uint16_t page;
+
+    if (p_desc == NULL)
+    {
+        return NRF_ERROR_NULL;
+    }
+
+    if (page_id_from_virtual_id(p_desc->vpage_id, &page) != NRF_SUCCESS)
+    {
+        return NRF_ERROR_INVALID_DATA;
+    }
+
+    CRITICAL_SECTION_ENTER();
+    m_pages[page].records_open--;
+    CRITICAL_SECTION_EXIT();
+
+    return NRF_SUCCESS;
+}
+
+
+static ret_code_t write_enqueue(fds_record_desc_t        * const p_desc,
+                                fds_record_key_t                 key,
+                                uint8_t                          num_chunks,
+                                fds_record_chunk_t               chunks[],
+                                fds_write_token_t  const * const p_tok,
+                                bool                             do_update)
+{
+    ret_code_t           ret;
+    fds_cmd_t          * p_cmd;
+    fds_record_chunk_t * p_chunk = NULL;
+    uint16_t             vpage_id;
+    uint16_t             length_words = 0;
+    uint8_t              cmd_queue_elems;
+
+    if (!flag_is_set(FDS_FLAG_INITIALIZED))
+    {
+        return NRF_ERROR_INVALID_STATE;
+    }
+
+    if ((key.type     == FDS_TYPE_ID_INVALID) ||
+        (key.instance == FDS_INSTANCE_ID_INVALID))
+    {
+        return NRF_ERROR_INVALID_DATA;
+    }
+
+    if (!chunk_is_aligned(chunks, num_chunks))
+    {
+        return NRF_ERROR_INVALID_ADDR;
+    }
+
+    cmd_queue_elems = do_update ? FDS_CMD_QUEUE_SIZE_UPDATE : FDS_CMD_QUEUE_SIZE_WRITE;
+
+    // Reserve space on both queues, and obtain pointers to the first elements reserved.
+    ret = queue_reserve(cmd_queue_elems,
+                        num_chunks,
+                        &p_cmd,
+                        &p_chunk);
+
+    if (ret != NRF_SUCCESS)
+    {
+        return ret;
+    }
+
+    // No space was previously reserved for this operation.
+    if (p_tok == NULL)
+    {
+        // Compute the total length of the record.
+        for (uint8_t i = 0; i < num_chunks; i++)
+        {
+            length_words += chunks[i].length_words;
+        }
+
+        /** Find a page where we can write the data. Reserve the space necessary
+         *  to write the metadata as well. */
+        ret = write_space_reserve(length_words, &vpage_id);
+        if (ret != NRF_SUCCESS)
+        {
+            // If there is no space available, cancel the queue reservation.
+            queue_reserve_cancel(cmd_queue_elems, num_chunks);
+            return ret;
+        }
+    }
+    else
+    {
+        length_words = p_tok->length_words;
+        vpage_id     = p_tok->vpage_id;
+    }
+
+    // Initialize the command.
+    p_cmd->id           = do_update ? FDS_CMD_UPDATE : FDS_CMD_WRITE;
+    p_cmd->op_code      = FDS_OP_WRITE_TL;
+    p_cmd->num_chunks   = num_chunks;
+    p_cmd->chunk_offset = FDS_WRITE_OFFSET_DATA;
+    p_cmd->vpage_id     = vpage_id;
+
+    // Fill in the header information.
+    p_cmd->record_header.id              = record_id_new();
+    p_cmd->record_header.tl.type         = key.type;
+    p_cmd->record_header.tl.length_words = length_words;
+    p_cmd->record_header.ic.instance     = key.instance;
+    p_cmd->record_header.ic.checksum     = 0;
+
+    // Buffer the record chunks in the queue.
+    for (uint8_t i = 0; i < num_chunks; i++)
+    {
+        p_chunk->p_data       = chunks[i].p_data;
+        p_chunk->length_words = chunks[i].length_words;
+        chunk_queue_next(&p_chunk);
+    }
+
+    if (do_update)
+    {
+        // Clear
+        cmd_queue_next(&p_cmd);
+        p_cmd->id      = FDS_CMD_CLEAR;
+        p_cmd->op_code = FDS_OP_CLEAR_TL;
+
+        p_cmd->vpage_id         = p_desc->vpage_id;
+        p_cmd->record_header.id = p_desc->record_id;
+    }
+
+    // Initialize the record descriptor, if provided.
+    if (p_desc != NULL)
+    {
+        p_desc->vpage_id  = vpage_id;
+        // Don't invoke record_id_new() again.
+        p_desc->record_id = p_cmd->record_header.id;
+    }
+
+    return cmd_queue_process_start();
+}
+
+
+ret_code_t fds_reserve(fds_write_token_t * const p_tok, uint16_t length_words)
+{
+    uint16_t vpage_id;
+
+    if (!flag_is_set(FDS_FLAG_INITIALIZED))
+    {
+        return NRF_ERROR_INVALID_STATE;
+    }
+
+    if (p_tok == NULL)
+    {
+        return NRF_ERROR_NULL;
+    }
+
+    // Reserve space on the page. write_space_reserve() accounts for the header.
+    if (write_space_reserve(length_words, &vpage_id) == NRF_SUCCESS)
+    {
+        p_tok->vpage_id     = vpage_id;
+        p_tok->length_words = length_words;
+
+        return NRF_SUCCESS;
+    }
+
+    return NRF_ERROR_NO_MEM;
+}
+
+
+ret_code_t fds_reserve_cancel(fds_write_token_t * const p_tok)
+{
+    fds_page_t * p_page;
+
+    if (!flag_is_set(FDS_FLAG_INITIALIZED))
+    {
+        return NRF_ERROR_INVALID_STATE;
+    }
+
+    if (p_tok == NULL)
+    {
+        return NRF_ERROR_NULL;
+    }
+
+    if (page_from_virtual_id(p_tok->vpage_id, &p_page) != NRF_SUCCESS)
+    {
+        // Could not find the virtual page. This shouldn't happen.
+        return NRF_ERROR_INVALID_DATA;
+    }
+
+    if ((p_page->words_reserved - p_tok->length_words) < 0)
+    {
+        /** We are trying to cancel a reservation for more words than how many are
+         *  currently reserved on the page. This is shouldn't happen. */
+        return NRF_ERROR_INVALID_DATA;
+    }
+
+    // Free the space which had been reserved.
+    p_page->words_reserved -= p_tok->length_words;
+
+    // Clean the token.
+    p_tok->vpage_id     = 0;
+    p_tok->length_words = 0;
+
+    return NRF_SUCCESS;
+}
+
+
+ret_code_t fds_write(fds_record_desc_t  * const p_desc,
+                     fds_record_key_t           key,
+                     uint8_t                    num_chunks,
+                     fds_record_chunk_t         chunks[])
+{
+    ret_code_t ret;
+    atomic_counter_inc();
+    ret = write_enqueue(p_desc, key, num_chunks, chunks, NULL, false /*not an update*/);
+    atomic_counter_dec();
+    return ret;
+}
+
+
+ret_code_t fds_write_reserved(fds_write_token_t  const * const p_tok,
+                              fds_record_desc_t        * const p_desc,
+                              fds_record_key_t                 key,
+                              uint8_t                          num_chunks,
+                              fds_record_chunk_t               chunks[])
+{
+    ret_code_t ret;
+    atomic_counter_inc();
+    ret = write_enqueue(p_desc, key, num_chunks, chunks, p_tok, false /*not an update*/);
+    atomic_counter_dec();
+    return ret;
+}
+
+
+static ret_code_t clear_enqueue(fds_record_desc_t * const p_desc)
+{
+    ret_code_t   ret;
+    fds_cmd_t  * p_cmd;
+
+    if (!flag_is_set(FDS_FLAG_INITIALIZED))
+    {
+        return NRF_ERROR_INVALID_STATE;
+    }
+
+    if (p_desc == NULL)
+    {
+        return NRF_ERROR_NULL;
+    }
+
+    ret = queue_reserve(FDS_CMD_QUEUE_SIZE_CLEAR, 0, &p_cmd, NULL);
+
+    if (ret != NRF_SUCCESS)
+    {
+        return ret;
+    }
+
+    // Initialize the command.
+    p_cmd->id        = FDS_CMD_CLEAR;
+    p_cmd->op_code   = FDS_OP_CLEAR_TL;
+
+    p_cmd->record_header.id = p_desc->record_id;
+    p_cmd->vpage_id         = p_desc->vpage_id;
+
+    return cmd_queue_process_start();
+}
+
+
+ret_code_t fds_clear(fds_record_desc_t * const p_desc)
+{
+    ret_code_t ret;
+    atomic_counter_inc();
+    ret = clear_enqueue(p_desc);
+    atomic_counter_dec();
+    return ret;
+}
+
+
+static ret_code_t clear_by_instance_enqueue(fds_instance_id_t instance)
+{
+    ret_code_t   ret;
+    fds_cmd_t  * p_cmd;
+
+    if (!flag_is_set(FDS_FLAG_INITIALIZED))
+    {
+        return NRF_ERROR_INVALID_STATE;
+    }
+
+    ret  = queue_reserve(FDS_CMD_QUEUE_SIZE_CLEAR, 0, &p_cmd, NULL);
+
+    if (ret != NRF_SUCCESS)
+    {
+        return ret;
+    }
+
+    p_cmd->id      = FDS_CMD_CLEAR_INST;
+    p_cmd->op_code = FDS_OP_CLEAR_INSTANCE;
+
+    p_cmd->record_header.ic.instance = instance;
+
+    return cmd_queue_process_start();
+}
+
+ret_code_t fds_clear_by_instance(fds_instance_id_t instance)
+{
+    ret_code_t ret;
+    atomic_counter_inc();
+    ret = clear_by_instance_enqueue(instance);
+    atomic_counter_dec();
+    return ret;
+}
+
+
+ret_code_t fds_update(fds_record_desc_t  * const p_desc,
+                      fds_record_key_t           key,
+                      uint8_t                    num_chunks,
+                      fds_record_chunk_t         chunks[])
+{
+    ret_code_t ret;
+    atomic_counter_inc();
+    ret = write_enqueue(p_desc, key, num_chunks, chunks, NULL, true /*update*/);
+    atomic_counter_dec();
+    return ret;
+}
+
+
+static ret_code_t gc_enqueue()
+{
+    ret_code_t  ret;
+    fds_cmd_t * p_cmd;
+
+    if (!flag_is_set(FDS_FLAG_INITIALIZED))
+    {
+        return NRF_ERROR_INVALID_STATE;
+    }
+
+    ret = queue_reserve(FDS_CMD_QUEUE_SIZE_GC, 0, &p_cmd, NULL);
+    if (ret != NRF_SUCCESS)
+    {
+        return ret;
+    }
+
+    p_cmd->id = FDS_CMD_GC;
+
+    // Set compression parameters.
+    m_gc.state = BEGIN;
+
+    return cmd_queue_process_start();
+}
+
+
+ret_code_t fds_gc()
+{
+    ret_code_t ret;
+    atomic_counter_inc();
+    ret = gc_enqueue();
+    atomic_counter_dec();
+    return ret;   
+}
+
+
+ret_code_t fds_find(fds_type_id_t             type,
+                    fds_instance_id_t         instance,
+                    fds_record_desc_t * const p_desc,
+                    fds_find_token_t  * const p_token)
+{
+    if (p_desc == NULL || p_token == NULL)
+    {
+        return NRF_ERROR_NULL;
+    }
+
+    return find_record(&type, &instance, p_desc, p_token);
+}
+
+
+ret_code_t fds_find_by_type(fds_type_id_t             type,
+                            fds_record_desc_t * const p_desc,
+                            fds_find_token_t  * const p_token)
+{
+    if (p_desc == NULL || p_token == NULL)
+    {
+        return NRF_ERROR_NULL;
+    }
+
+    return find_record(&type, NULL, p_desc, p_token);
+}
+
+
+ret_code_t fds_find_by_instance(fds_instance_id_t         instance,
+                                fds_record_desc_t * const p_desc,
+                                fds_find_token_t  * const p_token)
+{
+    if (p_desc == NULL || p_token == NULL)
+    {
+        return NRF_ERROR_NULL;
+    }
+
+    return find_record(NULL, &instance, p_desc, p_token);
+}
+
+
+ret_code_t fds_register(fds_cb_t cb)
+{
+    if (m_users == FDS_MAX_USERS)
+    {
+        return NRF_ERROR_NO_MEM;
+    }
+
+    m_cb_table[m_users] = cb;
+    m_users++;
+
+    return NRF_SUCCESS;
+}
+
+
+bool fds_descriptor_match(fds_record_desc_t const * const p_desc1,
+                          fds_record_desc_t const * const p_desc2)
+{
+    if ((p_desc1 == NULL) || (p_desc2 == NULL))
+    {
+        return false;
+    }
+
+    return (p_desc1->record_id == p_desc2->record_id);
+}
+
+
+ret_code_t fds_descriptor_from_rec_id(fds_record_desc_t * const p_desc,
+                                      fds_record_id_t           record_id)
+{
+    if (p_desc == NULL)
+    {
+        return NRF_ERROR_NULL;
+    }
+
+    p_desc->record_id = record_id;
+    p_desc->vpage_id  = FDS_VPAGE_ID_UNKNOWN;
+
+    return NRF_SUCCESS;
+}
+
+ret_code_t fds_record_id_from_desc(fds_record_desc_t const * const p_desc,
+                                   fds_record_id_t         * const p_record_id)
+{
+    if (p_desc == NULL || p_record_id == NULL)
+    {
+        return NRF_ERROR_NULL;
+    }
+
+    *p_record_id = p_desc->record_id;
+
+    return NRF_SUCCESS;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/source/nordic_sdk/components/libraries/fds/fds.h	Thu Apr 07 17:38:12 2016 +0100
@@ -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__
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/source/nordic_sdk/components/libraries/fds/fds_config.h	Thu Apr 07 17:38:12 2016 +0100
@@ -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__
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/source/nordic_sdk/components/libraries/fds/fds_types_internal.h	Thu Apr 07 17:38:12 2016 +0100
@@ -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__
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/source/nordic_sdk/components/libraries/fstorage/fstorage.c	Thu Apr 07 17:38:12 2016 +0100
@@ -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");
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/source/nordic_sdk/components/libraries/fstorage/fstorage.h	Thu Apr 07 17:38:12 2016 +0100
@@ -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__
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/source/nordic_sdk/components/libraries/fstorage/fstorage_config.h	Thu Apr 07 17:38:12 2016 +0100
@@ -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 FS_CONFIG_H__
+#define FS_CONFIG_H__
+
+#include <stdint.h>
+#include "nrf.h"
+
+/**
+ * @defgroup fstorage_config FStorage configuration
+ * @ingroup fstorage
+ * @{
+ * @brief FStorage configuration.
+ */
+
+
+/**@brief Macro for max number of operations in the fs cmd queue.
+ */
+#define FS_CMD_QUEUE_SIZE   (8)
+
+
+/**@brief Macro for max number of retries for a flash command before it notifies as failed.
+ */
+#define FS_CMD_MAX_RETRIES  (3)
+
+
+/**@brief Macro for the content of a flash address that has not been written to.
+ */
+#define FS_EMPTY_MASK       (0xFFFFFFFF)
+
+
+/**@brief Macro for flash page size according to chip family
+ */
+#if defined (NRF51)
+    #define FS_PAGE_SIZE    (1024)
+#elif defined (NRF52)
+    #define FS_PAGE_SIZE    (4096)
+#else
+    #error "Device family must be defined. See nrf.h."
+#endif
+
+
+/*@brief Macro for flash page size according to chip family
+*/
+#define FS_PAGE_SIZE_WORDS  (FS_PAGE_SIZE/4)
+
+
+/**@brief Static inline function that provides last page address
+ *
+ * @note    If there is a bootloader present the bootloader address read from UICR
+ *          will act as the page beyond the end of the available flash storage
+ */
+static __INLINE uint32_t fs_flash_page_end_addr()
+{
+    uint32_t const bootloader_addr = NRF_UICR->NRFFW[0];
+    return  ((bootloader_addr != FS_EMPTY_MASK) ?
+             bootloader_addr : NRF_FICR->CODESIZE * FS_PAGE_SIZE);
+}
+
+
+/**@brief Macro for last page address
+ *
+ * @note    If there is a bootloader present the bootloader address read from UICR
+ *          will act as the page beyond the end of the available flash storage
+ */
+#define FS_PAGE_END_ADDR  fs_flash_page_end_addr()
+
+
+/**@brief Macro to describe the write
+ *
+ */
+#if defined (NRF51)
+    #define FS_MAX_WRITE_SIZE_WORDS	    (256)
+#elif defined (NRF52)
+    #define FS_MAX_WRITE_SIZE_WORDS     (1024)
+#else
+    #error "Device family must be defined. see nrf.h"
+#endif
+
+/** @} */
+
+#endif // FS_CONFIG_H__
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/source/nordic_sdk/components/libraries/fstorage/fstorage_nosd.c	Thu Apr 07 17:38:12 2016 +0100
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) Nordic Semiconductor ASA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright notice, this
+ *   list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright notice, this
+ *   list of conditions and the following disclaimer in the documentation and/or
+ *   other materials provided with the distribution.
+ *
+ *   3. Neither the name of Nordic Semiconductor ASA nor the names of other
+ *   contributors to this software may be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/source/nordic_sdk/components/libraries/util/sdk_mapped_flags.c	Thu Apr 07 17:38:12 2016 +0100
@@ -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;
+}
\ No newline at end of file