Add files from peer manager from nRF51 SDK 10.0.0

Add a subset of the files from the peer manager from nRF51 SDK 10.0.0. This
code is required to implement whitelising in the ble-nrf51822 module.
This commit is contained in:
Andres Amaya Garcia 2016-01-07 18:55:04 +00:00
parent 79118e224c
commit 4c8760c90c
10 changed files with 2513 additions and 0 deletions

View File

@ -27,6 +27,7 @@
"source/nordic_sdk/components/ble/common", "source/nordic_sdk/components/ble/common",
"source/nordic_sdk/components/ble/device_manager", "source/nordic_sdk/components/ble/device_manager",
"source/nordic_sdk/components/ble/device_manager/config", "source/nordic_sdk/components/ble/device_manager/config",
"source/nordic_sdk/components/ble/peer_manager",
"source/nordic_sdk/components/device", "source/nordic_sdk/components/device",
"source/nordic_sdk/components/drivers_nrf/ble_flash", "source/nordic_sdk/components/drivers_nrf/ble_flash",
"source/nordic_sdk/components/drivers_nrf/delay", "source/nordic_sdk/components/drivers_nrf/delay",

View File

@ -16,6 +16,13 @@
source/nordic-sdk/components/ble/common/ble_advdata.h source/nordic-sdk/components/ble/common/ble_advdata.h
source/nordic-sdk/components/ble/common/ble_advdata_parser.c source/nordic-sdk/components/ble/common/ble_advdata_parser.c
source/nordic-sdk/components/ble/common/ble_advdata_parser.h source/nordic-sdk/components/ble/common/ble_advdata_parser.h
source/nordic-sdk/components/ble/peer_manager/id_manager.h
source/nordic-sdk/components/ble/peer_manager/id_manager.c
source/nordic-sdk/components/ble/peer_manager/peer_manager_types.h
source/nordic-sdk/components/ble/peer_manager/ble_gatt_db.h
source/nordic-sdk/components/ble/peer_manager/ble_conn_state.h
source/nordic-sdk/components/ble/peer_manager/sdk_mapped_flags.h
source/nordic-sdk/components/ble/peer_manager/peer_database.h
# source/nordic-sdk/components/ble/common/ble_conn_params.cpp the file is called # source/nordic-sdk/components/ble/common/ble_conn_params.cpp the file is called
source/nordic-sdk/components/ble/common/ble_conn_params.c source/nordic-sdk/components/ble/common/ble_conn_params.c
source/nordic-sdk/components/ble/common/ble_conn_params.h source/nordic-sdk/components/ble/common/ble_conn_params.h

View File

@ -0,0 +1,343 @@
/*
* 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_params.h"
#include <stdlib.h>
#include "nordic_common.h"
#include "ble_hci.h"
#include "app_timer.h"
#include "ble_srv_common.h"
#include "app_util.h"
static ble_conn_params_init_t m_conn_params_config; /**< Configuration as specified by the application. */
static ble_gap_conn_params_t m_preferred_conn_params; /**< Connection parameters preferred by the application. */
static uint8_t m_update_count; /**< Number of Connection Parameter Update messages that has currently been sent. */
static uint16_t m_conn_handle; /**< Current connection handle. */
static ble_gap_conn_params_t m_current_conn_params; /**< Connection parameters received in the most recent Connect event. */
APP_TIMER_DEF(m_conn_params_timer_id); /**< Connection parameters timer. */
static bool m_change_param = false;
static bool is_conn_params_ok(ble_gap_conn_params_t * p_conn_params)
{
// Check if interval is within the acceptable range.
// NOTE: Using max_conn_interval in the received event data because this contains
// the client's connection interval.
if (
(p_conn_params->max_conn_interval >= m_preferred_conn_params.min_conn_interval)
&&
(p_conn_params->max_conn_interval <= m_preferred_conn_params.max_conn_interval)
)
{
return true;
}
else
{
return false;
}
}
static void update_timeout_handler(void * p_context)
{
UNUSED_PARAMETER(p_context);
if (m_conn_handle != BLE_CONN_HANDLE_INVALID)
{
// Check if we have reached the maximum number of attempts
m_update_count++;
if (m_update_count <= m_conn_params_config.max_conn_params_update_count)
{
uint32_t err_code;
// Parameters are not ok, send connection parameters update request.
err_code = sd_ble_gap_conn_param_update(m_conn_handle, &m_preferred_conn_params);
if ((err_code != NRF_SUCCESS) && (m_conn_params_config.error_handler != NULL))
{
m_conn_params_config.error_handler(err_code);
}
}
else
{
m_update_count = 0;
// Negotiation failed, disconnect automatically if this has been configured
if (m_conn_params_config.disconnect_on_fail)
{
uint32_t err_code;
err_code = sd_ble_gap_disconnect(m_conn_handle, BLE_HCI_CONN_INTERVAL_UNACCEPTABLE);
if ((err_code != NRF_SUCCESS) && (m_conn_params_config.error_handler != NULL))
{
m_conn_params_config.error_handler(err_code);
}
}
// Notify the application that the procedure has failed
if (m_conn_params_config.evt_handler != NULL)
{
ble_conn_params_evt_t evt;
evt.evt_type = BLE_CONN_PARAMS_EVT_FAILED;
m_conn_params_config.evt_handler(&evt);
}
}
}
}
uint32_t ble_conn_params_init(const ble_conn_params_init_t * p_init)
{
uint32_t err_code;
m_conn_params_config = *p_init;
m_change_param = false;
if (p_init->p_conn_params != NULL)
{
m_preferred_conn_params = *p_init->p_conn_params;
// Set the connection params in stack
err_code = sd_ble_gap_ppcp_set(&m_preferred_conn_params);
if (err_code != NRF_SUCCESS)
{
return err_code;
}
}
else
{
// Fetch the connection params from stack
err_code = sd_ble_gap_ppcp_get(&m_preferred_conn_params);
if (err_code != NRF_SUCCESS)
{
return err_code;
}
}
m_conn_handle = BLE_CONN_HANDLE_INVALID;
m_update_count = 0;
return app_timer_create(&m_conn_params_timer_id,
APP_TIMER_MODE_SINGLE_SHOT,
update_timeout_handler);
}
uint32_t ble_conn_params_stop(void)
{
return app_timer_stop(m_conn_params_timer_id);
}
static void conn_params_negotiation(void)
{
// Start negotiation if the received connection parameters are not acceptable
if (!is_conn_params_ok(&m_current_conn_params))
{
uint32_t err_code;
uint32_t timeout_ticks;
if (m_change_param)
{
// Notify the application that the procedure has failed
if (m_conn_params_config.evt_handler != NULL)
{
ble_conn_params_evt_t evt;
evt.evt_type = BLE_CONN_PARAMS_EVT_FAILED;
m_conn_params_config.evt_handler(&evt);
}
}
else
{
if (m_update_count == 0)
{
// First connection parameter update
timeout_ticks = m_conn_params_config.first_conn_params_update_delay;
}
else
{
timeout_ticks = m_conn_params_config.next_conn_params_update_delay;
}
err_code = app_timer_start(m_conn_params_timer_id, timeout_ticks, NULL);
if ((err_code != NRF_SUCCESS) && (m_conn_params_config.error_handler != NULL))
{
m_conn_params_config.error_handler(err_code);
}
}
}
else
{
// Notify the application that the procedure has succeded
if (m_conn_params_config.evt_handler != NULL)
{
ble_conn_params_evt_t evt;
evt.evt_type = BLE_CONN_PARAMS_EVT_SUCCEEDED;
m_conn_params_config.evt_handler(&evt);
}
}
m_change_param = false;
}
static void on_connect(ble_evt_t * p_ble_evt)
{
// Save connection parameters
m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
m_current_conn_params = p_ble_evt->evt.gap_evt.params.connected.conn_params;
m_update_count = 0; // Connection parameter negotiation should re-start every connection
// Check if we shall handle negotiation on connect
if (m_conn_params_config.start_on_notify_cccd_handle == BLE_GATT_HANDLE_INVALID)
{
conn_params_negotiation();
}
}
static void on_disconnect(ble_evt_t * p_ble_evt)
{
uint32_t err_code;
m_conn_handle = BLE_CONN_HANDLE_INVALID;
// Stop timer if running
m_update_count = 0; // Connection parameters updates should happen during every connection
err_code = app_timer_stop(m_conn_params_timer_id);
if ((err_code != NRF_SUCCESS) && (m_conn_params_config.error_handler != NULL))
{
m_conn_params_config.error_handler(err_code);
}
}
static void on_write(ble_evt_t * p_ble_evt)
{
ble_gatts_evt_write_t * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;
// Check if this the correct CCCD
if (
(p_evt_write->handle == m_conn_params_config.start_on_notify_cccd_handle)
&&
(p_evt_write->len == 2)
)
{
// Check if this is a 'start notification'
if (ble_srv_is_notification_enabled(p_evt_write->data))
{
// Do connection parameter negotiation if necessary
conn_params_negotiation();
}
else
{
uint32_t err_code;
// Stop timer if running
err_code = app_timer_stop(m_conn_params_timer_id);
if ((err_code != NRF_SUCCESS) && (m_conn_params_config.error_handler != NULL))
{
m_conn_params_config.error_handler(err_code);
}
}
}
}
static void on_conn_params_update(ble_evt_t * p_ble_evt)
{
// Copy the parameters
m_current_conn_params = p_ble_evt->evt.gap_evt.params.conn_param_update.conn_params;
conn_params_negotiation();
}
void ble_conn_params_on_ble_evt(ble_evt_t * p_ble_evt)
{
switch (p_ble_evt->header.evt_id)
{
case BLE_GAP_EVT_CONNECTED:
on_connect(p_ble_evt);
break;
case BLE_GAP_EVT_DISCONNECTED:
on_disconnect(p_ble_evt);
break;
case BLE_GATTS_EVT_WRITE:
on_write(p_ble_evt);
break;
case BLE_GAP_EVT_CONN_PARAM_UPDATE:
on_conn_params_update(p_ble_evt);
break;
default:
// No implementation needed.
break;
}
}
uint32_t ble_conn_params_change_conn_params(ble_gap_conn_params_t * new_params)
{
uint32_t err_code;
m_preferred_conn_params = *new_params;
// Set the connection params in stack
err_code = sd_ble_gap_ppcp_set(&m_preferred_conn_params);
if (err_code == NRF_SUCCESS)
{
if (!is_conn_params_ok(&m_current_conn_params))
{
m_change_param = true;
err_code = sd_ble_gap_conn_param_update(m_conn_handle, &m_preferred_conn_params);
m_update_count = 1;
}
else
{
// Notify the application that the procedure has succeded
if (m_conn_params_config.evt_handler != NULL)
{
ble_conn_params_evt_t evt;
evt.evt_type = BLE_CONN_PARAMS_EVT_SUCCEEDED;
m_conn_params_config.evt_handler(&evt);
}
err_code = NRF_SUCCESS;
}
}
return err_code;
}

