From 3061ecca3d0fdfb87dabbf5f63c9e06c2a30f53a Mon Sep 17 00:00:00 2001 From: Trygve Laugstøl Date: Thu, 23 Aug 2018 17:08:59 +0200 Subject: o Initial import. --- .../components/ble/common/ble_conn_params.c | 572 +++++++++++++++++++++ 1 file changed, 572 insertions(+) create mode 100644 thirdparty/nRF5_SDK_15.0.0_a53641a/components/ble/common/ble_conn_params.c (limited to 'thirdparty/nRF5_SDK_15.0.0_a53641a/components/ble/common/ble_conn_params.c') diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/ble/common/ble_conn_params.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/ble/common/ble_conn_params.c new file mode 100644 index 0000000..4cb78f4 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/ble/common/ble_conn_params.c @@ -0,0 +1,572 @@ +/** + * Copyright (c) 2012 - 2018, 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 "sdk_common.h" +#if NRF_MODULE_ENABLED(NRF_BLE_CONN_PARAMS) +#include +#include "nrf.h" +#include "sdk_errors.h" +#include "ble_hci.h" +#include "ble_err.h" +#include "ble_conn_params.h" +#include "ble_srv_common.h" +#include "ble_conn_state.h" +#include "nrf_sdh_ble.h" +#include "app_timer.h" +#include "app_util.h" + + +#define NRF_BLE_CONN_PARAMS_INSTANCE_COUNT NRF_SDH_BLE_PERIPHERAL_LINK_COUNT //!< The number of @ref ble_conn_params_instance_t instances kept by the conn_params module. + +#if (NRF_BLE_CONN_PARAMS_INSTANCE_COUNT < 1) +#error Invalid NRF_SDH_BLE_PERIPHERAL_LINK_COUNT value. Set it in SDK config (nrf_sdh_ble). +#endif + +/** @brief Each peripheral link has such an instance associated with it. + */ +typedef struct +{ + uint16_t conn_handle; //!< The connection handle of this link. If this is @ref BLE_CONN_HANDLE_INVALID, the instance is free. + app_timer_id_t timer_id; //!< The ID of the timer associated with this link. + uint8_t update_count; //!< The number of times the connection parameters have been attempted negotiated on this link. + uint8_t params_ok; //!< Whether the current connection parameters on this link are acceptable according to the @p preferred_conn_params, and configured maximum deviations. + ble_gap_conn_params_t preferred_conn_params; //!< The desired connection parameters for this link. +} ble_conn_params_instance_t; + +static app_timer_t m_timer_data[NRF_BLE_CONN_PARAMS_INSTANCE_COUNT] = {{{0}}}; //!< Data needed for timers. +static ble_conn_params_instance_t m_conn_params_instances[NRF_BLE_CONN_PARAMS_INSTANCE_COUNT] = {{0}}; //!< Configuration data for each connection. +static ble_conn_params_init_t m_conn_params_config; //!< Configuration as provided by the application during intialization. +static ble_gap_conn_params_t m_preferred_conn_params; //!< The preferred connection parameters as specified during initialization. +//lint -esym(551, m_preferred_conn_params) "Not accessed" + + +/**@brief Function for retrieving the conn_params instance belonging to a conn_handle + * + * @params[in] conn_handle The connection handle to retrieve the instance of. + * + * @return A pointer to the instance, or NULL if no instance was found with that conn_handle. + */ +static ble_conn_params_instance_t * instance_get(uint16_t conn_handle) +{ + //lint -save -e681 "Loop not entered" when NRF_BLE_CONN_PARAMS_INSTANCE_COUNT is 0 + for (uint32_t i = 0; i < NRF_BLE_CONN_PARAMS_INSTANCE_COUNT; i++) + { + if (m_conn_params_instances[i].conn_handle == conn_handle) + { + return &m_conn_params_instances[i]; + } + } + //lint -restore + return NULL; +} + + +/**@brief Function for initializing an instance, and associating it with a conn_handle. + * + * @params[in] p_instance The instance to initialize and associate. + * @params[in] conn_handle The connection handle to associate with. + */ +static __INLINE void instance_claim(ble_conn_params_instance_t * p_instance, uint16_t conn_handle) +{ + p_instance->conn_handle = conn_handle; + p_instance->update_count = 0; + p_instance->preferred_conn_params = m_preferred_conn_params; +} + + +/**@brief Function for freeing an instance. + * + * @params[in] p_instance The instance to free. + */ +static __INLINE void instance_free(ble_conn_params_instance_t * p_instance) +{ + p_instance->conn_handle = BLE_CONN_HANDLE_INVALID; +} + + +/**@brief Function for validating a set of connection parameters against the preferred parameters. + * + * @param[in] p_preferred_conn_params The desired parameters. + * @param[in] p_actual_conn_params The parameters to validate. + * @param[in] max_slave_latency_err The amount of discrepancy in slave latency, in number of + * connection intervals, that will be accepted. + * @param[in] max_sup_timeout_err The amount of discrepancy in supervision timeout, in tens of + * milliseconds, that will be accepted. + * + * @return Whether the params in @p p_actual_conn_params are acceptable given the other parameters. + */ +static bool is_conn_params_ok(ble_gap_conn_params_t const * p_preferred_conn_params, + ble_gap_conn_params_t const * p_actual_conn_params, + uint16_t max_slave_latency_err, + uint16_t max_sup_timeout_err) +{ + uint32_t max_allowed_sl = p_preferred_conn_params->slave_latency + max_slave_latency_err; + uint32_t min_allowed_sl = p_preferred_conn_params->slave_latency + - MIN(max_slave_latency_err, p_preferred_conn_params->slave_latency); + uint32_t max_allowed_to = p_preferred_conn_params->conn_sup_timeout + max_sup_timeout_err; + uint32_t min_allowed_to = p_preferred_conn_params->conn_sup_timeout + - MIN(max_sup_timeout_err, p_preferred_conn_params->conn_sup_timeout); + + // 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_actual_conn_params->max_conn_interval < p_preferred_conn_params->min_conn_interval) + || (p_actual_conn_params->max_conn_interval > p_preferred_conn_params->max_conn_interval)) + { + return false; + } + + // Check if slave latency is within the acceptable deviation. + if ((p_actual_conn_params->slave_latency < min_allowed_sl) + || (p_actual_conn_params->slave_latency > max_allowed_sl)) + { + return false; + } + + // Check if supervision timeout is within the acceptable deviation. + if ((p_actual_conn_params->conn_sup_timeout < min_allowed_to) + || (p_actual_conn_params->conn_sup_timeout > max_allowed_to)) + { + return false; + } + + return true; +} + + +static void send_error_evt(ret_code_t err_code) +{ + if (m_conn_params_config.error_handler != NULL) + { + m_conn_params_config.error_handler(err_code); + } +} + + +/**@brief Function for sending a conn_param_update request on-air, and handling errors. + * + * @param[in] conn_handle Connection to send request on. + * @param[in] p_new_conn_params Connection parameters to request. + * + * @return Whether the request was successfully sent. + */ +static bool send_update_request(uint16_t conn_handle, ble_gap_conn_params_t * p_new_conn_params) +{ + ret_code_t err_code; + + err_code = sd_ble_gap_conn_param_update(conn_handle, p_new_conn_params); + if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_BUSY)) // NRF_ERROR_BUSY means another conn_param_update request is pending. + { + send_error_evt(err_code); + } + + return (err_code == NRF_SUCCESS); +} + + +/**@brief Function called after conn_params_update_delay has happened. This is triggered by app_timer. + * + * @param[in] p_context Context identifying which connection this is for. + */ +static void update_timeout_handler(void * p_context) +{ + uint32_t conn_handle = (uint32_t)p_context; + ble_conn_params_instance_t * p_instance = instance_get(conn_handle); + + if (p_instance != NULL) + { + // Check if we have reached the maximum number of attempts + if (p_instance->update_count < m_conn_params_config.max_conn_params_update_count) + { + bool update_sent = send_update_request(conn_handle, &p_instance->preferred_conn_params); + if (update_sent) + { + p_instance->update_count++; + } + } + else + { + p_instance->update_count = 0; + + // Negotiation failed, disconnect automatically if this has been configured + if (m_conn_params_config.disconnect_on_fail) + { + ret_code_t err_code; + + err_code = sd_ble_gap_disconnect(conn_handle, BLE_HCI_CONN_INTERVAL_UNACCEPTABLE); + if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_INVALID_STATE)) // NRF_ERROR_INVALID_STATE means disconnect is already in progress. + { + send_error_evt(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; + evt.conn_handle = conn_handle; + m_conn_params_config.evt_handler(&evt); + } + } + } +} + + +ret_code_t ble_conn_params_init(const ble_conn_params_init_t * p_init) +{ + ret_code_t err_code; + + VERIFY_PARAM_NOT_NULL(p_init); + + m_conn_params_config = *p_init; + m_conn_params_config.p_conn_params = &m_preferred_conn_params; + + if (p_init->p_conn_params != NULL) + { + // Set the connection params in stack. + err_code = sd_ble_gap_ppcp_set(p_init->p_conn_params); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + m_preferred_conn_params = *p_init->p_conn_params; + } + else + { + // Get the (default) connection params from stack. + err_code = sd_ble_gap_ppcp_get(&m_preferred_conn_params); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + } + + //lint -save -e681 "Loop not entered" when NRF_BLE_CONN_PARAMS_INSTANCE_COUNT is 0 + for (uint32_t i = 0; i < NRF_BLE_CONN_PARAMS_INSTANCE_COUNT; i++) + { + ble_conn_params_instance_t * p_instance = &m_conn_params_instances[i]; + + instance_free(p_instance); + p_instance->timer_id = &m_timer_data[i]; + + err_code = app_timer_create(&p_instance->timer_id, + APP_TIMER_MODE_SINGLE_SHOT, + update_timeout_handler); + if (err_code != NRF_SUCCESS) + { + return NRF_ERROR_INTERNAL; + } + } + //lint -restore + + return NRF_SUCCESS; +} + + +ret_code_t ble_conn_params_stop(void) +{ + ret_code_t err_code; + + //lint -save -e681 "Loop not entered" when NRF_BLE_CONN_PARAMS_INSTANCE_COUNT is 0 + for (uint32_t i = 0; i < NRF_BLE_CONN_PARAMS_INSTANCE_COUNT; i++) + { + err_code = app_timer_stop(m_conn_params_instances[i].timer_id); + switch (err_code) + { + case NRF_SUCCESS: + /* do nothing */ + break; + + case NRF_ERROR_INVALID_STATE: + /* do nothing */ + break; + + case NRF_ERROR_NO_MEM: + return NRF_ERROR_BUSY; + + case NRF_ERROR_INVALID_PARAM: + /* fallthrough */ + default: + return NRF_ERROR_INTERNAL; + } + } + //lint -restore + return NRF_SUCCESS; +} + + +/**@brief Function for taking appropriate action based on the current state of connection parameters. + * + * @param[in] conn_handle Connection to handle. + * @param[in] p_instance Configuration for the connection. + */ +static void conn_params_negotiation(uint16_t conn_handle, ble_conn_params_instance_t * p_instance) + { + // Start negotiation if the received connection parameters are not acceptable + if (!p_instance->params_ok) + { + ret_code_t err_code; + uint32_t timeout_ticks; + + if (p_instance->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(p_instance->timer_id, timeout_ticks, (void *)(uint32_t)conn_handle); + if (err_code != NRF_SUCCESS) + { + send_error_evt(err_code); + } + } + else + { + p_instance->update_count = 0; + + // Notify the application that the procedure has succeeded + if (m_conn_params_config.evt_handler != NULL) + { + ble_conn_params_evt_t evt; + + evt.evt_type = BLE_CONN_PARAMS_EVT_SUCCEEDED; + evt.conn_handle = conn_handle; + m_conn_params_config.evt_handler(&evt); + } + } +} + + +/**@brief Function for handling a connection event from the SoftDevice. + * + * @param[in] p_ble_evt Event from the SoftDevice. + */ +static void on_connect(ble_evt_t const * p_ble_evt) +{ + uint8_t role = p_ble_evt->evt.gap_evt.params.connected.role; + uint16_t conn_handle = p_ble_evt->evt.gap_evt.conn_handle; + + if (role != BLE_GAP_ROLE_PERIPH) + { + return; + } + + ble_conn_params_instance_t * p_instance = instance_get(BLE_CONN_HANDLE_INVALID); + + if (p_instance == NULL) + { + send_error_evt(NRF_ERROR_NO_MEM); + return; + } + + instance_claim(p_instance, conn_handle); + p_instance->params_ok = is_conn_params_ok(&p_instance->preferred_conn_params, + &p_ble_evt->evt.gap_evt.params.connected.conn_params, + NRF_BLE_CONN_PARAMS_MAX_SLAVE_LATENCY_DEVIATION, + NRF_BLE_CONN_PARAMS_MAX_SUPERVISION_TIMEOUT_DEVIATION); + + // 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(conn_handle, p_instance); + } +} + + +/**@brief Function for handling a disconnection event from the SoftDevice. + * + * @param[in] p_ble_evt Event from the SoftDevice. + */ +static void on_disconnect(ble_evt_t const * p_ble_evt) +{ + ret_code_t err_code; + uint16_t conn_handle = p_ble_evt->evt.gap_evt.conn_handle; + ble_conn_params_instance_t * p_instance = instance_get(conn_handle); + + if (p_instance != NULL) + { + // Stop timer if running + err_code = app_timer_stop(p_instance->timer_id); + if (err_code != NRF_SUCCESS) + { + send_error_evt(err_code); + } + + instance_free(p_instance); + } +} + + +/**@brief Function for handling a GATT write event from the SoftDevice. + * + * @details To provide the start_on_notify_cccd_handle functionality. + * + * @param[in] p_ble_evt Event from the SoftDevice. + */ +static void on_write(ble_evt_t const * p_ble_evt) +{ + ble_gatts_evt_write_t const * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write; + + // Check if this is the correct CCCD + if ( + (p_evt_write->handle == m_conn_params_config.start_on_notify_cccd_handle) + && + (p_evt_write->len == 2) + ) + { + uint16_t conn_handle = p_ble_evt->evt.gap_evt.conn_handle; + ble_conn_params_instance_t * p_instance = instance_get(conn_handle); + + if (p_instance != NULL) + { + // 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(conn_handle, p_instance); + } + else + { + ret_code_t err_code; + + // Stop timer if running + err_code = app_timer_stop(p_instance->timer_id); + if (err_code != NRF_SUCCESS) + { + send_error_evt(err_code); + } + } + } + } +} + + +/**@brief Function for handling a connection parameter update event from the SoftDevice. + * + * @details This event means the peer central has changed the connection parameters or declined our + * request. + * + * @param[in] p_ble_evt Event from the SoftDevice. + */ +static void on_conn_params_update(ble_evt_t const * p_ble_evt) +{ + uint16_t conn_handle = p_ble_evt->evt.gap_evt.conn_handle; + ble_conn_params_instance_t * p_instance = instance_get(conn_handle); + + if (p_instance != NULL) + { + p_instance->params_ok = is_conn_params_ok( + &p_instance->preferred_conn_params, + &p_ble_evt->evt.gap_evt.params.conn_param_update.conn_params, + NRF_BLE_CONN_PARAMS_MAX_SLAVE_LATENCY_DEVIATION, + NRF_BLE_CONN_PARAMS_MAX_SUPERVISION_TIMEOUT_DEVIATION); + + conn_params_negotiation(conn_handle, p_instance); + } +} + + +/** + * @brief Function for handling BLE events. + * + * @param[in] p_ble_evt Event received from the BLE stack. + * @param[in] p_context Context. + */ +static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context) +{ + 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; + } +} + + +ret_code_t ble_conn_params_change_conn_params(uint16_t conn_handle, + ble_gap_conn_params_t * p_new_params) +{ + ret_code_t err_code = BLE_ERROR_INVALID_CONN_HANDLE; + ble_conn_params_instance_t * p_instance = instance_get(conn_handle); + + if (p_new_params == NULL) + { + p_new_params = &m_preferred_conn_params; + } + + if (p_instance != NULL) + { + // Send request to central. + err_code = sd_ble_gap_conn_param_update(conn_handle, p_new_params); + if (err_code == NRF_SUCCESS) + { + p_instance->params_ok = false; + p_instance->update_count = 1; + p_instance->preferred_conn_params = *p_new_params; + } + } + + return err_code; +} + +NRF_SDH_BLE_OBSERVER(m_ble_observer, BLE_CONN_PARAMS_BLE_OBSERVER_PRIO, ble_evt_handler, NULL); + +#endif //ENABLED -- cgit v1.2.3