687 lines
24 KiB
C
687 lines
24 KiB
C
/**
|
|
* Copyright (c) 2018 - 2020, Nordic Semiconductor ASA
|
|
*
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without modification,
|
|
* are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
* list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form, except as embedded into a Nordic
|
|
* Semiconductor ASA integrated circuit in a product or a software update for
|
|
* such product, must reproduce the above copyright notice, this list of
|
|
* conditions and the following disclaimer in the documentation and/or other
|
|
* materials provided with the distribution.
|
|
*
|
|
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
|
|
* contributors may be used to endorse or promote products derived from this
|
|
* software without specific prior written permission.
|
|
*
|
|
* 4. This software, with or without modification, must only be used with a
|
|
* Nordic Semiconductor ASA integrated circuit.
|
|
*
|
|
* 5. Any software provided in binary form under this license must not be reverse
|
|
* engineered, decompiled, modified and/or disassembled.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
|
|
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
|
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
*/
|
|
#include "peer_manager_handler.h"
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include "sdk_errors.h"
|
|
#include "app_error.h"
|
|
#include "peer_manager.h"
|
|
#include "ble_gap.h"
|
|
#include "ble_gattc.h"
|
|
#include "ble_conn_state.h"
|
|
#include "fds.h"
|
|
#include "nrf_strerror.h"
|
|
#include "sdk_config.h"
|
|
|
|
#if PM_HANDLER_SEC_DELAY_MS > 0
|
|
#include "app_timer.h"
|
|
#endif
|
|
|
|
#define NRF_LOG_MODULE_NAME peer_manager_handler
|
|
#if PM_LOG_ENABLED
|
|
#define NRF_LOG_LEVEL PM_LOG_LEVEL
|
|
#define NRF_LOG_INFO_COLOR PM_LOG_INFO_COLOR
|
|
#define NRF_LOG_DEBUG_COLOR PM_LOG_DEBUG_COLOR
|
|
#else
|
|
#define NRF_LOG_LEVEL 0
|
|
#endif // PM_LOG_ENABLED
|
|
#include "nrf_log.h"
|
|
#include "nrf_log_ctrl.h"
|
|
NRF_LOG_MODULE_REGISTER();
|
|
#include "nrf_strerror.h"
|
|
|
|
static const char * m_roles_str[] =
|
|
{
|
|
"Invalid Role",
|
|
"Peripheral",
|
|
"Central",
|
|
};
|
|
|
|
static const char * m_sec_procedure_str[] =
|
|
{
|
|
"Encryption",
|
|
"Bonding",
|
|
"Pairing",
|
|
};
|
|
|
|
#define PM_EVT_STR(_name) [_name] = STRINGIFY(_name)
|
|
|
|
static const char * m_event_str[] =
|
|
{
|
|
PM_EVT_STR(PM_EVT_BONDED_PEER_CONNECTED),
|
|
PM_EVT_STR(PM_EVT_CONN_SEC_START),
|
|
PM_EVT_STR(PM_EVT_CONN_SEC_SUCCEEDED),
|
|
PM_EVT_STR(PM_EVT_CONN_SEC_FAILED),
|
|
PM_EVT_STR(PM_EVT_CONN_SEC_CONFIG_REQ),
|
|
PM_EVT_STR(PM_EVT_CONN_SEC_PARAMS_REQ),
|
|
PM_EVT_STR(PM_EVT_STORAGE_FULL),
|
|
PM_EVT_STR(PM_EVT_ERROR_UNEXPECTED),
|
|
PM_EVT_STR(PM_EVT_PEER_DATA_UPDATE_SUCCEEDED),
|
|
PM_EVT_STR(PM_EVT_PEER_DATA_UPDATE_FAILED),
|
|
PM_EVT_STR(PM_EVT_PEER_DELETE_SUCCEEDED),
|
|
PM_EVT_STR(PM_EVT_PEER_DELETE_FAILED),
|
|
PM_EVT_STR(PM_EVT_PEERS_DELETE_SUCCEEDED),
|
|
PM_EVT_STR(PM_EVT_PEERS_DELETE_FAILED),
|
|
PM_EVT_STR(PM_EVT_LOCAL_DB_CACHE_APPLIED),
|
|
PM_EVT_STR(PM_EVT_LOCAL_DB_CACHE_APPLY_FAILED),
|
|
PM_EVT_STR(PM_EVT_SERVICE_CHANGED_IND_SENT),
|
|
PM_EVT_STR(PM_EVT_SERVICE_CHANGED_IND_CONFIRMED),
|
|
PM_EVT_STR(PM_EVT_SLAVE_SECURITY_REQ),
|
|
PM_EVT_STR(PM_EVT_FLASH_GARBAGE_COLLECTED),
|
|
PM_EVT_STR(PM_EVT_FLASH_GARBAGE_COLLECTION_FAILED),
|
|
};
|
|
|
|
static const char * m_data_id_str[] =
|
|
{
|
|
"Outdated (0)",
|
|
"Service changed pending flag",
|
|
"Outdated (2)",
|
|
"Outdated (3)",
|
|
"Application data",
|
|
"Remote database",
|
|
"Peer rank",
|
|
"Bonding data",
|
|
"Local database",
|
|
"Central address resolution",
|
|
};
|
|
|
|
static const char * m_data_action_str[] =
|
|
{
|
|
"Update",
|
|
"Delete"
|
|
};
|
|
|
|
#define PM_SEC_ERR_STR(_name) {.error = _name, .error_str = #_name}
|
|
|
|
typedef struct
|
|
{
|
|
pm_sec_error_code_t error;
|
|
const char * error_str;
|
|
} sec_err_str_t;
|
|
|
|
static const sec_err_str_t m_pm_sec_error_str[] =
|
|
{
|
|
PM_SEC_ERR_STR(PM_CONN_SEC_ERROR_PIN_OR_KEY_MISSING),
|
|
PM_SEC_ERR_STR(PM_CONN_SEC_ERROR_MIC_FAILURE),
|
|
PM_SEC_ERR_STR(PM_CONN_SEC_ERROR_DISCONNECT),
|
|
PM_SEC_ERR_STR(PM_CONN_SEC_ERROR_SMP_TIMEOUT),
|
|
};
|
|
|
|
static const char * sec_err_string_get(pm_sec_error_code_t error)
|
|
{
|
|
static char errstr[30];
|
|
|
|
for (uint32_t i = 0; i < (sizeof(m_pm_sec_error_str)/sizeof(sec_err_str_t)); i++)
|
|
{
|
|
if (m_pm_sec_error_str[i].error == error)
|
|
{
|
|
return m_pm_sec_error_str[i].error_str;
|
|
}
|
|
}
|
|
|
|
int len = snprintf(errstr, sizeof(errstr), "%s 0x%hx", (error < PM_CONN_SEC_ERROR_BASE)
|
|
? "BLE_GAP_SEC_STATUS"
|
|
:"PM_CONN_SEC_ERROR", error);
|
|
UNUSED_VARIABLE(len);
|
|
return errstr;
|
|
}
|
|
|
|
|
|
static void _conn_secure(uint16_t conn_handle, bool force)
|
|
{
|
|
ret_code_t err_code;
|
|
|
|
if (!force)
|
|
{
|
|
pm_conn_sec_status_t status;
|
|
err_code = pm_conn_sec_status_get(conn_handle, &status);
|
|
if (err_code != BLE_ERROR_INVALID_CONN_HANDLE)
|
|
{
|
|
APP_ERROR_CHECK(err_code);
|
|
}
|
|
|
|
// If the link is already secured, don't initiate security procedure.
|
|
if (status.encrypted)
|
|
{
|
|
NRF_LOG_DEBUG("Already encrypted, skipping security.");
|
|
return;
|
|
}
|
|
}
|
|
|
|
err_code = pm_conn_secure(conn_handle, false);
|
|
|
|
if ((err_code == NRF_SUCCESS) || (err_code == NRF_ERROR_BUSY))
|
|
{
|
|
// Success.
|
|
}
|
|
else if (err_code == NRF_ERROR_TIMEOUT)
|
|
{
|
|
NRF_LOG_WARNING("pm_conn_secure() failed because an SMP timeout is preventing security on "\
|
|
"the link. Disconnecting conn_handle %d.",
|
|
conn_handle);
|
|
|
|
err_code = sd_ble_gap_disconnect(conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
|
|
if (err_code != NRF_SUCCESS)
|
|
{
|
|
NRF_LOG_WARNING("sd_ble_gap_disconnect() returned %s on conn_handle %d.",
|
|
nrf_strerror_get(err_code),
|
|
conn_handle);
|
|
}
|
|
}
|
|
else if (err_code == NRF_ERROR_INVALID_DATA)
|
|
{
|
|
NRF_LOG_WARNING("pm_conn_secure() failed because the stored data for conn_handle %d does "\
|
|
"not have a valid key.",
|
|
conn_handle);
|
|
}
|
|
else if (err_code == BLE_ERROR_INVALID_CONN_HANDLE)
|
|
{
|
|
NRF_LOG_WARNING("pm_conn_secure() failed because conn_handle %d is not a valid connection.",
|
|
conn_handle);
|
|
}
|
|
else
|
|
{
|
|
NRF_LOG_ERROR("Asserting. pm_conn_secure() returned %s on conn_handle %d.",
|
|
nrf_strerror_get(err_code),
|
|
conn_handle);
|
|
APP_ERROR_CHECK(err_code);
|
|
}
|
|
}
|
|
|
|
#if PM_HANDLER_SEC_DELAY_MS > 0
|
|
APP_TIMER_DEF(secure_delay_timer);
|
|
|
|
typedef union
|
|
{
|
|
struct
|
|
{
|
|
uint16_t conn_handle;
|
|
bool force;
|
|
} values;
|
|
void * p_void;
|
|
} conn_secure_context_t;
|
|
|
|
STATIC_ASSERT(sizeof(conn_secure_context_t) <= sizeof(void *), "conn_secure_context_t is too large.");
|
|
|
|
static void _conn_secure(uint16_t conn_handle, bool force);
|
|
|
|
static void delayed_conn_secure(void * context)
|
|
{
|
|
conn_secure_context_t sec_context = {.p_void = context};
|
|
_conn_secure(sec_context.values.conn_handle, sec_context.values.force);
|
|
}
|
|
|
|
static void conn_secure(uint16_t conn_handle, bool force)
|
|
{
|
|
ret_code_t err_code;
|
|
static bool created = false;
|
|
if (!created)
|
|
{
|
|
err_code = app_timer_create(&secure_delay_timer,
|
|
APP_TIMER_MODE_SINGLE_SHOT,
|
|
delayed_conn_secure);
|
|
APP_ERROR_CHECK(err_code);
|
|
created = true;
|
|
}
|
|
|
|
conn_secure_context_t sec_context = {0};
|
|
sec_context.values.conn_handle = conn_handle;
|
|
sec_context.values.force = force;
|
|
|
|
err_code = app_timer_start(secure_delay_timer,
|
|
APP_TIMER_TICKS(PM_HANDLER_SEC_DELAY_MS),
|
|
sec_context.p_void);
|
|
APP_ERROR_CHECK(err_code);
|
|
}
|
|
|
|
|
|
#else
|
|
static void conn_secure(uint16_t conn_handle, bool force)
|
|
{
|
|
_conn_secure(conn_handle, force);
|
|
}
|
|
#endif
|
|
|
|
|
|
void pm_handler_on_pm_evt(pm_evt_t const * p_pm_evt)
|
|
{
|
|
pm_handler_pm_evt_log(p_pm_evt);
|
|
|
|
if (p_pm_evt->evt_id == PM_EVT_BONDED_PEER_CONNECTED)
|
|
{
|
|
conn_secure(p_pm_evt->conn_handle, false);
|
|
}
|
|
else if (p_pm_evt->evt_id == PM_EVT_ERROR_UNEXPECTED)
|
|
{
|
|
NRF_LOG_ERROR("Asserting.");
|
|
APP_ERROR_CHECK(p_pm_evt->params.error_unexpected.error);
|
|
}
|
|
}
|
|
|
|
|
|
void pm_handler_flash_clean_on_return(void)
|
|
{
|
|
// Trigger the mechanism to make more room in flash.
|
|
pm_evt_t storage_full_evt = {.evt_id = PM_EVT_STORAGE_FULL};
|
|
pm_handler_flash_clean(&storage_full_evt);
|
|
}
|
|
|
|
|
|
static void rank_highest(pm_peer_id_t peer_id)
|
|
{
|
|
// Trigger a pm_peer_rank_highest() with internal bookkeeping.
|
|
pm_evt_t connected_evt = {.evt_id = PM_EVT_BONDED_PEER_CONNECTED, .peer_id = peer_id};
|
|
pm_handler_flash_clean(&connected_evt);
|
|
}
|
|
|
|
|
|
void pm_handler_flash_clean(pm_evt_t const * p_pm_evt)
|
|
{
|
|
ret_code_t err_code;
|
|
static bool flash_cleaning = false; // Indicates whether garbage collection is currently being run.
|
|
static bool flash_write_after_gc = true; // Indicates whether a successful write happened after the last garbage
|
|
// collection. If this is false when flash is full, it means just a
|
|
// garbage collection won't work, so some data should be deleted.
|
|
#define RANK_QUEUE_SIZE 8 // Size of rank_queue.
|
|
#define RANK_QUEUE_INIT() PM_PEER_ID_INVALID, // Initial value of rank_queue.
|
|
|
|
//lint -save -e40 -e26 -esym(628,MACRO_REPEAT_8)
|
|
static pm_peer_id_t rank_queue[8] = {MACRO_REPEAT(RANK_QUEUE_SIZE, RANK_QUEUE_INIT)}; // Queue of rank_highest calls that
|
|
// failed because of full flash.
|
|
//lint -restore
|
|
static int rank_queue_wr = 0; // Write pointer for rank_queue.
|
|
|
|
switch (p_pm_evt->evt_id)
|
|
{
|
|
case PM_EVT_BONDED_PEER_CONNECTED:
|
|
err_code = pm_peer_rank_highest(p_pm_evt->peer_id);
|
|
if ((err_code == NRF_ERROR_STORAGE_FULL) ||
|
|
(err_code == NRF_ERROR_BUSY))
|
|
{
|
|
// Queue pm_peer_rank_highest() call and attempt to clean flash.
|
|
rank_queue[rank_queue_wr] = p_pm_evt->peer_id;
|
|
rank_queue_wr = (rank_queue_wr + 1) % RANK_QUEUE_SIZE;
|
|
pm_handler_flash_clean_on_return();
|
|
}
|
|
else if ((err_code != NRF_ERROR_NOT_SUPPORTED) &&
|
|
(err_code != NRF_ERROR_INVALID_PARAM) &&
|
|
(err_code != NRF_ERROR_RESOURCES))
|
|
{
|
|
APP_ERROR_CHECK(err_code);
|
|
}
|
|
else
|
|
{
|
|
NRF_LOG_DEBUG("pm_peer_rank_highest() returned %s for peer id %d",
|
|
nrf_strerror_get(err_code),
|
|
p_pm_evt->peer_id);
|
|
}
|
|
break;
|
|
|
|
case PM_EVT_CONN_SEC_START:
|
|
break;
|
|
|
|
case PM_EVT_CONN_SEC_SUCCEEDED:
|
|
if ( (p_pm_evt->params.conn_sec_succeeded.procedure == PM_CONN_SEC_PROCEDURE_BONDING)
|
|
|| (p_pm_evt->params.conn_sec_succeeded.procedure == PM_CONN_SEC_PROCEDURE_ENCRYPTION))
|
|
// PM_CONN_SEC_PROCEDURE_ENCRYPTION in case peer was not recognized at connection time.
|
|
{
|
|
rank_highest(p_pm_evt->peer_id);
|
|
}
|
|
break;
|
|
|
|
case PM_EVT_CONN_SEC_FAILED:
|
|
case PM_EVT_CONN_SEC_CONFIG_REQ:
|
|
case PM_EVT_CONN_SEC_PARAMS_REQ:
|
|
break;
|
|
|
|
case PM_EVT_STORAGE_FULL:
|
|
if (!flash_cleaning)
|
|
{
|
|
err_code = NRF_SUCCESS;
|
|
NRF_LOG_INFO("Attempting to clean flash.");
|
|
if (!flash_write_after_gc)
|
|
{
|
|
// Check whether another user of FDS has deleted a record that can be GCed.
|
|
fds_stat_t fds_stats;
|
|
err_code = fds_stat(&fds_stats);
|
|
APP_ERROR_CHECK(err_code);
|
|
flash_write_after_gc = (fds_stats.dirty_records > 0);
|
|
}
|
|
if (!flash_write_after_gc)
|
|
{
|
|
pm_peer_id_t peer_id_to_delete;
|
|
err_code = pm_peer_ranks_get(NULL, NULL, &peer_id_to_delete, NULL);
|
|
if (err_code == NRF_SUCCESS)
|
|
{
|
|
NRF_LOG_INFO("Deleting lowest ranked peer (peer_id: %d)", peer_id_to_delete);
|
|
err_code = pm_peer_delete(peer_id_to_delete);
|
|
APP_ERROR_CHECK(err_code);
|
|
flash_write_after_gc = true;
|
|
}
|
|
if (err_code == NRF_ERROR_NOT_FOUND)
|
|
{
|
|
NRF_LOG_ERROR("There are no peers to delete.");
|
|
}
|
|
else if (err_code == NRF_ERROR_NOT_SUPPORTED)
|
|
{
|
|
NRF_LOG_WARNING("Peer ranks functionality is disabled, so no peers are deleted.");
|
|
}
|
|
else
|
|
{
|
|
APP_ERROR_CHECK(err_code);
|
|
}
|
|
}
|
|
if (err_code == NRF_SUCCESS)
|
|
{
|
|
err_code = fds_gc();
|
|
if (err_code == NRF_SUCCESS)
|
|
{
|
|
NRF_LOG_DEBUG("Running flash garbage collection.");
|
|
flash_cleaning = true;
|
|
}
|
|
else if (err_code != FDS_ERR_NO_SPACE_IN_QUEUES)
|
|
{
|
|
APP_ERROR_CHECK(err_code);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case PM_EVT_ERROR_UNEXPECTED:
|
|
break;
|
|
|
|
case PM_EVT_PEER_DATA_UPDATE_SUCCEEDED:
|
|
flash_write_after_gc = true;
|
|
break;
|
|
|
|
case PM_EVT_PEER_DATA_UPDATE_FAILED:
|
|
break;
|
|
|
|
case PM_EVT_PEER_DELETE_SUCCEEDED:
|
|
flash_write_after_gc = true;
|
|
break;
|
|
|
|
case PM_EVT_PEER_DELETE_FAILED:
|
|
case PM_EVT_PEERS_DELETE_SUCCEEDED:
|
|
case PM_EVT_PEERS_DELETE_FAILED:
|
|
case PM_EVT_LOCAL_DB_CACHE_APPLIED:
|
|
case PM_EVT_LOCAL_DB_CACHE_APPLY_FAILED:
|
|
case PM_EVT_SERVICE_CHANGED_IND_SENT:
|
|
case PM_EVT_SERVICE_CHANGED_IND_CONFIRMED:
|
|
case PM_EVT_SLAVE_SECURITY_REQ:
|
|
break;
|
|
|
|
case PM_EVT_FLASH_GARBAGE_COLLECTED:
|
|
flash_cleaning = false;
|
|
flash_write_after_gc = false;
|
|
{
|
|
// Reattempt queued pm_peer_rank_highest() calls.
|
|
int rank_queue_rd = rank_queue_wr;
|
|
for (int i = 0; i < RANK_QUEUE_SIZE; i++)
|
|
{
|
|
pm_peer_id_t peer_id = rank_queue[(i + rank_queue_rd) % RANK_QUEUE_SIZE];
|
|
if (peer_id != PM_PEER_ID_INVALID)
|
|
{
|
|
rank_queue[(i + rank_queue_rd) % RANK_QUEUE_SIZE] = PM_PEER_ID_INVALID;
|
|
rank_highest(peer_id);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case PM_EVT_FLASH_GARBAGE_COLLECTION_FAILED:
|
|
flash_cleaning = false;
|
|
|
|
if (p_pm_evt->params.garbage_collection_failed.error == FDS_ERR_BUSY
|
|
|| p_pm_evt->params.garbage_collection_failed.error == FDS_ERR_OPERATION_TIMEOUT)
|
|
{
|
|
// Retry immediately if error is transient.
|
|
pm_handler_flash_clean_on_return();
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void pm_handler_pm_evt_log(pm_evt_t const * p_pm_evt)
|
|
{
|
|
NRF_LOG_DEBUG("Event %s", m_event_str[p_pm_evt->evt_id]);
|
|
|
|
switch (p_pm_evt->evt_id)
|
|
{
|
|
case PM_EVT_BONDED_PEER_CONNECTED:
|
|
NRF_LOG_DEBUG("Previously bonded peer connected: role: %s, conn_handle: %d, peer_id: %d",
|
|
m_roles_str[ble_conn_state_role(p_pm_evt->conn_handle)],
|
|
p_pm_evt->conn_handle,
|
|
p_pm_evt->peer_id);
|
|
break;
|
|
|
|
case PM_EVT_CONN_SEC_START:
|
|
NRF_LOG_DEBUG("Connection security procedure started: role: %s, conn_handle: %d, procedure: %s",
|
|
m_roles_str[ble_conn_state_role(p_pm_evt->conn_handle)],
|
|
p_pm_evt->conn_handle,
|
|
m_sec_procedure_str[p_pm_evt->params.conn_sec_start.procedure]);
|
|
break;
|
|
|
|
case PM_EVT_CONN_SEC_SUCCEEDED:
|
|
NRF_LOG_INFO("Connection secured: role: %s, conn_handle: %d, procedure: %s",
|
|
m_roles_str[ble_conn_state_role(p_pm_evt->conn_handle)],
|
|
p_pm_evt->conn_handle,
|
|
m_sec_procedure_str[p_pm_evt->params.conn_sec_start.procedure]);
|
|
break;
|
|
|
|
case PM_EVT_CONN_SEC_FAILED:
|
|
NRF_LOG_INFO("Connection security failed: role: %s, conn_handle: 0x%x, procedure: %s, error: %d",
|
|
m_roles_str[ble_conn_state_role(p_pm_evt->conn_handle)],
|
|
p_pm_evt->conn_handle,
|
|
m_sec_procedure_str[p_pm_evt->params.conn_sec_start.procedure],
|
|
p_pm_evt->params.conn_sec_failed.error);
|
|
NRF_LOG_DEBUG("Error (decoded): %s",
|
|
sec_err_string_get(p_pm_evt->params.conn_sec_failed.error));
|
|
break;
|
|
|
|
case PM_EVT_CONN_SEC_CONFIG_REQ:
|
|
NRF_LOG_DEBUG("Security configuration request");
|
|
break;
|
|
|
|
case PM_EVT_CONN_SEC_PARAMS_REQ:
|
|
NRF_LOG_DEBUG("Security parameter request");
|
|
break;
|
|
|
|
case PM_EVT_STORAGE_FULL:
|
|
NRF_LOG_WARNING("Flash storage is full");
|
|
break;
|
|
|
|
case PM_EVT_ERROR_UNEXPECTED:
|
|
NRF_LOG_ERROR("Unexpected fatal error occurred: error: %s",
|
|
nrf_strerror_get(p_pm_evt->params.error_unexpected.error));
|
|
break;
|
|
|
|
case PM_EVT_PEER_DATA_UPDATE_SUCCEEDED:
|
|
NRF_LOG_INFO("Peer data updated in flash: peer_id: %d, data_id: %s, action: %s%s",
|
|
p_pm_evt->peer_id,
|
|
m_data_id_str[p_pm_evt->params.peer_data_update_succeeded.data_id],
|
|
m_data_action_str[p_pm_evt->params.peer_data_update_succeeded.action],
|
|
p_pm_evt->params.peer_data_update_succeeded.flash_changed ? "" : ", no change");
|
|
break;
|
|
|
|
case PM_EVT_PEER_DATA_UPDATE_FAILED:
|
|
// This can happen if the SoftDevice is too busy with BLE operations.
|
|
NRF_LOG_WARNING("Peer data updated failed: peer_id: %d, data_id: %s, action: %s, error: %s",
|
|
p_pm_evt->peer_id,
|
|
m_data_id_str[p_pm_evt->params.peer_data_update_failed.data_id],
|
|
m_data_action_str[p_pm_evt->params.peer_data_update_succeeded.action],
|
|
nrf_strerror_get(p_pm_evt->params.peer_data_update_failed.error));
|
|
break;
|
|
|
|
case PM_EVT_PEER_DELETE_SUCCEEDED:
|
|
NRF_LOG_ERROR("Peer deleted successfully: peer_id: %d", p_pm_evt->peer_id);
|
|
break;
|
|
|
|
case PM_EVT_PEER_DELETE_FAILED:
|
|
NRF_LOG_ERROR("Peer deletion failed: peer_id: %d, error: %s",
|
|
p_pm_evt->peer_id,
|
|
nrf_strerror_get(p_pm_evt->params.peer_delete_failed.error));
|
|
break;
|
|
|
|
case PM_EVT_PEERS_DELETE_SUCCEEDED:
|
|
NRF_LOG_INFO("All peers deleted.");
|
|
break;
|
|
|
|
case PM_EVT_PEERS_DELETE_FAILED:
|
|
NRF_LOG_ERROR("All peer deletion failed: error: %s",
|
|
nrf_strerror_get(p_pm_evt->params.peers_delete_failed_evt.error));
|
|
break;
|
|
|
|
case PM_EVT_LOCAL_DB_CACHE_APPLIED:
|
|
NRF_LOG_DEBUG("Previously stored local DB applied: conn_handle: %d, peer_id: %d",
|
|
p_pm_evt->conn_handle,
|
|
p_pm_evt->peer_id);
|
|
break;
|
|
|
|
case PM_EVT_LOCAL_DB_CACHE_APPLY_FAILED:
|
|
// This can happen when the local DB has changed.
|
|
NRF_LOG_WARNING("Local DB could not be applied: conn_handle: %d, peer_id: %d",
|
|
p_pm_evt->conn_handle,
|
|
p_pm_evt->peer_id);
|
|
break;
|
|
|
|
case PM_EVT_SERVICE_CHANGED_IND_SENT:
|
|
NRF_LOG_DEBUG("Sending Service Changed indication.");
|
|
break;
|
|
|
|
case PM_EVT_SERVICE_CHANGED_IND_CONFIRMED:
|
|
NRF_LOG_DEBUG("Service Changed indication confirmed.");
|
|
break;
|
|
|
|
case PM_EVT_SLAVE_SECURITY_REQ:
|
|
NRF_LOG_DEBUG("Security Request received from peer.");
|
|
break;
|
|
|
|
case PM_EVT_FLASH_GARBAGE_COLLECTED:
|
|
NRF_LOG_DEBUG("Flash garbage collection complete.");
|
|
break;
|
|
|
|
case PM_EVT_FLASH_GARBAGE_COLLECTION_FAILED:
|
|
NRF_LOG_WARNING("Flash garbage collection failed with error %s.",
|
|
nrf_strerror_get(p_pm_evt->params.garbage_collection_failed.error));
|
|
break;
|
|
|
|
default:
|
|
NRF_LOG_WARNING("Unexpected PM event ID: 0x%x.", p_pm_evt->evt_id);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void pm_handler_disconnect_on_sec_failure(pm_evt_t const * p_pm_evt)
|
|
{
|
|
ret_code_t err_code;
|
|
|
|
if (p_pm_evt->evt_id == PM_EVT_CONN_SEC_FAILED)
|
|
{
|
|
NRF_LOG_WARNING("Disconnecting conn_handle %d.", p_pm_evt->conn_handle);
|
|
err_code = sd_ble_gap_disconnect(p_pm_evt->conn_handle,
|
|
BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
|
|
if ((err_code != NRF_ERROR_INVALID_STATE) && (err_code != BLE_ERROR_INVALID_CONN_HANDLE))
|
|
{
|
|
APP_ERROR_CHECK(err_code);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void pm_handler_disconnect_on_insufficient_sec(pm_evt_t const * p_pm_evt,
|
|
pm_conn_sec_status_t * p_min_conn_sec)
|
|
{
|
|
if (p_pm_evt->evt_id == PM_EVT_CONN_SEC_SUCCEEDED)
|
|
{
|
|
if (!pm_sec_is_sufficient(p_pm_evt->conn_handle, p_min_conn_sec))
|
|
{
|
|
NRF_LOG_WARNING("Connection security is insufficient, disconnecting.");
|
|
ret_code_t err_code = sd_ble_gap_disconnect(p_pm_evt->conn_handle,
|
|
BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
|
|
APP_ERROR_CHECK(err_code);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void pm_handler_secure_on_connection(ble_evt_t const * p_ble_evt)
|
|
{
|
|
switch (p_ble_evt->header.evt_id)
|
|
{
|
|
case BLE_GAP_EVT_CONNECTED:
|
|
NRF_LOG_DEBUG("Connected, securing connection. conn_handle: %d", p_ble_evt->evt.gap_evt.conn_handle);
|
|
conn_secure(p_ble_evt->evt.gap_evt.conn_handle, false);
|
|
break;
|
|
|
|
#if PM_HANDLER_SEC_DELAY_MS > 0
|
|
case BLE_GAP_EVT_DISCONNECTED:
|
|
{
|
|
ret_code_t err_code = app_timer_stop(secure_delay_timer);
|
|
APP_ERROR_CHECK(err_code);
|
|
} break;
|
|
#endif
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void pm_handler_secure_on_error(ble_evt_t const * p_ble_evt)
|
|
{
|
|
if ((p_ble_evt->header.evt_id >= BLE_GATTC_EVT_BASE) && (p_ble_evt->header.evt_id <= BLE_GATTC_EVT_LAST))
|
|
{
|
|
if ((p_ble_evt->evt.gattc_evt.gatt_status == BLE_GATT_STATUS_ATTERR_INSUF_ENCRYPTION) ||
|
|
(p_ble_evt->evt.gattc_evt.gatt_status == BLE_GATT_STATUS_ATTERR_INSUF_AUTHENTICATION))
|
|
{
|
|
NRF_LOG_INFO("GATTC procedure (evt id 0x%x) failed because it needs encryption. Bonding: conn_handle=%d",
|
|
p_ble_evt->header.evt_id,
|
|
p_ble_evt->evt.gattc_evt.conn_handle);
|
|
conn_secure(p_ble_evt->evt.gattc_evt.conn_handle, true);
|
|
}
|
|
}
|
|
}
|