View File

@ -0,0 +1,287 @@
/*
* 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.
*
*/
/**
* @file
*
* @defgroup ble_conn_state Connection state
* @ingroup ble_sdk_lib
* @{
* @brief Module for storing data on BLE connections.
*
* @details This module stores certain states for each connection, which can be queried by
* connection handle. The module uses BLE events to keep the states updated.
*
* In addition to the preprogrammed states, this module can also keep track of a number of
* binary user states, or <i>user flags</i>. These are reset to 0 for new connections, but
* otherwise not touched by this module.
*
* This module uses the @ref sdk_mapped_flags module, with connection handles as keys and
* the connection states as flags.
*
* @note A connection handle is not immediately invalidated when it is disconnected. Certain states,
* such as the role, can still be queried until the next time a new connection is established
* to any device.
*
* To function properly, this module must be provided with BLE events from the SoftDevice
* through the @ref ble_conn_state_on_ble_evt() function. This module should be the first
* to receive BLE events if they are dispatched to multiple modules.
*/
#ifndef BLE_CONN_STATE_H__
#define BLE_CONN_STATE_H__
#include <stdbool.h>
#include <stdint.h>
#include "ble.h"
#include "sdk_mapped_flags.h"
/**@brief Connection handle statuses.
*/
typedef enum
{
BLE_CONN_STATUS_INVALID, /**< The connection handle is invalid. */
BLE_CONN_STATUS_DISCONNECTED, /**< The connection handle refers to a connection that has been disconnected, but not yet invalidated. */
BLE_CONN_STATUS_CONNECTED, /**< The connection handle refers to an active connection. */
} ble_conn_state_status_t;
#define BLE_CONN_STATE_N_USER_FLAGS 16 /**< The number of available user flags. */
/**@brief One ID for each user flag collection.
*
* @details These IDs are used to identify user flag collections in the API calls.
*/
typedef enum
{
BLE_CONN_STATE_USER_FLAG0 = 0,
BLE_CONN_STATE_USER_FLAG1,
BLE_CONN_STATE_USER_FLAG2,
BLE_CONN_STATE_USER_FLAG3,
BLE_CONN_STATE_USER_FLAG4,
BLE_CONN_STATE_USER_FLAG5,
BLE_CONN_STATE_USER_FLAG6,
BLE_CONN_STATE_USER_FLAG7,
BLE_CONN_STATE_USER_FLAG8,
BLE_CONN_STATE_USER_FLAG9,
BLE_CONN_STATE_USER_FLAG10,
BLE_CONN_STATE_USER_FLAG11,
BLE_CONN_STATE_USER_FLAG12,
BLE_CONN_STATE_USER_FLAG13,
BLE_CONN_STATE_USER_FLAG14,
BLE_CONN_STATE_USER_FLAG15,
BLE_CONN_STATE_USER_FLAG_INVALID,
} ble_conn_state_user_flag_id_t;
/**
* @defgroup ble_conn_state_functions BLE connection state functions
* @{
*/
/**@brief Function for initializing or resetting the module.
*
* @details This function sets all states to their default, removing all records of connection handles.
*/
void ble_conn_state_init(void);
/**@brief Function for providing BLE SoftDevice events to the connection state module.
*
* @param[in] p_ble_evt The SoftDevice event.
*/
void ble_conn_state_on_ble_evt(ble_evt_t * p_ble_evt);
/**@brief Function for querying whether a connection handle represents a valid connection.
*
* @details A connection might be valid and have a BLE_CONN_STATUS_DISCONNECTED status.
* Those connections are invalidated after a new connection occurs.
*
* @param[in] conn_handle Handle of the connection.
*
* @retval true If conn_handle represents a valid connection, thus a connection for which
we have a record.
* @retval false If conn_handle is @ref BLE_GAP_ROLE_INVALID, or if it has never been recorded.
*/
bool ble_conn_state_valid(uint16_t conn_handle);
/**@brief Function for querying the role of the local device in a connection.
*
* @param[in] conn_handle Handle of the connection to get the role for.
*
* @return The role of the local device in the connection (see @ref BLE_GAP_ROLES).
* If conn_handle is not valid, the function returns BLE_GAP_ROLE_INVALID.
*/
uint8_t ble_conn_state_role(uint16_t conn_handle);
/**@brief Function for querying the status of a connection.
*
* @param[in] conn_handle Handle of the connection.
*
* @return The status of the connection.
* If conn_handle is not valid, the function returns BLE_CONN_STATE_INVALID.
*/
ble_conn_state_status_t ble_conn_state_status(uint16_t conn_handle);
/**@brief Function for querying whether a connection is encrypted.
*
* @param[in] conn_handle Handle of connection to get the encryption state for.
*
* @retval true If the connection is encrypted.
* @retval false If the connection is not encrypted or conn_handle is invalid.
*/
bool ble_conn_state_encrypted(uint16_t conn_handle);
/**@brief Function for querying whether a connection encryption is protected from Man in the Middle
* attacks.
*
* @param[in] conn_handle Handle of connection to get the MITM state for.
*
* @retval true If the connection is encrypted with MITM protection.
* @retval false If the connection is not encrypted, or encryption is not MITM protected, or
* conn_handle is invalid.
*/
bool ble_conn_state_mitm_protected(uint16_t conn_handle);
/**@brief Function for querying the total number of connections.
*
* @return The total number of valid connections for which the module has a record.
*/
uint32_t ble_conn_state_n_connections(void);
/**@brief Function for querying the total number of connections in which the role of the local
* device is @ref BLE_GAP_ROLE_CENTRAL.
*
* @return The number of connections in which the role of the local device is
* @ref BLE_GAP_ROLE_CENTRAL.
*/
uint32_t ble_conn_state_n_centrals(void);
/**@brief Function for querying the total number of connections in which the role of the local
* device is @ref BLE_GAP_ROLE_PERIPH.
*
* @return The number of connections in which the role of the local device is
* @ref BLE_GAP_ROLE_PERIPH.
*/
uint32_t ble_conn_state_n_peripherals(void);
/**@brief Function for obtaining a list of all connection handles for which the module has a record.
*
* @details This function takes into account connections whose state is BLE_CONN_STATUS_DISCONNECTED.
*
* @return A list of all valid connection handles for which the module has a record.
*/
sdk_mapped_flags_key_list_t ble_conn_state_conn_handles(void);
/**@brief Function for obtaining a list of connection handles in which the role of the local
* device is @ref BLE_GAP_ROLE_CENTRAL.
*
* @details This function takes into account connections whose state is BLE_CONN_STATUS_DISCONNECTED.
*
* @return A list of all valid connection handles for which the module has a record and in which
* the role of local device is @ref BLE_GAP_ROLE_CENTRAL.
*/
sdk_mapped_flags_key_list_t ble_conn_state_central_handles(void);
/**@brief Function for obtaining the handle for the connection in which the role of the local device
* is @ref BLE_GAP_ROLE_PERIPH.
*
* @details This function takes into account connections whose state is BLE_CONN_STATUS_DISCONNECTED.
*
* @return A list of all valid connection handles for which the module has a record and in which
* the role of local device is @ref BLE_GAP_ROLE_PERIPH.
*/
sdk_mapped_flags_key_list_t ble_conn_state_periph_handles(void);
/**@brief Function for obtaining exclusive access to one of the user flag collections.
*
* @details The acquired collection contains one flag for each connection. These flags can be set
* and read individually for each connection.
*
* The state of user flags will not be modified by the connection state module, except to
* set it to 0 for a connection when that connection is invalidated.
*
* @return The ID of the acquired flag, or BLE_CONN_STATE_USER_FLAG_INVALID if none are available.
*/
ble_conn_state_user_flag_id_t ble_conn_state_user_flag_acquire(void);
/**@brief Function for reading the value of a user flag.
*
* @param[in] conn_handle Handle of connection to get the flag state for.
* @param[in] flag_id Which flag to get the state for.
*
* @return The state of the flag. If conn_handle is invalid, the function returns false.
*/
bool ble_conn_state_user_flag_get(uint16_t conn_handle, ble_conn_state_user_flag_id_t flag_id);
/**@brief Function for setting the value of a user flag.
*
* @param[in] conn_handle Handle of connection to set the flag state for.
* @param[in] flag_id Which flag to set the state for.
* @param[in] value Value to set the flag state to.
*/
void ble_conn_state_user_flag_set(uint16_t conn_handle,
ble_conn_state_user_flag_id_t flag_id,
bool value);
/**@brief Function for getting the state of a user flag for all connection handles.
*
* @details The returned collection can be used with the @ref sdk_mapped_flags API. The returned
* collection is a copy, so modifying it has no effect on the conn_state module.
*
* @param[in] flag_id Which flag to get states for.
*
* @return The collection of flag states. The collection is always all zeros when the flag_id is
* unregistered.
*/
sdk_mapped_flags_t ble_conn_state_user_flag_collection(ble_conn_state_user_flag_id_t flag_id);
/** @} */
/** @} */
#endif /* BLE_CONN_STATE_H__ */

