diff --git a/samples/bluetooth/mtu_update/README.rst b/samples/bluetooth/mtu_update/README.rst new file mode 100644 index 00000000000..b2cf3a8f569 --- /dev/null +++ b/samples/bluetooth/mtu_update/README.rst @@ -0,0 +1,21 @@ +.. _bluetooth_mtu_update_sample: + +Bluetooth: MTU Update +##################### + +Overview: +********* + +Application demonstrating the exchange of MTU between two devices to allow a +large notification to be sent. + +Updating the MTU is useful to send bigger packets and so have a better +throughput. + +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. diff --git a/samples/bluetooth/mtu_update/central/CMakeLists.txt b/samples/bluetooth/mtu_update/central/CMakeLists.txt new file mode 100644 index 00000000000..b1a6c12d956 --- /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 00000000000..ba93104ac90 --- /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 00000000000..6ece9e2cce3 --- /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 00000000000..a7289b3c39c --- /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 00000000000..65732828448 --- /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/peripheral/CMakeLists.txt b/samples/bluetooth/mtu_update/peripheral/CMakeLists.txt new file mode 100644 index 00000000000..8f69405476b --- /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 00000000000..3d7a2d69baf --- /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 00000000000..2ffbe15be17 --- /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 00000000000..f8f56dd8ebb --- /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 00000000000..f30e50a85ab --- /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); + } +}