diff --git a/samples/bluetooth/mtu_update/README.rst b/samples/bluetooth/mtu_update/README.rst new file mode 100644 index 00000000000000..500d5bbbaa8a38 --- /dev/null +++ b/samples/bluetooth/mtu_update/README.rst @@ -0,0 +1,120 @@ +.. _bluetooth_mtu_update_sample: + +Bluetooth: MTU Update +##################### + +Q&A: +**** + + Question: What do I have to do to update my ATT MTU? + +| Answer: To get an ATT MTU of x octets, do the following: +| Set :kconfig:option:`CONFIG_BT_L2CAP_TX_MTU` to at least x +| Set :kconfig:option:`CONFIG_BT_BUF_ACL_RX_SIZE` to at least x + L2CAP header + size (4 octets). +| Set :kconfig:option:`CONFIG_BT_BUF_ACL_RX_SIZE` to at least x + L2CAP header + + SDU length field length (6 octets) if using + :kconfig:option:`CONFIG_BT_EATT`. +| Ensure the remote side supports the same MTUs. + + Question: I only want to *send* large packets. I don't need to receive large + ones. + Do I still need to set :kconfig:option:`CONFIG_BT_BUF_ACL_RX_SIZE`? + +Answer: Yes. [#mtu_exchange]_ The Bluetooth specification mandates a symmetric MTU for ATT. + +Overview: +********* + +This sample demonstrates the exchange of MTU between two devices to allow a +large notification to be sent. +Updating the MTU can be useful to send bigger packets and so have a better +throughput. + +To be able to send a large notification both the server and the client need to +update their MTU. The MTU is not a negotiated value, the client and the server +will exchange their MTUs and choose the minimum of the two. Thus the two MTU can +be set to a different value, but the MTU of the server must be greater or equal +to the MTU of the client. + +According to the Bluetooth specification, [#mtu]_ MTU is the maximum size of +SDUs. +However, in Zephyr, we can assume that it also represents the maximum size of +the PDUs. Because, in Bluetooth LE, [#sud_encapsulation]_ unless we are using L2CAP dynamic +channels, SDUs are not segmented. +The Kconfig symbol used to configure the size of the TX MTU is +:kconfig:option:`CONFIG_BT_L2CAP_TX_MTU`. There is no Kconfig symbol to update +the size of the RX MTU, because Zephyr uses a buffer pool for ACL RX buffers +coming from the controller. +The L2CAP RX MTU is defined as the maximum size of ACL RX buffers minus the +L2CAP header size. +That maximum ACL RX buffer size is configured with +:kconfig:option:`CONFIG_BT_BUF_ACL_RX_SIZE`. +The resulting L2CAP RX MTU will be the value of this Kconfig symbol minus the +L2CAP header size. + +.. figure:: img/mtu.svg + :align: center + :alt: Diagram of the MTUs and their corresponding Kconfig symbols + + Diagram of the MTUs and their corresponding Kconfig symbols + +Hardware Setup +************** + +This sample use two applications, two devices need to be setup. +The first one should be flashed with the central and the second one with the +peripheral. + +The two devices need to be close enough to be able to connect. + +Building and Running +******************** + +This sample can be found under :zephyr_file:`samples/bluetooth/mtu_update` in +the Zephyr tree. + +See :ref:`bluetooth samples section ` for details. + +If the devices are close enough, the central should connect to the peripheral +and send his MTU to the other device. If the MTU exchange succeeds, the central +should subscribe and then the peripheral will send a large notification. Right +after receiving the notification the central should unsubscribe. + +Here are the outputs you should have on the devices: + +Central: + +.. code-block:: console + + *** Booting Zephyr OS build zephyr-v3.2.0-2251-g95d8943c69ce *** + Bluetooth initialized + Scanning successfully started + Device found: EB:BF:36:26:42:09 (random) (RSSI -34) + Connected: EB:BF:36:26:42:09 (random) + mtu_exchange: Current MTU = 23 + mtu_exchange: Exchange MTU... + mtu_exchange_cb: MTU exchange successful (247) + [ATTRIBUTE] handle 16 + [ATTRIBUTE] handle 17 + [ATTRIBUTE] handle 19 + [SUBSCRIBED] + [NOTIFICATION] data 0x20004b73 length 100 + [UNSUBSCRIBED] + +Peripheral: + +.. code-block:: console + + *** Booting Zephyr OS build zephyr-v3.2.0-2251-g95d8943c69ce *** + Updated MTU: TX: 23 RX: 23 bytes + Updated MTU: TX: 247 RX: 247 bytes + MTU Test Update: notifications enabled + MTU Test Update: notifications disabled + +References +********** + +.. [#mtu_exchange] Bluetooth Core Specification v. 5.3: Vol. 3, Part F, 3.4.2 +.. [#mtu] Bluetooth Core Specification v. 5.3: Vol. 3, Part A, 5.1 +.. [#sud_encapsulation] Bluetooth Core Specification v. 5.3: Vol. 3, Part A, 7.3 diff --git a/samples/bluetooth/mtu_update/central/CMakeLists.txt b/samples/bluetooth/mtu_update/central/CMakeLists.txt new file mode 100644 index 00000000000000..b1a6c12d956edc --- /dev/null +++ b/samples/bluetooth/mtu_update/central/CMakeLists.txt @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(mtu_update) + +target_sources(app PRIVATE + src/main.c + src/central_mtu_update.c +) + +zephyr_library_include_directories(${ZEPHYR_BASE}/samples/bluetooth) diff --git a/samples/bluetooth/mtu_update/central/prj.conf b/samples/bluetooth/mtu_update/central/prj.conf new file mode 100644 index 00000000000000..ba93104ac90d3d --- /dev/null +++ b/samples/bluetooth/mtu_update/central/prj.conf @@ -0,0 +1,15 @@ +CONFIG_BT=y +CONFIG_BT_CENTRAL=y +CONFIG_BT_DEVICE_NAME="Zephyr Central MTU Update Sample" + +CONFIG_BT_GATT_CLIENT=y + +CONFIG_LOG=y +CONFIG_BT_L2CAP_LOG_LEVEL_DBG=y + +# HCI ACL buffers size +# BT_L2CAP_RX_MTU = CONFIG_BT_BUF_ACL_RX_SIZE - BT_L2CAP_HDR_SIZE +CONFIG_BT_BUF_ACL_RX_SIZE=251 + +# L2CAP SDU/PDU TX MTU +CONFIG_BT_L2CAP_TX_MTU=247 diff --git a/samples/bluetooth/mtu_update/central/sample.yaml b/samples/bluetooth/mtu_update/central/sample.yaml new file mode 100644 index 00000000000000..6ece9e2cce3d26 --- /dev/null +++ b/samples/bluetooth/mtu_update/central/sample.yaml @@ -0,0 +1,9 @@ +sample: + name: Bluetooth Central MTU Update +tests: + sample.bluetooth.central_mtu_update: + harness: bluetooth + platform_allow: qemu_cortex_m3 qemu_x86 nrf52_bsim + tags: bluetooth + integration_platforms: + - qemu_cortex_m3 diff --git a/samples/bluetooth/mtu_update/central/src/central_mtu_update.c b/samples/bluetooth/mtu_update/central/src/central_mtu_update.c new file mode 100644 index 00000000000000..a7289b3c39cdea --- /dev/null +++ b/samples/bluetooth/mtu_update/central/src/central_mtu_update.c @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#define MTU_TEST_SERVICE_TYPE BT_UUID_128_ENCODE(0x2e2b8dc3, 0x06e0, 0x4f93, 0x9bb2, 0x734091c356f0) +#define MTU_TEST_SERVICE_NOTIFY_TYPE \ + BT_UUID_128_ENCODE(0x2e2b8dc3, 0x06e0, 0x4f93, 0x9bb2, 0x734091c356f3) + +#define BT_UUID_MTU_TEST BT_UUID_DECLARE_128(MTU_TEST_SERVICE_TYPE) +#define BT_UUID_MTU_TEST_NOTIFY BT_UUID_DECLARE_128(MTU_TEST_SERVICE_NOTIFY_TYPE) + +static void start_scan(void); + +static struct bt_conn *default_conn; + +static struct bt_uuid_128 uuid = BT_UUID_INIT_128(0); +static struct bt_gatt_discover_params discover_params; +static struct bt_gatt_subscribe_params subscribe_params; + +bt_gatt_notify_func_t notify_cb; + +static uint8_t notify_func(struct bt_conn *conn, + struct bt_gatt_subscribe_params *params, + const void *data, uint16_t length) +{ + if (!data) { + printk("[UNSUBSCRIBED]\n"); + params->value_handle = 0U; + return BT_GATT_ITER_STOP; + } + + printk("[NOTIFICATION] data %p length %u\n", data, length); + + if (notify_cb != NULL) { + notify_cb(conn, params, data, length); + } + + return BT_GATT_ITER_STOP; +} + +static uint8_t discover_func(struct bt_conn *conn, const struct bt_gatt_attr *attr, + struct bt_gatt_discover_params *params) +{ + int err; + + if (!attr) { + printk("Discover complete\n"); + (void)memset(params, 0, sizeof(*params)); + return BT_GATT_ITER_STOP; + } + + printk("[ATTRIBUTE] handle %u\n", attr->handle); + + if (!bt_uuid_cmp(discover_params.uuid, BT_UUID_MTU_TEST)) { + memcpy(&uuid, BT_UUID_MTU_TEST_NOTIFY, sizeof(uuid)); + discover_params.uuid = &uuid.uuid; + discover_params.start_handle = attr->handle + 1; + discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC; + + err = bt_gatt_discover(conn, &discover_params); + if (err) { + printk("Discover failed (err %d)\n", err); + } + } else if (!bt_uuid_cmp(discover_params.uuid, BT_UUID_MTU_TEST_NOTIFY)) { + memcpy(&uuid, BT_UUID_GATT_CCC, sizeof(uuid)); + discover_params.uuid = &uuid.uuid; + discover_params.start_handle = attr->handle + 2; + discover_params.type = BT_GATT_DISCOVER_DESCRIPTOR; + subscribe_params.value_handle = bt_gatt_attr_value_handle(attr); + + err = bt_gatt_discover(conn, &discover_params); + if (err) { + printk("Discover failed (err %d)\n", err); + } + } else { + subscribe_params.notify = notify_func; + subscribe_params.value = BT_GATT_CCC_NOTIFY; + subscribe_params.ccc_handle = attr->handle; + + err = bt_gatt_subscribe(conn, &subscribe_params); + if (err && err != -EALREADY) { + printk("Subscribe failed (err %d)\n", err); + } else { + printk("[SUBSCRIBED]\n"); + } + + return BT_GATT_ITER_STOP; + } + + return BT_GATT_ITER_STOP; +} + +static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type, + struct net_buf_simple *ad) +{ + char addr_str[BT_ADDR_LE_STR_LEN]; + int err; + + if (default_conn) { + return; + } + + /* We're only interested in connectable events */ + if (type != BT_GAP_ADV_TYPE_ADV_IND && + type != BT_GAP_ADV_TYPE_ADV_DIRECT_IND) { + return; + } + + bt_addr_le_to_str(addr, addr_str, sizeof(addr_str)); + printk("Device found: %s (RSSI %d)\n", addr_str, rssi); + + /* connect only to devices in close proximity */ + if (rssi < -40) { + return; + } + + if (bt_le_scan_stop()) { + return; + } + + err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN, + BT_LE_CONN_PARAM_DEFAULT, &default_conn); + if (err) { + printk("Create conn to %s failed (%u)\n", addr_str, err); + start_scan(); + } +} + +static void start_scan(void) +{ + int err; + + err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, device_found); + if (err) { + printk("Scanning failed to start (err %d)\n", err); + return; + } + + printk("Scanning successfully started\n"); +} + +static void mtu_exchange_cb(struct bt_conn *conn, uint8_t err, + struct bt_gatt_exchange_params *params) +{ + printk("%s: MTU exchange %s (%u)\n", __func__, + err == 0U ? "successful" : "failed", + bt_gatt_get_mtu(conn)); +} + +static struct bt_gatt_exchange_params mtu_exchange_params = { + .func = mtu_exchange_cb +}; + +static int mtu_exchange(struct bt_conn *conn) +{ + int err; + + printk("%s: Current MTU = %u\n", __func__, bt_gatt_get_mtu(conn)); + + printk("%s: Exchange MTU...\n", __func__); + err = bt_gatt_exchange_mtu(conn, &mtu_exchange_params); + if (err) { + printk("%s: MTU exchange failed (err %d)", __func__, err); + } + + return err; +} + +static void connected(struct bt_conn *conn, uint8_t err) +{ + char addr[BT_ADDR_LE_STR_LEN]; + + bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); + + if (err) { + printk("Failed to connect to %s (%u)\n", addr, err); + + bt_conn_unref(default_conn); + default_conn = NULL; + + start_scan(); + return; + } + + printk("Connected: %s\n", addr); + + (void)mtu_exchange(conn); + + if (conn == default_conn) { + memcpy(&uuid, BT_UUID_MTU_TEST, sizeof(uuid)); + discover_params.uuid = &uuid.uuid; + discover_params.func = discover_func; + discover_params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE; + discover_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE; + discover_params.type = BT_GATT_DISCOVER_PRIMARY; + + err = bt_gatt_discover(default_conn, &discover_params); + if (err) { + printk("Discover failed(err %d)\n", err); + return; + } + } +} + +static void disconnected(struct bt_conn *conn, uint8_t reason) +{ + char addr[BT_ADDR_LE_STR_LEN]; + + if (conn != default_conn) { + return; + } + + bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); + + printk("Disconnected: %s (reason 0x%02x)\n", addr, reason); + + bt_conn_unref(default_conn); + default_conn = NULL; + + start_scan(); +} + +BT_CONN_CB_DEFINE(conn_callbacks) = { + .connected = connected, + .disconnected = disconnected, +}; + +void run_central_sample(bt_gatt_notify_func_t cb) +{ + int err; + + notify_cb = cb; + + err = bt_enable(NULL); + if (err) { + printk("Bluetooth init failed (err %d)\n", err); + return; + } + + printk("Bluetooth initialized\n"); + + start_scan(); +} diff --git a/samples/bluetooth/mtu_update/central/src/main.c b/samples/bluetooth/mtu_update/central/src/main.c new file mode 100644 index 00000000000000..6573282844811e --- /dev/null +++ b/samples/bluetooth/mtu_update/central/src/main.c @@ -0,0 +1,18 @@ +/* main.c - Application main entry point */ + +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +extern void run_central_sample(bt_gatt_notify_func_t cb); + +void main(void) +{ + run_central_sample(NULL); +} diff --git a/samples/bluetooth/mtu_update/img/mtu.svg b/samples/bluetooth/mtu_update/img/mtu.svg new file mode 100644 index 00000000000000..6c1f0c5f6d9496 --- /dev/null +++ b/samples/bluetooth/mtu_update/img/mtu.svg @@ -0,0 +1,3 @@ + + +
HCI
HCI
PDUs
PDUs
BT_BUF_ACL_RX_SIZE
BT_BUF_ACL_RX_SIZE
BT_BUF_ACL_TX_SIZE
BT_BUF_ACL_TX_SIZE
BT_L2CAP_TX_MTU
BT_L2CAP_TX_MTU
BT_L2CAP_RX_MTU
(symbol does not exist)
BT_L2CAP_RX_MTU...
HCI ACL buffers pool
HCI ACL buffers po...
CONTROLLER
CONTROLLER