View File

@ -0,0 +1,62 @@
/*
* 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 BLE_GATT_DB_H__
#define BLE_GATT_DB_H__
#include "stdint.h"
#include "ble.h"
#include "ble_gattc.h"
#define BLE_GATT_DB_MAX_CHARS 4 /**< The maximum number of characteristics present in a service record. */
/**@brief Structure for holding the characteristic and the handle of its CCCD present on a server.
*/
typedef struct
{
ble_gattc_char_t characteristic; /**< Structure containing information about the characteristic. */
uint16_t cccd_handle; /**< CCCD Handle value for this characteristic. This will be set to BLE_GATT_HANDLE_INVALID if a CCCD is not present at the server. */
} ble_gatt_db_char_t;
/**@brief Structure for holding information about the service and the characteristics present on a
* server.
*/
typedef struct
{
ble_uuid_t srv_uuid; /**< UUID of the service. */
uint8_t char_count; /**< Number of characteristics present in the service. */
ble_gattc_handle_range_t handle_range; /**< Service Handle Range. */
ble_gatt_db_char_t charateristics[BLE_GATT_DB_MAX_CHARS]; /**< Array of information related to the characteristics present in the service. This list can extend further than one. */
} ble_gatt_db_srv_t;
#endif /* BLE_GATT_DB_H__ */

View File

