Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion doc/nrf-bm/release_notes/release_notes_changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,15 @@ Libraries

* :ref:`lib_ble_conn_params`:

* Added support for selecting more than one PHY mode (1M, 2M, and Coded) when setting the PHY mode preference with Kconfig.
* Added:

* Support for selecting more than one PHY mode (1M, 2M, and Coded) when setting the PHY mode preference with Kconfig.
* Support for the :c:macro:`BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST` SoftDevice event.

* Updated the :c:func:`ble_conn_params_phy_radio_mode_set` function to return :c:macro:`NRF_ERROR_INVALID_PARAM` if the ``phy_pref`` parameter contains PHY modes not supported by the SoftDevice.
* Updated the :c:func:`ble_conn_params_override` function to allow runtime overrides of the acceptable connection parameter window used when validating peripheral requests to change the connection parameters.
Previously, this window was set statically by Kconfig options and was not possible to override at runtime.


* Fixed:

Expand Down
81 changes: 58 additions & 23 deletions lib/bluetooth/ble_conn_params/conn_param.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,18 @@ LOG_MODULE_DECLARE(ble_conn_params, CONFIG_BLE_CONN_PARAMS_LOG_LEVEL);

extern void ble_conn_params_event_send(const struct ble_conn_params_evt *evt);