SDUs
SDUs
L2CAP
L2CAP
ATT, SMP, ...
ATT, SMP, ...
HOST
HOST
Text is not SVG - cannot display
\ No newline at end of file diff --git a/samples/bluetooth/mtu_update/peripheral/CMakeLists.txt b/samples/bluetooth/mtu_update/peripheral/CMakeLists.txt new file mode 100644 index 00000000000000..8f69405476bed1 --- /dev/null +++ b/samples/bluetooth/mtu_update/peripheral/CMakeLists.txt @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(mtu_update) + +target_sources(app PRIVATE + src/main.c + src/peripheral_mtu_update.c +) + +zephyr_library_include_directories(${ZEPHYR_BASE}/samples/bluetooth) diff --git a/samples/bluetooth/mtu_update/peripheral/prj.conf b/samples/bluetooth/mtu_update/peripheral/prj.conf new file mode 100644 index 00000000000000..3d7a2d69baf530 --- /dev/null +++ b/samples/bluetooth/mtu_update/peripheral/prj.conf @@ -0,0 +1,13 @@ +CONFIG_BT=y +CONFIG_BT_PERIPHERAL=y +CONFIG_BT_DEVICE_NAME="Zephyr Peripheral MTU Update Sample" + +CONFIG_LOG=y +CONFIG_BT_L2CAP_LOG_LEVEL_DBG=y + +# HCI ACL buffers size +# BT_L2CAP_RX_MTU = CONFIG_BT_BUF_ACL_RX_SIZE - BT_L2CAP_HDR_SIZE +CONFIG_BT_BUF_ACL_RX_SIZE=251 + +# L2CAP SDU/PDU TX MTU +CONFIG_BT_L2CAP_TX_MTU=247 diff --git a/samples/bluetooth/mtu_update/peripheral/sample.yaml b/samples/bluetooth/mtu_update/peripheral/sample.yaml new file mode 100644 index 00000000000000..2ffbe15be1732f --- /dev/null +++ b/samples/bluetooth/mtu_update/peripheral/sample.yaml @@ -0,0 +1,9 @@ +sample: + name: Bluetooth Peripheral MTU Update +tests: + sample.bluetooth.peripheral_mtu_update: + harness: bluetooth + platform_allow: qemu_cortex_m3 qemu_x86 nrf52_bsim + tags: bluetooth + integration_platforms: + - qemu_cortex_m3 diff --git a/samples/bluetooth/mtu_update/peripheral/src/main.c b/samples/bluetooth/mtu_update/peripheral/src/main.c new file mode 100644 index 00000000000000..f8f56dd8ebb572 --- /dev/null +++ b/samples/bluetooth/mtu_update/peripheral/src/main.c @@ -0,0 +1,22 @@ +/* main.c - Application main entry point */ + +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +extern void run_peripheral_sample(uint8_t *notify_data, size_t notify_data_size, uint16_t seconds); + +void main(void) +{ + uint8_t notify_data[100] = {}; + + notify_data[13] = 0x7f; + notify_data[99] = 0x55; + + run_peripheral_sample(notify_data, sizeof(notify_data), 0); +} diff --git a/samples/bluetooth/mtu_update/peripheral/src/peripheral_mtu_update.c b/samples/bluetooth/mtu_update/peripheral/src/peripheral_mtu_update.c new file mode 100644 index 00000000000000..f30e50a85ab71a --- /dev/null +++ b/samples/bluetooth/mtu_update/peripheral/src/peripheral_mtu_update.c @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#define MTU_TEST_SERVICE_TYPE BT_UUID_128_ENCODE(0x2e2b8dc3, 0x06e0, 0x4f93, 0x9bb2, 0x734091c356f0) + +static struct bt_uuid_128 mtu_test_service = BT_UUID_INIT_128(MTU_TEST_SERVICE_TYPE); + +static struct bt_uuid_128 notify_characteristic_uuid = + BT_UUID_INIT_128(BT_UUID_128_ENCODE(0x2e2b8dc3, 0x06e0, 0x4f93, 0x9bb2, 0x734091c356f3)); + +static const struct bt_data adv_ad_data[] = { + BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), + BT_DATA_BYTES(BT_DATA_UUID128_ALL, MTU_TEST_SERVICE_TYPE)}; + +static void ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value) +{ + ARG_UNUSED(attr); + + bool notif_enabled = (value == BT_GATT_CCC_NOTIFY); + + printk("MTU Test Update: notifications %s\n", notif_enabled ? "enabled" : "disabled"); +} + +BT_GATT_SERVICE_DEFINE(mtu_test, BT_GATT_PRIMARY_SERVICE(&mtu_test_service), + BT_GATT_CHARACTERISTIC(¬ify_characteristic_uuid.uuid, BT_GATT_CHRC_NOTIFY, + BT_GATT_PERM_NONE, NULL, NULL, NULL), + BT_GATT_CCC(ccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)); + +void mtu_updated(struct bt_conn *conn, uint16_t tx, uint16_t rx) +{ + printk("Updated MTU: TX: %d RX: %d bytes\n", tx, rx); +} + +static struct bt_gatt_cb gatt_callbacks = {.att_mtu_updated = mtu_updated}; + +void run_peripheral_sample(uint8_t *notify_data, size_t notify_data_size, uint16_t seconds) +{ + int err; + + err = bt_enable(NULL); + if (err) { + printk("Bluetooth init failed (err %d)\n", err); + return; + } + + bt_gatt_cb_register(&gatt_callbacks); + + struct bt_gatt_attr *notify_crch = + bt_gatt_find_by_uuid(mtu_test.attrs, 0xffff, ¬ify_characteristic_uuid.uuid); + + /* Advertise. Auto include name in adv data. Connectable. */ + bt_le_adv_start(BT_LE_ADV_CONN_NAME, adv_ad_data, ARRAY_SIZE(adv_ad_data), NULL, 0); + + bool infinite = seconds == 0; + + for (int i = 0; (i < seconds) || infinite; i++) { + k_sleep(K_SECONDS(1)); + bt_gatt_notify(NULL, notify_crch, notify_data, notify_data_size); + } +} diff --git a/tests/bluetooth/bsim_bt/bsim_test_mtu_update/CMakeLists.txt b/tests/bluetooth/bsim_bt/bsim_test_mtu_update/CMakeLists.txt new file mode 100644 index 00000000000000..af07761ebe1efd --- /dev/null +++ b/tests/bluetooth/bsim_bt/bsim_test_mtu_update/CMakeLists.txt @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +if (NOT DEFINED ENV{BSIM_COMPONENTS_PATH}) + message(FATAL_ERROR "This test requires the BabbleSim simulator. Please set \ + the environment variable BSIM_COMPONENTS_PATH to point to its \ + components folder. More information can be found in \ + https://babblesim.github.io/folder_structure_and_env.html") +endif() + +find_package(Zephyr HINTS $ENV{ZEPHYR_BASE}) +project(bsim_test_mtu_update) + +target_sources(app PRIVATE + src/main.c + ${ZEPHYR_BASE}/samples/bluetooth/mtu_update/central/src/central_mtu_update.c + ${ZEPHYR_BASE}/samples/bluetooth/mtu_update/peripheral/src/peripheral_mtu_update.c +) + +zephyr_include_directories( + $ENV{BSIM_COMPONENTS_PATH}/libUtilv1/src/ + $ENV{BSIM_COMPONENTS_PATH}/libPhyComv1/src/ +) diff --git a/tests/bluetooth/bsim_bt/bsim_test_mtu_update/prj.conf b/tests/bluetooth/bsim_bt/bsim_test_mtu_update/prj.conf new file mode 100644 index 00000000000000..95501689df0311 --- /dev/null +++ b/tests/bluetooth/bsim_bt/bsim_test_mtu_update/prj.conf @@ -0,0 +1,16 @@ +CONFIG_BT=y +CONFIG_BT_CENTRAL=y +CONFIG_BT_PERIPHERAL=y +CONFIG_BT_DEVICE_NAME="MTU Update Test" + +CONFIG_BT_GATT_CLIENT=y + +# HCI ACL buffers size +# BT_L2CAP_RX_MTU = CONFIG_BT_BUF_ACL_RX_SIZE - BT_L2CAP_HDR_SIZE +CONFIG_BT_BUF_ACL_RX_SIZE=251 + +# L2CAP SDU/PDU TX MTU +CONFIG_BT_L2CAP_TX_MTU=247 + +CONFIG_LOG=y +CONFIG_BT_L2CAP_LOG_LEVEL_DBG=y diff --git a/tests/bluetooth/bsim_bt/bsim_test_mtu_update/src/main.c b/tests/bluetooth/bsim_bt/bsim_test_mtu_update/src/main.c new file mode 100644 index 00000000000000..f599e49d2e5b06 --- /dev/null +++ b/tests/bluetooth/bsim_bt/bsim_test_mtu_update/src/main.c @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include + +#include + +#include + +#include "bs_types.h" +#include "bs_tracing.h" +#include "time_machine.h" +#include "bstests.h" + +#include +LOG_MODULE_REGISTER(bt_bsim_mtu_update, LOG_LEVEL_DBG); + +extern void run_central_sample(void *cb); +extern void run_peripheral_sample(uint8_t *notify_data, size_t notify_data_size, uint16_t seconds); + +#define FAIL(...) \ + do { \ + bst_result = Failed; \ + bs_trace_error_time_line(__VA_ARGS__); \ + } while (0) + +#define PASS(...) \ + do { \ + bst_result = Passed; \ + bs_trace_info_time(1, __VA_ARGS__); \ + } while (0) + +extern enum bst_result_t bst_result; + +#define CREATE_FLAG(flag) static atomic_t flag = (atomic_t)false +#define SET_FLAG(flag) (void)atomic_set(&flag, (atomic_t)true) +#define WAIT_FOR_FLAG(flag) \ + while (!(bool)atomic_get(&flag)) { \ + (void)k_sleep(K_MSEC(1)); \ + } + +#define WAIT_TIME (20e6) /* 20 seconds */ +#define PERIPHERAL_NOTIFY_TIME ((WAIT_TIME - 10e6) / 1e6) + +CREATE_FLAG(flag_notification_received); +uint8_t notify_data[100] = {}; +uint8_t is_data_equal; + +static uint8_t notify_cb(struct bt_conn *conn, struct bt_gatt_subscribe_params *params, + const void *data, uint16_t length) +{ + printk("BSIM NOTIFY_CALLBACK\n"); + + is_data_equal = (length == sizeof(notify_data) && !memcmp(notify_data, data, length)); + + LOG_HEXDUMP_DBG(data, length, "notification data"); + LOG_HEXDUMP_DBG(notify_data, sizeof(notify_data), "expected data"); + + SET_FLAG(flag_notification_received); + + return 0; +} + +static void test_central_main(void) +{ + notify_data[13] = 0x7f; + notify_data[99] = 0x55; + + run_central_sample(notify_cb); + + WAIT_FOR_FLAG(flag_notification_received); + + if (is_data_equal) { + PASS("MTU Update test passed\n"); + } else { + FAIL("MTU Update test failed\n"); + } +} + +static void test_peripheral_main(void) +{ + notify_data[13] = 0x7f; + notify_data[99] = 0x55; + + run_peripheral_sample(notify_data, sizeof(notify_data), PERIPHERAL_NOTIFY_TIME); + + PASS("MTU Update test passed\n"); +} + +void test_tick(bs_time_t HW_device_time) +{ + if (bst_result != Passed) { + FAIL("Test failed (not passed after %i seconds)\n", WAIT_TIME); + } +} + +static void test_mtu_update_init(void) +{ + bst_ticker_set_next_tick_absolute(WAIT_TIME); + bst_result = In_progress; +} + +static const struct bst_test_instance test_def[] = { + { + .test_id = "central", + .test_descr = "Central GATT MTU Update", + .test_post_init_f = test_mtu_update_init, + .test_tick_f = test_tick, + .test_main_f = test_central_main + }, + { + .test_id = "peripheral", + .test_descr = "Peripheral GATT MTU Update", + .test_post_init_f = test_mtu_update_init, + .test_tick_f = test_tick, + .test_main_f = test_peripheral_main + }, + BSTEST_END_MARKER +}; + +struct bst_test_list *test_mtu_update_install(struct bst_test_list *tests) +{ + return bst_add_tests(tests, test_def); +} + +bst_test_install_t test_installers[] = { + test_mtu_update_install, + NULL +}; + +void main(void) +{ + bst_main(); +} diff --git a/tests/bluetooth/bsim_bt/bsim_test_mtu_update/test_scripts/_compile.sh b/tests/bluetooth/bsim_bt/bsim_test_mtu_update/test_scripts/_compile.sh new file mode 100755 index 00000000000000..ac7732a78dc137 --- /dev/null +++ b/tests/bluetooth/bsim_bt/bsim_test_mtu_update/test_scripts/_compile.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +# Copyright 2023 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +set -eu +bash_source_dir="$(realpath "$(dirname "${BASH_SOURCE[0]}")")" + +# Read variable definitions output by _env.sh +source <("${bash_source_dir}/_env.sh") + + +: "${BSIM_OUT_PATH:?BSIM_OUT_PATH must be defined}" +: "${BSIM_COMPONENTS_PATH:?BSIM_COMPONENTS_PATH must be defined}" +: "${ZEPHYR_BASE:?ZEPHYR_BASE must be defined}" + +WORK_DIR="${WORK_DIR:-${ZEPHYR_BASE}/bsim_bt_out}" +BOARD="${BOARD:-nrf52_bsim}" +BOARD_ROOT="${BOARD_ROOT:-${ZEPHYR_BASE}}" +INCR_BUILD=1 +mkdir -p ${WORK_DIR} +source ${ZEPHYR_BASE}/tests/bluetooth/bsim_bt/compile.source +app="tests/bluetooth/bsim_bt/$test_name" compile diff --git a/tests/bluetooth/bsim_bt/bsim_test_mtu_update/test_scripts/_env.sh b/tests/bluetooth/bsim_bt/bsim_test_mtu_update/test_scripts/_env.sh new file mode 100755 index 00000000000000..c7041d7d4db36d --- /dev/null +++ b/tests/bluetooth/bsim_bt/bsim_test_mtu_update/test_scripts/_env.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +# Copyright 2023 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 +set -eu +bash_source_dir="$(realpath "$(dirname "${BASH_SOURCE[0]}")")" + +: "${BSIM_OUT_PATH:?BSIM_OUT_PATH must be defined}" + +test_name="$(basename "$(realpath "$bash_source_dir/..")")" +bsim_bin="${BSIM_OUT_PATH}/bin" +verbosity_level=2 +BOARD="${BOARD:-nrf52_bsim}" +simulation_id="$test_name" +central_exe="${bsim_bin}/bs_${BOARD}_tests_bluetooth_bsim_bt_${test_name}_prj_conf" +peripheral_exe="${bsim_bin}/bs_${BOARD}_tests_bluetooth_bsim_bt_${test_name}_prj_conf" + +function print_var { + # Print a shell-sourceable variable definition. + local var_name="$1" + local var_repr="${!var_name@Q}" + echo "$var_name=$var_repr" +} + +print_var test_name +print_var bsim_bin +print_var verbosity_level +print_var BOARD +print_var simulation_id +print_var central_exe +print_var peripheral_exe diff --git a/tests/bluetooth/bsim_bt/bsim_test_mtu_update/test_scripts/run_test.sh b/tests/bluetooth/bsim_bt/bsim_test_mtu_update/test_scripts/run_test.sh new file mode 100755 index 00000000000000..896508f9ed1ddc --- /dev/null +++ b/tests/bluetooth/bsim_bt/bsim_test_mtu_update/test_scripts/run_test.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +# Copyright 2023 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +set -eu +bash_source_dir="$(realpath "$(dirname "${BASH_SOURCE[0]}")")" + +# Read variable definitions output by _env.sh +source <("${bash_source_dir}/_env.sh") + +process_ids="" +exit_code=0 + +function Execute() { + if [ ! -f $1 ]; then + echo -e " \e[91m$(pwd)/$(basename $1) cannot be found (did you forget to\ + compile it?)\e[39m" + exit 1 + fi + timeout 30 $@ & + process_ids="$process_ids $!" +} + +: "${BSIM_OUT_PATH:?BSIM_OUT_PATH must be defined}" + +cd ${BSIM_OUT_PATH}/bin + +Execute "$central_exe" \ + -v=${verbosity_level} -s=${simulation_id} -d=0 -testid=central -RealEncryption=1 + +Execute "$peripheral_exe" \ + -v=${verbosity_level} -s=${simulation_id} -d=1 -testid=peripheral -RealEncryption=1 + +Execute ./bs_2G4_phy_v1 -v=${verbosity_level} -s=${simulation_id} \ + -D=2 -sim_length=60e6 $@ + +for process_id in $process_ids; do + wait $process_id || let "exit_code=$?" +done +exit $exit_code #the last exit code != 0 diff --git a/tests/bluetooth/bsim_bt/compile.sh b/tests/bluetooth/bsim_bt/compile.sh index 74d7cba73c0f94..51414416823f27 100755 --- a/tests/bluetooth/bsim_bt/compile.sh +++ b/tests/bluetooth/bsim_bt/compile.sh @@ -36,6 +36,7 @@ app=tests/bluetooth/bsim_bt/bsim_test_app conf_file=prj_split_privacy.conf \ compile & app=tests/bluetooth/bsim_bt/bsim_test_app conf_file=prj_split_low_lat.conf \ compile & +app=tests/bluetooth/bsim_bt/bsim_test_mtu_update compile & app=tests/bluetooth/bsim_bt/bsim_test_multiple compile & app=tests/bluetooth/bsim_bt/bsim_test_advx compile & app=tests/bluetooth/bsim_bt/bsim_test_adv_chain compile &