Nordic stack and drivers for the mbed BLE API
Dependents: Sensen-classic-v8-IAQ_copy_4
Fork of nRF51822 by
TARGET_MCU_NRF51822/sdk/source/ble/common/ble_conn_params.cpp
- Committer:
- amithy
- Date:
- 2017-11-10
- Revision:
- 639:7f459d371b75
- Parent:
- 638:c90ae1400bf2
File content as of revision 639:7f459d371b75:
/* * 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 "ble_srv_common.h" #include "app_util.h" #ifdef USE_APP_TIMER #include "app_timer.h" #else #ifdef YOTTA_CFG_MBED_OS #include "mbed-drivers/mbed.h" #else #include "mbed.h" #endif #endif 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. */ #ifdef USE_APP_TIMER static app_timer_id_t m_conn_params_timer_id; /**< Connection parameters timer. */ #else static Ticker m_conn_params_timer; #endif 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; } } #ifdef USE_APP_TIMER static void update_timeout_handler(void * p_context) { UNUSED_PARAMETER(p_context); #else /* #if !USE_APP_TIMER */ static void update_timeout_handler(void) { m_conn_params_timer.detach(); /* this is supposed to be a single-shot timer callback */ #endif /* #if !USE_APP_TIMER */ 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; #ifdef USE_APP_TIMER return app_timer_create(&m_conn_params_timer_id, APP_TIMER_MODE_SINGLE_SHOT, update_timeout_handler); #else return NRF_SUCCESS; #endif } uint32_t ble_conn_params_stop(void) { #ifdef USE_APP_TIMER return app_timer_stop(m_conn_params_timer_id); #else /* #if !USE_APP_TIMER */ m_conn_params_timer.detach(); return NRF_SUCCESS; #endif /* #if !USE_APP_TIMER */ } 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)) { #ifdef USE_APP_TIMER uint32_t err_code; #endif 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; } #ifdef USE_APP_TIMER 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 m_conn_params_timer.attach(update_timeout_handler, timeout_ticks / 32768); #endif } } 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) { #ifdef USE_APP_TIMER uint32_t err_code; #endif m_conn_handle = BLE_CONN_HANDLE_INVALID; // Stop timer if running m_update_count = 0; // Connection parameters updates should happen during every connection #ifdef USE_APP_TIMER 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); } #else m_conn_params_timer.detach(); #endif } 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 { #ifdef USE_APP_TIMER 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); } #else /* #if !USE_APP_TIMER */ m_conn_params_timer.detach(); #endif /* #if !USE_APP_TIMER */ } } } 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; }