@ -0,0 +1,751 @@
/*
* 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 "id_manager.h"
#include <string.h>
#include "nrf_soc.h"
#include "ble_gap.h"
#include "ble_conn_state.h"
#include "peer_manager_types.h"
#include "peer_database.h"
#include "nordic_common.h"
#define IM_MAX_CONN_HANDLES 8
#define IM_NO_INVALID_CONN_HANDLES 0xFF
#define MAX_REGISTRANTS 3
#define WHITELIST_MAX_COUNT MAX(BLE_GAP_WHITELIST_ADDR_MAX_COUNT, \
BLE_GAP_WHITELIST_IRK_MAX_COUNT)
#define IM_ADDR_CLEARTEXT_LENGTH 3
#define IM_ADDR_CIPHERTEXT_LENGTH 3
#define MODULE_INITIALIZED (m_im.n_registrants > 0)
#define VERIFY_MODULE_INITIALIZED() \
do \
{ \
if (!MODULE_INITIALIZED) \
{ \
return NRF_ERROR_INVALID_STATE; \
} \
} while(0)
#define VERIFY_PARAM_NOT_NULL(param) \
do \
{ \
if (param == NULL) \
{ \
return NRF_ERROR_NULL; \
} \
} while(0)
typedef struct
{
pm_peer_id_t peer_id;
uint16_t conn_handle;
ble_gap_addr_t peer_address;
} im_connection_t;
typedef struct
{
im_evt_handler_t evt_handlers[MAX_REGISTRANTS];
uint8_t n_registrants;
im_connection_t connections[8];
pm_peer_id_t whitelist_peer_ids[BLE_GAP_WHITELIST_IRK_MAX_COUNT];
ble_gap_irk_t whitelist_irks[BLE_GAP_WHITELIST_IRK_MAX_COUNT];
ble_gap_addr_t whitelist_addrs[BLE_GAP_WHITELIST_ADDR_MAX_COUNT];
uint8_t n_whitelist_peer_ids;
ble_conn_state_user_flag_id_t conn_state_user_flag_id;
} im_t;
static im_t m_im = {.n_registrants = 0};
static void internal_state_reset()
{
memset(&m_im, 0, sizeof(im_t));
m_im.n_registrants = 0;
m_im.n_whitelist_peer_ids = 0;
m_im.conn_state_user_flag_id = BLE_CONN_STATE_USER_FLAG_INVALID;
for (uint32_t i = 0; i < IM_MAX_CONN_HANDLES; i++)
{
m_im.connections[i].conn_handle = BLE_CONN_HANDLE_INVALID;
}
}
/**@brief Function for sending an event to all registered event handlers.
*
* @param[in] p_event The event to distribute.
*/
static void evt_send(im_evt_t * p_event)
{
for (uint32_t i = 0; i < m_im.n_registrants; i++)
{
m_im.evt_handlers[i](p_event);
}
}
/**@brief Function finding a free position in m_im.connections.
*
* @detail All connection handles in the m_im.connections array are checked against the connection
* state module. The index of the first one that is not a connection handle for a current
* connection is returned. This position in the array can safely be used for a new connection.
*
* @return Either the index of a free position in the array or IM_NO_INVALID_CONN_HANDLES if no free
position exists.
*/
uint8_t get_free_connection()
{
for (uint32_t i = 0; i < IM_MAX_CONN_HANDLES; i++)
{
// Query the connection state module to check if the connection handle does not belong to a
// valid connection.
if (!ble_conn_state_user_flag_get(m_im.connections[i].conn_handle, m_im.conn_state_user_flag_id))
{
return i;
}
}
// If all connection handles belong to a valid connection, return IM_NO_INVALID_CONN_HANDLES.
return IM_NO_INVALID_CONN_HANDLES;
}
/**@brief Function finding a particular connection handle m_im.connections.
*
* @param[in] conn_handle The handle to find.
*
* @return Either the index of the conn_handle in the array or IM_NO_INVALID_CONN_HANDLES if the
* handle was not found.
*/
uint8_t get_connection_by_conn_handle(uint16_t conn_handle)
{
if (ble_conn_state_user_flag_get(conn_handle, m_im.conn_state_user_flag_id))
{
for (uint32_t i = 0; i < IM_MAX_CONN_HANDLES; i++)
{
if (m_im.connections[i].conn_handle == conn_handle)
{
return i;
}
}
}
// If all connection handles belong to a valid connection, return IM_NO_INVALID_CONN_HANDLES.
return IM_NO_INVALID_CONN_HANDLES;
}
/**@brief Function for registering a new connection instance.
*
* @param[in] conn_handle The handle of the new connection.
* @param[in] p_ble_addr The address used to connect.
*
* @return Either the index of the new connection in the array or IM_NO_INVALID_CONN_HANDLES if no
* free position exists.
*/
uint8_t new_connection(uint16_t conn_handle, ble_gap_addr_t * p_ble_addr)
{
uint8_t conn_index = IM_NO_INVALID_CONN_HANDLES;
if ((p_ble_addr != NULL) && (conn_handle != BLE_CONN_HANDLE_INVALID))
{
ble_conn_state_user_flag_set(conn_handle, m_im.conn_state_user_flag_id, true);
conn_index = get_connection_by_conn_handle(conn_handle);
if (conn_index == IM_NO_INVALID_CONN_HANDLES)
{
conn_index = get_free_connection();
}
if (conn_index != IM_NO_INVALID_CONN_HANDLES)
{
m_im.connections[conn_index].conn_handle = conn_handle;
m_im.connections[conn_index].peer_id = PM_PEER_ID_INVALID;
m_im.connections[conn_index].peer_address = *p_ble_addr;
}
}
return conn_index;
}
/**@brief Function checking the validity of an IRK
*
* @detail An all-zero IRK is not valid. This function will check if a given IRK is valid.
*
* @param[in] irk The IRK for which the validity is going to be checked.
*
* @retval true The IRK is valid.
* @retval false The IRK is invalid.
*/
bool is_valid_irk(ble_gap_irk_t const * irk)
{
for (uint32_t i = 0; i < BLE_GAP_SEC_KEY_LEN; i++)
{
if (irk->irk[i] != 0)
{
return true;
}
}
return false;
}
/**@brief Function for comparing two addresses to determine if they are identical
*
* @note The address type need to be identical, as well as every bit in the address itself.
*
* @param[in] p_addr1 The first address to be compared.
* @param[in] p_addr2 The second address to be compared.
*
* @retval true The addresses are identical.
* @retval false The addresses are not identical.
*/
bool addr_compare(ble_gap_addr_t const * p_addr1, ble_gap_addr_t const * p_addr2)
{
if ((p_addr1 == NULL) || (p_addr2 == NULL))
{
return false;
}
// Check that the addr type is identical, return false if it is not
if (p_addr1->addr_type != p_addr2->addr_type)
{
return false;
}
// Check if the addr bytes are is identical
return (memcmp(p_addr1->addr, p_addr2->addr, BLE_GAP_ADDR_LEN) == 0);
}
void im_ble_evt_handler(ble_evt_t * ble_evt)
{
ret_code_t err_code;
switch (ble_evt->header.evt_id)
{
case BLE_GAP_EVT_CONNECTED:
{
pm_peer_id_t bonded_matching_peer_id = PM_PEER_ID_INVALID;
if (ble_evt->evt.gap_evt.params.connected.irk_match == 1)
{
// The peer was matched using a whitelist.
bonded_matching_peer_id
= m_im.whitelist_peer_ids[ble_evt->evt.gap_evt.params.connected.irk_match_idx];
}
else if ( ble_evt->evt.gap_evt.params.connected.peer_addr.addr_type
!= BLE_GAP_ADDR_TYPE_RANDOM_PRIVATE_NON_RESOLVABLE)
{
/* Search the database for bonding data matching the one that triggered the event.
* Public and static addresses can be matched on address alone, while resolvable
* random addresses can be resolved agains known IRKs. Non-resolvable random addresses
* are never matching because they are not longterm form of identification.
*/
pm_peer_id_t compared_peer_id = pdb_next_peer_id_get(PM_PEER_ID_INVALID);
while ( (compared_peer_id != PM_PEER_ID_INVALID)
&& (bonded_matching_peer_id == PM_PEER_ID_INVALID))
{
pm_peer_data_flash_t compared_data;
switch (ble_evt->evt.gap_evt.params.connected.peer_addr.addr_type)
{
case BLE_GAP_ADDR_TYPE_PUBLIC:
/* fall-through */
case BLE_GAP_ADDR_TYPE_RANDOM_STATIC:
err_code = pdb_read_buf_get(compared_peer_id,
PM_PEER_DATA_ID_BONDING,
&compared_data,
NULL);
if ((err_code == NRF_SUCCESS) &&
addr_compare(&ble_evt->evt.gap_evt.params.connected.peer_addr,
&compared_data.data.p_bonding_data->peer_id.id_addr_info)
)
{
bonded_matching_peer_id = compared_peer_id;
}
break;
case BLE_GAP_ADDR_TYPE_RANDOM_PRIVATE_RESOLVABLE:
err_code = pdb_read_buf_get(compared_peer_id,
PM_PEER_DATA_ID_BONDING,
&compared_data,
NULL);
if (err_code == NRF_SUCCESS &&
im_address_resolve(&ble_evt->evt.gap_evt.params.connected.peer_addr,
&compared_data.data.p_bonding_data->peer_id.id_info)
)
{
bonded_matching_peer_id = compared_peer_id;
}
break;
case BLE_GAP_ADDR_TYPE_RANDOM_PRIVATE_NON_RESOLVABLE:
// Should not happen.
break;
default:
break;
}
compared_peer_id = pdb_next_peer_id_get(compared_peer_id);
}
}
new_connection(ble_evt->evt.gap_evt.conn_handle, &ble_evt->evt.gap_evt.params.connected.peer_addr);
if (bonded_matching_peer_id != PM_PEER_ID_INVALID)
{
im_new_peer_id(ble_evt->evt.gap_evt.conn_handle, bonded_matching_peer_id);
// Send a bonded peer event
im_evt_t im_evt;
im_evt.conn_handle = ble_evt->evt.gap_evt.conn_handle;
im_evt.evt_id = IM_EVT_BONDED_PEER_CONNECTED;
evt_send(&im_evt);
}
}
}
}
/**@brief Function to compare two sets of bonding data to check if they belong to the same device.
* @note Invalid irks will never match even though they are identical.
*
* @param[in] p_bonding_data1 First bonding data for comparison
* @param[in] p_bonding_data2 Second bonding data for comparison
*
* @return True if the input matches, false if it does not.
*/
bool is_duplicate_bonding_data(pm_peer_data_bonding_t const * p_bonding_data1,
pm_peer_data_bonding_t const * p_bonding_data2)
{
bool valid_irk = is_valid_irk(&p_bonding_data1->peer_id.id_info);
bool duplicate_irk = valid_irk &&
(memcmp(p_bonding_data1->peer_id.id_info.irk,
p_bonding_data2->peer_id.id_info.irk,
BLE_GAP_SEC_KEY_LEN) == 0
);
bool duplicate_addr = addr_compare(&p_bonding_data1->peer_id.id_addr_info,
&p_bonding_data2->peer_id.id_addr_info
);
return duplicate_irk || duplicate_addr;
}
/**@brief Event handler for events from the peer_database module.
*
* @param[in] p_event The event that has happend with peer id and flags.
*/
static void pdb_evt_handler(pdb_evt_t const * p_event)
{
ret_code_t err_code;
if ((p_event != NULL) && (p_event->evt_id == PDB_EVT_WRITE_BUF_STORED))
{
// If new data about peer id has been stored it is compared to other peers peer ids in
// search of duplicates.
if (p_event->data_id == PM_PEER_DATA_ID_BONDING)
{
pm_peer_data_flash_t written_data;
err_code = pdb_read_buf_get(p_event->peer_id, PM_PEER_DATA_ID_BONDING, &written_data, NULL);
if (err_code == NRF_SUCCESS)
{
pm_peer_id_t compared_peer_id = pdb_next_peer_id_get(PM_PEER_ID_INVALID);
while (compared_peer_id != PM_PEER_ID_INVALID)
{
pm_peer_data_flash_t compared_data;
err_code = pdb_read_buf_get(compared_peer_id,
PM_PEER_DATA_ID_BONDING,
&compared_data,
NULL);
if ( err_code == NRF_SUCCESS &&
p_event->peer_id != compared_peer_id &&
is_duplicate_bonding_data(written_data.data.p_bonding_data,
compared_data.data.p_bonding_data)
)
{
im_evt_t im_evt;
im_evt.conn_handle = im_conn_handle_get(p_event->peer_id);
im_evt.evt_id = IM_EVT_DUPLICATE_ID;
im_evt.params.duplicate_id.peer_id_1 = p_event->peer_id;
im_evt.params.duplicate_id.peer_id_2 = compared_peer_id;
evt_send(&im_evt);
}
compared_peer_id = pdb_next_peer_id_get(compared_peer_id);
}
}
}
}
}
ret_code_t im_register(im_evt_handler_t evt_handler)
{
VERIFY_PARAM_NOT_NULL(evt_handler);
ret_code_t err_code = NRF_SUCCESS;
if (!MODULE_INITIALIZED)
{
internal_state_reset();
m_im.conn_state_user_flag_id = ble_conn_state_user_flag_acquire();
if (m_im.conn_state_user_flag_id == BLE_CONN_STATE_USER_FLAG_INVALID)
{
err_code = NRF_ERROR_NO_MEM;
}
else
{
err_code = pdb_register(pdb_evt_handler);
}
}
if (err_code == NRF_SUCCESS)
{
if ((m_im.n_registrants < MAX_REGISTRANTS))
{
m_im.evt_handlers[m_im.n_registrants++] = evt_handler;
}
else
{
err_code = NRF_ERROR_NO_MEM;
}
}
return err_code;
}
pm_peer_id_t im_peer_id_get_by_conn_handle(uint16_t conn_handle)
{
uint8_t conn_index = get_connection_by_conn_handle(conn_handle);
if (MODULE_INITIALIZED && (conn_index != IM_NO_INVALID_CONN_HANDLES))
{
return m_im.connections[conn_index].peer_id;
}
return PM_PEER_ID_INVALID;
}
ret_code_t im_ble_addr_get(uint16_t conn_handle, ble_gap_addr_t * p_ble_addr)
{
VERIFY_MODULE_INITIALIZED();
VERIFY_PARAM_NOT_NULL(p_ble_addr);
uint8_t conn_index = get_connection_by_conn_handle(conn_handle);
if (conn_index != IM_NO_INVALID_CONN_HANDLES)
{
*p_ble_addr = m_im.connections[conn_index].peer_address;
return NRF_SUCCESS;
}
return NRF_ERROR_NOT_FOUND;
}
/**@brief Function for comparing two master ids
* @note Two invalid master IDs will not match.
*
* @param[in] p_master_id1 First master id for comparison
* @param[in] p_master_id2 Second master id for comparison
*
* @return True if the input matches, false if it does not.
*/
bool master_id_compare(ble_gap_master_id_t const * p_master_id1,
ble_gap_master_id_t const * p_master_id2)
{
if(!im_master_id_is_valid(p_master_id1))
{
return false;
}
if (p_master_id1->ediv != p_master_id2->ediv)
{
return false;
}
return (memcmp(p_master_id1->rand, p_master_id2->rand, BLE_GAP_SEC_RAND_LEN) == 0);
}
pm_peer_id_t im_peer_id_get_by_master_id(ble_gap_master_id_t * p_master_id)
{
ret_code_t err_code;
// For each stored peer, check if the master_id match p_master_id
pm_peer_id_t compared_peer_id = pdb_next_peer_id_get(PM_PEER_ID_INVALID);
while (compared_peer_id != PM_PEER_ID_INVALID)
{
pm_peer_data_flash_t compared_data;
ble_gap_master_id_t const * p_compared_master_id;
err_code = pdb_read_buf_get(compared_peer_id, PM_PEER_DATA_ID_BONDING, &compared_data, NULL);
if (err_code == NRF_SUCCESS)
{
p_compared_master_id = &compared_data.data.p_bonding_data->own_ltk.master_id;
if (compared_data.data.p_bonding_data->own_role == BLE_GAP_ROLE_CENTRAL)
{
p_compared_master_id = &compared_data.data.p_bonding_data->peer_ltk.master_id;
}
if (master_id_compare(p_master_id, p_compared_master_id))
{
// If a matching master_id is found return the peer_id
return compared_peer_id;
}
}
compared_peer_id = pdb_next_peer_id_get(compared_peer_id);
}
// If no matching master_id is found return the PM_PEER_ID_INVALID
return PM_PEER_ID_INVALID;
}
pm_peer_id_t im_peer_id_get_by_irk_match_idx(uint8_t irk_match_idx)
{
// Verify that the requested idx is within the list
if (irk_match_idx < m_im.n_whitelist_peer_ids)
{
// Return the peer_id from the white list
return m_im.whitelist_peer_ids[irk_match_idx];
}
else
{
// Return PM_PEER_ID_INVALID to indicate that there was no peer with the requested idx
return PM_PEER_ID_INVALID;
}
}
uint16_t im_conn_handle_get(pm_peer_id_t peer_id)
{
for (uint32_t i = 0; i < IM_MAX_CONN_HANDLES; i++)
{
if (peer_id == m_im.connections[i].peer_id)
{
return m_im.connections[i].conn_handle;
}
}
return BLE_CONN_HANDLE_INVALID;
}
bool im_master_id_is_valid(ble_gap_master_id_t const * p_master_id)
{
if (p_master_id->ediv != 0)
{
return true;
}
for (uint32_t i = 0; i < BLE_GAP_SEC_RAND_LEN; i++)
{
if (p_master_id->rand[i] != 0)
{
return true;
}
}
return false;
}
void im_new_peer_id(uint16_t conn_handle, pm_peer_id_t peer_id)
{
uint8_t conn_index = get_connection_by_conn_handle(conn_handle);
if (conn_index != IM_NO_INVALID_CONN_HANDLES)
{
m_im.connections[conn_index].peer_id = peer_id;
}
}
ret_code_t im_wlist_create(pm_peer_id_t * p_peer_ids,
uint8_t n_peer_ids,
ble_gap_whitelist_t * p_whitelist)
{
VERIFY_MODULE_INITIALIZED();
VERIFY_PARAM_NOT_NULL(p_whitelist);
ret_code_t err_code;
p_whitelist->addr_count = 0;
p_whitelist->irk_count = 0;
m_im.n_whitelist_peer_ids = 0;
for (uint32_t peer_index = 0; peer_index < n_peer_ids; peer_index++)
{
bool peer_connected = false;
for (uint32_t conn_index = 0; conn_index < IM_MAX_CONN_HANDLES; conn_index++)
{
if (p_peer_ids[peer_index] == m_im.connections[conn_index].peer_id &&
ble_conn_state_user_flag_get(m_im.connections[conn_index].conn_handle, m_im.conn_state_user_flag_id)
)
{
peer_connected = true;
break;
}
}
if (!peer_connected)
{
pm_peer_data_flash_t peer_data;
err_code = pdb_read_buf_get(p_peer_ids[peer_index], PM_PEER_DATA_ID_BONDING, &peer_data, NULL);
if (err_code == NRF_ERROR_INVALID_PARAM || err_code == NRF_ERROR_NOT_FOUND)
{
return NRF_ERROR_INVALID_PARAM;
}
if (p_whitelist->pp_addrs != NULL &&
peer_data.data.p_bonding_data->peer_id.id_addr_info.addr_type
!= BLE_GAP_ADDR_TYPE_RANDOM_PRIVATE_RESOLVABLE &&
peer_data.data.p_bonding_data->peer_id.id_addr_info.addr_type
!= BLE_GAP_ADDR_TYPE_RANDOM_PRIVATE_NON_RESOLVABLE
)
{
memcpy(m_im.whitelist_addrs[peer_index].addr,
peer_data.data.p_bonding_data->peer_id.id_addr_info.addr,
BLE_GAP_ADDR_LEN
);
m_im.whitelist_addrs[peer_index].addr_type =
peer_data.data.p_bonding_data->peer_id.id_addr_info.addr_type;
p_whitelist->pp_addrs[peer_index] = &m_im.whitelist_addrs[peer_index];
p_whitelist->addr_count++;
}
if (p_whitelist->pp_irks != NULL &&
is_valid_irk(&(peer_data.data.p_bonding_data->peer_id.id_info))
)
{
memcpy(m_im.whitelist_irks[peer_index].irk,
peer_data.data.p_bonding_data->peer_id.id_info.irk,
BLE_GAP_SEC_KEY_LEN
);
p_whitelist->pp_irks[peer_index] = &m_im.whitelist_irks[peer_index];
p_whitelist->irk_count++;
m_im.whitelist_peer_ids[peer_index] = p_peer_ids[peer_index];
m_im.n_whitelist_peer_ids++;
}
}
}
return NRF_SUCCESS;
}
ret_code_t im_wlist_set(ble_gap_whitelist_t * p_whitelist)
{
pm_peer_id_t new_whitelist_peer_ids[BLE_GAP_WHITELIST_IRK_MAX_COUNT];
uint32_t n_new_whitelist_peer_ids = 0;
VERIFY_PARAM_NOT_NULL(p_whitelist);
for (uint32_t i = 0; i < BLE_GAP_WHITELIST_IRK_MAX_COUNT; i++)
{
new_whitelist_peer_ids[i] = PM_PEER_ID_INVALID;
}
pm_peer_id_t compared_peer_id = pdb_next_peer_id_get(PM_PEER_ID_INVALID);
while (compared_peer_id != PM_PEER_ID_INVALID)
{
pm_peer_data_flash_t compared_data;
pdb_read_buf_get(compared_peer_id, PM_PEER_DATA_ID_BONDING, &compared_data, NULL);
for (uint32_t i = 0; i < p_whitelist->irk_count; i++)
{
bool valid_irk = is_valid_irk(&compared_data.data.p_bonding_data->peer_id.id_info);
bool duplicate_irk = valid_irk &&
(memcmp(p_whitelist->pp_irks[i]->irk,
compared_data.data.p_bonding_data->peer_id.id_info.irk,
BLE_GAP_SEC_KEY_LEN) == 0
);
if (duplicate_irk)
{
new_whitelist_peer_ids[i] = compared_peer_id;
n_new_whitelist_peer_ids++;
}
}
compared_peer_id = pdb_next_peer_id_get(compared_peer_id);
}
if (n_new_whitelist_peer_ids != p_whitelist->irk_count)
{
return NRF_ERROR_NOT_FOUND;
}
else
{
for (uint32_t i = 0; i < n_new_whitelist_peer_ids; i++)
{
m_im.whitelist_peer_ids[i] = new_whitelist_peer_ids[i];
}
m_im.n_whitelist_peer_ids = n_new_whitelist_peer_ids;
return NRF_SUCCESS;
}
}
/**@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.
*/
void ah(uint8_t const * p_k, uint8_t const * p_r, uint8_t * p_local_hash)
{
nrf_ecb_hal_data_t ecb_hal_data;
for (uint32_t i = 0; i < SOC_ECB_KEY_LENGTH; i++)
{
ecb_hal_data.key[i] = p_k[SOC_ECB_KEY_LENGTH - 1 - i];
}
memset(ecb_hal_data.cleartext, 0, SOC_ECB_KEY_LENGTH - IM_ADDR_CLEARTEXT_LENGTH);
for (uint32_t i = 0; i < IM_ADDR_CLEARTEXT_LENGTH; i++)
{
ecb_hal_data.cleartext[SOC_ECB_KEY_LENGTH - 1 - i] = p_r[i];
}
sd_ecb_block_encrypt(&ecb_hal_data);
for (uint32_t i = 0; i < IM_ADDR_CIPHERTEXT_LENGTH; i++)
{
p_local_hash[i] = ecb_hal_data.ciphertext[SOC_ECB_KEY_LENGTH - 1 - i];
}
}
bool im_address_resolve(ble_gap_addr_t const * p_addr, ble_gap_irk_t const * p_irk)
{
if (p_addr->addr_type != BLE_GAP_ADDR_TYPE_RANDOM_PRIVATE_RESOLVABLE)
{
return false;
}
uint8_t hash[IM_ADDR_CIPHERTEXT_LENGTH];
uint8_t local_hash[IM_ADDR_CIPHERTEXT_LENGTH];
uint8_t prand[IM_ADDR_CLEARTEXT_LENGTH];
memcpy(hash, p_addr->addr, IM_ADDR_CIPHERTEXT_LENGTH);
memcpy(prand, &p_addr->addr[IM_ADDR_CIPHERTEXT_LENGTH], IM_ADDR_CLEARTEXT_LENGTH);
ah(p_irk->irk, prand, local_hash);
return (memcmp(hash, local_hash, IM_ADDR_CIPHERTEXT_LENGTH) == 0);
}

