diff --git a/samples/bluetooth/hci_vs_scan_req/CMakeLists.txt b/samples/bluetooth/hci_vs_scan_req/CMakeLists.txt new file mode 100644 index 00000000000..dfd126a454e --- /dev/null +++ b/samples/bluetooth/hci_vs_scan_req/CMakeLists.txt @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(hci_vs_scan_req) + +target_sources(app PRIVATE src/main.c) diff --git a/samples/bluetooth/hci_vs_scan_req/README.rst b/samples/bluetooth/hci_vs_scan_req/README.rst new file mode 100644 index 00000000000..a05f91ae613 --- /dev/null +++ b/samples/bluetooth/hci_vs_scan_req/README.rst @@ -0,0 +1,30 @@ +.. _bluetooth-hci-vs-scan-req-sample: + +Bluetooth: HCI VS Scan Request +############################## + +Overview +******** + +This simple application is a usage example to manage HCI VS commands to obtain +scan equest events even using legacy advertisements, while may result in lower +RAM usage than using extended advertising. +This is quite important in applications in which the broadcaster role is added +to the central role, where the RAM saving can be bigger. +This sample implements only the broadcaster role; the peripheral role with +connection can also be added, depending on configuration choices. + +Requirements +************ + +* A board with BLE support +* A central device & monitor (e.g. nRF Connect) to check the advertiments and + send scan requests. + +Building and Running +******************** + +This sample can be found under :zephyr_file:`samples/bluetooth/hci_vs_scan_req` +in the Zephyr tree. + +See :ref:`bluetooth samples section ` for details. diff --git a/samples/bluetooth/hci_vs_scan_req/prj.conf b/samples/bluetooth/hci_vs_scan_req/prj.conf new file mode 100644 index 00000000000..3d46f1c4c39 --- /dev/null +++ b/samples/bluetooth/hci_vs_scan_req/prj.conf @@ -0,0 +1,6 @@ +CONFIG_BT=y +CONFIG_LOG=y +CONFIG_BT_BROADCASTER=y +CONFIG_BT_DEVICE_NAME="VS Scan Notify" +CONFIG_BT_HCI_VS_EVT_USER=y +CONFIG_BT_CTLR_VS_SCAN_REQ_RX=y diff --git a/samples/bluetooth/hci_vs_scan_req/sample.yaml b/samples/bluetooth/hci_vs_scan_req/sample.yaml new file mode 100644 index 00000000000..9f2aad6f363 --- /dev/null +++ b/samples/bluetooth/hci_vs_scan_req/sample.yaml @@ -0,0 +1,12 @@ +sample: + name: Bluetooth HCI Vendor-Specific Scan Request +tests: + sample.bluetooth.hci_vs_scan_req: + harness: bluetooth + platform_allow: + - nrf52dk/nrf52832 + - qemu_cortex_m3 + - qemu_x86 + tags: bluetooth + integration_platforms: + - qemu_cortex_m3 diff --git a/samples/bluetooth/hci_vs_scan_req/src/main.c b/samples/bluetooth/hci_vs_scan_req/src/main.c new file mode 100644 index 00000000000..d43ae8b1034 --- /dev/null +++ b/samples/bluetooth/hci_vs_scan_req/src/main.c @@ -0,0 +1,152 @@ +/* main.c - Application main entry point */ + +/* + * Copyright (c) 2024 Giancarlo Stasi + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#define DEVICE_NAME CONFIG_BT_DEVICE_NAME +#define DEVICE_NAME_LENGTH (sizeof(DEVICE_NAME) - 1) + +/* Advertising Interval: the longer, the less energy consumption. + * Units: 0.625 milliseconds. + * The Minimum Advertising Interval and Maximum Advertising Interval should not be the same value + * (as stated in Bluetooth Core Spec 5.2, section 7.8.5) + */ +#define ADV_MIN_INTERVAL BT_GAP_ADV_SLOW_INT_MIN +#define ADV_MAX_INTERVAL BT_GAP_ADV_SLOW_INT_MAX + +#define ADV_OPTIONS (BT_LE_ADV_OPT_SCANNABLE | BT_LE_ADV_OPT_NOTIFY_SCAN_REQ) + +static uint8_t scan_data[] = {'V', 'S', ' ', 'S', 'a', 'm', 'p', 'l', 'e'}; + +static const struct bt_le_adv_param parameters = { + .options = ADV_OPTIONS, + .interval_min = ADV_MIN_INTERVAL, + .interval_max = ADV_MAX_INTERVAL, +}; + +static const struct bt_data adv_data[] = { + BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), + BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LENGTH), +}; + +static const struct bt_data scan_rsp_data[] = { + BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), + BT_DATA(BT_DATA_MANUFACTURER_DATA, scan_data, sizeof(scan_data)), +}; + +static const char *bt_addr_le_str(const bt_addr_le_t *addr) +{ + static char str[BT_ADDR_LE_STR_LEN]; + + bt_addr_le_to_str(addr, str, sizeof(str)); + + return str; +} + +/* Bluetooth specification doesn't allow the scan request event with legacy advertisements. + * Ref: Bluetooth Core Specification v5.4, section 7.7.65.19 "LE Scan Request Received event" : + * "This event shall only be generated if advertising was enabled using the + * HCI_LE_Set_Extended_Advertising_Enable command." + * Added a Vendor Specific command to add this feature and save RAM. + */ +static void enable_legacy_adv_scan_request_event(bool enable) +{ + struct bt_hci_cp_vs_set_scan_req_reports *cp; + struct net_buf *buf; + int err; + + buf = bt_hci_cmd_create(BT_HCI_OP_VS_SET_SCAN_REQ_REPORTS, sizeof(*cp)); + if (!buf) { + printk("%s: Unable to allocate HCI command buffer\n", __func__); + return; + } + + cp = net_buf_add(buf, sizeof(*cp)); + cp->enable = (uint8_t) enable; + + err = bt_hci_cmd_send(BT_HCI_OP_VS_SET_SCAN_REQ_REPORTS, buf); + if (err) { + printk("Set legacy cb err: %d\n", err); + return; + } +} + +static bool vs_scanned(struct net_buf_simple *buf) +{ + struct bt_hci_evt_vs_scan_req_rx *evt; + struct bt_hci_evt_vs *vs; + + vs = net_buf_simple_pull_mem(buf, sizeof(*vs)); + evt = (void *)buf->data; + + printk("%s subevent 0x%02x peer %s rssi %d\n", __func__, + vs->subevent, bt_addr_le_str(&evt->addr), evt->rssi); + + return true; +} + +static int start_advertising(void) +{ + int err; + + err = bt_hci_register_vnd_evt_cb(vs_scanned); + if (err) { + printk("VS user callback register err %d\n", err); + return err; + } + + enable_legacy_adv_scan_request_event(true); + err = bt_le_adv_start(¶meters, adv_data, ARRAY_SIZE(adv_data), + scan_rsp_data, ARRAY_SIZE(scan_rsp_data)); + if (err) { + printk("Start legacy adv err %d\n", err); + return err; + } + + printk("Advertising successfully started (%s)\n", CONFIG_BT_DEVICE_NAME); + + return 0; +} + +static void bt_ready(int err) +{ + if (err) { + printk("Bluetooth init failed (err %d)\n", err); + return; + } + + printk("Bluetooth initialized\n"); + + err = start_advertising(); + + if (err) { + printk("Advertising failed to start (err %d)\n", err); + return; + } + + printk("Vendor-Specific Scan Request sample started\n"); +} + +int main(void) +{ + int err; + + printk("Starting Vendor-Specific Scan Request sample\n"); + + /* Initialize the Bluetooth Subsystem */ + err = bt_enable(bt_ready); + if (err) { + printk("Bluetooth init failed (err %d)\n", err); + } + + printk("Main function end, leave stack running for scans\n"); + + return 0; +}