/* Preferred connection parameters */
static const ble_gap_conn_params_t ppcp = {
/* Preferred connection parameters. */
static const ble_gap_conn_params_t pcp_default = {
.min_conn_interval = CONFIG_BLE_CONN_PARAMS_MIN_CONN_INTERVAL,
.max_conn_interval = CONFIG_BLE_CONN_PARAMS_MAX_CONN_INTERVAL,
.slave_latency = CONFIG_BLE_CONN_PARAMS_PERIPHERAL_LATENCY,
.conn_sup_timeout = CONFIG_BLE_CONN_PARAMS_SUP_TIMEOUT,
};

static struct {
ble_gap_conn_params_t ppcp;
ble_gap_conn_params_t pcp;
uint8_t retries;
uint8_t role;
} links[CONFIG_NRF_SDH_BLE_TOTAL_LINK_COUNT] = {
[0 ... CONFIG_NRF_SDH_BLE_TOTAL_LINK_COUNT - 1] = {
.retries = CONFIG_BLE_CONN_PARAMS_NEGOTIATION_RETRIES,
Expand All @@ -37,33 +38,34 @@ static void conn_params_negotiate(uint16_t conn_handle, int idx)

LOG_DBG("Negotiating desired parameters with peer %#x", conn_handle);

nrf_err = sd_ble_gap_conn_param_update(conn_handle, &links[idx].ppcp);
nrf_err = sd_ble_gap_conn_param_update(conn_handle, &links[idx].pcp);
if (nrf_err) {
LOG_ERR("Failed to request GAP connection parameters update, nrf_error %#x",
nrf_err);
}
}

static bool conn_params_can_agree(const ble_gap_conn_params_t *conn_params)
static bool conn_params_can_agree(const ble_gap_conn_params_t *conn_params, int idx)
{
uint16_t peripheral_latency_min;
uint16_t peripheral_latency_max;
uint16_t conn_sup_timeout_min;
uint16_t conn_sup_timeout_max;
const ble_gap_conn_params_t *const pcp = &links[idx].pcp;

/* The max_conn_interval field in the event contains the client connection interval */
if ((conn_params->max_conn_interval < ppcp.min_conn_interval) ||
(conn_params->max_conn_interval > ppcp.max_conn_interval)) {
/* The max_conn_interval field in the event contains the central's connection interval. */
if ((conn_params->max_conn_interval < pcp->min_conn_interval) ||
(conn_params->max_conn_interval > pcp->max_conn_interval)) {
LOG_DBG("Could not agree on connection interval %#x",
conn_params->max_conn_interval);
return false;
}

peripheral_latency_min =
CLAMP(ppcp.slave_latency - CONFIG_BLE_CONN_PARAMS_MAX_PERIPHERAL_LATENCY_DEVIATION,
CLAMP(pcp->slave_latency - CONFIG_BLE_CONN_PARAMS_MAX_PERIPHERAL_LATENCY_DEVIATION,
0, UINT16_MAX);
peripheral_latency_max =
CLAMP(ppcp.slave_latency + CONFIG_BLE_CONN_PARAMS_MAX_PERIPHERAL_LATENCY_DEVIATION,
CLAMP(pcp->slave_latency + CONFIG_BLE_CONN_PARAMS_MAX_PERIPHERAL_LATENCY_DEVIATION,
0, UINT16_MAX);

if (conn_params->slave_latency < peripheral_latency_min ||
Expand All @@ -73,11 +75,11 @@ static bool conn_params_can_agree(const ble_gap_conn_params_t *conn_params)
}

conn_sup_timeout_min =
CLAMP(ppcp.conn_sup_timeout - CONFIG_BLE_CONN_PARAMS_MAX_SUP_TIMEOUT_DEVIATION, 0,
UINT16_MAX);
CLAMP(pcp->conn_sup_timeout - CONFIG_BLE_CONN_PARAMS_MAX_SUP_TIMEOUT_DEVIATION,
0, UINT16_MAX);
conn_sup_timeout_max =
CLAMP(ppcp.conn_sup_timeout + CONFIG_BLE_CONN_PARAMS_MAX_SUP_TIMEOUT_DEVIATION, 0,
UINT16_MAX);
CLAMP(pcp->conn_sup_timeout + CONFIG_BLE_CONN_PARAMS_MAX_SUP_TIMEOUT_DEVIATION,
0, UINT16_MAX);

if (conn_params->conn_sup_timeout < conn_sup_timeout_min ||
conn_params->conn_sup_timeout > conn_sup_timeout_max) {
Expand All @@ -92,12 +94,13 @@ static bool conn_params_can_agree(const ble_gap_conn_params_t *conn_params)
static void on_connected(uint16_t conn_handle, int idx, const ble_gap_evt_connected_t *evt)
{
links[idx].retries = CONFIG_BLE_CONN_PARAMS_NEGOTIATION_RETRIES;
links[idx].role = evt->role;

/* Copy default ppcp */
memcpy(&links[idx].ppcp, &ppcp, sizeof(ble_gap_conn_params_t));
/* Copy default pcp. */
memcpy(&links[idx].pcp, &pcp_default, sizeof(ble_gap_conn_params_t));

if (evt->role == BLE_GAP_ROLE_PERIPH) {
if (!conn_params_can_agree(&evt->conn_params)) {
if (!conn_params_can_agree(&evt->conn_params, idx)) {
conn_params_negotiate(conn_handle, idx);
}
}
Expand All @@ -113,7 +116,14 @@ static void on_conn_params_update(uint16_t conn_handle, int idx,
evt->conn_params.slave_latency,
evt->conn_params.conn_sup_timeout);

if (conn_params_can_agree(&evt->conn_params)) {
#if defined(CONFIG_SOFTDEVICE_CENTRAL)
if (links[idx].role == BLE_GAP_ROLE_CENTRAL) {
/* Central decides connection parameters. No retry logic required. */
return;
}
#endif /* CONFIG_SOFTDEVICE_CENTRAL */

if (conn_params_can_agree(&evt->conn_params, idx)) {
const struct ble_conn_params_evt app_evt = {
.evt_type = BLE_CONN_PARAMS_EVT_UPDATED,
.conn_handle = conn_handle,
Expand Down Expand Up @@ -144,6 +154,25 @@ static void on_conn_params_update(uint16_t conn_handle, int idx,
}
}

#if defined(CONFIG_SOFTDEVICE_CENTRAL)
static void on_conn_params_update_request(uint16_t conn_handle, int idx,
const ble_gap_evt_conn_param_update_request_t *evt)
{
uint32_t nrf_err;
const ble_gap_conn_params_t *conn_params = NULL;

/* Use the requested parameters if they are acceptable. Otherwise pass NULL to reject. */
if (conn_params_can_agree(&evt->conn_params, idx)) {
conn_params = &evt->conn_params;
}

nrf_err = sd_ble_gap_conn_param_update(conn_handle, conn_params);
if (nrf_err) {
LOG_ERR("Failed to update connection params, nrf_error %#x", nrf_err);
}
}
#endif /* CONFIG_SOFTDEVICE_CENTRAL */

static void on_ble_evt(const ble_evt_t *evt, void *ctx)
{
const uint16_t conn_handle = evt->evt.common_evt.conn_handle;
Expand All @@ -164,6 +193,12 @@ static void on_ble_evt(const ble_evt_t *evt, void *ctx)
on_conn_params_update(conn_handle, idx, &evt->evt.gap_evt.params.conn_param_update);
break;

#if defined(CONFIG_SOFTDEVICE_CENTRAL)
case BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST:
on_conn_params_update_request(conn_handle, idx,
&evt->evt.gap_evt.params.conn_param_update_request);
break;
#endif /* CONFIG_SOFTDEVICE_CENTRAL */
default:
/* Ignore */
break;
Expand All @@ -183,7 +218,7 @@ static int on_state_evt(enum nrf_sdh_state_evt evt, void *ctx)
return 0;
}

nrf_err = sd_ble_gap_ppcp_set(&ppcp);
nrf_err = sd_ble_gap_ppcp_set(&pcp_default);
if (nrf_err) {
LOG_ERR("Failed to set preferred conn params, nrf_error %#x", nrf_err);

Expand All @@ -194,9 +229,9 @@ static int on_state_evt(enum nrf_sdh_state_evt evt, void *ctx)
}

LOG_DBG("conn. interval min %u max %u, peripheral latency %u, sup. timeout %u",
ppcp.min_conn_interval, ppcp.max_conn_interval,
ppcp.slave_latency,
ppcp.conn_sup_timeout);
pcp_default.min_conn_interval, pcp_default.max_conn_interval,
pcp_default.slave_latency,
pcp_default.conn_sup_timeout);

return 0;
}
Expand All @@ -214,7 +249,7 @@ uint32_t ble_conn_params_override(uint16_t conn_handle, const ble_gap_conn_param
return NRF_ERROR_NULL;
}

links[idx].ppcp = *conn_params;
links[idx].pcp = *conn_params;
nrf_err = sd_ble_gap_conn_param_update(conn_handle, conn_params);
if (nrf_err) {
return nrf_err;
Expand Down
10 changes: 0 additions & 10 deletions samples/bluetooth/ble_hrs_central/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -158,16 +158,6 @@ static void on_ble_evt(const ble_evt_t *ble_evt, void *ctx)
}
break;

case BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST:
LOG_INF("ble gap event connection parameter update request");
nrf_err = sd_ble_gap_conn_param_update(
gap_evt->conn_handle,
&gap_evt->params.conn_param_update_request.conn_params);
if (nrf_err) {
LOG_ERR("Failed to update connection params, nrf_error %#x", nrf_err);
}
break;

case BLE_GATTC_EVT_TIMEOUT:
LOG_INF("GATT Client Timeout");
nrf_err = sd_ble_gap_disconnect(ble_evt->evt.gattc_evt.conn_handle,
Expand Down
9 changes: 0 additions & 9 deletions samples/bluetooth/ble_nus_central/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -215,15 +215,6 @@ static void on_ble_evt(ble_evt_t const *ble_evt, void *context)
LOG_ERR("gap_sec_params_reply failed, nrf_error %#x", nrf_err);
}
break;
case BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST:
/** Accepting parameters requested by peer. */
nrf_err = sd_ble_gap_conn_param_update(
gap_evt->conn_handle,
&gap_evt->params.conn_param_update_request.conn_params);
if (nrf_err) {
LOG_ERR("gap_conn_param_update failed, nrf_error %#x", nrf_err);
}
break;
case BLE_GAP_EVT_PHY_UPDATE_REQUEST:
LOG_DBG("PHY update request");
nrf_err = sd_ble_gap_phy_update(ble_evt->evt.gap_evt.conn_handle, &phys);
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/lib/bluetooth/ble_conn_params/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})

project(unit_test_ble_conn_params)

set(SOFTDEVICE_VARIANT "s115")
set(SOFTDEVICE_VARIANT "s145")
set(SOFTDEVICE_INCLUDE_DIR "${ZEPHYR_NRF_BM_MODULE_DIR}/components/softdevice/nrf54l/\
${SOFTDEVICE_VARIANT}/${SOFTDEVICE_VARIANT}_API/include")

Expand Down
3 changes: 3 additions & 0 deletions tests/unit/lib/bluetooth/ble_conn_params/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,7 @@ config NRF_SDH_BLE_GAP_EVENT_LENGTH
config NRF_SDH_BLE_GATT_MAX_MTU_SIZE
default 24

config SOFTDEVICE_CENTRAL
default y

source "Kconfig.zephyr"
62 changes: 62 additions & 0 deletions tests/unit/lib/bluetooth/ble_conn_params/src/unity_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -996,6 +996,68 @@ void test_ble_evt_conn_param_update_busy(void)
TEST_ASSERT_EQUAL(CONN_HANDLE, app_evt.conn_handle);
}

void test_ble_evt_conn_param_update_request_accepted(void)
{
ble_evt_t ble_evt = {
.header.evt_id = BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST,
.evt.gap_evt = {
.conn_handle = CONN_HANDLE,
/* Requested conn params are ignored by the central */
.params.conn_param_update_request.conn_params = {
.conn_sup_timeout = CONFIG_BLE_CONN_PARAMS_SUP_TIMEOUT,
.min_conn_interval = CONFIG_BLE_CONN_PARAMS_MIN_CONN_INTERVAL,
.max_conn_interval = CONFIG_BLE_CONN_PARAMS_MAX_CONN_INTERVAL,
.slave_latency = CONFIG_BLE_CONN_PARAMS_PERIPHERAL_LATENCY,
},
},
};
const ble_gap_conn_params_t conn_params_expected = {
.conn_sup_timeout = CONFIG_BLE_CONN_PARAMS_SUP_TIMEOUT,
.min_conn_interval = CONFIG_BLE_CONN_PARAMS_MIN_CONN_INTERVAL,
.max_conn_interval = CONFIG_BLE_CONN_PARAMS_MAX_CONN_INTERVAL,
.slave_latency = CONFIG_BLE_CONN_PARAMS_PERIPHERAL_LATENCY,
};

/* Calls from BLE observers. */
__cmock_nrf_sdh_ble_idx_get_ExpectAndReturn(CONN_HANDLE, 0);
__cmock_nrf_sdh_ble_idx_get_ExpectAndReturn(CONN_HANDLE, 0);
__cmock_nrf_sdh_ble_idx_get_ExpectAndReturn(CONN_HANDLE, 0);
__cmock_nrf_sdh_ble_idx_get_ExpectAndReturn(CONN_HANDLE, 0);

__cmock_sd_ble_gap_conn_param_update_ExpectWithArrayAndReturn(
CONN_HANDLE, &conn_params_expected, 1, NRF_SUCCESS);

ble_evt_send(&ble_evt);
}

void test_ble_evt_conn_param_update_request_rejected(void)
{
ble_evt_t ble_evt = {
.header.evt_id = BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST,
.evt.gap_evt = {
.conn_handle = CONN_HANDLE,
/* Requested conn params are ignored by the central */
.params.conn_param_update_request.conn_params = {
.conn_sup_timeout = 0x12,
.min_conn_interval = 0x13,
.max_conn_interval = 0x14,
.slave_latency = 0x15,
},
},
};

/* Calls from BLE observers. */
__cmock_nrf_sdh_ble_idx_get_ExpectAndReturn(CONN_HANDLE, 0);
__cmock_nrf_sdh_ble_idx_get_ExpectAndReturn(CONN_HANDLE, 0);
__cmock_nrf_sdh_ble_idx_get_ExpectAndReturn(CONN_HANDLE, 0);
__cmock_nrf_sdh_ble_idx_get_ExpectAndReturn(CONN_HANDLE, 0);

__cmock_sd_ble_gap_conn_param_update_ExpectWithArrayAndReturn(
CONN_HANDLE, NULL, 1, NRF_SUCCESS);

ble_evt_send(&ble_evt);
}

void test_ble_evt_data_length_update(void)
{

Expand Down
Loading