View File

@ -0,0 +1,234 @@
/*
* 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_MANAGER_H__
#define PEER_ID_MANAGER_H__
#include "stdint.h"
#include "sdk_errors.h"
#include "ble.h"
#include "ble_gap.h"
#include "peer_manager_types.h"
/**
* @defgroup id_manager ID Manager
* @ingroup peer_manager
* @{
* @brief An internal module of @ref peer_manager. A module for keeping track of peer identities
* (IRK and peer address).
*/
/**@brief Events that can come from the ID Manager module.
*/
typedef enum
{
IM_EVT_DUPLICATE_ID, /**< The ID Manager module has detected that two stored peers represent the same peer. */
IM_EVT_BONDED_PEER_CONNECTED, /**< A connected peer has been identified as one of the bonded peers. This can happen immediately on connection, or at a later time. */
} im_evt_id_t;
typedef struct
{
im_evt_id_t evt_id;
uint16_t conn_handle;
union
{
struct
{
pm_peer_id_t peer_id_1;
pm_peer_id_t peer_id_2;
} duplicate_id;
} params;
} im_evt_t;
/**@brief Event handler for events from the ID Manager module.
*
* @param[in] p_event The event that has happened.
*/
typedef void (*im_evt_handler_t)(im_evt_t const * p_event);
/**@brief Function for registering for events from the ID Manager module.
*
* @note This will also initialize the module if needed.
*
* @param[in] evt_handler Callback for events from the ID Manager module.
*
* @retval NRF_SUCCESS Registration was successful.
* @retval NRF_ERROR_NO_MEM No more registrations possible.
* @retval NRF_ERROR_NULL evt_handler was NULL.
*/
ret_code_t im_register(im_evt_handler_t evt_handler);
/**@brief Function for dispatching SoftDevice events to the ID Manager module.
*
* @param[in] p_ble_evt The SoftDevice event.
*/
void im_ble_evt_handler(ble_evt_t * p_ble_evt);
/**@brief Function for getting the corresponding peer ID from a connection handle.
*
* @param[in] conn_handle The connection handle.
*
* @return The corresponding peer ID, or @ref PM_PEER_ID_INVALID if none could be resolved.
*/
pm_peer_id_t im_peer_id_get_by_conn_handle(uint16_t conn_handle);
/**@brief Function for getting the corresponding peer ID from a master ID (EDIV and rand).
*
* @param[in] p_master_id The master ID.
*
* @return The corresponding peer ID, or @ref PM_PEER_ID_INVALID if none could be resolved.
*/
pm_peer_id_t im_peer_id_get_by_master_id(ble_gap_master_id_t * p_master_id);
/**@brief Function for getting the corresponding peer ID from an IRK match index, see @ref
* ble_gap_evt_connected_t.
*
* @param[in] irk_match_idx The IRK match index.
*
* @return The corresponding peer ID, or @ref PM_PEER_ID_INVALID if none could be resolved.
*/
pm_peer_id_t im_peer_id_get_by_irk_match_idx(uint8_t irk_match_idx);
/**@brief Function for getting the corresponding connection handle from a peer ID.
*
* @param[in] peer_id The peer ID.
*
* @return The corresponding connection handle, or @ref BLE_CONN_HANDLE_INVALID if none could be
* resolved.
*/
uint16_t im_conn_handle_get(pm_peer_id_t peer_id);
/**@brief Function for getting the BLE address used by the peer when connecting.
*
* @param[in] conn_handle The connection handle.
* @param[out] p_ble_addr The BLE address used by the peer when the connection specified by
* conn_handle was established.
*
* @retval NRF_SUCCESS The address was found and copied.
* @retval NRF_ERROR_INVALID_STATE Module not initialized.
* @retval BLE_ERROR_CONN_HANDLE_INVALID conn_handle does not refer to an active connection.
* @retval NRF_ERROR_NULL p_ble_addr was NULL.
*/
ret_code_t im_ble_addr_get(uint16_t conn_handle, ble_gap_addr_t * p_ble_addr);
/**@brief Function for checking whether a master ID is valid or invalid
*
* @param[in] p_master_id The master ID.
*
* @retval true The master id is valid.
* @retval true The master id is invalid (i.e. all zeros).
*/
bool im_master_id_is_valid(ble_gap_master_id_t const * p_master_id);
/**@brief Function for reporting that a new peer ID has been allocated for a specified connection.
*
* @param[in] conn_handle The connection.
* @param[in] peer_id The new peer ID.
*/
void im_new_peer_id(uint16_t conn_handle, pm_peer_id_t peer_id);
/**
* @brief Function for informing this module of what whitelist will be used.
*
* @details This function is meant to be used when the app wants to use a custom whitelist.
* When using peer manager, this function must be used if a custom whitelist is used.
*
* @note When using a whitelist, always use the whitelist created/set by the most recent
* call to @ref im_wlist_create or to this function, whichever happened most recently.
* @note Do not call this function while scanning with another whitelist.
* @note Do not add any irks to the whitelist that are not present in the bonding data of a peer in
* the peer database.
*
* @param[in] p_whitelist The whitelist.
*
* @retval NRF_SUCCESS Whitelist successfully set.
* @retval NRF_ERROR_NULL p_whitelist was NULL.
* @retval NRF_ERROR_NOT_FOUND One or more of the whitelists irks was not found in the peer_database.
*/
ret_code_t im_wlist_set(ble_gap_whitelist_t * p_whitelist);
/**
* @brief Function for constructing a whitelist for use when advertising.
*
* @note When advertising with whitelist, always use the whitelist created/set by the most recent
* call to this function or to @ref im_wlist_set, whichever happened most recently.
* @note Do not call this function while advertising with another whitelist.
*
* @param[in] p_peer_ids The ids of the peers to be added to the whitelist.
* @param[in] n_peer_ids The number of peer ids in p_peer_ids.
* @param[in,out] p_whitelist The constructed whitelist. Note that p_adv_whitelist->pp_addrs
* must be NULL or point to an array with size @ref
* BLE_GAP_WHITELIST_ADDR_MAX_COUNT and p_adv_whitelist->pp_irks
* must be NULL or point to an array with size @ref
* BLE_GAP_WHITELIST_IRK_MAX_COUNT.
*
* @retval NRF_SUCCESS Whitelist successfully created.
* @retval NRF_ERROR_NULL p_whitelist was NULL.
*/
ret_code_t im_wlist_create(pm_peer_id_t * p_peer_ids,
uint8_t n_peer_ids,
ble_gap_whitelist_t * p_whitelist);
/**
* @brief Function for resolving a resolvable address with an identity resolution key (IRK).
*
* @details This function will use the ECB peripheral to resolve a resolvable address.
* This can be used to resolve the identity of a device distributing a random
* resolvable address based on any IRKs you have received earlier. If an address is
* resolved by an IRK, the device disributing the address must also know the IRK.
*
* @param[in] p_addr A random resolvable address.
* @param[in] p_irk An identity resolution key (IRK).
*
* @retval true The irk used matched the one used to create the address.
* @retval false The irk used did not match the one used to create the address, or an argument was
* NULL.
*/
bool im_address_resolve(ble_gap_addr_t const * p_addr, ble_gap_irk_t const * p_irk);
/** @} */
#endif /* PEER_ID_MANAGER_H__ */

