diff --git a/CODEOWNERS b/CODEOWNERS index c181b937568..3ac094b5faf 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -311,6 +311,7 @@ Kconfig* @tejlmand /tests/modules/mcuboot/external_flash/ @hakonfam @sigvartmh /tests/nrf5340_audio/ @koffes @alexsven @erikrobstad @rick1082 @nordic-auko /tests/subsys/audio_module/ @koffes @alexsven @erikrobstad @rick1082 @gWacey +/tests/subsys/bluetooth/controller/ @nrfconnect/ncs-dragoon /tests/subsys/bluetooth/gatt_dm/ @doki-nordic /tests/subsys/bluetooth/mesh/ @ludvigsj /tests/subsys/bluetooth/fast_pair/ @alstrzebonski @MarekPieta @kapi-no diff --git a/Kconfig.nrf b/Kconfig.nrf index 335cad1bd51..8a506ff98c3 100644 --- a/Kconfig.nrf +++ b/Kconfig.nrf @@ -84,10 +84,6 @@ config MCUMGR_TRANSPORT_NETBUF_SIZE default 2475 if MCUMGR_TRANSPORT_BT_REASSEMBLY default 1024 if UPDATEABLE_IMAGE_NUMBER > 1 -# When using HCI on the nRF5340 we need a larger command buffer. -config BT_BUF_CMD_TX_COUNT - default 10 if SOC_COMPATIBLE_NRF5340_CPUAPP || SOC_COMPATIBLE_NRF5340_CPUNET - config INIT_ARCH_HW_AT_BOOT default y help diff --git a/VERSION b/VERSION index 2714f5313ae..3b576c31858 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.6.4 +2.6.4-NCSDK-34113-2-preview1 diff --git a/applications/connectivity_bridge/boards/thingy91_nrf52840.conf b/applications/connectivity_bridge/boards/thingy91_nrf52840.conf index 2407c48f6fb..bc8ca6fd432 100644 --- a/applications/connectivity_bridge/boards/thingy91_nrf52840.conf +++ b/applications/connectivity_bridge/boards/thingy91_nrf52840.conf @@ -19,6 +19,7 @@ CONFIG_BT_GATT_CLIENT=y CONFIG_BT_SMP=y CONFIG_BT_CTLR=y CONFIG_BT_CTLR_RX_BUFFERS=10 +CONFIG_BT_BUF_EVT_RX_COUNT=11 CONFIG_BT_BUF_ACL_TX_COUNT=10 CONFIG_BT_BUF_ACL_TX_SIZE=251 CONFIG_BT_CTLR_DATA_LENGTH_MAX=251 diff --git a/applications/connectivity_bridge/boards/thingy91x_nrf5340_cpuapp.conf b/applications/connectivity_bridge/boards/thingy91x_nrf5340_cpuapp.conf index 2105087bdc1..6a402c5a801 100644 --- a/applications/connectivity_bridge/boards/thingy91x_nrf5340_cpuapp.conf +++ b/applications/connectivity_bridge/boards/thingy91x_nrf5340_cpuapp.conf @@ -17,6 +17,7 @@ CONFIG_BT_BUF_ACL_RX_SIZE=251 CONFIG_BT_NUS=y CONFIG_BT_GATT_CLIENT=y CONFIG_BT_SMP=y +CONFIG_BT_BUF_EVT_RX_COUNT=11 CONFIG_BT_BUF_ACL_TX_COUNT=10 CONFIG_BT_BUF_ACL_TX_SIZE=251 CONFIG_BT_CTLR_DATA_LENGTH_MAX=251 diff --git a/applications/nrf_desktop/configuration/nrf52810dmouse_nrf52810/prj.conf b/applications/nrf_desktop/configuration/nrf52810dmouse_nrf52810/prj.conf index 8d8a6a921e4..62a0757259b 100644 --- a/applications/nrf_desktop/configuration/nrf52810dmouse_nrf52810/prj.conf +++ b/applications/nrf_desktop/configuration/nrf52810dmouse_nrf52810/prj.conf @@ -96,7 +96,9 @@ CONFIG_BT_MAX_PAIRED=2 CONFIG_BT_ID_MAX=3 CONFIG_BT_LL_SW_SPLIT=y -CONFIG_BT_CONN_TX_MAX=4 +CONFIG_BT_BUF_EVT_RX_COUNT=4 +CONFIG_BT_BUF_ACL_TX_COUNT=3 +CONFIG_BT_CONN_TX_MAX=3 CONFIG_BT_CTLR_FAL_SIZE=1 CONFIG_BT_CTLR_RL_SIZE=2 diff --git a/applications/nrf_desktop/configuration/nrf52820dongle_nrf52820/prj.conf b/applications/nrf_desktop/configuration/nrf52820dongle_nrf52820/prj.conf index 5997f5cc691..18bd7074181 100644 --- a/applications/nrf_desktop/configuration/nrf52820dongle_nrf52820/prj.conf +++ b/applications/nrf_desktop/configuration/nrf52820dongle_nrf52820/prj.conf @@ -63,11 +63,13 @@ CONFIG_SIZE_OPTIMIZATIONS=y CONFIG_LED=y CONFIG_LED_GPIO=y -CONFIG_USB_NRFX_EVT_QUEUE_SIZE=16 +CONFIG_USB_NRFX_EVT_QUEUE_SIZE=10 CONFIG_BT_PRIVACY=y CONFIG_BT_LL_SW_SPLIT=y +CONFIG_BT_BUF_EVT_RX_COUNT=4 +CONFIG_BT_BUF_ACL_TX_COUNT=3 CONFIG_BT_BUF_ACL_TX_SIZE=35 CONFIG_BT_CTLR_DATA_LENGTH_MAX=35 CONFIG_BT_L2CAP_TX_BUF_COUNT=6 diff --git a/applications/nrf_desktop/configuration/nrf52833dk_nrf52820/prj.conf b/applications/nrf_desktop/configuration/nrf52833dk_nrf52820/prj.conf index d83bacc0620..035fb9802e0 100644 --- a/applications/nrf_desktop/configuration/nrf52833dk_nrf52820/prj.conf +++ b/applications/nrf_desktop/configuration/nrf52833dk_nrf52820/prj.conf @@ -64,11 +64,13 @@ CONFIG_SIZE_OPTIMIZATIONS=y CONFIG_LED=y CONFIG_LED_GPIO=y -CONFIG_USB_NRFX_EVT_QUEUE_SIZE=16 +CONFIG_USB_NRFX_EVT_QUEUE_SIZE=10 CONFIG_BT_PRIVACY=y CONFIG_BT_LL_SW_SPLIT=y +CONFIG_BT_BUF_EVT_RX_COUNT=4 +CONFIG_BT_BUF_ACL_TX_COUNT=3 CONFIG_BT_BUF_ACL_TX_SIZE=35 CONFIG_BT_CTLR_DATA_LENGTH_MAX=35 CONFIG_BT_L2CAP_TX_BUF_COUNT=6 diff --git a/boards/arm/thingy91_nrf52840/thingy91_nrf52840_defconfig b/boards/arm/thingy91_nrf52840/thingy91_nrf52840_defconfig index 3eca8278eeb..faadfb1f316 100644 --- a/boards/arm/thingy91_nrf52840/thingy91_nrf52840_defconfig +++ b/boards/arm/thingy91_nrf52840/thingy91_nrf52840_defconfig @@ -15,3 +15,6 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_BOOTLOADER_MCUBOOT=y + +# BT +CONFIG_BT_BUF_CMD_TX_COUNT=3 diff --git a/samples/bluetooth/peripheral_lbs/prj_minimal.conf b/samples/bluetooth/peripheral_lbs/prj_minimal.conf index 08aede05b60..7ba3ed93d30 100644 --- a/samples/bluetooth/peripheral_lbs/prj_minimal.conf +++ b/samples/bluetooth/peripheral_lbs/prj_minimal.conf @@ -102,9 +102,9 @@ CONFIG_BT_CTLR_PHY_2M=n # Reduce Bluetooth buffers CONFIG_BT_BUF_EVT_DISCARDABLE_COUNT=1 CONFIG_BT_BUF_EVT_DISCARDABLE_SIZE=43 -CONFIG_BT_BUF_EVT_RX_COUNT=2 CONFIG_BT_CONN_TX_MAX=2 CONFIG_BT_L2CAP_TX_BUF_COUNT=2 +CONFIG_BT_BUF_EVT_RX_COUNT=4 CONFIG_BT_BUF_ACL_TX_COUNT=3 CONFIG_BT_BUF_ACL_TX_SIZE=27 diff --git a/samples/bluetooth/peripheral_uart/prj_minimal.conf b/samples/bluetooth/peripheral_uart/prj_minimal.conf index 806d22cc813..01a412aa071 100644 --- a/samples/bluetooth/peripheral_uart/prj_minimal.conf +++ b/samples/bluetooth/peripheral_uart/prj_minimal.conf @@ -122,9 +122,9 @@ CONFIG_BT_CTLR_PHY_2M=n # Reduce Bluetooth buffers CONFIG_BT_BUF_EVT_DISCARDABLE_COUNT=1 CONFIG_BT_BUF_EVT_DISCARDABLE_SIZE=43 -CONFIG_BT_BUF_EVT_RX_COUNT=2 CONFIG_BT_CONN_TX_MAX=2 CONFIG_BT_L2CAP_TX_BUF_COUNT=2 +CONFIG_BT_BUF_EVT_RX_COUNT=4 CONFIG_BT_BUF_ACL_TX_COUNT=3 CONFIG_BT_BUF_ACL_TX_SIZE=27 diff --git a/samples/bluetooth/throughput/prj.conf b/samples/bluetooth/throughput/prj.conf index 3ceafd62617..3a1b04c4a75 100644 --- a/samples/bluetooth/throughput/prj.conf +++ b/samples/bluetooth/throughput/prj.conf @@ -36,6 +36,7 @@ CONFIG_BT_L2CAP_TX_BUF_COUNT=10 CONFIG_BT_L2CAP_TX_MTU=498 CONFIG_BT_L2CAP_DYNAMIC_CHANNEL=y CONFIG_BT_CONN_TX_MAX=10 +CONFIG_BT_BUF_EVT_RX_COUNT=11 CONFIG_BT_BUF_ACL_TX_COUNT=10 CONFIG_BT_BUF_ACL_TX_SIZE=502 diff --git a/subsys/bluetooth/controller/CMakeLists.txt b/subsys/bluetooth/controller/CMakeLists.txt index 6f8d09b6a90..b4b9f6670f4 100644 --- a/subsys/bluetooth/controller/CMakeLists.txt +++ b/subsys/bluetooth/controller/CMakeLists.txt @@ -9,6 +9,7 @@ zephyr_library() zephyr_library_sources( hci_driver.c hci_internal.c + hci_internal_wrappers.c ) zephyr_library_sources_ifdef( diff --git a/subsys/bluetooth/controller/Kconfig b/subsys/bluetooth/controller/Kconfig index 7deec4fcf01..cc766b9c6bb 100644 --- a/subsys/bluetooth/controller/Kconfig +++ b/subsys/bluetooth/controller/Kconfig @@ -550,5 +550,13 @@ config BT_CTLR_SDC_CIS_SUBEVENT_LENGTH_US If this parameter is set to zero, the subevent length is chosen by the controller. +config BT_CTLR_SDC_LE_POWER_CLASS_1 + bool "Device supports transmitting at LE Power Class 1 level" + default y if BT_CTLR_TX_PWR_ANTENNA >= 10 + help + This should be set if the device supports transmitting above +10dBm. + See Bluetooth Core Specification, Vol 6, Part A, Section 3 + Transmitter Characteristics for more information. + endmenu endif # BT_LL_SOFTDEVICE diff --git a/subsys/bluetooth/controller/hci_driver.c b/subsys/bluetooth/controller/hci_driver.c index 65bdb2d3d85..e8151b5fbde 100644 --- a/subsys/bluetooth/controller/hci_driver.c +++ b/subsys/bluetooth/controller/hci_driver.c @@ -36,6 +36,14 @@ #include "zephyr/logging/log.h" LOG_MODULE_REGISTER(bt_sdc_hci_driver); + +#if defined(CONFIG_BT_BUF_EVT_DISCARDABLE_COUNT) +#define HCI_RX_BUF_SIZE MAX(BT_BUF_RX_SIZE, \ + BT_BUF_EVT_SIZE(CONFIG_BT_BUF_EVT_DISCARDABLE_SIZE)) +#else +#define HCI_RX_BUF_SIZE BT_BUF_RX_SIZE +#endif + #if defined(CONFIG_BT_CONN) && defined(CONFIG_BT_CENTRAL) #if CONFIG_BT_MAX_CONN > 1 @@ -270,6 +278,23 @@ static inline void receive_signal_raise(void) mpsl_work_submit(&receive_work); } +/** Storage for HCI packets from controller to host */ +static struct { + /* Buffer for the HCI packet. */ + uint8_t buf[HCI_RX_BUF_SIZE]; + /* Type of the HCI packet the buffer contains. */ + sdc_hci_msg_type_t type; +} rx_hci_msg; + +static void bt_buf_rx_freed_cb(enum bt_buf_type type_mask) +{ + if (((rx_hci_msg.type == SDC_HCI_MSG_TYPE_EVT && (type_mask & BT_BUF_EVT) != 0u) || + (rx_hci_msg.type == SDC_HCI_MSG_TYPE_DATA && (type_mask & BT_BUF_ACL_IN) != 0u) || + (rx_hci_msg.type == SDC_HCI_MSG_TYPE_ISO && (type_mask & BT_BUF_ISO_IN) != 0u))) { + receive_signal_raise(); + } +} + static int cmd_handle(struct net_buf *cmd) { LOG_DBG(""); @@ -371,16 +396,16 @@ static int hci_driver_send(struct net_buf *buf) return err; } -static void data_packet_process(uint8_t *hci_buf) +static int data_packet_process(uint8_t *hci_buf) { - struct net_buf *data_buf = bt_buf_get_rx(BT_BUF_ACL_IN, K_FOREVER); + struct net_buf *data_buf = bt_buf_get_rx(BT_BUF_ACL_IN, K_NO_WAIT); struct bt_hci_acl_hdr *hdr = (void *)hci_buf; uint16_t hf, handle, len; uint8_t flags, pb, bc; if (!data_buf) { - LOG_ERR("No data buffer available"); - return; + LOG_DBG("No data buffer available"); + return -ENOBUFS; } len = sys_le16_to_cpu(hdr->len); @@ -390,23 +415,47 @@ static void data_packet_process(uint8_t *hci_buf) pb = bt_acl_flags_pb(flags); bc = bt_acl_flags_bc(flags); + if (len + sizeof(*hdr) > HCI_RX_BUF_SIZE) { + LOG_ERR("Event buffer too small. %u > %u", + len + sizeof(*hdr), + HCI_RX_BUF_SIZE); + return -ENOMEM; + } + LOG_DBG("Data: handle (0x%02x), PB(%01d), BC(%01d), len(%u)", handle, pb, bc, len); net_buf_add_mem(data_buf, &hci_buf[0], len + sizeof(*hdr)); + bt_recv(data_buf); + + return 0; } -static void iso_data_packet_process(uint8_t *hci_buf) +static int iso_data_packet_process(uint8_t *hci_buf) { - struct net_buf *data_buf = bt_buf_get_rx(BT_BUF_ISO_IN, K_FOREVER); + struct net_buf *data_buf = bt_buf_get_rx(BT_BUF_ISO_IN, K_NO_WAIT); struct bt_hci_iso_hdr *hdr = (void *)hci_buf; uint16_t len = sys_le16_to_cpu(hdr->len); + if (!data_buf) { + LOG_DBG("No data buffer available"); + return -ENOBUFS; + } + + if (len + sizeof(*hdr) > HCI_RX_BUF_SIZE) { + LOG_ERR("Event buffer too small. %u > %u", + len + sizeof(*hdr), + HCI_RX_BUF_SIZE); + return -ENOMEM; + } + net_buf_add_mem(data_buf, &hci_buf[0], len + sizeof(*hdr)); bt_recv(data_buf); + + return 0; } static bool event_packet_is_discardable(const uint8_t *hci_buf) @@ -451,12 +500,19 @@ static bool event_packet_is_discardable(const uint8_t *hci_buf) } } -static void event_packet_process(uint8_t *hci_buf) +static int event_packet_process(uint8_t *hci_buf) { bool discardable = event_packet_is_discardable(hci_buf); struct bt_hci_evt_hdr *hdr = (void *)hci_buf; struct net_buf *evt_buf; + if (hdr->len + sizeof(*hdr) > HCI_RX_BUF_SIZE) { + LOG_ERR("Event buffer too small. %u > %u", + hdr->len + sizeof(*hdr), + HCI_RX_BUF_SIZE); + return -ENOMEM; + } + if (hdr->evt == BT_HCI_EVT_LE_META_EVENT) { struct bt_hci_evt_le_meta_event *me = (void *)&hci_buf[2]; @@ -480,67 +536,81 @@ static void event_packet_process(uint8_t *hci_buf) LOG_DBG("Event (0x%02x) len %u", hdr->evt, hdr->len); } - evt_buf = bt_buf_get_evt(hdr->evt, discardable, - discardable ? K_NO_WAIT : K_FOREVER); + evt_buf = bt_buf_get_evt(hdr->evt, discardable, K_NO_WAIT); if (!evt_buf) { if (discardable) { LOG_DBG("Discarding event"); - return; + return 0; } - LOG_ERR("No event buffer available"); - return; + LOG_DBG("No event buffer available"); + return -ENOBUFS; } net_buf_add_mem(evt_buf, &hci_buf[0], hdr->len + sizeof(*hdr)); + bt_recv(evt_buf); + + return 0; } -static bool fetch_and_process_hci_msg(uint8_t *p_hci_buffer) +static int fetch_hci_msg(uint8_t *p_hci_buffer, sdc_hci_msg_type_t *msg_type) { int errcode; - sdc_hci_msg_type_t msg_type; errcode = MULTITHREADING_LOCK_ACQUIRE(); if (!errcode) { - errcode = hci_internal_msg_get(p_hci_buffer, &msg_type); + errcode = hci_internal_msg_get(p_hci_buffer, msg_type); MULTITHREADING_LOCK_RELEASE(); } - if (errcode) { - return false; - } + return errcode; +} + +static int process_hci_msg(uint8_t *p_hci_buffer, sdc_hci_msg_type_t msg_type) +{ + int err; if (msg_type == SDC_HCI_MSG_TYPE_EVT) { - event_packet_process(p_hci_buffer); + err = event_packet_process(p_hci_buffer); } else if (msg_type == SDC_HCI_MSG_TYPE_DATA) { - data_packet_process(p_hci_buffer); + err = data_packet_process(p_hci_buffer); } else if (msg_type == SDC_HCI_MSG_TYPE_ISO) { - iso_data_packet_process(p_hci_buffer); + err = iso_data_packet_process(p_hci_buffer); } else { if (!IS_ENABLED(CONFIG_BT_CTLR_SDC_SILENCE_UNEXPECTED_MSG_TYPE)) { LOG_ERR("Unexpected msg_type: %u. This if-else needs a new branch", msg_type); } + err = 0; } - return true; + return err; } void hci_driver_receive_process(void) { -#if defined(CONFIG_BT_BUF_EVT_DISCARDABLE_COUNT) - static uint8_t hci_buf[MAX(BT_BUF_RX_SIZE, - BT_BUF_EVT_SIZE(CONFIG_BT_BUF_EVT_DISCARDABLE_SIZE))]; -#else - static uint8_t hci_buf[BT_BUF_RX_SIZE]; -#endif + int err; - if (fetch_and_process_hci_msg(&hci_buf[0])) { - /* Let other threads of same priority run in between. */ - receive_signal_raise(); + if (rx_hci_msg.type == SDC_HCI_MSG_TYPE_NONE && + fetch_hci_msg(&rx_hci_msg.buf[0], &rx_hci_msg.type) != 0) { + return; + } + + err = process_hci_msg(&rx_hci_msg.buf[0], rx_hci_msg.type); + if (err == -ENOBUFS) { + /* If we got -ENOBUFS, wait for the signal from the host. */ + return; + } else if (err) { + LOG_ERR("Unknown error when processing hci message %d", err); + k_panic(); } + + rx_hci_msg.type = SDC_HCI_MSG_TYPE_NONE; + + /* Let other threads of same priority run in between. */ + receive_signal_raise(); } static void receive_work_handler(struct k_work *work) @@ -828,6 +898,13 @@ static int configure_supported_features(void) } } + if (IS_ENABLED(CONFIG_BT_CTLR_SDC_LE_POWER_CLASS_1)) { + err = sdc_support_le_power_class_1(); + if (err) { + return -ENOTSUP; + } + } + return 0; } @@ -1165,7 +1242,7 @@ static int hci_driver_open(void) } #endif - err = sdc_enable(receive_signal_raise, sdc_mempool); + err = sdc_enable(hci_driver_receive_process, sdc_mempool); if (err) { MULTITHREADING_LOCK_RELEASE(); return err; @@ -1263,6 +1340,8 @@ static int hci_driver_open(void) MULTITHREADING_LOCK_RELEASE(); + bt_buf_rx_freed_cb_set(bt_buf_rx_freed_cb); + return 0; } @@ -1295,6 +1374,8 @@ static int hci_driver_close(void) MULTITHREADING_LOCK_RELEASE(); + bt_buf_rx_freed_cb_set(NULL); + return err; } diff --git a/subsys/bluetooth/controller/hci_internal.c b/subsys/bluetooth/controller/hci_internal.c index ed2e1c571e3..533daa0f5ea 100644 --- a/subsys/bluetooth/controller/hci_internal.c +++ b/subsys/bluetooth/controller/hci_internal.c @@ -16,6 +16,7 @@ #include #include "hci_internal.h" +#include "hci_internal_wrappers.h" #include "ecdh.h" #define CMD_COMPLETE_MIN_SIZE (BT_HCI_EVT_HDR_SIZE \ @@ -695,7 +696,6 @@ void hci_internal_le_supported_features( features->params.extended_reject_indication = 1; features->params.slave_initiated_features_exchange = 1; features->params.le_ping = 1; - features->params.le_Power_class_1 = 1; #ifdef CONFIG_BT_CTLR_DATA_LENGTH features->params.le_data_packet_length_extension = 1; @@ -774,6 +774,10 @@ void hci_internal_le_supported_features( #if defined(CONFIG_BT_CTLR_SDC_PAWR_SYNC) features->params.periodic_advertising_with_responses_scanner = 1; #endif + +#if defined(CONFIG_BT_CTLR_SDC_LE_POWER_CLASS_1) + features->params.le_Power_class_1 = 1; +#endif /* CONFIG_BT_CTLR_SDC_LE_POWER_CLASS_1 */ } static void le_read_supported_states(uint8_t *buf) @@ -892,7 +896,7 @@ static uint8_t controller_and_baseband_cmd_put(uint8_t const * const cmd, case SDC_HCI_OPCODE_CMD_CB_SET_CONTROLLER_TO_HOST_FLOW_CONTROL: return sdc_hci_cmd_cb_set_controller_to_host_flow_control((void *)cmd_params); case SDC_HCI_OPCODE_CMD_CB_HOST_BUFFER_SIZE: - return sdc_hci_cmd_cb_host_buffer_size((void *)cmd_params); + return sdc_hci_cmd_cb_host_buffer_size_wrapper((void *)cmd_params); case SDC_HCI_OPCODE_CMD_CB_HOST_NUMBER_OF_COMPLETED_PACKETS: return sdc_hci_cmd_cb_host_number_of_completed_packets((void *)cmd_params); #endif diff --git a/subsys/bluetooth/controller/hci_internal_wrappers.c b/subsys/bluetooth/controller/hci_internal_wrappers.c new file mode 100644 index 00000000000..7b04b763a67 --- /dev/null +++ b/subsys/bluetooth/controller/hci_internal_wrappers.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include "hci_internal_wrappers.h" +#include +#include + +#if defined(CONFIG_BT_HCI_ACL_FLOW_CONTROL) +/* + * The Host will generate up to acl_pkts number of Host Number of Completed Packets command plus a + * number of normal HCI commands, as such we need to ensure the tx command buffer count is big + * enough to not block incoming ACKs from the host. + * + * When Controller to Host data flow control is supported, ensure that `CONFIG_BT_BUF_CMD_TX_COUNT` + * is greater than or equal to `BT_BUF_ACL_RX_COUNT + Ncmd`, where Ncmd is the supported maximum + * Num_HCI_Command_Packets. + * + * The SDC controller (currently) does not support Num_HCI_Command_Packets > 1, which means Ncmd + * is always 1. + * + * `CONFIG_BT_BUF_CMD_TX_COUNT` is used differently depending on whether `CONFIG_BT_HCI_HOST` or + * `BT_HCI_RAW` is defined. And as ACL packet pools are only used in connections, + * we need to limit the build assert as such. See the comments above the asserts for more + * information on the specific scenario. + */ +#if defined(CONFIG_BT_HCI_HOST) && (BT_BUF_ACL_RX_COUNT > 0) +/* + * When `CONFIG_BT_HCI_HOST`, `CONFIG_BT_BUF_CMD_TX_COUNT` controls the capacity of + * `bt_hci_cmd_create`. Which is used for all BT HCI Commands, and can be called concurrently from + * many different application contexts, initiating various processes. + * + * Currently `bt_hci_cmd_create` is generally `K_FOREVER`, as such this build assert only guarantees + * to avoid deadlocks due to missing CMD buffers, if the host is only allocating the next command + * once the previous is completed. + */ +#define BT_BUF_CMD_TX_COUNT CONFIG_BT_BUF_CMD_TX_COUNT + +BUILD_ASSERT(BT_BUF_ACL_RX_COUNT < BT_BUF_CMD_TX_COUNT, + "Too low HCI command buffers compared to ACL Rx buffers."); +#else /* controller-only build */ +/* + * On a controller-only build (`BT_HCI_RAW`) `CONFIG_BT_BUF_CMD_TX_COUNT` controls the capacity of + * `bt_buf_get_tx(BT_BUF_CMD)`. Which is only used to receive commands from the Host at the rate the + * command flow control dictates. Considering one buffer is needed to the Num_HCI_Command_Packet, to + * do flow control, at least one more buffer is needed. + * + */ +#define BT_BUF_CMD_TX_COUNT (BT_BUF_RX_COUNT + 1) + +BUILD_ASSERT((BT_BUF_CMD_TX_COUNT - 1) > 0, + "We need at least two HCI command buffers to avoid deadlocks."); +#endif /* CONFIG_BT_CONN && CONFIG_BT_HCI_HOST */ + +/* + * This wrapper addresses a limitation in some Zephyr HCI samples such as Zephyr hci_ipc, namely the + * dropping of Host Number of Completed Packets (HNCP) messages when buffers are full. Dropping + * these messages causes a resource leak. + * + * The buffers are full when CONFIG_BT_BUF_CMD_TX_COUNT is exhausted. This wrapper implements a + * workaround that ensures the buffers are never exhausted, by limiting the + * Host_Total_Num_ACL_Data_Packets value. Limiting this value naturally limits the number of HNCP to + * one the sample buffers can handle. + * + * Considering the sample uses the same buffer pool for normal commands, which take up one buffer at + * most (look up `HCI_Command_Complete` for more details), this wrapper artifically limits the + * Host_Total_Num_ACL_Data_Packets to at most one less than the CONFIG_BT_BUF_CMD_TX_COUNT pool + * capacity. + * . + */ +int sdc_hci_cmd_cb_host_buffer_size_wrapper(const sdc_hci_cmd_cb_host_buffer_size_t *cmd_params) +{ + sdc_hci_cmd_cb_host_buffer_size_t ctrl_cmd_params = *cmd_params; + + ctrl_cmd_params.host_total_num_acl_data_packets = MIN( + ctrl_cmd_params.host_total_num_acl_data_packets, (BT_BUF_CMD_TX_COUNT - 1)); + + return sdc_hci_cmd_cb_host_buffer_size(&ctrl_cmd_params); +} +#endif /* CONFIG_BT_HCI_ACL_FLOW_CONTROL */ diff --git a/subsys/bluetooth/controller/hci_internal_wrappers.h b/subsys/bluetooth/controller/hci_internal_wrappers.h new file mode 100644 index 00000000000..1bfa772a994 --- /dev/null +++ b/subsys/bluetooth/controller/hci_internal_wrappers.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +/** @file + * @brief Internal HCI interface Wrappers + */ + +#include +#include +#include + +#ifndef HCI_INTERNAL_WRAPPERS_H__ +#define HCI_INTERNAL_WRAPPERS_H__ + +#if defined(__DOXYGEN__) || defined(CONFIG_BT_HCI_ACL_FLOW_CONTROL) +int sdc_hci_cmd_cb_host_buffer_size_wrapper(const sdc_hci_cmd_cb_host_buffer_size_t *cmd_params); +#endif + +#endif diff --git a/test-manifests/99-default-test-nrf.yml b/test-manifests/99-default-test-nrf.yml index 232e51ff041..29680499da0 100644 --- a/test-manifests/99-default-test-nrf.yml +++ b/test-manifests/99-default-test-nrf.yml @@ -11,7 +11,7 @@ manifest: - name: test_nrf remote: ncs-test repo-path: test-fw-nrfconnect-nrf - revision: v2.6-branch + revision: ncs-v2.6-branch-NCSDK-34113-2-preview1 import: name-blocklist: [sdk-nrf] userdata: diff --git a/tests/bluetooth/iso/prj.conf b/tests/bluetooth/iso/prj.conf index 062a80cae2b..a85b689c889 100644 --- a/tests/bluetooth/iso/prj.conf +++ b/tests/bluetooth/iso/prj.conf @@ -24,6 +24,7 @@ CONFIG_BT_NUS=y CONFIG_BT_NUS_CLIENT=y CONFIG_BT_GATT_DM=y CONFIG_BT_GATT_CLIENT=y +CONFIG_BT_BUF_EVT_RX_COUNT=11 CONFIG_BT_BUF_ACL_TX_COUNT=10 CONFIG_BT_ATT_PREPARE_COUNT=2 CONFIG_BT_L2CAP_TX_BUF_COUNT=10 diff --git a/tests/subsys/bluetooth/controller/CMakeLists.txt b/tests/subsys/bluetooth/controller/CMakeLists.txt new file mode 100644 index 00000000000..68f2a9255e7 --- /dev/null +++ b/tests/subsys/bluetooth/controller/CMakeLists.txt @@ -0,0 +1,26 @@ +# +# Copyright (c) 2025 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(hci_cmd_cb_host_buffer_size_test) + +# Generate runner for the test +test_runner_generate(src/hci_cmd_cb_host_buffer_size_test.c) + +# Add Unit Under Test source files +target_sources(app PRIVATE ${ZEPHYR_NRF_MODULE_DIR}/subsys/bluetooth/controller/hci_internal_wrappers.c) + +# Add test source file +target_sources(app PRIVATE src/hci_cmd_cb_host_buffer_size_test.c) + +# Create mocks +cmock_handle(${ZEPHYR_NRFXLIB_MODULE_DIR}/softdevice_controller/include/sdc_hci_cmd_controller_baseband.h) + +# Include paths +include_directories(${ZEPHYR_HAL_NORDIC_MODULE_DIR}/nrfx) +include_directories(${ZEPHYR_NRF_MODULE_DIR}/subsys/bluetooth/controller) diff --git a/tests/subsys/bluetooth/controller/_testcase.yaml b/tests/subsys/bluetooth/controller/_testcase.yaml new file mode 100644 index 00000000000..72ad74b6f9f --- /dev/null +++ b/tests/subsys/bluetooth/controller/_testcase.yaml @@ -0,0 +1,9 @@ +tests: + bluetooth.controller: + platform_allow: + - native_sim + integration_platforms: + - native_sim + tags: + - unittest + - ci_tests_subsys_bluetooth_controller diff --git a/tests/subsys/bluetooth/controller/prj.conf b/tests/subsys/bluetooth/controller/prj.conf new file mode 100644 index 00000000000..8c317c5c4c5 --- /dev/null +++ b/tests/subsys/bluetooth/controller/prj.conf @@ -0,0 +1,13 @@ +# +# Copyright (c) 2025 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# +CONFIG_BT_CENTRAL=y +CONFIG_BT_HCI=y +CONFIG_BT=y + +CONFIG_BT_HCI_ACL_FLOW_CONTROL=y +CONFIG_BT_BUF_CMD_TX_COUNT=10 + +CONFIG_UNITY=y diff --git a/tests/subsys/bluetooth/controller/src/hci_cmd_cb_host_buffer_size_test.c b/tests/subsys/bluetooth/controller/src/hci_cmd_cb_host_buffer_size_test.c new file mode 100644 index 00000000000..4f335398954 --- /dev/null +++ b/tests/subsys/bluetooth/controller/src/hci_cmd_cb_host_buffer_size_test.c @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ +#include +#include +#include +#include +#include + +#include "hci_internal_wrappers.h" +#include "cmock_sdc_hci_cmd_controller_baseband.h" + +#define TEST_HOST_ACL_DATA_PCKT_LNGTH 55 +#define TEST_HOST_SYNC_DATA_PCKT_LNGTH 55 +#define TEST_HOST_NUM_SYNC_DATA_PCKTS 5555 + +/* The unity_main is not declared in any header file. It is only defined in the generated test + * runner because of ncs' unity configuration. It is therefore declared here to avoid a compiler + * warning. + */ +extern int unity_main(void); + +typedef struct { + uint16_t acl_input_pckts; + uint16_t exp_acl_pckts; +} test_vector_t; + +static const test_vector_t test_vectors[] = { + {5, 5}, /* Input within range, no adjustment needed */ + {15, (CONFIG_BT_BUF_CMD_TX_COUNT - 1)}, /* Input exceeds TX buffer count - 1 limit */ + {0xFF, (CONFIG_BT_BUF_CMD_TX_COUNT - 1)}, /* Maximum input value, corner case */ +}; + +void test_sdc_hci_cmd_cb_host_buffer_size_wrapper(void) +{ + sdc_hci_cmd_cb_host_buffer_size_t cmd_params; + + cmd_params.host_acl_data_packet_length = TEST_HOST_ACL_DATA_PCKT_LNGTH; + cmd_params.host_sync_data_packet_length = TEST_HOST_SYNC_DATA_PCKT_LNGTH; + cmd_params.host_total_num_sync_data_packets = TEST_HOST_NUM_SYNC_DATA_PCKTS; + + for (size_t i = 0; i < ARRAY_SIZE(test_vectors); i++) { + const test_vector_t *v = &test_vectors[i]; + + sdc_hci_cmd_cb_host_buffer_size_t exp_cmd_params = cmd_params; + + exp_cmd_params.host_total_num_acl_data_packets = v->exp_acl_pckts; + __cmock_sdc_hci_cmd_cb_host_buffer_size_ExpectAndReturn(&exp_cmd_params, 0); + + cmd_params.host_total_num_acl_data_packets = v->acl_input_pckts; + sdc_hci_cmd_cb_host_buffer_size_wrapper(&cmd_params); + } +} + +int main(void) +{ + (void)unity_main(); + + return 0; +} diff --git a/west.yml b/west.yml index 71fd5cbf398..c3ca7ba4bee 100644 --- a/west.yml +++ b/west.yml @@ -61,7 +61,7 @@ manifest: # https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/zephyr/guides/modules.html - name: zephyr repo-path: sdk-zephyr - revision: v3.5.99-ncs1-4 + revision: v3.5.99-ncs1-4-NCSDK-34113-2-preview1 import: # In addition to the zephyr repository itself, NCS also # imports the contents of zephyr/west.yml at the above @@ -142,7 +142,7 @@ manifest: - name: nrfxlib repo-path: sdk-nrfxlib path: nrfxlib - revision: 4fd27c5a8bd45c75893fc30b734afeb46a81553c + revision: v2.6.4-NCSDK-34113-2-preview1 - name: trusted-firmware-m repo-path: sdk-trusted-firmware-m path: modules/tee/tf-m/trusted-firmware-m @@ -181,7 +181,7 @@ manifest: # Only for internal Nordic development repo-path: dragoon.git remote: dragoon - revision: e5d86ebfadebfb093bd99d5594813b1a6ff95f1c + revision: ncs-v2.6-branch-NCSDK-34113-2-preview1 submodules: true groups: - dragoon @@ -196,7 +196,7 @@ manifest: compare-by-default: false - name: sidewalk repo-path: sdk-sidewalk - revision: v2.6.4 + revision: v2.6.4-NCSDK-34113-2-preview1 groups: - sidewalk - name: find-my