From 02404d350d3c0fac4d31f3b4c594435bd3cd7007 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eivind=20J=C3=B8lsgard?= Date: Thu, 21 May 2026 14:50:21 +0200 Subject: [PATCH] lib: bluetooth: ble_conn_params: add conn param update request MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST to ble_conn_params library. * Add role field to the links structure. * Rename ppcp to pcp because this field will now also be used for central and not only peripheral. * The override function will now modify the window of acceptable connection parameters used when checking if requested parameters are acceptable. Co-authored-by: Andreas Moltumyr Signed-off-by: Eivind Jølsgard --- .../release_notes/release_notes_changelog.rst | 9 ++- lib/bluetooth/ble_conn_params/conn_param.c | 81 +++++++++++++------ samples/bluetooth/ble_hrs_central/src/main.c | 10 --- samples/bluetooth/ble_nus_central/src/main.c | 9 --- .../bluetooth/ble_conn_params/CMakeLists.txt | 2 +- .../lib/bluetooth/ble_conn_params/Kconfig | 3 + .../ble_conn_params/src/unity_test.c | 62 ++++++++++++++ 7 files changed, 132 insertions(+), 44 deletions(-) diff --git a/doc/nrf-bm/release_notes/release_notes_changelog.rst b/doc/nrf-bm/release_notes/release_notes_changelog.rst index 2e71617fcb..46b2fc6792 100644 --- a/doc/nrf-bm/release_notes/release_notes_changelog.rst +++ b/doc/nrf-bm/release_notes/release_notes_changelog.rst @@ -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: diff --git a/lib/bluetooth/ble_conn_params/conn_param.c b/lib/bluetooth/ble_conn_params/conn_param.c index e898fd3551..e110e88f36 100644 --- a/lib/bluetooth/ble_conn_params/conn_param.c +++ b/lib/bluetooth/ble_conn_params/conn_param.c @@ -14,8 +14,8 @@ 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, @@ -23,8 +23,9 @@ static const ble_gap_conn_params_t ppcp = { }; 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, @@ -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 || @@ -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) { @@ -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); } } @@ -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, @@ -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; @@ -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; @@ -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); @@ -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; } @@ -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; diff --git a/samples/bluetooth/ble_hrs_central/src/main.c b/samples/bluetooth/ble_hrs_central/src/main.c index 41548ed536..d202931ba1 100644 --- a/samples/bluetooth/ble_hrs_central/src/main.c +++ b/samples/bluetooth/ble_hrs_central/src/main.c @@ -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, diff --git a/samples/bluetooth/ble_nus_central/src/main.c b/samples/bluetooth/ble_nus_central/src/main.c index ea0eebf07f..313772248b 100644 --- a/samples/bluetooth/ble_nus_central/src/main.c +++ b/samples/bluetooth/ble_nus_central/src/main.c @@ -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); diff --git a/tests/unit/lib/bluetooth/ble_conn_params/CMakeLists.txt b/tests/unit/lib/bluetooth/ble_conn_params/CMakeLists.txt index 586977c81d..e0ca4c866e 100644 --- a/tests/unit/lib/bluetooth/ble_conn_params/CMakeLists.txt +++ b/tests/unit/lib/bluetooth/ble_conn_params/CMakeLists.txt @@ -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") diff --git a/tests/unit/lib/bluetooth/ble_conn_params/Kconfig b/tests/unit/lib/bluetooth/ble_conn_params/Kconfig index e3bdda1e66..b92b358d3c 100644 --- a/tests/unit/lib/bluetooth/ble_conn_params/Kconfig +++ b/tests/unit/lib/bluetooth/ble_conn_params/Kconfig @@ -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" diff --git a/tests/unit/lib/bluetooth/ble_conn_params/src/unity_test.c b/tests/unit/lib/bluetooth/ble_conn_params/src/unity_test.c index 703191cc79..08476e35f4 100644 --- a/tests/unit/lib/bluetooth/ble_conn_params/src/unity_test.c +++ b/tests/unit/lib/bluetooth/ble_conn_params/src/unity_test.c @@ -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) {