View File

@ -0,0 +1,354 @@
/*
* 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_DATABASE_H__
#define PEER_DATABASE_H__
#include <stdint.h>
#include "peer_manager_types.h"
#include "sdk_errors.h"
/**
* @defgroup peer_database Peer Database
* @ingroup peer_manager
* @{
* @brief An internal module of @ref peer_manager. A module for simple management of reading and
* writing of peer data into persistent storage.
*
*/
#define PDB_WRITE_BUF_SIZE (sizeof(pm_peer_data_bonding_t))
/**@brief Events that can come from the peer_database module.
*/
typedef enum
{
PDB_EVT_WRITE_BUF_STORED, /**< A pdb_write_buf_store operation has completed successfully. */
PDB_EVT_RAW_STORED, /**< A pdb_raw_store operation has completed successfully. */
PDB_EVT_RAW_STORE_FAILED, /**< A pdb_raw_store operation has failed. */
PDB_EVT_CLEARED, /**< A pdb_clear operation has completed successfully. */
PDB_EVT_CLEAR_FAILED, /**< A pdb_clear operation has failed. */
PDB_EVT_COMPRESSED, /**< A compress procedure has completed. */
PDB_EVT_ERROR_NO_MEM, /**< An operation is blocked because the flash is full. It will be reattempted automatically after the next compress procedure. */
PDB_EVT_ERROR_UNEXPECTED, /**< An unexpected error occurred. This is a fatal error. */
} pdb_evt_id_t;
/**@brief Events that can come from the peer_database module.
*/
typedef struct
{
pdb_evt_id_t evt_id; /**< The event that has happened. */
pm_peer_id_t peer_id; /**< The id of the peer the event pertains to. */
pm_peer_data_id_t data_id; /**< The data the event pertains to. */
union
{
struct
{
pm_store_token_t store_token; /**< A token identifying the store operation this event pertains to. */
} raw_stored_evt;
struct
{
pm_store_token_t store_token; /**< A token identifying the store operation this event pertains to. */
} error_raw_store_evt;
} params;
} pdb_evt_t;
/**@brief Event handler for events from the peer_data_storage module.
*
* @param[in] p_event The event that has happened.
*/
typedef void (*pdb_evt_handler_t)(pdb_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 pdb_register(pdb_evt_handler_t evt_handler);
/**@brief Function for allocating persistent bond storage for a peer.
*
* @return The ID of the newly allocated storage.
* @retval PM_PEER_ID_INVALID If no peer ID is available.
*/
pm_peer_id_t pdb_peer_allocate(void);
/**@brief Function for freeing a peer's persistent bond storage.
*
* @note This function will call @ref pdb_write_buf_release on the data for this peer.
*
* @param[in] peer_id ID to be freed.
*
* @retval NRF_SUCCESS Peer ID was released and clear operation was initiated successfully.
* @retval NRF_ERROR_BUSY Another peer_id clear was already requested or could not be started.
* @retval NRF_ERROR_INVALID_STATE Module is not initialized.
*/
ret_code_t pdb_peer_free(pm_peer_id_t peer_id);
/**@brief Function for retrieving pointers to read-only peer data.
*
* @note Reading this pointer is not safe in the strictest sense. If a safe read is required:
* - Disable interrupts
* - Call this function. If the return code is @ref NRF_SUCCESS, the following read is safe.
* - Read memory.
* - Enable interrupts.
* @note This buffer does not need to be released. It is a pointer directly to flash.
*
* @param[in] peer_id ID of peer to retrieve data for.
* @param[in] data_id Which piece of data to get.
* @param[out] p_peer_data Pointer to immutable peer data.
* @param[out] p_token Token that can be used to lock data in flash and check data validity.
*
* @retval NRF_SUCCESS Data retrieved successfully.
* @retval NRF_ERROR_INVALID_PARAM Data ID or Peer ID was invalid or unallocated.
* @retval NRF_ERROR_NULL p_peer_data was NULL.
* @retval NRF_ERROR_NOT_FOUND This data was not found for this peer ID.
* @retval NRF_ERROR_INVALID_STATE Module is not initialized.
*/
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);
/**@brief Function for retrieving pointers to a write buffer for peer data.
*
* @details This function will provide pointers to a buffer of the data. The data buffer will not be
* written to persistent storage until @ref pdb_write_buf_store is called. The buffer is
* released by calling either @ref pdb_write_buf_release, @ref pdb_write_buf_store, or
* @ref pdb_peer_free.
*
* When the data_id refers to a variable length data type, the available size is written
* to the data, both the top-level, and any internal length fields.
*
* @note Calling this function on a peer_id/data_id pair that already has a buffer created will
* give the same buffer, not create a new one. If n_bufs was increased since last time, the
* buffer might be relocated to be able to provide additional room. In this case, the data
* will be copied. If n_bufs was increased since last time, this function might return @ref
* NRF_ERROR_BUSY. In that case, the buffer is automatically released.
*
* @param[in] peer_id ID of peer to get a write buffer for.
* @param[in] data_id Which piece of data to get.
* @param[in] n_bufs The number of contiguous buffers needed.
* @param[out] p_peer_data Pointers to mutable peer data.
*
* @retval NRF_SUCCESS Data retrieved successfully.
* @retval NRF_ERROR_INVALID_PARAM Data ID or Peer ID was invalid or unallocated, or n_bufs was 0
* or more than the total available buffers.
* @retval NRF_ERROR_NULL p_peer_data was NULL.
* @retval NRF_ERROR_BUSY Not enough buffer(s) available.
* @retval NRF_ERROR_INTERNAL Unexpected internal error.
* @retval NRF_ERROR_INVALID_STATE Module is not initialized.
*/
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);
/**@brief Function for freeing a write buffer allocated with @ref pdb_write_buf_get.
*
* @note This function will not write peer data to persistent memory. Data in released buffer will
* be lost.
*
* @note This function will undo any previous call to @ref pdb_write_buf_store_prepare for this
* piece of data.
*
* @param[in] peer_id ID of peer to release buffer for.
* @param[in] data_id Which piece of data to release buffer for.
*
* @retval NRF_SUCCESS Successfully released buffer.
* @retval NRF_ERROR_NOT_FOUND No buffer was allocated for this peer ID/data ID pair.
* @retval NRF_ERROR_INVALID_STATE Module is not initialized.
* @retval NRF_ERROR_INTERNAL Unexpected internal error.
*/
ret_code_t pdb_write_buf_release(pm_peer_id_t peer_id, pm_peer_data_id_t data_id);
/**@brief Function for reserving space in persistent storage for data in a buffer.
*
* @note This function only works for data which has a write buffer allocated. If the write buffer
* is released, this prepare is undone.
*
* @note If space has already been reserved for this data, nothing is done.
*
* @param[in] peer_id The peer whose data to reserve space for.
* @param[in] data_id The type of data to reserve space for.
*
* @retval NRF_SUCCESS Successfully reserved space in persistent storage.
* @retval NRF_ERROR_NO_MEM Not enough room in persistent storage.
* @retval NRF_ERROR_BUSY Could not process request at this time. Reattempt later.
* @retval NRF_ERROR_NOT_FOUND No buffer has been allocated for this peer ID/data ID pair.
* @retval NRF_ERROR_INVALID_PARAM Data ID or Peer ID was invalid or unallocated.
* @retval NRF_ERROR_INVALID_STATE Module is not initialized.
*/
ret_code_t pdb_write_buf_store_prepare(pm_peer_id_t peer_id, pm_peer_data_id_t data_id);
/**@brief Function for writing data into persistent storage. Writing happens asynchronously.
*
* @note This will unlock the data after it has been written.
*
* @param[in] peer_id ID of peer to store data for.
* @param[in] data_id Which piece of data to store.
*
* @retval NRF_SUCCESS Data storing was successfully started.
* @retval NRF_ERROR_NO_MEM No space available in persistent storage. Please clear some
* space, the operation will be reattempted after the next compress
* procedure. This error will not happen if
* @ref pdb_write_buf_store_prepare is called beforehand.
* @retval NRF_ERROR_INVALID_PARAM Data ID was invalid.
* @retval NRF_ERROR_NOT_FOUND No buffer has been allocated for this peer ID/data ID pair.
* @retval NRF_ERROR_INVALID_STATE Module is not initialized.
* @retval NRF_ERROR_INTERNAL Unexpected internal error.
*/
ret_code_t pdb_write_buf_store(pm_peer_id_t peer_id,
pm_peer_data_id_t data_id);
/**@brief Function for clearing data from persistent storage.
*
* @param[in] peer_id ID of peer to clear data for.
* @param[in] data_id Which piece of data to clear.
*
* @retval NRF_SUCCESS Data clear was successfully started.
* @retval NRF_ERROR_INVALID_PARAM Data ID was invalid.
* @retval NRF_ERROR_NOT_FOUND Nothing to clear for this data for this peer ID.
* @retval NRF_ERROR_BUSY Could not process request at this time. Reattempt later.
* @retval NRF_ERROR_INVALID_STATE Module is not initialized.
*/
ret_code_t pdb_clear(pm_peer_id_t peer_id, pm_peer_data_id_t data_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.
*/
uint32_t pdb_n_peers(void);
/**@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.
*/
pm_peer_id_t pdb_next_peer_id_get(pm_peer_id_t prev_peer_id);
/**@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_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 pdb_peer_data_update(pm_peer_data_const_t peer_data,
pm_store_token_t old_token,
pm_store_token_t * p_store_token);
/**@brief Function for reading data directly from persistent storage to external memory.
*
* @param[in] peer_id ID of peer to read data for.
* @param[in] data_id Which piece of data to read.
* @param[inout] p_peer_data Where to store the data. If the data to be read has variable length,
* the appropriate length field needs to reflect the available buffer
* space. On a successful read, the length field is updated to match the
* length of the read data.
*
* @retval NRF_SUCCESS Data successfully read.
* @retval NRF_ERROR_INVALID_PARAM Data ID or Peer ID was invalid or unallocated.
* @retval NRF_ERROR_NULL p_peer_data contained a NULL pointer.
* @retval NRF_ERROR_NOT_FOUND This data was not found for this peer ID.
* @retval NRF_ERROR_DATA_SIZE The provided buffer was not large enough.
* @retval NRF_ERROR_INVALID_STATE Module is not initialized.
*/
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);
/**@brief Function for writing data directly to persistent storage from external memory.
*
* @param[in] peer_id ID of peer to write data for.
* @param[in] p_peer_data Data to store.
* @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 Data successfully written.
* @retval NRF_ERROR_INVALID_PARAM Data ID or Peer ID was invalid or unallocated.
* @retval NRF_ERROR_NULL p_peer_data contained a NULL pointer.
* @retval NRF_ERROR_NO_MEM No space available in persistent storage.
* @retval NRF_ERROR_INVALID_LENGTH Data length above the maximum allowed.
* @retval NRF_ERROR_INVALID_STATE Module is not initialized.
*/
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);
/** @} */
#endif /* PEER_DATABASE_H__ */

View File

@ -0,0 +1,301 @@
/*
* 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_MANAGER_TYPES_H__
#define PEER_MANAGER_TYPES_H__
#include <stdint.h>
#include <stdbool.h>
/**
* @file peer_manager_types.h
*
* @addtogroup peer_manager
* @{
*/
#include <stdint.h>
#include <stddef.h>
#include "ble_gap.h"
#include "ble_hci.h"
#include "ble_gatt_db.h"
#include "compiler_abstraction.h"
/**@brief Handle to uniquely identify a peer for which we have persistently stored data.
*/
typedef uint16_t pm_peer_id_t;
#define PM_PEER_ID_INVALID 0xFFFF /**< Invalid value for @ref pm_peer_id_t. */
#define PM_PEER_ID_N_AVAILABLE_IDS 256 /**< The number of available peer IDs. */
#define PM_LOCAL_DB_LEN_OVERHEAD_BYTES offsetof(pm_peer_data_local_gatt_db_flash_t, p_data)
#define PM_REMOTE_DB_LEN_OVERHEAD_BYTES offsetof(pm_peer_data_remote_gatt_db_flash_t, p_data)
static __INLINE uint16_t PM_N_WORDS(uint16_t n_bytes)
{
return ((n_bytes + 3) >> 2);
}
/**@brief Errors originating from the Peer Manager module.
*/
typedef enum
{
PM_SEC_ERROR_CODE_PIN_OR_KEY_MISSING = BLE_HCI_STATUS_CODE_PIN_OR_KEY_MISSING, /**< Encryption failed because the peripheral has lost the LTK for this bond. */
PM_SEC_ERROR_CODE_MIC_FAILURE = BLE_HCI_CONN_TERMINATED_DUE_TO_MIC_FAILURE, /**< Pairing ended with disconnection because of mismatching keys. */
PM_SEC_ERROR_SMP_TIMEOUT, /**< Pairing/bonding could not start because an SMP timeout has already happened on this link. This means that no more pairing or bonding can happen on this link. To be able to pair or bond, the link must be disconnected and then reconnected. See Bluetooth specification 4.2 section 3.H.3.4 */
} pm_sec_error_code_t;
/**@brief Enumeration describing the different procedures that can lead to an encrypted link.
*/
typedef enum
{
PM_LINK_SECURED_PROCEDURE_ENCRYPTION, /**< Using an LTK shared during a previous bonding procedure to encrypt the link. */
PM_LINK_SECURED_PROCEDURE_BONDING, /**< A pairing procedure, followed by a bonding procedure. */
PM_LINK_SECURED_PROCEDURE_PAIRING, /**< A pairing procedure with no bonding. */
} pm_sec_procedure_t;
/**@brief Data associated with a bond to a peer.
*/
typedef struct
{
uint8_t own_role; /**< The role of the local device during bonding. */
ble_gap_id_key_t peer_id; /**< The peer's peer address and identity resolution key. */
ble_gap_enc_key_t peer_ltk; /**< The peer's long term encryption key. */
ble_gap_enc_key_t own_ltk; /**< Locally generated long term encryption key, distributed to the peer. */
} pm_peer_data_bonding_t;
/**@brief Function for calculating the flash size of bonding data.
*
* @return The number of words the data will take in flash.
*/
static __INLINE uint16_t PM_BONDING_DATA_N_WORDS(void)
{
return PM_N_WORDS(sizeof(pm_peer_data_bonding_t));
}
/**@brief Function for calculating the flash size of service changed pending state.
*
* @return The number of words the data will take in flash.
*/
static __INLINE uint16_t PM_SC_STATE_N_WORDS(void)
{
return PM_N_WORDS(sizeof(bool));
}
/**@brief Data on a local GATT database.
*/
typedef struct
{
uint32_t flags; /**< Flags describing the database attributes. */
uint16_t len; /**< Size of attribute array. */
uint8_t * p_data; /**< Array to hold the database attributes. */
} pm_peer_data_local_gatt_db_t;
/**@brief Data on a local GATT database, as formatted in flash.
*/
typedef struct
{
uint32_t flags;
uint16_t len;
uint16_t _padding;
uint8_t p_data[];
} pm_peer_data_local_gatt_db_flash_t;
/**@brief Function for calculating the flash size of local GATT database data.
*
* @param[in] local_db_len The length of the database as reported by the SoftDevice.
*
* @return The number of words the data will take in flash.
*/
static __INLINE uint16_t PM_LOCAL_DB_N_WORDS(uint16_t local_db_len)
{
return PM_N_WORDS(local_db_len + PM_LOCAL_DB_LEN_OVERHEAD_BYTES);
}
/**@brief Function for calculating the length of a local GATT database attribute array.
*
* @param[in] n_words The number of words the data takes in flash.
*
* @return The length of the database attribute array.
*/
static __INLINE uint16_t PM_LOCAL_DB_LEN(uint16_t n_words)
{
return ((n_words * 4) - PM_LOCAL_DB_LEN_OVERHEAD_BYTES);
}
/**@brief Data on a remote GATT database.
*/
typedef struct
{
uint32_t service_count; /**< Number of stored services. */
ble_gatt_db_srv_t * p_data; /**< Array to hold the database attributes. */
} pm_peer_data_remote_gatt_db_t;
/**@brief Data on a remote GATT database, as formatted in flash.
*/
typedef struct
{
uint32_t service_count;
ble_gatt_db_srv_t p_data[];
} pm_peer_data_remote_gatt_db_flash_t;
/**@brief Function for calculating the flash size of remote GATT database data.
*
* @param[in] service_count The number of services in the service array.
*
* @return The number of words the data will take in flash.
*/
static __INLINE uint16_t PM_REMOTE_DB_N_WORDS(uint16_t service_count)
{
return PM_N_WORDS((sizeof(ble_gatt_db_srv_t) * service_count) + PM_REMOTE_DB_LEN_OVERHEAD_BYTES);
}
/**@brief Union of all data associated with one peer.
*/
typedef union
{
pm_peer_data_bonding_t * p_bonding_data; /**< The exchanged bond information in addition to metadata of the bonding. */
bool * p_service_changed_pending; /**< Whether a service changed indication should be sent to the peer. */
pm_peer_data_local_gatt_db_t * p_local_gatt_db; /**< Persistent information pertaining to a peer GATT client. */
pm_peer_data_remote_gatt_db_t * p_remote_gatt_db; /**< Persistent information pertaining to a peer GATT server. */
uint8_t * p_application_data; /**< Arbitrary data to associate with the peer. This data can be freely used by the application. */
} pm_peer_data_unit_t;
/**@brief Immutable version of @ref pm_peer_data_unit_t.
*/
typedef union
{
pm_peer_data_bonding_t const * p_bonding_data;
bool const * p_service_changed_pending;
pm_peer_data_local_gatt_db_t const * p_local_gatt_db;
pm_peer_data_remote_gatt_db_t const * p_remote_gatt_db;
uint8_t const * p_application_data;
} pm_peer_data_unit_const_t;
/**@brief Data from @ref pm_peer_data_unit_t, as mapped in flash.
*/
typedef union
{
pm_peer_data_bonding_t const * p_bonding_data;
bool const * p_service_changed_pending;
pm_peer_data_local_gatt_db_flash_t const * p_local_gatt_db;
pm_peer_data_remote_gatt_db_flash_t const * p_remote_gatt_db;
uint8_t const * p_application_data;
} pm_peer_data_unit_flash_t;
/**@brief The different types of data associated with a peer.
*/
typedef enum
{
PM_PEER_DATA_ID_BONDING,
PM_PEER_DATA_ID_SERVICE_CHANGED_PENDING,
PM_PEER_DATA_ID_GATT_LOCAL,
PM_PEER_DATA_ID_GATT_REMOTE,
PM_PEER_DATA_ID_APPLICATION,
PM_PEER_DATA_ID_INVALID,
} pm_peer_data_id_t;
//STATIC_ASSERT_MSG(sizeof(pm_peer_data_t) == sizeof(pm_peer_data_const_t), "Size of pm_peer_data_t different from immutable version.");
/**@brief Macro saying whether a data_id is valid, i.e. one of the valid enum values.
*
* @param[in] data_id The data_id to check.
*/
static __INLINE bool PM_PEER_DATA_ID_IS_VALID(pm_peer_data_id_t data_id)
{
return ((data_id - PM_PEER_DATA_ID_BONDING) < (PM_PEER_DATA_ID_INVALID - PM_PEER_DATA_ID_BONDING));
}
/**@brief One piece of data associated with a peer, together with the type.
*/
typedef struct
{
uint16_t length_words;
pm_peer_data_id_t data_type;
pm_peer_data_unit_t data;
} pm_peer_data_t;
/**@brief Immutable version of @ref pm_peer_data_t.
*/
typedef struct
{
uint16_t length_words;
pm_peer_data_id_t data_type;
pm_peer_data_unit_const_t data;
} pm_peer_data_const_t;
/**@brief Data from @ref pm_peer_data_t, as mapped in flash.
*/
typedef struct
{
uint16_t length_words;
pm_peer_data_id_t data_type;
pm_peer_data_unit_flash_t data;
} pm_peer_data_flash_t;
/**@brief Typedef for type used for write prepares. Used to reserve space in flash
*/
typedef uint32_t pm_prepare_token_t;
/**@brief Typedef for type used to hold reference to stored item in flash.
* this token can be used for locking and validity check
*/
typedef uint32_t pm_store_token_t;
/** @} */
#endif /* PEER_MANAGER_TYPES_H__ */

View File

@ -0,0 +1,173 @@
/*
* 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 SDK_MAPPED_FLAGS_H__
#define SDK_MAPPED_FLAGS_H__
#include <stdint.h>
#include <stdbool.h>
#include "app_util.h"
#include "compiler_abstraction.h"
/**
* @file
* @defgroup sdk_mapped_flags Mapped flags
* @ingroup app_common
* @{
* @brief Module for writing and reading flags that are associated
* with keys.
*
* @details The flags are represented as bits in a bitmap called a <i>flag collection</i>. The keys
* are uint16_t. Each flag collection contains all flags of the same type, one flag for
* each key.
*
* The mapped flags module does not keep the flag states, nor the list of keys. These are
* provided in the API calls. A key's index in the key list determines which bit in the
* flag collection is associated with it. This module does not ever edit the key list, and
* does not edit flags except in function calls that take the flag collection as a pointer.
*
*/
#define SDK_MAPPED_FLAGS_N_KEYS 8 /**< The number of keys to keep flags for. This is also the number of flags in a flag collection. If changing this value, you might also need change the width of the sdk_mapped_flags_t type. */
#define SDK_MAPPED_FLAGS_N_KEYS_PER_BYTE 8 /**< The number of flags that fit in one byte. */
#define SDK_MAPPED_FLAGS_INVALID_INDEX 0xFFFF /**< A flag index guaranteed to be invalid. */
typedef uint8_t sdk_mapped_flags_t; /**< The bitmap to hold flags. Each flag is one bit, and each bit represents the flag state associated with one key. */
// Test whether the flag collection type is large enough to hold all the flags. If this fails,
// reduce SDK_MAPPED_FLAGS_N_KEYS or increase the size of sdk_mapped_flags_t.
STATIC_ASSERT((
sizeof(sdk_mapped_flags_t)*SDK_MAPPED_FLAGS_N_KEYS_PER_BYTE) >= SDK_MAPPED_FLAGS_N_KEYS);
/**@brief Type used to present a subset of the registered keys.
*/
typedef struct
{
uint32_t len; /**< The length of the list. */
uint16_t flag_keys[SDK_MAPPED_FLAGS_N_KEYS]; /**< The list of keys. */
} sdk_mapped_flags_key_list_t;
/**@brief Function for getting the first index at which the flag is true in the provided
* collection.
*
* @param[in] flags The flag collection to search for a flag set to true.
*
* @return The first index that has its flag set to true. If none were found, the
* function returns @ref SDK_MAPPED_FLAGS_INVALID_INDEX.
*/
uint16_t sdk_mapped_flags_first_key_index_get(sdk_mapped_flags_t flags);
/**@brief Function for updating the state of a flag.
*
* @param[in] p_keys The list of associated keys (assumed to have a length of
* @ref SDK_MAPPED_FLAGS_N_KEYS).
* @param[out] p_flags The flag collection to modify.
* @param[in] key The key to modify the flag of.
* @param[in] value The state to set the flag to.
*/
void sdk_mapped_flags_update_by_key(uint16_t * p_keys,
sdk_mapped_flags_t * p_flags,
uint16_t key,
bool value);
/**@brief Function for updating the state of the same flag in multiple flag collections.
*
* @details The key and value are the same for all flag collections in the p_flags array.
*
* @param[in] p_keys The list of associated keys (assumed to have a length of
* @ref SDK_MAPPED_FLAGS_N_KEYS).
* @param[out] p_flags The flag collections to modify.
* @param[out] n_flag_collections The number of flag collections in p_flags.
* @param[in] key The key to modify the flag of.
* @param[in] value The state to set the flag to.
*/
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);
/**@brief Function for getting the state of a specific flag.
*
* @param[in] p_keys The list of associated keys (assumed to have a length of
* @ref SDK_MAPPED_FLAGS_N_KEYS).
* @param[in] flags The flag collection to read from.
* @param[in] key The key to get the flag for.
*
* @return The state of the flag.
*/
bool sdk_mapped_flags_get_by_key(uint16_t * p_keys, sdk_mapped_flags_t flags, uint16_t key);
/**@brief Function for getting a list of all keys that have a specific flag set to true.
*
* @param[in] p_keys The list of associated keys (assumed to have a length of
* @ref SDK_MAPPED_FLAGS_N_KEYS).
* @param[in] flags The flag collection to search.
*
* @return The list of keys.
*/
sdk_mapped_flags_key_list_t sdk_mapped_flags_key_list_get(uint16_t * p_keys,
sdk_mapped_flags_t flags);
/**@brief Function for getting the number of keys that have a specific flag set to true.
*
* @param[in] flags The flag collection to search.
*
* @return The number of keys.
*/
uint32_t sdk_mapped_flags_n_flags_set(sdk_mapped_flags_t flags);
/**@brief Function for querying whether any flags in the collection are set.
*
* @param[in] flags The flag collection to query.
*
* @retval true If one or more flags are set to true.
* @retval false Otherwise.
*/
static __INLINE bool sdk_mapped_flags_any_set(sdk_mapped_flags_t flags)
{
return (flags != 0);
}
/** @} */
#endif /* SDK_MAPPED_FLAGS_H__ */